<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Juun의 개발 기록</title>
    <link>https://puenti.tistory.com/</link>
    <description>어제보다 오늘 더 성장하는 모습을 기록합니다</description>
    <language>ko</language>
    <pubDate>Tue, 7 Apr 2026 08:41:26 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Juun</managingEditor>
    <image>
      <title>Juun의 개발 기록</title>
      <url>https://tistory1.daumcdn.net/tistory/4334290/attach/64d71995c0174119836158c6feda99f4</url>
      <link>https://puenti.tistory.com</link>
    </image>
    <item>
      <title>Next.js 이미지 충돌 문제 해결기: GCP 로드밸런서와 캐시 이슈</title>
      <link>https://puenti.tistory.com/103</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;2025년 5월, 운영 중인 Next.js 서비스에서 &lt;b&gt;사용자 프로필 이미지가 간헐적으로 다른 유저의 이미지로 표시되는 현상&lt;/b&gt;이 발생했습니다.&lt;br /&gt;주요 증상은 다음과 같았습니다.&lt;/p&gt;
&lt;h2 data-end=&quot;427&quot; data-start=&quot;417&quot; data-ke-size=&quot;size26&quot;&gt;현상 요약&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;584&quot; data-start=&quot;429&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-start=&quot;294&quot; data-end=&quot;330&quot;&gt;동일한 이미지 요청임에도 불구하고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;다른 이미지가 표시됨&lt;/b&gt;&lt;/li&gt;
&lt;li data-start=&quot;331&quot; data-end=&quot;388&quot;&gt;브라우저 개발자 도구에서 _next/image?... 경로의 이미지 응답이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;서버마다 다름&lt;/b&gt;&lt;/li&gt;
&lt;li data-start=&quot;389&quot; data-end=&quot;432&quot;&gt;특정 환경에서 304 Not Modified 응답이 의도와 다르게 작동&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;445&quot; data-start=&quot;434&quot; data-ke-size=&quot;size26&quot;&gt;원인 분석&lt;/h2&gt;
&lt;p data-end=&quot;519&quot; data-start=&quot;447&quot; data-ke-size=&quot;size16&quot;&gt;문제는 최근 &lt;b&gt;GCP 로드밸런서의 정책 변경&lt;/b&gt;과 &lt;b&gt;Next.js 이미지 최적화 캐시 처리 방식&lt;/b&gt;이 충돌하면서 발생했습니다.&lt;/p&gt;
&lt;h3 data-end=&quot;559&quot; data-start=&quot;521&quot; data-ke-size=&quot;size23&quot;&gt;1. GCP 로드밸런서 정책 변경 (2025.04.28 적용)&lt;/h3&gt;
&lt;p data-end=&quot;607&quot; data-start=&quot;560&quot; data-ke-size=&quot;size16&quot;&gt;Google Cloud는 2025년 4월 28일부터 다음과 같은 정책을 적용했습니다:&lt;/p&gt;
&lt;p data-end=&quot;607&quot; data-start=&quot;560&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;HTTP/1.1 트래픽에서 hop-by-hop 헤더를 참조하는 커스텀 헤더 사용 제한&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;752&quot; data-start=&quot;611&quot; data-ke-size=&quot;size16&quot;&gt;(참조: &lt;a data-end=&quot;751&quot; data-start=&quot;672&quot;&gt;GCP Release Notes&lt;/a&gt;)&lt;/p&gt;
&lt;p data-end=&quot;841&quot; data-start=&quot;754&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;841&quot; data-start=&quot;754&quot; data-ke-size=&quot;size16&quot;&gt;해당 변경으로 인해 &lt;b&gt;프록시나 로드밸런서가 Vary, Connection 등의 캐시 제어 헤더를 무시하거나 제거&lt;/b&gt;하는 사례가 발생할 수 있습니다.&lt;/p&gt;
&lt;h3 data-end=&quot;865&quot; data-start=&quot;843&quot; data-ke-size=&quot;size23&quot;&gt;2. 서버 간 이미지 캐시 불일치&lt;/h3&gt;
&lt;p data-end=&quot;995&quot; data-start=&quot;866&quot; data-ke-size=&quot;size16&quot;&gt;Next.js는 _next/image 경로를 통해 이미지를 서버에서 리사이징하고 .next/cache/images에 저장합니다.&lt;br /&gt;여러 서버가 분산되어 있는 환경(GCP에서 라운드 로빈 분산)에서는 아래 문제가 발생합니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1058&quot; data-start=&quot;997&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1027&quot; data-start=&quot;997&quot;&gt;서버 A와 B가 &lt;b&gt;서로 다른 캐시 상태&lt;/b&gt;를 유지&lt;/li&gt;
&lt;li data-end=&quot;1058&quot; data-start=&quot;1028&quot;&gt;동일한 요청에 대해 &lt;b&gt;다른 이미지 결과&lt;/b&gt;를 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;1078&quot; data-start=&quot;1060&quot; data-ke-size=&quot;size23&quot;&gt;3. 세션 어피니티 미설정&lt;/h3&gt;
&lt;p data-end=&quot;1142&quot; data-start=&quot;1079&quot; data-ke-size=&quot;size16&quot;&gt;로드밸런서에 세션 어피니티가 설정되지 않아 클라이언트 요청이 매번 다른 서버로 전달되며 충돌 확률이 증가했습니다.&lt;/p&gt;
&lt;p data-end=&quot;1142&quot; data-start=&quot;1079&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;1178&quot; data-start=&quot;1149&quot; data-ke-size=&quot;size26&quot;&gt;문제 해결: unoptimized 설정&lt;/h2&gt;
&lt;p data-end=&quot;1293&quot; data-start=&quot;1180&quot; data-ke-size=&quot;size16&quot;&gt;즉각적인 대응책으로 &lt;b&gt;Next.js 이미지 최적화 기능을 비활성화&lt;/b&gt;했습니다.&lt;br /&gt;이를 통해 서버에서 캐시 처리나 이미지 리사이징을 하지 않고, &lt;b&gt;브라우저가 원본 URL을 직접 요청&lt;/b&gt;하게 했습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1747107956698&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Next.js 13 이상
// Next.js 13 이상에서는 next.config.js에서 전역 설정이 가능합니다:
// next.config.js
module.exports = {
  images: {
    unoptimized: true,
  },
};

//Next.js 12에서는 전역 설정 불가
//하지만 Next.js 12 버전에서는 unoptimized 옵션이 전역 설정으로 지원되지 않으며, 개별 컴포넌트에서 직접 설정

&amp;lt;Image
  src={userImageUrl}
  alt=&quot;User profile&quot;
  width={100}
  height={100}
  unoptimized
/&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;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1593&quot; data-start=&quot;1522&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1545&quot; data-start=&quot;1522&quot;&gt;서버 간 이미지 결과 불일치 문제 해결&lt;/li&gt;
&lt;li data-end=&quot;1574&quot; data-start=&quot;1546&quot;&gt;브라우저 캐시에 저장되어 재요청 시 일관성 확보&lt;/li&gt;
&lt;li data-end=&quot;1593&quot; data-start=&quot;1575&quot;&gt;이미지 충돌 현상 즉시 해결됨&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;102&quot; data-start=&quot;86&quot; data-ke-size=&quot;size26&quot;&gt;추후 대책 및 고려사항&lt;/h2&gt;
&lt;p data-end=&quot;214&quot; data-start=&quot;104&quot; data-ke-size=&quot;size16&quot;&gt;이번 문제를 겪고 나서, 단순히 unoptimized 하나로 넘기기보단 조금 더 근본적인 개선이 필요하겠다는 생각이 들었습니다.&lt;/p&gt;
&lt;p data-end=&quot;214&quot; data-start=&quot;104&quot; data-ke-size=&quot;size16&quot;&gt;아래는 저희 팀이 실제로 검토했거나 적용 중인 후속 대응들입니다.&lt;/p&gt;
&lt;h3 data-end=&quot;239&quot; data-start=&quot;216&quot; data-ke-size=&quot;size23&quot;&gt;1. 세션 어피니티는 꼭 설정해두자&lt;/h3&gt;
&lt;p data-end=&quot;422&quot; data-start=&quot;241&quot; data-ke-size=&quot;size16&quot;&gt;서버가 여러 대인 환경에서는 같은 사용자의 요청이 매번 다른 서버로 전달되면 캐시 충돌이 발생할 수 있습니다.&lt;br /&gt;GCP 로드밸런서를 사용하는 경우, 백엔드 서비스 설정에서 &lt;b&gt;세션 어피니티를 &amp;lsquo;Client IP&amp;rsquo; 기반으로 설정&lt;/b&gt;해 주는 것이 좋습니다.&lt;br /&gt;이를 통해 같은 클라이언트의 요청이 항상 동일한 서버로 전달되도록 유도할 수 있습니다.&lt;/p&gt;
&lt;h3 data-end=&quot;451&quot; data-start=&quot;424&quot; data-ke-size=&quot;size23&quot;&gt;2. 이미지 캐시 디렉토리는 공유하면 좋다&lt;/h3&gt;
&lt;p data-end=&quot;638&quot; data-start=&quot;453&quot; data-ke-size=&quot;size16&quot;&gt;Next.js에서 이미지 최적화를 켜면 .next/cache/images에 서버별로 캐시가 쌓이는데, 이게 서버마다 다르면 똑같은 요청도 결과가 달라집니다.&lt;br /&gt;가능하다면 이 디렉토리를 &lt;b&gt;NFS나 Cloud Filestore 같은 네트워크 스토리지로 묶어서 공유&lt;/b&gt;하면 안정성이 높아집니다.&lt;/p&gt;
&lt;p data-end=&quot;638&quot; data-start=&quot;453&quot; data-ke-size=&quot;size16&quot;&gt;물론 운영 복잡도는 좀 올라갑니다.&lt;/p&gt;
&lt;h3 data-end=&quot;662&quot; data-start=&quot;640&quot; data-ke-size=&quot;size23&quot;&gt;3. 헤더 관련해서도 꼼꼼히 보자&lt;/h3&gt;
&lt;p data-end=&quot;839&quot; data-start=&quot;664&quot; data-ke-size=&quot;size16&quot;&gt;디버깅 과정에서 Vary, Accept, Authorization 등의 헤더가 &lt;b&gt;로드밸런서나 프록시에서 필터링되거나 누락되는 경우&lt;/b&gt;, 의도한 캐싱 동작이 깨질 수 있다는 사실을 확인했습니다.&lt;br /&gt;특히 Vary 헤더는 브라우저와 CDN이 어떤 기준으로 캐시할지를 결정하는 핵심 요소이므로, 누락될 경우 동일한 URL 요청에 대해 서로 다른 응답이 캐시될 수 있습니다.&lt;/p&gt;
&lt;h3 data-end=&quot;872&quot; data-start=&quot;841&quot; data-ke-size=&quot;size23&quot;&gt;4. Cloud CDN도 쓴다면 캐시 정책을 체크&lt;/h3&gt;
&lt;p data-end=&quot;1010&quot; data-start=&quot;874&quot; data-ke-size=&quot;size16&quot;&gt;Cloud CDN을 이미지 경로에 적용하고 있다면, 단순히 활성화하는 것만으로는 부족합니다.&lt;br /&gt;&lt;b&gt;캐시 키 구성에 어떤 요소가 포함되는지, 특히 헤더 기반 분기가 고려되는지&lt;/b&gt;를 반드시 확인해야 합니다.&lt;br /&gt;예를 들어 Vary 헤더를 포함한 상태에서 브라우저 포맷(WebP 등)에 따라 다른 응답이 나가도록 되어 있다면, 이를 CDN이 제대로 인식하고 처리하고 있는지 검토해야 합니다.&lt;/p&gt;
&lt;h3 data-end=&quot;1287&quot; data-start=&quot;1230&quot; data-ke-size=&quot;size23&quot;&gt;5. Next.js 13&lt;/h3&gt;
&lt;p data-end=&quot;1443&quot; data-start=&quot;1289&quot; data-ke-size=&quot;size16&quot;&gt;Next.js 13부터는 _next/image 경로로 반환되는 이미지 응답에 &lt;b&gt;Vary: Accept 헤더가 기본적으로 포함&lt;/b&gt;됩니다.&lt;br /&gt;이 헤더는 브라우저가 지원하는 이미지 포맷(WebP, AVIF 등)에 따라 다른 리소스를 캐시할 수 있도록 돕는 역할을 합니다.&lt;/p&gt;
&lt;p data-end=&quot;1613&quot; data-start=&quot;1445&quot; data-ke-size=&quot;size16&quot;&gt;문제는 GCP 로드밸런서나 기타 프록시 계층에서 이 헤더가 차단되거나 제거되는 경우, &lt;b&gt;서버 또는 브라우저에 잘못된 이미지가 캐시될 수 있다는 점&lt;/b&gt;입니다.&lt;br /&gt;Next.js 12에서는 이 헤더가 기본 포함되지 않기 때문에, 버전 업 이후 예상치 못한 캐시 충돌이 발생하는 경우가 생길 수 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;1701&quot; data-start=&quot;1615&quot; data-ke-size=&quot;size16&quot;&gt;따라서 Next.js 13 이상을 사용하는 경우, &lt;b&gt;Vary: Accept 헤더가 클라이언트까지 정확히 전달되고 있는지&lt;/b&gt;를 반드시 점검해야 합니다.&lt;/p&gt;
&lt;p data-end=&quot;1701&quot; data-start=&quot;1615&quot; data-ke-size=&quot;size16&quot;&gt;저희 팀은 Next 12를 사용중이라 이번 일을 통해 Next13으로 마이그레이션을 고려하고 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;1701&quot; data-start=&quot;1615&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;1701&quot; data-start=&quot;1615&quot; data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제는 단순한 이미지 충돌처럼 보였지만, &lt;b&gt;로드밸런서의 정책 변경&lt;/b&gt;, &lt;b&gt;Next.js 내부 캐시 전략&lt;/b&gt;, &lt;b&gt;서버 아키텍처 구성&lt;/b&gt;이 복합적으로 얽힌 이슈였습니다.&lt;br /&gt;unoptimized: true는 빠른 대응책으로 유효하지만, 장기적으로는 &lt;b&gt;분산 환경에 맞는 캐시 전략과 로드밸런서 설정 최적화&lt;/b&gt;가 필요하며 비슷한 이슈를 겪고 있는 다른분들에게도 도움이 되는 글이면 좋겠습니다.&lt;/p&gt;</description>
      <category>Programming/React.js, Next.js</category>
      <category>Next.js</category>
      <category>next12</category>
      <category>Next13</category>
      <category>Nextjs</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/103</guid>
      <comments>https://puenti.tistory.com/103#entry103comment</comments>
      <pubDate>Tue, 13 May 2025 12:58:09 +0900</pubDate>
    </item>
    <item>
      <title>[Next.js] next/dynamic 2. 효율적으로 활용하기</title>
      <link>https://puenti.tistory.com/101</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://puenti.tistory.com/100&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://puenti.tistory.com/100&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1733812832715&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;[Next.js] next/dynamic 1. 사용법과 빌드 과정&quot; data-og-description=&quot;회사 내 서비스가 커지다보니 빌드 시간과 초기 JS 파일의 크기가 커지는 문제가 있었습니다.이는 곧 성능과 속도에 문제가 생길 수 있어 next/dynamic을 적용시켜 개선해보았습니다.&amp;nbsp;next/dynamic은 Ne&quot; data-og-host=&quot;puenti.tistory.com&quot; data-og-source-url=&quot;https://puenti.tistory.com/100&quot; data-og-url=&quot;https://puenti.tistory.com/100&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cpIe4f/hyXKrkKeTb/WfUGFRD5UoMX3lKm6KFXCK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/TgO9k/hyXKlY7kb8/QhOxUBDWLBM1jfzakOY8Ek/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bROnqs/hyXKuIxCaS/xWnkVqSoBC7fEJT05fatmK/img.jpg?width=3024&amp;amp;height=3024&amp;amp;face=0_0_3024_3024&quot;&gt;&lt;a href=&quot;https://puenti.tistory.com/100&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://puenti.tistory.com/100&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cpIe4f/hyXKrkKeTb/WfUGFRD5UoMX3lKm6KFXCK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/TgO9k/hyXKlY7kb8/QhOxUBDWLBM1jfzakOY8Ek/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bROnqs/hyXKuIxCaS/xWnkVqSoBC7fEJT05fatmK/img.jpg?width=3024&amp;amp;height=3024&amp;amp;face=0_0_3024_3024');&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;[Next.js] next/dynamic 1. 사용법과 빌드 과정&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;회사 내 서비스가 커지다보니 빌드 시간과 초기 JS 파일의 크기가 커지는 문제가 있었습니다.이는 곧 성능과 속도에 문제가 생길 수 있어 next/dynamic을 적용시켜 개선해보았습니다.&amp;nbsp;next/dynamic은 Ne&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;puenti.tistory.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;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;p data-ke-size=&quot;size16&quot;&gt;next/dynamic은 동적 로드를 지원합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 만약 &lt;b&gt;해당 컴포넌트가 한 개가 아니라 여러 개일 경우 어떻게 적용시키면 좋을까&lt;/b&gt;에서 시작했습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;A 컴포넌트가 5곳에서 사용되고, 2곳에서만 next/dynamic을 적용한 경우:&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;b&gt;동적 로드가 적용된 2곳:&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 경우, A 컴포넌트는 초기 번들에서 제외됩니다.&lt;/li&gt;
&lt;li&gt;해당 페이지가 클라이언트에서 렌더링될 때, A 컴포넌트는 네트워크 요청을 통해 로드됩니다.&lt;/li&gt;
&lt;li&gt;이로 인해 초기 로드 속도가 개선될 수 있지만, A 컴포넌트를 사용하는 시점에 추가 네트워크 요청이 발생합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;동적 로드가 적용되지 않은 3곳:&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 경우, A 컴포넌트는 초기 번들에 포함됩니다.&lt;/li&gt;
&lt;li&gt;A 컴포넌트가 필요한 페이지를 방문하면 즉시 렌더링됩니다.&lt;/li&gt;
&lt;li&gt;네트워크 요청 없이 바로 사용 가능하므로 클라이언트 사이드 성능이 더 좋습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;빌드 시 처리 방식:&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Next.js는 &lt;b&gt;동적 로드가 적용된 경우와 그렇지 않은 경우를 개별적으로 처리&lt;/b&gt;합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동적 로드를 사용하는 2곳에서는 A 컴포넌트를 별도의 청크(chunk)로 분리합니다.&lt;/li&gt;
&lt;li&gt;나머지 3곳에서는 A 컴포넌트를 각 해당 페이지의 번들에 포함합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;따라서, A 컴포넌트는 두 가지 방식으로 빌드됩니다:
&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;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;주의사항:&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;A 컴포넌트가 크거나 자주 사용되지 않는다면 next/dynamic을 사용하는 것이 이점이 될 수 있습니다.&lt;/li&gt;
&lt;li&gt;하지만 A 컴포넌트가 매우 작거나, &lt;b&gt;앱에서 자주 사용되는 경우라면 next/dynamic은 불필요한 네트워크 요청을 초래해 오히려 성능에 부정적인 영향&lt;/b&gt;을 줄 수 있습니다.&lt;/li&gt;
&lt;li&gt;동일한 컴포넌트를 동적 로드와 정적 로드로 혼용하면, 코드 스플리팅 전략이 복잡해질 수 있으니 관리에 유의해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;고려사항:&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. 컴포넌트의 크기&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;컴포넌트가 크고 복잡한 경우&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;큰 컴포넌트(예: 복잡한 그래프 라이브러리, 무거운 UI 요소)를 next/dynamic으로 분리하면 초기 번들 크기를 줄일 수 있습니다.&lt;/li&gt;
&lt;li&gt;초기 페이지 로딩 속도가 개선되고, 컴포넌트가 필요한 시점에만 로드되므로 효율적입니다.&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;컴포넌트가 작거나 경량인 경우&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;next/dynamic으로 분리해도 성능 이점이 크지 않습니다.&lt;/li&gt;
&lt;li&gt;오히려 네트워크 요청 오버헤드가 발생해 성능 저하가 생길 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 사용 빈도&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;b&gt;자주 사용되지 않는 컴포넌트&lt;/b&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;이러한 컴포넌트는 next/dynamic을 적용하면 초기 로드 시 불필요한 코드를 줄일 수 있어 효과적입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자주 사용되는 컴포넌트&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예: 대부분의 페이지에서 사용되는 공통 UI 컴포넌트(헤더, 푸터).&lt;/li&gt;
&lt;li&gt;이런 경우 next/dynamic을 사용하면 매번 네트워크 요청을 해야 하므로 불필요한 오버헤드가 발생합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 초기 로드 시간 개선 필요 여부&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;b&gt;초기 로드 속도에 민감한 경우&lt;/b&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;이를 통해 &quot;First Contentful Paint (FCP)&quot;와 &quot;Largest Contentful Paint (LCP)&quot; 메트릭을 개선할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;로드 속도보다 인터랙티브 경험이 더 중요한 경우&lt;/b&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;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. 코드 스플리팅 효과&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;Next.js는 next/dynamic을 활용해 코드를 스플리팅합니다.
&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;따라서 페이지 크기가 크게 줄어드는 경우 next/dynamic의 효과가 매우 큽니다.&lt;/li&gt;
&lt;li&gt;하지만 컴포넌트를 너무 많이 동적으로 로드하면 요청 횟수가 증가해 HTTP/2 환경이 아닌 경우 성능 저하가 있을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;권장 사항:&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴포넌트를 사용하는 모든 곳에 대해 next/dynamic을 적용하거나, 모든 곳에서 적용하지 않는 일관된 접근을 사용하는 것이 관리 측면에서 더 효율적일 수 있습니다.&lt;/li&gt;
&lt;li&gt;필요하다면 dynamic의 ssr: false 옵션이나 loading 옵션을 사용해 성능을 세부적으로 조정하는 것이 좋습니다.&lt;/li&gt;
&lt;/ul&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;b&gt;페이지 전환 후 렌더링되는 컴포넌트&lt;/b&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;dynamic(import('...'))으로 처리하면 다른 페이지 로드에 영향을 주지 않음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SSR이 필요 없는 컴포넌트&lt;/b&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;ssr: false 옵션으로 서버사이드 렌더링을 비활성화해 더 빠른 렌더링이 가능.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;결론:&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자주 사용하는 컴포넌트라면 &lt;b&gt;next/dynamic&lt;/b&gt; &lt;b&gt;사용하지 않는게 성능에 더 좋을 수 있음.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;만약 사용할 것이라면 dynamic 옵션을 조정할 것!!&lt;/li&gt;
&lt;li&gt;자주 사용하는 공통 컴포넌트를 분리할 때는 효과가 미미할 수 있음.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Programming/React.js, Next.js</category>
      <category>next dynamic</category>
      <category>next/dynamic</category>
      <category>Nextjs</category>
      <category>동적 로드</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/101</guid>
      <comments>https://puenti.tistory.com/101#entry101comment</comments>
      <pubDate>Tue, 10 Dec 2024 16:02:44 +0900</pubDate>
    </item>
    <item>
      <title>[Next.js] next/dynamic 1. 사용법과 빌드 과정</title>
      <link>https://puenti.tistory.com/100</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;회사 내 서비스가 커지다보니 빌드 시간과 초기 JS 파일의 크기가 커지는 문제가 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 곧 성능과 속도에 문제가 생길 수 있어 next/dynamic을 적용시켜 개선해보았습니다.&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/dynamic은 Next.js에서 동적 임포트를 지원하는 기능으로, 컴포넌트를 필요한 시점에 로드하도록 설정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특정 컴포넌트에 next/dynamic을 적용하면 해당 컴포넌트는 초기 번들에 포함되지 않고 클라이언트에서 필요할 때 로드&lt;/b&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/dynamic을 사용하는 방법은 다음과 같습니다:&lt;/p&gt;
&lt;pre id=&quot;code_1733208896979&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import dynamic from 'next/dynamic';

const DynamicComponent = dynamic(() =&amp;gt; import('./DynamicComponent'));

function Page() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;My Page&amp;lt;/h1&amp;gt;
      &amp;lt;DynamicComponent /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default Page;&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;next/dynamic은 기본적으로 동적 컴포넌트를 로드하면서 렌더링할 때까지 기다리지만, 다양한 옵션을 추가할 수 있습니다.&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. Loading Component&lt;/b&gt;: 로딩 중에 표시할 컴포넌트를 지정할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1733208939555&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;const DynamicComponent = dynamic(
  () =&amp;gt; import('./DynamicComponent'),
  { loading: () =&amp;gt; &amp;lt;p&amp;gt;Loading...&amp;lt;/p&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;2. 서버 사이드 렌더링 비활성화&lt;/b&gt;: 일부 컴포넌트는 서버 사이드 렌더링이 필요 없는 경우가 있습니다. ssr: false를 사용하면 클라이언트에서만 렌더링할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1733208960411&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const DynamicComponent = dynamic(
  () =&amp;gt; import('./DynamicComponent'),
  { ssr: false }
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;next/dynamic은 페이지나 컴포넌트를 렌더링하는 동안 필요하지 않은 자바스크립트 번들을 지연시켜, 페이지 로딩 성능을 향상시킬 수 있습니다.&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/dynamic을 사용하면 동적으로 import된 컴포넌트는 빌드 시에 &lt;b&gt;정적으로 포함되지 않고, 별도의 청크(chunk)로 분리&lt;/b&gt;되어 클라이언트에서 필요한 시점에 로드됩니다. 즉, 빌드 시에는 해당 컴포넌트가 포함되지 않으며, 실행 시에만 로드됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;b&gt;컴파일 단계&lt;/b&gt;: next/dynamic을 사용하여 동적으로 import된 컴포넌트는 Next.js 빌드 중에 별도의 청크 파일로 분리됩니다. 이 파일은 페이지가 로드될 때만 브라우저에서 다운로드됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;클라이언트에서 로드&lt;/b&gt;: 해당 컴포넌트가 실제로 필요해질 때, 즉 사용자가 해당 페이지를 방문하거나 해당 컴포넌트가 렌더링되는 시점에 동적으로 로드됩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 빌드 시에 dynamic import된 컴포넌트는 바로 포함되지 않고, &lt;b&gt;클라이언트 사이드에서 로드될 때만 번들로 포함&lt;/b&gt;되며, &lt;b&gt;초기 로딩 시간은 줄어듭니다&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div data-message-model-slug=&quot;gpt-4o-mini&quot; data-message-id=&quot;4ce7f8f9-6549-4eaa-b288-4c6a9c1d7152&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드 후에는 Chart 컴포넌트가 별도의 파일(예: Chart-abc123.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;이번 포스팅은 원론적인 내용만 다뤘는데 다음 포스팅은 next/dynamic을 실무에서 적용해보면ㅅ 느낀 점을 포스팅했습니다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&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://puenti.tistory.com/101&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://puenti.tistory.com/101&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1733814191272&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;[Next.js] next/dynamic 2. 효율적으로 활용하기&quot; data-og-description=&quot;https://puenti.tistory.com/100&amp;nbsp;[Next.js] next/dynamic 1. 사용법과 빌드 과정회사 내 서비스가 커지다보니 빌드 시간과 초기 JS 파일의 크기가 커지는 문제가 있었습니다.이는 곧 성능과 속도에 문제가 생길 &quot; data-og-host=&quot;puenti.tistory.com&quot; data-og-source-url=&quot;https://puenti.tistory.com/101&quot; data-og-url=&quot;https://puenti.tistory.com/101&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/uUFOA/hyXKlx2tfQ/bzt5okkLA4FDqsIuNk6wGk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/gmQmN/hyXKnWXybl/yQbO4h9c6Uon4DUiOVyxHk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://puenti.tistory.com/101&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://puenti.tistory.com/101&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/uUFOA/hyXKlx2tfQ/bzt5okkLA4FDqsIuNk6wGk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/gmQmN/hyXKnWXybl/yQbO4h9c6Uon4DUiOVyxHk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&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;[Next.js] next/dynamic 2. 효율적으로 활용하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;https://puenti.tistory.com/100&amp;nbsp;[Next.js] next/dynamic 1. 사용법과 빌드 과정회사 내 서비스가 커지다보니 빌드 시간과 초기 JS 파일의 크기가 커지는 문제가 있었습니다.이는 곧 성능과 속도에 문제가 생길&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;puenti.tistory.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;</description>
      <category>Programming/React.js, Next.js</category>
      <category>next dynamic</category>
      <category>next/dynamic</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/100</guid>
      <comments>https://puenti.tistory.com/100#entry100comment</comments>
      <pubDate>Tue, 10 Dec 2024 15:39:51 +0900</pubDate>
    </item>
    <item>
      <title>우테코(우아한테크코스) 프론트엔드 폴더 구조 톺아보기</title>
      <link>https://puenti.tistory.com/99</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;배민의 웹서비스는 리액트와 자바로 구성되어 있다고 알고 있습니다.&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;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. packge.json&lt;/h2&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;&lt;b&gt;1. Next.js의 부재&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트 공식문서에서 조차 Next.js 사용을 권장하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 우테코의 프로젝트들을 살펴보면 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;많은 분들이 아시겠지만 리액트는 라이브러리입니다. Next.js는 리액트의 프레임워크죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js에서 제공하는 편리한 기능 대신 본인들이 필요한 라이브러리들을 하나하나 찾아가며 목적에 맞게 사용한다는 느낌을 받았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 예로, 많은 프로젝트에서 상태관리 라이브러리, Redux, Recoil, Zustand와 같은 편리한 라이브러리보단 리액트 ContexAPI를 사용하는 점이 인상 깊었네요.&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;&lt;b&gt;2. TDD&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 프로젝트에서 jest와 testing-library 같은 라이브러리를 통해 테스트 코드 작성을 통해 코드의 품질을 높이려는 노력이 보였습니다.&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. msw&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;msw란 &lt;span style=&quot;color: #333e4c; text-align: start;&quot;&gt;먼저 MSW를 간략하게 설명하면,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #333e4c; text-align: start;&quot; href=&quot;https://v1.mswjs.io/&quot;&gt;MSW(Mock Service Worker)&lt;/a&gt;&lt;span style=&quot;color: #333e4c; text-align: start;&quot;&gt;란, 클라이언트가 HTTP 요청을 전송하면 Service Worker가 요청을 가로챈(intercept) 후 Mocking된 응답 값을 반환함으로써 서버와의 통신을 모방하는 오픈소스 라이브러리입니다.&lt;/span&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;span style=&quot;color: #333e4c; text-align: start;&quot;&gt;요즘 웹 서비스는 프론트엔드와 백엔드가 분리되어 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333e4c; text-align: start;&quot;&gt;풀스택을 제외한 프로젝트나 신규 기능 개발을 하게 되면 우리는 협업을 통해 프로젝트를 진행하게 됩니다.&lt;/span&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;b&gt;적극 도입&lt;/b&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;2. 프론트엔드 디렉토리 구조&lt;/h2&gt;
&lt;pre id=&quot;code_1722821193982&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;src
├── apis
├── components
├── constants
├── contexts or store
├── hooks
├── mocks
├── pages
├── types
├── styles
├── routers
├── utils
└── ....etc&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;디렉토리 구조를 보면서 느낀점은 객체지향의 5원칙이 생각났습니다.&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;
&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. coponents 폴더&lt;/b&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;b&gt;2. constants 폴더&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공통적으로 사용되는 init value나 상수들을 정의한 파일들이 위치하는 폴더.&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;b&gt;3. contexts or Store&lt;/b&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;b&gt;4. hooks&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트 hooks 관련 폴더. 프로젝트마다 엄청난 양의 리액트 hooks들이 존재...&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;5. mocks&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 언급한 msw 관련 폴더, 가데이터와 handler 등이 있음.&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;6. routers&lt;/b&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;폴더가 없으면 Router.tsx와 같은 파일에서 관리&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;/h2&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;아는 만큼 보인다라는 말이 있습니다.&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;이번 우테코 프로젝트를 하나하나 상세히 보지는 못 했지만 살펴보면서 저희 회사 코드랑 비교도 해보고 좋아보이는건 도입해봐야지 라는 생각도하고 더 좋은 구조와 개발에 대해 고민하게 되는 좋은 시간이었습니다.&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;&lt;a href=&quot;https://github.com/woowacourse-teams&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/woowacourse-teams&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1722824732414&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;profile&quot; data-og-title=&quot;woowacourse-teams&quot; data-og-description=&quot;woowacourse-teams has 82 repositories available. Follow their code on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/woowacourse-teams&quot; data-og-url=&quot;https://github.com/woowacourse-teams&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bgrd3d/hyWKApi8AK/6ng4fCfqvvf2LUYdK87DD0/img.png?width=280&amp;amp;height=280&amp;amp;face=0_0_280_280&quot;&gt;&lt;a href=&quot;https://github.com/woowacourse-teams&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/woowacourse-teams&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bgrd3d/hyWKApi8AK/6ng4fCfqvvf2LUYdK87DD0/img.png?width=280&amp;amp;height=280&amp;amp;face=0_0_280_280');&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;woowacourse-teams&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;woowacourse-teams has 82 repositories available. Follow their code on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.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;</description>
      <category>Programming/React.js, Next.js</category>
      <category>우아한테크코스</category>
      <category>우테코</category>
      <category>우테코 프로젝트</category>
      <category>우테코 프론트엔드</category>
      <category>프론트엔드 폴더 구조</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/99</guid>
      <comments>https://puenti.tistory.com/99#entry99comment</comments>
      <pubDate>Mon, 5 Aug 2024 11:22:36 +0900</pubDate>
    </item>
    <item>
      <title>GCP로 node 배포하기 2. (HTTPS, SSL 인증 받기)</title>
      <link>https://puenti.tistory.com/98</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전 글과 이어지는 내용입니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이전 글 내용이 궁금하신 분은 아래의 링크를 참고해주시면 됩니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; 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://puenti.tistory.com/97&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://puenti.tistory.com/97&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1722387180678&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;[GCP] GCP로 node 배포하기 1. (네트워크, DNS 세팅)&quot; data-og-description=&quot;이번 토이 프로젝트로 구글에서 제공하는 API, storage를 사용해서 간단한 서비스를 만들어 봤습니다.구글에서 제공하는 서비스를 쓰다보니 배포 역시 AWS 보단 GCP를 사용해보기로 했습니다.번외로&quot; data-og-host=&quot;puenti.tistory.com&quot; data-og-source-url=&quot;https://puenti.tistory.com/97&quot; data-og-url=&quot;https://puenti.tistory.com/97&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/N8EAQ/hyWG3y6xq8/9lYyYv219MktLk8HM0t6Fk/img.png?width=570&amp;amp;height=1268&amp;amp;face=0_0_570_1268,https://scrap.kakaocdn.net/dn/brdWBw/hyWGUhRayu/OBWp5SAeb0Gf0a3YisfkD0/img.png?width=570&amp;amp;height=1268&amp;amp;face=0_0_570_1268&quot;&gt;&lt;a href=&quot;https://puenti.tistory.com/97&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://puenti.tistory.com/97&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/N8EAQ/hyWG3y6xq8/9lYyYv219MktLk8HM0t6Fk/img.png?width=570&amp;amp;height=1268&amp;amp;face=0_0_570_1268,https://scrap.kakaocdn.net/dn/brdWBw/hyWGUhRayu/OBWp5SAeb0Gf0a3YisfkD0/img.png?width=570&amp;amp;height=1268&amp;amp;face=0_0_570_1268');&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;[GCP] GCP로 node 배포하기 1. (네트워크, DNS 세팅)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이번 토이 프로젝트로 구글에서 제공하는 API, storage를 사용해서 간단한 서비스를 만들어 봤습니다.구글에서 제공하는 서비스를 쓰다보니 배포 역시 AWS 보단 GCP를 사용해보기로 했습니다.번외로&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;puenti.tistory.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;p data-ke-size=&quot;size16&quot;&gt;저번 포스팅에서 DNS 설정까지 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 HTTPS 인증을 받기 위한 SSL 인증을 해보겠습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div data-message-id=&quot;330f3a6a-e474-4d64-8ef8-27f5e515e78f&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HTTPS란 무엇인가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;HTTPS&lt;/b&gt;는 인터넷에서 데이터를 안전하게 주고받기 위해 사용하는 프로토콜입니다. HTTP에 SSL/TLS를 추가해 보안을 강화한 것입니다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&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 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그러나 HTTP 프로토콜을 사용하는 도메인으로 요청을 보내는 경우, &quot;Mixed Content&quot; 오류가 발생할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 오류는 브라우저의 보안 정책에 의해 발생하며, HTTPS를 통해 보안된 연결이 아닌 경우 브라우저에서 차단됩니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이를 방지하기 위해 SSL 인증서를 통해 도메인을 HTTPS로 설정하는 것이 중요합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;왜 HTTPS를 설정하는데 SSL 인증을 받아야 하는가?&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;데이터 암호화&lt;/b&gt;: SSL/TLS 인증서는 데이터를 암호화하여 중간에서 해커가 정보를 가로채거나 도청하지 못하게 합니다. 이는 사용자 이름, 비밀번호, 신용카드 정보 등의 민감한 데이터를 보호하는 데 필수적입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;서버 인증&lt;/b&gt;: SSL/TLS 인증서는 사용자가 접속하는 웹사이트가 실제로 신뢰할 수 있는 사이트인지 확인해 줍니다. 이를 통해 피싱 사이트나 가짜 웹사이트를 구분할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터 무결성&lt;/b&gt;: SSL/TLS 인증서는 데이터가 전송되는 동안 변경되지 않도록 보호합니다. 이를 통해 사용자와 웹사이트 간에 주고받는 데이터가 안전하게 도착했는지 확인할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Nginx &amp;amp;&amp;amp; fowarding&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 사이트 중에 도메인:3000 이런 사이트를 본적 없을겁니다.&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;포트 포워딩을 통해 외부 트래픽을 내부 서버의 특정 포트로 전달하거나, Nginx와 같은 리버스 프록시 서버를 사용해 클라이언트 요청을 실제 서버로 전달하면서 포트 번호를 감춥니다. 이를 통해 사용자에게는 단순한 도메인 이름만 보이게 됩니다.&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;Nginx를 통해 포트포워딩을 진행해보고 더 나아가 HTTPS까지 적용해보겠습니다.&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-filename=&quot;스크린샷 2024-07-30 오후 4.45.44.png&quot; data-origin-width=&quot;1178&quot; data-origin-height=&quot;560&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b04rkr/btsISpB1riI/wIHKwaGaXx6Pks2AmyOKSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b04rkr/btsISpB1riI/wIHKwaGaXx6Pks2AmyOKSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b04rkr/btsISpB1riI/wIHKwaGaXx6Pks2AmyOKSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb04rkr%2FbtsISpB1riI%2FwIHKwaGaXx6Pks2AmyOKSK%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;1178&quot; height=&quot;560&quot; data-filename=&quot;스크린샷 2024-07-30 오후 4.45.44.png&quot; data-origin-width=&quot;1178&quot; data-origin-height=&quot;560&quot;/&gt;&lt;/span&gt;&lt;/figure&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;1.1 Nginx 설치&lt;/h3&gt;
&lt;pre id=&quot;code_1722391336506&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo apt-get install nginx
$ sudo su
$ vim /etc/nginx/nginx.conf&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;Nginx 설정&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-07-31 오전 11.13.22.png&quot; data-origin-width=&quot;354&quot; data-origin-height=&quot;109&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eGzWl8/btsIPZ56tXK/iIlGmanBJS616j0VjAzif1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eGzWl8/btsIPZ56tXK/iIlGmanBJS616j0VjAzif1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eGzWl8/btsIPZ56tXK/iIlGmanBJS616j0VjAzif1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeGzWl8%2FbtsIPZ56tXK%2FiIlGmanBJS616j0VjAzif1%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;354&quot; height=&quot;109&quot; data-filename=&quot;스크린샷 2024-07-31 오전 11.13.22.png&quot; data-origin-width=&quot;354&quot; data-origin-height=&quot;109&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;http { } 안의 include 바로 밑 코드 추가&lt;/p&gt;
&lt;pre id=&quot;code_1722392109947&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server {
       server_name 도메인 주소
       listen 80;
       location / {
                proxy_set_header HOST $host;
                proxy_pass http://127.0.0.1:3000;
                proxy_redirect off;
       }
}&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;:wq&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;이 설정은 지정된 도메인으로 오는 모든 HTTP 요청을 받아 로컬에서 실행 중인 3000번 포트의 애플리케이션(예: Node.js 서버)으로 전달합니다. Nginx가 리버스 프록시 역할을 하여 외부 요청을 내부 서비스로 라우팅하는 구조입니다.&lt;/span&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;span style=&quot;color: #000000; text-align: start;&quot;&gt;설정 후 Nginx 재시작&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1722392319879&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo service nginx restart&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;이제는 도메인 뒤 3000 포트 없어도 잘 나옵니다.&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;2. HTTPS 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Certbot은 Let's Encrypt에서 제공하는 무료 SSL/TLS 인증서를 쉽게 발급받고 관리할 수 있게 해주는 도구입니다.&lt;/span&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;span style=&quot;color: #000000; text-align: start;&quot;&gt;2.1 certbot 설치&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1722398277542&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo snap install --classic certbot&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;2.2 Nginx 설정&lt;/h3&gt;
&lt;pre id=&quot;code_1722398341359&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo certbot --nginx&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;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;b&gt;Congratulations! You have successfully enabled https://[도메인 이름]&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: oklch(0.217 0.002 197.042); color: oklch(0.93 0.003 106.451); text-align: left;&quot;&gt;&lt;/span&gt;&lt;span&gt;이 메시지는 HTTPS 설정이 성공적으로 완료되었음을 나타냅니다.&lt;/span&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;span&gt;2.3 Nginx 80 포트 설정&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;마지막으로 &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;80 포트로 접속했을때 https 기본 포트인 443으로 리다이렉트 처리만 해주면 됩니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1722398932230&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo su
$ vi /etc/nginx/nginx.conf&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1722399063128&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server {
    if ($host = 본인 도메인) {
        return 301 https://$host$request_uri;
    } # managed by Certbot
    
    listen 80 ;
    listen [::]:80 ;
    server_name 본인 도메인;
    return 404; # managed by Certbot // 제거
    return 301 https://$host$request_uri; // 추가
}&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;입력 후 :wq, Nginx 재실행해주면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1722399122444&quot; class=&quot;routeros&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;$ sudo service nginx restart&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;설정 후 해당 등록한 도메인으로 접속해보면 https가 적용된 것을 확인할 수 있습니다.&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;h2 data-ke-size=&quot;size26&quot;&gt;※ 번외&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GCP에서는 위 방법말고 부하 분산(로드 밸런스)를 통해 HTTPS를 설정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 방법이 있으므로 본인에게 맞는 서비스를 선택해서 적용하면 되겠습니다.&lt;/p&gt;</description>
      <category>Programming/ETC</category>
      <category>certbot</category>
      <category>HTTP</category>
      <category>HTTPS</category>
      <category>https 설정</category>
      <category>Nginx</category>
      <category>SSL</category>
      <category>ssl 인증</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/98</guid>
      <comments>https://puenti.tistory.com/98#entry98comment</comments>
      <pubDate>Wed, 31 Jul 2024 13:15:41 +0900</pubDate>
    </item>
    <item>
      <title>GCP로 node 배포하기 1. (네트워크, DNS 세팅)</title>
      <link>https://puenti.tistory.com/97</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 토이 프로젝트로 구글에서 제공하는 API, storage를 사용해서 간단한 서비스를 만들어 봤습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구글에서 제공하는 서비스를 쓰다보니 배포 역시 AWS 보단 GCP를 사용해보기로 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;번외로 회사에서 GCP를 사용해서 겸사겸사 해보면 좋을거 같다는 생각도 했습니다.&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;1. GCP 접속&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;GCP &amp;gt; Compute Engine &amp;gt; VM 인스턴스 &amp;gt; 인스턴스 만들기&lt;/b&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;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A7Vzb/btsISIusjtp/iDfK0I5ZPpKqCHNu1ukQVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A7Vzb/btsISIusjtp/iDfK0I5ZPpKqCHNu1ukQVK/img.png&quot; data-origin-width=&quot;570&quot; data-origin-height=&quot;1268&quot; data-is-animation=&quot;false&quot; data-filename=&quot;스크린샷 2024-07-30 오후 4.35.24.png&quot; style=&quot;width: 27.5339%; margin-right: 10px;&quot; data-widthpercent=&quot;27.86&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A7Vzb/btsISIusjtp/iDfK0I5ZPpKqCHNu1ukQVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA7Vzb%2FbtsISIusjtp%2FiDfK0I5ZPpKqCHNu1ukQVK%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;570&quot; height=&quot;1268&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1zu4R/btsIRnkoozV/yNpVfxDl6UN77Kwkq249Uk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1zu4R/btsIRnkoozV/yNpVfxDl6UN77Kwkq249Uk/img.png&quot; data-filename=&quot;스크린샷 2024-07-30 오후 4.37.05.png&quot; data-origin-height=&quot;524&quot; data-origin-width=&quot;610&quot; data-is-animation=&quot;false&quot; style=&quot;width: 71.3034%;&quot; data-widthpercent=&quot;72.14&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1zu4R/btsIRnkoozV/yNpVfxDl6UN77Kwkq249Uk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1zu4R%2FbtsIRnkoozV%2FyNpVfxDl6UN77Kwkq249Uk%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;610&quot; height=&quot;524&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&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;2. 인스턴스 만들기&lt;/h2&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;액세스 할 서비스 계정을 설정하고 방화벽은 HTTP/ HTTPS 트래픽 허용&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OAHQg/btsIRftdG5g/RWc8sqfNvKlZVAYf2W4pV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OAHQg/btsIRftdG5g/RWc8sqfNvKlZVAYf2W4pV0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1523&quot; data-origin-height=&quot;1134&quot; data-filename=&quot;스크린샷 2024-07-30 오후 4.39.08.png&quot; style=&quot;width: 47.6812%; margin-right: 10px;&quot; data-widthpercent=&quot;48.24&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OAHQg/btsIRftdG5g/RWc8sqfNvKlZVAYf2W4pV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOAHQg%2FbtsIRftdG5g%2FRWc8sqfNvKlZVAYf2W4pV0%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;1523&quot; height=&quot;1134&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xtTio/btsIQmmdUS4/127PbVpjkdY0rTK32P2KN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xtTio/btsIQmmdUS4/127PbVpjkdY0rTK32P2KN1/img.png&quot; data-origin-width=&quot;951&quot; data-origin-height=&quot;660&quot; data-is-animation=&quot;false&quot; data-filename=&quot;스크린샷 2024-07-30 오후 4.39.29.png&quot; style=&quot;width: 51.156%;&quot; data-widthpercent=&quot;51.76&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xtTio/btsIQmmdUS4/127PbVpjkdY0rTK32P2KN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxtTio%2FbtsIQmmdUS4%2F127PbVpjkdY0rTK32P2KN1%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;951&quot; height=&quot;660&quot;/&gt;&lt;/span&gt;&lt;/div&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 프로젝트 세팅 (Git &amp;amp; Node)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSH 연결을 통해 인스턴스를 실행해봅시다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-07-30 오후 4.45.44.png&quot; data-origin-width=&quot;1178&quot; data-origin-height=&quot;560&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lZQ0B/btsIPTEOMa3/d15TAPtzOL3l0lA1CPJpsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lZQ0B/btsIPTEOMa3/d15TAPtzOL3l0lA1CPJpsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lZQ0B/btsIPTEOMa3/d15TAPtzOL3l0lA1CPJpsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlZQ0B%2FbtsIPTEOMa3%2Fd15TAPtzOL3l0lA1CPJpsK%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;1178&quot; height=&quot;560&quot; data-filename=&quot;스크린샷 2024-07-30 오후 4.45.44.png&quot; data-origin-width=&quot;1178&quot; data-origin-height=&quot;560&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GCP는 기본적으로 git이 설치되어 있지만 확인해보기&lt;/p&gt;
&lt;pre id=&quot;code_1722326112136&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git --version

// 설치가 안되어 있다면
$ sudo apt install git&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;Git으로 프로젝트 클론&lt;/p&gt;
&lt;pre id=&quot;code_1722326052134&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git clone 내프로젝트 주소&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;Node 설치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 세팅한 노드로 설치&lt;/p&gt;
&lt;pre id=&quot;code_1722326195053&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo apt-get update
$ sudo apt-get install -y build-essential
$ sudo apt-get install curl
$ curl -sL https://deb.nodesource.com/setup_20.x | sudo -E bash --
$ sudo apt-get install -y nodejs&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;pre id=&quot;code_1722326247156&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ node -v
$ npm -v&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;pre id=&quot;code_1722326412506&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ npm install&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;무중단 배포를 위한 pm2 설치&lt;/p&gt;
&lt;pre id=&quot;code_1722326341741&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo npm i pm2 --location=global&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;pre id=&quot;code_1722326589209&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 본인 환경에 맞는 환경변수 세팅하기
$ vim .env&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1722326460723&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ pm2 start app.js&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-07-30 오후 5.06.57.png&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;71&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oTre1/btsISWMNs4g/YKtXAQUzJ3K5GoTjQYudz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oTre1/btsISWMNs4g/YKtXAQUzJ3K5GoTjQYudz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oTre1/btsISWMNs4g/YKtXAQUzJ3K5GoTjQYudz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoTre1%2FbtsISWMNs4g%2FYKtXAQUzJ3K5GoTjQYudz1%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;632&quot; height=&quot;71&quot; data-filename=&quot;스크린샷 2024-07-30 오후 5.06.57.png&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;71&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;&amp;nbsp;&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;4. 방화벽 규칙 만들기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.1 서버 포트 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방화벽 설정하기 전에 백엔드 서버&lt;b&gt;&amp;nbsp;포트 확인&lt;/b&gt;하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 app.js에서 확인 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 3000번 포트를 열어놨습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인스턴스 외부ip:3000 을 통해서 접근이 가능하다는 얘기입니다.&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;TCP 포트 3000:&lt;/b&gt; 이 포트는 네트워크 서비스나 애플리케이션이 데이터를 송수신하는 데 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 웹 개발에서 자주 사용하는 Node.js 서버는 기본적으로 TCP 포트 3000을 사용합니다.&lt;/p&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;133&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/83TOI/btsISVf3gl5/V7kBHKL1mPIGZh17cPxhM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/83TOI/btsISVf3gl5/V7kBHKL1mPIGZh17cPxhM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/83TOI/btsISVf3gl5/V7kBHKL1mPIGZh17cPxhM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F83TOI%2FbtsISVf3gl5%2FV7kBHKL1mPIGZh17cPxhM1%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;133&quot; data-origin-width=&quot;306&quot; data-origin-height=&quot;133&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;h3 data-ke-size=&quot;size23&quot;&gt;4.2 VPC 네트워크 &amp;gt; 방화벽 &amp;gt; 방화벽 규칙 만들기&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-07-30 오후 5.12.52.png&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;1089&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuVQ8O/btsIR2fQgGU/4cakZuxKhgUeN1lfWB9sXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuVQ8O/btsIR2fQgGU/4cakZuxKhgUeN1lfWB9sXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuVQ8O/btsIR2fQgGU/4cakZuxKhgUeN1lfWB9sXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuVQ8O%2FbtsIR2fQgGU%2F4cakZuxKhgUeN1lfWB9sXK%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;580&quot; height=&quot;1089&quot; data-filename=&quot;스크린샷 2024-07-30 오후 5.12.52.png&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;1089&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;b&gt;my-project-3000&lt;/b&gt;으로 설정해줬습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소스 IPv4 범위는 &lt;b&gt;0.0.0.0/0&lt;/b&gt; 모든 ip 접근을 허용해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;위에서 확인한 Node 서버 포트인 3000 번을 TCP 포트 추가해줍니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clhS62/btsISfzikeq/OcALjOaP0zqOrpvolQUu91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clhS62/btsISfzikeq/OcALjOaP0zqOrpvolQUu91/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;597&quot; data-origin-height=&quot;1090&quot; data-filename=&quot;스크린샷 2024-07-30 오후 5.18.20.png&quot; style=&quot;width: 34.2257%; margin-right: 10px;&quot; data-widthpercent=&quot;34.63&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clhS62/btsISfzikeq/OcALjOaP0zqOrpvolQUu91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclhS62%2FbtsISfzikeq%2FOcALjOaP0zqOrpvolQUu91%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;597&quot; height=&quot;1090&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzamxT/btsISOVGCYO/36eNyjgbwYrtDjBRDreEi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzamxT/btsISOVGCYO/36eNyjgbwYrtDjBRDreEi1/img.png&quot; data-filename=&quot;스크린샷 2024-07-30 오후 5.19.03.png&quot; data-origin-height=&quot;530&quot; data-origin-width=&quot;548&quot; data-is-animation=&quot;false&quot; style=&quot;width: 64.6115%;&quot; data-widthpercent=&quot;65.37&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzamxT/btsISOVGCYO/36eNyjgbwYrtDjBRDreEi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzamxT%2FbtsISOVGCYO%2F36eNyjgbwYrtDjBRDreEi1%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;548&quot; height=&quot;530&quot;/&gt;&lt;/span&gt;&lt;/div&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;GCP &amp;gt; Compute Engine &amp;gt; 생성한 VM 인스턴스 &amp;gt; 수정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;네트워크 태그에 설정한 방화벽 규칙 추가&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-07-30 오후 5.38.59.png&quot; data-origin-width=&quot;564&quot; data-origin-height=&quot;682&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wJgff/btsIRltnFlv/J61AHAKInDk6RMi6fxdvFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wJgff/btsIRltnFlv/J61AHAKInDk6RMi6fxdvFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wJgff/btsIRltnFlv/J61AHAKInDk6RMi6fxdvFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwJgff%2FbtsIRltnFlv%2FJ61AHAKInDk6RMi6fxdvFk%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;564&quot; height=&quot;682&quot; data-filename=&quot;스크린샷 2024-07-30 오후 5.38.59.png&quot; data-origin-width=&quot;564&quot; data-origin-height=&quot;682&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서버가 열렸으니 접근이 가능한지 확인&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-07-30 오후 5.10.21.png&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;164&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n0gr5/btsIPVJnRfL/lugGzUAkVUBTHBZk0kSm21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n0gr5/btsIPVJnRfL/lugGzUAkVUBTHBZk0kSm21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n0gr5/btsIPVJnRfL/lugGzUAkVUBTHBZk0kSm21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn0gr5%2FbtsIPVJnRfL%2FlugGzUAkVUBTHBZk0kSm21%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;1125&quot; height=&quot;164&quot; data-filename=&quot;스크린샷 2024-07-30 오후 5.10.21.png&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;164&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인스턴스의 외부ip:3000 접속하면 아래와 같이 접근되면 완료입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-07-30 오후 5.42.40.png&quot; data-origin-width=&quot;441&quot; data-origin-height=&quot;165&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/11YFJ/btsIRpifx8p/mJQEORSxQsGHjPuGRCH84k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/11YFJ/btsIRpifx8p/mJQEORSxQsGHjPuGRCH84k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/11YFJ/btsIRpifx8p/mJQEORSxQsGHjPuGRCH84k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F11YFJ%2FbtsIRpifx8p%2FmJQEORSxQsGHjPuGRCH84k%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;441&quot; height=&quot;165&quot; data-filename=&quot;스크린샷 2024-07-30 오후 5.42.40.png&quot; data-origin-width=&quot;441&quot; data-origin-height=&quot;165&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.3 고정 ip 승급&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 해당 인스턴스의 외부ip 고정ip로 승급시켜주면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;고정 IP (Static IP):&lt;/b&gt; 고정 IP는 할당된 후에는 변경되지 않습니다. 인스턴스가 재부팅되거나 일시적으로 중지되더라도 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;VPC &amp;gt; IP 주소&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트의 인스턴스 외부 Ip인지 확인 후 고정 IP주소로 승급해주면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-07-30 오후 5.48.01.png&quot; data-origin-width=&quot;1593&quot; data-origin-height=&quot;262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JkIbF/btsISs6rPLs/AKsW4ifpN8jb3h7FOHA57K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JkIbF/btsISs6rPLs/AKsW4ifpN8jb3h7FOHA57K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JkIbF/btsISs6rPLs/AKsW4ifpN8jb3h7FOHA57K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJkIbF%2FbtsISs6rPLs%2FAKsW4ifpN8jb3h7FOHA57K%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;1593&quot; height=&quot;262&quot; data-filename=&quot;스크린샷 2024-07-30 오후 5.48.01.png&quot; data-origin-width=&quot;1593&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. DNS 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;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;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그러나 HTTP 프로토콜을 사용하는 도메인으로 요청을 보내는 경우, &quot;Mixed Content&quot; 오류가 발생할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 오류는 브라우저의 보안 정책에 의해 발생하며, HTTPS를 통해 보안된 연결이 아닌 경우 브라우저에서 차단됩니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이를 방지하기 위해 SSL 인증서를 통해 도메인을 HTTPS로 설정하는 것이 중요합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;네트워크 서비스 &amp;gt; Cloud Domains &amp;gt; 도메인 등록&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-07-30 오후 5.51.08.png&quot; data-origin-width=&quot;1240&quot; data-origin-height=&quot;608&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c7WWjd/btsIReHTNYf/VdohCTPONolOd3xEzo20H0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7WWjd/btsIReHTNYf/VdohCTPONolOd3xEzo20H0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7WWjd/btsIReHTNYf/VdohCTPONolOd3xEzo20H0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7WWjd%2FbtsIReHTNYf%2FVdohCTPONolOd3xEzo20H0%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;1240&quot; height=&quot;608&quot; data-filename=&quot;스크린샷 2024-07-30 오후 5.51.08.png&quot; data-origin-width=&quot;1240&quot; data-origin-height=&quot;608&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도메인 구매 후 Cloud 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;네트워크 서비스 &amp;gt; Cloud DNS &amp;gt; DNS 영역 만들기&lt;/b&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-filename=&quot;스크린샷 2024-07-30 오후 5.59.29.png&quot; data-origin-width=&quot;879&quot; data-origin-height=&quot;797&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wyFZE/btsIQLlERar/IqXdbj1rw2B4EclFvtLGHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wyFZE/btsIQLlERar/IqXdbj1rw2B4EclFvtLGHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wyFZE/btsIQLlERar/IqXdbj1rw2B4EclFvtLGHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwyFZE%2FbtsIQLlERar%2FIqXdbj1rw2B4EclFvtLGHK%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;879&quot; height=&quot;797&quot; data-filename=&quot;스크린샷 2024-07-30 오후 5.59.29.png&quot; data-origin-width=&quot;879&quot; data-origin-height=&quot;797&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영역 이름은 본인이 설정한 이름을 적고 DNS 이름은 구매한 도메인으로 설정해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만들고 난 후 영역 &lt;b&gt;세부정보&lt;/b&gt;로 들어갑니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cVzdZ1/btsIRw9nfJ0/tHsHfWzXC16cBV48WODVa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cVzdZ1/btsIRw9nfJ0/tHsHfWzXC16cBV48WODVa1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;915&quot; data-origin-height=&quot;349&quot; data-filename=&quot;스크린샷 2024-07-30 오후 6.05.39.png&quot; style=&quot;width: 54.4422%; margin-right: 10px;&quot; data-widthpercent=&quot;55.08&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cVzdZ1/btsIRw9nfJ0/tHsHfWzXC16cBV48WODVa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcVzdZ1%2FbtsIRw9nfJ0%2FtHsHfWzXC16cBV48WODVa1%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;915&quot; height=&quot;349&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pa5Wj/btsIQoYE9wr/vktsnDRNJ4Uu5JPWQfAOQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pa5Wj/btsIQoYE9wr/vktsnDRNJ4Uu5JPWQfAOQ0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;992&quot; data-origin-height=&quot;464&quot; data-filename=&quot;스크린샷 2024-07-30 오후 6.07.00.png&quot; style=&quot;width: 44.395%;&quot; data-widthpercent=&quot;44.92&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pa5Wj/btsIQoYE9wr/vktsnDRNJ4Uu5JPWQfAOQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpa5Wj%2FbtsIQoYE9wr%2FvktsnDRNJ4Uu5JPWQfAOQ0%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;992&quot; height=&quot;464&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영역 세부정보에 처음 들어가면 2가지 항목이 나올텐데 &lt;b&gt;표준 추가&lt;/b&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;b&gt;DNS 이름은 별도 설정 안해줘도 되고 리소스 &lt;span style=&quot;color: #ee2323;&quot;&gt;레코드 유형은 A, IPv4 주소는 이전에 설정했던 고정 IP 주소&lt;/span&gt;를 넣어줍니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-07-30 오후 6.09.51.png&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;629&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKyiZr/btsISLxUEwS/at4Bk54WFS2db0bTHn93y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKyiZr/btsISLxUEwS/at4Bk54WFS2db0bTHn93y1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKyiZr/btsISLxUEwS/at4Bk54WFS2db0bTHn93y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKyiZr%2FbtsISLxUEwS%2Fat4Bk54WFS2db0bTHn93y1%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;561&quot; height=&quot;629&quot; data-filename=&quot;스크린샷 2024-07-30 오후 6.09.51.png&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;629&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 설정한 도메인:3000 를 주소창에다 입력하면 고정 IP:3000을 넣어야 접근이 가능했던걸 이제는 도메인:3000를 입력하면 접근이 가능하게 됩니다.&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;다음 포스팅은 nginx를 통한 포트포워딩과 SSL 인증에 대해 작성해보겠습니다.&lt;/p&gt;</description>
      <category>Programming/ETC</category>
      <category>DNS 설정</category>
      <category>GCP</category>
      <category>node 배포</category>
      <category>구글 클라우드</category>
      <category>네트워크 설정</category>
      <category>노드 배포</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/97</guid>
      <comments>https://puenti.tistory.com/97#entry97comment</comments>
      <pubDate>Tue, 30 Jul 2024 18:14:44 +0900</pubDate>
    </item>
    <item>
      <title>[Next.JS] 성능개선 LCP 최적화</title>
      <link>https://puenti.tistory.com/96</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;통합검색 성능 개선 이후로 진행한 성능개선 작업 2편입니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1편인 통합검색 성능 개선이 궁금하신 분은 아래 링크 참고해주세요.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://puenti.tistory.com/89&quot;&gt;https://puenti.tistory.com/89&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; 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-filename=&quot;스크린샷 2024-07-03 오전 10.40.26.png&quot; data-origin-width=&quot;1532&quot; data-origin-height=&quot;1476&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbDirw/btsIly7oz6p/BBBwGgokhhsrCOe1E3zV7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbDirw/btsIly7oz6p/BBBwGgokhhsrCOe1E3zV7K/img.png&quot; data-alt=&quot;싲&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbDirw/btsIly7oz6p/BBBwGgokhhsrCOe1E3zV7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbDirw%2FbtsIly7oz6p%2FBBBwGgokhhsrCOe1E3zV7K%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;1532&quot; height=&quot;1476&quot; data-filename=&quot;스크린샷 2024-07-03 오전 10.40.26.png&quot; data-origin-width=&quot;1532&quot; data-origin-height=&quot;1476&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;싲&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 메시지는 Next.js 애플리케이션에서 Largest Contentful Paint (LCP)를 최적화하기 위해 이미지를 우선적으로 로드해야 한다는 경고입니다. LCP는 사용자가 페이지를 로드할 때 가장 큰 콘텐츠 요소가 화면에 나타나는 시간을 측정하는 웹 성능 지표이며 이는 사용자 경험을 개선하는 데 중요하다고 볼 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;로그를 확인해보니 메인 페이지에 배너와 사용자 사진 등이 들어가는데 이 부분에서 LCP가 발생하는 것으로 파악했습니다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;LCP (Largest Contentful Paint)란?&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;LCP는 페이지가 로드를 시작한 시점부터 뷰포트 내에서 가장 큰 이미지나 텍스트 블록이 렌더링되는 시간을 측정합니다&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;이는 페이지의 주요 콘텐츠가 사용자에게 표시되는 시점을 나타내므로, 사용자가 페이지를 &quot;유용하다&quot;고 인식하는 시간을 반영합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;b&gt;2.5초 시간 이내에 LCP를 달성&lt;/b&gt;해야 좋은 사용자 경험을 제공 할 수 있다고 합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;LCP 측정 대상&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;LCP는 다음과 같은 요소들을 측정 대상으로 고려합니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&amp;lt;img&amp;gt;&lt;/b&gt;&amp;nbsp;요소&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&amp;lt;svg&amp;gt;&lt;/b&gt;&amp;nbsp;요소 내부의&amp;nbsp;&lt;b&gt;&amp;lt;image&amp;gt;&lt;/b&gt;&amp;nbsp;요소&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&amp;lt;video&amp;gt;&lt;/b&gt;&amp;nbsp;요소 (포스터 이미지 사용 시)&lt;/li&gt;
&lt;li&gt;CSS&amp;nbsp;&lt;b&gt;background-image&lt;/b&gt;를 통해 로드된 이미지&lt;/li&gt;
&lt;li&gt;텍스트 노드나 인라인 텍스트 요소를 포함하는 블록 수준 요소&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;LCP 목표 수치&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Google은 다음과 같은 LCP 목표 수치를 제시합니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;좋음: 2.5초 이하&lt;/li&gt;
&lt;li&gt;개선 필요: 2.5초 ~ 4초&lt;/li&gt;
&lt;li&gt;나쁨: 4초 초과&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;LCP 최적화 방법&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;LCP를 개선하기 위해 다음과 같은 방법들을 고려할 수 있습니다:&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 서버&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;응답&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;시간&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;개선:&lt;/b&gt;&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;CDN(Content Delivery Network) 사용&lt;/li&gt;
&lt;li&gt;캐싱 전략 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. JavaScript&lt;span&gt;와&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;CSS&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;최적화:&lt;/span&gt;&lt;/b&gt;&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;중요한 CSS 인라인화&lt;/li&gt;
&lt;li&gt;JavaScript&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;지연&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;로딩&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;3. 리소스&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;최적화:&lt;/span&gt;&lt;/b&gt;&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;적절한 이미지 형식 사용 (예: WebP)&lt;/li&gt;
&lt;li&gt;이미지&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;크기&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;조정&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 클라이언트&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;측&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;렌더링&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;최소화&lt;span&gt;:&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버 사이드 렌더링(SSR) 또는 정적 사이트 생성(SSG) 고려&lt;/li&gt;
&lt;li&gt;LCP&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;요소를&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;서버에서&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;미리&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;렌더링&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5. 프리로드&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;중요&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;리소스&lt;span&gt;:&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;lt;link rel=&quot;preload&quot;&amp;gt;&amp;nbsp;사용하여&lt;span&gt;&amp;nbsp;&lt;/span&gt;중요한&lt;span&gt;&amp;nbsp;&lt;/span&gt;리소스&lt;span&gt;&amp;nbsp;&lt;/span&gt;미리&lt;span&gt;&amp;nbsp;&lt;/span&gt;로드&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6. 브라우저&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;캐싱&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;활용&lt;span&gt;:&lt;/span&gt;&lt;/b&gt;&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&gt;&amp;nbsp;&lt;/span&gt;캐시&lt;span&gt;&amp;nbsp;&lt;/span&gt;헤더&lt;span&gt;&amp;nbsp;&lt;/span&gt;설정&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위 방법을 참고해서 제가 한 방법은 이렇습니다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;1. Image 컴포넌트 최적화&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;priority 속성 추가,&lt;span&gt;&amp;nbsp;&lt;/span&gt;이 속성은 Next.js가 이미지 로딩을 우선시해줍니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1720006953584&quot; class=&quot;xml&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;&amp;lt;Image
	style={{ cursor: 'pointer' }}
	src={src}
	alt={src}
	layout=&quot;fill&quot;
	onClick={() =&amp;gt; onClickViewDetail(itemProp)}
	priority
/&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;2.&lt;span&gt;&amp;nbsp;&lt;/span&gt;폰트 요청 결합하기&lt;/h2&gt;
&lt;pre id=&quot;code_1720006953584&quot; class=&quot;perl&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;// 기존
&amp;lt;link
	href=&quot;https://fonts.googleapis.com/css?family=Roboto:400,300,300italic,400italic,700,700italic&amp;amp;subset=latin,vietnamese,latin-ext,cyrillic,cyrillic-ext,greek-ext,greek&amp;amp;display=optional&quot;
	rel=&quot;stylesheet&quot;
	/&amp;gt;
&amp;lt;link
	href=&quot;https://fonts.googleapis.com/css?family=Oswald:400,300,700&amp;amp;subset=latin,latin-ext&amp;amp;display=optional&quot;
	rel=&quot;stylesheet&quot;
	/&amp;gt;
&amp;lt;link
	href=&quot;https://fonts.googleapis.com/css?family=Montserrat:400,700&amp;amp;display=optional&quot;
	rel=&quot;stylesheet&quot;
	/&amp;gt;
    
// 변경
 &amp;lt;link
	rel=&quot;stylesheet preload&quot;
	as=&quot;style&quot;
	href=&quot;https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,300;0,300i;0,400;0,400i;0,700;0,700i&amp;amp;family=Oswald:wght@300;400;700&amp;amp;family=Montserrat:wght@400;700&amp;amp;family=Open+Sans+Condensed:wght@300;300i;700&amp;amp;display=swap&quot;
	/&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여러 개의 폰트를 사용할 경우, 이를 하나의 요청으로 결합하여 요청 수를 줄였습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;또한 preload 옵션을 사용함으로서 의도적으로 먼저 로딩시키게 하였습니다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;3. avif 포맷 사용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #373c46; text-align: start;&quot;&gt;png나 jpg 등 기존의 포맷보다 작은 용량인 webp지만 여전히 용량이 큰 문제가 있어&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;webp보다 20% 더 높은 압축률&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #373c46; text-align: start;&quot;&gt;을 보여준다는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;u&gt;avif&lt;/u&gt;&lt;span style=&quot;background-color: #ffffff; color: #373c46; text-align: start;&quot;&gt;를 적용했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Next.js는 v12.0.0부터 avif를 지원&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #373c46; text-align: start;&quot;&gt;하기 시작했습니다. 다음과 같이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;next.config.js&lt;b&gt;를 설정하면 avif&lt;span&gt;&amp;nbsp;&lt;/span&gt;지원을 활성화&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #373c46; text-align: start;&quot;&gt;할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #373c46; text-align: start;&quot;&gt;※ &lt;span style=&quot;background-color: #ffffff; color: #373c46; text-align: start;&quot;&gt;&lt;b&gt;Next.js에서 avif 포맷을 사용하고 싶다면&lt;/b&gt; 당연하지만 &lt;b&gt;Image 컴포넌트를 사용&lt;/b&gt;해야 합니다.&amp;nbsp;&lt;/span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #373c46; text-align: start;&quot;&gt;&lt;/span&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;span style=&quot;background-color: #9feec3;&quot;&gt;[참고] AVIF는 인코딩하는 데 일반적으로 WebP보다 20% 더 오래 걸리지만, 결과 파일 크기는 20% 더 작습니다. 따라서 이미지를 처음 요청할 때는 로딩 속도가 느릴 수 있지만, 이후 캐시된 요청에서는 더 빠른 성능을 기대할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1720007250004&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;module.exports = {
  images: {
    formats: ['image/avif', 'image/webp'], // default: ['image/webp']
  },
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-07-03 오후 8.17.51.png&quot; data-origin-width=&quot;397&quot; data-origin-height=&quot;109&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjUHt9/btsIlRmhW5z/hm06MV721mYJUF6MKyVBs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjUHt9/btsIlRmhW5z/hm06MV721mYJUF6MKyVBs0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjUHt9/btsIlRmhW5z/hm06MV721mYJUF6MKyVBs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjUHt9%2FbtsIlRmhW5z%2Fhm06MV721mYJUF6MKyVBs0%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;397&quot; height=&quot;109&quot; data-filename=&quot;스크린샷 2024-07-03 오후 8.17.51.png&quot; data-origin-width=&quot;397&quot; data-origin-height=&quot;109&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;이런 식으로 avif로 인코딩해서 출력되는 것을 확인할 수 있습니다.&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;LCP 성능은 3.4 -&amp;gt; 1.4초로 개선 된 것을 확인 할 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LCP 최적화 외에도 로컬 스토리지 활용, lazy 활용 등 방법을 적용해서 Light house 성능 향상 시킨 방법은 추후 포스팅에 기재하겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c7nFcG/btsIm5dnquw/KFCLWRqvWOFPpKgHUlXV0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7nFcG/btsIm5dnquw/KFCLWRqvWOFPpKgHUlXV0K/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1458&quot; data-origin-height=&quot;1318&quot; data-filename=&quot;스크린샷 2024-07-03 오후 1.15.54.png&quot; style=&quot;width: 49.8751%; margin-right: 10px;&quot; data-widthpercent=&quot;50.46&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7nFcG/btsIm5dnquw/KFCLWRqvWOFPpKgHUlXV0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7nFcG%2FbtsIm5dnquw%2FKFCLWRqvWOFPpKgHUlXV0K%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;1458&quot; height=&quot;1318&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wjHEF/btsImdwgWh1/GKFV0Ajc9gNop4VD5DRtnk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wjHEF/btsImdwgWh1/GKFV0Ajc9gNop4VD5DRtnk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1326&quot; data-filename=&quot;스크린샷 2024-07-03 오후 1.21.37.png&quot; style=&quot;width: 48.9621%;&quot; data-widthpercent=&quot;49.54&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wjHEF/btsImdwgWh1/GKFV0Ajc9gNop4VD5DRtnk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwjHEF%2FbtsImdwgWh1%2FGKFV0Ajc9gNop4VD5DRtnk%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;1440&quot; height=&quot;1326&quot;/&gt;&lt;/span&gt;&lt;/div&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;&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;&lt;a href=&quot;https://heycoding.tistory.com/130&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://heycoding.tistory.com/130&lt;/a&gt;&lt;/p&gt;</description>
      <category>Programming/React.js, Next.js</category>
      <category>largest contentful paint</category>
      <category>LCP</category>
      <category>프론트엔드 성능 향상</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/96</guid>
      <comments>https://puenti.tistory.com/96#entry96comment</comments>
      <pubDate>Thu, 4 Jul 2024 12:59:22 +0900</pubDate>
    </item>
    <item>
      <title>debounce와 throttle</title>
      <link>https://puenti.tistory.com/95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드 개발에서 debounce와 throttle는 성능 최적화와 사용자 경험 향상을 위해 자주 사용되는 기술입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 담당하고 있는 서비스에서도 많이 쓰이진 않지만 debounce는 종종 보이곤 합니다.&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;처음 debounce를 마주했을 때 이걸 왜 쓰는지에 대해 알아봤는데 debounce와 함께 나오는 개념이 throttle과 함께 정리해봤습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Debounce&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Debounce는 특정 이벤트가 여러 번 발생할 때, 지정된 시간 동안 이벤트가 발생하지 않을 때까지 대기했다가 한 번만 실행되도록 하는 기술입니다. 이를 통해 연속된 이벤트 호출을 줄여 불필요한 처리를 방지합니다. 예를 들어, 사용자가 검색창에 글자를 입력할 때마다 검색 요청을 서버에 보내면 서버에 과부하가 걸릴 수 있습니다. 이 때 debounce를 사용하면 사용자가 입력을 멈춘 후 일정 시간 동안 입력이 없을 때 한 번만 검색 요청을 보냅니다.&lt;/p&gt;
&lt;pre id=&quot;code_1719475336857&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function debounce(func, delay) {
    let timeoutId;
    return function(...args) {
        if (timeoutId) {
            clearTimeout(timeoutId);
        }
        timeoutId = setTimeout(() =&amp;gt; {
            func.apply(this, args);
        }, delay);
    };
}

const handleSearch = debounce((query) =&amp;gt; {
    console.log('Searching for', query);
}, 300);

document.getElementById('searchInput').addEventListener('input', (event) =&amp;gt; {
    handleSearch(event.target.value);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 handleSearch 함수는 사용자가 입력을 멈추고 300밀리초 후에만 실행됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Throttle&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Throttle는 특정 이벤트가 여러 번 발생할 때, 지정된 시간 간격 동안 이벤트를 한 번만 실행되도록 하는 기술입니다. 이를 통해 이벤트가 일정 간격으로만 발생하게 하여 시스템 자원을 절약할 수 있습니다. 예를 들어, 사용자가 스크롤 이벤트를 발생시킬 때, 스크롤 이벤트 핸들러가 너무 자주 실행되면 성능 저하가 발생할 수 있습니다. 이 때 throttle을 사용하면 일정 시간마다 한 번씩만 이벤트가 실행되도록 제한할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1719475670997&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function throttle(func, limit) {
    let inThrottle;
    return function(...args) {
        if (!inThrottle) {
            func.apply(this, args);
            inThrottle = true;
            setTimeout(() =&amp;gt; {
                inThrottle = false;
            }, limit);
        }
    };
}

const handleScroll = throttle(() =&amp;gt; {
    console.log('Scroll event handler');
}, 200);

window.addEventListener('scroll', handleScroll);&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;div data-message-id=&quot;34c95482-932e-4258-aefb-193a693da866&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 handleScroll 함수는 사용자가 스크롤할 때마다 200밀리초에 한 번씩만 실행됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;요약&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Debounce&lt;/b&gt;: 지정된 시간 동안 이벤트가 발생하지 않을 때까지 대기 후 한 번 실행 (입력 필드의 실시간 검색).&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Throttle&lt;/b&gt;: 지정된 시간 간격마다 이벤트를 한 번만 실행 (스크롤, 리사이즈 이벤트).&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 두 개념은 특히 사용자 인터페이스에서 이벤트를 효율적으로 처리하여 성능을 최적화하고, 불필요한 서버 요청이나 화면 렌더링을 줄이는 데 매우 유용합니다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span data-state=&quot;closed&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-state=&quot;closed&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-state=&quot;closed&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>Programming/React.js, Next.js</category>
      <category>debounce</category>
      <category>Throttle</category>
      <category>프론트엔드</category>
      <category>프론트엔드 면접</category>
      <category>프론트엔드 최적화</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/95</guid>
      <comments>https://puenti.tistory.com/95#entry95comment</comments>
      <pubDate>Mon, 1 Jul 2024 09:41:07 +0900</pubDate>
    </item>
    <item>
      <title>[Next.JS] getStaticProps를 이용해서 서버 자원을 효율적으로 사용하고, 비용을 절감하기</title>
      <link>https://puenti.tistory.com/94</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;개요&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;getStaticProps를 이용하려는 생각&lt;/b&gt;은 회사에서 서비스 중인 그룹웨어 메인 페이지를 부분을 개발하면서 시작하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메인페이지에 접속하게 되면 google api, 전자결재, 조직도, 게시판, 개인정보 등등 수 많은 API를 요청 보내고 있습니다.&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;p data-ke-size=&quot;size16&quot;&gt;또한 조직도 같은 경우는 자주 변경되지도 않는 데이터인데 사용자가 로그인 할 때마다 데이터를 호출할 필요가 있을까? 라는 생각을 가지게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 &lt;b&gt;서버상태관리 라이브러리인&lt;/b&gt; &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;SWR&lt;/b&gt;&lt;/span&gt; 덕에 사용자가 &lt;u&gt;최초 메인페이지에 접속했을 경우에만 1번 호출&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;이전 노션 API를 통해 게시판을 만드는 경험이 있어서 &lt;b&gt;getStaticProps&lt;/b&gt;를 통해 정적 데이터를 서버에 들고 있으면서 클라이언트 쪽에 요청이 오면 빌드 시점에 불러온 조직도 데이터를 불러주면 API 요청은 줄이면서 사용자는 미리 불러진 데이터를 통해 더 빠르게 화면을 확인 할 수 있어 사용자 경험 측면에서도 분명 좋을거라 생각했습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;SSR vs SSG&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드 개발을 하시는 분이라면 SSG와 SSR은 많이 들어보셨을겁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 설명하자면 아래와 같습니다.&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;SSR (Server-Side Rendering)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 요청마다 서버에서 HTML을 생성합니다.&lt;/li&gt;
&lt;li&gt;최신 데이터를 제공하므로 실시간 데이터를 반영할 수 있습니다.&lt;/li&gt;
&lt;li&gt;페이지 요청 시마다 서버에서 처리가 필요하므로 서버 부하가 발생하고, 로드 속도가 느릴 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;SSG (Static Site Generation)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #0d0d0d; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빌드 타임에 HTML을 생성하여 정적 파일로 저장합니다.&lt;/li&gt;
&lt;li&gt;생성된 정적 파일은 서버 요청 없이 클라이언트에게 바로 제공됩니다.&lt;/li&gt;
&lt;li&gt;초기 페이지 로드 속도가 매우 빠르고, 서버 부하가 적습니다.&lt;/li&gt;
&lt;li&gt;데이터가 변경될 때마다 빌드가 필요하지만, &lt;b&gt;revalidate&lt;/b&gt; 옵션을 통해 특정 간격으로 재생성할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;getStaticProps vs&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;getServerSideProps&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;getStaticProps&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;getStaticProps&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;는 빌드 시 데이터 페칭을 처리하여 정적 생성(static generation)을 가능하게 합니다. 이 메서드는 빌드 타임에 호출되며, 데이터를 미리 가져와 HTML 파일로 생성합니다. 그 결과, 페이지는 빠르고 효율적으로 제공됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #0d0d0d; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;콘텐츠가 자주 변경되지 않는 경우&lt;/li&gt;
&lt;li&gt;블로그 게시물, 마케팅 페이지 등 변경 빈도가 낮은 페이지&lt;/li&gt;
&lt;li&gt;SEO가 중요한 경우&lt;/li&gt;
&lt;li&gt;빌드 타임에 데이터가 페칭됩니다.&lt;/li&gt;
&lt;li&gt;데이터를 정적으로 생성하므로 페이지 로드 속도가 빠릅니다.&lt;/li&gt;
&lt;li&gt;재생성(revalidate)을 설정하여 데이터가 변경될 때마다 정적 페이지를 갱신할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;getServerSideProps&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;getServerSideProps&lt;span style=&quot;background-color: #ffffff; color: #0d0d0d; text-align: start;&quot;&gt;는 요청 시마다 서버에서 데이터를 페칭하여 SSR(Server-Side Rendering)을 가능하게 합니다. 이 메서드는 각 요청마다 호출되며, 최신 데이터를 기반으로 HTML을 생성합니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #0d0d0d; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터가 자주 변경되는 경우&lt;/li&gt;
&lt;li&gt;사용자별 데이터를 처리해야 하는 경우&lt;/li&gt;
&lt;li&gt;실시간 데이터를 반영해야 하는 경우&lt;/li&gt;
&lt;li&gt;각 요청 시마다 서버에서 데이터를 페칭합니다.&lt;/li&gt;
&lt;li&gt;최신 데이터를 제공하므로 실시간 데이터 반영이 가능합니다.&lt;/li&gt;
&lt;li&gt;페이지 로드 시마다 서버에서 처리되므로 다소 지연될 수 있습니다.&lt;b&gt;&lt;/b&gt;&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;&lt;b&gt;getStaticProps:&lt;/b&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;b&gt;getServerSideProps:&lt;/b&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;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SSR(&lt;b&gt;getServerSideProps&lt;/b&gt;) :&lt;/b&gt; 매 요청마다 서버가 페이지를 동적으로 생성하고 데이터를 페칭해야 합니다. 100명의 사용자가 동시에 접속하면 100번의 서버 요청이 발생하며, 이는 서버 자원을 많이 소모하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SSG(&lt;b&gt;getStaticProps&lt;/b&gt;) :&lt;/b&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;b&gt;&lt;b&gt;getStaticProps &lt;/b&gt;&lt;/b&gt;를 이용했고 혹시나 변하는 경우 &lt;b&gt;revalidate&lt;/b&gt;로 2시간마다 데이터를 업데이트 할 수 있게 처리하였습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1717503635938&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export async function getStaticProps() {
  // Fetch data from an API
  const res = await fetch('https://api.example.com/data');
  const data = await res.json();

  return {
    props: {
      data,
    },
    revalidate: 2 * 60 * 60, // Optional: Regenerate the page every 2 hours
  };
}&lt;/code&gt;&lt;/pre&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;Issue 사항&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 데이터 페칭 이슈&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;getStaticProps는 빌드시점에 데이터를 페칭&lt;/b&gt;하는 것으로 공식 문서에 나와 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콘솔을 찍어봐도 null로 나오는 경우가 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백엔드 쪽&lt;b&gt; CORS&lt;/b&gt;&amp;nbsp;설정이 어떻게 되어 있는지 확인해보았습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;607&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcs1Mf/btsHPzFlver/dgQ0kHY61s2WH1bcAMwst0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcs1Mf/btsHPzFlver/dgQ0kHY61s2WH1bcAMwst0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcs1Mf/btsHPzFlver/dgQ0kHY61s2WH1bcAMwst0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbcs1Mf%2FbtsHPzFlver%2FdgQ0kHY61s2WH1bcAMwst0%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;607&quot; height=&quot;210&quot; data-origin-width=&quot;607&quot; data-origin-height=&quot;210&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;스프링 쪽 allowedOrigins 쪽에 모든 경로에 대한 요청을 허용하게 되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 서비스 하는 도메인 경로만 허용하게 되어 있게 설정이 되어 있지만&amp;nbsp; 저희 그룹웨어 특성상 SAP, ERP, IT시스템 등등 수 많은 시스템과 연동이 되어 있어서 일단 모든 요청에 대한 허용을 한 것으로 보였습니다.&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;&lt;b&gt;그러면 왜 데이터를 빌드 시점에 불러오지를 못 할까??&lt;/b&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;@Controller 쪽 소스를 확인해보니&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1030&quot; data-origin-height=&quot;269&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mKHPs/btsHNtAhMgU/lHkijK4ObhUUGlYTtEPGT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mKHPs/btsHNtAhMgU/lHkijK4ObhUUGlYTtEPGT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mKHPs/btsHNtAhMgU/lHkijK4ObhUUGlYTtEPGT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmKHPs%2FbtsHNtAhMgU%2FlHkijK4ObhUUGlYTtEPGT0%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;1030&quot; height=&quot;269&quot; data-origin-width=&quot;1030&quot; data-origin-height=&quot;269&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@AuthenticantionPrincipal을 통해서 Member 정보, 즉 저희 직원 정보가 있어야 요청을 받을 수 있게 처리해놓은 것으로 확인되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 빌드 시점에 ADMIN 계정에 대한 정보를 넣어 데이터를 페칭할 수 있게 처리하였습니다.&lt;/p&gt;</description>
      <category>Programming/React.js, Next.js</category>
      <category>getServerSideProps</category>
      <category>getStaticProps</category>
      <category>Nextjs</category>
      <category>react</category>
      <category>리액트</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/94</guid>
      <comments>https://puenti.tistory.com/94#entry94comment</comments>
      <pubDate>Tue, 4 Jun 2024 21:24:34 +0900</pubDate>
    </item>
    <item>
      <title>[React Conf 2024] 이제 useMemo, useCallback 필요 없음. 리액트 컴파일러란??</title>
      <link>https://puenti.tistory.com/93</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 2024년 5월 15일부터 16일까지 개최한 행사인 React Conf 2024에서 발표된 내용을 기반으로 작성되었습니다.&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;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트 19에 도입될 리액트 컴파일러는 공식 문서의 일부분에는 이렇게 설명되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;React Compiler automatically memoizes your code. You may be familiar today with memoization through APIs such as &lt;/span&gt;useMemo&lt;span style=&quot;text-align: start;&quot;&gt;, &lt;/span&gt;useCallback&lt;span style=&quot;text-align: start;&quot;&gt;, and &lt;/span&gt;React.memo&lt;span style=&quot;text-align: start;&quot;&gt;. With these APIs you can tell React that certain parts of your application don&amp;rsquo;t need to recompute if their inputs haven&amp;rsquo;t changed, reducing work on updates. While powerful, it&amp;rsquo;s easy to forget to apply memoization or apply them incorrectly. This can lead to inefficient updates as React has to check parts of your UI that don&amp;rsquo;t have any &lt;/span&gt;meaningful&lt;span style=&quot;text-align: start;&quot;&gt; changes.&lt;/span&gt;&lt;/span&gt;&lt;/i&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;애플리케이션을 최적화하기 위해 React Compiler는 자동으로 코드를 메모합니다. useMemo, useCallback, 등의 API를 통한 메모이제이션은 오늘날 익숙하실 것입니다 React.memo. 이러한 API를 사용하면 입력이 변경되지 않은 경우 애플리케이션의 특정 부분을 다시 계산할 필요가 없음을 React에 알릴 수 있으므로 업데이트 작업이 줄어듭니다. 강력하지만 메모를 적용하는 것을 잊어버리거나 잘못 적용하기 쉽습니다. 이는 React가 의미 있는 변경 사항이 없는 UI 부분을 확인해야 하기 때문에 비효율적인 업데이트로 이어질 수 있습니다.&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;h2 data-ke-size=&quot;size26&quot;&gt;리액트 컴파일러가 없었을땐??&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트에서 값을 메모이제이션을 할때 보통 useMemo나 useCallback을 사용하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1717154575742&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// useMemo
const computedValue = useMemo(() =&amp;gt; expensiveComputation(count), [count]);&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;useMemo는&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt; 계산 비용이 큰 값을 메모이제이션하여 성능을 최적화하는 데 사용됩니다. 의존성 배열이 변경되지 않는 한, 메모이제이션된 값을 반환합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1717154644134&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// useCallback
const handleClick = useCallback(() =&amp;gt; {
    console.log('Button clicked');
    setCount(count + 1);
  }, [count]);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useCallback은 &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;콜백 함수의 메모이제이션을 위해 사용됩니다. 의존성 배열이 변경되지 않는 한, 동일한 함수 인스턴스를 반환합니다.&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;이 두 예시는 각각 &lt;/span&gt;useMemo&lt;span style=&quot;text-align: start;&quot;&gt;와 &lt;/span&gt;useCallback&lt;span style=&quot;text-align: start;&quot;&gt;을 사용하여 성능을 최적화하는 방법을 보여줍니다. &lt;/span&gt;useMemo&lt;span style=&quot;text-align: start;&quot;&gt;는 계산 비용이 큰 값을 메모이제이션하고, &lt;/span&gt;useCallback&lt;span style=&quot;text-align: start;&quot;&gt;은 함수의 인스턴스를 메모이제이션하여 불필요한 렌더링을 방지합니다.&lt;/span&gt;&lt;/span&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt; 의존성 관리를 잘못 관리&lt;/b&gt;&lt;/span&gt;하면 아래와 같은 문제가 생기곤 했습니다.&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;1. 불필요한 재계산 및 재생성:&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;있습니다.&lt;br /&gt;예를&amp;nbsp;들어,&amp;nbsp;useMemo나&amp;nbsp;useCallback의&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;사라집니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 의도하지 않은 메모이제이션 실패:&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;됩니다.&lt;br /&gt;예를&amp;nbsp;들어,&amp;nbsp;의존성&amp;nbsp;배열에서&amp;nbsp;상태&amp;nbsp;값이나&amp;nbsp;props를&amp;nbsp;누락하면,&amp;nbsp;해당&amp;nbsp;상태나&amp;nbsp;props가&amp;nbsp;변경되어도&amp;nbsp;훅이&amp;nbsp;다시&amp;nbsp;계산되지&amp;nbsp;않아&amp;nbsp;버그가&amp;nbsp;발생할&amp;nbsp;수&amp;nbsp;있습니다.&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;/h2&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-filename=&quot;스크린샷 2024-05-31 오후 8.30.24.png&quot; data-origin-width=&quot;1784&quot; data-origin-height=&quot;612&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/co60pL/btsHKDWjMFw/VXVqTK4ObaSaW5sn2c55IK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/co60pL/btsHKDWjMFw/VXVqTK4ObaSaW5sn2c55IK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/co60pL/btsHKDWjMFw/VXVqTK4ObaSaW5sn2c55IK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fco60pL%2FbtsHKDWjMFw%2FVXVqTK4ObaSaW5sn2c55IK%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;1784&quot; height=&quot;612&quot; data-filename=&quot;스크린샷 2024-05-31 오후 8.30.24.png&quot; data-origin-width=&quot;1784&quot; data-origin-height=&quot;612&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;h4 data-ke-size=&quot;size20&quot;&gt;1. 컴포넌트의 계단식 재렌더링 방지 (성능 최적화)&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;p data-ke-size=&quot;size14&quot;&gt;2. React 외부의 비용이 큰 계산을 건너뛰기 (성능 최적화)&lt;br /&gt;컴포넌트나 훅 내에서 expensivelyProcessAReallyLargeArrayOfObjects()와 같은 비용이 큰 함수를 호출하는 경우, 메모이제이션을 통해 이러한 계산을 건너뛰고 성능을 최적화할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 효과 훅의 의존성 메모이제이션:&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;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;&lt;b&gt;React&amp;nbsp;Compiler의&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;있습니다.&lt;br /&gt;&lt;br /&gt;&lt;/b&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;a href=&quot;https://react.dev/learn/react-compiler&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://react.dev/learn/react-compiler&lt;/a&gt;&lt;/p&gt;</description>
      <category>Programming/React.js, Next.js</category>
      <category>2024 리액트</category>
      <category>react</category>
      <category>react conf</category>
      <category>리액트 발표</category>
      <category>리액트 컴파일러</category>
      <category>리액트19</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/93</guid>
      <comments>https://puenti.tistory.com/93#entry93comment</comments>
      <pubDate>Fri, 31 May 2024 20:37:13 +0900</pubDate>
    </item>
    <item>
      <title>[모바일 / Flutter] 플러터 개발환경 세팅하기</title>
      <link>https://puenti.tistory.com/91</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;회사 내 모바일 개발자가 없고 외주사를 통해 앱 개발을 진행했던지라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버전만 달랑 명시되어 있고 혼자 머리 깨져가며 세팅한 과정과 에러를 공유합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Xcode 설치&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1194&quot; data-origin-height=&quot;680&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bS9Mud/btsFFLt8q7P/cYPB5M2PQg2DMj8nc4L3x0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bS9Mud/btsFFLt8q7P/cYPB5M2PQg2DMj8nc4L3x0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bS9Mud/btsFFLt8q7P/cYPB5M2PQg2DMj8nc4L3x0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbS9Mud%2FbtsFFLt8q7P%2FcYPB5M2PQg2DMj8nc4L3x0%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;1194&quot; height=&quot;680&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1194&quot; data-origin-height=&quot;680&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;b&gt;반드시 앱을 한 번 실행 시켜서 마저 다운로드&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1970&quot; data-origin-height=&quot;1022&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QMCLJ/btsFCCkTbA2/ttgkdL4LmoF2VkCZAbYCo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QMCLJ/btsFCCkTbA2/ttgkdL4LmoF2VkCZAbYCo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QMCLJ/btsFCCkTbA2/ttgkdL4LmoF2VkCZAbYCo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQMCLJ%2FbtsFCCkTbA2%2FttgkdL4LmoF2VkCZAbYCo0%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;1970&quot; height=&quot;1022&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1970&quot; data-origin-height=&quot;1022&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;맥북이 업데이트 됨에 따라 CommandLinTools를 해당 경로로 설정해주는게 좋다고 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1709870924391&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 경로 확인
xcode-select -p
// 경로가 Library/Developer/CommandLineTools로 나오면 아래 실행
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
// 경로가 /Applications/Xcode.app/Contents/Developer로 나와야 됨&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;/h3&gt;
&lt;pre id=&quot;code_1709870989920&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo xcodebuild -license

// agree 입력&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-filename=&quot;4.png&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;184&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r0vww/btsFBLJAHlz/iTx5cXP6wMPsLFyqPXQ3j1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r0vww/btsFBLJAHlz/iTx5cXP6wMPsLFyqPXQ3j1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r0vww/btsFBLJAHlz/iTx5cXP6wMPsLFyqPXQ3j1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr0vww%2FbtsFBLJAHlz%2FiTx5cXP6wMPsLFyqPXQ3j1%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;1212&quot; height=&quot;184&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;184&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 나오면 라이센스 동의까지 완료&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;버전 확인&lt;/h3&gt;
&lt;pre id=&quot;code_1709871056482&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Xcode 설정
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer

// 설정2
sudo xcodebuild -runFirstLaunch&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Xcode 14.2 버전 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세아웍스 Xcode는 14.2에서 정상 작동함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱스토어에서 제공하는 Xcode는 최신버전이라 에러발생&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Xcodes app 다운로드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플 Developer에서 다운로드도 가능하지만&amp;nbsp;저는 Xcodes라는 버전 관리툴을 사용해서 진행했답니다&lt;/p&gt;
&lt;pre id=&quot;code_1709871170475&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;brew install --cask xcodes&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;모바일 버전은 14.2 버전으로 실행이 가능해서 해당 14.2 버전 다운로드&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;902&quot; data-origin-height=&quot;446&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcaxIr/btsFDyWQI4P/kYZ6NkDgFO10XXVri2L8Gk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcaxIr/btsFDyWQI4P/kYZ6NkDgFO10XXVri2L8Gk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcaxIr/btsFDyWQI4P/kYZ6NkDgFO10XXVri2L8Gk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcaxIr%2FbtsFDyWQI4P%2FkYZ6NkDgFO10XXVri2L8Gk%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;902&quot; height=&quot;446&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;902&quot; data-origin-height=&quot;446&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다운 받은 Xcode를 Xcode-14.2.0.app으로 이름 변경&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;Xcode 14.2.0 버전 실행 할때&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널에서 아래 명령어 실행 후 프로젝트 실행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모바일 프로젝트 &amp;gt; ios 폴더 실행&lt;/p&gt;
&lt;pre id=&quot;code_1709871371836&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/Applications/Xcode-14.2.0.app/Contents/MacOS/Xcode&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;1701&quot; data-origin-height=&quot;467&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dK5Tzg/btsFAFiA4eO/vfKfIOtpGaj0F7vTCBvm11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dK5Tzg/btsFAFiA4eO/vfKfIOtpGaj0F7vTCBvm11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dK5Tzg/btsFAFiA4eO/vfKfIOtpGaj0F7vTCBvm11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdK5Tzg%2FbtsFAFiA4eO%2FvfKfIOtpGaj0F7vTCBvm11%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;1701&quot; height=&quot;467&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;1701&quot; data-origin-height=&quot;467&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;h2 data-ke-size=&quot;size26&quot;&gt;2. 코코아팟, 자바, 루비 설치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사 앱 플러터 버전이 2.10.5이라 코코아팟도 해당 플러터 버전과 호환되는 버전으로 설정&lt;/p&gt;
&lt;pre id=&quot;code_1709871554260&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 코코아팟 설치
sudo gem install cocoapods -v 1.11.3

// 설치 확인
pod --version

// 자바 설치
brew install openjdk@11

java --version

// 설치된 자바 버전 확인
/usr/libexec/java_home -V

// 원하는 버전으로 변경
export JAVA_HOME=$(/usr/libexec/java_home -v 11.0.22)
source ~/.bash_profile

아니면

// .zshrc 파일에서 환경변수 설정
export JAVA_HOME=/Users/루트디렉토리/Library/Java/JavaVirtualMachines/azul-11.0.22/Contents/Home

//Ruby 설치
brew install rbenv ruby-build
rbenv install 3.0.6
rbenv global 3.0.6
ruby --version

//환경변수 설정
//편집기 실행
vi ~/.zshrc

// Ruby PATH 추가
[[ -d ~/.rbenv  ]] &amp;amp;&amp;amp; \
  export PATH=${HOME}/.rbenv/bin:${PATH} &amp;amp;&amp;amp; \
  eval &quot;$(rbenv init -)&quot;

//저장 후 적용
source ~/.zshrc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 플러터 설치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #d44c47;&quot; data-token-index=&quot;0&quot;&gt;공식 문서 있는 설치 방법대로 따라하기&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.flutter.dev/get-started/install/macos/desktop?tab=download&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.flutter.dev/get-started/install/macos/desktop?tab=download&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1709871630106&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;Start building Flutter native desktop apps on macOS&quot; data-og-description=&quot;Configure your system to develop Flutter desktop apps on macOS.&quot; data-og-host=&quot;docs.flutter.dev&quot; data-og-source-url=&quot;https://docs.flutter.dev/get-started/install/macos/desktop?tab=download&quot; data-og-url=&quot;https://docs.flutter.dev/get-started/install/macos/desktop&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b5iFGt/hyVuhy495s/nmuiKnT8Vt8lDaDHTNykv1/img.png?width=937&amp;amp;height=461&amp;amp;face=0_0_937_461&quot;&gt;&lt;a href=&quot;https://docs.flutter.dev/get-started/install/macos/desktop?tab=download&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.flutter.dev/get-started/install/macos/desktop?tab=download&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b5iFGt/hyVuhy495s/nmuiKnT8Vt8lDaDHTNykv1/img.png?width=937&amp;amp;height=461&amp;amp;face=0_0_937_461');&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;Start building Flutter native desktop apps on macOS&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Configure your system to develop Flutter desktop apps on macOS.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.flutter.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;환경변수 설정&lt;/h3&gt;
&lt;pre id=&quot;code_1709871671082&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 기본 쉘 사용할 경우
vim ~/.zshrc vim

//경로
export PATH=&quot;$PATH:/Users/나의폴더명/flutter/bin&quot;


// 전부 볷붙
export ANDROID_HOME=&quot;/Users/나의폴더명/Library/Android/sdk&quot;
export PATH=&quot;$PATH:$ANDROID_HOME/tools\:$ANDROID_HOME/tools/bin:$ANDROID_HOME/platform-tools&quot;
 
export PATH=&quot;$PATH:/Users/나의폴더명/workspace/flutter/bin&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정 후 터미널 껐다가 flutter --version 입력&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;flutter 버전 변경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사 앱은 플러터 2.10.5 버전이라 아래 과정을 거침&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;flutter 디렉토리로 이동 후 아래 명령어&lt;/p&gt;
&lt;pre id=&quot;code_1709871701764&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git checkout 2.10.5

flutter --version
// flutter 버전 2.10.5 잘나오는지 확인

flutter doctor&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. &lt;span data-token-index=&quot;1&quot;&gt;Android Studio 설치&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;1&quot;&gt;공홈에서 다운로드 받아주세요&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;1&quot;&gt;&lt;a href=&quot;https://developer.android.com/studio?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.android.com/studio?hl=ko&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1709871756259&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;Android 스튜디오 및 앱 도구 다운로드 - Android 개발자 &amp;nbsp;|&amp;nbsp; Android Studio &amp;nbsp;|&amp;nbsp; Android Developers&quot; data-og-description=&quot;Android Studio provides app builders with an integrated development environment (IDE) optimized for Android apps. Download Android Studio today.&quot; data-og-host=&quot;developer.android.com&quot; data-og-source-url=&quot;https://developer.android.com/studio?hl=ko&quot; data-og-url=&quot;https://developer.android.com/studio?hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/daOYe6/hyVxw9bZjk/mJkIrBpbK8eKW27kB5OXek/img.png?width=1201&amp;amp;height=676&amp;amp;face=0_0_1201_676,https://scrap.kakaocdn.net/dn/gqG48/hyVxC9pKoc/fdcxnPa2mDWSIHADXynhXK/img.png?width=1832&amp;amp;height=1082&amp;amp;face=0_0_1832_1082,https://scrap.kakaocdn.net/dn/zq8HM/hyVuqCNnG8/kodW4eAjEeNXNkU4DgJ2AK/img.png?width=1480&amp;amp;height=772&amp;amp;face=0_0_1480_772&quot;&gt;&lt;a href=&quot;https://developer.android.com/studio?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.android.com/studio?hl=ko&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/daOYe6/hyVxw9bZjk/mJkIrBpbK8eKW27kB5OXek/img.png?width=1201&amp;amp;height=676&amp;amp;face=0_0_1201_676,https://scrap.kakaocdn.net/dn/gqG48/hyVxC9pKoc/fdcxnPa2mDWSIHADXynhXK/img.png?width=1832&amp;amp;height=1082&amp;amp;face=0_0_1832_1082,https://scrap.kakaocdn.net/dn/zq8HM/hyVuqCNnG8/kodW4eAjEeNXNkU4DgJ2AK/img.png?width=1480&amp;amp;height=772&amp;amp;face=0_0_1480_772');&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;Android 스튜디오 및 앱 도구 다운로드 - Android 개발자 &amp;nbsp;|&amp;nbsp; Android Studio &amp;nbsp;|&amp;nbsp; Android Developers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Android Studio provides app builders with an integrated development environment (IDE) optimized for Android apps. Download Android Studio today.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.android.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실행 후 플러그인 dart, flutter 설치&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;970&quot; data-origin-height=&quot;484&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/347rT/btsFBedZtcN/TUtTgBMQpwTwOtgkrins8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/347rT/btsFBedZtcN/TUtTgBMQpwTwOtgkrins8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/347rT/btsFBedZtcN/TUtTgBMQpwTwOtgkrins8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F347rT%2FbtsFBedZtcN%2FTUtTgBMQpwTwOtgkrins8K%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;970&quot; height=&quot;484&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;970&quot; data-origin-height=&quot;484&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Android SDK cmdline-tools 설치&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Android Studio 프로젝트 &amp;gt; SETTINGS &amp;gt; Language &amp;amp; Frameworks &amp;gt; SDK Tools &amp;gt; Android SDK Command-line Tools 체크&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;977&quot; data-origin-height=&quot;710&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbuz5c/btsFBbVTqb4/uf7G8LmFL5KXc2wt9J1oK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbuz5c/btsFBbVTqb4/uf7G8LmFL5KXc2wt9J1oK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbuz5c/btsFBbVTqb4/uf7G8LmFL5KXc2wt9J1oK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbbuz5c%2FbtsFBbVTqb4%2Fuf7G8LmFL5KXc2wt9J1oK0%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;977&quot; height=&quot;710&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;977&quot; data-origin-height=&quot;710&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Android SDK Build-Tool 33.0.1 체크&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;bull; &lt;b&gt;Android Studio 프로젝트 &amp;gt; SETTINGS &amp;gt; Language &amp;amp; Frameworks &amp;gt; SDK Tools &amp;gt; Android SDK Build-Tool 33.0.1 체크&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E2pMa/btsFB7MtGx1/PNtJYJbvmWw4kkHSKOt1n0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E2pMa/btsFB7MtGx1/PNtJYJbvmWw4kkHSKOt1n0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;769&quot; data-origin-height=&quot;902&quot; data-filename=&quot;2.png&quot; data-widthpercent=&quot;37.92&quot; style=&quot;width: 37.4839%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E2pMa/btsFB7MtGx1/PNtJYJbvmWw4kkHSKOt1n0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE2pMa%2FbtsFB7MtGx1%2FPNtJYJbvmWw4kkHSKOt1n0%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;769&quot; height=&quot;902&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVLkGa/btsFCwytYIY/6GUkhre3pxtTt2mJsdRbuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVLkGa/btsFCwytYIY/6GUkhre3pxtTt2mJsdRbuK/img.png&quot; data-origin-width=&quot;981&quot; data-origin-height=&quot;703&quot; data-is-animation=&quot;false&quot; data-filename=&quot;3.png&quot; style=&quot;width: 61.3533%;&quot; data-widthpercent=&quot;62.08&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVLkGa/btsFCwytYIY/6GUkhre3pxtTt2mJsdRbuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVLkGa%2FbtsFCwytYIY%2F6GUkhre3pxtTt2mJsdRbuK%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;981&quot; height=&quot;703&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Android SDK Command Line-Tool 8.0 체크&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Android Studio 프로젝트 &amp;gt; SETTINGS &amp;gt; Language &amp;amp; Frameworks &amp;gt; SDK Tools &amp;gt; Android SDK Command Line-Tool 8.0 체크&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1217&quot; data-origin-height=&quot;696&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfp7wZ/btsFD33riTi/UkxkKkFCKk1qIE9ZvfWH71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfp7wZ/btsFD33riTi/UkxkKkFCKk1qIE9ZvfWH71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfp7wZ/btsFD33riTi/UkxkKkFCKk1qIE9ZvfWH71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcfp7wZ%2FbtsFD33riTi%2FUkxkKkFCKk1qIE9ZvfWH71%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;1217&quot; height=&quot;696&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1217&quot; data-origin-height=&quot;696&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;pre id=&quot;code_1709871911505&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 안드로이드 라이센스 허가
flutter doctor --android-licenses&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;pre id=&quot;code_1709871922981&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;flutter doctor&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;578&quot; data-origin-height=&quot;245&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bph8uV/btsFA8SHjev/3zMhEMNStAYEzbkwusLZmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bph8uV/btsFA8SHjev/3zMhEMNStAYEzbkwusLZmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bph8uV/btsFA8SHjev/3zMhEMNStAYEzbkwusLZmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbph8uV%2FbtsFA8SHjev%2F3zMhEMNStAYEzbkwusLZmk%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;578&quot; height=&quot;245&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;578&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;h2 data-ke-size=&quot;size26&quot;&gt;Dart / Project 환경 세팅&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;flutter 버전 확인&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;86&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvcr8U/btsFACe93CX/Tx0CUuqc34yw3ZKik8Wspk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvcr8U/btsFACe93CX/Tx0CUuqc34yw3ZKik8Wspk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvcr8U/btsFACe93CX/Tx0CUuqc34yw3ZKik8Wspk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcvcr8U%2FbtsFACe93CX%2FTx0CUuqc34yw3ZKik8Wspk%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;591&quot; height=&quot;86&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;86&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&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dRLJ8L/btsFB6Und9W/HXMkNTKB54oK3ZSPV5ZPdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dRLJ8L/btsFB6Und9W/HXMkNTKB54oK3ZSPV5ZPdK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1994&quot; data-origin-height=&quot;1438&quot; data-filename=&quot;2.png&quot; style=&quot;width: 52.5412%; margin-right: 10px;&quot; data-widthpercent=&quot;53.16&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dRLJ8L/btsFB6Und9W/HXMkNTKB54oK3ZSPV5ZPdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdRLJ8L%2FbtsFB6Und9W%2FHXMkNTKB54oK3ZSPV5ZPdK%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;1994&quot; height=&quot;1438&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhcLLX/btsFCxEafQd/rT058aEgphoNCEbhDv4c5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhcLLX/btsFCxEafQd/rT058aEgphoNCEbhDv4c5k/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;2060&quot; data-origin-height=&quot;1686&quot; data-filename=&quot;3.png&quot; style=&quot;width: 46.296%;&quot; data-widthpercent=&quot;46.84&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhcLLX/btsFCxEafQd/rT058aEgphoNCEbhDv4c5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhcLLX%2FbtsFCxEafQd%2FrT058aEgphoNCEbhDv4c5k%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;2060&quot; height=&quot;1686&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&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;5. flutter 의존성 설치&lt;/h2&gt;
&lt;pre id=&quot;code_1709872152598&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 라이브러리 설치 명령어실행
flutter pub get&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;h3 data-ke-size=&quot;size23&quot;&gt;Build &amp;amp; Run&lt;/h3&gt;
&lt;pre id=&quot;code_1709872176286&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;flutter pub run build_runner build --delete-conflicting-outputs&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Flutter</category>
      <category>모바일 개발</category>
      <category>모바일 앱 개발</category>
      <category>플러터</category>
      <category>플러터 개발</category>
      <category>플러터 세팅</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/91</guid>
      <comments>https://puenti.tistory.com/91#entry91comment</comments>
      <pubDate>Fri, 8 Mar 2024 13:31:22 +0900</pubDate>
    </item>
    <item>
      <title>실무에서 개발, 로컬 Node 버전 업데이트를 해보자</title>
      <link>https://puenti.tistory.com/90</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;서론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사 내 TDD를 적용하려 리액트 테스트 라이브러리인 Jest와 RTL(React Testing Library)을 설치하려 하니??&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 쓰는 Node 버전이 낮아서 설치가 안된다는 문구가ㅠ....&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;알아보니 현재 내가 사용하는 Node 버전은 16.7.0&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Jest를 설치하기 위한 최소 Node 버전은 16.10.0 이라 설치가 안된다는 문구;;&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;운영 node : 16.14.0&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 node : 16.13.0&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 node : 16.7.0&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;심지어 로컬 환경 세팅 가이드는 16.7.0으로 세팅되게 되어 있었네요...&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;TDD 도입하려다 노드 버전까지 바꾸게 되는 작업까지 했네요ㅠ...&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;h2 data-ke-size=&quot;size26&quot;&gt;1. 노드 버전 다운로드&lt;/h2&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;a href=&quot;https://nodejs.org/download/release/v16.14.0/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://nodejs.org/download/release/v16.14.0/&lt;/a&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;2. 환경변수 세팅&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;686&quot; data-origin-height=&quot;400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5l37e/btsD1mROySM/T2yJnboOrTCcKbPhtMNk81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5l37e/btsD1mROySM/T2yJnboOrTCcKbPhtMNk81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5l37e/btsD1mROySM/T2yJnboOrTCcKbPhtMNk81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5l37e%2FbtsD1mROySM%2FT2yJnboOrTCcKbPhtMNk81%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;686&quot; height=&quot;400&quot; data-origin-width=&quot;686&quot; data-origin-height=&quot;400&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;다운로드 받은 경로에서&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여담으로 개인 컴퓨터는 맥북인데 맥북은 brew, nvm 으로 쉽게 원하는 노드 버전을 바꿀 수 있었습니다!&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_1706275879144&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;node -v
npm -v&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;214&quot; data-origin-height=&quot;139&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzfNBW/btsDZ0Iu8uZ/jvngZlZnCkvqaJZVOQAMK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzfNBW/btsDZ0Iu8uZ/jvngZlZnCkvqaJZVOQAMK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzfNBW/btsDZ0Iu8uZ/jvngZlZnCkvqaJZVOQAMK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzfNBW%2FbtsDZ0Iu8uZ%2FjvngZlZnCkvqaJZVOQAMK0%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;214&quot; height=&quot;139&quot; data-origin-width=&quot;214&quot; data-origin-height=&quot;139&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. node_modules, .next 폴더 삭제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 node_modules, .next 폴더는 이전 node 버전으로 라이브러리가 설치되어 있어 삭제 필요합니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에 설치되어 있는 node_modules 폴더 삭제&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 폴더는 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;프로젝트 폴더에서 해당 폴더 삭제해도 되고 아래의 명령어로 삭제해도 됩니다&lt;/p&gt;
&lt;pre id=&quot;code_1706277259456&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;rm -rf node_modules
rm -rf .next&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. yarn 설치&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;yarn이 설치되어 있는지 확인하기!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 캐시 클린 후 install하고 프로젝트 run 하면 된다!&lt;/p&gt;
&lt;pre id=&quot;code_1706277745314&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;yarn -v

npm install -g yarn

yarn cache clean

yarn install&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 우분투에서 node 버전 변경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 서버는 우분투에 노드가 올라가 있어서 바꾸는 방법이 달랐습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 방법이 있었지만 저는 n으로 버전을 변경해봤습니다&lt;/p&gt;
&lt;pre id=&quot;code_1706278256172&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// n 설치
sudo npm install -g n

// n 설치확인
n -V 

// lts 버전 설치
n lts &amp;nbsp;
// 최신 버전 설치
n latest&amp;nbsp;
// 특정 버전 설치
n 16.14.0

n list // 설치된 노드 확인

// 노드 버전을 변경하기 전에 현재 사용 중인 버전을 사용 중지합니다
n rm &amp;lt;현재_버전&amp;gt;

// 목표로 하는 노드 버전을 사용 가능한 버전으로 설치합니다.
n use 16.14.0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널 껐다 켜고 버전 확인하기&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결과&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 운영, 개발, 로컬 노드 버전 통일 (node 16.14.0)&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;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;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;에러&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. yarn.lock과 package-lock.json 충돌&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;패키지 매너저로 yarn을 사용한다면 package-lock.json 없어도 됨 npm install 때리면 현재 package.json 기준으로 다시 생김&lt;/li&gt;
&lt;li&gt;npm, yarn 두개 같이 사용하는건 권장하지 않음. 충돌 자주 발생함.&lt;/li&gt;
&lt;li&gt;package.json 파일이 가장 중요&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;2. npm cache clean --force&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;npm cache clean --force 안되는 문제 발생&lt;/li&gt;
&lt;li&gt;branch 새로 따고 node_modules, yarn.lock, package-lock.json 삭제 후 처음부터 진행&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;3. 노드 비트 문제&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;64비트 컴퓨터에 32비트 노드를 설치했을 때 발생&lt;/li&gt;
&lt;li&gt;error - uncaughtException: RangeError: Array buffer allocation failed&lt;/li&gt;
&lt;li&gt;환경 설정하고 컴퓨터 재시작 후 안되는 현상 발생, 다음날 다시 하니 잘됨&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;왜 그런지 찾아보니 이런 문제가 아니었을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  cpu에 붙어있는 L1, L2, L3 메모리는 SRAM 이라고 하는 휘발성 메모리.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU 전원 공급이 안되면 날아가는 데이터&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터를 재시작하는 경우, 전원이 끊기지 않고 즉시 다시 켜지기 때문에 전원이 완전히 꺼진 것과는 다릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재시작하는 동안에는 L1, L2, L3 캐시의 데이터가 일반적으로 손실되지 않습니다.&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;64비트로 설치, 환경 변수 설정 후 컴퓨터 완전히 껐다가 켜기&lt;/p&gt;</description>
      <category>Programming/React.js, Next.js</category>
      <category>node version</category>
      <category>node version update</category>
      <category>노드 버전</category>
      <category>노드 버전 업데이트</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/90</guid>
      <comments>https://puenti.tistory.com/90#entry90comment</comments>
      <pubDate>Fri, 26 Jan 2024 23:23:13 +0900</pubDate>
    </item>
    <item>
      <title>[React] 실무에서 리팩토링으로 리액트 성능 개선 후기</title>
      <link>https://puenti.tistory.com/89</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;리액트 성능 개선 후기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;안녕하세요? 준입니다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그간 현생이 너무 바빠 글을 많이 올리지 못 했는데요.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;어쩌다 보니 리액트 성능 개선을 한 후기와 제가 고민했던 부분을 적어보려 합니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;결론부터 말씀드리자면 &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;통합검색 로직 개선을 통해 약 50% 성능 향상&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 있었습니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;먼저 보안 때문에 코드와 많은 사진을 못 보여드리는 점 양해부탁드립니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 개선&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;통합검색 페이지는 3가지 섹션으로 구성되어 있습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 전계열사 임직원 정보(소속, 담당업무 등), 2. 전자결재, 3. 게시판으로 이루어져 있습니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;에러 픽스 요청으로 게시판과 전자결재에서 '더보기' 버튼 클릭 시 동일한 컨텐츠가 중복 노출되는 문제를 발견하게 되었고, &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이를 수정하면서 통합검색 로직이 비효율적으로 동작하는 것을 확인했습니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;통합검색 로직&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 메인페이지에서 통합검색을 하면 검색할 때 통합검색 api 호출&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. 통합검색 페이지로 라우팅&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. 통합검색 페이지에서 useEffect를 통해서 동일한 통합검색 api 호출&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;왜 이렇게 비효율적으로 되어 있나라는 생각에 로직을 좀 더 파보니&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;통합검색은 아래처럼 페이지 접근이 가능합니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;메인 페이지 검색 -&amp;gt; 통합검색 페이지(임직원 정보, 전자결재, 게시판) -&amp;gt; 통합검색 상세페이지(전자결재, 게시판)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;통합검색 상세페이지에서 뒤로가기를 했을때 통합검색 페이지에서 이전 검색 결과를 다시 보여주려 통합검색 페이지에서 useEffect를 통한 호출을 하는걸 확인하였습니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oIUkz/btsCDeBwHDX/zaon2waJWxsndvZWYjmW01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oIUkz/btsCDeBwHDX/zaon2waJWxsndvZWYjmW01/img.png&quot; data-origin-width=&quot;502&quot; data-origin-height=&quot;1269&quot; style=&quot;width: 34.1299%;&quot; data-is-animation=&quot;false&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oIUkz/btsCDeBwHDX/zaon2waJWxsndvZWYjmW01/img.png&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoIUkz%2FbtsCDeBwHDX%2Fzaon2waJWxsndvZWYjmW01%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;502&quot; height=&quot;1269&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MckRe/btsCKpvu54m/Xl8PoIeJdSfHOK0ysA9rH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MckRe/btsCKpvu54m/Xl8PoIeJdSfHOK0ysA9rH1/img.png&quot; data-origin-width=&quot;888&quot; data-origin-height=&quot;1184&quot; style=&quot;width: 64.7074%;&quot; data-is-animation=&quot;false&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MckRe/btsCKpvu54m/Xl8PoIeJdSfHOK0ysA9rH1/img.png&quot; alt=&quot;&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMckRe%2FbtsCKpvu54m%2FXl8PoIeJdSfHOK0ysA9rH1%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;888&quot; height=&quot;1184&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1번 로직을 삭제하여 검색어만 통합검색 컴포넌트로 넘겨주는 방식으로 했습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;다행히 예상되는 부분이 있어 해당 로직 부분 개선은 수월하게 했습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(이 방법이 옳은 방법이라 생각한건 아닙니다. 더 좋은 방법이 있을거라 생각하지만 저 때는 저게 최선이었습니다ㅠ..)&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;개선 후 기존 평균 43점 (10회 측정) 개선 후 67점 (10회 측정)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;약 50% 성능 향상이 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2.&amp;nbsp; 고민(?)했던 부분&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;개인적으로 이번 개선을 통해 제일 고민을 많이 했던 부분은 3가지입니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 좋은 설계와 유지보수&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;통합검색 로직을 보면서 개인적으로 로직이 너무 복잡하다라는 생각을 했습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여러 페이지와 컴포넌트를 오가고 Redux 로직 등을 보면서 좋은 설계와 유지보수가 용이한 코드에 대해서 많이 생각했습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;주석 한 줄 없는 코드 속에서 한 줄의 주석을 보면서 사막의 오아시스를 느끼며 주석의 중요성을 한 번 더 느끼곤 했습니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. useEffect 더 잘 사용하기&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;리액트 공식 문서에서 useEffect에서는 방대한 로직은 피하라고 되어 있습니다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;방대한 로직이 포함해야 할 경우 useEffect 여러 개로 나눠 사용하라는 말도 있죠.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;메인페이지에서 통합검색을 하면 검색할 때 통합검색 api 호출하는게 나을까?&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;통합검색 페이지에서 useEffect를 통해서 동일한 통합검색 api 호출하는게 나을까?&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;useEffect에 방대한 로직을 담는게 맞는건가? 방대한 로직을 어떻게 분리하면 좋을까?&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;등등&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. 이상적인 코드, 설계와 실무 그리고 리팩토링&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;많은 개발자, 현업에 계신 분들은 공감하실텐데요.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;바로 &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;일정&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;의 압박이죠...&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이런 시간의 압박 속에서 이상적인 코드와 설계는 어렵지 않나 라는 생각을 했습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그렇다고 시간을 많이 들인다고 더 좋은 코드와 설계를 할 수 있을까? 하는 생각도 했네요.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;개인적으로&amp;nbsp;이상적인 코드와 설계도 중요하지만 그보다 더 중요한건 일정이라고 생각하는 사람 중 한 사람입니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그래서 저는 일단 기능 구현, 요구사항 만족을 기간 내에 하고 시간이 남으면 더 나은 코드, 유지보수가 용이하게 수정하는 편인데요.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;막상 일정의 압박과 넘치는 업무에서 위처럼 하기란 쉽지 않습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그래서 리팩토링의 중요성을 한 번 더 체감하지 않았나...&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. 결론&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;뭔가 적다보니 제 넋두리인 것 같은데요ㅎㅎ;;;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;제가 성능 개선 한 방법 말고도 더 나은 방법, 방향이 있다고 생각합니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;글을 적다보니 엄청 거창하거나 특별한 방법이 있는게 아니라 쑥스럽긴 합니다ㅎ;;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;라이트 하우스를 통해 병목지점, 여러 문제 등을 확인하고 해당 로직을 개선 할 수 있는 방법도 있는데&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;다음엔 라이트 하우스를 이용해 성능개선 할 수 있는 포스팅을 할 수 있었으면 좋겠네요ㅎ&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;구글에 수 많은 최적화, 성능 개선 방법 중 하나였다고 읽어주시면 좋겠습니다!&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;세줄요약&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 통합검색 성능 개선 약 50% 향상&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. 로직 개선&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. 방법은 아주 많음!&lt;/span&gt;&lt;/p&gt;</description>
      <category>Programming/React.js, Next.js</category>
      <category>리액트</category>
      <category>리액트 성능개선</category>
      <category>성능최적화</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/89</guid>
      <comments>https://puenti.tistory.com/89#entry89comment</comments>
      <pubDate>Thu, 28 Dec 2023 17:30:37 +0900</pubDate>
    </item>
    <item>
      <title>[React] useState는 동기일까? 비동기일까?</title>
      <link>https://puenti.tistory.com/87</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;useState는 동기일까? 비동기일까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론부터 말하자면 &lt;span style=&quot;color: #333333;&quot;&gt;React에서 setState를 호출하면 상태 업데이트는 일반적으로 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;비동기적으로 처리&lt;/b&gt;&lt;/span&gt;됩니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이는 React가 여러 상태 업데이트를 &lt;b&gt;batch 처리&lt;/b&gt;하고, &lt;b&gt;성능을 향상시키기 위해 상태 업데이트를 함께 묶어서 처리하기 때문&lt;/b&gt;입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;예를 들어, 다음과 같이 useState를 사용하여 상태 변수를 업데이트하는 예제를 살펴봅시다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1688534088363&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useState } from 'react';

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

  const increment = () =&amp;gt; {
    setCount(count + 1);
    console.log(count); // 이 부분에서는 업데이트된 count 값을 가져오지 못합니다.
  };

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p&amp;gt;Count: {count}&amp;lt;/p&amp;gt;
      &amp;lt;button onClick={increment}&amp;gt;Increment&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;위의 예제에서 setCount를 호출한 직후에 console.log(count)를 실행하면 업데이트된 count 값을 가져오지 못합니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;setCount는 비동기적으로 처리되며, React는 &lt;b&gt;상태 업데이트를 batch로 처리하여 성능을 최적화하기 때문&lt;/b&gt;입니다.&lt;/span&gt;&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;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;caret-color: #333333;&quot;&gt;리액트에서 batch 처리란?&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트는 불필요한 렌더링을 줄이기 위해서 state 변경 호출을 단 한번의 호출로 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 합치는 과정을 batch라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;batch를 하기 위해 함수는 비동기로 처리가 되어 변경됩니다.&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;span style=&quot;color: #333333;&quot;&gt;useState를 동기적으로 사용하려면?&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useState만큼 많이 사용하는 hook인 &lt;b&gt;useEffect&lt;/b&gt;를 사용하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useEffect는 리액트 컴포넌트가 렌더링될 때마다 특정 작업을 수행하도록 설정할 수 있는 hook입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 코드처럼 useEffect의 &lt;b&gt;두 번째 파라미터로 전달되는 배열 안에 검사하고 싶은 값&lt;/b&gt;을 넣어주면 동기적으로 사용가능합니다.&lt;/p&gt;
&lt;pre class=&quot;pf&quot; style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;code&gt;useEffect(() =&amp;gt; {
  // ... //
},[바꾸고싶은 state]);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming/React.js, Next.js</category>
      <category>react</category>
      <category>useState 동기</category>
      <category>useState 비동기</category>
      <category>리액트</category>
      <category>리액트 useState</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/87</guid>
      <comments>https://puenti.tistory.com/87#entry87comment</comments>
      <pubDate>Wed, 5 Jul 2023 14:27:50 +0900</pubDate>
    </item>
    <item>
      <title>[Next.JS] getServerSideProps와 getStaticProps, revalidate</title>
      <link>https://puenti.tistory.com/86</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 만들어본 서비스에 SSR을 지원하는 Next.JS를 적용하고 Notion API를 이용해서 공지사항을 구현하는 과정을 기록합니다.&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://puenti.tistory.com/85&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://puenti.tistory.com/85&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1688006419762&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;[React] Notion API 연동해보기&quot; data-og-description=&quot;https://about-home.net/ 우리집은 안전할까? 우리집 정보 확인하기 우리집은 안전할까? 우리집 정보 확인하기 about-home.net 이번에 친구와 사이드 프로젝트를 진행했던 어바웃홈입니다. 기본적인 기능 &quot; data-og-host=&quot;puenti.tistory.com&quot; data-og-source-url=&quot;https://puenti.tistory.com/85&quot; data-og-url=&quot;https://puenti.tistory.com/85&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/v2bYv/hyS9KWDvHN/NYLyrbqPX59dXZRaBTEeo0/img.png?width=624&amp;amp;height=479&amp;amp;face=0_0_624_479,https://scrap.kakaocdn.net/dn/deEp8v/hyS9QbtF6T/5XBOXsBhYK1FBOH8kNBeQk/img.png?width=624&amp;amp;height=479&amp;amp;face=0_0_624_479,https://scrap.kakaocdn.net/dn/dvdl16/hyS9GtbdNY/qrO96vj4CEkKLiF7nyPMFk/img.jpg?width=3024&amp;amp;height=3024&amp;amp;face=0_0_3024_3024&quot;&gt;&lt;a href=&quot;https://puenti.tistory.com/85&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://puenti.tistory.com/85&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/v2bYv/hyS9KWDvHN/NYLyrbqPX59dXZRaBTEeo0/img.png?width=624&amp;amp;height=479&amp;amp;face=0_0_624_479,https://scrap.kakaocdn.net/dn/deEp8v/hyS9QbtF6T/5XBOXsBhYK1FBOH8kNBeQk/img.png?width=624&amp;amp;height=479&amp;amp;face=0_0_624_479,https://scrap.kakaocdn.net/dn/dvdl16/hyS9GtbdNY/qrO96vj4CEkKLiF7nyPMFk/img.jpg?width=3024&amp;amp;height=3024&amp;amp;face=0_0_3024_3024');&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] Notion API 연동해보기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;https://about-home.net/ 우리집은 안전할까? 우리집 정보 확인하기 우리집은 안전할까? 우리집 정보 확인하기 about-home.net 이번에 친구와 사이드 프로젝트를 진행했던 어바웃홈입니다. 기본적인 기능&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;puenti.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Next.JS에서 지원하는 getSerSideProps와 getStaticProps에 대해 알아보면 이렇습니다.&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;getServerSideProps&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getServerSideProps는 매번 요청이 있을 때마다 서버 측에서 데이터를 가져옵니다. 즉, 매 요청마다 실행되며, 데이터를 서버에서 가져와 페이지를 렌더링합니다. 이는 데이터가 항상 최신 상태를 유지해야 하는 경우에 유용합니다. 예를 들어, 외부 API로부터 실시간 데이터를 가져와야 하는 경우에 적합합니다. 이 방법은 페이지가 항상 최신 데이터로 업데이트되어야 하는 동적인 페이지에 적합합니다.&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;getStaticProps&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getStaticProps는 빌드 시점에 데이터를 미리 가져옵니다. 이 메서드는 서버 측에서 한 번 실행되며, 데이터를 사전 렌더링하여 정적인 HTML 파일을 생성합니다. 이렇게 생성된 정적 파일은 요청 시 클라이언트에게 제공됩니다. 이는 페이지가 자주 변하지 않거나, 모든 요청마다 데이터를 다시 가져오지 않아도 되는 경우에 유용합니다. 예를 들어, 블로그 게시물, 제품 목록 등과 같이 정적으로 유지되는 데이터에 적합합니다. 정적 생성을 사용하면 서버 부하가 줄어들고, 성능이 향상됩니다.&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;기존 구현, getStaticProps&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에는 성능과 사용자 경험 측면에서 보았을 때 getStaticProps를 이용해서 구현을 했습니다.&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;하지만 문제가 있었습니다. &quot;&lt;b&gt;getStaticProps는 빌드 시점에 데이터를 미리 가져옵니다&quot;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;즉, 이후에 데이터가 변경되어도 정적 파일은 업데이트되지 않고 이전에 생성된 데이터를 계속 사용합니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;따라서 클라이언트는 새로운 데이터를 알지 못하고 이전의 데이터만을 볼 수 있게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 부랴부랴 getServerSideProps로 바꿔봅니다.&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;변경, getServerSideProps&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 문제 때문에 이용자가 공지사항에 들어가면 매번 요청을 하는 getServerSideProps로 바꾸게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 getServerSideProps를 이용하니 성능에 문제가 생깁니다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 이용자가 공지사항에 들어갈때마다 서버에 요청을 하다보니 렌더링하기까지 짧으면 1초에서 길면 4초라는 시간이 걸렸습니다.&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;최종 getStaticProps와 revalidate&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js는 데이터의 업데이트 주기에 따라 정적 파일을 다시 생성할 수 있는 기능을 제공합니다. 이를 통해 정적 사이트 재생성 (Static Site Regeneration)이 가능합니다. 재생성 주기를 설정하여 일정 시간마다 정적 파일을 갱신하거나, 요청 시마다 업데이트된 데이터를 가져올 수 있습니다.&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;데이터 업데이트 주기를 설정하려면 getStaticProps의 options 매개변수를 사용하여 revalidate 값을 설정합니다. revalidate 값은 정적 파일을 재생성하는 주기를 지정하는데 사용됩니다. 예를 들어, 다음과 같이 설정하면 10초마다 정적 파일이 재생성됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1688007248776&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export async function getStaticProps() {
  // 데이터 가져오기 로직
  return {
    props: {},
    revalidate: 10, // 10초마다 재생성
  };
}&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;span style=&quot;color: #333333; text-align: start;&quot;&gt;이렇게 설정하면 Next.js는 설정된 주기에 따라 정적 파일을 재생성하고 클라이언트에게 업데이트된 데이터를 제공합니다. 따라서 getStaticProps를 사용하더라도 일정 주기로 데이터를 업데이트할 수 있습니다.&lt;/span&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;span style=&quot;color: #333333; text-align: start;&quot;&gt;공지사항 페이지 특성상 게시글이 자주 올라가는 게시판이 아니라 getStaticProps를 이용해서 미리 렌더링을 하되 revalidate를 통해 주기적으로 서버에 요청을 해서 성능과 사용자 경험을 해결할 수 있도록 하였습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming/React.js, Next.js</category>
      <category>getServerSideProps</category>
      <category>getStaticProps</category>
      <category>getStaticProps getServerSideProps란?</category>
      <category>Next.js</category>
      <category>react</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/86</guid>
      <comments>https://puenti.tistory.com/86#entry86comment</comments>
      <pubDate>Thu, 29 Jun 2023 11:58:19 +0900</pubDate>
    </item>
    <item>
      <title>[React] Notion API 연동해보기</title>
      <link>https://puenti.tistory.com/85</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;어바웃홈&quot; href=&quot;https://about-home.net/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://about-home.net/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687920524685&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;우리집은 안전할까? 우리집 정보 확인하기&quot; data-og-description=&quot;우리집은 안전할까? 우리집 정보 확인하기&quot; data-og-host=&quot;about-home.net&quot; data-og-source-url=&quot;https://about-home.net/&quot; data-og-url=&quot;https://about-home.net/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eA2ra/hyS8xDZehQ/TKPYSUk73VDUH4oRMyiYBk/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080&quot;&gt;&lt;a href=&quot;https://about-home.net/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://about-home.net/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eA2ra/hyS8xDZehQ/TKPYSUk73VDUH4oRMyiYBk/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080');&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;우리집은 안전할까? 우리집 정보 확인하기&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;about-home.net&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;p data-ke-size=&quot;size16&quot;&gt;기본적인 기능 구현은 다되어 있고 공지사항을 어떻게 구현할지에 대한 고민이 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;친구와 말한 기존안은 어드민 페이지를 만들어서 거기에 공지사항 CRUD 페이지를 만들고 클라이언트에서 DB에 저장되어 있는 공지사항을 호출하는 방식을 생각했는데....&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;친구도 이직을 하고 시간도 많이 없어 일단 노션 API를 통해 공지사항을 구현하기로 했습니다.&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;1. 사용 라이브러리&amp;nbsp;&lt;/h3&gt;
&lt;pre id=&quot;code_1687921452213&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;dependencies&quot;: {
    &quot;@notionhq/client&quot;: &quot;^2.2.5&quot;,
    &quot;notion-client&quot;: &quot;^6.16.0&quot;,
    &quot;react-notion-x&quot;: &quot;^6.16.0&quot;,
    &quot;notion-to-md&quot;: &quot;^3.1.0&quot;,
  },&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;@notionhq/client와 notion-client&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 분은 notionhq/client를 쓰고 다른 분은 notion-client 쓰는 사람도 있고 둘 다 같은거 같은데 찾아보니 아래와 같은 차이를 가지고 있습니다.&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;span style=&quot;color: #333333;&quot;&gt;notionhq/client&lt;/span&gt;&lt;/b&gt;와 &lt;b&gt;notion-client&lt;/b&gt;는 Notion API를 사용하기 위한 두 가지 서드파티 라이브러리입니다. 이 라이브러리들은 Notion API를 호출하고 데이터를 가져오거나 업데이트하는 등의 작업을 수행할 수 있습니다.&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;span style=&quot;color: #333333;&quot;&gt;notionhq/client는 Notion의 공식 클라이언트 라이브러리로, Notion에서 직접 유지보수하고 지원합니다. 이 라이브러리는 Node.js 및 브라우저 환경에서 사용할 수 있으며, Notion API의 다양한 기능을 활용할 수 있는 많은 메서드와 도우미 함수를 제공합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;notionhq/client는 안정적이고 문서화가 잘 되어 있으며, 공식 지원을 받기 때문에 업데이트 및 버그 수정이 빠르게 이루어집니다. &lt;/span&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;span style=&quot;color: #333333;&quot;&gt;반면에 notion-client는 notionhq/client와는 별개의 개발자에 의해 만들어진 라이브러리입니다. 이 라이브러리도 Notion API와 상호작용할 수 있지만, 공식적인 Notion 지원이 아니므로 업데이트나 버그 수정이 느리게 이루어질 수 있습니다.&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;또한, notionhq/client보다는 기능이 제한적일 수도 있습니다. 따라서, Notion API를 사용하여 개발을 진행할 때는 공식 지원을 받는 notionhq/client를 사용하는 것이 가장 좋습니다. 이는 안정성과 업데이트, 지원 측면에서 더 많은 혜택을 받을 수 있기 때문입니다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&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;span style=&quot;color: #333333; text-align: start;&quot;&gt;저는 둘 다 써봤지만 공식문서가 잘되어 있는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;notionhq/client&lt;/b&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;react-notion-x&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;react-notion-x는 Notion의 블록 구조와 일치하는 컴포넌트 구조를 가지고 있어, Notion 페이지의 구조를 정확하게 재현할 수 있습니다. 또한, Notion의 스타일과 테마를 적용하여 일관된 뷰를 제공할 수 있습니다. 라이브러리는 다양한 커스터마이징 옵션과 플러그인을 제공하므로, Notion 페이지를 개발자의 필요에 맞게 조정하고 확장할 수 있습니다.&lt;/span&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;해당 라이브러리는 notionhq/client가 아닌 notion-client를 사용해야 해서 사용하지는 않았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 블로그는 react-notion-x를 통해서 구현 예시를 잘보여줘 참고바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://enfp-jake.tistory.com/214&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://enfp-jake.tistory.com/214&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687922213885&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;[React][NextJS][Notion] React에 Notion페이지를 가져오는 방법&quot; data-og-description=&quot;# react-notion-x를 사용해서 React에서 Notion 페이지 보여주기 - Notion은 쉽게 웹 상에 공개된 페이지를 만들 수 있고, 기본 제공되는 디자인이 훌륭하기 때문에, 약관 동의나 사용법과 같은 서비스의 &quot; data-og-host=&quot;enfp-jake.tistory.com&quot; data-og-source-url=&quot;https://enfp-jake.tistory.com/214&quot; data-og-url=&quot;https://enfp-jake.tistory.com/214&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cbjM6d/hyS9GsxgKV/nXW99MkK4o4wW80KqAREk0/img.png?width=480&amp;amp;height=320&amp;amp;face=0_0_480_320,https://scrap.kakaocdn.net/dn/bPdeeA/hyS9NSIQEa/zKK97JhwuAbudslkjwAs6k/img.png?width=480&amp;amp;height=320&amp;amp;face=0_0_480_320,https://scrap.kakaocdn.net/dn/e97DL/hyS8lDxwnN/kNNNOG4mSqWW0T0W74EFGK/img.png?width=480&amp;amp;height=320&amp;amp;face=0_0_480_320&quot;&gt;&lt;a href=&quot;https://enfp-jake.tistory.com/214&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://enfp-jake.tistory.com/214&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cbjM6d/hyS9GsxgKV/nXW99MkK4o4wW80KqAREk0/img.png?width=480&amp;amp;height=320&amp;amp;face=0_0_480_320,https://scrap.kakaocdn.net/dn/bPdeeA/hyS9NSIQEa/zKK97JhwuAbudslkjwAs6k/img.png?width=480&amp;amp;height=320&amp;amp;face=0_0_480_320,https://scrap.kakaocdn.net/dn/e97DL/hyS8lDxwnN/kNNNOG4mSqWW0T0W74EFGK/img.png?width=480&amp;amp;height=320&amp;amp;face=0_0_480_320');&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][NextJS][Notion] React에 Notion페이지를 가져오는 방법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;# react-notion-x를 사용해서 React에서 Notion 페이지 보여주기 - Notion은 쉽게 웹 상에 공개된 페이지를 만들 수 있고, 기본 제공되는 디자인이 훌륭하기 때문에, 약관 동의나 사용법과 같은 서비스의&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;enfp-jake.tistory.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;h3 data-ke-size=&quot;size23&quot;&gt;notion-to-md&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;notion-to-md는 Notion 페이지를 Markdown 형식으로 변환하는 도구나 라이브러리입니다. 이 라이브러리를 사용하면 Notion에서 작성한 콘텐츠를 Markdown 파일로 내보낼 수 있습니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;2. notion api와 block 이해&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 기능을 구현하면서 개인적으로 notion api를 이해하는 부분이 가장 어려웠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노션 데이터베이스라는 개념이 있고 해당 데이터베이스를 호출하고 response 값을 아래처럼 받아왔습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 해당 하위 프로퍼티에 접근할 수 있을줄 알았는데 그렇지 못 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 찾아보니 공식notion api를 통해서는 제가 원하는 content에 접근할 수 있는 방법은 없더라구요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-28 오후 12.24.51.png&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;97&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpmCLW/btslCliKqZK/8mKeaaKIwK6ha6XciLq3sK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpmCLW/btslCliKqZK/8mKeaaKIwK6ha6XciLq3sK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpmCLW/btslCliKqZK/8mKeaaKIwK6ha6XciLq3sK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpmCLW%2FbtslCliKqZK%2F8mKeaaKIwK6ha6XciLq3sK%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;451&quot; height=&quot;97&quot; data-filename=&quot;스크린샷 2023-06-28 오후 12.24.51.png&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;97&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-28 오후 12.25.51.png&quot; data-origin-width=&quot;454&quot; data-origin-height=&quot;288&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bm0Lhc/btslDFnthZW/Ihh0rbsAGZnx1gLXPoJw41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bm0Lhc/btslDFnthZW/Ihh0rbsAGZnx1gLXPoJw41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bm0Lhc/btslDFnthZW/Ihh0rbsAGZnx1gLXPoJw41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbm0Lhc%2FbtslDFnthZW%2FIhh0rbsAGZnx1gLXPoJw41%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;288&quot; data-filename=&quot;스크린샷 2023-06-28 오후 12.25.51.png&quot; data-origin-width=&quot;454&quot; data-origin-height=&quot;288&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;notion block&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;노션(Notion)의 API를 사용할 때 &quot;block&quot;은 중요한 개념입니다. 노션은 다양한 유형의 콘텐츠를 블록(block) 단위로 구성합니다. 블록은 텍스트, 이미지, 목록, 코드 등 다양한 형태의 콘텐츠를 나타내며, 이러한 블록들이 모여서 페이지를 구성합니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;노션의 블록 구조는 트리 형태로 되어 있습니다. 페이지는 최상위 블록이며, 이 아래에 다양한 블록 유형이 포함될 수 있습니다. 블록은 부모-자식 관계를 가지며, 자식 블록은 부모 블록에 속합니다. 이러한 구조를 통해 복잡한 콘텐츠 구성을 가능하게 합니다.&lt;/span&gt;&lt;/span&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;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 저희 홈페이지 내에서 마크다운된 content를 보여주고 싶었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;notionhq/client를 통해 page_id를 가져오고 그 아이디를 notion-to-md를 통해 마크다운하기를 하고 싶었으나 렌더링 문제, fetch 문제로 인해서 아래처럼 구현했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-28 오후 12.36.16.png&quot; data-origin-width=&quot;626&quot; data-origin-height=&quot;219&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SY0Of/btslCIxYQ1G/8B6UoB4Hrw8uiODSEPGeuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SY0Of/btslCIxYQ1G/8B6UoB4Hrw8uiODSEPGeuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SY0Of/btslCIxYQ1G/8B6UoB4Hrw8uiODSEPGeuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSY0Of%2FbtslCIxYQ1G%2F8B6UoB4Hrw8uiODSEPGeuk%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;626&quot; height=&quot;219&quot; data-filename=&quot;스크린샷 2023-06-28 오후 12.36.16.png&quot; data-origin-width=&quot;626&quot; data-origin-height=&quot;219&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;notionhq/client를 통해 title과 created_date, 해당 page_id를 가져올 수 있었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 공지사항 list를 구현하고 해당 상세 페이지는 page_id를 통해 해당 페이지로 이동할 수 있게 하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-06-28 오후 12.39.01.png&quot; data-origin-width=&quot;624&quot; data-origin-height=&quot;479&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNW7Wf/btslErCvKrJ/gEjkSzV7h7kfjkw1FZdwWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNW7Wf/btslErCvKrJ/gEjkSzV7h7kfjkw1FZdwWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNW7Wf/btslErCvKrJ/gEjkSzV7h7kfjkw1FZdwWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNW7Wf%2FbtslErCvKrJ%2FgEjkSzV7h7kfjkw1FZdwWK%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;624&quot; height=&quot;479&quot; data-filename=&quot;스크린샷 2023-06-28 오후 12.39.01.png&quot; data-origin-width=&quot;624&quot; data-origin-height=&quot;479&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;아무래도 노션 page로 이동하는 문제가 있다보니 해당 페이지 내에 &quot;공지사항 돌아가기&quot;, &quot;어바웃홈 돌아가기&quot; url을 삽입해서 사용자 편의성을 높이려고 했습니다.&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;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노션을 통해 채용페이지나 많은 페이지를 만드는걸 볼 수 있었는데 이건 notion-client와 react-notion-x을 통해 구현했겠구나 라는 생각이 들었습니다. 저는 제가 만든 페이지 내에서 해당 페이지 내에서 notion api를 호출한 데이터로 이리저리 사용하다보니 렌더링 문제, 성능 문제 외에도 많은 문제가 있었지만 다음에 노션 api를 사용해 페이지를 만든다면 &lt;b&gt;notion-client / react-notion-x 조합이나 notionhq/client / notion-to-md 조합&lt;/b&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;&lt;a href=&quot;https://blog.hwahae.co.kr/all/tech/10960&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://blog.hwahae.co.kr/all/tech/10960&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1687924161287&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;Notion API와 함께 정적 페이지로의 여정 &amp;ndash; 화해 블로그 | 기술 블로그&quot; data-og-description=&quot;Notion API와 함께 정적 페이지로의 여정 정적 페이지를 notion으로 관리하는 방법으로 접근하기까지의 과정을 약 3주 간 리서치한 과정을 글로 옮겼습니다. 과제 완성 후 최종 결과물을 공유하는 것&quot; data-og-host=&quot;blog-wp.hwahae.co.kr&quot; data-og-source-url=&quot;https://blog.hwahae.co.kr/all/tech/10960&quot; data-og-url=&quot;https://blog-wp.hwahae.co.kr/all/tech/10960/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/I7TLG/hyS8qx7djO/hdK1GiVh5ITfB4PVKxhXq0/img.jpg?width=1050&amp;amp;height=660&amp;amp;face=0_0_1050_660,https://scrap.kakaocdn.net/dn/hVQVt/hyS9L1Ihtz/LZCOSMgJ4gedZxAhlvCTg1/img.jpg?width=1520&amp;amp;height=2360&amp;amp;face=0_0_1520_2360,https://scrap.kakaocdn.net/dn/fceXJ/hyS8ypmoDj/SRoYgWbNzVgzhCWVqbSMF0/img.jpg?width=1520&amp;amp;height=1090&amp;amp;face=0_0_1520_1090&quot;&gt;&lt;a href=&quot;https://blog.hwahae.co.kr/all/tech/10960&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://blog.hwahae.co.kr/all/tech/10960&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/I7TLG/hyS8qx7djO/hdK1GiVh5ITfB4PVKxhXq0/img.jpg?width=1050&amp;amp;height=660&amp;amp;face=0_0_1050_660,https://scrap.kakaocdn.net/dn/hVQVt/hyS9L1Ihtz/LZCOSMgJ4gedZxAhlvCTg1/img.jpg?width=1520&amp;amp;height=2360&amp;amp;face=0_0_1520_2360,https://scrap.kakaocdn.net/dn/fceXJ/hyS8ypmoDj/SRoYgWbNzVgzhCWVqbSMF0/img.jpg?width=1520&amp;amp;height=1090&amp;amp;face=0_0_1520_1090');&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;Notion API와 함께 정적 페이지로의 여정 &amp;ndash; 화해 블로그 | 기술 블로그&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Notion API와 함께 정적 페이지로의 여정 정적 페이지를 notion으로 관리하는 방법으로 접근하기까지의 과정을 약 3주 간 리서치한 과정을 글로 옮겼습니다. 과제 완성 후 최종 결과물을 공유하는 것&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;blog-wp.hwahae.co.kr&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming/React.js, Next.js</category>
      <category>Notion API</category>
      <category>노션 api</category>
      <category>노션 블로그</category>
      <category>노션 블로그 만들기</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/85</guid>
      <comments>https://puenti.tistory.com/85#entry85comment</comments>
      <pubDate>Wed, 28 Jun 2023 12:50:04 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] 이벤트 루프(event loop) 란???</title>
      <link>https://puenti.tistory.com/83</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이 글을 읽기 전 참고하면 좋은 글&lt;br /&gt;&lt;a href=&quot;https://puenti.tistory.com/82&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;https://puenti.tistory.com/82&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;[JavaScript] 자바스크립트에서 heap, stack, queue란?&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;1. Heap(힙) Heap은 동적으로 할당되는 메모리 영역으로, 객체와 배열과 같은 복합 데이터 타입이 저장되는 곳입니다. Heap의 데이터는 크기가 동적으로 결정되기 때문에 메모리 공간이 필요한 만큼 &quot; data-og-host=&quot;puenti.tistory.com&quot; data-og-source-url=&quot;https://puenti.tistory.com/82&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/IXYvv/hySygIlNQD/KTai5etozh8xR9vJKWEd11/img.png?width=584&amp;amp;height=542&amp;amp;face=0_0_584_542,https://scrap.kakaocdn.net/dn/ckpRrn/hySymV5LnR/mfqkHxx3DggLoKFVlt1q3K/img.png?width=584&amp;amp;height=542&amp;amp;face=0_0_584_542,https://scrap.kakaocdn.net/dn/cCfCUs/hySykKKrbW/PJ4vmQG3KrNEuVShCISJy0/img.jpg?width=3024&amp;amp;height=3024&amp;amp;face=0_0_3024_3024&quot; data-og-url=&quot;https://puenti.tistory.com/82&quot;&gt;&lt;a href=&quot;https://puenti.tistory.com/82&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://puenti.tistory.com/82&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/IXYvv/hySygIlNQD/KTai5etozh8xR9vJKWEd11/img.png?width=584&amp;amp;height=542&amp;amp;face=0_0_584_542,https://scrap.kakaocdn.net/dn/ckpRrn/hySymV5LnR/mfqkHxx3DggLoKFVlt1q3K/img.png?width=584&amp;amp;height=542&amp;amp;face=0_0_584_542,https://scrap.kakaocdn.net/dn/cCfCUs/hySykKKrbW/PJ4vmQG3KrNEuVShCISJy0/img.jpg?width=3024&amp;amp;height=3024&amp;amp;face=0_0_3024_3024');&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;[JavaScript] 자바스크립트에서 heap, stack, queue란?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;1. Heap(힙) Heap은 동적으로 할당되는 메모리 영역으로, 객체와 배열과 같은 복합 데이터 타입이 저장되는 곳입니다. Heap의 데이터는 크기가 동적으로 결정되기 때문에 메모리 공간이 필요한 만큼&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;puenti.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이벤트&amp;nbsp;루프(event&amp;nbsp;loop)&amp;nbsp;란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이벤트 루프&lt;/b&gt;란 자바스크립트에서 가장 중요한 개념 중 하나 입니다.&lt;br /&gt;자바스크립트는 싱글 스레드 기반의 언어(인터프리터 언어)로서 한 가지 작업 밖에 못하는 언어적 한계를 가지고 있습니다.&lt;br /&gt;그러다보니 자바와 같은 컴파일 언어와 비교하면&lt;span style=&quot;color: #000000;&quot;&gt; 컴파일 하는 과정이 없기 때문에 컴파일 하는 시간은 소요되지 않으나, 인터프리터 언어는 실행파일을 별도로 생성하지 않기 때문에&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;실행시마다 인터프리트 과정이 반복 수행되어 실행 속도가 느리다&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;는 단점이 있습니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;이러한 단점을 상쇄시키기 위해 나온 개념이 바로 &lt;b&gt;이벤트 루프&lt;/b&gt;입니다.&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;코드의 실행이 차단(blocked)되는 동기(synchronous) 처리 방식으로 작성하면 다른 작업을 수행할 수 없습니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그러나 JavaScript에서는 &lt;b&gt;비동기 방식으로 처리&lt;/b&gt;를 해서 이러한 제한을 극복할 수 있습니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;이벤트 루프&lt;/b&gt;는 이러한 비동기성을 지원하는 방식 중 하나로,&lt;b&gt; 브라우저나 Node.js와 같은 JavaScript 실행환경에서 이벤트 처리를 담당&lt;/b&gt;합니다. 이벤트 루프는 이벤트 발생 시 해당 이벤트를 처리하기 위해 콜백(callback) 함수를 대기열(Queue)에 추가하고, 대기열(Queue)에서 이벤트를 꺼내와 실행합니다.&lt;/span&gt;&lt;br /&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;542&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bz1flP/btseddlNu3z/zcwe27uRJyKvupKGR1DUDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bz1flP/btseddlNu3z/zcwe27uRJyKvupKGR1DUDK/img.png&quot; data-alt=&quot;출처 - 자바스크립트 MDN&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bz1flP/btseddlNu3z/zcwe27uRJyKvupKGR1DUDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbz1flP%2FbtseddlNu3z%2Fzcwe27uRJyKvupKGR1DUDK%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;542&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;542&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 - 자바스크립트 MDN&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이벤트 루프는 크게 두 가지 단계로 구성됩니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;첫 번째 단계는&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; 이벤트 발생을 감지&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하고, 두 번째 단계는&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt; 대기열에서 콜백 함수를 꺼내와 실행&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하는 것입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. 이벤트 발생 감지는 브라우저와 Node.js일 경우로 나뉘게 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;브라우저의 경우 DOM 이벤트, 타이머 이벤트, XMLHttpRequest 이벤트 등을 감지하고, Node.js의 경우 I/O 작업, 타이머 이벤트 등을 감지합니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2. 대기열에서 콜백 함수를 꺼내와 실행할 때는 콜백 함수가 완료될 때까지 기다리지 않고, 다음 이벤트 발생을 감지하기 위해 바로 대기열로 돌아갑니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;간단한 코드로 이벤트 루프의 작동원리에 대해 알아보겠습니다&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;(function Test() {

&amp;nbsp;&amp;nbsp;console.log('시작');

&amp;nbsp;&amp;nbsp;setTimeout(function cb() {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;console.log('콜백 1: 콜백 메시지');
&amp;nbsp;&amp;nbsp;}); // has a default time value of 0

&amp;nbsp;&amp;nbsp;console.log('평범한 메시지');

&amp;nbsp;&amp;nbsp;setTimeout(function cb1() {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;console.log('콜백 2: 콜백 메시지');
&amp;nbsp;&amp;nbsp;}, 0);

&amp;nbsp;&amp;nbsp;console.log('종료');

})();

// &quot;시작&quot;
// &quot;평범한 메시지&quot;
// &quot;종료&quot;
// &quot;콜백 1: 콜백 메시지&quot;
// &quot;콜백 2: 콜백 메시지&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위 코드를 실행하면 &quot;시작&quot;&amp;rarr;&quot;콜백 1: 콜백 메시지&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;rarr;&quot;평범한 메시지&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;rarr;&quot;콜백 2: 콜백 메시지&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;rarr;&quot;종료&quot; 순서대로 출력되는 것이 아니라&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&quot;시작&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;rarr;&quot;평범한 메시지&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;rarr;&quot;종료&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;rarr;&quot;콜백1: 콜백 메시지&quot;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;rarr;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&quot;콜백2: 콜백 메시지&quot; 순서대로 출력이 됩니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이는 setTimeout 함수를 호출하면, 이벤트 루프에서는 콜백 함수를 이벤트 큐에 저장하고, 일정 시간이 지난 후에 이벤트 큐에서 콜백 함수를 꺼내 실행하기 때문입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_616B7738C2DD-1.jpeg&quot; data-origin-width=&quot;1635&quot; data-origin-height=&quot;994&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6DYPi/btserD4HKAR/K9ykkrsLFNHKF2s4expcdK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6DYPi/btserD4HKAR/K9ykkrsLFNHKF2s4expcdK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6DYPi/btserD4HKAR/K9ykkrsLFNHKF2s4expcdK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6DYPi%2FbtserD4HKAR%2FK9ykkrsLFNHKF2s4expcdK%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;1635&quot; height=&quot;994&quot; data-filename=&quot;IMG_616B7738C2DD-1.jpeg&quot; data-origin-width=&quot;1635&quot; data-origin-height=&quot;994&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;즉, &lt;b&gt;이벤트 루프에서 스택과 큐를 조합하여 비동기 작업을 처리&lt;/b&gt;하는데, 스택에 있는 작업이 모두 처리되면 이벤트 큐에서 대기 중인 작업을 하나씩 꺼내어 스택에 쌓아 처리합니다. 이렇게 함으로써 자바스크립트 엔진은 비동기 작업을 순서대로 처리하는 것이 아니라, 가능한 빠르게 처리할 수 있도록 구현되어 있습니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이렇게&lt;b&gt; 이벤트 루프를 통해 JavaScript는 비동기적으로 처리되는 코드들을 순서대로 실행하면서, 다른 작업을 수행할 수 있습니다.&lt;/b&gt; &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이를 통해 JavaScript는 효율적인 비동기 처리를 구현할 수 있습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>Programming/JavaScript</category>
      <category>event loop</category>
      <category>이벤트 루프란?</category>
      <category>이벤트루프</category>
      <category>자바스크립트 이벤트루프</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/83</guid>
      <comments>https://puenti.tistory.com/83#entry83comment</comments>
      <pubDate>Tue, 9 May 2023 08:03:34 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] 자바스크립트에서 heap, stack, queue란?</title>
      <link>https://puenti.tistory.com/82</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Heap(힙)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Heap은 동적으로 할당되는 메모리 영역으로, 객체와 배열과 같은 복합 데이터 타입이 저장되는 곳입니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Heap의 데이터는 크기가 동적으로 결정되기 때문에 메모리 공간이 필요한 만큼 동적으로 할당되고, 필요하지 않은 메모리는 &lt;b&gt;가비지 컬렉터(garbage collector)&lt;/b&gt;에 의해 자동으로 해제가 됩니다.&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;/span&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;span style=&quot;color: #000000; text-align: start;&quot;&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;할당된 메모리를 사용 (읽기, 쓰기)&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;더 이상 필요하지 않으면 해제&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Stack(스택)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Stack은 정적으로 할당되는 메모리 영역으로, 함수 호출 시 함수 내의 변수, 매개변수, 복귀 주소 등의 정보가 저장되는 곳입니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;함수가 호출될 때마다 스택 프레임(stack frame)이 생성되고, 함수가 종료될 때마다 스택 프레임이 제거됩니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;이를 스택(stack)이라고 부릅니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Queue(큐)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;Queue는 자바스크립트에서는 일반적으로 메모리 구조로 사용되지 않지만, 자바스크립트에서 비동기(asynchronous) 처리를 위해 사용되는 자료 구조입니다. 비동기 처리에서는 작업을 처리하는 순서가 보장되지 않기 때문에, Queue를 사용하여 작업을 순차적으로 처리합니다. 작업이 Queue에 추가되면, 이벤트 루프(event loop)가 이를 처리하기 위해 대기하다가, 이전 작업이 완료되면 Queue에서 다음 작업을 가져와 실행합니다.&lt;/span&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;&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;542&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfOnMF/btseddlKkdl/jj92sRZ9XKiVVlKZyDMjL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfOnMF/btseddlKkdl/jj92sRZ9XKiVVlKZyDMjL0/img.png&quot; data-alt=&quot;출처 - 자바스크립트 MDN&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfOnMF/btseddlKkdl/jj92sRZ9XKiVVlKZyDMjL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfOnMF%2FbtseddlKkdl%2Fjj92sRZ9XKiVVlKZyDMjL0%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;542&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;542&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 - 자바스크립트 MDN&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;이러한 &lt;b&gt;Heap, Stack, Queue&lt;/b&gt;는 자바스크립트의 동작 방식과 메모리 관리를 이해하는 데 매우 중요합니다. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;특히 &lt;b&gt;Heap&lt;/b&gt;과 &lt;b&gt;Stack&lt;/b&gt;은 &lt;b&gt;자바스크립트 엔진의 메모리 관리 방식&lt;/b&gt;을 이해하는 데 중요하며, &lt;b&gt;Queue&lt;/b&gt;는 &lt;b&gt;비동기 처리와 이벤트 처리&lt;/b&gt;를 이해하는 데 중요합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming/JavaScript</category>
      <category>자바스크립트 heap</category>
      <category>자바스크립트 heap stack queue</category>
      <category>자바스크립트 queue</category>
      <category>자바스크립트 stack</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/82</guid>
      <comments>https://puenti.tistory.com/82#entry82comment</comments>
      <pubDate>Mon, 8 May 2023 15:24:15 +0900</pubDate>
    </item>
    <item>
      <title>[알고리즘] 재귀함수란? 재귀함수로 팩토리얼과 피보나치 수열 구현 / For문과의 차이</title>
      <link>https://puenti.tistory.com/81</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;재귀함수란??&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;재귀함수(Recursive Function)는 자신이 수행하는 함수 내에서 자기 자신을 다시 호출하여 작업을 수행하는 함수를 말합니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이러한 재귀적 호출을 통해 복잡한 문제를 간단하게 해결할 수 있습니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;재귀함수로 팩토리얼 구현&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;재귀 함수의 대표적 예시인, 팩토리얼 함수를 구현해보겠습니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;팩토리얼 함수는 양의 정수 n에 대해 n!을 구하는 함수이며, n!은 1부터 n까지의 정수를 모두 곱한 값입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function factorial(n) {
&amp;nbsp;&amp;nbsp;if (n === 0) {&amp;nbsp;&amp;nbsp;// n이 0일 때, 1을 반환합니다.
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 1;
&amp;nbsp;&amp;nbsp;} else {&amp;nbsp;&amp;nbsp;// n이 0이 아닐 때, 자신을 다시 호출합니다.
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return n * factorial(n-1);
&amp;nbsp;&amp;nbsp;}
}

console.log(factorial(5));&amp;nbsp;&amp;nbsp;// 5! = 5 * 4 * 3 * 2 * 1 = 120을 출력합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위 코드에서 factorial 함수는 자신이 받은 인자 n이 0일 때, 1을 반환합니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그 외의 경우에는 n과 factorial(n-1)의 곱을 반환합니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 때 factorial(n-1)은 factorial 함수를 자신보다 작은 인자에 대해 다시 호출한 결과를 의미합니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;피보나치 수열 구현&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;피보나치 수열의 재귀적 정의는 다음과 같습니다.&lt;/span&gt;&lt;br /&gt;&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;&lt;span style=&quot;color: #000000;&quot;&gt;피보나치 수열의 0번째 항은 0입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;피보나치 수열의 1번째 항은 1입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;n (n&amp;gt;=2)번째 항은 (n-1)번째 항과 (n-2)번째 항의 합입니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function fibonacci(n) {
  if (n === 0 || n === 1) {
    // n이 0이나 1인 경우, 그냥 n을 반환한다.
    return n;
  } else {
    // n이 2 이상인 경우, n-1번째 피보나치 수와 n-2번째 피보나치 수의 합을 반환한다.
    return fibonacci(n-1) + fibonacci(n-2);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위 코드에서 &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;fibonacci&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt; 함수는 인자 &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;n&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;번째 피보나치 수열의 값을 반환합니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;n&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 0이거나 1인 경우에는 그대로 반환하고, 그 외의 경우에는 &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(n-1)&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;번째 항과 &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(n-2)&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;번째 항을 더한 값을 반환합니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이렇게 재귀적으로 정의된 피보나치 수열을 재귀함수를 이용하여 구현할 수 있습니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이렇게 &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;재귀적으로 함수를 호출하여 작업을 수행하는 것이 재귀함수의 핵심적인 개념&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;입니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;재귀함수는 복잡한 알고리즘을 간단하게 구현할 수 있지만, 재귀적 호출이 너무 깊어지면 스택 오버플로우( Stack Overflow ) 에러가 발생할 수 있으므로 주의가 필요합니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;For문과의 차이&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;재귀함수와 for문은 모두 반복적인 작업을 수행할 때 사용되는 제어문&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;입니다. 그러나 두 제어문은 다른 방식으로 동작합니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;for문은 초기값, 조건식, 증감식을 이용하여 반복 횟수를 제어합니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;예를 들어, 0부터 4까지의 숫자를 출력하는 for문은 다음과 같이 작성할 수 있습니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;for (let i = 0; i &amp;lt; 5; i++) {
&amp;nbsp;&amp;nbsp;console.log(i);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;반면에, 재귀함수는 자기 자신을 다시 호출하여 작업을 수행합니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;예를 들어, 1부터 n까지의 합을 구하는 재귀함수는 다음과 같이 작성할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function sum(n) {
&amp;nbsp;&amp;nbsp;if (n === 1) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 1;
&amp;nbsp;&amp;nbsp;} else {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return n + sum(n-1);
&amp;nbsp;&amp;nbsp;}
}

console.log(sum(5));&amp;nbsp;&amp;nbsp;// 1 + 2 + 3 + 4 + 5 = 15를 출력합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위 코드에서 sum 함수는 자신이 받은 인자 n이 1일 때, 1을 반환합니다. 그 외의 경우에는 n과 sum(n-1)의 합을 반환합니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 때 sum(n-1)은 sum 함수를 자신보다 작은 인자에 대해 다시 호출한 결과를 의미합니다. &lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이렇게 재귀적으로 함수를 호출하여 작업을 수행하는 것이 재귀함수의 핵심적인 개념입니다.&lt;/span&gt;&lt;br /&gt;&lt;b&gt;그래서 &lt;/b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;재귀함수는 함수를 반복적으로 호출하기 때문에 스택 메모리를 사용&lt;/b&gt;&lt;/span&gt;&lt;b&gt;하는데 (스택 오버플로우가 발생할 수 있습니다. )&lt;/b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;반면 &lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;반복문은 메모리 힙을 사용&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;합니다&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;따라서 for문과 재귀함수는 반복적인 작업을 수행할 때 사용되지만, &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;for문은 반복횟수를 제어하는 것에 초점을 두고, &lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;재귀함수는 자기 자신을 호출하여 작업을 수행하는 것에 초점을 두고 동작합니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>CS (Computer Science)/알고리즘</category>
      <category>알고리즘</category>
      <category>재귀함수</category>
      <category>재귀함수 For문</category>
      <category>재귀함수 알고리즘</category>
      <category>재귀함수 피보나치 수열</category>
      <category>피보나치 수열 구현</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/81</guid>
      <comments>https://puenti.tistory.com/81#entry81comment</comments>
      <pubDate>Wed, 3 May 2023 13:59:11 +0900</pubDate>
    </item>
    <item>
      <title>[React] useState란? 여러 개의 state 변수 사용하기</title>
      <link>https://puenti.tistory.com/78</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;useState란?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;리액트의 useState는 함수형 컴포넌트에서 상태를 관리하기 위한 Hook 중 하나입니다. &lt;/span&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;span style=&quot;color: #000000;&quot;&gt;Hook&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;은 React 16.8버전에 새로 추가되었습니다. Hook은 클래스 컴포넌트를 작성하지 않아도 state와 같은 특징들을 사용할 수 있습니다. 또한&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;Hook은 함수형 컴포넌트에서 상태를 지속적으로 추적하고 업데이트할 수 있도록 도와줍니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;useState를 사용하면, 컴포넌트에서 변경 가능한 상태를 정의하고, 해당 상태가 변경될 때마다 리액트가 컴포넌트를 다시 렌더링하도록 설정할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;useState는 다음과 같이 사용합니다&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1682308202184&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useState } from 'react';

function Example() {
  // useState를 사용해 &quot;count&quot; 상태를 초기값 &quot;0&quot;으로 설정합니다.
  const [count, setCount] = useState(0);

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p&amp;gt;You clicked {count} times&amp;lt;/p&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; setCount(count + 1)}&amp;gt;
        Click me
      &amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위 코드에서, useState를 사용하여 count라는 상태를 초기값 0으로 설정합니다. 이 상태를 변경하려면, setCount 함수를 사용합니다. setCount 함수는 새로운 값을 받아 해당 상태를 업데이트하고, 리액트에게 컴포넌트를 다시 렌더링하도록 알립니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위 예제에서는 &quot;Click me&quot; 버튼을 클릭할 때마다 count 상태가 증가하고, 이를 화면에 출력합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;useState를 사용하면 여러 개의 상태를 관리할 수도 있으며, 객체나 배열과 같은 복잡한 데이터 구조를 다룰 수도 있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;또한, 이 Hook을 사용하여 상태 값이 변경될 때 추가적인 동작을 수행하도록 설정할 수도 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt; 예를 들어, useEffect Hook을 사용하여 특정 상태가 변경될 때마다 API 호출을 수행하도록 설정할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여러 개의 state 변수&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;[something, setSomething]&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;의 쌍처럼 state 변수를 선언하는 것은 유용합니다. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;왜냐하면 여러 개의 변수를 선언할 때 각각 다른 이름을 줄 수 있기 때문입니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1682308372089&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function ExampleWithManyStates() {
  // 여러 개의 state를 선언할 수 있습니다!
  const [age, setAge] = useState(42);
  const [fruit, setFruit] = useState('banana');
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);&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;span style=&quot;color: #000000; text-align: start;&quot;&gt;위의 코드는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;age&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;fruit&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;todos&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;라는 지역 변수를 가지며 개별적으로 갱신할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1682308387083&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function handleOrangeClick() {
    // this.setState({ fruit: 'orange' })와 같은 효과를 냅니다.
    setFruit('orange');
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;여러 개의 state 변수를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;사용하지 않아도 됩니다.&lt;/b&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;state 변수는 객체와 배열을 잘 가지고 있을 수 있으므로 서로 연관있는 데이터를 묶을 수 있습니다. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;하지만 클래스 컴포넌트의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;this.setState&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;와 달리 state를 갱신하는 것은 병합하는 것이 아니라&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;대체&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;하는 것입니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming/React.js, Next.js</category>
      <category>react</category>
      <category>react useState</category>
      <category>useState</category>
      <category>useState란?</category>
      <category>리액트</category>
      <category>리액트 useState</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/78</guid>
      <comments>https://puenti.tistory.com/78#entry78comment</comments>
      <pubDate>Mon, 24 Apr 2023 12:54:06 +0900</pubDate>
    </item>
    <item>
      <title>[React] State와 Props란???</title>
      <link>https://puenti.tistory.com/77</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;리액트를 하다보면 State와 Props에 대해 많이 나오는데 오늘은 이것에 대해 간단하게 정리해볼까 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;State란?&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;부모 컴포넌트에서 자녀 컴포넌트로 데이터를 보내는게 아닌 해당 컴포넌트 내부에서 데이터를 전달하는 방법!&lt;br /&gt;예를 들어, 검색 창에 입력할때 글이 변하는 것은 state로 바꾸게 됩니다.&lt;/li&gt;
&lt;li&gt;State는 변경 가능한 mutable 값입니다.&lt;/li&gt;
&lt;li&gt;State가 변하면 re-render가 됩니다.&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;State는 해당 컴포넌트에서 useState라는 React hooks를 통해 사용가능합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 코드로 설명을 하면 아래와 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1681880852140&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useState } from 'react';

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

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

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;div&amp;gt;Count: {count}&amp;lt;/div&amp;gt;
      &amp;lt;button onClick={handleClick}&amp;gt;Click me&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;위의 코드에서는 useState라는 Hook을 사용하여 상태를 정의하고 있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;useState는 상태 값과 상태를 변경하는 함수를 반환합니다. 첫 번째 인자는 초기값이며, 여기서는 0으로 설정되어 있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;상태 값을 count 변수에 할당하고, 상태를 변경하는 함수를 setCount 변수에 할당하고 있습니다.&lt;/span&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;span style=&quot;color: #000000; text-align: start;&quot;&gt;handleClick 함수는 setCount를 호출하여 count 값을 증가시키고 있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;그리고, 버튼을 클릭할 때 handleClick 함수가 실행되도록 onClick 이벤트를 등록하고 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React hook의 useState 부분은 나중에 포스팅으로 더 자세히 적어보도록 하겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;props란?&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;props는 properties를 줄인 표현으로 컴포넌트 속성을 설정할 때 사용하는 요소!&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;props 값은 해당 컴포넌트를 불러와 사용하는 부모 컴포넌트에서 설정할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Props는 읽기 전용인 immutable 값으로 자녀 컴포넌트 입장에서는 변하지 않습니다.&amp;nbsp;&lt;br /&gt;변하게 하려면 부모 컴포넌트의 state를 변경시켜야 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1681881010640&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function MyComponent(props) {
  return &amp;lt;div&amp;gt;Hello, {props.name}!&amp;lt;/div&amp;gt;;
}

&amp;lt;MyComponent name=&quot;John&quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 코드에서는 name이라는 Props를 MyComponent에 전달하고 있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;MyComponent에서는 props를 인자로 받아와서 name을 읽어와 &quot;Hello, John!&quot;이라는 문구를 출력하고 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Props는 다양한 자료형을 가질 수 있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;예를 들어, 문자열(String), 숫자(Number), 객체(Object), 배열(Array) 등을 Props로 전달할 수 있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;또한, 컴포넌트 자체도 Props로 전달할 수 있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이를 통해 컴포넌트에서 다른 컴포넌트를 렌더링하거나 조건부 렌더링을 할 수 있습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>Programming/React.js, Next.js</category>
      <category>react</category>
      <category>react props</category>
      <category>react props란</category>
      <category>react state</category>
      <category>react state란</category>
      <category>리액트 상태</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/77</guid>
      <comments>https://puenti.tistory.com/77#entry77comment</comments>
      <pubDate>Wed, 19 Apr 2023 14:11:35 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] 비구조화 할당이란 (분해 구조화) / 배열, 객체에서의 비구조화 할당</title>
      <link>https://puenti.tistory.com/76</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;비구조화 할당이란?&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;리액트를 공부하다 보면 비구조화 할당이란 단어가 많이 보입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;b&gt;비구조화 할당(Destructuring assignment)&lt;/b&gt;은 객체나 배열에서 데이터를 추출하여 변수나 상수로 개별적으로 할당하는 문법으로, 배열에서도 동일하게 사용할 수 있습니다. 이때 배열에서 비구조화 할당을 사용하는 것을 배열 분해 구조(Array destructuring)라고 부릅니다.&lt;/span&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;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;객체에서의 비구조화 할당&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;객체에서 비구조화 할당을 사용하면 객체의 프로퍼티를 추출하여 변수나 상수로 할당할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1681394467281&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const { 프로퍼티1, 프로퍼티2, ... } = 객체;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같이 객체를 생성하고, 해당 객체의 프로퍼티를 변수로 할당하는 코드를 작성할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1681394530435&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const person = {
  name: 'John',
  age: 30,
  gender: 'male'
};

const { name, age } = person;

console.log(name); // 'John'
console.log(age); // 30&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;이 예제에서는 &lt;/span&gt;person&lt;span style=&quot;text-align: start;&quot;&gt; 객체의 &lt;/span&gt;name&lt;span style=&quot;text-align: start;&quot;&gt; 프로퍼티와 &lt;/span&gt;age&lt;span style=&quot;text-align: start;&quot;&gt; 프로퍼티를 추출하여 &lt;/span&gt;name&lt;span style=&quot;text-align: start;&quot;&gt;과 &lt;/span&gt;age&lt;span style=&quot;text-align: start;&quot;&gt; 변수로 할당합니다. &lt;/span&gt;&lt;/span&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;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;비구조화 할당을 사용할 때 프로퍼티 이름을 변경하여 할당할 수 있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;이때는 다음과 같이 콜론(&lt;/span&gt;:&lt;span style=&quot;text-align: start;&quot;&gt;)과 함께 새로운 이름을 지정합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1681394670273&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const { 프로퍼티1: 새이름1, 프로퍼티2: 새이름2, ... } = 객체;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1681394686995&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const person = {
  name: 'John',
  age: 30,
  gender: 'male'
};

const { name: personName, age: personAge } = person;

console.log(personName); // 'John'
console.log(personAge); // 30&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start; color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;이 예제에서는 &lt;/span&gt;person&lt;span style=&quot;text-align: start;&quot;&gt; 객체의 &lt;/span&gt;name&lt;span style=&quot;text-align: start;&quot;&gt; 프로퍼티를 &lt;/span&gt;personName&lt;span style=&quot;text-align: start;&quot;&gt; 변수로, &lt;/span&gt;age&lt;span style=&quot;text-align: start;&quot;&gt; 프로퍼티를 &lt;/span&gt;personAge&lt;span style=&quot;text-align: start;&quot;&gt; 변수로 할당합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;text-align: start; color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;기본값 설정&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;비구조화 할당을 사용할 때 해당 프로퍼티가 존재하지 않을 경우 기본값을 설정할 수 있습니다. 이때는 다음과 같이 등호(&lt;/span&gt;=&lt;span style=&quot;text-align: start;&quot;&gt;)와 함께 기본값을 지정합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1681394747600&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const { 프로퍼티1 = 기본값1, 프로퍼티2 = 기본값2, ... } = 객체;&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;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;예를 들어 다음과 같이 객체를 생성하고, 해당 객체의 프로퍼티를 변수로 할당하는 코드를 작성할 수 있습니다&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;배열에서의 비구조화 할당&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1681394883648&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const [ 요소1, 요소2, ... ] = 배열;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1681394895064&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const fruits = ['apple', 'banana', 'orange'];

const [ first, second ] = fruits;

console.log(first); // 'apple'
console.log(second); // 'banana'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;이 예제에서는 &lt;/span&gt;fruits&lt;span style=&quot;text-align: start;&quot;&gt; 배열의 첫 번째 요소(&lt;/span&gt;'apple'&lt;span style=&quot;text-align: start;&quot;&gt;)와 두 번째 요소(&lt;/span&gt;'banana'&lt;span style=&quot;text-align: start;&quot;&gt;)를 추출하여 &lt;/span&gt;first&lt;span style=&quot;text-align: start;&quot;&gt;와 &lt;/span&gt;second&lt;span style=&quot;text-align: start;&quot;&gt; 변수로 할당합니다.&lt;/span&gt;&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;요소 건너뛰기&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;배열에서 비구조화 할당을 사용할 때 특정 요소를 건너뛸 수도 있습니다. 이때는 건너뛸 요소 수만큼 쉼표(&lt;/span&gt;,&lt;span style=&quot;text-align: start;&quot;&gt;)를 사용합니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1681394935794&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const [ 요소1, , 요소3, ... ] = 배열;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1681394944779&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const fruits = ['apple', 'banana', 'orange', 'grape'];

const [ first, , third ] = fruits;

console.log(first); // 'apple'
console.log(third); // 'orange'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;이 예제에서는 fruits 배열의 첫 번째 요소('apple')와 세 번째 요소('orange')를 추출하여 first와 third 변수로 할당합니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;기본값 설정&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;배열에서 비구조화 할당을 사용할 때 해당 요소가 존재하지 않을 경우 기본값을 설정할 수 있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이때는 다음과 같이 등호(=)와 함께 기본값을 지정합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1681394999783&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const [ 요소1 = 기본값1, 요소2 = 기본값2, ... ] = 배열;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1681395008530&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const fruits = ['apple', 'banana'];

const [ first, second, third = 'orange' ] = fruits;

console.log(first); // 'apple'
console.log(second); // 'banana'
console.log(third); // 'orange'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;이 예제에서는 &lt;/span&gt;fruits&lt;span style=&quot;text-align: start;&quot;&gt; 배열의 첫 번째 요소(&lt;/span&gt;'apple'&lt;span style=&quot;text-align: start;&quot;&gt;)와 두 번째 요소(&lt;/span&gt;'banana'&lt;span style=&quot;text-align: start;&quot;&gt;)를 추출하여 &lt;/span&gt;first&lt;span style=&quot;text-align: start;&quot;&gt;와 &lt;/span&gt;second&lt;span style=&quot;text-align: start;&quot;&gt; 변수로 할당하고, &lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;세 번째 요소(&lt;/span&gt;'orange'&lt;span style=&quot;text-align: start;&quot;&gt;)는 존재하지 않기 때문에 기본값(&lt;/span&gt;'orange'&lt;span style=&quot;text-align: start;&quot;&gt;)이 할당됩니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;나머지 요소 추출하기&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;배열에서 비구조화 할당을 사용할 때 나머지 요소를 추출하여 새로운 배열로 할당할 수도 있습니다. 이때는 ... 연산자를 사용합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1681395084545&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const [ 요소1, 요소2, ..., 요소N ] = 배열;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1681395093114&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const fruits = ['apple', 'banana', 'orange', 'grape'];

const [ first, ...rest ] = fruits;

console.log(first); // 'apple'
console.log(rest); // [ 'banana', 'orange', 'grape' ]&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;이 예제에서는 &lt;/span&gt;fruits&lt;span style=&quot;text-align: start;&quot;&gt; 배열의 첫 번째 요소(&lt;/span&gt;'apple'&lt;span style=&quot;text-align: start;&quot;&gt;)를 추출하여 &lt;/span&gt;first&lt;span style=&quot;text-align: start;&quot;&gt; 변수로 할당하고, 나머지 요소(&lt;/span&gt;'banana'&lt;span style=&quot;text-align: start;&quot;&gt;, &lt;/span&gt;'orange'&lt;span style=&quot;text-align: start;&quot;&gt;, &lt;/span&gt;'grape'&lt;span style=&quot;text-align: start;&quot;&gt;)를 새로운 배열로 할당하여 &lt;/span&gt;rest&lt;span style=&quot;text-align: start;&quot;&gt; 변수에 저장합니다.&lt;/span&gt;&lt;/span&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;&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;</description>
      <category>Programming/JavaScript</category>
      <category>destructuring assignment</category>
      <category>비구조할당이란</category>
      <category>자바스크립트 구조 분해</category>
      <category>자바스크립트 문법</category>
      <category>자바스크립트 비구조할당</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/76</guid>
      <comments>https://puenti.tistory.com/76#entry76comment</comments>
      <pubDate>Thu, 13 Apr 2023 23:13:25 +0900</pubDate>
    </item>
    <item>
      <title>[React] component 란?? 함수형 컴포넌트, 클래스형 컴포넌트</title>
      <link>https://puenti.tistory.com/75</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;component 란??&lt;/span&gt;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;component&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;리액트는 컴포넌트 기반 아키텍처를 사용하여 UI를 구성합니다. 컴포넌트는 UI의 작은 조각을 나타내며, 다른 컴포넌트와 조합하여 복잡한 UI를 만들 수 있습니다. 이러한 컴포넌트 기반 아키텍처는 코드의 재사용성과 유지 보수성을 높여주는 장점을 가지고 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;리액트 컴포넌트는 함수형 컴포넌트와 클래스형 컴포넌트로 나눌 수 있습니다.&lt;/span&gt;&lt;/span&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;/h2&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_1680542124243&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function App() {
	const name = &quot;리액트&quot;;
    return &amp;lt;div className=&quot;react&quot;&amp;gt; {name} &amp;lt;/div&amp;gt;
}

export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;함수형 컴포넌트는 코드가 간결하고 읽기 쉽다는 장점이 있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;또한, 클래스형 컴포넌트에 비해 메모리 사용량이 적고 성능이 더 우수합니다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;함수형 컴포넌트는 상태(state)를 가질 수 없으며, 라이프사이클 API를 사용할 수 없습니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 단점은 리액트 v16.8 업데이트 이후 Hooks라는 기능이 도입되면서 해결되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;함수형 컴포넌트는 React Hooks를 사용하여 상태를 유지할 수 있습니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;React Hooks는 함수형 컴포넌트에서 상태를 유지하고, 이벤트 핸들러를 추가하고, 라이프사이클 메서드를 호출하는 등의 작업을 수행할 수 있도록 해줍니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;※ 라이프사이클 메서드는 컴포넌트가 생성, 갱신, 제거되는 과정에서 호출되는 메서드입니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;클래스형 컴포넌트&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1680542161086&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;importReact,{Component}from'react';

class App extends Component {
	render() {
    	constname = 'react';
        return &amp;lt;div className=&quot;react&quot;&amp;gt; {name} &amp;lt;/div&amp;gt;;  
		}
	}
exportdefaultApp;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;클래스형 컴포넌트는 state(상태)와 lifecycle methods(라이프사이클 메서드)를 사용하여 UI를 렌더링하고 상호작용합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;state는 컴포넌트 내에서 유지되는 데이터&lt;/b&gt;를 의미하며, 이를 변경하면 컴포넌트가 다시 렌더링됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;클래스형 컴포넌트는 render() 메서드를 사용하여 UI를 반환합니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 메서드는 JSX(JavaScript XML)를 사용하여 UI를 정의할 수 있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;또한, 클래스형 컴포넌트는 props(속성)를 전달받을 수 있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;props는 부모 컴포넌트에서 전달되는 데이터&lt;/b&gt;를 의미합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;클래스형 컴포넌트는 state와 라이프사이클 메서드를 사용하여 복잡한 로직을 구현할 수 있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그러나 클래스형 컴포넌트는 코드가 길고 복잡해지기 쉽고, 작성하기 어렵다는 단점이 있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;또한, 클래스형 컴포넌트는 함수형 컴포넌트보다 메모리 사용량이 많아 성능이 떨어지기도 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start; color: #000000;&quot;&gt;함수형 컴포넌트를 사용할지 클래스형 컴포넌트를 사용할지는 선택사항입니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start; color: #000000;&quot;&gt;그러나&lt;b&gt; 최근 리액트에서는 함수형 컴포넌트를 권장하고, 함수형 컴포넌트를 사용하면 코드를 더욱 간결하고 읽기 쉽게 작성할 수 있습니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; 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>Programming/React.js, Next.js</category>
      <category>react component</category>
      <category>리액트</category>
      <category>리액트 컴포넌트</category>
      <category>리액트 클래스형 컴포넌트</category>
      <category>리액트 함수형 컴포넌트</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/75</guid>
      <comments>https://puenti.tistory.com/75#entry75comment</comments>
      <pubDate>Wed, 5 Apr 2023 18:02:24 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] 화살표 함수(Arrow function)란 ?? 화살표 함수에서 this 바인딩</title>
      <link>https://puenti.tistory.com/74</link>
      <description>&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;화살표&amp;nbsp;함수(Arrow&amp;nbsp;function)란&amp;nbsp;??&amp;nbsp;&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;자바스크립트의 &lt;b&gt;화살표 함수(arrow function)&lt;/b&gt;는 함수를 선언하는 다른 방법 중 하나입니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;ES6(2015)에서 소개되었으며, 일반 함수보다 간결하고 가독성이 높으며, this 바인딩에 대한 동작 방식이 다릅니다.&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;화살표 함수는 다음과 같은 구문으로 선언됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1680246425621&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(parameter1, parameter2, ..., parameterN) =&amp;gt; { 
   // function body 
};&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #d1d5db; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여기서&lt;span&gt;&amp;nbsp;&lt;/span&gt;parameter1, parameter2, ..., parameterN는 함수에 전달할 매개변수이고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;{}&lt;span&gt;&amp;nbsp;&lt;/span&gt;안에 있는&lt;span&gt;&amp;nbsp;&lt;/span&gt;function body는 함수가 실행될 때 수행할 코드입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;화살표 함수는 다음과 같은 특징을 가집니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;함수의 매개변수가 하나뿐이면 괄호를 생략할 수 있습니다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;함수의 코드 블록이 한 줄이면 중괄호와&lt;span&gt;&amp;nbsp;&lt;/span&gt;return을 생략할 수 있습니다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;함수 내부에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;this는 상위 스코프의&lt;span&gt;&amp;nbsp;&lt;/span&gt;this를 가리킵니다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;생성자 함수로 사용할 수 없습니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #000000; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;아래는 예시 코드입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1680246481268&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 기존 함수 선언 방식
function double(x) {
  return x * 2;
}

// 화살표 함수 선언 방식
const double = (x) =&amp;gt; x * 2;

// 함수 내부 코드 블록이 한 줄일 경우
const double = (x) =&amp;gt; x * 2;

// 매개변수가 하나인 경우 괄호 생략 가능
const double = x =&amp;gt; x * 2;

// 상위 스코프의 this를 가리키는 예시
const obj = {
  value: 1,
  printValue: function() {
    setTimeout(() =&amp;gt; {
      console.log(this.value);
    }, 1000);
  }
};&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;화살표 함수에서 this 바인딩에 대한 동작 방식&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 통해 화살표 함수는 어떻게 this바인딩이 되는지 확인을 해보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1680246625476&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;functionBlackDog() {  
	this.name ='흰둥이';  
    return {
    	name:'검둥이',
        bark: function() {
        	console.log(this.name +':멍멍!');
            }  
        }
    } 
    
 const blackDog = newBlackDog();
 blackDog.bark(); //검둥이:멍멍! 
 
 
 functionWhiteDog() {  
 	this.name ='흰둥이';  
    return {
    	name:'검둥이',
        bark:() =&amp;gt; {
        	console.log(this.name +':멍멍!');    
        }  
     }
 } 
 
 const whiteDog = newWhiteDog();
 whiteDog.bark(); //흰둥이:멍멍!&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;function()을 사용했을 때는 검둥이가 나타나고, ()=&amp;gt;를 사용했을 때는 흰둥이가 나타납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;일반 함수는 자신이 종속된 객체를 가리키며, 화살표 함수는 자신이 종속된 인스턴스를 가리킵니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;화살표 함수는 값을 연산하여 바로 반환해야 할 때 사용하면 가독성을 높일 수 있습니다.&lt;/b&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;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;또한&amp;nbsp;&lt;span style=&quot;text-align: start;&quot;&gt;화살표 함수는 일반 함수보다 간단하고 가독성이 좋아서, 콜백 함수나 배열의 &lt;/span&gt;map&lt;span style=&quot;text-align: start;&quot;&gt;, &lt;/span&gt;filter&lt;span style=&quot;text-align: start;&quot;&gt;, &lt;/span&gt;reduce&lt;span style=&quot;text-align: start;&quot;&gt;와 같은 고차 함수 등에서 자주 사용됩니다.&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>Programming/JavaScript</category>
      <category>arrow function</category>
      <category>javascript arrow function</category>
      <category>자바스크립트 화살표 함수</category>
      <category>화살표 함수</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/74</guid>
      <comments>https://puenti.tistory.com/74#entry74comment</comments>
      <pubDate>Fri, 31 Mar 2023 16:14:50 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] blocking / non-blocking, 동기 / 비동기</title>
      <link>https://puenti.tistory.com/73</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서 blocking과 non-blocking, 동기와 비동기는 프로그램의 실행과 관련된 중요한 개념입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Blocking vs Non-blocking&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Blocking&lt;/b&gt;은 특정 코드의 실행이 완료될 때까지 다음 코드의 실행을 중지시키는 것을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 코드 실행 방식은 호출된 함수가 작업을 수행할 때까지 스크립트의 실행을 차단(block)시키기 때문에, 다른 작업이나 이벤트를 처리하는 것을 방해할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Blocking이 발생하는 경우에는 대개 시스템 리소스를 많이 사용하거나 시간이 오래 걸리는 작업을 수행하는 경우입니다.&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;Non-blocking&lt;/b&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;Non-blocking은 대개 비동기적인 작업을 수행하는 경우에 발생합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Synchronous vs Asynchronous&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Synchronous(동기)&lt;/b&gt;는 코드가 실행되는 순서대로 실행되는 것을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 함수가 호출되면 그 함수의 작업이 완료될 때까지 스크립트의 실행이 차단되며, 이후에 다음 코드가 실행됩니다. 이러한 코드 실행 방식은 대개 Blocking과 함께 발생합니다.&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;Asynchronous(비동기)&lt;/b&gt;는 코드가 실행되는 순서와는 상관없이 실행되는 것을 의미합니다. &lt;br /&gt;예를 들어, 비동기 함수가 호출되면 그 함수의 작업이 완료될 때까지 스크립트의 실행이 차단되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신, 비동기 함수가 완료되면 이벤트 루프(event loop)를 통해 콜백 함수가 실행됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, Blocking/Non-blocking과 Synchronous/Asynchronous는 각각 다른 의미를 가지며, 자바스크립트에서 이들을 효율적으로 사용할 수 있어야 합니다.&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;span style=&quot;color: #374151;&quot;&gt;아래 코드는 Blocking과 Non-blocking, 그리고 Synchronous와 Asynchronous의 차이를 보여주는 간단한 예시입니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1680109988397&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Blocking and Synchronous
function blockingSync() {
  console.log(&quot;Start blockingSync function&quot;);
  const result = 1 + 2;
  console.log(&quot;Result: &quot; + result);
  console.log(&quot;End blockingSync function&quot;);
}
blockingSync();

// Non-blocking and Asynchronous
function nonBlockingAsync() {
  console.log(&quot;Start nonBlockingAsync function&quot;);
  setTimeout(() =&amp;gt; {
    const result = 3 + 4;
    console.log(&quot;Result: &quot; + result);
  }, 2000);
  console.log(&quot;End nonBlockingAsync function&quot;);
}
nonBlockingAsync();

// Non-blocking and Asynchronous with Callback
function nonBlockingAsyncWithCallback(callback) {
  console.log(&quot;Start nonBlockingAsyncWithCallback function&quot;);
  setTimeout(() =&amp;gt; {
    const result = 5 + 6;
    console.log(&quot;Result: &quot; + result);
    callback(result);
  }, 2000);
  console.log(&quot;End nonBlockingAsyncWithCallback function&quot;);
}
nonBlockingAsyncWithCallback((result) =&amp;gt; {
  console.log(&quot;Callback Result: &quot; + result);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 blockingSync() 함수는 Blocking과 Synchronous한 작업을 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수가 실행되면 1+2의 결과를 계산하고, 그 결과를 출력한 후, 함수의 끝을 알리는 메시지를 출력합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 함수의 실행이 완료될 때까지 다음 코드의 실행이 중지되기 때문에, &quot;Start nonBlockingAsync function&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;반면, nonBlockingAsync() 함수는 Non-blocking과 Asynchronous한 작업을 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수가 실행되면, setTimeout() 함수를 호출하여 2초 후에 3+4의 결과를 계산하고 출력하는 작업을 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 함수의 실행이 차단되지 않기 때문에, &quot;End nonBlockingAsync function&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;마지막으로, nonBlockingAsyncWithCallback() 함수는 Non-blocking과 Asynchronous하면서도 Callback 함수를 사용하여 결과를 처리합니다. 함수가 실행되면, setTimeout() 함수를 호출하여 2초 후에 5+6의 결과를 계산하고, 콜백 함수를 호출하여 결과를 전달합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 콜백 함수가 실행되기 전에 &quot;End nonBlockingAsyncWithCallback function&quot; 메시지가 출력되기 때문에, 다른 작업을 수행할 수 있습니다.&lt;/p&gt;</description>
      <category>Programming/JavaScript</category>
      <category>자바스크립트 blocking</category>
      <category>자바스크립트 non-blocking</category>
      <category>자바스크립트 동기</category>
      <category>자바스크립트 비동기</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/73</guid>
      <comments>https://puenti.tistory.com/73#entry73comment</comments>
      <pubDate>Thu, 30 Mar 2023 02:43:36 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] async / await 와 promise란??</title>
      <link>https://puenti.tistory.com/72</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;async&amp;nbsp;/&amp;nbsp;await&amp;nbsp;와&amp;nbsp;promise란??&lt;/h2&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;span&gt;※&amp;nbsp;비동기 프로그래밍 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;특정 작업이 완료될 때까지 코드 실행을 멈추지 않고, 다른 작업을 동시에 수행할 수 있는 방식을 의미&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;자바스크립트에서 비동기 프로그래밍을 구현하는 대표적인 방법으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;1&quot;&gt;콜백(callback), 프로미스(promise), 비동기 함수(async/await)&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;가 있습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;이전 포스팅에 콜백에 대한 설명이 있으므로 짧게 설명하자면 이렇습니다.&lt;/span&gt;&lt;/span&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콜백은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;함수의 인자로 다른 함수를 전달하여, 작업이 완료될 때 해당 함수를 호출하는 방식&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, setTimeout 함수를 사용하여 일정 시간이 지난 후에 함수를 호출할 수 있습니다.&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;a title=&quot;콜백함수란?&quot; href=&quot;https://puenti.tistory.com/71&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://puenti.tistory.com/71&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;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;비동기 작업이 완료될 때 결과값을 반환하는 객체&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로미스는 비동기 작업을 수행하는 함수에서 반환되며, 이후에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;.then() 메서드를 사용하여 작업이 완료될 때 실행할 함수를 등록&lt;/b&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;비동기 함수(async/await)는 ES2017에서 도입된 기능으로,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;async 키워드로 함수를 선언하고, 내부에서 await 키워드를 사용하여 비동기 작업이 완료될 때까지 기다릴 수 있습니다&lt;/b&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;먼저 promise입니다.&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;promise(프로미스)란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;프로미스(Promise)는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;1&quot;&gt;비동기 작업을 다루기 위한 자바스크립트 객체&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;입니다.&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;프로미스는 비동기 작업의 성공 또는 실패와 같은 결과를 나타내는 객체입니다. 일반적으로 프로미스는 다음과 같은 세 가지 상태를 가집니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대기(pending): 작업이 완료되지 않은 초기 상태&lt;/li&gt;
&lt;li&gt;이행(fulfilled): 작업이 성공적으로 완료된 상태&lt;/li&gt;
&lt;li&gt;거부(rejected): 작업이 실패한 상태&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로미스 객체를 생성할 때는 콜백 함수를 인자로 받습니다. 이 콜백 함수는 두 개의 인자를 가지는데,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;하나는 이행(resolve)이 되었을 때 실행되는 함수이고, 다른 하나는 거부(reject)되었을 때 실행되는 함수&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로미스 객체를 사용하는 방법은 다음과 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1679980406332&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const myPromise = new Promise((resolve, reject) =&amp;gt; {
  // 비동기 작업 수행
  // 작업이 완료되면 resolve 또는 reject를 호출하여 결과 전달
});

myPromise
  .then(result =&amp;gt; {
    // 작업이 성공적으로 완료된 경우 실행되는 코드
  })
  .catch(error =&amp;gt; {
    // 작업이 실패한 경우 실행되는 코드
  });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 myPromise는 비동기 작업을 수행하는 프로미스 객체입니다. 이 객체를 사용할 때는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;then&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;메서드와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;catch&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;메서드를 사용합니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;then&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;메서드는 작업이 성공적으로 완료된 경우 실행되는 콜백 함수를 등록합니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;catch&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&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;h2 data-ke-size=&quot;size26&quot;&gt;async / await란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;async는 함수의 앞에 붙이며 해당 함수가 비동기적으로 실행될 것임을 명시합니다. 이 함수는 반드시 Promise 객체를 반환해야합니다. Promise는 비동기적으로 실행되는 작업이 완료되면 값을 반환하는 객체입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;await는 Promise가 처리될 때까지 함수의 실행을 일시 중지합니다. 이는 일반적으로 Promise가 처리되기를 기다린 다음, 결과 값을 반환하고 다음 작업을 계속 진행하게 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1679980493926&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async function getData() {
  const response = await fetch('https://api.example.com/data');
  const data = await response.json();
  return data;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;fetch()&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수를 사용하여 비동기적으로 데이터를 가져오고, **await**를 사용하여 데이터가 성공적으로 반환될 때까지 기다립니다. 그런 다음&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;json()&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;메서드를 사용하여 응답을 JSON 형식으로 파싱하고, 파싱된 데이터를 반환합니다. 이렇게 작성된 코드는 가독성이 높아지며, 콜백함수를 사용하는 경우보다 훨씬 간결하고 유지보수하기 쉬워집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나, await는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;async&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수 내에서만 사용될 수 있으며, 기본적으로 동기적인 코드에서는 사용할 수 없습니다. 따라서 비동기적인 작업을 처리해야 하는 함수 내에서만 async와 await를 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming/JavaScript</category>
      <category>await async</category>
      <category>비동기처리란</category>
      <category>자바스크립트</category>
      <category>자바스크립트 async</category>
      <category>자바스크립트 await</category>
      <category>자바스크립트 비동기</category>
      <category>자바스크립트 프로미스</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/72</guid>
      <comments>https://puenti.tistory.com/72#entry72comment</comments>
      <pubDate>Tue, 28 Mar 2023 14:16:47 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] 클로저(Closure)와 콜백함수(callback)</title>
      <link>https://puenti.tistory.com/71</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;클로저(Closure)란???&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;함수 내부에서 외부 스코프에 선언된 변수를 참조하는 것을 클로저(Closure)&lt;/b&gt;라고 합니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;클로저는 함수와 함수가 선언된 어휘적 환경(Lexical Environment)의 조합입니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;클로저는 함수가 선언될 당시의 환경을 기억하고, 함수가 호출될 때 그 환경을 다시 만들어내는 기능을 합니다.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이를 통해 함수 안에서 선언한 변수를 함수 외부에서도 사용할 수 있게 됩니다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;클로저는 자바스크립트에서 매우 중요한 개념으로, &lt;b&gt;다른 함수에 인자로 함수를 전달하거나&lt;/b&gt;, 객체의 메서드로 함수를 정의할 때 클로저를 사용하여 특정한 상태를 유지할 수 있습니다. 클로저를 사용하면 상태를 보존하고, 함수를 더 유연하고 재사용 가능하게 만들 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;또한, 클로저는 자바스크립트에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;비동기 처리를 할 때 매우 유용하게 사용됩니다. &lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;클로저를 이용하여 비동기 함수에서 비동기 처리가 완료되었을 때 실행해야 하는 콜백 함수에 필요한 상태 정보를 전달할 수 있습니다&lt;/b&gt;.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이를 통해 비동기 함수 내부의 변수를 외부에서도 참조하고, 상태를 보존할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그러나, 클로저를 사용할 때는 메모리 누수(Memory Leak)에 유의해야 합니다. 클로저는 함수가 반환될 때 함수를 선언한 Lexical Environment를 계속 유지하기 때문에, 메모리 사용량이 늘어날 수 있습니다. 따라서, 필요하지 않은 클로저는 적극적으로 해제해야 합니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;콜백(callback)이란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;b&gt;콜백(callback) 함수는 다른 함수의 인자로 전달되어, 특정 이벤트나 비동기적인 작업이 끝나면 호출되는 함수&lt;/b&gt;입니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;즉, 콜백 함수는 특정 함수의 작업 결과를 처리하는 작업을 수행합니다. 자바스크립트에서는 비동기적인 작업이 많기 때문에 콜백 함수가 매우 중요한 역할을 합니다. 예를 들어, setTimeout 함수는 콜백 함수를 사용합니다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;클로저와 콜백은 자바스크립트에서 비동기 처리를 위한 매우 중요한 개념이라 볼 수 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;클로저를 사용하면 함수 내에서 선언된 변수를 함수 외부에서 액세스할 수 있으므로, 이를 이용하여 콜백 함수를 생성할 수 있습니다. 이러한 콜백 함수는 비동기 작업이 완료되면 호출되어 결과를 처리하는 데 사용됩니다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;&lt;b&gt;이러한 특징 때문에 클로저와 콜백은 항상 같이 실행될거라는 착각을 할 수 있습니다.&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;&lt;b&gt;클로저와 콜백은 항상 같이 실행이 될까???&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&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;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;클로저는 함수가 정의될 때 함수 내부에서 선언된 변수를 포함하여 외부 스코프의 변수에 접근할 수 있도록 하는 기술입니다. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;클로저를 이용하면 함수를 리턴하거나 다른 함수의 인자로 전달할 때, 내부 변수를 계속 유지하면서 사용할 수 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;반면에 콜백은 비동기적인 처리를 위해 사용되는 함수를 말합니다. 콜백은 함수를 인자로 전달하고, 처리가 완료된 후에 이 함수를 호출하여 결과를 전달합니다. 이를 이용하여 비동기적인 작업을 수행하거나 이벤트를 처리하는 등의 작업을 처리할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;물론, 클로저와 콜백은 함께 사용되는 경우가 많기 때문에 서로 연관되어 있는 것처럼 느껴질 수 있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;예를 들어, 비동기적으로 처리된 결과를 콜백 함수에서 사용해야 하는 경우에 클로저를 이용하여 결과를 유지할 수 있습니다. 그러나 이는 클로저와 콜백이 서로 다른 개념임을 보여주는 예시일 뿐입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;따라서, 클로저와 콜백은 함께 움직이는 경우가 많을뿐 항상 같이 실행되지는 않습니다!&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&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;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;개념적 설명은 이까지하고 이해를 돕기 위한 코드를 통해 확인해보겠습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;다음은 &lt;b&gt;클로저와 콜백을 함께 사용&lt;/b&gt;하여 비동기적으로 데이터를 처리하는 예시 코드입니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1678955067982&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function counter() {
  let count = 0;
  return function() {
    count++;
    console.log(count);
  }
}

const increment = counter();

function delayedIncrement(callback) {
  setTimeout(function() {
    increment();
    callback();
  }, 1000);
}

delayedIncrement(function() {
  console.log(&quot;increment completed&quot;);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;이 예제 코드에서는 counter 함수가 정의됩니다. 이 함수는 클로저를 사용하여 count 변수를 내부에 가지고 있습니다. &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;이후 counter 함수가 반환하는 함수가 increment 변수에 할당됩니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;delayedIncrement 함수는 콜백 함수를 인자로 받으며, 1초 후에 increment 함수를 호출한 후, 전달된 콜백 함수를 실행합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;따라서, increment 함수가 클로저를 이용하여 count 변수에 접근하여 값이 증가하고 콘솔에 출력됩니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그리고 delayedIncrement 함수가 실행되면서 1초 후에 increment 함수가 호출되고, 다시 delayedIncrement 함수의 콜백 함수가 실행되어 &quot;increment completed&quot; 메시지가 콘솔에 출력됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이렇게 클로저와 콜백 함수를 함께 사용하면 비동기적으로 데이터를 처리하거나 이벤트를 처리하는 등 다양한 상황에서 유용하게 사용할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;b&gt;클로저 없이 콜백만 사용하는 예시&lt;/b&gt;로는 Node.js에서 자주 사용되는 콜백 패턴이 있습니다. 이를 통해 비동기적으로 작업을 수행할 수 있습니다. 예를 들어, 다음은 Node.js의 파일 시스템 모듈을 사용하여 파일을 읽는 코드입니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1678955693634&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const fs = require('fs');

fs.readFile('example.txt', 'utf8', function(err, data) {
  if (err) throw err;
  console.log(data);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 코드에서는 fs.readFile() 함수를 호출하고, 파일 이름과 인코딩 방식을 전달합니다. 그리고 마지막 인자로 콜백 함수를 전달합니다. 이 함수는 파일 읽기 작업이 완료되면 호출되며, 첫 번째 인자로는 오류가 발생한 경우 해당 오류를 전달하고, 두 번째 인자로는 파일 내용을 전달합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 콜백 함수를 사용하여 파일을 읽은 후, 파일 내용을 출력하고 있습니다. 이 코드에서는 클로저를 사용하지 않았고, 대신에 비동기적으로 작업을 수행하기 위해 콜백 패턴을 사용하고 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #d1d5db; text-align: start;&quot; 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;</description>
      <category>Programming/JavaScript</category>
      <category>Callback</category>
      <category>close callback</category>
      <category>Closure</category>
      <category>자바스크립트 콜백함수</category>
      <category>자바스크립트 클로저</category>
      <category>콜백함수</category>
      <category>클로저</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/71</guid>
      <comments>https://puenti.tistory.com/71#entry71comment</comments>
      <pubDate>Thu, 16 Mar 2023 17:36:34 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] this란? /this 바인딩이란</title>
      <link>https://puenti.tistory.com/70</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;this란??&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;JavaScript에서 this는 &lt;b&gt;현재 실행 중인 함수 내에서 참조하는 객체&lt;/b&gt;를 나타냅니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;this는 호출되는 방법에 따라 달라지며, 함수가 호출되는 방법에 따라 다른 객체가 this를 가리킵니다.&lt;/b&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;this는 자바스크립트의 중요한 개념 중 하나이며, 오해하거나 이해하지 못하면 코드에서 버그를 일으킬 수 있습니다.&lt;/span&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;this 바인딩&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수를 호출하는 방법에는 네 가지가 있습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;일반 함수로 호출하는 경우&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;메소드로 호출하는 경우&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;apply() 또는 call() 메소드를 사용하여 호출하는 경우&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;생성자 함수로 객체를 생성하는 경우&lt;/b&gt;&lt;/li&gt;
&lt;/ol&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;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;함수를 일반적인 방법으로 호출하는 경우, this는 전역 객체를 가리킵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저에서는 window 객체, Node.js에서는 global 객체가 전역 객체입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1678003078867&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function greet() {
  console.log(this);
}

greet(); // window (브라우저) 또는 global (Node.js)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&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;메소드는 객체 내부에서 정의된 함수입니다. 메소드 내부에서 this를 사용하면 해당 메소드를 호출한 객체를 가리킵니다.&lt;/p&gt;
&lt;pre id=&quot;code_1678003121751&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const person = {
  name: 'Alice',
  greet: function() {
    console.log(`Hello, my name is ${this.name}`);
  }
};

person.greet(); // &quot;Hello, my name is Alice&quot;를 출력합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위 예제에서 greet 메소드 내부에서 this는 person 객체를 가리킵니다.&lt;/span&gt;&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;3. apply() 또는 call() 메소드를 사용하여 호출하는 경우&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;apply() 또는 call() 메소드를 사용하여 함수를 호출하는 경우, this는 해당 메소드의 첫 번째 인수로 전달된 객체를 가리킵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;apply()와 call()의 차이점은 인수를 전달하는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;apply()는 배열로 인수를 전달하고, call()은 쉼표로 구분된 인수를 전달합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1678003174570&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function greet() {
  console.log(`Hello, my name is ${this.name}`);
}

const person = {
  name: 'Alice'
};

greet.apply(person); // &quot;Hello, my name is Alice&quot;를 출력합니다.
greet.call(person); // &quot;Hello, my name is Alice&quot;를 출력합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위 예제에서 apply()와 call() 메소드를 사용하여 greet 함수를 호출하고 있습니다. 이 경우 this는 person 객체를 가리킵니다.&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. 생성자 함수로 객체를 생성하는 경우&lt;/b&gt;&lt;/h4&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;생성자 함수는 객체를 생성하기 위해 사용되는 함수로, new 연산자를 사용하여 호출됩니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;생성자 함수 내에서 this 키워드를 사용하여 새로 생성된 객체에 접근할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1678003364883&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;

  this.getMake = function() {
    return this.make;
  };

  this.getModel = function() {
    return this.model;
  };

  this.getYear = function() {
    return this.year;
  };
}

var car1 = new Car(&quot;Toyota&quot;, &quot;Corolla&quot;, 2019);
var car2 = new Car(&quot;Honda&quot;, &quot;Accord&quot;, 2020);

console.log(car1.getMake()); // 출력 결과: Toyota
console.log(car2.getModel()); // 출력 결과: Accord&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드에서 Car 생성자 함수는 자동차의 제조사, 모델, 연식을 인자로 받아서 객체를 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;this 키워드를 사용하여 생성된 객체의 속성을 설정하고, getMake(), getModel(), getYear() 메서드를 정의합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 new 연산자를 사용하여 Car 생성자 함수를 호출하면, 새로운 객체가 생성되고 this는 해당 객체를 참조하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 생성된 객체는 car1, car2 변수에 저장되고, getMake(), getModel(), getYear() 메서드를 호출할 수 있습니다.&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;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;this 키워드를 올바르게 이해하면, 객체 지향 프로그래밍에서 메서드를 작성하고 객체 간 상호작용을 구현하는 등의 작업에서 매우 유용합니다. 따라서 JavaScript를 사용하여 프로그래밍할 때는 this의 개념과 동작 방식에 대해 이해하고 활용할 수 있도록 해야 합니다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;</description>
      <category>Programming/JavaScript</category>
      <category>자바스크립트 this</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/70</guid>
      <comments>https://puenti.tistory.com/70#entry70comment</comments>
      <pubDate>Sun, 5 Mar 2023 17:07:35 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] call by value(값에 의한 호출) vs call by reference(참조에 의한 호출) 차이는?</title>
      <link>https://puenti.tistory.com/69</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;call&amp;nbsp;by&amp;nbsp;value(값에&amp;nbsp;의한&amp;nbsp;호출)&amp;nbsp;vs&amp;nbsp;call&amp;nbsp;by&amp;nbsp;reference(참조에&amp;nbsp;의한&amp;nbsp;호출) 차이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서는 원시 타입(primitive type)의 값은 call by value(값에 의한 호출)로 전달되고, 객체 타입(object type)은 call by reference(참조에 의한 호출)로 전달됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;call by value(값에 의한 호출)은 변수에 저장된 값을 복사해서 전달하는 방식입니다. 예를 들어, 다음과 같이 숫자를 전달하는 경우를 생각해봅시다.&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;span style=&quot;letter-spacing: 0px;&quot;&gt;숫자(number),&amp;nbsp;&lt;/span&gt;문자열(string),&amp;nbsp;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;불리언(boolean),&amp;nbsp;&lt;/span&gt;null,&amp;nbsp;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;undefined,&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;심볼(symbol) - &lt;span style=&quot;color: #000000;&quot;&gt;각각의 원시 타입은 값(value)을 갖고 있으며, 이 값은 변경될 수 없습니다(immutable)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1677771464557&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let x = 10;

function changeValue(val) {
  val = 20;
}

changeValue(x);
console.log(x); // 10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제에서는 함수 changeValue 에 x 값을 전달했는데, 이때 x 값의 복사본이 val 매개변수에 전달됩니다. changeValue 함수 안에서 val 값이 변경되어도, x 변수의 값은 그대로 10으로 유지됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면에 &lt;b&gt;call by reference(참조에 의한 호출)는 객체의 메모리 주소를 전달하는 방식&lt;/b&gt;입니다. 예를 들어, 다음과 같이 객체를 전달하는 경우를 생각해봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1677771481289&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let obj = { value: 10 };

function changeValue(obj) {
  obj.value = 20;
}

changeValue(obj);
console.log(obj.value); // 20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제에서는 함수 changeValue 에 obj 객체를 전달했는데, 이때 obj 객체의 메모리 주소가 obj 매개변수에 전달됩니다. changeValue 함수 안에서 obj 객체의 value 프로퍼티를 변경하면, 원본 obj 객체의 value 프로퍼티도 함께 변경됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 자바스크립트에서는 객체 타입을 다룰 때 주의해야 합니다. &lt;b&gt;객체를 전달하는 경우, 객체의 복사본이 아닌 객체의 참조가 전달되기 때문에 함수 안에서 객체를 수정하면 전역에서도 객체가 변경될 수 있습니다.&lt;/b&gt;&lt;/p&gt;</description>
      <category>Programming/JavaScript</category>
      <category>CallByReference</category>
      <category>CallByValue</category>
      <category>값에의한호출</category>
      <category>참조에의한호출</category>
      <category>콜바이레퍼런스</category>
      <category>콜바이밸류</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/69</guid>
      <comments>https://puenti.tistory.com/69#entry69comment</comments>
      <pubDate>Fri, 3 Mar 2023 00:43:00 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] var, let, const의 차이점 / 스코프 / 변수 호이스팅</title>
      <link>https://puenti.tistory.com/68</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;var,&amp;nbsp;let,&amp;nbsp;const의 차이를 알기 전에..&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 var, let, const 차이를 알기 전에 &lt;b&gt;스코프&lt;/b&gt;에 대해서 알아야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스코프&lt;/b&gt;란 간단하게 변수를 사용할 수 있는 위치를 의미합니다.&amp;nbsp;&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;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScript에서는 &lt;b&gt;전역 스코프(Global Scope)&lt;/b&gt;와 &lt;b&gt;지역 스코프(Local Scope)&lt;/b&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;함수 내에서 선언된 변수는 함수 외부에서는 접근할 수 없습니다.&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;h2 data-ke-size=&quot;size26&quot;&gt;var, let, const의 차이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트는 ES5 이전까진 var라는 변수를 사용했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;var 키워드로 선언한 변수는 중복선언이 가능하고 전역 스코프로 인정되어 동일한 이름의 변수가 이미 선언되어 있는걸 모르고 변수를 중복 선언하면 먼저 선언된 변수 값이 변경되는 부작용(&lt;b&gt;변수 호이스팅&lt;/b&gt;)이 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 단점을 보완하고자 ES6에서 let, const의 새로운 변수 선언 키워드가 나왔습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;let과 const는 모든 코드 블럭(함수, if문, for문, try/catch문 등)을 지역 스코프로 인정하는 블록 레벨 스코프를 따른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;let은 중복 선언 불가, 재할당이 가능하며, const는 중복 선언 불가, 재할당 불가하다.&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: 98.023256%; height: 130px;&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: 18px;&quot;&gt;
&lt;td style=&quot;width: 33.333333%; height: 18px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; height: 18px; text-align: center;&quot;&gt;스코프&lt;/td&gt;
&lt;td style=&quot;width: 16.666667%; height: 18px; text-align: center;&quot;&gt;변수 선언&lt;/td&gt;
&lt;td style=&quot;width: 16.666667%; height: 18px; text-align: center;&quot;&gt;변수 할당&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 33.333333%; height: 18px; text-align: center;&quot;&gt;var&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; height: 18px; text-align: center;&quot;&gt;전역 스코프&lt;/td&gt;
&lt;td style=&quot;width: 16.666667%; height: 18px; text-align: center;&quot;&gt;중복 선언 가능&lt;/td&gt;
&lt;td style=&quot;width: 16.666667%; height: 18px; text-align: center;&quot;&gt;재할당 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 33.333333%; height: 18px; text-align: center;&quot;&gt;let&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; height: 18px; text-align: center;&quot;&gt;블록 스코프&lt;/td&gt;
&lt;td style=&quot;width: 16.666667%; height: 18px; text-align: center;&quot;&gt;중복 선언 불가&lt;/td&gt;
&lt;td style=&quot;width: 16.666667%; height: 18px; text-align: center;&quot;&gt;재할당 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 33.333333%; height: 18px; text-align: center;&quot;&gt;const&lt;/td&gt;
&lt;td style=&quot;width: 33.333333%; height: 18px; text-align: center;&quot;&gt;블록 스코프&lt;/td&gt;
&lt;td style=&quot;width: 16.666667%; height: 18px; text-align: center;&quot;&gt;중복 선언 불가&lt;/td&gt;
&lt;td style=&quot;width: 16.666667%; height: 18px; text-align: center;&quot;&gt;재할당 불가&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;변수 호이스팅이란??&lt;/h2&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;pre id=&quot;code_1677585119103&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(x); // undefined가 출력됩니다.
x = 10; // 변수에 값을 할당합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수 x가 선언되기 전에 변수를 참조하면, 해당 변수는 undefined로 초기화됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 동작 방식 때문에, 변수 호이스팅은 코드의 실행 결과를 예측하기 어렵게 만들 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ES6 이후에는 let과 const 키워드를 사용하여 변수를 선언하면, 변수 호이스팅이 발생하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 가능하면 var 키워드를 사용하지 않고 let과 const 키워드를 사용하여 변수를 선언하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론, var는 쓰지말자!!&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이 글을 읽고 관련된 아래의 내용도 찾아보면 좋습니다!&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;/li&gt;
&lt;li&gt;TDZ (Temporal DeadZone)&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Programming/JavaScript</category>
      <category>var let const</category>
      <category>변수호이스팅</category>
      <category>자바스크립트 var</category>
      <category>자바스크립트 개념</category>
      <category>자바스크립트 변수</category>
      <category>자바스크립트 변수호이스팅</category>
      <category>자바스크립트 스코프</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/68</guid>
      <comments>https://puenti.tistory.com/68#entry68comment</comments>
      <pubDate>Tue, 28 Feb 2023 21:01:26 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Lombok이란? / Lombok의 사용 이유 / Lombok의 단점 / Lombok 사용법</title>
      <link>https://puenti.tistory.com/67</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Lombok이란??&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복되는 메서드를 Annotation을 사용하여 자동으로 작성해주는 라이브러리입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 DTO, VO, Model, Entity 등의 데이터 클래스에서 많이 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로 많이 사용되는 Annotation&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;@Getter&lt;/li&gt;
&lt;li&gt;@Setter&lt;/li&gt;
&lt;li&gt;@NoArgConstructor&lt;/li&gt;
&lt;li&gt;@AllArgConstructor&lt;/li&gt;
&lt;li&gt;@Data&lt;/li&gt;
&lt;li&gt;@ToString&lt;/li&gt;
&lt;li&gt;@Builder&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Lombok의 사용이유&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Annotation 기반의 코드 자동생성으로 인한 생산성이 증가합니다.&lt;/li&gt;
&lt;li&gt;반복코드를 줄일 수 있어 유지보수와 가독성을 향상시킬 수 있습니다.&lt;/li&gt;
&lt;li&gt;Getter / Setter 외 Builder 패턴이나 Log 생성 등의 다양한 방면으로 활용이 가능합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Lombok의 단점&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Lombok을 지원하는 IDE는 IntelliJ와 Eclipse 2개입니다.&lt;/li&gt;
&lt;li&gt;자바의 컴파일러에 강하게 결합되어 있는 한계가 있습니다. 컴파일어의 수정은 공개 API를 많이 사용하는데 컴파일러를 업그레이드 하게 되면 깨질 수도 있습니다.&lt;/li&gt;
&lt;li&gt;Lombok 사용시, 모든 계층이 Lombok에 의존하게 됩니다.&lt;/li&gt;
&lt;li&gt;모든 사람이 Lombok plugin이 필요합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Lombok 사용법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pom.xml에 의존성 주입해줍니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;dependencies&amp;gt; 태그 안에 &amp;lt;dependency&amp;gt;를 넣어주면 됩니다&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;dependencies&amp;gt;
...
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;org.projectlombok&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;lombok&amp;lt;/artifactId&amp;gt;
		&amp;lt;optional&amp;gt;true&amp;lt;/optional&amp;gt;
	&amp;lt;/dependency&amp;gt;
...
&amp;lt;/dependencies&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;@Getter / @Setter&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 클래스에 선언되어 있는 필드를 기반으로 &amp;lsquo;getField&amp;rsquo; , &amp;lsquo;setField&amp;rsquo; 와 같은 식으로 자동으로 메소드 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;xxx라는 필드에 선언하면 자동으로&amp;nbsp;getXxx()으로 만들어주고 만약(boolean 타입인 경우 isXxx()와&amp;nbsp;setXxx()&amp;nbsp;메소드를 생성해줍니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;@NoArgsConstructor / @AllArgsConstructor / @RequiredArgsConstructor&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@NoArgsConstructor : 파라미터가 없는 생성자를 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@AllArgsConstructor : 모든 필드값을 파라미터로 갖는 생성자를 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@RequiredArgsConstructor : 필드값 중 final이나 @NotNull인 값을 갖는 생성자를 생성&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;@ToString&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;toString 메소드를 작성해주는 Annotation&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@ToString 어노테이션에 exclude 속성을 사용하여 특정 필드를 toString에서 제외시킬 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;@EqualsAndHashCode&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바 빈을 만들 때&amp;nbsp;equals와&amp;nbsp;hashCode&amp;nbsp;메소드를 자주 오버라이딩 하는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@EqualsAndHashCode 어노테이션을 사용하면 자동으로 이 메소드를 생성할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;callSuper 속성을 통해 equals와 hashCode&amp;nbsp;메소드 자동 생성 시&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부모 클래스의 필드까지 감안할지 안 할지에 대해서 설정할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;equals : 두 객체의 내용이 같은지 동등성(equality)를 비교하는 연산자
hashCode : 두 객체가 같은 객체인지 동일성(identity)를 비교하는 연산자
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&amp;nbsp;callSuper = true로 설정하면 부모 클래스 필드 값들도 동일한지 체크하며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;callSuper = false로 설정(기본값)하면 자신 클래스의 필드 값들만 고려합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;@Data&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Data는 위에서 설명한 @Getter, @Setter, @RequiredArgsConstructor, @ToString, @EqualsAndHashCode를 한꺼번에 설정해주는 매우 유용한 어노테이션입니다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;@Builder&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Builder를 사용하면 객체 생성이 명확해집니다. 가급적 클래스 보다는 직접 만든 생성자 혹은 static 객체 생성 메소드에 붙이는 것을 권장합니다. @Builder를 붙이면 파라미터 순서가 아닌 이름으로 값을 설정하기 때문에 리팩토링에 유연하게 대응할 수 있고, 필드 순서를 변경해도 문제가 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지가 자주 쓰이는 Lombok Annotation 입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 Annotation도 있으니 참고 부탁드립니다&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;@NonNull&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동으로 null 체크를 진행하여 null 인 경우 NullPointerException&amp;nbsp;을 발생 시킵니다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;@Cleanup&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동으로 자원을 관리합니다.&amp;nbsp;close()&amp;nbsp;메소드를 호출하여 자원을&amp;nbsp;종료시킵니다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;@Value&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불변 클래스를 쉽게 생성해 줍니다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;@SneakyThrows&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Exception 발생시 체크된 Throable로 감싸서 전달해줍니다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;@Synchronized&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메소드에서 동기화 Lock을 설정을 해줍니다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;@Log&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종류별 로그를 사용할 수 있도록 해줍니다. (@Log, @Slf4j, @CommonLog 등)등등.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;b&gt;@Accessors&lt;/b&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기본 생성된 setter 들은 return type 이 void 이므로 method chaining 을 사용할 수 없습니다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;**@Accessors에 chain=true 파라미터를 사용하면 생성되는 setter 의 리턴 타입이 this 로 변경됩니다&lt;/p&gt;</description>
      <category>Programming/Java, Spring</category>
      <category>lombok</category>
      <category>롬복</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/67</guid>
      <comments>https://puenti.tistory.com/67#entry67comment</comments>
      <pubDate>Fri, 14 Oct 2022 13:00:36 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] AOP란?? / AOP를 사용하는 이유</title>
      <link>https://puenti.tistory.com/66</link>
      <description>&lt;h1&gt;AOP(Aspect Oriented Programming)&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AOP(Aspect Oriented Programming)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 여러 오브젝트에 나타나는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;공통적인 부가 기능&lt;/b&gt;을 모듈화하여&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;재사용&lt;/b&gt;하는 기법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;관심사의 분리(기능의 분리), 핵심적인 기능에서 부가적인 기능을 분리&lt;/b&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;rarr; OOP를 보완한다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AOP를 사용하는 이유&lt;/h2&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;각 모듈에 수정이 필요하면 다른 모듈의 수정 없이 해당 로직만 변경&lt;/li&gt;
&lt;li&gt;공통 로직을 적용할 대상을 선택 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;99F4E5475C722F6C09.png&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;433&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buQwII/btrN5E3pEs6/Riz486OjSE1aKCgM2XTYu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buQwII/btrN5E3pEs6/Riz486OjSE1aKCgM2XTYu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buQwII/btrN5E3pEs6/Riz486OjSE1aKCgM2XTYu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuQwII%2FbtrN5E3pEs6%2FRiz486OjSE1aKCgM2XTYu0%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;433&quot; data-filename=&quot;99F4E5475C722F6C09.png&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;433&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;부가기능(인프라 로직)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;부가기능을 Aspect 또는 advice로 정의함&lt;/li&gt;
&lt;li&gt;중복코드를 만들어낼 가능성 때문에 유지보수가 힘들어짐&lt;/li&gt;
&lt;li&gt;비즈니스 로직과 함께 있으면 비즈니스 로직을 이해하기 어려워짐&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 그래서 분리가 필요&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AOP 용어&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;용어설명&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 118px;&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: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;target&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;advice가 추가될 객체&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;advice&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;targer에 동적으로 추가될 부가 기능(코드)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;join point&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;advice가 추가(join)될 대상(메서드)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;pointcut&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;join point들을 정의한 패턴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;proxy&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;target에 advice가 동적으로 추가되어 생성된 객체&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;weaving&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;target에 advice를 추가해서 proxy를 생성하는 것&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Advice 종류&lt;/h2&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&gt;kind&lt;/td&gt;
&lt;td&gt;Annotation&lt;/td&gt;
&lt;td&gt;desc&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;around advice&lt;/td&gt;
&lt;td&gt;@Around&lt;/td&gt;
&lt;td&gt;메서드의 시작과 끝 부분에 추가되는 부가 기능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;before advice&lt;/td&gt;
&lt;td&gt;@Before&lt;/td&gt;
&lt;td&gt;메서드의 시작 부분에 추가되는 부가 기능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;after advice&lt;/td&gt;
&lt;td&gt;@After&lt;/td&gt;
&lt;td&gt;메서드의 끝 부분에 추가되는 부가기능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;after returing&lt;/td&gt;
&lt;td&gt;@AfterReturing&lt;/td&gt;
&lt;td&gt;예외가 발생하지 않았을 때, 실행되는 부가 기능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;after throwing&lt;/td&gt;
&lt;td&gt;@AfterThrowing&lt;/td&gt;
&lt;td&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;</description>
      <category>Programming/Java, Spring</category>
      <category>AOP</category>
      <category>스프링</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/66</guid>
      <comments>https://puenti.tistory.com/66#entry66comment</comments>
      <pubDate>Thu, 13 Oct 2022 13:00:42 +0900</pubDate>
    </item>
    <item>
      <title>[Spring boot] 스프링 부트란???</title>
      <link>https://puenti.tistory.com/65</link>
      <description>&lt;h1&gt;Spring boot&lt;/h1&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. Spring boot란?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring을 더 쉽게 이용하기 위한 도구입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring은 다양한 기능을 제공하고 있지만, 그 기능을 사용하기 위한 설정에 많은 시간이 걸립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) Transaction Manager, Hibernate Datasource, Entity Manager, Session Factory 등&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; Spring boot는 기존의 Spring의 오래 걸렸던 설정을 편리하게 도와주기 위해 나온 도구입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Spring boot의 특징&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Spring Boot는 자동설정(AutoConfiguration)을 이용합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어플리케이션 개발에 필요한 모든 Dependency를 Framework에서 관리&lt;/li&gt;
&lt;li&gt;jar 파일이 클래스 패스에 있는 경우 스프링 부트는 Dispatcher Servlet으로 자동 구성&lt;/li&gt;
&lt;li&gt;Spring boot는 미리 설정되어 있는 Starter 프로젝트를 제공&lt;/li&gt;
&lt;li&gt;xml 설정없이 자바 코드를 통해 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;어플리케이션을 개발하면서 사용되는 Dependency들은 호환되는 버전으로 관리해줘야 합니다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이런 복잡도를 줄이기 위해 Spring boot는 SpringBoot-Starter를 제공하여 자동으로 호환되는 버전을 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;모니터링 관리를 위한 Spring Actuator 제공
&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;Spring Actuator는 Spring boot에서 제공하는 상태 정보를 보다 쉽게 모니터링 할 수 있게 기능을 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Spring boot 프로젝트의 의존성 관리
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Spring voot starter dependency를 통해 다양한 패키지를 수용하고 있음 &amp;rarr; Spring boot의 가장 큰 장점&lt;/li&gt;
&lt;/ul&gt;
이를 통해 개발자는 dependency 관리(호환성 체크 등)에 대해 고려할 필요가 없어짐&lt;/li&gt;
&lt;li&gt;Spring boot 프로젝트의 starter dependency
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;spring-boot-starter-web-service : SOAP 웹 서비스&lt;/li&gt;
&lt;li&gt;spring-boot-starter-web : RESTful 응용 프로그램&lt;/li&gt;
&lt;li&gt;spring-boot-starter-test : 단위테스트, 통합 테스트&lt;/li&gt;
&lt;li&gt;spring-boot-starter-jdbc : 기본적인 jdbc&lt;/li&gt;
&lt;li&gt;spring-boot-starter-security : 스프링 시큐리티(인증, 권한)&lt;/li&gt;
&lt;li&gt;spring-boot-starter-data-jpa : Spring Data JPA(Hibernate)&lt;/li&gt;
&lt;li&gt;spring-boot-starter-cache : 캐시&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Spring boot와 Spring Legacy의 차이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring boot와 특징에 대해선 먼저 설명했으니 Spring Legacy에 대해서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;살펴보고 Spring boot와 어떤 차이가 있는지 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Legacy는 일반적으로 Spring Framework를 지칭합니다.&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;우리가 흔히 Spring이라 부르는 스프링의 정확한 표현은 Spring Framework입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Framework는 자바에서 가장 많이 사용되는 Framework인 의존성 주입(DI, Dependency Injection)과 제어역전(IOC, Inversion of Control), 관점지향 프로그래밍(AOP)이 가장 중요한 요소입니다. 이 요소들은 하나의 공통된 목적을 가지고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 느슨한 결합(Loose)입니다.&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;Spring Framework는 다양한 기능이 있는데 그 기능들은 약 20개의 모듈로 구성되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 모듈 몇가지를 보면 Spring JDBC, Spring MVC, Spring AOP, Spring ORM, Spring Test, Spring Epression Language(SpEL) 등이 있습니다.&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;그럼 이제 Spring Framework가 이렇게 다양한 기능을 가지고 있는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 Spring boot가 나오게 되었는지에 대해 살펴보겠습니다.&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;Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can &amp;ldquo;Just run&amp;rdquo;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring boot는 단지 실행만 하면 되는 Spring 기반의 어플리케이션을 쉽게 만들 수 있다.&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;위 글을 보면 Spring boot의 개념을 직접 관통하는 문장이라 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring boot는 Spring 기반이라는 것에는 차이가 없습니다.&lt;/p&gt;</description>
      <category>Programming/Java, Spring</category>
      <category>spring</category>
      <category>Spring Boot</category>
      <category>스프링</category>
      <category>스프링 부트</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/65</guid>
      <comments>https://puenti.tistory.com/65#entry65comment</comments>
      <pubDate>Wed, 12 Oct 2022 13:00:20 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] DI / IOC란? / 의존성을 주입하는 방법</title>
      <link>https://puenti.tistory.com/64</link>
      <description>&lt;h1&gt;DI / IOC&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;컨테이너란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IoC와 DI를 이해하기 전에 먼저 이해해야 할 개념이 있습니다. 그것은 바로 '컨테이너'입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너란, 우리 대신&amp;nbsp;&lt;b&gt;객체의 생성과 소멸 같은 부분을 전담해주는 역할&lt;/b&gt;을 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;IoC란(Inversion of Control)?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그대로 해석을 돌리면&amp;nbsp;&lt;b&gt;제어의 역전&lt;/b&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;b&gt;무언가를 조종하고 다루는 존재&lt;/b&gt;가 바뀌는 것을 IoC라고 합니다.&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;b&gt;컨테이너&lt;/b&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;b&gt;제어의 역전&lt;/b&gt;이라고 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;DI(의존성&amp;nbsp;주입)란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DI는&amp;nbsp;의존성을&amp;nbsp;주입시켜준다는&amp;nbsp;의미입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체를&amp;nbsp;직접&amp;nbsp;생성하는게&amp;nbsp;아니라&amp;nbsp;외부에서&amp;nbsp;생성한&amp;nbsp;후&amp;nbsp;주입을&amp;nbsp;시켜주는&amp;nbsp;방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&amp;nbsp;객체를&amp;nbsp;직접&amp;nbsp;생성하지&amp;nbsp;않고,&amp;nbsp;외부에서&amp;nbsp;생성된&amp;nbsp;객체를&amp;nbsp;getter()&amp;nbsp;혹은&amp;nbsp;생성자를&amp;nbsp;이용해서&amp;nbsp;사용하며&amp;nbsp;이걸&amp;nbsp;'주입'&amp;nbsp;한다고&amp;nbsp;합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;DI를 사용하는 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DI를 하지 않으면 객체와 객체 간의 결합력이 강하기 때문에 객체가 바뀌게 되면 많은 수정을 해야하는 번거로움이 있기 때문입니다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;public class A {
	private B b;

	public A() {
			this.b = new B();
		}
	
	public void printHello() {
		this.b.hello();
	}
	
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드를 보면 A 클래스에서 printHello 메서드가 호출하려면 B 클래스가 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &amp;lsquo;A클래스는 B 클래스에 의존한다.&amp;rsquo; , &amp;lsquo;A클래스는 B클래스의 의존성을 가진다&amp;rsquo; 라고 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 결합도가 높다고 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면 B 클래스가 수정되면 A클래스도 B 클래스의 수정부분을 똑같이 바꿔줘야하기 때문입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;의존성을 주입하는 방법&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 스프링 어노테이션&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의존성을 주입하는 방법은 크게 두가지가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거부터 사용해오던 XML 설정방식과 스프링에서 제공해주는 어노테이션 @AutoWired를 활용한 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 스프링 어노테이션인 @AutoWired 방식을 살펴보겠습니다&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생성자 주입&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;public class A{
	private B b;

	@AutoWired
	public A(B b){
		this.b = b;
	}
	public void printHello() {
		this.b.hello();
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Setter 주입&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;public class A {
	private B b;
	
	@AutoWired
	public void setB(B b){
		this.b = b;
	}

	public void printHello(){
		this.b.hello();
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Field 주입&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;public class A {

	@AutoWired
	private B b;
	
	public void printHello() {
		this.b.hello();
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@AutoWired를 붙인다면 어떠한 클래스를 갖다 쓸 수 있지 않습니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 IoC 컨테이너에 들어있는 Bean 객체에서만 사용할 수 있습니다.&lt;/p&gt;</description>
      <category>Programming/Java, Spring</category>
      <category>DI</category>
      <category>IOC</category>
      <category>스프링 컨테이너</category>
      <category>의존성 주입</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/64</guid>
      <comments>https://puenti.tistory.com/64#entry64comment</comments>
      <pubDate>Tue, 11 Oct 2022 13:00:52 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] POJO란? / POJO vs Java Beans</title>
      <link>https://puenti.tistory.com/63</link>
      <description>&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;&lt;span style=&quot;color: #000000;&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;POJO란?&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;P&lt;/b&gt;lain&amp;nbsp;&lt;b&gt;O&lt;/b&gt;ld&amp;nbsp;&lt;b&gt;J&lt;/b&gt;ava&amp;nbsp;&lt;b&gt;O&lt;/b&gt;bject, 오래된&amp;nbsp;방식의&amp;nbsp;단순한 자바 오브젝트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&amp;nbsp;&lt;b&gt;객체 지향적인 원리&lt;/b&gt;에 충실하면서&amp;nbsp;&lt;b&gt;환경과 기술에 종속되지 않고&lt;/b&gt;&amp;nbsp;필요에 따라 재활용될 수 있는 방식으로 설계된 오브젝트를 지칭합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토비의 스프링에서는 특정 기술규약과 환경에 종속되지 않는다고 해서 모두 POJO라고 지칭할 순 없다고 합니다. 진정한 POJO란 객체지향적인 원리에 충실하면서, 환경과 기술에 종속되지 않고 필요에 따라 재활용될 수 있는 방식으로 설계된 오브젝트를 POJO 라고 설명하기도 했습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;POJO가 주목 받은 배경&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;EJB(Enterprise JavaBeans)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기업의 IT 시스템의 요구사항이 늘어나고 기초적인 JDK로는 한계가 있어서 EJB 기술이 등장하였습니다. EJB의 경우에 아래의 문제를을 해결하기 위해 등장했습니다.&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;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만&amp;nbsp;&lt;b&gt;EJB의 경우에는 현실적이지 않고 과도한 엔지니어링으로 실패&lt;/b&gt;했습니다. EJB는 필요한 것이 많고, 복잡하며 컨테이너 밖에서는 정상적으로 동작하지 않았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히,&amp;nbsp;&lt;b&gt;EJB 스펙을 따르는 비지니스 오브젝트들은 객체지향적인 특징과 장점을 포기해야합니다.&lt;/b&gt;&amp;nbsp;EJB는 상속과 다형성 등을 사용할 수 없고, 간단한 기능 하나를 위해서 많은 인터페이스와 EJB 의존적인 상속을 해야했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EJB는&amp;nbsp;&lt;b&gt;형편없는 생산성과 느린 성능, 불필요한 기술적인 복잡도, 과도한 스펙 등의 문제&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;로 인해 POJO 방식으로 돌아가게 됩니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;POJO를 지향해야하는 이유&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;POJO의&amp;nbsp;목적은&amp;nbsp;자바의&amp;nbsp;&amp;nbsp;객체지향적인&amp;nbsp;특징을&amp;nbsp;살려&amp;nbsp;&lt;b&gt;비즈니스&amp;nbsp;로직&lt;/b&gt;에&amp;nbsp;충실한&amp;nbsp;개발이&amp;nbsp;가능하도록&amp;nbsp;하는&amp;nbsp;것입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;POJO의 장점&lt;/b&gt;&lt;/h2&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;객체지향적인 설계를 자유롭게 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;POJO 프레임워크&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;POJO 프레임워크란 POJO 프로그래밍이 가능하도록 기술적인 기반을 제공하는 프레임워크입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 프레임워크를 대표적인 POJO 프레임워크로 꼽을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링을 이용하면 POJO 프로그래밍의 장점을 그대로 살려서 엔터프라이즈 애플리케이션(EA)의 핵심로직을 객체지향적인 POJO를 기반으로 깔끔하게 구현하고, 동시에 엔터프라이즈 환경의 각종 서비스와 기술적인 필요를 POJO방식으로 만들어진 코드에 적용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EA (Enterprise Application)는 비즈니스 또는 정부와 같은 회사 환경에서 작동하도록 설계된 대규모 소프트웨어 시스템 플랫폼&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;POJO 예제&lt;/h2&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class MyPojo {    
	private String name;    
	private int age;     
   
	public String getName() {    	
		return name;    
	}    
	
	public String getAge() {    	
		return age;    
	}    

	public void setName(String name) {    	
		this.name = name;    
	}    

	public void setAge(int age) {    	
		this.age = age;    
	}
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 가장 기본적인 형태의 JAVA 객체를 POJO라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JAVA Bean이 아닌 Getter와 Setter로 구성된 가장 순수한 형태의 기본 클래스를 POJO라 지칭합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;POJO는 가독성과 재사용성을 높이기 위해 객체를 정의하는데 사용되는 Bean과 유사한 점이 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;POJO vs Java Beans&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java bean은 POJO이다. 그러나 POJO는 Java bena이 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;POJO는 java bean 보다 범주가 더 넓은 개념입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;POJO&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;POJO는 자바 객체 표준이며 어떤 특정하고 엄격한 규칙이 존재하지 않습니다.&lt;/li&gt;
&lt;li&gt;POJO는 가독성과 재사용성을 중요시하게 여깁니다.&lt;/li&gt;
&lt;li&gt;POJO는 엄격하지 않은 만큼 쓰기도 쉽고 읽기도 쉬워 많이 사용되어 왔습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class Animal {
        String dog;
        public String cat;
        private int legs;

        public Animal(String dog, String cat, int legs) {
            this.dog = dog;
            this.cat = cat;
            this.legs = legs;
        }

        public getDog() {
            return this.dog;
        }
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Java Beans&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;POJO와 달리 엄격한 규칙을 가지고 있습니다.&lt;/li&gt;
&lt;li&gt;Fields 는 접근제어자가&amp;nbsp;&lt;b&gt;private&lt;/b&gt;&amp;nbsp;이어야만 합니다.&lt;/li&gt;
&lt;li&gt;Fields 는 getter 와 setter 를 가져야 합니다.&lt;/li&gt;
&lt;li&gt;constructor 는 argument 를 가져서는 안됩니다.&lt;/li&gt;
&lt;li&gt;Fileds 는 getter 와 setter 로만 접근되어야만 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;public class Animal {
        private int legs;

        Animal() {

        }

        public setLegs(int legs) {
            this.legs = legs;
        }

        public getLegs() {
            return legs;
        }
    }
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Programming/Java, Spring</category>
      <category>POJO</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/63</guid>
      <comments>https://puenti.tistory.com/63#entry63comment</comments>
      <pubDate>Mon, 10 Oct 2022 12:32:47 +0900</pubDate>
    </item>
    <item>
      <title>[디자인 패턴] Builder 패턴</title>
      <link>https://puenti.tistory.com/62</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;b&gt;Builder 패턴이란?&lt;/b&gt;&lt;/b&gt;&lt;/h2&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;h2 data-ke-size=&quot;size26&quot;&gt;Builder 패턴의 장점&lt;/h2&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;가독성을 높임&lt;/li&gt;
&lt;li&gt;불변성 확보&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Builder 패턴을 쓰는 이유&lt;/h2&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;/**
 * 제약사항 : 이 객체는 한번 생성되면 읽기(Read)만 가능해야 합니다.
 */
public class PersonInfo {
    private String name;
    private Integer age;
    private String favoriteColor;
    private String favoriteAnimal;
    private Integer favoriteNumber;

    public PersonInfo(String name, Integer age, String favoriteColor, String favoriteAnimal, Integer favoriteNumber){
        this.name = name;
        this.age = age;
        this.favoriteColor = favoriteColor;
        this.favoriteAnimal = favoriteAnimal;
        this.favoriteNumber = favoriteNumber;
    }

    public String getName() {
        return name;
    }

    public Integer getAge() {
        return age;
    }

    public String getFavoriteColor() {
        return favoriteColor;
    }

    public String getFavoriteAnimal() {
        return favoriteAnimal;
    }

    public Integer getFavoriteNumber() {
        return favoriteNumber;
    }

    public String getPersonInfo(){
        String personInfo = String.format(&quot;name:%s, age:%d, favoriteColor:%s, favoriteAnimal:%s, favoriteNumber:%d&quot;
                , name, age, favoriteColor, favoriteAnimal, favoriteNumber);
        return personInfo;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 클래스는 한 사람의 정보를 담기 위한 것입니다. 하지만 제약 사항이 걸려 있습니다. 따라서 setter 메소드는 존재할 수 없고 오로지 생성자를 통해서만 데이터를 입력 받는다는 가정으로 만든 클래스입니다. 생성자를 보면 모든 매개변수를 받도록 하고 있습니다. 하지만 어떤 사람들은 모든 데이터가 없을 수도 있습니다. 예를 들어 &quot;이름&quot;과 &quot;나이&quot;만 알고 있는 사람의 경우는 다음처럼 객체를 만들어 내야 할 것입니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;new PersonInfo(&quot;JDM&quot;, 20, null, null, null);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이경우는 가독성이 안좋겠죠. 그래서 추가 생성자를 만들어서 처리하도록 해봅시다. 다음과 같은 생성자가 추가될 겁니다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;public PersonInfo(String name, Integer age){
    this(name, age, null, null, null);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 해서 하나의 케이스에 대해서는 처리를 했습니다.&amp;nbsp;&lt;b&gt;하지만&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&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;b&gt;문제는 하나 더 있습니다.&lt;/b&gt;&amp;nbsp;만약 데이터를 입력하는 순서가 달라진다면 그것도 큰 문제가 될겁니다.&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;// 이름이 들어가야할 곳에 좋아하는 동물이 들어갔습니다.
// 반대로 좋아하는 동물이 들어가야 할 곳에 사람 이름이 들어갔네요.
new PersonInfo(&quot;cat&quot;, 20, &quot;JDM&quot;, null, null);
&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;그래서 불편함을 해소하고자 쓰는 패턴이 Builder 패턴입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Builder 패턴 예제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Builder 패턴을 사용하기전에 아래의 3가지를 고려해야합니다.&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;/li&gt;
&lt;li&gt;사용자가 봤을때 명시적이고 이해할 수 있어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 다음과 같은 빌더 패턴을 적용한 Builder 클래스가 필요합니다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;@Getter
public class PersonInfoBuilder {
    private String name;
    private Integer age;
    private String favoriteColor;
    private String favoriteAnimal;
    private Integer favoriteNumber;

    public PersonInfoBuilder setName(String name) {
        this.name = name;
        return this;
    }

    public PersonInfoBuilder setAge(Integer age) {
        this.age = age;
        return this;
    }

    public PersonInfoBuilder setFavoriteColor(String favoriteColor) {
        this.favoriteColor = favoriteColor;
        return this;
    }

    public PersonInfoBuilder setFavoriteAnimal(String favoriteAnimal) {
        this.favoriteAnimal = favoriteAnimal;
        return this;
    }

    public PersonInfoBuilder setFavoriteNumber(Integer favoriteNumber) {
        this.favoriteNumber = favoriteNumber;
        return this;
    }

    public PersonInfo build(){
        PersonInfo personInfo = new PersonInfo(name, age, favoriteColor, favoriteAnimal, favoriteNumber);
        return personInfo;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public class BuilderPattern {
    public static void main(String[] args) {
        // 빌더 객체를 하나 만듭니다.
        PersonInfoBuilder personInfoBuilder = new PersonInfoBuilder();
        // 빌더 객체에 원하는 데이터를 입력합니다. 순서는 상관 없습니다.
        PersonInfo result = personInfoBuilder
                .setName(&quot;MISTAKE&quot;)
                .setAge(20)
                .setFavoriteAnimal(&quot;cat&quot;)
                .setFavoriteColor(&quot;black&quot;)
                .setName(&quot;JDM&quot;) // 다시 같은 메소드를 호출한다면 나중에 호출한 값이 들어갑니다.
                .setFavoriteNumber(7)
                // 마지막에 .build() 메소드를 호출해서 최종적인 결과물을 만들어 반환합니다.
                .build();
        // print is &quot;name:JDM, age:20, favoriteColor:black, favoriteAnimal:cat, favoriteNumber:7&quot;
        System.out.println(result.getPersonInfo());
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 입장에서 PersonInfo 객체를 만들어 낼 때 PersonInfoBuilder 클래스를 이용한다면 조금 더 명확하게 이해하고 만들어 낼 수 있을겁니다.&lt;/p&gt;</description>
      <category>CS (Computer Science)</category>
      <category>Builder Pattern</category>
      <category>빌더패턴</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/62</guid>
      <comments>https://puenti.tistory.com/62#entry62comment</comments>
      <pubDate>Thu, 29 Sep 2022 23:37:47 +0900</pubDate>
    </item>
    <item>
      <title>[디자인 패턴] 싱글톤&amp;nbsp;패턴이란?</title>
      <link>https://puenti.tistory.com/61</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;싱글톤&amp;nbsp;패턴이란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체의&amp;nbsp;인스턴스가&amp;nbsp;오직&amp;nbsp;1개만&amp;nbsp;생성되는&amp;nbsp;패턴을&amp;nbsp;의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의&amp;nbsp;코드는&amp;nbsp;싱글톤패턴의&amp;nbsp;간단한&amp;nbsp;코드입니다.&lt;/p&gt;
&lt;pre class=&quot;smali&quot;&gt;&lt;code&gt;public class&amp;nbsp;Singleton&amp;nbsp;{

	private&amp;nbsp;static&amp;nbsp;Singleton&amp;nbsp;instance&amp;nbsp;= new&amp;nbsp;Singleton();&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
		private&amp;nbsp;Singleton() {&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
		// 생성자는 외부에서 호출못하게 private 으로 지정해야 한다.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
		}
	
	public&amp;nbsp;static&amp;nbsp;Singleton&amp;nbsp;getInstance() {&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
		return&amp;nbsp;instance;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
	}
	
	public&amp;nbsp;void&amp;nbsp;say() {&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
		System.out.println(&quot;hi, there&quot;);&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
		}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;싱글톤&amp;nbsp;패턴의&amp;nbsp;장점&lt;/b&gt;&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모리&amp;nbsp;낭비를&amp;nbsp;방지할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최초의&amp;nbsp;new&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;있습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다른&amp;nbsp;클래스&amp;nbsp;간에&amp;nbsp;데이터&amp;nbsp;공유가&amp;nbsp;쉽습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;싱글톤&amp;nbsp;패턴의&amp;nbsp;단점&lt;/b&gt;&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;싱글톤&amp;nbsp;패턴을&amp;nbsp;구현하는&amp;nbsp;코드&amp;nbsp;자체가&amp;nbsp;많이&amp;nbsp;필요합니다.&lt;/li&gt;
&lt;li&gt;테스트하기&amp;nbsp;어렵습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싱글톤 인스턴스가 너무 많은 일을 하거나 많은 데이터를 공유시킬 경우 다른 클래스의 인스턴스들 간에 결합도가 높아져 &quot;개방-폐쇄 원칙&quot; 을 위배하게 됩니다. (=객체 지향 설계&amp;nbsp;5&amp;nbsp;원칙&amp;nbsp;DIP에 어긋남)&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;또한 자식&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;많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;있을 것 입니다.&lt;/p&gt;</description>
      <category>CS (Computer Science)</category>
      <category>singleton</category>
      <category>싱글톤</category>
      <category>싱글톤 패턴</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/61</guid>
      <comments>https://puenti.tistory.com/61#entry61comment</comments>
      <pubDate>Wed, 28 Sep 2022 20:48:14 +0900</pubDate>
    </item>
    <item>
      <title>[Database] DBMS에서 쿼리를 처리하는 단계(쿼리 파싱 순서)</title>
      <link>https://puenti.tistory.com/60</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;DBMS에서 쿼리를 처리하는 5단계&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구문 분석&amp;nbsp;(Parsing)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요청하는&amp;nbsp;쿼리가&amp;nbsp;잘못됐는지&amp;nbsp;확인하는&amp;nbsp;과정&lt;/li&gt;
&lt;li&gt;구문 오류 및 맞춤법 오류는 이 단계에서 검색할 수 있습니다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;/ul&gt;
&lt;/li&gt;
&lt;li&gt;액세스 계획 생성&lt;/li&gt;
&lt;li&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;여러 대안을 탐색한 후 DBMS는 그 중 하나를 선택합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;실행&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DBMS는 액세스 계획을 실행하여 쿼리문을 실행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SQL 파싱 순서&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;R1280x0.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;593&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VffNL/btrM1dLehkt/HWUFNSZewqELbbLFr9gF0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VffNL/btrM1dLehkt/HWUFNSZewqELbbLFr9gF0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VffNL/btrM1dLehkt/HWUFNSZewqELbbLFr9gF0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVffNL%2FbtrM1dLehkt%2FHWUFNSZewqELbbLFr9gF0K%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;593&quot; data-filename=&quot;R1280x0.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;593&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Syntax 에러(문법, 구문 오류) 체크&lt;/li&gt;
&lt;li&gt;Semantic(권한, 존재 여부) 체크&lt;/li&gt;
&lt;li&gt;Library Cache 영역으로 가서 저장되어 있는 정보가 있는지 체크
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예전에 같은 SQL을 실행한적이 있고 최적화 된 실행계획이 Library Cache에 저장되어 있다면&lt;/li&gt;
&lt;li&gt;Cache 영역에서 바로 꺼내서 실행하고 끝이 납니다.&lt;/li&gt;
&lt;li&gt;Library Cache에 저장되어 있는 정보가 없다면 옵티마이저가 수 많은 실행계획들을 도출해낸 다음에 최적화를 거치게 된 다음 SQL 엔진이 해석할 수 있는 언어로 변환을 하는 Row-source generation이라는 과정을 거쳐서 실행이 됩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소프트 파싱과 하드 파싱&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Soft Parsing(소프트 파싱)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 SQL 파싱 순서에서 3.a에 해당하는 Librarry Cache에서 바로 꺼내서 쓰는게 소프트 파싱입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hard Parsing(하드 파싱)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.b에 해당하는 옵티마이저에 의해서 최적화를 하고 Row-source generation까지 하고 그 다음 실행되는게 하드 파싱입니다.&lt;/p&gt;</description>
      <category>CS (Computer Science)/데이터베이스</category>
      <category>DBMS</category>
      <category>SQL</category>
      <category>SQL파싱</category>
      <category>소프트파싱</category>
      <category>하드파싱</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/60</guid>
      <comments>https://puenti.tistory.com/60#entry60comment</comments>
      <pubDate>Mon, 26 Sep 2022 09:36:37 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA]람다식이란? / 람다식의 특징 / Lambda Expression</title>
      <link>https://puenti.tistory.com/59</link>
      <description>&lt;h1&gt;1. Lambda 식이란?&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Java 8부터 지원되는 함수지향형 코드입니다.&lt;/li&gt;
&lt;li&gt;람다식이란 함수명을 선언하고 사용하는 것이 아닌 식별자 없이 실행가능한 함수입니다.&lt;/li&gt;
&lt;li&gt;절차형 프로그래밍, 객체지향 프로그래밍과는 다르게 함수의 구현과 호출만으로 프로그램을 만드는 방식인 함수형 프로그래밍에서 자주 사용됩니다.&lt;/li&gt;
&lt;li&gt;Lambda식은 함수형 프로그래밍 언어에서 사용되는 개념으로 익명 함수(Anonymous functions)를 지칭하는 용어입니다&lt;/li&gt;
&lt;li&gt;함수(Method)를 간단한 &amp;lsquo;식(Expression)&amp;rsquo;으로 표현하는 방법위 메서드를 아래의 Lambda식으로 바꿀 수 있습니다&lt;/li&gt;
&lt;li&gt;(a,b) -&amp;gt; a &amp;gt; b ? a : b&lt;/li&gt;
&lt;li&gt;int max(int a, int b){ return a &amp;gt; b ? a : b; }&lt;/li&gt;
&lt;li&gt;익명 함수(이름이 없는 함수, anonymous function)
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;(a,b) -&amp;gt; a &amp;gt; b ? a : b

// 반환타입 x, 이름 x, 화살표 추가
&lt;/code&gt;&lt;/pre&gt;
&amp;rarr; Lambda 식으로 표현할 때 이름이 없어지기 때문에 익명 함수라고 합니다&lt;/li&gt;
&lt;li&gt;int max(int a, int b){ return a &amp;gt; b ? a : b; }&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;람다식 작성법&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;메서드의 이름과 반환타입을 제거하고 &amp;lsquo; - &amp;gt;&amp;rsquo;를 블록 { } 앞에 추가합니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;int max(int a, int b) {
	return a &amp;gt; b ? a : b;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 식이 아래의 식으로&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;(int a, int b) -&amp;gt; {
	return a &amp;gt; b ? a : b;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;반환값이 있는 경우, 식이나 값만 적고 return 문 생략 가능합니다 (끝에 &amp;lsquo; ; &amp;rsquo; 안붙임)&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;(int a, int b) -&amp;gt; {
	return a &amp;gt; b ? a : b;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 식이 아래 식으로&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;(int a, int b) -&amp;gt; a &amp;gt; b ? a : b
&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;매개변수의 타입이 추론 가능하면 생략가능합니다(대부분의 경우 생략 가능합니다)&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;(int a, int b) -&amp;gt; a &amp;gt; b ? a : b
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 식이 아래 식으로!&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;(a, b) -&amp;gt; a &amp;gt; b ? a : b
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;단, 매개변수가 하나인 경우, 괄호() 생략 가능합니다 (타입이 없을 때만)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;(a) -&amp;gt; a * a
(int a) -&amp;gt; a * a
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 식이 아래 식으로&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;a -&amp;gt; a * a // ok
int a -&amp;gt; a * a // error, 괄호 있어야 함!
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Lambda 식의 특징&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;익명&amp;nbsp;: 보통의 메서드와 달리 이름이 없으므로 익명입니다.&lt;/li&gt;
&lt;li&gt;함수&amp;nbsp;: 람다는 메서드처럼 특정 클래스에 종속되지 않으므로 함수 입니다.&lt;/li&gt;
&lt;li&gt;전달&amp;nbsp;: Lambda 표현식을 메서드 인수로 전달하거나 변수로 저장할 수 있습니다.&lt;/li&gt;
&lt;li&gt;간결성&amp;nbsp;: 익명 클래스처럼 코드를 구현할 필요는 없습니다.&lt;/li&gt;
&lt;li&gt;람다식&amp;nbsp;내에서&amp;nbsp;사용되는&amp;nbsp;지역변수는&amp;nbsp;final이&amp;nbsp;붙지&amp;nbsp;않아도&amp;nbsp;상수로&amp;nbsp;간주됩니다&lt;/li&gt;
&lt;li&gt;람다식으로 선언된 변수명은 다른 변수명과 중복될 수 없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 기존 로직과 Lambda 식의 차이점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 객체 지향은 명령형 프로그래밍이고, 함수형 프로그래밍은 선언형 프로그래밍입니다.&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;b&gt;어떻게&lt;/b&gt;&amp;nbsp;처리할 지에 대해 명령을 통해 풀어 나아갔다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수형 프로그래밍은 선언적 함수를&amp;nbsp;통해&amp;nbsp;&lt;b&gt;무엇을&lt;/b&gt;&amp;nbsp;풀어나아갈지 결정하는 것 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;명령형 언어 :&lt;/b&gt;&amp;nbsp;a 라는 변수에 1을 담으세요 (대입)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;함수형 언어 :&lt;/b&gt;&amp;nbsp;a 를 1로 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 로직 : 익명 내부클래스&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;/li&gt;
&lt;li&gt;익명 내부 클래스는 일반적으로 추상 / 구현 클래스 또는 인터페이스를 구현합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;익명 내부클래스 vs Lambda 식&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 180px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 18px;&quot;&gt;익명 내부클래스&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 18px;&quot;&gt;람다식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 18px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이름이&amp;nbsp;없는 클래스입니다&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 18px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이름이 없는 메서드입니다&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 18px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;추상 및 구체 클래스를 확장할 수 있습니다.&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 18px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;추상 및 구체 클래스를 확장할 수 없습니다&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 18px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여러 추상 메서드를 포함하는 인터페이스를 구현할 수 있습니다.&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 18px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;단일 추상 메서드를 퐇마하는 인터페이스를 구현할 수 있습니다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 18px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;익명 내부 클래스 내부에서 인스턴스 변수를 선언할 수 있습니다.&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 18px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;인스턴스 변수를 선언할 수 없습니다&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 18px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;익명 내부 클래스를 인스턴스화&amp;nbsp;할 수 있습니다.&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 18px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;람다 표현식을 인스턴스화 할 수 없습니다&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 36px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 36px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;익명 내부 클래스 내부의 this 키워드는 현재 익명 내부 클래스를 참조합니다&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 36px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;람다 표현식 내부의 this 키워드는 현재 외부 클래스 객체를 나타냅니다&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description>
      <category>Programming/Java, Spring</category>
      <category>lambda</category>
      <category>람다식</category>
      <category>자바 람다식</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/59</guid>
      <comments>https://puenti.tistory.com/59#entry59comment</comments>
      <pubDate>Sun, 25 Sep 2022 22:34:49 +0900</pubDate>
    </item>
    <item>
      <title>[라이브러리] Swagger &amp;amp; REST API</title>
      <link>https://puenti.tistory.com/58</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Swagger가 나온 배경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;협업을 위해 필요한 라이브러리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 개발자B가 클라이언트 개발자A에게 API 스펙이 담긴 문서를 전달해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로는 엑셀이나 워드 등으로 API 스펙을 작성해서 보내줬습니다.&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;그래서 이런 문제들을 해결하고자 해서 나온 라이브러리가 Swagger입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Swagger란??&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서버로 요청되는 API 리스트를 HTML 화면으로 문서화하여 테스트 할 수 있는 라이브러리를 의미합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 라이브러리는 서버가 가동되면서 @RestController를 읽어 API를 분석하여 HTML 문서를 작성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 말한 서버로 요청되는 API 리스트는 스프링 부트와 연관되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swagger는 스프링 부트뿐만 아니라 여러가지 언어들 클라이언트, 서버 각 곳곳에서 사용되는 언어들 거의 다 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swagger는 서버가 가동되면서 RestController 어노테이션에 붙어있는 클래스들을 읽어서 API를 자동으로 분석하고 HTML 문서로 작성을 해줍니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Swagger가 필요한 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rest API의 스펙을 문서화 하는 것은 매우 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API를 변경할 때마다 Reference 문서를 계속 바꿔줘야하는 불편함이 있는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swagger를 사용하면 자동으로 내가 어떤 코드를 수정하든 말든 가동할때마다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문서를 자동으로 갱신해서 업데이트 해주기 때문에 협업에 있어서 효율적으로 사용할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Rest API&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swagger는 Rest API를 관리하기 쉽게 나온 라이브러리입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에 Swagger를 알려면 Rest API가 뭔지 알아보도록 하겠습니다.&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;REST API는 정보들이 주고 받아지는데 있어서 개발자들 사이에 널리 쓰이는 일종의 형식입니다.&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;p data-ke-size=&quot;size16&quot;&gt;먼저 API를 살펴보면 어떤 기계를 만들면, 사용자가 그 기능들을&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전부 제어할 수 있도록 제어장치를 만들어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 TV는 켜거나 끄고, 채널을 선택하고, 음량을 조절할 수 있게 해줘야 합니다.&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;lsquo;인터페이스&amp;rsquo; 라고 부릅니다. 즉, 기계와 인간 간의 소통창구입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 명령을 넣는거 뿐만 아니라 그 결과와 정보들을 받아오기 위한&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TV의 스크린, 컴퓨터의 모니터 또한 인터페이스에 속한다고 할 수 있습니다.&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;lsquo;User Interface&amp;rsquo;라 볼 수 있습니다.&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;p data-ke-size=&quot;size16&quot;&gt;소프트웨어가 다른 소프트웨어로부터 지정된 형식으로 요청, 명령을 받을 수 있는 수단을&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lsquo;Application Programming Interface&amp;rsquo; API&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;라고 부릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 API는 네트워크 뿐만 아니라 웹, OS 등에서도 존재하는 개념입니다.&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;이제 API를 알았으니 REST API에 대해서 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드 웹에서 서버에 데이터를 요청하거나 배달앱에서 서버에 주문을 넣는 등&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 서비스에서 널리 활용되는 형식이 바로 REST API 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST API의 가장 중요한 특성은 각 요청이 어떤 동작이나 정보를 위한 것인지를&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;이런 서비스는 개발자 혼자 만드는게 아니라 나중에 인계받을 개발자나 이 API를&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;활용해 다른 제품을 만들 개발자들은 개발하기 힘들어질 겁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 RESTful 하게 만든 API는 요청을 보내는 주소만으로도 대략 이게 뭘하는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청이지 파악이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에 REST API로 요청을 보낼때는 HTTP 규약에 따라 신호를 전송합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 HTTP로 요청을 보낼 때도 여러 메소드가 있는데 REST API 에서는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;get, post, delete, put, patch 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;각 요청의 의도를 쉽게 파악할 수 있도록 RESTful 하게 API를 만들기 위해서는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메소드의 목적에 따라 구분해서 사용해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;get은 데이터를 Read, 조회하는데 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;post는 Create, 새로운 정보를 추가하는데 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;put, patch는 Update, 변경하는데 사용됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;put은 정보를 통째로 갈아끼울 때&lt;/li&gt;
&lt;li&gt;patch는 정보 중 일부를 특정 방식으로 변경할 때 사용&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;delete는 삭제히는데 사용됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;REST API란 HTTP 요청을 보낼때 어떤 URI에 어떤 메소드를 사용할지&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;개발자들 사이에 널리 지켜지는 약속과 같습니다.&lt;/b&gt;&lt;/p&gt;</description>
      <category>Programming</category>
      <category>REST API</category>
      <category>Swagger</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/58</guid>
      <comments>https://puenti.tistory.com/58#entry58comment</comments>
      <pubDate>Sat, 24 Sep 2022 20:49:03 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] 스웨거 어노테이션 / Swagger 2.0 vs Swagger 3.0 차이</title>
      <link>https://puenti.tistory.com/57</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Swagger Annotation&lt;/b&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;span&gt;Swagger&lt;/span&gt;란&lt;span&gt;, &lt;/span&gt;서버로&lt;span&gt; &lt;/span&gt;요청되는&lt;span&gt; API &lt;/span&gt;리스트를&lt;span&gt; HTML &lt;/span&gt;화면으로&lt;span&gt; &lt;/span&gt;문서화하여&lt;span&gt; &lt;/span&gt;테스트&lt;span&gt; &lt;/span&gt;할&lt;span&gt; &lt;/span&gt;수&lt;span&gt; &lt;/span&gt;있는&lt;span&gt; &lt;/span&gt;라이브러리를&lt;span&gt; &lt;/span&gt;의미합니다&lt;span&gt;.&lt;/span&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;b&gt;Open API 2.0 (Swagger 2.0) vs Swagger 3.0&lt;/b&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;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;b&gt;Swagger 3.0 Annotation&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;b&gt;Swagger 2.0&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;b&gt;Description&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;b&gt;@Tag&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;b&gt;@API&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;클래스를&lt;/span&gt; Swagger &lt;span&gt;리소스로&lt;/span&gt; &lt;span&gt;표시&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;b&gt;@Parameter&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;b&gt;(hidden = true)&amp;nbsp;or&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;b&gt;@Operation&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;b&gt;(hidden = true)&amp;nbsp;or&amp;nbsp;@Hidden&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;b&gt;&lt;/b&gt;&lt;br /&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;b&gt;@ApiIgnore&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;API &lt;/span&gt;작업에서&lt;span&gt; &lt;/span&gt;단일&lt;span&gt; &lt;/span&gt;매개&lt;span&gt; &lt;/span&gt;변수를&lt;span&gt; &lt;/span&gt;나타냄&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;b&gt;@Parameter&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;@&lt;/b&gt;&lt;/span&gt;&lt;b&gt;ApiImplicitParam&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;&lt;br /&gt;API &lt;/span&gt;작업에서&lt;span&gt; &lt;/span&gt;단일&lt;span&gt; &lt;/span&gt;매개&lt;span&gt; &lt;/span&gt;변수를&lt;span&gt; &lt;/span&gt;나타냄&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;b&gt;@Parameters&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;b&gt;@ApiImplicitParams&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;API &lt;/span&gt;작업에서&lt;span&gt; &lt;/span&gt;복수&lt;span&gt; &lt;/span&gt;매개&lt;span&gt; &lt;/span&gt;변수를&lt;span&gt; &lt;/span&gt;나타냄&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;b&gt;@Schema&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;b&gt;@ApiModel&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;Swagger &lt;span&gt;모델에&lt;/span&gt; &lt;span&gt;대한&lt;/span&gt; &lt;span&gt;추가&lt;/span&gt; &lt;span&gt;정보를&lt;/span&gt; &lt;span&gt;제공&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;b&gt;@Schema(accessMode = READ_ONLY)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;b&gt;@ApiModelProperty&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;b&gt;(hidden = true)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;모델&lt;span&gt; &lt;/span&gt;속성의&lt;span&gt; &lt;/span&gt;데이터를&lt;span&gt; &lt;/span&gt;추가하고&lt;span&gt; &lt;/span&gt;조작&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;b&gt;@Schema&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;b&gt;@ApiModelProperty&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;Swagger &lt;span&gt;모델에&lt;/span&gt; &lt;span&gt;대한&lt;/span&gt; &lt;span&gt;추가&lt;/span&gt; &lt;span&gt;정보를&lt;/span&gt; &lt;span&gt;제공&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;b&gt;@Operation&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;b&gt;(summary =&quot;foo&quot;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;b&gt;description = &quot;bar&quot;)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;b&gt;@ApiOperation&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;b&gt;(value = &quot;foo&quot;,&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;notes = &quot;bar&quot;)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;특정&lt;span&gt; &lt;/span&gt;경로에&lt;span&gt; &lt;/span&gt;대한&lt;span&gt; &lt;/span&gt;작업&lt;span&gt; &lt;/span&gt;또는&lt;span&gt; &lt;/span&gt;일반적으로&lt;span&gt; HTTP &lt;/span&gt;메서드를&lt;span&gt; &lt;/span&gt;설명&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;b&gt;@Parameter&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;b&gt;@ApiParam&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;작업&lt;span&gt; &lt;/span&gt;매개&lt;span&gt; &lt;/span&gt;변수에&lt;span&gt; &lt;/span&gt;대한&lt;span&gt; &lt;/span&gt;추가&lt;span&gt; &lt;/span&gt;메타&lt;span&gt; &lt;/span&gt;데이터를&lt;span&gt; &lt;/span&gt;추가&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;b&gt;@ApiResponse&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;b&gt;(responseCode = &quot;404&quot;,&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;description = &quot;foo&quot;)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;b&gt;@ApiResponse&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;b&gt;(code = 404,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;b&gt;message = &quot;foo&quot;)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;작업의&lt;span&gt; &lt;/span&gt;가능한&lt;span&gt; &lt;/span&gt;응답을&lt;span&gt; &lt;/span&gt;설명&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Swagger Annotation 3.0&lt;/b&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. api &lt;span&gt;그룹&lt;/span&gt; &lt;span&gt;설정&lt;/span&gt;: &lt;b&gt;@Tag&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;대상&lt;/span&gt;: ANNOTATION_TYPE, METHOD, TYPE&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tag&lt;span&gt;에&lt;/span&gt; &lt;span&gt;설정된&lt;/span&gt; name&lt;span&gt;이&lt;/span&gt; &lt;span&gt;같은&lt;/span&gt; &lt;span&gt;것끼리&lt;/span&gt; &lt;span&gt;하나의&lt;/span&gt; api&lt;span&gt;그룹으로&lt;/span&gt; &lt;span&gt;묶는다&lt;/span&gt;. &lt;span&gt;주로&lt;/span&gt; Controller&lt;span&gt;나&lt;/span&gt; Controller&lt;span&gt;의&lt;/span&gt; &lt;span&gt;메서드&lt;/span&gt; &lt;span&gt;영역에&lt;/span&gt; &lt;span&gt;설정합니다&lt;/span&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;2. api Schema &lt;span&gt;설정&lt;/span&gt;: &lt;b&gt;@Schema&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;대상&lt;/span&gt;: ANNOTATION_TYPE, METHOD, PARAMETER, TYPE&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Schema(=Model)&lt;span&gt;에&lt;/span&gt; &lt;span&gt;대한&lt;/span&gt; &lt;span&gt;정보를&lt;/span&gt; &lt;span&gt;작성하는&lt;/span&gt; &lt;span&gt;곳입니다&lt;/span&gt;. Schema&lt;span&gt;를&lt;/span&gt; &lt;span&gt;설정할&lt;/span&gt; &lt;span&gt;시&lt;/span&gt;, &lt;span&gt;더욱&lt;/span&gt; &lt;span&gt;완성도&lt;/span&gt; &lt;span&gt;높은&lt;/span&gt; Swagger UI&lt;span&gt;를&lt;/span&gt; &lt;span&gt;만들&lt;/span&gt; &lt;span&gt;수&lt;/span&gt; &lt;span&gt;있습니다&lt;/span&gt;. &lt;span&gt;아래&lt;/span&gt; &lt;span&gt;이미지는&lt;/span&gt; @Schema &lt;span&gt;설정을&lt;/span&gt; &lt;span&gt;하지&lt;/span&gt; &lt;span&gt;않은&lt;/span&gt; dto&lt;span&gt;클래스들의&lt;/span&gt; &lt;span&gt;모델&lt;/span&gt; &lt;span&gt;정보입니다&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각&lt;span&gt; &lt;/span&gt;필드&lt;span&gt; &lt;/span&gt;값들에&lt;span&gt; &lt;/span&gt;대한&lt;span&gt; &lt;/span&gt;설명이나&lt;span&gt; &lt;/span&gt;기본값&lt;span&gt;, &lt;/span&gt;허용가능한&lt;span&gt; &lt;/span&gt;값&lt;span&gt; &lt;/span&gt;등에&lt;span&gt; &lt;/span&gt;대한&lt;span&gt; &lt;/span&gt;정보&lt;span&gt; &lt;/span&gt;없이&lt;span&gt; string &lt;/span&gt;요소가&lt;span&gt; &lt;/span&gt;들어간다는&lt;span&gt; &lt;/span&gt;정보&lt;span&gt; &lt;/span&gt;뿐이라&lt;span&gt; &lt;/span&gt;모호합니다&lt;span&gt;.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;api &lt;/span&gt;문서의&lt;span&gt; &lt;/span&gt;완성도를&lt;span&gt; &lt;/span&gt;높이기&lt;span&gt; &lt;/span&gt;위해서는&lt;span&gt; @Schema &lt;/span&gt;설정을&lt;span&gt; &lt;/span&gt;통해&lt;span&gt; &lt;/span&gt;어떤&lt;span&gt; &lt;/span&gt;값들이&lt;span&gt; &lt;/span&gt;들어가는지&lt;span&gt; &lt;/span&gt;설정을&lt;span&gt; &lt;/span&gt;추가해주는&lt;span&gt; &lt;/span&gt;것이&lt;span&gt; &lt;/span&gt;좋습니다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Schema &lt;span&gt;적용을&lt;/span&gt; &lt;span&gt;마친&lt;/span&gt; &lt;span&gt;후의&lt;/span&gt; PostsListResponseDto &lt;span&gt;모델의&lt;/span&gt; &lt;span&gt;정보는&lt;/span&gt; &lt;span&gt;좀&lt;/span&gt; &lt;span&gt;더&lt;/span&gt; &lt;span&gt;자세하게&lt;/span&gt; &lt;span&gt;스키마에&lt;/span&gt; &lt;span&gt;대한&lt;/span&gt; &lt;span&gt;정보를&lt;/span&gt; &lt;span&gt;확인할&lt;/span&gt; &lt;span&gt;수&lt;/span&gt; &lt;span&gt;있게&lt;/span&gt; &lt;span&gt;됩니다&lt;/span&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. api &lt;span&gt;상세&lt;/span&gt; &lt;span&gt;정보&lt;/span&gt; &lt;span&gt;설정&lt;/span&gt;: &lt;b&gt;@Operation&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;대상&lt;/span&gt;: ANNOTATION_TYPE, METHOD&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;summary: api&lt;span&gt;에&lt;/span&gt; &lt;span&gt;대한&lt;/span&gt; &lt;span&gt;간략한&lt;/span&gt; &lt;span&gt;설명&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;description: api&lt;span&gt;에&lt;/span&gt; &lt;span&gt;대한&lt;/span&gt; &lt;span&gt;상세&lt;/span&gt; &lt;span&gt;설명&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;response: api Response &lt;span&gt;리스트&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;parameters: api &lt;span&gt;파라미터&lt;/span&gt; &lt;span&gt;리스트&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Operation&lt;span&gt;어노테이션은&lt;/span&gt; api&lt;span&gt;동작에&lt;/span&gt; &lt;span&gt;대한&lt;/span&gt; &lt;span&gt;명세를&lt;/span&gt; &lt;span&gt;작성하기&lt;/span&gt; &lt;span&gt;위해&lt;/span&gt; Controller &lt;span&gt;메소드에&lt;/span&gt; &lt;span&gt;설정합니다&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간략히&lt;span&gt; &lt;/span&gt;확인할&lt;span&gt; &lt;/span&gt;수&lt;span&gt; &lt;/span&gt;있는&lt;span&gt; &lt;/span&gt;간략정보는&lt;span&gt; &lt;b&gt;summary&lt;/b&gt;&lt;/span&gt;에&lt;span&gt; &lt;/span&gt;작성하고&lt;span&gt;, &lt;/span&gt;필요에&lt;span&gt; &lt;/span&gt;따라&lt;span&gt; &lt;/span&gt;상세&lt;span&gt; &lt;/span&gt;정보를&lt;span&gt; &lt;/span&gt;표기하고자&lt;span&gt; &lt;/span&gt;한다면&lt;span&gt; &lt;b&gt;description&lt;/b&gt;&lt;/span&gt;에&lt;span&gt; &lt;/span&gt;설명을&lt;span&gt; &lt;/span&gt;추가하면&lt;span&gt; &lt;/span&gt;됩니다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;responses&lt;/b&gt;&lt;span&gt;는&lt;/span&gt; &lt;span&gt;아래에서&lt;/span&gt; &lt;span&gt;설명할&lt;/span&gt; @ApiResponse &lt;span&gt;리스트들&lt;/span&gt; &lt;span&gt;설정하는&lt;/span&gt; &lt;span&gt;요소입니다&lt;/span&gt;.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;parameters&lt;/b&gt;&lt;span&gt;는&lt;/span&gt; path, query, header, cookie &lt;span&gt;등의&lt;/span&gt; &lt;span&gt;형태로&lt;/span&gt; &lt;span&gt;들어오는&lt;/span&gt; &lt;span&gt;파라미터에&lt;/span&gt; &lt;span&gt;대한&lt;/span&gt; &lt;span&gt;정보를&lt;/span&gt; &lt;span&gt;설정하는&lt;/span&gt; &lt;span&gt;요소입니다&lt;/span&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;4. api response &lt;span&gt;설정&lt;/span&gt;: &lt;b&gt;@ApiResponse&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;대상&lt;/span&gt;:&amp;nbsp;ANNOTATION_TYPE, METHOD, TYPE&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;responseCode: http &lt;span&gt;상태코드&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;description: response&lt;span&gt;에&lt;/span&gt; &lt;span&gt;대한&lt;/span&gt; &lt;span&gt;설명&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;content: Response payload &lt;span&gt;구조&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@ApiResponse&lt;span&gt;는&lt;/span&gt; &lt;span&gt;응답&lt;/span&gt; &lt;span&gt;결과에&lt;/span&gt; &lt;span&gt;따른&lt;/span&gt; response &lt;span&gt;구조를&lt;/span&gt; &lt;span&gt;미리&lt;/span&gt; &lt;span&gt;확인할&lt;/span&gt; &lt;span&gt;수&lt;/span&gt; &lt;span&gt;있게&lt;/span&gt; &lt;span&gt;해줍니다&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;무늬&lt;/span&gt; &lt;span&gt;뿐인&lt;/span&gt; response &lt;span&gt;구조가&lt;/span&gt; &lt;span&gt;아닌&lt;/span&gt;, api &lt;span&gt;조회&lt;/span&gt; &lt;span&gt;성공&lt;/span&gt; &lt;span&gt;및&lt;/span&gt; &lt;span&gt;실패&lt;/span&gt; &lt;span&gt;시&lt;/span&gt; &lt;span&gt;발생될&lt;/span&gt; &lt;span&gt;상태코드&lt;/span&gt; &lt;span&gt;및&lt;/span&gt; Response &lt;span&gt;구조를&lt;/span&gt; &lt;span&gt;설정하고자&lt;/span&gt; &lt;span&gt;한다면&lt;/span&gt;&amp;nbsp;@ApiResponse&lt;span&gt;를&lt;/span&gt; &lt;span&gt;설정하면&lt;/span&gt; &lt;span&gt;됩니다&lt;/span&gt;.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;게시글&lt;/span&gt; &lt;span&gt;조회&lt;/span&gt; API&lt;span&gt;에&lt;/span&gt; @ApiResponse Annotation&lt;span&gt;을&lt;/span&gt; &lt;span&gt;추가하면&lt;/span&gt; &lt;span&gt;테스트&lt;/span&gt; &lt;span&gt;애플리케이션의&lt;/span&gt; &lt;span&gt;게시글&lt;/span&gt; &lt;span&gt;조회&lt;/span&gt; API&lt;span&gt;에서는&lt;/span&gt; 200, 404 &lt;span&gt;상태코드가&lt;/span&gt; &lt;span&gt;설정되어&lt;/span&gt; &lt;span&gt;있습니다&lt;/span&gt;.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;조회가&lt;/span&gt; &lt;span&gt;성공되었을&lt;/span&gt; &lt;span&gt;시&lt;/span&gt;, 200 &lt;span&gt;상태코드와&lt;/span&gt; PostsResponseDto&lt;span&gt;클래스가&lt;/span&gt; reseponseBody&lt;span&gt;에&lt;/span&gt; &lt;span&gt;반환되고&lt;/span&gt; &lt;span&gt;조회&lt;/span&gt; &lt;span&gt;실패시엔&lt;/span&gt;, 404 &lt;span&gt;상태코드와&lt;/span&gt; ErrorResponse&lt;span&gt;클래스가&lt;/span&gt; responseBody&lt;span&gt;가&lt;/span&gt; &lt;span&gt;반환됩니다&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. api parameter &lt;span&gt;설정&lt;/span&gt;:&lt;b&gt; @Parameter&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;대상&lt;/span&gt;:&amp;nbsp;ANNOTATION_TYPE, FIELD, METHOD, PARAMETER&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;name: &lt;span&gt;파라미터&lt;/span&gt; &lt;span&gt;이름&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;description: &lt;span&gt;파라미터&lt;/span&gt; &lt;span&gt;설명&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;in: &lt;/span&gt;파라미터&lt;span&gt; &lt;/span&gt;위치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;query, header, path, cookie&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@ApiResponse&lt;span&gt;와&lt;/span&gt; &lt;span&gt;마찬가지로&lt;/span&gt; @parameters Annotation&lt;span&gt;에&lt;/span&gt; @Parameter &lt;span&gt;리스트를&lt;/span&gt; &lt;span&gt;담아&lt;/span&gt; api&lt;span&gt;메서드에&lt;/span&gt; &lt;span&gt;설정할&lt;/span&gt; &lt;span&gt;수&lt;/span&gt; &lt;span&gt;있고&lt;/span&gt;, @Operation &lt;span&gt;어노테이션에&lt;/span&gt; parameters &lt;span&gt;요소에&lt;/span&gt; &lt;span&gt;설정할&lt;/span&gt; &lt;span&gt;수도&lt;/span&gt; &lt;span&gt;있습니다&lt;/span&gt;. &lt;span&gt;그리고&lt;/span&gt; &lt;span&gt;파라미터의&lt;/span&gt; &lt;span&gt;경우&lt;/span&gt; api method&lt;span&gt;의&lt;/span&gt; &lt;span&gt;인자&lt;/span&gt; &lt;span&gt;값에&lt;/span&gt; &lt;span&gt;붙여&lt;/span&gt; &lt;span&gt;명시적으로&lt;/span&gt; &lt;span&gt;설정할&lt;/span&gt; &lt;span&gt;수도&lt;/span&gt; &lt;span&gt;있습니다&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Parameters&lt;span&gt;나&lt;/span&gt; @Operation&lt;span&gt;에&lt;/span&gt; &lt;span&gt;설정하는&lt;/span&gt; &lt;span&gt;방법은&lt;/span&gt; @ApiResponse&lt;span&gt;에서와&lt;/span&gt; &lt;span&gt;유사합니다&lt;/span&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;span&gt;&lt;b&gt; @Parameters &lt;/b&gt;&lt;/span&gt;나&lt;span&gt;&lt;b&gt; @Operations&lt;/b&gt;&lt;/span&gt;에&lt;span&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;파라미터를&lt;span&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;설정할&lt;span&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;경우에는&lt;span&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;어떤&lt;span&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;파라미터에&lt;span&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;대한&lt;span&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;설정인지&lt;span&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;알&lt;span&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;수&lt;span&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;없기&lt;span&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;때문에&lt;span&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;반드시&lt;span&gt;&lt;b&gt; name&lt;/b&gt;&lt;/span&gt;을&lt;span&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;설정해&lt;span&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;줘야&lt;span&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;합니다&lt;span&gt;&lt;b&gt;.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>Programming/Java, Spring</category>
      <category>Swagger</category>
      <category>swagger2.0</category>
      <category>swagger3.0</category>
      <category>스웨거</category>
      <category>스웨거2.0</category>
      <category>스웨거3.0</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/57</guid>
      <comments>https://puenti.tistory.com/57#entry57comment</comments>
      <pubDate>Thu, 22 Sep 2022 22:03:21 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] Generic이란??</title>
      <link>https://puenti.tistory.com/56</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Generic이란??&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스 내부에서 사용할 데이터 타입을 외부에서 지정하는 기법&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;class Person&amp;lt;T&amp;gt; {
	public T info;
}

public class GenericDemo {
	
	public static void main(String[] args) {
		Person&amp;lt;String&amp;gt; p1 = new Person&amp;lt;String&amp;gt;();
		Person&amp;lt;StringBuilder&amp;gt; p2 = new Person&amp;lt;StringBuilder&amp;gt;();
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;T 는 info 필드의 데이터 타입입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person 클래스를 정의하는 시점에서는 info의 데이터 타입을 명시적으로 지정하지 않고 있다가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person을 인스턴스화 할때 &amp;lt;&amp;gt; 안에 구체적인 데이터 타입인 &amp;lt;String&amp;gt;을 언급하면 &amp;lt;T&amp;gt;는 String이 되고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;info의 데이터 타입은 String을 갖게 된다는 뜻입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 자연스럽게 인스턴스를 담아낼 수 있는 p1의 데이터 타입 역시 String이 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Generic을 이용해서 두 개의 데이터 타입을 만들어봤습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나는 Info의 데이터 타입이 String인 것이고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 다른 하나의 Info의 데이터 타입이 StringBuilder인 것 입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Generic을 사용하면 얻는 이점&lt;/h2&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;&amp;rarr; 컴파일시에 미리 타입을 강하게 체크해서 사전에 에러를 방지합니다.&lt;/li&gt;
&lt;li&gt;타입변환을 제거할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;List list = new ArrayList();
list.add(&quot;Hello&quot;); //실제 저장하는건 String이지만 내부적으로 저장은 object로 저장
String str = (String) list.get(0); 
// 그래서 호출시 object 타입으로 반환이 된다. 
// 그래서 이런 점을 방지하고자 앞에 (String)을 붙혀줌으로 강제 타입변환이 필요하다
&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;처음 list.add(&amp;rdquo;hello&amp;rdquo;); 을 String으로 저장이 될 때 그리고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(String) list.get(0); 으로 찾아올 때 Object 타입이 String으로 타입 변환이 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 Generic을 사용하게 되면 아래 처럼 코드가 바뀝니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;List&amp;lt;String&amp;gt; list = new ArrayList&amp;lt;String&amp;gt;();
list.add(&quot;hello&quot;);
String str = list.get(0);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;List&amp;lt;String&amp;gt; list 는 객체를 저장할 수 있는 컬렉션인 List에 String만 저장한다는 의미입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현 객체 new ArrayList&amp;lt;String&amp;gt;(); 에도 String 만 받겠다는 의미입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;list.add(&amp;rdquo;hello&amp;rdquo;);에 String을 주게 되면 내부에 저장될 때도 String으로 저장됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 String이 아니라 다른 타입이 들어오게 되면 컴파일 에러가 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에 매개값을 줄때도 String으로 주고 내부 저장값도 String으로 저장되기 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동 타입변환이 발생하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 저장된 객체를 찾아오는 get이라는 메서드를 사용할때도 String 타입으로 바로 나옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 두 코드를 비교하면 타입체크와 형변환을 생략할 수 있으므로 코드가 간결해 집니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Generic의 사용&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;// 클래스
public class ClassName &amp;lt;T&amp;gt; { ... }

// 인터페이스
public Interface InterfaceName &amp;lt;T&amp;gt; { ... }

// 제네릭 타입을 두 개 이상 사용하는 경우
public class MultiGeneric &amp;lt;K, V&amp;gt; { ... }
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스나 인터페이스에 선언한 경우와 제네릭 타입을 두 개 이상 사용하는 경우입니다. 이때 T, K, V 타입은 해당 블록 {...} 안에서까지 유효합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;타입 변수**(Type Variable)**&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기호의 종류만 다를 뿐 '임의의 참조형 타입'을 의미한다는 것은 모두 같으며, 상황에 맞게 의미 있는 문자를 선택해서 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;T : Object       &amp;lt;T&amp;gt;
E : Element      &amp;lt;E&amp;gt;
K : Key          &amp;lt;K&amp;gt;
V : Value        &amp;lt;V&amp;gt;
N : Number       &amp;lt;N&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Generic 용어&lt;/h2&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;class Box&amp;lt;T&amp;gt; { } 
//지네릭 클래스 선언

Box&amp;lt;String&amp;gt; b = new Box&amp;lt;String&amp;gt;();
Box&amp;lt;TV&amp;gt; c = new Box&amp;lt;TV&amp;gt;();

// 객체를 만들때마다 타입을 바꿀 수 있습니다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Box&amp;lt;T&amp;gt; : Generic 클래스. &amp;lsquo;T의 Box&amp;rsquo; 또는 &amp;lsquo;T Box&amp;rsquo; 라고 읽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;T : 타입 변수 또는 타입 매개변수.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Box : 원시타입(raw type)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;String&amp;gt;, &amp;lt;TV&amp;gt; : parameterized type, 매개변수화된 타입&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Generic 타입과 다형성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다형성 : 하나의 참조변수로 여러 타입의 객체를 참조하는 것&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;참조 변수와 생성자의 parameterized type은 일치해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;ArrayList&amp;lt;TV&amp;gt; list = new ArrayList&amp;lt;TV&amp;gt;();     // ok, 일치
ArrayLisy&amp;lt;Product&amp;gt; list = new ArrayList&amp;lt;TV&amp;gt;();// 에러, parameterized type 불일치
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Generic 클래스간의 다형성은 성립합니다 (parameterized type은 일치!!)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;List&amp;lt;TV&amp;gt; list = new ArrayList&amp;lt;TV&amp;gt;();  // ok, 다형성, ArrayList가 List를 구현
List&amp;lt;TV&amp;gt; list = new LinkedList&amp;lt;TV&amp;gt;(); // ok, 다형성, LinkedList가 List를 구현
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;제한된 Generic 클래스&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;extends로 대입할 수 있는 타입을 제한합니다&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;class FruitBox&amp;lt;T extneds Fruit&amp;gt; { // Fruit의 자손만 타입으로 지정 가능
	ArrayList&amp;lt;T&amp;gt; list = new ArrayList&amp;lt;T&amp;gt;();
}

FruitBox&amp;lt;Apple&amp;gt; appleBox = new FruitBox&amp;lt;Apple&amp;gt;();  // ok
FruitBox&amp;lt;Toy&amp;gt; toyBox = new FruitBox&amp;lt;Toy&amp;gt;();       // 에러, toy는 Fruit의 자손x
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입 T만 있으면 모든 타입이 가능했는데 extends Fruit이라는 제한 조건을 줌으로서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fruit의 자손만 대입가능합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인터페이스인 경우에도 extends를 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;interface Eatable { }
class FruitBox&amp;lt;T extends Eatable&amp;gt; {  }
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Generic의 제약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입 변수에 대입은 인스턴스 별로 다르게 가능합니다.&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;Box&amp;lt;Apple&amp;gt; appleBox = new Box&amp;lt;Apple&amp;gt;(); // ok, Apple 객체만 저장 가능
Box&amp;lt;Grape&amp;gt; grapeBox = new Box&amp;lt;Grape&amp;gt;(); // ok, Grape 객체만 저장 가능
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;static 멤버에 타입 변수 사용 불가능합니다&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;class Box&amp;lt;T&amp;gt; {
	static T item; // 에러
	static int compare(T t1, T t2) { /*생략*/ } // 에러
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; Box 클래스가 인스턴스가 되기 전에 static은 메모리에 올라가는데 이 때 item의 타입인 T가 결정되지 않기 때문에 위와 같이 사용할 수 없는 것이다.&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;/ul&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class Box&amp;lt;T&amp;gt; {
	T[] itemArr; // ok, T타입의 배열을 위한 참조변수
		....
	T[] toArray() {
		T[] tmpArr = new T[itemArr.length] // 에러, 지네릭 배열 생성불가
		....
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;new 다음에 T가 오면 안된다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;new 연산자 뒤에 타입은 확정되어 있어야 하는데 T는 어떤 타입이 올지 모르기 때문에 사용 불가능합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;와일드 카드 &amp;lt;?&amp;gt;&lt;/h2&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;/ul&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;ArrayList&amp;lt;? extemds Product&amp;gt; list = new ArrayList&amp;lt;TV&amp;gt;(); // ok
ArrayList&amp;lt;? extemds Product&amp;gt; list = new ArrayList&amp;lt;Audio&amp;gt;(); // ok
ArrayList&amp;lt;Product&amp;gt; list = new ArrayList&amp;lt;TV&amp;gt;(); //에러, parameterized type불일치
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;? extends T&amp;gt; : 와일드 카드와 상한 제한. T와 그 자손들만 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;? super T&amp;gt; : 와일드 카드와 하한 제한. T와 그 조상들만 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;?&amp;gt; : 제한 없음, 모든 타입이 가능 &amp;lt;? extends Object&amp;gt;와 동일&lt;/p&gt;</description>
      <category>Programming/Java, Spring</category>
      <category>generic</category>
      <category>java</category>
      <category>Java Generic</category>
      <category>자바</category>
      <category>자바 제네릭</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/56</guid>
      <comments>https://puenti.tistory.com/56#entry56comment</comments>
      <pubDate>Sat, 16 Jul 2022 15:47:38 +0900</pubDate>
    </item>
    <item>
      <title>[네트워크] 3-way-handShake와 TCP 와 UDP</title>
      <link>https://puenti.tistory.com/54</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3 - way - handShake란???&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;TCP&lt;/b&gt;&lt;/b&gt;와&lt;b&gt;&lt;b&gt; UDP&lt;/b&gt;&lt;/b&gt;의 차이를 알려면 먼저 3 - Way - Handshake를 먼저 알아야 한다!!&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;b&gt;3 - Way-Handshake&lt;/b&gt;란,&lt;/b&gt;&lt;/b&gt; 전송제어 프로토콜(TCP)에서 통신을 하는 장치간 서로 연결이 잘 되어있는지 확인하는 과정/방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 쉽게 말해서&amp;nbsp;송수신자&amp;nbsp;(데이터를 주고 받는 2사람이라고 생각하면 쉬울 것 같다)사이에 연결을 확인하는 과정이다.&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-filename=&quot;225A964D52F1BB6917.png&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;351&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eSvsVN/btrFt8Sf9vk/Z6pdKkeQYHXsUvFN6HbPF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eSvsVN/btrFt8Sf9vk/Z6pdKkeQYHXsUvFN6HbPF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eSvsVN/btrFt8Sf9vk/Z6pdKkeQYHXsUvFN6HbPF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeSvsVN%2FbtrFt8Sf9vk%2FZ6pdKkeQYHXsUvFN6HbPF1%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;550&quot; height=&quot;351&quot; data-filename=&quot;225A964D52F1BB6917.png&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;351&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;* TCP의 3-way Handshaking 과정&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;[STEP 1]&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;클라이언트는 서버에 접속을 요청하는 SYN 패킷을 보낸다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이때 클라이언트는 SYN 을 보내고 SYN/ACK 응답을 기다리는SYN_SENT 상태가 된다.&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;[STEP 2]&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;서버는 SYN요청을 받고 클라이언트에게 요청을 수락한다는 ACK 와 SYN flag 가 설정된 패킷을 발송하고 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;클라이언가 다시 ACK으로 응답하기를 기다린다. 이때 B서버는 SYN_RECEIVED 상태가 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;[STEP 3]&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;클라이언트는 서버에게 ACK을 보내고 이후로부터는 연결이 이루어지고 데이터가 오가게 되는것이다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이때의 서버 상태가 ESTABLISHED 이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;위와 같은 방식으로 통신하는것이 신뢰성 있는 연결을 맺어 준다는 TCP의 3 Way handshake 방식이다.&lt;/span&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;&amp;nbsp;&lt;/p&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;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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;b&gt;TCP와 UDP의 비교&lt;/b&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TCP(Transfer Control Protocol)&lt;/b&gt;는 연결형 서비스로 3-way handshaking 과정을 통해 연결을 설정한다.&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;b&gt;UDP(User Datagram Protocol)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;는 비연결성 서비스로 3-way handshaking을 사용하지 않기 때문이 빠르지만 신뢰성이 떨어지는 단점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt; TCP와 UDP의 차이는 3-way handshake을 쓰냐 안쓰냐, 속도의 차이, 신뢰성의 차이!!&lt;/b&gt;&lt;/span&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;span style=&quot;color: #212121;&quot;&gt;webstylez.egloos.com, &lt;span style=&quot;color: #212121;&quot;&gt;blazebyte.blogspot.kr&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>CS (Computer Science)/네트워크</category>
      <category>3way handshake</category>
      <category>tcp</category>
      <category>udp</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/54</guid>
      <comments>https://puenti.tistory.com/54#entry54comment</comments>
      <pubDate>Wed, 22 Jun 2022 18:42:58 +0900</pubDate>
    </item>
    <item>
      <title>[Programming] 자바와 자바스크립트 공통점과 차이점</title>
      <link>https://puenti.tistory.com/53</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;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;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바는 Sun에서 개발한 언어로 자바스크립트보다 먼저 나왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트는 NetScape 사에서 개발하였고 초기엔 Mocha, LiveScript라는 이름으로 변경되었지만&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;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 공통점 몇 개를 살펴보자면 이렇다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;자바 vs 자바스크립트 공통점&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;br /&gt;자바스크립트는 프론트엔드 개발에 주로 사용되어 왔지만 Node.js가 등장한 후로는 백엔드 개발이 가능해졌다.&lt;br /&gt;자바는 백엔드 개발에 주로 사용되어 왔지만 jsp를 통해 프론트엔드도 구현 할 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;자바 vs 자바스크립트 차이&lt;/b&gt;&lt;/h2&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;color: #ee2323;&quot;&gt;Java&lt;/span&gt;는 OOP 프로그래밍 언어인 반면, &lt;span style=&quot;color: #006dd7;&quot;&gt;JavaScript&lt;/span&gt;는 OOP 스크립팅 언어입니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Java&lt;/span&gt;는 가상 시스템 또는 브라우저에서 실행되는 응용 프로그램을 작성하는 반면, &lt;span style=&quot;color: #006dd7;&quot;&gt;JavaScript&lt;/span&gt; 코드는 브라우저에서만 실행됩니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Java&lt;/span&gt; 코드는 컴파일이 필요하지만, &lt;span style=&quot;color: #006dd7;&quot;&gt;JavaScript&lt;/span&gt; 코드는 모두 텍스트입니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Java&lt;/span&gt; 코드와 &lt;span style=&quot;color: #006dd7;&quot;&gt;JavaScript&lt;/span&gt; 코드 서로 다른 플러그인을 필요로 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ 스크립팅 언어 : 컴파일(compile)을 하지 않고, 작성해서 바로 실행시킬 수 있는 언어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※&amp;nbsp;컴파일(compile) : &lt;span&gt;인간이 구분하기 쉬운 언어로 작성된&lt;span&gt;&amp;nbsp;프로그램&lt;/span&gt;&lt;/span&gt;을 기계어(컴퓨터&amp;nbsp;&lt;span&gt;등의 기계가 이해할 수 있는 언어)로 번역하는 것&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;※ 플러그인 : 특정한 프로그램의 기능을 보강하기 위해 추가된 프로그램.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&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%; height: 108px;&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: 18px;&quot;&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 18px;&quot;&gt;Java&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 18px;&quot;&gt;JavaScript&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 18px;&quot;&gt;OOP 프로그래밍 언어&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 18px;&quot;&gt;OOP 스크립팅 언어&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 18px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;시스템 또는 브라우저에서 실행되는 &lt;b&gt;응용 프로그램에서 실행&lt;br /&gt;(ex. JVM)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 18px;&quot;&gt;&lt;b&gt;브라우저에서만 실행&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 18px;&quot;&gt;컴파일 O&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 18px;&quot;&gt;컴파일 X&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;출처 java.com&lt;/p&gt;</description>
      <category>Programming</category>
      <category>java</category>
      <category>Javascript</category>
      <category>자바</category>
      <category>자바 자바스크립트 차이</category>
      <category>자바스크립트</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/53</guid>
      <comments>https://puenti.tistory.com/53#entry53comment</comments>
      <pubDate>Sun, 19 Jun 2022 16:02:28 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] JSP &amp;amp; Servlet / JSP와 Servlet의 역할</title>
      <link>https://puenti.tistory.com/52</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;자바를 배우면 JSP, Servlet, Spring 에 대해서 많이 배우고 듣게 되는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 하나씩 알아보도록 하자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSP, Servlet, 스프링을 각각의 특징을 간단하게 알아보는 포스팅이라&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;서블릿(Servlet)&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;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;HTML in JAVA&lt;/b&gt;&lt;/span&gt;)&lt;b&gt;&lt;/b&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;b&gt;&lt;/b&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;스프링도 서블릿을 통해 동작을 하기 때문에!&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;JSP (Java Server Page)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 HTML 안에 자바코드를 삽입한 것이라 생각하자! (&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Java in HTML&lt;/span&gt;&lt;/b&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서블릿은 HTML 안에 자바 코드가 있는거라 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSP는 HTML을 코딩하기 어렵고 불편해서&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML 내부에 자바 코드를 삽입하는 형식이라 생각하면 된다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한마디로 서블릿의 단점을 보완한 &lt;b&gt;서블릿 기반&lt;/b&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;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;% 메서드 영역 %&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;%! 클래스 영역 %&amp;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;나중에는 &amp;lt;% %&amp;gt; 이렇게 쓰는것 마저 쓰는게 불편하다고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EL, JSTL 이라는 개념도 나왔는데&amp;nbsp;그건 추후에 따로 설명 할 수 있도록 하겠다!!&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;JSP와 Servlet 의 역할&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 내용만 보면 서블릿이나 JSP나 방법의 차이만 있을뿐 같은 역할을 한다는 것을 알 수 있다ㅣ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 자바를 통한 웹개발은 서블릿을 이용한 개발이었다고 한다. 추후에 서블릿의 단점을 보완하고자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSP가 나오고 JSP만을 이용한 개발(Model1 방식)이 유행하게 되고 지금에 와서 JSP와 서블릿의 역할을 나누어&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서블릿 + JSP 형태의 개발(Model2 방식)을 이루고 있는데 그게 바로 스프링의 핵심이라 할 수 있는 MVC 패턴이다!&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;JSP는 HTML 사용이 용이하고 자바코드 사용이 불편하기 때문에 View(Client) 담당을,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서블릿은 자바코드 작성이 편리하기 때문에 자료를 받아 모델에 저장하고 그 모델을 다시 화면에 전달하는 Controller를 담당하고 있다.&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-filename=&quot;스크린샷 2022-06-17 오후 10.45.16.png&quot; data-origin-width=&quot;734&quot; data-origin-height=&quot;555&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDnsPo/btrE3xTn6eD/g6FEStmYkXDwCDEvrwdkt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDnsPo/btrE3xTn6eD/g6FEStmYkXDwCDEvrwdkt1/img.png&quot; data-alt=&quot;출처 - 에이콘아카데미 공식블로그&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDnsPo/btrE3xTn6eD/g6FEStmYkXDwCDEvrwdkt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDnsPo%2FbtrE3xTn6eD%2Fg6FEStmYkXDwCDEvrwdkt1%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;734&quot; height=&quot;555&quot; data-filename=&quot;스크린샷 2022-06-17 오후 10.45.16.png&quot; data-origin-width=&quot;734&quot; data-origin-height=&quot;555&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 - 에이콘아카데미 공식블로그&lt;/figcaption&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;이렇게 서블릿과 JSP에서 알아보았고 더 나아가 이 둘을 활용한 스프링까지도 알아봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스프링을 알려면 서블릿, JSP를 알아야 한다!!&lt;/b&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;1. 서블릿 (&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;HTML in JAVA&lt;/b&gt;&lt;/span&gt;) - Controller 역할&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. JSP (&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Java in HTML&lt;/span&gt;&lt;/b&gt;) - View 역할&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 스프링 : 서블릿 + JSP&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 MVC 패턴은 이전 포스팅 한 글이 있어 참고하면 좋을 것 같다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://puenti.tistory.com/51&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.06.15 - [Frameworks/Spring] - [Spring] MVC (Model, View, Controller) 패턴이란??&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1655473651844&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;[Spring] MVC (Model, View, Controller) 패턴이란??&quot; data-og-description=&quot;MVC 패턴이란??? 모델-뷰-컨트롤러(model&amp;ndash;view&amp;ndash;controller, MVC)는&amp;nbsp;소프트웨어 공학에서 사용되는&amp;nbsp;소프트웨어 디자인 패턴이다. 이 패턴을 성공적으로 사용하면,&amp;nbsp;사용자 인터페이스로부터&amp;nbsp;비즈니스&quot; data-og-host=&quot;puenti.tistory.com&quot; data-og-source-url=&quot;https://puenti.tistory.com/51&quot; data-og-url=&quot;https://puenti.tistory.com/51&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/keodq/hyONTjiALk/bJqK0w6tMk7Ur1wzuK6voK/img.jpg?width=500&amp;amp;height=550&amp;amp;face=0_0_500_550,https://scrap.kakaocdn.net/dn/DF5w6/hyONKUcgKI/TK2Yi4zCpTQsT5yEsNG0u0/img.jpg?width=500&amp;amp;height=550&amp;amp;face=0_0_500_550,https://scrap.kakaocdn.net/dn/bymgLL/hyONHDbS7f/WDTMYkrjM4ldKnWZvwDIs1/img.jpg?width=612&amp;amp;height=706&amp;amp;face=0_0_612_706&quot;&gt;&lt;a href=&quot;https://puenti.tistory.com/51&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://puenti.tistory.com/51&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/keodq/hyONTjiALk/bJqK0w6tMk7Ur1wzuK6voK/img.jpg?width=500&amp;amp;height=550&amp;amp;face=0_0_500_550,https://scrap.kakaocdn.net/dn/DF5w6/hyONKUcgKI/TK2Yi4zCpTQsT5yEsNG0u0/img.jpg?width=500&amp;amp;height=550&amp;amp;face=0_0_500_550,https://scrap.kakaocdn.net/dn/bymgLL/hyONHDbS7f/WDTMYkrjM4ldKnWZvwDIs1/img.jpg?width=612&amp;amp;height=706&amp;amp;face=0_0_612_706');&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;[Spring] MVC (Model, View, Controller) 패턴이란??&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;MVC 패턴이란??? 모델-뷰-컨트롤러(model&amp;ndash;view&amp;ndash;controller, MVC)는&amp;nbsp;소프트웨어 공학에서 사용되는&amp;nbsp;소프트웨어 디자인 패턴이다. 이 패턴을 성공적으로 사용하면,&amp;nbsp;사용자 인터페이스로부터&amp;nbsp;비즈니스&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;puenti.tistory.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;</description>
      <category>Programming/Java, Spring</category>
      <category>jsp</category>
      <category>servlet</category>
      <category>spring</category>
      <category>서블릿</category>
      <category>스프링</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/52</guid>
      <comments>https://puenti.tistory.com/52#entry52comment</comments>
      <pubDate>Fri, 17 Jun 2022 22:52:23 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] MVC (Model, View, Controller) 패턴이란??</title>
      <link>https://puenti.tistory.com/51</link>
      <description>&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;MVC 패턴이란???&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;모델-뷰-컨트롤러&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #202122;&quot;&gt;model&amp;ndash;view&amp;ndash;controller, MVC&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;)는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4_%EA%B3%B5%ED%95%99&quot;&gt;소프트웨어 공학&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;에서 사용되는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4_%EB%94%94%EC%9E%90%EC%9D%B8_%ED%8C%A8%ED%84%B4&quot;&gt;소프트웨어 디자인 패턴&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;이다. 이 패턴을 성공적으로 사용하면,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%82%AC%EC%9A%A9%EC%9E%90_%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4&quot;&gt;사용자 인터페이스&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;로부터&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EB%B9%84%EC%A6%88%EB%8B%88%EC%8A%A4_%EB%A1%9C%EC%A7%81&quot;&gt;비즈니스 로직&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;을 분리하여&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%9D%91%EC%9A%A9_%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4&quot;&gt;애플리케이션&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;의 시각적 요소나 그 이면에서 실행되는 비즈니스 로직을 서로 영향 없이 쉽게 고칠 수 있는 애플리케이션을 만들 수 있다. MVC에서 모델은 애플리케이션의 정보(데이터)를 나타내며, 뷰는 텍스트, 체크박스 항목 등과 같은 사용자 인터페이스 요소를 나타내고, 컨트롤러는 데이터와 비즈니스 로직 사이의 상호동작을 관리한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #202122;&quot;&gt;&lt;span style=&quot;caret-color: #202122; background-color: #ffffff;&quot;&gt;출처 - 위키피디아, mvc 패턴&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #202122;&quot;&gt;&lt;span style=&quot;caret-color: #202122; background-color: #ffffff;&quot;&gt;사전적 의미는 위키피디아에서 가져왔지만 감이 아직 안잡힌다... 핵심만 간단히 풀어서 적어보려 한다.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #202122;&quot;&gt;&lt;span style=&quot;caret-color: #202122; background-color: #ffffff;&quot;&gt;먼저 MVC패턴을 설명하기에 앞서 옛날 개발자들의 말을 들어 보면 FrameWork 라는 개념이 없어 사람마다 코드가 뒤죽박죽이었다고 한다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #202122;&quot;&gt;&lt;span style=&quot;caret-color: #202122; background-color: #ffffff;&quot;&gt;또한 옛날 개발자들이 많은 고민 중 하나인 '어떻게 하면 좋은 설계를 할 수 있을까?' 하는 고민과 앞서 말했던 FrameWork의 필요성 때문에 나온 것이 바로&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #202122;&quot;&gt;&lt;span style=&quot;caret-color: #202122; background-color: #ffffff;&quot;&gt;MVC 패턴이라고 한다.&lt;/span&gt;&lt;/span&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;span style=&quot;color: #202122;&quot;&gt;&lt;span style=&quot;caret-color: #202122; background-color: #ffffff;&quot;&gt;개발공부를 하는 사람이라면 어떻게 하면 더 나은 코드를 설계할지 끊임없이 고민하고 생각해야한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #202122;&quot;&gt;&lt;span style=&quot;caret-color: #202122; background-color: #ffffff;&quot;&gt;좋은 설계의 핵심은 두 가지라고 생각한다! 바로 코드의 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;재사용성&lt;/b&gt;&lt;/span&gt;과 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;중복코드 제거&lt;/b&gt;&lt;/span&gt;!&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #202122;&quot;&gt;&lt;span style=&quot;caret-color: #202122; background-color: #ffffff;&quot;&gt;위 두가지를 충족시켜주려면 바로 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;분리&lt;/b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;를 잘해줘야한다!!!&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&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;span style=&quot;color: #202122;&quot;&gt;&lt;span style=&quot;caret-color: #202122; background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;MVC 패턴은 바로 이&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt; 분리&lt;/b&gt;&lt;/span&gt;에서부터 시작이 된다!&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;caret-color: #202122; background-color: #ffffff;&quot;&gt;자! 그러면 우리는 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;어떤 것을 어떻게 분리&lt;/span&gt;&lt;/b&gt;를 해줘야 할까?&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OOP 5대 설계 원칙인 SOLID 개념의 S(SRP)에 따르면 이렇다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;S(SRP) : 단일책임의 원칙 : 하나의 메서드는 하나의 책임(=관심사)만 진다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 좋은 설계를 위해서는 '&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;분리&lt;/b&gt;&lt;/span&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;b&gt;※ 관심사(concern) : 해야할 작업&lt;/b&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;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;변하는 것과 (자주) 변하지 않는 것(common, uncommon)&lt;/li&gt;
&lt;li&gt;공통(중복) 코드&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 위 세가지의 분리에 대해서 하나씩 알아보도록 하자&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 관심사의 분리&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1655273743873&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Controller
public class YoilTeller {
    @RequestMapping(&quot;/getYoil&quot;) // http://localhost:8080/ch2/getYoil?year=2021&amp;amp;month=10&amp;amp;day=1
    //    public static void main(String[] args) {
    public void main(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 1. 입력
//        String year = args[0];
//        String month = args[1];
//        String day = args[2];
        String year = request.getParameter(&quot;year&quot;);
        String month = request.getParameter(&quot;month&quot;);
        String day = request.getParameter(&quot;day&quot;);

        int yyyy = Integer.parseInt(year);
        int mm = Integer.parseInt(month);
        int dd = Integer.parseInt(day);

        // 2. 처리
        Calendar cal = Calendar.getInstance();
        cal.set(yyyy, mm - 1, dd);

        int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
        char yoil = &quot; 일월화수목금토&quot;.charAt(dayOfWeek);

        // 3. 출력
//        System.out.println(year + &quot;년 &quot; + month + &quot;월 &quot; + day + &quot;일은 &quot;);
//        System.out.println(yoil + &quot;요일입니다.&quot;);
        response.setContentType(&quot;text/html&quot;);    // 응답의 형식을 html로 지정
        response.setCharacterEncoding(&quot;utf-8&quot;);  // 응답의 인코딩을 utf-8로 지정
        PrintWriter out = response.getWriter();  // 브라우저로의 출력 스트림(out)을 얻는다.
        out.println(&quot;&amp;lt;html&amp;gt;&quot;);
        out.println(&quot;&amp;lt;head&amp;gt;&quot;);
        out.println(&quot;&amp;lt;/head&amp;gt;&quot;);
        out.println(&quot;&amp;lt;body&amp;gt;&quot;);
        out.println(year + &quot;년 &quot; + month + &quot;월 &quot; + day + &quot;일은 &quot;);
        out.println(yoil + &quot;요일입니다.&quot;);
        out.println(&quot;&amp;lt;/body&amp;gt;&quot;);
        out.println(&quot;&amp;lt;/html&amp;gt;&quot;);
        out.close();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 YoilTeller 클래스는 1. 입력, 2. 처리, 3. 출력 3가지의 관심사로 나눌 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 공통 코드의 분리 - 입력의 분리&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;입력은 보통 @Controller를 사용한다!&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;b&gt;중복코드가 제거 되고 코드가 간결해지므로 유지보수가 쉬워진다&lt;/b&gt;!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 변하는 것과 변하지 않는 것의 분리 - 출력(view)의 분리&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;rarr; 서로 다른 영역으로 분리되서 참조할 수 없다.&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;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;MODEL&lt;/b&gt;&lt;/span&gt;이다!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저 데이터 둘을 주고 받을 수 있는 부분의 객체 Model&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;MODEL&lt;/b&gt;&lt;/span&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;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;MODEL&lt;/b&gt;&lt;/span&gt;을 출력하는 부분에 전달한다!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;VIEW&lt;/b&gt;&lt;/span&gt;에서는 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;MODEL&lt;/b&gt;&lt;/span&gt;에서 전달받은 값을 출력한다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분리된 두 코드간의 데이터를 전달하기 위한 객체 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;MODEL&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력, 보여주는 부분이 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;VIEW&lt;/b&gt;&lt;/span&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;그래서 MVC 패턴의 Model, View, Controller 의 기능을 간단히 정리하자면&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;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;MVC 패턴&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전달 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;MODEL&lt;/b&gt;&lt;/span&gt; : 데이터 전달&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;View&lt;/b&gt;&lt;/span&gt; : 보여주는 부분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처리 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Controller&lt;/b&gt;&lt;/span&gt; : 작업을 처리함&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;MVC-Process.svg&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;550&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/V5sp9/btrER25NYPL/nIfLijKNJFtSAwGY5WrFkk/tfile.svg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/V5sp9/btrER25NYPL/nIfLijKNJFtSAwGY5WrFkk/tfile.svg&quot; data-alt=&quot;출처 - 위키피디아 MVC패턴&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/V5sp9/btrER25NYPL/nIfLijKNJFtSAwGY5WrFkk/tfile.svg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FV5sp9%2FbtrER25NYPL%2FnIfLijKNJFtSAwGY5WrFkk%2Ftfile.svg&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;500&quot; height=&quot;550&quot; data-filename=&quot;MVC-Process.svg&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;550&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 - 위키피디아 MVC패턴&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 MVC 패턴을 이용함으로서 view를 controller와 분리하고 나서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상황에 따른 view를 보여주는게 편리해졌고 코드 관리가 쉬워져 유지보수 또한 수월해졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex. 은행 거래내역 조회 1년치를 보고 싶다면 pdf, csv, excel 버튼을 누르면 해당 view를 다운받아서 볼 수 있다.&lt;/p&gt;</description>
      <category>Programming/Java, Spring</category>
      <category>mvc</category>
      <category>MVC패턴</category>
      <category>spring</category>
      <category>Spring MVC</category>
      <category>스프링</category>
      <category>스프링 MVC</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/51</guid>
      <comments>https://puenti.tistory.com/51#entry51comment</comments>
      <pubDate>Wed, 15 Jun 2022 15:36:32 +0900</pubDate>
    </item>
    <item>
      <title>[네트워크] HTTP(Hyper Text Transfer Protocol) 란? HTTP의 요청과 응답</title>
      <link>https://puenti.tistory.com/50</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;HTTP란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;HTTP&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;(&lt;/span&gt;&lt;b&gt;H&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;yper&lt;/span&gt;&lt;b&gt;T&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;ext&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;T&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;ransfer&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;P&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;rotocol&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;)는&lt;span&gt;&amp;nbsp;W3&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;상에서 정보를 주고받을 수 있는 프로토콜&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;이다.&lt;/span&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;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;&lt;b&gt;HTTP&lt;/b&gt;는&lt;span&gt;&amp;nbsp;클라이언트&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;와&lt;span&gt;&amp;nbsp;서버&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;사이에 이루어지는 요청/응답(request/response) 프로토콜이다. 예를 들면, 클라이언트인&lt;span&gt;&amp;nbsp;웹 브라우저&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;가 &lt;b&gt;HTTP&lt;/b&gt;를 통하여&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;주로&lt;span&gt;&amp;nbsp;HTML&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;문서를 주고받는 데에 쓰인다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;&lt;b&gt;HTTP&lt;/b&gt;는 상태를 가지고 있지 않는 Stateless 프로토콜이며 Method, Path, Version, Header, Body 등으로 구성된다!&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;그래서 HTTP는 클라이언트 정보를 저장하지 않아서 클라이언트를 구분하기 어렵다!&lt;/span&gt;&lt;/span&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;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;※ W3란 &lt;b&gt;월드 와이드 웹&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;(World Wide Web, WWW, W3)은&lt;span&gt;&amp;nbsp;인터넷&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;에 연결된&lt;span&gt;&amp;nbsp;컴퓨터&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;를 통해 사람들이 정보를 공유할 수 있는 전 세계적인 정보 공간을 말한다. 간단히&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;웹&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;(the Web)이라 부르는 경우가 많다.&lt;/span&gt;&lt;/span&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ Hyper Text는&amp;nbsp;&lt;span style=&quot;color: #000000;&quot;&gt;기존의&amp;nbsp;책과 같은 선형적인 텍스트가 아니라,&amp;nbsp;월드 와이드 웹에서 사용되는&amp;nbsp;하이퍼링크와 하이퍼텍스트를 통해서 이어지는 비선형적인 텍스트가 신개념이라는 의미에서 만들어진&amp;nbsp;용어이다.&amp;nbsp;HTML의 HT가 Hypertext의 줄임말이다.&lt;/span&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;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;※ 프로토콜(Protocol)은 서로 간의 통신을 위한 약속, 규칙이다. 서로 주고 받을 데이터에 대한 형식을 정의한 것이다!&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;HTTP 요청과 응답&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;HTTP 응답 메세지&lt;/b&gt;는 텍스트, 헤더, 바디로 이루어져 있는 편지와 같다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 HTTP 프로토콜은 요청편지를 보내고 응답편지를 받는 것과 같다고 생각하면 된다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 브라우저 주소창에 URL을 입력하면 브라우저는 요청 메세지를 만들어주고 그 요청 메세지를 만들어 서버에 보내주면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버는 받은 요청을 응답해준다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP 응답 메세지는 서버가 클라이언트한테 응답해주는 메세지 형식이라고 생각하면 된다!&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;HTTP 요청 메세지는 메서드를 통해 요청을 보낸다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 가지 메서드가 있지만 대표적인 GET과 POST를 알아보면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GET은 서버의 리소스를 가져오기 위한 설계라면 POST는 서버에 정보를 제공하기 위한 설계이다&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;span style=&quot;color: #000000;&quot;&gt;클라이언트 : 서비스를 요청하는 애플리케이션 or 컴퓨터 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;※ 서버 : 서비스를 제공하는 어플리케이션 or 컴퓨터&lt;/span&gt;&lt;/p&gt;</description>
      <category>CS (Computer Science)/네트워크</category>
      <category>HTTP</category>
      <category>http 프로토콜</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/50</guid>
      <comments>https://puenti.tistory.com/50#entry50comment</comments>
      <pubDate>Tue, 14 Jun 2022 14:41:50 +0900</pubDate>
    </item>
    <item>
      <title>[IntelliJ Error]war exploded: error during artifact deployment. see server log for details.</title>
      <link>https://puenti.tistory.com/49</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;인텔리제이로 메이븐을 통해서 스프링 프로젝트를 생성중 생긴 오류이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pom.xml에 &lt;b&gt;properties, dependency, plugin&lt;/b&gt;을 추가하고 톰캣과 연결후 연결이 잘되었는지 확인하는 과정에서 생긴 에러이다!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-06-08 오후 5.23.54.png&quot; data-origin-width=&quot;1421&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BdXsO/btrEjiVR3ji/Yc8zo1umbkeaH3VH6xxqLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BdXsO/btrEjiVR3ji/Yc8zo1umbkeaH3VH6xxqLk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BdXsO/btrEjiVR3ji/Yc8zo1umbkeaH3VH6xxqLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBdXsO%2FbtrEjiVR3ji%2FYc8zo1umbkeaH3VH6xxqLk%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;1421&quot; height=&quot;720&quot; data-filename=&quot;스크린샷 2022-06-08 오후 5.23.54.png&quot; data-origin-width=&quot;1421&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-06-09 오전 9.16.09.png&quot; data-origin-width=&quot;1421&quot; data-origin-height=&quot;868&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9kV1h/btrEjihgo4L/Rg0PDf83wjte6KiXUKIl21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9kV1h/btrEjihgo4L/Rg0PDf83wjte6KiXUKIl21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9kV1h/btrEjihgo4L/Rg0PDf83wjte6KiXUKIl21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9kV1h%2FbtrEjihgo4L%2FRg0PDf83wjte6KiXUKIl21%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;1421&quot; height=&quot;868&quot; data-filename=&quot;스크린샷 2022-06-09 오전 9.16.09.png&quot; data-origin-width=&quot;1421&quot; data-origin-height=&quot;868&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Project Structure -&amp;gt; Artifact에 Available Elements를 모두 삭제하고 다시 설정을 해줬는데도 안됐다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 에러 찾고 잡는다고 2시간정도 쌩 고생한거 같다...&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&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;b&gt;ideaProject / 만든 프로젝트 폴더에 들어가보면&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;src와 out 폴더가 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;out 폴더에 WEB-INF 페이지가 없어 src에 있는 WEB-INF 폴더를 복사해서 붙여넣어주니 해결...&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;잘 모르시는 분들을 위해 경로를 적어보자면&lt;/b&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;b&gt;&lt;b&gt;ideaProject / 만든 프로젝트 폴더 / out / artifacts / 내가 만든 프로젝트 폴더 / WEB-INF&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;를 복붙해서 넣어주면 해결되었다!!&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Error Note</category>
      <category>intelliJ Error</category>
      <category>인텔리제이 에러</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/49</guid>
      <comments>https://puenti.tistory.com/49#entry49comment</comments>
      <pubDate>Thu, 9 Jun 2022 09:21:42 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] JS로 그림판 만들기, save 기능 구현하기 [8/8]</title>
      <link>https://puenti.tistory.com/48</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://puenti.tistory.com/47&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.06.07 - [Practice] - [JavaScript] JS로 그림판 만들기, Fill 기능 구현하기! [7/8]&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1654614204744&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;[JavaScript] JS로 그림판 만들기, Fill 기능 구현하기! [7/8]&quot; data-og-description=&quot;2022.06.07 - [Practice] - [JavaScript] JS로 그림판 만들기, paint 색상, 붓 사이즈 바꾸기! [6/8] [JavaScript] JS로 그림판 만들기, paint 색상, 붓 사이즈 바꾸기! [6/9] 2022.06.06 - [Practice] - [JavaScr..&quot; data-og-host=&quot;puenti.tistory.com&quot; data-og-source-url=&quot;https://puenti.tistory.com/47&quot; data-og-url=&quot;https://puenti.tistory.com/47&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cy8qf1/hyOFJoPW1h/UVxFjJUMInCMm0Mqk6gJBk/img.png?width=800&amp;amp;height=827&amp;amp;face=0_0_800_827,https://scrap.kakaocdn.net/dn/btH6fB/hyOGVOJERH/YD4j6ofpDH4jCO2AKlfam1/img.png?width=800&amp;amp;height=827&amp;amp;face=0_0_800_827,https://scrap.kakaocdn.net/dn/msKv5/hyOFxoppAa/EaTdKwaPAlSWPWdOptGggK/img.png?width=1181&amp;amp;height=1221&amp;amp;face=0_0_1181_1221&quot;&gt;&lt;a href=&quot;https://puenti.tistory.com/47&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://puenti.tistory.com/47&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cy8qf1/hyOFJoPW1h/UVxFjJUMInCMm0Mqk6gJBk/img.png?width=800&amp;amp;height=827&amp;amp;face=0_0_800_827,https://scrap.kakaocdn.net/dn/btH6fB/hyOGVOJERH/YD4j6ofpDH4jCO2AKlfam1/img.png?width=800&amp;amp;height=827&amp;amp;face=0_0_800_827,https://scrap.kakaocdn.net/dn/msKv5/hyOFxoppAa/EaTdKwaPAlSWPWdOptGggK/img.png?width=1181&amp;amp;height=1221&amp;amp;face=0_0_1181_1221');&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;[JavaScript] JS로 그림판 만들기, Fill 기능 구현하기! [7/8]&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;2022.06.07 - [Practice] - [JavaScript] JS로 그림판 만들기, paint 색상, 붓 사이즈 바꾸기! [6/8] [JavaScript] JS로 그림판 만들기, paint 색상, 붓 사이즈 바꾸기! [6/9] 2022.06.06 - [Practice] - [JavaScr..&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;puenti.tistory.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;p data-ke-size=&quot;size16&quot;&gt;저번 포스팅은 Fill 기능까지 넣어줬다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅은 마지막 save 기능을 넣어주도록 해보자!&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;canvas는 pixel을 다루는거라 기본적으로 image를 만들면 download와 save는 이미 내장되어 있다!&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;app.js 에 아래 코드 추가&lt;/p&gt;
&lt;pre id=&quot;code_1654533823819&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  canvas.addEventListener(&quot;contextmenu&quot;, handleCM); // 이벤트 추가

function handleCM(event) {
  event.preventDefault();
}

function handleSaveClick() {
  const image = canvas.toDataURL();
  const link = document.createElement(&quot;a&quot;);
  link.href = image;
  link.download = &quot;PaintJS&quot;;
  link.click();
}

if (saveBtn) {
  saveBtn.addEventListener(&quot;click&quot;, handleSaveClick);
}&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-filename=&quot;스크린샷 2022-06-07 오전 1.43.11.png&quot; data-origin-width=&quot;1181&quot; data-origin-height=&quot;1287&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dxA47L/btrEaNUUfa7/rsB1kBKuOkI8kGnv6Osyu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dxA47L/btrEaNUUfa7/rsB1kBKuOkI8kGnv6Osyu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dxA47L/btrEaNUUfa7/rsB1kBKuOkI8kGnv6Osyu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdxA47L%2FbtrEaNUUfa7%2FrsB1kBKuOkI8kGnv6Osyu1%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;1181&quot; height=&quot;1287&quot; data-filename=&quot;스크린샷 2022-06-07 오전 1.43.11.png&quot; data-origin-width=&quot;1181&quot; data-origin-height=&quot;1287&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;save 버튼을 누르면 저장까지 잘되고 실행되는걸 볼 수 있다!!&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;app.js 의 전체 코드이다!&lt;/p&gt;
&lt;pre id=&quot;code_1654533994979&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const canvas = document.getElementById(&quot;jsCanvas&quot;);
const ctx = canvas.getContext(&quot;2d&quot;);
const colors = document.getElementsByClassName(&quot;jsColor&quot;);
const range = document.getElementById(&quot;jsRange&quot;);
const mode = document.getElementById(&quot;jsMode&quot;);
const saveBtn = document.getElementById(&quot;jsSave&quot;);

const INITIAL_COLOR = &quot;#2c2c2c&quot;;
const CANVAS_SIZE = 700;


//컨버스 크기
canvas.width = CANVAS_SIZE;
canvas.height = CANVAS_SIZE;


ctx.fillStyle = &quot;white&quot;;
ctx.fillRect(0, 0, CANVAS_SIZE, CANVAS_SIZE);
ctx.strokeStyle = INITIAL_COLOR; // 우리가 그릴 선들은 모두 이 색을 갖는다
ctx.fillStyle = INITIAL_COLOR;
ctx.lineWidth = 2.5; // 라인의 너비가 2.5

let painting = false; 
let filling = false; // 기본값은 안채워져있음

function stopPainting() {
    painting = false;
  }

function startPainting() {
    painting = true;
}

function onMouseMove(event){
    const x = event.offsetX;
    const y = event.offsetY;
    if (!painting) {
        ctx.beginPath(); // path는 선이다, path를 만들면 마우스의 x,y, 좌표로 path를 옮긴다
        ctx.moveTo(x, y);
      } else {
        ctx.lineTo(x, y); // lineTo는 path의 이전 위치에서 지금 위치까지 선을 만드는 것
        ctx.stroke();
      } // lineTo()와 stroke()는 마우스를 움직이는 내내 발생한다! 마우스를 움직이는 동안 계속 발생한다!
}

function handleModeClick() {
    if (filling === true) {
        filling = false;
        mode.innerText = &quot;Fill&quot;;
      } else {
        filling = true;
        mode.innerText = &quot;Paint&quot;;
      }
  }


function onMouseUp(event){
    painting = false;
}

function onMouseLeave(event){
    painting = false;
}

function handleRangeChange(event) {
    const size = event.target.value;
    ctx.lineWidth = size;
  }

function handleColorClick(event) {
    const color = event.target.style.backgroundColor;
    ctx.strokeStyle = color; // strokeStyle이 target에 있는 색상이 된다!
    ctx.fillStyle = color;
  }

  function handleCanvasClick() {
    if (filling) {
      ctx.fillRect(0, 0, CANVAS_SIZE, CANVAS_SIZE);
    }
  }

  function handleCM(event) {
    event.preventDefault();
  }

  function handleSaveClick() {
    const image = canvas.toDataURL();
    const link = document.createElement(&quot;a&quot;);
    link.href = image;
    link.download = &quot;PaintJS&quot;;
    link.click();
  }
  

if(canvas){
    canvas.addEventListener(&quot;mousemove&quot;, onMouseMove);
    canvas.addEventListener(&quot;mousedown&quot;, startPainting);
    canvas.addEventListener(&quot;mouseup&quot;, stopPainting);
    canvas.addEventListener(&quot;mouseleave&quot;, stopPainting);
    canvas.addEventListener(&quot;click&quot;, handleCanvasClick);
    canvas.addEventListener(&quot;contextmenu&quot;, handleCM);
}

Array.from(colors).forEach(color =&amp;gt;
    color.addEventListener(&quot;click&quot;, handleColorClick)
  );
  
if (range) {
    range.addEventListener(&quot;input&quot;, handleRangeChange);
}

if (mode) {
    mode.addEventListener(&quot;click&quot;, handleModeClick);
  }

if (saveBtn) {
    saveBtn.addEventListener(&quot;click&quot;, handleSaveClick);
}&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;되게 재밌게 했던 그림판 만들기 끝!!&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&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;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/Canvas_API/Tutorial&quot;&gt;https://developer.mozilla.org/ko/docs/Web/API/Canvas_API/Tutorial&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1654614210473&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;캔버스 튜토리얼 - Web API | MDN&quot; data-og-description=&quot;&amp;lt;canvas&amp;gt;는&amp;nbsp;HTML 요소 중 하나로서, 스크립트(보통은 자바스크립트)를 사용하여 그림을 그리는 데에 사용됩니다. 예를 들면, 그래프를 그리거나 사진을 합성하거나, 간단한(혹은&amp;nbsp;복잡할 수도 있는)&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/API/Canvas_API/Tutorial&quot; data-og-url=&quot;https://developer.mozilla.org/ko/docs/Web/API/Canvas_API/Tutorial&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/FzNU7/hyOFxaSDwY/VyUL6qRK6Op1LOHIT4TAK1/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/hY9Jb/hyOGTDoiGj/KDKMGaOnFQktEVY5D1iYm0/img.jpg?width=200&amp;amp;height=450&amp;amp;face=0_0_200_450&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/Canvas_API/Tutorial&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/API/Canvas_API/Tutorial&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/FzNU7/hyOFxaSDwY/VyUL6qRK6Op1LOHIT4TAK1/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/hY9Jb/hyOGTDoiGj/KDKMGaOnFQktEVY5D1iYm0/img.jpg?width=200&amp;amp;height=450&amp;amp;face=0_0_200_450');&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;캔버스 튜토리얼 - Web API | MDN&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lt;canvas&amp;gt;는&amp;nbsp;HTML 요소 중 하나로서, 스크립트(보통은 자바스크립트)를 사용하여 그림을 그리는 데에 사용됩니다. 예를 들면, 그래프를 그리거나 사진을 합성하거나, 간단한(혹은&amp;nbsp;복잡할 수도 있는)&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.mozilla.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>Practice</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/48</guid>
      <comments>https://puenti.tistory.com/48#entry48comment</comments>
      <pubDate>Wed, 8 Jun 2022 00:03:38 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] JS로 그림판 만들기, Fill 기능 구현하기! [7/8]</title>
      <link>https://puenti.tistory.com/47</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://puenti.tistory.com/46&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.06.07 - [Practice] - [JavaScript] JS로 그림판 만들기, paint 색상, 붓 사이즈 바꾸기! [6/8]&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1654531990632&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;[JavaScript] JS로 그림판 만들기, paint 색상, 붓 사이즈 바꾸기! [6/9]&quot; data-og-description=&quot;2022.06.06 - [Practice] - [JavaScript] JS로 그림판 만들기, 그림 그려보기[5/9] [JavaScript] JS로 그림판 만들기, 그림 그려보기[5/9] 2022.06.06 - [Programming/JavaScript] - [JavaScript] JS로 그림판 만..&quot; data-og-host=&quot;puenti.tistory.com&quot; data-og-source-url=&quot;https://puenti.tistory.com/46&quot; data-og-url=&quot;https://puenti.tistory.com/46&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/rUxqy/hyOFIQejWG/5lx7NuzA8oIhbZRkbxPSO0/img.png?width=800&amp;amp;height=827&amp;amp;face=0_0_800_827,https://scrap.kakaocdn.net/dn/bsboFZ/hyOFKgdcmR/om9dZ4NKTDjRBeGwcaor21/img.png?width=800&amp;amp;height=827&amp;amp;face=0_0_800_827,https://scrap.kakaocdn.net/dn/p1oeW/hyOFwCfiNq/bmUXwfH9KNpBb4BlISVaOk/img.png?width=1181&amp;amp;height=1221&amp;amp;face=0_0_1181_1221&quot;&gt;&lt;a href=&quot;https://puenti.tistory.com/46&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://puenti.tistory.com/46&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/rUxqy/hyOFIQejWG/5lx7NuzA8oIhbZRkbxPSO0/img.png?width=800&amp;amp;height=827&amp;amp;face=0_0_800_827,https://scrap.kakaocdn.net/dn/bsboFZ/hyOFKgdcmR/om9dZ4NKTDjRBeGwcaor21/img.png?width=800&amp;amp;height=827&amp;amp;face=0_0_800_827,https://scrap.kakaocdn.net/dn/p1oeW/hyOFwCfiNq/bmUXwfH9KNpBb4BlISVaOk/img.png?width=1181&amp;amp;height=1221&amp;amp;face=0_0_1181_1221');&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;[JavaScript] JS로 그림판 만들기, paint 색상, 붓 사이즈 바꾸기! [6/9]&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;2022.06.06 - [Practice] - [JavaScript] JS로 그림판 만들기, 그림 그려보기[5/9] [JavaScript] JS로 그림판 만들기, 그림 그려보기[5/9] 2022.06.06 - [Programming/JavaScript] - [JavaScript] JS로 그림판 만..&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;puenti.tistory.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;p data-ke-size=&quot;size16&quot;&gt;이제 fill 버튼 기능을 만들어보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fill 버튼을 누르고 원하는 색상을 클릭하면 컨버스 색상이 클릭한 색상으로 변하게 되는 기능이다!!&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;app.js&lt;/p&gt;
&lt;pre id=&quot;code_1654532321199&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const mode = document.getElementById(&quot;jsMode&quot;);

let filling = false;

if (mode) {
  mode.addEventListener(&quot;click&quot;, handleModeClick);
}

function handleModeClick() {
  if (filling === true) {
    filling = false;
    mode.innerText = &quot;Fill&quot;;
  } else {
    filling = true;
    mode.innerText = &quot;Paint&quot;;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 내가 filling mode에서 canvas를 클릭하면 canvas 전체에 색이 채워지게 한다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 filling mode인지 아닌지 알 수 있는게 필요하다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 기능을 해줄게 handleModeClick() 이다&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;버튼 Fill을 누르면 paint가 보이고 다시 한 번더 누르면 Fill이 나온다!&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_1654533062642&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const canvas = document.getElementById(&quot;jsCanvas&quot;);
const ctx = canvas.getContext(&quot;2d&quot;);
const colors = document.getElementsByClassName(&quot;jsColor&quot;);
const range = document.getElementById(&quot;jsRange&quot;);
const mode = document.getElementById(&quot;jsMode&quot;);

const INITIAL_COLOR = &quot;#2c2c2c&quot;;
const CANVAS_SIZE = 700;


//컨버스 크기
canvas.width = CANVAS_SIZE;
canvas.height = CANVAS_SIZE;


ctx.fillStyle = &quot;white&quot;;
ctx.fillRect(0, 0, CANVAS_SIZE, CANVAS_SIZE);
ctx.strokeStyle = INITIAL_COLOR; // 우리가 그릴 선들은 모두 이 색을 갖는다
ctx.fillStyle = INITIAL_COLOR;
ctx.lineWidth = 2.5; // 라인의 너비가 2.5

let painting = false; 
let filling = false; // 기본값은 안채워져있음

function stopPainting() {
    painting = false;
  }

function startPainting() {
    painting = true;
}

function onMouseMove(event){
    const x = event.offsetX;
    const y = event.offsetY;
    if (!painting) {
        ctx.beginPath(); // path는 선이다, path를 만들면 마우스의 x,y, 좌표로 path를 옮긴다
        ctx.moveTo(x, y);
      } else {
        ctx.lineTo(x, y); // lineTo는 path의 이전 위치에서 지금 위치까지 선을 만드는 것
        ctx.stroke();
      } // lineTo()와 stroke()는 마우스를 움직이는 내내 발생한다! 마우스를 움직이는 동안 계속 발생한다!
}

function handleModeClick() {
    if (filling === true) {
        filling = false;
        mode.innerText = &quot;Fill&quot;;
      } else {
        filling = true;
        mode.innerText = &quot;Paint&quot;;
      }
  }


function onMouseUp(event){
    painting = false;
}

function onMouseLeave(event){
    painting = false;
}

function handleRangeChange(event) {
    const size = event.target.value;
    ctx.lineWidth = size;
  }

function handleColorClick(event) {
    const color = event.target.style.backgroundColor;
    ctx.strokeStyle = color; // strokeStyle이 target에 있는 색상이 된다!
    ctx.fillStyle = color;
  }

  function handleCanvasClick() {
    if (filling) {
      ctx.fillRect(0, 0, CANVAS_SIZE, CANVAS_SIZE);
    }
  }

if(canvas){
    canvas.addEventListener(&quot;mousemove&quot;, onMouseMove);
    canvas.addEventListener(&quot;mousedown&quot;, startPainting);
    canvas.addEventListener(&quot;mouseup&quot;, stopPainting);
    canvas.addEventListener(&quot;mouseleave&quot;, stopPainting);
    canvas.addEventListener(&quot;click&quot;, handleCanvasClick);
}

Array.from(colors).forEach(color =&amp;gt;
    color.addEventListener(&quot;click&quot;, handleColorClick)
  );
  
if (range) {
    range.addEventListener(&quot;input&quot;, handleRangeChange);
}

if (mode) {
    mode.addEventListener(&quot;click&quot;, handleModeClick);
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;app.js가 이렇게 된다!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-06-07 오전 1.29.41.png&quot; data-origin-width=&quot;1181&quot; data-origin-height=&quot;1221&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/du7DCC/btrD2IVyNKa/Jw8K3MPLy68XG7XF0TykUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/du7DCC/btrD2IVyNKa/Jw8K3MPLy68XG7XF0TykUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/du7DCC/btrD2IVyNKa/Jw8K3MPLy68XG7XF0TykUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdu7DCC%2FbtrD2IVyNKa%2FJw8K3MPLy68XG7XF0TykUk%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;1181&quot; height=&quot;1221&quot; data-filename=&quot;스크린샷 2022-06-07 오전 1.29.41.png&quot; data-origin-width=&quot;1181&quot; data-origin-height=&quot;1221&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 자세하게 알고 싶으면 아래 사이트를 참고하면 문서로 잘 설명되어 있다!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/Canvas_API/Tutorial&quot;&gt;https://developer.mozilla.org/ko/docs/Web/API/Canvas_API/Tutorial&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1654614058391&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;캔버스 튜토리얼 - Web API | MDN&quot; data-og-description=&quot;&amp;lt;canvas&amp;gt;는&amp;nbsp;HTML 요소 중 하나로서, 스크립트(보통은 자바스크립트)를 사용하여 그림을 그리는 데에 사용됩니다. 예를 들면, 그래프를 그리거나 사진을 합성하거나, 간단한(혹은&amp;nbsp;복잡할 수도 있는)&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/API/Canvas_API/Tutorial&quot; data-og-url=&quot;https://developer.mozilla.org/ko/docs/Web/API/Canvas_API/Tutorial&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/FzNU7/hyOFxaSDwY/VyUL6qRK6Op1LOHIT4TAK1/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/hY9Jb/hyOGTDoiGj/KDKMGaOnFQktEVY5D1iYm0/img.jpg?width=200&amp;amp;height=450&amp;amp;face=0_0_200_450&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/Canvas_API/Tutorial&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/API/Canvas_API/Tutorial&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/FzNU7/hyOFxaSDwY/VyUL6qRK6Op1LOHIT4TAK1/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/hY9Jb/hyOGTDoiGj/KDKMGaOnFQktEVY5D1iYm0/img.jpg?width=200&amp;amp;height=450&amp;amp;face=0_0_200_450');&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;캔버스 튜토리얼 - Web API | MDN&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lt;canvas&amp;gt;는&amp;nbsp;HTML 요소 중 하나로서, 스크립트(보통은 자바스크립트)를 사용하여 그림을 그리는 데에 사용됩니다. 예를 들면, 그래프를 그리거나 사진을 합성하거나, 간단한(혹은&amp;nbsp;복잡할 수도 있는)&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.mozilla.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>Practice</category>
      <author>Juun</author>
      <guid isPermaLink="true">https://puenti.tistory.com/47</guid>
      <comments>https://puenti.tistory.com/47#entry47comment</comments>
      <pubDate>Tue, 7 Jun 2022 03:32:36 +0900</pubDate>
    </item>
  </channel>
</rss>