<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Inpa Dev  &amp;zwj; </title>
    <link>https://inpa.tistory.com/</link>
    <description>성장 욕구가 가파른 초보 개발자로서 공부한 내용을 쉽게 풀어쓴 기술 개발자 블로그를 운영하고 있습니다.</description>
    <language>ko</language>
    <pubDate>Wed, 15 Apr 2026 18:46:33 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>인파_</managingEditor>
    <image>
      <title>Inpa Dev  &amp;zwj; </title>
      <url>https://tistory1.daumcdn.net/tistory/4939852/attach/e06412764cbf478bbe4b9580196ea60a</url>
      <link>https://inpa.tistory.com</link>
    </image>
    <item>
      <title>[Cypress Studio]   더욱 간편해진 웹 테스트 자동화 툴</title>
      <link>https://inpa.tistory.com/entry/Cypress-Studio-%EB%8D%94%EC%9A%B1-%EA%B0%84%ED%8E%B8%ED%95%B4%EC%A7%84-%EC%9B%B9-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%9E%90%EB%8F%99%ED%99%94-%ED%88%B4</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;cypress-end-to-end-test.webp&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;630&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZbayB/btsFOTTi6SS/5K1CEEYIcXwrI1K5FvbEC0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZbayB/btsFOTTi6SS/5K1CEEYIcXwrI1K5FvbEC0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZbayB/btsFOTTi6SS/5K1CEEYIcXwrI1K5FvbEC0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZbayB%2FbtsFOTTi6SS%2F5K1CEEYIcXwrI1K5FvbEC0%2Fimg.webp&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; alt=&quot;cypress-studio&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;630&quot; data-filename=&quot;cypress-end-to-end-test.webp&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;630&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Cypress Studio 소개&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;앞선 시간에 자바스크립트에 익숙하다면 쉽게 접할 수 있는 테스트 라이브러리인 cypress 에 대해서 기본적인 사용법을 알아보았다.&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 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;---------------------------------------------------&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: #333333; text-align: start;&quot;&gt;기본적으로 테스팅을 위한 동작을 일일히 프로그래머가 코드를 짜야 하는데,&lt;span&gt; 그러면 비전공자혹은 비개발 직군이 E2E 테스트를 하기 위해선 프로그래밍을 배워야 하는 러닝 커브가 있으며, 또한 &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;테스트 케이스 수만큼 비슷한 E2E 테스트 코드를 반복적으로 작성해야 하여 테스트 코드 작성 시간이 오래걸리게 된다는 아쉬운점이 있다. &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;Cypress Studio란 Cypress에서 제공하는 테스트 코드 자동화 기능으로, 웹 애플리케이션과 상호작용하면서 테스트 코드를 알아서 생성해주는 기능이다. 즉, 코드를 작성하고 사용자 동작 시나리오를 구성하는것이 아닌, 거꾸로 &lt;b&gt;사용자의 마우스, 키보드 등의 액션과 페이지의 동작을 기록하여 자동으로 코드를 완성&lt;/b&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: #333333; text-align: start;&quot;&gt;Cypress를 새로 사용하거나 학습하는 사람들에게 유용하고 빠르게 테스트를 만들 수 있게 되어 업무 효율일 높일 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;다만 Cypress Studio는 아직 실험적인 기능이라 완성형 도구는 아님을 유의하자. 예를들어 Cypress Studio는 현재 E2E 테스트 작성에만 사용할 수 있으며, 여러 출처의 도메인을 사용하는 작성 테스트를 지원하지 않는다.&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Cypress Studio 사용법&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Cypress 설치하기&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기존 cypress를 설치하여 사용하고 있는 독자분들이라면, 이 단계는 그냥 건너뛰어도 된다. 하지만 cypress를 처음 접하시는 분들은 &lt;a href=&quot;https://www.cypress.io/install&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.cypress.io/install&lt;/a&gt; 링크에서 [Download Cypress.zip] 버튼을 눌러 exe 파일을 다운받자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1214&quot; data-origin-height=&quot;855&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4QmcS/btsFOUq766v/gOruWuw6hTyTLICcUs2l70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4QmcS/btsFOUq766v/gOruWuw6hTyTLICcUs2l70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4QmcS/btsFOUq766v/gOruWuw6hTyTLICcUs2l70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4QmcS%2FbtsFOUq766v%2FgOruWuw6hTyTLICcUs2l70%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; alt=&quot;cypress-studio&quot; loading=&quot;lazy&quot; width=&quot;1214&quot; height=&quot;855&quot; data-origin-width=&quot;1214&quot; data-origin-height=&quot;855&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그리고 압축 파일을 적절한곳에 압축을 풀고 경로의 파일을 실행하면, 런치패드(Launchpad) 화면이 띄워지게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;422&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qW3BQ/btsFLSu30p7/Ekwz1swVL0rczkSot4SQx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qW3BQ/btsFLSu30p7/Ekwz1swVL0rczkSot4SQx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qW3BQ/btsFLSu30p7/Ekwz1swVL0rczkSot4SQx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqW3BQ%2FbtsFLSu30p7%2FEkwz1swVL0rczkSot4SQx1%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; alt=&quot;cypress-studio&quot; loading=&quot;lazy&quot; width=&quot;646&quot; height=&quot;422&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;422&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;958&quot; data-origin-height=&quot;628&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5Xj69/btsFHt1Ky0f/xoci6vayOk0qGRlmTKMcMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5Xj69/btsFHt1Ky0f/xoci6vayOk0qGRlmTKMcMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5Xj69/btsFHt1Ky0f/xoci6vayOk0qGRlmTKMcMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5Xj69%2FbtsFHt1Ky0f%2Fxoci6vayOk0qGRlmTKMcMk%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; alt=&quot;cypress-studio&quot; loading=&quot;lazy&quot; width=&quot;958&quot; height=&quot;628&quot; data-origin-width=&quot;958&quot; data-origin-height=&quot;628&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;style3&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Cypress Studio 설정하기&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;초기 프로젝트 폴더 생성&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;먼저 중앙에 Drag your project directory here or browse manually 문구 버튼을 클릭하고 테스트 코드를 관리할 프로젝트 폴더를 선택해준다. 처음이라면 빈 폴더를 생성하고 등록해 주면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;958&quot; data-origin-height=&quot;473&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eQFs2n/btsFOolIqro/s9beyAFRPK6bvJtY50h2hk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eQFs2n/btsFOolIqro/s9beyAFRPK6bvJtY50h2hk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eQFs2n/btsFOolIqro/s9beyAFRPK6bvJtY50h2hk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeQFs2n%2FbtsFOolIqro%2Fs9beyAFRPK6bvJtY50h2hk%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; alt=&quot;cypress-studio&quot; loading=&quot;lazy&quot; width=&quot;958&quot; height=&quot;473&quot; data-origin-width=&quot;958&quot; data-origin-height=&quot;473&quot;/&gt;&lt;/span&gt;&lt;/figure&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&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;957&quot; data-origin-height=&quot;673&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bu2TUJ/btsFLSaOspJ/ezrjdMxl87jaGBAiZGndRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bu2TUJ/btsFLSaOspJ/ezrjdMxl87jaGBAiZGndRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bu2TUJ/btsFLSaOspJ/ezrjdMxl87jaGBAiZGndRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbu2TUJ%2FbtsFLSaOspJ%2FezrjdMxl87jaGBAiZGndRk%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; alt=&quot;cypress-studio&quot; loading=&quot;lazy&quot; width=&quot;957&quot; height=&quot;673&quot; data-origin-width=&quot;957&quot; data-origin-height=&quot;673&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;674&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7wBSk/btsFOEaRfnV/vApJyV8ztRkxpLQJm3WOeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7wBSk/btsFOEaRfnV/vApJyV8ztRkxpLQJm3WOeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7wBSk/btsFOEaRfnV/vApJyV8ztRkxpLQJm3WOeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7wBSk%2FbtsFOEaRfnV%2FvApJyV8ztRkxpLQJm3WOeK%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; alt=&quot;cypress-studio&quot; loading=&quot;lazy&quot; width=&quot;938&quot; height=&quot;674&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;674&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;957&quot; data-origin-height=&quot;669&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFDPaF/btsFLsQXa2l/qngXZVVkdGgztoShdIOA8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFDPaF/btsFLsQXa2l/qngXZVVkdGgztoShdIOA8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFDPaF/btsFLsQXa2l/qngXZVVkdGgztoShdIOA8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFDPaF%2FbtsFLsQXa2l%2FqngXZVVkdGgztoShdIOA8K%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; alt=&quot;cypress-studio&quot; loading=&quot;lazy&quot; width=&quot;957&quot; height=&quot;669&quot; data-origin-width=&quot;957&quot; data-origin-height=&quot;669&quot;/&gt;&lt;/span&gt;&lt;/figure&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;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;테스트 파일 생성&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;브라우저를 선택하면, 마치 웹앱과 같이 cypress 전용 웹애플리케이션이 새 창으로 열리게 된다. 새 테스트 파일을 생성하도록 하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1150&quot; data-origin-height=&quot;868&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bv1T8W/btsFMrKOxYa/lotwkwzRmD9msZMh9sixK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bv1T8W/btsFMrKOxYa/lotwkwzRmD9msZMh9sixK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bv1T8W/btsFMrKOxYa/lotwkwzRmD9msZMh9sixK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbv1T8W%2FbtsFMrKOxYa%2FlotwkwzRmD9msZMh9sixK1%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; alt=&quot;cypress-studio&quot; loading=&quot;lazy&quot; width=&quot;1150&quot; height=&quot;868&quot; data-origin-width=&quot;1150&quot; data-origin-height=&quot;868&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;732&quot; data-origin-height=&quot;351&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nNdeX/btsFM8DXniT/yikiQySCkRb9aaJQcKwmYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nNdeX/btsFM8DXniT/yikiQySCkRb9aaJQcKwmYK/img.png&quot; data-alt=&quot;테스트 코드 파일 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nNdeX/btsFM8DXniT/yikiQySCkRb9aaJQcKwmYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnNdeX%2FbtsFM8DXniT%2FyikiQySCkRb9aaJQcKwmYK%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; alt=&quot;cypress-studio&quot; loading=&quot;lazy&quot; width=&quot;652&quot; height=&quot;313&quot; data-origin-width=&quot;732&quot; data-origin-height=&quot;351&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;테스트 코드 파일 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;384&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXKkFR/btsFLQjL0aN/1Rgqx7R8l3e1EXSOoIHLAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXKkFR/btsFLQjL0aN/1Rgqx7R8l3e1EXSOoIHLAk/img.png&quot; data-alt=&quot;생성된 테스트 코드 파일 실행&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXKkFR/btsFLQjL0aN/1Rgqx7R8l3e1EXSOoIHLAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXKkFR%2FbtsFLQjL0aN%2F1Rgqx7R8l3e1EXSOoIHLAk%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; alt=&quot;cypress-studio&quot; loading=&quot;lazy&quot; width=&quot;652&quot; height=&quot;384&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;384&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;생성된 테스트 코드 파일 실행&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1148&quot; data-origin-height=&quot;553&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YJjNr/btsFF2KxJP5/2zPbUjjENZPti98mZwPL3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YJjNr/btsFF2KxJP5/2zPbUjjENZPti98mZwPL3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YJjNr/btsFF2KxJP5/2zPbUjjENZPti98mZwPL3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYJjNr%2FbtsFF2KxJP5%2F2zPbUjjENZPti98mZwPL3K%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; alt=&quot;cypress-studio&quot; loading=&quot;lazy&quot; width=&quot;1148&quot; height=&quot;553&quot; data-origin-width=&quot;1148&quot; data-origin-height=&quot;553&quot;/&gt;&lt;/span&gt;&lt;/figure&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;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Studio 설정 활성화 하기&lt;/b&gt;&lt;/h4&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;s&gt;cypress.config.js&lt;/s&gt; 파일을 열어서 안에 &lt;s&gt;experimentalStudio: true&lt;/s&gt;를 아래와 같이 추가해준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1152&quot; data-origin-height=&quot;602&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H9p1W/btsFGVRJ8LA/MfQ4U5ly4JbQbbJXXDY1Y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H9p1W/btsFGVRJ8LA/MfQ4U5ly4JbQbbJXXDY1Y1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H9p1W/btsFGVRJ8LA/MfQ4U5ly4JbQbbJXXDY1Y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH9p1W%2FbtsFGVRJ8LA%2FMfQ4U5ly4JbQbbJXXDY1Y1%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; alt=&quot;cypress-studio&quot; loading=&quot;lazy&quot; width=&quot;1152&quot; height=&quot;602&quot; data-origin-width=&quot;1152&quot; data-origin-height=&quot;602&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1122&quot; data-origin-height=&quot;584&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBq6GS/btsFFNfMMUn/VVBswHjVfN2X46wT3kEhY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBq6GS/btsFFNfMMUn/VVBswHjVfN2X46wT3kEhY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBq6GS/btsFFNfMMUn/VVBswHjVfN2X46wT3kEhY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBq6GS%2FbtsFFNfMMUn%2FVVBswHjVfN2X46wT3kEhY0%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; alt=&quot;cypress-studio&quot; loading=&quot;lazy&quot; width=&quot;1122&quot; height=&quot;584&quot; data-origin-width=&quot;1122&quot; data-origin-height=&quot;584&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/P0mpg/btsFE4PAZHa/daDmkzwybH38EMWsnDAvxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/P0mpg/btsFE4PAZHa/daDmkzwybH38EMWsnDAvxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/P0mpg/btsFE4PAZHa/daDmkzwybH38EMWsnDAvxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FP0mpg%2FbtsFE4PAZHa%2FdaDmkzwybH38EMWsnDAvxk%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; alt=&quot;cypress-studio&quot; loading=&quot;lazy&quot; width=&quot;486&quot; height=&quot;308&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;308&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1710126757318&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;module.exports = {
  e2e: {
    // 자동 테스트 코드 생성 설정
    experimentalStudio: true,

    setupNodeEvents(on, config) {
      // implement node event listeners here
    },
  },
};&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;코드를 저장하면, cypress studio 크롬 웹앱이 재부팅되게 되고, 상단에 '자동화된 테스트 소프트웨어에 의해 제어되고 있습니다'라고 표기가 되며, 다시 세팅으로 들어가서 Project settings 를 클릭하고 스크롤을 내리면 아래와 같이 옵션이 활성화가 된 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1102&quot; data-origin-height=&quot;730&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tnETQ/btsFFLvtCYq/Im0Wh1IOH6kscjz6tddEc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tnETQ/btsFFLvtCYq/Im0Wh1IOH6kscjz6tddEc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tnETQ/btsFFLvtCYq/Im0Wh1IOH6kscjz6tddEc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtnETQ%2FbtsFFLvtCYq%2FIm0Wh1IOH6kscjz6tddEc1%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; alt=&quot;cypress-studio&quot; loading=&quot;lazy&quot; width=&quot;1102&quot; height=&quot;730&quot; data-origin-width=&quot;1102&quot; data-origin-height=&quot;730&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;style3&quot; /&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Cypress Studio 시작하기&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;테스트 동작 코드를 수정하려면 위에서 생성한 spec.cy.js 파일을 수정하면 된다. 다만 우리는 사용자의 동작을 기록해서 자동으로 테스트 코드를 생성하는 기능을 하용할 것이기 때문에, 웹앱에서 cypress studio를 진행하도록 해보자.&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;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;자동 테스트 기록 &amp;amp; 생성&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;먼저 기록을 시작하기 위해선 아래와 같이 template spec 테스트 문구 옆에 마우스 커서를 살짝 올려보면 옆에 말풍선으로 Add New Test 문구가 뜨면서 클릭이 활성화 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;819&quot; data-origin-height=&quot;515&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/slaYp/btsFEO0U7QB/JMUYTcVkhWwvy0g1AeVXR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/slaYp/btsFEO0U7QB/JMUYTcVkhWwvy0g1AeVXR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/slaYp/btsFEO0U7QB/JMUYTcVkhWwvy0g1AeVXR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FslaYp%2FbtsFEO0U7QB%2FJMUYTcVkhWwvy0g1AeVXR1%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; alt=&quot;cypress-studio&quot; loading=&quot;lazy&quot; width=&quot;819&quot; height=&quot;515&quot; data-origin-width=&quot;819&quot; data-origin-height=&quot;515&quot;/&gt;&lt;/span&gt;&lt;/figure&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;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;www.naver.com&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;을 입력해보자.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;518&quot; data-origin-height=&quot;264&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rrUAo/btsFF0lXRRT/jnjoyVK5JHD8hFjczFsLQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rrUAo/btsFF0lXRRT/jnjoyVK5JHD8hFjczFsLQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rrUAo/btsFF0lXRRT/jnjoyVK5JHD8hFjczFsLQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrrUAo%2FbtsFF0lXRRT%2FjnjoyVK5JHD8hFjczFsLQK%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; alt=&quot;cypress-studio&quot; loading=&quot;lazy&quot; width=&quot;518&quot; height=&quot;264&quot; data-origin-width=&quot;518&quot; data-origin-height=&quot;264&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Continue 버튼을 누르게 되면 네이버 웹페이지로 이동하게 되는데, 이제 이 웹페이지에서 사용자가 행위, 동작을 하게되면 이에 맞게 왼쪽 로그 메뉴에 기록이 되게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1350&quot; data-origin-height=&quot;771&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/K11rv/btsFFYBHYSJ/htnny2pVSHHc9yZxoaUdOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/K11rv/btsFFYBHYSJ/htnny2pVSHHc9yZxoaUdOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/K11rv/btsFFYBHYSJ/htnny2pVSHHc9yZxoaUdOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FK11rv%2FbtsFFYBHYSJ%2Fhtnny2pVSHHc9yZxoaUdOK%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; alt=&quot;cypress-studio&quot; loading=&quot;lazy&quot; width=&quot;1350&quot; height=&quot;771&quot; data-origin-width=&quot;1350&quot; data-origin-height=&quot;771&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예를들어 네이버 검색창에 텍스트를 입력하고 우측의 돋보기 버튼을 클릭하는 동작을 행하면, 좌측에 이에 대한 행위값이 기록되게 되며, Save Commands 버튼을 누르면 이를 코드로 변환하게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1348&quot; data-origin-height=&quot;805&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mqHXH/btsFF8xftQL/K9pUM0Xz9uuUSOKx6w5gJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mqHXH/btsFF8xftQL/K9pUM0Xz9uuUSOKx6w5gJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mqHXH/btsFF8xftQL/K9pUM0Xz9uuUSOKx6w5gJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmqHXH%2FbtsFF8xftQL%2FK9pUM0Xz9uuUSOKx6w5gJ1%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; alt=&quot;cypress-studio&quot; loading=&quot;lazy&quot; width=&quot;1348&quot; height=&quot;805&quot; data-origin-width=&quot;1348&quot; data-origin-height=&quot;805&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1313&quot; data-origin-height=&quot;808&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LxQx1/btsFE2K3MTX/hK4cLKGQp253POcFeZL830/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LxQx1/btsFE2K3MTX/hK4cLKGQp253POcFeZL830/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LxQx1/btsFE2K3MTX/hK4cLKGQp253POcFeZL830/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLxQx1%2FbtsFE2K3MTX%2FhK4cLKGQp253POcFeZL830%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; alt=&quot;cypress-studio&quot; loading=&quot;lazy&quot; width=&quot;1313&quot; height=&quot;808&quot; data-origin-width=&quot;1313&quot; data-origin-height=&quot;808&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;375&quot; data-origin-height=&quot;167&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnMEO6/btsFIisbzfk/hF70O4oAFG8Zdln0yo2Us0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnMEO6/btsFIisbzfk/hF70O4oAFG8Zdln0yo2Us0/img.png&quot; data-alt=&quot;테스트 이름 입력&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnMEO6/btsFIisbzfk/hF70O4oAFG8Zdln0yo2Us0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnMEO6%2FbtsFIisbzfk%2FhF70O4oAFG8Zdln0yo2Us0%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; alt=&quot;cypress-studio&quot; loading=&quot;lazy&quot; width=&quot;375&quot; height=&quot;167&quot; data-origin-width=&quot;375&quot; data-origin-height=&quot;167&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;실제 테스트 코드는 위에서 생성하고 실행한 &lt;s&gt;spec.cy.js&lt;/s&gt; 파일을 오픈하면 아래와 같이 사용자의 행위가 자동으로 테스트 코드로 변환된 것을 확인할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1710127599289&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// template spec이라는 테스트 스위트를 정의합니다.
describe('template spec', () =&amp;gt; {

  /* ==== Test Created with Cypress Studio ==== */
  
  // 네이버라는 테스트 케이스를 정의합니다.
  it('네이버', function() {
    /* ==== Generated with Cypress Studio ==== */
    cy.visit('www.naver.com'); // www.naver.com 라는 웹사이트를 방문합니다.
    cy.get('#query').clear('r'); // id가 query인 요소를 찾아서 r이라는 문자를 지웁니다.
    cy.get('#query').type('검색어를 입력하세요'); // id가 query인 요소에 '검색어를 입력하세요'라고 타이핑합니다.
    cy.get('#search-btn').click();  // id가 search-btn인 요소를 클릭합니다.
    /* ==== End Cypress Studio ==== */
  });
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 생성된 테스트 코드 파일을 실행시키려면, GUI 앱에서 직접 파일을 클릭하면 자동으로 진행되게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;885&quot; data-origin-height=&quot;462&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckjQc0/btsFMz9Q4Wm/rxAs7C7qnPpnpPT1r2AKR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckjQc0/btsFMz9Q4Wm/rxAs7C7qnPpnpPT1r2AKR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckjQc0/btsFMz9Q4Wm/rxAs7C7qnPpnpPT1r2AKR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckjQc0%2FbtsFMz9Q4Wm%2FrxAs7C7qnPpnpPT1r2AKR0%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;885&quot; height=&quot;462&quot; data-origin-width=&quot;885&quot; data-origin-height=&quot;462&quot;/&gt;&lt;/span&gt;&lt;/figure&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;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;테스트 명령 이어서 추가하기&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;만일 추가적인 테스팅 동작 코드가 필요하다라고 한다면, 아래 사진처럼 테스트 그룹에 마우스 커서를 갖다대면, 'Add Commands to Test' 말충선이 뜨게 된다. 이걸 클릭하면 다시 Cypress Studio 가 발동되며 동작 코드 변환을 시행하게 된다. 추가 수정을 한뒤 이전 처럼 커맨드를 저장해주면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;875&quot; data-origin-height=&quot;728&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDMfDd/btsFMJxHAF1/OUa3RiOkjmKUIt9ok5eph0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDMfDd/btsFMJxHAF1/OUa3RiOkjmKUIt9ok5eph0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDMfDd/btsFMJxHAF1/OUa3RiOkjmKUIt9ok5eph0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDMfDd%2FbtsFMJxHAF1%2FOUa3RiOkjmKUIt9ok5eph0%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; alt=&quot;cypress-studio&quot; loading=&quot;lazy&quot; width=&quot;875&quot; height=&quot;728&quot; data-origin-width=&quot;875&quot; data-origin-height=&quot;728&quot;/&gt;&lt;/span&gt;&lt;/figure&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;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;엘리먼트 값 추적하기&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;내가 클릭한 메뉴나 버튼의 css 선택자를 알고 싶을 때가 있을 것이다. 이런 경우 개발자 도구를 열어 일일히 추적하기 보다는 cypress에서 제공해주는 기능을 이용하면 된다. 아래와 같이 간편히 셀렉터를 얻을 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;684&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lw7Yb/btsFD4ilDzh/2lNquPGFPDMqN1NgnKV3KK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lw7Yb/btsFD4ilDzh/2lNquPGFPDMqN1NgnKV3KK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lw7Yb/btsFD4ilDzh/2lNquPGFPDMqN1NgnKV3KK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flw7Yb%2FbtsFD4ilDzh%2F2lNquPGFPDMqN1NgnKV3KK%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; alt=&quot;cypress-studio&quot; loading=&quot;lazy&quot; width=&quot;986&quot; height=&quot;684&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;684&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;style3&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;참고문헌&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://docs.cypress.io/guides/references/cypress-studio&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot; data-swap-href=&quot;https://dev.to/lydiahallie/javascript-visualized-promises-async-await-5gke&quot;&gt;테스트 코드 자동으로 만들기 feat. Cypress Studio | 카카오엔터테인먼트 FE 기술블로그&lt;/p&gt;</description>
      <category>깜지</category>
      <category>Cypress</category>
      <category>cypress studio</category>
      <category>e2e</category>
      <category>qa</category>
      <category>테스트 자동화</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1114</guid>
      <comments>https://inpa.tistory.com/entry/Cypress-Studio-%EB%8D%94%EC%9A%B1-%EA%B0%84%ED%8E%B8%ED%95%B4%EC%A7%84-%EC%9B%B9-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%9E%90%EB%8F%99%ED%99%94-%ED%88%B4#entry1114comment</comments>
      <pubDate>Mon, 25 Mar 2024 09:33:24 +0900</pubDate>
    </item>
    <item>
      <title>[Cypress]   웹 테스트 자동화 사용법   한눈에 정리</title>
      <link>https://inpa.tistory.com/entry/Cypress-%F0%9F%93%9D-%EC%9B%B9-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%9E%90%EB%8F%99%ED%99%94-%EC%82%AC%EC%9A%A9%EB%B2%95-%F0%9F%91%80-%ED%95%9C%EB%88%88%EC%97%90-%EC%A0%95%EB%A6%AC</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;cypress-end-to-end-test.webp&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;630&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PJAiq/btsFACZXDyM/O45jZjmlNEuvOBfGE1PZ00/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PJAiq/btsFACZXDyM/O45jZjmlNEuvOBfGE1PZ00/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PJAiq/btsFACZXDyM/O45jZjmlNEuvOBfGE1PZ00/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPJAiq%2FbtsFACZXDyM%2FO45jZjmlNEuvOBfGE1PZ00%2Fimg.webp&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; alt=&quot;cypress-사용법&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;630&quot; data-filename=&quot;cypress-end-to-end-test.webp&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;630&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Cypress 소개&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;소프트웨어 테스팅은 소프트웨어의 품질을 보장하고 결함을 예방하고 수정하는 데 매우 중요한 과정이다. 특히 지속적이고 반복적이고 자주 일어나는 작업들은 가능한 &lt;b&gt;자동화&lt;/b&gt;하고 효율적으로 수행하는 것이 좋다.&amp;nbsp; &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;text-align: start;&quot;&gt;cypress는 웹애플리케이션을 테스트하기 위한 자바스크립트로 작성된 가벼운 라이브러리로, 실제&amp;nbsp;애플리케이션과&amp;nbsp;테스트&amp;nbsp;코드를&amp;nbsp;동일한&amp;nbsp;브라우저에서&amp;nbsp;실행하는&amp;nbsp;방식을&amp;nbsp;취하고&amp;nbsp;있다. &lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;브라우저 기반의 GUI를 사용하여 테스트의 실행 상태를 확인하고 디버깅할 수 있는 다양한 편의 기능을 제공한다.&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;63bc513bd59ddf30469e5dde_Untitled.gif&quot; data-origin-width=&quot;1368&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4dK4q/btsFDCLoZ4u/CgW3I0TSNqp9fTfdX4PQj0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4dK4q/btsFDCLoZ4u/CgW3I0TSNqp9fTfdX4PQj0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4dK4q/btsFDCLoZ4u/CgW3I0TSNqp9fTfdX4PQj0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/b4dK4q/btsFDCLoZ4u/CgW3I0TSNqp9fTfdX4PQj0/img.gif&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; alt=&quot;cypress-사용법&quot; loading=&quot;lazy&quot; width=&quot;1368&quot; height=&quot;720&quot; data-filename=&quot;63bc513bd59ddf30469e5dde_Untitled.gif&quot; data-origin-width=&quot;1368&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&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;text-align: start;&quot;&gt;예를들어 실행된 모든 테스트 명령과 각 명령이 실행될 때의 UI 상태를 스냅샷 형태로 모두 저장해 특정 시점의 UI 상태를 눈으로 확인할 수 있다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;또한 전체 테스트 진행 과정을 동영상으로 저장하거나 테스트가 실패했을 때 자동으로 스크린샷을 남길 수 있어 테스트가 실패했을 때 원인을 파악하기가 매우 쉽다.&amp;nbsp;&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&gt;브라우저에서 실행되기 때문에 필요한 경우 크롬 개발자 도구를 사용해 디버깅을 할 수도 있으며, 무엇보다&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;자바스크립트로 작성되었기 때문에 테스트 코드 역시&lt;span&gt;&amp;nbsp;&lt;/span&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;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;E2E 테&lt;/b&gt;&lt;b&gt;스트 도구&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;E2E( End-to-End ) 테스트란&amp;nbsp; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;'시작부터 끝까지'라는 의미로, &lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;애플리케이션&lt;/span&gt;이 제대로 작동하는지, &lt;b&gt;사용자 관점에서 시스템 흐름을 테스트&lt;/b&gt;하는 소프트웨어 테스트 방법이다. &lt;span style=&quot;background-color: #ffffff;&quot;&gt;이 테스트의 목적은 실제 생산 환경과 유사한 환경에서 시스템이 전체적인 비즈니스 목표에 충족하는지 확인하기 위해 실제 사용자 시나리오를 시뮬레이션하고 해당 구성 요소의 통합 및 데이터 무결성을 검증하는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1169&quot; data-origin-height=&quot;829&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRfPrg/btsFAE4F9cD/wqYvG2QnzJngUPJGj2mgbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRfPrg/btsFAE4F9cD/wqYvG2QnzJngUPJGj2mgbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRfPrg/btsFAE4F9cD/wqYvG2QnzJngUPJGj2mgbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRfPrg%2FbtsFAE4F9cD%2FwqYvG2QnzJngUPJGj2mgbK%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; alt=&quot;cypress-e2e&quot; loading=&quot;lazy&quot; width=&quot;503&quot; height=&quot;357&quot; data-origin-width=&quot;1169&quot; data-origin-height=&quot;829&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;이처럼, E2E 테스트는 테스트 방법론이며, Cypress는 이 방법론을 실제로 구현하기 위한 도구인 것이다.&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;Cypress &lt;span style=&quot;color: #ef5369;&quot;&gt;vs&lt;/span&gt; Selenium&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹애플리케이션을 테스트 자동화할때 가장 유명한 도구를 고르자면 이구동성으로 Seleniun(셀레니움)을 고를 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cypress와 Selenium은 웹 테스팅을 위한 도구로 각각 장단점과 차이점을 가지고 있다. 그렇다면 이 둘 중 어떤 도구를 선택해야 할까? 어떤 도구를 선택해야 할지는 테스팅의 목적, 범위, 요구사항, 환경 등에 따라 달라질 수 있다.&lt;/p&gt;
&lt;table style=&quot;color: #000000; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&lt;b&gt; cypress &lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt; selenium &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #000000; text-align: left;&quot;&gt;언어&lt;/td&gt;
&lt;td style=&quot;color: #000000; text-align: left;&quot;&gt;JavaScript만 지원&lt;/td&gt;
&lt;td style=&quot;color: #000000; text-align: left;&quot;&gt;Java, Python, JavaScript, C#, Ruby 등 다양한 언어 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #000000; text-align: left;&quot;&gt;지원 브라우저&lt;/td&gt;
&lt;td style=&quot;color: #000000; text-align: left;&quot;&gt;Chrome, Edge, Electron 일부 브라우저만 지원&lt;/td&gt;
&lt;td style=&quot;color: #000000; text-align: left;&quot;&gt;Chrome, Firefox, Edge, Safari, Opera 등 대부분의 브라우저 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #000000; text-align: left;&quot;&gt;난이도&lt;/td&gt;
&lt;td style=&quot;color: #000000; text-align: left;&quot;&gt;개발자 친화적이고, 테스트 환경 구축이 쉬움&lt;/td&gt;
&lt;td style=&quot;color: #000000; text-align: left;&quot;&gt;테스트 환경 구축이 복잡하고, 드라이버나 서버가 필요함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #000000; text-align: left;&quot;&gt;테스트 속도&lt;/td&gt;
&lt;td style=&quot;color: #000000; text-align: left;&quot;&gt;브라우저 내부에서 직접 테스트를 수행하므로 빠름&lt;/td&gt;
&lt;td style=&quot;color: #000000; text-align: left;&quot;&gt;브라우저 드라이버를 통해 테스트를 수행하므로 상대적으로 느림&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #000000; text-align: left;&quot;&gt;테스트 결과&lt;/td&gt;
&lt;td style=&quot;color: #000000; text-align: left;&quot;&gt;테스트 러너에서 테스트 과정과 결과를 시각적으로 확인 가능&lt;/td&gt;
&lt;td style=&quot;color: #000000; text-align: left;&quot;&gt;테스트 과정과 결과를 시각적으로 확인하기 어려움&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;color: #000000; text-align: left;&quot;&gt;테스트 범위&lt;/td&gt;
&lt;td style=&quot;color: #000000; text-align: left;&quot;&gt;프론트엔드 테스트에 특화되어 있음&lt;/td&gt;
&lt;td style=&quot;color: #000000; text-align: left;&quot;&gt;프론트엔드와 백엔드 테스트 모두 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하자면 다음과 같은 상황에서는 Cypress를 선택하는 것이 좋다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;프론트엔드 개발자가 자바스크립트로 테스트를 작성하고자 할 때&lt;/li&gt;
&lt;li&gt;모던 웹 애플리케이션의 UI와 기능을 테스트하고자 할 때&lt;/li&gt;
&lt;li&gt;테스트 환경을 쉽게 설정하고, 테스트 결과를 실시간으로 확인하고, 테스트를 디버깅하고자 할 때&lt;/li&gt;
&lt;li&gt;테스트 주도 개발을 수행하고자 할 때&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 상황에서는 Selenium을 선택하는 것이 좋다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;다양한 언어로 테스트를 작성하고자 할 때&lt;/li&gt;
&lt;li&gt;다양한 브라우저와 플랫폼에서 테스트를 수행하고자 할 때&lt;/li&gt;
&lt;li&gt;웹 서버를 통해 분산 테스팅이나 병렬 테스팅을 수행하고자 할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Cypress 설치하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.cypress.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.cypress.io/&lt;/a&gt; 홈페이지에 들어가면 설치를 진행 할 수가 있다. 두가지 방법으로 가능한데, 모두 소개해본다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;618&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZtPDT/btsFGnusQFn/s89d2WrDSmHrIKAh3CxOek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZtPDT/btsFGnusQFn/s89d2WrDSmHrIKAh3CxOek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZtPDT/btsFGnusQFn/s89d2WrDSmHrIKAh3CxOek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZtPDT%2FbtsFGnusQFn%2Fs89d2WrDSmHrIKAh3CxOek%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; alt=&quot;cypress-사용법&quot; loading=&quot;lazy&quot; width=&quot;1300&quot; height=&quot;618&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;618&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;&lt;b&gt;Node.js 패키지 설치&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;npm 명령으로 cypress를 설치하고 open 명령을 통해 cypress 앱을 실행 시킬 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1710136052782&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt; npm install cypress -D&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1710136518382&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt; npx cypress open # cypress 앱 오픈&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;cypress를 실행하면 아래와 같은 화면으로 넘어가게 되고 사진에 따라 진행해준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;957&quot; data-origin-height=&quot;673&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cScgud/btsFFY2HUmc/OkmCmwlSmY8k5vv5DOppD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cScgud/btsFFY2HUmc/OkmCmwlSmY8k5vv5DOppD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cScgud/btsFFY2HUmc/OkmCmwlSmY8k5vv5DOppD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcScgud%2FbtsFFY2HUmc%2FOkmCmwlSmY8k5vv5DOppD1%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; alt=&quot;cypress-사용법&quot; loading=&quot;lazy&quot; width=&quot;957&quot; height=&quot;673&quot; data-origin-width=&quot;957&quot; data-origin-height=&quot;673&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;674&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bd1Usn/btsFIwcMYIn/eFBrCfQw2zENd8eJFOJpmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bd1Usn/btsFIwcMYIn/eFBrCfQw2zENd8eJFOJpmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bd1Usn/btsFIwcMYIn/eFBrCfQw2zENd8eJFOJpmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbd1Usn%2FbtsFIwcMYIn%2FeFBrCfQw2zENd8eJFOJpmk%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; alt=&quot;cypress-사용법&quot; loading=&quot;lazy&quot; width=&quot;938&quot; height=&quot;674&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;674&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;957&quot; data-origin-height=&quot;669&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lNOGs/btsFEu8QRli/UXhRzJ1SofXG0bl42FaDA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lNOGs/btsFEu8QRli/UXhRzJ1SofXG0bl42FaDA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lNOGs/btsFEu8QRli/UXhRzJ1SofXG0bl42FaDA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlNOGs%2FbtsFEu8QRli%2FUXhRzJ1SofXG0bl42FaDA0%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; alt=&quot;cypress-사용법&quot; loading=&quot;lazy&quot; width=&quot;957&quot; height=&quot;669&quot; data-origin-width=&quot;957&quot; data-origin-height=&quot;669&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;&lt;b&gt;exe 설치&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;노드 생태계가 없어도 바로 직접 실행 파일로도 cypress 앱을 다운 받을 수가 있다. &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;압축 파일을 다운받으면, 파일을 적절한곳에 압축을 풀고 경로의 파일을 실행하면, 런치패드(Launchpad) 화면이 띄워지게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;exe 버전은 Cypress를 빠르게 사용해 볼 수 있는 방법으로만 시험적으로 제작된 도구이며, 몇가지 기능 제한이 따라 붙는다. 예를들어 Cypress Cloud로의 녹화 실행이 불가능하다고 한다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;778&quot; data-origin-height=&quot;394&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tzBNX/btsFGkZbFk3/JV2Ch8TKejTiU1IUNvpQA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tzBNX/btsFGkZbFk3/JV2Ch8TKejTiU1IUNvpQA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tzBNX/btsFGkZbFk3/JV2Ch8TKejTiU1IUNvpQA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtzBNX%2FbtsFGkZbFk3%2FJV2Ch8TKejTiU1IUNvpQA1%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; alt=&quot;cypress-사용법&quot; loading=&quot;lazy&quot; width=&quot;778&quot; height=&quot;394&quot; data-origin-width=&quot;778&quot; data-origin-height=&quot;394&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;422&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZg0OZ/btsFF9pjUWl/Q7SS3aetJS3pFMHZ9Iwkj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZg0OZ/btsFF9pjUWl/Q7SS3aetJS3pFMHZ9Iwkj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZg0OZ/btsFF9pjUWl/Q7SS3aetJS3pFMHZ9Iwkj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZg0OZ%2FbtsFF9pjUWl%2FQ7SS3aetJS3pFMHZ9Iwkj0%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; alt=&quot;cypress-사용법&quot; loading=&quot;lazy&quot; width=&quot;646&quot; height=&quot;422&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;422&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;958&quot; data-origin-height=&quot;473&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bahyva/btsFFkd9RyU/Aekq9eOnTEQJXoxgIXiZTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bahyva/btsFFkd9RyU/Aekq9eOnTEQJXoxgIXiZTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bahyva/btsFFkd9RyU/Aekq9eOnTEQJXoxgIXiZTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbahyva%2FbtsFFkd9RyU%2FAekq9eOnTEQJXoxgIXiZTK%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; alt=&quot;cypress-사용법&quot; loading=&quot;lazy&quot; width=&quot;958&quot; height=&quot;473&quot; data-origin-width=&quot;958&quot; data-origin-height=&quot;473&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 창에서 Drag your project directory here or browse manually 문구 버튼을 클릭하고 테스트 코드를 관리할 프로젝트 폴더를 선택해준다. 처음이라면 빈 폴더를 생성하고 등록해 주면 된다. 그리고 위에 Node.js 패키지 설치방법에 연재한 순서와 같이 똑같이 진행해주면 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Cypress 실행하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;테스트 프로젝트 생성&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;브라우저를 선택하면, 마치 웹앱과 같이 cypress 전용 GUI창이 새 브라우저 창으로 열리게 된다. 그리고 아래와 같이 새 테스트 파일을 생성하도록 하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1150&quot; data-origin-height=&quot;868&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vQff8/btsFE5OvyzL/V5sRE10c2IWdlpPgRgpMM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vQff8/btsFE5OvyzL/V5sRE10c2IWdlpPgRgpMM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vQff8/btsFE5OvyzL/V5sRE10c2IWdlpPgRgpMM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvQff8%2FbtsFE5OvyzL%2FV5sRE10c2IWdlpPgRgpMM1%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; alt=&quot;cypress-사용법&quot; loading=&quot;lazy&quot; width=&quot;1150&quot; height=&quot;868&quot; data-origin-width=&quot;1150&quot; data-origin-height=&quot;868&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;732&quot; data-origin-height=&quot;351&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSYgPG/btsFF9iA1Lh/yW2OnUBgCOpKyGu20E7iLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSYgPG/btsFF9iA1Lh/yW2OnUBgCOpKyGu20E7iLk/img.png&quot; data-alt=&quot;테스트 코드 파일 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSYgPG/btsFF9iA1Lh/yW2OnUBgCOpKyGu20E7iLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSYgPG%2FbtsFF9iA1Lh%2FyW2OnUBgCOpKyGu20E7iLk%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; alt=&quot;cypress-사용법&quot; loading=&quot;lazy&quot; width=&quot;652&quot; height=&quot;313&quot; data-origin-width=&quot;732&quot; data-origin-height=&quot;351&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;테스트 코드 파일 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;384&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VjclX/btsFGprUJRJ/V3oBneG877wgrKlCVgkJ11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VjclX/btsFGprUJRJ/V3oBneG877wgrKlCVgkJ11/img.png&quot; data-alt=&quot;생성된 테스트 코드 파일 실행&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VjclX/btsFGprUJRJ/V3oBneG877wgrKlCVgkJ11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVjclX%2FbtsFGprUJRJ%2FV3oBneG877wgrKlCVgkJ11%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; alt=&quot;cypress-사용법&quot; loading=&quot;lazy&quot; width=&quot;652&quot; height=&quot;384&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;384&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;생성된 테스트 코드 파일 실행&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1164&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJjb1d/btsFHufPeq7/wQeqkGzcTTHMMKaNjjkUrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJjb1d/btsFHufPeq7/wQeqkGzcTTHMMKaNjjkUrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJjb1d/btsFHufPeq7/wQeqkGzcTTHMMKaNjjkUrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJjb1d%2FbtsFHufPeq7%2FwQeqkGzcTTHMMKaNjjkUrK%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; alt=&quot;cypress-사용법&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;1164&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1164&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 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;테스트 시나리오 작성&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;테스트 동작 코드를 수정하려면 위에서 생성한 &lt;s&gt;spec.cy.js&lt;/s&gt; 파일을 수정하면 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;우선 에디터로 돌아와서 &lt;s&gt;spec.cy.js&lt;/s&gt; 파일을 오픈해보자. 그러면 아래와 같이 적혀져 있을 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1710139058370&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;describe('template spec', () =&amp;gt; {
  it('passes', () =&amp;gt; {
    cy.visit('https://example.cypress.io')
  })
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 api 설명은 아래와 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;s&gt;describe()&lt;/s&gt; : 테스트 할 동작들을 묶어주는 전체 그룹.&lt;/li&gt;
&lt;li&gt;&lt;s&gt;it()&lt;/s&gt; : 테스트 할 동작의 세부 단위 케이스.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;s&gt;cy.visit()&lt;/s&gt; : 웹사이트 방문.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;코드를 보면 JEST 테스트 라이브러리 코드 양식과 굉장히 유사하다고 볼 수 있다.&lt;/blockquote&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_1710139634199&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;describe('네이버 사이트 테스트 시트', () =&amp;gt; {
  it('네이버 사이트 방문', () =&amp;gt; {
    cy.visit('https://www.naver.com')
  })
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 cypress GUI 앱에서 아래와 같이 js 파일을 더블클릭하게 되면, 코드에 따라 자동화 테스트가 수행되게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1466&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bY4IXV/btsFIvrLUZL/yfAukyBctDrHUZeO83Oqp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bY4IXV/btsFIvrLUZL/yfAukyBctDrHUZeO83Oqp1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bY4IXV/btsFIvrLUZL/yfAukyBctDrHUZeO83Oqp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbY4IXV%2FbtsFIvrLUZL%2FyfAukyBctDrHUZeO83Oqp1%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; alt=&quot;cypress-사용법&quot; loading=&quot;lazy&quot; width=&quot;1466&quot; height=&quot;450&quot; data-origin-width=&quot;1466&quot; data-origin-height=&quot;450&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1195&quot; data-origin-height=&quot;628&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c6Ddjy/btsFHm9H9rg/jYpU3ahL6anEul5ePiLai0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c6Ddjy/btsFHm9H9rg/jYpU3ahL6anEul5ePiLai0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c6Ddjy/btsFHm9H9rg/jYpU3ahL6anEul5ePiLai0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc6Ddjy%2FbtsFHm9H9rg%2FjYpU3ahL6anEul5ePiLai0%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; alt=&quot;cypress-사용법&quot; loading=&quot;lazy&quot; width=&quot;1195&quot; height=&quot;628&quot; data-origin-width=&quot;1195&quot; data-origin-height=&quot;628&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;버튼 클릭 자동화&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cypress의 자바스크립트 코드는 jQuery 함수 형식과 되게 비슷하게 되어있다. 예를들어 자바스크립트나 제이쿼리에서 버튼 엘리먼트를 가져오려면 아래와 같이 코드를 작성하는데,&lt;/p&gt;
&lt;pre id=&quot;code_1710142392749&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;document.querySelector('button');
$('button')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cypress도 이와 비슷하게 &lt;s&gt;cy&lt;/s&gt; 객체의&amp;nbsp;&lt;s&gt;get()&lt;/s&gt; 함수를 통해 안에 엘리먼트의 id값이나 class값을 적으면 자동으로 html의 엘리먼트를 가져오게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1710142439640&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cy.get('button')

cy.get('.className');
cy.get('#idName');
cy.get('[class^=&quot;button_&quot;]')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 엘리먼트의 id값이나 class값은 개발자도구를 열어서 직접 추적해 얻는 방식도 있지만, cypress GUI 앱에서 더 간단히 얻을 수 있는 방법이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상단의 저격 크로스헤어 버튼을 누르고 아래처럼 클릭을 진행하면, 자동으로 cy 객체 코드가 완성되게 되고, 우측의 복사 버튼을 눌러 코드에 적용하면 되게 된다. 다만 복잡한 트리 구조의 구성일 경우 직접 개발자도구로 찾는것이 더 정확할 수도 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;908&quot; data-origin-height=&quot;727&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q5MNk/btsFGqdJxSO/AZstfir2Ueret1rwpBeeHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q5MNk/btsFGqdJxSO/AZstfir2Ueret1rwpBeeHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q5MNk/btsFGqdJxSO/AZstfir2Ueret1rwpBeeHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq5MNk%2FbtsFGqdJxSO%2FAZstfir2Ueret1rwpBeeHK%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; alt=&quot;cypress-사용법&quot; loading=&quot;lazy&quot; width=&quot;908&quot; height=&quot;727&quot; data-origin-width=&quot;908&quot; data-origin-height=&quot;727&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;그렇게해서 얻은 엘리먼트 객체를 뒤에 동작을 행하는 함수를 체이닝(chaining)으로 붙여주면 간단하게 버튼 클릭 행위를 자동화 할 수 있게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1710142478565&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cy.get('#button').click()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버튼 클릭에 대한 시나리오를 짜고 싶다면 아래 처럼 &lt;s&gt;wait()&lt;/s&gt; 함수와 적절히 섞어주면 마치 이른바 매크로를 구현할 수 있게 되는 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1710143470711&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;describe('무신사 사이트 테스트 시트', () =&amp;gt; {
    it('쇼핑몰 버튼 클릭', () =&amp;gt; {
        cy.visit('https://www.musinsa.com/app/'); // 무신사 방문
        cy.get('[u_cat_cd=&quot;001&quot;] &amp;gt; a').click(); // 특정 버튼 클릭
        cy.wait(1000); // 1초 대기
        cy.get('[u_cat_cd=&quot;002&quot;] &amp;gt; a').click();
        cy.wait(1000);
        cy.get('[u_cat_cd=&quot;003&quot;] &amp;gt; a').click();
        cy.wait(1000);
        cy.get('[u_cat_cd=&quot;004&quot;] &amp;gt; a').click();
    });
});&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;GIF 2024-03-11 오후 5-39-35.gif&quot; data-origin-width=&quot;1245&quot; data-origin-height=&quot;669&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rKzdy/btsFFMVXuD8/6CMoJoHxo04RfkKaVMPig1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rKzdy/btsFFMVXuD8/6CMoJoHxo04RfkKaVMPig1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rKzdy/btsFFMVXuD8/6CMoJoHxo04RfkKaVMPig1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/rKzdy/btsFFMVXuD8/6CMoJoHxo04RfkKaVMPig1/img.gif&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; alt=&quot;cypress-사용법&quot; loading=&quot;lazy&quot; width=&quot;1245&quot; height=&quot;669&quot; data-filename=&quot;GIF 2024-03-11 오후 5-39-35.gif&quot; data-origin-width=&quot;1245&quot; data-origin-height=&quot;669&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;&lt;b&gt;타이핑 자동화&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이른바 쇼핑몰의 검색창에 검색 텍스트를 자동으로 써서 검색을 수행하는 매크로를 만들어보자. 사용하는 함수 역시 직관적으로 &lt;s&gt;type()&lt;/s&gt; 을 통해 아주 간단히 구현할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1710145129786&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;describe('무신사 사이트 테스트 시트', () =&amp;gt; {
    it('쇼핑몰 버튼 클릭', () =&amp;gt; {
        // 무신사 사이트 방문
        cy.visit('https://www.musinsa.com/app/');

        // 무신사 검색
        cy.get('input#commonLayoutSearchForm').type('구찌 가방'); // 검색 입력창 엘리먼트를 가져와서 텍스트 타이핑
        cy.get('.sc-1ppcy5v-5').click(); // 돋보기 버튼 클릭

    });
});&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;GIF 2024-03-11 오후 5-43-50.gif&quot; data-origin-width=&quot;1245&quot; data-origin-height=&quot;669&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0xgbe/btsFGiU4ZQA/4WdiKbSLUFvaP1dNMIteN1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0xgbe/btsFGiU4ZQA/4WdiKbSLUFvaP1dNMIteN1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0xgbe/btsFGiU4ZQA/4WdiKbSLUFvaP1dNMIteN1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/b0xgbe/btsFGiU4ZQA/4WdiKbSLUFvaP1dNMIteN1/img.gif&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; alt=&quot;cypress-사용법&quot; loading=&quot;lazy&quot; width=&quot;1245&quot; height=&quot;669&quot; data-filename=&quot;GIF 2024-03-11 오후 5-43-50.gif&quot; data-origin-width=&quot;1245&quot; data-origin-height=&quot;669&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_1710145243098&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;describe('네이버 테스트', () =&amp;gt; {
    it('네이버 로그인', () =&amp;gt; {
        cy.visit('https://nid.naver.com/nidlogin.login');

        cy.get('#id').type('test');
        cy.get('#pw').type('123123');

        cy.get('#log\\.login').click();
    })
})&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;1106&quot; data-origin-height=&quot;664&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PP5cz/btsFHozLKEI/dxeITttMoUByb6L77OkITk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PP5cz/btsFHozLKEI/dxeITttMoUByb6L77OkITk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PP5cz/btsFHozLKEI/dxeITttMoUByb6L77OkITk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPP5cz%2FbtsFHozLKEI%2FdxeITttMoUByb6L77OkITk%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; alt=&quot;cypress-사용법&quot; loading=&quot;lazy&quot; width=&quot;1106&quot; height=&quot;664&quot; data-origin-width=&quot;1106&quot; data-origin-height=&quot;664&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;&lt;b&gt;드롭박스 자동 선택&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;&amp;lt;select&amp;gt;&lt;/s&gt; 태그의 엘리먼트를 선택하는 자동화 테스트를 구현해 보자. 직관적으로 &lt;s&gt;select()&lt;/s&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/b3LLDQ/btsFJKoxWG8/DpwaMEMLRVcpry48tUmQG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3LLDQ/btsFJKoxWG8/DpwaMEMLRVcpry48tUmQG0/img.png&quot; data-origin-width=&quot;433&quot; data-origin-height=&quot;239&quot; data-is-animation=&quot;false&quot; style=&quot;width: 51.163%; margin-right: 10px;&quot; data-widthpercent=&quot;51.76&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3LLDQ/btsFJKoxWG8/DpwaMEMLRVcpry48tUmQG0/img.png&quot; alt=&quot;cypress-사용법&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3LLDQ%2FbtsFJKoxWG8%2FDpwaMEMLRVcpry48tUmQG0%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;433&quot; height=&quot;239&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/C9KbZ/btsFI5GI22m/xxgg95MCkd4UzqqX2J7Dz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/C9KbZ/btsFI5GI22m/xxgg95MCkd4UzqqX2J7Dz1/img.png&quot; data-origin-width=&quot;471&quot; data-origin-height=&quot;279&quot; data-is-animation=&quot;false&quot; style=&quot;width: 47.6742%;&quot; data-widthpercent=&quot;48.24&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/C9KbZ/btsFI5GI22m/xxgg95MCkd4UzqqX2J7Dz1/img.png&quot; alt=&quot;cypress-사용법&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FC9KbZ%2FbtsFI5GI22m%2Fxxgg95MCkd4UzqqX2J7Dz1%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;471&quot; height=&quot;279&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1710148677924&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;describe('무신사 사이트 테스트 시트', () =&amp;gt; {
    it('상품 메뉴 선택하기', () =&amp;gt; {
        cy.visit('https://www.musinsa.com/app/goods/3791036?loc=goods_rank');

        // 상품 옵션 선택하기
        cy.get('.product-detail__sc-1d13nsy-1').select('M'); 
        cy.get('.product-detail__sc-1k1gum8-0').select('589908') 
    });
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;439&quot; data-origin-height=&quot;331&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AuueX/btsFFXchqGN/eT1YG16aOntAqgnIbyry1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AuueX/btsFFXchqGN/eT1YG16aOntAqgnIbyry1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AuueX/btsFFXchqGN/eT1YG16aOntAqgnIbyry1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAuueX%2FbtsFFXchqGN%2FeT1YG16aOntAqgnIbyry1K%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; alt=&quot;cypress-사용법&quot; loading=&quot;lazy&quot; width=&quot;439&quot; height=&quot;331&quot; data-origin-width=&quot;439&quot; data-origin-height=&quot;331&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;&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;blockquote data-ke-style=&quot;style3&quot;&gt;cypress의 함수 코드들은 기본적으로 비동기 함수들이며 실행 방식은 마치 자바스크립트의 await 비동기 방식&lt;br /&gt;으로 동작된다. 그래서 별도로 Promise나 await 처리 없이 테스트 코드들을 써내려가도 충돌없이 사용이 가능하다.&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Cypress 문법 정리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 종류의 api 문법을 알고하고 싶다면 아래 공식문서를 참고해보자. 이번 글은 cypress의 기본적인 뼈대 구조를 이해하는데 초점을 두어 설명해본다.&lt;/p&gt;
&lt;figure id=&quot;og_1710211725484&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Table of Contents | Cypress Documentation&quot; data-og-description=&quot;Commands&quot; data-og-host=&quot;docs.cypress.io&quot; data-og-source-url=&quot;https://docs.cypress.io/api/table-of-contents&quot; data-og-url=&quot;https://docs.cypress.io/api/table-of-contents&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cWeMmd/hyVxBKdOH7/4atabZ4xrKPpyrFgNJskn0/img.png?width=300&amp;amp;height=300&amp;amp;face=0_0_300_300,https://scrap.kakaocdn.net/dn/oTPP2/hyVxAdulSC/gdBEKe6maVolv09QBLZXY1/img.png?width=300&amp;amp;height=300&amp;amp;face=0_0_300_300&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://docs.cypress.io/api/table-of-contents&quot; data-source-url=&quot;https://docs.cypress.io/api/table-of-contents&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cWeMmd/hyVxBKdOH7/4atabZ4xrKPpyrFgNJskn0/img.png?width=300&amp;amp;height=300&amp;amp;face=0_0_300_300,https://scrap.kakaocdn.net/dn/oTPP2/hyVxAdulSC/gdBEKe6maVolv09QBLZXY1/img.png?width=300&amp;amp;height=300&amp;amp;face=0_0_300_300');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;Table of Contents | Cypress Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;Commands&lt;/p&gt;
&lt;p class=&quot;og-host&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;docs.cypress.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1710211725485&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Cypress.io: Kitchen Sink&quot; data-og-description=&quot;Cypress API The Cypress API enables you to configure the behavior of how Cypress works internally. You can do things like access Environment Variables, change configuration, create custom commands, and more.&quot; data-og-host=&quot;example.cypress.io&quot; data-og-source-url=&quot;https://example.cypress.io/&quot; data-og-url=&quot;https://example.cypress.io/&quot; data-og-image=&quot;&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://example.cypress.io/&quot; data-source-url=&quot;https://example.cypress.io/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('&amp;quot;&amp;quot;');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;Cypress.io: Kitchen Sink&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;Cypress API The Cypress API enables you to configure the behavior of how Cypress works internally. You can do things like access Environment Variables, change configuration, create custom commands, and more.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;example.cypress.io&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 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Cypress 그룹핑&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 명령어들을 보기좋게 모듈화하여 묶는 그룹 함수들이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;s&gt;describe()&lt;/s&gt;&amp;nbsp;: 테스트 코드를 묶는 가장 큰 단위. 테스트 스위트&lt;/li&gt;
&lt;li&gt;&lt;s&gt;context()&lt;/s&gt;&amp;nbsp;: describe 내부에서 새로운 테스트 그룹을 정의하는 함수 (context는 describe와 기능적으로는 차이가 없거 단지, 의미적으로 구분하기 위해 사용한다. 생략해도 문제 없다)&lt;/li&gt;
&lt;li&gt;&lt;s&gt;it()&lt;/s&gt;&amp;nbsp;: 실제 테스트 케이스를 정의하는 함수&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;// 'My App'이라는 테스트 스위트를 정의합니다.
describe('My App', () =&amp;gt; {

  // 'Login'이라는 테스트 그룹을 정의합니다. 이 테스트 그룹은 로그인 기능과 관련된 테스트 케이스들을 묶어줍니다.
  context('Login', () =&amp;gt; {
  
    // 'Valid user'라는 테스트 케이스를 정의합니다. 이 테스트 케이스는 유효한 사용자 정보로 로그인하는 것을 테스트합니다.
    it('Valid user', () =&amp;gt; {
      // 테스트할 내용을 작성합니다.
      cy.visit('/login'); // 로그인 페이지에 접속합니다.
      cy.get('#username').type('test'); // 아이디 입력창에 'test'라고 입력합니다.
      cy.get('#password').type('1234'); // 비밀번호 입력창에 '1234'라고 입력합니다.
      cy.get('#login-button').click(); // 로그인 버튼을 클릭합니다.
      cy.url().should('include', '/dashboard'); // 로그인이 성공하면 대시보드 페이지로 이동합니다.
    });
    
    // 'Invalid user'라는 테스트 케이스를 정의합니다. 이 테스트 케이스는 유효하지 않은 사용자 정보로 로그인하는 것을 테스트합니다.
    it('Invalid user', () =&amp;gt; {
      // 테스트할 내용을 작성합니다.
      cy.visit('/login'); // 로그인 페이지에 접속합니다.
      cy.get('#username').type('wrong'); // 아이디 입력창에 'wrong'이라고 입력합니다.
      cy.get('#password').type('4321'); // 비밀번호 입력창에 '4321'이라고 입력합니다.
      cy.get('#login-button').click(); // 로그인 버튼을 클릭합니다.
      cy.contains('Invalid username or password'); // 로그인이 실패하면 에러 메시지를 표시합니다.
    });
  });
  
  // 'Logout'이라는 테스트 그룹을 정의합니다. 이 테스트 그룹은 로그아웃 기능과 관련된 테스트 케이스들을 묶어줍니다.
  context('Logout', () =&amp;gt; {
  
    // 'Logout from dashboard'라는 테스트 케이스를 정의합니다. 이 테스트 케이스는 대시보드 페이지에서 로그아웃하는 것을 테스트합니다.
    it('Logout from dashboard', () =&amp;gt; {
      // 테스트할 내용을 작성합니다.
      cy.login('test', '1234'); // cy.login()은 커스텀 커맨드로, 로그인을 수행하는 함수입니다.
      cy.visit('/dashboard'); // 대시보드 페이지에 접속합니다.
      cy.get('#logout-button').click(); // 로그아웃 버튼을 클릭합니다.
      cy.url().should('include', '/login'); // 로그아웃이 성공하면 로그인 페이지로 이동합니다.
    });
    
  });
  
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Cypress Hook&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cypress hook는 &lt;b&gt;테스트 스위트의 실행 전후에 특정한 작업을 수행&lt;/b&gt;할 수 있게 해주는 기능이다. 이를 이용해 테스트 환경을 설정하거나, 테스트 데이터를 준비하거나, 테스트 결과를 정리하거나, 테스트 로그를 기록하거나, 테스트 에러를 처리하거나 등의 작업을 할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;s&gt;before()&lt;/s&gt; : 테스트 스위트의 첫 번째 테스트 케이스가 실행되기 전에 한 번만 실행&lt;/li&gt;
&lt;li&gt;&lt;s&gt;after()&lt;/s&gt; : 테스트 스위트의 마지막 테스트 케이스가 실행된 후에 한 번만 실행&lt;/li&gt;
&lt;li&gt;&lt;s&gt;beforeEach()&lt;/s&gt; : 테스트 스위트의 각 테스트 케이스가 실행되기 전에 매번 실행됩&lt;/li&gt;
&lt;li&gt;&lt;s&gt;afterEach()&lt;/s&gt; : 테스트 스위트의 각 테스트 케이스가 실행된 후에 매번 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Cypress hook은 describe()나 context() 안에 작성할 수 있으며, it()안에는 작성할 수 없다&lt;/blockquote&gt;
&lt;pre id=&quot;code_1710211709352&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;describe('Test E2E by Cypress', () =&amp;gt; {

  context('Mobile Version', () =&amp;gt; {
  
    // 이 hook은 Mobile Version 테스트 그룹의 각 테스트 케이스가 실행되기 전에 매번 실행. 
    beforeEach(() =&amp;gt; {
      // ... Codes executed before each test case inside it() hooks ...
    });
    
    // 이 테스트 케이스는 beforeEach() hook이 실행된 후에 실행
    it('Login Button Should be Somewhere', () =&amp;gt; {
      // ... Real Executing test codes for some application ...
    }); 
    
  });
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Cypress Assertion&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cypress Assertion이란 Cypress에서 테스트의 결과가 예상한 값과 일치하는지 검증하는 방법을 말한다. 예를들어 어느 제목의 텍스트가 특정 문자열과 일치하는지 확인하는데 이용된다.&lt;/p&gt;
&lt;pre id=&quot;code_1710383580586&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// h2태그의 text가 특정 제목인지 확인
cy.get('h2').should('have.text', '특정 제목');

// button을 클릭하고 나면 class에 active가 있어야 하는 경우
cy.get('button').click().should('have.class', 'active')

// 해당 객체가 없어야 하는 경우
cy.get('button').should('not.exist')

// 두 조건을 만족해야 하는 경우 (and)
cy.get('#header a')
  .should('have.class', 'active') // 클래스명이 active 일 경우
  .and('have.attr', 'href', '/users') // href 속성 값이 /users 일경우

// 체크박스가 disabled된 경우
cy.get(':checkbox').should('be.disabled')

// input에 특정 값이 없어야 하는 경우
cy.get('input').should('not.have.value', 'US')

// URL 로 호출을 하고, 해당 주소 RETURN BODY에 {name: 'inpa'}이 있어야 하는 경우
cy.request('/users/1').its('body').should('deep.eq', { name: 'inpa' })&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cypress Assertion은 비동기적으로 동작하며, 테스트 대상이 조건을 만족할 때까지 기다린다. 만약 조건을 만족하지 못하면, Cypress는 에러를 발생시키게 된다. 또한 체이닝(chaining)역시 가능하며, and나 or를 사용하여 여러 개의 조건을 연결할 수 있다.&amp;nbsp; &amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Cypress Commands&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그래밍에서 반복되는 코드가 있으면 이를 함수로 묶어 모듈화 하는것이 중요하다. cypress 역시 테스트를 자동화시킴에 따라 즐비하게 중복 및 반복되는 코드들이 많아질수 있는데, 이 역시 함수 단위로 묶어 사용하는 기능을 '사용자 정의 명령어' 라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 사용자 정의 명령어를 사용하기 위해서는, cypress 프로젝트의 support 폴더 내의 commands.js 파일을 이용할 필요가 있다.&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;251&quot; data-origin-height=&quot;354&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tNSVe/btsFLrRfPaA/amJeaCkIkW0N1xi2bhLQ7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tNSVe/btsFLrRfPaA/amJeaCkIkW0N1xi2bhLQ7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tNSVe/btsFLrRfPaA/amJeaCkIkW0N1xi2bhLQ7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtNSVe%2FbtsFLrRfPaA%2FamJeaCkIkW0N1xi2bhLQ7k%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; alt=&quot;cypress-사용법&quot; loading=&quot;lazy&quot; width=&quot;251&quot; height=&quot;354&quot; data-origin-width=&quot;251&quot; data-origin-height=&quot;354&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 새로운 사용자 정의 명령어를 추가하려면 commands.js 파일 안에 &lt;s&gt;Cypress.Commands.add&lt;/s&gt; 메소드를 사용하여 추가해주면 된다. 예를들어 로그인 프로세스는 아이디 입력, 비밀번호 입력, 로그인 버튼 클릭 이렇게 3개의 프로세스로 이루어져 있는데, 만일 로그인을 테스트하는데 있어 자주 이용하여 모듈화를 시킨다고 하면 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1710384608236&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Cypress.Commands.add('login', (email, password) =&amp;gt; {
  cy.get('input[name=email]').type(email);
  cy.get('input[name=password]').type(password);
  cy.get('form').submit();
});

/*
어렵게 생각할 필요없이 아래의 함수를 새로 만들어 사용한다고 보면 된다.

const login = (email, password) =&amp;gt; {
  cy.get('input[name=email]').type(email);
  cy.get('input[name=password]').type(password);
  cy.get('form').submit();
}
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 추가된 사용자 정의 명령어는 별도의 require나 import 없이도, Cypress에서 자동으로 인식하기 때문에 테스트 파일 어디에서나 전역적으로 사용할 수 있게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1710384800945&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;describe('네이버 테스트', () =&amp;gt; {
    it('네이버 로그인', () =&amp;gt; {
        cy.visit('https://nid.naver.com/nidlogin.login');
		
        // commands.js에 정의된 사용자 정의 명령어 수행
        cy.login('test', '123123')
    })
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Cypress then&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cypress &lt;s&gt;then()&lt;/s&gt; 메소드는 명령어와 결과를 콜백 함수의 인자로 전달해, 명령 결과에 액세스하고 이에 대한 추가 로직을 수행하는데 사용된다.&lt;/p&gt;
&lt;pre id=&quot;code_1710392614324&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cy.get('.list-item').then(($listItem) =&amp;gt; {
  // $listItem은 .list-item의 첫 번째 DOM 요소를 jQuery 객체로 감싼 것입니다.
  // 여기서 추가 작업을 수행할 수 있습니다.
  $listItem.css('color', 'red');
});

cy.get('.element').then(($el) =&amp;gt; {
  if ($el.hasClass('active')) {
    // .element가 'active' 클래스를 가지고 있으면 추가 작업을 수행합니다.
  }
});

 cy.request('https://jsonplaceholder.typicode.com/posts/1').then((response) =&amp;gt; {
    cy.log(response)
    
    expect(response.status).to.equal(200);
    expect(response.status).to.eq(200);
});&lt;/code&gt;&lt;/pre&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;cypress then 과 promise then 은 서로 연관 없다&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 유의할점은, Cypress의 then 키워드는 자바스크립트의 &lt;s&gt;Promise.then&lt;/s&gt; 과 전혀 아무런 관련이 없다는 점이다. 아무래도 코드 구문 생김새가 매우 비슷하여 이러한 착각을 할 수도 있지만, &lt;span style=&quot;color: #ee2323;&quot;&gt;Cypress의 then 키워드는 동기/비동기와 관련이 없는 명령어&lt;/span&gt;이다. 실지로 catch 문은 통하지 않는다는걸 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하자면, Cypress의 then 은 단순히 명령 결과에 액세스하고 이에 대한 추가 작업을 수행하는 데 사용되는 것일 뿐이다. 예를들어 cy 객체에서 지원하는 메소드로 체이닝해서 사용해도 충분하지만, 가져온 값에 뭘 더하거나 하는 등 별도의 추가 연산 작업이 필요할때 연산 코드를 then 블록 안에 쓰는 용도인 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1710467736583&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cy.get('option:first')
  .should('be.selected')
  .then(($option) =&amp;gt; {
    // $option is yielded
  })&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Cypress Wrapping&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;cy.wrap()&lt;/s&gt; 은 자바스크립트 객체나 값을 Cypress 명령어 체인에 포함시키기 위해 사용되는 메소드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 생각해서 간단하게 비유를 들자면 제이쿼리에서 제이쿼리 전용 체이닝 메소드를 사용하기 위해서는 &lt;s&gt;$(obj)&lt;/s&gt; 와 같이 $ 괄호 안에 객체를 넣어서 사용해야 했다. 이와 똑같은 원리라고 보면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1710386649107&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// JavaScript 객체를 cy.wrap()으로 감싸고, .should() 명령어를 사용하여 객체의 속성을 검사
const object = { name: 'Cypress' };
cy.wrap(object).should('have.property', 'name', 'Cypress');&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 래핑 기능은 주로 비동기적으로 반환된 값들을 cy 객체로 감싸 Cypress 명령어 체인에 포함시켜 테스트의 유연성을 높일 수가 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1710386706665&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cy.get('button').then(($button) =&amp;gt; {
  if ($button.hasClass('active')) {
    cy.wrap($button).click();
  }
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Cypress Window&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트할 웹페이지의 DOM에 접근해서 조작을 하고 싶을 상황이 있을 것이다. 이때&amp;nbsp;&lt;s&gt;cy.window&lt;/s&gt; 를 통해 현재 테스트 중인 페이지의 window 객체를 반환받을 수 있다. &lt;s&gt;cy.window&lt;/s&gt; 는 비동기적으로 실행되므로, 역시 위에서 배운 &lt;s&gt;.then()&lt;/s&gt; 메소드를 사용하여 결과를 처리해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1710386413658&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cy.window().then((win) =&amp;gt; {

  // win은 현재 페이지의 window 객체입니다.
  console.log('Window object:', win);
  
  // DOM 요소에 직접 접근하여 조작합니다.
  win.document.querySelector('button').click();
  
  // 페이지에 정의된 전역 변수나 함수에 접근합니다.
  console.log('Page title:', win.title);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Cypress Cookie&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 중에 사용자 인증 상태를 유지하거나, 특정 쿠키 값을 검사하는 등의 작업을 지원한다. &lt;s&gt;cy.getCookies()&lt;/s&gt; 명령어는 현재 브라우저의 모든 쿠키를 가져와 쿠키 객체 배열을 반환하며, 각 객체는 쿠키의 이름, 값, 도메인 등의 정보를 포함한다.&lt;/p&gt;
&lt;pre id=&quot;code_1710392531718&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cy.getCookies().then((cookies) =&amp;gt; {
  // cookies는 쿠키 객체 배열입니다.
});

cy.setCookie('auth_key', '123key');
cy.getCookie('auth_key').should('have.property', 'value', '123key');&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Cypress는 테스트 실행 시 기본적으로 모든 쿠키를 지우므로, 필요한 경우 테스트 시작 전에 쿠키를 설정해야 할 수도 있다.&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Cypress 설정 옵션&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cypress를 처음 시작하면 Cypress 구성 파일인 &lt;s&gt;cypress.config.js&lt;/s&gt;이 파일이 생성되게 되는데, 여기서 전반적은 cypress의 전역 설정들을 지정할 수가 있다. 자세한 여러 세팅값들은 아래 공식문서를 참고하면 된다.&lt;/p&gt;
&lt;figure id=&quot;og_1710144369416&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bdC9sn/hyVxqWbSvX/lK0QOrkaesslo7redT2R81/img.png?width=300&amp;amp;height=300&amp;amp;face=0_0_300_300,https://scrap.kakaocdn.net/dn/Zw2X4/hyVxvQLiH0/2WutM8lkkslXFBIBPsioQ1/img.png?width=300&amp;amp;height=300&amp;amp;face=0_0_300_300,https://scrap.kakaocdn.net/dn/bTYJqp/hyVxzS8JNc/HYH4Msy08bZnVuaWiH0Hkk/img.png?width=2544&amp;amp;height=1138&amp;amp;face=0_0_2544_1138&quot; data-og-url=&quot;https://docs.cypress.io/guides/references/configuration&quot; data-og-source-url=&quot;https://docs.cypress.io/guides/references/configuration&quot; data-og-host=&quot;docs.cypress.io&quot; data-og-description=&quot;This guide is for Cypress 10+ and the new JavaScript configuration file format.&quot; data-og-title=&quot;Configuration | Cypress Documentation&quot; data-og-type=&quot;website&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://docs.cypress.io/guides/references/configuration&quot; data-source-url=&quot;https://docs.cypress.io/guides/references/configuration&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bdC9sn/hyVxqWbSvX/lK0QOrkaesslo7redT2R81/img.png?width=300&amp;amp;height=300&amp;amp;face=0_0_300_300,https://scrap.kakaocdn.net/dn/Zw2X4/hyVxvQLiH0/2WutM8lkkslXFBIBPsioQ1/img.png?width=300&amp;amp;height=300&amp;amp;face=0_0_300_300,https://scrap.kakaocdn.net/dn/bTYJqp/hyVxzS8JNc/HYH4Msy08bZnVuaWiH0Hkk/img.png?width=2544&amp;amp;height=1138&amp;amp;face=0_0_2544_1138');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;Configuration | Cypress Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;This guide is for Cypress 10+ and the new JavaScript configuration file format.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;docs.cypress.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;브라우저 출처 정책 비활성화&lt;/b&gt;&lt;/h3&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;534&quot; data-origin-height=&quot;250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bichse/btsFIh8pYvJ/iDTz89D2DhC6IDKaw5kdu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bichse/btsFIh8pYvJ/iDTz89D2DhC6IDKaw5kdu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bichse/btsFIh8pYvJ/iDTz89D2DhC6IDKaw5kdu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbichse%2FbtsFIh8pYvJ%2FiDTz89D2DhC6IDKaw5kdu0%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; alt=&quot;cypress-사용법&quot; loading=&quot;lazy&quot; width=&quot;534&quot; height=&quot;250&quot; data-origin-width=&quot;534&quot; data-origin-height=&quot;250&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;훌륭한 보안 정책이지만 아무래도 테스트 하는 입장에서는 불필요한 부분이기 마련이다. 따라서 &lt;s&gt;chromeWebSecurity&lt;/s&gt; 옵션을 비활성함에 따라 동일 출처 정책 및 안전하지 않은 혼합 콘텐츠에 대해 Chromium 기반 브라우저의 웹 보안을 끌 수가 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1710145501606&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;module.exports = {
    e2e: {
        // 크로미움 동일 출처 정책 보안 비활성화
        chromeWebSecurity: false,
    },
};&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;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;페이지 로딩 제한시간 설정&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기본적으로 테스트 페이지 로딩 제한 시간이 60초로 설정이 되어잇기 때문에, 만일 로딩이 좀 오래걸리는 사이트를 테스트할 예정이라면 로딩 시간 초과로 테스트가 실패하기 때문에 추가 설정을 통해 로딩 제한 시간을 늘려주어야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1710144369418&quot; class=&quot;dts&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;module.exports = {
  e2e: {
    // 자동 테스트 코드 생성 설정
    experimentalStudio: true,

    // 페이지 로드 제한 시간 설정
    pageLoadTimeout: 120000 // 120sec
  },
};&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;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;페이지 크기 설정&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기본적으로 테스트할 웹페이지의 크기는 1000x660 으로 설정하였기 때문에 아래와 같이 웹페이지의 스타일에 따라 짤리는 현상이 발생하기도 한다. 추가 설정을 통해 페이지의 뷰포트를 맞출 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1351&quot; data-origin-height=&quot;757&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Vx9lA/btsFIise1RX/KzlfMDmM49iGOkdGf06321/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Vx9lA/btsFIise1RX/KzlfMDmM49iGOkdGf06321/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Vx9lA/btsFIise1RX/KzlfMDmM49iGOkdGf06321/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVx9lA%2FbtsFIise1RX%2FKzlfMDmM49iGOkdGf06321%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; alt=&quot;cypress-사용법&quot; loading=&quot;lazy&quot; width=&quot;1351&quot; height=&quot;757&quot; data-origin-width=&quot;1351&quot; data-origin-height=&quot;757&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1710144369418&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;module.exports = {
  e2e: {
     // 테스트 페이지 뷰포트 설정
     viewportWidth: 1600, 
     viewportHeight: 900,

    setupNodeEvents(on, config) {
      // implement node event listeners here
    },
  },
};&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;1343&quot; data-origin-height=&quot;632&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFU2gW/btsFJnUhdDa/lWSNc5jKPQRFOMuUeTRFkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFU2gW/btsFJnUhdDa/lWSNc5jKPQRFOMuUeTRFkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFU2gW/btsFJnUhdDa/lWSNc5jKPQRFOMuUeTRFkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFU2gW%2FbtsFJnUhdDa%2FlWSNc5jKPQRFOMuUeTRFkK%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; alt=&quot;cypress-사용법&quot; loading=&quot;lazy&quot; width=&quot;1343&quot; height=&quot;632&quot; data-origin-width=&quot;1343&quot; data-origin-height=&quot;632&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;style3&quot; /&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 class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://microsoft.github.io/code-with-engineering-playbook/automated-testing/e2e-testing/&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://docs.cypress.io/guides/overview/why-cypress&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;ttps://rae-gi.tistory.com/23&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://blog.naver.com/o_zak/222671883835&lt;/p&gt;</description>
      <category>깜지</category>
      <category>Cypress</category>
      <category>e2e</category>
      <category>jest</category>
      <category>qa</category>
      <category>Selenium</category>
      <category>test</category>
      <category>자동화</category>
      <category>테스트</category>
      <category>퍼펫티어</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1113</guid>
      <comments>https://inpa.tistory.com/entry/Cypress-%F0%9F%93%9D-%EC%9B%B9-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%9E%90%EB%8F%99%ED%99%94-%EC%82%AC%EC%9A%A9%EB%B2%95-%F0%9F%91%80-%ED%95%9C%EB%88%88%EC%97%90-%EC%A0%95%EB%A6%AC#entry1113comment</comments>
      <pubDate>Sun, 24 Mar 2024 01:19:50 +0900</pubDate>
    </item>
    <item>
      <title>  Window 10 컴퓨터 부팅 / 절전 시간 확인하기</title>
      <link>https://inpa.tistory.com/entry/%F0%9F%92%BB-Window-10-boot-log</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1616661883_File-Explorer-changes-its-icons.webp&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oafOg/btsf2WU9rXO/RUY1nvK8Dsq7cchd4Dn0y1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oafOg/btsf2WU9rXO/RUY1nvK8Dsq7cchd4Dn0y1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oafOg/btsf2WU9rXO/RUY1nvK8Dsq7cchd4Dn0y1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoafOg%2Fbtsf2WU9rXO%2FRUY1nvK8Dsq7cchd4Dn0y1%2Fimg.webp&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; alt=&quot;window-boot-time&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; data-filename=&quot;1616661883_File-Explorer-changes-its-icons.webp&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;윈도우 10 부팅 / 절전 시간 확인하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가끔 사내에서 출퇴근을 하였는데도 사원의 실수로 출퇴근을 찍지 않았거나 혹은 전산 오류로 인해 기록이 남아 있지 않은 경우, 자신의 출퇴근 기록을 증빙할 수 있는 자료를 요청할 수 있다. 이에 대한 대표적인 증명 방법으로 자신의 컴퓨터 시스템 부팅 기록을 확인하는 것인데, 이는 윈도우 OS의 이벤트 뷰어를 통해 확인 할 수가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;1.&lt;/span&gt; 이벤트 뷰어 실행하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 이벤트 뷰어를 실행한다. 방법은 다음과 같이 두가지로 이행이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;Win+X&lt;/s&gt; 키를 누르고 이벤트 뷰어 메뉴를 클릭하여 실행하거나,&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;473&quot; data-origin-height=&quot;581&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KtHsO/btsf3qV4Yg8/QWUhWks6N4plCBuuMlIe20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KtHsO/btsf3qV4Yg8/QWUhWks6N4plCBuuMlIe20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KtHsO/btsf3qV4Yg8/QWUhWks6N4plCBuuMlIe20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKtHsO%2Fbtsf3qV4Yg8%2FQWUhWks6N4plCBuuMlIe20%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; alt=&quot;window-boot-time&quot; loading=&quot;lazy&quot; width=&quot;473&quot; height=&quot;581&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;473&quot; data-origin-height=&quot;581&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;Win+R&lt;/s&gt; 키를 눌르고 실행 입력창에 &quot;eventvwr&quot; 를 적고 실행하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;397&quot; data-origin-height=&quot;203&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qfqma/btsfjIypu33/ijdcRANutk7kHxcSCoKda0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qfqma/btsfjIypu33/ijdcRANutk7kHxcSCoKda0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qfqma/btsfjIypu33/ijdcRANutk7kHxcSCoKda0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fqfqma%2FbtsfjIypu33%2FijdcRANutk7kHxcSCoKda0%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; alt=&quot;window-boot-time&quot; loading=&quot;lazy&quot; width=&quot;397&quot; height=&quot;203&quot; data-origin-width=&quot;397&quot; data-origin-height=&quot;203&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;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;2.&lt;/span&gt; 로그 필터링 하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 컴퓨터 동작을 나타내는 윈도우 이벤트 ID 라는 것이 존재하는데, 그 수가 무수히 많다. 따라서 이들 중에 '부팅' 관련된 이벤트 ID만을 필터링해야 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 뷰어 창이 뜨면 [Windows 로그] &amp;rarr; [시스템 메뉴]를 선택하고, 우측 작업 항목 중 [현재 로그 필터링] 메뉴를 선택한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;694&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cONf6s/btsfWXubYmo/9R5QgAkHF4u7viGlKY4Pt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cONf6s/btsfWXubYmo/9R5QgAkHF4u7viGlKY4Pt0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cONf6s/btsfWXubYmo/9R5QgAkHF4u7viGlKY4Pt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcONf6s%2FbtsfWXubYmo%2F9R5QgAkHF4u7viGlKY4Pt0%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; alt=&quot;window-boot-time&quot; loading=&quot;lazy&quot; width=&quot;1219&quot; height=&quot;694&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;694&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 로그 필터링 창에서 찾고자 하는 행위의 이벤트 ID를 입력하고 확인을 눌러주면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;541&quot; data-origin-height=&quot;549&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Vsfza/btsf2IpqqIQ/AgaQbPEEOoNELvVkCpuKdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Vsfza/btsf2IpqqIQ/AgaQbPEEOoNELvVkCpuKdK/img.png&quot; data-alt=&quot;6005번과 6006번은 컴퓨터 부팅 관련 이벤트 ID 이다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Vsfza/btsf2IpqqIQ/AgaQbPEEOoNELvVkCpuKdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVsfza%2Fbtsf2IpqqIQ%2FAgaQbPEEOoNELvVkCpuKdK%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; alt=&quot;window-boot-time&quot; loading=&quot;lazy&quot; width=&quot;541&quot; height=&quot;549&quot; data-origin-width=&quot;541&quot; data-origin-height=&quot;549&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;6005번과 6006번은 컴퓨터 부팅 관련 이벤트 ID 이다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 중앙 로그 부분에 이벤트 ID 6005 와 6006 번이 필터링 되어 날자 및 시간 별로 부팅 로그들이 날짜 별로 정렬되게 된다.&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;1142&quot; data-origin-height=&quot;652&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEvGLW/btsftJX886A/MWbNjkChizJVfY1umk8041/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEvGLW/btsftJX886A/MWbNjkChizJVfY1umk8041/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEvGLW/btsftJX886A/MWbNjkChizJVfY1umk8041/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEvGLW%2FbtsftJX886A%2FMWbNjkChizJVfY1umk8041%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; alt=&quot;window-boot-time&quot; loading=&quot;lazy&quot; width=&quot;1142&quot; height=&quot;652&quot; data-origin-width=&quot;1142&quot; data-origin-height=&quot;652&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 로그를 더블 클릭하면 언제 시스템이 시작되었고 시스템 종료가 되었는지 구체적인 날짜 정보와 내용을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1250&quot; data-origin-height=&quot;435&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dFCybV/btsf0WBD3Sc/2kQ8Zqe041YmYdMTE7iUc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dFCybV/btsf0WBD3Sc/2kQ8Zqe041YmYdMTE7iUc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dFCybV/btsf0WBD3Sc/2kQ8Zqe041YmYdMTE7iUc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdFCybV%2Fbtsf0WBD3Sc%2F2kQ8Zqe041YmYdMTE7iUc0%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; alt=&quot;window-boot-time&quot; loading=&quot;lazy&quot; width=&quot;1250&quot; height=&quot;435&quot; data-origin-width=&quot;1250&quot; data-origin-height=&quot;435&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;&lt;b&gt;컴퓨터 &lt;span style=&quot;color: #ef5369;&quot;&gt;부팅&lt;/span&gt; 관련 이벤트 ID&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;6005 : 이벤트 로그 시작, 부팅 기록&lt;/li&gt;
&lt;li&gt;6006 : 정상적인 시스템 종료 기록&lt;/li&gt;
&lt;li&gt;6008&amp;nbsp;:&amp;nbsp;비정상&amp;nbsp;시스템&amp;nbsp;종료&amp;nbsp;기록&lt;/li&gt;
&lt;/ul&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;컴퓨터 &lt;span style=&quot;color: #ef5369;&quot;&gt;절전 모드&lt;/span&gt; 관련 이벤트 ID&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;절전모드 관련 이벤트는 조금 겹치는 ID가 많다. 이 중에는 더이상 사용되지 않는 ID도 있기도 하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle; background-color: #ffffff; color: #1f1f1f; text-align: start;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;1 or 15 or 108 : 컴퓨터가&amp;nbsp;절전&amp;nbsp;모드에서&amp;nbsp;다시&amp;nbsp;시작되었음&lt;/li&gt;
&lt;li&gt;42 or 107 : 컴퓨터가 절전 모드로 전환&lt;/li&gt;
&lt;li&gt;109 : 컴퓨터가 절전 모드에서 취소&lt;/li&gt;
&lt;li&gt;110 : 컴퓨터가 절전 모드로 전환을 시도했지만 실패&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;540&quot; data-origin-height=&quot;549&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bk0IVR/btsfOJiGDC3/5wseiMJIrEIX4G0cL6rI2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bk0IVR/btsfOJiGDC3/5wseiMJIrEIX4G0cL6rI2K/img.png&quot; data-alt=&quot;1번, 42번, 107번 이면 충분할것 같다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bk0IVR/btsfOJiGDC3/5wseiMJIrEIX4G0cL6rI2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbk0IVR%2FbtsfOJiGDC3%2F5wseiMJIrEIX4G0cL6rI2K%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; alt=&quot;window-boot-time&quot; loading=&quot;lazy&quot; width=&quot;540&quot; height=&quot;549&quot; data-origin-width=&quot;540&quot; data-origin-height=&quot;549&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;1번, 42번, 107번 이면 충분할것 같다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;컴퓨터 &lt;span style=&quot;color: #ef5369;&quot;&gt;로그인 / 로그아웃&lt;/span&gt; 관련 이벤트 ID&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;4624 : 계정이 성공적으로 로그인 됨&lt;/li&gt;
&lt;li&gt;4627 : 계정이 로그아웃됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>OS/Window</category>
      <category>window boot</category>
      <category>window event</category>
      <category>윈도우 부팅 시간</category>
      <category>윈도우 이벤트</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1112</guid>
      <comments>https://inpa.tistory.com/entry/%F0%9F%92%BB-Window-10-boot-log#entry1112comment</comments>
      <pubDate>Mon, 15 May 2023 15:16:10 +0900</pubDate>
    </item>
    <item>
      <title>  스레드를 많이 쓸수록 항상 성능이 좋아질까?</title>
      <link>https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-Is-more-threads-always-better</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;161127499.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oYskW/btr5PR4ACcx/i29WQMKll1nmK2QWzknn41/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oYskW/btr5PR4ACcx/i29WQMKll1nmK2QWzknn41/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oYskW/btr5PR4ACcx/i29WQMKll1nmK2QWzknn41/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoYskW%2Fbtr5PR4ACcx%2Fi29WQMKll1nmK2QWzknn41%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; alt=&quot;single-multi-thread&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; data-filename=&quot;161127499.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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;a href=&quot;https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%E2%9A%94%EF%B8%8F-%EC%93%B0%EB%A0%88%EB%93%9C-%EC%B0%A8%EC%9D%B4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;프로세스와 스레드&lt;/a&gt;를 처음 학습할때, 스레드는 프로세스 내에 존재하는 실행 단위이며, 이 스레드가 여러개인 덕분에 우리가 음악을 들으며 웹서핑을하거나 파일을 다운 받는 '동시 작업'이 가능하다라고 배웠을 것이다. 그래서인지 이러한 질문에 대해 깊게 고민할 필요 없이, 스레드가 많을 수록 동시 처리 작업 수가 증가하는 것이니, 당연히 &lt;a href=&quot;https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-multi-process-multi-thread#multi_thread&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;멀티 스레드&lt;/a&gt;가 무조건 성능이 더 좋다고 생각할 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;626&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCL1LF/btr591mhyvB/eUfsHY9KPX0kGM0Yz5lHl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCL1LF/btr591mhyvB/eUfsHY9KPX0kGM0Yz5lHl0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCL1LF/btr591mhyvB/eUfsHY9KPX0kGM0Yz5lHl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCL1LF%2Fbtr591mhyvB%2FeUfsHY9KPX0kGM0Yz5lHl0%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; alt=&quot;single-multi-thread&quot; loading=&quot;lazy&quot; width=&quot;1219&quot; height=&quot;626&quot; data-origin-width=&quot;1219&quot; data-origin-height=&quot;626&quot;/&gt;&lt;/span&gt;&lt;/figure&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;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;모든 상황에 대해 무조건 멀티 스레드가 싱글 스레드보다 무조건 좋다고 말할 순 없다.&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&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;b&gt;동시성(Concurrency)&lt;/b&gt;에 대한 깊은 지식이 있어야만 대답할 수 있는 질문이기 때문이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;509&quot; data-origin-height=&quot;225&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfUoyB/btr8xVJisk3/7udZdHFUor4a4n333AjjW0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfUoyB/btr8xVJisk3/7udZdHFUor4a4n333AjjW0/img.jpg&quot; data-alt=&quot;사람이 생각하는 동시성과 컴퓨터가 행하는 동시성 작동 차이&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfUoyB/btr8xVJisk3/7udZdHFUor4a4n333AjjW0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfUoyB%2Fbtr8xVJisk3%2F7udZdHFUor4a4n333AjjW0%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; alt=&quot;single-multi-thread&quot; loading=&quot;lazy&quot; width=&quot;509&quot; height=&quot;225&quot; data-origin-width=&quot;509&quot; data-origin-height=&quot;225&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;지금부터 싱글 스레드와 멀티 스레드 두 가지 모델을 비교하고 언제 어느때에 무엇이 더 좋은지 왜 멀티 스레드가 항상 성능이 좋지 않은지에 대해 알아보도록하자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;1.&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;임계 영역에 대한 동기화 비용&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; 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;span style=&quot;color: #333333; text-align: start;&quot;&gt;임계 영역(Critical Section)의 공유 자원에 접근할 수 있기 때문에, 데이터의 일관성과 정확성을 유지하기 위해 &lt;b&gt;동기화(Synchronized) 기법&lt;/b&gt;을 사용하여야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1278&quot; data-origin-height=&quot;592&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dokOvx/btr8jNrADwv/UM1tmCGIVJpZkJhVITioK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dokOvx/btr8jNrADwv/UM1tmCGIVJpZkJhVITioK1/img.png&quot; data-alt=&quot;공유 자원에 대한 임계 영역&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dokOvx/btr8jNrADwv/UM1tmCGIVJpZkJhVITioK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdokOvx%2Fbtr8jNrADwv%2FUM1tmCGIVJpZkJhVITioK1%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; alt=&quot;single-multi-thread&quot; loading=&quot;lazy&quot; width=&quot;1278&quot; height=&quot;592&quot; data-origin-width=&quot;1278&quot; data-origin-height=&quot;592&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;공유 자원에 대한 임계 영역&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;임계 영역(Critical Section)&lt;/b&gt;&lt;br /&gt;멀티 스레드 프로그래밍에서 임계 영역은 공유 자원을 접근하는 코드 영역을 말한다. 대표적으로 전역 변수나 heap 메모리 영역을 들 수 있겠다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;동기화 기법에는 여러가지가 있는데 대표적인 두가지로 뮤텍스(Mutex) 나 세마포어(Semaphore) 같은 잠금 기법이 있다. 이들은 한 번에 하나의 스레드만 공유 자원에 접근할 수 있도록 제한해야 하여 데이터에 대한 일관성을 유지한다. 그러나 이러한 동기화 기법은 스레드 간의 경쟁과 대기 상황을 발생시키므로, 오히려 성능에 부정적인 영향을 미칠 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예를들어, 뮤텍스나 세마포어와 같은 동기화 기법을 사용하는 경우 스레드가 데이터에 접근하기 전에 락(Lock)을 획득하고, 데이터에 접근한 후에 락을 해제하는데, 이러한 &lt;span style=&quot;color: #ee2323;&quot;&gt;락 획득 및 해제 작업은 추가적인 시간이 소요되며, 나머지 스레드의 실행을 중지하거나, 대기&lt;/span&gt;하게 만들어야 하므로 프로그램의 성능이 저하될 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;794&quot; data-origin-height=&quot;516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvw8U7/btr8tDJPHwV/BM3SEnUL7CCyJ1lDw1G6HK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvw8U7/btr8tDJPHwV/BM3SEnUL7CCyJ1lDw1G6HK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvw8U7/btr8tDJPHwV/BM3SEnUL7CCyJ1lDw1G6HK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbvw8U7%2Fbtr8tDJPHwV%2FBM3SEnUL7CCyJ1lDw1G6HK%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; alt=&quot;single-multi-thread&quot; loading=&quot;lazy&quot; width=&quot;587&quot; height=&quot;381&quot; data-origin-width=&quot;794&quot; data-origin-height=&quot;516&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;또한 &lt;b&gt;CPU 캐시와 메모리 사이의 캐시 데이터 일관성&lt;/b&gt; 문제도 발생할 수 있는데,&amp;nbsp;여러 개의 스레드가 동시에 공유하는 메모리나 데이터에 대해 수정을 가할때&amp;nbsp;특정 CPU 코어의 캐시에 저장된 데이터와 다른 CPU 코어의 캐시에 저장된 데이터가 일치하지 않는 경우가 발생한다. 이럴 경우, 다른 CPU 코어에서 변경한 데이터가 현재 CPU 코어의 캐시에 반영되지 않은 상태로 사용되어 문제가 발생할 수 있어, &lt;span style=&quot;color: #ee2323;&quot;&gt;CPU 캐시에서 데이터를 불러오는 비용&lt;/span&gt;이 발생하므로 성능에 영향을 미치게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;578&quot; data-origin-height=&quot;333&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDsMbY/btr8huzpBmQ/RLYlpT2xiTPOWBaLEkEER1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDsMbY/btr8huzpBmQ/RLYlpT2xiTPOWBaLEkEER1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDsMbY/btr8huzpBmQ/RLYlpT2xiTPOWBaLEkEER1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDsMbY%2Fbtr8huzpBmQ%2FRLYlpT2xiTPOWBaLEkEER1%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; alt=&quot;single-multi-thread&quot; loading=&quot;lazy&quot; width=&quot;578&quot; height=&quot;333&quot; data-origin-width=&quot;578&quot; data-origin-height=&quot;333&quot;/&gt;&lt;/span&gt;&lt;/figure&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: #ee2323;&quot;&gt;동기화 및 캐시 일관성 작업으로 인해 병목이 일어나 성능이 떨어진다.&lt;/span&gt; 그럼 반대로 만일 공유 데이터의 양이 적고, CPU와 메모리 사용량이 적은 경우 오히려 싱글 스레드 프로그램이 멀티 스레드보다 더 좋다고 할 수 있을까? 이는 아주 틀린말은 아니지만, 실제로 대부분의 프로그램에서는 많은 양의 데이터와 복잡한 로직을 처리해야 하고 요즘 CPU는 기본적으로 다중 코어를 탑재하므로, 이러한 병목 현상이 있더라도 왠만한 상황에선 멀티 스레드 프로그램이 싱글 스레드 프로그램보다 빠르다. 그렇지만 멀티 스레드라고 해서 싱글 스레드보다 성능 상승 곡선이 무조건적으로 가파르지 않다.&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;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;2.&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;컨텍스트 스위칭 오버헤드&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨텍스트 스위칭 오버헤드(context switching overhead)는 여러개의 프로세스나 스레드가 있을때, CPU가 현재 프로세스나 스레드의 상태를 저장하고 다른 프로세스나 스레드로 전환될 때 발생하는 비용을 의미한다. 이 &lt;span style=&quot;color: #ee2323;&quot;&gt;스위칭 하는 과정에서 CPU 시간과 자원을 소모&lt;/span&gt;하므로 성능에 영향을 미치는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;991&quot; data-origin-height=&quot;546&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXlTsh/btses7SMG2G/UHVZCbLH7xctjtRKBKpNh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXlTsh/btses7SMG2G/UHVZCbLH7xctjtRKBKpNh0/img.png&quot; data-alt=&quot;프로세스 1에서 프로세스 2 로 전환하는 과정에서 약간의 틈(오버헤드) 가 있다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXlTsh/btses7SMG2G/UHVZCbLH7xctjtRKBKpNh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXlTsh%2Fbtses7SMG2G%2FUHVZCbLH7xctjtRKBKpNh0%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; alt=&quot;single-multi-thread&quot; loading=&quot;lazy&quot; width=&quot;991&quot; height=&quot;546&quot; data-origin-width=&quot;991&quot; data-origin-height=&quot;546&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;프로세스 1에서 프로세스 2 로 전환하는 과정에서 약간의 틈(오버헤드) 가 있다&lt;/figcaption&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;span style=&quot;color: #ee2323;&quot;&gt;스레드가 많으면 많을 수록 스위칭 횟수도 많아지고 덩달아 오버헤드도 많아져&lt;/span&gt; 성능이 저하될 수 있다는 의미이다. 특히 싱글 코어와 같은 옛날 CPU와 같이 코어 수는 적은데 스레드 수를 계속 늘리게 되면, 각 코어에서 경합하는 스레드 수가 점점 많아질 거고, 어느 순간에는 오버헤드 때문에 성능 한계에 부딪히게 될 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;만일 단순히 CPU만을 사용하는 계산작업이라면 오히려 멀티 스레드보다 싱글 스레드로 프로그래밍하는 것이 더 효율적일 수 있다. 조금 극단적인 예를 들어보자면, 1부터 1억 까지 덧셈을 하는 작업을 100번 수행해야 하는 프로그램이 있다고 하자. &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;싱글 스레드 어플리케이션이라면 다음과 같이 for문을 중첩함으로써 구성할 수 있겠다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1680771322752&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;long sum = 0; 
for(int j=0; j&amp;lt;100; j++) {
  for(int i=0; i&amp;lt;10000000; i++) {
    sum += i;  
  } 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;반면 멀티 스레드 어플리케이션 이라면 아래의 코드를 각각의 100개의 스레드에게 분배하고 동시에 실행시키게 될 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1680771712868&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;long sum = 0; 

/* --------- 스레드 1 --------- */
new Thread(() -&amp;gt; {
    for (int i = 0; i &amp;lt; 10000000; i++) {
        sum += i;
    }
}).start();
/* --------- -------- --------- */

/* --------- 스레드 2 --------- */
new Thread(() -&amp;gt; {
    for (int i = 0; i &amp;lt; 10000000; i++) {
        sum += i;
    }
}).start();
/* --------- -------- --------- */

// ...

/* --------- 스레드 100 --------- */
new Thread(() -&amp;gt; {
    for (int i = 0; i &amp;lt; 10000000; i++) {
        sum += i;
    }
}).start();
/* --------- -------- --------- */&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;실제론 그렇지는 않겠지만, 1부터 1억 까지 덧셈하는데 10초가 걸린다고 가정해보자. 일반적으로 생각하면 싱글 스레드 모델에선 10초가 걸리는 작업을 100번 직렬로 수행했으니 1000초가 걸리고, 멀티 스레드 모델에선 100개의 스레드가 동시에 실행하므로 10~11초 사이 정도 걸릴것이라고 예상 할 것이다. 하지만 이는 옳지 않다. 왜냐하면 멀티 스레드에서 발생하는 context swtiching overhead 비용 때문에, 100개의 스레드들이 서로 스위칭되면서 발생되는 병목으로 인해 동시 처리의 이점보다 소요 시간이 클 수 있으므로 오히려 싱글 스레드보다 연산 처리가 느려져 버릴수가 있게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;664&quot; data-origin-height=&quot;246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bW5aVv/btr8wPP8zbX/oh9sdZdPyzt7ArSZMGxKw1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bW5aVv/btr8wPP8zbX/oh9sdZdPyzt7ArSZMGxKw1/img.jpg&quot; data-alt=&quot;context switching 이라는 잠재적인 비용때문에 실제 걸리는 시간은 더 크다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bW5aVv/btr8wPP8zbX/oh9sdZdPyzt7ArSZMGxKw1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbW5aVv%2Fbtr8wPP8zbX%2Foh9sdZdPyzt7ArSZMGxKw1%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; alt=&quot;single-multi-thread&quot; loading=&quot;lazy&quot; width=&quot;664&quot; height=&quot;246&quot; data-origin-width=&quot;664&quot; data-origin-height=&quot;246&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;context switching 이라는 잠재적인 비용때문에 실제 걸리는 시간은 더 크다&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;다시 한번 말하지만 이는 조금 극단적인 예시 이긴 하다. 비록 컨텍스트 스위칭 오버헤드와 추가적인 동기화 오버헤드가 발생하지만, 멀티 스레드는 CPU 코어를 효율적으로 활용하기 때문에 아주 옛날의 싱글 코어 프로세서가 아닌 이상 멀티 스레드의 컨텍스트 스위칭 오버헤드쪽이 더 느리다고 단언할 수는 없다. 따라서 이 부분은 단지 멀티 스레딩의 잠재적인 단점을 보여주기 위해 특별히 설계된 예시이며, 실제 시나리오에서는 다양한 상황에 따라 결과가 달라질 수 있으므로 참고에 유의해야 한다. 글쓴이가 말하고 싶은 것은, &lt;span style=&quot;color: #ee2323;&quot;&gt;&quot;스레드가 많을수록 작업을 분리해서 동시에 처리하니까 항상 빠르다&quot;는 고정 관념을 컴퓨터를 배운 독자분들이라면 깰 필요성&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;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;3.&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;잔여 스레드의 리소스 낭비&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 양의 작업을 여러개의 스레드로 빠르게 처리한다는 멀티 스레드 취지는 좋지만, 회사의 서비스 이용률이 24시간 항상 바쁜 상태이지는 않을 것이다. 즉, 멀티 스레드 어플리케이션에서 이용률이 한산하여 스레드를 한두개 밖에 이용하지 않을때, 나머지 잔여 스레드들이 CPU, 메모리, 네트워크 등의 자원을 불필요하게 점유해서 성능 저하나 오류의 원인이 될 수 있게 된다. 우선 놀고 있는 스레드가 많을 수록 불필요하게 메모리를 차지하고 있는 셈이기 때문에 당연히 &lt;span style=&quot;color: #ee2323;&quot;&gt;시스템 자원 낭비&lt;/span&gt;가 발생된다. 그런데 놀고 있음에도 CPU는&lt;span&gt;&amp;nbsp;&lt;/span&gt;다른 스레드에게 CPU 시간을 양도하도록 설계 되어 있기 때문에 &lt;span style=&quot;color: #ee2323;&quot;&gt;노는 스레드와 다른 스레드 간에 컨텍스트 스위칭을 하여 CPU의 효율성을 떨어뜨린다.&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;스레드 풀(Thread Pool)&lt;/b&gt;과 같은 매커니즘을 사용하여 스레드의 개수를 관리하여&lt;span&gt;&amp;nbsp;&lt;/span&gt;리소스 낭비를 최소화하는 것이 중요하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;737&quot; data-origin-height=&quot;308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdA3Ai/btr8xOctSiT/mUfVs82mIY0KODkHKkkV30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdA3Ai/btr8xOctSiT/mUfVs82mIY0KODkHKkkV30/img.png&quot; data-alt=&quot;스레드 풀은 나중에 쓰일 예비 스레드들을 담고 있는 상자이다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdA3Ai/btr8xOctSiT/mUfVs82mIY0KODkHKkkV30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdA3Ai%2Fbtr8xOctSiT%2FmUfVs82mIY0KODkHKkkV30%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; alt=&quot;single-multi-thread&quot; loading=&quot;lazy&quot; width=&quot;737&quot; height=&quot;308&quot; data-origin-width=&quot;737&quot; data-origin-height=&quot;308&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;&amp;nbsp;&lt;/p&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;span style=&quot;color: #ef5369;&quot;&gt;4.&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;어플리케이션 성격에 따른 제약&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&quot;스레드를 많이 쓰면 쓸수록 동시에 더 많은 작업들을 실행할 수 있다&quot;라는 말에는 우리도 모르게 암묵적으로 해당 어플리케이션은 더 작은 작업들로 잘게 쪼개서 동시에 실행이 가능한 성격의 어플리케이션이라는 전제를 깔고 생각한다. 즉, 어플리케이션의 목적과 주제에 따라 아키텍쳐가 달라질 수 있다는 얘기인데, 예를들어 만약 개발하는 어플리케이션의 동작이 순차적으로 실행돼야만 하는 특징을 가지거나 잘게 쪼개서 동시에 실행하기에 매우 어려운 성격의 어플리케이션이라면 오히려 멀티 스레드 구성은 별 이점이 없다는 것이다. 이에 대한 대표적인 예시로는 CPU 바운드 어플리케이션과 I/O 바운드 어플리케이션을 예로 들 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;CPU 바운드&lt;/b&gt;&lt;br /&gt;- CPU 연산 능력에 의존하는 작업을 말한다&lt;br /&gt;- 데이터 마이닝, 영상 처리 작업, 이미지 프로세싱, 암호화폐 마이닝 ..등&lt;br /&gt;&lt;br /&gt;&lt;b&gt;I/O 바운드&lt;br /&gt;&lt;/b&gt;- I/O 장치의 응답 속도에 의존하는 작업을 말한다&lt;br /&gt;- 파일 입출력, 네트워크 통신, 데이터베이스 접근 ..등&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;CPU 바운드 어플리케이션&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;오라클에서 아키텍트로 일하고 있는 Goetz가 2002, 2006년에 발표한 자바 병렬 프로그래밍에 관한 책과 논문에 따르면 CPU 바운드 작업에서 적절한 스레드 수는 &lt;span style=&quot;color: #006dd7;&quot;&gt;코어 수 + 1&lt;/span&gt; 이라고 주장했었다. 이는 위에서 살펴보았던 컨텍스트 스위칭 오버헤드 비용 때문이라는 걸 유추할 수 있을 것이다. 따라서&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;CPU 바운드 어플리케이션은 cpu를 많이 쓰기 때문에 코어수와 비슷한 수준 이상으로 스레드 수를 늘려봤자 별 이점이 없으며, &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;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;I/O 바운드 어플리케이션&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; 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;I/O 장치의 응답 속도&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;에 의존하는 것이 큰데, 예를 들어 파일 입출력은 파일을 읽기 위해 디스크에서 데이터를 조회하는데 있어 하드웨어 한계상 입출력 작업에 걸리는 시간이 상대적으로 오래 걸리므로 이때 CPU는&lt;span&gt; 입출력이 완료될때까지 휴무 상태가 된다. &lt;/span&gt;&lt;/span&gt;그래서 만일 싱글 스레드일 경우 I/O 작업하는 동안 블로킹 되어 아래와 같이 낭비가 발생하게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;395&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AWbSm/btr8nRWBy7E/6ZJtFw80f2jK7lkYFlSkZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AWbSm/btr8nRWBy7E/6ZJtFw80f2jK7lkYFlSkZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AWbSm/btr8nRWBy7E/6ZJtFw80f2jK7lkYFlSkZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAWbSm%2Fbtr8nRWBy7E%2F6ZJtFw80f2jK7lkYFlSkZk%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; alt=&quot;io-bound&quot; loading=&quot;lazy&quot; width=&quot;850&quot; height=&quot;262&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;395&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;멀티 스레드는 I/O 작업이 처리될 동안 다음에 이행할 작업들을 다른 스레드에게 자원을 할당하여 수행할 수 있도록 할 수 있다. 네트워크 통신도 마찬가지로 네트워크 상황에 따라 전송 시간이 오래 걸릴 수 있으므로 통신 지연동안 스레드를 재활용 시키는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1197&quot; data-origin-height=&quot;537&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBdacl/btr8u8v8LkX/T6hyiKA2tZAtZut2gpqr5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBdacl/btr8u8v8LkX/T6hyiKA2tZAtZut2gpqr5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBdacl/btr8u8v8LkX/T6hyiKA2tZAtZut2gpqr5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBdacl%2Fbtr8u8v8LkX%2FT6hyiKA2tZAtZut2gpqr5K%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; alt=&quot;io-bound&quot; loading=&quot;lazy&quot; width=&quot;728&quot; height=&quot;327&quot; data-origin-width=&quot;1197&quot; data-origin-height=&quot;537&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;따라서 I/O 작업이 많은 상황에서는 CPU 코어 수보다 2배 3배 혹은 그 이상으로 스레드 수를 늘려주는 것이 코어들을 더 효율적으로 쓸 수 있기 때문에 성능 면에서 이점이 얻을 수 있다. 그러나 우리가 앞서 배웠듯이 스레드를 늘리면 늘릴수록, 잔여 스레드의 컨텍스트 오버헤드와 동기화 등의 문제점이 동반 될 수 있기 때문에, I/O 바운드 어플리케이션에 &lt;span style=&quot;color: #ee2323;&quot;&gt;멀티 스레드 모델 대신 비동기 I/O 처리에 특화된 &lt;b&gt;이벤트 기반 프로그래밍 모델&lt;/b&gt;&lt;/span&gt;을 접목하기도 한다. 가장 대표적으로 Node.js 어플리케이션을 들 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Node.js와 같은 싱글 스레드 + 이벤트 기반 프로그래밍 모델에서는 &lt;a href=&quot;https://inpa.tistory.com/entry/%F0%9F%94%84-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84-%EA%B5%AC%EC%A1%B0-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이벤트 루프(Event Loop)&lt;/a&gt;를 통해 이벤트를 감지하고, 이벤트 핸들러(Event Handler)를 호출하여 해당 이벤트를 처리하는 식이다. 만약 입출력 작업을 수행해야 하는 경우, 이벤트 핸들러는 비동기 I/O를 사용하여 입출력 작업을 수행하고, 입출력 작업이 완료될 때까지 이벤트 루프를 블로킹하지 않게 된다. 그래서 작업 처리 도중 다른 작업을 처리할 수 있어서, 멀티 스레드 모델에 비해 더 적은 리소스를 사용하고 높은 처리량을 보여줄 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;933&quot; data-origin-height=&quot;537&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOkyrM/btr8tuz1XAN/p4qmFR7ChLtEHA5YN23dHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOkyrM/btr8tuz1XAN/p4qmFR7ChLtEHA5YN23dHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOkyrM/btr8tuz1XAN/p4qmFR7ChLtEHA5YN23dHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOkyrM%2Fbtr8tuz1XAN%2Fp4qmFR7ChLtEHA5YN23dHk%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; alt=&quot;io-bound&quot; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;346&quot; data-origin-width=&quot;933&quot; data-origin-height=&quot;537&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;물론 비동기 I/O 처리 주체는 Node.js에 내장된 libuv 라이브러리의 스레드 풀에서 가져온 멀티 스레드로 처리하는 것이다. 웹브라우저도 멀티 스레드 프로그램이며 이를 이용해 비동기 작업을 수행하는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;765&quot; data-origin-height=&quot;314&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfVSME/btsexguR6Wg/KoJKkXO2H3XkwkovZxaeI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfVSME/btsexguR6Wg/KoJKkXO2H3XkwkovZxaeI0/img.png&quot; data-alt=&quot;Node.js 내부 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfVSME/btsexguR6Wg/KoJKkXO2H3XkwkovZxaeI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfVSME%2FbtsexguR6Wg%2FKoJKkXO2H3XkwkovZxaeI0%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; alt=&quot;io-bound&quot; loading=&quot;lazy&quot; width=&quot;765&quot; height=&quot;314&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;765&quot; data-origin-height=&quot;314&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Node.js 내부 구조&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;그러면 멀티 스레드 프로그래밍 모델과 차이점이 없냐고 말할 수 는 있겠지만, Node.js의 메인 스레드는 싱글 스레드이기 때문에 멀티 스레드 모델에서 발생할 수 있는 동기화 문제나 경쟁 상태 등을 걱정할 필요가 없이 CPU 바운드 혹은 I/O 바운드 작업이 발생하면 그때에만 멀티 스레드를 가져와 사용해, 이른바 멀티 스레드 모델 변형 버전인 '싱글 스레드 + 이벤트 기반 프로그래밍 모델 + 비동기 I/O 모델' 이라고 말한다.&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;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;GUI 어플리케이션&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;GUI 어플리케이션은 창(window), 아이콘(icon), 메뉴(menu), 포인터(pointer) 등의 그래픽 사용자 인터페이스(GUI)를 통해 사용자와 상호작용하는 애플리케이션이다. 키보드, 마우스, 터치스크린 등의 입력 장치로부터 발생하는 이벤트를 감지하고, 적절한 메시지나 액션을 수행 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;545&quot; data-origin-height=&quot;481&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tbNOp/btr8lnabPhW/wJeUWt7vo38JmexIoKuk10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tbNOp/btr8lnabPhW/wJeUWt7vo38JmexIoKuk10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tbNOp/btr8lnabPhW/wJeUWt7vo38JmexIoKuk10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtbNOp%2Fbtr8lnabPhW%2FwJeUWt7vo38JmexIoKuk10%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; alt=&quot;gui-application&quot; loading=&quot;lazy&quot; width=&quot;545&quot; height=&quot;481&quot; data-origin-width=&quot;545&quot; data-origin-height=&quot;481&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이러한 GUI 어플리케이션에서는 보통 싱글 스레드를 사용하는 것이 안정적이다. 멀티 스레드를 사용할 경우 스레드 간의 동기화 문제로 인해 이벤트 처리 순서가 뒤바뀌거나, 데드락이 발생할 가능성이 있어, UI 갱신에 문제가 생기게 되어 사용자 경험을 해치게 되기 때문이다. 따라서 이벤트 루프(Event Loop)를 이용해 이벤트 처리를 싱글 스레드로 순차적으로 처리하는 이벤트 기반 프로그래밍 모델에 적합하다.&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;5.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;프로그래밍 난이도&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싱글 스레드는 단 하나의 스레드가 모든 작업을 처리하기 때문에, 프로그램 구조가 단순하여 개발이 더 쉽고 CPU, 메모리를 적게 사용한다. 반면 멀티 스레드는 스레드 간의 동기화 처리, 잘못된 스레드 관리로 인해 메모리 누수, 데드락 등의 문제에 대해 관리가 필요하기 때문에 개발이 굉장히 복잡해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글쓴이가 말하고 싶은 것은 당연히 대체적으로 멀티 스레드가 싱글 스레드 보다 성능이 더 뛰어나지만, 그렇다고 해서 무턱대고 어플리케이션 개발을 무작정 멀티 스레드로 진행하다 온갖 동시성 문제에 직면하여, 어플리케이션의 안정성과 유지보수성을 저해하는 것은 &lt;span style=&quot;color: #ee2323;&quot;&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;ezgif.com-resize.gif&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;1157&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CkKzj/btr8vPwfpeN/3bqpaK7abwud98VnnWINS0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CkKzj/btr8vPwfpeN/3bqpaK7abwud98VnnWINS0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CkKzj/btr8vPwfpeN/3bqpaK7abwud98VnnWINS0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/CkKzj/btr8vPwfpeN/3bqpaK7abwud98VnnWINS0/img.gif&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; alt=&quot;Thread Pool&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;1157&quot; data-filename=&quot;ezgif.com-resize.gif&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;1157&quot;/&gt;&lt;/span&gt;&lt;/figure&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;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;프로그래머에게 높은 수준의 역량과 경험이 필요하기 때문에 모델 선택에 있어&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;신중하게 고려하고,&lt;span&gt;&amp;nbsp;&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;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://youtu.be/jSaBkvtHhrM&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://www.bryanbraun.com/2012/06/25/multitasking-and-context-switching/&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://www.baeldung.com/java-volatile-variables-thread-safety&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://medium.com/@punyatoya213/multithreading-can-be-fun-too-part-2-e27f1841c8ca&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://medium.com/pocs/single-thread-server-vs-multi-thread-server-c1fda66bbcd0&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://realpython.com/python-concurrency/&lt;/p&gt;</description>
      <category>개발 지식/CS 지식</category>
      <category>thread</category>
      <category>스레드</category>
      <category>쓰레드</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1111</guid>
      <comments>https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-Is-more-threads-always-better#entry1111comment</comments>
      <pubDate>Wed, 10 May 2023 07:53:13 +0900</pubDate>
    </item>
    <item>
      <title>  자바스크립트 이벤트 루프 동작 구조 &amp;amp; 원리 끝판왕</title>
      <link>https://inpa.tistory.com/entry/%F0%9F%94%84-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84-%EA%B5%AC%EC%A1%B0-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2ykzr/btsemBr9Zhr/2iCIBBqwzwBgECkH2yvE70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2ykzr/btsemBr9Zhr/2iCIBBqwzwBgECkH2yvE70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2ykzr/btsemBr9Zhr/2iCIBBqwzwBgECkH2yvE70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2ykzr%2FbtsemBr9Zhr%2F2iCIBBqwzwBgECkH2yvE70%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; alt=&quot;js-event-loop&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;720&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; 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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;b&gt;브라우저의&amp;nbsp;&lt;/b&gt;멀티 스레드로 작업을 동시에&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Javascript는 싱글 스레드 언어라고 들어본 적이 있을 것이다. '싱글' 스레드라 한 번에 하나의 작업만 수행이 가능하다. 반면 Java 나 Python은 멀티 스레드를 지원하여 원하는 코드 로직을 동시에 수행 시키는 멀티 작업이 가능하다.&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;span style=&quot;color: #ee2323;&quot;&gt;브라우저 내부의 멀티 스레드인 Web APIs에서 비동기 + 논블로킹으로 처리&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;된다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-%EB%8F%99%EA%B8%B0%EB%B9%84%EB%8F%99%EA%B8%B0-%EB%B8%94%EB%A1%9C%ED%82%B9%EB%85%BC%EB%B8%94%EB%A1%9C%ED%82%B9-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;비동기 + 논블로킹(Async + Non blocking)&lt;/a&gt;는 메인 스레드가 작업을 다른 곳에 요청하여 대신 실행하고, 그 작업이 완료되면 이벤트나 콜백 함수를 받아 결과를 실행하는 방식을 말한다. (쉽게 말해 파일 다운로드 요청 작업을 백그라운드 작업으로 전이하여 동시에 처리가 가능하도록 한 것으로 이해하면 된다)&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;즉, 비동기로 동작하는 핵심요소는 자바스크립트 언어가 아니라 브라우저라는 소프트웨어가 가지고 있다고 보면 된다. Node.js 에서는 libuv 내장 라이브러리가 처리한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;이벤트 루프는 브라우저 동작을 제어하는 관리자&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싱글 스레드인 자바스크립트의 작업을 멀티 스레드로 돌려 작업을 동시에 처리시키게 하던가, 또는 여러 작업 중 어떤 작업을 우선으로 동작시킬 것인지 결정하는 세심한 컨트롤을 하기 위해 존재하는 것이 바로 이벤트 루프(Event Loop) 이다. 이벤트 루프는 브라우저 내부의 Call Stack, Callback Queue, Web APIs 등의 요소들을 모니터링하면서 비동기적으로 실행되는 작업들을 관리하고, 이를 순서대로 처리하여 프로그램의 실행 흐름을 제어하는 녀석이다. 간단히 표현하자면 &lt;b&gt;브라우저의 동작 타이밍을 제어하는 관리자&lt;/b&gt;라고 보면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 루프의 동작 과정을 간단히 살펴보자면, 자바스크립트의 &lt;s&gt;setTimeout&lt;/s&gt;이나 &lt;s&gt;fetch&lt;/s&gt; 와 같은 비동기 자바스크립트 코드를 브라우저 Web APIs에게 맡기고, 백그라운드 작업이 끝난 결과를 콜백 함수 형태로 큐(Callback Queue)에 넣고 처리 준비가 되면 호출 스택(Call Stack)에 넣어 마무리 작업을 진행한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEeJN4/btsabeBnUWX/exb9jS9LXWWW7oM1Yk832K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEeJN4/btsabeBnUWX/exb9jS9LXWWW7oM1Yk832K/img.png&quot; data-alt=&quot;마치 순회(loop) 하는듯하여 이벤트 루프라 부르는 것이다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEeJN4/btsabeBnUWX/exb9jS9LXWWW7oM1Yk832K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEeJN4%2FbtsabeBnUWX%2Fexb9jS9LXWWW7oM1Yk832K%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; alt=&quot;js-event-loop&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;마치 순회(loop) 하는듯하여 이벤트 루프라 부르는 것이다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 이벤트 루프를 이용한 프로그램 방식을 이벤트 기반(Event Driven) 프로그래밍이라고 한다. 이벤트 기반 프로그래밍은 프로그램의 흐름이 이벤트에 의해 결정되는 방식이다. 예를 들어 사용자의 클릭이나 키보드 입력과 같은 이벤트가 발생하면, 그에 맞는 콜백 함수가 실행한다. 대표적으로 자바스크립트의 &lt;s&gt;addEventListener(이벤트명, 콜백함수)&lt;/s&gt; 가 있겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 기반 프로그래밍은 으로 비동기 작업을 쉽게 처리할 수 있고, 멀티 스레드 언어에 비해 단순하고 직관적인 코드 작성을 가능하게 하며, 브라우저와 같은 환경에서도 안정적인 실행을 가능하게 하여 사용자와의 상호작용을 높일 수 있다. 따라서 이를 이해하고 적절한 방식으로 비동기 작업을 처리하는 것은, 자바스크립트를 이용한 웹 애플리케이션 개발에 있어서 매우 중요하다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;  자바스크립트는 왜 싱글 스레드 인가?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;자바스크립트는 1995년에 넷스케이프에서 웹 브라우저에서 동적인 웹 페이지를 만들기 위해 개발된 스크립트 언어이다. 당시에는 멀티 코어 프로세서가 보편화되지 않았고, 자바스크립트는 웹 브라우저에서 간단한 스크립트 동작을 수행하는 데 주로 사용되었기 때문에 복잡한 병렬 처리를 필요로 하지 않아, 메모리 사용량이 적고, 동기화 문제를 피할 수 있는 싱글 스레드로 구현하였다. 그러나 싱글 스레드는 오래 걸리는 작업이 실행되면 다른 작업들이 대기해야 하므로 응답성이 떨어진다. 또한 CPU 코어를 여러 개 사용할 수 없으므로 성능이 제한된다. 이러한 문제들을 해결하기 위해 언어 자체의 설계를 바꾸는 것 보단, 브라우저의 멀티 스레드를 이용하는 자바스크립트의 비동기 프로그래밍을 지원하는 것이다. 그리고 이 비동기 프로그래밍의 핵심이 이벤트 루프인 것이다. (다만 Web worker 최신 기술을 통해 자바스크립트도 멀티 스레드 구현이 가능해졌다)&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;자바스크립트 엔진 구동 환경&lt;/b&gt;&lt;/h2&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;자바스크립트를 실행하는 소프트웨어로는 우리가 잘 알고 있는 &lt;span style=&quot;color: #0593d3;&quot;&gt;웹브라우저&lt;/span&gt;와 런타임인 &lt;span style=&quot;color: #009a87;&quot;&gt;Node.js&lt;/span&gt; 가 있다. 이벤트 루프 동작 원리를 배우기 앞서, 싱글 스레드인 자바스크립트 엔진이 어느 곳을 거쳐 비동기 작업을 수행하는지 우선 이 둘의 내부 구성도를 눈에 익혀보자.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;본 포스팅에서 예시를 드는 웹브라우저는 Chrome 브라우저이다. 다른 브라우저는 자바스크립트 엔진이나 내부 구성이 약간식 다를수 있다는 점은 유의하자&lt;/blockquote&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;브라우저의 내부 구성도&lt;/b&gt;&lt;/h3&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;브라우저는 웹 사이트를 화면에 보여주기 위해 여러가지 역할을 하는 부품들로 이루어져 있다. 그중 우리가 알아볼 것은 자바스크립트 비동기 코드의 동작 과정이니, 이에 관련된 구성 요소로는&lt;span&gt;&amp;nbsp;&lt;/span&gt;Web APIs,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Event Table,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Callback Queue,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Event Loop&lt;span&gt;&amp;nbsp;&lt;/span&gt;등이 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;508&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZPhwH/btsd15BHBgX/EDe61wsY0PBpFAk37PnqT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZPhwH/btsd15BHBgX/EDe61wsY0PBpFAk37PnqT1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZPhwH/btsd15BHBgX/EDe61wsY0PBpFAk37PnqT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZPhwH%2Fbtsd15BHBgX%2FEDe61wsY0PBpFAk37PnqT1%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; alt=&quot;이벤트루프&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;508&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;508&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Call Stack : 자바스크립트&amp;nbsp;엔진이&amp;nbsp;코드&amp;nbsp;실행을&amp;nbsp;위해&amp;nbsp;사용하는&amp;nbsp;메모리&amp;nbsp;구조&lt;/li&gt;
&lt;li&gt;Heap : 동적으로 생성된 자바스크립트 객체가 저장되는 공간&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;Web APIs&lt;/span&gt;: 브라우저에서 제공하는 API 모음으로, 비동기적으로 실행되는 작업들을 전담하여 처리한다. (AJAX 호출, 타이머 함수, DOM 조작 등)&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;Callback Queue&lt;/span&gt; : 비동기적 작업이 완료되면 실행되는 함수들이 대기하는 공간&lt;/li&gt;
&lt;li&gt;Event Loop : 비동기 함수들을 적절한 시점에 실행시키는 관리자&lt;/li&gt;
&lt;li&gt;Event Table: 특정 이벤트(timeout, click, mouse 등)가 발생했을 때 어떤 callback 함수가 호출되야 하는지를 알고 있는 자료구조 (위 그림에는 없음)&lt;/li&gt;
&lt;/ul&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;Web APIs의 종류&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Web APIs는 타이머, 네트워크 요청, 파일 입출력, 이벤트 처리 등 브라우저에서 제공하는 다양한 API를 포괄하는 총칭이다. Web API는 브라우저(Chrome)에서 멀티 스레드로 구현되어 있다. 그래서 브라우저는 비동기 작업에 대해 메인 스레드를 차단하지 않고 다른 스레드를 사용하여 동시에 처리할수 있는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;976&quot; data-origin-height=&quot;508&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EKfHg/btsepN6Bmc2/3MSHkEiSXT4CpKOM5CquTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EKfHg/btsepN6Bmc2/3MSHkEiSXT4CpKOM5CquTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EKfHg/btsepN6Bmc2/3MSHkEiSXT4CpKOM5CquTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEKfHg%2FbtsepN6Bmc2%2F3MSHkEiSXT4CpKOM5CquTK%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; alt=&quot;Web APIs&quot; loading=&quot;lazy&quot; width=&quot;976&quot; height=&quot;508&quot; data-origin-width=&quot;976&quot; data-origin-height=&quot;508&quot;/&gt;&lt;/span&gt;&lt;/figure&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: #333333; text-align: start;&quot;&gt;예를 들어,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;s&gt;setTimeout&lt;/s&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;비동기 작업은 Web APIs의 한 종류인 Timer API 에서 타이머 스레드를 사용하여 타이머를 수행한다. 마찬가지로,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;s&gt;XMLHttpRequest&lt;/s&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;s&gt;fetch&lt;/s&gt;와 같은 네트워크 관련 API는 네트워크 스레드를 사용하여 네트워크 요청과 응답을 처리된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1349&quot; data-origin-height=&quot;463&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BhZ5X/btscRYWrtIX/1RjSMfQFTWqrC1RfclCddK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BhZ5X/btscRYWrtIX/1RjSMfQFTWqrC1RfclCddK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BhZ5X/btscRYWrtIX/1RjSMfQFTWqrC1RfclCddK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBhZ5X%2FbtscRYWrtIX%2F1RjSMfQFTWqrC1RfclCddK%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; alt=&quot;Web APIs&quot; loading=&quot;lazy&quot; width=&quot;1349&quot; height=&quot;463&quot; data-origin-width=&quot;1349&quot; data-origin-height=&quot;463&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Web APIs의 대표적인 종류로는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DOM : HTML 문서의 구조와 내용을 표현하고 조작할 수 있는 객체&lt;/li&gt;
&lt;li&gt;XMLHttpRequest: 서버와 비동기적으로 데이터를 교환할 수 있는 객체. AJAX기술의 핵심.&lt;/li&gt;
&lt;li&gt;Timer API: 일정한 시간 간격으로 함수를 실행하거나 지연시키는 메소드들을 제공&lt;/li&gt;
&lt;li&gt;Console API : 개발자 도구에서 콘솔 기능을 제공&lt;/li&gt;
&lt;li&gt;Canvas API: &lt;s&gt;&amp;lt;canvas&amp;gt;&lt;/s&gt; 요소를 통해 그래픽을 그리거나 애니메이션을 만들 수 있는 메소드들을 제공&lt;/li&gt;
&lt;li&gt;Geolocation API: 웹 브라우저에서 사용자의 현재 위치 정보를 얻을 수 있는 메소드들을 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이때 오해하지 말하야할 것이 모든 Web API들이 비동기로 동작되는 것이 아니다. Web API에는 동기적으로 처리되는 것과 비동기적으로 처리되는 것이 모두 있다. 예를 들어 DOM API나 Console API는 동기적으로 처리되고, XMLHttpRequest나 Timer API는 비동기적으로 처리된다.&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;Callback Queue의 종류&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;Web APIs가 여러 API들을 묶어 말하듯이, Callback Queue도 여러가지 종류의 &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;Queue를 묶어 총칭하는 개념이다. &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;Callback Queue에는 &lt;b&gt;(macro)task queue&lt;/b&gt;와 &lt;b&gt;microtask queue&lt;/b&gt; 두 가지 종류가 있다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;987&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjvMkH/btscRrduQdT/zQ0j5GmiKHwolv41XPxw7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjvMkH/btscRrduQdT/zQ0j5GmiKHwolv41XPxw7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjvMkH/btscRrduQdT/zQ0j5GmiKHwolv41XPxw7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjvMkH%2FbtscRrduQdT%2FzQ0j5GmiKHwolv41XPxw7k%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; alt=&quot;Callback Queue&quot; loading=&quot;lazy&quot; width=&quot;987&quot; height=&quot;540&quot; data-origin-width=&quot;987&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Task Queue :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;setTimeout&lt;/s&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;setInterval&lt;/s&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;fetch&lt;/s&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;addEventListener&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;와 같이 비동기로 처리되는 함수들의 콜백 함수가 들어가는 큐&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;macrotask queue 는 보통 task queue 라고 부른다)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Microtask Queue :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;promise.then&lt;/s&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;process.nextTick&lt;/s&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;MutationObserver&lt;/s&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;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;Callback Queue의 종류에 따라 이벤트 루프가 콜 스택으로 옮기는 순서가 달라진다. &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;일반적으로 microtask queue가 가장 우선순위가 높아 먼저 microtask queue를 처리하여 먼저 비우고 그라음 task queue의 콜백을 처리한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이벤트 루프를 배우기 전에 &lt;s&gt;Promise.then&lt;/s&gt; 결과가 &lt;s&gt;setTimeout&lt;/s&gt;보다 우선 된다는 것을 미리 배웠다면, 왜 프로미스가 먼저 처리되는지에 대한 이유가 이벤트 루프의 동작 원리와 관련이 있다는걸 알 수 있을 것이다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;또한 같은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;queue 안에 적재되는 콜백이라도 어떠한 비동기 작업이냐에 따라&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;우선순위가 다른 태스크들이 있을 수 있다. 예를들어&lt;span&gt;&amp;nbsp;&lt;/span&gt;Microtask Queue에 적재되는 Promise 와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-MutationObserver-DOM%EC%9D%98-%EB%B3%80%ED%99%94%EB%A5%BC-%EA%B0%90%EC%8B%9C&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Mutation Observer&lt;/a&gt; 콜백 중&lt;span&gt;&amp;nbsp;&lt;/span&gt;Mutation Observer이 먼저 처리되는 식이다.&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;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;AnimationFrame Queue&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저의 큐는 콜백 큐 뿐만 아니라 브라우저 애니메이션 작업에 대한 처리를 담당하는 AnimationFrame Queue도 있다. 자바스크립트 애니메이션 동작을 제어하는 &lt;s&gt;requestAnimationFrame&lt;/s&gt; 메소드를 통해 콜백을 등록하면, 이 큐에 적재되어 브라우저가 repaint 직전에 AnimationFrame Queue에 있는 작업들을 전부 처리한다. 따라서 자바스크립트 스타일 관련 코드들을 AnimationFrame Queue에 비동기로 처리하도록 구성하면&amp;nbsp;브라우저가 애니메이션의 타이밍을 관리하고, 적절한 프레임 속도를 유지하고, 다른 탭이나 창에 있을 때 애니메이션을 중지함으로써 브라우저의 애니메이션 동작의 성능과 품질을 향상시킬 수 있다.&lt;/p&gt;
&lt;figure id=&quot;og_1682509610170&quot; style=&quot;color: #333333; text-align: start;&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;  웹 애니메이션 최적화 requestAnimationFrame 가이드&quot; data-og-description=&quot;자바스크립트 웹 애니메이션 웹페이지의 애니메이션을 구현할때 CSS의 animatoin , transition , transform 속성을 통해 구현할 수도 있지만, 보다 사용자와의 복잡한 상호작용을 구현하게 하기 위해 Javasc&quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/%F0%9F%8C%90-requestAnimationFrame-%EA%B0%80%EC%9D%B4%EB%93%9C&quot; data-og-url=&quot;https://inpa.tistory.com/entry/%F0%9F%8C%90-requestAnimationFrame-%EA%B0%80%EC%9D%B4%EB%93%9C&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dNXMGV/hySpEpgfIN/KHiKpQ8TeibwkjhSxwUE60/img.jpg?width=800&amp;amp;height=397&amp;amp;face=0_0_800_397,https://scrap.kakaocdn.net/dn/oWTZV/hySpz2yw4F/C5hKwBMHoSdUKadUAO4rIK/img.jpg?width=800&amp;amp;height=397&amp;amp;face=0_0_800_397,https://scrap.kakaocdn.net/dn/sdP1R/hySpBMPTde/6KwK2xuqui2WNWkNcvtCZK/img.png?width=1073&amp;amp;height=888&amp;amp;face=0_0_1073_888&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://inpa.tistory.com/entry/%F0%9F%8C%90-requestAnimationFrame-%EA%B0%80%EC%9D%B4%EB%93%9C&quot; data-source-url=&quot;https://inpa.tistory.com/entry/%F0%9F%8C%90-requestAnimationFrame-%EA%B0%80%EC%9D%B4%EB%93%9C&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dNXMGV/hySpEpgfIN/KHiKpQ8TeibwkjhSxwUE60/img.jpg?width=800&amp;amp;height=397&amp;amp;face=0_0_800_397,https://scrap.kakaocdn.net/dn/oWTZV/hySpz2yw4F/C5hKwBMHoSdUKadUAO4rIK/img.jpg?width=800&amp;amp;height=397&amp;amp;face=0_0_800_397,https://scrap.kakaocdn.net/dn/sdP1R/hySpBMPTde/6KwK2xuqui2WNWkNcvtCZK/img.png?width=1073&amp;amp;height=888&amp;amp;face=0_0_1073_888');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;  웹 애니메이션 최적화 requestAnimationFrame 가이드&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;자바스크립트 웹 애니메이션 웹페이지의 애니메이션을 구현할때 CSS의 animatoin , transition , transform 속성을 통해 구현할 수도 있지만, 보다 사용자와의 복잡한 상호작용을 구현하게 하기 위해 Javasc&lt;/p&gt;
&lt;p class=&quot;og-host&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;inpa.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Node.js의 내부 구성도&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NodeJS&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;환경에서도 브라우저와 거의 비슷한 구조를 볼 수 있는데, 차이점이 있다면 내장된 &lt;/span&gt;&lt;b&gt;libuv&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;라이브러리&lt;/b&gt;를 사용하여 &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;비동기 IO를 지원한다는 점이다.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;또한 브라우저에서는 Web API를 사용하여 DOM 조작, AJAX 호출, 타이머 및 애니메이션 등과 같은 다양한 작업을 처리하지만, Node.js에서는 Web API가 아닌 Node.js API를 사용하여 파일 시스템 액세스, 네트워크 액세스, 암호화, 압축 및 해제 등과 같은 다양한 작업을 처리한다. 예를 들어, Node.js에서 HTTP 요청을 수행하려면 http 모듈을 사용한다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;단, Node.js에서도 일부 Web API를 사용할 수 있는데, &lt;s&gt;setTimeout&lt;/s&gt;, &lt;s&gt;setInterval&lt;/s&gt; 등이 그렇다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1120&quot; data-origin-height=&quot;464&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPLXeH/btsace2uNJ7/wAJN5t2YXnQuoKz9kzVR8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPLXeH/btsace2uNJ7/wAJN5t2YXnQuoKz9kzVR8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPLXeH/btsace2uNJ7/wAJN5t2YXnQuoKz9kzVR8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPLXeH%2Fbtsace2uNJ7%2FwAJN5t2YXnQuoKz9kzVR8k%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; alt=&quot;Node.js&quot; loading=&quot;lazy&quot; width=&quot;1120&quot; height=&quot;464&quot; data-origin-width=&quot;1120&quot; data-origin-height=&quot;464&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 Node.js에서 자바스크립트 엔진은 비동기 작업을 위해서&amp;nbsp;Node.js의 API를 호출하며, 이때 넘겨진 콜백은&amp;nbsp;libuv&amp;nbsp;의 이벤트 루프를 통해 스케쥴되고 실행된다. Node.js의 내부 구성으로는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;V8 (JavaScript 엔진) : Node.js에서 사용하는 JavaScript 엔진으로 코드를 컴파일하고 실행한다&lt;/li&gt;
&lt;li&gt;Bindings (Node API) : Node.js 시스템과 V8 엔진 간의 상호작용을 가능하게 하는 C++ 라이브러리&lt;/li&gt;
&lt;li&gt;Libuv 라이브러리 : Node.js에서 비동기 I/O 작업을 처리하기 위한 C 라이브러리&lt;/li&gt;
&lt;li&gt;Event Queue : 비동기 I/O 작업 결과를 저장하고 처리하기 위한 자료구조 (웹브라우저의 Task Queue와 비슷하다)&lt;/li&gt;
&lt;li&gt;Event Loop : Event Queue에 저장된 I/O 작업 결과를 처리하고, 다음 작업을 수행하도록 하는 관리자&lt;/li&gt;
&lt;li&gt;Worker Threads : CPU 집약적인 작업을 처리하기 위해 Node.js 10 버전부터 추가된 멀티 스레드. worker threads는 메인 스레드와 독립적인 V8 엔진 인스턴스를 가진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;b&gt;자바스크립트 &lt;/b&gt;이벤트 루프 동작 과정&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞의 내용을 복습하자면, 싱글 스레드인 자바스크립트에서도 작업의 동시 처리을 지원할 수 있는 비결에는 이벤트 루프가 자바스크립트 엔진과 브라우저의 웹 API를 연결하여 비동기적인 일 처리를 가능케 하기 때문이다. 다만 모든 자바스크립트 코드를 비동기로 처리할 수 있는 것은 아니다. 자바스크립트에는 비동기로 동작하는 &lt;b&gt;비동기 전용 함수&lt;/b&gt;가 있는데 대표적으로 &lt;s&gt;setTimeout&lt;/s&gt; 이나 &lt;s&gt;fetch&lt;/s&gt;, &lt;s&gt;addEventListener&lt;/s&gt; 가 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;545&quot; data-origin-height=&quot;502&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dKJfb7/btsahzxLUCl/aMJXnS5yjMbmdQgl9Gu5K0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dKJfb7/btsahzxLUCl/aMJXnS5yjMbmdQgl9Gu5K0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dKJfb7/btsahzxLUCl/aMJXnS5yjMbmdQgl9Gu5K0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdKJfb7%2FbtsahzxLUCl%2FaMJXnS5yjMbmdQgl9Gu5K0%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; alt=&quot;js-event-loop&quot; loading=&quot;lazy&quot; width=&quot;545&quot; height=&quot;502&quot; data-origin-width=&quot;545&quot; data-origin-height=&quot;502&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;브라우저의 Web APIs는 위 그림과 같이 각각 전용 작업을 처리하는 API 스레드들로 구성된 집합을 말한다. 따라서 &lt;s&gt;setTimeout&lt;/s&gt; 이 호출되면 Timer API 라는 별도의 스레드에서 타이머 동작이 별도로 실행되는 것이며, &lt;s&gt;fetch&lt;/s&gt; 가 호출되면 Ajax API 스레드에서 네트워크 통신이 이루어 지는 것이다.&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;이벤트 루프(Event Loop)는 이 비동기 함수 작업을 Web API에 옮기는 역할을 하고 작업이 완료되면 콜백을 큐(Queue)에 적재했다가 다시 자바스크립트 엔진에 적재해 수행시키는 일종의 &lt;span style=&quot;color: #ee2323;&quot;&gt;'작업을 옮기는 역할'&lt;/span&gt; 만을 한다. 작업을 처리하는 주체는 자바스크립트 엔진과 웹 API 이다. 그래서 이벤트 루프는 Call Stack에 현재 실행 중인 작업이 있는지 그리고 Task Queue에 대기 중인 작업이 있는지 &lt;span style=&quot;color: #ee2323;&quot;&gt;반복적으로 확인하는 일종의 무한 루프&lt;/span&gt;만을 돌고, 대기 작업이 있다면 작업을 옮겨주는 형태로 동작한다고 보면 된다. (그래서 이벤트 '루프' 이다)&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1681459426616&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 이벤트 루프의 동작을 나타내는 가상의 코드
while(queue.waitForMessage()){ // 큐에 메시지가 있을 때까지 대기
  queue.processNextMessage(); // 가장 오래된 메시지를 큐에서 꺼내서 호출 스택으로 옮김
}&lt;/code&gt;&lt;/pre&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;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://dev.to/lydiahallie&quot;&gt;Lydia Hellie&lt;/a&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;분이&lt;/span&gt; 아주 고퀄리티로 gif 애니메이션으로 표현한 이미지가 있어서 이를 참고하여 소개해본다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;setTimeOut 내부 동작 과정&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 가장 흔한 타이머 비동기 함수의 이벤트 루프 과정을 알아보도록 하자.&lt;/p&gt;
&lt;pre id=&quot;code_1681460710697&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function bar() {
  setTimeout(() =&amp;gt; {
  	console.log(&quot;Second&quot;)
  }, 500);
}

function foo() {
  console.log(&quot;First&quot;);
}

function baz() {
  console.log(&quot;Third&quot;);
}

bar();
foo();
baz();&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;gif14.1.gif&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;405&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PLDmM/btsae6cCQlI/hddR9cc75XkgnsO3EUqSoK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PLDmM/btsae6cCQlI/hddR9cc75XkgnsO3EUqSoK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PLDmM/btsae6cCQlI/hddR9cc75XkgnsO3EUqSoK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/PLDmM/btsae6cCQlI/hddR9cc75XkgnsO3EUqSoK/img.gif&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; alt=&quot;setTimeOut&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;405&quot; data-filename=&quot;gif14.1.gif&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;405&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;s&gt;bar()&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수가 호출되고 그안의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;&lt;s&gt;setTimeout()&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수가 호출되어 스택에 쌓인다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;s&gt;setTimeout()&lt;/s&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수의 매개변수에 할당된 콜백 함수를 Timer Web API에 전달한다. 그리고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;Timer Web API 에서는 백그라운드로 500 밀리초를 셈한다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;다음&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;foo()&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수가 호출되고 콘솔창(output)에&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&quot;&lt;/span&gt;First&quot;&lt;span&gt;&amp;nbsp;&lt;/span&gt;가 출력된다.&lt;/li&gt;
&lt;li&gt;이때&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;500 밀리초&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;대기 시간이 만료되면서&lt;span&gt;, &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;이벤트 루프는&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;Timer Web API에서 가지고 있던 콜백 함수를&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;Task Queue&lt;span&gt;&amp;nbsp;&lt;/span&gt;로 옮긴다.&lt;/li&gt;
&lt;li&gt;그다음&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;baz()&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수가 호출되고 콘솔창에&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&quot;Third&quot;&lt;span&gt;&amp;nbsp;&lt;/span&gt;가 출력된다.&lt;/li&gt;
&lt;li&gt;스택에 있는 모든 메인 자바스크립트 코드가 실행 완료 되어&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;Call Stack이 비워지게 된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;이벤트 루프는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;Call Stack&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;이 비어있는 경우를 탐지하여,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Task Queue&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;에 있는 콜백 함수를&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;Call Stack&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;으로 옮긴다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Call Stack&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;&quot;&lt;/span&gt;Second&quot;&lt;span&gt;&amp;nbsp;&lt;/span&gt;가 출력된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 동작 원리의 핵심은 특정한 작업에 대해 비동기로 멀티 작업을 할 수 있다는 것이다. 비동기 동작 예시가 고작 타이머를 셈하는 &lt;s&gt;setTimeout&lt;/s&gt; 이라 잘 와닿지 않을 수 있다. &lt;span style=&quot;color: #ee2323;&quot;&gt;하지만 파일 입출력이나 키보드 타이핑 하는 이벤트 동작일 경우라면 어떻까? 비동기가 없다면 파일을 다운 받거나 &lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;키보드를 타이핑 하는 동안에&lt;/span&gt;는 동안 웹사이트는 멈추게 되어 아무것도 못하게 될 것이다. 바로 이벤트 루프는 이러한 작업들을 별도로 브라우저의 멀티 스레드에게 인가하여 비동기로 처리해주는 핸들러 역할&lt;/span&gt;을 하는 것이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;웹브라우저와 Node.js의 Web API 차이&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;웹브라우저의 Web APIs 와 &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;Node.js 의 &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;Node.js APIs 들은 구성은 비슷하지만 동작 측면에서 약간 차이가 있다. &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;웹브라우저의 Web APIs&lt;/span&gt;&lt;/span&gt;는 비동기 작업이 끝나면 스스로 callback queue에 적재하지만, Node.js API들은 이벤트 루프가 직접 옮겨준다. 예를들어 Timer Web API에서 타이머가 모두 지나가면, 자바스크립트 환경이 웹브라우저냐 Node.js 냐에 따라 차이가 갈린다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Node.js : Timer API가 타이머 완료 이벤트를 발생시키고, 이벤트 루프가 이를 감지하여 Task Queue에 콜백 함수를 추가한다.&lt;/li&gt;
&lt;li&gt;웹브라우저 : Timer API가 스스로 Task Queue에 콜백 함수를 추가한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Promise 내부 동작 과정&lt;/b&gt;&lt;/h3&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;Task Queue 와 Microtask Queue&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Callback&lt;/span&gt; Queue는 Web API가 수행한 비동기 함수를 넘겨받아 Event Loop가 해당 함수를 Call Stack에 넘겨줄 때까지 비동기 함수들을 쌓아놓는 곳이다.&amp;nbsp;&lt;/span&gt;위에서 Callback Queue의 종류에는 (Macro)Task Queue, MicroTask Queue 2가지가 있다고 하였는데, 그중 자바스크립트 Promise 객체의 콜백이 쌓이는 곳이 바로 MicroTask Queue이다. 그리고 &lt;span style=&quot;color: #ee2323;&quot;&gt;MicroTask Queue는 그 어떤 곳보다 가장 먼저 우선으로 콜백이 처리&lt;/span&gt;되게 된다. (심지어 브라우저 화면 렌더링하기 전보다 말이다)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zl8dR/btsaipa7J1Z/ZPFXZZikQbb2s8b1cv6jNk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zl8dR/btsaipa7J1Z/ZPFXZZikQbb2s8b1cv6jNk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zl8dR/btsaipa7J1Z/ZPFXZZikQbb2s8b1cv6jNk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/zl8dR/btsaipa7J1Z/ZPFXZZikQbb2s8b1cv6jNk/img.gif&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; alt=&quot;Event-Loop&quot; loading=&quot;lazy&quot; width=&quot;880&quot; height=&quot;495&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;MicroTask Queue 처리 과정&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;실제 예시 코드에서&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;setTimeout&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;가&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;Promise&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;객체의 콜백이 동시에 주어졌을때 어떻게 처리되는지 확인해보자.&lt;span&gt; &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;코드 내용을 살펴보면 먼저&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;setTimeout&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;을 통해서 0초동안 대기 하였다가&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;Timeout!&quot;&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;을 출력하는 콜백 함수를 실행한다. 그다음 Promise 객체에 의해 &quot;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;Promise!&quot;&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;라는 텍스트를 출력하는 then 핸들러의 콜백 함수를 실행한다. 이 코드를 실행해보면 아래와 같이 동작 애니메이션이 발생한다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1683182337116&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log('Start!');

setTimeout(() =&amp;gt; {
	console.log('Timeout!');
}, 0);

Promise.resolve('Promise!').then(res =&amp;gt; console.log(res));

console.log('End!');&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: #ef5369;&quot;&gt;1.&lt;/span&gt; &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;Call Stack에 &lt;s&gt;console.log('Start!')&lt;/s&gt; 코드 부분이 쌓인 뒤 실행 되어 콘솔창에&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;Start!&quot;&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;가 출력&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDBeDc/btsai5wHXgq/cKKavQEiNFiadjMDFkGq41/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDBeDc/btsai5wHXgq/cKKavQEiNFiadjMDFkGq41/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDBeDc/btsai5wHXgq/cKKavQEiNFiadjMDFkGq41/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bDBeDc/btsai5wHXgq/cKKavQEiNFiadjMDFkGq41/img.gif&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; alt=&quot;Event-Loop&quot; loading=&quot;lazy&quot; width=&quot;880&quot; height=&quot;495&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;2.&lt;/span&gt; &lt;s&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;setTimeout&lt;/span&gt;&lt;/s&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt; 코드가&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt; 콜 스택에 적재되고 실행되면, 그 안의 콜백 함수가 이벤트 루프에 의해 &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;Web API로 옮겨지고&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt; 타이머가 작동하게 된다. (0초라서 사실상 바로 타이머는 종료된다)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTgarW/btsajdaqFYo/GZo7cnWmdNj2ReoYZEQq31/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTgarW/btsajdaqFYo/GZo7cnWmdNj2ReoYZEQq31/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTgarW/btsajdaqFYo/GZo7cnWmdNj2ReoYZEQq31/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bTgarW/btsajdaqFYo/GZo7cnWmdNj2ReoYZEQq31/img.gif&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; alt=&quot;Event-Loop&quot; loading=&quot;lazy&quot; width=&quot;880&quot; height=&quot;495&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;3.&lt;/span&gt; 타이머가 종료됨에 따라&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;setTimeout&lt;/span&gt;&lt;/s&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;의 콜백 함수는&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;MacroTask&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt; Queue에 이벤트 루프에 의해 적재되게 &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;된다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #ef5369; text-align: start;&quot;&gt;4. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;s&gt;Promis&lt;/s&gt;e&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt; 코드가 &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;콜스택에 적재 되어 &lt;/span&gt;실행되고 &lt;s&gt;then&lt;/s&gt; 핸들러의 콜백 함수가 이벤트 루프에 의해 &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;MicroTask&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Queue&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;에 적재되게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qU8M0/btsafe2PCgw/OJzzPhbPwnsknK065db4y0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qU8M0/btsafe2PCgw/OJzzPhbPwnsknK065db4y0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qU8M0/btsafe2PCgw/OJzzPhbPwnsknK065db4y0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/qU8M0/btsafe2PCgw/OJzzPhbPwnsknK065db4y0/img.gif&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; alt=&quot;Event-Loop&quot; loading=&quot;lazy&quot; width=&quot;880&quot; height=&quot;495&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;5.&lt;/span&gt; &lt;s&gt;console.log('End!')&lt;/s&gt; 코드가 실행되고&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;End!&quot;&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt; 텍스트가&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt; 콘솔창에 출력된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvPkz8/btsaepD56CQ/zebvq7iDfESjMcndjQwXok/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvPkz8/btsaepD56CQ/zebvq7iDfESjMcndjQwXok/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvPkz8/btsaepD56CQ/zebvq7iDfESjMcndjQwXok/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bvPkz8/btsaepD56CQ/zebvq7iDfESjMcndjQwXok/img.gif&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; alt=&quot;Event-Loop&quot; loading=&quot;lazy&quot; width=&quot;880&quot; height=&quot;495&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;6.&lt;/span&gt; 모든 메인 스레드의 자바스크립트 코드가 실행이되어 더이상 Call Stack엔 실행할 스택이 없어 비워지게 된다.&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;7.&lt;/span&gt; 그러면 이벤트 핸들러가 이를 감지하여, Callback Queue에 남아있는 콜백 함수들을 빼와 Call Stack에 적재하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;8.&lt;/span&gt; 이때 2종류의 Queue 중 MicroTask Queue에 남아있는 콜백이 우선적으로 처리된다. (만일 콟개이 여러개가 있다면 전부 처리된다)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWjCGD/btsafqCaL58/24KWYAif1iBKJ5Ec0tE4dk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWjCGD/btsafqCaL58/24KWYAif1iBKJ5Ec0tE4dk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWjCGD/btsafqCaL58/24KWYAif1iBKJ5Ec0tE4dk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bWjCGD/btsafqCaL58/24KWYAif1iBKJ5Ec0tE4dk/img.gif&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; alt=&quot;Event-Loop&quot; loading=&quot;lazy&quot; width=&quot;880&quot; height=&quot;495&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&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;span style=&quot;color: #ef5369;&quot;&gt;9.&lt;/span&gt; &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;MicroTask Queue가 비어지면, 이제 이벤트 루프는 MacroTask Queue에 있는 콜백 함수를 Call Stack에 적재해 실행되게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tr16U/btsaibjF5gy/g3cKJDWWCoakGef1H30w41/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tr16U/btsaibjF5gy/g3cKJDWWCoakGef1H30w41/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tr16U/btsaibjF5gy/g3cKJDWWCoakGef1H30w41/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/tr16U/btsaibjF5gy/g3cKJDWWCoakGef1H30w41/img.gif&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; alt=&quot;Event-Loop&quot; loading=&quot;lazy&quot; width=&quot;880&quot; height=&quot;495&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;따라서 최종 코드의 실행 순서는 아래와 같이 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;220&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KM2vZ/btsdXJjHIYm/7nSjqHdLe1hkt5j9KSxbAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KM2vZ/btsdXJjHIYm/7nSjqHdLe1hkt5j9KSxbAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KM2vZ/btsdXJjHIYm/7nSjqHdLe1hkt5j9KSxbAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKM2vZ%2FbtsdXJjHIYm%2F7nSjqHdLe1hkt5j9KSxbAK%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; alt=&quot;Event-Loop&quot; loading=&quot;lazy&quot; width=&quot;596&quot; height=&quot;220&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;220&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Async/Await 내부 동작 과정&lt;/b&gt;&lt;/h3&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;자바스크립트의&amp;nbsp;Async/Await&lt;span&gt; &lt;/span&gt;는 비동기 논블로킹 동작을 동기적으로 처리하기 위해&lt;span&gt;&amp;nbsp;&lt;/span&gt;ES7&lt;span&gt;&amp;nbsp;&lt;/span&gt;부터 새롭게 도입된 것으로 복잡한 콜백이나 &lt;s&gt;then&lt;/s&gt; 핸들러의 지옥(hell) 코드를 극복하는 핵심이다. Async/Await&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt; 의&amp;nbsp;&lt;/span&gt;&lt;/span&gt;기본적인 사용 문법에 대해서는 독자분들도 익히 알고 있을 것이다. 하지만 많은 사람들이 Async/Await&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt; 을 단순히 비동기를 동기적으로 처리해준다는 효과만 알고있어서인지, &lt;/span&gt;&lt;/span&gt;비동기 코드와 동기 코드가 같이 쓰여져 있을 경우 이들의 확실한 처리 과정을 정확하게 알지 못한다.&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;예를들어 다음과 같이 async 함수를 동일 코드 레벨에서 실행한다고 해보자.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; style=&quot;color: #000000; text-align: left;&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;const one = () =&amp;gt; Promise.resolve('One!');

async function myFunc(){
	console.log('In function!');
	const res = await One();
	console.log(res);
}

console.log('Before Function!');
myFunc();
console.log('After Function!');&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;1.&lt;/span&gt; 콘솔에 'Before Function!' 이 출력된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zfFHR/btsafp4jCC1/uJ0m7Nwnxhk6QHazz8AaV0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zfFHR/btsafp4jCC1/uJ0m7Nwnxhk6QHazz8AaV0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zfFHR/btsafp4jCC1/uJ0m7Nwnxhk6QHazz8AaV0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/zfFHR/btsafp4jCC1/uJ0m7Nwnxhk6QHazz8AaV0/img.gif&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; alt=&quot;Event-Loop&quot; loading=&quot;lazy&quot; width=&quot;880&quot; height=&quot;495&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;2.&lt;/span&gt; async 함수인 &lt;s&gt;myFunc()&lt;/s&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;이 호출된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;3.&lt;/span&gt; async 함수 안에 있는 콘솔 함수가 실행되어 &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;콘솔에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;'&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;In Function!&lt;/span&gt;' 이 출력된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;가끔 async 함수를 메인 코드가 모두 실행되어야 나중에 실행되는 비동기 함수로 알고 있는 사람들이 많은데, async 함수는 블럭 내부에서 await 키워드를 사용하기 위한 함수 키워드 일 뿐 그냥 프로미스 객체를 반환하는 조금 특벽한 일반적인 함수이다. 따라서 async 함수라도 일반 함수처럼 호출 스택이 쌓이고, async 함수 내의 비동기가 아닌 동기 코드는 일반 코드와 같이 실행된다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b93k2Z/btsaff8yapn/Vv9Rfl66vXpvj6Xz2OxgK0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b93k2Z/btsaff8yapn/Vv9Rfl66vXpvj6Xz2OxgK0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b93k2Z/btsaff8yapn/Vv9Rfl66vXpvj6Xz2OxgK0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/b93k2Z/btsaff8yapn/Vv9Rfl66vXpvj6Xz2OxgK0/img.gif&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; alt=&quot;Event-Loop&quot; loading=&quot;lazy&quot; width=&quot;880&quot; height=&quot;495&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;4.&lt;/span&gt; Promise 객체를 반환하는 &lt;s&gt;one()&lt;/s&gt; 비동기 함수를 호출한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;5.&lt;/span&gt; 이때 &lt;s&gt;one()&lt;/s&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;비동기 함수 왼쪽에 &lt;s&gt;await&lt;/s&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;키워드로 인해, &lt;s&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;myFunc&lt;/span&gt;&lt;/s&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;함수의 내부 실행은 잠시 중단되고&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;Call stack&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;에서 빠져나와 나머지 부분은&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;Microtask Queue&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;에 적재된다. 이는 자바스크립트 엔진이&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;await&lt;/span&gt;&lt;/s&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;키워드를 인식하면&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;async&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;함수의 실행은 지연되는 것으로 처리하기 때문이다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q0IXr/btsaeYTv6TX/OenBKphVMQk1inFvDcVJIK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q0IXr/btsaeYTv6TX/OenBKphVMQk1inFvDcVJIK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q0IXr/btsaeYTv6TX/OenBKphVMQk1inFvDcVJIK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/q0IXr/btsaeYTv6TX/OenBKphVMQk1inFvDcVJIK/img.gif&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; alt=&quot;Event-Loop&quot; loading=&quot;lazy&quot; width=&quot;880&quot; height=&quot;495&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;6.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;span&gt; 마지막으로&amp;nbsp;&lt;/span&gt;콘솔에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;'&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;After &lt;/span&gt;Function!' 이 출력된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOLCDm/btsajduJexA/Xehs8yznMC17HGKrI3iKa0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOLCDm/btsajduJexA/Xehs8yznMC17HGKrI3iKa0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOLCDm/btsajduJexA/Xehs8yznMC17HGKrI3iKa0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bOLCDm/btsajduJexA/Xehs8yznMC17HGKrI3iKa0/img.gif&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; alt=&quot;Event-Loop&quot; loading=&quot;lazy&quot; width=&quot;880&quot; height=&quot;495&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&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;span style=&quot;color: #ef5369;&quot;&gt;7.&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;모든 메인 스레드의 자바스크립트 코드가 실행이되어 더이상 Call Stack엔 실행할 스택이 없어 비워지게 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;8.&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;그러면 이벤트 핸들러가 이를 감지하여, Microtask Queue에 남아있는 async 함수를 빼와 Call Stack에 적재하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;8.&lt;/span&gt;&lt;span&gt; Promise 객체의 결과물인 'One!' 문자열을 변수 &lt;s&gt;res&lt;/s&gt; 에 받고 이를 콘솔에 출력한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EwKK1/btsah97dnhU/89l6MTtAgOTWBYAakG6L1k/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EwKK1/btsah97dnhU/89l6MTtAgOTWBYAakG6L1k/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EwKK1/btsah97dnhU/89l6MTtAgOTWBYAakG6L1k/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/EwKK1/btsah97dnhU/89l6MTtAgOTWBYAakG6L1k/img.gif&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; alt=&quot;Event-Loop&quot; loading=&quot;lazy&quot; width=&quot;880&quot; height=&quot;495&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Async/Await 오해와 진짜 동작&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그럼 이번에는 &lt;s&gt;myFunc()&lt;/s&gt; 함수를 호출하는 메인 스택에서 &lt;s&gt;await&lt;/s&gt; 키워드를 붙여주면 어떻게 될까? 참고로 최신 자바스크립트에서는 최상위 await 을 지원하기 때문에 별도로 &lt;a href=&quot;https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-IIFE-%EC%A6%89%EC%8B%9C%EC%8B%A4%ED%96%89-%ED%95%A8%EC%88%98-%ED%91%9C%ED%98%84%EC%8B%9D&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;즉시 실행 IIFE&lt;/a&gt;를 묶지 않아도 동작이 가능하다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; style=&quot;color: #000000; text-align: left;&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;const one = () =&amp;gt; Promise.resolve('One!');

async function myFunc(){
	console.log('In function!');
	const res = await one();
	console.log(res);
}

console.log('Before Function!');
await myFunc();
console.log('After Function!');&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;433&quot; data-origin-height=&quot;93&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ri5H7/btsd0tJod8k/f8hDQFasnKjxVrIKNWLah0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ri5H7/btsd0tJod8k/f8hDQFasnKjxVrIKNWLah0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ri5H7/btsd0tJod8k/f8hDQFasnKjxVrIKNWLah0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fri5H7%2Fbtsd0tJod8k%2Ff8hDQFasnKjxVrIKNWLah0%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; alt=&quot;Event-Loop&quot; loading=&quot;lazy&quot; width=&quot;433&quot; height=&quot;93&quot; data-origin-width=&quot;433&quot; data-origin-height=&quot;93&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;출력 결과를 보면, 위의 Async/Await 처리 과정 1 과 다른 결과가 나오는걸 볼 수 있다. 일단 기본적인 Async/Await 문법 개념을 알고 있다면 이 부분은 그렇게 어렵지 않을 것이다. 하지만 우리는 지금 이벤트 루프 동작에 대해 배우고 있다. 그럼 저 최상위 await 코드 동작을 이벤트 루프로 내부적으로 따져보자.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;아마 많은 새내기들이 여기서 막힐 것이다. 우리는 await 키워드를 사용하는 Promise 반환 비동기 &lt;s&gt;one()&lt;/s&gt; 함수의 결과 콜백이 Microtask Queue에 적재된다는 것을 알고 있다. 그런데 &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;Microtask Queue의 콜백들은 우선 메인 Call Stack이 비워져 있어야 이벤트 루프가 옮겨 처리한다. 하지만 아직 메인 &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;Call Stack에는 &lt;s&gt;console.log(&amp;lsquo;After Function!&amp;rsquo;)&lt;/s&gt; 이 남아 있다. 그런데 출력 결과를 보면 &lt;s&gt;console.log(&amp;lsquo;After Function!&amp;rsquo;)&lt;/s&gt; 이 실행되기 전에 &lt;s&gt;await myFunc()&lt;/s&gt; 먼저 처리되고 콘솔을 출력함을 볼 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이게 어떻게 된 일 일까? 우리가 지금까지 배운 이벤트 루프 동작이 잘못된 것일까?&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사실 Async/Await의 진짜 동작은 다음과 같다. 예를 들어, 아래와 같은 코드가 있다고 하자.&lt;/p&gt;
&lt;pre id=&quot;code_1683514829946&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let x = await bar(); // bar() 함수 정의는 생략
console.log(x);
console.log('Done');&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위의 코드는 사실 다음과 같은 코드와 같은 의미를 가진다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1683514882147&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;bar().then(x =&amp;gt; { 
    console.log(x); 
    console.log('Done'); 
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;즉, await 키워드 다음에 나오는 동일 라인의 코드들은 모두 &lt;s&gt;await bar()&lt;/s&gt; 의 &lt;s&gt;then&lt;/s&gt; 핸들러의 콜백 함수로 들어간다는 뜻이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 async/await 키워드는 &lt;s&gt;promise.then()&lt;/s&gt; 메소드를 사용하지 않고도 비동기 코드를 작성할 수 있게 해주는 문법적인 편의 기능(syntactic sugar)일 뿐이다. 즉, async/await 키워드는 &lt;s&gt;promise.then()&lt;/s&gt; 메소드를 대체하는 것이 아니라, promise.then() 메소드를 간결하고 명확하게 작성할 수 있게 해주는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 최종 정리하자면 &lt;s&gt;myFunc()&lt;/s&gt; 코드는 프로미스 핸들러로 풀면 아래와 같이 구성되게 되고, 각 then 핸들러들이 MicroTask Queue에 차곡 차곡 쌓임으로써 순서대로 출력되는 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1683515119226&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const one = () =&amp;gt; Promise.resolve('One!');

async function myFunc(){
	console.log('In function!');
	const res = await one();
	console.log(res);
}

console.log('Before Function!');
await myFunc();
console.log('After Function!');

/* ---------------- &amp;darr;&amp;darr;&amp;darr; 변환 &amp;darr;&amp;darr;&amp;darr; ---------------- */

const one = () =&amp;gt; Promise.resolve('One!'); 

function myFunc(){
	console.log('In function!');
	return one().then(res =&amp;gt; {
	  console.log(res);
	});
}

console.log('Before Function!');
myFunc().then(() =&amp;gt; {
  console.log('After Function!');
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;s&gt;await myFunc()&lt;/s&gt; 다음에 나오는 코드들이 &lt;s&gt;myFunc()&lt;/s&gt;의 then 핸들러의 콜백으로서 &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;Microtask Queue에 적재되고 이벤트 루프에 다시 Call stack에 옮겨져서 실행되는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;MicroTask Queue의 무한 루프&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Task Queue와 MicroTask Queue의 차이점은 단순한 우선순위 차이 정도 밖에 없지만, 좀더 확장해서 브라우저 동작 관점에서 보면 이는 굉장히 중요한 요소이다. MicroTask Queue의 콜백은 가장 높은 우선 순위로 처리되지만 이는 브라우저가 화면을 렌더링하는 과정보다 더 먼저 처리되게 되는데, 만일 MicroTask Queue에 잘못된 무한 루프 동작의 버그의 코드가 들어가게 되면 웹페이지가 먹통이 되어버리는 현상이 발생할 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드를 봐보자. 버튼이 하나 있고 여기에 이벤트 콜백이 등록 되어 있다. 이 이벤트 콜백은 Task Queue에 적재되어 실행되게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1683516850579&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;button id=&quot;btn&quot;&amp;gt;Click me&amp;lt;/button&amp;gt;

&amp;lt;script&amp;gt;
  // 버튼을 클릭하면 콘솔에 Clicked를 출력하는 이벤트 핸들러를 등록합니다.
  document.getElementById(&quot;btn&quot;).addEventListener(&quot;click&quot;, () =&amp;gt; {
    document.body.insertAdjacentHTML('beforeend', &quot;&amp;lt;p&amp;gt;Clicked&amp;lt;/p&amp;gt;&quot;);
  });
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 343px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;343&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;vYVdRzO&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/vYVdRzO&quot;&gt; MicroTask Queue의 무한 루프 1&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서 한번 &lt;s&gt;setTimeout&lt;/s&gt; 무한 루프를 구현해보자. 아래 '무한루프 시작' 버튼을 누르면 타이머가 계속 돌여 &quot;Loop&quot; 콘솔을 출력할 것이다. 그 상태에서 버튼을 Click me 해보자. 아무 이상 없이 화면에 'Clicked'가 표시될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;setTimeout&lt;/s&gt; 의 루프 콜백이 Task Queue에 실시간으로 적재되면서 처리되는 도중, 버튼 클릭 이벤트를 발생시키면 이벤트 콜백 역시 Task Queue에 적재되어 실행이 되기 때문에, 비록 무한 루프 코드라도 브라우저 동작 자체는 문제없는 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1683517013865&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;button id=&quot;btn&quot;&amp;gt;Click me&amp;lt;/button&amp;gt;
&amp;lt;button id=&quot;btn2&quot;&amp;gt;무한루프 시작&amp;lt;/button&amp;gt;

&amp;lt;div id=&quot;loopArea&quot; style=&quot;border: 1px solid black; max-height: 150px; overflow-y: auto;&quot;&amp;gt;&amp;lt;/div&amp;gt;

&amp;lt;script&amp;gt;
  // 버튼을 클릭하면 콘솔에 Clicked를 출력하는 이벤트 핸들러를 등록합니다.
  document.getElementById(&quot;btn&quot;).addEventListener(&quot;click&quot;, () =&amp;gt; {
    document.body.insertAdjacentHTML('beforeend', &quot;&amp;lt;p&amp;gt;Clicked&amp;lt;/p&amp;gt;&quot;);
  });

  document.getElementById(&quot;btn2&quot;).addEventListener(&quot;click&quot;, () =&amp;gt; {
    // setTimeout 함수는 Task Queue에 콜백을 추가
    function loop() {
      setTimeout(() =&amp;gt; {
        document.getElementById(&quot;loopArea&quot;).append(&quot;Loop\n&quot;);
        loop(); // loop 함수가 setTimeout 객체의 콜백으로 자신을 계속해서 추가
      }, 0);
    }

    // loop 함수를 호출
    loop();
  });
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 500px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;500&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;ZEqrxZM&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/ZEqrxZM&quot;&gt; MicroTask Queue의 무한 루프 2&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 Promise 객체를 이용해 재귀 함수 호출로 무한 루프를 구현해 보자. 로프 동작 코드 자체는 위의 &lt;s&gt;setTimeout&lt;/s&gt;과 별다른 차이가 없다. 다만 이 루프 콜백이 Task Queue 가 아닌 MicroTask Queue에 들어간다는 차이점이 있는데, MicroTask Queue 가 무한 루프에 빠지게 되면 어떻게 되는지 보자. (예비 브라우저를 하나 준비해서 테스트하기를 권장한다)&lt;/p&gt;
&lt;pre id=&quot;code_1683517474840&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;button id=&quot;btn&quot;&amp;gt;Click me&amp;lt;/button&amp;gt;
&amp;lt;button id=&quot;btn2&quot;&amp;gt;무한루프 시작&amp;lt;/button&amp;gt;

&amp;lt;div id=&quot;loopArea&quot; style=&quot;border: 1px solid black; max-height: 150px; overflow-y: auto;&quot;&amp;gt;&amp;lt;/div&amp;gt;

&amp;lt;script&amp;gt;
  // 버튼을 클릭하면 콘솔에 Clicked를 출력하는 이벤트 핸들러를 등록합니다.
  document.getElementById(&quot;btn&quot;).addEventListener(&quot;click&quot;, () =&amp;gt; {
    document.body.insertAdjacentHTML('beforeend', &quot;&amp;lt;p&amp;gt;Clicked&amp;lt;/p&amp;gt;&quot;);
  });

  document.getElementById(&quot;btn2&quot;).addEventListener(&quot;click&quot;, () =&amp;gt; {
    // Promise 객체는 MicroTask Queue에 콜백을 추가
    function loop() {
      Promise.resolve().then(() =&amp;gt; {
        document.getElementById(&quot;loopArea&quot;).append(&quot;Loop\n&quot;);
        loop(); // loop 함수가 Promise 객체의 콜백으로 자신을 계속해서 추가
      });
    }

    // loop 함수를 호출
    loop();
  });
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 500px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;500&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;rNqJdbQ&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/rNqJdbQ&quot;&gt; MicroTask Queue의 무한 루프 3&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹사이트가 먹통이 되어 버릴 것이다. 똑같은 무한 루프 동작인데 하나는 웹사이트 동작에 문제가 없었고, 하나는 웹사이트 자체가 블락이 되어버려 어떠한 동작을 할 수 없게 된 것이다. 왜냐하면 MicroTask Queue가 비어있지 않으므로 다른 매크로태스크나 이벤트 콜백이 실행될 수 없기 때문이다. 무한 루프 코드 때문에 끊임없이 MicroTask Queue에 콜백이 적재되게 되고 이벤트 루프가 이들을 브라우저 화면 렌더링을 하기전에 우선으로 끊임 없이 처리 또 처리하고 있기 때문에, 결국&amp;nbsp;사용자 이벤트에 반응하지 못하고, 페이지가 멈추거나 응답하지 않게 되는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예시 코드의 사례는 조금 극단적인 억지 코드라 잘 와닿지 않을 수 있다. 하지만 이 파트의 핵심은 &lt;span style=&quot;color: #ee2323;&quot;&gt;만일 MicroTask Queue를 사용하는 Promise 코드나 Mutation Observer 의 콜백에서 무한 루프 까지는 아니더라도 지나치게 MicroTask Queue에 콜백을 지나치게 적재하는 작업이 있다면 이를 조심히 다루어야 한다는 점&lt;/span&gt;이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Animation&amp;nbsp;Frames&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Animation Frames는 브라우저가 화면을 다시 그릴 때 실행되는 함수들을 담아두는 곳이다. 예를 들어, 웹 페이지에서 움직이는 동그라미를 그리고 싶다면, requestAnimationFrame이라는 함수를 사용해서 동그라미의 위치를 바꿔주는 콜백 함수를 매개변수로 넣어줘 Animation Frames에 적재되어 실행된다. 그러면 브라우저가 화면을 갱신할 때마다 동그라미의 위치가 바뀌면서 움직이는 것처럼 보이게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1683519698546&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// setTimeout() 함수 매개변수에 콜백 함수를 넣어주듯이 requestAnimationFrame도 마찬가지이다
requestAnimationFrame(function() {
    // 동그라미의 요소를 찾아서 변수에 저장
    let circle = document.getElementById(&quot;circle&quot;);

    // 동그라미의 스타일을 변경하여 위치를 반영
    circle.style.left = x + &quot;px&quot;;
    circle.style.top = y + &quot;px&quot;;
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Animation Frames의 우선순위는 브라우저에 따라 다를 수 있다. Task Queue와 Microtask Queue가 모두 처리된 후에 실행될 수도 있고, T&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;ask Queue와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Microtask Queue 사이에 실행될 수도 있다. 이는 이벤트 루프의 명세가 업데이트 렌더링 단계에서 언제 콜백을 호출할지 정확하게 정의하지 않기 때문이다.&lt;/p&gt;
&lt;pre id=&quot;code_1683518567956&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(&quot;script start&quot;);

// task queue에 적재
setTimeout(function () {
  console.log(&quot;setTimeout&quot;);
}, 0);

// microtask queue에 적재
Promise.resolve()
  .then(function () {
    console.log(&quot;promise1&quot;);
  })
  .then(function () {
    // 8. microtask 실행
    console.log(&quot;promise2&quot;);
  });

// AnimationFrame에 적재
requestAnimationFrame(function () {
  console.log(&quot;animation&quot;);
});

console.log(&quot;script end&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;330&quot; data-origin-height=&quot;162&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqDYGr/btseghguGUD/pp5BcNVMxmSmzzn2bfTnrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqDYGr/btseghguGUD/pp5BcNVMxmSmzzn2bfTnrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqDYGr/btseghguGUD/pp5BcNVMxmSmzzn2bfTnrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqDYGr%2FbtseghguGUD%2Fpp5BcNVMxmSmzzn2bfTnrK%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; alt=&quot;Animation Frames&quot; loading=&quot;lazy&quot; width=&quot;330&quot; height=&quot;162&quot; data-origin-width=&quot;330&quot; data-origin-height=&quot;162&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&quot;script start&quot;를 출력&lt;/li&gt;
&lt;li&gt;setTimeout 함수가 Task&amp;nbsp;Queue에 &amp;ldquo;setTimeout&amp;rdquo; 태스크를 추가&lt;/li&gt;
&lt;li&gt;Promise.resolve 함수가 MicroTask Queue에 &amp;ldquo;promise1&amp;rdquo; 태스크를 추가&lt;/li&gt;
&lt;li&gt;requestAnimationFrame 함수가 Animation Frames Queue에 &amp;ldquo;animation&amp;rdquo; 태스크를 추가&lt;/li&gt;
&lt;li&gt;&quot;script end&quot;를 출력&lt;/li&gt;
&lt;li&gt;&quot;promise1&quot;을 출력&lt;/li&gt;
&lt;li&gt;Promise.then 함수가 마이크로태스크 큐에 &amp;ldquo;promise2&amp;rdquo; 태스크를 추가&lt;/li&gt;
&lt;li&gt;&quot;promise2&quot;를 출력&lt;/li&gt;
&lt;li&gt;&quot;animation&quot;을 출력&lt;/li&gt;
&lt;li&gt;&quot;setTimeout&quot;을 출력&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;b&gt;자바스크립트 &lt;/b&gt;이벤트 루프 시각화&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 코드를 쓰고 조작하여 애니메이션으로 볼 수 있는 시각화 사이트가 있어 소개해본다. 이것저것 실험해보며 브라우저 내부에서 비동기 동작이 어떠한 순서로 일어나는지 알아보자.&lt;/p&gt;
&lt;figure id=&quot;og_1681449855807&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;http://latentflip.com/loupe/?code=JC5vbignYnV0dG9uJywgJ2NsaWNrJywgZnVuY3Rpb24gb25DbGljaygpIHsKICAgIHNldFRpbWVvdXQoZnVuY3Rpb24gdGltZXIoKSB7CiAgICAgICAgY29uc29sZS5sb2coJ1lvdSBjbGlja2VkIHRoZSBidXR0b24hJyk7ICAgIAogICAgfSwgMjAwMCk7Cn0pOwoKY29uc29sZS5sb2coIkhpISIpOwoKc2V0VGltZW91dChmdW5jdGlvbiB0aW1lb3V0KCkgewogICAgY29uc29sZS5sb2coIkNsaWNrIHRoZSBidXR0b24hIik7Cn0sIDUwMDApOwoKY29uc29sZS5sb2coIldlbGNvbWUgdG8gbG91cGUuIik7%21%21%21PGJ1dHRvbj5DbGljayBtZSE8L2J1dHRvbj4%3D&quot; data-og-description=&quot;&quot; data-og-host=&quot;latentflip.com&quot; data-og-source-url=&quot;http://latentflip.com/loupe/?code=JC5vbignYnV0dG9uJywgJ2NsaWNrJywgZnVuY3Rpb24gb25DbGljaygpIHsKICAgIHNldFRpbWVvdXQoZnVuY3Rpb24gdGltZXIoKSB7CiAgICAgICAgY29uc29sZS5sb2coJ1lvdSBjbGlja2VkIHRoZSBidXR0b24hJyk7ICAgIAogICAgfSwgMjAwMCk7Cn0pOwoKY29uc29sZS5sb2coIkhpISIpOwoKc2V0VGltZW91dChmdW5jdGlvbiB0aW1lb3V0KCkgewogICAgY29uc29sZS5sb2coIkNsaWNrIHRoZSBidXR0b24hIik7Cn0sIDUwMDApOwoKY29uc29sZS5sb2coIldlbGNvbWUgdG8gbG91cGUuIik7!!!PGJ1dHRvbj5DbGljayBtZSE8L2J1dHRvbj4%3D&quot; data-og-url=&quot;http://latentflip.com/loupe/?code=JC5vbignYnV0dG9uJywgJ2NsaWNrJywgZnVuY3Rpb24gb25DbGljaygpIHsKICAgIHNldFRpbWVvdXQoZnVuY3Rpb24gdGltZXIoKSB7CiAgICAgICAgY29uc29sZS5sb2coJ1lvdSBjbGlja2VkIHRoZSBidXR0b24hJyk7ICAgIAogICAgfSwgMjAwMCk7Cn0pOwoKY29uc29sZS5sb2coIkhpISIpOwoKc2V0VGltZW91dChmdW5jdGlvbiB0aW1lb3V0KCkgewogICAgY29uc29sZS5sb2coIkNsaWNrIHRoZSBidXR0b24hIik7Cn0sIDUwMDApOwoKY29uc29sZS5sb2coIldlbGNvbWUgdG8gbG91cGUuIik7%21%21%21PGJ1dHRvbj5DbGljayBtZSE8L2J1dHRvbj4%3D&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;http://latentflip.com/loupe/?code=JC5vbignYnV0dG9uJywgJ2NsaWNrJywgZnVuY3Rpb24gb25DbGljaygpIHsKICAgIHNldFRpbWVvdXQoZnVuY3Rpb24gdGltZXIoKSB7CiAgICAgICAgY29uc29sZS5sb2coJ1lvdSBjbGlja2VkIHRoZSBidXR0b24hJyk7ICAgIAogICAgfSwgMjAwMCk7Cn0pOwoKY29uc29sZS5sb2coIkhpISIpOwoKc2V0VGltZW91dChmdW5jdGlvbiB0aW1lb3V0KCkgewogICAgY29uc29sZS5sb2coIkNsaWNrIHRoZSBidXR0b24hIik7Cn0sIDUwMDApOwoKY29uc29sZS5sb2coIldlbGNvbWUgdG8gbG91cGUuIik7!!!PGJ1dHRvbj5DbGljayBtZSE8L2J1dHRvbj4%3D&quot; data-source-url=&quot;http://latentflip.com/loupe/?code=JC5vbignYnV0dG9uJywgJ2NsaWNrJywgZnVuY3Rpb24gb25DbGljaygpIHsKICAgIHNldFRpbWVvdXQoZnVuY3Rpb24gdGltZXIoKSB7CiAgICAgICAgY29uc29sZS5sb2coJ1lvdSBjbGlja2VkIHRoZSBidXR0b24hJyk7ICAgIAogICAgfSwgMjAwMCk7Cn0pOwoKY29uc29sZS5sb2coIkhpISIpOwoKc2V0VGltZW91dChmdW5jdGlvbiB0aW1lb3V0KCkgewogICAgY29uc29sZS5sb2coIkNsaWNrIHRoZSBidXR0b24hIik7Cn0sIDUwMDApOwoKY29uc29sZS5sb2coIldlbGNvbWUgdG8gbG91cGUuIik7!!!PGJ1dHRvbj5DbGljayBtZSE8L2J1dHRvbj4%3D&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;http://latentflip.com/loupe/?code=JC5vbignYnV0dG9uJywgJ2NsaWNrJywgZnVuY3Rpb24gb25DbGljaygpIHsKICAgIHNldFRpbWVvdXQoZnVuY3Rpb24gdGltZXIoKSB7CiAgICAgICAgY29uc29sZS5sb2coJ1lvdSBjbGlja2VkIHRoZSBidXR0b24hJyk7ICAgIAogICAgfSwgMjAwMCk7Cn0pOwoKY29uc29sZS5sb2coIkhpISIpOwoKc2V0VGltZW91dChmdW5jdGlvbiB0aW1lb3V0KCkgewogICAgY29uc29sZS5sb2coIkNsaWNrIHRoZSBidXR0b24hIik7Cn0sIDUwMDApOwoKY29uc29sZS5sb2coIldlbGNvbWUgdG8gbG91cGUuIik7%21%21%21PGJ1dHRvbj5DbGljayBtZSE8L2J1dHRvbj4%3D&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;latentflip.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1193&quot; data-origin-height=&quot;694&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFsVNK/btsae6CHKf0/ZAFNNRqZVHDmYOsymBELRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFsVNK/btsae6CHKf0/ZAFNNRqZVHDmYOsymBELRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFsVNK/btsae6CHKf0/ZAFNNRqZVHDmYOsymBELRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFsVNK%2Fbtsae6CHKf0%2FZAFNNRqZVHDmYOsymBELRK%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; alt=&quot;자바스크립트 이벤트 루프 시각화&quot; loading=&quot;lazy&quot; width=&quot;1193&quot; height=&quot;694&quot; data-origin-width=&quot;1193&quot; data-origin-height=&quot;694&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;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot; data-swap-href=&quot;https://youtu.be/YpQTeIqjC4o&quot;&gt;[10분 테코톡] 병민의 브라우저의 Event Loop&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://dev.to/lydiahallie/javascript-visualized-event-loop-3dif&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://dev.to/lydiahallie/javascript-visualized-promises-async-await-5gke&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://iamsjy17.github.io/javascript/2019/07/20/how-to-works-js.html&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://meetup.nhncloud.com/posts/89&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://sculove.github.io/post/javascriptflow/&lt;/p&gt;</description>
      <category>Language/JavaScript</category>
      <category>call stack</category>
      <category>Event Loop</category>
      <category>macrotask queue</category>
      <category>microtask queue</category>
      <category>task queue</category>
      <category>web api</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1110</guid>
      <comments>https://inpa.tistory.com/entry/%F0%9F%94%84-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84-%EA%B5%AC%EC%A1%B0-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC#entry1110comment</comments>
      <pubDate>Mon, 8 May 2023 13:30:25 +0900</pubDate>
    </item>
    <item>
      <title> &amp;zwj;  완벽히 이해하는 동기/비동기 &amp;amp; 블로킹/논블로킹</title>
      <link>https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-%EB%8F%99%EA%B8%B0%EB%B9%84%EB%8F%99%EA%B8%B0-%EB%B8%94%EB%A1%9C%ED%82%B9%EB%85%BC%EB%B8%94%EB%A1%9C%ED%82%B9-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Synchronous-and-Asynchronous-Programming-1024x491.webp&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;491&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eb7TAo/btr8NCYq0ag/Qw8QwvloA0I56B4UHQwx10/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eb7TAo/btr8NCYq0ag/Qw8QwvloA0I56B4UHQwx10/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eb7TAo/btr8NCYq0ag/Qw8QwvloA0I56B4UHQwx10/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Feb7TAo%2Fbtr8NCYq0ag%2FQw8QwvloA0I56B4UHQwx10%2Fimg.webp&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; alt=&quot;sync-async&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;491&quot; data-filename=&quot;Synchronous-and-Asynchronous-Programming-1024x491.webp&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;491&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;동기/비동기 &lt;span style=&quot;color: #0593d3;&quot;&gt;&amp;amp;&lt;/span&gt; 블로킹/논블록킹&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;프로그래밍에서 웹 서버 혹은 입출력(I/O)을 다루다 보면 &lt;/span&gt;&lt;b&gt;동기/비동기 &amp;amp; 블로킹/논블로킹&lt;/b&gt; 이러한 용어들을 접해본 경험이 한번 쯤은 있을 것이다. 대부분 사람들은 용어들이 나타내고자 하는 행위에 대해선 멀티 태스킹과 밀접한 관련이 있다는 것을 알고 있다. 그래서 두 개념을 비슷한 것으로 오해하는 사람들이 꽤 많다.  &amp;zwj; &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동기/비동기 와 블로킹/논블로킹 이 두 개념은 표현 형태는 비슷해 보일지라도, 서로 다른 차원에서 작업의 수행 방식을 설명하는 개념이다. &lt;span style=&quot;color: #ee2323;&quot;&gt;동기/비동기&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;요청한 작업에 대해 완료 여부를 신경 써서 &lt;span style=&quot;color: #ee2323;&quot;&gt;작업을 순차적으로 수행할지 아닌지&lt;/span&gt;에 대한 관점이고&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;,&lt;span&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;블로킹/논블록킹&lt;/span&gt;은 단어 그대로 &lt;span style=&quot;color: #ef6f53;&quot;&gt;현재 작업이 block(차단, 대기) 되느냐 아니냐&lt;/span&gt;에 따라&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;다른 작업을 수행할 수 있는지에 대한&amp;nbsp;관점이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;787&quot; data-origin-height=&quot;476&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxdoIN/btsdsRqwj1H/UDfs5lz8ZnGg9A1oiD1kK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxdoIN/btsdsRqwj1H/UDfs5lz8ZnGg9A1oiD1kK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxdoIN/btsdsRqwj1H/UDfs5lz8ZnGg9A1oiD1kK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxdoIN%2FbtsdsRqwj1H%2FUDfs5lz8ZnGg9A1oiD1kK1%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; alt=&quot;sync-async&quot; loading=&quot;lazy&quot; width=&quot;787&quot; height=&quot;476&quot; data-origin-width=&quot;787&quot; data-origin-height=&quot;476&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 두 개념에 대한 의미 차이는 명확하지만, 프로그래밍에서는 종종 혼용되어 사용되기도 한다. 대표적으로 자바스크립트의 &lt;s&gt;setTimeout()&lt;/s&gt; 함수를 일반적으로 비동기 함수라고만 부르지만 동시에 논블로킹 함수이기도 하다. 즉, 우리가 편의상 부르는 자바스크립트 비동기 함수는 사실 비동기 + 논블로킹 함수인 것이다. 따라서 이들을 정확하게 구분하고 이해하는 것이 컴퓨터 아키텍쳐를 이해하는데 있어 중요하다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;동기(Synchronous) / 비동기(Asynchronous)&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;동기(同期)와 비동기(非同期)를 알아보기 앞서, 이 두 개념을 한글이 아닌 영단어로 학습 하길 강력히 권장하는 바다. 왜냐하면 한글로 직역하자면 '같은 기간' 또는 '같은 주기'라는 뜻의 한자를 사용하는데 오히려 의미를 이해하는데 있어 더욱 혼동만 주기 때문이다. 그래서 &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;영단어를 파보자면 Synchronous의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Syn&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;그리스어로&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;'함께&lt;/span&gt;&lt;/span&gt;'&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;이란 뜻이고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;chrono&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;'시간&lt;/span&gt;&lt;/span&gt;'&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&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;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;즉, Synchronous는 작업 시간을 함께 맞춰서 실행한다 라는 뜻으로 해석된다. 작업을 맞춰 실행한다는 말은&amp;nbsp;&lt;b&gt;요청한 작업에 대해 완료 여부를 따져&lt;/b&gt; 순차대로 처리하는 것을 말한다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Asynchronous는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;앞에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;A&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;라는 접두사가 붙어&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;부정&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;하는 형태이다. 그래서&lt;/span&gt;&amp;nbsp;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;동기와 반대로 &lt;b&gt;요청한 작업에 대해 완료 여부를 따지지 않기&lt;/b&gt; 때문에 자신의 다음 작업을 그대로 수행하게 된다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img_(9)-transformed.jpeg&quot; data-origin-width=&quot;1174&quot; data-origin-height=&quot;670&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ba8wJ1/btr9wHMcO9E/a1rq2AuO9NKSKxWfvrPL9k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ba8wJ1/btr9wHMcO9E/a1rq2AuO9NKSKxWfvrPL9k/img.jpg&quot; data-alt=&quot;동기는 작업 B가 완료되어야 다음 작업을 수행하고, 비동기는 작업 B의 완료 여부를 따지지 않고 바로 다음 작업을 수행한다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ba8wJ1/btr9wHMcO9E/a1rq2AuO9NKSKxWfvrPL9k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fba8wJ1%2Fbtr9wHMcO9E%2Fa1rq2AuO9NKSKxWfvrPL9k%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; alt=&quot;Synchronous-Asynchronous&quot; loading=&quot;lazy&quot; width=&quot;1174&quot; height=&quot;670&quot; data-filename=&quot;img_(9)-transformed.jpeg&quot; data-origin-width=&quot;1174&quot; data-origin-height=&quot;670&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;동기는 작업 B가 완료되어야 다음 작업을 수행하고, 비동기는 작업 B의 완료 여부를 따지지 않고 바로 다음 작업을 수행한다&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;비동기의 성능 이점&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;보통 비동기 특징을 이용하여 성능과 연관지어 말한다. 왜냐하면 요청한 작업에 대하여 완료 여부를 신경쓰지 않고 자신의 그다음 작업을 수행한다는 것은, I/O 작업과 같은 느린 작업이 발생할 때, 기다리지 않고 다른 작업을 처리하면서 동시에 처리하여 멀티 작업을 진행할수 있기 때문이다. 이는 전반적인 시스템 성능 향상에 도움을 줄 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;581&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTgK4J/btscyGQ1Krv/0oy6aje1smUpbd7K1JlyTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTgK4J/btscyGQ1Krv/0oy6aje1smUpbd7K1JlyTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTgK4J/btscyGQ1Krv/0oy6aje1smUpbd7K1JlyTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTgK4J%2FbtscyGQ1Krv%2F0oy6aje1smUpbd7K1JlyTK%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; alt=&quot;Synchronous-Asynchronous&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;581&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;581&quot;/&gt;&lt;/span&gt;&lt;/figure&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;a href=&quot;https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-multi-process-multi-thread&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;멀티 스레드나 멀티 프로세싱&lt;/a&gt;과 같은 방식으로 구현될 수 있다. 자바스크립트 같은 경우 비동기로 작업을 요청하면 브라우저에 내장된 멀티 스레드로 이루어진 Web API에 작업이 인가되어 메인 Call Stack과 작업이 동시에 처리되게 된다. 쉽게 비유하자면 작업을 백그라운드에 인가한다고 생각하면 된다. 대표적인 비동기 작업의 종류로는 애니메이션 실행, 네트워크 통신, 마우스 키보드 입력, 타이머 등 많다. 다만 자바스크립트 코드 실행 자체는 Web API가 아닌 Call Stack에서 실행된다.&lt;/p&gt;
&lt;figure id=&quot;og_1683593943141&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;  자바스크립트 이벤트 루프 구조 &amp;amp; 동작 원리&quot; data-og-description=&quot;자바스크립트 비동기와 이벤트 루프 브라우저의 멀티 스레드로 작업을 동시에 Javascript는 싱글 스레드 언어라고 들어본 적이 있을 것이다. '싱글' 스레드라 한 번에 하나의 작업만 수행이 가능하&quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/%F0%9F%94%84-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84-%EA%B5%AC%EC%A1%B0-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC&quot; data-og-url=&quot;https://inpa.tistory.com/entry/%F0%9F%94%84-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84-%EA%B5%AC%EC%A1%B0-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/buiBqg/hySykkmnK8/x1ajNosv2T0mzGqh04Xgd1/img.png?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/cyPupO/hySyifMjuD/XRHwI4bhPA7N7x7PTYLJM1/img.png?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/c0SMPE/hySyspaIUQ/pHWgdc4sWIPlEQ2qt5diQ1/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/%F0%9F%94%84-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84-%EA%B5%AC%EC%A1%B0-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inpa.tistory.com/entry/%F0%9F%94%84-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84-%EA%B5%AC%EC%A1%B0-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/buiBqg/hySykkmnK8/x1ajNosv2T0mzGqh04Xgd1/img.png?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/cyPupO/hySyifMjuD/XRHwI4bhPA7N7x7PTYLJM1/img.png?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/c0SMPE/hySyspaIUQ/pHWgdc4sWIPlEQ2qt5diQ1/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;  자바스크립트 이벤트 루프 구조 &amp;amp; 동작 원리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;자바스크립트 비동기와 이벤트 루프 브라우저의 멀티 스레드로 작업을 동시에 Javascript는 싱글 스레드 언어라고 들어본 적이 있을 것이다. '싱글' 스레드라 한 번에 하나의 작업만 수행이 가능하&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;inpa.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;span&gt;동기와 비동기는 작업 순서 처리 차이&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;span&gt;위에서 동기와 비동기를 요청한 작업에 대해 완료 여부에 대한 차이라고 말했었다. 이를 쉽게 말하자면 여러개의 요청 작업을 순차적으로 처리하느냐 아니냐에 따른 차이로 보면 된다. &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;요청한 작업에 대한 완료 알림을 반드시 받아야 다음 작업을 수행한다는 말은 작업을 순서대로 처리한다는 말이기 때문이다. &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;span&gt;따라서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #ee2323; text-align: start;&quot;&gt;동기 작업은&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&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: #333333; text-align: start;&quot;&gt;예를들어 A, B, C 라는 3 개의 작업(Task)이 있다고 가정하자. &lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;동기 방식으로 실행하면 A &amp;rarr; B&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;rarr; C 순서대로 실행되게 되고, 비동기 방식으로 실행하면 A&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;rarr; C&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;rarr; B 또는 C&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;rarr; A&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&amp;rarr; B 등의 무작위의 순서로 실행되게 되는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1144&quot; data-origin-height=&quot;333&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biNRlJ/btscJnIEMHp/mezKcex8T5JRW6kG29DNYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biNRlJ/btscJnIEMHp/mezKcex8T5JRW6kG29DNYK/img.png&quot; data-alt=&quot;동기(Synchronous)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biNRlJ/btscJnIEMHp/mezKcex8T5JRW6kG29DNYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiNRlJ%2FbtscJnIEMHp%2FmezKcex8T5JRW6kG29DNYK%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; alt=&quot;Synchronous-Asynchronous&quot; loading=&quot;lazy&quot; width=&quot;667&quot; height=&quot;194&quot; data-origin-width=&quot;1144&quot; data-origin-height=&quot;333&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;동기(Synchronous)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1264&quot; data-origin-height=&quot;671&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5wxEK/btsdHQpRfix/2gAr8oIJJBphVmQSXke6sK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5wxEK/btsdHQpRfix/2gAr8oIJJBphVmQSXke6sK/img.png&quot; data-alt=&quot;비동기(Asynchronous)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5wxEK/btsdHQpRfix/2gAr8oIJJBphVmQSXke6sK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5wxEK%2FbtsdHQpRfix%2F2gAr8oIJJBphVmQSXke6sK%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; alt=&quot;Synchronous-Asynchronous&quot; loading=&quot;lazy&quot; width=&quot;683&quot; height=&quot;363&quot; data-origin-width=&quot;1264&quot; data-origin-height=&quot;671&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;비동기(Asynchronous)&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;정리하자면 작업 3개를 요청했는데 응답에서 그 순서가 지켜진다면 동기이고 어떤 게 먼저 올지 모른다면 비동기라고 보면 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Blocking / Non-Blocking&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;블로킹과 논블록킹은 단어에서 알 수 있듯이 다른 요청의 작업을 처리하기 위해 현재 작업을 block(차단, 대기) 하냐 안하냐의 유무를 나타내는 프로세스의 실행 방식이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;동기/비동기가 전체적인 작업에 대한 순차적인 흐름 유무라면, 블로킹/논블로킹은 전체적인 작업의 &lt;b&gt;흐름 자체를 막냐 안 막냐&lt;/b&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;850&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cUknFY/btr9GgNFkvY/Gk512NEyKX2nkfLlC7w7ok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cUknFY/btr9GgNFkvY/Gk512NEyKX2nkfLlC7w7ok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cUknFY/btr9GgNFkvY/Gk512NEyKX2nkfLlC7w7ok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcUknFY%2Fbtr9GgNFkvY%2FGk512NEyKX2nkfLlC7w7ok%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; alt=&quot;Blocking-Non-Blocking&quot; loading=&quot;lazy&quot; width=&quot;850&quot; height=&quot;306&quot; data-origin-width=&quot;850&quot; data-origin-height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;/figure&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;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;비동기와 논블로킹 개념 차이&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아마 새내기 개발자들이 가장 혼동하는 부분이 비동기와 논블로킹의 차이 부분일 것이다. 포스팅 초반에서 자바스크립트의 &lt;s&gt;setTimeout&lt;/s&gt; 함수를 비동기 함수이며 동시에 논블로킹 함수이라고 했다. 이는 어떠한 관점으로 &lt;s&gt;setTimeout&lt;/s&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt; 을 바라보느냐에 따라 각기 부르는게 달라지기 때문이다. &lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;예를들어 아래 코드는 &lt;s&gt;setTimeout&lt;/s&gt; 함수를 사용하여 1초 후에 &quot;Hello, world!&quot;를 출력하는 비동기 논블로킹 코드의 예제이다. 코드를 실행하면, &quot;시작&quot;과 &quot;끝&quot;이 먼저 출력되고, 1초 후에 &quot;1초 후에 실행됩니다!&quot;가 출력되게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1681276171401&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(&quot;시작&quot;);

setTimeout(() =&amp;gt; {
  console.log(&quot;1초 후에 실행됩니다!&quot;);
}, 1000);

console.log(&quot;끝&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;222&quot; data-origin-height=&quot;106&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cHTEos/btr9NBiDVn0/b9Z2TAuQc7C3RvRL3wJN3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cHTEos/btr9NBiDVn0/b9Z2TAuQc7C3RvRL3wJN3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cHTEos/btr9NBiDVn0/b9Z2TAuQc7C3RvRL3wJN3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcHTEos%2Fbtr9NBiDVn0%2Fb9Z2TAuQc7C3RvRL3wJN3K%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; alt=&quot;Blocking-Non-Blocking&quot; loading=&quot;lazy&quot; width=&quot;222&quot; height=&quot;106&quot; data-origin-width=&quot;222&quot; data-origin-height=&quot;106&quot;/&gt;&lt;/span&gt;&lt;/figure&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: #333333; text-align: start;&quot;&gt;&lt;s&gt;setTimeout&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;함수에 대해 타이머 작업 완료 여부를 신경 쓰지 않고 바로 그 다음 콘솔 작업을 수행하였기 때문이다. 그리고 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;s&gt;setTimeout&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;함수의 타이머 작업 완료 알람을 콜백 함수를 통해 값을 받아 출력하였다. 따라서&amp;nbsp;&lt;s&gt;setTimeout&lt;/s&gt; 은 비동기(Asynchronouse)이다.&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: #333333; text-align: start;&quot;&gt;&lt;s&gt;setTimeout&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;함수는 자신의 타이머 작업을 수행하기 위해 메인 함수를 블락하지 않고 백그라운드에서 별도로 처리되었다. 메인 함수를 블락하지 않으니 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;s&gt;setTimeout&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;함수를 호출하고 바로 그 다음 콘솔 함수를 호출한 것이다. 따라서&amp;nbsp;&lt;s&gt;setTimeout&lt;/s&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;은 논블로킹(Non-blocking)이다.&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: #333333; text-align: start;&quot;&gt;그런데 결국은 비동기나 논블로킹이나 추구하는 결과로만 보자면 동시에 다른 작업을 수행한다는 점에서 그게 그것이라고 볼 수도 있다. 이는 &lt;span style=&quot;color: #ee2323;&quot;&gt;시점과 관련된 이론적인 개념이라 실제 코드에서 경계를 구분하기가 애매&lt;/span&gt;하기도 하다. 그래서 &lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;프로그래밍에선 &lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;이를 혼용하여 사용하는 것 같다. 그러나 엄연한 나타내는 의미는 다르다.  &lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;우리가 동기/비동기, 블로킹/논블로킹을 혼동하는 이유는, 자바스크립트를 처음 배울 때 프로그래밍 서적이나 강의에서 &lt;s&gt;setTimeout&lt;/s&gt; 과 같은 함수를 '비동기 함수' 라고 지칭하고 외워버려서 그렇다고 생각한다. 그래서 '비동기' 라고 하면 자연스럽게 병렬 실행을 떠올라 비동기와 논블로킹을 혼동하는 것이다. &lt;br /&gt;비동기는 출력 순서와 관련된 개념이고 논블로킹이 병렬 실행과 관련된 개념이다. 따라서 글쓴이는 개인적으로 이 부분은 잘못되었다고 비판하고 싶다. 아무리 조금 난해한 개념이라도 처음부터 비동기 + 논블로킹 함수라고 가르치는것이 추후를 위해 개념을 다잡는데 옳다고 본다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;비동기 논블로킹과 콜백 함수&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;자바스크립트 비동기 프로그래밍에서 빠지지 않는 개념이 바로 콜백(callback) 이다. 비동기 논블로킹이란 다른 작업의 결과를 기다리지 않고 병렬적으로 실행되는 방식을 말하는데, 이때 다른 작업의 완료 여부나 결과에 대한 후처리를 위해 이용되는 방식이 콜백 함수이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그럼 콜백 함수는 비동기와 관련이 있는 것일까? 논블로킹과 관련이 있는 것일까? 정답은 둘다 관련이 있다. 엄밀히 따지자면 콜백 함수는 비동기 논블로킹을 구현하는 하나의 기술이지 개념이 아니다. 콜백 지옥(callback hell) 때문에 콜백 함수 방식으로 비동기 논블로킹을 구현하기 싫다면 &lt;a href=&quot;https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%B2%98%EB%A6%AC-Promise&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Promise 객체&lt;/a&gt;를 이용하여도 되고 아예 이벤트로 emit 하여 처리하여도 된다. 방법은 여러가지이다. 핵심은 비동기 논블로킹을 구현하는 기술은 콜백 외에 여러가지가 있다는 점이고 콜백은 그 중 하나일 뿐이라는 것이지 동기/비동기 &amp;amp; 블로킹/논블로킹 개념과 직접적인 관련은 없다.&lt;/p&gt;
&lt;pre id=&quot;code_1683020122031&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* 콜백 함수 방식으로 구현한 비동기 + 논블로킹 서버 요청 작업 */

$.ajax({
  url: 'https://jsonplaceholder.typicode.com/todos/1', 
  type: 'GET', 
  dataType: 'json', 
  success: function(data) { // 요청이 성공하면 호출될 콜백 함수
    console.log(data); 
  },
  error: function(err) { // 요청이 실패하면 호출될 콜백 함수
    throw err;
  }
});

// 요청을 보내는 동시에 다른 작업을 수행할 수 있습니다.
console.log('Hello');&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1683020155695&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* 프로미스 객체 방식으로 구현한 비동기 + 논블로킹 서버 요청 작업 */

fetch('https://jsonplaceholder.typicode.com/todos/1') 
  .then((response) =&amp;gt; {
    return response.json(); 
  })
  .then((data) =&amp;gt; {
    console.log(data); 
  })
  .catch((err) =&amp;gt; {
    throw err; 
  });

// 요청을 보내는 동시에 다른 작업을 수행할 수 있습니다.
console.log('Hello');&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1683020167670&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* 이벤트 방식으로 구현한 비동기 + 논블로킹 파일 다운 작업 */

const fileReader = new FileReader(); // FileReader 객체를 생성
const input = document.querySelector('input');
const file = input.files[0]; // input 태그에서 선택된 파일을 가져옵니다

fileReader.addEventListener('load', function() {
	console.log(fileReader.result); // 파일이 load가 완료되면 내용을 출력합니다.
});

fileReader.addEventListener('error', function(err) {
	throw err;
});

// 요청을 보내는 동시에 다른 작업을 수행할 수 있습니다.
console.log('Hello');&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;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;누가 제어권을 가지고 있느냐&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; 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;span style=&quot;color: #333333; text-align: start;&quot;&gt;블로킹/논블로킹을 보다 명확히 구분하기 위해서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;'제어권'&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;이라는 어려운 단어를 쓴다. 제어권은 간단히 말해서 함수의 코드나 프로세스의 실행 흐름을 제어할 수 있는 권리 같은 것이다. 이 개념은 운영체제(OS)의 커널(kernel)에서 따온 개념으로 I/O 동작에서 설명하는 부분이다. &lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;즉, Blocking과 Non-Blocking은 호출된 함수(callee)가 호출한 함수(caller)에게 제어권을 바로 주느냐 안주느냐로 구분된다. 제어권이 넘어가버리면 해당 스레드는 블로킹되게 된다.&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: #333333; text-align: start;&quot;&gt;&lt;/span&gt;다음은 A 함수와 B 함수 작업에 대해 A 함수가 B 함수를 Blocking 방식으로 호출할시 제어권 상태를 나타낸 그림이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;581&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3mzcH/btrpJRZSuus/WJLlZj8R2IVqLkO3lwTa60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3mzcH/btrpJRZSuus/WJLlZj8R2IVqLkO3lwTa60/img.png&quot; data-alt=&quot;Blocking&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3mzcH/btrpJRZSuus/WJLlZj8R2IVqLkO3lwTa60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3mzcH%2FbtrpJRZSuus%2FWJLlZj8R2IVqLkO3lwTa60%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; alt=&quot;동기-비동기-블로킹-논블로킹&quot; loading=&quot;lazy&quot; width=&quot;514&quot; height=&quot;531&quot; data-origin-width=&quot;581&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Blocking&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;position: absolute;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;A 함수가 B 함수를 호출하면 B에게 제어권이 넘어간다&lt;/li&gt;
&lt;li&gt;제어권을 넘겨받은 B는 함수를 실행한다.&lt;/li&gt;
&lt;li&gt;이때 A는 B에게 제어권을 넘겨주었기 때문에 A 함수 실행을 잠시 멈춘다. (Block)&lt;/li&gt;
&lt;li&gt;B 함수가 실행이 끝나면 자신을 호출한 A에게 제어권을 돌려준다.&lt;/li&gt;
&lt;li&gt;제어권을 다시 받은 A 함수는 그다음 작업을 실행한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 A 함수와 B 함수 작업에 대해 A 함수가 B 함수를 Non-Blocking 방식으로 호출할시 제어권 상태를 나타낸 그림이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dobMy7/btrpLub29Xo/TQtuJa2moQ2EHfXmkStTe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dobMy7/btrpLub29Xo/TQtuJa2moQ2EHfXmkStTe0/img.png&quot; data-alt=&quot;Non Blocking&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dobMy7/btrpLub29Xo/TQtuJa2moQ2EHfXmkStTe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdobMy7%2FbtrpLub29Xo%2FTQtuJa2moQ2EHfXmkStTe0%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; alt=&quot;동기-비동기-블로킹-논블로킹&quot; loading=&quot;lazy&quot; width=&quot;471&quot; height=&quot;530&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Non Blocking&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;A 함수가 B 함수를 호출한다.&lt;/li&gt;
&lt;li&gt;호출된 B 함수는 실행되지만,&lt;span&gt;&amp;nbsp;&lt;/span&gt;제어권은 A 함수가 그대로 가지고 있는다.&lt;/li&gt;
&lt;li&gt;A 함수는 계속 제어권을 가지고 있기 때문에 B 함수를 호출한 이후에도 자신의 코드를 계속 실행한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;동기/비동기 &lt;span style=&quot;color: #ef5369;&quot;&gt;+&lt;/span&gt; 블로킹/논블로킹 조합&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 동기/비동기 와 블로킹/논블로킹 각각의 차이점에 대해 알아보았다. 이처럼 둘은 유사하지만 엄연히 다른 개념이다. 그래서 프로그램 아키텍쳐에서는 이 두 개념이 함께 조합되어 사용된다. 예를들어 다음 4가지로 조합이 가능하다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Sync Blocking (동기 + 블로킹)&lt;/li&gt;
&lt;li&gt;Async Blocking (비동기 + 블로킹)&lt;/li&gt;
&lt;li&gt;Sync Non-Blocking (동기 + 논블로킹)&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Async Non-Blocking (비동기 + 논블로킹)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;다운로드 (13).png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;713&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1HN9m/btsakdV6qwm/JifIi4frB5yBST4PrCAU1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1HN9m/btsakdV6qwm/JifIi4frB5yBST4PrCAU1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1HN9m/btsakdV6qwm/JifIi4frB5yBST4PrCAU1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1HN9m%2FbtsakdV6qwm%2FJifIi4frB5yBST4PrCAU1k%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; alt=&quot;sync-async-blocking-nonblocking&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;713&quot; data-filename=&quot;다운로드 (13).png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;713&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;대표적인 예로 Node.js 를 들 수 있다. Node.js에서 비동기 방식으로 파일을 읽거나 네트워크 요청을 보낼 때는 비동기 &amp;amp; 논블로킹 방식을 사용하여 작업이 완료될 때까지 다른 작업을 수행할 수 있도록 한다. 반면에, Node.js에서 코드 실행 시점을 늦춰주거나 순차적인 의존성이 있는 작업을 처리할 때는 동기 &amp;amp; 블로킹 방식을 사용하여 작업의 순서와 타이밍을 제어할 수 있도록 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;458&quot; data-origin-height=&quot;588&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1twHf/btsdG9i1QOt/tKuuTEOKO4CqJQJRw5X1qk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1twHf/btsdG9i1QOt/tKuuTEOKO4CqJQJRw5X1qk/img.jpg&quot; data-alt=&quot;제로초(조현영)의 node.js 교제&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1twHf/btsdG9i1QOt/tKuuTEOKO4CqJQJRw5X1qk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1twHf%2FbtsdG9i1QOt%2FtKuuTEOKO4CqJQJRw5X1qk%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; alt=&quot;sync-async-blocking-nonblocking&quot; loading=&quot;lazy&quot; width=&quot;278&quot; height=&quot;357&quot; data-origin-width=&quot;458&quot; data-origin-height=&quot;588&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;제로초(조현영)의 node.js 교제&lt;/figcaption&gt;
&lt;/figure&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;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;개념들을 명확히 알고 있으면 Node.js의 동작 원리와 장점을 더 잘 이해하고 활용할 수 있을 것이다.&lt;span&gt;&amp;nbsp;&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;style3&quot; /&gt;
&lt;h3 id=&quot;1-sync-blocking&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Sync Blocking 조합&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sync Blocking 조합은 다른 작업이 진행되는 동안 자신의 작업을 처리하지 않고 (Blocking), 다른 작업의 완료 여부를 바로 받아 순차적으로 처리하는 (Sync) 방식이다. 다른 작업의 결과가 자신의 작업에 영향을 주는 경우에 활용할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;386&quot; data-origin-height=&quot;467&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAYvZv/btsdGQKtwyl/0gx6l3MC6OW5BQZph1DkmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAYvZv/btsdGQKtwyl/0gx6l3MC6OW5BQZph1DkmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAYvZv/btsdGQKtwyl/0gx6l3MC6OW5BQZph1DkmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAYvZv%2FbtsdGQKtwyl%2F0gx6l3MC6OW5BQZph1DkmK%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; alt=&quot;Sync Blocking&quot; loading=&quot;lazy&quot; width=&quot;350&quot; height=&quot;467&quot; data-origin-width=&quot;386&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;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;실생활 동작 예시&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;어느 개발팀에서 사원1, 사원2, 사원3 와 팀장이 있다고 하자. 개발팀장은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: left;&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;팀장 : 사원1씨 업무 A 좀 해주세요&lt;/li&gt;
&lt;li&gt;사원1 : 네 알겠습니다. (A를 처리중)&lt;/li&gt;
&lt;li&gt;팀장 : (사원1이 A를 다할때까지 아무일도 하지 않고 기다린다)&lt;/li&gt;
&lt;li&gt;사원1 : 팀장님 A 업무 완료했습니다.&lt;/li&gt;
&lt;li&gt;팀장 : 수고했어요. 사원2씨 업무 B좀 해주세요.&lt;/li&gt;
&lt;li&gt;사원2 : 네 알겠습니다. (B를 처리중)&lt;/li&gt;
&lt;li&gt;팀장 : (사원2가 B를 다할때까지 아무일도 하지 않고 기다린다)&lt;/li&gt;
&lt;li&gt;사원2 : 팀장님 B 업무 완료했습니다.&lt;/li&gt;
&lt;li&gt;팀장 : 수고했어요. 사원3씨 업무 C좀 해주세요.&lt;/li&gt;
&lt;li&gt;...생략&lt;/li&gt;
&lt;/ol&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;코드 동작 예시&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Sync Blocking&lt;span&gt; 의&amp;nbsp;&lt;/span&gt;&lt;/span&gt;대표적인 예로 파일을 읽어 내용을 처리하는 로직을 들 수 있다. 파일을 먼저 읽어야 그다음 작업을 처리할 수 있기 때문이다.&lt;/p&gt;
&lt;pre id=&quot;code_1681301422849&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const fs = require('fs'); // 파일 시스템 모듈 불러오기

// 동기적으로 파일 읽기
const data1 = fs.readFileSync('file1.txt', 'utf8'); // file1을 sync으로 read 함
console.log(data1); // 파일 내용 출력하고 적절한 처리를 진행

const data2 = fs.readFileSync('file2.txt', 'utf8'); 
console.log(data2); 

const data3 = fs.readFileSync('file3.txt', 'utf8'); 
console.log(data3);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 Sync Blocking&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt; 은 코드가 순차적으로 실행되는 특성을 가지고 있다. 그래서 &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Sync Blocking 조합은 일반적으로 작업이 간단하거나 작업량이 적은 경우에 사용된다. 작은 데이터를 처리하거나 파일 하나를 읽고 쓰는 경우에는 Sync Blocking 방식이 더 간단하고 직관적일 수 있다. &lt;/span&gt;하지만 작업량이 많거나 시간이 오래 걸리는 작업을 처리해야 하는 경우에는 Sync Blocking 방식을 사용하면 독이 된다. 왜냐하면 작업을 처리하면 작업이 끝날 때까지 다른 작업을 처리하지 못하므로, 전체 처리 시간이 오래 걸리게 되어 비효율적이게 되게 된다. 따라서 이러한 경우에는 바로 뒤에서 다룰 Async Non-Blocking 방식을 사용하여 작업을 처리하는 것이 좋다.&lt;b&gt;&lt;br /&gt;&lt;/b&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;적용 프로그램 예시&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로 &amp;nbsp;C나 JAVA의 코드 실행 후 커맨드에서 입력을 받는 경우가 이에 해당된다. 사용자로부터 입력을 받아야 그 입력값을 가지고 내부 처리를 하여 결과값을 콘솔에 출력해주기 때문에 순차적인 작업이 요구된다. 내부적으로 본다면 실행 코드가 콘솔창을 띄우고 Please enter your name 텍스트를 치고 난 다음 사용자의 리턴값이 필요하기 때문에 제어권을 시스템에서 사용자로 넘겨 사용자가 값을 입력할때까지 기다리는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;841&quot; data-origin-height=&quot;390&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n3P4U/btrpJXZAf30/Q4n8oEhQUqCHWsyNWkLJ41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n3P4U/btrpJXZAf30/Q4n8oEhQUqCHWsyNWkLJ41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n3P4U/btrpJXZAf30/Q4n8oEhQUqCHWsyNWkLJ41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn3P4U%2FbtrpJXZAf30%2FQ4n8oEhQUqCHWsyNWkLJ41%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; alt=&quot;동기-비동기-블로킹-논블로킹&quot; loading=&quot;lazy&quot; width=&quot;569&quot; height=&quot;264&quot; data-origin-width=&quot;841&quot; data-origin-height=&quot;390&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;style3&quot; /&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Async Non-Blocking&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;조합&lt;/b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Async Non Blocking 조합은 다른 작업이 진행되는 동안에도 자신의 작업을 처리하고 (Non Blocking), 다른 작업의 결과를 바로 처리하지 않아 작업 순서가 지켜지지 않는 (Async) 방식이다. 다른 작업의 결과가 자신의 작업에 영향을 주지 않은 경우에 활용할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;402&quot; data-origin-height=&quot;284&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d0qjW1/btsdBO7NOlp/CKKA6s2MybjBx4mV5lwjh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d0qjW1/btsdBO7NOlp/CKKA6s2MybjBx4mV5lwjh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d0qjW1/btsdBO7NOlp/CKKA6s2MybjBx4mV5lwjh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd0qjW1%2FbtsdBO7NOlp%2FCKKA6s2MybjBx4mV5lwjh1%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; alt=&quot;Async Non-Blocking&quot; loading=&quot;lazy&quot; width=&quot;402&quot; height=&quot;284&quot; data-origin-width=&quot;402&quot; data-origin-height=&quot;284&quot;/&gt;&lt;/span&gt;&lt;/figure&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;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;실생활 동작 예시&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;팀장 : 사원1씨 A업무좀 해주세요. (동시에 지시)&lt;/li&gt;
&lt;li&gt;팀장 : 사원2씨 B업무좀 해주세요. (동시에 지시)&lt;/li&gt;
&lt;li&gt;팀장 : 사원3씨 C업무좀 해주세요. (동시에 지시)&lt;/li&gt;
&lt;li&gt;팀장 : 다른일을 해야지 ~&lt;/li&gt;
&lt;li&gt;사원2 : 팀장인 B 모두 처리했습니다. (업무량에 따라 각 사원마다 완료하는 시간이 제각기 다를 수 있다)&lt;/li&gt;
&lt;li&gt;사원1 : 팀장인 A 모두 처리했습니다.&lt;/li&gt;
&lt;li&gt;사원3 : 팀장인 C 모두 처리했습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;코드 동작 예시&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 파일 읽기 코드를 Sync Blocking 방식으로 구현하였는데 이를 그대로 Async Non-Blocking 방식으로도 코드를 구현할 수 있다. Sync Blocking 방식과 두드러진 차이점은 호출 함수에 콜백 함수를 넣었다는 점이다. 이를 통해 비동기 논블로킹 방식대로 처리된 작업의 결과를 후처리 할 수 있게 된다. 그리고 비동기 이기 때문에 작업의 순서를 고려하지 않아 동기 블로킹과는 거꾸로 가장 먼저 'done' 이라는 콘솔 로그가 찍히게 되고 그 후에 파일 내용이 출력될 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1683019197557&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 비동기적으로 파일 읽기
const fs = require('fs'); // 파일 시스템 모듈 불러오기

fs.readFile('file.txt', 'utf8', (err, data) =&amp;gt; { // 파일 읽기 요청과 콜백 함수 전달
  if (err) throw err; // 에러 처리
  console.log(data); // 파일 내용 출력
});

fs.readFile('file2.txt', 'utf8', (err, data) =&amp;gt; {
  if (err) throw err; 
  console.log(data);
});

fs.readFile('file3.txt', 'utf8', (err, data) =&amp;gt; { 
  if (err) throw err; 
  console.log(data);
});

console.log('done'); // 작업 완료 메시지 출력&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;s&gt;fs.readFileSync&lt;/s&gt; 와 &lt;s&gt;fs.readFile&lt;/s&gt; 함수는 둘다 똑같이 파일을 읽는 함수이지만 동기냐 비동기냐에 따라 차이가 있다.&lt;br /&gt;즉, &lt;s&gt;fs.readFileSync&lt;/s&gt; 함수는 동기적으로 파일을 읽고 &lt;s&gt;fs.readFile&lt;/s&gt; 함수는 비동기적으로 파일을 읽는다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이처럼 Async Non Blocking 조합은 작업량이 많거나 시간이 오래 걸리는 작업을 처리해야 하는 경우에 적합하다. 예를 들어, 대용량 데이터를 처리하거나 많은 요청을 처리하는 서비스에서는 Async Non Blocking 방식을 사용하여 한 작업이 처리되는 동안 다른 작업을 처리할 수 있으므로 전체 처리 시간을 줄일 수 있어 어플리케이션 처리 성능을 향상시킬 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;활용 예시 프로그램&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 논블로킹을 활용하는 프로그램은 많이 있다. 그중 예를 들자면, 웹 브라우저의 파일 다운로드가 비동기 논블로킹을 활용하는 예시 라고 할 수 있다. 웹 브라우저는 웹 사이트에서 파일을 다운로드할 때, 파일의 전송이 완료될 때까지 다른 작업을 하지 않고 기다리는 것이 아니라, 다른 탭이나 창을 열거나 웹 서핑을 할 수 있다. 이는 웹 브라우저가 파일 다운로드를 비동기적으로 처리하고, 콜백 함수를 통해 다운로드가 완료되면 알려주는 방식으로 구현되어 있기 때문이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;743&quot; data-origin-height=&quot;564&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccYF7W/btsdIIrgZFi/a2TKMREXvMPkAw0kescbi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccYF7W/btsdIIrgZFi/a2TKMREXvMPkAw0kescbi0/img.png&quot; data-alt=&quot;파일들이 거의 동시에 다운 받음을 Waterfall 그래프에서 시각적으로 확인할 수 있다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccYF7W/btsdIIrgZFi/a2TKMREXvMPkAw0kescbi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FccYF7W%2FbtsdIIrgZFi%2Fa2TKMREXvMPkAw0kescbi0%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; alt=&quot;Async Non-Blocking&quot; loading=&quot;lazy&quot; width=&quot;743&quot; height=&quot;564&quot; data-origin-width=&quot;743&quot; data-origin-height=&quot;564&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;파일들이 거의 동시에 다운 받음을 Waterfall 그래프에서 시각적으로 확인할 수 있다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 id=&quot;2-sync-nonblocking&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Sync Non-Blocking 조합&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Sync Non-Blocking 조합은 다른 작업이 진행되는 동안에도 자신의 작업을 처리하고 (Non Blocking), 다른 작업의 결과를 바로 처리하여 작업을 순차대로 수행 하는 (Sync) 방식이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;385&quot; data-origin-height=&quot;298&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4NkUG/btsdGG2e2s9/Csl49wse9kpiRQkkPxO0Bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4NkUG/btsdGG2e2s9/Csl49wse9kpiRQkkPxO0Bk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4NkUG/btsdGG2e2s9/Csl49wse9kpiRQkkPxO0Bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4NkUG%2FbtsdGG2e2s9%2FCsl49wse9kpiRQkkPxO0Bk%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; alt=&quot;Sync Non-Blocking&quot; loading=&quot;lazy&quot; width=&quot;385&quot; height=&quot;298&quot; data-origin-width=&quot;385&quot; data-origin-height=&quot;298&quot;/&gt;&lt;/span&gt;&lt;/figure&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;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;실생활 동작 예시&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀장이 업무 A, B, C를 사원 1, 2, 3 에게 시키려고 한다. 그런데 업무 특성상 업무 A를 완료해야 업무 B를 할 수 있고, 업무 B를 완료해야 업무 C를 할 수 있다고 한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;팀장 : 사원1씨 업무 A 좀 해주세요&lt;/li&gt;
&lt;li&gt;사원1 : 네 알겠습니다. (A를 처리중)&lt;/li&gt;
&lt;li&gt;팀장 : 다음 업무 B를 하려면 A가 완료되야 하는데.. 사원1씨 다했어요?&lt;/li&gt;
&lt;li&gt;사원1 : 아직이요 A 처리중입니다&lt;/li&gt;
&lt;li&gt;팀장 : 사원1씨 다했어요?&lt;/li&gt;
&lt;li&gt;사원1 : 아직이요 A 처리중입니다&lt;/li&gt;
&lt;li&gt;사원1 : 팀장님 A 모두 완료했습니다&lt;/li&gt;
&lt;li&gt;팀장 : 수고했어요. 사원2씨 업무 B좀 해주세요.&lt;/li&gt;
&lt;li&gt;사원2 : 네 알겠습니다. (B를 처리중)&lt;/li&gt;
&lt;li&gt;팀장 : 다음 업무 C를 하려면 B가 완료되야 하는데.. 사원2씨 다했어요?&lt;/li&gt;
&lt;li&gt;...생략&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;코드 동작 예시&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동기 + 논블로킹 코드를 표현하는데 적합한 대중적인 언어로 자바(Java)를 들 수있다. 스레드(Thread) 객체를 만들어 요청 작업을 백그라운드에 돌게 하고 메인 메서드에서 while문을 통해 스레드가 모두 처리되었는지 끊임없이 확인하고, 처리가 완료되면 다음 메인 작업을 수행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1683025720518&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Runnable 인터페이스를 구현하는 클래스 정의
class MyTask implements Runnable {
    @Override
    public void run() {
        // 비동기로 실행할 작업
        System.out.println(&quot;Hello from a thread!&quot;);
    }
}

public class Main {
    public static void main(String[] args) {
        // Thread 객체 생성
        Thread thread = new Thread(new MyTask());

        // 스레드 실행
        thread.start();

        // Non-Blocking이므로 다른 작업 계속 가능
        System.out.println(&quot;Main thread is running...&quot;);

        // Sync를 위해 스레드의 작업 완료 여부 확인
        while (thread.isAlive()) {
            System.out.println(&quot;Waiting for the thread to finish...&quot;);
        }
        System.out.println(&quot;Thread finished!&quot;);
        
        System.out.println(&quot;Run the next tasks&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;314&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbpn9V/btsdGc8FWLP/2Zx6WPUxO8pKWRvb7rNcbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbpn9V/btsdGc8FWLP/2Zx6WPUxO8pKWRvb7rNcbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbpn9V/btsdGc8FWLP/2Zx6WPUxO8pKWRvb7rNcbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcbpn9V%2FbtsdGc8FWLP%2F2Zx6WPUxO8pKWRvb7rNcbK%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; alt=&quot;Sync Non-Blocking&quot; loading=&quot;lazy&quot; width=&quot;314&quot; height=&quot;210&quot; data-origin-width=&quot;314&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;분명 스레드를 이용하여 작업을 병렬적으로 처리하도록 지시했지만, 메인 코드의 while문을 수행함으로서 요청한 작업의 완료 여부를 계속 확인하고 결과적으로 결국 동기적으로 작업을 순서대로 수행됨을 볼 수 있다.&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;534&quot; data-origin-height=&quot;543&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uRHfw/btrpJQs6CvA/ywikZdAksl1u3IoDnBo520/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uRHfw/btrpJQs6CvA/ywikZdAksl1u3IoDnBo520/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uRHfw/btrpJQs6CvA/ywikZdAksl1u3IoDnBo520/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuRHfw%2FbtrpJQs6CvA%2FywikZdAksl1u3IoDnBo520%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; alt=&quot;Sync Non-Blocking&quot; loading=&quot;lazy&quot; width=&quot;521&quot; height=&quot;530&quot; data-origin-width=&quot;534&quot; data-origin-height=&quot;543&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트(Javascript) 경우에는 동기 + 논블로킹 코드를 구현하기에는 지원하는 메서드에 한계가 있어 완벽히 표현할 수는 없다. 다만 이와 비슷하게 표현할 수 있는 기법이 있는 데 바로 async/await 키워드 이다. 기본적으로 &lt;s&gt;Promise.then&lt;/s&gt; 핸들러 방식은 비동기 논블로킹 방식이라 볼 수 있다. 이때 비동기 작업들 간의 순서가 중요하다면 &lt;s&gt;await&lt;/s&gt; 키워드를 통해 동기적으로 처리해 줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Node.js에서 여러개의 파일을 읽어서 내용을 비교하는 작업을 한다고 가정해보자. 이를 Sync Non-Blocking 방식으로 작성한다면, 세 파일을 동시에 읽기 시작하고 (Non Blocking), 두 파일의 읽기가 모두 완료되면 내용을 비교하는 후처리를 진행한다 (Sync).&lt;/p&gt;
&lt;pre id=&quot;code_1683024209672&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const fs = require('fs'); 
const { promisify } = require('util'); // 유틸리티 모듈 불러오기
const readFileAsync = promisify(fs.readFile); // fs.readFile 함수를 Promise 객체를 반환하는 함수로 변환

async function readFiles() {
  try {
    // Promise.all() 메소드를 사용하여 여러 개의 비동기 작업을 병렬로 처리합니다. (비동기 논블로킹)
    const [data1, data2, data3] = await Promise.all([
      readFileAsync('file.txt', 'utf8'), // file.txt 파일을 읽습니다.
      readFileAsync('file2.txt', 'utf8'), // file2.txt 파일을 읽습니다.
      readFileAsync('file3.txt', 'utf8') // file3.txt 파일을 읽습니다.
    ]);

    // 파일 읽기가 완료되면 data에 파일 내용이 들어옵니다.
    console.log(data1); // file.txt 파일 내용을 출력합니다.
    console.log(data2); // file2.txt 파일 내용을 출력합니다.
    console.log(data3); // file3.txt 파일 내용을 출력합니다.

    // 파일 비교 로직 실행...

  } catch (err) {
    throw err;
  }
}

readFiles(); // async 함수를 호출&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;async/await은 비동기 논블로킹 방식을 동기 논블로킹 방식으로 바꿔주는 기법이라고 할 수 있지만, 그러나 async/await은 내부적으로는 여전히 비동기 논블로킹 방식으로 동작한다는 점은 유의하자. async 함수 자체가 메인 콜 스택(call stack)이 모두 실행되어 비워져야 수행하기 때문에 비동기 논블로킹이기 때문이다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;활용 예시 프로그램&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동기 논블로킹은 흔하지 않는 케이스이지만 그래도 이를 표현할 수 있는 프로그램이 있다. 게임에서 맵을 이동할때를 생각해보자. 우선 맵 데이터를 모두 다운로드 해야 할 것이다. 그동안 화면에는 로딩 스크린이 뜬다. 이 로딩 스크린은 로딩바가 채워지는 프로그램이 수행하고 있는 것이다. 즉, &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;제어권은 여전히 나한테 있어 화면에 로드율이 표시되는 것이다. 그리고 끊임없이 맵 데이터가 어느정도 로드가 됬는지 끊임없이 조회한다. 자신의 작업을 계속하고 있지만 다른 작업과의 동기를 위해 계속해서 다른 작업이 끝났는지 조회하는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;899&quot; data-origin-height=&quot;505&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3eCg2/btsdG51O7aC/Gl1kf9x0Q7SvdwzkcwpEh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3eCg2/btsdG51O7aC/Gl1kf9x0Q7SvdwzkcwpEh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3eCg2/btsdG51O7aC/Gl1kf9x0Q7SvdwzkcwpEh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3eCg2%2FbtsdG51O7aC%2FGl1kf9x0Q7SvdwzkcwpEh1%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; alt=&quot;동기-비동기-블로킹-논블로킹&quot; loading=&quot;lazy&quot; width=&quot;899&quot; height=&quot;505&quot; data-origin-width=&quot;899&quot; data-origin-height=&quot;505&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 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Sync Blocking&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;vs&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Sync&amp;nbsp;Non-Blocking&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그런데 가만히 보면 블로킹이든 논블로킹이든 메인 함수에서 결국은 코드를 순차적으로 수행하기 때문에 전체 작업의 최종 걸린 시간은 둘이 차이가 없어 보인다. 그럼 두 방식은 &lt;span style=&quot;background-color: #ffffff; color: #4d5156; text-align: left;&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;성능 차이는 상황에 따라 다르겠지만, 일반적으로 동기 + 논블로킹이 동기 + 블로킹보다 효율적일 수 있다. 왜냐하면 동기 + 논블로킹은 호출하는 함수가 제어권을 가지고 있어서 다른 작업을 병렬적으로 수행할 수 있기 때문이다. 반면에 동기 + 블로킹은 호출하는 함수가 제어권을 잃어서 다른 작업을 수행할 수 없기 때문입니다&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: #ee2323;&quot;&gt;파일을 다운로드하면 나타나는 다운로드 진행바&lt;/span&gt;를 들 수 가 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;407&quot; data-origin-height=&quot;263&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfjILH/btr9N9UF6AJ/TsXzcH4mEsEzg7BWP9eT00/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfjILH/btr9N9UF6AJ/TsXzcH4mEsEzg7BWP9eT00/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfjILH/btr9N9UF6AJ/TsXzcH4mEsEzg7BWP9eT00/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfjILH%2Fbtr9N9UF6AJ%2FTsXzcH4mEsEzg7BWP9eT00%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; alt=&quot;Sync Non-Blocking&quot; loading=&quot;lazy&quot; width=&quot;407&quot; height=&quot;263&quot; data-origin-width=&quot;407&quot; data-origin-height=&quot;263&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;웹브라우저에서 파일을 다운로드 할 경우 작업은 웹브라우저의 Web APIs 으로 작업을 백그라운드로 넘기고 '제어권'을 바로 반환받아 다른 작업을 수행할 수 있다. 이것이 Non Blocking 이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그런데 웹브라우저는 파일 다운로드 작업의 완료 여부에 관심이 있다. 즉, 다운로드 작업이 언제 끝나고 얼마나 진행되었는지 파일 다운로드 로드율을 브라우저 하단바에 표시해준다. 그리고 파일이 모두 다운로드 완료되면 사용자가 원하는 최종 작업인 파일 다운로드 작업을 수행하게 된다. 이것이 Synchronous 이다.&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: #ee2323;&quot;&gt;파일을 다운로드 하면서 웹서핑을 하거나 유튜브 음악을 들을 수 있다.&lt;/span&gt; 제어권을 가지고 있기 때문에 브라우저가 멀티 태스킹을 하도록 적절히 코드를 구현하였기 때문에 가능한 것이다. 따라서 동기 + 논블로킹 방식은 일반적인 동기 + 블로킹 방식보다 구현에 따라 더 효율적으로 작업을 처리 할 수 있다고 볼 수 있다. (자바스크립트의 async/await 으로 상상하면 오히려 이해가 안될수가 있다. 자바의 스레드 객체로 상상하자)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 id=&quot;4-async-blocking&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Async Blocking&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;조합&lt;/b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Async Blocking 조합은 다른 작업이 진행되는 동안 자신의 작업을 멈추고 기다리는 (Blocking), 다른 작업의 결과를 바로 처리하지 않아 순서대로 작업을 수행하지 않는 (Async) 방식이다. &lt;span style=&quot;color: #ee2323; text-align: start;&quot;&gt;Async-blocking 의 경우는 실무에서 잘 마주하기 쉽지 않아 다룰일이 거의 없다. 그래서 그냥 넘어가도 크게 문제는 없다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;376&quot; data-origin-height=&quot;489&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OsfHq/btsdGAnSAue/rBVpEkJDw5LUXKbkANC81K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OsfHq/btsdGAnSAue/rBVpEkJDw5LUXKbkANC81K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OsfHq/btsdGAnSAue/rBVpEkJDw5LUXKbkANC81K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOsfHq%2FbtsdGAnSAue%2FrBVpEkJDw5LUXKbkANC81K%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; alt=&quot;Async Blocking&quot; loading=&quot;lazy&quot; width=&quot;376&quot; height=&quot;489&quot; data-origin-width=&quot;376&quot; data-origin-height=&quot;489&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 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Async Blocking&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;vs&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Sync Blocking&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 사진만 보더라도 위의 Sync-blocking 수행 그림과 큰 차이가 없어 보인다. 실제로 독자분이 느낀 그대로 개념적으로 차이가 있을 뿐이지 정말로 성능적으로 차이가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 Async-blocking은 개발자가 비동기 논블록킹으로 처리 하려다가 실수하는 경우에 발생하거나, 자기도 모르게 블로킹 작업을 실행하는 의도치 않은 경우에 사용된다. 그래서 이 방식을 안티 패턴(anti-pattern)이라고 치부하기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;활용 예시 프로그램&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 Async Blocking 이 실제로 적용된 실무 사례가 있긴 하다. &lt;span style=&quot;color: #009a87;&quot;&gt;Node.js + MySQL의 조합&lt;/span&gt;이 대표적인데, Node.js에서 비동기 방식으로 데이터베이스에 접근하기 때문에 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Async&lt;span&gt; 이지만, MySQL 데이터베이스에 접근하기 위한 MySQL 드라이버가 블로킹 방식으로 작동되기 때문이다.&lt;/span&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;blob&quot; data-origin-width=&quot;783&quot; data-origin-height=&quot;203&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bo7S2Q/btsdIHlZwF5/06wmpXqZUIaQkPmrHiwKI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bo7S2Q/btsdIHlZwF5/06wmpXqZUIaQkPmrHiwKI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bo7S2Q/btsdIHlZwF5/06wmpXqZUIaQkPmrHiwKI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbo7S2Q%2FbtsdIHlZwF5%2F06wmpXqZUIaQkPmrHiwKI0%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; alt=&quot;Async Blocking&quot; loading=&quot;lazy&quot; width=&quot;783&quot; height=&quot;203&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;783&quot; data-origin-height=&quot;203&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;JavaScript는 비동기 방식으로 MySQL에 쿼리를 보낸다. (Async)&lt;/li&gt;
&lt;li&gt;MySQL은 쿼리를 처리하면서 JavaScript에게 제어권을 넘겨주지 않는다. (Blocking)&lt;/li&gt;
&lt;li&gt;그러면 JavaScript는 다른 작업을 계속 수행할 수 있지만, MySQL의 결과값을 필요로 하기 때문에 MySQL이 쿼리를 완료할 때까지 기다려야 된다.&lt;/li&gt;
&lt;li&gt;결국 Sync Blocking과 작업 수행에 차이가 없게 된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 JavaScript와 MySQL의 조합은 비동기적이면서도 블로킹되는 Async Blocking 조합이라고 할 수 있다. 이러한 오묘한 조합은 오히려 개발자에게 혼동만 일으키기 때문에 그래서 실무에서는 Node.js 서버 프로그래밍할때 아예 async/await로 동기 처리를 하는 편이다.&lt;/p&gt;
&lt;pre id=&quot;code_1683027456011&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const mysql = require('mysql');

// connectDB 함수를 정의
async function connectDB () {
  // DB 연결 정보를 담은 객체 생성
  let connectionInfo = {
    host: 'localhost', // DB 호스트 주소
    user: 'root', // DB 유저 이름
    password: '1234', // DB 비밀번호
    database: 'spyncdb' // DB 이름
  };
  
  // connection 객체를 생성하고 반환
  let connection = mysql.createConnection(connectionInfo);
  return connection;
}

// mysql 라이브러리를 사용하는 경우
async function userHandler (username, displayName, profilePicture, email) {
  // DB에 연결
  connection = await connectDB() 
  
  // DB를 선택
  await connection.query ('USE spyncdb;'); 
  
  // 쿼리를 실행하고 결과를 받음
  let result = await connection.query ('SELECT * FROM users WHERE username = ?', [username]); 
}

// userHandler 함수를 호출하고 user_id 값을 얻으려고 함
let user_id = await userHandler (aUser.username, aUser.displayName, aUser.profilePicture, aUser.email); // userHandler 함수가 비동기 작업을 완료할 때까지 기다림
console.log (user_id); // user_id 출력&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://medium.com/from-the-scratch/wtf-is-synchronous-and-asynchronous-1a75afd039df&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://notes.arkalim.org/notes/programming/asynchronous%20programming/&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://www.youtube.com/watch?v=oEIoqGd-Sns&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://joooing.tistory.com/entry/%EB%8F%99%EA%B8%B0%EB%B9%84%EB%8F%99%EA%B8%B0-%EB%B8%94%EB%A1%9C%ED%82%B9%EB%85%BC%EB%B8%94%EB%A1%9C%ED%82%B9&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://velog.io/@ellyheetov/Asynchoronous-VS-Synchoronous-1&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://velog.io/@nittre/%EB%B8%94%EB%A1%9C%ED%82%B9-Vs.-%EB%85%BC%EB%B8%94%EB%A1%9C%ED%82%B9-%EB%8F%99%EA%B8%B0-Vs.-%EB%B9%84%EB%8F%99%EA%B8%B0&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://ttl-blog.tistory.com/782&lt;/p&gt;</description>
      <category>개발 지식/CS 지식</category>
      <category>Async</category>
      <category>blocking</category>
      <category>non blocking</category>
      <category>Sync</category>
      <category>논블로킹</category>
      <category>동기</category>
      <category>블로킹</category>
      <category>비동기</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/529</guid>
      <comments>https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-%EB%8F%99%EA%B8%B0%EB%B9%84%EB%8F%99%EA%B8%B0-%EB%B8%94%EB%A1%9C%ED%82%B9%EB%85%BC%EB%B8%94%EB%A1%9C%ED%82%B9-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC#entry529comment</comments>
      <pubDate>Tue, 2 May 2023 21:25:07 +0900</pubDate>
    </item>
    <item>
      <title>  자바스크립트의 핵심 '비동기' 완벽 이해 ❗</title>
      <link>https://inpa.tistory.com/entry/%F0%9F%8C%90-js-async</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;maxresdefault (7).webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8ieQ0/btscxmcFkns/IpLW56HqBbajkN7QqqQINK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8ieQ0/btscxmcFkns/IpLW56HqBbajkN7QqqQINK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8ieQ0/btscxmcFkns/IpLW56HqBbajkN7QqqQINK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8ieQ0%2FbtscxmcFkns%2FIpLW56HqBbajkN7QqqQINK%2Fimg.webp&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; alt=&quot;js-sync-async&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;720&quot; data-filename=&quot;maxresdefault (7).webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id=&quot;2d3e&quot; style=&quot;background-color: #ffffff; color: #292929; text-align: start;&quot; 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;span style=&quot;color: #333333; text-align: start;&quot;&gt;우리가 프로그래밍을 하면서 일반적으로 각 함수와 코드들이 위에서 아래로 차례로 동작하는 방식이라고 할 수 있다. 이러한 코드 순차 실행을 &lt;b&gt;동기(Synchronous) &lt;/b&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;&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;714&quot; data-origin-height=&quot;576&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m8GzI/btsbkHpQtV7/pCvAcvfDD30A3jAYYAKXLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m8GzI/btsbkHpQtV7/pCvAcvfDD30A3jAYYAKXLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m8GzI/btsbkHpQtV7/pCvAcvfDD30A3jAYYAKXLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm8GzI%2FbtsbkHpQtV7%2FpCvAcvfDD30A3jAYYAKXLK%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; alt=&quot;Synchronous&quot; loading=&quot;lazy&quot; width=&quot;509&quot; height=&quot;411&quot; data-origin-width=&quot;714&quot; data-origin-height=&quot;576&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 자바스크립트로 여러 작업을 동시에 처리하기 위해 &lt;b&gt;비동기(Asynchronous)&lt;/b&gt; 라는 개념을 도입하여, 특정 작업의 완료를 기다리지 않고 다른 작업을 동시에 수행할 수 있도록 하였다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;자바스크립트를 배우다 보면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;s&gt;setTimeout()&lt;/s&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수나&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;s&gt;fetch()&lt;/s&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수를 접해볼 것이고, 이들은 비동기로 동작한다는 소리를 한번 쯤은 들어본 적이 있을 것이다. &lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;비동기는 메인 스레드가 작업을 다른 곳에 인가하여 처리되게 하고, 그 작업이 완료되면 콜백 함수를 받아 실행하는 방식으로, 쉽게 말해 작업을 백그라운드에 요청하여 처리되게 하여&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;멀티로 작업을 동시에 처리&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;하는 것으로 보면 된다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;429&quot; data-origin-height=&quot;568&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t67AU/btsbbs0SqfO/mMkpWdfJNjrA4jkxnptzj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t67AU/btsbbs0SqfO/mMkpWdfJNjrA4jkxnptzj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t67AU/btsbbs0SqfO/mMkpWdfJNjrA4jkxnptzj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft67AU%2Fbtsbbs0SqfO%2FmMkpWdfJNjrA4jkxnptzj1%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; alt=&quot;Asynchronous&quot; loading=&quot;lazy&quot; width=&quot;319&quot; height=&quot;422&quot; data-origin-width=&quot;429&quot; data-origin-height=&quot;568&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에 데이터를 요청하고 응답을 받아야 하는 작업이 있다면, 응답이 오는 것과 상관없이 다른 작업을 계속 이어나가 병렬로 작업을 동시 처리가 가능해져 프로그램의 흐름이 멈추거나 지연되지 않게 된다. 따라서 Task들이 병렬적으로 동시에 처리되게 되고 총 코드 실행 시간은 획기적으로 줄어들게 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;비동기 처리의 유용성&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 웹 애플리케이션에서 데이터베이스 쿼리를 수행하는 작업이 있다고 가정해보자. 이 작업을 만일 동기적으로 수행하면, 데이터베이스에서 응답이 올 때까지 기다려야 한다. 그러면 이 때 웹 애플리케이션은 다른 요청을 처리하지 못하므로, 대규모 트래픽이 발생할 경우 웹 애플리케이션의 성능이 저하될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 비동기 방식으로 데이터베이스 쿼리를 수행하면, 데이터베이스에서 응답이 올 때까지 기다리는 동안에도 다른 요청을 처리할 수 있게 된다. &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&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;대표적으로 웹에서 비동기 처리를 가능하게 하는 Ajax 기술이 있다. 다른 서버에게 데이터를 요청할때&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;XMLHttpRequest&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;객체나 혹은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;fetch&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;메서드로 요청을하게 하는데, &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;서버로부터 응답을 기다리는 동안에도 사용자와의 인터랙션을 유지할 수 있으므로 사용자 경험을 향상시킬 수 있게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1681912079798&quot; class=&quot;scilab&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;// fetch 함수에 URL 전달
fetch(&quot;https://jsonplaceholder.typicode.com/todos/1&quot;)
  .then(function(response) {
    return response.json(); // 응답을 JSON 형식으로 변환
  })
  .then(function(data) {
    console.log(data); // JSON 데이터를 출력
  })
  .catch(function(error) {
    console.error(error); // 에러를 출력
  });&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;비동기의 병렬 처리 원리&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; 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;span&gt;&amp;nbsp;&lt;/span&gt;일 것이다. 그리고 아래와 같은 그림을 눈빠지게 봐왔을 것이다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;비동기 함수의 콜백 함수가 이벤트 루프에 의해서 Callback Queue에 담기고 다시 싱글 스레드인 Call Stack에 담겨서 콜백 함수가 실행되는 동작 원리 까지는 아마 대부분의 강의에서 접해보고 넘어갔을 것이다.&lt;span&gt;&amp;nbsp;&lt;/span&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;img - 2023-04-24T201334.961.png&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;508&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lbFeL/btscv0gJmH1/e8yV5aLcHXTCVAGhAzqcQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lbFeL/btscv0gJmH1/e8yV5aLcHXTCVAGhAzqcQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lbFeL/btscv0gJmH1/e8yV5aLcHXTCVAGhAzqcQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlbFeL%2Fbtscv0gJmH1%2Fe8yV5aLcHXTCVAGhAzqcQ1%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; alt=&quot;js-Asynchronous&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;508&quot; data-filename=&quot;img - 2023-04-24T201334.961.png&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;508&quot;/&gt;&lt;/span&gt;&lt;/figure&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;letter-spacing: 0px;&quot;&gt;그런데 자바스크립트는 싱글 스레드 언어라고 했는데 어떻게 &lt;/span&gt;&lt;b&gt;작업(task)들을 동시에 처리&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&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;이에 대해 정확히 설명하지 못하는 독자분들은, 아마 다른 곳에서 자바스크립트 이벤트 루프와 호출 스택 과정을 학습하는데 있어 높은 확률로 Call Stack 우측에 있는 Web APIs 에 대해서 대충 보고 넘어갔을 것이다. 왜냐하면 자바스크립트를 실행하는 콜 스택(Call Stack)은 싱글 스레드지만, 서버에게 리소스를 요청하거나 파일 입출력 혹은 타이머 대기 작업을 실행하는 &lt;span style=&quot;color: #ee2323;&quot;&gt;Web APIs 들은 멀티 스레드&lt;/span&gt;이기 때문에 동시 작업 처리가 가능하기 때문이다.&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(멀티 스레드를 잘 모른다면 백그라운드에서 동시에 처리된다고 이해하면 된다)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;976&quot; data-origin-height=&quot;508&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBl9lg/btsb6ZDoIJ3/q71frFgzfUoqv9VQxouxOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBl9lg/btsb6ZDoIJ3/q71frFgzfUoqv9VQxouxOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBl9lg/btsb6ZDoIJ3/q71frFgzfUoqv9VQxouxOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBl9lg%2Fbtsb6ZDoIJ3%2Fq71frFgzfUoqv9VQxouxOK%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; alt=&quot;js-Asynchronous&quot; loading=&quot;lazy&quot; width=&quot;976&quot; height=&quot;508&quot; data-origin-width=&quot;976&quot; data-origin-height=&quot;508&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Web API는 타이머, 네트워크 요청, 파일 입출력, 이벤트 처리 등 브라우저에서 제공하는 다양한 API를 포괄하는 API의 총칭이다. 브라우저마다 다르겠지만, 크롬 브라우저 일 경우 Web API는 멀티 스레드로 구현되어 있다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;즉, &lt;span style=&quot;color: #ee2323;&quot;&gt;브라우저라는 소프트웨어가 멀티 스레드&lt;/span&gt; 이기 때문에 메인 자바스크립트 스레드를 차단하지 않고 다른 스레드를 사용하여 Web API의 작업을 처리하여 동시 처리가 가능한 것이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;만일 아래와 같이 3초를 대기하는 setTimeout 비동기 함수와 그외 작업(Task)들이 있다고 한다면, 이 setTimeout 코드가 Web APIs 들 중 타이머 처리를 담당하는 Timer API에 넘어가서 3000ms 밀리초를 병렬로 처리되면서, 동시에 메인 콜 스택의 Task1, Task2 ... 를 처리하는 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1682335580961&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;setTimeout(() =&amp;gt; {
	console.log('5초 대기 완료')
}, 3000);

Task1();
Task2();
Task3();&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;820&quot; data-origin-height=&quot;508&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4gU6W/btscwmYghto/zSIsG1O8ia9DC24wFDuV4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4gU6W/btscwmYghto/zSIsG1O8ia9DC24wFDuV4k/img.png&quot; data-alt=&quot;각 싱글 스레드가 동시에 처리하니 멀티 스레드이다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4gU6W/btscwmYghto/zSIsG1O8ia9DC24wFDuV4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4gU6W%2FbtscwmYghto%2FzSIsG1O8ia9DC24wFDuV4k%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; alt=&quot;js-Asynchronous&quot; loading=&quot;lazy&quot; width=&quot;820&quot; height=&quot;508&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;508&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;정리하자면 브라우저는 멀티스레드로 이루어져 있고 비동기 함수는 이러한 동시적 처리 작업 원리 덕분에 우리는 비동기 함수를 통해 성능 향상을 누릴 수 있었던 것이었다.&lt;/p&gt;
&lt;figure id=&quot;og_1683593944352&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;  자바스크립트 이벤트 루프 구조 &amp;amp; 동작 원리&quot; data-og-description=&quot;자바스크립트 비동기와 이벤트 루프 브라우저의 멀티 스레드로 작업을 동시에 Javascript는 싱글 스레드 언어라고 들어본 적이 있을 것이다. '싱글' 스레드라 한 번에 하나의 작업만 수행이 가능하&quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/%F0%9F%94%84-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84-%EA%B5%AC%EC%A1%B0-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC&quot; data-og-url=&quot;https://inpa.tistory.com/entry/%F0%9F%94%84-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84-%EA%B5%AC%EC%A1%B0-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/buiBqg/hySykkmnK8/x1ajNosv2T0mzGqh04Xgd1/img.png?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/cyPupO/hySyifMjuD/XRHwI4bhPA7N7x7PTYLJM1/img.png?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/c0SMPE/hySyspaIUQ/pHWgdc4sWIPlEQ2qt5diQ1/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/%F0%9F%94%84-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84-%EA%B5%AC%EC%A1%B0-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inpa.tistory.com/entry/%F0%9F%94%84-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84-%EA%B5%AC%EC%A1%B0-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/buiBqg/hySykkmnK8/x1ajNosv2T0mzGqh04Xgd1/img.png?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/cyPupO/hySyifMjuD/XRHwI4bhPA7N7x7PTYLJM1/img.png?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/c0SMPE/hySyspaIUQ/pHWgdc4sWIPlEQ2qt5diQ1/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;  자바스크립트 이벤트 루프 구조 &amp;amp; 동작 원리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;자바스크립트 비동기와 이벤트 루프 브라우저의 멀티 스레드로 작업을 동시에 Javascript는 싱글 스레드 언어라고 들어본 적이 있을 것이다. '싱글' 스레드라 한 번에 하나의 작업만 수행이 가능하&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;inpa.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;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;왜 완벽한 멀티 스레딩이 아닌가&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;setTimeout을 이용해 비동기의 멀티 작업 처리를 설명했지만, 사실 자바스크립트의 비동기는 완벽한 멀티 스레딩이 아니다. 왜냐하면 타이머 3000ms 만 병렬적으로 처리되고, 그 안의 콜백 함수 실행 코드는 추후에 이벤트 루프에 인해 콜 스택(Call Stack)에 들어가 싱글 스레드로 처리되기 때문이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;460&quot; data-origin-height=&quot;223&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ItOqn/btscArxtxU1/ieSl40QckzqDfDrZfZuQok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ItOqn/btscArxtxU1/ieSl40QckzqDfDrZfZuQok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ItOqn/btscArxtxU1/ieSl40QckzqDfDrZfZuQok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FItOqn%2FbtscArxtxU1%2FieSl40QckzqDfDrZfZuQok%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; alt=&quot;js-Asynchronous&quot; loading=&quot;lazy&quot; width=&quot;460&quot; height=&quot;223&quot; data-origin-width=&quot;460&quot; data-origin-height=&quot;223&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;setTimeout 뿐만 아니라 fetch 비동기 함수도 마찬가지이다. 서버에 요청해서 리소스를 다운로드 하는 것은 멀티 스레드로 병렬적으로 처리되지만, 요청이 완료되고 나서의 후처리 then 핸들러의 콜백 함수는 콜 스택에 별도로 처리된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;569&quot; data-origin-height=&quot;267&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0fepe/btscxbWon7G/lX4WAe966Uj5IEvfQCybG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0fepe/btscxbWon7G/lX4WAe966Uj5IEvfQCybG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0fepe/btscxbWon7G/lX4WAe966Uj5IEvfQCybG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0fepe%2FbtscxbWon7G%2FlX4WAe966Uj5IEvfQCybG0%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; alt=&quot;js-Asynchronous&quot; loading=&quot;lazy&quot; width=&quot;569&quot; height=&quot;267&quot; data-origin-width=&quot;569&quot; data-origin-height=&quot;267&quot;/&gt;&lt;/span&gt;&lt;/figure&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: #ee2323;&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;아마 자바의 멀티 스레드(Thread) 프로그래밍을 해보신 독자분들은 이에 대한 이유를 알고 있을 것이다. 완전한 병렬 처리는 성능 만큼은 이득을 얻을수 있을지도 모르겠지만, 항상 동시성 문제가 따라와 synchronized 처리가 수반된다는 사실 말이다. 자바스크립트 비동기만 배우신 분들은 이에 대해 잘 와닿지 않을 수 있지만, 이 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;synchronized&lt;span&gt; 처리를 잘못하면 오히려 성능 감소가 일어나기 때문에 고난이도의 지식과 실력을 요구한다. 따라서 자바스크립트에서는 동시성 문제에 대해 심플하게 처리하기 위해 비동기 콜백 함수 방식을 채택하였다고 보면 된다. &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;자바스크립트도 멀티 스레딩을 사용하고 싶어&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;그럼 자바스크립트는 영원히 멀티 스레딩을 구현 못하는 것일까? &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;그래서 나온 것이 바로 &lt;b&gt;웹 워커(web workers)&lt;/b&gt;이다. 웹 워커를 이용하면 자바스크립트도 자바의 스레드와 같이 멀티 스레드 프로그래밍을 할 수가 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1682341188412&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 웹 워커 스크립트 파일(worker.js)
self.addEventListener('message', function(e) {
  // 메인 스크립트로부터 메시지를 받으면 실행할 함수
  var result = &quot;Hello &quot; + e.data;
  self.postMessage(result); // 결과를 메인 스크립트로 전송
}, false);&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1682337509617&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 메인 스크립트에서 웹 워커 사용 예시
var worker = new Worker('./worker.js'); // 웹 워커 객체 생성

worker.addEventListener('message', function(e) {
  // 웹 워커로부터 메시지를 받으면 실행할 함수
  console.log(e.data); // 결과 출력
}, false);

worker.postMessage('World'); // 웹 워커에게 메시지 전송&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;582&quot; data-origin-height=&quot;470&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/meYgi/btscmLR33pU/LMn6dRjqZ0jLk0D6zv5Rp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/meYgi/btscmLR33pU/LMn6dRjqZ0jLk0D6zv5Rp1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/meYgi/btscmLR33pU/LMn6dRjqZ0jLk0D6zv5Rp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmeYgi%2FbtscmLR33pU%2FLMn6dRjqZ0jLk0D6zv5Rp1%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; alt=&quot;web-worker&quot; loading=&quot;lazy&quot; width=&quot;582&quot; height=&quot;470&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;470&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Node.js에서도 Worker_Threads 모듈로 멀티 스레드를 구현할 수 있다.&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;브라우저의 비동기 처리&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이러한 비동기 원리는 꼭 자바스크립트 언어 뿐만 아니라 브라우저의 HTML 마크업 언어에서도 동일하게 적용된다. HTML 의 파싱 동작 방식을 예로 들 수 있는데, 아래와 같이 다음과&amp;nbsp;같이&amp;nbsp;&lt;s&gt;&amp;lt;script&amp;gt;&lt;/s&gt;&amp;nbsp;태그를&amp;nbsp;HTML&amp;nbsp;파일의&amp;nbsp;&lt;s&gt;&amp;lt;head&amp;gt;&lt;/s&gt; 태그 안에 넣으면, 자바스크립트 파일이 다운로드되고 실행될 때까지 HTML 파싱이 중단되게 된다. 이는 사용자가 웹 페이지의 내용을 보는데 오래 기다려야 하게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1681912046358&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;script src=&quot;script1.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;script src=&quot;script2.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;script src=&quot;script3.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  &amp;lt;h1&amp;gt;웹 페이지 제목&amp;lt;/h1&amp;gt;
  &amp;lt;p&amp;gt;웹 페이지 내용&amp;lt;/p&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1157&quot; data-origin-height=&quot;248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rMNQR/btr9zwp4S4R/iyS1NrFsojFLQpIlraWY20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rMNQR/btr9zwp4S4R/iyS1NrFsojFLQpIlraWY20/img.png&quot; data-alt=&quot;Synchronous&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rMNQR/btr9zwp4S4R/iyS1NrFsojFLQpIlraWY20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrMNQR%2Fbtr9zwp4S4R%2FiyS1NrFsojFLQpIlraWY20%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; alt=&quot;브라우저의 비동기&quot; loading=&quot;lazy&quot; width=&quot;1157&quot; height=&quot;248&quot; data-origin-width=&quot;1157&quot; data-origin-height=&quot;248&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Synchronous&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;자바스크립트에 비동기 함수가 있다면 html에도 비동기를 이용할 수 있는 기술이 있다. 바로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;&amp;lt;script&amp;gt;&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;태그에 붙이는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;async&lt;/s&gt;&lt;span&gt; 과 &lt;s&gt;defer &lt;/s&gt;&lt;/span&gt;키워드 이다. async와 defer 속성은 자바스크립트 파일을 비동기적으로 다운로드하고 실행할 수 있게 해준다. 이렇게 하면 HTML 파싱과 자바스크립트 다운로드가 동시에 진행되어 페이지 로딩 속도를 향상시킬 수 있게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1681342837105&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;script async src=&quot;script1.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;script async src=&quot;script2.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;script async src=&quot;script3.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  &amp;lt;h1&amp;gt;웹 페이지 제목&amp;lt;/h1&amp;gt;
  &amp;lt;p&amp;gt;웹 페이지 내용&amp;lt;/p&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1157&quot; data-origin-height=&quot;271&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZe73K/btr9DV2WANI/1fVN2GRabMeGDun7wOq3D1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZe73K/btr9DV2WANI/1fVN2GRabMeGDun7wOq3D1/img.png&quot; data-alt=&quot;Asynchronous&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZe73K/btr9DV2WANI/1fVN2GRabMeGDun7wOq3D1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZe73K%2Fbtr9DV2WANI%2F1fVN2GRabMeGDun7wOq3D1%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; alt=&quot;html-async&quot; loading=&quot;lazy&quot; width=&quot;1157&quot; height=&quot;271&quot; data-origin-width=&quot;1157&quot; data-origin-height=&quot;271&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Asynchronous&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;그림을 비교하면 똑같은 작업을 처리하는데 있어 Asynchronous가 총 걸린 Time이 더 적은걸 볼 수 있다.&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;이 역시 위에서 배운 자바스크립트와 Web APIs 원리와 같이, 파일 다운로드 동작 자체를 비동기로 브라우저 내부의 멀티 스레드에 양도하고 계속 html 파싱을 이어나가면서 동시에 파일을 다운받는 원리이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;HTML의 비동기는 자바스크립트의 콜 스택과 이벤트 루프 동작과는 별개로 브라우저 내부 구현에 의존하는 것이다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;웹 어플리케이션을 이용하는 사용자들은 빠른 서비스를 원하지, 불러오는 속도가 느리고 반응이 없는 것을 원하지 않기 때문에 비동기적으로 페이지를 구성하는 것은 필수 스킬이라 볼 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;비동기 처리의 문제점&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다만 성능 향상을 위해 비동기 처리를 이용할때 주의해야 할 점이 있다. Asynchronous는 요청한 작업의 완료 여부를 기다리지 않고 자신의 그다음 작업을 계속 수행해 나간다고 했다. 그런데 만일 그다음 실행할 작업이 이전에 요청한 작업의 결과가 반드시 필요할 경우 문제가 생긴다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;쉽게 예를 들어보면 아래의 html의 스크립트 코드는 에러를 일으키게 된다. 왜냐하면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;$('body').appned()&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;코드는 제이쿼리 전용 코드로서 제이쿼리 라이브러리 파일이 다운을 모두 받은 상태여야만 사용할 수 있기 때문이다. 하지만 제이쿼리 소스파일 호출을 비동기적으로 진행해 소스파일이 다운로드 되기도 전에 제이쿼리 코드를 사용했으니 에러가 일어나는게 당연한 것이다. (만일 소스파일 다운을 엄청 빨리한다면 에러가 일어나지 않을 수 있다)&lt;/p&gt;
&lt;pre id=&quot;code_1681342837107&quot; class=&quot;xml&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script async src='https://code.jquery.com/jquery-3.6.0.min.js'&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script&amp;gt;
	// 제이쿼리 파일을 비동기적으로 호출하고 바로 제이쿼리 전용 코드를 실행 시켰기 때문에 에러가 발생하다
    $('body').append('&amp;lt;h1&amp;gt;Hello World&amp;lt;/h1&amp;gt;'); // ! ERROR
&amp;lt;/script&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;예를 들어 다음 코드는 서버의 데이터베이스를 조회하여 데이터를 가져오는 로직을 간단하게 표현한 예제이다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;getDB()&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수를 통해 데이터베이스를 조회하는데, 이때 조회 시간이 3초 걸린다고 가정하자. 그리고 DB로부터&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;응답을 받게 되면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;s&gt;data&lt;/s&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;변수에 저장하고&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;값을 두배 곱셈 연산 후 출력하려고 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1681342837108&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 getDB() {
    let data;
    // 데이터베이스에서 값을 가져오는 3초 걸린다고 가정 (비동기 처리)
    setTimeout(() =&amp;gt; {
        data = 100;
    }, 3000);

    return data;
}

function main() {
    let value = getDB();
    value *= 2;
    console.log('value의 값 : ', value);
}
main(); // 메인 스레드 실행&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;169&quot; data-origin-height=&quot;57&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAREMQ/btr9N2g7ufu/k0t21MlCxdLH9J1ttZSotK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAREMQ/btr9N2g7ufu/k0t21MlCxdLH9J1ttZSotK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAREMQ/btr9N2g7ufu/k0t21MlCxdLH9J1ttZSotK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAREMQ%2Fbtr9N2g7ufu%2Fk0t21MlCxdLH9J1ttZSotK%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; alt=&quot;js-async&quot; loading=&quot;lazy&quot; width=&quot;169&quot; height=&quot;57&quot; data-origin-width=&quot;169&quot; data-origin-height=&quot;57&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 결과를 확인해 보니&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;data&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;변수에 NaN이라는 이상한 값이 들어가 있다. 왜 이런 결과가 나왔을까? 그 이유는 비동기 함수인 setTimeout 함수가 3초 동안 대기하는 동안 완료될 때까지 기다리지 않고 다음 코드인&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;console.log(data)&lt;/s&gt;를 실행하 였기 때문이다. 이때&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;data&lt;/s&gt;&lt;span&gt;&amp;nbsp;&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&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;작업의 순서를 맞추는 것이 필수 불가결일 경우&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;어쩔수 없이 비동기를 포기하고 동기로 처리해야 되나 싶지만, 이를 해결하는 몇가지 기법이 있다. 가장 대표적인 것이 &lt;b&gt;콜백 함수&lt;/b&gt; 기법이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;비동기를 알맞게 처리하기 위한 기법&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;비동기와 콜백 함수&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;흔히 비동기를 다룰때 자주 엮여 등장하는 개념이 콜백(callback) 함수이다. 콜백 함수는 자바스크립트의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://inpa.tistory.com/entry/CS-%F0%9F%91%A8%E2%80%8D%F0%9F%92%BB-%EC%9D%BC%EA%B8%89-%EA%B0%9D%EC%B2%B4first-class-object&quot;&gt;일급 객체&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&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;pre id=&quot;code_1681342837109&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 getDB(callback) {
    // 데이터베이스로부터 3초 후에 데이터 값을 받아온 후, 콜백 함수 호출
    setTimeout(() =&amp;gt; {
        const value = 100;
        callback(value);
    }, 3000);
}

function main() {
    // 호출할 작업에 콜백 함수를 넘긴다
    getDB(function(value) {
        let data = value * 2;
        console.log('data의 값 : ', data);
    });
}
main();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;189&quot; data-origin-height=&quot;56&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dLlNON/btr9NBKSbLT/zLt6FFtNlCuhMeSwv373Y0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dLlNON/btr9NBKSbLT/zLt6FFtNlCuhMeSwv373Y0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dLlNON/btr9NBKSbLT/zLt6FFtNlCuhMeSwv373Y0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdLlNON%2Fbtr9NBKSbLT%2FzLt6FFtNlCuhMeSwv373Y0%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; alt=&quot;js-async&quot; loading=&quot;lazy&quot; width=&quot;189&quot; height=&quot;56&quot; data-origin-width=&quot;189&quot; data-origin-height=&quot;56&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위 코드는 콜백 함수 내에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;data&lt;/s&gt;&lt;span&gt;&amp;nbsp;&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&gt;&amp;nbsp;&lt;/span&gt;너무 복잡하게 얽힌 비동기 처리 때문에 콜백 함수 방식은 코드 복잡도를 증가시켜,&lt;span&gt;&amp;nbsp;&lt;/span&gt;개발자가 어플리케이션의 흐름을 읽기 어려워지는 등의 문제가 있을 수 있어 잘못하면 콜백 지옥(callback hell) 에 빠질수 있다는 단점이 있다. 조금 억지스러운 코드지만 정말로 콜백 함수들을 복잡하게 중첩하여 구성한다면 아래와 같은 '아됴겐'에 맞아 허리가 구부러진 우스꽝 스러운 코드 모양이 되게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;466&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/djGlZ4/btsakibWVJW/jVOsVUpMkzxTXjzQKKc0TK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/djGlZ4/btsakibWVJW/jVOsVUpMkzxTXjzQKKc0TK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/djGlZ4/btsakibWVJW/jVOsVUpMkzxTXjzQKKc0TK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdjGlZ4%2FbtsakibWVJW%2FjVOsVUpMkzxTXjzQKKc0TK%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; alt=&quot;callback-hell&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;466&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;466&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;style3&quot; /&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;비동기와 프로미스 객체&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;콜백 함수는 엄연히 말하자면 비동기를 순차적으로 처리하기 위한 일종의 '편법' 같은 것이지 정식으로 지원하는 비동기 전용 함수가 아니다. 따라서 자바스크립트의 Promise 객체는 이러한 한계점을 극복하기 위해 비동기 처리를 위한 전용 객체로서 탄생하였다.&amp;nbsp;Promise는 비동기 작업의 성공 또는 실패와 그 결과값을 나타내는 객체이다. 그래서 Promise를 사용하면 비동기 작업을 쉽고 깔끔하게 연결할 수 있게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1682338823755&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function getDB() {
  return new Promise((resolve) =&amp;gt; {
    setTimeout(() =&amp;gt; {
      const value = 100;
      resolve(value);
    }, 3000);
  });
}

function main() {
  getDB()
    .then((value) =&amp;gt; {
      let data = value * 2;
      console.log('data의 값 : ', data);
    })
    .catch((error) =&amp;gt; {
      console.error(error);
    });
}

main();&lt;/code&gt;&lt;/pre&gt;
&lt;figure id=&quot;og_1682340027558&quot; style=&quot;color: #333333; text-align: start;&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;  자바스크립트 Promise 개념 &amp;amp; 문법 정복하기&quot; data-og-description=&quot;콜백 지옥을 탈출하는 새로운 문법 자바스크립트에서 '비동기 처리' 란 현재 실행중인 작업과는 별도로 다른 작업을 수행하는 것을 말한다. 예를 들어 서버에서 데이터를 받아오는 작업은 시간&quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%B2%98%EB%A6%AC-Promise&quot; data-og-url=&quot;https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%B2%98%EB%A6%AC-Promise&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bsFM54/hySkMPvkUi/8ffY1Y8pnhvOQzdUQgCnhk/img.jpg?width=800&amp;amp;height=452&amp;amp;face=0_0_800_452,https://scrap.kakaocdn.net/dn/bT0xRf/hySkVeBozZ/dgPm9sCAKtAkv07swTk2S1/img.jpg?width=800&amp;amp;height=452&amp;amp;face=0_0_800_452,https://scrap.kakaocdn.net/dn/UvyrV/hySkWkhCCH/FMGoJk7OFP7C6imppoe8I1/img.png?width=801&amp;amp;height=297&amp;amp;face=0_0_801_297&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%B2%98%EB%A6%AC-Promise&quot; data-source-url=&quot;https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%B2%98%EB%A6%AC-Promise&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bsFM54/hySkMPvkUi/8ffY1Y8pnhvOQzdUQgCnhk/img.jpg?width=800&amp;amp;height=452&amp;amp;face=0_0_800_452,https://scrap.kakaocdn.net/dn/bT0xRf/hySkVeBozZ/dgPm9sCAKtAkv07swTk2S1/img.jpg?width=800&amp;amp;height=452&amp;amp;face=0_0_800_452,https://scrap.kakaocdn.net/dn/UvyrV/hySkWkhCCH/FMGoJk7OFP7C6imppoe8I1/img.png?width=801&amp;amp;height=297&amp;amp;face=0_0_801_297');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;  자바스크립트 Promise 개념 &amp;amp; 문법 정복하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;콜백 지옥을 탈출하는 새로운 문법 자바스크립트에서 '비동기 처리' 란 현재 실행중인 작업과는 별도로 다른 작업을 수행하는 것을 말한다. 예를 들어 서버에서 데이터를 받아오는 작업은 시간&lt;/p&gt;
&lt;p class=&quot;og-host&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;inpa.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;비동기와 async / await&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 프로미스도 완벽한 해결책은 아니다. 왜냐하면 Callback Hell이 있듯이 지나친 then 핸들러 함수의 남용으로 인한 Promise Hell이 존재하기 때문이다. 즉, 프로미스가 여러 개 연결되면 코드가 길어지고 복잡해질 수 있다는 것이다. 그래서 자바스크립트에는 async/await라는 문법이 또한 추가되었다. async/await는 프로미스를 기반으로 하지만, 마치 동기 코드처럼 작성할 수 있게 해준다. 비동기 작업을 쉽게 읽고 이해할 수 있게 해주기 때문에 비동기 작업을 처리할 일이 있다면 대게 async/await 방식을 쓰는 것이 보통이다.&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;자바스크립트에서&amp;nbsp;Promise나&amp;nbsp;async/await와&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;/blockquote&gt;
&lt;pre id=&quot;code_1681342837111&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 getDB() {
    return new Promise((resolve, reject) =&amp;gt; {
        // 데이터베이스에서 값을 가져오는 3초 걸린다고 가정 (비동기 처리)
        setTimeout(() =&amp;gt; {
            const value = 100;
            resolve(value); // Promise 객체 반환
        }, 3000);
    });
}

async function main() {
    let data = await getDB(); // await 키워드로 Promise가 완료될 때까지 기다린다
    data *= 2;
    console.log('data의 값 : ', data);
}
main(); // 메인 스레드 실행&lt;/code&gt;&lt;/pre&gt;
&lt;figure id=&quot;og_1682340030495&quot; style=&quot;color: #333333; text-align: start;&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;  자바스크립트 Async/Await 개념 &amp;amp; 문법 정복&quot; data-og-description=&quot;자바스크립트 비동기 처리 3가지 방식 자바스크립트는 싱글 스레드 프로그래밍 언어기 때문에 멀티 작업을 하기 위해선 비동기 처리 방식이 자주 쓰인다. 비동기 처리는 백그라운드로 동작되기&quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%B2%98%EB%A6%AC-async-await&quot; data-og-url=&quot;https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%B2%98%EB%A6%AC-async-await&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bOqxGi/hySm2ZmiVO/OVhPUO5n5eYYb5wSzRbvxK/img.jpg?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/bDnjHo/hySoDwSU8o/gF3RjkaMODy0FvqdiowXJK/img.jpg?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/s52qW/hySmZuMYtw/DO3DgK4YlG3qdZJAVwMCOk/img.png?width=615&amp;amp;height=462&amp;amp;face=0_0_615_462&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%B2%98%EB%A6%AC-async-await&quot; data-source-url=&quot;https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%B2%98%EB%A6%AC-async-await&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bOqxGi/hySm2ZmiVO/OVhPUO5n5eYYb5wSzRbvxK/img.jpg?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/bDnjHo/hySoDwSU8o/gF3RjkaMODy0FvqdiowXJK/img.jpg?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/s52qW/hySmZuMYtw/DO3DgK4YlG3qdZJAVwMCOk/img.png?width=615&amp;amp;height=462&amp;amp;face=0_0_615_462');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;  자바스크립트 Async/Await 개념 &amp;amp; 문법 정복&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;자바스크립트 비동기 처리 3가지 방식 자바스크립트는 싱글 스레드 프로그래밍 언어기 때문에 멀티 작업을 하기 위해선 비동기 처리 방식이 자주 쓰인다. 비동기 처리는 백그라운드로 동작되기&lt;/p&gt;
&lt;p class=&quot;og-host&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;inpa.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;4-그럼-무조건-await가-답인가&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;그럼 무조건 await가 답인가?&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 보면 async/await이 비동기를 처리함에 있어 callback이나 Promise 방식보다 훨씬 좋아 보이지만, &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;사용 방법에 따라서는 코드가 복잡해질 수도 있다. 따라서&lt;/span&gt; 이 비동기 처리에 대한 3가지 방식은 용도에 맞춰서 적절히 사용해야 한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;왜냐하면 callback 방식은 별 다른 키워드 없이도 정말 단순하게 구현할 수 있는 문법이기 때문에, 콜백 지옥을 맞이할 정도의 복잡한 상황이 아닐 때면 오히려 사용하면 가독성이 좋다. 대표적인 예로 Node.js의 Express 프레임워크는 서버 라우팅을 콜백 함수로 처리하는 방식을 제공한다.&lt;/p&gt;
&lt;pre id=&quot;code_1682340765849&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const express = require(&quot;express&quot;); // Express 모듈 불러오기
const app = express(); // Express 앱 객체 생성

// /home url 경로에 GET 요청이 들어오면 이에 대한 라우팅 정의
app.get(&quot;/home&quot;, function (req, res) {
  // 응답 보내기
  res.send(&quot;Hello, Express!&quot;);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;따라서, 콜백 함수는 복잡하기 않고 비교적 심플한 비동기 작업을 처리해야 할 때 사용하면 오히려 프로미스 방식보다 더 좋을 수 있다. 반면에 비교적 복잡한 비동기 작업을 처리할 때는 Promise 객체를 사용하면 코드를 보다 간결하게 작성할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://velog.io/@nittre/%EB%B8%94%EB%A1%9C%ED%82%B9-Vs.-%EB%85%BC%EB%B8%94%EB%A1%9C%ED%82%B9-%EB%8F%99%EA%B8%B0-Vs.-%EB%B9%84%EB%8F%99%EA%B8%B0&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://dev.to/lydiahallie/javascript-visualized-event-loop-3dif&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://medium.com/sjk5766/javascript-%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%95%B5%EC%8B%AC-event-loop-%EC%A0%95%EB%A6%AC-422eb29231a8&lt;/p&gt;
&lt;div id=&quot;gtx-trans&quot; style=&quot;position: absolute; left: -53px; top: 5266.97px;&quot;&gt;
&lt;div class=&quot;gtx-trans-icon&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;</description>
      <category>Language/JavaScript (WEB)</category>
      <category>Async</category>
      <category>Callback</category>
      <category>sync async</category>
      <category>동기 비동기</category>
      <category>콜백</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1109</guid>
      <comments>https://inpa.tistory.com/entry/%F0%9F%8C%90-js-async#entry1109comment</comments>
      <pubDate>Mon, 1 May 2023 08:58:48 +0900</pubDate>
    </item>
    <item>
      <title>  PurgeCSS 사용법 - CSS 프레임워크 코드 최적화</title>
      <link>https://inpa.tistory.com/entry/%F0%9F%94%AE-PurgeCSS</link>
      <description>&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;1998&quot; data-origin-height=&quot;1125&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/emQ48w/btr6bumVPQD/wlSUYE6fEj3Cyul5UqgXUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/emQ48w/btr6bumVPQD/wlSUYE6fEj3Cyul5UqgXUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/emQ48w/btr6bumVPQD/wlSUYE6fEj3Cyul5UqgXUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FemQ48w%2Fbtr6bumVPQD%2FwlSUYE6fEj3Cyul5UqgXUk%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; alt=&quot;purgecss&quot; loading=&quot;lazy&quot; width=&quot;1998&quot; height=&quot;1125&quot; data-filename=&quot;그림2.png&quot; data-origin-width=&quot;1998&quot; data-origin-height=&quot;1125&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;CSS 프레임워크의 문제점&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 어플리케이션을 구축할때 서비스 품질과 성능도 중요하지만, 빠지지 않는 것이 웹디자인이다. 웹디자인은 사용자의 첫인상과 만족도에 큰 영향을 미치기 때문에 신경써야 한다. 하지만 웹디자인을 처음부터 만드는 것은 쉽지 않다. 복잡한 CSS 코드를 작성하고 반응형으로 만들고 호환성을 확인하는 과정은 많은 시간과 노력이 필요하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 많은 웹 개발자들은 Bootstrap, Foundation, Bulma, Pure.css, UIKit, Materialise, Tailwind와 같은 CSS 프레임워크를 사용한다. CSS 프레임워크는 미리 정의된 CSS 클래스와 구성 요소를 제공하여, 일관되고 표준화된 방식으로 쉽고 빠르게 고퀄리티의 웹디자인을 만들 수 있으며, 재사용하고 유지 관리에도 용이하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1002&quot; data-origin-height=&quot;191&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/He4cp/btr8Jkc8lco/KsKoPXGxuhpNzCnEs4iKAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/He4cp/btr8Jkc8lco/KsKoPXGxuhpNzCnEs4iKAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/He4cp/btr8Jkc8lco/KsKoPXGxuhpNzCnEs4iKAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHe4cp%2Fbtr8Jkc8lco%2FKsKoPXGxuhpNzCnEs4iKAk%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; alt=&quot;purgecss&quot; loading=&quot;lazy&quot; width=&quot;1002&quot; height=&quot;191&quot; data-origin-width=&quot;1002&quot; data-origin-height=&quot;191&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 이러한 CSS 프레임워크에는 치명적인 단점이 존재한다. 온갖 디자인 요소들을 css 클래스로 미리 정의하여 파일로 제공하기 때문에, 현재 내 서비스에 사용하지도 않은 css 코드가 있어도 &lt;span style=&quot;color: #ee2323;&quot;&gt;불필요한 CSS 코드가 로딩되어 웹 페이지의 로딩 속도를 저하&lt;/span&gt;하기 때문이다. 디자인도 디자인이지만 만일 웹 페에지 속도가 느리면 소비자들은 망설임없이 뒤로 가기를 해버리기 때문에 서비스에 치명적이게 된다. 거기다 CSS 파일을 비동기로 로드하면 레이아웃이 꺠져 SEO에도 점수가 깎이기 때문에 우회적인 최적화 방법은 전무하다고 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img_-_2023-04-08T175054_794-transformed.jpeg&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;644&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ER8Hx/btr8J1xwSuY/zQapQUTpm0gGVE07hzPsz0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ER8Hx/btr8J1xwSuY/zQapQUTpm0gGVE07hzPsz0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ER8Hx/btr8J1xwSuY/zQapQUTpm0gGVE07hzPsz0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FER8Hx%2Fbtr8J1xwSuY%2FzQapQUTpm0gGVE07hzPsz0%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; alt=&quot;bootstrap&quot; loading=&quot;lazy&quot; width=&quot;1400&quot; height=&quot;644&quot; data-filename=&quot;img_-_2023-04-08T175054_794-transformed.jpeg&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;644&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 사용하지 않은 CSS 클래스 코드를 제거하는 것이 유일한 방법인데, 이러한 작업을 개발자가 일일히 대조하며 할 수도 없는 노릇이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서인지 Tailwind CSS 프레임워크에서는 자체로 CSS Tree Shaking 기능을 제공하여, 불필요한 CSS 코드를 자동으로 제거하여 웹 페이지의 로딩 속도를 개선해준다. Tailwind는 프로덕션(Production) 빌드 시 사용하지 않는 CSS 코드를 자동으로 찾아 제거하므로 최종 CSS 번들은 필요한 코드만 있는 작은 용량의 파일이 되게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GIF 2023-03-26 오후 4-42-41.gif&quot; data-origin-width=&quot;1229&quot; data-origin-height=&quot;570&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYmqOi/btr5PF5lf8S/VVUY01OORR6L0uiHXecwzk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYmqOi/btr5PF5lf8S/VVUY01OORR6L0uiHXecwzk/img.gif&quot; data-alt=&quot;웹페이지에 현재 사용하고 있는 css 클래스만 빌드한다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYmqOi/btr5PF5lf8S/VVUY01OORR6L0uiHXecwzk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bYmqOi/btr5PF5lf8S/VVUY01OORR6L0uiHXecwzk/img.gif&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; alt=&quot;Tailwind&quot; loading=&quot;lazy&quot; width=&quot;1229&quot; height=&quot;570&quot; data-filename=&quot;GIF 2023-03-26 오후 4-42-41.gif&quot; data-origin-width=&quot;1229&quot; data-origin-height=&quot;570&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;웹페이지에 현재 사용하고 있는 css 클래스만 빌드한다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 Tailwind를 제외한 부트스트랩과 같은 나머지 CSS 프레임워크는 이러한 최적화 기능을 제공하지 않아 고려대상에서 제외 되어야 하는 것일까?&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;PurgeCSS&lt;/b&gt;&lt;/h2&gt;
&lt;figure id=&quot;og_1680945353662&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;Getting Started | PurgeCSS&quot; data-og-description=&quot;Getting Started Most bundlers and frameworks to build websites are using PostCSS. The easiest way to configure PurgeCSS is with its PostCSS plugin. Install the PostCSS plugin: npm i -D @fullhuman/postcss-purgecss yarn add @fullhuman/postcss-purgecss --dev &quot; data-og-host=&quot;purgecss.com&quot; data-og-source-url=&quot;https://purgecss.com/getting-started.html&quot; data-og-url=&quot;https://purgecss.com/getting-started&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/FIkWu/hyR4dLDB4W/Mhy62MTIdRxpQOvHRSUiFK/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512&quot;&gt;&lt;a href=&quot;https://purgecss.com/getting-started.html&quot; data-source-url=&quot;https://purgecss.com/getting-started.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/FIkWu/hyR4dLDB4W/Mhy62MTIdRxpQOvHRSUiFK/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Getting Started | PurgeCSS&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Getting Started Most bundlers and frameworks to build websites are using PostCSS. The easiest way to configure PurgeCSS is with its PostCSS plugin. Install the PostCSS plugin: npm i -D @fullhuman/postcss-purgecss yarn add @fullhuman/postcss-purgecss --dev&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;purgecss.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PurgeCSS는 HTML, JS 등의 파일에서 사용된 CSS 선택자와 일치하는지 확인하고, CSS 파일에서 사용하지 않는 선택자를 제거하여 파일 크기를 줄이고 성능을 향상시키는 자바스크립트 라이브러리이다. 사실 Tailwind도 자체적으로 PurgeCSS를 내장하고 있어 CSS 트리 쉐이킹이 가능한 것이다. 부트스트랩 공식 홈페이지에 가봐도 Unused CSS에 대한 최적화로 PurgeCSS를 안내하고 있는 걸 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;817&quot; data-origin-height=&quot;158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zLxpi/btr8Ndctc31/iMEiEd4ykKlruSHnPokYT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zLxpi/btr8Ndctc31/iMEiEd4ykKlruSHnPokYT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zLxpi/btr8Ndctc31/iMEiEd4ykKlruSHnPokYT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzLxpi%2Fbtr8Ndctc31%2FiMEiEd4ykKlruSHnPokYT0%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; alt=&quot;PurgeCSS&quot; loading=&quot;lazy&quot; width=&quot;817&quot; height=&quot;158&quot; data-origin-width=&quot;817&quot; data-origin-height=&quot;158&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 다양한 웹 플러그인과 함께 쓰일수 있으니, 확장성도 좋다고 볼 수 있다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;296&quot; data-origin-height=&quot;507&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjlhhd/btr8MqJP9O1/ngffMzcxK4a6841ty2ssO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjlhhd/btr8MqJP9O1/ngffMzcxK4a6841ty2ssO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjlhhd/btr8MqJP9O1/ngffMzcxK4a6841ty2ssO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbjlhhd%2Fbtr8MqJP9O1%2FngffMzcxK4a6841ty2ssO0%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; alt=&quot;PurgeCSS&quot; loading=&quot;lazy&quot; width=&quot;296&quot; height=&quot;507&quot; data-origin-width=&quot;296&quot; data-origin-height=&quot;507&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;PurgeCSS CLI 사용법&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 포스팅에서는 아주 기초적인 커맨드 라인 사용법만을 소개한다. 만일 React나 Webpack과 같은 플러그인과 혼합 사용법을 알고 싶다면 위의 가이드 링크에서 참고하길 바란다.&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: #ef5369;&quot;&gt;1.&lt;/span&gt; PurgeCSS를 프로젝트에 다운로드 한다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1680944194813&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# purgecss는 dev용이니 devDependencies에 설치
&amp;gt; npm i --save-dev purgecss&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;&lt;span style=&quot;color: #ef5369;&quot;&gt;2.&lt;/span&gt; CSS 프레임워크를 적용한 html or js와 css 파일을 프로젝트 폴더에 위치 시킨다&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;287&quot; data-origin-height=&quot;115&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d6sUQz/btr8Jj6mMZn/F7SJuWIXjFZ4mHzsKpUNj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d6sUQz/btr8Jj6mMZn/F7SJuWIXjFZ4mHzsKpUNj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d6sUQz/btr8Jj6mMZn/F7SJuWIXjFZ4mHzsKpUNj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd6sUQz%2Fbtr8Jj6mMZn%2FF7SJuWIXjFZ4mHzsKpUNj1%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; alt=&quot;PurgeCSS&quot; loading=&quot;lazy&quot; width=&quot;287&quot; height=&quot;115&quot; data-origin-width=&quot;287&quot; data-origin-height=&quot;115&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;dist : 최적화된 css 파일을 담을 폴더&lt;/li&gt;
&lt;li&gt;b-boot.css : 부트스트랩 통짜 파일&lt;/li&gt;
&lt;li&gt;index.html : 부트스트랩 CSS 코드가 적용된 웹페이지 문서&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1682312250371&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- 부트스트랩 클래스 코드가 사용된 html 파일 --&amp;gt;
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Bootstrap Sample&amp;lt;/title&amp;gt;
    &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css&quot;&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;div class=&quot;container&quot;&amp;gt;
      &amp;lt;div class=&quot;row&quot;&amp;gt;
        &amp;lt;div class=&quot;col-sm-6&quot;&amp;gt;
          &amp;lt;h2 class=&quot;ps-md-6 m-5&quot;&amp;gt;Column 1&amp;lt;/h2&amp;gt;
          &amp;lt;p&amp;gt;Some text..&amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class=&quot;col-sm-6&quot;&amp;gt;
          &amp;lt;h2&amp;gt;Column 2&amp;lt;/h2&amp;gt;
          &amp;lt;p&amp;gt;Some text..&amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&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;&lt;span style=&quot;color: #ef5369;&quot;&gt;3.&lt;/span&gt; 아래 CLI 명령어를 실행한다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1680945160456&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt; npx purgecss --css [부트스트랩 파일] --content [html or js 파일] --output [결과물을 저장할 폴더]

&amp;gt; npx purgecss --css bootstrap/b-boot.css --content bootstrap/index.html --output bootstrap/dist&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1680945263471&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Options:
  -V, --version                        버전 번호를 출력합니다
  -con, --content &amp;lt;files...&amp;gt;           콘텐츠 파일의 글로브 패턴
  -css, --css &amp;lt;files...&amp;gt;               CSS 파일의 글로브 패턴
  -c, --config &amp;lt;path&amp;gt;                  설정 파일의 경로
  -o, --output &amp;lt;path&amp;gt;                  정리된 CSS 파일을 쓸 파일 경로 또는 디렉토리
  -font, --font-face                   사용하지 않는 폰트 페이스를 제거하는 옵션
  -keyframes, --keyframes              사용하지 않는 키프레임을 제거하는 옵션
  -v, --variables                      사용하지 않는 변수를 제거하는 옵션
  -rejected, --rejected                제거된 선택자를 출력하는 옵션
  -rejected-css, --rejected-css        제거된 CSS를 출력하는 옵션
  -s, --safelist &amp;lt;list...&amp;gt;             제거되지 않아야 할 클래스의 목록
  -b, --blocklist &amp;lt;list...&amp;gt;            제거되어야 할 선택자의 목록
  -k, --skippedContentGlobs &amp;lt;list...&amp;gt;  스캔되지 않아야 할 폴더/파일의 글로브 패턴 목록
  -h, --help                           명령어에 대한 도움말을 표시합니다&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;&lt;span style=&quot;color: #ef5369;&quot;&gt;4.&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;CSS 최적화된 결과물이 나오면 원본 파일 대신 프로젝트에 적용하면 된다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;293&quot; data-origin-height=&quot;137&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vobIZ/btr8XzeJkpP/QAHT5ri9SgSb4FnJH73Pw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vobIZ/btr8XzeJkpP/QAHT5ri9SgSb4FnJH73Pw1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vobIZ/btr8XzeJkpP/QAHT5ri9SgSb4FnJH73Pw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvobIZ%2Fbtr8XzeJkpP%2FQAHT5ri9SgSb4FnJH73Pw1%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; alt=&quot;PurgeCSS&quot; loading=&quot;lazy&quot; width=&quot;293&quot; height=&quot;137&quot; data-origin-width=&quot;293&quot; data-origin-height=&quot;137&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Style Sheet/Bootstrap5</category>
      <category>css 용량</category>
      <category>css 줄이기</category>
      <category>CSS 최적화</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1108</guid>
      <comments>https://inpa.tistory.com/entry/%F0%9F%94%AE-PurgeCSS#entry1108comment</comments>
      <pubDate>Sat, 22 Apr 2023 11:55:05 +0900</pubDate>
    </item>
    <item>
      <title>  관계 대수 &amp;amp; 관계 해석 표현법   총정리</title>
      <link>https://inpa.tistory.com/entry/DB-%F0%9F%93%9A-%EA%B4%80%EA%B3%84-%EB%8C%80%EC%88%98-%EA%B4%80%EA%B3%84-%ED%95%B4%EC%84%9D-SQL-%F0%9F%95%B5%EF%B8%8F-%EC%A0%95%EB%A6%AC</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;What-is-a-relational-database.webp&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;560&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uJSLz/btsbn4dbvlP/oQSig2GsnK1YwVyzSZfOz1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uJSLz/btsbn4dbvlP/oQSig2GsnK1YwVyzSZfOz1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uJSLz/btsbn4dbvlP/oQSig2GsnK1YwVyzSZfOz1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuJSLz%2Fbtsbn4dbvlP%2FoQSig2GsnK1YwVyzSZfOz1%2Fimg.webp&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; alt=&quot;관계대수-관계해석&quot; loading=&quot;lazy&quot; width=&quot;900&quot; height=&quot;560&quot; data-filename=&quot;What-is-a-relational-database.webp&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;560&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;관계 대수 (Relation Algebra)&lt;/b&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-origin-width=&quot;766&quot; data-origin-height=&quot;275&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yNWPj/btsbmcInTud/diO8VMOuHQmsYs62S1lQ61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yNWPj/btsbmcInTud/diO8VMOuHQmsYs62S1lQ61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yNWPj/btsbmcInTud/diO8VMOuHQmsYs62S1lQ61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyNWPj%2FbtsbmcInTud%2FdiO8VMOuHQmsYs62S1lQ61%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; alt=&quot;Relation Algebra&quot; loading=&quot;lazy&quot; width=&quot;766&quot; height=&quot;275&quot; data-origin-width=&quot;766&quot; data-origin-height=&quot;275&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;쉽게 생각해 우리가 숫자를 더하거나 나누어 원하는 수를 도출하듯이, 릴레이션을 관계 대수라는 전용 연산자를 통해 더하거나 곱해 원하는 릴레이션을 도출하는 것으로 생각하면 된다.&lt;/blockquote&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;연산자들은 릴레이션의 특성에 따라 일반 집합 연산과 순수 관계 연산으로 나뉘며, 각각의 연산 결과는 또 다른 관계 대수식의 입력으로 사용될 수 있다.&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;678&quot; data-origin-height=&quot;381&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Vyumb/btsbmc9thRN/l4rwcHNo8Emhdkd4Ymksr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Vyumb/btsbmc9thRN/l4rwcHNo8Emhdkd4Ymksr0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Vyumb/btsbmc9thRN/l4rwcHNo8Emhdkd4Ymksr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVyumb%2Fbtsbmc9thRN%2Fl4rwcHNo8Emhdkd4Ymksr0%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; alt=&quot;Relation Algebra&quot; loading=&quot;lazy&quot; width=&quot;678&quot; height=&quot;381&quot; data-origin-width=&quot;678&quot; data-origin-height=&quot;381&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 관계 대수식은 사용자가 원하는 데이터를 얻기 위한 절차를 시스템에 명세하는 데이터 언어로서 관계 데이터 모델에서의 릴레이션을 조작하기 위한 기본 연산 중 하나이다. 그래서 관계 대수는 상용 관계 DBMS들에서 널리 사용되는 SQL의 이론적인 기초이기도 하다. 또한 SQL을 구현하고 최적화하기 위해 DBMS의 내부 언어로서도 사용된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;관계 대수의 8대 연산자&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8대 관계 연산자란, 관계형 데이터베이스에서 자주 사용되는 8가지 기본적인 연산자들을 의미한다. 이러한 연산자들은 데이터베이스의 기초적인 연산들이며, 관계형 데이터베이스에서 매우 중요한 개념이다. 관계 연산자들을 잘 이해하고 사용함으로써, 데이터베이스의 질의를 보다 효율적으로 수행할 수 있기 때문이다.&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;합집합 : union 겹치는 걸 제외하고 테이블 합&lt;/li&gt;
&lt;li&gt;교집합 : 겹치는 것만 테이블&lt;/li&gt;
&lt;li&gt;차집합 : A - B 한 결과 테이블&lt;/li&gt;
&lt;li&gt;카티션 곱 : 나올수 있는 조합 경우의 수&lt;/li&gt;
&lt;li&gt;조인 : 결합&lt;/li&gt;
&lt;li&gt;디비전 : 분할&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;709&quot; data-origin-height=&quot;724&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pfDIi/btsbmgqwOXA/GzaXwgAHkRzKqRiJbRQCfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pfDIi/btsbmgqwOXA/GzaXwgAHkRzKqRiJbRQCfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pfDIi/btsbmgqwOXA/GzaXwgAHkRzKqRiJbRQCfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpfDIi%2FbtsbmgqwOXA%2FGzaXwgAHkRzKqRiJbRQCfk%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; alt=&quot;관계대수-관계해석&quot; loading=&quot;lazy&quot; width=&quot;709&quot; height=&quot;724&quot; data-origin-width=&quot;709&quot; data-origin-height=&quot;724&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;셀렉션 연산자 &lt;span style=&quot;color: #006dd7;&quot;&gt;&amp;sigma;&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;135&quot; data-origin-height=&quot;173&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXTolb/btsbmXef0GK/9r19xt2OpLRDK2KfZqWl9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXTolb/btsbmXef0GK/9r19xt2OpLRDK2KfZqWl9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXTolb/btsbmXef0GK/9r19xt2OpLRDK2KfZqWl9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXTolb%2FbtsbmXef0GK%2F9r19xt2OpLRDK2KfZqWl9K%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; alt=&quot;셀렉션&quot; loading=&quot;lazy&quot; width=&quot;135&quot; height=&quot;173&quot; data-origin-width=&quot;135&quot; data-origin-height=&quot;173&quot;/&gt;&lt;/span&gt;&lt;/figure&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;/li&gt;
&lt;li&gt;&amp;sigma; (sigma)로 연산자를 표현&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;&amp;sigma; DNO=3 (EMPLOYEE)&lt;/s&gt; : EMPLOYEE 테이블에서 DNO가 3인 행을 도출&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img_-_2023-04-20T124807_246-ONlb0SfG4-transformed.png&quot; data-origin-width=&quot;1238&quot; data-origin-height=&quot;714&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bY1wbj/btsblrglkiM/kpUR8npngE2Ajt3zBxdxEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bY1wbj/btsblrglkiM/kpUR8npngE2Ajt3zBxdxEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bY1wbj/btsblrglkiM/kpUR8npngE2Ajt3zBxdxEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbY1wbj%2FbtsblrglkiM%2FkpUR8npngE2Ajt3zBxdxEk%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; alt=&quot;셀렉션&quot; loading=&quot;lazy&quot; width=&quot;810&quot; height=&quot;467&quot; data-filename=&quot;img_-_2023-04-20T124807_246-ONlb0SfG4-transformed.png&quot; data-origin-width=&quot;1238&quot; data-origin-height=&quot;714&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;프로젝션 연산자&lt;span style=&quot;color: #006dd7;&quot;&gt; &amp;Pi;&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;137&quot; data-origin-height=&quot;162&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oOb9z/btsblzLI4ub/4zEvJsMUSSJ83LGDeyTSoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oOb9z/btsblzLI4ub/4zEvJsMUSSJ83LGDeyTSoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oOb9z/btsblzLI4ub/4zEvJsMUSSJ83LGDeyTSoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoOb9z%2FbtsblzLI4ub%2F4zEvJsMUSSJ83LGDeyTSoK%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; alt=&quot;프로젝션&quot; loading=&quot;lazy&quot; width=&quot;137&quot; height=&quot;162&quot; data-origin-width=&quot;137&quot; data-origin-height=&quot;162&quot;/&gt;&lt;/span&gt;&lt;/figure&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;/li&gt;
&lt;li&gt;&amp;Pi; (pi)로 연산자를 표현&lt;/li&gt;
&lt;li&gt;보통 셀렉션의 결과 릴레이션에는 중복 튜플이 존재할 수 없지만, 프로젝션 연산의 결과 릴레이션에는 중복된 튜플들이 존재할 수 있다. 따라서 도출된 릴레이션에 중복이 들어있다면 이 중복은 자동으로 제거된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;&amp;Pi; TITLE(&lt;s&gt;EMPLOYEE&lt;/s&gt;)&lt;/s&gt; : EMPLOYEE 테이블에서 TITLE 열을 도출&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;993&quot; data-origin-height=&quot;624&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0H3DR/btsbmXk26C9/BtoDd61zpa14RrmnzRcg31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0H3DR/btsbmXk26C9/BtoDd61zpa14RrmnzRcg31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0H3DR/btsbmXk26C9/BtoDd61zpa14RrmnzRcg31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0H3DR%2FbtsbmXk26C9%2FBtoDd61zpa14RrmnzRcg31%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; alt=&quot;프로젝션&quot; loading=&quot;lazy&quot; width=&quot;769&quot; height=&quot;483&quot; data-origin-width=&quot;993&quot; data-origin-height=&quot;624&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;291&quot; data-origin-height=&quot;692&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1B1b8/btsbkFFP7gM/ESLw1Vc8ejpkP7pquFskQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1B1b8/btsbkFFP7gM/ESLw1Vc8ejpkP7pquFskQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1B1b8/btsbkFFP7gM/ESLw1Vc8ejpkP7pquFskQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1B1b8%2FbtsbkFFP7gM%2FESLw1Vc8ejpkP7pquFskQ1%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; alt=&quot;프로젝션&quot; loading=&quot;lazy&quot; width=&quot;291&quot; height=&quot;692&quot; data-origin-width=&quot;291&quot; data-origin-height=&quot;692&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;합집합 연산자 &lt;span style=&quot;color: #006dd7;&quot;&gt;&amp;cup;&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;432&quot; data-origin-height=&quot;203&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1KrjJ/btsbtdNYLEX/SYFpKCruiOoufn2HbIS1RK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1KrjJ/btsbtdNYLEX/SYFpKCruiOoufn2HbIS1RK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1KrjJ/btsbtdNYLEX/SYFpKCruiOoufn2HbIS1RK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1KrjJ%2FbtsbtdNYLEX%2FSYFpKCruiOoufn2HbIS1RK%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; alt=&quot;합집합&quot; loading=&quot;lazy&quot; width=&quot;432&quot; height=&quot;203&quot; data-origin-width=&quot;432&quot; data-origin-height=&quot;203&quot;/&gt;&lt;/span&gt;&lt;/figure&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;&amp;cup; 로 연산자를 표현&lt;/li&gt;
&lt;li&gt;합집합은 &lt;b&gt;합집합 호환 조건&lt;/b&gt;이 맞아야만 실행할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;768&quot; data-origin-height=&quot;613&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5g2ZP/btsbmaydh3b/gVMXZCUwrFykreMIsk56S0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5g2ZP/btsbmaydh3b/gVMXZCUwrFykreMIsk56S0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5g2ZP/btsbmaydh3b/gVMXZCUwrFykreMIsk56S0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5g2ZP%2Fbtsbmaydh3b%2FgVMXZCUwrFykreMIsk56S0%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; alt=&quot;합집합&quot; loading=&quot;lazy&quot; width=&quot;768&quot; height=&quot;613&quot; data-origin-width=&quot;768&quot; data-origin-height=&quot;613&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;합집합 호환 (union compatible)&lt;/b&gt;&lt;br /&gt;서로 다른 테이블에 union 하는데 있어, 갯수가 다르거나 도메인이 다르면 안된다. 이 규칙은 합집합, 차집합, 교집합에 모두 적용된다.&lt;br /&gt;예를들어, 아래의 EMPLOYEE 릴레이션과 DEPARTMENT 릴레이션은 기본적으로 어트리뷰트 수가 다르므로 합집합 호환 조건에 맞지 않는다고 생각할 수 있다.&lt;br /&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;367&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDGOXE/btsbmQlQeoc/Y4c0BTnVBlkYie89ldPh31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDGOXE/btsbmQlQeoc/Y4c0BTnVBlkYie89ldPh31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDGOXE/btsbmQlQeoc/Y4c0BTnVBlkYie89ldPh31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDGOXE%2FbtsbmQlQeoc%2FY4c0BTnVBlkYie89ldPh31%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; alt=&quot;union compatible&quot; loading=&quot;lazy&quot; width=&quot;576&quot; height=&quot;311&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;367&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;그러나 EMPLOYEE 릴레이션에서 DNO를 프로젝션한 결과 릴레이션과 DEPARTMENT 관계에서 DEPTNO를 프로젝션한 결과 릴레이션은 애트리뷰트수가 같으며&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;DEPTNO와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;DNO 도메인이 같기 때문에, 따라서 두 릴레이션은 결과적으로 합집합 호환 조건이 부합된다.&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&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;859&quot; data-origin-height=&quot;597&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sy8KS/btsbmf0Czgy/sAlsGWaXAimTzxOKcaikRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sy8KS/btsbmf0Czgy/sAlsGWaXAimTzxOKcaikRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sy8KS/btsbmf0Czgy/sAlsGWaXAimTzxOKcaikRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fsy8KS%2Fbtsbmf0Czgy%2FsAlsGWaXAimTzxOKcaikRK%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; alt=&quot;합집합&quot; loading=&quot;lazy&quot; width=&quot;859&quot; height=&quot;597&quot; data-origin-width=&quot;859&quot; data-origin-height=&quot;597&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;572&quot; data-origin-height=&quot;567&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4GWNR/btsboX5Y6jF/OxMIQwsJGsXYaDA0EMCfHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4GWNR/btsboX5Y6jF/OxMIQwsJGsXYaDA0EMCfHK/img.png&quot; data-alt=&quot;김창섭이 속한 부서의 부서번호 + 개발 부서의 부서번호&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4GWNR/btsboX5Y6jF/OxMIQwsJGsXYaDA0EMCfHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4GWNR%2FbtsboX5Y6jF%2FOxMIQwsJGsXYaDA0EMCfHK%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; alt=&quot;합집합&quot; loading=&quot;lazy&quot; width=&quot;572&quot; height=&quot;567&quot; data-origin-width=&quot;572&quot; data-origin-height=&quot;567&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;김창섭이 속한 부서의 부서번호 + 개발 부서의 부서번호&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;교집합 연산자&lt;span style=&quot;color: #006dd7;&quot;&gt; &amp;cap;&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;399&quot; data-origin-height=&quot;200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKQl6J/btsbmfGpBWB/GUC6QayBZPzX3dMKic8wN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKQl6J/btsbmfGpBWB/GUC6QayBZPzX3dMKic8wN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKQl6J/btsbmfGpBWB/GUC6QayBZPzX3dMKic8wN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKQl6J%2FbtsbmfGpBWB%2FGUC6QayBZPzX3dMKic8wN1%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; alt=&quot;교집합&quot; loading=&quot;lazy&quot; width=&quot;399&quot; height=&quot;200&quot; data-origin-width=&quot;399&quot; data-origin-height=&quot;200&quot;/&gt;&lt;/span&gt;&lt;/figure&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;&amp;cap;로 연산자를 표현&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;956&quot; data-origin-height=&quot;566&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Li9UY/btsbqr6Q06L/rNC6H892WzzrKJTWnqUwZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Li9UY/btsbqr6Q06L/rNC6H892WzzrKJTWnqUwZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Li9UY/btsbqr6Q06L/rNC6H892WzzrKJTWnqUwZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLi9UY%2Fbtsbqr6Q06L%2FrNC6H892WzzrKJTWnqUwZK%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; alt=&quot;교집합&quot; loading=&quot;lazy&quot; width=&quot;808&quot; height=&quot;478&quot; data-origin-width=&quot;956&quot; data-origin-height=&quot;566&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;633&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dbe2bX/btsboWFVdmR/IVPbASUoJDeDju7zp3GkaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dbe2bX/btsboWFVdmR/IVPbASUoJDeDju7zp3GkaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dbe2bX/btsboWFVdmR/IVPbASUoJDeDju7zp3GkaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdbe2bX%2FbtsboWFVdmR%2FIVPbASUoJDeDju7zp3GkaK%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; alt=&quot;교집합&quot; loading=&quot;lazy&quot; width=&quot;854&quot; height=&quot;633&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;633&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;567&quot; data-origin-height=&quot;192&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqKUzu/btsbmQlS29I/r0zjltwoD4WoxEFSgfxp9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqKUzu/btsbmQlS29I/r0zjltwoD4WoxEFSgfxp9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqKUzu/btsbmQlS29I/r0zjltwoD4WoxEFSgfxp9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqKUzu%2FbtsbmQlS29I%2Fr0zjltwoD4WoxEFSgfxp9k%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; alt=&quot;교집합&quot; loading=&quot;lazy&quot; width=&quot;567&quot; height=&quot;192&quot; data-origin-width=&quot;567&quot; data-origin-height=&quot;192&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;577&quot; data-origin-height=&quot;304&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brGZpR/btsboW0klkz/S3gesn2r72OCVKAKWuLCHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brGZpR/btsboW0klkz/S3gesn2r72OCVKAKWuLCHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brGZpR/btsboW0klkz/S3gesn2r72OCVKAKWuLCHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrGZpR%2FbtsboW0klkz%2FS3gesn2r72OCVKAKWuLCHk%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; alt=&quot;교집합&quot; loading=&quot;lazy&quot; width=&quot;577&quot; height=&quot;304&quot; data-origin-width=&quot;577&quot; data-origin-height=&quot;304&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;차집합 연산자 &lt;span style=&quot;color: #006dd7;&quot;&gt;-&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;409&quot; data-origin-height=&quot;189&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBdsYj/btsbmO2LdSE/SKU3XYYpD6hmoi60Z00pDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBdsYj/btsbmO2LdSE/SKU3XYYpD6hmoi60Z00pDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBdsYj/btsbmO2LdSE/SKU3XYYpD6hmoi60Z00pDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBdsYj%2FbtsbmO2LdSE%2FSKU3XYYpD6hmoi60Z00pDk%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; alt=&quot;차집합&quot; loading=&quot;lazy&quot; width=&quot;409&quot; height=&quot;189&quot; data-origin-width=&quot;409&quot; data-origin-height=&quot;189&quot;/&gt;&lt;/span&gt;&lt;/figure&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;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;613&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crguQk/btsbk1hv8Im/99hKobqhfTLrik4CRvuHVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crguQk/btsbk1hv8Im/99hKobqhfTLrik4CRvuHVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crguQk/btsbk1hv8Im/99hKobqhfTLrik4CRvuHVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrguQk%2Fbtsbk1hv8Im%2F99hKobqhfTLrik4CRvuHVK%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; alt=&quot;차집합&quot; loading=&quot;lazy&quot; width=&quot;790&quot; height=&quot;529&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;613&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;324&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dIOow7/btsbmP8niud/Kl2hHoPis02vyUFlsTbxc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dIOow7/btsbmP8niud/Kl2hHoPis02vyUFlsTbxc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dIOow7/btsbmP8niud/Kl2hHoPis02vyUFlsTbxc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdIOow7%2FbtsbmP8niud%2FKl2hHoPis02vyUFlsTbxc1%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; alt=&quot;차집합&quot; loading=&quot;lazy&quot; width=&quot;790&quot; height=&quot;279&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;324&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;858&quot; data-origin-height=&quot;428&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dq4B3z/btsbmeOjBIR/jwO2pxTmcrxKsTiBiFFeBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dq4B3z/btsbmeOjBIR/jwO2pxTmcrxKsTiBiFFeBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dq4B3z/btsbmeOjBIR/jwO2pxTmcrxKsTiBiFFeBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdq4B3z%2FbtsbmeOjBIR%2FjwO2pxTmcrxKsTiBiFFeBk%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; alt=&quot;차집합&quot; loading=&quot;lazy&quot; width=&quot;858&quot; height=&quot;428&quot; data-origin-width=&quot;858&quot; data-origin-height=&quot;428&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;다운로드 (20).png&quot; data-origin-width=&quot;258&quot; data-origin-height=&quot;186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGPcLJ/btsbk2HuU1J/3AeYsjjP2g8OdUhPh4Xjq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGPcLJ/btsbk2HuU1J/3AeYsjjP2g8OdUhPh4Xjq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGPcLJ/btsbk2HuU1J/3AeYsjjP2g8OdUhPh4Xjq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGPcLJ%2Fbtsbk2HuU1J%2F3AeYsjjP2g8OdUhPh4Xjq1%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; alt=&quot;차집합&quot; loading=&quot;lazy&quot; width=&quot;246&quot; height=&quot;177&quot; data-filename=&quot;다운로드 (20).png&quot; data-origin-width=&quot;258&quot; data-origin-height=&quot;186&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;695&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXcs9b/btsbmb4W60C/O1OlzXx8cQ1s1ebWejF9E1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXcs9b/btsbmb4W60C/O1OlzXx8cQ1s1ebWejF9E1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXcs9b/btsbmb4W60C/O1OlzXx8cQ1s1ebWejF9E1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXcs9b%2Fbtsbmb4W60C%2FO1OlzXx8cQ1s1ebWejF9E1%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; alt=&quot;차집합&quot; loading=&quot;lazy&quot; width=&quot;755&quot; height=&quot;695&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;695&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;카티션 곱 연산자&lt;span style=&quot;color: #006dd7;&quot;&gt; &amp;times;&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;260&quot; data-origin-height=&quot;281&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d4ZAHT/btsbmTXiIOG/Go6wtlbSqgPztkceGc8dMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d4ZAHT/btsbmTXiIOG/Go6wtlbSqgPztkceGc8dMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d4ZAHT/btsbmTXiIOG/Go6wtlbSqgPztkceGc8dMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd4ZAHT%2FbtsbmTXiIOG%2FGo6wtlbSqgPztkceGc8dMK%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; alt=&quot;카티션 곱&quot; loading=&quot;lazy&quot; width=&quot;199&quot; height=&quot;215&quot; data-origin-width=&quot;260&quot; data-origin-height=&quot;281&quot;/&gt;&lt;/span&gt;&lt;/figure&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;카디션 곱해서 나오는게 값이 크면 오버헤드가 너무 커지기 때문에 실제로는 카티션 곱 연산자는 사용하지않고 뒤에서 배울 조인(join) 연산자를 사용한다.&lt;/li&gt;
&lt;li&gt;&amp;times;로 연산자를 표현&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;402&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lqQYj/btsbmf7Cg7x/zuKZYzmgBlWjtVq1FmhmC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lqQYj/btsbmf7Cg7x/zuKZYzmgBlWjtVq1FmhmC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lqQYj/btsbmf7Cg7x/zuKZYzmgBlWjtVq1FmhmC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlqQYj%2Fbtsbmf7Cg7x%2FzuKZYzmgBlWjtVq1FmhmC1%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; alt=&quot;카티션 곱&quot; loading=&quot;lazy&quot; width=&quot;798&quot; height=&quot;402&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;402&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;디비전 연산자 &lt;span style=&quot;color: #006dd7; text-align: start;&quot;&gt;&amp;divide;&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;186&quot; data-origin-height=&quot;213&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNNk3B/btsbjiqkgmB/sBSrBma1IoH4E9KEIOGZNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNNk3B/btsbjiqkgmB/sBSrBma1IoH4E9KEIOGZNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNNk3B/btsbjiqkgmB/sBSrBma1IoH4E9KEIOGZNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNNk3B%2FbtsbjiqkgmB%2FsBSrBma1IoH4E9KEIOGZNk%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; alt=&quot;디비전&quot; loading=&quot;lazy&quot; width=&quot;166&quot; height=&quot;190&quot; data-origin-width=&quot;186&quot; data-origin-height=&quot;213&quot;/&gt;&lt;/span&gt;&lt;/figure&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;nbsp;테이블에서&amp;nbsp;다른&amp;nbsp;테이블의&amp;nbsp;모든&amp;nbsp;값을&amp;nbsp;가지고&amp;nbsp;있는&amp;nbsp;행들을&amp;nbsp;찾아주는&amp;nbsp;연산자&lt;/li&gt;
&lt;li&gt;'모든 ~에 대해 ~하는' 형태의 질의에 사용될 수 있음&lt;/li&gt;
&lt;li&gt;디비전은 나누는 테이블의 열의 개수만큼 결과 테이블의 열의 개수가 줄어들게 된다&lt;/li&gt;
&lt;li&gt;&amp;divide;로 연산자를 표현&lt;/li&gt;
&lt;/ul&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/dnOSCo/btsbmeHEgnZ/lEwTFAqPPvUkVqK3b7ALe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dnOSCo/btsbmeHEgnZ/lEwTFAqPPvUkVqK3b7ALe1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;537&quot; data-origin-height=&quot;382&quot; data-filename=&quot;img - 2023-04-20T130057.655.png&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dnOSCo/btsbmeHEgnZ/lEwTFAqPPvUkVqK3b7ALe1/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%2FdnOSCo%2FbtsbmeHEgnZ%2FlEwTFAqPPvUkVqK3b7ALe1%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;537&quot; height=&quot;382&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxrqyV/btsbqBVZ8zH/Nbudj0h27EcEqQq9kyUmYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxrqyV/btsbqBVZ8zH/Nbudj0h27EcEqQq9kyUmYk/img.png&quot; data-origin-width=&quot;537&quot; data-origin-height=&quot;382&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;50&quot; style=&quot;width: 49.4186%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxrqyV/btsbqBVZ8zH/Nbudj0h27EcEqQq9kyUmYk/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%2FbxrqyV%2FbtsbqBVZ8zH%2FNbudj0h27EcEqQq9kyUmYk%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;537&quot; height=&quot;382&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;조인 연산자 &lt;span style=&quot;color: #006dd7;&quot;&gt;⋈&lt;/span&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;span&gt;&amp;nbsp;&lt;/span&gt;연관된 투플들을 결합하는 연산자&lt;/li&gt;
&lt;li&gt;⋈로 연산자를 표현&lt;/li&gt;
&lt;li&gt;조인 연산자는 다음과 같이 연산 방식에 따라 여러 조인으로 나뉜다&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;세타 조인(theta join)&lt;/li&gt;
&lt;li&gt;동등 조인(equi join)&lt;/li&gt;
&lt;li&gt;자연 조인(natural join)&lt;/li&gt;
&lt;li&gt;외부 조인(outer join)&lt;/li&gt;
&lt;li&gt;세미 조인(semi join)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;​&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;세타 조인 &amp;amp; 동등 조인&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;세타 조인은 두 릴레이션에서 공통된 애트리뷰트를 기준으로 비교 연산자(=,&amp;lt;&amp;gt;,&amp;lt;=,&amp;lt;,&amp;gt;=,&amp;gt;)를 사용하여 조건을 만족하는 튜플들을 결합하는 것이다.&lt;/li&gt;
&lt;li&gt;동등 조인은 세타 조인 중에서 비교 연산자가 =인 조인이다. &lt;br /&gt;즉, 두 릴레이션에서 공통된 애트리뷰트의 값이 같은 튜플들을 결합하는 것을 말한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;483&quot; data-origin-height=&quot;60&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GWOzO/btsbzKZIoHi/51oqtD0p6Ccl2jSTU7gK5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GWOzO/btsbzKZIoHi/51oqtD0p6Ccl2jSTU7gK5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GWOzO/btsbzKZIoHi/51oqtD0p6Ccl2jSTU7gK5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGWOzO%2FbtsbzKZIoHi%2F51oqtD0p6Ccl2jSTU7gK5k%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; alt=&quot;join&quot; loading=&quot;lazy&quot; width=&quot;483&quot; height=&quot;60&quot; data-origin-width=&quot;483&quot; data-origin-height=&quot;60&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;622&quot; data-origin-height=&quot;408&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ynTMd/btsbmOvFpKt/bKkrGIKRPonIUMhHkH7p8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ynTMd/btsbmOvFpKt/bKkrGIKRPonIUMhHkH7p8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ynTMd/btsbmOvFpKt/bKkrGIKRPonIUMhHkH7p8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FynTMd%2FbtsbmOvFpKt%2FbKkrGIKRPonIUMhHkH7p8K%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; alt=&quot;join&quot; loading=&quot;lazy&quot; width=&quot;622&quot; height=&quot;408&quot; data-origin-width=&quot;622&quot; data-origin-height=&quot;408&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;&lt;b&gt;자연 조인&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동등 조인의 결과 릴레이션에서 조인 애트리뷰트를 제외한 조인 (중복 필드 제거)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;430&quot; data-origin-height=&quot;197&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcyNLG/btsbzKyFNGv/D0KkqDeYDtEoVM3t0ohIV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcyNLG/btsbzKyFNGv/D0KkqDeYDtEoVM3t0ohIV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcyNLG/btsbzKyFNGv/D0KkqDeYDtEoVM3t0ohIV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcyNLG%2FbtsbzKyFNGv%2FD0KkqDeYDtEoVM3t0ohIV0%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; alt=&quot;join&quot; loading=&quot;lazy&quot; width=&quot;430&quot; height=&quot;197&quot; data-origin-width=&quot;430&quot; data-origin-height=&quot;197&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;&lt;b&gt;외부 조인&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;널값이 들어 있는 투플들을 다루기 위해서 조인 연산을 확장한 조인&lt;/li&gt;
&lt;li&gt;일반적인 조인은 두 릴레이션에서 대응되는 튜플이 없을 경우, 그 튜플을 결과에 포함시키지 않는다. 하지만 외부 조인은 대응되는 튜플이 없어도 결과에 포함시키고, 상대 릴레이션의 애트리뷰트 값은 null로 채운다.&lt;/li&gt;
&lt;li&gt;외부 조인에는 어느 릴레이션을 기준으로 null로 채우는지에 따라 3가지로 나뉜다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;왼쪽 외부 조인(left outer join)&lt;/li&gt;
&lt;li&gt;오른쪽 외부 조인(right outer join)&lt;/li&gt;
&lt;li&gt;완전 외부 조인(full outer join)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;764&quot; data-origin-height=&quot;152&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l8URz/btsbtfFOL1r/n0jhYBr8uIN8zXniGNZgIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l8URz/btsbtfFOL1r/n0jhYBr8uIN8zXniGNZgIK/img.png&quot; data-alt=&quot;left outer join&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l8URz/btsbtfFOL1r/n0jhYBr8uIN8zXniGNZgIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl8URz%2FbtsbtfFOL1r%2Fn0jhYBr8uIN8zXniGNZgIK%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; alt=&quot;left outer join&quot; loading=&quot;lazy&quot; width=&quot;764&quot; height=&quot;152&quot; data-origin-width=&quot;764&quot; data-origin-height=&quot;152&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;left outer join&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;767&quot; data-origin-height=&quot;253&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brQQWH/btsboVujvhd/cdLrNN4ET8v6j5Ux84to31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brQQWH/btsboVujvhd/cdLrNN4ET8v6j5Ux84to31/img.png&quot; data-alt=&quot;right outer join&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brQQWH/btsboVujvhd/cdLrNN4ET8v6j5Ux84to31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrQQWH%2FbtsboVujvhd%2FcdLrNN4ET8v6j5Ux84to31%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; alt=&quot;right outer join&quot; loading=&quot;lazy&quot; width=&quot;767&quot; height=&quot;253&quot; data-origin-width=&quot;767&quot; data-origin-height=&quot;253&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;right outer join&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c53tjb/btsboXlm5Og/gmNN9YSwen0Rrs31hpfKXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c53tjb/btsboXlm5Og/gmNN9YSwen0Rrs31hpfKXK/img.png&quot; data-alt=&quot;full outer join&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c53tjb/btsboXlm5Og/gmNN9YSwen0Rrs31hpfKXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc53tjb%2FbtsboXlm5Og%2FgmNN9YSwen0Rrs31hpfKXK%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; alt=&quot;full outer join&quot; loading=&quot;lazy&quot; width=&quot;773&quot; height=&quot;232&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;232&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;full outer join&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&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;한 릴레이션에서 특정 속성들의 값들에 대해 총합, 평균, 최대, 최소, 개수 등의 연산을 수행하는 함수&lt;/li&gt;
&lt;li&gt;관계 대수의 표현력을 높이기 위해 새로 추가된 연산자&lt;/li&gt;
&lt;li&gt;집단 함수는 각 그룹에 대해 독립적으로 적용되며, 그룹화 연산자와 함께 사용 된다.&lt;/li&gt;
&lt;li&gt;AVG, SUM, MIN, MAX, COUNT&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img_-_2023-04-20T173704_773-transformed.png&quot; data-origin-width=&quot;1016&quot; data-origin-height=&quot;658&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4lQFL/btsbAN3GbKw/5u5ykcl2T2jxyFtgC9MbBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4lQFL/btsbAN3GbKw/5u5ykcl2T2jxyFtgC9MbBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4lQFL/btsbAN3GbKw/5u5ykcl2T2jxyFtgC9MbBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4lQFL%2FbtsbAN3GbKw%2F5u5ykcl2T2jxyFtgC9MbBK%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; alt=&quot;집단 함수&quot; loading=&quot;lazy&quot; width=&quot;671&quot; height=&quot;435&quot; data-filename=&quot;img_-_2023-04-20T173704_773-transformed.png&quot; data-origin-width=&quot;1016&quot; data-origin-height=&quot;658&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;style3&quot; /&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;한 릴레이션에서 특정 속성들의 값에 따라 튜플들을 여러 그룹으로 분류하고, 각 그룹에 대해 집단 함수를 적용하는 연산자&lt;/li&gt;
&lt;li&gt;그룹화 연산자는 G로 표기됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img_-_2023-04-20T173709_623-transformed.png&quot; data-origin-width=&quot;1122&quot; data-origin-height=&quot;790&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HHMXK/btsbBPfE0Yu/WiQSHW0DYlPXkNR8uyniv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HHMXK/btsbBPfE0Yu/WiQSHW0DYlPXkNR8uyniv1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HHMXK/btsbBPfE0Yu/WiQSHW0DYlPXkNR8uyniv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHHMXK%2FbtsbBPfE0Yu%2FWiQSHW0DYlPXkNR8uyniv1%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; alt=&quot;그룹화&quot; loading=&quot;lazy&quot; width=&quot;692&quot; height=&quot;487&quot; data-filename=&quot;img_-_2023-04-20T173709_623-transformed.png&quot; data-origin-width=&quot;1122&quot; data-origin-height=&quot;790&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;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;관계 대수와 SQL&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관계 대수는 관계 데이터베이스에서 릴레이션을 조작하기 위한 절차적 언어이다. 즉, 원하는 데이터를 얻기 위해 어떤 연산을 어떤 순서로 수행할 것인지를 수학 공식 처럼 명시함으로써 릴레이션을 도출 할 수 있다. 관계 대수에는 일반 집합 연산자와 순수 관계 연산자가 있으며, 이들은 모두 릴레이션을 입력으로 받아 릴레이션을 출력한다. &lt;br /&gt;SQL은 관계 데이터베이스에서 데이터를 정의하고 조작하기 위한 선언적 언어이다. 즉, 원하는 데이터가 무엇인지만 선언하면 데이터베이스 시스템이 알아서 최적의 방법으로 질의를 수행한다. SQL에는 DDL(Data Definition Language), DML(Data Manipulation Language), DCL(Data Control Language) 등의 구성 요소가 있으며, 이들은 모두 릴레이션에 대한 연산을 표현이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;906&quot; data-origin-height=&quot;439&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLSgGT/btsbBUBbLX8/eZBsOtXZ4c58ZQmKZvkpI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLSgGT/btsbBUBbLX8/eZBsOtXZ4c58ZQmKZvkpI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLSgGT/btsbBUBbLX8/eZBsOtXZ4c58ZQmKZvkpI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLSgGT%2FbtsbBUBbLX8%2FeZBsOtXZ4c58ZQmKZvkpI0%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; alt=&quot;SQL&quot; loading=&quot;lazy&quot; width=&quot;906&quot; height=&quot;439&quot; data-origin-width=&quot;906&quot; data-origin-height=&quot;439&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;관계 대수와 SQL은 서로 밀접한 관계에 있다고 볼 수 있다. 애초에 관계 대수의 업그레이드 버전이 SQL 이기 때문이다. SQL은 관계 대수의 한계를 극복하고, 관계 데이터베이스에서 다양한 데이터를 쉽고 강력하게 조작할 수 있도록 해준다. 아주 쉽게 비유하자면, &lt;b&gt;수학 공식과 같은 관계 대수식을 프로그래밍 언어처럼 구성한게 SQL&lt;/b&gt;이라고 보면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 관계 대수의 셀렉트 연산자는 SQL의 WHERE 절과 대응되고, 프로젝트 연산자는 SQL의 SELECT 절과 대응된다. 또한, 관계 대수의 조인 연산자는 SQL의 JOIN 절과 대응되며, 집단 함수와 그룹화 연산자는 SQL의 집합 함수와 GROUP BY 절과 대응된다.&lt;/p&gt;
&lt;pre id=&quot;code_1681980553880&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 셀렉트 연산자: &amp;sigma;&amp;lt;salary &amp;gt; 3000000&amp;gt; (EMP)
SELECT * FROM EMP WHERE salary &amp;gt; 3000000;

-- 프로젝트 연산자: &amp;Pi;&amp;lt;name, dept&amp;gt; (EMP)
SELECT name, dept FROM EMP;
  
-- 합집합 연산자: EMP &amp;cup; MANAGER
SELECT * FROM EMP UNION SELECT * FROM MANAGER;
  
-- 교집합 연산자: EMP &amp;cap; MANAGER
SELECT * FROM EMP INTERSECT SELECT * FROM MANAGER;
  
-- 차집합 연산자: EMP - MANAGER
SELECT * FROM EMP EXCEPT SELECT * FROM MANAGER;
  
-- 카티션 곱 연산자: EMP &amp;times; DEPT
SELECT * FROM EMP CROSS JOIN DEPT;
  
-- 조인 연산자: EMP ⋈&amp;lt;EMP.dept = DEPT.dno&amp;gt; DEPT
SELECT * FROM EMP JOIN DEPT ON EMP.dept = DEPT.dno;
  
-- 디비전 연산자: WORKS &amp;divide; PROJECT
SELECT empno FROM WORKS GROUP BY empno HAVING COUNT(*) = (SELECT COUNT(*) FROM PROJECT);
  
-- 집단 함수와 그룹화 연산자: DNO G AVG_SALARY (EMP)
SELECT DNO, AVG(SALARY) AS AVG_SALARY FROM EMP GROUP BY DNO;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, SQL 언어를 학습하기 앞서 먼저 관계 대수를 이해하면 릴레이션 도출 원리는 거의 같으니 단순히 SQL 문법만 추가로 배우면 이미 데이터베이스 테이블을 다루는데 문제가 없다. 물론 처음부터 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;SQL을 통해 관계 대수의 개념과 원리를 학습할 수도 있다.&lt;span&gt;&amp;nbsp;&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;style1&quot; /&gt;
&lt;h2 id=&quot;SE-685d8c78-07fa-4179-8b41-930b89b7f4b7&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;b&gt;&lt;span&gt; &lt;/span&gt;&lt;/b&gt;관계 해석 (Relation&amp;nbsp;Calculus)&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;span style=&quot;color: #ee2323;&quot;&gt;원하는 데이터가 무엇인지만 선언&lt;/span&gt;하는 이른바 비절차적 언어이다. 즉, 원하는 데이터만 명시하고 &quot;어떻게 질의를 해석하는가&quot;에 대해 언급이 없는 선언만 하는 언어인 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관계 해석은 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;튜플 관계 해석 (Tuple Relational Calculus),&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;도메인 관계 해석 (Domain Relational Calculus) 종류가 있으며, 수학 식 형태로는 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1682040122811&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- EMPLOYEE 릴레이션에서 급여가 3000 이상인 사원들의 이름과 급여를 찾는 관계 해석 식
{&amp;lt;t.Ename, t.Salary&amp;gt; | t &amp;isin; EMPLOYEE &amp;and; t.Salary &amp;ge; 3000}

-- 위의 관계 해석식과 매칭되는 SQL 코드
SELECT Ename, Salary
FROM EMPLOYEE
WHERE Salary &amp;gt;= 3000;&lt;/code&gt;&lt;/pre&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #9b9b9b; color: #ffffff;&quot; colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #9b9b9b; color: #ffffff;&quot; colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;b&gt;구성요소&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #9b9b9b; color: #ffffff;&quot; colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;b&gt;기호&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #9b9b9b; color: #ffffff;&quot; colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot; colspan=&quot;1&quot; rowspan=&quot;3&quot;&gt;연산자&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;OR 연산&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;V&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;원자식 간 &amp;ldquo;또는&amp;rdquo;이라는 관계로 연결&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot; colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;AND 연산&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot; colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&amp;and;&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot; colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;원자식 간 &amp;ldquo;그리고&amp;rdquo;라는 관계로 연결&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot; colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;NOT 연산&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;ㄱ&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;원자식에 대해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;부정&lt;/u&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot; colspan=&quot;1&quot; rowspan=&quot;2&quot;&gt;정량자&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot; colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;전칭 정량자&lt;br /&gt;(Universal Quantifier&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot; colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&amp;forall;&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot; colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;모든 가능한 튜플 &amp;ldquo;For All&amp;rdquo;&lt;br /&gt;# All의 &amp;lsquo;A&amp;rsquo;를 뒤집은 형&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot; colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;존재 정량자&lt;br /&gt;(Existential Quantifier)&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&amp;exist;&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;어떤 튜플 하나라도 존재 &amp;ldquo;There Exists&amp;rdquo;&lt;br /&gt;# Exists의 &amp;lsquo;E&amp;rsquo;를 뒤집은 형&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img_-_2023-04-21T101859_450-transformed.png&quot; data-origin-width=&quot;1204&quot; data-origin-height=&quot;562&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4CDEu/btsbHk0uo0C/SVFUZ1R5klhUTtPGmiKXoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4CDEu/btsbHk0uo0C/SVFUZ1R5klhUTtPGmiKXoK/img.png&quot; data-alt=&quot;관계 대수식과 완전히 다른 관계 해석식을 보니 머리만 아프다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4CDEu/btsbHk0uo0C/SVFUZ1R5klhUTtPGmiKXoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4CDEu%2FbtsbHk0uo0C%2FSVFUZ1R5klhUTtPGmiKXoK%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; alt=&quot;Relation Calculus&quot; loading=&quot;lazy&quot; width=&quot;1204&quot; height=&quot;562&quot; data-filename=&quot;img_-_2023-04-21T101859_450-transformed.png&quot; data-origin-width=&quot;1204&quot; data-origin-height=&quot;562&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;관계 대수식과 완전히 다른 관계 해석식을 보니 머리만 아프다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;관계 대수 &lt;span style=&quot;color: #ef5369;&quot;&gt;vs&lt;/span&gt; 관계 해석&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관계대수는 어떻게 데이터를 처리할지를 효율적으로 정하는 데 사용되며, 관계해석은 어떤 데이터가 필요한지를 더욱 간단하게 정의하는 데 사용된다고 보면 된다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.814%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;관계대수&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 54.0698%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;관계해석&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.814%;&quot;&gt;절차적 언어(순서 명시)&lt;/td&gt;
&lt;td style=&quot;width: 54.0698%;&quot;&gt;비절차적 언어(계산 수식의 유연적 사용),&lt;br /&gt;프레디킷 해석(Predicate Calculus) 기반&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.814%;&quot;&gt;원하는 정보를 얻기 위해 어떻게 (how) 질의를 해석하는지를 기술&lt;/td&gt;
&lt;td style=&quot;width: 54.0698%;&quot;&gt;원하는 정보가 무엇 (what) 인지만 명시하고 어떻게 질의를 해석하는지에 대해 언급하지 않는 선언&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.814%;&quot;&gt;연산자와&amp;nbsp;피연산자로&amp;nbsp;구성되며,&amp;nbsp;연산자는&amp;nbsp;릴레이션에&amp;nbsp;적용되어&amp;nbsp;새로운&amp;nbsp;릴레이션을&amp;nbsp;생성&lt;/td&gt;
&lt;td style=&quot;width: 54.0698%;&quot;&gt;명제와 변수로 구성되며, 명제는 참 또는 거짓의 값을 가지고, 변수는 릴레이션의 튜플이나 애트리뷰트 값을 나타냄&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.814%;&quot;&gt;연산자의 종류에 따라 다양한 데이터 조작을 할 수 있음&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 54.0698%;&quot;&gt;연결자와 한정자를 사용하여 복잡한 조건식을 만들 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 45.814%;&quot;&gt;종류로는 순수관계 연산자, 일반집합 연산자가 있다&lt;/td&gt;
&lt;td style=&quot;width: 54.0698%;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;종류로는&lt;span&gt; &lt;/span&gt;&lt;/span&gt;튜플 관계 해석, 도메인 관계 해석이 있다&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div id=&quot;SE-9bc0774e-3a42-4d66-8253-279d82eb86b0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;관계 해석을 반드시 공부해야 되는가  &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 대학 강의나 데이터베이스 교제를 보면, 관계 대수에 대해 자세히 다루고 바로 SQL로 넘어가지, 관계 해석을 중점적으로 다루지는 않는다. 관계 대수로 릴레이션 조합법 및 도출 원리를 배우고 바로 SQL로 실전 데이터베이스 질의를 습득하는 것 만으로도 실무를 하는데 충분하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애초에 관계 해석은 비절차적인 언어이기 때문에, 컴퓨터가 직접 실행할 수 있는 형태가 아니다. 그래서 관계 해석으로 표현된 질의를 관계 대수로 변환하거나 SQL로 번역해야 하는 추가 작업이 든다. 따라서 데이터베이스 관련 서적에서는 관계 대수에 대해 자세히 다루고 관계 해석은 간단하게 다루는 경우가 많다. 하지만 이것은 관계 해석이 중요하지 않다는 의미가 아니라, 관계 대수가 실제 데이터베이스 시스템에서 더 많이 사용되고 구현하기 쉽기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관계 해석을 공부하면 데이터베이스의 이론적인 배경과 논리적인 구조를 이해할 수 있고, SQL을 보다 효과적으로 작성하고 분석할 수 있다. 또한 관계 해석은 데이터베이스의 의미와 제약 조건을 명확하게 표현할 수 있는 방법이므로, 데이터베이스 설계와 모델링에도 도움이 된다. 실제로 관계 해석은 관계 대수와 동등한 표현력을 가지고 있으며, 논리적인 추론과 수학적인 표현을 사용하여 질의를 정의할 수 있다. 따라서 관계 해석은 데이터베이스에 대한 깊은 지식과 실력을 향상시키는 데 유용하다.&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;시간적인 여유 때문에 현실적으로는 힘든것은 사실이다. 그래서 관계 대수를 우선적으로 공부하고, 관계 해석은 필요한 부분만 공부하는 것도 나쁘지 않다. 중요한 것은 데이터베이스의 이론과 SQL을 통해 데이터베이스 실습을 병행하면서 데이터베이스에 대한 깊은 지식과 실력을 향상시키는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://slidesplayer.org/slide/17323973/&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://velog.io/@ieed0205/%EA%B4%80%EA%B3%84%EB%8C%80%EC%88%98-SQL-LEEToday&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://wook-2124.tistory.com/333&lt;/p&gt;</description>
      <category>DBMS/데이터베이스 이론</category>
      <category>관계 기호</category>
      <category>관계대수</category>
      <category>관계해석</category>
      <category>데이터베이스 기호</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/386</guid>
      <comments>https://inpa.tistory.com/entry/DB-%F0%9F%93%9A-%EA%B4%80%EA%B3%84-%EB%8C%80%EC%88%98-%EA%B4%80%EA%B3%84-%ED%95%B4%EC%84%9D-SQL-%F0%9F%95%B5%EF%B8%8F-%EC%A0%95%EB%A6%AC#entry386comment</comments>
      <pubDate>Fri, 21 Apr 2023 10:41:08 +0900</pubDate>
    </item>
    <item>
      <title>  first-child / first-of-type 속성 차이점</title>
      <link>https://inpa.tistory.com/entry/%F0%9F%8C%9F-first-child-first-of-type</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1444&quot; data-origin-height=&quot;812&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JhfT2/btr0DWphboa/vpSBcQdI1tpdkGKLBjvOKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JhfT2/btr0DWphboa/vpSBcQdI1tpdkGKLBjvOKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JhfT2/btr0DWphboa/vpSBcQdI1tpdkGKLBjvOKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJhfT2%2Fbtr0DWphboa%2FvpSBcQdI1tpdkGKLBjvOKk%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; alt=&quot;first-child-first-of-type&quot; loading=&quot;lazy&quot; width=&quot;1444&quot; height=&quot;812&quot; data-origin-width=&quot;1444&quot; data-origin-height=&quot;812&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;first-child 와 first-of-type 차이점&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;first-child 와 first-of-type은 똑같이 자식 형제 요소 고르는것같은데 무엇이 다를까? 한방에 이해하기 쉽게 간단명료하게 설명하면 이렇다.&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;&lt;b&gt;div &amp;gt; p&lt;span style=&quot;color: #ef5369;&quot;&gt;:first-child&lt;/span&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;'div 하위의 p 요소 중 첫번째 요소' 로 읽는 게 아니라 &lt;span style=&quot;color: #ee2323;&quot;&gt;'div 하위의 첫번째 자식인 요소가 p 요소' &lt;/span&gt;이면이다.&lt;/li&gt;
&lt;li&gt;즉, div의 자손 요소중에 first-child를 고르는데 그게 p이면 스타일을 적용한다.&lt;/li&gt;
&lt;li&gt;만일 p가 없다면 &lt;span style=&quot;color: #ee2323;&quot;&gt;스타일을 적용하지 않는다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;'div first-child is p'&lt;/span&gt; 라고 영어 문장으로 이해하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1677212020734&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.parent &amp;gt; span:first-child {
	background: lightgreen;
}

.parent2 &amp;gt; p:first-child {
	background: lightgreen;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1677211049919&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div class=&quot;parent&quot;&amp;gt;
    &amp;lt;span class=&quot;box box1&quot;&amp;gt;span&amp;lt;/span&amp;gt;
    &amp;lt;p class=&quot;box box1&quot;&amp;gt;p&amp;lt;/p&amp;gt;
    &amp;lt;span class=&quot;box box1&quot;&amp;gt;span&amp;lt;/span&amp;gt;
    &amp;lt;p class=&quot;box box1&quot;&amp;gt;p&amp;lt;/p&amp;gt;
    &amp;lt;span class=&quot;box box1&quot;&amp;gt;span&amp;lt;/span&amp;gt;
    &amp;lt;p class=&quot;box box1&quot;&amp;gt;p&amp;lt;/p&amp;gt;
    &amp;lt;p class=&quot;box box1&quot;&amp;gt;p&amp;lt;/p&amp;gt;
    &amp;lt;p class=&quot;box box1&quot;&amp;gt;p&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;div class=&quot;parent2&quot;&amp;gt;
    &amp;lt;span class=&quot;box box1&quot;&amp;gt;span&amp;lt;/span&amp;gt;
    &amp;lt;p class=&quot;box box1&quot;&amp;gt;p&amp;lt;/p&amp;gt;
    &amp;lt;span class=&quot;box box1&quot;&amp;gt;span&amp;lt;/span&amp;gt;
    &amp;lt;p class=&quot;box box1&quot;&amp;gt;p&amp;lt;/p&amp;gt;
    &amp;lt;span class=&quot;box box1&quot;&amp;gt;span&amp;lt;/span&amp;gt;
    &amp;lt;p class=&quot;box box1&quot;&amp;gt;p&amp;lt;/p&amp;gt;
    &amp;lt;p class=&quot;box box1&quot;&amp;gt;p&amp;lt;/p&amp;gt;
    &amp;lt;p class=&quot;box box1&quot;&amp;gt;p&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 388.1818542480469px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;388.1818542480469&quot; data-default-tab=&quot;result&quot; data-slug-hash=&quot;bGxezej&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/bGxezej&quot;&gt; first-child&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;b&gt;div &amp;gt; p&lt;/b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;:first-of-type&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;/span&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;span style=&quot;color: #ee2323;&quot;&gt; 'div 하위의 p 요소 중(OF) 첫번째 요소'&lt;/span&gt; 라고 읽으면 바로 이해가 될 것이다.&lt;/li&gt;
&lt;li&gt;즉, div의 자손요소중에 p태그를 모두 고르고 그중에 first-of-type만 스타일을 적용한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;'div p first-of-type'&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;라고 영어 문장으로 이해하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1677212015078&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.parent &amp;gt; span:first-of-type {
	background: lightgreen;
}

.parent2 &amp;gt; p:first-of-type {
	background: lightgreen;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1632922530534&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div class=&quot;parent&quot;&amp;gt;
    &amp;lt;span class=&quot;box box1&quot;&amp;gt;span&amp;lt;/span&amp;gt;
    &amp;lt;p class=&quot;box box1&quot;&amp;gt;p&amp;lt;/p&amp;gt;
    &amp;lt;span class=&quot;box box1&quot;&amp;gt;span&amp;lt;/span&amp;gt;
    &amp;lt;p class=&quot;box box1&quot;&amp;gt;p&amp;lt;/p&amp;gt;
    &amp;lt;span class=&quot;box box1&quot;&amp;gt;span&amp;lt;/span&amp;gt;
    &amp;lt;p class=&quot;box box1&quot;&amp;gt;p&amp;lt;/p&amp;gt;
    &amp;lt;p class=&quot;box box1&quot;&amp;gt;p&amp;lt;/p&amp;gt;
    &amp;lt;p class=&quot;box box1&quot;&amp;gt;p&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;div class=&quot;parent2&quot;&amp;gt;
    &amp;lt;span class=&quot;box box1&quot;&amp;gt;span&amp;lt;/span&amp;gt;
    &amp;lt;p class=&quot;box box1&quot;&amp;gt;p&amp;lt;/p&amp;gt;
    &amp;lt;span class=&quot;box box1&quot;&amp;gt;span&amp;lt;/span&amp;gt;
    &amp;lt;p class=&quot;box box1&quot;&amp;gt;p&amp;lt;/p&amp;gt;
    &amp;lt;span class=&quot;box box1&quot;&amp;gt;span&amp;lt;/span&amp;gt;
    &amp;lt;p class=&quot;box box1&quot;&amp;gt;p&amp;lt;/p&amp;gt;
    &amp;lt;p class=&quot;box box1&quot;&amp;gt;p&amp;lt;/p&amp;gt;
    &amp;lt;p class=&quot;box box1&quot;&amp;gt;p&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 384.54547119140625px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;384.54547119140625&quot; data-default-tab=&quot;result&quot; data-slug-hash=&quot;XWPKOMb&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/XWPKOMb&quot;&gt; first-of-type&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;last-child 와 last-of-type 차이점&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;last-child 와 last-of-type 도 똑같은 개념으로 적용된다고 보면된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;b&gt;div &amp;gt; p&lt;span style=&quot;color: #ef5369;&quot;&gt;:&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;last-child&lt;/span&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;div의 자손 요소중에 first-child를 고르는데 그게 p이면 스타일을 적용한다.&lt;/li&gt;
&lt;li&gt;만일 p가 없다면 &lt;span style=&quot;color: #ee2323;&quot;&gt;스타일을 적용하지 않는다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1677212098478&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.parent &amp;gt; span:last-child {
	background: lightgreen;
}

.parent2 &amp;gt; p:last-child {
	background: lightgreen;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 387.272705078125px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;387.272705078125&quot; data-default-tab=&quot;result&quot; data-slug-hash=&quot;wvEWNpQ&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/wvEWNpQ&quot;&gt; Untitled&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;b&gt;div &amp;gt; p&lt;span style=&quot;color: #ef5369;&quot;&gt;:&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;last-of-type&lt;/span&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;div의 자손요소중에 p태그를 모두 고르고 그중에 first-of-type만 스타일을 적용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1677212156970&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.parent &amp;gt; span:last-of-type {
	background: lightgreen;
}

.parent2 &amp;gt; p:last-of-type {
	background: lightgreen;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 380px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;380&quot; data-default-tab=&quot;result&quot; data-slug-hash=&quot;abaZXqm&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/abaZXqm&quot;&gt; Untitled&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Style Sheet/CSS</category>
      <category>first-child</category>
      <category>first-of-type</category>
      <category>last-child</category>
      <category>last-of-type</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1091</guid>
      <comments>https://inpa.tistory.com/entry/%F0%9F%8C%9F-first-child-first-of-type#entry1091comment</comments>
      <pubDate>Thu, 20 Apr 2023 08:17:16 +0900</pubDate>
    </item>
    <item>
      <title>  웹 애니메이션 최적화 requestAnimationFrame 가이드</title>
      <link>https://inpa.tistory.com/entry/%F0%9F%8C%90-requestAnimationFrame-%EA%B0%80%EC%9D%B4%EB%93%9C</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img_20(95).webp&quot; data-origin-width=&quot;1132&quot; data-origin-height=&quot;562&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqsI5M/btr7He38Uax/Pkl2VvwRxx06b1ZXQpBb71/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqsI5M/btr7He38Uax/Pkl2VvwRxx06b1ZXQpBb71/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqsI5M/btr7He38Uax/Pkl2VvwRxx06b1ZXQpBb71/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqsI5M%2Fbtr7He38Uax%2FPkl2VvwRxx06b1ZXQpBb71%2Fimg.webp&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; alt=&quot;requestAnimationFrame&quot; loading=&quot;lazy&quot; width=&quot;1132&quot; height=&quot;562&quot; data-filename=&quot;img_20(95).webp&quot; data-origin-width=&quot;1132&quot; data-origin-height=&quot;562&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&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;웹페이지의 애니메이션을 구현할때 CSS의 &lt;s&gt;animatoin&lt;/s&gt; ,&amp;nbsp;&lt;s&gt;transition&lt;/s&gt; , &lt;s&gt;transform&lt;/s&gt; 속성을 통해 구현할 수도 있지만, 보다 사용자와의 복잡한 상호작용을 구현하게 하기 위해 Javascript와 함께 사용하여 스타일을 변화시키도 한다. 예를들어 특정 영역을 클릭하거나 웹페이지를 스크롤할때 변화무쌍한 애니메이션 작업들이 그러하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 간단하고 규칙적인 애니메이션은 CSS로만 요소의 좌표값이나 스타일 크기를 변화시키고, 세밀한 조작이 필요한 애니메이션은 자바스크립트로 스타일 속성을 변경 시키는 편이다. 하지만 자바스크립트로 스타일 속성을 변화시키는 방법은 CSS보다 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;(특히 모바일에서)&lt;span&gt; &lt;/span&gt;&lt;/span&gt;성능이 좋지 않다. 따라서 어쩔수 없이 자바스크립트와의 상호 협력이 필요할 경우 이를 위한 &lt;b&gt;최적화 기법&lt;/b&gt;이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅은 이러한 애니메이션 관련 최적화 API인 &lt;s&gt;requestAnimationFrame()&lt;/s&gt; 에 대한 사용법과 원리를 소개해보는 시간을 가져 보겠다. 브라우저라는 소프트웨어를 전문적으로 다루는 프론트엔드 개발자일 경우 이에 관한 지식은 거의 필수라고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;브라우저 렌더링 단계&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저가 화면에 무언가를 그리기까지는 아래 사진과 같이 게 3 단계로 나눌 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1093&quot; data-origin-height=&quot;167&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhhdbd/btr5OefC7vh/BFs1Qlz0seo0nVfrk8w4j1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhhdbd/btr5OefC7vh/BFs1Qlz0seo0nVfrk8w4j1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhhdbd/btr5OefC7vh/BFs1Qlz0seo0nVfrk8w4j1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbhhdbd%2Fbtr5OefC7vh%2FBFs1Qlz0seo0nVfrk8w4j1%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; alt=&quot;브라우저 렌더링 단계&quot; loading=&quot;lazy&quot; width=&quot;1093&quot; height=&quot;167&quot; data-origin-width=&quot;1093&quot; data-origin-height=&quot;167&quot;/&gt;&lt;/span&gt;&lt;/figure&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;span style=&quot;color: #f3c000;&quot;&gt;Javascript&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;- 애니메이션 및 기타 작업 스크립트를 수행 (DOM 생성)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;Style&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;- CSS 규칙을 어떤 요소에 적용할지 계산하는 프로세스 (CSSOM 생성)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;Layout&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 브라우저는 DOM과 CSSOM을 결합하여 객체들의 위치와 크기 등을 계산하는 렌더 트리(Render Tree)를 생성.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;Paint(redraw)&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 브라우저는 렌더 트리를 사용하여 실제로 화면에 픽셀을 출력 (객체가&amp;nbsp;실제&amp;nbsp;화면에&amp;nbsp;그려지는&amp;nbsp;것을&amp;nbsp;의미)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;Composite&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 브라우저는&amp;nbsp;화면에&amp;nbsp;출력되는&amp;nbsp;객체들을&amp;nbsp;합성하여&amp;nbsp;최종&amp;nbsp;화면을&amp;nbsp;생성&lt;/li&gt;
&lt;/ul&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;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;게임 모니터를 고르다보면 60hz, 120hz 라고 모니터 주사율을 들어본 적이 있을 것이다. 이는 1초동안 모니터 화면의 출력 빈도를 나타내는 단위 이다. 그리고 보통 hz는 frame과 관련이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 영화나 애니메이션을 보는 것은 사실 짧은 시간 간격에 이어지는 장면을 보는 것이다. 이 각각의 장면을 frame이라고 한다. 즉, 프레임은 한 장의 사진이라 봐도 무방하다. 그리고 특정 시간 내에 보여지는 frame 갯수를 frame rate 혹은 frame per second 줄여서 fps라고 한다. (게임을 해보면 가장 자주 듣는 단위일 것이다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 인간의 눈은 1초에 60번 장면이 넘어가야 부드럽다고 느낀다고 한다. 그래서 현대 기기들은 시각적인 효과를 위해 초당 60번 화면을 다시 그리도록 기본적으로 설계된다. 이를 60fps 혹은 60hz 라고 불리우는 이유이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;296c947cfb40cbf17287c58f19b575b4.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/L5dCY/btr5RhB9uka/wks0Irj8aQ7NdZ2zP7sFV1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/L5dCY/btr5RhB9uka/wks0Irj8aQ7NdZ2zP7sFV1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/L5dCY/btr5RhB9uka/wks0Irj8aQ7NdZ2zP7sFV1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/L5dCY/btr5RhB9uka/wks0Irj8aQ7NdZ2zP7sFV1/img.gif&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; alt=&quot;frame&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;600&quot; data-filename=&quot;296c947cfb40cbf17287c58f19b575b4.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;굳이 FPS를 설명하고 Hz까지 설명하는 이유는 웹 화면에 부드러운 애니메이션 움직임 효과를 주기 위해선 이 프레임 단위에 맞게 설계해야 되기 때문이다. 초당 60개의 프레임을 렌더링 한다는 말은, 16.666 밀리세컨드&lt;span&gt;(1000ms / 60fps)&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 style=&quot;color: #ee2323;&quot;&gt;자바스크립트로 사용자에게 부드러운 애니메이션을 구현하려면&lt;span&gt;&amp;nbsp;&lt;/span&gt;16.6ms밀리초 마다 코드를 호출하는 식으로 구현&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;b&gt;타이머 함수를 이용한 애니메이션 스크립팅&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 자바스크립트로 일정 시간마다 코드를 반복 호출하는 대표적인 방법으로는 타이머 함수인&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;setInterval()&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;setTimeout()&lt;/s&gt;&lt;span&gt; &lt;/span&gt;을 이용해 60프레임에 맞게 스크립팅을 한다면 아래 코드와 같이 구성할 수가 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1679726836462&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const performAnimation = () =&amp;gt; {
  /* 스타일 조정 스크립트 */
}

// 1초에 60번 무한 반복
setInterval(performAnimation, 1000 / 60)&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1679726842008&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const performAnimation = () =&amp;gt; {
  /* 스타일 조정 스크립트 */
  
  setTimeout(performAnimation, 1000 / 60); // 함수 내부에서 다시 setTimeout을 호출하여 반복
}

setTimeout(performAnimation, 1000 / 60);&lt;/code&gt;&lt;/pre&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;타이머 함수의 문제점&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 &lt;s&gt;setInterval()&lt;/s&gt;&amp;nbsp;와&amp;nbsp;&lt;s&gt;setTimeout()&lt;/s&gt; 의 문제점은 주어진 시간내에 동작을 할 뿐 &lt;span style=&quot;color: #ee2323;&quot;&gt;프레임을 신경 쓰지 않고 동작&lt;/span&gt;한다는 점이다. 타이머 함수는 프레임 단위로 프레임 시작 시간에 맞춰 실행됨을 보장하지 못하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만일 아래 그림처럼 약 16ms 간격으로 프레임 단위가 진행되어야 하는데, 브라우저가 다른 작업 수행으로 인해 지연되어 자바스크립트의 콜백 코드 부분이 단위 중간에서 호출되었다고 하자.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;579&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NirJZ/btr5TqFm0j7/4WOfzDRmHzl7MPxtXLvCj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NirJZ/btr5TqFm0j7/4WOfzDRmHzl7MPxtXLvCj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NirJZ/btr5TqFm0j7/4WOfzDRmHzl7MPxtXLvCj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNirJZ%2Fbtr5TqFm0j7%2F4WOfzDRmHzl7MPxtXLvCj0%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; alt=&quot;setInterval&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;579&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;579&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트 실행에 의해 리플로우가 일어나 위에서 본 브라우저 렌더링 단계인 레이아웃 - 페인트 - 합성 과정이 다시 일어나게 되는데, 그러면 프레임이 생성되지 못하고 누락되어 버려 1 프레임이 깎여버리는 현상이 나타나게 된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이러한 현상이 일어날 수 있는 이유는 자바스크립트의 콜 스택(call stack)은 싱글 스레드 이기 때문이다&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프레임이 깎인다는 것은 곧 프레임 드랍이 일어나 결국 화면이 버벅이게 된다. 이러한 지연(delay) 발생 문제 때문에 대안으로 탄생한 것이 바로 rAF(requestAnimationFrame) 이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;requestAnimationFrame&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;requestAnimationFrame 함수는 시스템이 프레임을 그릴 준비가 되면 애니메이션 프레임을 호출하여 애니메이션 웹페이지를 보다 원활하고 효율적으로 생성할 수 있도록 해준다. 실제 화면이 갱신되어 표시되는 주기에 따라 함수를 호출해주기 때문에 자바스크립트가 &lt;b&gt;프레임 시작 시 실행되도록 보장&lt;/b&gt;해주어 위와 같은 밀림 현상을 방지해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;아래 이미지는&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;requestAnimationFrame 를 통해&lt;span style=&quot;text-align: start;&quot;&gt; 애니메이션을 구현할 때, 각 프레임이 브라우저의 프레임 주기에 맞추어 일정한 시간 간격으로 렌더링됨을 보여준다. &lt;span style=&quot;text-align: start;&quot;&gt;requestAnimationFrame&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;함수가 실행되면 브라우저는 다음 프레임이 그려지기 전에 함수를 실행하도록 예약하기 때문에 &lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;각 프레임이 정확히 16.6ms 간격으로 렌더링되게 되게 된다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bu29fd/btr5ZkEOmiG/hrw8Yu25PHeUVCNLK9hbK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bu29fd/btr5ZkEOmiG/hrw8Yu25PHeUVCNLK9hbK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bu29fd/btr5ZkEOmiG/hrw8Yu25PHeUVCNLK9hbK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbu29fd%2Fbtr5ZkEOmiG%2Fhrw8Yu25PHeUVCNLK9hbK0%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; alt=&quot;requestAnimationFrame&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;416&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;416&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-default-tab=&quot;result&quot; data-slug-hash=&quot;XWPOVZB&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/XWPOVZB&quot;&gt; setInterval vs requestAnimationFrame&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼&amp;nbsp;애니메이션 프레임을 페인팅할 준비가 됐을 때 호출한다는 부분은, 이전 setInterval 방식이 준비가 되지 않아도 페인팅을 요청하는 것과 대비되면서, 보다 웹브라우저에 최적화 되어 있다고 말할 수 있다. 또한&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;&lt;b&gt;requestAnimationFrame 장점&lt;/b&gt;&lt;/h3&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;백그라운드 동작 중지&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setInterval 같은 경우 브라우저의 다른 탭 화면을 보거나 브라우저가 최소화되어 있을 때 계속 타이머가 돌아 콜백을 호출하기 때문에 시스템 리소스 낭비를 초래하고 불필요한 전력을 소모하게 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 requestAnimationFrame는 페이지가 비활성화 된 상태이면 페이지 화면 그리기 작업도 브라우저에 의해 일시 중지됨으로 CPU 리소스나 배터리 수명을 낭비하지 않게 된다.&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;&lt;/b&gt;&lt;b&gt;디스플레이 주사율에 맞게 호출 횟수&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;이 특징은 최적화된 웹 애니메이션을 구현하는데 있어 setInterval 과 requestAnimationFrame 의 결정적인 차이점이라고 말할 수 있다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;지금까지 1초에 60번 호출한다고 써왔지만, 정확히 말하자면 &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;자신의 모니터가 60hz 주사율 모니터일 경우이다. 즉, 웹브라우저는 &lt;b&gt;디스플레이의 주사율을 따르며&lt;/b&gt; 만일 144hz 주사율 고사양 모니터일 경우 rAF 역시 1초에 144번 호출되게 된다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;정리하자면&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt; setInterval 에서는 콜백 함수 호출 간격을 시간을 지정해주고 호출 횟수를 설정하지만, rAF에서는 모니터의 주사율을 그대로 따르게 되어 최적화 되어 있다는 것이다. 그리고 이러한 특성 때문에 rAF는 스크롤 이벤트 최적화 기법으로도 쓰이기도 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;854&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgNtM4/btr7hvGBXFk/rzv2FnY9bpkFX1alUJEfDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgNtM4/btr7hvGBXFk/rzv2FnY9bpkFX1alUJEfDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgNtM4/btr7hvGBXFk/rzv2FnY9bpkFX1alUJEfDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgNtM4%2Fbtr7hvGBXFk%2Frzv2FnY9bpkFX1alUJEfDk%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; alt=&quot;requestAnimationFrame&quot; loading=&quot;lazy&quot; width=&quot;655&quot; height=&quot;579&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;854&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;&lt;b&gt;Animation frames &lt;/b&gt;&lt;b&gt;큐에서 처리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;requestAnimationFrame(rAF)&lt;span&gt; 함수도 setTimeout 이나 여타 이벤트 핸들러와 같이 &lt;/span&gt;&lt;/span&gt;&quot;애니메이션 프레임&quot;을 그리기 위한 콜백 함수를 등록하고 비동기 task로 분류하여 처리된다. 이때 중요한 특징은 rAF는&lt;span&gt; 일반적인 task&lt;/span&gt;&amp;nbsp;queue가 아니라&lt;span&gt;&amp;nbsp;&lt;/span&gt;animation frame이라는 별개의 queue에서 처리된다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Animation frames 은 브라우저의 렌더링 엔진이 다음 프레임을 그리기 전에 실행해야 하는 rAF에 등록한 콜백 함수들을 담는 큐이다. 별도의 큐에서 적재되어 이벤트 루프에 의해 꺼내지기 때문에 실행이 뒤쳐지거나 하는 현상을 감소할수가 있다. 단, requestAnimationFrame도 브라우저의 CPU나 GPU 사용량 여부 등에 따라 콜백 함수 실행이 밀릴 수도 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;scroll-opt-js-queue.png&quot; data-origin-width=&quot;1073&quot; data-origin-height=&quot;888&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/riHLy/btr5Oc3i6pr/pkxuZHOA64l45j6y6yvpZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/riHLy/btr5Oc3i6pr/pkxuZHOA64l45j6y6yvpZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/riHLy/btr5Oc3i6pr/pkxuZHOA64l45j6y6yvpZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FriHLy%2Fbtr5Oc3i6pr%2FpkxuZHOA64l45j6y6yvpZ0%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; alt=&quot;requestAnimationFrame&quot; loading=&quot;lazy&quot; width=&quot;1073&quot; height=&quot;888&quot; data-filename=&quot;scroll-opt-js-queue.png&quot; data-origin-width=&quot;1073&quot; data-origin-height=&quot;888&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위는 브라우저의 자바스크립트 이벤트 루프 주기의 실행을 그림으로 표현한 것이다. 그림을 처음 보면 되게 눈 아프로 복잡하게 보일 수 있는데 간단히 정리하면 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;Task queue&lt;/b&gt; &lt;b&gt;1&lt;/b&gt; - 이벤트 콜백 함수, setTimeout 및 setInterval 비동기 콜백 함수를 처리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Task queue 2 &lt;/b&gt;- Task queue 1에 있는 모든 콜백 함수들이 실행된 후 실행되어야 하는 다음 콜백 함수들이 들어간다.&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;b&gt;Microtask queue&lt;/b&gt;&lt;/span&gt; - Promise 객체, Mutation Observer 객체를 처리 &lt;span style=&quot;color: #0593d3;&quot;&gt;(가장 우선순위가 높음)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #953b34;&quot;&gt;&lt;b&gt;Animation frames&lt;/b&gt;&lt;/span&gt; - requestAnimationFrame 와 같이 브라우저 렌더링과 관련된 태스크 처리&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 이벤트 루프 호출 스택만을 본 독자분들은 난이도가 수직 상승하는 느낌을 받을 수 있겠지만, 똑같은 비동기 함수라도 이렇게 처리 우선순위 큐가 별도로 있다는 정도 알고 있으면 된다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;s&gt;Promise.then&lt;/s&gt; 결과가 &lt;s&gt;setTimeout&lt;/s&gt;보다 우선 된다는 소리도 바로 이 때문이다.&lt;/blockquote&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;requestAnimationFrame 사용법&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;requestAnimationFrame&lt;/s&gt; 사용법은 &lt;s&gt;setTimeout&lt;/s&gt; 처럼 콜백 함수 내부에서 재귀 호출 하는 식으로 구성하면 된다. 브라우저는 애니메이션 프레임을 출력할 때마다 requestAnimationFrame 에 등록된 콜백 함수들을 비동기로 호출하여, 애니메이션을 부드럽게 출력해준다. 대신 차이점은 &lt;span&gt;&lt;s&gt;setTimeout&lt;/s&gt;&lt;span&gt; 은 타이머를 지정해주어야 하지만, &lt;span&gt;&lt;s&gt;requestAnimationFrame&lt;/s&gt;&lt;span&gt; 은 프레임 단위로 동작하기 때문에 별도의 반복 플래그가 필요 없다는 점이다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1679742572774&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const performAnimation = () =&amp;gt; {
  /* 스타일 조정 스크립트 */
  
  requestAnimationFrame(performAnimation) // 함수 내부에서 다시 requestAnimationFrame을 호출하여 반복
}

requestAnimationFrame(performAnimation);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고&amp;nbsp;&lt;s&gt;setTimeout&lt;/s&gt; 을 취소하기 위해 &lt;s&gt;clearTimout&lt;/s&gt; 을 사용하듯이, &lt;s&gt;requestAnimationFrame&lt;/s&gt; 를 취소하는 방법으로 &lt;s&gt;cancelAnimationFrame&lt;/s&gt; 를 사용한다. 아래와 같이 특정한 조건에서 애니메이션을 중지하고 싶을때 이용하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1679744200387&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let raf; // requestAnimationFrame을 담을 변수

const performAnimation = () =&amp;gt; {
    /* 스타일 조정 스크립트 */
	
    // 특정한 조건일 경우 raf를 중지하고 콜백 종료
    if(조건) {
    	cancelAnimationFrame(raf);
		return;
    }

    raf = requestAnimationFrame(performAnimation) // 함수 내부에서 다시 requestAnimationFrame을 호출하여 반복
}

requestAnimationFrame(performAnimation);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;requestAnimationFrame 예제&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 로켓 요소가 있고, &lt;s&gt;requestAnimationFrame&lt;/s&gt;를 통해 y좌표 값을 조정하여 위로 올리는 코드를 반복 호출하여 애니메이션을 실행하는 예제이다.&lt;/p&gt;
&lt;pre id=&quot;code_1679744444972&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;img class=&quot;rocket&quot; src=&quot;https://www.freeiconspng.com/thumbs/rocket-png/rocket-png-1.png&quot; alt=&quot;로켓 애니메이션&quot;&amp;gt;
&amp;lt;count class=&quot;value&quot;&amp;gt;0&amp;lt;/count&amp;gt;
&amp;lt;button&amp;gt;다시 시작&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1679744490041&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const rocket = document.querySelector('.rocket');
const value = document.querySelector('.value');
let yPos = 0;
let rafId;

// 콜백 함수
const render = () =&amp;gt; {
  yPos += 2; // y 좌표 증가

  rocket.style.transform = `translateY(${-yPos}px)`; // 로켓을 위로 올리기
  value.innerHTML = yPos; // 카운터 표시

  // 만약 로켓 위치가 일정 y좌표값일 경우 requestAnimationFrame 종료
  if (yPos &amp;gt;= 500) {
    cancelAnimationFrame(rafId);
    return;
  }

  rafId = requestAnimationFrame(render); // rAF 반복 호출
}

requestAnimationFrame(render); // 애니메이션 시작&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1679744515419&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 만일 로켓에 마우스 커서를 올려놓으면 애니메이션 중지
rocket.addEventListener('mouseover', () =&amp;gt; {
  cancelAnimationFrame(rafId);
});

// 만일 로켓에 마우스 커서를 빼면 애니메이션 재시작
rocket.addEventListener('mouseout', () =&amp;gt; {
  requestAnimationFrame(render);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 786px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;786&quot; data-default-tab=&quot;result&quot; data-slug-hash=&quot;bGxOzVL&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/bGxOzVL&quot;&gt; requestAnimationFrame&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&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;b&gt;애니메이션 타이머 이용하기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;requestAnimationFrame 함수의 콜백 함수의 인자로 매개변수를 전달하면 requestAnimationFrame 함수가 실행되기 시작한 이후의 시간을 얻을 수 있다. 이를 이용해 애니메이션 수행 시간을 구해 특정 타이머일때 스크립트를 실행하는 식의 응용이 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1679745137206&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let startTime;

// requestAnimationFrame의 콜백 함수에 매개변수를 정의
const draw = (timestamp) =&amp;gt; {
    if (!start) start = timestamp; // 애니메이션 시작 시간

	currentTime = timestamp - startTime;

	// 애니메이션이 실행된지 2초가 지나면..
	if(currentTime &amp;gt; 2000) { 
        console.log('END');
        return;
    }

	requestAnimationFrame(draw);
}

requestAnimationFrame(draw);&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://youtu.be/9XnqDSabFjM&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://dribbble.com/shots/1945400-FPS-frames-per-second&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://jbee.io/web/optimize-scroll-event/&lt;/p&gt;</description>
      <category>Language/JavaScript (WEB)</category>
      <category>animation</category>
      <category>optimize</category>
      <category>RAF</category>
      <category>requestAnimationFrame</category>
      <category>렌더링</category>
      <category>최적화</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1104</guid>
      <comments>https://inpa.tistory.com/entry/%F0%9F%8C%90-requestAnimationFrame-%EA%B0%80%EC%9D%B4%EB%93%9C#entry1104comment</comments>
      <pubDate>Tue, 18 Apr 2023 09:55:49 +0900</pubDate>
    </item>
    <item>
      <title>  한방에 이해하는 attribute 와 property 속성 차이</title>
      <link>https://inpa.tistory.com/entry/%F0%9F%8C%90-attribute-property-%EC%B0%A8%EC%9D%B4</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;209f6b4b.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Xg9XO/btr5Qyq5vCc/bb6lfr5P6YXCEpuIqkSI9k/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Xg9XO/btr5Qyq5vCc/bb6lfr5P6YXCEpuIqkSI9k/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Xg9XO/btr5Qyq5vCc/bb6lfr5P6YXCEpuIqkSI9k/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXg9XO%2Fbtr5Qyq5vCc%2Fbb6lfr5P6YXCEpuIqkSI9k%2Fimg.webp&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; alt=&quot;attribute-property&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;720&quot; data-filename=&quot;209f6b4b.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Attribute&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;vs&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Property&lt;span&gt; 정의 차이&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #292c32;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;attribute 와 property 를 한국어로 번역하자면 둘다 '속성' 이라는 의미를 가지고 있다. 단순히 단어명이 같은 것을 떠나서 실제로 둘은 html 요소에 대한 클래스와 아이디와 같은 속성을 가리킨다. 이처럼 &lt;span style=&quot;background-color: #ffffff; color: #292c32;&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;HTML의 Attribute&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어트리뷰트는 HTML의 속성이다. 엘리먼트에 아이디나 클래스와 같은 추가적인 정보를 일컫는다고 보면 된다&lt;/p&gt;
&lt;pre id=&quot;code_1679816948940&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- div 엘리먼트의 id와 class 속성은 어트리뷰트 이다 --&amp;gt;
&amp;lt;div id=&quot;inpa&quot; class=&quot;bold&quot;&amp;gt;&amp;lt;/div&amp;gt;

&amp;lt;!-- input 엘리먼트의 type과 value 속성은 어트리뷰트 이다 --&amp;gt;
&amp;lt;input type=&quot;text&quot; value=&quot;0&quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;DOM의 Property&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로퍼티는 DOM의 속성이다. 즉, html의 attribute를 DOM 내에서 대신해서 표현이라고 보면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1679817110647&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div class='my-class' style=&quot;color: red;&quot;&amp;gt;&amp;lt;/div&amp;gt;

&amp;lt;script&amp;gt;
    // className과 style은 프로퍼티이다
    document.querySelector('div').className; // &quot;my-class&quot;
    document.querySelector('div').style.color; // &quot;red&quot;
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;149&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/U9CiE/btr5PRYNGk8/kszYJc3P4KKqUeNJWxGSG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/U9CiE/btr5PRYNGk8/kszYJc3P4KKqUeNJWxGSG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/U9CiE/btr5PRYNGk8/kszYJc3P4KKqUeNJWxGSG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FU9CiE%2Fbtr5PRYNGk8%2FkszYJc3P4KKqUeNJWxGSG0%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; alt=&quot;DOM의 Property&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;149&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;149&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 트리는 &lt;s&gt;&amp;lt;div class= &amp;lsquo;my-class&amp;rsquo;&amp;gt;&amp;lt;/div&amp;gt;&lt;/s&gt; 태그를 DOM 으로 표현한 것인데 위에서 &lt;s&gt;className&lt;/s&gt;이 property가 되는 것으로 보면 된다. div 태그&amp;nbsp;안에&amp;nbsp;&lt;s&gt;class=&quot;이름&quot;&lt;/s&gt; 으로 되어있는데 &lt;s&gt;className&lt;/s&gt; 속성명으로 접근해서 설정할 수 있는 이유는 어트리뷰트와 프로퍼티가 비록 이름은 다를지라도 서로 연결되어있어서 그렇다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DOM은 HTML을 자바스크립트 객체(Object)로 표현한 문서 모델이라고 보면 된다. 그래서 &lt;span&gt;자바스크립트로 HTML 구조의 부분과 통신하고, 업데이트하고, 생성하고, 삭제하는 행위를 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1679814083328&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;[JS]   브라우저 DOM 다루기 문법   총정리&quot; data-og-description=&quot;브라우저 DOM 종류 브라우저는 HTML 문서를 로드한 후, 해당 문서에 대한 모델을 메모리에 생성한다. 이때 모델은 객체의 트리로 구성되는데, 이것을 DOM tree라 한다. 문서 노드(Document Node) 트리의 &quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-DOM-%EB%AC%B8%EB%B2%95-%F0%9F%92%AF-%EC%B4%9D%EC%A0%95%EB%A6%AC&quot; data-og-url=&quot;https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-DOM-%EB%AC%B8%EB%B2%95-%F0%9F%92%AF-%EC%B4%9D%EC%A0%95%EB%A6%AC&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cHAuy3/hyR2RXHN9m/PltjWi3UALi2qtf2HADnYK/img.jpg?width=800&amp;amp;height=441&amp;amp;face=0_0_800_441,https://scrap.kakaocdn.net/dn/bZqcVO/hyR39JeA6G/YSkBlzOndI0pDRbVJwTDp0/img.jpg?width=800&amp;amp;height=441&amp;amp;face=0_0_800_441,https://scrap.kakaocdn.net/dn/cwZiPQ/hyR2Ma2PUy/tWNdaj5XMmryuflBxhEMP1/img.png?width=1200&amp;amp;height=1242&amp;amp;face=0_0_1200_1242&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-DOM-%EB%AC%B8%EB%B2%95-%F0%9F%92%AF-%EC%B4%9D%EC%A0%95%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-DOM-%EB%AC%B8%EB%B2%95-%F0%9F%92%AF-%EC%B4%9D%EC%A0%95%EB%A6%AC&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cHAuy3/hyR2RXHN9m/PltjWi3UALi2qtf2HADnYK/img.jpg?width=800&amp;amp;height=441&amp;amp;face=0_0_800_441,https://scrap.kakaocdn.net/dn/bZqcVO/hyR39JeA6G/YSkBlzOndI0pDRbVJwTDp0/img.jpg?width=800&amp;amp;height=441&amp;amp;face=0_0_800_441,https://scrap.kakaocdn.net/dn/cwZiPQ/hyR2Ma2PUy/tWNdaj5XMmryuflBxhEMP1/img.png?width=1200&amp;amp;height=1242&amp;amp;face=0_0_1200_1242');&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;[JS]   브라우저 DOM 다루기 문법   총정리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;브라우저 DOM 종류 브라우저는 HTML 문서를 로드한 후, 해당 문서에 대한 모델을 메모리에 생성한다. 이때 모델은 객체의 트리로 구성되는데, 이것을 DOM tree라 한다. 문서 노드(Document Node) 트리의&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;inpa.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Attribute &lt;span style=&quot;color: #ef5369;&quot;&gt;vs&lt;/span&gt; Property 기능 차이&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;엘리먼트 속성 접근 차이&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;attribute는 html 안에서 property 는 DOM tree 안에서 존재한다. 이것이 뜻하는 바는 &lt;span style=&quot;color: #ee2323;&quot;&gt;attribute는 정적으로 변하지 않고&lt;/span&gt; &lt;span style=&quot;color: #006dd7;&quot;&gt;property는 동적으로 그 값이 변할&lt;/span&gt; 수 있다는 의미를 내포하고 있다. 예를 들어 체크 박스 태그가 있을 때 유저가 체크박스에 체크를 하면 attribute의 상태는 변하지 않지만 property의 상태는 checked로 변하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실전 예를 들어보자. 아래 input 태그가 있고 &lt;s&gt;value&lt;/s&gt; 어트리뷰트로 0 값을 가지고 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1679820425208&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;input type=&quot;text&quot; value=&quot;0&quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 자바스크립트 DOM 문법을 통해서 input 엘리먼트에 접근해 value 속성 값을 가져올 수 있다. 여기서 &lt;s&gt;input.value&lt;/s&gt; 는 프로퍼티이다.&lt;/p&gt;
&lt;pre id=&quot;code_1679820495745&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const input = document.querySelector('input');
console.log(input.value); // '0'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번엔 value 속성값을 임의의 값으로 변경해보자. &lt;s&gt;input.value&lt;/s&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;의 값을 재설정하고 다시 확인해 보면 변경된 값으로 설정 되었음을 확인 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1679820548239&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;input.value = 66;
console.log(input.value); // '66'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 HTML 태그를 보면 여전히 value의 값은 0 인채 그대로 있는걸 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;243&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTIYtD/btr5PHa7kJ5/ySq50cdQV6VxFwJbhtwJZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTIYtD/btr5PHa7kJ5/ySq50cdQV6VxFwJbhtwJZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTIYtD/btr5PHa7kJ5/ySq50cdQV6VxFwJbhtwJZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTIYtD%2Fbtr5PHa7kJ5%2FySq50cdQV6VxFwJbhtwJZ0%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; alt=&quot;attribute-property&quot; loading=&quot;lazy&quot; width=&quot;587&quot; height=&quot;243&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;243&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 말한 어트리뷰트는 정적이고 프로퍼티는 동적 이라는 말이 바로 이것이다. 어트리뷰트와 프로퍼티는 둘이 연결되어 있긴 하지만 엄밀히 다른 녀석이라, 프로퍼티의 값을 변경해도 DOM 객체만 업데이트 되고 HTML은 업데이트 되지 않아 어트리뷰트의 값은 그대로 인 것이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;DOM은 JavaScript 모델이므로 굳이 HTML 속성을 계속 업데이트할 필요가 없다. 실제로 리소스 낭비가 될 수 있어 이런식으로 설계 되었다고 보면 된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 DOM과 HTML 둘다 특정 속성에 대해 값을 변경하고 싶다면, &lt;s&gt;setAttribute()&lt;/s&gt; 메서드를 통해 업데이트 해주면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1679820818508&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const input = document.querySelector('input');

input.setAttribute('value', 99);

console.log(input.value); // '99'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;620&quot; data-origin-height=&quot;215&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qE5wR/btr5Pb4hZJz/h7zkaftRx588MY1IfMhtWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qE5wR/btr5Pb4hZJz/h7zkaftRx588MY1IfMhtWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qE5wR/btr5Pb4hZJz/h7zkaftRx588MY1IfMhtWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqE5wR%2Fbtr5Pb4hZJz%2Fh7zkaftRx588MY1IfMhtWk%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; alt=&quot;attribute-property&quot; loading=&quot;lazy&quot; width=&quot;620&quot; height=&quot;215&quot; data-origin-width=&quot;620&quot; data-origin-height=&quot;215&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;&lt;b&gt;사용자 정의 속성 접근 차이&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML에 미리 정의된 태그의 속성 뿐만 아니라 개발자가 임의로 엘리먼트에 사용자 정의 속성을 만들어 넣을 수도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1679821104168&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- 실제 html에 없는 사용자 정의 임의 속성 custom --&amp;gt;
&amp;lt;input type=&quot;text&quot; value=&quot;0&quot; custom=&quot;9999&quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 프로퍼티의 특징으론 엘리먼트가 가진 모든 속성이 프로퍼티로 되지는 않다는 점이다. HTML에 지정된 속성들은 프로퍼티로도 표현되지만, 엘리먼트에 임의로 지정한 사용자 정의 속성은 프로퍼티로 자동으로 표현되지 않는다. 실제로 자바스크립트로 접근해보면 9999 가 아닌 undefined 라는 결과값을 내뱉는다.&lt;/p&gt;
&lt;pre id=&quot;code_1679821218962&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const input = document.querySelector('input');
console.log(input.custom); // undefined&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 사용자 정의 속성 값에 접근하려면 &lt;s&gt;getAttribute()&lt;/s&gt; 와 &lt;s&gt;setAttribute()&lt;/s&gt;를 사용하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1679821264479&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(input.getAttribute('custom')); // '9999'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;속성 이름 제약사항&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로퍼티는 자바스크립트 객체 이기 때문에 프로퍼티 이름은 점(.)연산자 표기법을 사용하는 식별자로 간주되고, 식별자 규칙을 따라야해서 프로퍼티명을 이름 짓는데 제약이 있다. 예를들어 어트리뷰트는 &lt;s&gt;background-image&lt;/s&gt; 와 같이 &lt;s&gt;-&lt;/s&gt; 문자를 사용해도 되지만, 프로퍼티는 변수명 규칙에 따라야 하기 때문에 &lt;s&gt;backgroundImage&lt;/s&gt; 로 접근해야 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1679821535840&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const div = document.querySelector('div');

// 프로퍼티
div.style.backgroundImage = 'url()';

// 어트리뷰트
div.setAttribute('style', 'background-image: url()');&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로퍼티 이름과 속성 이름이 서로 다른 몇몇 예로는, class/className , readonly/readOnly, maxlength/maxLength 등이 있다. 예약어인 경우 &lt;s&gt;className&lt;/s&gt; 처럼 다른 이름이 존재하고, 여러 단어로 이루어진 속성이름인 경우 낙타 표기법(CamelCase)을 사용해서 표현된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;성능 차이&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로&amp;nbsp;프로퍼티&amp;nbsp;접근이 &lt;s&gt;getAttribute()&lt;/s&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;s&gt;setAttribute()&lt;/s&gt; 메서드보다 약간 더 빠르다. 따라서 사용자 정의 속성과 같이 별도의 프로퍼티가 없는 경우에만 메서드를 이용하고 그외에는 프로퍼티로 접근하는 방법이 이상적이라 볼 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://ultimatecourses.com/blog/attributes-versus-properties-in-javascript&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://medium.com/hexlant/attribute-%EC%99%80-property-%EC%9D%98-%EC%B0%A8%EC%9D%B4-c6f1c91ba91&lt;/p&gt;</description>
      <category>Language/JavaScript (WEB)</category>
      <category>attribute</category>
      <category>html 속성</category>
      <category>property</category>
      <category>어트리뷰트</category>
      <category>프로퍼티</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1102</guid>
      <comments>https://inpa.tistory.com/entry/%F0%9F%8C%90-attribute-property-%EC%B0%A8%EC%9D%B4#entry1102comment</comments>
      <pubDate>Mon, 17 Apr 2023 08:12:01 +0900</pubDate>
    </item>
    <item>
      <title>[JS]   재미있게 확실히 이해하는 null / undefined / NaN 차이</title>
      <link>https://inpa.tistory.com/entry/%F0%9F%93%9A-null-undefined-NaN</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;React_20(4)-transformed.jpeg&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLD5ur/btr6N3pilkD/T3BwOSehs0gRIdKG1pjhQ0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLD5ur/btr6N3pilkD/T3BwOSehs0gRIdKG1pjhQ0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLD5ur/btr6N3pilkD/T3BwOSehs0gRIdKG1pjhQ0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLD5ur%2Fbtr6N3pilkD%2FT3BwOSehs0gRIdKG1pjhQ0%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; alt=&quot;null-NaN-undefined&quot; loading=&quot;lazy&quot; width=&quot;1400&quot; height=&quot;700&quot; data-filename=&quot;React_20(4)-transformed.jpeg&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;자바스크립트의 요상한 falsy 값&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서 null, NaN, undefined는 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;다른 프로그래밍 언어에는 없는 자바스크립트에만 있는 요상한 falsy 값으로서,&amp;nbsp;&lt;/span&gt;이들은 모두 값이 없음(falsy)을 나타내는 특별한 값이다. 그래서 이들은 &lt;b&gt;조건문에서 false로 평가&lt;/b&gt;되어 진다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;b&gt;값&lt;/b&gt;&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;b&gt;Boolean 문맥&lt;/b&gt;&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;b&gt;Number 문맥&lt;/b&gt;&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&lt;b&gt;String 문맥&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;null&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;false&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;0&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&quot;null&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;undefined&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;false&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;NaN&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&quot;undefined&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;NaN&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;false&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;NaN&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&quot;NaN&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;Infinity&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;true&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;Infinity&lt;/td&gt;
&lt;td colspan=&quot;1&quot; rowspan=&quot;1&quot;&gt;&quot;Infinity&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 대부분 null, NaN, undefined 간의 의미의 구체적인 차이에 대해 애매모호하게 알거나 정확히 모르는 사람이 꽤 많다. 따라서 이번 포스팅에는 null, NaN, undefined값을 두루마리 휴지로 비유한 이미지를 통해 정확히 어떤 의미이며 왜 이러한 의미인지 알아보는 시간을 가져 보겠다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;두루마리 휴지로 비유하는 falsy&amp;nbsp;값&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1988&quot; data-origin-height=&quot;576&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/B0svc/btr6FgCX5ty/UAxdwRwTnh99PxgUzja2y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/B0svc/btr6FgCX5ty/UAxdwRwTnh99PxgUzja2y1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/B0svc/btr6FgCX5ty/UAxdwRwTnh99PxgUzja2y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FB0svc%2Fbtr6FgCX5ty%2FUAxdwRwTnh99PxgUzja2y1%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; alt=&quot;null-NaN-undefined&quot; loading=&quot;lazy&quot; width=&quot;1988&quot; height=&quot;576&quot; data-origin-width=&quot;1988&quot; data-origin-height=&quot;576&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 이미지는 자바스크립트 falsy 값에 대한 일종의 유머적 비유이다. 모두 의미 없거나 부적절한 값들이지만 휴지 형태에 따라 확실히 의미하는 바가 다름을 볼 수 있다. 이들 4개의 값을 설명하자면 다음과 같다.&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;0 의 상태&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정수 0은 대다수 프로그래밍 언어에서 false로 평가된다. 이를 휴지로 비유하면 휴지 내용물이 없다는 의미이다. 단, 0 이라도 고유한 숫자 타입 이듯이 휴지 내용물이 없어도 휴지임을 알 수 있는 '곽티슈' 와 이를 담는 '휴지걸이'는 잔존한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;309&quot; data-origin-height=&quot;156&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mopNi/btr6NZNZhZw/sK9aossak8NpzG5NlzK6HK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mopNi/btr6NZNZhZw/sK9aossak8NpzG5NlzK6HK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mopNi/btr6NZNZhZw/sK9aossak8NpzG5NlzK6HK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmopNi%2Fbtr6NZNZhZw%2FsK9aossak8NpzG5NlzK6HK%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; alt=&quot;null-NaN-undefined&quot; loading=&quot;lazy&quot; width=&quot;309&quot; height=&quot;156&quot; data-origin-width=&quot;309&quot; data-origin-height=&quot;156&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;null &lt;b&gt;의 상태&lt;/b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 null을 그저 값이 없는 상태라고 알고 있는 분들이 많지만, 이는 정확하지 않은 의미이다. 정확히 null은 어느 reference 변수에 대해 &lt;b&gt;주소값이 없는 것&lt;/b&gt;을 표현하기 위한 키워드 값이다. 객체의 속성 값이 존재하지 않을 때나 함수의 매개변수를 초기화하는 용도로 사용 되기도 한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;레퍼런스 변수란 객체와 같이 힙(heap) 공유 메모리 영역에 저장되는 포인터 라고 이해하면 된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 null은 오로지 reference 변수에만 담을 수 있기 때문에, 일반적인 값인 정수나 문자를 저장하는 primitive 변수에는 null을 저장할 수 없다. 따라서 Java에서는 이 primitive 변수에 대해 값을 주지 않을 경우 지정된 초기화 값을 자동으로 할당하게 하였고, C언어에서는 가비지 값을 자동으로 할당한다.&lt;/p&gt;
&lt;pre id=&quot;code_1681887654852&quot; class=&quot;c arduino&quot; data-ke-language=&quot;c&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;

int main() {
   int num;
   printf(&quot;%d&quot;, num); // -56982792084 이상한 쓰레기 값
   return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1681887659213&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class MyClass {
    int number; // 인스턴스 변수

    public static void main(String[] args) {
        MyClass obj = new MyClass();
        System.out.println(obj.number); // 출력 결과: 0 (원래는 C언어 처럼 쓰레기값이 나오지만 JVM에 의해서 기본값으로 설정된다)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;reference 변수냐 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;primitive 변수냐 에 따라 타입이 없는 &lt;/span&gt;자바스크립트로도 알수가 있는데, 1을 할당한 변수의 타입은 number 타입이지만, null을 할당한 변수의 타입은 object 임을 알 수가 있다.&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;482&quot; data-origin-height=&quot;111&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceow4H/btr6OVjVsyv/mQ8wPuGNHnxK4UrXjc6zJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceow4H/btr6OVjVsyv/mQ8wPuGNHnxK4UrXjc6zJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceow4H/btr6OVjVsyv/mQ8wPuGNHnxK4UrXjc6zJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fceow4H%2Fbtr6OVjVsyv%2FmQ8wPuGNHnxK4UrXjc6zJ0%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; alt=&quot;null-NaN-undefined&quot; loading=&quot;lazy&quot; width=&quot;482&quot; height=&quot;111&quot; data-origin-width=&quot;482&quot; data-origin-height=&quot;111&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로, 위의 0과 다른점은 곽티슈는 없지만 휴지걸이는 잔존해 있다는 점이다. 왜냐하면 null 값을 들고 있는 객체 자체는 프로그래머가 정의하여 &lt;b&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;/span&gt;이다. 따라서 휴지를 담는 휴지걸이 자체는 잔존해 있는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;382&quot; data-origin-height=&quot;414&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bijFoB/btsblyyi7Fh/xjloic35sRlB9cq2KL8iR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bijFoB/btsblyyi7Fh/xjloic35sRlB9cq2KL8iR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bijFoB/btsblyyi7Fh/xjloic35sRlB9cq2KL8iR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbijFoB%2Fbtsblyyi7Fh%2Fxjloic35sRlB9cq2KL8iR0%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; alt=&quot;null-NaN-undefined&quot; loading=&quot;lazy&quot; width=&quot;164&quot; height=&quot;178&quot; data-origin-width=&quot;382&quot; data-origin-height=&quot;414&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;undefined &lt;b&gt;의 상태&lt;/b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;undefined는 변수가 정의되어 있지 않은 상태, 즉 값이 할당되지 않은 상태를 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 위에서 primitive, reference 변수를 들며 자바와 C언어에서 어떻게 초기화를 진행하는지에 대해 설명하였었다. 두 언어만 하더라도 어떤 타입의 변수냐에 따라 초기화되는 과정이 달랐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 자바스크립트에선 심플하게 어떤 타입의 변수든 간에 &lt;span style=&quot;color: #ee2323;&quot;&gt;값이 없다는걸 undefined 라는 표현으로 퉁쳤다&lt;/span&gt;고 보면 된다. 심지어 변수 타입 자체도 &lt;span&gt;&lt;s&gt;undefined&lt;/s&gt;&lt;span&gt; 로 되어 있다. &lt;/span&gt;&lt;/span&gt;심플하게 표현하기 위해 이러한 방식을 취한것 같은데, 만일 기존의 C언어나 자바에 익숙한 개발자들에겐 오히려 혼동만 일으킨 것 같다고 개인적으로 생각된다.&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;null&lt;span style=&quot;color: #ef5369;&quot;&gt; vs&lt;/span&gt; undefined&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;null과 undefined 차이점은 내부 메모리적인 측면에서 봐야 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;undefined는 변수가 초기화되지 않았거나, 객체의 속성이 존재하지 않는 등의 경우에 자동으로 할당되는 값으로, 이때의 변수는 메모리에 존재하지만 값이 없기 때문에 크기가 매우 작다.&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;따라서, undefined 값을 가진 변수의 메모리 용량은 작고, null 값을 가진 변수의 메모리 용량은 크다고 정리할 수 있겠다. (이는 상대적인 비교일 뿐이므로 실제로는 거의 차이가 없다고 봐도 된다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만, 자바스크립트에서는 변수가 컴파일 시점이 아닌 런타임 시점에 동적으로 할당되기 때문에, 변수의 크기는 런타임 환경에 따라 다를 수 있어 메모리 크기는 정확히 알 수 없다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;반면 자바(Java)에선 reference 변수 같은 경우&amp;nbsp; 32비트 JVM 기준으로 4바이트가 할당되게 된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;null 과 undefined가 서로 완전히 다른 존재라는 점은, 자바스크립트에서 타입을 출력해보면 알 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;226&quot; data-origin-height=&quot;110&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0ViZt/btr6OZGEeyJ/FXRrqTXpdDap1cHBDKnGSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0ViZt/btr6OZGEeyJ/FXRrqTXpdDap1cHBDKnGSK/img.png&quot; data-alt=&quot;null 과 undefined 비교&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0ViZt/btr6OZGEeyJ/FXRrqTXpdDap1cHBDKnGSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0ViZt%2Fbtr6OZGEeyJ%2FFXRrqTXpdDap1cHBDKnGSK%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; alt=&quot;undefined&quot; loading=&quot;lazy&quot; width=&quot;226&quot; height=&quot;110&quot; data-origin-width=&quot;226&quot; data-origin-height=&quot;110&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;null 과 undefined 비교&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 휴지로 비유한 그림을 보면 아예 빈 공간임을 볼 수가 있는데, 휴지 내용물도 그 휴지를 담을 휴지걸이 자체도 없기 때문이다. 왜냐하면 타입 자체가 &lt;s&gt;undefined&lt;/s&gt; 이기 때문이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;379&quot; data-origin-height=&quot;410&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cj0qZS/btsa9HXy3sp/OF1tcTpgy0uwpRpTeoUVRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cj0qZS/btsa9HXy3sp/OF1tcTpgy0uwpRpTeoUVRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cj0qZS/btsa9HXy3sp/OF1tcTpgy0uwpRpTeoUVRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcj0qZS%2Fbtsa9HXy3sp%2FOF1tcTpgy0uwpRpTeoUVRk%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; alt=&quot;undefined&quot; loading=&quot;lazy&quot; width=&quot;159&quot; height=&quot;172&quot; data-origin-width=&quot;379&quot; data-origin-height=&quot;410&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 null을 비유할때 아무것도 담기지 않은 그릇이라 표현했는데, undefined는 그릇 자체가 통째로 없는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;740&quot; data-origin-height=&quot;157&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TcF1I/btsbgmexc75/cK13yrAmY1EvvldgFD8wkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TcF1I/btsbgmexc75/cK13yrAmY1EvvldgFD8wkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TcF1I/btsbgmexc75/cK13yrAmY1EvvldgFD8wkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTcF1I%2Fbtsbgmexc75%2FcK13yrAmY1EvvldgFD8wkK%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; alt=&quot;undefined&quot; loading=&quot;lazy&quot; width=&quot;740&quot; height=&quot;157&quot; data-origin-width=&quot;740&quot; data-origin-height=&quot;157&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;NaN &lt;b&gt;의 상태&lt;/b&gt;&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NaN은 Not a Number의 약자로 숫자가 아닌 값을 나타낸다. 그러나 숫자가 아니지만 타입은 Number 타입으로 취급된다. 숫자 타입이면서 숫자가 아닌 값이라니 뭔가 말이야 방구야 싶겠지만, 실제로 이러한 표현에 대한 필요성이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어 문자 &quot;100&quot; 을 숫자 타입으로 형변환하면 어떠한 에러 없이 숫자 100으로 치환이 된다. 이러한 기능이 있는 이유는 대부분의 사용자가 입력한 값이 문자열 형태인 경우가 많아 이를 숫자로 변환하여 계산해야 하는 상황이 많기 때문이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;431&quot; data-origin-height=&quot;339&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bubKjw/btsbkFEPTNA/LSLra3BWLxdslGyhzzrkP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bubKjw/btsbkFEPTNA/LSLra3BWLxdslGyhzzrkP0/img.png&quot; data-alt=&quot;대표적으로 form에 입력한 모든 값은 문자열이다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bubKjw/btsbkFEPTNA/LSLra3BWLxdslGyhzzrkP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbubKjw%2FbtsbkFEPTNA%2FLSLra3BWLxdslGyhzzrkP0%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; alt=&quot;NaN&quot; loading=&quot;lazy&quot; width=&quot;431&quot; height=&quot;339&quot; data-origin-width=&quot;431&quot; data-origin-height=&quot;339&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;대표적으로 form에 입력한 모든 값은 문자열이다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 해당 문자가 &quot;100ABC&quot; 와 같은 숫자와 일반 문자열이 섞인 형태일 경우, 이를 함수를 통해 숫자로 변환할때 문제가 된다. 그래서 타입 자체는 넘버 타입으로 변환은 됬지만 내용물이 숫자가 아니라는 값을 표현하기 위해 NaN 이라는 표현을 쓰는 것이다. 그래서 NaN은 일종의 오류 값으로 취급된다. 이밖에도 숫자와 문자열을 연산하려고 해도 NaN이 발생한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;반면 자바(Java)에선 문자열이 유효한 숫자 형식이 아닐경우 NumberFormatException을 발생시킨다.&lt;/blockquote&gt;
&lt;pre id=&quot;code_1680069060404&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 문자 100을 + 연산자를 통해 숫자로 형변환
let a = +&quot;100&quot;; 
console.log(a); // 숫자 100

// 문자 ABC를  + 연산자를 통해 숫자로 형변환
let b = +&quot;ABC&quot;;
console.log(b); // NaN (숫자로 형변환해서 숫자 타입 값이지만 숫자가 아님)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;379&quot; data-origin-height=&quot;135&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5pkxg/btr6Jonejbw/tVd22qqQbGtItD3qypalB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5pkxg/btr6Jonejbw/tVd22qqQbGtItD3qypalB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5pkxg/btr6Jonejbw/tVd22qqQbGtItD3qypalB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5pkxg%2Fbtr6Jonejbw%2FtVd22qqQbGtItD3qypalB1%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; alt=&quot;NaN&quot; loading=&quot;lazy&quot; width=&quot;379&quot; height=&quot;135&quot; data-origin-width=&quot;379&quot; data-origin-height=&quot;135&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;374&quot; data-origin-height=&quot;404&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxyotw/btsa9JVoThT/oWUYkaT5pImXVo4pbt9nM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxyotw/btsa9JVoThT/oWUYkaT5pImXVo4pbt9nM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxyotw/btsa9JVoThT/oWUYkaT5pImXVo4pbt9nM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcxyotw%2Fbtsa9JVoThT%2FoWUYkaT5pImXVo4pbt9nM0%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; alt=&quot;NaN&quot; loading=&quot;lazy&quot; width=&quot;176&quot; height=&quot;190&quot; data-origin-width=&quot;374&quot; data-origin-height=&quot;404&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Language/JavaScript</category>
      <category>Nan</category>
      <category>null</category>
      <category>undefined</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1103</guid>
      <comments>https://inpa.tistory.com/entry/%F0%9F%93%9A-null-undefined-NaN#entry1103comment</comments>
      <pubDate>Fri, 14 Apr 2023 09:47:21 +0900</pubDate>
    </item>
    <item>
      <title>⚙️ 난잡한 if-else 문 리팩토링 기법 (가독성 &amp;amp; 성능&amp;uarr;)</title>
      <link>https://inpa.tistory.com/entry/%E2%9A%99%EF%B8%8F-if-else-refactoring</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1__I1Jk4Sl03er2SHvUHABwg.jpeg&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;875&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQZXd2/btrZz53FhPF/AsiztRkzKSHPj4XRj9Xra0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQZXd2/btrZz53FhPF/AsiztRkzKSHPj4XRj9Xra0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQZXd2/btrZz53FhPF/AsiztRkzKSHPj4XRj9Xra0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQZXd2%2FbtrZz53FhPF%2FAsiztRkzKSHPj4XRj9Xra0%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; alt=&quot;자바스크립트-if최적화&quot; loading=&quot;lazy&quot; width=&quot;1400&quot; height=&quot;875&quot; data-filename=&quot;1__I1Jk4Sl03er2SHvUHABwg.jpeg&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;875&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;자바스크립트 조건문 리팩토링&lt;/b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무리 고급적인 프로그래밍 문법, 기법들을 익히고 그걸 활용하더라도, 자신의 코드에서 가장 많이 비중을 차지하는 문법은 아마 조건문 일 것이다. 프로그램은 반복문과 조건문만 있으면 돌아간다라는 말이 있듯이 조건 분기문은 프로그래밍의 기본이기도 하다. 그런데 이 if-else 분기문을 생각없이 지나치게 중첩하고 생각없이 코드를 나열한다면 가독성이 크게 떨어진다는 문제점이 생기게 된다. 예를들어&amp;nbsp; if문 안에 if가 있고 그 else문 안에 if~else 안의 if 등등.. 이렇게 코드를 짜듯이 말이다. 농담 삼아 아래와 같이 코드가 마치 아됴겐을 맞은것과 같은 모습이 되어 버릴지도 모른다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;511&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRZ7ch/btrZH1MmBZv/5CzKTHmR6mxnR4B7zSQEsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRZ7ch/btrZH1MmBZv/5CzKTHmR6mxnR4B7zSQEsk/img.png&quot; data-alt=&quot;아됴겐 ~&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRZ7ch/btrZH1MmBZv/5CzKTHmR6mxnR4B7zSQEsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRZ7ch%2FbtrZH1MmBZv%2F5CzKTHmR6mxnR4B7zSQEsk%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; alt=&quot;자바스크립트-if최적화&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;511&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;511&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;물론 컴퓨터 입장에선 코드 로직상 문제는 없을텐지만 이를 나중에 사람이 유지보수 하기 위해 코드를 복기하는 과정에서 굉장한 노력과 시간 그리고 스트레스가 소모될 것이다. 그래서 이러한 if문을 최적화하는 스타일 기법이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에선 4가지의 방법론을 소개할 예정인데, 첫번째와 두번째는 if문의 위치를 조정함으로써 좀더 보기 편하게 유지보수 좋게 만들어주는 코드 순서에 가깝다면, 세번째 네번째는 '디자인 패턴'에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주니어 프로그래머와 시니어 프로그래머를 가르는 기준은 회사마다 '사바사' 이기 때문에 항상 논란이 되지만, if문을 어떤식으로 짜느냐에 따라 이 둘을 가를수 있다는 점은 누구나 동의한다. 그만큼 조건문 리팩토링은 사소한 것 처럼 보이지만 유지보수가 중요한 현업에서 굉장히 중요하게 여겨진다. 지금부터 주니어 딱지의 1mg 이라도 떼보러 가보자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&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;/p&gt;
&lt;pre id=&quot;code_1680254724300&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const now = new Date();
const year = date.getFullYear();

if (now &amp;gt; new Date(year, 6, 25) &amp;amp;&amp;amp; now &amp;lt; new Date(year, 7, 31)) {
	// 매년 6월 25일 ~ 7월 31일 일 경우 
	console.log('여름 방학 입니다');
} else if (now &amp;gt; new Date(year, 11, 19) &amp;amp;&amp;amp; now &amp;lt; new Date(year + 1, 1, 28)) {
	// 매년 11월 19일 ~ 내년 1월 28일 일 경우 
	console.log('겨울 방학 입니다');
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주석을 통해 어느정도 뜻하는 바를 한눈에 알 수는 있지만 분기문 코드 자체가 여전히 난잡하고 불성 사납다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 경우 조건식 자체를 아예 함수로 만들어서 심플하게 구성하는 것이 좋다. 이때 함수명을 이름만 보고도 무엇을 판단하는 로직인지 쉽게 알 수 있게 하기 위해 적절한 함수명을 지어준다.&amp;nbsp;조건문을 리팩토링한 결과는 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1680254988442&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function isSummerVacation(now) {
  const year = date.getFullYear();
  return now &amp;gt; new Date(year, 6, 25) &amp;amp;&amp;amp; now &amp;lt; new Date(year, 7, 31)
}

function isWinterVacation(now) {
  const year = date.getFullYear();
  return now &amp;gt; new Date(year, 11, 19) &amp;amp;&amp;amp; now &amp;lt; new Date(year + 1, 1, 28)
}

const now = new Date();

if (isSummerVacation(now)) {
	// 매년 6월 25일 ~ 7월 31일 일 경우 
	console.log('여름 방학 입니다');
} else if (isWinterVacation(now)) {
	// 매년 11월 19일 ~ 내년 1월 28일 일 경우 
	console.log('겨울 방학 입니다');
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;isSummerVacation()&lt;/s&gt; 함수와 &lt;s&gt;isWinterVacation()&lt;/s&gt; 함수를 만들어주고 길다란 조건문을 함수의 return 문으로 넣었다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;그리고 if 문의 블록에는 함수 호출문으로 변경해준다. 이렇게 함으로써 코드가 더욱 가독성이 좋아짐을 볼수있다. &lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;전체적인 로직 자체는 변한게 없다. 코드를 모듈화하여 그저 if 문의 조건식 코드 길이를 함수 호출문을 통해 획기적으로 줄인 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;early-return-설명&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Early Return 기법&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Early Return이란 조건문에서 먼저 Return할 수 있는 부분은 분리하여 우선 초반 라인의 if문 내에서 Return하여 함수를 미리 종료하는 기법을 말한다. &lt;span&gt;if-else문이 너무 많으면 읽기가 어렵고 조건에 대해 명시적이지 않을 수 있는데,&lt;span&gt; Early Return을 적용&lt;/span&gt;&lt;/span&gt;하면 뒤의 코드로 진입하지 않아 else문을 사용하지 않아, 로직은 변함이 없으면서 가독성이 좋고 더 명시적인 코드로 리팩토링 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 &lt;s&gt;loginService()&lt;/s&gt; 라는 함수에 로그인 여부, 토큰 여부, 가입 여부를 확인하는 중첩 분기문이 있다고 가정해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1676859026617&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function loginService(isLogin, user) {
    let result = '';

    // 1. 로그인 여부 확인
    if (isLogin == false) {
        // 2. 토큰 존재 확인
        if (checkToken() == true) {
            // 3. 가입 여부 재확인
            if (user.nickName == undefined) {
                registerUser(user); // 회원 가입하기
                result = '회원가입 성공';
            } else {
                refreshToken(); // 토큰 발급
                result = '로그인 성공';
            }
        } else {
            throw new Error('에러 - 토큰이 없습니다 !');
        }
    } else {
        result = '이미 로그인 중';
    }

    result += ` (+ 시도 횟수 ${count++}번)`;
    return result;
}&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;272&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lKPUY/btrZIS2ezEx/gXzCnXN1994bewDZxMFPXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lKPUY/btrZIS2ezEx/gXzCnXN1994bewDZxMFPXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lKPUY/btrZIS2ezEx/gXzCnXN1994bewDZxMFPXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlKPUY%2FbtrZIS2ezEx%2FgXzCnXN1994bewDZxMFPXk%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; alt=&quot;js-Early-Return&quot; loading=&quot;lazy&quot; width=&quot;272&quot; height=&quot;480&quot; data-origin-width=&quot;272&quot; data-origin-height=&quot;480&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Early Return 리팩토링 순서&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;1. &lt;/b&gt;&lt;/span&gt;&lt;b&gt;if문 다음에 나오는&lt;/b&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt; 공통된 절차&lt;/span&gt;를 &lt;span style=&quot;color: #ee2323;&quot;&gt;첫번째 분기점 내부&lt;/span&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;span style=&quot;color: #ee2323;&quot;&gt;첫번째 분기점&lt;/span&gt;이란, 코드 레벨의 첫 if문을 말한다. 즉, if문 안의 if문 같은 경우 제외 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;452&quot; data-origin-height=&quot;558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/X9R4z/btrZLUUHYRh/jnLihakt0gNOMdHWT3TXaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/X9R4z/btrZLUUHYRh/jnLihakt0gNOMdHWT3TXaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/X9R4z/btrZLUUHYRh/jnLihakt0gNOMdHWT3TXaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FX9R4z%2FbtrZLUUHYRh%2FjnLihakt0gNOMdHWT3TXaK%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; alt=&quot;js-Early-Return&quot; loading=&quot;lazy&quot; width=&quot;452&quot; height=&quot;558&quot; data-origin-width=&quot;452&quot; data-origin-height=&quot;558&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1632844739190&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function loginService(isLogin, user) {
    let result = '';

    if (isLogin == false) {
        if (checkToken() == true) {
            if (user.nickName == undefined) {
                registerUser(user);
                result = '회원가입 성공';
            } else {
                refreshToken();
                result = '로그인 성공';
            }
        } else {
            throw new Error('에러 - 토큰이 없습니다 !');
        }

        result += ` (+ 시도 횟수 ${count++}번)`; // 공통된 절차
        return result; // 공통된 절차
    } else {
        result = '이미 로그인 중';

        result += ` (+ 시도 횟수 ${count++}번)`; // 공통된 절차
        return result; // 공통된 절차
    }
}&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;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt; 2.&lt;/span&gt; 분기점에서 짧은 절차부터 실행하게 if문을 반전 시킨다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;if문 과 else문을 블럭을 보면 코드 절차가 긴것은 if문이다. 따라서 절차가 짧은 else 블럭 코드 부분을 if 문에 넣어주기 위해 if-else를 반전 시킨다. 간단하게 기존의 &lt;s&gt;if&amp;nbsp;(isLogin&amp;nbsp;==&amp;nbsp;false)&lt;/s&gt; 를 거꾸로 &lt;s&gt;if (isLogin == true)&lt;/s&gt; 로 구성해주면 코드를 뒤바꿀 수 있게 되어 짧은 부분이 위쪽으로 오게 되고 길다란 부분이 아래쪽으로 오게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1632844822731&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function loginService(isLogin, user) {
    let result = '';

    if (isLogin == true) { // else와 뒤바꾸기 위하여 boolean 을 반전시킨다
        result = '이미 로그인 중';

        result += ` (+ 시도 횟수 ${count++}번)`;
        return result;
    } else {
        if (checkToken() == true) {
            if (user.nickName == undefined) {
                registerUser(user);
                result = '회원가입 성공';
            } else {
                refreshToken();
                result = '로그인 성공';
            }
        } else {
            throw new Error('에러 - 토큰이 없습니다 !');
        }

        result += ` (+ 시도 횟수 ${count++}번)`;
        return result;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만일 아래와 같이 첫 if문이 else문이 없는 순수 if문이라서 진행에 무리가 있다면, 아래와 같이 간단하게 빈 else문을 만들어 버리고 진행하면 된다. (그게 그거기 때문)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;329&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2G4C2/btrZQekEAM6/PzsJzXlIr8OSMxJ94wkkq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2G4C2/btrZQekEAM6/PzsJzXlIr8OSMxJ94wkkq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2G4C2/btrZQekEAM6/PzsJzXlIr8OSMxJ94wkkq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2G4C2%2FbtrZQekEAM6%2FPzsJzXlIr8OSMxJ94wkkq1%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; alt=&quot;js-Early-Return&quot; loading=&quot;lazy&quot; width=&quot;828&quot; height=&quot;329&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;329&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;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt; 3.&lt;/span&gt; 짧은 절차가 끝나면 return(함수 내부의 경우)이나 break(for문 내부의 경우) 로 일찍 중단점을 추가한다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본문의 예제는 함수문 이고, 이미 리턴문을 위로 올렸으니 그대로 진행하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;449&quot; data-origin-height=&quot;597&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ubtPO/btrZR1ThpIQ/Y4qbXkYZDsxlKAKgoFsZGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ubtPO/btrZR1ThpIQ/Y4qbXkYZDsxlKAKgoFsZGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ubtPO/btrZR1ThpIQ/Y4qbXkYZDsxlKAKgoFsZGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FubtPO%2FbtrZR1ThpIQ%2FY4qbXkYZDsxlKAKgoFsZGK%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; alt=&quot;js-Early-Return&quot; loading=&quot;lazy&quot; width=&quot;449&quot; height=&quot;597&quot; data-origin-width=&quot;449&quot; data-origin-height=&quot;597&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 data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt; 4. &lt;/span&gt;else를 제거한다. (이 때 중첩하나가 제거된다)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1632844878526&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function loginService(isLogin, user) {
    let result = '';

    if (isLogin == true) {
        result = '이미 로그인 중';

        result += ` (+ 시도 횟수 ${count++}번)`;
        return result;
    } // else { 를 제거한다

    if (checkToken() == true) {
        if (user.nickName == undefined) {
            registerUser(user); 
            result = '회원가입 성공';
        } else {
            refreshToken(); 
            result = '로그인 성공';
        }
    } else {
        throw new Error('에러 - 토큰이 없습니다 !');
    }

    result += ` (+ 시도 횟수 ${count++}번)`;
    return result;
}&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;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;5.&lt;span&gt; &lt;/span&gt;&lt;/span&gt;다시 1번 부터 돌아가 작업을 반복한다&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;555&quot; data-origin-height=&quot;601&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bw8nDL/btrZNv1HE1W/5Y8gcXS67CGs5q5HadTNVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bw8nDL/btrZNv1HE1W/5Y8gcXS67CGs5q5HadTNVk/img.png&quot; data-alt=&quot;공통된 절차를 첫번째 분기점 내부에 넣는다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bw8nDL/btrZNv1HE1W/5Y8gcXS67CGs5q5HadTNVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbw8nDL%2FbtrZNv1HE1W%2F5Y8gcXS67CGs5q5HadTNVk%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; alt=&quot;js-Early-Return&quot; loading=&quot;lazy&quot; width=&quot;555&quot; height=&quot;601&quot; data-origin-width=&quot;555&quot; data-origin-height=&quot;601&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;공통된 절차를 첫번째 분기점 내부에 넣는다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;537&quot; data-origin-height=&quot;597&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bE4ocb/btrZLTVSUep/O6WBAs74OnvdYBviNcBRvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bE4ocb/btrZLTVSUep/O6WBAs74OnvdYBviNcBRvk/img.png&quot; data-alt=&quot;분기점에서 짧은 절차부터 실행하게 if문을 반전한다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bE4ocb/btrZLTVSUep/O6WBAs74OnvdYBviNcBRvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbE4ocb%2FbtrZLTVSUep%2FO6WBAs74OnvdYBviNcBRvk%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; alt=&quot;js-Early-Return&quot; loading=&quot;lazy&quot; width=&quot;537&quot; height=&quot;597&quot; data-origin-width=&quot;537&quot; data-origin-height=&quot;597&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;분기점에서 짧은 절차부터 실행하게 if문을 반전한다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;444&quot; data-origin-height=&quot;601&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/szK3b/btrZ657I467/TZBQw1zsk3DPUV6FuljYv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/szK3b/btrZ657I467/TZBQw1zsk3DPUV6FuljYv1/img.png&quot; data-alt=&quot;else를 제거한다 (중첩이 하나 제거된다)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/szK3b/btrZ657I467/TZBQw1zsk3DPUV6FuljYv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FszK3b%2FbtrZ657I467%2FTZBQw1zsk3DPUV6FuljYv1%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; alt=&quot;js-Early-Return&quot; loading=&quot;lazy&quot; width=&quot;444&quot; height=&quot;601&quot; data-origin-width=&quot;444&quot; data-origin-height=&quot;601&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;else를 제거한다 (중첩이 하나 제거된다)&lt;/figcaption&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;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;6.&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;리팩토링 결과&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 중첩 조건문들을 하나하나 꺼내면서 리팩토링한 결과 코드의 모습은 마치 아래와 같이 중첩되지 않고 하나의 레벨에서 각 조건식을 판별하는 형태가 되게 된다. 이러헥 구성하면 로직이 의미하고자 하는 바를 한눈에 알 수 있고 조건식을 중첩하여 생각하지않고 분리하여 생각함으로써 코드를 빠르게 복기할 수 있게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1676881011834&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function loginService(isLogin, user) {
    let result = '';

    // 1. 로그인 여부 확인
    if (isLogin == true) {
        result = '이미 로그인 중';

        result += ` (+ 시도 횟수 ${count++}번)`;
        return result;
    }

    // 2. 토큰 존재 확인
    if (checkToken() == false) {
        throw new Error('에러 - 토큰이 없습니다 !');
    }

    // 3. 가입 여부 재확인
    if (user.nickName == undefined) {
        registerUser(user); // 회원 가입하기
        result = '회원가입 성공';
    } else {
        refreshToken(); // 토큰 발급
        result = '로그인 성공';
    }

    result += ` (+ 시도 횟수 ${count++}번)`;
    return result;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;263&quot; data-origin-height=&quot;510&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NGMWq/btrZKp6JnnQ/GiharZKAYX0IYPcdTXcQc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NGMWq/btrZKp6JnnQ/GiharZKAYX0IYPcdTXcQc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NGMWq/btrZKp6JnnQ/GiharZKAYX0IYPcdTXcQc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNGMWq%2FbtrZKp6JnnQ%2FGiharZKAYX0IYPcdTXcQc1%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; alt=&quot;js-Early-Return&quot; loading=&quot;lazy&quot; width=&quot;263&quot; height=&quot;510&quot; data-origin-width=&quot;263&quot; data-origin-height=&quot;510&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;444&quot; data-origin-height=&quot;601&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cH2N4m/btrZPTBlfeI/Gm6aJcfl94zJcaNnjTAVVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cH2N4m/btrZPTBlfeI/Gm6aJcfl94zJcaNnjTAVVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cH2N4m/btrZPTBlfeI/Gm6aJcfl94zJcaNnjTAVVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcH2N4m%2FbtrZPTBlfeI%2FGm6aJcfl94zJcaNnjTAVVk%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; alt=&quot;js-Early-Return&quot; loading=&quot;lazy&quot; width=&quot;444&quot; height=&quot;601&quot; data-origin-width=&quot;444&quot; data-origin-height=&quot;601&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3번째 분기인 가입 여부 재확인 부분은 다시한번 리팩토링을 하여 else를 없애도 되고 안없애도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Early Return 기법은 비단 자바스크립트 프로그래밍 언어 뿐만 아니라 다른 프로그래밍 언어에도 당연히 똑같이 적용되는 부분이니 연습해두면 좋다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;271c&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Lookup Table 기법&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 좀더 자바스크립트 스러운 조건문 클린 코드를 소개해볼 것이다. &lt;span&gt;이 리팩토링 기법은 자바스크립트 프로그래밍 언어만의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/CS-%F0%9F%91%A8%E2%80%8D%F0%9F%92%BB-%EC%9D%BC%EA%B8%89-%EA%B0%9D%EC%B2%B4first-class-object&quot;&gt;일급 객체&lt;/a&gt;의 특성을 이용한 방법으로, &lt;span style=&quot;color: #ee2323;&quot;&gt;기존의 else if 로직을&amp;nbsp;key-value 쌍의 형태&lt;/span&gt;로&lt;span&gt;&amp;nbsp;&lt;/span&gt;각각의 논리를 캡슐화한 것이다. 마치 객체 테이블을 조회하여 분기를 실행하는것과 같다하여 Lookup Table 기법이라 불리운다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래와 같이 각 소셜 서비스들의 전용 로그인 함수들이 있다고 가정하고, &lt;s&gt;socialLogin()&lt;/s&gt; 함수에서 하나의 매개변수 &lt;s&gt;where&lt;/s&gt;에 대해 조건별로 로그인 분기가 일자 형태로 나뉘어져 있다고 하자.&lt;/p&gt;
&lt;pre id=&quot;code_1676619756095&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ... 복잡한 로그인 과정 생략한 모듈
const naverLogin = (id) =&amp;gt; { return '네이버'; };
const kakaoLogin = (id) =&amp;gt; { return '카카오'; };
const facebookLogin = (id) =&amp;gt; { return '페이스북'; };
const googleLogin = (id) =&amp;gt; { return '구글'; };

const socialLogin = (where, id) =&amp;gt; {
    let domain;

    if (where === 'naver') {
        domain = naverLogin(id);
    } else if (where === 'kakao') {
        domain = kakaoLogin(id);
    } else if (where === 'facebook') {
        domain = facebookLogin(id);
    } else if (where === 'google') {
        domain = googleLogin(id);
    }

    return `${domain} ${id}`;
};

console.log(socialLogin('naver', 'inpa'));
console.log(socialLogin('google', 'inpa'));&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;432&quot; data-origin-height=&quot;76&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqYRtQ/btrZIsJJVyo/7KQWzxP5g9RGssI3P1UM31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqYRtQ/btrZIsJJVyo/7KQWzxP5g9RGssI3P1UM31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqYRtQ/btrZIsJJVyo/7KQWzxP5g9RGssI3P1UM31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqYRtQ%2FbtrZIsJJVyo%2F7KQWzxP5g9RGssI3P1UM31%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; alt=&quot;js-Lookup-Table&quot; loading=&quot;lazy&quot; width=&quot;432&quot; height=&quot;76&quot; data-origin-width=&quot;432&quot; data-origin-height=&quot;76&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;else if의 연속으로, &lt;/span&gt;이 코드 모습을 대충 그림으로 흐름도를 표현하자면 아래와 같이 될 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cm2162/btrZHRYymIc/77Q3InzXZFFiPJlGFqgyV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cm2162/btrZHRYymIc/77Q3InzXZFFiPJlGFqgyV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cm2162/btrZHRYymIc/77Q3InzXZFFiPJlGFqgyV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcm2162%2FbtrZHRYymIc%2F77Q3InzXZFFiPJlGFqgyV1%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; alt=&quot;js-Lookup-Table&quot; loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;246&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;246&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Lookup Table 리팩토링 순서&lt;/b&gt;&lt;/h3&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;&lt;span style=&quot;color: #ef5369;&quot;&gt;1.&lt;/span&gt; 조건문만을 따로 분리한다&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 &lt;s&gt;socialLogin()&lt;/s&gt; 함수내 분기문 코드만 따로 뽑아내어 별도의 &lt;s&gt;executeLogin()&lt;/s&gt; 함수로 &lt;b&gt;분리&lt;/b&gt;해 구성해준다. 그러면 기존 &lt;s&gt;socialLogin()&lt;/s&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수는 &lt;s&gt;executeLogin()&lt;/s&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수를 호출해 결과값을 받는 형식이 된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1676867700523&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const executeLogin = (where, id) =&amp;gt; {
    if (where === 'naver') {
        domain = naverLogin(id);
    } else if (where === 'kakao') {
        domain = kakaoLogin(id);
    } else if (where === 'facebook') {
        domain = facebookLogin(id);
    } else if (where === 'google') {
        domain = googleLogin(id);
    }
};

const socialLogin = (where, id) =&amp;gt; {
    let domain = executeLogin(where, id);
    return `${domain} ${id}`;
};&lt;/code&gt;&lt;/pre&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;&lt;span style=&quot;color: #ef5369;&quot;&gt;2.&lt;/span&gt; if-else 문을 switch-case 문으로 변환한다&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중첩 if문이 아닌 형태는 switch 문으로 변환이 가능하다. 변환하면 아래와 같이 구성될 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1676625186060&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const executeLogin = (where, id) =&amp;gt; {
    switch (where) {
        case 'naver':
            return naverLogin(id);
        case 'kakao':
            return kakaoLogin(id);
        case 'facebook':
            return facebookLogin(id);
        case 'google':
            return googleLogin(id);
    }
};

const socialLogin = (where, id) =&amp;gt; {
    let domain = executeLogin(where, id);
    return `${domain} ${id}`;
};&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;참고로 자바스크립트에서 switch-case문일 경우, 다른 언어에서는 해당 케이스로 jump를 해서 바로 원하는 곳으로 이동하지만, js는 case를 나열된 순서대로 평가하기 때문에 사용을 지양하라는 설도 있다.&lt;/blockquote&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;&lt;span style=&quot;color: #ef5369;&quot;&gt;3.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;switch-case 문을 객체로 변환한다&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;switch문을 잘보면 각 문자열 case마다 이에 맞는 함수를 호출하고 있다는 규칙성을 찾을 수 있다. 따라서 case 부분을 객체의 key 값으로, 리턴문을 객체의 value 값으로 구성해준다면 아래와 같이 자바스크립트 객체로 똑같이 구성이 가능하게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1676625192736&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const executeLoginMap = {
    naver: naverLogin,
    kakao: kakaoLogin,
    facebook: facebookLogin,
    google: googleLogin,
};

const socialLogin = (where, id) =&amp;gt; {
    let domain = executeLoginMap[where](id); // naver일 경우 naverLogin(id) 로 함수 실행
    return `${domain} ${id}`;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그리고 기존&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;s&gt;socialLogin()&lt;/s&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;executeLoginMap&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;객체를 호출 할때 배열 인자를 통해 호출할 key값을 얻게하고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;executeLoginMap[where]&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;통해 얻은 객체의 value값인 함수 표현식을 가져와 그대로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;(id)&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;매개변수를 할당하여 함수를 실행함으로써 결과값을 얻는, 마치 아름다운(?) 코드 디자인 패턴의 일종이라고 볼 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;616&quot; data-origin-height=&quot;316&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tGcnN/btrZJ2DWUNt/YKOLm0c5XzsHDSdrbZqZI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tGcnN/btrZJ2DWUNt/YKOLm0c5XzsHDSdrbZqZI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tGcnN/btrZJ2DWUNt/YKOLm0c5XzsHDSdrbZqZI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtGcnN%2FbtrZJ2DWUNt%2FYKOLm0c5XzsHDSdrbZqZI0%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; alt=&quot;lookuptable&quot; loading=&quot;lazy&quot; width=&quot;616&quot; height=&quot;316&quot; data-origin-width=&quot;616&quot; data-origin-height=&quot;316&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 그림과 비교하자면, 기존에는 올바른 분기를 검사하기 위해 일일히 각 A, B, C 분기를 일일히 모두 순회하여야 하지만, Lookup Table 기법을 적용하면 모든 조건을 순회할 필요없이 매개변수의 값에 따라 이에 해당하는 객체의 key-value 구성에 따라 각각의 논리는 별도의 함수로 다시 작성되므로 함수를 호출하는 식으로 구성하였기 때문에, 프로세스가 아래 그림과 같이 병렬적으로 분기에 접근하게 되는 형태가 되고 이는 곧 &lt;span style=&quot;color: #006dd7;&quot;&gt;성능 향상&lt;/span&gt;으로 직결되게 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;b707&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Responsibility chain pattern&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 Lookup Table 기법은 매치된 분기가 있으면 그 분기만 실행하고 함수를 종료한다. 그리고 key 매칭 방식을 사용하기 때문에 하나의 인자값(where)에 대해서만 비교를 하니 만일 &lt;span style=&quot;color: #ee2323;&quot;&gt;여러개의 인자를 비교한다고 하면 적합하지가 않게&lt;/span&gt; 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이에 대한 대응 기법으로 &lt;b&gt;책임 연쇄 패턴(Chain Responsility Pattern)&lt;/b&gt;이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;책임 연쇄 패턴은 GOF 디자인 패턴의 한 종류로서, 분기문의 블럭들을 객체화 하여, 다수의 처리 객체(핸들러)들을 체인 형태로 묶는 패턴이다. 그래서 &lt;span style=&quot;background-color: #ffffff; color: #343a40;&quot;&gt;어떠한 요청이 발생하였을 때 그 요구를 처리할 객체 핸들러를 순회하는 식으로 하여 분기문을 객체 지향적으로 표현한 기법이라고 보면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6gAFf/btr0n6MzrXA/4Q6zkKnkQKyRhi44ijIiG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6gAFf/btr0n6MzrXA/4Q6zkKnkQKyRhi44ijIiG1/img.png&quot; data-alt=&quot;각 네모 박스의 핸들러들이 연쇄적으로 작용된다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6gAFf/btr0n6MzrXA/4Q6zkKnkQKyRhi44ijIiG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6gAFf%2Fbtr0n6MzrXA%2F4Q6zkKnkQKyRhi44ijIiG1%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; alt=&quot;Responsibility chain pattern&quot; loading=&quot;lazy&quot; width=&quot;673&quot; height=&quot;250&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;250&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;위의 Early Return 예제의 결과를 다시 가져와 보았다.&lt;/p&gt;
&lt;pre id=&quot;code_1677044075294&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const refreshToken = () =&amp;gt; {};
const registerUser = () =&amp;gt; {};

function loginService(isLogin, checkToken, user) {
    let result = '';
    let count = 0;

    // 1. 로그인 여부 확인
    if (isLogin == true) {
        result += '이미 로그인 중';
        result += ` (+ 시도 횟수 ${++count}번)`;
        return result;
    }

    // 2. 토큰 존재 확인
    if (checkToken == false) {
        throw new Error('에러 - 토큰이 없습니다 !');
    }

    // 3. 가입 여부 재확인
    if (user.nickName == undefined) {
        registerUser(user); // 회원 가입하기
        result += '회원가입 성공';
        result += ` (+ 시도 횟수 ${++count}번)`;
        return result;
    }

    refreshToken(); // 토큰 발급
    result += '로그인 성공';
    result += ` (+ 시도 횟수 ${++count}번)`;
    return result;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1677047414229&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(loginService(false, true, { nickName: 'inpa' })); // 로그인 성공 (+ 시도 횟수 1번)
console.log(loginService(true, false, { nickName: 'inpa' })); // 이미 로그인 중 (+ 시도 횟수 1번)
console.log(loginService(false, true, {})); // 회원가입 성공 (+ 시도 횟수 1번)
console.log(loginService(false, false, { nickName: 'inpa' })); // Error: 에러 - 토큰이 없습니다 !&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;544&quot; data-origin-height=&quot;116&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dAwP2F/btr0glSdXWz/lKhdRCDRWqwxVUpMkKkhVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dAwP2F/btr0glSdXWz/lKhdRCDRWqwxVUpMkKkhVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dAwP2F/btr0glSdXWz/lKhdRCDRWqwxVUpMkKkhVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdAwP2F%2Fbtr0glSdXWz%2FlKhdRCDRWqwxVUpMkKkhVk%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; alt=&quot;Responsibility chain pattern&quot; loading=&quot;lazy&quot; width=&quot;544&quot; height=&quot;116&quot; data-origin-width=&quot;544&quot; data-origin-height=&quot;116&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;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;따라서 위의 Early Return 예제의 결과를 책임 연쇄 패턴으로 재구성 해보자. 코드를 자바(Java) 프로그래밍 언어 스럽게 클래스를 이용해 설계하자면 다음과 같다.&lt;/p&gt;
&lt;figure id=&quot;og_1680254027108&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;[GOF]   Chain Of Responsibility 패턴 - 완벽 마스터하기&quot; data-og-description=&quot;Chain Of Responsibility Pattern 책임 연쇄 패턴(Chain Of Responsibility Pattern, COR)은 클라이어트의 요청에 대한 세세한 처리를 하나의 객체가 몽땅 하는 것이 아닌, 여러개의 처리 객체들로 나누고, 이들을 사&quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-Chain-Of-Responsibility-%ED%8C%A8%ED%84%B4-%EC%99%84%EB%B2%BD-%EB%A7%88%EC%8A%A4%ED%84%B0%ED%95%98%EA%B8%B0&quot; data-og-url=&quot;https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-Chain-Of-Responsibility-%ED%8C%A8%ED%84%B4-%EC%99%84%EB%B2%BD-%EB%A7%88%EC%8A%A4%ED%84%B0%ED%95%98%EA%B8%B0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bkKKLm/hyR6MPlFyo/Xa0SkXb8njUxVpzZzaMA30/img.jpg?width=800&amp;amp;height=426&amp;amp;face=0_0_800_426,https://scrap.kakaocdn.net/dn/bcyTfc/hyR6RiP3cw/sifyf093zCEKCUwKYqd6uK/img.jpg?width=800&amp;amp;height=426&amp;amp;face=0_0_800_426,https://scrap.kakaocdn.net/dn/dFylZ3/hyR6L3Wv8l/Ymu09WCTfvUOYix09UzNXk/img.png?width=688&amp;amp;height=833&amp;amp;face=0_0_688_833&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-Chain-Of-Responsibility-%ED%8C%A8%ED%84%B4-%EC%99%84%EB%B2%BD-%EB%A7%88%EC%8A%A4%ED%84%B0%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-Chain-Of-Responsibility-%ED%8C%A8%ED%84%B4-%EC%99%84%EB%B2%BD-%EB%A7%88%EC%8A%A4%ED%84%B0%ED%95%98%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bkKKLm/hyR6MPlFyo/Xa0SkXb8njUxVpzZzaMA30/img.jpg?width=800&amp;amp;height=426&amp;amp;face=0_0_800_426,https://scrap.kakaocdn.net/dn/bcyTfc/hyR6RiP3cw/sifyf093zCEKCUwKYqd6uK/img.jpg?width=800&amp;amp;height=426&amp;amp;face=0_0_800_426,https://scrap.kakaocdn.net/dn/dFylZ3/hyR6L3Wv8l/Ymu09WCTfvUOYix09UzNXk/img.png?width=688&amp;amp;height=833&amp;amp;face=0_0_688_833');&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;[GOF]   Chain Of Responsibility 패턴 - 완벽 마스터하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Chain Of Responsibility Pattern 책임 연쇄 패턴(Chain Of Responsibility Pattern, COR)은 클라이어트의 요청에 대한 세세한 처리를 하나의 객체가 몽땅 하는 것이 아닌, 여러개의 처리 객체들로 나누고, 이들을 사&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;inpa.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;pre id=&quot;code_1677050370237&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Handler {
    nextHandler; // 연결될 다음 핸들러 객체를 저장하는 필드
    result = '';
    count = 0;

    setNext(handler) {
        this.nextHandler = handler; // 메서드를 통해 연결할 핸들러를 등록한다
        return handler; // 메서드 체이닝을 위해 인자의 핸들러를 리턴한다
    }
}

class LoginHandler extends Handler {
    check(isLogin, checkToken, user) {
        // 1. 로그인 여부 확인
        if (isLogin == true) {
            this.result = '이미 로그인 중';
            this.result += ` (+ 시도 횟수 ${++this.count}번)`;
            return this.result;
        } else {
            return this.nextHandler.check(isLogin, checkToken, user);
        }
    }
}

class TokenHandler extends Handler {
    check(isLogin, checkToken, user) {
        // 2. 토큰 존재 확인
        if (checkToken == false) {
            throw new Error('에러 - 토큰이 없습니다 !');
        } else {
            return this.nextHandler.check(isLogin, checkToken, user);
        }
    }
}

class JoinHandler extends Handler {
    check(isLogin, checkToken, user) {
        // 3. 가입 여부 재확인
        if (user.nickName == undefined) {
            registerUser(user); // 회원 가입하기
            this.result = '회원가입 성공';
            this.result += ` (+ 시도 횟수 ${++this.count}번)`;
            return this.result;
        } else {
            return this.nextHandler.check(isLogin, checkToken, user);
        }
    }
}
class SuccessHandler extends Handler {
    check(isLogin, checkToken, user) {
        refreshToken(); // 토큰 발급
        this.result = '로그인 성공';
        this.result += ` (+ 시도 횟수 ${++this.count}번)`;
        return this.result;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1677050376753&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const loginService = (isLogin, checkToken, user) =&amp;gt; {
    let result = '';

    // 1. 핸들러(처리 객체) 생성
    const handler1 = new LoginHandler();
    const handler2 = new TokenHandler();
    const handler3 = new JoinHandler();
    const handler4 = new SuccessHandler();

    // 2. 핸들러 체이닝 등록
    handler1.setNext(handler2).setNext(handler3).setNext(handler4);

    // 3. 핸들러 실행
    result = handler1.check(isLogin, checkToken, user); // handler1 &amp;rarr; handler2 &amp;rarr; handler3 &amp;rarr; handler4

    return result;
};

console.log(loginService(false, true, { nickName: 'inpa' })); // 로그인 성공 (+ 시도 횟수 1번)
console.log(loginService(true, false, { nickName: 'inpa' })); // 이미 로그인 중 (+ 시도 횟수 1번)
console.log(loginService(false, true, {})); // 회원가입 성공 (+ 시도 횟수 1번)
console.log(loginService(false, false, { nickName: 'inpa' })); // Error: 에러 - 토큰이 없습니다 !&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #292c32;&quot;&gt;&lt;span&gt;각 핸들러들이 자기 고유의 요청에 대한 처리 책임을 지니고 있으며, 실행부에서 setNext() 메서드를 통해 핸들러를 체인 시켜준다. 그리고 실행 함수를 호출하면 마치 if-else 처럼 분기를 객체마다 순회하면서 처리를 하게 되는 것이다. &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #292c32;&quot;&gt;이런 식으로 복합문 else if 논리를 분리하는 데 성공했다.&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;b&gt;좀더 심플한 책임 연쇄 패턴 설계&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 자바스크립트는 &lt;a href=&quot;https://inpa.tistory.com/entry/CS-%F0%9F%91%A8%E2%80%8D%F0%9F%92%BB-%EC%9D%BC%EA%B8%89-%EA%B0%9D%EC%B2%B4first-class-object&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;일급 객체&lt;/a&gt;의 특징을 갖는 언어로서 굳이 클래스 지향으로 설계할 필요가 없다. 이 부분은 모든 GOF 디자인 패턴의 단점이기도 한데, 당장 위의 코드만 봐도 엄청나게 길어진걸 확인 할 수 있다. 따라서 일반 객체를 이용해 다음과 같이 좀 더 자바스크립트 프로그래밍 답게 심플하게 리팩토링 할 수가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 객체를 담은 배열을 생성하고, 원소 객체마다 match와 action이라는 함수를 각각 만들어 준다. match 부분은 if문의 조건식을 넣는 부분이고, action은 if문 블록 안의 실행 코드를 넣는 부분이다. match는 false일 경우 다음 match로 넘어가게 된다. action은 match가 true 일경우 실행되고 평가를 종료하게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1680256077409&quot; class=&quot;actionscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const rules = [
    {
        match: function (a, b, c) { /* ... */ },
        action: function (a, b, c) { /* ... */ }
    },
    {
        match: function (a, b, c) { /* ... */ },
        action: function (a, b, c) { /* ... */ }
    },
    {
        match: function (a, b, c) { /* ... */ },
        action: function (a, b, c) { /* ... */ }
    }
]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;448&quot; data-origin-height=&quot;338&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IGzUq/btr0pvSGlOQ/w3sJPWtMk8kMWicAE9FKPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IGzUq/btr0pvSGlOQ/w3sJPWtMk8kMWicAE9FKPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IGzUq/btr0pvSGlOQ/w3sJPWtMk8kMWicAE9FKPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIGzUq%2Fbtr0pvSGlOQ%2Fw3sJPWtMk8kMWicAE9FKPK%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; alt=&quot;Responsibility chain pattern&quot; loading=&quot;lazy&quot; width=&quot;448&quot; height=&quot;338&quot; data-origin-width=&quot;448&quot; data-origin-height=&quot;338&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;555&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tGlnT/btr0klDUPsE/w9qU7FcFNkDDmI2a5QkAy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tGlnT/btr0klDUPsE/w9qU7FcFNkDDmI2a5QkAy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tGlnT/btr0klDUPsE/w9qU7FcFNkDDmI2a5QkAy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtGlnT%2Fbtr0klDUPsE%2Fw9qU7FcFNkDDmI2a5QkAy1%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; alt=&quot;Responsibility chain pattern&quot; loading=&quot;lazy&quot; width=&quot;654&quot; height=&quot;555&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;555&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_1677050595789&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const Handler = {
    result: '',
    count: 0,
    rules: [
        {
            // match는 false일 경우 다음 match로 넘어가게 된다.
            match(isLogin, checkToken, user) {
                return isLogin ? true : false; // 1. 로그인 여부 확인
            },
            // action은 match가 true 인 실행 부분을 기재한다
            action(isLogin, checkToken, user) {
                Handler.result = '이미 로그인 중';
                Handler.result += ` (+ 시도 횟수 ${Handler.count++}번)`;
                return Handler.result;
            },
        },
        {
            match(isLogin, checkToken, user) {
                return !checkToken ? true : false;  // 2. 토큰 존재 확인
            },
            action(isLogin, checkToken, user) {
                throw new Error('에러 - 토큰이 없습니다 !');
            },
        },
        {
            match(isLogin, checkToken, user) {
                return user.nickName == undefined ? true : false; // 3. 가입 여부 재확인
            },
            action(isLogin, checkToken, user) {
                registerUser(user); // 회원 가입하기
                Handler.result = '회원가입 성공';
                Handler.result += ` (+ 시도 횟수 ${++Handler.count}번)`;
                return Handler.result;
            },
        },
        {
            match(isLogin, checkToken, user) {
                return true; // 마지막 핸들러 부분이니까 바로 action() 이 실행되도록
            },
            action(isLogin, checkToken, user) {
                refreshToken(); // 토큰 발급
                Handler.result = '로그인 성공';
                Handler.result += ` (+ 시도 횟수 ${++Handler.count}번)`;
                return Handler.result;
            },
        },
    ],
};&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1677050604558&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const socialLogin = (isLogin, checkToken, user) =&amp;gt; {
    let result = '';

    // {march, action} 핸들러 객체가 들어있는 의사 결정 규칙 배열 rules를 순회하면서
    for (const rule of Handler.rules) {
        // 만일 해당 분기에 해당되면
        if (rule.match(isLogin, checkToken, user)) {
            result += rule.action(isLogin, checkToken, user); // 그 분기의 실행부를 호출한다
            return result;
        }
    }
};

console.log(loginService(false, true, { nickName: 'inpa' })); // 로그인 성공 (+ 시도 횟수 1번)
console.log(loginService(true, false, { nickName: 'inpa' })); // 이미 로그인 중 (+ 시도 횟수 1번)
console.log(loginService(false, true, {})); // 회원가입 성공 (+ 시도 횟수 1번)
console.log(loginService(false, false, { nickName: 'inpa' })); // Error: 에러 - 토큰이 없습니다 !&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot; data-swap-href=&quot;https://youtu.be/p0YEviesgRM&quot;&gt;FE재남 유튜브 - if else 리팩토링&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://javascript.plainenglish.io/how-to-refactor-your-complex-nested-if-else-code-28aa162047d5&lt;/p&gt;</description>
      <category>Language/JavaScript</category>
      <category>if문 최적화</category>
      <category>리팩토링</category>
      <category>분기문 최적화</category>
      <category>조건문 최적화</category>
      <category>클린 코드</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1090</guid>
      <comments>https://inpa.tistory.com/entry/%E2%9A%99%EF%B8%8F-if-else-refactoring#entry1090comment</comments>
      <pubDate>Thu, 13 Apr 2023 08:19:59 +0900</pubDate>
    </item>
    <item>
      <title> &amp;zwj;  멀티 태스킹 &amp;amp; 멀티 프로세싱 개념 한방 정리</title>
      <link>https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-multi-programming-tasking-processing</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;10_J59-Multitasking_1200x675.webp&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;675&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8E2Wx/btr60h9iv9P/NdZXUAPl3IpixSXA63yoCK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8E2Wx/btr60h9iv9P/NdZXUAPl3IpixSXA63yoCK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8E2Wx/btr60h9iv9P/NdZXUAPl3IpixSXA63yoCK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8E2Wx%2Fbtr60h9iv9P%2FNdZXUAPl3IpixSXA63yoCK%2Fimg.webp&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; alt=&quot;Multi-tasking&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;675&quot; data-filename=&quot;10_J59-Multitasking_1200x675.webp&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;675&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;컴퓨터의 병행 처리&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; 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;음악을 들으면서, 웹서핑을 하고, 메신저의 메시지를 확인할 수 있는 이유는 컴퓨터가 자원을 효율적으로 사용하는 멀티 태스킹(Multi Tasking) 기술 덕분이다. 멀티 태스킹이란 컴퓨터에서 여러 작업을 동시에 실행하는 능력을 말한다. 좀더 자세히 말하자면,&amp;nbsp;OS를 통해 CPU가 작업하는데 필요한 자원을 프로세스 또는 스레드간에 나누는 행위를 말한다. 두 가지 이상 작업을 동시에 처리할 수 있는 이유가 이러한 자원 분담 원리 때문이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;지금 부터 제한된 자원을 가지고 수십가지의 프로그램을 동시에 실행할 수 있는 원리를 파헤쳐 보자.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VdpCZ/btr5RSPjThz/BupKIrGcwKB8w34hCOb841/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VdpCZ/btr5RSPjThz/BupKIrGcwKB8w34hCOb841/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VdpCZ/btr5RSPjThz/BupKIrGcwKB8w34hCOb841/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVdpCZ%2Fbtr5RSPjThz%2FBupKIrGcwKB8w34hCOb841%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; alt=&quot;Multi-tasking&quot; loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;900&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&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;style1&quot; /&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;프로세싱의 발전 과정&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;단일 프로세스 (Single Process)&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;초창기 컴퓨터는 단일 프로세스 시스템 이었다. 한번에 하나의 프로그램만이 실행할 수 있어서, 또 다른 프로그램을 실행하고 싶다면 현재 프로그램을 종료해야 했다. 거기다 CPU 사용률이 효율적이지 못했는데, 프로그램 실행 도중 I/O(입출력) 작업과 같은 일이 생기게 되면 CPU는 프로세스를 멈추고 그대로 놀아버리기 때문이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;IO(input/output) 이란?&lt;/b&gt;&lt;br /&gt;흔히 프로그래밍에서 IO 라고 하면 대표적으로 3가지 뜻을 내포하고 있다.&lt;br /&gt;- 파일을 읽고 쓰는 것&lt;br /&gt;- 서버 네트워크의 어딘가와 데이터를 주고 받는 것&lt;br /&gt;- 마우스, 키보드와 같은 입출력 장치와 데이터를 주거나 받는 것&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;241&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/U7aZ1/btr5OcPeOkq/NZRSiL9tyKEZpvtvuuQ5bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/U7aZ1/btr5OcPeOkq/NZRSiL9tyKEZpvtvuuQ5bk/img.png&quot; data-alt=&quot;작업 도중 I/O 작업을 시작하게 되면 끝날 때까지 기다려야 한다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/U7aZ1/btr5OcPeOkq/NZRSiL9tyKEZpvtvuuQ5bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FU7aZ1%2Fbtr5OcPeOkq%2FNZRSiL9tyKEZpvtvuuQ5bk%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; alt=&quot;Single Process&quot; loading=&quot;lazy&quot; width=&quot;828&quot; height=&quot;241&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;241&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;작업 도중 I/O 작업을 시작하게 되면 끝날 때까지 기다려야 한다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;멀티 프로그래밍 (Multi Programming)&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이러한 문제를 해결하기 위해 여러개의 프로그램을 메모리에 올려놓고 &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;하나의 CPU에서 번갈아 가며&amp;nbsp;&lt;/span&gt;동시에 실행시키는 방안을 첨가 하였다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;멀티 프로그래밍은 여러개의 프로그램이 동시에 실행된다는 의미로서,&amp;nbsp;CPU 사용률을 극대화 시키는게 목적을 두었다. 단일 프로세스의 문제점이었던&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;프로세스 #1 작업 시작 후 I/O 작업을 만나게 되면 CPU가 노는게 아니라 프로세스 #2 작업을 시작하게 된다. 다시&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;프로세스2&amp;nbsp;작업&amp;nbsp;시작&amp;nbsp;후&amp;nbsp;I/O&amp;nbsp;작업을&amp;nbsp;만나게&amp;nbsp;되면&amp;nbsp;프로세스1&amp;nbsp;작업을&amp;nbsp;시작하게&amp;nbsp;된다.&amp;nbsp;&lt;/span&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;image (1).png&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;241&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cYLtGL/btr5Q4Jdf5Z/C5rgpeZrkKhNNroOJkmFu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cYLtGL/btr5Q4Jdf5Z/C5rgpeZrkKhNNroOJkmFu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cYLtGL/btr5Q4Jdf5Z/C5rgpeZrkKhNNroOJkmFu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcYLtGL%2Fbtr5Q4Jdf5Z%2FC5rgpeZrkKhNNroOJkmFu1%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; alt=&quot;Multi Programming&quot; loading=&quot;lazy&quot; width=&quot;828&quot; height=&quot;241&quot; data-filename=&quot;image (1).png&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;241&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;그러나 어느 한 프로그램이 CPU 사용 시간이 길어지면 다른 프로세스는 계속 대기를 해야 한다는 문제점이 존재 하였다. 위의 그림에서 P #1 작업 시간이 엄청 길어질 경우 P #2 는 실행될 기회조차 못 얻게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;멀티 태스킹 (Multi Tasking)&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;멀티 프로그래밍의 취지는 좋았지만 어느한 프로세스가 모두 실행되야 다음 프로세스가 실행될 수 있는 한계점이 있었다. 따라서&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;이것을 해결하기 위해 프로세스는 한번 CPU를 사용할때 아주 짧은 시간(quantum)만 실행되도록 하여 &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;각 프로그램의 작업을 &lt;span style=&quot;color: #ee2323;&quot;&gt;아주 작은 단위로 번갈아 가며 처리&lt;/span&gt;하면서, &lt;b&gt;작업 응답 시간을 최소화&lt;/b&gt; 시키는 멀티 태스킹 원리가 탄생하였다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;이때 잘게 나뉘어진 프로세스 끼리 작업이 스위칭 되는 것을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;b&gt;문맥 교환(Context Switching)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;이라고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;268&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boH2Gy/btr5OdtOvc8/DFiYGND38etVtHrjS3QZHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boH2Gy/btr5OdtOvc8/DFiYGND38etVtHrjS3QZHK/img.png&quot; data-alt=&quot;프로세스를 아주 잘게 쪼개 번갈아가며 처리&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boH2Gy/btr5OdtOvc8/DFiYGND38etVtHrjS3QZHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboH2Gy%2Fbtr5OdtOvc8%2FDFiYGND38etVtHrjS3QZHK%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; alt=&quot;Multi Tasking&quot; loading=&quot;lazy&quot; width=&quot;828&quot; height=&quot;268&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;268&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;프로세스를 아주 잘게 쪼개 번갈아가며 처리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 프로세스 &lt;span style=&quot;color: #ee2323;&quot;&gt;작업이 잘게 나뉘어져 교대로 실행&lt;/span&gt;함으로써 얻게 되면 이점은 다음과 같다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;1.&lt;/span&gt; 빠른 반응성 제공&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;첫째, CPU가 잘게 나눈 각 작업들을 빠르게 번갈아 가며 수행함으로서, 여러 작업을 동시에 처리하는 것처럼 보이게 만들어, 사용자에게 더 빠른 반응성을 제공하여 &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;정말로 동시에 프로그램이 실행되는 것처럼 느껴 차이를 못느끼게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;2.&lt;/span&gt; 문제 해결 용이성&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;둘째, 작업을 나누었기 때문에 예기치 않은 문제가 발생했을 때, 해당 작업만 중지할 수 있게 된다. 이러한 방식으로, 전체 시스템이 멈추는 것을 방지할 수도 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;3.&lt;/span&gt; 멀티 프로세서의 효율적인 활용&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;셋째, &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;멀티 프로세싱 또는 멀티 코어를 사용하는 시스템에서&lt;span&gt; &lt;/span&gt;&lt;/span&gt;여러 개의 프로세서(CPU)가 동시에 작업을 효율적으로 처리할 수 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;4.&lt;/span&gt; 프로세스 우선순위 조절&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;넷째, 프로세스 간의 우선순위를 조절이 수월해진다. 예를들어 우선순위가 높은 작업은 더 자주 실행되어, 더 빠른 처리를 할 수도 있는 셈이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;따라서, 작업을 작게 나누어 처리하는 원리는 빠른 반응성을 제공하는 핵심 부분인 셈이다. 그러나 &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;응답성을 향상하기 위해서 문맥 교환(Context Switching)이 자주 일어나게 될 경우&amp;nbsp;&lt;/span&gt;오히려 성능 이슈가 발생할 수 있다는 문제점이 있다. 왜냐하면 프로세스 끼리 스위칭 하는 작업은 우리가 생각하는 것 보다 꽤나 &lt;b&gt;무거운 작업&lt;/b&gt;에 해당하기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;멀티 프로세싱 (Multi Processing)&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;옛날 컴퓨터의 CPU는 싱글 코어(core) 였다. 이 싱글 코어에서 여러개의 작업을 동시에 처리하기 위해 멀티 태스킹과 같은 기술을 차용해 작업을 처리한 것이다. 그러나 시대가 발전함에 동시에 CPU 코어의 발열 한계로 인해 코어 깡 클럭을 높이지 않고 코어를 늘리는 방향으로 듀얼 코어, 쿼드 코어 CPU 제품을 출시하기 시작했다.&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;312&quot; data-origin-height=&quot;208&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OwXPX/btr7Oo0npnH/ACk7IDeGOk2J9Us8jZYruk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OwXPX/btr7Oo0npnH/ACk7IDeGOk2J9Us8jZYruk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OwXPX/btr7Oo0npnH/ACk7IDeGOk2J9Us8jZYruk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOwXPX%2Fbtr7Oo0npnH%2FACk7IDeGOk2J9Us8jZYruk%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; alt=&quot;Multi Processing&quot; loading=&quot;lazy&quot; width=&quot;312&quot; height=&quot;208&quot; data-origin-width=&quot;312&quot; data-origin-height=&quot;208&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;컴퓨터를 맞출때 한번 쯤은 '4코어 8스레드'와 같은 CPU 스펙에 대해 들어본적이 있을텐데 바로 이것을 말하는 것이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1019&quot; data-origin-height=&quot;580&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLf4ou/btr6UC0nn6r/fOjQ0QkYUHs3xHnTe2Z4Ek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLf4ou/btr6UC0nn6r/fOjQ0QkYUHs3xHnTe2Z4Ek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLf4ou/btr6UC0nn6r/fOjQ0QkYUHs3xHnTe2Z4Ek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLf4ou%2Fbtr6UC0nn6r%2FfOjQ0QkYUHs3xHnTe2Z4Ek%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; alt=&quot;Multi Processing&quot; loading=&quot;lazy&quot; width=&quot;1019&quot; height=&quot;580&quot; data-origin-width=&quot;1019&quot; data-origin-height=&quot;580&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;즉, 한 CPU에 두개 이상의 코어를 둬서 전체적인 성능을 향상시키는 방향으로 전환을 한 것이다. 그리고 여러 개의 CPU 코어가 동시에 작업을 처리하는 것을 멀티 프로세싱 (Multi Processing) 이라 한다. 이러한 멀티 프로세싱을 아키텍쳐 구조에 따라 종류도 여러가지 이다.&lt;/span&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/LbLcy/btr7Tkb25YX/yEPMkWjv1OUkhu7dv77SSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LbLcy/btr7Tkb25YX/yEPMkWjv1OUkhu7dv77SSk/img.png&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;844&quot; data-is-animation=&quot;false&quot; style=&quot;width: 44.5818%; margin-right: 10px;&quot; data-widthpercent=&quot;45.11&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LbLcy/btr7Tkb25YX/yEPMkWjv1OUkhu7dv77SSk/img.png&quot; alt=&quot;Multi Processing&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLbLcy%2Fbtr7Tkb25YX%2FyEPMkWjv1OUkhu7dv77SSk%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;960&quot; height=&quot;844&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BKMYj/btr7OOruVEF/wMVI2vWkv0ztKhLKIjEh80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BKMYj/btr7OOruVEF/wMVI2vWkv0ztKhLKIjEh80/img.png&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;838&quot; data-is-animation=&quot;false&quot; style=&quot;width: 54.2554%;&quot; data-widthpercent=&quot;54.89&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BKMYj/btr7OOruVEF/wMVI2vWkv0ztKhLKIjEh80/img.png&quot; alt=&quot;Multi Processing&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBKMYj%2Fbtr7OOruVEF%2FwMVI2vWkv0ztKhLKIjEh80%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;1160&quot; height=&quot;838&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;멀티 CPU 코어가 하나의 JOB에 대해 병렬로 처리하거나 여러 JOB에 대해 병렬로 처리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;멀티 프로세싱 &lt;span style=&quot;color: #ef5369;&quot;&gt;vs&lt;/span&gt; 멀티 태스킹&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀티 프로세싱 (Multi Processing)이란, 여러 개의 CPU 코어가 동시에 작업을 처리하는 것을 의미한다. 여러 개의 프로세서가 병렬로 작업을 수행하므로, 단일 프로세스보다 빠른 처리 속도를 보장할 수 있다.&lt;br /&gt;멀티 태스킹 (Multi Tasking)은 단일 CPU에서 여러 개의 작업을 동시에 처리하는 것을 의미한다. 하나의 CPU가 여러 작업들을 번갈아가며 처리하므로, 여러 개의 작업을 동시에 수행하는 것처럼 보이게 된다.&amp;nbsp;&lt;br /&gt;따라서, 둘은 여러 작업에 대해서 동시에 처리하는 목적은 비슷하지만, 멀티 태스킹은 하나의 CPU에서 여러 개의 작업을 처리하는 반면, 멀티 프로세싱은 여러 개의 CPU가 각각의 작업을 처리하는 것이라는 차이점이 있다.&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/yu4yp/btr6YQ5a8sZ/BJRHBtm2uSMvb3O7idFM0k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yu4yp/btr6YQ5a8sZ/BJRHBtm2uSMvb3O7idFM0k/img.jpg&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;375&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;47.05&quot; style=&quot;width: 46.4983%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yu4yp/btr6YQ5a8sZ/BJRHBtm2uSMvb3O7idFM0k/img.jpg&quot; alt=&quot;Multi Tasking&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fyu4yp%2Fbtr6YQ5a8sZ%2FBJRHBtm2uSMvb3O7idFM0k%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;500&quot; height=&quot;375&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qgPjo/btr610ln7BY/Zkaq8Vh2pX85YnvR56k851/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qgPjo/btr610ln7BY/Zkaq8Vh2pX85YnvR56k851/img.jpg&quot; width=&quot;615&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;617&quot; data-is-animation=&quot;false&quot; style=&quot;width: 52.3389%;&quot; data-widthpercent=&quot;52.95&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qgPjo/btr610ln7BY/Zkaq8Vh2pX85YnvR56k851/img.jpg&quot; alt=&quot;Multi Processing&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqgPjo%2Fbtr610ln7BY%2FZkaq8Vh2pX85YnvR56k851%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;926&quot; height=&quot;617&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;멀티 태스킹이 두 팔로 여러 작업을 번갈아 빠르게 처리하는 것이라면, 멀티 프로세싱은 팔이 6개인 것이다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;멀티 프로세싱&lt;span&gt; &lt;/span&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;+&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;멀티 태스킹 조합&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘은 서로 다른 동시 처리 방법이기 때문에 이 둘을 조합하여 더욱 더 시너지 효과를 누릴 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;701&quot; data-origin-height=&quot;216&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q9cvA/btr7MUZtRgi/oa1XlQ833tzlLebuRMGaW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q9cvA/btr7MUZtRgi/oa1XlQ833tzlLebuRMGaW1/img.png&quot; data-alt=&quot;멀티 태스킹&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q9cvA/btr7MUZtRgi/oa1XlQ833tzlLebuRMGaW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq9cvA%2Fbtr7MUZtRgi%2Foa1XlQ833tzlLebuRMGaW1%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; alt=&quot;Multi Tasking&quot; loading=&quot;lazy&quot; width=&quot;701&quot; height=&quot;216&quot; data-origin-width=&quot;701&quot; data-origin-height=&quot;216&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;멀티 태스킹&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;391&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qtphM/btr7QmAJREO/MZlliQiKo3IZMPWQI7nUrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qtphM/btr7QmAJREO/MZlliQiKo3IZMPWQI7nUrk/img.png&quot; data-alt=&quot;멀티 프로세싱&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qtphM/btr7QmAJREO/MZlliQiKo3IZMPWQI7nUrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqtphM%2Fbtr7QmAJREO%2FMZlliQiKo3IZMPWQI7nUrk%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; alt=&quot;Multi Processing&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;391&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;391&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;멀티 프로세싱&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;406&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FiuVq/btr7QnsSF84/JAYQKCaMgpwK44Vj4SJB1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FiuVq/btr7QnsSF84/JAYQKCaMgpwK44Vj4SJB1K/img.png&quot; data-alt=&quot;여러 작업에 대한 멀티 프로세싱 + 멀티 태스킹&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FiuVq/btr7QnsSF84/JAYQKCaMgpwK44Vj4SJB1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFiuVq%2Fbtr7QnsSF84%2FJAYQKCaMgpwK44Vj4SJB1K%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; alt=&quot;Multi-Tasking-Processing&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;406&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;406&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;여러 작업에 대한 멀티 프로세싱 + 멀티 태스킹&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;491&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGazfa/btr7C1Sr080/fud7JJVWh92QMKuEyz9ceK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGazfa/btr7C1Sr080/fud7JJVWh92QMKuEyz9ceK/img.png&quot; data-alt=&quot;하나의 작업에 대한 멀티 프로세싱 + 멀티 태스킹&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGazfa/btr7C1Sr080/fud7JJVWh92QMKuEyz9ceK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGazfa%2Fbtr7C1Sr080%2Ffud7JJVWh92QMKuEyz9ceK%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; alt=&quot;Multi-Tasking-Processing&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;491&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;491&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;h4 style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;멀티 프로세싱&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;vs&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;멀티 프로세스&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;multi processing 과 multi process 는 영어는 그저 동명사(-ing) 차이 이지만, 이 둘이 의미하는 바는 전혀 다르다. 아마 많은 사람들이 이 둘을 햇깔려 할 것이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;멀티 프로세싱은 하나의 시스템에서 여러 개의 프로세서(CPU)를 사용하여 작업을 처리하는 것을 의미한다. 즉, 프로세서(CPU)가 멀티인 것이다. 여러 개의 프로세서가 동시에 작업을 처리하므로, &lt;span style=&quot;color: #ee2323;&quot;&gt;전체적인 처리 속도가 빨라지는&lt;/span&gt; 효과에 목적을 둔다.&lt;br /&gt;반면, 멀티 프로세스는 하나의 응용 프로그램을 여러 개의 프로세스로 분리하여 실행하는 것을 의미하는 것이다. 즉, 프로세스(process)가 멀티인 것이다. 여러 개의 프로세스가 동시에 실행되므로, 하나의 프로세스가 죽어도 프로그램이 죽지않아 프로그램 전체의 &lt;span style=&quot;color: #ee2323;&quot;&gt;안정성이 높아지는 &lt;/span&gt;것에 목적을 둔다. 보통 멀티 프로세스는 부모 프로세스와 그의 여러 자식 프로세스로 이루어져 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;803&quot; data-origin-height=&quot;543&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIqXry/btr5Cwe2cbY/DLGIKHIqej3GEXkkMfdBx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIqXry/btr5Cwe2cbY/DLGIKHIqej3GEXkkMfdBx1/img.png&quot; data-alt=&quot;브라우저의 멀티 프로세스&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIqXry/btr5Cwe2cbY/DLGIKHIqej3GEXkkMfdBx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIqXry%2Fbtr5Cwe2cbY%2FDLGIKHIqej3GEXkkMfdBx1%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; alt=&quot;멀티 프로세스&quot; loading=&quot;lazy&quot; width=&quot;803&quot; height=&quot;543&quot; data-origin-width=&quot;803&quot; data-origin-height=&quot;543&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;브라우저의 멀티 프로세스&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://youtu.be/QmtYKZC0lMU&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://velog.io/@ashappyasikonw/Single-Process-System-Multi-programming-Multitasking-%EC%9D%B4%EB%9E%80&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://jenkov.com/tutorials/java-concurrency/concurrency-vs-parallelism.html&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://donghoson.tistory.com/15&lt;/p&gt;</description>
      <category>개발 지식/CS 지식</category>
      <category>multi tasking</category>
      <category>동시성</category>
      <category>멀티 태스킹</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1105</guid>
      <comments>https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-multi-programming-tasking-processing#entry1105comment</comments>
      <pubDate>Thu, 6 Apr 2023 10:14:00 +0900</pubDate>
    </item>
    <item>
      <title> &amp;zwj;  멀티 프로세스 vs 멀티 스레드 비교   완전 총정리</title>
      <link>https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-multi-process-multi-thread</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;nvme-t.webp&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;630&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mifx0/btr6odFEj2p/ysSFZKXQVQP5ErQ4hxdA91/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mifx0/btr6odFEj2p/ysSFZKXQVQP5ErQ4hxdA91/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mifx0/btr6odFEj2p/ysSFZKXQVQP5ErQ4hxdA91/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmifx0%2Fbtr6odFEj2p%2FysSFZKXQVQP5ErQ4hxdA91%2Fimg.webp&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; alt=&quot;multi-thread-process&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;630&quot; data-filename=&quot;nvme-t.webp&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;630&quot;/&gt;&lt;/span&gt;&lt;/figure&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;span&gt;&amp;nbsp;&lt;/span&gt;이라고 보면 된다. 단순히 프로그램을 여러개 띄워놓는 것이 멀티 프로세스가 아니라 이 둘은 언제 어느때에 어떤 방식으로 처리하느냐에 따라 다른 것으로 이해해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름으로 유추할 수 있듯이 멀티 프로세스와 멀티 스레드는 여러개의 프로세스, 스레드가 동작하는 것을 일 컫는다. 단일이 아닌 다중으로 돌아감으로써 성능 향상 등 여러가지 효과를 얻을 수 있다. 하지만 또한 이로 인해 발생되는 부가적인 문제점도 발생하게 된다. 지금 부터 이에 대해 자세히 알아보자&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img_(55)-transformed.png&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;522&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVFgDw/btr6btbzJi2/LEBvURfpSlYMVZG3nzSkHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVFgDw/btr6btbzJi2/LEBvURfpSlYMVZG3nzSkHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVFgDw/btr6btbzJi2/LEBvURfpSlYMVZG3nzSkHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVFgDw%2Fbtr6btbzJi2%2FLEBvURfpSlYMVZG3nzSkHK%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; alt=&quot;multi-thread-process&quot; loading=&quot;lazy&quot; width=&quot;1392&quot; height=&quot;522&quot; data-filename=&quot;img_(55)-transformed.png&quot; data-origin-width=&quot;1392&quot; data-origin-height=&quot;522&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1680660570096&quot; style=&quot;color: #333333; text-align: start;&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot; &amp;zwj;  &amp;zwj;완전히 정복하는 프로세스 vs 스레드 개념&quot; data-og-description=&quot;한눈에 이해하는 프로세스 &amp;amp; 스레드 개념 전공 지식 없이 컴퓨터의 프로그램을 이용하는데는 문제 없어 왔지만 소프트웨어를 개발하는 사람으로서 컴퓨터 실행 내부 요소를 따져보게 될때, 아&quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%E2%9A%94%EF%B8%8F-%EC%93%B0%EB%A0%88%EB%93%9C-%EC%B0%A8%EC%9D%B4&quot; data-og-url=&quot;https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%E2%9A%94%EF%B8%8F-%EC%93%B0%EB%A0%88%EB%93%9C-%EC%B0%A8%EC%9D%B4&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bXWoHW/hyR9B1WM3s/Aai4cuLe7bYdHGBWyTHzD1/img.png?width=800&amp;amp;height=352&amp;amp;face=0_0_800_352,https://scrap.kakaocdn.net/dn/f8ypy/hyR9AvfgLU/AsRjCE4PT6kfDfSsKo3hNK/img.png?width=800&amp;amp;height=352&amp;amp;face=0_0_800_352,https://scrap.kakaocdn.net/dn/eB1Uxw/hyR9DS0EZv/ikG65QzMS3Mt6QSKlukuX0/img.png?width=1403&amp;amp;height=618&amp;amp;face=0_0_1403_618&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%E2%9A%94%EF%B8%8F-%EC%93%B0%EB%A0%88%EB%93%9C-%EC%B0%A8%EC%9D%B4&quot; data-source-url=&quot;https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%E2%9A%94%EF%B8%8F-%EC%93%B0%EB%A0%88%EB%93%9C-%EC%B0%A8%EC%9D%B4&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bXWoHW/hyR9B1WM3s/Aai4cuLe7bYdHGBWyTHzD1/img.png?width=800&amp;amp;height=352&amp;amp;face=0_0_800_352,https://scrap.kakaocdn.net/dn/f8ypy/hyR9AvfgLU/AsRjCE4PT6kfDfSsKo3hNK/img.png?width=800&amp;amp;height=352&amp;amp;face=0_0_800_352,https://scrap.kakaocdn.net/dn/eB1Uxw/hyR9DS0EZv/ikG65QzMS3Mt6QSKlukuX0/img.png?width=1403&amp;amp;height=618&amp;amp;face=0_0_1403_618');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt; &amp;zwj;  &amp;zwj;완전히 정복하는 프로세스 vs 스레드 개념&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;한눈에 이해하는 프로세스 &amp;amp; 스레드 개념 전공 지식 없이 컴퓨터의 프로그램을 이용하는데는 문제 없어 왔지만 소프트웨어를 개발하는 사람으로서 컴퓨터 실행 내부 요소를 따져보게 될때, 아&lt;/p&gt;
&lt;p class=&quot;og-host&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;inpa.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;h2 id=&quot;c0e8&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Multi Process&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀티 프로세스는 운영체제에서 하나의 응용 프로그램에 대해 동시에 여러 개의 프로세스를 실행할 수 있게 하는 기술을 말한다. 보통 하나의 프로그램 실행에 대해 하나의 프로세스가 메모리에 생성되지만, 부가적인 기능을 위해 여러개의 프로세스를 생성하는 것이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;멀티 프로세스 vs 멀티 프로세서&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;프로세스(process)는 프로그램의 실행 상태를 말하고, 프로세서(processer)는 CPU 코어를 일컫는다. 둘이 단어가 유사해서 굉장히 혼동할 수 있을텐데, 어쨋든 둘이 의미하는바는 완전히 다르다고 보면 된다.&lt;br /&gt;따라서 멀티 프로세스는 하나의 프로그램에서 여러 개의 프로세스를 실행하는 것을 의미하고, 멀티 프로세서는 여러 개의 CPU 코어가 하나의 시스템에서 동시에 실행되는 것을 의미한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀티 프로세스 내부를 보면, 하나의 부모 프로세스가 여러 개의 자식 프로세스를 생성함으로서 다중 프로세스를 구성하는 구조이다. 한 프로세스는 실행되는 도중 프로세스 생성 시스템 콜을 통해 새로운 프로세스들을 생성할 수 있는데, 다른 프로세스를 생성하는 프로세스를 부모 프로세스(Parent Process)라 하고, 다른 프로세스에 의해 생성된 프로세스를 자식 프로세스(Child Process)라 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;993&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FhV9X/btr5Zk0YNaj/C95IMf4K1H8n1PYvsZQpKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FhV9X/btr5Zk0YNaj/C95IMf4K1H8n1PYvsZQpKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FhV9X/btr5Zk0YNaj/C95IMf4K1H8n1PYvsZQpKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFhV9X%2Fbtr5Zk0YNaj%2FC95IMf4K1H8n1PYvsZQpKk%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; alt=&quot;Multi Process&quot; loading=&quot;lazy&quot; width=&quot;484&quot; height=&quot;653&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;993&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부모 프로세스와 자식 프로세스는 각각 고유한 PID(Process ID)를 가지고 있다. 부모 프로세스는 자식 프로세스의 PID를 알고 있으며, 이를 통해 자식 프로세스를 제어할 수 있다. 또한, 자식 프로세스는 부모 프로세스의 PID와 PPID(Parent Process ID)를 알고 있어, 이를 통해 부모 프로세스와의 통신이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만, 통신이 가능할 뿐이지, 부모 프로세스와 자식 프로세스는 엄연히 서로 다른 프로세스로 독립적으로 실행되며, 독립적인 메모리 공간을 가지고 있어 서로 다른 작업을 수행한다. 대표적인 예로 &lt;span style=&quot;color: #006dd7;&quot;&gt;웹 브라우저의 상단 탭(Tab)&lt;/span&gt; 이나 &lt;span style=&quot;color: #006dd7;&quot;&gt;새 창&lt;/span&gt;을 들 수 있다. 각 브라우저 탭은 같은 브라우저 프로그램 실행이지만, 각기 다른 사이트 실행을 행하기 때문이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1192&quot; data-origin-height=&quot;307&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/babIUA/btr74f9jeLm/jvAv28Xy6mkZhowV0xREq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/babIUA/btr74f9jeLm/jvAv28Xy6mkZhowV0xREq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/babIUA/btr74f9jeLm/jvAv28Xy6mkZhowV0xREq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbabIUA%2Fbtr74f9jeLm%2FjvAv28Xy6mkZhowV0xREq0%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; alt=&quot;Multi Process&quot; loading=&quot;lazy&quot; width=&quot;1192&quot; height=&quot;307&quot; data-origin-width=&quot;1192&quot; data-origin-height=&quot;307&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 브라우저 멀티 프로세스 원리를 확인하기 위한 간단한 실험도 가능하다. 여러개의 탭을 띄운뒤, 하나의 탭에서 F12로 개발자 도구를 열고 콘솔 탭에 &lt;s&gt;while(1) {}&lt;/s&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;520&quot; data-origin-height=&quot;196&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cPIo79/btscJreQnpd/ivb2d1VYj50o2xEO0Q11d1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cPIo79/btscJreQnpd/ivb2d1VYj50o2xEO0Q11d1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cPIo79/btscJreQnpd/ivb2d1VYj50o2xEO0Q11d1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcPIo79%2FbtscJreQnpd%2Fivb2d1VYj50o2xEO0Q11d1%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; alt=&quot;Multi Process&quot; loading=&quot;lazy&quot; width=&quot;520&quot; height=&quot;196&quot; data-origin-width=&quot;520&quot; data-origin-height=&quot;196&quot;/&gt;&lt;/span&gt;&lt;/figure&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;style3&quot; /&gt;
&lt;h3 id=&quot;4c67&quot; 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;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;b&gt;&lt;b&gt;1. &lt;/b&gt;&lt;/b&gt;&lt;/span&gt;프로그램 안전성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀티 프로세스는 각 프로세스가 독립적인 메모리 공간을 가지므로, 한 프로세스가 비정상적으로 종료되어도 다른 프로세스에 영향을 주지 않는다. 그래서 프로그램 전체의 안전성을 확보할 수 있다는 장점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;예를 들자면 크롬 브라우저에서 여러개의 탭을 띄우고 여러곳의 웹사이트를 방문해 서비스를 이용한다고 하자. 이때 어느 한 탭의 웹사이트에서 무언가 잘못되어 먹통이 되었다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;366&quot; data-origin-height=&quot;180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kHJF8/btr5ZlLn4eN/WZ94SUUikwiuAlvM0hQ180/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kHJF8/btr5ZlLn4eN/WZ94SUUikwiuAlvM0hQ180/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kHJF8/btr5ZlLn4eN/WZ94SUUikwiuAlvM0hQ180/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkHJF8%2Fbtr5ZlLn4eN%2FWZ94SUUikwiuAlvM0hQ180%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; alt=&quot;multi-process&quot; loading=&quot;lazy&quot; width=&quot;366&quot; height=&quot;180&quot; data-origin-width=&quot;366&quot; data-origin-height=&quot;180&quot;/&gt;&lt;/span&gt;&lt;/figure&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;803&quot; data-origin-height=&quot;543&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cYWWkm/btr74vyHiea/j4YZKVYPdnXNM36RINNGO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cYWWkm/btr74vyHiea/j4YZKVYPdnXNM36RINNGO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cYWWkm/btr74vyHiea/j4YZKVYPdnXNM36RINNGO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcYWWkm%2Fbtr74vyHiea%2Fj4YZKVYPdnXNM36RINNGO0%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; alt=&quot;multi-process&quot; loading=&quot;lazy&quot; width=&quot;803&quot; height=&quot;543&quot; data-origin-width=&quot;803&quot; data-origin-height=&quot;543&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;&lt;b&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;b&gt;&lt;b&gt;2.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/b&gt;&lt;/span&gt;프로그램 병렬성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀티 프로세스와 여러개의 CPU 코어를 활용하여 둘의 시너지를 합쳐, 다중 CPU 시스템에서 각 프로세스를 병렬적으로 실행하여 성능을 향상 시킬 수 있다. 예를 들어 이미지 처리나 비디오 인코딩과 같은 작업을 여러 개의 코어나 CPU에 분산시켜 빠르게 처리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;다만, 이 부분은 멀티 프로세스 만의 장점이라기 보단, 멀티 프로세스와 멀티 스레드 둘의 장점이 옳다. 그리고 멀티 스레드로 구성하는 것이 멀티 프로세스로 구성하는 것보다 훨씬 효율적이고 빠르기 때문에, 멀티 프로세스로 성능을 올리는 행위는 거의 없다고 보면 된다. 이에 대해선 뒤의 멀티 스레드 파트에서 다시 다룬다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2997&quot; data-origin-height=&quot;1757&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ra7qE/btr7TkxScdB/TOdfbsr639aWxBGymcVK30/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ra7qE/btr7TkxScdB/TOdfbsr639aWxBGymcVK30/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ra7qE/btr7TkxScdB/TOdfbsr639aWxBGymcVK30/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fra7qE%2Fbtr7TkxScdB%2FTOdfbsr639aWxBGymcVK30%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; alt=&quot;multi-process&quot; loading=&quot;lazy&quot; width=&quot;2997&quot; height=&quot;1757&quot; data-origin-width=&quot;2997&quot; data-origin-height=&quot;1757&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;&lt;b&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;b&gt;&lt;b&gt;3.&lt;span&gt; &lt;/span&gt;&lt;/b&gt;&lt;/b&gt;&lt;/span&gt;시스템 확장성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀티 프로세스는 각 프로세스가 독립적이므로, 새로운 기능이나 모듈을 추가하거나 수정할때 다른 프로세스에 영향을 주지 않는다. 그래서 시스템의 규모를 쉽게 확장할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분에 대해서는 컴퓨터의 소프트웨어로 예시를 드는 것보다 네트워크의 서버(server)로 드는 것이 적절하기 때문에 잠시 분산 서버에 대해서 말해보겠다. 대규모 웹 서비스에서는 수많은 요청을 동시에 처리하기 위해 여러대의 서버를 두고 로드 밸런서(Load Balancer)와 같은 장비를 사용하여 클라이언트 요청 트래픽을 분산 시킨다. 이때 여러대의 서버는 컴퓨터를 여러개를 말하는 것일 수도 있고, &lt;b&gt;하나의 성능 좋은 컴퓨터에 여러개의 서버 프로세스를 두는 것&lt;/b&gt;을 말하기도 한다. 멀티 프로세스의 상황은 후자이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 프로그래밍을 해본 백엔드 개발자분들은 서버 클러스터(cluster)를 구성해본 적이 있을 것이다. 하나의 컴퓨터에 여러개의 서버 프로세스를 띄움으로써 요청을 분산시키는 것이다. Node.js 진영에선 대표적으로 &lt;a href=&quot;https://inpa.tistory.com/entry/node-%F0%9F%93%9A-PM2-%EB%AA%A8%EB%93%88-%EC%82%AC%EC%9A%A9%EB%B2%95-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0-%EB%AC%B4%EC%A4%91%EB%8B%A8-%EC%84%9C%EB%B9%84%EC%8A%A4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;PM2&lt;/a&gt; 가 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;422&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcozTV/btr72XPgQV8/YjpruntsWaa7HI8ztNyv0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcozTV/btr72XPgQV8/YjpruntsWaa7HI8ztNyv0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcozTV/btr72XPgQV8/YjpruntsWaa7HI8ztNyv0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcozTV%2Fbtr72XPgQV8%2FYjpruntsWaa7HI8ztNyv0k%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; alt=&quot;multi-process&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;422&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;422&quot;/&gt;&lt;/span&gt;&lt;/figure&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;style3&quot; /&gt;
&lt;h3 id=&quot;4c67&quot; 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;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;&lt;b&gt;1.&amp;nbsp;&lt;/b&gt;&lt;/b&gt;&lt;/span&gt;Context Switching Overhead&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-multi-programming-tasking-processing&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;멀티 태스킹(multi tasking)&lt;/a&gt;을 구성하는데 핵심 기술인 컨텍스트 스위칭(context switching) 과정에서 성능 저하가 올 수 있다. 특히나 프로세스를 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;컨텍스트 스위칭&lt;span&gt; 하면, CPU는 다음 프로세스의 정보를 불러오기 위해 메모리를 검색하고, CPU 캐시 메모리를 초기화하며, 프로세스 상태를 저장하고, 불러올 데이터를 준비해야 하기 때문에, 이로 인한 빈번한 Context Switching 작업으로 인해 비용 오버헤드가 발생할 수 있게 된다. 반면 스레드를 컨텍스트 스위칭하면 프로세스 스위칭 보다 가벼워 훨씬 빠르고 좋다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;991&quot; data-origin-height=&quot;546&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8hWqD/btr6rw5WrBY/6Zun2QryWJBoiyh4FGrqCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8hWqD/btr6rw5WrBY/6Zun2QryWJBoiyh4FGrqCk/img.png&quot; data-alt=&quot;프로세스 1에서 2로 스위칭할때 아주 약간의 빈 시간(오버헤드)가 발생한다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8hWqD/btr6rw5WrBY/6Zun2QryWJBoiyh4FGrqCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8hWqD%2Fbtr6rw5WrBY%2F6Zun2QryWJBoiyh4FGrqCk%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;991&quot; height=&quot;546&quot; data-origin-width=&quot;991&quot; data-origin-height=&quot;546&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;프로세스 1에서 2로 스위칭할때 아주 약간의 빈 시간(오버헤드)가 발생한다&lt;/figcaption&gt;
&lt;/figure&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;따라서, 멀티 프로세스 환경에서는 Context Switching Overhead를 최소화하는 방법이 중요하다. 이를 위해서 프로세스 수를 적정하게 유지하거나, I/O 바운드 작업이 많은 프로세스와 CPU 바운드 작업이 많은 프로세스를 분리하여 관리하고, CPU 캐시를 효율적으로 활용하는 등의 방법을 고려해 봐야 한다.&lt;/span&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;b&gt;&lt;span&gt;&lt;span&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;2.&amp;nbsp;&lt;/b&gt;&lt;/span&gt;자원 공유 비효율성&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀티 프로세스는 각 프로세스가 독립적인 메모리 공간을 가지므로, 결과적으로 메모리 사용량이 증가하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만일 각 프로세스간에 자원 공유가 필요할 경우 &lt;span style=&quot;background-color: #ffffff; color: #383838; text-align: start;&quot;&gt;프로세스 사이의 어렵고 복잡한 통신 기법인 IPC(Inter-Process Commnuication)을 사용하여야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;469&quot; data-origin-height=&quot;203&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wHlQM/btr53ErRepe/bojoHmQqek8FjkKLY25Zo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wHlQM/btr53ErRepe/bojoHmQqek8FjkKLY25Zo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wHlQM/btr53ErRepe/bojoHmQqek8FjkKLY25Zo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwHlQM%2Fbtr53ErRepe%2FbojoHmQqek8FjkKLY25Zo0%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; alt=&quot;IPC&quot; loading=&quot;lazy&quot; width=&quot;469&quot; height=&quot;203&quot; data-origin-width=&quot;469&quot; data-origin-height=&quot;203&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;p id=&quot;25f8&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;IPC란 운영체제 상에서 실행 중인 프로세스 간에 정보를 주고받는 메커니즘을 말한다. 이를 위해 파이프, 소켓, 메세지 큐 등 다양한 방법이 사용된다. 그런데 IPC 자체로 오버헤드가 발생한다. &lt;span style=&quot;background-color: #ffffff; color: #383838; text-align: start;&quot;&gt;예를 들어, 파이프나 소켓과 같은 IPC 기법은 데이터를 복사하거나 버퍼링하는 과정에서 성능 저하가 발생할 수 있기 때문이다. 또한 코드의 복잡도를 증가시킨다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;c0e8&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;b&gt;Multi Thread&lt;/b&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p id=&quot;e3f6&quot; data-ke-size=&quot;size16&quot;&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;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;542&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvfndW/btrjAeFcoaB/7amMfccrT6JOdWyn4ldaV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvfndW/btrjAeFcoaB/7amMfccrT6JOdWyn4ldaV1/img.png&quot; data-alt=&quot;네트워크, 데이터베이스 작업을 하면서 사용자와 상호작용을 동시에&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvfndW/btrjAeFcoaB/7amMfccrT6JOdWyn4ldaV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvfndW%2FbtrjAeFcoaB%2F7amMfccrT6JOdWyn4ldaV1%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; alt=&quot;Multi-Thread&quot; loading=&quot;lazy&quot; width=&quot;506&quot; height=&quot;542&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;542&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;&lt;span style=&quot;background-color: #ffffff; color: #273239;&quot;&gt;보다 멀티 프로세스와의 차이점을 부각시키기 위해, 멀티 프로세스를 설명할때 예시를 들었던 웹 브라우저를 다시 들어보겠다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #273239;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #273239; text-align: start;&quot;&gt;멀티 프로세스는 &lt;/span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;웹 브라우저에서의 여러 탭이나 여러 창&lt;/span&gt;이라고 말했었다. 대신 &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #273239;&quot;&gt;멀티 스레드는 &lt;span style=&quot;color: #ee2323;&quot;&gt;웹 브라우저의 단일 탭 또는 창 내에서 &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #273239; text-align: start;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;브라우저 이벤트 루프, 네트워크 처리, I/O 및 기타 작업을 관리하고 처리&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;style3&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;&lt;span style=&quot;color: #ee2323;&quot;&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;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;1.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;스레드는 프로세스보다 가벼움&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 스레드는 프로세스 보다 용량이 가볍다. 그도 그럴게 스레드는 프로세스 내에서 생성되기 때문에 스레드의 실행 환경을 설정하는 작업이 매우 간단하여 생성 및 종료가 빠르다. 또한 스레드는 프로세스와 달리, 코드, 데이터, 스택 영역을 제외한 나머지 자원을 서로 공유하기 때문에 기본적으로 내장되어 있는 데이터 용량이 프로세스보다 당연히 작다. 그래서 스레드를 생성하고 제거할 때, 프로세스 내부의 자원만을 관리하면 되기 때문에 프로세스 생성, 제거 보다 훨씬 빠른 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;2.&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;자원의 효율성&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀티 스레드는 하나의 프로세스 내에서 여러 개의 스레드를 생성되기 때문에, heap 영역과 같은 공유 메모리에 대해 스레드 간에 자원을 공유가 가능하다. 이를 통해, 프로세스 간 통신 (IPC)을 사용하지 않고도 데이터를 공유할 수 있기 때문에, 자원의 효율적인 활용이 가능해 시스템 자원 소모가 줄어든다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;462&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bilJ94/btr6rxcJ12e/yGcKc7yuB3nDWXWso0l3T0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bilJ94/btr6rxcJ12e/yGcKc7yuB3nDWXWso0l3T0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bilJ94/btr6rxcJ12e/yGcKc7yuB3nDWXWso0l3T0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbilJ94%2Fbtr6rxcJ12e%2FyGcKc7yuB3nDWXWso0l3T0%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; alt=&quot;Multi-Thread&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;462&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;462&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;&lt;b&gt;&lt;span&gt;&lt;span&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;3.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;b&gt;&lt;b&gt;Context Switching &lt;/b&gt;비용 감소&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-selectable-paragraph=&quot;&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;img_-_2023-04-04T191751_430-transformed.jpeg&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;310&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PpwcB/btr73xQQciT/mosSUj7hHNLFKQ4s3sXPK1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PpwcB/btr73xQQciT/mosSUj7hHNLFKQ4s3sXPK1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PpwcB/btr73xQQciT/mosSUj7hHNLFKQ4s3sXPK1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPpwcB%2Fbtr73xQQciT%2FmosSUj7hHNLFKQ4s3sXPK1%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; alt=&quot;Multi-Thread&quot; loading=&quot;lazy&quot; width=&quot;798&quot; height=&quot;310&quot; data-filename=&quot;img_-_2023-04-04T191751_430-transformed.jpeg&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;310&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;프로세스 컨텍스트 스위칭 비용은 스위칭할 때마다 CPU 캐시에 있는 내용을 모두 초기화하고, 새로운 프로세스 정보를 CPU 캐시에 적재해야 하므로 높은 비용이 든다. 반면, 스레드 컨텍스트 스위칭 비용은 스위칭할 때 스레드 간에 공유하는 자원을 제외한 스레드 정보(stack, register)만을 교체하면 되므로 프로세스 컨텍스트 스위칭 비용보다 상대적으로 낮은 것이다.&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;&lt;span&gt;&lt;span&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;4.&lt;/span&gt;&lt;span&gt; &lt;/span&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;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞의 멀티 스레드의 장점을 종합해보자면, 멀티 스레드는 스레드 간의 통신이나 자원 공유가 더욱 용이하며, 프로세스 보다 가벼워 컨텍스트 스위칭 오버헤드도 작다. 따라서 멀티 프로세스 보다 응답 시간이 빠르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 웹 서버에서 클라이언트 요청을 처리하는 경우, 멀티 프로세스 방식에서는 각 요청마다 프로세스를 생성하여 처리해야 하므로, 오버헤드가 크지만, 멀티 스레드 방식에서는 여러 개의 스레드가 하나의 프로세스 내에서 요청을 처리할 수 있으므로, 오버헤드가 감소해 더욱 빠른 응답 시간을 보장할 수 있는 것이다.&lt;/p&gt;
&lt;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;style3&quot; /&gt;
&lt;h3 id=&quot;멀티-스레딩의-문제점&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;멀티 스레드의 단점&lt;/b&gt;&lt;/h3&gt;
&lt;p id=&quot;144c&quot; style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;1.&lt;/span&gt; 안정성 문제&lt;/b&gt;&lt;/h4&gt;
&lt;p data-selectable-paragraph=&quot;&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-origin-width=&quot;1280&quot; data-origin-height=&quot;836&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/L5gYp/btr6eUGAI4R/9UC9f5ZWqb9TakHZWmCc30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/L5gYp/btr6eUGAI4R/9UC9f5ZWqb9TakHZWmCc30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/L5gYp/btr6eUGAI4R/9UC9f5ZWqb9TakHZWmCc30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FL5gYp%2Fbtr6eUGAI4R%2F9UC9f5ZWqb9TakHZWmCc30%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; alt=&quot;Multi-Thread&quot; loading=&quot;lazy&quot; width=&quot;693&quot; height=&quot;453&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;836&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;물론 이는 프로그래머의 역량에 따라 극복할 수 가 있다. 예를들어 스레드에 에러가 발생할 경우 이에 대한 적절한 예외 처리를 잘 해놓는다던지, 에러 발생 시 새로운 스레드를 생성하거나 스레드 풀(Thread Pool)에서 잔여 스레드를 가져오던지 하여 프로그램 종료를 방지할 수 있다. 다만, 이때 새로운 스레드 생성이나 놀고 있는 스레드 처리에 추가 비용이 발생하게 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;81af&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;2.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;동기화로 인한 성능 저하&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;멀티 스레드 모델은 여러 개의 스레드가 공유 자원에 동시에 접근할 수 있기 때문에, 동기화 문제가 발생할 수 있다.&lt;span&gt; 예를들어 여러 스레드가 동시에 한 자원을 변경해 버린다면 의도되지 않은 엉뚱한 값을 읽어 서비스에 치명적인 버그가 생길수도 있다. 따라서 스레드 간 동기화(syncronized)는 데이터 접근을 제어하기 위한 필수적인 기술이다. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;동기화 작업은 여러 스레드들이 자원에 대한 접근을 순차적으로 통제하는 것이다. 그러면 동시 접근으로 인한 동시 수정과 같은 현상은 일어나지 않게 된다. 그러나 동기화 작업은 여러 스레드 접근을 제한하는 것이기 때문에 &lt;b&gt;병목 현상&lt;/b&gt;이 일어나 성능이 저하될 가능성이 높다는 단점이 있다.&lt;/span&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;blob&quot; data-origin-width=&quot;777&quot; data-origin-height=&quot;363&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmehI6/btr736r6Tw6/wTH0UPpQfmgkgozqmiwte1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmehI6/btr736r6Tw6/wTH0UPpQfmgkgozqmiwte1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmehI6/btr736r6Tw6/wTH0UPpQfmgkgozqmiwte1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmehI6%2Fbtr736r6Tw6%2FwTH0UPpQfmgkgozqmiwte1%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; alt=&quot;Multi-Thread&quot; loading=&quot;lazy&quot; width=&quot;777&quot; height=&quot;363&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;777&quot; data-origin-height=&quot;363&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해 임계 영역(Critical Section)에 대하여 뮤텍스(mutex), 또는 세마포어(Semaphore) 방식을 활용한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;임계 영역(Critical Section)&lt;/b&gt;&lt;br /&gt;- 멀티 스레드 프로그래밍에서 임계 영역은 공유 자원을 접근하는 코드 영역을 말한다. 대표적으로 전역 변수나 heap 메모리 영역을 들 수 있겠다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;뮤텍스(Mutex)&lt;/b&gt;&lt;br /&gt;- 공유 자원에 대한 접근을 제어하기 위한 상호 배제 기법 중 하나로, 임계 영역에 진입하기 전에 락(lock)을 획득하고, 임계 영역을 빠져나올 때 락을 해제하여 다른 스레드들이 접근할 수 있도록 한다. 한마디로 오직 1개의 스레드만이 공유 자원에 접근할 수 있도록 제어하는 기법이다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;세마포어(Semaphore)&lt;/b&gt;&lt;br /&gt;- 세마포어는 동시에 접근 가능한 스레드의 개수를 지정할 수 있다. 세마포어 값이 1이면 뮤텍스와 동일한 역할을 하며, 값이 2 이상이면 동시에 접근 가능한 스레드의 수를 제어할 수 있다. 스레드가 임계 영역에 진입하기 전에 세마포어 값을 확인하고, 값이 허용된 범위 내에 있을 때만 락을 획득할 수 있는 형식이다. 한마디로 뮤텍스 상위 호환 이라고 보면 된다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;81af&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;3.&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;데드락 (교착 상태)&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;Deadlock 이란, 다수의 프로세스나 스레드가 서로 자원을 점유하고, 다른 프로세스나 스레드가 점유한 자원을 기다리는 상황에서 발생하는 교착 상태를 말한다. 여러 개의 스레드가 서로 대기하면서 무한정 기다리게되는 무한 루프와 같은 증상이라고 보면된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;예를들어, 스레드 1 은 자원 A을 점유하고 있는 상태에서 자원 B가 필요한 상황이다. 그리고 스레드 2 는 자원 B를 점유하고 있는 상태에서 자원 A이 필요한 상황이다. 하지만 스레드 1은 자원 B가 필요한 상황에서 자원 A을 빌려줄 수 있는 상황이 아니고, 스레드 2또한 자원 A이 필요한 상태에서 자원 B를 빌려줄 수 없는 상황인 것이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;이처럼 다수의 쓰레드가 같은 lock을 동시에, 다른 명령에 의해 획득하려 할 때 서로 절대 불가능한 일을 계속적으로 기다리는 상황을 이야기 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;648&quot; data-origin-height=&quot;499&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgRSP9/btr6ouO1S99/B3N1ylkovSB8eUTXKAOKb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgRSP9/btr6ouO1S99/B3N1ylkovSB8eUTXKAOKb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgRSP9/btr6ouO1S99/B3N1ylkovSB8eUTXKAOKb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgRSP9%2Fbtr6ouO1S99%2FB3N1ylkovSB8eUTXKAOKb1%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; alt=&quot;Deadlock&quot; loading=&quot;lazy&quot; width=&quot;648&quot; height=&quot;499&quot; data-origin-width=&quot;648&quot; data-origin-height=&quot;499&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;이러한 현상은 스레드의 특징인 공유 자원에 대한 동시 엑세스로 인한 문제로, 이를 방지하기 위한 상호배제(Mutual Exclusion), 점유와 대기(Hold and Wait), 비선점(No Preemption), 순환 대기(Circular Wait) 등의 알고리즘을 통해 극복해야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;다만, 데드락은 멀티 스레드만의 단점이라기 보다는 멀티 프로세스와 스레드 모델의 공통된 문제점이라고 말하는 것이 옳다. 왜냐하면 프로세스 끼리는 기본적으로 독립적인 메모리 공간이지만 IPC를 통해 공유 자원을 사용할 수 있기 때문에 멀티 스레드와 똑같이 교착 상태에 빠질 수 있기 때문이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;81af&quot; style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;4.&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;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Context Switching Overhead&lt;/b&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;앞서 멀티 프로세스보다 멀티 스레드의 컨텍스트 스위칭 오버헤드가 작아 성능에 유리하다라고 설명했었지만, 그래도 컨텍스트 스위칭 오버헤드 비용 자체를 무시할수는 없다. 특히나 스레드 수가 많으면 많을 수록 그만큼 컨텍스트 스위칭이 많이 발생되게 되고 당연히 이는 성능 저하로 이어진다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;이 부분은 &lt;span style=&quot;color: #ee2323;&quot;&gt;'스레드를 많이 쓸수록 항상 성능이 좋아질까?'&lt;/span&gt; 라는 물음으로 던질 수 있다. 보통 사람들이 생각하기에는 스레드가 많으면 많을 수록 그만큼 동시 처리수가 늘어나 당연히 스레드가 많으면 무조건 좋다고 이야기할 것이다. 하지만 '컨텍스트 스위칭 오베허드'라는 개념을 알고 있는 개발자인 우리들은 '과연 꼭 그럴까?' 라는 의문을 던져야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;이 부분은 스레드를 겉햝기로만 배운 지원자를 걸러내기 위해 기술 면접에서 가끔 등장하는 고수준의 질문이기도 하다. 이에 대한 자세한 내용은 아래 포스팅을 참고하길 바란다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;-------------------------------------- 링크 나중에 추가 --------------------------&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;81af&quot; style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;5.&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;디버깅이 어려움&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;멀티 스레드를 사용하면, 여러 개의 스레드가 동시에 실행되기 때문에, 각 스레드의 동작을 추적하기 어려울 수 있다. 예를들어 코드를 디버깅하는 도중에 다른 스레드가 실행되어 예기치 않은 결과가 발생할 수 있다. 또한 어떤 스레드가 언제 어떤 자원에 접근하고, 어떤 순서로 실행되는지 등을 파악하기 어려울 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-selectable-paragraph=&quot;&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-origin-width=&quot;640&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sxYJz/btr76ZeA0uk/UZQiQEy8Ki1jcnH5AzsmP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sxYJz/btr76ZeA0uk/UZQiQEy8Ki1jcnH5AzsmP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sxYJz/btr76ZeA0uk/UZQiQEy8Ki1jcnH5AzsmP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsxYJz%2Fbtr76ZeA0uk%2FUZQiQEy8Ki1jcnH5AzsmP1%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; alt=&quot;Multi-Thread&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;240&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;81af&quot; style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;6.&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;운영체제의&amp;nbsp;지원이&amp;nbsp;필요&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;오늘날의 윈도우, 리눅스, 맥 OS에선 모두 기본적으로 멀티 스레딩을 기본적으로 지원하도록 설계 되었으니 문제점이라기에는 약간 어폐가 있긴 하다. 하지만, 1980년대의 SunOS3와 같은 오래된 유닉스 시스템에는 스레드가 없었고 프로세스만 있는, 멀티 스레딩을 지원하지 않는 운영 체제가 있었기 때문에 멀티 스레드의 단점으로 넣어 보았다. (그만 잊어도 된다  )&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://brunch.co.kr/@huewu/4&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://charlezz.medium.com/process%EC%99%80-thread-%EC%9D%B4%EC%95%BC%EA%B8%B0-5b96d0d43e37&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://eunjinii.tistory.com/41&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://jojozhuang.github.io/programming/java-concurrency-dead-lock/&lt;/p&gt;</description>
      <category>개발 지식/CS 지식</category>
      <category>multi</category>
      <category>멀티 스레드</category>
      <category>멀티 쓰레드</category>
      <category>멀티 프로세스</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1107</guid>
      <comments>https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-multi-process-multi-thread#entry1107comment</comments>
      <pubDate>Wed, 5 Apr 2023 12:18:00 +0900</pubDate>
    </item>
    <item>
      <title> &amp;zwj;  &amp;zwj;완전히 정복하는 프로세스 vs 스레드 개념</title>
      <link>https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%E2%9A%94%EF%B8%8F-%EC%93%B0%EB%A0%88%EB%93%9C-%EC%B0%A8%EC%9D%B4</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1403&quot; data-origin-height=&quot;618&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vRjZL/btr5UlMIPVj/hYwqkdTncpD04qhq11Aik0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vRjZL/btr5UlMIPVj/hYwqkdTncpD04qhq11Aik0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vRjZL/btr5UlMIPVj/hYwqkdTncpD04qhq11Aik0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvRjZL%2Fbtr5UlMIPVj%2FhYwqkdTncpD04qhq11Aik0%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; alt=&quot;프로세스-쓰레드&quot; loading=&quot;lazy&quot; width=&quot;1403&quot; height=&quot;618&quot; data-origin-width=&quot;1403&quot; data-origin-height=&quot;618&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;한눈에 이해하는 프로세스 &amp;amp; 스레드 개념&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전공 지식 없이 컴퓨터의 프로그램을 이용하는데는 문제 없어 왔지만 소프트웨어를 개발하는 사람으로서 컴퓨터 실행 내부 요소를 따져보게 될때, 아마 컴퓨터 운영체제에 대해 입문하게 되면 가장 먼저 듣고 배우게 될 개념이 &lt;b&gt;프로세스와 스레드&lt;/b&gt; 일 것이다. 결론 부터 말하자면 이 둘을 한마디로 정의하자면 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style11&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;b&gt;프로세스 (Process)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;b&gt;스레드 (Thread)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;운영체제로부터 자원을 할당받은 &lt;b&gt;작업의 단위&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;프로세스가 할당받은 자원을 이용하는 &lt;b&gt;실행 흐름의 단위&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;운영체제(OS)에 대한 기본 배경 지식이 없다면 위의 정의가 무슨 소리인지 이해가 잘 되지 않을 것이다. 그래도 위의 문장에는 나름 설명이 함축되어 잘 정리 되어 있는 셈이다. 일단 프로세스의 &lt;b&gt;작업의 단위&lt;/b&gt; 라는 단어와 스레드의 &lt;b&gt;실행 흐름의 단위&lt;/b&gt; 라는 단어를 기억해 두고 글을 읽어보자. 글을 정독했을때 저 단어의 의미에 대해 확실히 깨닫게 될 것이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;프로그램 과 프로세스&lt;/b&gt;&lt;/h3&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;정적&amp;nbsp;프로그램&amp;nbsp;(Static&amp;nbsp;Program)&lt;/b&gt;&lt;/h4&gt;
&lt;p id=&quot;c914&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;컴퓨터 전공이 아니라서 '프로세스' 라는 명칭은 낯설수 있는데, '프로그램' 은 친숙하리라 생각된다.&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;프로그램은 윈도우의 &lt;s&gt;*.exe&lt;/s&gt; 파일이나 Mac의 &lt;s&gt;*.dmg&lt;/s&gt; 파일과 같은 &lt;b&gt;컴퓨터에서 실행 할 수 있는 파일&lt;/b&gt;을 통칭한다. 단, 아직 &lt;b&gt;파일을 실행하지 않은 상태&lt;/b&gt;이기 때문에 정적 프로그램(Static Program) 줄여서 프로그램(Program)이라고 부른 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;255&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceVAkD/btrjmWmm6EV/5QmuHJzIiEGs2P8naJIIx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceVAkD/btrjmWmm6EV/5QmuHJzIiEGs2P8naJIIx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceVAkD/btrjmWmm6EV/5QmuHJzIiEGs2P8naJIIx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceVAkD%2FbtrjmWmm6EV%2F5QmuHJzIiEGs2P8naJIIx1%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; alt=&quot;Static Program&quot; loading=&quot;lazy&quot; width=&quot;480&quot; height=&quot;120&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;255&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;어떠한 프로그램을 개발하기 위해선 자바나 C언어와 같은 언어를 이용해 코드를 작성하여 완성된다. 즉, 프로그램은 쉽게 말해서 그냥 &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-origin-width=&quot;259&quot; data-origin-height=&quot;247&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1kOEf/btr5BmXGOQZ/TbEhxnH6Pk2kZKwJeGJ370/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1kOEf/btr5BmXGOQZ/TbEhxnH6Pk2kZKwJeGJ370/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1kOEf/btr5BmXGOQZ/TbEhxnH6Pk2kZKwJeGJ370/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1kOEf%2Fbtr5BmXGOQZ%2FTbEhxnH6Pk2kZKwJeGJ370%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; alt=&quot;Static Program&quot; loading=&quot;lazy&quot; width=&quot;259&quot; height=&quot;247&quot; data-origin-width=&quot;259&quot; data-origin-height=&quot;247&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;프로세스 (Process)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;프로그램이 그냥 코드 덩어리이면, 프로세스는 프로그램을 실행 시켜 &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;정적인 프로그램이 동적(動的)으로 변하여 &lt;b&gt;프로그램이 돌아가고 있는 상태&lt;/b&gt;를 말한다. 즉, 컴퓨터에서 &lt;b&gt;작업&lt;/b&gt; 중인 프로그램을 의미하는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;이 개념은 절대 생소한 것이 아니다. &lt;s&gt;ctrl + alt + del&lt;/s&gt; 단축키를 눌러 우리가 항상 보던 '작업' 관리자를 열어보면 개념이 고대로 들어있는걸 볼 수 있을 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;834&quot; data-origin-height=&quot;711&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHJCJX/btr5qe72byg/EKg3e9idwQuug9BO9Pw7ck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHJCJX/btr5qe72byg/EKg3e9idwQuug9BO9Pw7ck/img.png&quot; data-alt=&quot;작업 끝내기는 실행중인 프로그램인 프로세스를 끝내는 것이다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHJCJX/btr5qe72byg/EKg3e9idwQuug9BO9Pw7ck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHJCJX%2Fbtr5qe72byg%2FEKg3e9idwQuug9BO9Pw7ck%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; alt=&quot;Process&quot; loading=&quot;lazy&quot; width=&quot;732&quot; height=&quot;624&quot; data-origin-width=&quot;834&quot; data-origin-height=&quot;711&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;작업 끝내기는 실행중인 프로그램인 프로세스를 끝내는 것이다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;모든 프로그램은 운영체제가 실행되기 위한 메모리 공간을 할당해 줘야 실행될 수 있다. 그래서 프로그램을 실행하는 순간 파일은 컴퓨터 메모리에 올라가게 되고, &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;운영체제로부터 시스템 자원(CPU)을 할당받아&lt;span&gt; 프로그램 코드를 실행시켜 우리가 서비스를 이용할 수 있게 되는 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/owelr/btr5qetomgT/NlCK025Ylg0L7u4s1ot1V1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/owelr/btr5qetomgT/NlCK025Ylg0L7u4s1ot1V1/img.png&quot; data-alt=&quot;프로그램 &amp;amp;amp;rarr; 프로세스&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/owelr/btr5qetomgT/NlCK025Ylg0L7u4s1ot1V1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fowelr%2Fbtr5qetomgT%2FNlCK025Ylg0L7u4s1ot1V1%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; alt=&quot;Process&quot; loading=&quot;lazy&quot; width=&quot;721&quot; height=&quot;311&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;388&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;프로그램 &amp;amp;rarr; 프로세스&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;어쨋든 똑같은 어플리케이션을 실행 하냐 안하냐 차이일 뿐이라서, 일반적으로 프로세스와 프로그램을 같은 개념으로 이야기할 때가 많지만, 정의를 보았듯이 엄밀히 따지면 이 둘은 다른 개념인 것이다.&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;최종적으로 이 둘을 간단 명료하게 정리하면 아래 표와 같다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;b&gt;프로그램&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;b&gt;프로세스&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;어떤 작업을 하기 위해 실행할 수 있는 파일&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;실행되어 작업중인 컴퓨터 프로그램&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;파일이 저장 장치에 있지만 메모리에는 올라가 있지 않은 정적인 상태&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;메모리에 적재되고 CPU 자원을 할당받아 프로그램이 실행되고 있는 상태&lt;br /&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;쉽게 말해 그냥 코드 덩어리&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;그 코드 덩어리를 실행한 것&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;프로세스의 한계&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거에는&amp;nbsp;프로그램을&amp;nbsp;실행할&amp;nbsp;때 프로세스 하나만을 사용해서 이용했었다. 하지만 기술이 발전됨에 따라 프로그램이 복잡해지고 다채로워짐으로써 프로세스 작업 하나만을 사용해서 프로그램을 실행하기에는 한계가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘날 컴퓨터는 파일을 다운 받으며 다른 일을 하는 멀티 작업은 너무 당연한 기능이라고 생각할지 모르겠지만, 과거에는 파일을 다운받으면 실행 시작부터 실행 끝까지 프로세스 하나만을 사용하기 때문에 다운이 완료될때까지 하루종일 기다려야 했다. 그렇다고 동일한 프로그램을 여러 개의 프로세스로 만들게 되면, 그만큼 메모리를 차지하고 CPU에서 할당받는 자원이 중복되게 될 것이다.&amp;nbsp;스레드(Thread)는 이러한 프로세스 특성의 한계를 해결하기 위해 탄생 하였다.&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;스레드의 개념&lt;/b&gt;&lt;/h4&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;b&gt;프로세스 내에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;b&gt;동시에 진행되는 작업 갈래, 흐름의 단위&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt; &lt;/span&gt;를 말한다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이해하기 쉽게 비유를 들자면, 크롬 브라우저가 실행 되면 프로세스 하나가 생성될 것이다. 그런데 우리는 브라우저에서 파일을 다운 받으며 온라인 쇼핑을 하면서 게임을 하기도 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1254&quot; data-origin-height=&quot;215&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZTlpX/btr5sc925yl/44UlJFuSzqE1aXQorelcQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZTlpX/btr5sc925yl/44UlJFuSzqE1aXQorelcQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZTlpX/btr5sc925yl/44UlJFuSzqE1aXQorelcQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZTlpX%2Fbtr5sc925yl%2F44UlJFuSzqE1aXQorelcQK%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; alt=&quot;thread&quot; loading=&quot;lazy&quot; width=&quot;1254&quot; height=&quot;215&quot; data-origin-width=&quot;1254&quot; data-origin-height=&quot;215&quot;/&gt;&lt;/span&gt;&lt;/figure&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;&amp;nbsp;&lt;/span&gt;&lt;/span&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;/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;784&quot; data-origin-height=&quot;421&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DsoFf/btr5DUzHEjF/c0iSc5X72znm0QQpbvrr4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DsoFf/btr5DUzHEjF/c0iSc5X72znm0QQpbvrr4k/img.png&quot; data-alt=&quot;스레드를 지렁이 같이 표현한 이유는 번역하면 실타래 이며, 실행 흐름을 표현하기 위해서이다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DsoFf/btr5DUzHEjF/c0iSc5X72znm0QQpbvrr4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDsoFf%2Fbtr5DUzHEjF%2Fc0iSc5X72znm0QQpbvrr4k%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; alt=&quot;thread&quot; loading=&quot;lazy&quot; width=&quot;633&quot; height=&quot;340&quot; data-origin-width=&quot;784&quot; data-origin-height=&quot;421&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;span style=&quot;color: #333333; text-align: start;&quot;&gt;를 갖는다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;즉, 프로세스를 생성하면 기본적으로 하나의 main 스레드가 생성되게 된다. 스레드 2개, 3개.. 는 프로그램을 개발한 개발자가 직접 프로그래밍하여 위치 시켜주어야 한다. 그래서 별도로 스레드 프로그래밍 과목이 있는 이유이기도 하다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 프로그램, 프로세스, 스레드에 관한 아주 기본적인 개념 흐름과 차이에 대해 알아보았다. 그러나 이정도의 운영체제 지식만으로는&amp;nbsp;&quot;프로세스와 스레드의 차이에 대해 말하라는&quot;&lt;span&gt;&amp;nbsp;&lt;/span&gt;기술 면접에서 &lt;span style=&quot;color: #ee2323;&quot;&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;b&gt;내부 작동 방식&lt;/b&gt;을 이해하고 있는지 궁금해하는 면접관이 대부분이기 때문이다. 사실 위에서 한 설명은 뉴비라도 최대한 이해하기 쉽게 풀어쓴 내용이라 저 부분만으로는 프로세스와 스레드를 이해하는데 있어 많이 부족하다. 운영체제가 시스템 자원을 어떤 방식으로 할당하고 프로그램은 자원을 어떤 방식으로 활용하는 내부 작동 방식을 이해하고 있어야, 기술 면접도 그렇고 나중에 스레드 프로그래밍 할때 응용이 가능하기 때문에 이 포스팅을 끝까지 읽는 것을 권장하는 바다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;프로세스 &amp;amp; 스레드의 메모리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;프로세스의 자원 구조&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램이 실행되어 프로세스가 만들어지면 다음 4가지의 메모리 영역으로 구성되어 할당 받게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;236&quot; data-origin-height=&quot;366&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brkLMD/btr62G1oUxX/ViLrf3C3n5j2Vi5rUuroUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brkLMD/btr62G1oUxX/ViLrf3C3n5j2Vi5rUuroUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brkLMD/btr62G1oUxX/ViLrf3C3n5j2Vi5rUuroUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrkLMD%2Fbtr62G1oUxX%2FViLrf3C3n5j2Vi5rUuroUk%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; alt=&quot;process&quot; loading=&quot;lazy&quot; width=&quot;236&quot; height=&quot;366&quot; data-origin-width=&quot;236&quot; data-origin-height=&quot;366&quot;/&gt;&lt;/span&gt;&lt;/figure&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;코드 영역(Code / Text)&lt;/b&gt; : &lt;span style=&quot;text-align: start;&quot;&gt;프로그래머가 작성한 프로그램 함수들의 코드가 CPU가 해석 가능한 기계어 형태로 저장되어 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터 영역(Data)&lt;/b&gt; : 코드가 실행되면서 사용하는 전역 변수나 각종 데이터들이 모여있다. 데이터영역은 .data ,.rodata, .bss 영역으로 세분화 된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;.data : 전역 변수 또는 static 변수 등 프로그램이 사용하는 데이터를 저장&lt;/li&gt;
&lt;li&gt;.BSS : 초기값 없는 전역 변수, static 변수가 저장&lt;/li&gt;
&lt;li&gt;.rodata : const같은&amp;nbsp;상수&amp;nbsp;키워드&amp;nbsp;선언&amp;nbsp;된&amp;nbsp;변수나&amp;nbsp;문자열&amp;nbsp;상수가&amp;nbsp;저장&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스택 영역(Stack)&lt;/b&gt; : 지역 변수와 같은 호출한 함수가 종료되면 되돌아올 임시적인 자료를 저장하는 독립적인 공간이다. Stack은 함수의 호출과 함께 할당되며, 함수의 호출이 완료되면 소멸한다. 만일 stack 영역을 초과하면 stack overflow 에러가 발생한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;힙 영역(Heap)&lt;/b&gt; : &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: left;&quot;&gt;생성자, 인스턴스와 같은&lt;span&gt; &lt;/span&gt;&lt;/span&gt;동적으로 할당되는 데이터들을 위해 존재하는 공간이다. 사용자에&amp;nbsp;의해&amp;nbsp;메모리&amp;nbsp;공간이&amp;nbsp;동적으로&amp;nbsp;할당되고&amp;nbsp;해제된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 그림에서 Stack과 Heap 영역이 위아래로 화살표가 쳐 있는 것을 볼 수 있는데, 이는 코드 영역과 데이터 영역은 선언할 때 그 크기가 결정되는 정적 영역이지만, 스택 영역과 힙 영역은 프로세스가 실행되는 동안 &lt;b&gt;크기가 늘어났다 줄어들기도 하는 동적 영역&lt;/b&gt;이기 때문에 이를 표현한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 그림을 보면서 간략히 정리하자면, 프로그램이 여러개 실행된다면 메모리에 프로세스들이 담길 주소 공간이 생성되게 되고 그 안에 Code, Data, Stack, Heap 공간이 만들어지게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;954&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsW59V/btrjmVgGOOf/p9x76pH3Dp7KrF1MOOkRKk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsW59V/btrjmVgGOOf/p9x76pH3Dp7KrF1MOOkRKk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsW59V/btrjmVgGOOf/p9x76pH3Dp7KrF1MOOkRKk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bsW59V/btrjmVgGOOf/p9x76pH3Dp7KrF1MOOkRKk/img.gif&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; alt=&quot;process&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;954&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;954&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&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;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;프로세스의 자원을 공유&lt;/b&gt;하면서 프로세스 실행 흐름의 일부가 되기 때문에 동시 작업이 가능한 것이다. 그래서 아래 사진과 같이 하나의 프로세스 내에 여러개의 스레드가 들어있는 상태인 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;637&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rEyKM/btrjvscVVdS/KIm70G6RBncpnX4xBi8Rh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rEyKM/btrjvscVVdS/KIm70G6RBncpnX4xBi8Rh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rEyKM/btrjvscVVdS/KIm70G6RBncpnX4xBi8Rh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrEyKM%2FbtrjvscVVdS%2FKIm70G6RBncpnX4xBi8Rh0%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; alt=&quot;thread&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;637&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;637&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 프로세스의 4가지 메모리 영역(Code, Data, Heap, Stack) 중 스레드는 Stack만 할당받아 복사하고 Code, Data, Heap은 프로세스내의 다른 스레드들과 &lt;b&gt;공유&lt;/b&gt;된다. 따라서 각각의 스레드는 별도의 stack을 가지고 있지만 heap 메모리는 고유하기 때문에 서로 다른 스레드에서 가져와 읽고 쓸 수 있게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1148&quot; data-origin-height=&quot;297&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c6hP8P/btr5A5vOmq1/lc1Ya3NafKtUsg6s10SCIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c6hP8P/btr5A5vOmq1/lc1Ya3NafKtUsg6s10SCIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c6hP8P/btr5A5vOmq1/lc1Ya3NafKtUsg6s10SCIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc6hP8P%2Fbtr5A5vOmq1%2Flc1Ya3NafKtUsg6s10SCIK%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; alt=&quot;thread&quot; loading=&quot;lazy&quot; width=&quot;1148&quot; height=&quot;297&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1148&quot; data-origin-height=&quot;297&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;stack은 함수 호출 시 전달되는 인자, 되돌아갈 주소값, 함수 내에서 선언하는 변수 등을 저장하는 메모리 공간이기 때문에, 독립적인 스택을 가졌다는 것은 독립적인 함수 호출이 가능하다 라는 의미이다. 그리고 독립적인 함수 호출이 가능하다는 것은 독립적인 실행 흐름이 추가된다는 말이다.&lt;br /&gt;즉, stack을 가짐으로써 스레드는 독립적인 실행 흐름을 가질 수 있게 되는 것이다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;반면에 프로세스는 기본적으로 프로세스 끼리 다른 프로세스의 메모리에 직접 접근할 수는 없다.&lt;/blockquote&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;style3&quot; /&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;프로세스의 자원 공유&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기본적으로 각 프로세스는 메모리에 별도의 주소 공간에서 실행되기 때문에, 한 프로세스는 다른 프로세스의 변수나 자료구조에 접근할 수는 없다. 그렇다면 프로세스는 영원히 다른 프로세스 정보에 접근할 수 없을까?&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;현재 우리가 사용하는 대부분의 컴퓨터 프로그램을 보면 다른 프로그램에 있는 정보를 가져오는 경우는 심심치 않게 볼 수 있을 것이다. 이처럼 특별한 방법을 통해 프로세스가 다른 프로세스의 정보에 접근하는 것이 가능하다.&amp;nbsp;&lt;/span&gt;프로세스 간 정보를 공유하는 방법에는 다음과 같은 방법들이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;IPC(Inter-Process Communication) 사용&lt;/li&gt;
&lt;li&gt;LPC(Local inter-Process Communication) 사용&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-origin-width=&quot;520&quot; data-origin-height=&quot;330&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dg3c8b/btr5NaDQOYJ/Ad0k1iu2eZdI6x5fMRzfdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dg3c8b/btr5NaDQOYJ/Ad0k1iu2eZdI6x5fMRzfdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dg3c8b/btr5NaDQOYJ/Ad0k1iu2eZdI6x5fMRzfdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdg3c8b%2Fbtr5NaDQOYJ%2FAd0k1iu2eZdI6x5fMRzfdk%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; alt=&quot;프로세스의 자원 공유&quot; loading=&quot;lazy&quot; width=&quot;520&quot; height=&quot;330&quot; data-origin-width=&quot;520&quot; data-origin-height=&quot;330&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그러나 프로세스 자원 공유는 단순히 CPU 레지스터 교체뿐만이 아니라 RAM과 CPU 사이의 캐시 메모리까지 초기화되기 때문에 &lt;span style=&quot;color: #ee2323;&quot;&gt;자원 부담이 크다는 단점이 있다. 그래서 다중 작업이 필요한경우 스레드를 이용하는 것이 훨씬 효율적&lt;/span&gt;이라, 현대 컴퓨터의 운영체제에선 다중 프로세싱을 지원하고 있지만 다중 스레딩을 기본으로 하고 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;프로세스 &amp;amp; 스레드의 동시 실행 원리&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; 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;a href=&quot;https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-multi-programming-tasking-processing&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;멀티 태스킹(multi tasking)&lt;/a&gt; 기술 때문이다. 하지만 여기서 동시에 처리한다는 것이 심플하게 CPU 프로세서가 프로그램들을 한꺼번에 동시에 돌리는 것으로 생각하겠지만, 내부적으로 복잡한 원리에 의해 처리가 된다. 그리고 이 원리가 운영체제 이론의 핵심 원리이기도 하다. 지금부터 그 원리에 대해 파헤쳐 보도록 하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9BqIv/btr7Su0b1Cn/tien9xWFjXcApNwz8i3fa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9BqIv/btr7Su0b1Cn/tien9xWFjXcApNwz8i3fa0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9BqIv/btr7Su0b1Cn/tien9xWFjXcApNwz8i3fa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9BqIv%2Fbtr7Su0b1Cn%2Ftien9xWFjXcApNwz8i3fa0%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; alt=&quot;Process 와 Thread&quot; loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;900&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;900&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;style3&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;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;4코어 8쓰레드 CPU&lt;/b&gt;에 대한 단어를 본 적이 있을 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1019&quot; data-origin-height=&quot;580&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bub75V/btr7OypayUB/YrMxZ3c6EmNhQpTBU1FLz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bub75V/btr7OypayUB/YrMxZ3c6EmNhQpTBU1FLz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bub75V/btr7OypayUB/YrMxZ3c6EmNhQpTBU1FLz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbub75V%2Fbtr7OypayUB%2FYrMxZ3c6EmNhQpTBU1FLz1%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; alt=&quot;Process 와 Thread&quot; loading=&quot;lazy&quot; width=&quot;1019&quot; height=&quot;580&quot; data-origin-width=&quot;1019&quot; data-origin-height=&quot;580&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU 한 개는 여러개의 코어를 가질수 있다.&amp;nbsp;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;코어는 말그대로 CPU 코어 유닛이다. 즉, 명령어를 메모리에서 뽑아 해석하고 실행하는 반도체 유닛이 4개가 있는 것이다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;4코어가 물리적 코어 갯수면, 8쓰레드는 논리적 코어 갯수를 말한다. 이 경우 물리적 코어 하나가 스레드 두 개 이상을 동시에 실행 가능하다는 의미가 된다. &lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;즉, 운영체제가 8개의 작업을 동시에 처리할 수 있다는 뜻이다. 이를 하이퍼스레딩(Hyper-Threading) 기술이라 말한다.&lt;/span&gt;&lt;/span&gt;&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;590&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjb3Ie/btr5NuCu8wO/9pIz5ySKT8L1YhebTitTj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjb3Ie/btr5NuCu8wO/9pIz5ySKT8L1YhebTitTj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjb3Ie/btr5NuCu8wO/9pIz5ySKT8L1YhebTitTj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbjb3Ie%2Fbtr5NuCu8wO%2F9pIz5ySKT8L1YhebTitTj1%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; alt=&quot;Process 와 Thread&quot; loading=&quot;lazy&quot; width=&quot;383&quot; height=&quot;387&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;590&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;단, 여기서 CPU의 쓰레드는 우리가 배운 프로세스의 쓰레드와는 조금 다른 개념이다. 엄밀히 말하자면 CPU의 스레드는 하드웨어적 스레드이고 프로그램의 스레드는 소프트웨어적 스레드로 구분한다.&lt;/blockquote&gt;
&lt;p id=&quot;6a8c&quot; style=&quot;color: #333333; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;그런데 우리는 컴퓨터를 이용할때 프로그램을 수십, 수백개를 켜 놓고 이용한다. 그럼 그 수십수백개의 프로세스들을 고작 8개의 논리적인 스레드도 어떻게 처리하는 것일까?&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: #ee2323;&quot;&gt;병렬성(Parallelism)&lt;/span&gt; 과 &lt;span style=&quot;color: #ee2323;&quot;&gt;동시성(Concurrency)&lt;/span&gt;이라는 개념을 알고 있어야 한다. 이 개념은 운영체제의 프로세스, 스레드를 이해하는데 있어 가장 핵심 골자가 되는 녀석들이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;CPU의 작업 처리 방식&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;병렬성 (Parallelism)&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;병렬성은 직관적으로 &lt;span style=&quot;text-align: start;&quot;&gt;명령어를 메모리에서 뽑아 해석하고 실행하는 반도체 유닛인 &lt;/span&gt;여러개의 코어에 맞춰 여러개의 프로세스, 스레드를 돌려 병렬로 작업들을 동시 수행하는 것을 말한다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;973c3ade-550e-4c0a-9bc4-fc01c57c6fd4.jpg&quot; data-origin-width=&quot;349&quot; data-origin-height=&quot;307&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Pcsub/btr6pcBtQ4X/yT06aPpQtfuR61ZGUxK3K1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Pcsub/btr6pcBtQ4X/yT06aPpQtfuR61ZGUxK3K1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Pcsub/btr6pcBtQ4X/yT06aPpQtfuR61ZGUxK3K1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPcsub%2Fbtr6pcBtQ4X%2FyT06aPpQtfuR61ZGUxK3K1%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; alt=&quot;Parallelism&quot; loading=&quot;lazy&quot; width=&quot;349&quot; height=&quot;307&quot; data-filename=&quot;973c3ade-550e-4c0a-9bc4-fc01c57c6fd4.jpg&quot; data-origin-width=&quot;349&quot; data-origin-height=&quot;307&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;듀얼코어, 쿼드코어, 옥타코어 등등 이런 명칭이 붙는 멀티코어 프로세서가 달린 컴퓨터에서 할 수 있는 방식이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;208&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxfEzj/btr5DTA7uSB/oqAaKD5L2a5aMJPLlIIkA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxfEzj/btr5DTA7uSB/oqAaKD5L2a5aMJPLlIIkA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxfEzj/btr5DTA7uSB/oqAaKD5L2a5aMJPLlIIkA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxfEzj%2Fbtr5DTA7uSB%2FoqAaKD5L2a5aMJPLlIIkA0%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; alt=&quot;Parallelism&quot; loading=&quot;lazy&quot; width=&quot;1125&quot; height=&quot;208&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;208&quot;/&gt;&lt;/span&gt;&lt;/figure&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;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;동시성 (Concurrency)&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;동시성은 둘 이상의 작업이 동시에 실행되는 것을 의미한다. 이 '동시' 라는 의미에서 병렬성과 동시성의 한글 의미가 헷갈릴수 있다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;Parallelism가 물리적으로 정말로 동시에 실행하는 것이라고 하면, Concurrency는 동시에 실행하는 것처럼 보이게 하는 것으로 이해하면 된다.&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;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;즉, 1개의 코어가 있고 4개의 작업이 있다고 가정하다면, 아래 그림과 같이 &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;프로세스들을 ​계속 번갈아가면서 조금씩 처리함으로써 마치 프로그램이 동시에 실행되는 것 처럼 보이는 것이다. 이때&amp;nbsp;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;프로세스들을 ​번갈아가면서 매우 빠르게 처리하기 때문에 컴퓨터를 &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;모르는 사람들이 보면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;마치 동시에 돌아가는 것처럼 보이게 된다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;472&quot; data-origin-height=&quot;332&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sQEdm/btr5E4vBnEv/OkkR4QyAi8BHVr2oAewA8k/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sQEdm/btr5E4vBnEv/OkkR4QyAi8BHVr2oAewA8k/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sQEdm/btr5E4vBnEv/OkkR4QyAi8BHVr2oAewA8k/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/sQEdm/btr5E4vBnEv/OkkR4QyAi8BHVr2oAewA8k/img.gif&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; alt=&quot;Concurrency&quot; loading=&quot;lazy&quot; width=&quot;472&quot; height=&quot;332&quot; data-origin-width=&quot;472&quot; data-origin-height=&quot;332&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;단, 이때 작업들을 번갈아가면서 실행할때 작업들을 &lt;span style=&quot;color: #ee2323;&quot;&gt;아주 잘게 나누어 아주 조금씩만 작업을 수행&lt;/span&gt;하고 다음 작업으로 넘어가는 식으로 동작된다. 이렇게 하는 이유는 &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;여러 작업을 동시에 처리하는 것처럼 보이게 만들어, 사용자에게 더 빠른 반응성을 제공하기 위해서다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;그리고 이렇게 진행 중인 작업들을 A &amp;rarr; B&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&amp;rarr;&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;C&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&amp;rarr; D&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;로 번갈아 바꾸는 것을&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Context Switching&lt;/b&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;이라고 부른다.&lt;/span&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;blob&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;340&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9geHW/btr60hVMU9c/CE6fmKqDakFv0kJIl7GCEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9geHW/btr60hVMU9c/CE6fmKqDakFv0kJIl7GCEk/img.png&quot; data-alt=&quot;각 프로세스 작업을 작게 쪼개어 번갈아 처리&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9geHW/btr60hVMU9c/CE6fmKqDakFv0kJIl7GCEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9geHW%2Fbtr60hVMU9c%2FCE6fmKqDakFv0kJIl7GCEk%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; alt=&quot;Concurrency&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;340&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;340&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;각 프로세스 작업을 작게 쪼개어 번갈아 처리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;동시성이 필요한 이유&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그런데 상식적으로 생각해보면 동시성(Concurrency)은&amp;nbsp; '동시에 돌아가는 것 처럼' 보이는 거지, 정말 실제로 동시에 돌아가는 것이 아니기 때문에 최종 작업이 걸리는 시간은 거의 차이가 없을 것이다. 병렬성은 정말로 각 코어에 프로세스를 나눠 실행하는 거니까 듀얼 코어면 반 이상 줄어들텐데 말이다. 그렇다면 왜 이렇게 번거롭게 작업들을 스위칭 하며 처리하는 것일까?&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;첫번째는 &lt;b&gt;하드웨어적 한계&lt;/b&gt; 때문이라고 할 수있다. CPU 발열 때문에 깡 클럭으로 성능을 올리기에는 한계에 봉착됬기 때문에 코어의 성능을 올리는 대신 코어를 여러개 탑재하여 쿼드 코어, 옥타 코어 CPU들을 출시하고 있다. 하지만 아무리 코어를 많이 넣어도 수십개의 코어를 넣은순 없으니 결국 하드웨어적 제한이 걸리게 되고 수십수백개의 프로세스를 돌리기 위해선 결국 &lt;/span&gt;&lt;span style=&quot;color: #212529;&quot;&gt;동시성이 필요한 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;두번째는 보다 &lt;b&gt;논리적인 효율&lt;/b&gt;적인 이유에서이다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;4코어 8스레드의 CPU 환경에서 현재 총 16개의 작업이 있다고 가정을 해보자. 그중 8개는 오래 걸리는 작업이고, 나머지 8개는 짧은 시간을 필요로 하는 작업이라고 한다. 논리적인 8개의 코어이니 최대 8개까지 동시에 실행할수 있을텐데, 만일 최악의 경우 8개의 오래 걸리는 작업이 먼저 동시에 처리되기 시작했다고 하자. 이 경우 나머지 가벼운 8개의 작업은 처리하는데 짧은 시간이 걸리는 데에도 불구하고 현재 처리중인 8개의 &lt;span style=&quot;color: #ee2323;&quot;&gt;작업이 다 끝날때 까지 기다려야 할 것이다.&lt;/span&gt; 따라서 이러한 비효율적인 면을 극복하기 위해 &lt;span style=&quot;color: #ee2323;&quot;&gt;작업을 아주 잘게 나눠 번갈아 가면서 처리&lt;/span&gt;하는 동시성 개념을 채택한 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;따라서 최대 8개의 작업에 대해서 8개의 논리적인 스레드가 병렬적으로 아주 빠르게 동시적으로 작업을 하면서, 그보다 많은 수십개의 소프트웨어적 스레드가 있다면 &lt;span style=&quot;color: #ee2323;&quot;&gt;적절히 병렬성과 동시성을 섞어&lt;/span&gt; 동시에 돌리게 되게 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;프로세스 &amp;amp; 스레드의 생명 주기&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;프로세스-스케줄링-process-scheduling&quot; style=&quot;background-color: #ffffff; color: #383838; 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;size16&quot;&gt;프로세스 스케줄링(Process Scheduling)은 운영체제에서 CPU를 사용할 수 있는 프로세스를 선택하고, CPU를 할당하는 작업을 말한다. 프로세스 스케줄링은 프로세스의 우선순위, 작업량 등을 고려하여 효율적으로 배치하여, 이를 통해 운영체제는 CPU를 효율적으로 사용하며 시스템 전반적인 성능을 향상시킨다. 그래서 스케줄링은 멀티 태스킹 작업을 만들어내는 데에 있어서 핵심적인 부분이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스케쥴링은 운영체제의 특징과 시스템 요구사항에 따라 다양한 알고리즘 방식으로 동작된다. 알고리즘 종류로는 대표적으로 &lt;span style=&quot;background-color: #ffffff; color: #383838; text-align: left;&quot;&gt;FCFS(First-Come, First-Served),&lt;/span&gt;&lt;span style=&quot;color: #383838; text-align: left;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #383838; text-align: left;&quot;&gt;SJF(Shortest-Job-First),&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #383838; text-align: left;&quot;&gt;Priority,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #383838; text-align: left;&quot;&gt;RR(Round-Robin),&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #383838; text-align: left;&quot;&gt;Multilevel Queue 등이 있다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;287&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lkph4/btr7dUtmtAP/Nh8k3BklHTD4bEOwpVUokK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lkph4/btr7dUtmtAP/Nh8k3BklHTD4bEOwpVUokK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lkph4/btr7dUtmtAP/Nh8k3BklHTD4bEOwpVUokK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flkph4%2Fbtr7dUtmtAP%2FNh8k3BklHTD4bEOwpVUokK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;287&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;287&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;style3&quot; /&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;size16&quot;&gt;프로세스의 상태는 프로세스가 실행되는 동안 변경되는 고유 상태를 의미한다. 프로세스가 생성되어 실행하기 까지 프로세스는 여러가지의 상태를 갖게 되고, 상태의 변화에 따라 프로세스가 동작되는 것이다. 프로세스는 일반적으로 다음과 같은 5가지 상태를 가진다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.4651%;&quot;&gt;&lt;b&gt;프로세스 상태&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 79.4186%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.4651%;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;생성 (new)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 79.4186%;&quot;&gt;프로세스가&amp;nbsp;생성되고&amp;nbsp;아직&amp;nbsp;준비가&amp;nbsp;되지&amp;nbsp;않은&amp;nbsp;상태&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.4651%;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;준비 &lt;span style=&quot;text-align: left;&quot;&gt;(&lt;/span&gt;ready)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 79.4186%;&quot;&gt;프로세스가 실행을 위해 기다리는 상태&lt;br /&gt;CPU를 할당받을 수 있는 상태이며, 언제든지 실행될 준비가 되어있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.4651%;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;실행 &lt;span style=&quot;text-align: left;&quot;&gt;(&lt;/span&gt;running&lt;span style=&quot;text-align: left;&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 79.4186%;&quot;&gt;프로세스가&amp;nbsp;CPU를&amp;nbsp;할당받아&amp;nbsp;실행되는&amp;nbsp;상태&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.4651%;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;대기 &lt;span style=&quot;text-align: left;&quot;&gt;(&lt;/span&gt;waiting)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 79.4186%;&quot;&gt;프로세스가 특정 이벤트(입출력 요청 등)가 발생하여 대기하는 상태&lt;br /&gt;CPU를 할당받지 못하며, 이벤트가 발생하여 다시 READY 상태로 전환될 때까지 대기한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.4651%;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;종료 (terminated&lt;span style=&quot;text-align: left;&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 79.4186%;&quot;&gt;프로세스가 실행을 완료하고 종료된 상태&lt;br /&gt;더 이상 실행될 수 없으며, 메모리에서 제거되게 된다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;프로세스&amp;nbsp;상태&amp;nbsp;전이&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스 상태 전이란 프로세스가 실행되는 동안 상태가 OS에 의해 변경되는 것을 말한다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;운영체제는 프로세스의 상태를 감시하고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;프로세스 상태를 기반으로 프로세스 스케쥴링을 통해프로세스를 관리&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;하고 제어한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;예를 들어, READY 상태에 있는 여러 프로세스 중에서 어떤 프로세스를 RUNNING 상태로 바꿀지,&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;TERMINATED 상태에 있는 프로세스를 제거하고 READY 상태에 있는 다른 프로세스를 선택할지 스케쥴링 알고리즘에 의해 동작된다.&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;blob&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;474&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cs9ku3/btr5E9eoCKU/1DeDr8f5MUTUlpJ6mHQex1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cs9ku3/btr5E9eoCKU/1DeDr8f5MUTUlpJ6mHQex1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cs9ku3/btr5E9eoCKU/1DeDr8f5MUTUlpJ6mHQex1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcs9ku3%2Fbtr5E9eoCKU%2F1DeDr8f5MUTUlpJ6mHQex1%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; alt=&quot;process-state&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;474&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;474&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Admitted (new &amp;rarr; ready) : 프로세스 생성을 승인 받음&lt;/li&gt;
&lt;li&gt;Dispatch&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(ready&amp;rarr; running) &lt;/span&gt;: 준비 상태에 있는 여러 프로세스들 중 하나가 스케줄러에 의해 실행됨&lt;/li&gt;
&lt;li&gt;Interrupt&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(running &amp;rarr; ready)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;: Timeout, 예기치 않은 이벤트가 발생하여 현재 실행 중인 프로세스를 준비 상태로 전환하고, 해당 작업을 먼저 처리&lt;/li&gt;
&lt;li&gt;I/O or event wait&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(running &amp;rarr; wainting)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;: 실행 중인 프로세스가 입출력이나 이벤트를 처리해야 하는 경우, 입출력이나 이벤트가 끝날 때까지 대기 상태로 전환&lt;/li&gt;
&lt;li&gt;I/O or event completion&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(waiting &amp;rarr; ready)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;: 입출력이나 이벤트가 모두 끝난 프로세스를 다시 준비 상태로 만들어 스케줄러에 의해 선택될 수 있는 상태로 전환&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 id=&quot;프로세스-스케줄링-process-scheduling&quot; style=&quot;background-color: #ffffff; color: #383838; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;프로세스 컨텍스트&amp;nbsp;스위칭&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨텍스트 스위칭(Context Switching)은 CPU가 한 프로세스에서 다른 프로세스로 전환할 때 발생하는 일련의 과정을 말한다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;위의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;동시성 (Concurrency) 파트에서 다뤘듯이 &lt;/span&gt;CPU는 한 번에 하나의 프로세스만 실행할 수 있으므로, 여러 개의 프로세스를 번갈아가며 실행하여 CPU 활용률을 높이기 위해 컨텍스트 스위칭이 필요한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨텍스트 스위칭을 좀더 구체적으로 말하자면, 동작 중인 프로세스가 대기를 하면서 해당 프로세스의 상태(Context)를 보관하고, 대기하고 있던 다음 순서의 프로세스가 동작하면서 이전에 보관했던 프로세스의 상태를 복구하는 작업을 말한다. 이러한 컨텍스트 스위칭이 일어날 때 다음번 프로세스는 스케줄러가 결정하게 된다. 즉, 컨텍스트 스위칭을 하는 주체는 스케줄러이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GIF 2023-03-27 오전 11-09-34.gif&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;431&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ROR24/btr5YUUuMc3/967JnWEFrsKtK5aW5BeGMk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ROR24/btr5YUUuMc3/967JnWEFrsKtK5aW5BeGMk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ROR24/btr5YUUuMc3/967JnWEFrsKtK5aW5BeGMk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/ROR24/btr5YUUuMc3/967JnWEFrsKtK5aW5BeGMk/img.gif&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; alt=&quot;Context Switching&quot; loading=&quot;lazy&quot; width=&quot;918&quot; height=&quot;431&quot; data-filename=&quot;GIF 2023-03-27 오전 11-09-34.gif&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;431&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 id=&quot;프로세스-제어-블록process-control-block이란&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;PCB (Process&amp;nbsp;Control&amp;nbsp;Block)&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;PCB(프로세스 제어 블록)는 운영체제에서 프로세스를 관리하기 위해 해당 프로세스의 상태 정보를 담고 있는 자료구조를 말한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;프로세스를 컨텍스트 스위칭 할때 기존 프로세스의 상태를 어딘가에 저장해 둬야 다음에 똑같은 작업을 이어서 할 수 있을 것이고, 새로 해야 할 작업의 상태 또한 알아야 어디서부터 다시 작업을 시작할지 결정할 수 있을 것이다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;즉,&lt;span&gt; PCB는 &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;프로세스 스케줄링을 위해 프로세스에 관한 모든 정보 저장하는 &lt;b&gt;임시 저장소&lt;/b&gt;인 것이다. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;399&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QuhKy/btr5QzD9EJE/63kiqKFs0A4QjwdmVMm6bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QuhKy/btr5QzD9EJE/63kiqKFs0A4QjwdmVMm6bk/img.png&quot; data-alt=&quot;PCB는 프로세스의 생성과 동시에 만들어진다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QuhKy/btr5QzD9EJE/63kiqKFs0A4QjwdmVMm6bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQuhKy%2Fbtr5QzD9EJE%2F63kiqKFs0A4QjwdmVMm6bk%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;856&quot; height=&quot;399&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;399&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;PCB는 프로세스의 생성과 동시에 만들어진다&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;따라서 운영체제는 PCB에 담긴 프로세스 고유 정보를 통해 프로세스를 관리하며, 프로세스의 실행 상태를 파악하고, 우선순위를 조정하며, 스케줄링을 수행하고, 다른 프로세스와의 동기화를 제어한다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;운영체제에 따라 PCB에 포함되는 항목이 다를 수 있지만 일반적으로 PCB내 에는 다음과 같은 정보가 포함되어 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfOW87/btr5zQTBXXl/2APoJmgGVnZ3WyrkU5mgzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfOW87/btr5zQTBXXl/2APoJmgGVnZ3WyrkU5mgzK/img.png&quot; data-alt=&quot;PCB 메모리&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfOW87/btr5zQTBXXl/2APoJmgGVnZ3WyrkU5mgzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfOW87%2Fbtr5zQTBXXl%2F2APoJmgGVnZ3WyrkU5mgzK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;748&quot; height=&quot;280&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;280&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;PCB 메모리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;포인터 (Pointer) : 프로세스의 현재 위치를 저장하는 포인터 정보&lt;/li&gt;
&lt;li&gt;프로세스 상태 (Process state) : 프로세스의 각 상태 - 생성(New), 준비(Ready), 실행(Running), 대기(Waiting), 종료(Terminated) 를 저장&lt;/li&gt;
&lt;li&gt;프로세스 아이디 (Process ID, PID) : 프로세스 식별자를 지정하는 고유한 ID&lt;/li&gt;
&lt;li&gt;프로그램 카운터 (Program counter) :&amp;nbsp; 프로세스를 위해 실행될 다음 명령어의 주소를 포함하는 카운터를 저장&lt;/li&gt;
&lt;li&gt;레지스터 (Register) : 누산기, 베이스, 레지스터 및 범용 레지스터를 포함하는 CPU 레지스터에 있는 정보&lt;/li&gt;
&lt;li&gt;메모리 제한 (Memory Limits) : 운영 체제에서 사용하는 메모리 관리 시스템에 대한 정보&lt;/li&gt;
&lt;li&gt;열린 파일 목록 (List of open file) : 프로세스를 위해 열린 파일 목록&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Context Switching 과정&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 개의 프로세스 간에 컨텍스트 스위칭 과정을 그림으로 표현한 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/euCkUA/btr591esxPm/4SgWyh8utzcIibON7hrqGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/euCkUA/btr591esxPm/4SgWyh8utzcIibON7hrqGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/euCkUA/btr591esxPm/4SgWyh8utzcIibON7hrqGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeuCkUA%2Fbtr591esxPm%2F4SgWyh8utzcIibON7hrqGk%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; alt=&quot;Context Switching&quot; loading=&quot;lazy&quot; width=&quot;960&quot; height=&quot;540&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;CPU는 Process P1을 실행한다 (Executing)&lt;/li&gt;
&lt;li&gt;일정 시간이 지나 Interrupt 또는 system call이 발생한다. (CPU는&amp;nbsp;idle&amp;nbsp;상태)&lt;/li&gt;
&lt;li&gt;현재 실행 중인 Process P1의 상태를 PCB1에 저장한다. (Save state into PCB1)&lt;/li&gt;
&lt;li&gt;다음으로 실행할 Process P2를 선택한다. (CPU 스케줄링)&lt;/li&gt;
&lt;li&gt;Process P2의 상태를 PCB2에서 불러온다. (Reload state from PCB2)&lt;/li&gt;
&lt;li&gt;CPU는 Process P2를 실행한다. (Executing)&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;일정 시간이 지나&lt;span&gt; &amp;nbsp;&lt;/span&gt;&lt;/span&gt;Interrupt 또는 system call이 발생한다. (CPU는&amp;nbsp;idle&amp;nbsp;상태)&lt;/li&gt;
&lt;li&gt;현재 실행 중인 Process P2의 상태를 PCB2에 저장한다. (Save state into PCB2)&lt;/li&gt;
&lt;li&gt;다시 Process P1을 실행할 차례가 된다. (CPU 스케줄링)&lt;/li&gt;
&lt;li&gt;Process P1의 상태를 PCB1에서 불러온다. (Reload state from PCB1)&lt;/li&gt;
&lt;li&gt;CPU는 Process P1을 중간 시점 부터 실행한다. (Executing)&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;idle(대기) 과 executing(실행) 은 CPU의 동작 상태를 나타낸 것이다&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Context Switching Overhead&lt;/b&gt;&lt;/h4&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;위의 컨텍스트 스위칭 과정 그림을 보면 P1이 Execute에서 idle이 될 때 P2가 바로 Execute가 되지 않고 idle을 상태에 조금 있다가 Execute가 되는걸 볼 수 있다. 이 간극이 바로 컨텍스트 스위칭 오버헤드(overhead) 인 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1224&quot; data-origin-height=&quot;484&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQQY2E/btr5UkTCk44/eDerURFIpM9uLqGajfkIoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQQY2E/btr5UkTCk44/eDerURFIpM9uLqGajfkIoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQQY2E/btr5UkTCk44/eDerURFIpM9uLqGajfkIoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQQY2E%2Fbtr5UkTCk44%2FeDerURFIpM9uLqGajfkIoK%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; alt=&quot;Context Switching Overhead&quot; loading=&quot;lazy&quot; width=&quot;1224&quot; height=&quot;484&quot; data-origin-width=&quot;1224&quot; data-origin-height=&quot;484&quot;/&gt;&lt;/span&gt;&lt;/figure&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PCB&amp;nbsp;저장&amp;nbsp;및&amp;nbsp;복원&amp;nbsp;비용&lt;/li&gt;
&lt;li&gt;CPU 캐시 메모리 무효화에 따른 비용&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;컨텍스트 스위칭 과정에서 PCB를 저장하고 복원하는데 비용이 발생하며, 프로세스 자체가 교체되는 것이니 CPU의 캐시 메모리에 저장된 데이터가 무효화가 된다. 이 과정에서는 메모리 접근 시간이 늘어나고, 성능 저하가 발생할 수 있다. 또한 CPU 스케줄링 알고리즘에 따라 프로세스를 선택하는 비용도 만만치 않다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;바로 뒤에서 다루겠지만, 컨텍스트 스위칭은 꼭 프로세스 뿐만 아니라 여러개의 스레드들 끼리도 스위칭이 일어난다. 보통 멀티 스레드라고 하면 여러개의 스레드가 동시에 돌아가니 프로그램 성능이 무조건 상승할거라 예상하지만, 이는 정확하지 않다. 컨텍스트 스위칭 오버헤드라는 변수 때문에 스레드 교체 과정에서 과하게 오버헤드가 발생하면 오히려 멀티 스레드가 싱글 스레드보다 성능이 떨어지는 현상이 나타날수 있기 때문이다.&lt;/p&gt;
&lt;figure id=&quot;og_1686111245897&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;  스레드를 많이 쓸수록 항상 성능이 좋아질까?&quot; data-og-description=&quot;스레드를 많이 쓰면 쓸수록 성능이 높아지는가 프로세스와 스레드를 처음 학습할때, 스레드는 프로세스 내에 존재하는 실행 단위이며, 이 스레드가 여러개인 덕분에 우리가 음악을 들으며 웹서&quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-Is-more-threads-always-better&quot; data-og-url=&quot;https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-Is-more-threads-always-better&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/FfPwk/hySVAMNoFq/mG4Lubd0u7jPqiNTOknFKK/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/cvszpF/hySVMzHE27/3UafAVsk9NRVPIF5aV5QfK/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/h8uX2/hySVCqi9NR/xuZAklitkgpKoWL8L9MdmK/img.png?width=1219&amp;amp;height=626&amp;amp;face=0_0_1219_626&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-Is-more-threads-always-better&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-Is-more-threads-always-better&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/FfPwk/hySVAMNoFq/mG4Lubd0u7jPqiNTOknFKK/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/cvszpF/hySVMzHE27/3UafAVsk9NRVPIF5aV5QfK/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/h8uX2/hySVCqi9NR/xuZAklitkgpKoWL8L9MdmK/img.png?width=1219&amp;amp;height=626&amp;amp;face=0_0_1219_626');&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;inpa.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&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;size16&quot;&gt;프로세스 스케쥴링과 마찬가지로, 스레드 스케줄링(Thread Scheduling)은 운영체제에서 다중 스레드를 관리하며, CPU를 사용할 수 있는 스레드를 선택하고, CPU를 할당하는 작업을 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스레드의 우선순위, 실행 시간, 입출력 요청 등의 정보를 고려하여 CPU를 사용할 수 있는 스레드를 선택하는, 스레드 스케줄링 알고리즘은 프로세스 스케줄링 알고리즘과 유사하게 동작한다. 다양한 알고리즘이 있으며, 대표적으로는 Round Robin, Priority-based scheduling, Multi-level Queue scheduling 등이 있다.&lt;br /&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-origin-width=&quot;744&quot; data-origin-height=&quot;482&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nU5a9/btr7e0lZR4r/fe46kSvYHmeE51PeM3eTt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nU5a9/btr7e0lZR4r/fe46kSvYHmeE51PeM3eTt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nU5a9/btr7e0lZR4r/fe46kSvYHmeE51PeM3eTt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnU5a9%2Fbtr7e0lZR4r%2Ffe46kSvYHmeE51PeM3eTt1%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; alt=&quot;Thread Scheduling&quot; loading=&quot;lazy&quot; width=&quot;744&quot; height=&quot;482&quot; data-origin-width=&quot;744&quot; data-origin-height=&quot;482&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;style3&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;프로세스 상태와 마찬가지로, 스레드에도 상태가 있다. 일반적으로 다음과 같은 4가지 상태를 가진다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.4651%;&quot;&gt;&lt;b&gt;스레드 상태&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 79.4186%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.4651%;&quot;&gt;NEW&lt;/td&gt;
&lt;td style=&quot;width: 79.4186%;&quot;&gt;스레드가 생성되고 아직 호출되지 않은 상태&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.4651%;&quot;&gt;RUNNABLE&lt;/td&gt;
&lt;td style=&quot;width: 79.4186%;&quot;&gt;스레드가 실행되기 위해 기다리는 상태&lt;br /&gt;CPU를 할당받을 수 있는 상태이며, 언제든지 실행될 준비가 되어있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.4651%;&quot;&gt;BLOCKED&lt;/td&gt;
&lt;td style=&quot;width: 79.4186%;&quot;&gt;스레드가 특정 이벤트(입출력 요청 등)가 발생하여 대기하는 상태&lt;br /&gt;CPU를 할당받지 못하며, 이벤트가 발생하여 다시 RUNNABLE 상태로 전환될 때까지 대기한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.4651%;&quot;&gt;TERMINATED&lt;/td&gt;
&lt;td style=&quot;width: 79.4186%;&quot;&gt;스레드가 실행을 완료하고 종료된 상태&lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;더 이상 실행될 수 없으며,&lt;span&gt;&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;스레드 상태 전이&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스레드 상태 전이에 대한 제어는 자바와 같은 스레드 프로그래밍에서 자세히 다루게 될 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;350&quot; data-origin-height=&quot;312&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rPlgW/btr7C2KXJlQ/LhR4z5kQNI5fMRwB8OK0Sk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rPlgW/btr7C2KXJlQ/LhR4z5kQNI5fMRwB8OK0Sk/img.png&quot; data-alt=&quot;실제로는 더 복잡하다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rPlgW/btr7C2KXJlQ/LhR4z5kQNI5fMRwB8OK0Sk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrPlgW%2Fbtr7C2KXJlQ%2FLhR4z5kQNI5fMRwB8OK0Sk%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; alt=&quot;thread-state&quot; loading=&quot;lazy&quot; width=&quot;350&quot; height=&quot;312&quot; data-origin-width=&quot;350&quot; data-origin-height=&quot;312&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실제로는 더 복잡하다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 id=&quot;프로세스-스케줄링-process-scheduling&quot; style=&quot;background-color: #ffffff; color: #383838; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;스레드&lt;span&gt;&amp;nbsp;&lt;/span&gt;컨텍스트&amp;nbsp;스위칭&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스레드 컨텍스트 스위칭(thread context switching)은 멀티 스레딩 환경에서 스레드 간의 실행을 전환하는 기술이다. 프로세스 컨텍스트 스위칭과 다른점은 스레드 컨텍스트 스위칭은 하나의 프로내스 내의 스레드들을 교환한다는 점이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;769&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOlLRI/btr5QnwBsDI/zZYsCGwsP7pFjkuERLxNj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOlLRI/btr5QnwBsDI/zZYsCGwsP7pFjkuERLxNj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOlLRI/btr5QnwBsDI/zZYsCGwsP7pFjkuERLxNj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOlLRI%2Fbtr5QnwBsDI%2FzZYsCGwsP7pFjkuERLxNj1%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; alt=&quot;thread context switching&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;769&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;769&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;&lt;b&gt;TCB (Thread Control Block)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PCB 처럼, TCB(스레드 제어 블록)는 각 스레드마다 운영 체제에서 유지하는 스레드에 대한 정보를 담고 있는 자료구조이다.&amp;nbsp;그림에서 볼 수 있듯이 TCB는 PCB 안에 들어있다. 스레드가 프로세스 내에 위치한 것 처럼 말이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;401&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bycDGM/btr7wf5dA65/zo7iWKQel5sHiPvryvg9ck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bycDGM/btr7wf5dA65/zo7iWKQel5sHiPvryvg9ck/img.png&quot; data-alt=&quot;PCB와 TCB는 LinkedList로 구현되어 있다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bycDGM/btr7wf5dA65/zo7iWKQel5sHiPvryvg9ck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbycDGM%2Fbtr7wf5dA65%2Fzo7iWKQel5sHiPvryvg9ck%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; alt=&quot;Thread Control Block&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;401&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;401&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;PCB와 TCB는 LinkedList로 구현되어 있다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역시 스레드의 상태 정보, 스레드 ID, 스레드 우선순위, 스케줄링 정보 등 다양한 정보를 저장한다. TCB도 스레드가 생성될 때 운영 체제에 의해 생성되며, 스레드가 실행을 마치고 소멸될 때 함께 소멸된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;또한 스레드 간의 자원 공유와 동기화도 TCB를 사용하여 관리된다. 예를 들어, 뮤텍스(mutual exclusion)나 세마포어(semaphore)와 같은 동기화 기법을 사용할 때, TCB에서 해당 스레드의 뮤텍스나 세마포어 정보를 관리하고, 스레드가 해당 자원에 대한 접근 권한을 획득하거나 반납할 때 TCB의 정보를 업데이트하게 된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;뮤텍스(mutex)&lt;/b&gt;&lt;br /&gt;임계 구역에 1개의 스레드만 들어갈 수 있는 동기화 기법.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;세마포어(semaphore)&lt;/b&gt;&lt;br /&gt;임계 구역에 여러 스레드가 들어갈 수 있고, counter를 두어서 허용 가능한 스레드를 제한하는 기법&amp;nbsp;&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;프로세스 컨텍스트 스위칭 &lt;span style=&quot;color: #ef5369;&quot;&gt;vs&lt;/span&gt; 스레드 컨텍스트 스위칭&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot;&gt;프로세스 컨텍스트 스위칭과 스레드 컨텍스트 스위칭은 모두 멀티태스킹 환경에서 여러 프로세스 또는 스레드를 동시에 실행하기 위한 기술이다. 그러나 두 기술은 몇 가지 차이점이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;1.&lt;/span&gt; TCB가 PCB보다 가볍다&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot;&gt;결론부터 말하자면, &lt;span style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot;&gt;스레드 컨텍스트 스위칭이 프로세스 컨텍스트 스위칭보다 더 빠르다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot; 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;&amp;nbsp;&lt;span style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot;&gt;text, data, heap 영역&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;메모리를 공유하기 때문에 TCB에는 stack 및 간단한 register 포인터 정보만을 저장하기 때문에 PCB보다 TCB가 가벼워 더 빨리 읽고 쓸수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;2.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot;&gt;&lt;span&gt; &lt;/span&gt;캐시 메모리 초기화 여부&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot;&gt;CPU 캐쉬 메모리는 CPU와 메인 메모리 사이에 위치하며 CPU에서 한번 이상 읽어들인 메모리의 데이터를 저장하고 있다가, CPU가 다시 그 메모리에 저장된 데이터를 요구할 때, 메인 메모리를 통하지 않고 곧바로 데이터를 전달해 주는 용도이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;347&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bX1zDN/btr7Pj57YmU/dOnxKJqOLBwdYd4SYlsiH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bX1zDN/btr7Pj57YmU/dOnxKJqOLBwdYd4SYlsiH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bX1zDN/btr7Pj57YmU/dOnxKJqOLBwdYd4SYlsiH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbX1zDN%2Fbtr7Pj57YmU%2FdOnxKJqOLBwdYd4SYlsiH0%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; alt=&quot;CPU 캐쉬 메모리&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;347&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;347&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그런데 프로세스 컨텍스트 스위칭이 일어날 경우, 다른 프로세스의 실행으로 인해 CPU가 새로운 명령어와 데이터를 로드해야 하기 때문에 CPU 캐시 메모리를 초기화 하여야 한다. 이것이 프로세스 컨텍스트 스위칭에 부담이 되는 요소이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스레드 컨텍스트 스위칭일 경우, 프로세스 내 스레드 간에 스택과 레지스터 값 등 일부 컨텍스트 정보만 변경되므로 CPU 캐시 메모리는 초기화되지 않는다. 다만 스레드가 다른 CPU 코어에서 실행될 때는 해당 코어의 캐시 메모리에 스레드 컨텍스트 정보가 로드되어야 하므로 초기화될 수 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;3.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;자원 동기화 문제&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;스레드 컨텍스트 스위칭이 발생해 다른 스레드가 heap 영역의 공유 데이터에 접근할때, 이전 스레드가 이미 공유 자원을 사용하고 있는 경우 동기화 문제가 발생할 수 있다. 예를 들어, 두 개의 스레드가 동시에 하나의 변수를 수정하려고 할 때, 스레드 컨텍스트 스위칭이 발생하면 변수의 값을 잘못된 값으로 업데이트 할 수 있는 것이다. 이것을 &lt;span style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot;&gt;스레드 간에 경쟁 조건 (race condition)이라고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot;&gt;프로세스는 기본적으로 독립된 공간이지만, IPC와 같은 공유 자원을 사용하는 경우에 똑같이 경쟁 조건이 발생 할 수가 있다. 예를 들어, 여러 개의 프로세스가 동시에 파일 시스템에 접근하여 파일을 수정하려고 할 때, 컨텍스트 스위칭이 발생할 때 다른 프로세스가 그 파일에 접근할 수 있기 때문에 파일 내용이 손상될 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot;&gt;따라서 이들을 해결하기 위해선 각 상황에 적절한 &lt;span style=&quot;background-color: #ffffff; color: #3e3e3e; text-align: start;&quot;&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;style1&quot; /&gt;
&lt;h2 id=&quot;c0e8&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Multi Process 와 &lt;b&gt;Multi Thread&lt;/b&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀티 프로세스와 멀티 스레드는 &lt;b&gt;한 어플리케이션에 대한 처리방식&lt;/b&gt; 이라고 보면 된다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;이름으로 유추할 수 있듯이 멀티 프로세스와 멀티 스레드는 여러개의 프로세스, 스레드가 동작하는 것을 일 컫는다.&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_img_(54)-transformed.png&quot; data-origin-width=&quot;1422&quot; data-origin-height=&quot;694&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dChKQY/btr5LQscpNy/kqC7CbKsr62WtIFYKTVUE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dChKQY/btr5LQscpNy/kqC7CbKsr62WtIFYKTVUE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dChKQY/btr5LQscpNy/kqC7CbKsr62WtIFYKTVUE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdChKQY%2Fbtr5LQscpNy%2FkqC7CbKsr62WtIFYKTVUE0%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; alt=&quot;Multi-Process-Multi-Thread&quot; loading=&quot;lazy&quot; width=&quot;1422&quot; height=&quot;694&quot; data-filename=&quot;edited_img_(54)-transformed.png&quot; data-origin-width=&quot;1422&quot; data-origin-height=&quot;694&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예상할 수 있듯이, 프로세스와 스레드가 단일이 아닌 다중으로 돌아감으로써 성능 향상 등 여러가지 효과를 얻을 수 있게 된다. 하지만 또한 이로 인해 발생되는 부가적인 문제점도 발생하게 된다. 따라서 각각의 장단점을 파악하여 적재적소에 사용하는 것이 요구된다. 멀티 프로세스와 멀티 스레드 구성의 자세한 장단점에 대한 비교는 아래 포스팅을 참고하길 바란다.&lt;/p&gt;
&lt;figure id=&quot;og_1681459762826&quot; style=&quot;color: #333333; text-align: start;&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot; &amp;zwj;  멀티 프로세스 vs 멀티 스레드 장단점   완벽 정복&quot; data-og-description=&quot;멀티 프로세스와 멀티 스레드는 한 어플리케이션에 대한 처리방식 이라고 보면 된다. 단순히 프로그램을 여러개 띄워놓는 것이 멀티 프로세스가 아니라 이 둘은 언제 어느때에 어떤 방식으로 &quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-multi-process-multi-thread&quot; data-og-url=&quot;https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-multi-process-multi-thread&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ytJfJ/hySaUr6VUd/tENYr2ldiG5Qw0Dt3RILQk/img.jpg?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420,https://scrap.kakaocdn.net/dn/CfnYy/hyR9Ej9lCg/hFTJ2tvy59f0oNH783ZgN1/img.jpg?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420,https://scrap.kakaocdn.net/dn/cqOlko/hySa7rrY22/fjfPgPyu3EF06ESOLAYD30/img.png?width=1392&amp;amp;height=522&amp;amp;face=0_0_1392_522&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-multi-process-multi-thread&quot; data-source-url=&quot;https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-multi-process-multi-thread&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ytJfJ/hySaUr6VUd/tENYr2ldiG5Qw0Dt3RILQk/img.jpg?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420,https://scrap.kakaocdn.net/dn/CfnYy/hyR9Ej9lCg/hFTJ2tvy59f0oNH783ZgN1/img.jpg?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420,https://scrap.kakaocdn.net/dn/cqOlko/hySa7rrY22/fjfPgPyu3EF06ESOLAYD30/img.png?width=1392&amp;amp;height=522&amp;amp;face=0_0_1392_522');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt; &amp;zwj;  멀티 프로세스 vs 멀티 스레드 장단점   완벽 정복&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;멀티 프로세스와 멀티 스레드는 한 어플리케이션에 대한 처리방식 이라고 보면 된다. 단순히 프로그램을 여러개 띄워놓는 것이 멀티 프로세스가 아니라 이 둘은 언제 어느때에 어떤 방식으로&lt;/p&gt;
&lt;p class=&quot;og-host&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;inpa.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://youtu.be/1grtWKqTn50&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://youtu.be/QmtYKZC0lMU&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://www.youtube.com/watch?v=iks_Xb9DtTM&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://zangzangs.tistory.com/109&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://wookkingkim.tistory.com/m/4&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://badcandy.github.io/2019/01/14/concurrency-01/&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://fastcampus.co.kr/media_branding_cs&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://lotuus.tistory.com/92&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://www.crocus.co.kr/1364&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://velog.io/@ssh1997/CS-Process-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://velog.io/@curiosity806/Context-Switching%EC%9C%BC%EB%A1%9C-%EC%95%8C%EC%95%84%EB%B3%B4%EB%8A%94-process%EC%99%80-thread&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://blog.naver.com/PostView.naver?isHttpsRedirect=true&amp;amp;blogId=jhc9639&amp;amp;logNo=221707378026&lt;/p&gt;
&lt;div id=&quot;gtx-trans&quot; style=&quot;position: absolute; left: -39px; top: 20199.4px;&quot;&gt;
&lt;div class=&quot;gtx-trans-icon&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;</description>
      <category>개발 지식/CS 지식</category>
      <category>PCB</category>
      <category>TCB</category>
      <category>동시성</category>
      <category>병렬성</category>
      <category>스레드</category>
      <category>쓰레드</category>
      <category>컨텍스트 스위칭</category>
      <category>프로세스</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/357</guid>
      <comments>https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%E2%9A%94%EF%B8%8F-%EC%93%B0%EB%A0%88%EB%93%9C-%EC%B0%A8%EC%9D%B4#entry357comment</comments>
      <pubDate>Mon, 3 Apr 2023 22:24:35 +0900</pubDate>
    </item>
    <item>
      <title>  console.log는 이제 그만 ❗ - 다양한 콘솔 API 모음</title>
      <link>https://inpa.tistory.com/entry/%F0%9F%93%9A-%EB%8B%A4%EC%96%91%ED%95%9C-%EC%BD%98%EC%86%94-API</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img (5).webp&quot; data-origin-width=&quot;1770&quot; data-origin-height=&quot;980&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIWrWa/btrZ6I6eAGq/5ijtVIm1HR47GESbJRNW51/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIWrWa/btrZ6I6eAGq/5ijtVIm1HR47GESbJRNW51/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIWrWa/btrZ6I6eAGq/5ijtVIm1HR47GESbJRNW51/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIWrWa%2FbtrZ6I6eAGq%2F5ijtVIm1HR47GESbJRNW51%2Fimg.webp&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; alt=&quot;javascript-console-api&quot; loading=&quot;lazy&quot; width=&quot;1770&quot; height=&quot;980&quot; data-filename=&quot;img (5).webp&quot; data-origin-width=&quot;1770&quot; data-origin-height=&quot;980&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;자바스크립트 Console API&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;자바스크립트의 코드의 결과값을 출력하거나 테스트를 하기 위해 &lt;s&gt;console.log()&lt;/s&gt; 함수를 정말 많이 써왔을 것이다. 이때 &lt;s&gt;console.log()&lt;/s&gt; 함수는 console 이라는 객체 안의 &lt;s&gt;log()&lt;/s&gt; 라는 메서드를 불러다가 쓴 것이다. 즉, &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;console&lt;span&gt; 객체 내에는 log 뿐만 아니라 여러 기능을 하는 콘솔 관련 메서드들이 있는 것이다. 이 콘솔 메서드들은 log 보다 훨씬더 강력하고 다채로운 기능을 제공한다. 다만 익숙치 않아서 지금까지 log 만 주궁장창 써왔던 것이다. &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;지금 부터 강력한 자바스크립트 콘솔 API 유용한 기능에 대해 알아보자.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1679455892798&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(&quot;콘솔 로그&quot;) 
console.info(&quot;콘솔 정보&quot;) 
console.debug(&quot;콘솔 디버그&quot;) 
console.warn(&quot;콘솔 경고&quot;) 
console.error(&quot;콘솔 오류&quot;)
console.table(&quot;콘솔 테이블&quot;)
console.group(&quot;콘솔 그룹&quot;)
console.time(&quot;콘솔 타이머&quot;)
console.count(&quot;콘솔 카운드&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;console.log&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;기본적으로 인자로 전달된 값들을 화면으로 출력해준다. 이때 콤마(,)를 통해 출력하고 싶은 여러개의 인자를 집어 넣을 수 있다. 콤마를 통해 출력값을 이을 경우 별다른 개행 문자 없이 자동으로 띄어쓰기 해준다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1631456575509&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const a = 1;
const b = 'hello';
const c = true;

console.log(a); // 하나만 로그
console.log(a, b, c); // 여러 개를 동시에 로그&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;문자열 포맷팅&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;C언어나 Java를 먼저 해본 분이라면 아마 &lt;s&gt;printf()&lt;/s&gt; 라는 메서드를 잘 알 것이다. &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;이 메서드는 첫번째 문자열 내의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;s style=&quot;color: #333333; text-align: left;&quot;&gt;%d&lt;/s&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;s style=&quot;color: #333333; text-align: left;&quot;&gt;%s&lt;/s&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;같은 치환자를 콤마 뒤에 입력한 인자로 매칭하여 변환시킨다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1679456151563&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;System.out.printf(&quot;Hello my name is %s&quot;, &quot;인파&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 136px;&quot;&gt;&lt;b&gt;치환 문자열&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 718px;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 136px;&quot;&gt;%o 또는 %O&lt;/td&gt;
&lt;td style=&quot;width: 718px;&quot;&gt;JavaScript 객체를 출력합니다. 객체 이름을 클릭하면 검사기에 더 자세한 정보를 보여줍니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 136px;&quot;&gt;%d 또는 %i&lt;/td&gt;
&lt;td style=&quot;width: 718px;&quot;&gt;정수를 출력합니다. 서식도 지원합니다. 예를 들어 console.log(&quot;Foo %.2d&quot;, &quot;1.1&quot;)은 정수부를 0이 앞서는 두 자리로 표현하므로 Foo 01 을 출력합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 136px;&quot;&gt;%s&lt;/td&gt;
&lt;td style=&quot;width: 718px;&quot;&gt;문자열을 출력합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 136px;&quot;&gt;%f&lt;/td&gt;
&lt;td style=&quot;width: 718px;&quot;&gt;부동소수점 수를 출력합니다. 서식도 지원합니다. 예를 들어 console.log(&quot;Foo %.2f&quot;, &quot;1.1&quot;)은 소수부를 두 자리로 표현하므로 Foo 1.10 을 출력합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;다만 요즘에는 &lt;a href=&quot;https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-ES6-%ED%85%9C%ED%94%8C%EB%A6%BF-%EB%A6%AC%ED%84%B0%EB%9F%B4-%EC%B5%9C%EC%8B%A0-%EB%AC%B8%EB%B2%95-%EC%A0%95%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;템플릿 리터럴&lt;/a&gt; 문법으로 &lt;s&gt;console.log(`${a}는 숫자 ${b}는 문자열`)&lt;/s&gt; 와 같이 사용하는 것을 선호하는 편이다.&lt;/p&gt;
&lt;pre id=&quot;code_1631456601204&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const a = 1;
const b = 'hello';

// 문자열 포매팅
console.log('%d는 숫자 %s는 문자열', a, b); 

// 템플릿 문자열
console.log('${a}는 숫자 ${b}는 문자열');&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 style=&quot;text-align: left;&quot; 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;html 요소를 스타일링 해주기 위해 css 를 가미하듯이 이와 비슷하게 콘솔 출력물 자체에 css를 적용할 수 가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 본 문자열 포매팅 문법으로 스타일링을 뜻하는 기호는 &lt;s&gt;%c&lt;/s&gt; 를 이용해, 콤마 다음으로 css 문법을 인라인으로 그대로 적어 주면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1631456612904&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(&quot;This is %cMy stylish message&quot;, &quot;color: yellow; font-style: italic; background-color: blue; padding: 2px&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;255&quot; data-origin-height=&quot;32&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bq7Hah/btr5gvgtddd/WY9ccFAFAXX0JjOXaKKcCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bq7Hah/btr5gvgtddd/WY9ccFAFAXX0JjOXaKKcCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bq7Hah/btr5gvgtddd/WY9ccFAFAXX0JjOXaKKcCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbq7Hah%2Fbtr5gvgtddd%2FWY9ccFAFAXX0JjOXaKKcCK%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; alt=&quot;console.log&quot; loading=&quot;lazy&quot; width=&quot;255&quot; height=&quot;32&quot; data-origin-width=&quot;255&quot; data-origin-height=&quot;32&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1679456659138&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(&quot;%cText color is green and increased font size&quot;, &quot;color: green; font-size: 2rem;&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;761&quot; data-origin-height=&quot;120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qGu4v/btr5doQcwcz/Y4QCkTOLqTqg1XyhZssgS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qGu4v/btr5doQcwcz/Y4QCkTOLqTqg1XyhZssgS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qGu4v/btr5doQcwcz/Y4QCkTOLqTqg1XyhZssgS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqGu4v%2Fbtr5doQcwcz%2FY4QCkTOLqTqg1XyhZssgS1%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; alt=&quot;console.log&quot; loading=&quot;lazy&quot; width=&quot;761&quot; height=&quot;120&quot; data-origin-width=&quot;761&quot; data-origin-height=&quot;120&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;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #292929; text-align: start;&quot;&gt;다중 출력 스타일&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #292929; text-align: start;&quot;&gt;&lt;s&gt;%c&lt;/s&gt; 를 여러 번 사용하여 각 문자마다 스타일링을 적용할 수 가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1679456681709&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(&quot;Multiple styles: %cred %corange&quot;, &quot;color: red&quot;, &quot;color: orange&quot;, &quot;Additional unformatted message&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;31&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nkXBD/btr5e7Aow22/KMiD7TIphsT2j39kJ2Li0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nkXBD/btr5e7Aow22/KMiD7TIphsT2j39kJ2Li0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nkXBD/btr5e7Aow22/KMiD7TIphsT2j39kJ2Li0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnkXBD%2Fbtr5e7Aow22%2FKMiD7TIphsT2j39kJ2Li0k%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; alt=&quot;console.log&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;31&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;31&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;&lt;b&gt;출력 스타일 모듈화&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만일 위와 같은 방식이 가독성 측면에 좋지 않으면 다음과 같이 배열로 css 를 지정하는 식으로도 응용이 가능하다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;배열을 선언하고 각 배열 인자에 문자열로 css 문법을 기재한다.&lt;/li&gt;
&lt;li&gt;그리고 이들을 &lt;s&gt;String.join&lt;/s&gt; 시켜 배열을 하나의 문자열로 치환하시킨다.&lt;/li&gt;
&lt;li&gt;이때 인자마다 끝에 &lt;s&gt;;&lt;/s&gt; 기호를 첨가해준다. 인라인으로 css 속성이 여러개 있을 경우 &lt;s&gt;;&lt;/s&gt; 로 구분해 주어야 되기 때문이다.&lt;/li&gt;
&lt;li&gt;그러면 결과로 하나의 문자열로 된 css 문법이 반환되게 되는 식이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1679461660041&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let styles = [
    'background: linear-gradient(#D33106, #571402)', 
    'border: 1px solid #3E0E02',
    'color: white',
    'display: block',
    'text-shadow: 0 1px 0 rgba(0, 0, 0, 0.3)',
    'box-shadow: 0 1px 0 rgba(255, 255, 255, 0.4) inset, 0 5px 3px -5px rgba(0, 0, 0, 0.5), 0 -13px 5px -10px rgba(255, 255, 255, 0.4) inset',
    'line-height: 40px',
    'text-align: center',
    'font-weight: bold'
].join(';'); // 각 배열 인자들을 join 시켜 하나의 문자열로 치환하고 인자마다 끝에 ; 기호를 첨가해준다

console.log('%c a spicy log message ?', styles);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;756&quot; data-origin-height=&quot;254&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sukj6/btr5fOH1ndy/ZKaZzlKGaZuYIkVslRWe0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sukj6/btr5fOH1ndy/ZKaZzlKGaZuYIkVslRWe0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sukj6/btr5fOH1ndy/ZKaZzlKGaZuYIkVslRWe0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fsukj6%2Fbtr5fOH1ndy%2FZKaZzlKGaZuYIkVslRWe0K%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; alt=&quot;출력 스타일 모듈화&quot; loading=&quot;lazy&quot; width=&quot;756&quot; height=&quot;254&quot; data-origin-width=&quot;756&quot; data-origin-height=&quot;254&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;style3&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;/p&gt;
&lt;pre id=&quot;code_1679456951937&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const name = 'fatfish'
const age = 24
const job = 'Front end development engineer'
const hobbies = 'read, write article'

console.log(name, age, job, hobbies)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 이렇게 무지성으로 나열할 경우, 출력할 인자가 많아질 수록 몇번째의 값이 어떤 인자값인지 혼동스러울때가 있다. 이때는 &lt;a href=&quot;https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EB%B9%84%EA%B5%AC%EC%A1%B0%ED%99%94%EA%B5%AC%EC%A1%B0%EB%B6%84%ED%95%B4-%ED%95%A0%EB%8B%B9&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;구조 분해&lt;/a&gt; 문법으로 그대로 콘솔 로그 인자에 넣어주면 콘솔 화면에 객체 형식으로 포매팅하여 출력해주게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1679457085767&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log({ name, age, job, hobbies })&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;91&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tykPx/btr5flrHKjN/fWOYR2BAwWPondqEaNZ541/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tykPx/btr5flrHKjN/fWOYR2BAwWPondqEaNZ541/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tykPx/btr5flrHKjN/fWOYR2BAwWPondqEaNZ541/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtykPx%2Fbtr5flrHKjN%2FfWOYR2BAwWPondqEaNZ541%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; alt=&quot;console.log&quot; loading=&quot;lazy&quot; width=&quot;680&quot; height=&quot;91&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;91&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;콘솔 로그의 함정 &amp;amp; 주의점&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;브라우저의 &lt;s&gt;console.log&lt;/s&gt; 를 사용할때 주의해야할 점이 있다. 예를들어 다음 예제가 콘솔에 어떻게 표시될지 예상해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1679462642030&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;var obj = {};

console.log(obj);

obj.a = 1;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;콘솔이 출력되는 라인은 아직 &lt;s&gt;obj&lt;/s&gt; 에 값을 넣기 전이니 당연히 결과값이 &lt;s&gt;{}&lt;/s&gt; 빈값이 출력이 될 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;188&quot; data-origin-height=&quot;90&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/I2Blc/btr5gwNUrYc/J6pe2tyAlVRTELLuTCRkWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/I2Blc/btr5gwNUrYc/J6pe2tyAlVRTELLuTCRkWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/I2Blc/btr5gwNUrYc/J6pe2tyAlVRTELLuTCRkWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FI2Blc%2Fbtr5gwNUrYc%2FJ6pe2tyAlVRTELLuTCRkWK%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; alt=&quot;console.log&quot; loading=&quot;lazy&quot; width=&quot;188&quot; height=&quot;90&quot; data-origin-width=&quot;188&quot; data-origin-height=&quot;90&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;그런데 아래 화살표를 눌러보면, 아래와 같이 값이 a: 1 이 들어가 있음을 볼 수 있다. 즉, 결과값이 무언가 이상하게 짬뽕이 되어버린 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;277&quot; data-origin-height=&quot;126&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3np1l/btr5guJnvjH/W93WnKUXg0iVEjRacWVQ7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3np1l/btr5guJnvjH/W93WnKUXg0iVEjRacWVQ7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3np1l/btr5guJnvjH/W93WnKUXg0iVEjRacWVQ7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3np1l%2Fbtr5guJnvjH%2FW93WnKUXg0iVEjRacWVQ7K%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; alt=&quot;console.log&quot; loading=&quot;lazy&quot; width=&quot;277&quot; height=&quot;126&quot; data-origin-width=&quot;277&quot; data-origin-height=&quot;126&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이러한 현상이 일어나는 이유는, &lt;s&gt;console.log&lt;/s&gt;는 참조를 로깅하기 때문에 객체와 같이 내용물이 변할 수 있는 것들은 내용이 &lt;span style=&quot;color: #ee2323;&quot;&gt;실시간으로 바뀌기 때문&lt;/span&gt;이다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;객체 뿐만 아니라 배열도 마찬가지이다.&lt;/p&gt;
&lt;pre id=&quot;code_1679462642034&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;var arr = [];

console.log(arr, arr.length);

// 복잡한 코드가 있다고 가정

setTimeout(function() { // 비동기 가정
  arr.push(5);
}, 0);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;146&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clskx7/btr5pzbtaDF/qzrXfYmrjXhUK7paMkg6UK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clskx7/btr5pzbtaDF/qzrXfYmrjXhUK7paMkg6UK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clskx7/btr5pzbtaDF/qzrXfYmrjXhUK7paMkg6UK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fclskx7%2Fbtr5pzbtaDF%2FqzrXfYmrjXhUK7paMkg6UK%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; alt=&quot;console.log&quot; loading=&quot;lazy&quot; width=&quot;363&quot; height=&quot;146&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;146&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기를 통해 배열값 push를 후처리 하였기 때문에 당장 콘솔 결과값은 빈배열에 배열 length는 0이지만, 화살표를 눌러 안의 내용을 살펴보면 첫번째 인덱스 값이 들어가 있고 length 역시 1로 찍혀져 있는 걸 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;434&quot; data-origin-height=&quot;195&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bglqZc/btr5gwf8Xse/0kLItMe3KPnEqVi44cguT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bglqZc/btr5gwf8Xse/0kLItMe3KPnEqVi44cguT1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bglqZc/btr5gwf8Xse/0kLItMe3KPnEqVi44cguT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbglqZc%2Fbtr5gwf8Xse%2F0kLItMe3KPnEqVi44cguT1%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; alt=&quot;console.log&quot; loading=&quot;lazy&quot; width=&quot;434&quot; height=&quot;195&quot; data-origin-width=&quot;434&quot; data-origin-height=&quot;195&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;style3&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;직접 스타일링 해주는 것도 좋지만 아래 미리 제공하는 콘솔 API 들도 이용하면 유용하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;s&gt;console.log&lt;/s&gt; : 아이콘이 없는 검은색 텍스트&lt;/li&gt;
&lt;li&gt;&lt;s&gt;console.info&lt;/s&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&gt;&lt;/span&gt;진한 텍스트&lt;/li&gt;
&lt;li&gt;&lt;s&gt;console.debug&lt;/s&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&gt;&lt;/span&gt;파란색 텍스트&lt;/li&gt;
&lt;li&gt;&lt;s&gt;console.warn&lt;/s&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;아이콘이 있는 노란색 텍스트&lt;/li&gt;
&lt;li&gt;&lt;s&gt;console.error&lt;/s&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;아이콘이 있는 빨간색 텍스트&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;단, 브라우저 종류마다 출력되는 스타일은 약간 다르다는 점은 유의하자&lt;/blockquote&gt;
&lt;pre id=&quot;code_1679461293487&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var playerOne = 120;
var playerTwo = 130;
var playerThree = 140;
var playerFour = 150;
var playerFive = 160;

console.log(&quot;Console.log&quot; + &quot; &quot; +  playerOne);
console.debug(&quot;Console.debug&quot; + &quot; &quot; +playerTwo);
console.info(&quot;Console.info&quot; + &quot; &quot; + playerFour);
console.warn(&quot;Console.warn&quot; + &quot; &quot; + playerThree);
console.error(&quot;Console.error&quot; + &quot; &quot; + playerFive);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;586&quot; data-origin-height=&quot;156&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCRDBp/btr5ep2FyXD/LF5RFaGSXA7Kss9uVA07dk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCRDBp/btr5ep2FyXD/LF5RFaGSXA7Kss9uVA07dk/img.png&quot; data-alt=&quot;크롬 콘솔 출력 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCRDBp/btr5ep2FyXD/LF5RFaGSXA7Kss9uVA07dk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCRDBp%2Fbtr5ep2FyXD%2FLF5RFaGSXA7Kss9uVA07dk%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; alt=&quot;console.warn&quot; loading=&quot;lazy&quot; width=&quot;586&quot; height=&quot;156&quot; data-origin-width=&quot;586&quot; data-origin-height=&quot;156&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;이때 &lt;s&gt;console.error&lt;/s&gt; 와 &lt;s&gt;console.warn&lt;/s&gt; 은 단순한 값 출력 뿐만 아니라 문제가 발생하는 코드 라인 스택을 표시한다는 점에서 기능상 차이를 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 catch 문과 같이 오류 메세지를 콘솔 화면에 출력하기 위해 에러 내용을 습관적으로 &lt;s&gt;console.log&lt;/s&gt; 로 처리하는 사람들이 있는데, 그릇된 방법은 아니지만 이보다 &lt;s&gt;console.error&lt;/s&gt; 메서드를 사용하는 것이 훨씬 현명하다. 왜냐하면 &lt;s&gt;console.warn&lt;/s&gt;과 같이 붉은색 배경에 기호 출력 뿐만 아니라 누적된 &lt;span style=&quot;color: #ee2323;&quot;&gt;함수 호출 스택을 콘솔 화면에 표시&lt;/span&gt;해 주기 때문이다.&lt;/p&gt;
&lt;pre id=&quot;code_1679457643288&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const a = () =&amp;gt; {
  console.error(&quot;error&quot;);
}
const b = () =&amp;gt; {
  a()
}
const c = () =&amp;gt; {
  b()
}

c()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;532&quot; data-origin-height=&quot;272&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/F5Z81/btr5cEe59Uf/aCjmROhuhs8KDaHtkS14Y0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/F5Z81/btr5cEe59Uf/aCjmROhuhs8KDaHtkS14Y0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/F5Z81/btr5cEe59Uf/aCjmROhuhs8KDaHtkS14Y0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF5Z81%2Fbtr5cEe59Uf%2FaCjmROhuhs8KDaHtkS14Y0%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; alt=&quot;console.error&quot; loading=&quot;lazy&quot; width=&quot;532&quot; height=&quot;272&quot; data-origin-width=&quot;532&quot; data-origin-height=&quot;272&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;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;console.assert&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;assert는 error와 같이 동작하지만 첫번째 인수가 false인 경우에만 메시지 및 스택 추적을 콘솔에 기록한다. 첫 번째 인수가 true이면 아무 작업도 수행하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그냥 if문 과 &lt;s&gt;console.error&lt;/s&gt; 문을 좀더 코드 한 줄로 심플하게 처리하기 위해 있는 것으로 보면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1679459862225&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let name = &quot;inpa&quot;;
let msg = 2;

console.assert(typeof msg === 'number', { name, msg: msg }); // 출력 안함

msg = &quot;2&quot;;

console.assert(typeof msg === 'number', { name, msg: msg }); // 출력&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;479&quot; data-origin-height=&quot;149&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CWefx/btr5d36utEJ/PhO2yuHzlVmWCPUKpRLiW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CWefx/btr5d36utEJ/PhO2yuHzlVmWCPUKpRLiW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CWefx/btr5d36utEJ/PhO2yuHzlVmWCPUKpRLiW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCWefx%2Fbtr5d36utEJ%2FPhO2yuHzlVmWCPUKpRLiW1%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; alt=&quot;console.assert&quot; loading=&quot;lazy&quot; width=&quot;479&quot; height=&quot;149&quot; data-origin-width=&quot;479&quot; data-origin-height=&quot;149&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;style1&quot; /&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;console.group&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만일 콘솔 화면에 출력할 값들이 많을때, 이들을 그룹화 하여 무엇에 대한 출력값인지 구분하기 위해 가독성을 높이기 위한 메서드이다. &lt;s&gt;console.group&lt;/s&gt; 은 중첩해서 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1631456634638&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.group(&quot;쇼핑몰 로그&quot;);
console.log(&quot;로그인 됐는지 우선 확인&quot;);

console.group(&quot;회원 관련 작업&quot;);
console.log(&quot;아이디 확인&quot;);
console.log(&quot;비번 확인&quot;);
console.groupEnd();

console.group(&quot;상품 관련 작업&quot;);
console.log(&quot;가격 변동이 있는가?&quot;);
console.log(&quot;재고는 남아있는가?&quot;);
console.groupEnd();

console.log(&quot;마무리 작업&quot;);
console.groupEnd();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;418&quot; data-origin-height=&quot;235&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dJyCZn/btr5jbpd3dz/wij8R3im0LbT4vS8gOOkj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dJyCZn/btr5jbpd3dz/wij8R3im0LbT4vS8gOOkj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dJyCZn/btr5jbpd3dz/wij8R3im0LbT4vS8gOOkj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdJyCZn%2Fbtr5jbpd3dz%2Fwij8R3im0LbT4vS8gOOkj0%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; alt=&quot;console.group&quot; loading=&quot;lazy&quot; width=&quot;418&quot; height=&quot;235&quot; data-origin-width=&quot;418&quot; data-origin-height=&quot;235&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;443&quot; data-origin-height=&quot;191&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mVczr/btr5guCvHa1/Idv7Qf8clxed7ukO1ilFUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mVczr/btr5guCvHa1/Idv7Qf8clxed7ukO1ilFUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mVczr/btr5guCvHa1/Idv7Qf8clxed7ukO1ilFUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmVczr%2Fbtr5guCvHa1%2FIdv7Qf8clxed7ukO1ilFUk%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; alt=&quot;console.group&quot; loading=&quot;lazy&quot; width=&quot;443&quot; height=&quot;191&quot; data-origin-width=&quot;443&quot; data-origin-height=&quot;191&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;s&gt;console.group&lt;/s&gt; 안에 그룹할 내용을 작성하고 끝에 &lt;s&gt;console.groupEnd&lt;/s&gt; 로 그룹을 묶어 하나의 집합을 형성한다고 보면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;그룹 열기 / 닫기&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;만일 처음부터 그룹이 닫혀져 있게 하고 싶다면&lt;/span&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;console.groupCollpased&lt;/s&gt; 를 사용 하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1679462294022&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.group(&quot;쇼핑몰 로그&quot;); // 그룹 열기
console.log(&quot;로그인 됐는지 우선 확인&quot;);

console.groupCollapsed(&quot;회원 관련 작업&quot;); // 그룹 닫기
console.log(&quot;아이디 확인&quot;);
console.log(&quot;비번 확인&quot;);
console.groupEnd();

console.groupCollapsed(&quot;상품 관련 작업&quot;); // 그룹 닫기
console.log(&quot;가격 변동이 있는가?&quot;);
console.log(&quot;재고는 남아있는가?&quot;);
console.groupEnd();

console.log(&quot;마무리 작업&quot;);
console.groupEnd();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;518&quot; data-origin-height=&quot;333&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nPgso/btr5gwf2jvG/cOhsN2e2Ba5tPKaoKDQVMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nPgso/btr5gwf2jvG/cOhsN2e2Ba5tPKaoKDQVMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nPgso/btr5gwf2jvG/cOhsN2e2Ba5tPKaoKDQVMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnPgso%2Fbtr5gwf2jvG%2FcOhsN2e2Ba5tPKaoKDQVMk%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; alt=&quot;console.group&quot; loading=&quot;lazy&quot; width=&quot;518&quot; height=&quot;333&quot; data-origin-width=&quot;518&quot; data-origin-height=&quot;333&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr class=&quot;se-hr&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;b&gt;console.&lt;/b&gt;time&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;자바스크립트의 코드 수행 시간을 확인할때 유용한 콘솔 메서드 이다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;보통은 &lt;s&gt;Date.now()&lt;/s&gt; 를 통해 현재 시간을 ms 세컨드로 구하고 코드 수행이 끝난 시간을 구해 이의 차이값으로 구할수도 있지만 이런 번거로운 방법보다 콘솔 API를 통해 간단하고 정확히 수행 시간을 구할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1631456920614&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// time과 timeEnd에 같은 타이머 이름을 주어야 정상적으로 작동한다.
let count = 0

console.time('time1')

for (let i = 0; i &amp;lt; 1000000000; i++) {
  count++
}

console.timeEnd('time1') // time1: 1382.543212890625 ms&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1679457888138&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.time('time2')

for (let i = 0; i &amp;lt; 1000000000; i++) {
  count++
}

console.timeEnd('time2') // time2: 6366.410888671875 ms&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;382&quot; data-origin-height=&quot;289&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HA52F/btr5dSDBTsd/zt2I0kfohhozWV0K8Ad3yK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HA52F/btr5dSDBTsd/zt2I0kfohhozWV0K8Ad3yK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HA52F/btr5dSDBTsd/zt2I0kfohhozWV0K8Ad3yK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHA52F%2Fbtr5dSDBTsd%2Fzt2I0kfohhozWV0K8Ad3yK%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; alt=&quot;console.time&quot; loading=&quot;lazy&quot; width=&quot;382&quot; height=&quot;289&quot; data-origin-width=&quot;382&quot; data-origin-height=&quot;289&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;style1&quot; /&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;b&gt;&lt;b&gt;console.&lt;/b&gt;&lt;/b&gt;table&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;만일 다음과 같이 반복되고 규칙적인 key와 value가 있는 객체가 들어있는 배열일 경우, 마치 &lt;b&gt;엑셀과 같이 테이블 형식&lt;/b&gt;으로 가독성 좋게 출력이 가능하다. 최신 웹브라우저에서는 모두 지원되는 기능으로 콘솔을 이용해 배열 데이터 내용을 파악할 때 편리하다.&lt;/p&gt;
&lt;pre id=&quot;code_1679458182006&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const foods = [
  {
    name: ' ',
    price: 30.89,
    group: 1,
  },
  {
    name: ' ',
    price: 20.71,
    group: 1,
  },
  {
    name: ' ',
    price: 10.31,
    group: 2,
  },
  {
    name: ' ',
    price: 5.98,
    group: 2,
  },
]

console.table(foods)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;108&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LOUx1/btr5guB0KDv/YnMxHXzJS2ybOGLwuGbPrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LOUx1/btr5guB0KDv/YnMxHXzJS2ybOGLwuGbPrk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LOUx1/btr5guB0KDv/YnMxHXzJS2ybOGLwuGbPrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLOUx1%2Fbtr5guB0KDv%2FYnMxHXzJS2ybOGLwuGbPrk%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; alt=&quot;console.table&quot; loading=&quot;lazy&quot; width=&quot;718&quot; height=&quot;108&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;108&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;배열 객체 뿐만 아니라 다차원 배열 역시 테이블 형태로 출력이 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1679458345077&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const dolls = [ ['라이언', 5], ['어피치', 3], ['콘', 2], ['무지', 3], ['프로도', 3] ];
console.table(dolls)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;713&quot; data-origin-height=&quot;127&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y7WmR/btr5jbPKEVD/XDYNyNDrdOFXvu7EXtkBH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y7WmR/btr5jbPKEVD/XDYNyNDrdOFXvu7EXtkBH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y7WmR/btr5jbPKEVD/XDYNyNDrdOFXvu7EXtkBH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy7WmR%2Fbtr5jbPKEVD%2FXDYNyNDrdOFXvu7EXtkBH0%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; alt=&quot;console.table&quot; loading=&quot;lazy&quot; width=&quot;713&quot; height=&quot;127&quot; data-origin-width=&quot;713&quot; data-origin-height=&quot;127&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr class=&quot;se-hr&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;console.&lt;/b&gt;&lt;b&gt;dir&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;예를들어 DOM의 body 부분을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;console.log&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;로 그대로 출력해보면 html 태그가 출력되지만,&lt;/p&gt;
&lt;pre id=&quot;code_1679462999823&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(document.body)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;188&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwdErf/btr5eoWMiME/nRgjEb8yjeaVSdCw88em6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwdErf/btr5eoWMiME/nRgjEb8yjeaVSdCw88em6k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwdErf/btr5eoWMiME/nRgjEb8yjeaVSdCw88em6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwdErf%2Fbtr5eoWMiME%2FnRgjEb8yjeaVSdCw88em6k%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; alt=&quot;console.dir&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;188&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;188&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;console.dir로 출력하면 DOM 객체(Object)로 콘솔에 출력됨을 확인 할 수가 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1679462999824&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.dir(document.body)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;433&quot; data-origin-height=&quot;393&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zIdPF/btr5e6u3344/ljpnRfTe0hfeqV2aMNwEk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zIdPF/btr5e6u3344/ljpnRfTe0hfeqV2aMNwEk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zIdPF/btr5e6u3344/ljpnRfTe0hfeqV2aMNwEk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzIdPF%2Fbtr5e6u3344%2FljpnRfTe0hfeqV2aMNwEk0%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; alt=&quot;console.dir&quot; loading=&quot;lazy&quot; width=&quot;433&quot; height=&quot;393&quot; data-origin-width=&quot;433&quot; data-origin-height=&quot;393&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;style1&quot; /&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;b&gt;console.trace&lt;/b&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출된 함수 스택을 자동으로 추적해준다. 함수가 여기 저기 모듈 파일에서 호출될 경우 이를 추적할때 유리하다.&lt;/p&gt;
&lt;pre id=&quot;code_1631456948694&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function foo() {
  function bar() {
    console.trace(); // 스택 추적
  }
  bar();
}

foo();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;377&quot; data-origin-height=&quot;209&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dnk1cz/btr5nGBTPik/ovNDSob0Ce1wwYAXKZkgqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dnk1cz/btr5nGBTPik/ovNDSob0Ce1wwYAXKZkgqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dnk1cz/btr5nGBTPik/ovNDSob0Ce1wwYAXKZkgqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdnk1cz%2Fbtr5nGBTPik%2FovNDSob0Ce1wwYAXKZkgqk%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; alt=&quot;console.trace&quot; loading=&quot;lazy&quot; width=&quot;377&quot; height=&quot;209&quot; data-origin-width=&quot;377&quot; data-origin-height=&quot;209&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;style1&quot; /&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;b&gt;console.&lt;/b&gt;count&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;특정 호출된 값이 몇 번 호출 되었나 횟수를 기록하고 싶을 때 사용한다고 보면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1679458632681&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.count('카운터1'); // 카운터1: 1
console.count('카운터1'); // 카운터1: 2

console.count('카운터2'); // 카운터2: 1
console.count('카운터2'); // 카운터2: 2

console.count('카운터1'); // 카운터1: 3&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;b&gt;console.&lt;/b&gt;clear&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콘솔 화면이 지저분할 경우 clear 해 줄 수 있다. 직접 치기 귀찮으면 아래 사진의 왼쪽 상단의 버튼을 눌러주면 자동으로 메서드를 호출하여 청소시킨다.&lt;/p&gt;
&lt;pre id=&quot;code_1679459984399&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.clear()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;374&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3f9Ov/btr5jbvFfFh/txeE4ZKm88RcWfrF7fiyn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3f9Ov/btr5jbvFfFh/txeE4ZKm88RcWfrF7fiyn1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3f9Ov/btr5jbvFfFh/txeE4ZKm88RcWfrF7fiyn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3f9Ov%2Fbtr5jbvFfFh%2FtxeE4ZKm88RcWfrF7fiyn1%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; alt=&quot;console.clear&quot; loading=&quot;lazy&quot; width=&quot;798&quot; height=&quot;374&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;374&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr class=&quot;se-hr&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;https://javascript.plainenglish.io/its-2022-please-don-t-just-use-console-log-anymore-217638337c7d&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;https://www.zerocho.com/category/JavaScript/post/5b2b45cf1350f9001b662ba6&lt;/p&gt;</description>
      <category>Language/JavaScript</category>
      <category>console api</category>
      <category>console method</category>
      <category>javascript console</category>
      <category>js console</category>
      <category>자바스크립트 콘솔</category>
      <category>콘솔 api</category>
      <category>콘솔 메서드</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1101</guid>
      <comments>https://inpa.tistory.com/entry/%F0%9F%93%9A-%EB%8B%A4%EC%96%91%ED%95%9C-%EC%BD%98%EC%86%94-API#entry1101comment</comments>
      <pubDate>Mon, 3 Apr 2023 10:26:26 +0900</pubDate>
    </item>
    <item>
      <title>  모바일 / 태블릿 에서만 CSS 파일 로드 최적화</title>
      <link>https://inpa.tistory.com/entry/%F0%9F%9A%80-%EB%AA%A8%EB%B0%94%EC%9D%BC-%ED%83%9C%EB%B8%94%EB%A6%BF-CSS-%EB%A1%9C%EB%93%9C</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;630&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkRWSD/btr2zlUKtbx/qieg2tOaCkkNhCV7MoVIw1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkRWSD/btr2zlUKtbx/qieg2tOaCkkNhCV7MoVIw1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkRWSD/btr2zlUKtbx/qieg2tOaCkkNhCV7MoVIw1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkRWSD%2Fbtr2zlUKtbx%2Fqieg2tOaCkkNhCV7MoVIw1%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; alt=&quot;css-load-mobile&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;630&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;630&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;CSS 파일 로드 최적화&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 &lt;s&gt;&amp;lt;link&amp;gt;&lt;/s&gt; 태그의 css 파일 로드는 브라우저의 렌더링을 차단(blocking) 하기 때문에, css 파일이 크면 클수록 그만큼 웹페이지 속도에 영향을 미치게 된다. 따라서 왠만해선 css 파일의 용량을 최적화하여 줄이는 것이 좋으며 소스를 분리할 수 있는 것은 따로 분리하여 비동기 로드 시키는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어 모바일 혹은 태블릿에서만 실행되는 미디어쿼리 선택자 부분은 메인 css 파일에 한꺼번에 때려넣기에는 약간 무리가 있다. 이때 모바일 소스를 다른 css 파일로 분리하고, 사용자의 화면 비율에 따라 &lt;span style=&quot;color: #ee2323;&quot;&gt;선택적으로 css 파일을 로드&lt;/span&gt;하도록 하는 방법이 있는데, 이는 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1678155894860&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- 모바일(480px 이하에서만 css 파일 로드) --&amp;gt;
&amp;lt;link
  rel=&quot;stylesheet&quot;
  href=&quot;mobile.css&quot;
  media=&quot;screen and (max-width: 480px)&quot; 
/&amp;gt;
  
&amp;lt;!-- 태블릿(1080px 이하에서만 css 파일 로드) --&amp;gt;  
&amp;lt;link
  rel=&quot;stylesheet&quot;
  href=&quot;tablet.css&quot;
  media=&quot;screen and (max-width: 1080px)&quot; 
/&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;css 파일을 분할하여 메인 css의 크기가 작아지는 효과도 있고 별도로 모바일, 태블릿 화면의 css 소스들을 관리할 수 있다는 일거양득의 효과를 얻을 수 있을 것이다.&lt;/p&gt;</description>
      <category>Snippet/CSS 스니펫</category>
      <category>CSS 최적화</category>
      <category>모바일 css</category>
      <category>웹 최적화</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1098</guid>
      <comments>https://inpa.tistory.com/entry/%F0%9F%9A%80-%EB%AA%A8%EB%B0%94%EC%9D%BC-%ED%83%9C%EB%B8%94%EB%A6%BF-CSS-%EB%A1%9C%EB%93%9C#entry1098comment</comments>
      <pubDate>Sun, 2 Apr 2023 13:31:55 +0900</pubDate>
    </item>
    <item>
      <title>  웹에서 음악 객체(Audio) 다루기</title>
      <link>https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EC%9D%8C%EC%95%85-%EA%B0%9D%EC%B2%B4Audio-%EB%8B%A4%EB%A3%A8%EA%B8%B0</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;codepen-qBbPaLY.webp&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;682&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQ24Ek/btr5907jrpW/MoorWX3fGHaQT1ckJJWiEk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQ24Ek/btr5907jrpW/MoorWX3fGHaQT1ckJJWiEk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQ24Ek/btr5907jrpW/MoorWX3fGHaQT1ckJJWiEk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQ24Ek%2Fbtr5907jrpW%2FMoorWX3fGHaQT1ckJJWiEk%2Fimg.webp&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; alt=&quot;음악-객체&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;682&quot; data-filename=&quot;codepen-qBbPaLY.webp&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;682&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;HTML Audio 태그&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML &lt;s&gt;&amp;lt;audio&amp;gt;&lt;/s&gt; 태그는 웹 페이지에서 오디오를 재생하기 위해 사용되는 요소이다. MP3, WAV, OGG 등 다양한 오디오 포맷 파일을 웹에서 재생할 수 있다.&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;audio 태그 속성&lt;/b&gt;&lt;/h3&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;style9&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;속성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;값&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;설정 내용&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;src&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;파일 경로&lt;/td&gt;
&lt;td&gt;재생할 음원 파일의 경로 설정(mp3, wav, ogg 파일 등)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;autoplay&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;autoplay&lt;/td&gt;
&lt;td&gt;자동 재생 여부 설정(브라우저에 html파일이 로드되자마자 음악파일이 재생됨)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&amp;nbsp;loop&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;loop&lt;/td&gt;
&lt;td&gt;반복 재생 여부 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;controls&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;controls&lt;/td&gt;
&lt;td&gt;컨트롤 패널(재생/정지 버튼 등) 노출 여부 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;muted&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;muted&lt;/td&gt;
&lt;td&gt;음소거 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1680342112863&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- src 속성을 통해 오디오 파일의 경로를 지정 --&amp;gt;
&amp;lt;!-- controls 속성을 추가하면 오디오 재생 컨트롤러가 표시 --&amp;gt;
&amp;lt;!-- autoplay 속성을 추가하면 페이지가 로드될 때 자동으로 오디오가 재생 --&amp;gt;
&amp;lt;!-- loop 속성을 추가하면 오디오가 끝나면 자동으로 반복 재생 --&amp;gt;
&amp;lt;audio src=&quot;sound.mp3&quot; autoplay loop controls id=&quot;myAudio&quot;&amp;gt;&amp;lt;/audio&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2021-06-19 오전 10.03.33.png&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;142&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/B8qwe/btrgINqB35G/XAkUllj5aS2KwZlL39v4sK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/B8qwe/btrgINqB35G/XAkUllj5aS2KwZlL39v4sK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/B8qwe/btrgINqB35G/XAkUllj5aS2KwZlL39v4sK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FB8qwe%2FbtrgINqB35G%2FXAkUllj5aS2KwZlL39v4sK%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; alt=&quot;HTML Audio 태그&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;142&quot; data-filename=&quot;스크린샷 2021-06-19 오전 10.03.33.png&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;142&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;&lt;b&gt;오디오 파일 포맷 지정&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;&amp;lt;audio&amp;gt;&lt;/s&gt; 태그 내부에 &lt;s&gt;&amp;lt;source&amp;gt;&lt;/s&gt; 태그를 통해 다양한 타입의 오디오 포맷을 입력해주면, 브라우저가 알아서 지원하는 포맷으로 재생한다. 그리고 type 속성에는 해당 오디오 파일의 MIME 타입을 지정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 여러 개의 &lt;s&gt;&amp;lt;source&amp;gt;&lt;/s&gt; 태그를 사용하면 브라우저에서 지원하는 오디오 파일 타입을 우선적으로 재생하게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1633341518026&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;audio autoplay loop controls&amp;gt;
  &amp;lt;source src=&quot;sound.mp3&quot; type=&quot;audio/mpeg&quot;&amp;gt;
  &amp;lt;source src=&quot;sound.ogg&quot; type=&quot;audio/ogg&quot;&amp;gt;
  오디오가 지원되지 않는 브라우저입니다. &amp;lt;!-- &amp;lt;audio&amp;gt; 요소 내에 위치하는 텍스트는 사용자의 브라우저가 &amp;lt;audio&amp;gt; 요소를 지원하지 않을 경우 화면에 표시 --&amp;gt;
&amp;lt;/audio&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;브라우저&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;지원 포맷&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Internet Explorer&lt;/td&gt;
&lt;td&gt;MP3, WAV&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chrome&lt;/td&gt;
&lt;td&gt;MP3, WAV, Ogg Vorbis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Firefox&lt;/td&gt;
&lt;td&gt;MP3, WAV, Ogg Vorbis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Safari&lt;/td&gt;
&lt;td&gt;MP3, WAV, AAC&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Javascript로&amp;nbsp;&lt;b&gt;Audio 다루기&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;자바스크립트 상에서 Audio 객체를 생성하거나, 혹은 HTML 상에 &lt;s&gt;&amp;lt;audio&amp;gt;&lt;/s&gt; 태그를 선언해둔 상태라면, 엘리먼트 취득 방식으로 audio 객체를 확보할 수도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1633341679989&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const audio = new Audio('audio.mp3'); // `audio.mp3` 파일을 로딩
audio.play(); // 오디오 재생&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1633341800612&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- HTML audio 태그 --&amp;gt;
&amp;lt;audio src=&quot;sound.mp3&quot; id=&quot;myAudio&quot;&amp;gt;&amp;lt;/audio&amp;gt;

&amp;lt;script&amp;gt;
    const myAudio = document.getElementById(&quot;myAudio&quot;) // Audio객체 취득
    myAudio.play(); // 음원 재생
    myAudio.pause(); // 일시 정지
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한&amp;nbsp; Audio 객체는 다양한 메서드와 이벤트를 제공하므로, 다양한 오디오 제어 기능을 구현할 수 있다.&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 639px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;639&quot; data-default-tab=&quot;js,result&quot; data-slug-hash=&quot;doWVjv&quot; data-user=&quot;ansonkao&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/ansonkao/pen/doWVjv&quot;&gt; MP3 Playback via HTML5 Audio Elements&lt;/a&gt; by Anson Kao (&lt;a href=&quot;https://codepen.io/ansonkao&quot;&gt;@ansonkao&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Audio 객체&lt;/b&gt;&lt;/h3&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;Audio 객체 프로퍼티&lt;/b&gt;&lt;/h4&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;style9&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;프로퍼티&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;설정 내용&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;src&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;음원파일 경로 : &quot;경로/파일명.확장자&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;volume&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;볼륨 : 0~1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;loop&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;반복 여부: true | false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;autoplay&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;자동재생 여부: true | false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;muted&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;음소거 여부: true | false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;paused&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;일시정지 여부: true | false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;ended&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;재생완료 여부: true | false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;duration&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;음원의 전체 길이(초 단위)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;currentTime&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;음원의 현재 재생 위치(초 단위)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1680345206586&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const audio = new Audio('audio.mp3');

// audio가 load 될 때 자동재생 됨
audio.autoplay = true;

// audio의 재생시점을 5초로 설정함
audio.currentTime = 5;

// audio의 길이를 초(seconds) 단위로 반환
audio.duration;

// audio를 반복 재생함
audio.loop = true;

// audio 음소거
audio.muted = true;

// audio의 경로를 지정함(URL)
audio.src = &quot;my_audio.mp3&quot;;

// audio의 음량을 0.2로 지정함
// 음량은 0.0 ~ 1.0 사이 값으로 지정할 수 있고, 1.0이 가장 큰 음량
audio.volume = 0.2;&lt;/code&gt;&lt;/pre&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;Audio 객체 메서드&lt;/b&gt;&lt;/h4&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;style9&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;메서드&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;작동 내용&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;play()&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;재생&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;pause()&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;일시정지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;addTextTrack()&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;새로운 트랙을 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;canPlayType()&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;브라우저가 해당 오디오 타입을 재생할 수 있는지 체크&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;load()&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;오디오 객체를 리로드&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1680344519804&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const audio = new Audio('audio.mp3');

audio2.loop = true; // 반복재생하지 않음
audio2.volume = 0.5; // 음량 설정

audio2.play(); // sound2.mp3 재생

setTimeout(function () { // 5초 후 sound2.mp3 일시정지
	audio2.pause();
}, 5000);

// audio를 다시 load함. 주로 audio의 src나 설정이 바뀌었을 경우에 사용
audio.src = &quot;sound2.mp3&quot;;
audio.load();&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1633341908071&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* 오디오 처음부터 재생 */
audio.currentTime = 0; // 재생 위치를 처음으로 설정
audio.play(); // 처음부터 다시 재생됨&lt;/code&gt;&lt;/pre&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;Audio 객체 이벤트&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오디오가 재생되거나 일시정지될 때 발생하는 이벤트 등을 처리할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style9&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;이벤트&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;상황&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;canplaythrough&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;음원 파일이 모드 로드되어 재생 가능할 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;play&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;재생이 시작될 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;playing&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;재생 중일 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;puase&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;일시 정지되었을 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;ended&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;재생이 완료되었을 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;volumechange&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;볼륨이 변경될 때&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1680344491656&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 오디오 재생 시
audio.addEventListener('play', function() {
  console.log('오디오가 재생되었습니다.');
});

// 오디오 일시정지 시
audio.addEventListener('pause', function() {
  console.log('오디오가 일시정지되었습니다.');
});&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어 아래와 같이 자바스크립트로 오디오 객체를 로드하고 자동으로 &lt;s&gt;play()&lt;/s&gt; 메서드를 통해 음원을 재생하도록 설정하였지만 브라우저 상에서 이를 차단한다는 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1680344116650&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const audio = new Audio('audio.mp3');
audio.play();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 사용자가 &lt;span style=&quot;color: #ee2323;&quot;&gt;버튼 클릭과 같은 작업을 통해&lt;/span&gt; &lt;s&gt;play()&lt;/s&gt; 메서드를 호출해야만 음원을 재생할 수 있다는 것이다. 따라서 아래와 같이 코드를 구성해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1680344542686&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;button id=&quot;play-button&quot;&amp;gt;음악 재생&amp;lt;/button&amp;gt;

&amp;lt;script&amp;gt;
    const audio = new Audio('audio.mp3');
    const playButton = document.getElementById('play-button');

    playButton.addEventListener('click', function() {
      audio.play();
    });
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에는, Chrome 브라우저에서 자바스크립트를 이용하여 음원을 자동으로 재생하는 것이 가능했지만, 이는 악성코드의 유포나 사용자의 개인정보를 탈취하는 등의 보안 위협이 될 수 있었기 때문이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Audio Player 라이브러리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만일 웹페이지에 멋진 mp3 기능을 추가하고 싶다면 미리 만들어진 자바스크립트 라이브러리를 사용하는 것도 방법이다. 자체적으로 CSS 스타일링이 되어 있어서 느낌 좋은 미디어 플레이어를 자신의 홈페이지에 추가할 수 있다.&lt;/p&gt;
&lt;figure id=&quot;og_1680346801529&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;Calamansi.js - HTML5 &amp;amp; JS Audio Player by Voerro | Voerro Tutorials&quot; data-og-description=&quot;Live demo of Calamansi.js - a HTML5 &amp;amp; JS Audio Player by Voerro&quot; data-og-host=&quot;www.voerro.com&quot; data-og-source-url=&quot;http://www.voerro.com/en/projects/calamansi-js&quot; data-og-url=&quot;http://www.voerro.com/en/projects/calamansi-js&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;http://www.voerro.com/en/projects/calamansi-js&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;http://www.voerro.com/en/projects/calamansi-js&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Calamansi.js - HTML5 &amp;amp; JS Audio Player by Voerro | Voerro Tutorials&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Live demo of Calamansi.js - a HTML5 &amp;amp; JS Audio Player by Voerro&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.voerro.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1043&quot; data-origin-height=&quot;616&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cp7QRj/btr7iLaFoQI/3gMe62lNZtQKUUJqr1e6Ik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cp7QRj/btr7iLaFoQI/3gMe62lNZtQKUUJqr1e6Ik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cp7QRj/btr7iLaFoQI/3gMe62lNZtQKUUJqr1e6Ik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcp7QRj%2Fbtr7iLaFoQI%2F3gMe62lNZtQKUUJqr1e6Ik%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; alt=&quot;Audio Player&quot; loading=&quot;lazy&quot; width=&quot;1043&quot; height=&quot;616&quot; data-origin-width=&quot;1043&quot; data-origin-height=&quot;616&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;style3&quot; /&gt;
&lt;figure id=&quot;og_1680346916835&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;OnAirCode&quot; data-og-description=&quot;We&amp;rsquo;ll provide resources, examples, tutorials related to web design, graphics, mobile app development for designer and developer with ideas.&quot; data-og-host=&quot;onaircode.com&quot; data-og-source-url=&quot;https://onaircode.com/javascript-js-mp3-music-audio-player-examples/%EF%BB%BF&quot; data-og-url=&quot;https://onaircode.com/javascript-js-mp3-music-audio-player-examples/%EF%BB%BF&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cfV6Y3/hyR6MvLuTQ/KJ519T7AvCq8K7Gv6R0kU1/img.jpg?width=1280&amp;amp;height=853&amp;amp;face=0_0_1280_853,https://scrap.kakaocdn.net/dn/SY7ui/hyR7uAgmFF/LZAEqYJXDgzyxuGz3HO44k/img.jpg?width=1080&amp;amp;height=675&amp;amp;face=0_0_1080_675,https://scrap.kakaocdn.net/dn/bIYuBA/hyR6Ng5RUs/BvJFZW0sLuuM5tQoPeGhk0/img.jpg?width=1080&amp;amp;height=675&amp;amp;face=0_0_1080_675&quot;&gt;&lt;a href=&quot;https://onaircode.com/javascript-js-mp3-music-audio-player-examples/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://onaircode.com/javascript-js-mp3-music-audio-player-examples/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cfV6Y3/hyR6MvLuTQ/KJ519T7AvCq8K7Gv6R0kU1/img.jpg?width=1280&amp;amp;height=853&amp;amp;face=0_0_1280_853,https://scrap.kakaocdn.net/dn/SY7ui/hyR7uAgmFF/LZAEqYJXDgzyxuGz3HO44k/img.jpg?width=1080&amp;amp;height=675&amp;amp;face=0_0_1080_675,https://scrap.kakaocdn.net/dn/bIYuBA/hyR6Ng5RUs/BvJFZW0sLuuM5tQoPeGhk0/img.jpg?width=1080&amp;amp;height=675&amp;amp;face=0_0_1080_675');&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;OnAirCode&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;We&amp;rsquo;ll provide resources, examples, tutorials related to web design, graphics, mobile app development for designer and developer with ideas.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;onaircode.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1680346837545&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;35+ JavaScript Music Players - Free Code + Demos&quot; data-og-description=&quot;1. Music Player App Author: Rafaela Lucas (rafaelavlucas) Created on: December 26, 2019 Made with: HTML, SCSS, JS Tags: music-player, music stream, player songs, daily ui, daily ui challenge 2. HTML Audio Player Author: Zakari Abdessamad (vanderzak) Create&quot; data-og-host=&quot;devsnap.me&quot; data-og-source-url=&quot;https://devsnap.me/javascript-music-players&quot; data-og-url=&quot;https://devsnap.me/javascript-music-players&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dLopCv/hyR7GOfGgU/GkCwgpNVoTFXCRRI0KcBPK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://devsnap.me/javascript-music-players&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://devsnap.me/javascript-music-players&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dLopCv/hyR7GOfGgU/GkCwgpNVoTFXCRRI0KcBPK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;35+ JavaScript Music Players - Free Code + Demos&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;1. Music Player App Author: Rafaela Lucas (rafaelavlucas) Created on: December 26, 2019 Made with: HTML, SCSS, JS Tags: music-player, music stream, player songs, daily ui, daily ui challenge 2. HTML Audio Player Author: Zakari Abdessamad (vanderzak) Create&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;devsnap.me&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1680346873033&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;26 HTML Audio Player For Websites In Codepen&quot; data-og-description=&quot;If you have a music related website and want to improve the design or add more functions capabilities for your music player to attract as&amp;hellip;&quot; data-og-host=&quot;niemvuilaptrinh.medium.com&quot; data-og-source-url=&quot;https://niemvuilaptrinh.medium.com/26-html-audio-player-for-websites-in-codepen-7f0e3ca4bc1b&quot; data-og-url=&quot;https://niemvuilaptrinh.medium.com/26-html-audio-player-for-websites-in-codepen-7f0e3ca4bc1b&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/2tsnw/hyR6KEGCnc/PgMlyJbA8l24TSJfKmPG9k/img.png?width=1200&amp;amp;height=467&amp;amp;face=0_0_1200_467&quot;&gt;&lt;a href=&quot;https://niemvuilaptrinh.medium.com/26-html-audio-player-for-websites-in-codepen-7f0e3ca4bc1b&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://niemvuilaptrinh.medium.com/26-html-audio-player-for-websites-in-codepen-7f0e3ca4bc1b&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/2tsnw/hyR6KEGCnc/PgMlyJbA8l24TSJfKmPG9k/img.png?width=1200&amp;amp;height=467&amp;amp;face=0_0_1200_467');&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;26 HTML Audio Player For Websites In Codepen&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;If you have a music related website and want to improve the design or add more functions capabilities for your music player to attract as&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;niemvuilaptrinh.medium.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Web&amp;nbsp;Audio&amp;nbsp;API&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 HTML에서 audio 객체를 이용하면, 오디오를 재생/정지하고 볼륨을 조정하거나 이벤트 처리를 할 수 있다. 그러나 딱 이 정도의 기능이 끝이다. 그 이상은 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Web Audio API는 웹 브라우저에서 오디오 처리와 재생을 위한 강력한 신생 API 이다. 오디오 데이터를 샘플링하고, 필터링하고, 조작하여 오디오 음원 자체를 컨트롤하고 분석할 수 있는 방법을 제공해주어 일반 Audio 객체보다 더 높은 수준의 오디오 제어가 가능하다는 점 등이 있다. 예를들어 음원에 다양한 이펙트 효과를 줄 수 있으며, 음원의 주파수 데이터 취득을 통한 Pitch(높낮이), Gain(볼륨) 등의 계산도 가능하고 마이크를 통해 스트림으로 들어오는 소리에 대한 분석도 가능하다.&lt;/p&gt;
&lt;figure id=&quot;og_1680347385705&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 Audio API - Web API | MDN&quot; data-og-description=&quot;Web Audio API는 웹에서 오디오를 제어하기 위한 강력하고 다양한 기능을 제공합니다. Web Audio API를 이용하면 오디오 소스를 선택할 수 있도록 하거나, 오디오에 이펙트를 추가하거나, 오디오를 시&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/API/Web_Audio_API&quot; data-og-url=&quot;https://developer.mozilla.org/ko/docs/Web/API/Web_Audio_API&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bm2sxh/hyR7CdYM51/AjKbh8BDaysUPEJEx8jcb1/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/bM1mov/hyR7AN1xch/UgfjUNp3Ofk5KbZ2TjrNQ0/img.png?width=1200&amp;amp;height=400&amp;amp;face=0_0_1200_400&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/Web_Audio_API&quot; data-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/API/Web_Audio_API&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bm2sxh/hyR7CdYM51/AjKbh8BDaysUPEJEx8jcb1/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/bM1mov/hyR7AN1xch/UgfjUNp3Ofk5KbZ2TjrNQ0/img.png?width=1200&amp;amp;height=400&amp;amp;face=0_0_1200_400');&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 Audio API - Web API | MDN&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Web Audio API는 웹에서 오디오를 제어하기 위한 강력하고 다양한 기능을 제공합니다. Web Audio API를 이용하면 오디오 소스를 선택할 수 있도록 하거나, 오디오에 이펙트를 추가하거나, 오디오를 시&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;pre id=&quot;code_1680347416291&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;audio src=&quot;audio.mp3&quot;&amp;gt;&amp;lt;/audio&amp;gt;
&amp;lt;label for=&quot;playback-rate&quot;&amp;gt;Playback Rate&amp;lt;/label&amp;gt;
&amp;lt;input type=&quot;range&quot; id=&quot;playback-rate&quot; min=&quot;0.5&quot; max=&quot;2&quot; step=&quot;0.1&quot; value=&quot;1&quot;&amp;gt;

&amp;lt;script&amp;gt;
    // 오디오 파일 로드
    const audioContext = new AudioContext(); // 오디오 컨텍스트를 생성
    const audioElement = document.querySelector('audio'); // HTML audio 요소 가져오기
    const audioSource = audioContext.createMediaElementSource(audioElement); // 오디오 소스를 생성

    // 재생 속도 변경을 위한 슬라이더 생성
    const playbackRateSlider = document.querySelector('#playback-rate');
    playbackRateSlider.addEventListener('input', () =&amp;gt; {
      // 사용자가 슬라이더를 조작하면 playbackRate 속성(오디오 재생 속도)가 변경
      audioElement.playbackRate = playbackRateSlider.value;
    });

    // 오디오 재생
    audioSource.connect(audioContext.destination); // 오디오 소스를 오디오 컨텍스트의 출력에 연결
    audioElement.play();
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 Web Audio API는 강력한 만큼 일반 Audio 객체보다 복잡해 높은 학습률을 요구한다. 따라서 단순한 오디오 재생 기능만을 필요로 하는 경우에는 일반 Audio 객체를 사용하는 것이 적합할 수 있고, 다양한 사운드 작업 및 분석이 필요하다면 Web Audio API를 사용하는 것이 좋다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;http://www.tcpschool.com/html-tags/audio&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://stackoverflow.com/questions/9419263/how-to-play-audio&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://curryyou.tistory.com/337&lt;/p&gt;</description>
      <category>Language/JavaScript (WEB)</category>
      <category>audio tag</category>
      <category>audio 태그</category>
      <category>소리 태그</category>
      <category>오디오 태그</category>
      <category>음악 태그</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/232</guid>
      <comments>https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EC%9D%8C%EC%95%85-%EA%B0%9D%EC%B2%B4Audio-%EB%8B%A4%EB%A3%A8%EA%B8%B0#entry232comment</comments>
      <pubDate>Sat, 1 Apr 2023 20:18:43 +0900</pubDate>
    </item>
    <item>
      <title>☕ 람다식 리팩토링 하기 (Comparator 축약 원리)</title>
      <link>https://inpa.tistory.com/entry/%E2%98%95-%EB%9E%8C%EB%8B%A4%EC%8B%9D-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-Comparator</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1_S5KkDX11wbKa6suvBPBFJA.webp&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;875&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k1081/btrWR7IdtjO/gWsWj5KTpbkBEFvjIkaJR0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k1081/btrWR7IdtjO/gWsWj5KTpbkBEFvjIkaJR0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k1081/btrWR7IdtjO/gWsWj5KTpbkBEFvjIkaJR0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk1081%2FbtrWR7IdtjO%2FgWsWj5KTpbkBEFvjIkaJR0%2Fimg.webp&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; alt=&quot;lambda-refactoring&quot; loading=&quot;lazy&quot; width=&quot;1400&quot; height=&quot;875&quot; data-filename=&quot;1_S5KkDX11wbKa6suvBPBFJA.webp&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;875&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Comparator 람다식 리팩토링 해보기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 실제로 자바 프로그래밍에서 배열을 정렬(sort) 할때 사용되는 Comparator 인터페이스 사용 예제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apple 클래스가 있고 생성자 인자로 사과의 무게(weight)값을 받는다. 그리고 실행부에서 배열로 사과 객체를 담고, 사과 무게에 따라 배열 요소들을 정렬하려고 한다. 이를 코드로 구현하면 아래와 같이 구현할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1674202545483&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Apple {
    private final int weight; // 사과 무게

    public Apple(int weight) {
        this.weight = weight;
    }

    public int getWeight() {
        return weight;
    }

    @Override
    public String toString() {
        return &quot;Apple{&quot; + &quot;weight=&quot; + weight + '}';
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1674270043113&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Main {
    public static void main(String[] args) {
        Apple[] inventory = new Apple[] {
                new Apple(34),
                new Apple(12),
                new Apple(76),
                new Apple(91),
                new Apple(55)
        };

        Arrays.sort(inventory, new Comparator&amp;lt;Apple&amp;gt;() {
            @Override
            public int compare(Apple o1, Apple o2) {
                return Integer.compare(o1.getWeight(), o2.getWeight());
            }
        });
        
        System.out.println(Arrays.toString(inventory));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;33&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEXkrl/btrWQbxmwAO/hdWoOj84C64Nvskr0XzNfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEXkrl/btrWQbxmwAO/hdWoOj84C64Nvskr0XzNfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEXkrl/btrWQbxmwAO/hdWoOj84C64Nvskr0XzNfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEXkrl%2FbtrWQbxmwAO%2FhdWoOj84C64Nvskr0XzNfk%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; alt=&quot;lambda-refactoring&quot; loading=&quot;lazy&quot; width=&quot;738&quot; height=&quot;33&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;33&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;하지만 인텔리제이와 같은 IDE를 쓴다면, Comparator 익명 구현 클래스를 &lt;a href=&quot;https://inpa.tistory.com/entry/%E2%98%95-Lambda-Expression&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;람다표현식&lt;/a&gt;으로 리팩토링 하라고 조언을 해줄 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;757&quot; data-origin-height=&quot;321&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bM6SL3/btrWSwVmvpS/g3bt0gLc9l4kcM21akiRrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bM6SL3/btrWSwVmvpS/g3bt0gLc9l4kcM21akiRrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bM6SL3/btrWSwVmvpS/g3bt0gLc9l4kcM21akiRrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbM6SL3%2FbtrWSwVmvpS%2Fg3bt0gLc9l4kcM21akiRrK%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; alt=&quot;lambda-refactoring&quot; loading=&quot;lazy&quot; width=&quot;757&quot; height=&quot;321&quot; data-origin-width=&quot;757&quot; data-origin-height=&quot;321&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 람다식으로 변환했더니 이번엔 또 변환하라고 IDE에서 조언해준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;797&quot; data-origin-height=&quot;224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cICawI/btrWRjBugzS/mUkYxjdDnK7KAPHLkAvjH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cICawI/btrWRjBugzS/mUkYxjdDnK7KAPHLkAvjH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cICawI/btrWRjBugzS/mUkYxjdDnK7KAPHLkAvjH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcICawI%2FbtrWRjBugzS%2FmUkYxjdDnK7KAPHLkAvjH0%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; alt=&quot;lambda-refactoring&quot; loading=&quot;lazy&quot; width=&quot;797&quot; height=&quot;224&quot; data-origin-width=&quot;797&quot; data-origin-height=&quot;224&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변환해보니 확실히 기존 람다식 보단 코드 줄이 확연히 줄었음을 볼 수 있다. 그런데 갑자기 쌩뚱맞게 Comparator의 정적 메소드를 호출하고 그 안에는 람다의 메서드 참조 문법을 써 놓았다. 대체 어떤 원리로 람다표현식을 이렇게 축약을 할 수 있는지 가늠이 안간다.&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Comparator의 축약 원리 파헤치기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 해당 람다식은 &lt;a href=&quot;https://inpa.tistory.com/entry/JAVA8-%E2%98%95-%EB%9E%8C%EB%8B%A4%EC%8B%9D%EC%9D%84-%EB%8D%94-%EC%A7%A7%EA%B2%8C-%EB%A9%94%EC%86%8C%EB%93%9C-%EC%B0%B8%EC%A1%B0Method-Reference&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;메소드 참조&lt;/a&gt;로 문법적으로 생략이 불가능 하다.&lt;/p&gt;
&lt;pre id=&quot;code_1674271095740&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Arrays.sort(inventory, (o1, o2) -&amp;gt; Integer.compare(o1.getWeight(), o2.getWeight()));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;만일&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;s&gt;(a1, a2) -&amp;gt; Integer.compare(a1, a2)&lt;/s&gt; 처럼 되어있으면 &lt;s&gt;Integer::compare &lt;/s&gt;&lt;/span&gt;로 생략할 수 있었겠지만, 매개변수가 Integer.compare&amp;nbsp;메서드의 인자로 그대로 들어간게 아니라 별도로 메서드를 호출하고 있기 때문이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;781&quot; data-origin-height=&quot;126&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HCHey/btrWQd4qQvR/sZhTOSgeavBFx0OTYkz5pk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HCHey/btrWQd4qQvR/sZhTOSgeavBFx0OTYkz5pk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HCHey/btrWQd4qQvR/sZhTOSgeavBFx0OTYkz5pk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHCHey%2FbtrWQd4qQvR%2FsZhTOSgeavBFx0OTYkz5pk%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; alt=&quot;람다 메소드 참조 고급 응용&quot; loading=&quot;lazy&quot; width=&quot;781&quot; height=&quot;126&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;781&quot; data-origin-height=&quot;126&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;u&gt;&lt;b&gt;람다 함수 자체를 메서드가 반환&lt;/b&gt;&lt;/u&gt;하도록 만들면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;람다 함수식은 &lt;a href=&quot;https://inpa.tistory.com/entry/CS-%F0%9F%91%A8%E2%80%8D%F0%9F%92%BB-%EC%9D%BC%EA%B8%89-%EA%B0%9D%EC%B2%B4first-class-object&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;일급 객체&lt;/a&gt;로 취급된다. 일급 객체의 특징으로는 변수에 함수를 할당할수 있고 함수를 리턴값으로도 활용할 수 있다. 대표적으로 자바스크립트의 함수가 변수에 넣거나 자체 함수를 반환하는 기법을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 이러한 특성을 이용해 별도의 정적 메서드를 만들고, 그 메서드가 특정 람다 함수식을 리턴하도록 해주면, 길다란 람다식을 단순히 메서드 호출 코드로 줄일수 있는 것이다. 그래서 우선 간단하게 프로토타입으로 람다식을 반환하는 정적 메서드를 작성해보면 다음과 같이 되겠다.&lt;/p&gt;
&lt;pre id=&quot;code_1674204429781&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Main {

    public static Comparator&amp;lt;Apple&amp;gt; comparingInt() {
        // 람다식을 Comparator 인터페이스 타입으로 반환
        return (a1, a2) -&amp;gt; Integer.compare(a1.getWeight(), a2.getWeight());
    }

    public static void main(String[] args) {
        Apple[] inventory = new Apple[]{
                new Apple(34),
                new Apple(12),
                new Apple(76),
                new Apple(91),
                new Apple(55)
        };

        Arrays.sort(inventory, comparingInt()); // 단순한 메서드 호출로 생략함 (가독성 &amp;uarr;)
        
        System.out.println(Arrays.toString(inventory));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현한 메서드를 좀 더 사용성 좋게 바꿔보자. 지금은 Apple 객체의 weight 값만 다루니까 저렇게 작성해도 되지만, 나중에 Banana 객체의 prize 값으로 정렬할수도 있는 기능을 확장할 수 도 있다. 따라서 외부에 의해 바뀔수 있는 부분인 &lt;s&gt;a.getWeight()&lt;/s&gt; 부분을 매개변수화 하여 처리하도록 하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 &lt;s&gt;a.getWeight()&lt;/s&gt; 는 단순히 데이터가 아닌 함수를 호출하는 코드 로직이다. 이를 어떻게 매개변수화 하면 되는 것일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 자바 람다에 대해 배우고 있다. 파라미터로 들어올 값은 반드시 정수나 문자열과 같은 고정된 데이터만 들어오라는 법은 없다. 즉, &lt;s&gt;a.getWeight()&lt;/s&gt; 를 리턴하는 람다식으로 매개변수화 하여 함수형 인터페이스로 받으면 되는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;569&quot; data-origin-height=&quot;136&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PJwff/btrWPJhkXOV/c5uXAEffxwgZkkZQZcLNG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PJwff/btrWPJhkXOV/c5uXAEffxwgZkkZQZcLNG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PJwff/btrWPJhkXOV/c5uXAEffxwgZkkZQZcLNG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPJwff%2FbtrWPJhkXOV%2Fc5uXAEffxwgZkkZQZcLNG1%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; alt=&quot;람다 메소드 참조 고급 응용&quot; loading=&quot;lazy&quot; width=&quot;569&quot; height=&quot;136&quot; data-origin-width=&quot;569&quot; data-origin-height=&quot;136&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 어떠한 함수형 인터페이스 타입을 매개변수의 타입으로 정해야 할지 고민해야 되는데, Apple 타입의 객체를 파라미터로 받아 int 값인 무게를 반환하기 때문에 여기에 적절한 &lt;a href=&quot;https://inpa.tistory.com/entry/%E2%98%95-%ED%95%A8%EC%88%98%ED%98%95-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-API&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;함수형 인터페이스&lt;/a&gt;는 &lt;s&gt;ToIntFunction&amp;lt;Apple&amp;gt;&lt;/s&gt; 이다.&lt;/p&gt;
&lt;pre id=&quot;code_1674205516230&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static Comparator&amp;lt;Apple&amp;gt; comparingInt(ToIntFunction&amp;lt;Apple&amp;gt; keyExtractor) {
    // 매개변수로 람다식을 받아서 추상 메서드 실행 : keyExtractor.applyAsInt(a1) == a1.getWeight()
    return (a1, a2) -&amp;gt; Integer.compare(keyExtractor.applyAsInt(a1), keyExtractor.applyAsInt(a2));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;813&quot; data-origin-height=&quot;265&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dej6l5/btrWQYeoYBL/oyoXKGXfATucNgNDLVeQC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dej6l5/btrWQYeoYBL/oyoXKGXfATucNgNDLVeQC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dej6l5/btrWQYeoYBL/oyoXKGXfATucNgNDLVeQC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdej6l5%2FbtrWQYeoYBL%2FoyoXKGXfATucNgNDLVeQC0%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; alt=&quot;람다 메소드 참조 고급 응용&quot; loading=&quot;lazy&quot; width=&quot;813&quot; height=&quot;265&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;813&quot; data-origin-height=&quot;265&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 다른 제네릭 타입도 받을수 있게 타입 파라미터를 &lt;span style=&quot;color: #006dd7;&quot;&gt;T&lt;/span&gt; 기호로 바꿔주면 완성이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &lt;s&gt;(a) -&amp;gt; a.getWeight()&lt;/s&gt; 람다식은 메서드 참조로 &lt;s&gt;Apple::getWeight&lt;/s&gt; 로 축약이 가능하니 최종 완성본은 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1674205917771&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Main {
    public static &amp;lt;T&amp;gt; Comparator&amp;lt;T&amp;gt; comparingInt(ToIntFunction&amp;lt;? super T&amp;gt; keyExtractor) {
        return (a1, a2) -&amp;gt; Integer.compare(keyExtractor.applyAsInt(a1), keyExtractor.applyAsInt(a2));
    }

    public static void main(String[] args) {
        Apple[] inventory = new Apple[]{
                new Apple(34),
                new Apple(12),
                new Apple(76),
                new Apple(91),
                new Apple(55)
        };

        Arrays.sort(inventory, comparingInt(Apple::getWeight)); // (a) -&amp;gt; a.getWeight()
        
        System.out.println(Arrays.toString(inventory));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;실제 Comparator와 비교하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 자바에 정의되어 있는 Compator 인터페이스의 &lt;s&gt;comparingInt()&lt;/s&gt; 메서드 시그니처를 보면 지금까지 우리가 구현한 것과 별반 차이가 없는 것을 볼 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1674206118115&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Arrays.sort(inventory, Comparator.comparingInt(Apple::getWeight));&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;833&quot; data-origin-height=&quot;353&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbyaf7/btrWQK1FRJu/Za6sokrFVxILmmqeeDNrIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbyaf7/btrWQK1FRJu/Za6sokrFVxILmmqeeDNrIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbyaf7/btrWQK1FRJu/Za6sokrFVxILmmqeeDNrIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbbyaf7%2FbtrWQK1FRJu%2FZa6sokrFVxILmmqeeDNrIK%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; alt=&quot;람다 메소드 참조 고급 응용&quot; loading=&quot;lazy&quot; width=&quot;833&quot; height=&quot;353&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;833&quot; data-origin-height=&quot;353&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;</description>
      <category>Language/Java</category>
      <category>Comparable</category>
      <category>Comparator</category>
      <category>compare</category>
      <category>LAMBDA</category>
      <category>람다</category>
      <category>리팩토링</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1070</guid>
      <comments>https://inpa.tistory.com/entry/%E2%98%95-%EB%9E%8C%EB%8B%A4%EC%8B%9D-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-Comparator#entry1070comment</comments>
      <pubDate>Fri, 31 Mar 2023 09:28:16 +0900</pubDate>
    </item>
    <item>
      <title> &amp;zwj;  프로세스 / 데몬 / 서비스 차이 한방 정리</title>
      <link>https://inpa.tistory.com/entry/%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EB%8D%B0%EB%AA%AC-%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%A0%95%EB%A6%AC</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;how-to-create-a-daemon-featured-img.webp&quot; data-origin-width=&quot;1680&quot; data-origin-height=&quot;840&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dHutqq/btr6K2kYzEW/nx9kHswa5CE8cElIzMsK60/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dHutqq/btr6K2kYzEW/nx9kHswa5CE8cElIzMsK60/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dHutqq/btr6K2kYzEW/nx9kHswa5CE8cElIzMsK60/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdHutqq%2Fbtr6K2kYzEW%2Fnx9kHswa5CE8cElIzMsK60%2Fimg.webp&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; alt=&quot;프로세스-데몬-서비스&quot; loading=&quot;lazy&quot; width=&quot;1680&quot; height=&quot;840&quot; data-filename=&quot;how-to-create-a-daemon-featured-img.webp&quot; data-origin-width=&quot;1680&quot; data-origin-height=&quot;840&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;프로세스 (Process)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스는 메모리에 적재되어 CPU에 의해 실행되는 컴퓨터 프로그램을 지칭한다. 각 프로세스는 고유한 프로세스 ID(PID)를 가지며, 운영체제는 프로세스 간의 자원 공유를 관리한다. 예를 들어, 웹 브라우저를 실행하는 경우 브라우저라는 프로그램은 하나의 프로세스로 실행되는 것이다. 그리고 프로세스는 눈에 보이는 프로세스(foreground process) 와 눈에 보이지 않은 프로세스(background process) 로 구분된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;583&quot; data-origin-height=&quot;412&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nztHu/btr6RYAYuoE/ibxP8TKpyEUsDpZOsRtLD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nztHu/btr6RYAYuoE/ibxP8TKpyEUsDpZOsRtLD0/img.png&quot; data-alt=&quot;작업관리자 창을 보면 백그라운드 프로세스 카테고리가 있다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nztHu/btr6RYAYuoE/ibxP8TKpyEUsDpZOsRtLD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnztHu%2Fbtr6RYAYuoE%2FibxP8TKpyEUsDpZOsRtLD0%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; alt=&quot;프로세스-데몬-서비스-차이정리&quot; loading=&quot;lazy&quot; width=&quot;583&quot; height=&quot;412&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;583&quot; data-origin-height=&quot;412&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;작업관리자 창을 보면 백그라운드 프로세스 카테고리가 있다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;b&gt;포그라운드 프로세스&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;&lt;b&gt;백그라운드 프로세스&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;사용자와&amp;nbsp;상호작용하는&amp;nbsp;프로세스&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;프로세스는&amp;nbsp;사용자와&amp;nbsp;상호작용하지&amp;nbsp;않고&amp;nbsp;시스템에서&amp;nbsp;실행되는&amp;nbsp;프로세스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;최소화를 하더라도 눈에 보이는 프로세스를 포그라운드 프로세스&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;눈에 보이지 않지만 뒤에서 일을 하고 있는 프로세스를 백그라운드 프로세스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;인터넷, 카카오톡, 메모장, 그림판 등&amp;nbsp;눈에 보이는&amp;nbsp;프로그램&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;백신 프로그램, 그래픽 드라이버, 마이크 드라이버 등 눈에 보이지 않는 프로그램&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;데몬 (Daemon)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;512&quot; data-origin-height=&quot;417&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bID7WJ/btrghPX3X31/EjYHu99WoQ7UU9OKCHQmgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bID7WJ/btrghPX3X31/EjYHu99WoQ7UU9OKCHQmgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bID7WJ/btrghPX3X31/EjYHu99WoQ7UU9OKCHQmgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbID7WJ%2FbtrghPX3X31%2FEjYHu99WoQ7UU9OKCHQmgk%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; alt=&quot;Daemon&quot; loading=&quot;lazy&quot; width=&quot;195&quot; height=&quot;159&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;417&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;Daemon 단어의 어원&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;데몬이란 용어는 맥스웰의 도깨비 사고 실험에서 영감을 얻어 탄생하였다. 맥스웰의 도깨비는 보이지 않는 곳에서 분자들을 골라주는 일을 하는데, 보이지 않는 곳에서 조용히 일한다는 점에서 맥스웰의 도깨비와 데몬 프로그램은 비슷하기 때문이다. 또한 데몬는 악마나 유령을 뜻하는데, 유령은 걷지 않고 항상 떠 있다는 점에서 백그라운드에서 조용하게 항상 수행되는 프로그램을 데몬 프로그램이라 부르게 된 것이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데몬은 항상 돌아가고 있어야하는 웹서버에 적합하여 일반적으로 서버에서 주로 사용된다. 대표적인 예로&amp;nbsp;Apache&amp;nbsp;웹서버의 httpd가 바로 데몬 프로그램이다.&amp;nbsp;httpd는 웹서버에 백그라운드에서 실행되면서 서버로 들어오는 요청에 자동으로 응답해 준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yyjEL/btr61g2HX8D/pjDQJiwipEZvgWx8A3zNrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yyjEL/btr61g2HX8D/pjDQJiwipEZvgWx8A3zNrk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yyjEL/btr61g2HX8D/pjDQJiwipEZvgWx8A3zNrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyyjEL%2Fbtr61g2HX8D%2FpjDQJiwipEZvgWx8A3zNrk%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; alt=&quot;Daemon&quot; loading=&quot;lazy&quot; width=&quot;568&quot; height=&quot;200&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;200&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이밖에도 유닉스(리눅스) 운영체제에서 운영체제에서 프로세스 이름 끝이 &lt;/span&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;d&lt;/span&gt;로 끝나는 프로세스들이 대표적인데 inetd, httpd, nfsd, sshd, named, lpd 등이 있다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.4495%;&quot;&gt;&lt;b&gt;서비스 명&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.2636%;&quot;&gt;&lt;b&gt;&lt;b&gt;데몬 명&lt;/b&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 52.2868%;&quot;&gt;&lt;b&gt;정의&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.4495%;&quot;&gt;Linux 슈퍼데몬&lt;/td&gt;
&lt;td style=&quot;width: 19.2636%;&quot;&gt;xinetd&lt;/td&gt;
&lt;td style=&quot;width: 52.2868%;&quot;&gt;Linux 내 여러 서비스를 한번에 관리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.4495%;&quot;&gt;File Transfer Protocol&lt;/td&gt;
&lt;td style=&quot;width: 19.2636%;&quot;&gt;vsftpd&lt;/td&gt;
&lt;td style=&quot;width: 52.2868%;&quot;&gt;File을 전송하는 서비스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.4495%;&quot;&gt;Secure Shell Service&lt;/td&gt;
&lt;td style=&quot;width: 19.2636%;&quot;&gt;sshd&lt;/td&gt;
&lt;td style=&quot;width: 52.2868%;&quot;&gt;암호화 원격 작업이 가능한 서비스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.4495%;&quot;&gt;Network File System&lt;/td&gt;
&lt;td style=&quot;width: 19.2636%;&quot;&gt;nfs&lt;/td&gt;
&lt;td style=&quot;width: 52.2868%;&quot;&gt;Network 를 이용하여 FileSystem 을 공유하는 서비스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.4495%;&quot;&gt;Domain Name Service&lt;/td&gt;
&lt;td style=&quot;width: 19.2636%;&quot;&gt;named&lt;/td&gt;
&lt;td style=&quot;width: 52.2868%;&quot;&gt;IP와 Domain 을 mapping 시켜주는 서비스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.4495%;&quot;&gt;Web Service&lt;/td&gt;
&lt;td style=&quot;width: 19.2636%;&quot;&gt;httpd&lt;/td&gt;
&lt;td style=&quot;width: 52.2868%;&quot;&gt;Browser 를 이용하여 웹 문서 페이지를 출력시킬 수 있는 서비스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.4495%;&quot;&gt;Database Management&lt;/td&gt;
&lt;td style=&quot;width: 19.2636%;&quot;&gt;mysqld&lt;/td&gt;
&lt;td style=&quot;width: 52.2868%;&quot;&gt;DB를 관리하는 서비스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.4495%;&quot;&gt;Mail Service(send)&lt;/td&gt;
&lt;td style=&quot;width: 19.2636%;&quot;&gt;sendmail&lt;/td&gt;
&lt;td style=&quot;width: 52.2868%;&quot;&gt;mail을 전송할 수 있는 서비스&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;서비스 (Service)&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;이렇게만 보면 데몬과 서비스의 차이가 잘 와닿지 않을 것인데, 윈도우 OS에서는 백그라운드에서 실행되는 응용 프로그램을 service 라 부르고 유닉스(리눅스) OS에서는 daemon 이라고 불리운다고 보면 된다. 즉, 둘이 거의 비슷하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;546&quot; data-origin-height=&quot;359&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pT1KG/btr61mIAE88/mqHpTeoymGGvjTxPzjgVXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pT1KG/btr61mIAE88/mqHpTeoymGGvjTxPzjgVXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pT1KG/btr61mIAE88/mqHpTeoymGGvjTxPzjgVXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpT1KG%2Fbtr61mIAE88%2FmqHpTeoymGGvjTxPzjgVXK%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; alt=&quot;Service&quot; loading=&quot;lazy&quot; width=&quot;546&quot; height=&quot;359&quot; data-origin-width=&quot;546&quot; data-origin-height=&quot;359&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;style1&quot; /&gt;
&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;데몬 &lt;span style=&quot;color: #ef5369;&quot;&gt;vs&lt;/span&gt; 백그라운드 프로세스&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 프로세스와 데몬의 차이는 프로세스는 필요시 실행시키는 것이고 데몬은 자동 실행이라는 것이다. 예를들어 노트패드를 실행시키려면 사용자가 직접 조작해야되지만, 백신 프로그램은 컴퓨터를 재부팅해도 자동으로 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 포그라운드가 아닌 백그라운드 프로세스와 데몬은 무슨 차이일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데몬과 백그라운드 프로세스의 가장 큰 차이점은 운영체제에서 사용되는 점이다. 데몬은 주로 유닉스나 리눅스와 같은 운영체제에서 사용되며 시스템 레벨에서 실행되는 반면, 백그라운드 프로세스는 윈도우 운영체제에서 사용되며 데몬과 비슷한 역할을 수행하지만 대신 서비스(Service)라는 용어를 사용한다는 점에서 미세한 차이가 있다고 보면 된다. 또한 데몬은 유닉스와 같은 운영체제에서는 일반적으로 &lt;b&gt;init 프로세&lt;/b&gt;스를 통해 실행되는 반면, 윈도우 운영체제에서는 &lt;b&gt;서비스 매니저&lt;/b&gt;를 통해 실행된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;init 프로세스는 시스템이 종료될 때까지 계속 실행되는 데몬 프로세스이며 다른 모든 프로세스의 직간접 부모 프로세스이다.&lt;/blockquote&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/QozeH/btr60Dw63V3/bQIExs1AufzvrVlh0y2FU0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QozeH/btr60Dw63V3/bQIExs1AufzvrVlh0y2FU0/img.webp&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;371&quot; data-is-animation=&quot;false&quot; style=&quot;width: 46.9257%; margin-right: 10px;&quot; data-widthpercent=&quot;47.48&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QozeH/btr60Dw63V3/bQIExs1AufzvrVlh0y2FU0/img.webp&quot; alt=&quot;프로세스-데몬-서비스&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQozeH%2Fbtr60Dw63V3%2FbQIExs1AufzvrVlh0y2FU0%2Fimg.webp&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;600&quot; height=&quot;371&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t8MWP/btr6P2lahVW/HahKoDouvF4qB16AKWEd90/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t8MWP/btr6P2lahVW/HahKoDouvF4qB16AKWEd90/img.webp&quot; data-origin-width=&quot;721&quot; data-origin-height=&quot;403&quot; data-is-animation=&quot;false&quot; style=&quot;width: 51.9115%;&quot; data-widthpercent=&quot;52.52&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t8MWP/btr6P2lahVW/HahKoDouvF4qB16AKWEd90/img.webp&quot; alt=&quot;프로세스-데몬-서비스&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft8MWP%2Fbtr6P2lahVW%2FHahKoDouvF4qB16AKWEd90%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;721&quot; height=&quot;403&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;윈도우 서비스 매니저 / init 프로세스 리스트&lt;/figcaption&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;&lt;b&gt;데몬 &lt;span style=&quot;color: #ef5369;&quot;&gt;vs&lt;/span&gt; 서비스&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Daemon 과 Service는 비슷한 개념이지만, 운영 체제 차이점 있다고 했다. 그외에도 사용처에도 차이가 있는데, 데몬은 일반적으로 &lt;b&gt;서버&lt;/b&gt;에서 주로 사용되고, 서비스는 다른 프로그램이나 서비스를 &lt;b&gt;지원&lt;/b&gt;하기 위해 실행된다. 데몬은 시스템에서 독립적으로 실행되고, 서비스는 시스템의&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;하지만 이들 용어는 종종 상호교환적으로 사용되기도 하며, 컴퓨터 시스템에서 다양한 역할을 수행하므로, 구체적인 용어 해석은 사용되는 컨텍스트에 따라 달라질 수 있다. 예를들어 리눅스에서 포트/프로토콜 정보를 정의해놓은 /etc/services 디렉토리가 있기 때문에 두 OS에서 '서비스'와 '데몬'은 혼용되어 쓰인다고 보면 된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;그렇지만 윈도우 일반 솔루션에서도 데몬이라는 용어를 쓰기도 한다. 그 솔루션의 기능을 수행하기 위해서는 필수적으로 항상 실행이 되어 있어야 하기에, 프로세스라 부르면 조금 어색해져 데몬이라 부르는 것이다.&lt;/blockquote&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;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://hec-ker.tistory.com/323&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://jins-dev.tistory.com/entry/Daemon-%EA%B3%BC-Background-Process-%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://blogger.pe.kr/770&lt;/p&gt;</description>
      <category>개발 지식/CS 지식</category>
      <category>데몬</category>
      <category>서비스</category>
      <category>프로세스</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/212</guid>
      <comments>https://inpa.tistory.com/entry/%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EB%8D%B0%EB%AA%AC-%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%A0%95%EB%A6%AC#entry212comment</comments>
      <pubDate>Thu, 30 Mar 2023 17:03:16 +0900</pubDate>
    </item>
    <item>
      <title>☕ 람다식을 더 짧게 - 메소드::참조 문법</title>
      <link>https://inpa.tistory.com/entry/JAVA8-%E2%98%95-%EB%9E%8C%EB%8B%A4%EC%8B%9D%EC%9D%84-%EB%8D%94-%EC%A7%A7%EA%B2%8C-%EB%A9%94%EC%86%8C%EB%93%9C-%EC%B0%B8%EC%A1%B0Method-Reference</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1_S5KkDX11wbKa6suvBPBFJA.webp&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;875&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JoBlK/btrQ5ldUlVo/bkglCmTp2Du17Qt5aaAswk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JoBlK/btrQ5ldUlVo/bkglCmTp2Du17Qt5aaAswk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JoBlK/btrQ5ldUlVo/bkglCmTp2Du17Qt5aaAswk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJoBlK%2FbtrQ5ldUlVo%2FbkglCmTp2Du17Qt5aaAswk%2Fimg.webp&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; alt=&quot;lambda-Method-Reference&quot; loading=&quot;lazy&quot; width=&quot;1400&quot; height=&quot;875&quot; data-filename=&quot;1_S5KkDX11wbKa6suvBPBFJA.webp&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;875&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;람다식 메소드&lt;span style=&quot;color: #ef5369;&quot;&gt;::&lt;/span&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;b&gt;메소드 참조(Method&amp;nbsp;Reference)&lt;/b&gt;는 말 그대로 실행하려는 메소드를 참조해서 매개 변수의 정보 및 리턴 타입을 알아내어, 람다식에서 굳이 선언이 불필요한 부분을 생략하는 것을 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;람다식은 복잡하고 길다란 로직보단 기존 메소드들을 단순히 호출만 하는 경우가 많다. 예를 들어 두 개의 값을 받아 작은 수를 리턴하는 Math 클래스의 &lt;s&gt;max()&lt;/s&gt; 정적 메소드를 호출하는 람다식은 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1666169420275&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(x, y) -&amp;gt; Math.max(x, y)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 자체로도 간략해 보이지만, 자바 개발진은 그러지 않은가 보다. 함수 형태를 보면 리턴값 자체가 또다른 Math 클래스의 메소드를 호출하는 것 뿐이고, 람다 함수의 매개변수 역시 그대로 &lt;s&gt;max()&lt;/s&gt; 메서드의 매개변수로 들어가기 때문에 코드 중복이 발생하기 때문이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0xsvm/btrWEKfnDY6/5JApyrQt0UNbZ0bFybSKs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0xsvm/btrWEKfnDY6/5JApyrQt0UNbZ0bFybSKs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0xsvm/btrWEKfnDY6/5JApyrQt0UNbZ0bFybSKs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0xsvm%2FbtrWEKfnDY6%2F5JApyrQt0UNbZ0bFybSKs1%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; alt=&quot;lambda-Method-Reference&quot; loading=&quot;lazy&quot; width=&quot;426&quot; height=&quot;246&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;426&quot; data-origin-height=&quot;246&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 중복되는 매개변수를 없애고, &lt;span&gt;화살표를 없애고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;클래스가 메소드를 참조하는 기호인 &lt;s&gt;.&lt;/s&gt; 기호를 &lt;s&gt;::&lt;/s&gt; 기호로 변환하면 다음과 같이 표현 할 수 있게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1666170451169&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Math::max; // (x, y) -&amp;gt; Math.max(x, y)&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;메서드 참조 조건과 타입 추론&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;만약 &lt;s&gt;Math::min&lt;/s&gt; 메서드 참조를 변수에 담고 싶다면, &lt;a href=&quot;https://inpa.tistory.com/entry/%E2%98%95-%ED%95%A8%EC%88%98%ED%98%95-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-API&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;함수형 인터페이스&lt;/a&gt; 타입인 IntBinaryOperator 인터페이스를 사용하면 된다. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;두 개의 int 형 입력값을 받아 int 값을 리턴하는 함수 디스크립터를 갖고 있기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;408&quot; data-origin-height=&quot;109&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2pPUz/btrOQm7UQAC/IvzR7GrbASjywcKhgZ3yQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2pPUz/btrOQm7UQAC/IvzR7GrbASjywcKhgZ3yQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2pPUz/btrOQm7UQAC/IvzR7GrbASjywcKhgZ3yQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2pPUz%2FbtrOQm7UQAC%2FIvzR7GrbASjywcKhgZ3yQ1%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; alt=&quot;lambda-Method-Reference&quot; loading=&quot;lazy&quot; width=&quot;408&quot; height=&quot;109&quot; data-origin-width=&quot;408&quot; data-origin-height=&quot;109&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1666170497260&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;IntBinaryOperator b = Math::min;
b.applyAsInt(100, 200); // 100&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;span style=&quot;color: #000000;&quot;&gt;하기 때문이다. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이때 람다 타입 추론은 기존의 람다 익명 함수와는 약간 차이가 있다. &lt;/span&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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;595&quot; data-origin-height=&quot;511&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wiCxn/btrOQny0Exn/DMOcurIrtp72kdXS3TRjyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wiCxn/btrOQny0Exn/DMOcurIrtp72kdXS3TRjyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wiCxn/btrOQny0Exn/DMOcurIrtp72kdXS3TRjyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwiCxn%2FbtrOQny0Exn%2FDMOcurIrtp72kdXS3TRjyK%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; alt=&quot;lambda-Method-Reference&quot; loading=&quot;lazy&quot; width=&quot;595&quot; height=&quot;511&quot; data-origin-width=&quot;595&quot; data-origin-height=&quot;511&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 그림과 같이, IntBinaryOperator 인터페이스의 추상 메서드는 int형 매개변수 2개를 받으며 반환 타입이 int형이고, Math 클래스의 max 메서드 역시 타입이 똑같이 구성되어 있기 때문에, 이를 컴파일러가 추론해서 가능한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하자면, 람다식의 메소드 참조 문법을 사용하기 위해서는 다음의 3가지 조건을 만족하면 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;함수형 인터페이스의 매개변수 타입 == 메소드의 매개변수 타입&lt;/li&gt;
&lt;li&gt;함수형 인터페이스의 매개변수 개수 == 메소드의 매개변수 개수&lt;/li&gt;
&lt;li&gt;함수형 인터페이스의 반환 타입 == 메소드의 반환 타입&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&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;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;종류&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;람다 표현식&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;메서드 참조&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;정적 메서드 참조&lt;/td&gt;
&lt;td&gt;&lt;s&gt;(x) -&amp;gt; ClassName.method(x)&lt;/s&gt;&lt;/td&gt;
&lt;td&gt;&lt;s&gt;ClassName::method&lt;/s&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;인스턴스 메서드 참조&lt;/td&gt;
&lt;td&gt;&lt;s&gt;(x) -&amp;gt; obj.method(x)&lt;/s&gt;&lt;/td&gt;
&lt;td&gt;&lt;s&gt;obj::method&lt;/s&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;매개변수의 메서드 참조&lt;/td&gt;
&lt;td&gt;&lt;s&gt;(obj, x) -&amp;gt; obj.method(x)&lt;/s&gt;&lt;/td&gt;
&lt;td&gt;&lt;s&gt;ClassName::method&lt;/s&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;생성자 참조&lt;/td&gt;
&lt;td&gt;&lt;s&gt;(x, y) -&amp;gt; new ClassName(x, y)&lt;/s&gt;&lt;/td&gt;
&lt;td&gt;&lt;s&gt;ClassName::new&lt;/s&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;정적 메소드 참조&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 Interger 클래스의 &lt;s&gt;parseInt()&lt;/s&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;s&gt;::&lt;/s&gt; 기호 앞부분에 클래스명을 그대로 기재하는 것이 특징이다.&lt;/p&gt;
&lt;pre id=&quot;code_1674192528527&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Function&amp;lt;String, Integer&amp;gt; stringToInt;

// (x) -&amp;gt; ClassName.method(x)
stringToInt = (s) -&amp;gt; Integer.parseInt(s);

// ClassName::method
stringToInt = Integer::parseInt;

stringToInt.apply(&quot;100&quot;);&lt;/code&gt;&lt;/pre&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;&lt;span&gt;인스턴스 메소드 참조&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 ArrayList 클래스의 인스턴스를 만들고 인자로 Collection을 받아 리스트에 요소들을 추가하는 &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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;인스턴스의 메소드를 참조할때,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;메서드 참조&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;s&gt;::&lt;/s&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;기호 앞부분에 상단에 선언한 인스턴스 변수를 기재하는 것이 특징이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1674192951752&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ArrayList&amp;lt;Number&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();
Consumer&amp;lt;Collection&amp;lt;Number&amp;gt;&amp;gt; addElements;

// (x) -&amp;gt; obj.method(x)
addElements = (arr) -&amp;gt; list.addAll(arr);

// obj::method
addElements = list::addAll;

addElements.accept(List.of(1, 2, 3, 4, 5));

System.out.println(list); // [1, 2, 3, 4, 5]&lt;/code&gt;&lt;/pre&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;매개변수의 메서드 참조&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 String 클래스의 인스턴스를 받아 문자열의 길이를 구하는 &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;/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;s&gt;::&lt;/s&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;기호 앞부분에 정적 메소드 참조와 똑같이 매개변수의 클래스 타입명을 기재하는 것이 특징이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;311&quot; data-origin-height=&quot;72&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Cvmq0/btrWO22Vssv/ddwTQBkksWUulW1PCkJ950/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Cvmq0/btrWO22Vssv/ddwTQBkksWUulW1PCkJ950/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Cvmq0/btrWO22Vssv/ddwTQBkksWUulW1PCkJ950/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCvmq0%2FbtrWO22Vssv%2FddwTQBkksWUulW1PCkJ950%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; alt=&quot;매개변수의 메서드 참조&quot; loading=&quot;lazy&quot; width=&quot;311&quot; height=&quot;72&quot; data-origin-width=&quot;311&quot; data-origin-height=&quot;72&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1674193624782&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Function&amp;lt;String, Integer&amp;gt; size;

// (obj, x) -&amp;gt; obj.method(x)  
size = (String s1) -&amp;gt;  s1.length();

// ClassName::method
size = String::length;

size.apply(&quot;Hello World&quot;); // 11&lt;/code&gt;&lt;/pre&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;&lt;span&gt;생성자 참조&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자도 일종의 메소드 이기 때문에 메서드 참조가 가능하다. 리턴값이 단순히 객체를 생성하는 것이기 때문에 적용이 가능한 것이다. 대신 생성자는 고유 메서드명이 없기 때문에 &lt;s&gt;new&lt;/s&gt; 로 표시한다.&lt;/p&gt;
&lt;pre id=&quot;code_1674193699713&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;BiFunction&amp;lt;Integer, Integer, Object&amp;gt; constructor;

// (x, y) -&amp;gt; new ClassName(x, y)  
constructor = (x, y) -&amp;gt; new Object(x, y);

// ClassName::new
constructor = Object::new;&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;생성자 오버로딩&lt;/b&gt;을 통해 매개 변수 개수에 따라서 인스턴스화를 구현하려고 할때, 람다식을 각기 다른 함수형 인터페이스에 대입하여 실행시켜야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1674034008692&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;

class Customer {
    String name;
    int age;

    public Customer() {
    }

    public Customer(String name) {
        this.name = name;
    }

    public Customer(String name, int age) {
        this.name = name;
        this.age = age;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1666169545042&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    // 생성자 인자 없이 생성할때
    Supplier&amp;lt;Customer&amp;gt; function1 = Customer::new;
    Customer customer1 = function1.get();

    // 생성자 인자 1개로만 생성할때 (입력타입, 생성자클래스)
    Function&amp;lt;String, Customer&amp;gt; function2 = Customer::new;
    Customer customer2 = function2.apply(&quot;홍길동&quot;);

    // 생성자 인자 2개로만 생성할때 (입력타입1, 입력타입2, 생성자클래스)
    BiFunction&amp;lt;String, Integer, Customer&amp;gt; function3 = Customer::new;
    Customer customer3 = function3.apply(&quot;홍길동&quot;, 55);
}&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;이를 고급 응용하자면 Map 컬렉션에 생성자 람다를 넣어서 인스턴스 관리를 통해 '지연 인스턴스화' 기법을 구현할 수 있다. 이 기법은 &lt;a href=&quot;https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-Dynamic-Factory-%EB%B3%80%ED%98%95-%ED%8C%A8%ED%84%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;다이나믹 팩토리 패턴&lt;/a&gt;으로서 리플렉션 대안으로 응용이 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1674195512710&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Fruit {}

class Apple extends Fruit {
    int count;

    public Apple(int count) {
        this.count = count;
    }
}
class Banana extends Fruit {
    int count;

    public Banana(int count) {
        this.count = count;
    }
}

class Constructor {
    public static Map&amp;lt;String, Function&amp;lt;Integer, Fruit&amp;gt;&amp;gt; map = new HashMap&amp;lt;&amp;gt;();

    static {
        map.put(&quot;apple&quot;, Apple::new);
        map.put(&quot;banana&quot;, Banana::new);
    }

    public static Fruit makeFruit(String name, int count) {
        if(map.containsKey(name)) {
            return map.get(name.toLowerCase()).apply(count);
        }
        return null;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1674195515627&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    Apple apples = (Apple) Constructor.makeFruit(&quot;apple&quot;, 10);
    Banana bananas = (Banana) Constructor.makeFruit(&quot;banana&quot;, 4);
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;부록)&lt;/span&gt; 자바스크립트의 메소드 참조&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트의 화살표 함수도 사실 람다 표현식의 일종이다. 그래서 자바와 같이 화살표 함수에 대한 메서드 참조가 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1674177938901&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;List&amp;lt;Number&amp;gt; list = Arrays.asList(1, 2, 3, 4, 5, 6);

// 기존 람다 방식
list.forEach( (e) -&amp;gt; System.out.println(e) );

// 메소드 참조 방식
list.forEach(System.out::println);&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1674108470501&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

// 기존 콜백 방식
arr.forEach( (e) =&amp;gt; console.log(e) );

// 메소드 참조 방식
arr.forEach(console.log);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;397&quot; data-origin-height=&quot;292&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9ZFen/btrWGeofiw3/LIDRrEbB8tHA9GVn91Uy70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9ZFen/btrWGeofiw3/LIDRrEbB8tHA9GVn91Uy70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9ZFen/btrWGeofiw3/LIDRrEbB8tHA9GVn91Uy70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9ZFen%2FbtrWGeofiw3%2FLIDRrEbB8tHA9GVn91Uy70%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; alt=&quot;java-javascript-lambda&quot; loading=&quot;lazy&quot; width=&quot;397&quot; height=&quot;292&quot; data-origin-width=&quot;397&quot; data-origin-height=&quot;292&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;자바스크립트로 브라우저의 이벤트를 등록할때 다음과 같이 addEventListener의 핸들러로 바로 화살표 함수를 정의해줘도 되지만 한단계 거쳐 먼저 함수를 선언하고 함수 자체를 넘겨줘본 경험이 있을 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1674197981882&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const handler = () =&amp;gt; {};

window.addEventListener('click', handler);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;addEventListener 메서드가 매개변수로 callback 함수를 받아 내부에서 실행시켜주기 때문에 이러한 수식이 가능한 것이다.&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;자바스크립트의 forEach 구현&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 forEach를 간단하게 직접 구현해보면 아래와 같이 될 것이다. &lt;span&gt;자바스크립트 함수 매개변수로 함수 자체를 받아 함수 내부에서 매개변수를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;s&gt;callback()&lt;/s&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;괄호를 붙여 함수 호출을 해주는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1674197448485&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function forEach(arr, callback) {
    for(const i of arr) {
        callback(i); // console.log(i)
    }
}

const arr = [0, 1, 2, 3, 4, 5];
forEach(arr, console.log);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;163&quot; data-origin-height=&quot;132&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k7giK/btrWPf1zqky/m4x391xQthI9133lLlrvcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k7giK/btrWPf1zqky/m4x391xQthI9133lLlrvcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k7giK/btrWPf1zqky/m4x391xQthI9133lLlrvcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk7giK%2FbtrWPf1zqky%2Fm4x391xQthI9133lLlrvcK%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; alt=&quot;java-javascript-lambda&quot; loading=&quot;lazy&quot; width=&quot;163&quot; height=&quot;132&quot; data-origin-width=&quot;163&quot; data-origin-height=&quot;132&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;&lt;b&gt;자바의 forEach 구현&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;다음은 forEach 동작을 자바로 아주 간단하게 구현해본 코드이다.&lt;/p&gt;
&lt;pre id=&quot;code_1674177960740&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void forEach(int[] arr, Consumer&amp;lt;? super Number&amp;gt; callback) {
    for (int i : arr) {
        callback.accept(i); // System.out.println(i)
    }
}

public static void main(String[] args) {
    int[] arr = {1,2,3,4,5};
    forEach(arr, System.out::println);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;150&quot; data-origin-height=&quot;120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWc7QD/btrWQbXKibF/m4xxbAqL7A44Jgf6ksxDQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWc7QD/btrWQbXKibF/m4xxbAqL7A44Jgf6ksxDQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWc7QD/btrWQbXKibF/m4xxbAqL7A44Jgf6ksxDQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWc7QD%2FbtrWQbXKibF%2Fm4xxbAqL7A44Jgf6ksxDQk%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; alt=&quot;java-javascript-lambda&quot; loading=&quot;lazy&quot; width=&quot;150&quot; height=&quot;120&quot; data-origin-width=&quot;150&quot; data-origin-height=&quot;120&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;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot;data-ke-size=&quot;size16&quot;&gt;https://dev-kani.tistory.com/38&lt;/p&gt;
&lt;p class=&quot;swap-href&quot;data-ke-size=&quot;size16&quot;&gt;https://steady-coding.tistory.com/307&lt;/p&gt;</description>
      <category>Language/Java</category>
      <category>람다</category>
      <category>람다 ::</category>
      <category>람다식</category>
      <category>메소드 참조</category>
      <category>자바 함수</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/970</guid>
      <comments>https://inpa.tistory.com/entry/JAVA8-%E2%98%95-%EB%9E%8C%EB%8B%A4%EC%8B%9D%EC%9D%84-%EB%8D%94-%EC%A7%A7%EA%B2%8C-%EB%A9%94%EC%86%8C%EB%93%9C-%EC%B0%B8%EC%A1%B0Method-Reference#entry970comment</comments>
      <pubDate>Thu, 30 Mar 2023 09:52:54 +0900</pubDate>
    </item>
    <item>
      <title>☕ 함수 디스크립터(Function Descriptor) 란?</title>
      <link>https://inpa.tistory.com/entry/%F0%9F%9A%80-Function-Descriptor</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1_S5KkDX11wbKa6suvBPBFJA.webp&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;875&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgHWNx/btrSqYu72cf/rJu7smwhFYTuRHlg8fohR0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgHWNx/btrSqYu72cf/rJu7smwhFYTuRHlg8fohR0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgHWNx/btrSqYu72cf/rJu7smwhFYTuRHlg8fohR0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgHWNx%2FbtrSqYu72cf%2FrJu7smwhFYTuRHlg8fohR0%2Fimg.webp&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; alt=&quot;Function Descriptor&quot; loading=&quot;lazy&quot; width=&quot;1400&quot; height=&quot;875&quot; data-filename=&quot;1_S5KkDX11wbKa6suvBPBFJA.webp&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;875&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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;보통 자바의 람다 표현식(Lambda Expression)의 사용 용도를 두가지로 정의하는데, 하나는 &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;b&gt;함수 디스크립터(Function Descriptor)&lt;/b&gt;는 단어 그대로 함수가 어떤 입력값을 받고 어떤 반환값을 주는지에 대한 설명을 람다 표현식 문법으로 표현한 것을 의미한다고 보면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어,&amp;nbsp;&lt;s&gt;() -&amp;gt; void&lt;/s&gt;&amp;nbsp;는 파라미터 리스트가 없으며 void를 반환하는 함수를 의미하는 디스크립터며,&amp;nbsp;&lt;s&gt;(int, int) -&amp;gt; double&lt;/s&gt;&amp;nbsp;는 두개의 int를 파라미터로 받아 double형 자료를 반환하는 함수를 설명한다고 보면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 함수 디스크립터는 주로 java.util.function 패키지의 함수형 인터페이스를 람다식으로 사용할때 어떤 역할을 하는 함수인지 간략하게 보여주는 시그너처이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;타입스크립트 진영에서는 아예 함수 자체 타입을 람다식(화살표 함수)로 표현하는데 이것도 함수 디스크립터 일종이라고 봐도 무방하다.&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 129px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.4884%;&quot;&gt;&lt;b&gt;함수형 인터페이스&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 71.3953%;&quot;&gt;&lt;b&gt;함수 디스크립터 (추상 메서드가 어떤 역할을 하는지 간략하게 설명)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px; width: 28.4884%;&quot;&gt;Predicate&lt;/td&gt;
&lt;td style=&quot;height: 17px; width: 71.3953%;&quot;&gt;T -&amp;gt; boolean&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px; width: 28.4884%;&quot;&gt;Consumer&lt;/td&gt;
&lt;td style=&quot;height: 17px; width: 71.3953%;&quot;&gt;T -&amp;gt; void&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px; width: 28.4884%;&quot;&gt;Function&amp;lt;T,R&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px; width: 71.3953%;&quot;&gt;T -&amp;gt; R&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px; width: 28.4884%;&quot;&gt;Supplier&lt;/td&gt;
&lt;td style=&quot;height: 17px; width: 71.3953%;&quot;&gt;( ) -&amp;gt; T&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px; width: 28.4884%;&quot;&gt;BiPredicate&amp;lt;L, R&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px; width: 71.3953%;&quot;&gt;(L, R) -&amp;gt; boolean&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px; width: 28.4884%;&quot;&gt;BiConsumer&amp;lt;L, R&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px; width: 71.3953%;&quot;&gt;(T, U) -&amp;gt; void&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px; width: 28.4884%;&quot;&gt;BiFunction&amp;lt;T,U,R&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px; width: 71.3953%;&quot;&gt;(T, U) -&amp;gt; R&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 28.4884%;&quot;&gt;Runnable&lt;/td&gt;
&lt;td style=&quot;width: 71.3953%;&quot;&gt;() -&amp;gt; void&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>Language/Java</category>
      <category>람다</category>
      <category>함수 디스크립터</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/997</guid>
      <comments>https://inpa.tistory.com/entry/%F0%9F%9A%80-Function-Descriptor#entry997comment</comments>
      <pubDate>Wed, 29 Mar 2023 13:39:02 +0900</pubDate>
    </item>
    <item>
      <title>☕ 함수형 인터페이스 표준 API 총정리</title>
      <link>https://inpa.tistory.com/entry/%E2%98%95-%ED%95%A8%EC%88%98%ED%98%95-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-API</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1_S5KkDX11wbKa6suvBPBFJA.webp&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;875&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czKlAq/btrQ1K6B9f9/nDRy8k6MV1NkT26rfEKqW1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czKlAq/btrQ1K6B9f9/nDRy8k6MV1NkT26rfEKqW1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czKlAq/btrQ1K6B9f9/nDRy8k6MV1NkT26rfEKqW1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczKlAq%2FbtrQ1K6B9f9%2FnDRy8k6MV1NkT26rfEKqW1%2Fimg.webp&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; alt=&quot;java-functional-interface-api&quot; loading=&quot;lazy&quot; width=&quot;1400&quot; height=&quot;875&quot; data-filename=&quot;1_S5KkDX11wbKa6suvBPBFJA.webp&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;875&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;함수형 인터페이스 표준 API&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수형 인터페이스(functional interface)는 추상메서드가 1개만 정의된 인터페이스를 통칭하여 일컫는다. 이 인터페이스 형태의 목적은 자바에서 &lt;a href=&quot;https://inpa.tistory.com/entry/%E2%98%95-Lambda-Expression&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;람다 표현식(Lambda Expression)&lt;/a&gt;을 이용해 함수형 프로그래밍을 구현하기 위해서 이다.&lt;/p&gt;
&lt;pre id=&quot;code_1666092965756&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// @FunctionalInterface 어노테이션을 인터페이스에 붙여주면, 
// 두 개 이상의 메소드 선언 시 컴파일 오류를 발생시켜 개발자의 실수를 줄일 수 있다.
@FunctionalInterface
public interface Animal {
		public void method();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 곰곰히 생각해보면 함수의 형태(Signature)는 다양하다. 함수의 리턴 값이 있을수도 없을수도 있고 매개변수 갯수가 1개 혹은 2개일 수도 있다. 이러한 형태의 함수를 일일히 정의해서 사용하기엔 너무 양이 많으니, 자바 개발진들이 미리 함수형 인터페이스를 만들어 제공하는 것이 바로 이번 시간에 배울&lt;b&gt; 함수형 인터페이스 표준 API&lt;/b&gt; 이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;자바에서 자료구조를 컬렉션 프레임워크로 미리 만들어 제공하듯이, 자주 사용할 것 같은 람다 함수 형태를 함수형 인터페이스 표준 API로 미리 만들어 제공해준다라고 이해하면 된다.&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;표준 API 종류&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수적 인터페이스 표준 API는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;java.util.function&lt;/s&gt;&amp;nbsp;패키지로 제공된다.&lt;span&gt; &lt;/span&gt;이 패키지 안에 있는 함수적 인터페이스 종류로는 Consumer, Supplier, Function, Operator, Predicate 가 있다. 이 인터페이스명은 각 함수의 형태와 목적에 따라 조금 재미있게 붙여진 이름인데 이에 대해서는 뒤에서 자세히 다룬다.&lt;/p&gt;
&lt;pre id=&quot;code_1674114426224&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.function.*;&lt;/code&gt;&lt;/pre&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 162px;&quot; border=&quot;1&quot; data-ke-style=&quot;style12&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 17.2093%; height: 20px;&quot;&gt;&lt;b&gt;함수형 인터페이스&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 16.7442%; height: 20px;&quot;&gt;&lt;b&gt;메서드 형태&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 48.721%; height: 20px;&quot;&gt;&lt;b&gt;API 활용&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 9.06977%; height: 20px;&quot;&gt;&lt;b&gt;매개변수&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.25581%; height: 20px;&quot;&gt;&lt;b&gt;반환값&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 17.2093%; height: 40px;&quot;&gt;Runnable&lt;/td&gt;
&lt;td style=&quot;width: 16.7442%; height: 40px;&quot;&gt;void run()&lt;/td&gt;
&lt;td style=&quot;width: 48.721%; height: 40px;&quot;&gt;매개 변수를 사용 안하고 &lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;리턴을 하지 않는 함수 형태로 이용&lt;/span&gt;&lt;br /&gt;대표적으로 쓰레드의 매개 변수로 이용&lt;/td&gt;
&lt;td style=&quot;width: 9.06977%; height: 40px;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;X&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.25581%; height: 40px;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;X&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;width: 17.2093%; height: 22px;&quot;&gt;Consumer&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 16.7442%; height: 22px;&quot;&gt;void accept(T t)&lt;/td&gt;
&lt;td style=&quot;width: 48.721%; height: 22px;&quot;&gt;매개 변수를 사용만 하고 리턴을 하지 않는 함수 형태로 이용&lt;/td&gt;
&lt;td style=&quot;width: 9.06977%; height: 22px;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;O&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.25581%; height: 22px;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;X&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 17.2093%; height: 20px;&quot;&gt;Supplier&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 16.7442%; height: 20px;&quot;&gt;T get()&lt;/td&gt;
&lt;td style=&quot;width: 48.721%; height: 20px;&quot;&gt;매개 변수를 사용 안하고 리턴만 하는 함수 형태로 이용&lt;/td&gt;
&lt;td style=&quot;width: 9.06977%; height: 20px;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;X&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.25581%; height: 20px;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;O&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 17.2093%; height: 20px;&quot;&gt;Function&amp;lt;T, R&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 16.7442%; height: 20px;&quot;&gt;R apply(T t)&lt;/td&gt;
&lt;td style=&quot;width: 48.721%; height: 20px;&quot;&gt;매개값을&amp;nbsp;&lt;span style=&quot;color: #ee2323;&quot;&gt;매핑&lt;/span&gt;(=타입변환)해서 리턴하기&lt;/td&gt;
&lt;td style=&quot;width: 9.06977%; height: 20px;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;O&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.25581%; height: 20px;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;O&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 17.2093%; height: 20px;&quot;&gt;Predicate&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 16.7442%; height: 20px;&quot;&gt;boolean test(T t)&lt;/td&gt;
&lt;td style=&quot;width: 48.721%; height: 20px;&quot;&gt;매개값이 조건에 맞는지 &lt;span style=&quot;color: #ee2323;&quot;&gt;단정&lt;/span&gt;해서&amp;nbsp;boolean&amp;nbsp;리턴&lt;/td&gt;
&lt;td style=&quot;width: 9.06977%; height: 20px;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;O&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.25581%; height: 20px;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;O&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 17.2093%; height: 20px;&quot;&gt;Operator&lt;/td&gt;
&lt;td style=&quot;width: 16.7442%; height: 20px;&quot;&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;R applyAs(T t)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 48.721%; height: 20px;&quot;&gt;매개값을&amp;nbsp;&lt;span style=&quot;color: #ee2323;&quot;&gt;연산&lt;/span&gt;해서 결과 리턴하기&lt;/td&gt;
&lt;td style=&quot;width: 9.06977%; height: 20px;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;O&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 8.25581%; height: 20px;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;O&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #ef6f53;&quot;&gt;(T: 데이터 타입, R: 리턴 타입)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 처럼 다양한 함수적 형태가 될수 있는 것들을 추려 모아 정리하여 API로 제공함으로써, 우리 같은 개발자들은 굳이 추상 메소드 하나 가진 인터페이스를 일일히 정의할 필요없이 API 만 가져다 쓰면 되는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;표준 API는 타입 제공 용도이다&lt;/b&gt;&lt;/h3&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;pre id=&quot;code_1666143325130&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let a = () =&amp;gt; {};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 자바는 강타입 언어이기 때문에 어떠한 정수면 정수에 맞는 타입을, 함수면 함수에 맞는 타입을 지정해 주어야 한다. 그래서 람다 함수를 담을 수 있는 대표할 타입을 인터페이스 타입으로 정한 것이라고 이해하면 된다. (정확히는 인터페이스 익명 구현 객체를 간략화 한 것이다)&lt;/p&gt;
&lt;pre id=&quot;code_1666143311442&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@FunctionalInterface
public interface Animal {
		public void run();
}

// run() 이라는 메서드를 인터페이스 타입으로 선언하여 정의
Animal a = () -&amp;gt; {};&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;이것이 왜 문제냐면 메소드를 설계할때 애매해진다. 만일 파라미터로 람다 함수를 받는 어떠한 메소드를 설계한다고 하면, 이 &lt;span style=&quot;color: #ee2323;&quot;&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;/span&gt;하는 것이 위의 표에서 본 Runnable, Consumer, Supplier, Function, Operator, Predicate 인터페이스인 것이다.&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;Runnable 인터페이스&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수형 인터페이스와 람다 함수를 이해하는데 있어 가장 좋은 것이 바로 Runnable 인터페이스이다. 실제 자바 패키지에 정의 되어있는 Runnalbe 인터페이스 선언 코드를 보면 다음과 같이 되어 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;701&quot; data-origin-height=&quot;233&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Mp7KC/btrOQl1xQHA/DkKLc785pzK79osi85AvNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Mp7KC/btrOQl1xQHA/DkKLc785pzK79osi85AvNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Mp7KC/btrOQl1xQHA/DkKLc785pzK79osi85AvNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMp7KC%2FbtrOQl1xQHA%2FDkKLc785pzK79osi85AvNK%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; alt=&quot;java-runnable-interface&quot; loading=&quot;lazy&quot; width=&quot;701&quot; height=&quot;233&quot; data-origin-width=&quot;701&quot; data-origin-height=&quot;233&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;앞서 말했듯이 이러한 인터페이스들을 만든 이유는 람다 함수의 타입명을 미리 지정하기 위해서 라고 했다. 즉, 매개변수를 받지도 않고 리턴값이 없고 그냥 실행만 하는 람다 함수 형태를 받는 메서드를 설계할때 람다 매개변수의 인터페이스 타입을 Runnable로 설정하면 되는 것이다. 그리고 그 메소드가 바로 우리가 잘 아는 Thread 클래스의 메서드(생성자)이다.&lt;/p&gt;
&lt;pre id=&quot;code_1666152697303&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Thread thread = new Thread( () -&amp;gt; {
    for(int i = 0; i &amp;lt; 10; i++) {
        System.out.println(i);
    }
} );&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 Thread 클래스 정의문에 가서 생성자 형태를 보면 다음과 같이 파라미터로 Runnalbe 타입의 &lt;s&gt;target&lt;/s&gt; 변수를 받아 사용한 다는 것을 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;848&quot; data-origin-height=&quot;162&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGMt1K/btrO3E7LPF4/youM1A5mbnXI102WrnNdj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGMt1K/btrO3E7LPF4/youM1A5mbnXI102WrnNdj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGMt1K/btrO3E7LPF4/youM1A5mbnXI102WrnNdj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGMt1K%2FbtrO3E7LPF4%2FyouM1A5mbnXI102WrnNdj0%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; alt=&quot;java-runnable-interface&quot; loading=&quot;lazy&quot; width=&quot;848&quot; height=&quot;162&quot; data-origin-width=&quot;848&quot; data-origin-height=&quot;162&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 함수형 인터페이스 표준 API의 존재 이유다. Runnalbe외에도 여러가지 자바 메소드에서 미리 파라미터 타입으로 정의된 여러 종류의 함수형 인터페이스 표준 API에 정의된 타입을 받도록 설계하였기 때문에 자유롭게 람다식을 할당 할 수 있는 것이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;함수형 인터페이스 API 사용법&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제부터 함수적 인터페이스 API의 구체적인 사용법에 대해 상세히 알아볼 시간을 가질텐데, 엄청난 양의 표준 API를 보고 절대 겁먹지 말자. 절대로 일일히 다 외우려 하지말고 String 이나 List 클래스의 메서드 처럼 필요할때마다 찾아서 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 표준 API들은 사실상 알고보면 별거 아닌 놈들이다. 단어만 그럴싸하게 함수형 인터페이스 API 라고 하지, 그냥 람다식을 이용해 프로그래밍할때 자주 사용될것 같은 함수 모양, 형태를 미리 만들어놓고 인터페이스 이름만 아주 그럴싸하게 전문가 처럼 지어놓은 것 뿐이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어 바로 다음에 배울 Consumer 인터페이스 API도 그냥 뭔가 대단한 자료형 마냥 보이겠지만, 그냥 &lt;b&gt;매개변수만 받고&lt;/b&gt;&amp;nbsp;&lt;b&gt;반환값이 없는 형태의 함수를 표현&lt;/b&gt;하기 위해 '소비' 라는 단어를 골라 넣은 것 뿐이다. 그리고 자바에는 타입이 여러개이니 각 타입들에 맞춰 &lt;span&gt;Consumer&lt;span&gt; 를 여러개로 구성한 것이다. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 점을 유의해서 인터페이스 표준 API를 살펴보면 공부에 대한 거부감이나 압박감 없이 스무스하게 학습 할 수 있을 것이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Consumer 인터페이스&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;역할 : 매개값만 받고 처리 (리턴값 X)&lt;/li&gt;
&lt;li&gt;실행 메서드 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;accept()&lt;/s&gt;&lt;/li&gt;
&lt;li&gt;소비(consume)한다는 말은 사용만 할 뿐 리턴값이 없다는 뜻으로 보면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;인터페이스 형태&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;내용&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consumer&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td&gt;T 형태의 인자값을 받는다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BiConsumer&amp;lt;T,&amp;nbsp;U&amp;gt;&lt;/td&gt;
&lt;td&gt;T, U 형태의 인자값 2개를 받는다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;XXX&lt;/span&gt;Consumer&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;XXX&lt;/span&gt; 형태의 인자값을 받는다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Obj&lt;span style=&quot;color: #6164c6;&quot;&gt;XXX&lt;/span&gt;Consumer&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td&gt;T, &lt;span style=&quot;color: #6164c6;&quot;&gt;XXX&lt;/span&gt; 형태의 인자값 2개를 받는다&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;BiConsumer의 Bi 뜻은, 라틴어에서 파생된 영어 접두사 bi- 로 &quot;둘&quot;을 의미한다. 따라서 매개변수를 두개 받는다로 이해하며 암기하면 된다.&lt;/blockquote&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;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Consumer 종류&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; width=&quot;100%&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;인터페이스 명&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;추상 메소드&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;설명&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;Consumer&amp;lt;T&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;void accept(T t)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;객체 T를 받아 소비&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;BiConsumer&amp;lt;T, U&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;void accept(T t, U u)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;객체 T와 U를 받아 소비&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;DoubleConsumer&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;void accept(double value)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;double 값을 받아 소비&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;IntConsumer&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;void accept(int value)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;int 값을 받아 소비&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;LongConsumer&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;void accept(long value)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;long 값을 받아 소비&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;ObjDoubleConsumer&amp;lt;T&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;void accept(T t, double value)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;객체 T와 double 값을 받아 소비&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;ObjIntConsumer&amp;lt;T&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;void accept(T t, int value)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;객체 T와 int 값을 받아 소비&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;ObjLongConsumer&amp;lt;T&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;void accept(T t, long value)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;객체 T와 long 값을 받아 소비&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1666090774626&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {

    // 객체 T를 받아 출력하는 함수 정의
    Consumer&amp;lt;String&amp;gt; c1 = t -&amp;gt; System.out.println(&quot;입력값 : &quot;+ t);
    c1.accept(&quot;홍길동&quot;);

    // 객체 T와 U를 받아 출력하는 함수 정의
    BiConsumer&amp;lt;String, Integer&amp;gt; c2 =  (a, b) -&amp;gt; System.out.println(&quot;입력값1 : &quot;+ a+ &quot;, 입력값2 : &quot;+ b);
    c2.accept(&quot;홍길동&quot;, 100);

    // int 값을 받아 출력하는 함수 정의
    IntConsumer c3 = a -&amp;gt; System.out.println(&quot;입력값 : &quot;+ a);
    c3.accept(100);

    // double 값을 받아 출력하는 함수 정의
    DoubleConsumer c4 = a -&amp;gt; System.out.println(&quot;입력값 : &quot;+ a);
    c4.accept(100.01);

    // long 값을 받아 출력하는 함수 정의
    LongConsumer c5 = a -&amp;gt; System.out.println(&quot;입력값 : &quot;+ a);
    c5.accept(2100000000);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;437&quot; data-origin-height=&quot;144&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfyRtq/btrOZazUYN3/CHDkavzEfb711ET37FhtAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfyRtq/btrOZazUYN3/CHDkavzEfb711ET37FhtAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfyRtq/btrOZazUYN3/CHDkavzEfb711ET37FhtAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfyRtq%2FbtrOZazUYN3%2FCHDkavzEfb711ET37FhtAk%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; alt=&quot;java-Consumer&quot; loading=&quot;lazy&quot; width=&quot;437&quot; height=&quot;144&quot; data-origin-width=&quot;437&quot; data-origin-height=&quot;144&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만일 &lt;b&gt;3개 이상의 매개변수&lt;/b&gt;를 받는 람다 함수를 만들고 싶다면, 직접 인터페이스를 정의해서 사용하여야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1666091302599&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface TripleConsumer&amp;lt;T, U, K&amp;gt; {
    void accept(T t, U u, K k);
}

public class Main {
    public static void main(String[] args) {

        TripleConsumer&amp;lt;String, Double, Integer&amp;gt; c1 = (t, u, k) -&amp;gt; {
        	System.out.printf(&quot;%s + %f + %d&quot;, t, u, k);
        };
        
        c1.accept(&quot;안녕&quot;, 101.23, 999);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;&lt;span&gt;Supplier 인터페이스&lt;/span&gt;&lt;/b&gt;&lt;/span&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;/li&gt;
&lt;li&gt;실행 메서드 : &lt;s&gt;getXXX()&lt;/s&gt;&lt;/li&gt;
&lt;li&gt;생산(supply)한다는 말은 데이터를 반환(공급) 한다는 뜻으로 보면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;인터페이스 형태&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;내용&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Supplier&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td&gt;T형 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;XXX&lt;/span&gt;Supplier&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;XXX&lt;/span&gt;형 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Supplier 종류&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; width=&quot;100%&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;인터페이스 명&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;추상 메소드&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;설명&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;Supplier&amp;lt;T&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;T get()&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;T 객체를 리턴&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;BooleanSupplier&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;Boolean getAsBoolean()&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;Boolean 값을 리턴&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;DoubleSupplier&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;double getAsDouble()&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;double 값을 리턴&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;IntSupplier&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;int getAsInt()&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;int 값을 리턴&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;LongSupplier&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;long getAsLong()&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;long 값을&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;리턴&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1674118302859&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {

    // T 객체를 리턴하는 함수 정의
    Supplier&amp;lt;Object&amp;gt; supplier = () -&amp;gt; new Object();
    System.out.println(supplier.get());

    // Boolean 값을 리턴하는 함수 정의
    BooleanSupplier booleanSup =  () -&amp;gt; true;
    System.out.println(booleanSup.getAsBoolean());

    // int 값을 리턴하는 함수 정의
    IntSupplier intSup = () -&amp;gt; {
        int num = (int) (Math.random() * 6) + 1;
        return num;
    };
    System.out.println(&quot;주사위 랜덤 숫자 : &quot; + intSup.getAsInt());

    // double 값을 리턴하는 함수 정의
    DoubleSupplier doubleSup = () -&amp;gt; 1.0;
    System.out.println(doubleSup.getAsDouble());

    // long 값을 리턴하는 함수 정의
    LongSupplier longSup = () -&amp;gt; 1L;
    System.out.println(longSup.getAsLong());
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;245&quot; data-origin-height=&quot;122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIfeYH/btrWJdbcgUH/mA3fRBTDd6zCwKljdbIYR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIfeYH/btrWJdbcgUH/mA3fRBTDd6zCwKljdbIYR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIfeYH/btrWJdbcgUH/mA3fRBTDd6zCwKljdbIYR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIfeYH%2FbtrWJdbcgUH%2FmA3fRBTDd6zCwKljdbIYR0%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; alt=&quot;Supplier-inteface&quot; loading=&quot;lazy&quot; width=&quot;245&quot; height=&quot;122&quot; data-origin-width=&quot;245&quot; data-origin-height=&quot;122&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Function &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;인터페이스&lt;/span&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;/li&gt;
&lt;li&gt;실행 메서드 : &lt;s&gt;applyXXX()&lt;/s&gt;&lt;/li&gt;
&lt;li&gt;매핑 한다는 말은, 예를들어 여러 데이터 항목들이 들은 객체에서 특정 타입 값을 추출하거나 혹은 다른 타입으로 변환하는 작업에 사용한다고 보면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;인터페이스 형태&lt;/td&gt;
&lt;td&gt;내용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Function&amp;lt;T, R&amp;gt;&lt;/td&gt;
&lt;td&gt;T 받아서 R 리턴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BiFunction&amp;lt;T,&amp;nbsp;U,&amp;nbsp;R&amp;gt;&lt;/td&gt;
&lt;td&gt;T, U 받아서 R 리턴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;XXX&lt;/span&gt;Function&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;XXX&lt;/span&gt; 받아서 T 리턴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;XXX&lt;/span&gt;to&lt;span style=&quot;color: #8a3db6;&quot;&gt;YYY&lt;/span&gt;Function&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;XXX&lt;/span&gt; 받아서 &lt;span style=&quot;color: #8a3db6;&quot;&gt;YYY&lt;/span&gt; 리턴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;to&lt;span style=&quot;color: #6164c6;&quot;&gt;XXX&lt;/span&gt;Function&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td&gt;T 받아서&lt;span style=&quot;color: #6164c6;&quot;&gt; XXX&lt;/span&gt; 리턴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;to&lt;span style=&quot;color: #6164c6;&quot;&gt;XXX&lt;/span&gt;BiFunction&amp;lt;T, U&amp;gt;&lt;/td&gt;
&lt;td&gt;T, U 받아서 &lt;span style=&quot;color: #6164c6;&quot;&gt;XXX&lt;/span&gt; 리턴&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;T와 U는 매개변수 타입이고 R을 리턴(return) 타입 기호하고 보면 된다.&lt;/blockquote&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;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Function 종류&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 300px;&quot; border=&quot;1&quot; width=&quot;100%&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;인터페이스 명&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;추상 메소드&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;설명&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;Function&amp;lt;T, R&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;R apply(T t)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;객체 T를 객체 R로 매핑&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;BiFunction&amp;lt;T, U, R&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;R apply(T t, U u)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;객체 T와 U를 객체 R로 매핑&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;DoubleFunction&amp;lt;R&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;R apply(double value)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;double을 객체 R로 매핑&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;IntFunction&amp;lt;R&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;R apply(int value)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;int를 객체 R로 매핑&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;IntToDoubleFunction&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;double applyAsDouble(int value)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;int를 double로 매핑&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;IntToLongFunction&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;long applyAsLong(int value)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;int를 long으로 매핑&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;LongToDoubleFunction&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;double applyAsDouble(long value)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;long을 double로 매핑&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;LongToIntFunction&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;int applyAsInt(long value)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;long을 int로 매핑&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;ToDoubleBiFunction&amp;lt;T, U&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;double applyAsDouble(T t, U u)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;객체 T와 U를 double로 매핑&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;ToDoubleFunction&amp;lt;T&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;double applyAsDouble(T value)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;객체 T를 double로 매핑&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;ToIntBiFunction&amp;lt;T, U&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;int applyAsInt(T t, U u)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;객체 T와 U를 int로 매핑&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;ToIntFunction&amp;lt;T&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;int applyAsInt(T t)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;객체 T를 int로 매핑&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;ToLongBiFunction&amp;lt;T, U&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;long applyAsLong(T t, U u)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;객체 T와 U를 long으로 매핑&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;ToLongFunction&amp;lt;T&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;long applyAsLong(T t)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;객체 T를 long으로 매핑&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1674120807381&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {

    // int형을 String으로 타입 변환하는 함수 정의
    Function&amp;lt;Integer, String&amp;gt; intToStr = t -&amp;gt; String.valueOf(t);
    String str = intToStr.apply(100);

    // String을 int형으로 타입 변환하는 함수 정의
    ToIntFunction&amp;lt;String&amp;gt; strToInt = t -&amp;gt; Integer.parseInt(t);
    int num = strToInt.applyAsInt(&quot;100&quot;);

    // int형을 double형으로 타입 변환하는 함수 정의
    IntToDoubleFunction intToDouble = t -&amp;gt; (double) t;
    double d = intToDouble.applyAsDouble(100);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;108&quot; data-origin-height=&quot;74&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTVWri/btrWIOjlPZk/pdBquqb5EFpMTTmewu2dgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTVWri/btrWIOjlPZk/pdBquqb5EFpMTTmewu2dgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTVWri/btrWIOjlPZk/pdBquqb5EFpMTTmewu2dgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTVWri%2FbtrWIOjlPZk%2FpdBquqb5EFpMTTmewu2dgk%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; alt=&quot;Function-interface&quot; loading=&quot;lazy&quot; width=&quot;108&quot; height=&quot;74&quot; data-origin-width=&quot;108&quot; data-origin-height=&quot;74&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1674128117725&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Student {
    private String name;

    int korean_score;
    int english_score;
    int math_score;

    Student(String name, int korean_score, int english_score, int math_score) {
        this.name = name;
        this.korean_score = korean_score;
        this.english_score = english_score;
        this.math_score = math_score;
    }

    String getName() {
        return name;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1674128558025&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {

    List&amp;lt;Student&amp;gt; list = List.of(
            new Student(&quot;홍길동&quot;, 99, 12, 45),
            new Student(&quot;임꺽정&quot;, 76, 20, 8),
            new Student(&quot;고담덕&quot;, 36, 50, 33),
            new Student(&quot;김좌진&quot;, 77, 89, 91)
    );

    // 각 학생의 이름 가져오는 함수 정의 (매개타입 : Student 객체, 리턴타입 : String형)
    Function&amp;lt;Student, String&amp;gt; getNameFunc = (s) -&amp;gt; s.getName();

    // 각 학생의 교과목 평균 계산하고 가져오는 함수 정의 (매개타입 : Student 객체, 리턴타입 : int형)
    ToDoubleFunction&amp;lt;Student&amp;gt; getScoreFunc = (s) -&amp;gt; {
        int sum = s.korean_score + s.english_score + s.korean_score;
        double avg = sum / 3.0;
        return avg;
    };

    for (Student student : list) {
        String name = getNameFunc.apply(student);
        double avg = getScoreFunc.applyAsDouble(student);
        System.out.printf(&quot;%s 평균 점수 : %f\n&quot;, name, avg);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;233&quot; data-origin-height=&quot;100&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bod3Zz/btrWJo5cCeH/c5ATpBaHIa7pXJQLyL17K0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bod3Zz/btrWJo5cCeH/c5ATpBaHIa7pXJQLyL17K0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bod3Zz/btrWJo5cCeH/c5ATpBaHIa7pXJQLyL17K0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbod3Zz%2FbtrWJo5cCeH%2Fc5ATpBaHIa7pXJQLyL17K0%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; alt=&quot;Function-interface&quot; loading=&quot;lazy&quot; width=&quot;233&quot; height=&quot;100&quot; data-origin-width=&quot;233&quot; data-origin-height=&quot;100&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Operator 인터페이스&lt;/b&gt;&lt;/span&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;/li&gt;
&lt;li&gt;실행 메서드 : &lt;s&gt;applyXXX()&lt;/s&gt;&lt;/li&gt;
&lt;li&gt;Function과 비슷하지만, 매개값을 리턴값으로 매핑(타입 변환)하는 역할보다는 매개값을 이용해서 연산을 수행한 후 동일한 타입으로 리턴값을 제공하는 역할에 초점이 가있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;인터페이스 형태&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;내용&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UnaryOprater&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td&gt;T타입 연산하고 리턴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BinaryOperator&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td&gt;T타입 연산하고 리턴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;XXX&lt;/span&gt;UnaryOperator&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;XXX&lt;/span&gt; 타입 1개 연산&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;XXX&lt;/span&gt;BinaryOperator&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;XXX&lt;/span&gt; 타입 2개 연산&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Unary는 단항(연산이 1개) 이고, Binary는 이항(연산이 2개) 이라는 뜻이다.&lt;/blockquote&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;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Operator 종류&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 213px;&quot; border=&quot;1&quot; width=&quot;100%&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;&amp;nbsp;인터페이스 명&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;&amp;nbsp;추상 메소드&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;&amp;nbsp;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 37px;&quot;&gt;
&lt;td style=&quot;height: 37px;&quot;&gt;&amp;nbsp;UnaryOperator&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 37px;&quot;&gt;&amp;nbsp;T apply(T t)&lt;br /&gt;&amp;nbsp;Function&amp;lt;T, R&amp;gt;의 하위 인터페이스&lt;/td&gt;
&lt;td style=&quot;height: 37px;&quot;&gt;&amp;nbsp;T를 연산한 후 R 리턴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 54px;&quot;&gt;
&lt;td style=&quot;height: 54px;&quot;&gt;&amp;nbsp;BinaryOperator&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;height: 54px;&quot;&gt;&amp;nbsp;T apply(T t, T u)&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;BiFunction&amp;lt;T, U, R&amp;gt;의 하위 인터페이스&lt;/td&gt;
&lt;td style=&quot;height: 54px;&quot;&gt;&amp;nbsp;T와 U를 연산 후 R 리턴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;nbsp;DoubleUnaryOperator&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;nbsp;double applyAsDouble(double)&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;nbsp;한 개의 double을 연산&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;nbsp;DoubleBinaryOperator&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;nbsp;double applyAsDouble(double, double)&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;nbsp;두 개의 double을 연산&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;nbsp;IntUnaryOperator&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;nbsp;int applyAsInt(int)&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;nbsp;한 개의 int를 연산&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;nbsp;IntBinaryOperator&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;nbsp;int applyAsInt(int, int)&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;nbsp;두 개의 int를 연산&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;nbsp;LongUnarayOperator&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;nbsp;long applyAsLong(long)&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;nbsp;한 개의 long을 연산&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;nbsp;LongBinaryOperator&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;nbsp;long applyAsLong(long, long)&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;nbsp;두 개의 long을 연산&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1674179511124&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Operation {
    static int calculate(int[] arr, IntBinaryOperator o) {
        int result = arr[0];

        for (int i = 1; i &amp;lt; arr.length; i++) {
            result = o.applyAsInt(result, arr[i]);
        }

        return result;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1674179508597&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {

    int[] numbers = {3, 1, 7, 6, 5};

    // 배열 요소의 모든 합 구하기
    int sum = Operation.calculate(numbers, (x, y) -&amp;gt; {
        return x + y;
    });
    System.out.println(sum);

    // 배열 요소의 모든 곱 구하기
    int mul = Operation.calculate(numbers, (x, y) -&amp;gt; {
        return x * y;
    });
    System.out.println(mul);

    // 배열 요소중 가장 큰 수 구하기
    int max = Operation.calculate(numbers, (x, y) -&amp;gt; {
        int tmp;

        if(x &amp;gt; y)
            tmp = x;
        else
            tmp = y;

        return tmp;
    });
    System.out.println(max);

    // 배열 요소중 가장 작은 수 구하기
    int min = Operation.calculate(numbers, (x, y) -&amp;gt; {
        int tmp;

        if(x &amp;lt; y)
            tmp = x;
        else
            tmp = y;

        return tmp;
    });
    System.out.println(min);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;153&quot; data-origin-height=&quot;99&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CNbP7/btrWNS4ZErL/1ZiT8gafubUJUX6JkmMhM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CNbP7/btrWNS4ZErL/1ZiT8gafubUJUX6JkmMhM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CNbP7/btrWNS4ZErL/1ZiT8gafubUJUX6JkmMhM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCNbP7%2FbtrWNS4ZErL%2F1ZiT8gafubUJUX6JkmMhM0%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; alt=&quot;operator-interface&quot; loading=&quot;lazy&quot; width=&quot;153&quot; height=&quot;99&quot; data-origin-width=&quot;153&quot; data-origin-height=&quot;99&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Predicate 인터페이스&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;역할 : 매개값을 받고 true / false 리턴&lt;/li&gt;
&lt;li&gt;&lt;span&gt;실행&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;메서드 : &lt;s&gt;test()&lt;/s&gt;&lt;s&gt;&lt;/s&gt;&lt;/li&gt;
&lt;li&gt;매개값을 받아 참/거짓을 단정(predicate) 한다고 생각하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;인터페이스 형태&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;내용&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Predicate&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td&gt;T 를 받아 boolean 리턴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BiPredicate&amp;lt;T, U&amp;gt;&lt;/td&gt;
&lt;td&gt;T, U를 받아 boolean 리턴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;XXX&lt;/span&gt;Predicate&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;XXX&lt;/span&gt;를 받아 boolean 리턴&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Predicate 종류&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 120px;&quot; border=&quot;1&quot; width=&quot;100%&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;인터페이스 명&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;추상 메소드&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;설명&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;Predicate&amp;lt;T&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;Boolean test(T t)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;객체 T를 조사&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;BiPredicate&amp;lt;T, U&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;Boolean test(T t, U u)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;객체 T와 U를 비교 조사&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;DoublePredicate&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;Boolean test(double value)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;double 값을 조사&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;IntPredicate&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;Boolean test(int value)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;int 값을 조사&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;LongPredicate&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;Boolean test(long value)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;long 값을 조사&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1674130903680&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Student {
    String name;
    int score;

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1674130918680&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {

    List&amp;lt;Student&amp;gt; list = List.of(
            new Student(&quot;홍길동&quot;, 99),
            new Student(&quot;임꺽정&quot;, 76),
            new Student(&quot;고담덕&quot;, 36),
            new Student(&quot;김좌진&quot;, 77)
    );

    // int형 매개값을 받아 특정 점수 이상이면 true 아니면 false 를 반환하는 함수 정의
    IntPredicate scoring = (t) -&amp;gt; {
        return t &amp;gt;= 60;
    };

    for (Student student : list) {
        String name = student.name;
        int score = student.score;
		
        // 함수 실행하여 참 / 거짓 값 얻기
        boolean pass = scoring.test(score);
        
        if(pass) {
            System.out.println(name + &quot;님 &quot; + score + &quot;점은 국어 합격입니다.&quot;);
        } else {
            System.out.println(name + &quot;님 &quot; + score + &quot;점은 국어 불합격입니다.&quot;);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;271&quot; data-origin-height=&quot;102&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eNDuAV/btrWJ9Nhr0R/osbkmGPG3wbb5MSLF2Zuhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eNDuAV/btrWJ9Nhr0R/osbkmGPG3wbb5MSLF2Zuhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eNDuAV/btrWJ9Nhr0R/osbkmGPG3wbb5MSLF2Zuhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeNDuAV%2FbtrWJ9Nhr0R%2FosbkmGPG3wbb5MSLF2Zuhk%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; alt=&quot;Predicate-interface&quot; loading=&quot;lazy&quot; width=&quot;271&quot; height=&quot;102&quot; data-origin-width=&quot;271&quot; data-origin-height=&quot;102&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;style1&quot; /&gt;
&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Function 합성&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중고등학생때 수학 시간에 &lt;s&gt;f(x)&lt;/s&gt; 함수와 &lt;s&gt;g(x)&lt;/s&gt; 함수가 있을 때, 이 두 함수를 합성하여 &lt;s&gt;f(g(x))&lt;/s&gt; 라는 합성 함수를 다뤄본 기억이 어렴풋이나마 있을 것이다. &lt;s&gt;f(g(x))&lt;/s&gt; 는&amp;nbsp;&lt;s&gt;g(x)&lt;/s&gt;의 결과를 다시 &lt;s&gt;f(x)&lt;/s&gt; 함수의 인자로 넣어준 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 두 람다 함수를 연결하여 합성시킬 수 있는데, 이 합성 시키는 메서드를 자바에서 함수형 인터페이스의 디폴트 메서드로서 제공한다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; width=&quot;&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 67.9069%;&quot;&gt;&lt;b&gt;인터페이스 메서드&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 31.9768%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 67.9069%;&quot;&gt;default &amp;lt;V&amp;gt; Function &amp;lt;T, V&amp;gt; andThen (Function &amp;lt;? super R, ? extends V&amp;gt; after);&lt;/td&gt;
&lt;td style=&quot;width: 31.9768%;&quot;&gt;f(g(x)) 합성함수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 67.9069%;&quot;&gt;default &amp;lt;V&amp;gt; Function &amp;lt;V, R&amp;gt; compose(Function &amp;lt;? super V, ? extends T&amp;gt; before);&lt;/td&gt;
&lt;td style=&quot;width: 31.9768%;&quot;&gt;g(f(x)) 합성함수 (andThen의 반대)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 67.9069%;&quot;&gt;static &amp;lt;T&amp;gt; Function&amp;lt;T, T&amp;gt; identity();&lt;/td&gt;
&lt;td style=&quot;width: 31.9768%;&quot;&gt;항등함수 (자기 자신 반환)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;744&quot; data-origin-height=&quot;536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nzn0X/btrWJUDgui3/6UPdK9soWUdZqhnUbg28gK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nzn0X/btrWJUDgui3/6UPdK9soWUdZqhnUbg28gK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nzn0X/btrWJUDgui3/6UPdK9soWUdZqhnUbg28gK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fnzn0X%2FbtrWJUDgui3%2F6UPdK9soWUdZqhnUbg28gK%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; alt=&quot;자바-인터페이스-합성&quot; loading=&quot;lazy&quot; width=&quot;744&quot; height=&quot;536&quot; data-origin-width=&quot;744&quot; data-origin-height=&quot;536&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;합성 함수는 Function 인터페이스 뿐만 아니라 Consumer 이나 Operator 인터페이스도 존재한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;119&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zLHlO/btrWJIiMkrT/AnntKWwtGz00wTquLVPMg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zLHlO/btrWJIiMkrT/AnntKWwtGz00wTquLVPMg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zLHlO/btrWJIiMkrT/AnntKWwtGz00wTquLVPMg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzLHlO%2FbtrWJIiMkrT%2FAnntKWwtGz00wTquLVPMg1%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; alt=&quot;자바-인터페이스-합성&quot; loading=&quot;lazy&quot; width=&quot;550&quot; height=&quot;119&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;119&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;&lt;b&gt;람다 합성 사용 예시 1&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 수학 함수를 코드로 표현해 보았다. 숫자를 받으면 4를 빼는 함수 f(x) 와 숫자를 받으면 두배 곱해주는 함수 g(x) 를 람다표현식으로 선언하였다. 그리고 이 둘을 andThen 과 compose로 합성하여 사용하면 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1674187117313&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {

    Function&amp;lt;Integer, Integer&amp;gt; f = num -&amp;gt; (num - 4); // f(x)
    Function&amp;lt;Integer, Integer&amp;gt; g = num -&amp;gt; (num * 2); // g(x)

    // f(g(x))
    int a = f.andThen(g).apply(10);
    System.out.println(a); // (10 - 4) * 2 = 12

    // g(f(x)) - andThen을 반대로 해석하면 된다
    int b = f.compose(g).apply(10);
    System.out.println(b); // 10 * 2 - 4 = 16

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;andThen 과 compose 의 차이는 간단하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;f.andThen(g)&lt;/s&gt; 를 수행하면 f 함수를 실행한 결과 값을 다시 g 함수의 인자로 전달하여 결과를 얻게 된다. 단, f 함수의 리턴 타입이 g 함수의 매개변수 타입과 호환되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;f.compose(g)&lt;/s&gt; 를 수행하면 g 함수를 실행한 결과 값을 다시 f 함수의 인자로 전달하여 결과를 얻게 된다. 즉, andThen의 반대 버전이라고 보면 된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;즉, &lt;s&gt;x.andThen(y)&lt;/s&gt; 는 &lt;s&gt;y.compose(x)&lt;/s&gt; 와 동일하다고 보면 된다.&lt;/blockquote&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;람다 합성 사용 예시 2&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 Member 클래스와 Address 클래스가 있고, Member 클래스에서 Address 객체를 &lt;a href=&quot;https://inpa.tistory.com/entry/OOP-%F0%9F%92%A0-%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5%EC%9D%98-%EC%83%81%EC%86%8D-%EB%AC%B8%EC%A0%9C%EC%A0%90%EA%B3%BC-%ED%95%A9%EC%84%B1Composition-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;합성(composition)&lt;/a&gt; 하여 가지고 있다. 이 객체끼리 합성된 관계를 합성 함수를 통해 멤버의 도시 주소값을 불러오는 간단한 예제이다.&lt;/p&gt;
&lt;pre id=&quot;code_1674187013205&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Member {
    private String name;
    private Address address; // Address 객체를 합성(composition)

    public Member(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    public String getName() { return name; }

    public Address getAddress() { return address; }
}

class Address {
    private String country;
    private String city;

    public Address(String country, String city) {
        this.country = country;
        this.city = city;
    }

    public String getCountry() { return country; }

    public String getCity() { return city; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1674188154023&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {

    Member member = new Member(&quot;홍길동&quot;, new Address(&quot;조선&quot;, &quot;한양&quot;));

    // Member 매개타입과 Address 리턴타입
    Function&amp;lt;Member, Address&amp;gt; f = x -&amp;gt; x.getAddress(); // Address 객체를 얻기

    // Address 매개타입과 String 리턴타입
    Function&amp;lt;Address, String&amp;gt; g = x -&amp;gt; x.getCity(); // city 문자열 얻기

    // f(g(x))
    Function&amp;lt;Member, String&amp;gt; fg = f.andThen(g);
    String city = fg.apply(member); // Address 객체를 얻고(f 실행), Address 객체에서 city 필드값을 얻기(g 실행)
    System.out.println(&quot;거주 도시 : &quot; + city);

    fg = g.compose(f);
    city = fg.apply(member);
    System.out.println(&quot;거주 도시 : &quot; + city);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;152&quot; data-origin-height=&quot;53&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dcM7zk/btrWORSJzPE/QCTOz0h30P65IgS1hOMfDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dcM7zk/btrWORSJzPE/QCTOz0h30P65IgS1hOMfDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dcM7zk/btrWORSJzPE/QCTOz0h30P65IgS1hOMfDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdcM7zk%2FbtrWORSJzPE%2FQCTOz0h30P65IgS1hOMfDK%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; alt=&quot;Function 합성&quot; loading=&quot;lazy&quot; width=&quot;152&quot; height=&quot;53&quot; data-origin-width=&quot;152&quot; data-origin-height=&quot;53&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Predicate 결합&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Predicate 함수형 인터페이스는 참 / 거짓 값을 리턴하는 함수를 다룬다. 즉, true / false 조건식에 대하여 이들을 결합하여 and 연산, or 연산을 행한다고 보면 된다.&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; width=&quot;&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 53.8372%;&quot;&gt;&lt;b&gt;&lt;span&gt;&amp;nbsp;인터페이스 메소드&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 46.0466%;&quot;&gt;&lt;b&gt;&lt;span&gt;설명&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 53.8372%;&quot;&gt;&lt;span&gt;default Predicate&amp;lt;T&amp;gt; and (Predicate&amp;lt;? super T&amp;gt; other)&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 46.0466%;&quot;&gt;&lt;span&gt;and 연산&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 53.8372%;&quot;&gt;default Predicate&amp;lt;T&amp;gt; or (Predicate&amp;lt;? super T&amp;gt; other)&lt;/td&gt;
&lt;td style=&quot;width: 46.0466%;&quot;&gt;or 연산&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 53.8372%;&quot;&gt;default Predicate&amp;lt;T&amp;gt; negate()&lt;/td&gt;
&lt;td style=&quot;width: 46.0466%;&quot;&gt;역 부정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 53.8372%;&quot;&gt;static &amp;lt;T&amp;gt; Predicate&amp;lt;T&amp;gt; isEqual(Object targetRef)&lt;/td&gt;
&lt;td style=&quot;width: 46.0466%;&quot;&gt;객체 비교&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;662&quot; data-origin-height=&quot;845&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4ypEL/btrWM90cQIP/mwyHHPZiekV1gtGZiMUsUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4ypEL/btrWM90cQIP/mwyHHPZiekV1gtGZiMUsUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4ypEL/btrWM90cQIP/mwyHHPZiekV1gtGZiMUsUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4ypEL%2FbtrWM90cQIP%2FmwyHHPZiekV1gtGZiMUsUK%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; alt=&quot;Predicate 결합&quot; loading=&quot;lazy&quot; width=&quot;662&quot; height=&quot;845&quot; data-origin-width=&quot;662&quot; data-origin-height=&quot;845&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;&lt;b&gt;람다 결합 사용 예시&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건문을 구성할때 &lt;s&gt;if(x &amp;gt; 10 &amp;amp;&amp;amp; x &amp;lt; 20)&lt;/s&gt; 이런식으로 조건 연산자를 이용해 범위를 구성해본적이 있을 것이다. 이것을 람다 함수로 표현한 것이라고 보면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1674189950728&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {

    Predicate&amp;lt;Integer&amp;gt; greater = x -&amp;gt; x &amp;gt; 10;
    Predicate&amp;lt;Integer&amp;gt; less = x -&amp;gt; x &amp;lt; 20;

    // x &amp;gt; 10 &amp;amp;&amp;amp; x &amp;lt; 20
    Predicate&amp;lt;Integer&amp;gt; between = greater.and(less); 
    System.out.println(between.test(15)); // true

    // x &amp;gt; 10 || x &amp;lt; 20
    Predicate&amp;lt;Integer&amp;gt; all = greater.or(less); 
    System.out.println(all.test(5)); // true

    // x &amp;lt;= 10
    Predicate&amp;lt;Integer&amp;gt; negate = greater.negate(); 
    System.out.println(negate.test(50)); // false
}&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;s&gt;isEqual()&lt;/s&gt; 정적 인터페이스 메소드는 입력값으로 받은 객체와 같은지 판단해주는 메소드 이다. 그냥 &lt;s&gt;equals()&lt;/s&gt; 를 람다 함수로 표현한 것이라고 보면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1674190378391&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    // 함수의 인자로 들어온 문자열이 &quot;홍길동&quot; 인지 판별해주는 함수
    Predicate&amp;lt;String&amp;gt; checkMyName = Predicate.isEqual(&quot;홍길동&quot;);

    System.out.println(checkMyName.test(&quot;임꺽정&quot;)); // false
    System.out.println(checkMyName.test(&quot;홍길동&quot;)); // true
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&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;a href=&quot;https://inpa.tistory.com/entry/JCF-%F0%9F%A7%B1-Collections-Framework-%EC%A2%85%EB%A5%98-%EC%B4%9D%EC%A0%95%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;컬렉션 프레임워크&lt;/a&gt;에 함수형 인터페이스와 함께 사용할 수 있는 메서드도 별도로 지원한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1071&quot; data-origin-height=&quot;451&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/P5YAa/btrWKSLLro3/bTt1vvKDMlW8TgKBM8gLcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/P5YAa/btrWKSLLro3/bTt1vvKDMlW8TgKBM8gLcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/P5YAa/btrWKSLLro3/bTt1vvKDMlW8TgKBM8gLcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FP5YAa%2FbtrWKSLLro3%2FbTt1vvKDMlW8TgKBM8gLcK%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; alt=&quot;collection-functional-interface&quot; loading=&quot;lazy&quot; width=&quot;1071&quot; height=&quot;451&quot; data-origin-width=&quot;1071&quot; data-origin-height=&quot;451&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; width=&quot;&quot; data-ke-style=&quot;style12&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.4883%;&quot; rowspan=&quot;1&quot;&gt;&lt;b&gt;인터페이스&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 38.8372%;&quot; rowspan=&quot;1&quot;&gt;&lt;b&gt;메서드&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot; rowspan=&quot;1&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.4883%;&quot; rowspan=&quot;1&quot;&gt;Collection&lt;/td&gt;
&lt;td style=&quot;width: 38.8372%;&quot; rowspan=&quot;1&quot;&gt;boolean removeIf(Predicate&amp;lt;E&amp;gt; filter);&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot; rowspan=&quot;1&quot;&gt;조건에 맞는 엘리먼트를 삭제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.4883%;&quot; rowspan=&quot;1&quot;&gt;List&lt;/td&gt;
&lt;td style=&quot;width: 38.8372%;&quot; rowspan=&quot;1&quot;&gt;void replaceAll(UnaryOperator&amp;lt;E&amp;gt; operator);&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot; rowspan=&quot;1&quot;&gt;모든 엘리먼트에 operator를 적용하여 대체(replace)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.4883%;&quot; rowspan=&quot;1&quot;&gt;Iterable&lt;/td&gt;
&lt;td style=&quot;width: 38.8372%;&quot; rowspan=&quot;1&quot;&gt;void forEach(Consumer&amp;lt;T&amp;gt; action);&lt;/td&gt;
&lt;td style=&quot;width: 47.5581%;&quot; rowspan=&quot;1&quot;&gt;모든 엘리먼트에 action 수행&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; width=&quot;&quot; data-ke-style=&quot;style12&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3721%;&quot;&gt;&lt;b&gt;인터페이스&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.0698%;&quot;&gt;&lt;b&gt;메서드&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 42.4418%;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3721%;&quot;&gt;Map&lt;/td&gt;
&lt;td style=&quot;width: 44.0698%;&quot;&gt;V compute(K key, BiFunction&amp;lt;K, V, V&amp;gt; f);&lt;/td&gt;
&lt;td style=&quot;width: 42.4418%;&quot;&gt;지정된 키에 해당하는 값에 f를 수행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3721%;&quot;&gt;Map&lt;/td&gt;
&lt;td style=&quot;width: 44.0698%;&quot;&gt;V computeIfAbsent(K key, Function&amp;lt;K, V&amp;gt; f);&lt;/td&gt;
&lt;td style=&quot;width: 42.4418%;&quot;&gt;지정된 키가 없으면 f 수행후 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3721%;&quot;&gt;Map&lt;/td&gt;
&lt;td style=&quot;width: 44.0698%;&quot;&gt;V cumputeIfPresent(K key, BiFunction&amp;lt;K, V, V&amp;gt; f)&lt;/td&gt;
&lt;td style=&quot;width: 42.4418%;&quot;&gt;지정된 키가 있을 때, f 수행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3721%;&quot;&gt;Map&lt;/td&gt;
&lt;td style=&quot;width: 44.0698%;&quot;&gt;V merge(K key, V value, BiFunction&amp;lt;V, V, V&amp;gt; f);&lt;/td&gt;
&lt;td style=&quot;width: 42.4418%;&quot;&gt;모든 엘리먼트에 Merge 작업 수행, 키에 해당하는 값이 있으면 f 수행해서 병합후 할당&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3721%;&quot;&gt;Map&lt;/td&gt;
&lt;td style=&quot;width: 44.0698%;&quot;&gt;void forEach(BiConsumer&amp;lt;K, V&amp;gt; action);&lt;/td&gt;
&lt;td style=&quot;width: 42.4418%;&quot;&gt;모든 엘리먼트에 action 수행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3721%;&quot;&gt;Map&lt;/td&gt;
&lt;td style=&quot;width: 44.0698%;&quot;&gt;void replaceAll(BiFunction&amp;lt;K, V, V&amp;gt; f);&lt;/td&gt;
&lt;td style=&quot;width: 42.4418%;&quot;&gt;모든 엘리먼트에 f 수행후&amp;nbsp; 대체&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1674184472704&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    List&amp;lt;Integer&amp;gt; list = Arrays.asList(1, 2, 3, 4, 5);

    // 각 요소에 10을 곱함
    list.replaceAll( (x) -&amp;gt; x * 10 );

    // 리스트는 Iteratble을 상속하니까 사용이 가능
    list.forEach( (x) -&amp;gt; System.out.println(x) );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;115&quot; data-origin-height=&quot;120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MChSZ/btrWKSSwm5L/z7DMLUDREKvp6I4yoYXKxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MChSZ/btrWKSSwm5L/z7DMLUDREKvp6I4yoYXKxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MChSZ/btrWKSSwm5L/z7DMLUDREKvp6I4yoYXKxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMChSZ%2FbtrWKSSwm5L%2Fz7DMLUDREKvp6I4yoYXKxK%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; alt=&quot;collection-functional-interface&quot; loading=&quot;lazy&quot; width=&quot;115&quot; height=&quot;120&quot; data-origin-width=&quot;115&quot; data-origin-height=&quot;120&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;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;함수형 인터페이스 API 사용 정리표&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;span style=&quot;color: #009a87;&quot;&gt;매개변수 타입&lt;/span&gt;을 나타내는 것이고, 각 표의 위쪽 부분은 &lt;span style=&quot;color: #009a87;&quot;&gt;함수 반환 타입&lt;/span&gt;을 나타내는 것이다. 만일 double 형 매개변수를 받아 void 로 행하는 함수형 인터페이스가 무엇인지 찾으려면, 왼쪽에서 &lt;span style=&quot;color: #009a87;&quot;&gt;(double) &amp;rarr;&lt;/span&gt; 을 찾고 표 상단의 &lt;span style=&quot;color: #009a87;&quot;&gt;&amp;rarr; void&lt;/span&gt; 를 매치해보면 바로 DoubleConsumer 인터페이스를 사용한다는 것을 알 수 있게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Java 8 Functional Interface Naming Guide.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;959&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bM5b2c/btrWIytndvw/YHEQjXnu25JAetTjRFoFX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bM5b2c/btrWIytndvw/YHEQjXnu25JAetTjRFoFX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bM5b2c/btrWIytndvw/YHEQjXnu25JAetTjRFoFX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbM5b2c%2FbtrWIytndvw%2FYHEQjXnu25JAetTjRFoFX0%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; alt=&quot;함수형 인터페이스 API 사용 정리표&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;959&quot; data-filename=&quot;Java 8 Functional Interface Naming Guide.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;959&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;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://hbase.tistory.com/78&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://palpit.tistory.com/673&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;http://blog.orfjackal.net/2014/07/java-8-functional-interface-naming-guide.html&lt;/p&gt;</description>
      <category>Language/Java</category>
      <category>LAMBDA</category>
      <category>람다</category>
      <category>람다식</category>
      <category>함수적 인터페이스</category>
      <category>함수형 인터페이스</category>
      <category>함수형 프로그래밍</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/964</guid>
      <comments>https://inpa.tistory.com/entry/%E2%98%95-%ED%95%A8%EC%88%98%ED%98%95-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-API#entry964comment</comments>
      <pubDate>Tue, 28 Mar 2023 09:49:35 +0900</pubDate>
    </item>
    <item>
      <title>☕ 람다 표현식(Lambda Expression) 완벽 정리</title>
      <link>https://inpa.tistory.com/entry/%E2%98%95-Lambda-Expression</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1_S5KkDX11wbKa6suvBPBFJA.webp&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;875&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o71JD/btrQ4WL06iF/2cxGyFLzNkK5C2Wmmdcqqk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o71JD/btrQ4WL06iF/2cxGyFLzNkK5C2Wmmdcqqk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o71JD/btrQ4WL06iF/2cxGyFLzNkK5C2Wmmdcqqk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo71JD%2FbtrQ4WL06iF%2F2cxGyFLzNkK5C2Wmmdcqqk%2Fimg.webp&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; alt=&quot;java-lambda-function&quot; loading=&quot;lazy&quot; width=&quot;1400&quot; height=&quot;875&quot; data-filename=&quot;1_S5KkDX11wbKa6suvBPBFJA.webp&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;875&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;람다 표현식 (Lambda Expression)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;람다 표현식(lambda expression)이란 함수형 프로그래밍을 구성하기 위한 함수식이며, 간단히 말해 자바의 &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;s&gt;int add(int a, int b) {}&lt;/s&gt; 메소드 표현식을, &lt;span style=&quot;color: #ee2323;&quot;&gt;메서드 타입, 메서드 이름, 매개변수 타입, 중괄호, return 문&lt;/span&gt;을 생략하고, &lt;span style=&quot;color: #ee2323;&quot;&gt;화살표 기호&lt;/span&gt;를 넣음으로써 코드를 혁명적으로 함축했음을 볼 수 있다. 이러한 특징으로 람다식을 이름이 없는 함수 &lt;b&gt;익명 함수(anonymous function)&lt;/b&gt;&amp;nbsp;라고도 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_img - 2022-10-18T151500.084 (1).png&quot; data-origin-width=&quot;1054&quot; data-origin-height=&quot;212&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1qJWM/btrWCFSdDQa/JIrn9KFKm4KuBDu1V2auv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1qJWM/btrWCFSdDQa/JIrn9KFKm4KuBDu1V2auv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1qJWM/btrWCFSdDQa/JIrn9KFKm4KuBDu1V2auv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1qJWM%2FbtrWCFSdDQa%2FJIrn9KFKm4KuBDu1V2auv0%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; alt=&quot;java-lambda-function&quot; loading=&quot;lazy&quot; width=&quot;1054&quot; height=&quot;212&quot; data-filename=&quot;edited_img - 2022-10-18T151500.084 (1).png&quot; data-origin-width=&quot;1054&quot; data-origin-height=&quot;212&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1666074550046&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int add(int x, int y) {
    return x + y;
}

// 위의 메서드를 람다 표현식을 이용해 아래와 같이 단축 시킬수 있다. (메서드 반환 타입, 메서드 이름 생략)
(int x, int y) -&amp;gt; {
	return x + y;
};

// 매개변수 타입도 생략 할 수 있다.
(x, y) -&amp;gt; {
	return x + y;
};

// 함수에 리턴문 한줄만 있을 경우 더욱 더 단축 시킬 수 있다. (중괄호, return 생략)
(x, y) -&amp;gt; x + y;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;타입을 생략을 해도 컴파일러가 에러를 띄우지않는 이유는, 컴파일러 나름대로 생략된 타입 위치를 추론하여 동작하게 해주기 때문이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;람다식이 처음에는 생소하게 느껴져 오히려 거부감이 들 수 있다. 하지만 문법이 혁신적으로 간결해 지는 것만큼, 람다식에 익숙해지면 가독성 면에서 큰 장점을 얻게 된다. 특히 컬렉션(Collection)의 요소를 필터링하거나 매핑하여 원하는 결과를 쉽게 얻을 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&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;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;익명 화살표 함수&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;자체가 람다 함수 일종이기 때문이다. 괄호를 생략하거나 등 람다의 기본 문법 체계도 자바와 자바스크리브 둘다 비슷하다. 단지 화살표 모양이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;=&amp;gt;&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;-&amp;gt;&lt;/s&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;pre id=&quot;code_1666167512305&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const MyFunction = {
    print: function() {}
};

MyFunction.print = (str) =&amp;gt; console.log(str);
let myfunc = MyFunction;
myfunc.print(&quot;Hello World&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1666167512305&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface MyFunction {
	void print(String str);
}

public class Main {
    public static void main(String[] args) {
        MyFunction myfunc = (str) -&amp;gt; System.out.println(str);
        myfunc.print(&quot;Hello World&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수에 함수를 담을때, 자바스크립트는 약타입 언어라 타입에 관계없이 자유롭게 받을 수 있지만, 자바 같은 경우 강타입 언어 이기 때문에 &lt;span style=&quot;color: #ee2323;&quot;&gt;반드시&amp;nbsp;함수에 대한 타입&lt;/span&gt;을 선언하여야 한다. 하지만 자바에는 8가지 타입(primitive 타입과 reference 타입) 밖에 없기 때문에 함수 데이터 자체를 담을수 있을만한 자료형이 딱히 적합한 것이 없다. 그래서 자바 개발진들은 인터페이스를 익명 구현 객체 타입으로써, &lt;span style=&quot;color: #ee2323;&quot;&gt;함수를&amp;nbsp;해당 인터페이스 타입&lt;/span&gt;으로 받을 수 있게 설계한 것이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&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;/p&gt;
&lt;pre id=&quot;code_1674018710954&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;MyFunction myfunc = (str) -&amp;gt; System.out.println(str);
myfunc.print(&quot;Hello World&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 람다식도 결국은 객체이다. 정확히 말하면 &lt;span&gt;인터페이스를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EC%9D%B5%EB%AA%85-%ED%81%B4%EB%9E%98%EC%8A%A4Anonymous-Class-%EC%82%AC%EC%9A%A9%EB%B2%95-%EB%A7%88%EC%8A%A4%ED%84%B0%ED%95%98%EA%B8%B0&quot;&gt;익명 클래스&lt;/a&gt;&lt;span&gt;로 구현한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;익명 구현 객체를 짧게 표현한 것 뿐이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;객체-지향-프로그래밍-방식-vs-람다식&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;객체 지향 방식&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;vs&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;람다 표현 방식&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 대해서 기존 자바7에서 표현 했던 객체 지향 방식과 람다 표현 방식을 비교해보며 이해해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 IAdd 라는 인터페이스가 있고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;add()&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;추상 메서드가 있다. 우리는 이 인터페이스를 구현하여 메서드를 정의해 덧셈 기능을 이용할 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에는 인터페이스를 클래스에 implements 하고 오버라이딩하여 사용해 왔다.&lt;/p&gt;
&lt;pre id=&quot;code_1666084564057&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface IAdd {
    int add(int x, int y);
}

class Add implements IAdd {
    public int add(int x, int y) {
        return x + y;
    }
}
        
public class Main {
    public static void main(String[] args) {
        // 생 클래스로 메소드 사용하기
        Add a = new Add();
        
        int result1 = a.add(1, 2);
        System.out.println(result1);
    }
}&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 id=&quot;code_1666084564058&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface IAdd {
    int add(int x, int y);
}

public class Main {
    public static void main(String[] args) {
        // 익명 클래스로 정의해 사용하기 (일회용)
        Iadd a = new IAdd() {
            public int add(int x, int y) {
                return x + y;
            }
        };
        
        int result2 = a.add(1, 2);
        System.out.println(result2);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 람다는 이 익명클래스 코드 부분을 짧게 표현한 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1666084564058&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface IAdd {
    int add(int x, int y);
}

public class Main {
    public static void main(String[] args) {
        // 람다 표현식으로 함축 하기
        IAdd lambda = (x, y) -&amp;gt; { return x + y; }; // 람다식 끝에 세미콜론을 잊지말자
        
        int result3 = lambda.add(1, 2);
        System.out.println(result3);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;454&quot; data-origin-height=&quot;67&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qC36X/btrO4hkB3t7/ylC3LDbNcFxhv6NYOX0K81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qC36X/btrO4hkB3t7/ylC3LDbNcFxhv6NYOX0K81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qC36X/btrO4hkB3t7/ylC3LDbNcFxhv6NYOX0K81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqC36X%2FbtrO4hkB3t7%2FylC3LDbNcFxhv6NYOX0K81%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; alt=&quot;java-lambda-function&quot; loading=&quot;lazy&quot; width=&quot;454&quot; height=&quot;67&quot; data-origin-width=&quot;454&quot; data-origin-height=&quot;67&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;람다식 객체를 콘솔에 출력 해보면, 익명 클래스 표현 형식과 또다른&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;&lt;span style=&quot;background-color: #ffffff; color: #006dd7;&quot;&gt;외부클래스명$$Lambda$번호&lt;/span&gt;&lt;/s&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;와 같은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;독자적인 표현 형식을 지니고 있음을 알 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;602&quot; data-origin-height=&quot;207&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uvWft/btrWwKnOwKs/gVWf26C2Owy1v5wtKUX3VK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uvWft/btrWwKnOwKs/gVWf26C2Owy1v5wtKUX3VK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uvWft/btrWwKnOwKs/gVWf26C2Owy1v5wtKUX3VK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuvWft%2FbtrWwKnOwKs%2FgVWf26C2Owy1v5wtKUX3VK%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; alt=&quot;java-lambda-function&quot; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;207&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;602&quot; data-origin-height=&quot;207&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;즉,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;함수형 인터페이스 란?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수형 인터페이스란 &lt;span style=&quot;color: #ee2323;&quot;&gt;딱 하나의 추상 메소드가 선언된 인터페이스&lt;/span&gt;를 말한다. 위의 IAdd 인터페이스 예제 코드가 바로 함수형 인터페이스 이다. 그리고 람다식은 함수형 인터페이스 안에 정의된 하나의 추상 메소드 선언을 짧게 표현한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생각해보면 람다식 자체가 하나의 메소드를 한줄로 정의하는 표현식이기 때문에, 인터페이스에 두개 이상 추상 메서드가 들어있으면 이를 코드로 겹쳐 표현할 방법이 달리 없기 때문에, 오로지 추상 메소드 한개만 가진 인터페이스가 람다식의 타겟 타입(targe type)이 될 수 있는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, Java 8 버전 부터 이용이 가능한 인터페이스의 final 상수나 default, static, private 메서드는 추상 메서드가 아니기 때문에, 이들 여러개가 인터페이스에 들어있어도 오로지 추상 메서드가 한개이면 함수형 인터페이스로 취급 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1674019297461&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 함수형 인터페이스가 될 수 있다.
interface IAdd {
    int add(int x, int y);
}

// 함수형 인터페이스가 될수 없다.
interface ICalculate {
    int add(int x, int y);
    int min(int x, int y);
}

// 구성요소가 많아도 결국 추상 메서드는 한개이기 때문에 함수형 인터페이스이다.
interface IAdd {
    int add(int x, int y);

    final boolean isNumber = true; // final 상수
    default void print() {}; // 디폴트 메서드
    static void print2() {}; // static 메서드
}&lt;/code&gt;&lt;/pre&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;@FunctionalInterface&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나만의 함수적 인터페이스를 만들 때 두 개 이상의 추상 메소드가 선언되지 않도록 컴파일러가 checking 해주는 기능이 있는데, 인터페이스 선언 시&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;@FunctionalInterface&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;어노테이션을 붙여주게 된다면 두 개 이상의 메소드 선언 시 컴파일 오류를 발생시켜준다. 이는 개발자의 실수를 줄여주는 역할을 한다.&lt;/p&gt;
&lt;div&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;@FunctionalInterface
public interface MyFunctional {
    public void method();
    public void otherMethod(); // 컴파일 오류 발생
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;이밖의 함수형 인터페이스의 개념 원리 및 자바에서 제공하는 함수형 인터페이스 표준 API 종류에 대해서 상세히 이해하고 싶다면 다음 포스팅을 참고하길 바란다.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;figure id=&quot;og_1679965447838&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;☕ 함수형 인터페이스 표준 API 총정리&quot; data-og-description=&quot;함수형 인터페이스 표준 API 함수형 인터페이스(functional interface)는 추상메서드가 1개만 정의된 인터페이스를 통칭하여 일컫는다. 이 인터페이스 형태의 목적은 자바에서 람다 표현식(Lambda Expressi&quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/%E2%98%95-%ED%95%A8%EC%88%98%ED%98%95-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-API&quot; data-og-url=&quot;https://inpa.tistory.com/entry/%E2%98%95-%ED%95%A8%EC%88%98%ED%98%95-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-API&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Q3tSA/hyR352FH8Y/VT4b7UlGdNgwQAJ1nqTsGk/img.jpg?width=800&amp;amp;height=500&amp;amp;face=0_0_800_500,https://scrap.kakaocdn.net/dn/bSU4uj/hyR5xXyjLp/RCvm94QsGClQjFyp2jgQy1/img.jpg?width=800&amp;amp;height=500&amp;amp;face=0_0_800_500,https://scrap.kakaocdn.net/dn/c91a0Q/hyR31eU9vQ/Ha028204O9UO7D17QAeZmk/img.png?width=744&amp;amp;height=536&amp;amp;face=0_0_744_536&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/%E2%98%95-%ED%95%A8%EC%88%98%ED%98%95-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-API&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inpa.tistory.com/entry/%E2%98%95-%ED%95%A8%EC%88%98%ED%98%95-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-API&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Q3tSA/hyR352FH8Y/VT4b7UlGdNgwQAJ1nqTsGk/img.jpg?width=800&amp;amp;height=500&amp;amp;face=0_0_800_500,https://scrap.kakaocdn.net/dn/bSU4uj/hyR5xXyjLp/RCvm94QsGClQjFyp2jgQy1/img.jpg?width=800&amp;amp;height=500&amp;amp;face=0_0_800_500,https://scrap.kakaocdn.net/dn/c91a0Q/hyR31eU9vQ/Ha028204O9UO7D17QAeZmk/img.png?width=744&amp;amp;height=536&amp;amp;face=0_0_744_536');&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;☕ 함수형 인터페이스 표준 API 총정리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;함수형 인터페이스 표준 API 함수형 인터페이스(functional interface)는 추상메서드가 1개만 정의된 인터페이스를 통칭하여 일컫는다. 이 인터페이스 형태의 목적은 자바에서 람다 표현식(Lambda Expressi&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;inpa.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;람다식의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;타입 추론&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;람다식으로 코드를 혁신적으로 줄일 수 있다는 점은 알았다. 그런데 리턴 타입도 파라미터 타입도 없는 람다식을 컴파일러가 이 함수가 어떤 타입 함수인지 알고 문법을 허용하는 것일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 컴파일러 스스로 람다 함수식을 보고 추론하여&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;타입을 유추&lt;/b&gt;하기 때문에 가능한 것이다. 무슨 AI 처럼 추론할 정도는 아니고 사람이 미리 정의해놓은 정의문을 보고 추론해주는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;757&quot; data-origin-height=&quot;390&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cn7U2T/btrO5fmjTWz/nyvTCPpVLFLntKKNrQr3g1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cn7U2T/btrO5fmjTWz/nyvTCPpVLFLntKKNrQr3g1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cn7U2T/btrO5fmjTWz/nyvTCPpVLFLntKKNrQr3g1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcn7U2T%2FbtrO5fmjTWz%2FnyvTCPpVLFLntKKNrQr3g1%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; alt=&quot;java-lambda-function&quot; loading=&quot;lazy&quot; width=&quot;757&quot; height=&quot;390&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;757&quot; data-origin-height=&quot;390&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;a href=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EC%A0%9C%EB%84%A4%EB%A6%ADGenerics-%EA%B0%9C%EB%85%90-%EB%AC%B8%EB%B2%95-%EC%A0%95%EB%B3%B5%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;제네릭(Generics)&lt;/a&gt;을 사용하게 되는데, 컴파일러가 타입을 추론하는 데 필요한 타입 정보 대부분을 제네릭에서 판별하여 얻는다고 보면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 코드를 봐보자. List 자료형을 만들고 리스트의 타입 파라미터를 String으로 지정하였다. 그리고 Collections 클래스의&amp;nbsp;&lt;s&gt;sort&lt;/s&gt;&amp;nbsp;메소드를 불러와 첫번째 매개변수로는 리스트 객체를 두번째 매개변수로는 람다 함수를 전달 하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1674025025755&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List&amp;lt;String&amp;gt; words = Arrays.asList(&quot;aaa&quot;, &quot;bbb&quot;, &quot;ccc&quot;, &quot;ddd&quot;);

        Collections.sort(words, (s1, s2) -&amp;gt; Integer.compare(s1.length(), s2.length()));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 아래의 Collections 클래스의 sort 메서드 정의문을 보면, 람다함수의 함수형 인터페이스 타입은 &lt;s&gt;java.util.Comparator&lt;/s&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;로 지정되어 있으며, 람다의 매개변수 타입은 Comparator 인터페이스의 제네릭 T 타입으로 지정되어 있는걸 볼 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;753&quot; data-origin-height=&quot;565&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oZiNs/btrWyMFKNvI/LumuSR7ajm4qhX2HITxA00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oZiNs/btrWyMFKNvI/LumuSR7ajm4qhX2HITxA00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oZiNs/btrWyMFKNvI/LumuSR7ajm4qhX2HITxA00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoZiNs%2FbtrWyMFKNvI%2FLumuSR7ajm4qhX2HITxA00%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; alt=&quot;java-lambda-function&quot; loading=&quot;lazy&quot; width=&quot;753&quot; height=&quot;565&quot; data-origin-width=&quot;753&quot; data-origin-height=&quot;565&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 형태에서 컴파일러가 타입을 유추하는 순서는 다음과 같이 된다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;sort 메소드의 첫번째 매개변수로 List&amp;lt;String&amp;gt; 형태의 객체가 들어온다.&lt;/li&gt;
&lt;li&gt;첫번째 매개변수의 타입 지정에 의해 sort 메소드의 제네릭 타입 매개변수는 모두 String으로 지정되게 된다.&lt;/li&gt;
&lt;li&gt;따라서 Comparator 인터페이스 제네릭 타입도 String으로 지정되며, 추상 메서드의 매개변수 타입도 String으로 지정된다.&lt;/li&gt;
&lt;li&gt;최종적으로 람다 함수의 타입 구성은 int형 메소드 반환 타입과 String 형 매개변수 타입 두개로 추론되게 된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;831&quot; data-origin-height=&quot;577&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/F1zOo/btrO35rpMbu/Q6mqp2ckR5Ie35SX97eq4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/F1zOo/btrO35rpMbu/Q6mqp2ckR5Ie35SX97eq4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/F1zOo/btrO35rpMbu/Q6mqp2ckR5Ie35SX97eq4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF1zOo%2FbtrO35rpMbu%2FQ6mqp2ckR5Ie35SX97eq4K%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; alt=&quot;java-lambda-function&quot; loading=&quot;lazy&quot; width=&quot;831&quot; height=&quot;577&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;831&quot; data-origin-height=&quot;577&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 개발자 입장에선 코드를 복기할때 람다식의 타입을 위와 같이 유추하기에는 시간이 걸리기에, 상황에 따라 명시적으로 람다식 파라미터에 타입을 기재하기도 한다. 무엇이 좋은지 정답은 없고 상황에 따라 개발자가 결정해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1674025881659&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Collections.sort(words, (String s1, String s2) -&amp;gt; Integer.compare(s1.length(), s2.length()));&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;다양한 람다식의 할당&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;람다식의 가장 큰 특징은 바로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&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;b&gt;메모리 주소가 할당&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;되어 있다. 지금까지 클래스로 메소드를 사용해왔기에 메소드의 메모리 주소를 변수에 할당하는 일은 없었지만, 람다식을 이용하면 함수(실행코드)의 주소를 사용하여 C언어, 파이썬 같은 함수 스타일의 프로그래밍을 작성할 수 있게 된 것이다.&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;람다식 변수 할당&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1666157394599&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface IAdd {
    int add(int x, int y);
}

public class Main {
    public static void main(String[] args) {
        IAdd lambda = (x, y) -&amp;gt; x + y; // 함수를 변수에 할당
        lambda.add(1, 2); // 함수 사용
    }
}&lt;/code&gt;&lt;/pre&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;람다식 매개변수 할당&lt;/b&gt;&lt;/h4&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;pre id=&quot;code_1666157394600&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface IAdd {
    int add(int x, int y);
}

public class Main {
    public static void main(String[] args) {
    	int n = result( (x, y) -&amp;gt; x + y ); // 메소드의 매개변수에 람다식을 전달
        System.out.println(n); // 3
    }
    
    public static int result(IAdd lambda) {
    	return lambda.add(1,2);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 자바의 메소드 같은 경우 변수에 할당하거나 매개변수로 넣거나 리턴값으로 사용하는 행위는 꿈도 못 꾸어 왔을 것이다. 하지만 람다식이 이런식으로 응용이 가능한 이유는 람다는 익명 함수(Anonymous Function)이며, 익명 함수는 모두&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/CS-%F0%9F%91%A8%E2%80%8D%F0%9F%92%BB-%EC%9D%BC%EA%B8%89-%EA%B0%9D%EC%B2%B4first-class-object&quot;&gt;일급 객체&lt;/a&gt;로 취급 되기 때문이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;함수를 매개변수로 전달해서 사용하는 것을 무슨 신박한 기능처럼 소개했지만, 사실 클래스 참조 객체를 넘겨 준 것과 다름이 없다. 왜냐하면 람다식은 익명 구현 객체(익명 클래스)를 심플화 환것이고, 익명 클래스도 결국 클래스 참조 객체이니, 우리가 메소드에 참조 객체를 넘겨 메소드 내에서 객체의 메서드를 사용한 것처럼 별반 다르지 않다.&lt;/blockquote&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;람다식 반환값 할당&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일급 객체의 또다른 특징이라고 말할수 있는 메서드의 반환값을&lt;b&gt;&amp;nbsp;람다함수 자체를 리턴&lt;/b&gt;하도록 지정 해줄 수 있다. 즉, 메서드의 리턴값이 메서드(함수)인 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1666160519178&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface IAdd {
    int add(int x, int y);
}

public class Main {
    public static void main(String[] args) {
        IAdd func = makeFunction(); // 메소드의 반환값이 람다 함수
        int result = func.add(1, 2);
        System.out.println(result); // 3
    }

    public static IAdd makeFunction() {
        return (x, y) -&amp;gt; x + y;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Thread 호출&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바의 쓰레드를 먼저 배우신 독자 분들이라면, 다음과 같은 코드 문법 구성에 대해 익숙할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 신박한 문법이 아니고 결국은 자세히 보면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;new Thread()&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;생성자 안에 매개변수로서 람다식을 넣은 것 뿐이다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;Thread thread = new Thread( () -&amp;gt; {
    for (int i = 0; i &amp;lt; 10; i++) {
        System.out.println(i);
    }
});&lt;/code&gt;&lt;/pre&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;enum을 깔끔하게&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;enum 객체의 확장&lt;span style=&quot;background-color: #ffffff; color: #404040;&quot;&gt;을 조금 더 간결하고 깔끔하게 만들 수도 있다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #404040;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1674102237342&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;[JAVA] ☕ Enum 열거형 타입 문법 &amp;amp; 응용   정리&quot; data-og-description=&quot;Enum 열거 타입 먼저 Enum은 &amp;quot;Enumeration&amp;quot;의 약자다. Enumeration은 &amp;quot;열거, 목록, 일람표&amp;quot; 라는 뜻을 가지고 있으며, 보통 한글로는 열거형이라고 부른다. 즉, 열거형(enum)은 요소, 멤버라 불리는 명명된 값&quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EC%97%B4%EA%B1%B0%ED%98%95Enum-%ED%83%80%EC%9E%85-%EB%AC%B8%EB%B2%95-%ED%99%9C%EC%9A%A9-%EC%A0%95%EB%A6%AC&quot; data-og-url=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EC%97%B4%EA%B1%B0%ED%98%95Enum-%ED%83%80%EC%9E%85-%EB%AC%B8%EB%B2%95-%ED%99%9C%EC%9A%A9-%EC%A0%95%EB%A6%AC&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/lAkWS/hyRltVSORt/ltnjknIIsnkxWK8H1yCbAK/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/ZZMCE/hyRkqsVbY8/U7KPp16EBH5sx05YNsph6k/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/TXZTn/hyRkuvjiRY/kqW28TkgzIy3Ccjrubvn01/img.png?width=1280&amp;amp;height=589&amp;amp;face=0_0_1280_589&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EC%97%B4%EA%B1%B0%ED%98%95Enum-%ED%83%80%EC%9E%85-%EB%AC%B8%EB%B2%95-%ED%99%9C%EC%9A%A9-%EC%A0%95%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EC%97%B4%EA%B1%B0%ED%98%95Enum-%ED%83%80%EC%9E%85-%EB%AC%B8%EB%B2%95-%ED%99%9C%EC%9A%A9-%EC%A0%95%EB%A6%AC&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/lAkWS/hyRltVSORt/ltnjknIIsnkxWK8H1yCbAK/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/ZZMCE/hyRkqsVbY8/U7KPp16EBH5sx05YNsph6k/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/TXZTn/hyRkuvjiRY/kqW28TkgzIy3Ccjrubvn01/img.png?width=1280&amp;amp;height=589&amp;amp;face=0_0_1280_589');&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;[JAVA] ☕ Enum 열거형 타입 문법 &amp;amp; 응용   정리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Enum 열거 타입 먼저 Enum은 &quot;Enumeration&quot;의 약자다. Enumeration은 &quot;열거, 목록, 일람표&quot; 라는 뜻을 가지고 있으며, 보통 한글로는 열거형이라고 부른다. 즉, 열거형(enum)은 요소, 멤버라 불리는 명명된 값&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;inpa.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;pre id=&quot;code_1674102145080&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;enum Operation {
    PLUS(&quot;+&quot;) { 
        public double apply(double x, double y) { return x + y; }
    },
    MINUS(&quot;-&quot;) {
        public double apply(double x, double y) { return x - y; }
    },
    TIMES(&quot;*&quot;) {
        public double apply(double x, double y) { return x * y; }
    },
    DIVIDE(&quot;/&quot;) {
        public double apply(double x, double y) { return x * y; }
    };
    
    private final String symbol;
   
    Operation(String symbol) { this.symbol = symbol; }
    
    @Override public String toString() { return symbol; } 
    public abstract double apply(double x, double y);
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1674102145081&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.function.DoubleBinaryOperator;

enum Operation {
    PLUS(&quot;+&quot;, (x, y) -&amp;gt; x + y),
    MINUS(&quot;-&quot;, (x, y) -&amp;gt; x - y),
    TIMES(&quot;*&quot;, (x, y) -&amp;gt; x * y),
    DIVIDE(&quot;/&quot;, (x, y) -&amp;gt; x / y);

    private final String symbol;
    private final DoubleBinaryOperator op;

    Operation(String symbol, DoubleBinaryOperator op) {
        this.symbol = symbol;
        this.op = op;
    }

    @Override
    public String toString() { return symbol; }

    public double apply(double x, double y) {
        return op.applyAsDouble(x, y);
    }
}

public class Main {
    public static void main(String[] args) {
        // 사용은 아래와 같이
        Operation.PLUS.apply(2, 3);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;576&quot; data-origin-height=&quot;523&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MF92S/btrWCTrzP24/k6mvVXujpRp1ceiYYFd0L1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MF92S/btrWCTrzP24/k6mvVXujpRp1ceiYYFd0L1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MF92S/btrWCTrzP24/k6mvVXujpRp1ceiYYFd0L1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMF92S%2FbtrWCTrzP24%2Fk6mvVXujpRp1ceiYYFd0L1%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; alt=&quot;enum-lambda&quot; loading=&quot;lazy&quot; width=&quot;576&quot; height=&quot;523&quot; data-origin-width=&quot;576&quot; data-origin-height=&quot;523&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;style3&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;/p&gt;
&lt;pre id=&quot;code_1674102264954&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;IAdd func = () -&amp;gt; {});

IAdd func = (IAdd) (() -&amp;gt; {}); // 원래는 양변의 타입이 다르므로 형변환이 필요&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 특징을 이용해 람다식을 모든 클래스의 최상위 클래스인 Object 클래스로 형변환이 가능하다. 이러한 특징이 있다는 정도만 알고 넘어가자.&lt;/p&gt;
&lt;pre id=&quot;code_1674102264954&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface IAdd {
    int add(int x, int y);
}

public class Main {
    public static void main(String[] args) {
        IAdd lambda = (x, y) -&amp;gt; x + y;
        System.out.println(lambda);

        // Object 타입으로 업캐스팅하기 위해선 두번 캐스팅 해주어야 한다
        Object lambda_obj = (Object) (IAdd) ((x, y) -&amp;gt; x + y);
        System.out.println(lambda_obj);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;람다의-한계&quot; 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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;1.&lt;/span&gt; 람다는 문서화를 할 수 없다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;람다 자체는 이름이 없는 함수이기 때문에 메서드나 클래스와 다르게 문서화를 할 수 없다. 그래서 코드 자체로 동작이 명확하게 설명되지 않거나 람다가 길거나 읽기 어렵다면, 쓰지 않는 방향으로 리팩토링하는 것을 고려해야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;2.&lt;/span&gt; 람다는&amp;nbsp;디버깅이 다소 까다롭다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;람다식은 기본적으로 익명 구현 객체 기반이기 때문에, 익명 객체 특성상 디버깅 할때 콜 스택(call stack) 추적이 매우 어려운 단점을 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어 0을 나누는 같은 오류가 나타나는 코드를 일반 for문과 람다식을 이용한 표현을 코드를 실행하면 다음 과 같이 에러 줄 서부터 확연히 다른걸 볼 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1674111908868&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    List&amp;lt;Integer&amp;gt; list = Arrays.asList(1, 2, 3, 4, 5);

    for (Integer i : list) {
        for (int j = 0; j &amp;lt; i; j++) {
            System.out.println(i / j);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;668&quot; data-origin-height=&quot;60&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8PTaN/btrWIYkITsm/M2JPTnrO3okLfIsKemiSBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8PTaN/btrWIYkITsm/M2JPTnrO3okLfIsKemiSBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8PTaN/btrWIYkITsm/M2JPTnrO3okLfIsKemiSBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8PTaN%2FbtrWIYkITsm%2FM2JPTnrO3okLfIsKemiSBK%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; alt=&quot;java-lambda-cons&quot; loading=&quot;lazy&quot; width=&quot;668&quot; height=&quot;60&quot; data-origin-width=&quot;668&quot; data-origin-height=&quot;60&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1674111931156&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    List&amp;lt;Integer&amp;gt; list = Arrays.asList(1, 2, 3, 4, 5);

    list.forEach(i -&amp;gt; {
        IntStream.range(0, i).forEach(j -&amp;gt; {
            System.out.println(i/j);
        });
    });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;783&quot; data-origin-height=&quot;166&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwyVR0/btrWI8m6x3Q/F5UpmKlcnc6ZFawocteDxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwyVR0/btrWI8m6x3Q/F5UpmKlcnc6ZFawocteDxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwyVR0/btrWI8m6x3Q/F5UpmKlcnc6ZFawocteDxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwyVR0%2FbtrWI8m6x3Q%2FF5UpmKlcnc6ZFawocteDxK%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; alt=&quot;java-lambda-cons&quot; loading=&quot;lazy&quot; width=&quot;783&quot; height=&quot;166&quot; data-origin-width=&quot;783&quot; data-origin-height=&quot;166&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;3.&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;stream에서 람다를 사용할 시 for문 보다 성능이 떨어진다&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;아래 예제는 0 부터 10000 까지 단순 순회하는 로직을 stream의 람다와 단순 for문으로 구성하고 각각 실행시간을 나노초로 구하는 코드이다. 결과를 보듯이 두 실행시간 차이는 결코 적지 않다.&lt;/p&gt;
&lt;pre id=&quot;code_1674111839967&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {

    // 람다식 stream 순회 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    long startTime = System.nanoTime(); // 코드 시작 시간

    IntStream.range(0,10000).forEach((value) -&amp;gt; {});

    long endTime = System.nanoTime(); // 코드 끝난 시간
    long durationTimeSec = endTime - startTime;
    System.out.println(&quot;람다식 stream 순회 : &quot; + durationTimeSec + &quot;n/s&quot;);


    // 일반 for문 순회 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    startTime = System.nanoTime(); // 코드 시작 시간

    for(int i=0; i&amp;lt;10000; i++){
    }

    endTime = System.nanoTime(); // 코드 끝난 시간
    durationTimeSec = endTime - startTime;
    System.out.println(&quot;일반 for문 순회 : &quot; + durationTimeSec + &quot;n/s&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;270&quot; data-origin-height=&quot;55&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LEqp7/btrWJIH9209/eiKcZiXbq33bcFoETgPvJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LEqp7/btrWJIH9209/eiKcZiXbq33bcFoETgPvJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LEqp7/btrWJIH9209/eiKcZiXbq33bcFoETgPvJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLEqp7%2FbtrWJIH9209%2FeiKcZiXbq33bcFoETgPvJK%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; alt=&quot;java-lambda-cons&quot; loading=&quot;lazy&quot; width=&quot;270&quot; height=&quot;55&quot; data-origin-width=&quot;270&quot; data-origin-height=&quot;55&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;&lt;b&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;4.&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;람다를 남발하면 코드가 지저분해질 수 있다 &lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에는 동작 행위를 미리 클래스의 메서드로 정의해놓고 실행부에서 갖다 쓰는 것이었지만, 람다는 동작 행위를 실행부에서 지정하는 식이다. 그래서인지 람다식을 남발하다보면 비슷하게 생긴 함수를 계속 중복 생성하고 있는 자신을 발견 할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1674110181214&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface OperationStrategy {
    // (int x, int y) -&amp;gt; int
    int calculate(int x, int y);
}

// Template
class OperationTemplate {
    int calculate(int x, int y, OperationStrategy cal) {
        int result = cal.calculate(x, y);
        return result;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1674110189449&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    int x = 100;
    int y = 30;

    OperationContext cxt = new OperationContext();

    int result = cxt.calculate(x, y, (x1, y1) -&amp;gt; x1 + y1);
    System.out.println(result); // 130

    result = cxt.calculate(x, y, (x1, y1) -&amp;gt; x1 - y1);
    System.out.println(result); // 70

    result = cxt.calculate(x, y, (x1, y1) -&amp;gt; x1 * y1);
    System.out.println(result); // 3000

    result = cxt.calculate(x, y, (x1, y1) -&amp;gt; x1 / y1);
    System.out.println(result); // 3
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비스무리한 람다 함수를 메서드 아규먼트로 지정하고 있다. 위의 예제는 아주 간단한 예시라 와닿지 않을 수 있지만 람다식 로직이 두줄 세줄이 넘어간다면 실행부의 코드가 지저분해 질 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;5.&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;재귀로 만들경우에는 다소 부적합하다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;람다식을 통해 재귀 함수를 구축하면 실행 조차 안되는 컴파일 에러가 나타난다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674104878611&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {

    UnaryOperator&amp;lt;Long&amp;gt; factorial = (x) -&amp;gt; {
        x == 0 ? 1 : x * factorial.apply(x - 1); // compile error
    };

    factorial(1);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;514&quot; data-origin-height=&quot;62&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IIVA8/btrWIylTM9V/A4wCNcYRG1vFZ1mXZDkPRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IIVA8/btrWIylTM9V/A4wCNcYRG1vFZ1mXZDkPRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IIVA8/btrWIylTM9V/A4wCNcYRG1vFZ1mXZDkPRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIIVA8%2FbtrWIylTM9V%2FA4wCNcYRG1vFZ1mXZDkPRk%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; alt=&quot;java-lambda-cons&quot; loading=&quot;lazy&quot; width=&quot;514&quot; height=&quot;62&quot; data-origin-width=&quot;514&quot; data-origin-height=&quot;62&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;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://github.com/yeGenieee/java-live-study/blob/main/%5B15%5DJava%20Live%20Study.md&lt;/p&gt;</description>
      <category>Language/Java</category>
      <category>Functional</category>
      <category>LAMBDA</category>
      <category>람다</category>
      <category>자바 함수형</category>
      <category>함수형 프로그래밍</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/968</guid>
      <comments>https://inpa.tistory.com/entry/%E2%98%95-Lambda-Expression#entry968comment</comments>
      <pubDate>Mon, 27 Mar 2023 09:09:16 +0900</pubDate>
    </item>
    <item>
      <title>  @media는 이제 그만 ! 최신 @container 사용법</title>
      <link>https://inpa.tistory.com/entry/%F0%9F%8C%9F-css-container-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;78fedf5e.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M5fzO/btr3Q9tvRq2/jJJ5JrTt5hU9zGl3B46kXk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M5fzO/btr3Q9tvRq2/jJJ5JrTt5hU9zGl3B46kXk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M5fzO/btr3Q9tvRq2/jJJ5JrTt5hU9zGl3B46kXk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM5fzO%2Fbtr3Q9tvRq2%2FjJJ5JrTt5hU9zGl3B46kXk%2Fimg.webp&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; alt=&quot;media-container-query&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;720&quot; data-filename=&quot;78fedf5e.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;컨테이너 쿼리&amp;nbsp;&lt;span style=&quot;color: #ef5369;&quot;&gt;vs&lt;/span&gt; 미디어 쿼리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 쿼리는 미디어 쿼리와 같이 문서의 스타일을 &lt;b&gt;반응형&lt;/b&gt;으로 지정할 수 있다. 이 둘의 작동 방식은 유사하지만, 차이점은 어느 것을 기반으로 동작되냐는 점이다. 예를들어 미디어 쿼리는 디바이스 또는 미디어 유형을 기반으로 뷰포트에 의해 반응하지만, 컨테이너 쿼리는 페이지내의 특정 컴포넌트 &lt;span style=&quot;color: #333333; text-align: start;&quot;&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;상위 호환&lt;/b&gt;이라고 할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1723&quot; data-origin-height=&quot;835&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baT8wB/btr3V7H0izi/ezkQyMhrj1KFZrRDrh1Ja0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baT8wB/btr3V7H0izi/ezkQyMhrj1KFZrRDrh1Ja0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baT8wB/btr3V7H0izi/ezkQyMhrj1KFZrRDrh1Ja0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaT8wB%2Fbtr3V7H0izi%2FezkQyMhrj1KFZrRDrh1Ja0%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; alt=&quot;media-container-query&quot; loading=&quot;lazy&quot; width=&quot;1723&quot; height=&quot;835&quot; data-origin-width=&quot;1723&quot; data-origin-height=&quot;835&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 쿼리가 없었을 시절에는 자바스크립트의 Resize Observer 기술을 통해 특정 요소의 크기 변화를 관찰하여 스크립트로 스타일링을 해주어야 했다. 하지만 이제 간단한 CSS 컨테이너 문법 지정을 통해 매우 간편하게 보다 세밀하고 정확한 반응형 쿼리를 구현할 수 있게 되었다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&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;span style=&quot;color: #333333; text-align: start;&quot;&gt;컨테이너 쿼리 기능이 빛을 발하는 순간은 아래와 같은 '심플 모드' 나 '확장 모드' 기능을 지원하는 반응형 사이드바를 설계할 때 우측 본문 영역에 대한 반응형 스타일링이 필요할 때이다. &lt;/span&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/cTkw8B/btr3SYZoIyG/zIs6Yi8cw3AThePaAnAZj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTkw8B/btr3SYZoIyG/zIs6Yi8cw3AThePaAnAZj1/img.png&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;532&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTkw8B/btr3SYZoIyG/zIs6Yi8cw3AThePaAnAZj1/img.png&quot; alt=&quot;media-container-query&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTkw8B%2Fbtr3SYZoIyG%2FzIs6Yi8cw3AThePaAnAZj1%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;996&quot; height=&quot;532&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UlXmN/btr3XyS0c7l/jVKEUjLujzQ75sLXRDbgUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UlXmN/btr3XyS0c7l/jVKEUjLujzQ75sLXRDbgUk/img.png&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;532&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UlXmN/btr3XyS0c7l/jVKEUjLujzQ75sLXRDbgUk/img.png&quot; alt=&quot;media-container-query&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUlXmN%2Fbtr3XyS0c7l%2FjVKEUjLujzQ75sLXRDbgUk%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;996&quot; height=&quot;532&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;예를들어 좌측 사이드바 영역을 제외한 우측 본문 영역 안에 있는 여러 요소들을 홈페이지의 가로폭에 따라 요소에 변화를 주어야 한다고 할때, 기존 방식대로 미디어 쿼리(@media)를 쓴다면 사이드바 영역을 포함한 전체 브라우저 가로폭을 계산하기 때문에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;'심플 모드' 일때 와 '확장 모드'&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;일때를 모두 고려해야 되어 &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&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: #333333; text-align: start;&quot;&gt;'심플 모드' 냐 '확장 모드'&lt;span&gt; 에 따라 사이드바의 길이에 따라 미디어 쿼리를 두번 구현해야 할지도 모른다. 왜냐하면 본문(main) 영역 부분이 사이드바 길이와 함께 브라우저 폭을 계산해야 되기 때문이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1678853198840&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;main {
	width: 1100px;
}

/* 사이드바 확장 모드 상태일때의 반응형 쿼리 */
@media screen and (max-width: 1400px) {
    html[data-sidebar='expand'] main {
        padding: 0 35px;
        width: 100%;
    } 
}

/* 사이드바 심플 모드 상태일때의 반응형 쿼리 */
@media screen and (max-width: 1200px) {
    html[data-sidebar='simple'] main {
        padding: 0 35px;
        width: 100%;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 위의 본문의 main 영역을 컨테이너 쿼리로 지정한다면, 좌측 사이드바 상태를 따지지 않고 오로지 main 영역의 가로폭에 따라 반응하기 때문에 좀더 직관적이고 유지 보수 좋게 구성할 수 있게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1678853396974&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;main {
    width: 1100px;
    container-name : main-container;
    container-type : inline-size;
}

/* 본문 메인 특정 영역에 따른 반응형 쿼리 */
@container main-container (max-width: 992px) { 
    html main {
        padding: 0 35px;
        width: 100%;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;CSS Container Queries 정리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;컨테이너 쿼리&amp;nbsp;사용법&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;컨테이너 쿼리는 브라우저 viewport 기준이 아닌, 특정 요소의 크기에 따라 반응적인 동작을 한다. 그래서 가장 먼저 해야 할 일은 반응형을 적용할 컨테이너 요소를 지정하는 것이다. 그래야 특정 요소를 기준으로 치수를 계산하기 때문이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;1.&lt;/span&gt; 먼저 반응형으로 등록할 요소를 컨테이너로 등록해준다.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;container-name&lt;/s&gt; 을 통해 쿼리 컨테이너의 이름을 지정한다.&lt;/p&gt;
&lt;pre id=&quot;code_1678850301710&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;div {
  container-name: div-container; /* 컨테이너 쿼리 요소 이름 */
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;2.&lt;/span&gt; 컨테이너 요소의 타입을 지정해준다.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;container-type&lt;/s&gt; 에는 &lt;s&gt;size&lt;/s&gt;, &lt;s&gt;inline-size&lt;/s&gt;, &lt;s&gt;normal&lt;/s&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 style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;&lt;s&gt;inline-size&lt;/s&gt; : 인라인 레벨 기준으로 컨테이너를 적용. 요소의 &lt;s&gt;width&lt;/s&gt; 값에 따라 반응형이 동작된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;s&gt;size&lt;/s&gt; : 블록 레벨 기준으로 컨테이너를 적용. &lt;s&gt;width&lt;/s&gt; 뿐만 아니라 &lt;s&gt;height&lt;/s&gt; 값에 따라 반응형이 동작 된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;s&gt;normal&lt;/s&gt; : 해당 값이 부여된 요소를 container에서 제외시킨다. 일종의 none 의미라고 보면 되겠다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1678850326353&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;div {
    container-name: div-container;
    container-type: inline-size; /* 왠만한 상황에선 inline-size 로 이용한다고 보면 된다 */
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1678850591714&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;div {
    /* container-name / container-type */
    container : div-container / inline-size; /* 한줄로도 단축 표현이 가능하다 */
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;3.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;@container 반응 치수를 지정해준다&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@media 쿼리를 등록했던 것 처럼 똑같이 @container 쿼리를 등록해주면 된다. 이때 &lt;b&gt;컨테이너 이름을 지정&lt;/b&gt;해주면 특정 컨테이너 내에서만 반응하고, 이름을 지정 안해주면 모든 컨테이너에 대해 전역으로 반응하게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1678850940339&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* 모든 컨테이너 요소에 반응 */
@container (min-width: 700px) {
  div {
    font-size: 2em;
  }
}

/* 특정 container-name의 요소에 반응 */
@container div-container (min-width: 700px) {
  div {
    font-size: 2em;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #252525; text-align: start;&quot;&gt;컨테이너 쿼리 길이 단위&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;viewport의 vw, vh 단위 처럼 container의 너비와 높이를 기준으로 하는 &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;상대 기준점&lt;/span&gt; 단위가 존재한다. 이 단위값을 이용하면 요소의 구체적인 길이 값을 다시 계산할 필요 없이 다른 컨테이너에서 유연하게 사용할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;cqw : 쿼리 컨테이너 너비의 1%&lt;/li&gt;
&lt;li&gt;cqh : 쿼리 컨테이너 높이의 1%&lt;/li&gt;
&lt;li&gt;cqi : 쿼리 컨테이너 인라인 크기의 1%&lt;/li&gt;
&lt;li&gt;cqb : 쿼리 컨테이너의 블록 크기의 1%&lt;/li&gt;
&lt;li&gt;cqmin : cqi 또는 cqb 중 더 작은 값&lt;/li&gt;
&lt;li&gt;cqmax : cqi 또는 cqb 중 더 큰 값&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1678850520090&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* 컨테이너의 인라인 크기를 기준으로 제목의 글꼴 크기를 설정 */
@container (min-width: 700px) {
  div {
    font-size: 1em + 2cqi;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&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;IE를 제외하곤 왠만한 PC, Mobile 브라우저에서 문제없이 모두 지원됨을 볼 수 있어 더욱더 사용하지 않을 이유가 없다.&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;1045&quot; data-origin-height=&quot;343&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mmLMZ/btr3S1V1ioM/IW3yhBJ9K3atXSP1MKBJu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mmLMZ/btr3S1V1ioM/IW3yhBJ9K3atXSP1MKBJu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mmLMZ/btr3S1V1ioM/IW3yhBJ9K3atXSP1MKBJu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmmLMZ%2Fbtr3S1V1ioM%2FIW3yhBJ9K3atXSP1MKBJu0%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; alt=&quot;media-container-query&quot; loading=&quot;lazy&quot; width=&quot;1045&quot; height=&quot;343&quot; data-origin-width=&quot;1045&quot; data-origin-height=&quot;343&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 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;컨테이너 쿼리 폴리필&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만일 마이너한 구형 브라우저를 꼭 지원해야만 한다면, 폴리필(polyfill) 된 라이브러리가 있으니 이걸 사용하면 된다. 사용법도 css에 컨테이너 쿼리 문법을 쓰고 라이브러리를 로드해 스크립트만 실행하면 알아서 브라우저의 컨테이너 쿼리 지원 유무를 감지하고 폴리필 해준다.&lt;/p&gt;
&lt;figure id=&quot;og_1678843973589&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;A first look at container-query-polyfill, a polyfill for CSS Container Queries&quot; data-og-description=&quot;Surma has been working on container-query-polyfill, a lightweight polyfill for CSS Container Queries. Let&amp;rsquo;s take a look at how it works and how it differs from cqfill &amp;hellip; What Unlike cqfill &amp;mdash;which was covered here before&amp;mdash; this Polyfill for Container &quot; data-og-host=&quot;www.bram.us&quot; data-og-source-url=&quot;https://www.bram.us/2021/11/26/a-first-look-at-container-query-polyfill-a-polyfill-for-css-container-queries/&quot; data-og-url=&quot;https://www.bram.us/2021/11/26/a-first-look-at-container-query-polyfill-a-polyfill-for-css-container-queries/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bI38Iy/hyRU2lavAm/3yRkP05UzkrmR5wio02mk1/img.png?width=400&amp;amp;height=277&amp;amp;face=0_0_400_277,https://scrap.kakaocdn.net/dn/T9iLp/hyRWAAyFlj/uaQSUAkF0iAHw2I51KQ5yk/img.png?width=400&amp;amp;height=277&amp;amp;face=0_0_400_277,https://scrap.kakaocdn.net/dn/gbwUm/hyRVe0coxd/j3m6teL6iFmiRkAr3bvQjK/img.png?width=400&amp;amp;height=277&amp;amp;face=0_0_400_277&quot;&gt;&lt;a href=&quot;https://www.bram.us/2021/11/26/a-first-look-at-container-query-polyfill-a-polyfill-for-css-container-queries/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.bram.us/2021/11/26/a-first-look-at-container-query-polyfill-a-polyfill-for-css-container-queries/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bI38Iy/hyRU2lavAm/3yRkP05UzkrmR5wio02mk1/img.png?width=400&amp;amp;height=277&amp;amp;face=0_0_400_277,https://scrap.kakaocdn.net/dn/T9iLp/hyRWAAyFlj/uaQSUAkF0iAHw2I51KQ5yk/img.png?width=400&amp;amp;height=277&amp;amp;face=0_0_400_277,https://scrap.kakaocdn.net/dn/gbwUm/hyRVe0coxd/j3m6teL6iFmiRkAr3bvQjK/img.png?width=400&amp;amp;height=277&amp;amp;face=0_0_400_277');&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;A first look at container-query-polyfill, a polyfill for CSS Container Queries&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Surma has been working on container-query-polyfill, a lightweight polyfill for CSS Container Queries. Let&amp;rsquo;s take a look at how it works and how it differs from cqfill &amp;hellip; What Unlike cqfill &amp;mdash;which was covered here before&amp;mdash; this Polyfill for Container&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.bram.us&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;pre id=&quot;code_1678844044345&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script src=&quot;https://unpkg.com/container-query-polyfill/dist/container-query-polyfill.modern.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script&amp;gt;
    const supportsContainerQueries = &quot;container&quot; in document.documentElement.style;
    if (!supportsContainerQueries) {
      import(&quot;container-query-polyfill&quot;);
    }
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Container_Queries&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://web.dev/cq-stable/&lt;/p&gt;
&lt;div id=&quot;gtx-trans&quot; style=&quot;position: absolute; left: 373px; top: 4268.17px;&quot;&gt;
&lt;div class=&quot;gtx-trans-icon&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;</description>
      <category>Style Sheet/CSS</category>
      <category>Container</category>
      <category>Media</category>
      <category>미디어</category>
      <category>미디어 쿼리</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1099</guid>
      <comments>https://inpa.tistory.com/entry/%F0%9F%8C%9F-css-container-%EC%82%AC%EC%9A%A9%EB%B2%95#entry1099comment</comments>
      <pubDate>Fri, 24 Mar 2023 09:44:38 +0900</pubDate>
    </item>
    <item>
      <title>  크롬 브라우저 PNA 권한과 CORS 해결하기</title>
      <link>https://inpa.tistory.com/entry/%F0%9F%8C%90-%ED%81%AC%EB%A1%AC-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-PNA-%EA%B6%8C%ED%95%9C%EA%B3%BC-CORS-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;150.jpg&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;844&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YloBF/btr3LEfwgTx/8rxBkOSrJLhakUTLhDXFcK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YloBF/btr3LEfwgTx/8rxBkOSrJLhakUTLhDXFcK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YloBF/btr3LEfwgTx/8rxBkOSrJLhakUTLhDXFcK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYloBF%2Fbtr3LEfwgTx%2F8rxBkOSrJLhakUTLhDXFcK%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; alt=&quot;chrome-pna&quot; loading=&quot;lazy&quot; width=&quot;1500&quot; height=&quot;844&quot; data-filename=&quot;150.jpg&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;844&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Chrome PNA (Private Network Access)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사설망 접근(private network access)&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;이란, &lt;b&gt;비인증된 공인(public)&lt;/b&gt; 웹사이트에서, 사이트를 방문한 사용자의 와 같은 &lt;b&gt;사설 네트워크망&lt;/b&gt;(&lt;s style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;localhost(127.0.0.1)&lt;/span&gt;&lt;/s&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt; or&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;s style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;192.168.0.*&lt;/span&gt;&lt;/s&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;아이피&lt;/span&gt;) 엔드포인트에 엑세스하려 할때, 브라우저가 요청을 제한하는 새로운 보안 향상 WSC 사양을 말한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;PNA 동작 메커니즘은 CORS(Cross-Origin Resource Sharing) 정책을 확장한 개념으로 적용된다. 그래서 &lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;사이트에서 사설 네트워크 서버의 허가를 우선 예비 요청(Preflight) 하고 브라우저가 요청을 승인한 경우에만, 공공 웹사이트에서 사설 네트워크 서버의 리소스에 엑세스가 가능하고 그렇지 않으면 CORS 에러가 뜨게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1679538668119&quot; style=&quot;color: #333333; text-align: start;&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;  악명 높은 CORS 개념 &amp;amp; 해결법 - 정리 끝판왕  &quot; data-og-description=&quot;악명 높은 CORS 에러 메세지 웹 개발을 하다보면 반드시 마주치는 멍멍 같은 에러가 바로 CORS 이다. 웹 개발의 신입 신고식이라고 할 정도로, CORS는 누구나 한 번 정도는 겪게 된다고 해도 과언이 &quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-CORS-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-%F0%9F%91%8F&quot; data-og-url=&quot;https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-CORS-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-%F0%9F%91%8F&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bw43c9/hyR0q7qpZl/GQQx2L05DEa8ZM78bBAWR0/img.png?width=800&amp;amp;height=466&amp;amp;face=0_0_800_466,https://scrap.kakaocdn.net/dn/btunOB/hyR0l59uKe/6L1vukphHknOjMUeKUEus0/img.png?width=800&amp;amp;height=466&amp;amp;face=0_0_800_466,https://scrap.kakaocdn.net/dn/sPcgr/hyR0vVemCp/DMk0pK1S2k8czJ8Cqawar1/img.png?width=1200&amp;amp;height=700&amp;amp;face=0_0_1200_700&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-CORS-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-%F0%9F%91%8F&quot; data-source-url=&quot;https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-CORS-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-%F0%9F%91%8F&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bw43c9/hyR0q7qpZl/GQQx2L05DEa8ZM78bBAWR0/img.png?width=800&amp;amp;height=466&amp;amp;face=0_0_800_466,https://scrap.kakaocdn.net/dn/btunOB/hyR0l59uKe/6L1vukphHknOjMUeKUEus0/img.png?width=800&amp;amp;height=466&amp;amp;face=0_0_800_466,https://scrap.kakaocdn.net/dn/sPcgr/hyR0vVemCp/DMk0pK1S2k8czJ8Cqawar1/img.png?width=1200&amp;amp;height=700&amp;amp;face=0_0_1200_700');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;  악명 높은 CORS 개념 &amp;amp; 해결법 - 정리 끝판왕  &lt;/p&gt;
&lt;p class=&quot;og-desc&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;악명 높은 CORS 에러 메세지 웹 개발을 하다보면 반드시 마주치는 멍멍 같은 에러가 바로 CORS 이다. 웹 개발의 신입 신고식이라고 할 정도로, CORS는 누구나 한 번 정도는 겪게 된다고 해도 과언이&lt;/p&gt;
&lt;p class=&quot;og-host&quot; style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;inpa.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 앞으로 공공 인터넷 웹사이트에서 내부 네트워크 자원에 접근하려면 브라우저의 허락을 받아야만 하는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;848&quot; data-origin-height=&quot;289&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mlDmm/btr3SYMnx44/L3MiOtnRFXcXJAYukr3631/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mlDmm/btr3SYMnx44/L3MiOtnRFXcXJAYukr3631/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mlDmm/btr3SYMnx44/L3MiOtnRFXcXJAYukr3631/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmlDmm%2Fbtr3SYMnx44%2FL3MiOtnRFXcXJAYukr3631%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; alt=&quot;chrome-pna&quot; loading=&quot;lazy&quot; width=&quot;848&quot; height=&quot;289&quot; data-origin-width=&quot;848&quot; data-origin-height=&quot;289&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;style3&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;PNA&lt;span&gt; 탄생 배경&lt;/span&gt;&lt;/b&gt;&lt;/h3&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;브라우저를&amp;nbsp;통한&amp;nbsp;네트워크&amp;nbsp;침입&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 사양은 브라우저를 통해 침투하는 방식의 해킹 공격을 보다 어렵게 만들겠다는 취지로 탄생 되었다. 브라우저는 많은 서비스와 상호 작용해야 하기 때문에, 기본적으로 로컬 네트워크 내부의 거의 모든 리소스에 연결할 수 있다는 특징을 악용하여, &lt;span style=&quot;color: #ee2323;&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;실제로 해커가 CSRF 공격을 통해 300,000개 이상의 무선 라우터에 대한 DNS 서버 설정을 변경하여 정보를 탈취한 사례도 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1181&quot; data-origin-height=&quot;895&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oj65Q/btr3Sxmz4QC/PqiayTVwg2c4OO6u6LlQWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oj65Q/btr3Sxmz4QC/PqiayTVwg2c4OO6u6LlQWk/img.png&quot; data-alt=&quot;크로스 사이트 요청 취약점을 악용하여 라우터의 DNS 설정을 변경하는 공격의 3단계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oj65Q/btr3Sxmz4QC/PqiayTVwg2c4OO6u6LlQWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Foj65Q%2Fbtr3Sxmz4QC%2FPqiayTVwg2c4OO6u6LlQWk%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; alt=&quot;chrome-pna&quot; loading=&quot;lazy&quot; width=&quot;1181&quot; height=&quot;895&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1181&quot; data-origin-height=&quot;895&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;크로스 사이트 요청 취약점을 악용하여 라우터의 DNS 설정을 변경하는 공격의 3단계&lt;/figcaption&gt;
&lt;/figure&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;악성 자바스크립트가 로컬 네트워크 내부의 컴퓨터에서 로드되어 로컬 컴퓨터가 라우터의 DNS 설정을 자동으로 변경하도록 한다.&lt;/li&gt;
&lt;li&gt;라우터는 네트워크의 모든 디바이스에 대해 악성 네임서버(DNS)를 사용하도록 설정됨에 따라, 해당 망에 연결된 모든 사용자들의 라우팅 요청을 오염시킨다.&lt;/li&gt;
&lt;li&gt;금융(또는 기타) 사이트에 연결을 시도하는 디바이스는 이제 로그인 자격 증명을 캡처할 수 있는 가짜 웹사이트로 리다이렉션 될 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 보안적인 취약점 때문에 브라우저는 이제 사설망에 대해서 검사를 철저히 한다는 개념으로 PNA 사양이 탄생하게 된 것이다. 그러면 각종 엔드포인트 장비들을 노리는 CSRF(교차 사이트 요청 조작) 공격으로부터 비교적 안전해지게 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Chrome PNA 정책 적용 시기&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;Google에 따르면 &lt;/span&gt;Chrome 94 버전부터 PNA가 등장했으며 현재까지 시범적으로 운용되다가, 2023년 5월에 출시될 Chrome 113 버전에서 정식으로 릴리즈 될 예정이라고 한다. 같은 크로미움 계열인 Edge 브라우저 역시 베타 채널에 도입된 상태이다. 따라서 브라우저 자체에서 비공개 네트워크 요청을 하는 애플리케이션이 있다면 이에 맞게 대처를 해야 할 것이다.&lt;/p&gt;
&lt;figure id=&quot;og_1678865415658&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;Private Network Access update: Introducing a deprecation trial - Chrome Developers&quot; data-og-description=&quot;Chrome is deprecating access to private network endpoints from non-secure public websites in Chrome 94 as part of the Private Network Access specification. Read on for recommended actions.&quot; data-og-host=&quot;developer.chrome.com&quot; data-og-source-url=&quot;https://developer.chrome.com/blog/private-network-access-update/&quot; data-og-url=&quot;https://developer.chrome.com/blog/private-network-access-update/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bv9S2q/hyRWlXUq9W/Zpqun9uZ9dYLDsL14UuEuK/img.jpg?width=1521&amp;amp;height=761&amp;amp;face=0_0_1521_761,https://scrap.kakaocdn.net/dn/bcciQn/hyRWtPbl58/NSHWEGm7PWYqIVsRHUugOk/img.jpg?width=1521&amp;amp;height=761&amp;amp;face=0_0_1521_761,https://scrap.kakaocdn.net/dn/fd637/hyRWs3MYxC/4YCtChB8Mph2128vDDdJB0/img.jpg?width=3500&amp;amp;height=1750&amp;amp;face=0_0_3500_1750&quot;&gt;&lt;a href=&quot;https://developer.chrome.com/blog/private-network-access-update/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.chrome.com/blog/private-network-access-update/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bv9S2q/hyRWlXUq9W/Zpqun9uZ9dYLDsL14UuEuK/img.jpg?width=1521&amp;amp;height=761&amp;amp;face=0_0_1521_761,https://scrap.kakaocdn.net/dn/bcciQn/hyRWtPbl58/NSHWEGm7PWYqIVsRHUugOk/img.jpg?width=1521&amp;amp;height=761&amp;amp;face=0_0_1521_761,https://scrap.kakaocdn.net/dn/fd637/hyRWs3MYxC/4YCtChB8Mph2128vDDdJB0/img.jpg?width=3500&amp;amp;height=1750&amp;amp;face=0_0_3500_1750');&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;Private Network Access update: Introducing a deprecation trial - Chrome Developers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Chrome is deprecating access to private network endpoints from non-secure public websites in Chrome 94 as part of the Private Network Access specification. Read on for recommended actions.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.chrome.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;PNA CORS 에러 현상&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;text-align: start;&quot;&gt;CORS 의 메세지 종류&lt;/span&gt;&lt;/b&gt;&lt;/h3&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;&lt;span style=&quot;text-align: start;&quot;&gt;일반적인 CORS 메세지&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;먼저 일반적으로 발생하는 CORS 에러는 다음과 같다. 에러 메세지에서 안내하듯이&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;이 부분은 백엔드쪽에서 CORS 관련 헤더인 &lt;s&gt;Access-Control-Allow-*&lt;/s&gt; 응답만 잘 해주면 별다른 문제없이 해결이 된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;817&quot; data-origin-height=&quot;72&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgLL0I/btr5cTiuXEU/q6hzMRJYnrM6Eb0Zwth1KK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgLL0I/btr5cTiuXEU/q6hzMRJYnrM6Eb0Zwth1KK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgLL0I/btr5cTiuXEU/q6hzMRJYnrM6Eb0Zwth1KK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgLL0I%2Fbtr5cTiuXEU%2Fq6hzMRJYnrM6Eb0Zwth1KK%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; alt=&quot;PNA-CORS&quot; loading=&quot;lazy&quot; width=&quot;817&quot; height=&quot;72&quot; data-origin-width=&quot;817&quot; data-origin-height=&quot;72&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;Access to XMLHttpRequest at 'http://xxxx' from origin 'http://xxxxx' has been blocked by CORS policy:&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;Response to preflight request doesn't pass access control check:&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;No&amp;nbsp;'Access-Control-Allow-Origin'&amp;nbsp;header&amp;nbsp;is&amp;nbsp;present&amp;nbsp;on&amp;nbsp;the&amp;nbsp;requested&amp;nbsp;resource.&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;PNA CORS 메세지&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 서버에서 CORS 관련 헤더 요청 응답을 모두 처리해줬는데, 이번엔 다음과 같은 또다른 CORS 에러가 발생해서 당황한 경험이 있을 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;811&quot; data-origin-height=&quot;34&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byEkqY/btr3U1giab8/Wko5E5xzpmgIEGbVG4WQ10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byEkqY/btr3U1giab8/Wko5E5xzpmgIEGbVG4WQ10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byEkqY/btr3U1giab8/Wko5E5xzpmgIEGbVG4WQ10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyEkqY%2Fbtr3U1giab8%2FWko5E5xzpmgIEGbVG4WQ10%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; alt=&quot;PNA-CORS&quot; loading=&quot;lazy&quot; width=&quot;811&quot; height=&quot;34&quot; data-origin-width=&quot;811&quot; data-origin-height=&quot;34&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;Access&amp;nbsp;to&amp;nbsp;XMLHttpRequest&amp;nbsp;at&amp;nbsp;'http://10.x.x.x/xxxx'&amp;nbsp;from&amp;nbsp;origin&amp;nbsp;'http://xxxx.xxx.com' has been blocked by CORS policy:&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;The&amp;nbsp;request&amp;nbsp;client&amp;nbsp;is&amp;nbsp;not&amp;nbsp;a&amp;nbsp;secure&amp;nbsp;context&amp;nbsp;and&amp;nbsp;the&amp;nbsp;resource&amp;nbsp;is&amp;nbsp;in&amp;nbsp;more-private&amp;nbsp;address&amp;nbsp;space&amp;nbsp;`private`&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 에러 메세지 내용 부분을 해석하자면, 요청 클라이언트가 보안 컨텍스트가 아니며 리소스가 비공개 주소 공간 로컬에 있다는 말이다. 즉, 위에서 다루었던 PNA(Priavte Network Address) 관련 사양이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;PNA CORS 발생 상황&lt;/b&gt;&lt;/h3&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;요청 단계 방향&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포스팅 초반에 잠깐 소개했듯이, PNA CORS 발생 상황은 공인 IP에서 사설/로컬 IP로의 낮은 수준으로 가는 요청을 할때 발생된다. 예를 들어 아래 그림에서 &lt;span style=&quot;color: #006dd7;&quot;&gt;Public 네트워크(http://example.com)&lt;/span&gt;에서 &lt;span style=&quot;color: #ef6f53;&quot;&gt;Private 네트워크(http://router.local)&lt;/span&gt; 로의 요청을 하거나, 또는 &lt;span style=&quot;color: #ef6f53; text-align: start;&quot;&gt;Private 네트워크&lt;/span&gt;에서 &lt;span style=&quot;color: #8a3db6;&quot;&gt;Local 호스트&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;public 네트워크 사이트 &amp;rarr; private 네트워크 사이트 (CORS !!)&lt;/li&gt;
&lt;li&gt;public 네트워크 사이트 &amp;rarr; 로컬 호스트&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(CORS !!)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;private 네트워크 사이트&amp;nbsp;&amp;rarr; 로컬 호스트&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(CORS !!)&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;1023&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVNsY6/btr3QwOXkec/B0iGkqAXuDCVihQBZnkuhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVNsY6/btr3QwOXkec/B0iGkqAXuDCVihQBZnkuhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVNsY6/btr3QwOXkec/B0iGkqAXuDCVihQBZnkuhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVNsY6%2Fbtr3QwOXkec%2FB0iGkqAXuDCVihQBZnkuhK%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; alt=&quot;pna-cors&quot; loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;1023&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;1023&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;&lt;b&gt;insecure public website&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 무조건 공인 웹사이트에서 요청하면 PNA CORS에 걸리는게 아니라, 정확히 말하자면 &lt;span style=&quot;color: #ee2323;&quot;&gt;비인증된*&lt;/span&gt; &lt;span style=&quot;color: #ee2323;&quot;&gt;공인&lt;/span&gt; 웹사이트에서 요청하면 걸리는 것이다. 즉, https 나 wss 와 같은 ssl을 먹인 사이트라면 PNA CORS는 발생하지 않게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;344&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/R8fBX/btr5jbIDlXM/erJKoAqvZwPQKdMaKzUFu1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/R8fBX/btr5jbIDlXM/erJKoAqvZwPQKdMaKzUFu1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/R8fBX/btr5jbIDlXM/erJKoAqvZwPQKdMaKzUFu1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FR8fBX%2Fbtr5jbIDlXM%2FerJKoAqvZwPQKdMaKzUFu1%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; alt=&quot;pna-cors&quot; loading=&quot;lazy&quot; width=&quot;456&quot; height=&quot;218&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;344&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;&lt;b&gt;도메인 여부&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 요청을 보낸 공인 웹사이트가 아이피 형태가 아닌 도메인이 씌워져 있을 경우에만 발생한다. 아무리 insecure public website 이라도 도메인 형태가 아닐경우 PNA CORS 에러는 발생하지 않는다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1403&quot; data-origin-height=&quot;484&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjM6LH/btr5oz4hD3z/lzUnzoPmRcrGzAAVldXHXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjM6LH/btr5oz4hD3z/lzUnzoPmRcrGzAAVldXHXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjM6LH/btr5oz4hD3z/lzUnzoPmRcrGzAAVldXHXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjM6LH%2Fbtr5oz4hD3z%2FlzUnzoPmRcrGzAAVldXHXK%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; alt=&quot;pna-cors&quot; loading=&quot;lazy&quot; width=&quot;1403&quot; height=&quot;484&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1403&quot; data-origin-height=&quot;484&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;style3&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;PNA CORS 관련 헤더&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 CORS 현상을 해결하기 위해 &lt;s&gt;Access-Control-Allow-Origin&lt;/s&gt; 헤더를 설정하여 해결 하였 듯이, PNA CORS 역시 관련 헤더인 &lt;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt;&lt;s&gt;Access-Control-Allow-Private-Network&lt;/s&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;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt;하위 자원에 대한 요청을 사설망에 보내기 전에, 앞서 &lt;/span&gt;예비 요청(Preflight)을 보낸다.&lt;/li&gt;
&lt;li&gt;이때 요청 헤더에 &lt;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt;&lt;s&gt;Access-Control-Request-Private-Network: true&lt;/s&gt; 를 실어 서버에 보낸다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;요청 전달을 받은 서버에서&amp;nbsp;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;&lt;s&gt;Access-Control-Allow-Private-Network: true&lt;/s&gt; 를 통해 &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;허락이나 불허를 응답한다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;엑세스 허락이 되면 본요청 통신이 이루어지게 된다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1197&quot; data-origin-height=&quot;773&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJpXLf/btr3WbqSKgx/0Eg2RUFvxBzvtdBOhjBTZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJpXLf/btr3WbqSKgx/0Eg2RUFvxBzvtdBOhjBTZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJpXLf/btr3WbqSKgx/0Eg2RUFvxBzvtdBOhjBTZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJpXLf%2Fbtr3WbqSKgx%2F0Eg2RUFvxBzvtdBOhjBTZk%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; alt=&quot;pna-cors&quot; loading=&quot;lazy&quot; width=&quot;1197&quot; height=&quot;773&quot; data-origin-width=&quot;1197&quot; data-origin-height=&quot;773&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;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;PNA CORS 해결 방법&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;1.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;요청 주체를 변경&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 말했듯이 공인 IP라고 해도 insecure일 경우에만 PNA CORS가 발생한다고 하였으니, 호출하는 주체의 scheme를 HTTPS로 설정하면 가져오려는 리소스가 HTTP여도 문제없이 호출이 가능해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹은 요청하려는 리소스를 사설/로컬 네트워크가 아닌 공인 네트워크로 변경하는 방법도 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;2.&lt;/span&gt; 크롬 브라우저 옵션 변경&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크롬 혹은 엣지 브라우저 검색창에 다음 URL로 이동한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;chrome://flags&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;edge://flags&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &lt;span style=&quot;color: #ef5369;&quot;&gt;Block insecure private network requests&lt;/span&gt; 항목과 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;Send&amp;nbsp;&lt;/span&gt;Private&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #202124; text-align: start;&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&amp;nbsp;Network Access preflights&lt;/span&gt; 항목&lt;/span&gt;의 설정값을 Disabled로 설정 하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;781&quot; data-origin-height=&quot;521&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qBtcV/btr45kOAy4M/nzTp1hgczBWeR2XNkXPdo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qBtcV/btr45kOAy4M/nzTp1hgczBWeR2XNkXPdo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qBtcV/btr45kOAy4M/nzTp1hgczBWeR2XNkXPdo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqBtcV%2Fbtr45kOAy4M%2FnzTp1hgczBWeR2XNkXPdo1%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; alt=&quot;pna-cors&quot; loading=&quot;lazy&quot; width=&quot;781&quot; height=&quot;521&quot; data-origin-width=&quot;781&quot; data-origin-height=&quot;521&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;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;3.&lt;/span&gt; Chrome Origin Trials 이용&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;a href=&quot;https://developer.chrome.com/origintrials/#/trials/active&quot;&gt;https://developer.chrome.com/origintrials/#/trials/active&lt;/a&gt; 로 접속해서, Private Network Access from non-secure contexts 항목의 REGISTER 를 클릭한다. 단, 이 방법은 PNA를 허용할 웹사이트의 API 키를 발급받아 사용하는 형태이기 때문에 사용할 수 있는 기간이 정해져 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1519&quot; data-origin-height=&quot;644&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/668cX/btr3XWtyY3f/ZoqYigdfsHg0oi9wfRVOp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/668cX/btr3XWtyY3f/ZoqYigdfsHg0oi9wfRVOp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/668cX/btr3XWtyY3f/ZoqYigdfsHg0oi9wfRVOp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F668cX%2Fbtr3XWtyY3f%2FZoqYigdfsHg0oi9wfRVOp0%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; alt=&quot;pna-cors&quot; loading=&quot;lazy&quot; width=&quot;1519&quot; height=&quot;644&quot; data-origin-width=&quot;1519&quot; data-origin-height=&quot;644&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 style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;4.&lt;/span&gt;&lt;span&gt; Access-Control-Allow-Private-Network &lt;/span&gt;헤더 설정&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 소개한 위의 방법은 간단하게 설정만으로 해결이 가능하겠지만, 이는 보안적으로 안전하지 않으며, CORS는 서버가 아닌 브라우저에서 차단하는 메커니즘 이기 때문에, 서비스 이용자들이 직접 자기가 쓰는 컴퓨터의 브라우저 설정을 일일히 조작해야 한다는 번거롭다는 치명적인 단점이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 가장 이상적인 해결책은 백엔드쪽에서 &lt;s&gt;Access-Control-Allow-Private-Network&lt;/s&gt; 헤더 설정을 하면 되는 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1679452093799&quot; class=&quot;http&quot; data-ke-language=&quot;http&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;HTTP/1.1 OPTIONS localhost:8080/cat.gif
Origin: https://foo.example
Access-Control-Request-Private-Network: true&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1679452098829&quot; class=&quot;http&quot; data-ke-language=&quot;http&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://foo.example
Access-Control-Allow-Private-Network: true&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://www.boannews.com/media/view.asp?idx=104187&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://developer.chrome.com/blog/private-network-access-update/&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://arstechnica.com/information-technology/2022/01/new-chrome-security-measure-aims-to-curtail-an-entire-class-of-web-attack/&lt;/p&gt;</description>
      <category>개발 지식/WEB 지식</category>
      <category>Access-Control-Allow-Private-Network</category>
      <category>Access-Control-Request-Private-Network</category>
      <category>cors</category>
      <category>PNA</category>
      <category>Private Network Access</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1100</guid>
      <comments>https://inpa.tistory.com/entry/%F0%9F%8C%90-%ED%81%AC%EB%A1%AC-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-PNA-%EA%B6%8C%ED%95%9C%EA%B3%BC-CORS-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0#entry1100comment</comments>
      <pubDate>Thu, 23 Mar 2023 07:29:39 +0900</pubDate>
    </item>
    <item>
      <title>  Chain Of Responsibility 패턴 - 완벽 마스터하기</title>
      <link>https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-Chain-Of-Responsibility-%ED%8C%A8%ED%84%B4-%EC%99%84%EB%B2%BD-%EB%A7%88%EC%8A%A4%ED%84%B0%ED%95%98%EA%B8%B0</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img (84).webp&quot; data-origin-width=&quot;1237&quot; data-origin-height=&quot;660&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjPwN2/btr0kliswvg/7aIKZqWcQoakghqEf5dH4K/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjPwN2/btr0kliswvg/7aIKZqWcQoakghqEf5dH4K/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjPwN2/btr0kliswvg/7aIKZqWcQoakghqEf5dH4K/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjPwN2%2Fbtr0kliswvg%2F7aIKZqWcQoakghqEf5dH4K%2Fimg.webp&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; alt=&quot;Chain-Of-Responsibility&quot; loading=&quot;lazy&quot; width=&quot;1237&quot; height=&quot;660&quot; data-filename=&quot;img (84).webp&quot; data-origin-width=&quot;1237&quot; data-origin-height=&quot;660&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;83&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OLWs4/btrZ6jTKijo/1pvYaAb8T6Lk1iMKlkMgb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OLWs4/btrZ6jTKijo/1pvYaAb8T6Lk1iMKlkMgb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OLWs4/btrZ6jTKijo/1pvYaAb8T6Lk1iMKlkMgb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOLWs4%2FbtrZ6jTKijo%2F1pvYaAb8T6Lk1iMKlkMgb0%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; alt=&quot;Chain-Of-Responsibility&quot; loading=&quot;lazy&quot; width=&quot;140&quot; height=&quot;83&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;83&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Chain Of Responsibility Pattern&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;책임 연쇄 패턴(Chain Of Responsibility Pattern, COR)은 클라이어트의 요청에 대한 세세한 처리를 하나의 객체가 몽땅 하는 것이 아닌, 여러개의 &lt;b&gt;처리 객체&lt;/b&gt;들로 나누고, 이들을&amp;nbsp;&lt;b&gt;사슬(chain) &lt;/b&gt;처럼 연결해 집합 안에서 연쇄적으로 처리하는 행동 패턴이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 처리 객체들을 &lt;b&gt;핸들러(handler)&lt;/b&gt;라고 부르는데, 요청을 받으면 각 핸들러는 요청을 처리할 수 있는지, 없으면 체인의 다음 핸들러로 처리에 대한 책임을 전가한다. 한마디로 책임 연쇄라는 말은 요청에 대한 책임을 다른 객체에 떠넘긴다는 소리이다. 떠넘긴다고 하니까 부정적인 의미로 들릴수도 있겠지만, 이러한 체인 구성은 하나의 객체에 처리에 대한 책임을 요청을 보내는 쪽(sender)과 요청을 처리하는(receiver) 쪽을 분리하여 각 객체를 부품으로 독립시키고 결합도를 느슨하게 만들며, 상황에 따라서 요청을 처리할 객체가 변하는 프로그램에도 유연하게 대응할 수 있다는 장점을 가지고 있다. 특히나 중첩 if-else문들을 최적화하는데 있어 실무에서도 많이 애용되는 패턴중 하나이기도 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀 더 접근하기 쉽게 실생활로 비유를 든다면, 예를들어 소비자가 문의를 위해 고객센터에 전화를 걸었다고 해보자. 그러면 아래와 같은 순서로 요청을 처리해본 경험을 한 번 씩은 겪어 봤을 것이다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;첫 번째로,&lt;span&gt; &lt;/span&gt;&lt;/span&gt;자동 응답기 음성 로봇이 응답하게 된다. 그런데 만일 음성 로봇이 제시하는 해결책에 대해서 1부터 4까지 선택사항중 해당하는 번호가 없다면 아마 여러분도 그렇고 대다수의 사람들이 기타 버튼을 눌러 상담원 연결을 누를 것이다.&lt;/li&gt;
&lt;li&gt;두 번째로, 상담사가 전화를 받았지만 길게 통화한 결과 제대로된 기술적인 도움을 받지 못했다고 한다. 그러면 상담원이 직접 엔지니어에게 전화를 연결해주게 된다.&lt;/li&gt;
&lt;li&gt;&lt;span&gt;세 번째로,&lt;span&gt; 엔지니어와 통화하고 적합한 솔루션을 제시해줌으로써 문제가 해결되어 통화를 종료한다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oRaRe/btrZ4buSuF0/cCAQwn2T0q5DK3F1AKmyQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oRaRe/btrZ4buSuF0/cCAQwn2T0q5DK3F1AKmyQK/img.png&quot; data-alt=&quot;자동 응답기 -&amp;amp;gt; 상담사 -&amp;amp;gt; 엔지니어 체인 구성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oRaRe/btrZ4buSuF0/cCAQwn2T0q5DK3F1AKmyQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoRaRe%2FbtrZ4buSuF0%2FcCAQwn2T0q5DK3F1AKmyQK%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; alt=&quot;Chain-Of-Responsibility&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;300&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;300&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;자동 응답기 -&amp;gt; 상담사 -&amp;gt; 엔지니어 체인 구성&lt;/figcaption&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;여기서 핸들러(Handler)는 자동 응답기 로봇, 상담사, 엔지니어가 된다. 즉, 각 핸들러에서 요청을 처리할 수 있는지의 여부를 따지고 불가능하면 다음 핸들러로&amp;nbsp; 떠넘기는 과정을 진행한 것을 코드로 구현한 것이 책임 연쇄 패턴인 것이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;책임 연쇄 패턴 구조&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;532&quot; data-origin-height=&quot;470&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5XkyR/btrZ72jwHHK/hkEcBLCb43CwBKaAcckXY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5XkyR/btrZ72jwHHK/hkEcBLCb43CwBKaAcckXY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5XkyR/btrZ72jwHHK/hkEcBLCb43CwBKaAcckXY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5XkyR%2FbtrZ72jwHHK%2FhkEcBLCb43CwBKaAcckXY0%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; alt=&quot;Chain-Of-Responsibility&quot; loading=&quot;lazy&quot; width=&quot;532&quot; height=&quot;470&quot; data-origin-width=&quot;532&quot; data-origin-height=&quot;470&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;Handler&lt;/b&gt; : 요청을 수신하고 처리 객체들의 집합을 정의하는 인터페이스&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ConcreteHandler&lt;/b&gt;&amp;nbsp;: 요청을 처리하는 실제 처리 객체&lt;br /&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;핸들러에 대한 필드를 내부에 가지고 있으며 메서드를 통해 다음 핸들러를 체인시키고 다음&amp;nbsp; 바라본다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;자신이 처리할 수 없는 요구가 나오면 바라보고 있는 다음 체인의 핸들러에게 요청을 떠넘긴다.&lt;/li&gt;
&lt;li&gt;ConcreteHandler1 - ConcreteHandler2 - ConcreteHandler3 - ... 이런식으로 체인 형식이 구성되게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Client&lt;/b&gt; : 요청을 Handler 전달한다&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;b&gt;책임 연쇄&lt;/b&gt;&amp;nbsp;패턴&lt;span&gt;&amp;nbsp;특징&lt;/span&gt;&lt;/b&gt;&lt;/h3&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;패&lt;/b&gt;&lt;b&gt;턴 사용 시기&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 요청을 2개 이상의 여러 객체에서 판별하고 처리해야 할때&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 data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;패턴 장점&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트는 처리 객체의 체인 집합 내부의 구조를 알 필요가 없다.&lt;/li&gt;
&lt;li&gt;각각의 체인은 자신이 해야하는 일만 하기 때문에&lt;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;/li&gt;
&lt;li&gt;클라이언트 코드를 변경하지 않고 핸들러를 체인에 동적으로 추가하거나 처리 순서를 변경하거나 삭제할 수 있어 유연해진다&lt;/li&gt;
&lt;li&gt;요청의 호출자(invoker)와 &lt;span&gt;수신자(receiver)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 분리시킬 수 있다.&amp;nbsp;
&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;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;패턴 단점&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;실행 시에 코드의&amp;nbsp;흐름이&amp;nbsp;많아져서 과정을 살펴보거나 디버깅 및 테스트가 쉽지 않다.&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;예제를 통해 알아보는&lt;span&gt;&lt;span&gt; Chain&amp;nbsp;Of&amp;nbsp;Responsibility&lt;/span&gt;&lt;/span&gt;&lt;b&gt;&amp;nbsp;패턴&lt;/b&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;책임을 분리하여 연결짖기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자로부터 url 문자열을 입력받으면, 각 url의 프로토콜, 도메인, 포트를 파싱해서 정보를 출력해주는 프로그램을 만든다고 해보자.&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;클린하지 않은 문제의 코드 ❌&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로는 아래와 같이 처리문을 하나의 메서드로 통짜로 구성할 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1676964748287&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class UrlParser {
    public static void run(String url) {
        // protocol 파싱
        int index = url.indexOf(&quot;://&quot;);
        if (index != -1) {
            System.out.println(&quot;PROTOCOL : &quot; + url.substring(0, index));
        } else {
            System.out.println(&quot;NO PROTOCOL&quot;);
        }

        // domain 파싱
        int startIndex = url.indexOf(&quot;://&quot;);
        int lastIndex = url.lastIndexOf(&quot;:&quot;);

        System.out.print(&quot;DOMAIN : &quot;);
        if (startIndex == -1) {
            if (lastIndex == -1) {
                System.out.println(url);
            } else {
                System.out.println(url.substring(0, lastIndex));
            }
        } else if (startIndex != lastIndex) {
            System.out.println(url.substring(startIndex + 3, lastIndex));
        } else {
            System.out.println(url.substring(startIndex + 3));
        }

        // port 파싱
        int index2 = url.lastIndexOf(&quot;:&quot;);
        if (index2 != -1) {
            String strPort = url.substring(index2 + 1);
            try {
                int port = Integer.parseInt((strPort));
                System.out.println(&quot;PORT : &quot; + port);
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
        }

    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1676964679933&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Client {
    public static void main(String[] args) {
        String url1 = &quot;http://www.youtube.com:80&quot;;
        System.out.println(&quot;INPUT: &quot; + url1);
        UrlParser.run(url1);

        String url2 = &quot;https://www.inpa.tistory.com:443&quot;;
        System.out.println(&quot;INPUT: &quot; + url2);
        UrlParser.run(url2);

        String url3 = &quot;http://localhost:8080&quot;;
        System.out.println(&quot;INPUT: &quot; + url3);
        UrlParser.run(url3);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;369&quot; data-origin-height=&quot;318&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/070t6/btr0e9Qu99A/6elkxrBJXm0HvaF6b3DepK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/070t6/btr0e9Qu99A/6elkxrBJXm0HvaF6b3DepK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/070t6/btr0e9Qu99A/6elkxrBJXm0HvaF6b3DepK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F070t6%2Fbtr0e9Qu99A%2F6elkxrBJXm0HvaF6b3DepK%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; alt=&quot;Chain-Of-Responsibility&quot; loading=&quot;lazy&quot; width=&quot;369&quot; height=&quot;318&quot; data-origin-width=&quot;369&quot; data-origin-height=&quot;318&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동작에는 문제는 없지만, 이런식으로 구성할 경우 만일 path나 queryString도 구하는 새로운 처리 로직이 추가될 경우 메서드 로직을 통짜로 수정해야할 것이고 이전의 로직과 겹치는게 없는지 복기해야 한다. 당장 코드만 봐도 프로토콜, 도메인, 포트를 구하는데 있어 &lt;s&gt;indexOf()&lt;/s&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;/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;&lt;span&gt;책임 연쇄 패턴을 적용한 코드 ✔️&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 프로토콜만을 파싱하는 책임과 도메인만을 파싱하는 책임 이렇게 처리 책임을 각 객체들로 분리하고 이들 끼리 체인으로 연결함으로써 좀더 유연하게 프로그램 로직을 구성할 수 있게 된다. 이를 그림으로 표현하자면 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;795&quot; data-origin-height=&quot;174&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mQQqJ/btr0fUSNAoN/elYx9UkDyWyAg0TCEYiQ0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mQQqJ/btr0fUSNAoN/elYx9UkDyWyAg0TCEYiQ0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mQQqJ/btr0fUSNAoN/elYx9UkDyWyAg0TCEYiQ0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmQQqJ%2Fbtr0fUSNAoN%2FelYx9UkDyWyAg0TCEYiQ0K%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; alt=&quot;Chain-Of-Responsibility&quot; loading=&quot;lazy&quot; width=&quot;795&quot; height=&quot;174&quot; data-origin-width=&quot;795&quot; data-origin-height=&quot;174&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;책임 연쇄를 적용하는 방법은 그리 어렵지 않다. 기존의 코드에서 각 담당하는 부분을 따로 핸들러(Handler) 객체로 내빼주면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;833&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZpTh6/btr0eS9jF5p/fUvyRPj7qxpVqByK2dsbf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZpTh6/btr0eS9jF5p/fUvyRPj7qxpVqByK2dsbf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZpTh6/btr0eS9jF5p/fUvyRPj7qxpVqByK2dsbf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZpTh6%2Fbtr0eS9jF5p%2FfUvyRPj7qxpVqByK2dsbf1%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; alt=&quot;Chain-Of-Responsibility&quot; loading=&quot;lazy&quot; width=&quot;688&quot; height=&quot;833&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;833&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EC%A0%84%EB%9E%B5Strategy-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90&quot;&gt;전략 패턴&lt;/a&gt;&lt;span&gt;이 전략 알고리즘 코드를 객체화 한 것이고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EC%83%81%ED%83%9CState-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90&quot;&gt;상태 패턴&lt;/a&gt;&lt;span&gt;은 객체의 상태를, 명령 패턴이 커맨드를 객체화 한 것과 같이, 책임 연쇄 패턴은 &lt;b&gt;조건문의 요청 처리 로직 자체를 객체화&lt;/b&gt; 한 것으로 보면 된다. 그리고 일반적인 조건 분기문 같은 경우 다중으로 구성될 수 있으니 이를 체인으로 객체끼리 연결함으로써,&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;각&amp;nbsp;&lt;/span&gt;&lt;/span&gt;if문 로직을 클래스로 표현하였다고 보면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;669&quot; data-origin-height=&quot;289&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Qw6Kn/btr0fKpd9S4/GBjXXKfeX2lkcxQFeDnn7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Qw6Kn/btr0fKpd9S4/GBjXXKfeX2lkcxQFeDnn7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Qw6Kn/btr0fKpd9S4/GBjXXKfeX2lkcxQFeDnn7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQw6Kn%2Fbtr0fKpd9S4%2FGBjXXKfeX2lkcxQFeDnn7k%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; alt=&quot;Chain-Of-Responsibility&quot; loading=&quot;lazy&quot; width=&quot;669&quot; height=&quot;289&quot; data-origin-width=&quot;669&quot; data-origin-height=&quot;289&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1676965809326&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 구체적인 핸들러를 묶는 인터페이스 (추상 클래스)
abstract class Handler {
    // 다음 체인으로 연결될 핸들러
    protected Handler nextHandler = null;

    // 생성자를 통해 연결시킬 핸들러를 등록
    public Handler setNext(Handler handler) {
        this.nextHandler = handler;
        return handler; // 메서드 체이닝 구성을 위해 인자를 그대로 반환함
    }

    // 자식 핸들러에서 구체화 하는 추상 메서드
    protected abstract void process(String url);

    // 핸들러가 요청에 대해 처리하는 메서드 
    public void run(String url) {
        process(url);

        // 만일 핸들러가 연결된게 있다면 다음 핸들러로 책임을 떠넘긴다
        if (nextHandler != null)
            nextHandler.run(url);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1676965815857&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ProtocolHandler extends Handler {
    @Override
    protected void process(String url) {
        int index = url.indexOf(&quot;://&quot;);
        if (index != -1) {
            System.out.println(&quot;PROTOCOL : &quot; + url.substring(0, index));
        } else {
            System.out.println(&quot;NO PROTOCOL&quot;);
        }
    }
}

class DomianHandler extends Handler {
    @Override
    protected void process(String url) {
        int startIndex = url.indexOf(&quot;://&quot;);
        int lastIndex = url.lastIndexOf(&quot;:&quot;);

        System.out.print(&quot;DOMAIN : &quot;);
        if (startIndex == -1) {
            if (lastIndex == -1) {
                System.out.println(url);
            } else {
                System.out.println(url.substring(0, lastIndex));
            }
        } else if (startIndex != lastIndex) {
            System.out.println(url.substring(startIndex + 3, lastIndex));
        } else {
            System.out.println(url.substring(startIndex + 3));
        }
    }
}

class PortHandler extends Handler {
    @Override
    protected void process(String url) {
        int index = url.lastIndexOf(&quot;:&quot;);
        if (index != -1) {
            String strPort = url.substring(index + 1);
            try {
                int port = Integer.parseInt((strPort));
                System.out.println(&quot;PORT : &quot; + port);
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1676965828005&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Client {
    public static void main(String[] args) {
        // 1. 핸들러 생성
        Handler handler1 = new ProtocolHandler();
        Handler handler2 = new DomianHandler();
        Handler handler3 = new PortHandler();

        // 2. 핸들러 연결 설정 (handler1 &amp;rarr; handler2 &amp;rarr; handler3)
        handler1.setNext(handler2).setNext(handler3);

        // 3. 요청에 대한 처리 연쇄 실행
        String url1 = &quot;http://www.youtube.com:80&quot;;
        System.out.println(&quot;INPUT: &quot; + url1);
        handler1.run(url1);

        System.out.println();

        String url2 = &quot;https://www.inpa.tistory.com:443&quot;;
        System.out.println(&quot;INPUT: &quot; + url2);
        handler1.run(url2);

        System.out.println();

        String url3 = &quot;http://localhost:8080&quot;;
        System.out.println(&quot;INPUT: &quot; + url3);
        handler1.run(url3);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;369&quot; data-origin-height=&quot;318&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/070t6/btr0e9Qu99A/6elkxrBJXm0HvaF6b3DepK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/070t6/btr0e9Qu99A/6elkxrBJXm0HvaF6b3DepK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/070t6/btr0e9Qu99A/6elkxrBJXm0HvaF6b3DepK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F070t6%2Fbtr0e9Qu99A%2F6elkxrBJXm0HvaF6b3DepK%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; alt=&quot;Chain-Of-Responsibility&quot; loading=&quot;lazy&quot; width=&quot;369&quot; height=&quot;318&quot; data-origin-width=&quot;369&quot; data-origin-height=&quot;318&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;중첩된 if-else 문 재구성하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째 패턴 예제는 중앙 집권화한 로직을 핸들러 객체로 분리하여 책임을 분산시키는 방법을 소개했다면, 두번째 패턴 예제는 중첩된 if-else 조건문을 책임 연쇄 패턴으로 유연하며 가독성 좋게 구성하는 사례를 들어볼 것이다.&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;클린하지 않은 문제의 코드 ❌&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버가 있고 사용자가 미들웨어를 통해 로그인 하는 과정을 자바의 클래스로 간단하게 표현해 보았다. 코드가 길어보여 어려워보이지만 간단하게 구성해본 예제이다. 서버는 각종 사용자 정보들을 Map 자료형태로 지니고 있고, 미들웨어는 각종 로그인 인증 로직들을 관리하며, 클라이언트에서 각종 미들웨어의 boolean 메서드를 통해 조건 분기문을 통해 로그인 과정을 처리한다.&lt;/p&gt;
&lt;pre id=&quot;code_1676969079433&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Server {
    private Map&amp;lt;String, String&amp;gt; users = new HashMap&amp;lt;&amp;gt;();

    // 서버에 유저 등록
    public void register(String email, String password) {
        users.put(email, password);
    }

    // 서버에 해당 이메일이 가입되어 있는지
    public boolean hasEmail(String email) {
        return users.containsKey(email);
    }
    
    // 서버에 해당 계정의 비밀번호가 일치하는지
    public boolean isValidPassword(String email, String password) {
        return users.get(email).equals(password);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1676969085613&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Middleware {
    private int limit = 3;
    private int count = 0;

    Server server;

    public Middleware(Server server) {
        this.server = server;
    }

    // 로그인 횟수 시도
    public boolean limitLoginAttempt() {
        if (count &amp;gt; limit) {
            System.out.println(&quot;로그인 요청 횟수 제한 !!&quot;);
            return false;
        }
        count++;
        return true;
    }

    // 아이디, 패스워드 인증
    public boolean authorize(String email, String password) {
        if (!server.hasEmail(email)) {
            System.out.println(&quot;가입된 이메일 계정이 없습니다 !&quot;);
            return false;
        }

        if (!server.isValidPassword(email, password)) {
            System.out.println(&quot;패스워드가 다릅니다 !&quot;);
            return false;
        }

        System.out.println(&quot;로그인 완료 !!&quot;);
        return true;
    }

    // 사용자가 admin 계정인지, 혹은 일반 user 계정인지 검증
    public boolean authentication(String email) {
        if (email.equals(&quot;inpa@tistory.com&quot;)) {
            System.out.println(&quot;Hello, admin!&quot;);
            return true;
        } else {
            System.out.println(&quot;Hello, user!&quot;);
            return false;
        }
    }

    // 만일 관리자가 아닌 일반 유저일 경우 유저의 요청에 대해선 모두 로깅
    public void logging() {
        System.out.println(&quot;요청을 로깅합니다.&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1676969227594&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Client {
    public static void main(String[] args) throws IOException {
        // 1. 서버 생성 및 등록
        Server server = new Server();
        server.register(&quot;john@naver.com&quot;, &quot;123123&quot;);
        server.register(&quot;kail@google.com&quot;, &quot;456456&quot;);
        server.register(&quot;inpa@tistory.com&quot;, &quot;789789&quot;);

        // 2. 인증 로직을 처리하는 미들웨어 생성
        Middleware middleware = new Middleware(server);

        // 3. 클라이언트로부터 로그인 시도
        do {
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            System.out.print(&quot;\nEmail: &quot;);
            String email = reader.readLine();
            System.out.print(&quot;Password: &quot;);
            String password = reader.readLine();

            // 만일 로그인 횟수가 남아있을 경우
            if (middleware.limitLoginAttempt()) {
                // 이메일 패스워드 인증
                if (middleware.authorize(email, password)) {
                    // 인증되었을 경우 일반 유저 계정인지 관리자 계정인지 검증
                    if (middleware.authentication(email)) {
                        // ...
                    } else {
                        // 관리자가 아닌 일반 유저 계정일 경우 로깅 처리
                        middleware.logging();
                    }
                    break;
                } else {
                    continue;
                }
            } else {
                throw new RuntimeException(&quot;로그인 시도 횟수 초과로 프로그램을 종료합니다&quot;);
            }
        } while (true);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;303&quot; data-origin-height=&quot;304&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bw8iaG/btr0hqqrTFS/B5qMiLdEoF76Ixojdo1K8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bw8iaG/btr0hqqrTFS/B5qMiLdEoF76Ixojdo1K8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bw8iaG/btr0hqqrTFS/B5qMiLdEoF76Ixojdo1K8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbw8iaG%2Fbtr0hqqrTFS%2FB5qMiLdEoF76Ixojdo1K8K%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; alt=&quot;Chain-Of-Responsibility&quot; loading=&quot;lazy&quot; width=&quot;303&quot; height=&quot;304&quot; data-origin-width=&quot;303&quot; data-origin-height=&quot;304&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;224&quot; data-origin-height=&quot;97&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dboFqx/btr0aQqMJ6J/KPBPjsUXFJiagUmLKM1eNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dboFqx/btr0aQqMJ6J/KPBPjsUXFJiagUmLKM1eNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dboFqx/btr0aQqMJ6J/KPBPjsUXFJiagUmLKM1eNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdboFqx%2Fbtr0aQqMJ6J%2FKPBPjsUXFJiagUmLKM1eNk%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; alt=&quot;Chain-Of-Responsibility&quot; loading=&quot;lazy&quot; width=&quot;224&quot; height=&quot;97&quot; data-origin-width=&quot;224&quot; data-origin-height=&quot;97&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동작은 문제없어 보이지만 딱봐도 클라이언트 쪽 실행 코드가 굉장히 복잡하다. 여러 인증 로직들을 하나하나 따져가며 만일 '참'일 경우 다음 인증 로직을 따져가는 중첩 if-else문 형태로 되어 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;628&quot; data-origin-height=&quot;406&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/edzPVN/btr0aQLaRRx/zAWDHl86cskhaItkxEGds1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/edzPVN/btr0aQLaRRx/zAWDHl86cskhaItkxEGds1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/edzPVN/btr0aQLaRRx/zAWDHl86cskhaItkxEGds1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FedzPVN%2Fbtr0aQLaRRx%2FzAWDHl86cskhaItkxEGds1%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; alt=&quot;Chain-Of-Responsibility&quot; loading=&quot;lazy&quot; width=&quot;628&quot; height=&quot;406&quot; data-origin-width=&quot;628&quot; data-origin-height=&quot;406&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 코드는 코드를 복기할때나 테스트할때 굉장히 난해하게 되며 특히나 if문 중첩은 단계가 늘어날 수록 중첩 블록이 분기에서 안정적으로 작동하는지 확신하기도 어렵다. 거기다 만일 새로운 인증 메서드가 추가된다면 분기문 전체를 뜯어 고쳐야 될 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Bp8qW/btr0aT2jZEQ/cto1mAEXXvnvjR839J001k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Bp8qW/btr0aT2jZEQ/cto1mAEXXvnvjR839J001k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Bp8qW/btr0aT2jZEQ/cto1mAEXXvnvjR839J001k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBp8qW%2Fbtr0aT2jZEQ%2Fcto1mAEXXvnvjR839J001k%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; alt=&quot;Chain-Of-Responsibility&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;240&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;370&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WwviF/btr0e76x003/fdGXIJvdfywrRH21zMa8w1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WwviF/btr0e76x003/fdGXIJvdfywrRH21zMa8w1/img.png&quot; data-alt=&quot;코드가 커질수록 점점 복잡해진다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WwviF/btr0e76x003/fdGXIJvdfywrRH21zMa8w1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWwviF%2Fbtr0e76x003%2FfdGXIJvdfywrRH21zMa8w1%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; alt=&quot;Chain-Of-Responsibility&quot; loading=&quot;lazy&quot; width=&quot;610&quot; height=&quot;370&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;370&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;책임 연쇄 패턴을 적용한 코드 ✔️&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 각 인증 로직들이 별개의 메서드로 분리되어 있지만, 결국 실행부에선 복잡한 if-else로 구성되는건 마찬가지이고 하나의 미들웨어 클래스에서 중앙 집권 적인 책임을 지니고 있기 때문에, 책임 연쇄 패턴으로 인증 로직들을 별개의 핸들러 클래스로 분리하고 체이닝 함으로써 if-else 형태를 보다 유연하게 구성할 수가 있게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQsCH9/btr0gCEDR0N/RSg9J1PIpM6qXLJinj2YdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQsCH9/btr0gCEDR0N/RSg9J1PIpM6qXLJinj2YdK/img.png&quot; data-alt=&quot;인증 로직들을 별개의 핸들러로 분리하고 연결한다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQsCH9/btr0gCEDR0N/RSg9J1PIpM6qXLJinj2YdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQsCH9%2Fbtr0gCEDR0N%2FRSg9J1PIpM6qXLJinj2YdK%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; alt=&quot;Chain-Of-Responsibility&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;160&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;160&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;이번 패턴 적용 예제는 첫번째 예제와는 약간 차이를 두었는데, 핸들러 메서드를 추상 메서드로 각 객체별로 구체화 하는 것이 아닌, 부모의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;check()&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;메서드를 자식 핸들러에서 오버라이드(Override)하고, 만일 체인 호출이 필요할 경우&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;super&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;키워드를 통해 부모 메서드를 호출함으로써 다음 핸들러를 호출하는, 자바 클래스의 상속 특징을 이용하였다라고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 요청에 대한 로직 및 결과를 모두 핸들러 클래스에게 위임 했으니, 복잡한 if-else 분기에 따른 로직을 처리하기 위해 핸들러에서 flag 라는 정수를 반환하도록 하여, 실행부에서 정수값에 따라 루프문 동작을 제어하는 식으로 구성하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;517&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgTxbv/btr0dq0okq3/c7rDiQKaQXUEDNZ1bXyof0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgTxbv/btr0dq0okq3/c7rDiQKaQXUEDNZ1bXyof0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgTxbv/btr0dq0okq3/c7rDiQKaQXUEDNZ1bXyof0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgTxbv%2Fbtr0dq0okq3%2Fc7rDiQKaQXUEDNZ1bXyof0%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; alt=&quot;Chain-Of-Responsibility&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;517&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;517&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1677033265329&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Handler 역할
abstract class Middleware {
    // 다음 체인으로 연결될 핸들러
    protected Middleware nextMiddleware = null;

    // setter 메서드를 통해 연결시킬 핸들러를 등록
    public Middleware setNext(Middleware middleware) {
        this.nextMiddleware = middleware;
        return middleware; // 메서드 체이닝 구성을 위해 인자를 그대로 반환함
    }

    // 핸들러가 요청에 대해 처리하는 메서드
    public short check(String email, String password) {
        /*
          flag는 클라이언트 실행부에서 while문을 빠져나가기 위한 조건값으로 사용된다
          -2 : Exception 일으킴
          -1 : break 동작 (루프문 종료)
          0 : continue 동작 (처음부터 콘솔 입력 받기)
          1 : 그대로 처리를 진행함
         */
        short flag = 1;

        // 만일 핸들러가 연결된게 있다면 다음 핸들러로 책임을 떠넘긴다
        if (nextMiddleware != null) {
            flag = nextMiddleware.check(email, password);
        }

        return flag;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1677033268480&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ConcreteHandler 역할
class LimitLoginAttemptMiddleware extends Middleware {
    private int limit = 3; // 로그인 시도 최대 횟수 3회
    private int count = 0;

    @Override
    public short check(String email, String password) {
        short flag = 1;

        if (count &amp;gt; limit) {
            System.out.println(&quot;로그인 요청 횟수 제한 !!&quot;);
            flag = -2; // Exception 플래그
        } else {
            flag = super.check(email, password);
        }

        count++;

        return flag;
    }
}

class AuthorizeMiddleware extends Middleware {
    private Server server;

    public AuthorizeMiddleware(Server server) {
        this.server = server;
    }

    @Override
    public short check(String email, String password) {
        short flag = 1;

        if (!server.hasEmail(email)) {
            System.out.println(&quot;This email is not registered!&quot;);
            flag = 0; // continue 플래그
        } else if (!server.isValidPassword(email, password)) {
            System.out.println(&quot;Wrong password!&quot;);
            flag = 0; // continue 플래그
        } else {
            flag = super.check(email, password);
        }

        return flag;
    }
}

class AuthenticationMiddleware extends Middleware {
    @Override
    public short check(String email, String password) {
        short flag = 1;

        if (email.equals(&quot;inpa@tistory.com&quot;)) {
            System.out.println(&quot;Hello, admin!&quot;);
            flag = -1; // break 플래그
        } else {
            System.out.println(&quot;Hello, user!&quot;);
            flag = super.check(email, password);
        }

        return flag;
    }
}

class LoggingMiddleware extends Middleware {
    @Override
    public short check(String email, String password) {
        System.out.println(&quot;요청을 로깅합니다.&quot;);
        return -1; // break 플래그
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1677033277172&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Client {
    public static void main(String[] args) throws IOException {
        // 1. 서버 생성 및 등록
        Server server = new Server();
        server.register(&quot;john@naver.com&quot;, &quot;123123&quot;);
        server.register(&quot;kail@google.com&quot;, &quot;456456&quot;);
        server.register(&quot;inpa@tistory.com&quot;, &quot;789789&quot;);

        // 2. 인증 로직을 처리하는 핸들러 생성
        LimitLoginAttemptMiddleware middleware1 = new LimitLoginAttemptMiddleware();
        AuthorizeMiddleware middleware2 = new AuthorizeMiddleware(server);
        AuthenticationMiddleware middleware3 = new AuthenticationMiddleware();
        LoggingMiddleware middleware4 = new LoggingMiddleware();

        // 3. 핸들러 체인
        middleware1
                .setNext(middleware2)
                .setNext(middleware3)
                .setNext(middleware4);

        // 4. 클라이언트로부터 로그인 시도
        do {
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            System.out.print(&quot;\nEmail: &quot;);
            String email = reader.readLine();
            System.out.print(&quot;Password: &quot;);
            String password = reader.readLine();

            // 핸들러부터 정수 flag를 받아, 정수값에 따라 루프문 다음 동작을 처리
            short result = middleware1.check(email, password);

            if (result == -2) {
                throw new RuntimeException(&quot;로그인 시도 횟수 초과로 프로그램을 종료합니다&quot;);
            } else if (result == -1) {
                break;
            } else if (result == 0) {
                continue;
            }

        } while (true);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;266&quot; data-origin-height=&quot;196&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vatOt/btr0gCxYB3p/a4LzFanqptvfGl0YKnFTDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vatOt/btr0gCxYB3p/a4LzFanqptvfGl0YKnFTDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vatOt/btr0gCxYB3p/a4LzFanqptvfGl0YKnFTDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvatOt%2Fbtr0gCxYB3p%2Fa4LzFanqptvfGl0YKnFTDK%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; alt=&quot;Chain-Of-Responsibility&quot; loading=&quot;lazy&quot; width=&quot;266&quot; height=&quot;196&quot; data-origin-width=&quot;266&quot; data-origin-height=&quot;196&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;315&quot; data-origin-height=&quot;255&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yg1ef/btr0dpNraZZ/AzxWUEGf5kXtfIHgbdvPtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yg1ef/btr0dpNraZZ/AzxWUEGf5kXtfIHgbdvPtk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yg1ef/btr0dpNraZZ/AzxWUEGf5kXtfIHgbdvPtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fyg1ef%2Fbtr0dpNraZZ%2FAzxWUEGf5kXtfIHgbdvPtk%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; alt=&quot;Chain-Of-Responsibility&quot; loading=&quot;lazy&quot; width=&quot;315&quot; height=&quot;255&quot; data-origin-width=&quot;315&quot; data-origin-height=&quot;255&quot;/&gt;&lt;/span&gt;&lt;/figure&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;style1&quot; /&gt;
&lt;h2 id=&quot;실무에서_찾아보는_전략_패턴&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;실무에서 찾아보는 Chain&amp;nbsp;Of&amp;nbsp;Responsibility 패턴&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;Java&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Java&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;java.util.logging.Logger의 log()&lt;/li&gt;
&lt;li&gt;javax.servlet.Filter의 doFilter()&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 요청시 서블릿에 접근하기 전에 다양한 필터를 거치도록 할 수 있다. 만약 아래와 같은 컨트롤러가 있고, &quot;/hello&quot;로 요청을 받으면 필터를 타도록한다.&lt;/p&gt;
&lt;pre id=&quot;code_1676894964293&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@ServletComponentScan
@RestController
public class HelloController {
    @GetMapping(&quot;/hello&quot;)
    public String hello() {
        return &quot;hello&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1676894994241&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@WebFilter(urlPatterns = &quot;/hello&quot;) // 특정 url 서블릿에 접근할시 다음 필터를 적용
public class MyFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println(&quot;게임 참여&quot;);
        chain.doFilter(request, response);
        System.out.println(&quot;게임 끝&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 id=&quot;Spring_Framework&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Spring Framework&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 프로젝트에 Spring Securiry 의존성을 추가하고 아래와 같이 Security 필터에 대한 체인을 설정을 할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1676895035421&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    // 체인 필터를 설정하는 코드
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 모든 페이지의 접근권한을 인증없이 허가
        http.authorizeRequests().anyRequest().permitAll().and().addFilter(new MyFilter());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot; data-swap-href=&quot;https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4?inst=cf68b212&quot;&gt;코딩으로 학습하는 GoF의 디자인 패턴 - 백기선&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://refactoring.guru/design-patterns/chain-of-responsibility&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot; data-swap-href=&quot;https://youtu.be/FAHEWQD6EVE&quot;&gt;GIS DEVELOPER 유튜브 - Chain&amp;nbsp;of&amp;nbsp;Responsibility&lt;/p&gt;</description>
      <category>디자인 패턴/GOF</category>
      <category>Design Pattern</category>
      <category>객체 책임</category>
      <category>디자인 패턴</category>
      <category>책임 연쇄</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1089</guid>
      <comments>https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-Chain-Of-Responsibility-%ED%8C%A8%ED%84%B4-%EC%99%84%EB%B2%BD-%EB%A7%88%EC%8A%A4%ED%84%B0%ED%95%98%EA%B8%B0#entry1089comment</comments>
      <pubDate>Wed, 22 Mar 2023 08:57:47 +0900</pubDate>
    </item>
    <item>
      <title>  복합체(Composite) 패턴 - 완벽 마스터하기</title>
      <link>https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EB%B3%B5%ED%95%A9%EC%B2%B4Composite-%ED%8C%A8%ED%84%B4-%EC%99%84%EB%B2%BD-%EB%A7%88%EC%8A%A4%ED%84%B0%ED%95%98%EA%B8%B0</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img (84).webp&quot; data-origin-width=&quot;1237&quot; data-origin-height=&quot;660&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cP3rQO/btrXGUhghlG/bgRwBNeanxhKKtR2ZTvc40/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cP3rQO/btrXGUhghlG/bgRwBNeanxhKKtR2ZTvc40/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cP3rQO/btrXGUhghlG/bgRwBNeanxhKKtR2ZTvc40/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcP3rQO%2FbtrXGUhghlG%2FbgRwBNeanxhKKtR2ZTvc40%2Fimg.webp&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; alt=&quot;Composite-pattern&quot; loading=&quot;lazy&quot; width=&quot;1237&quot; height=&quot;660&quot; data-filename=&quot;img (84).webp&quot; data-origin-width=&quot;1237&quot; data-origin-height=&quot;660&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;100&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwyhdK/btrWVLSiL4U/6N8e9pRknJynvt7nKiqEm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwyhdK/btrWVLSiL4U/6N8e9pRknJynvt7nKiqEm1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwyhdK/btrWVLSiL4U/6N8e9pRknJynvt7nKiqEm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwyhdK%2FbtrWVLSiL4U%2F6N8e9pRknJynvt7nKiqEm1%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; alt=&quot;Composite-pattern&quot; loading=&quot;lazy&quot; width=&quot;140&quot; height=&quot;100&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;100&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Composite Pattern&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복합체 패턴(Composite Pattern)은 &lt;b&gt;복합 객체(Composite) &lt;/b&gt;와 &lt;b&gt;단일 객체(Leaf)&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;폴더(디렉토리) 안에는 파일이 들어 있을수도 있고 파일을 담은 또 다른 폴더도 들어있을 수 있다. 이를 복합적으로 담을수 있다 해서 Composite 객체라고 불리운다. 반면 파일은 단일 객체 이기 때문에 이를 Leaf 객체라고 불리운다. 즉 Leaf는 자식이 없다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;414&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/effnAt/btrXuaLnBFM/w2P1ro4uWfSVNZTOQa5Kl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/effnAt/btrXuaLnBFM/w2P1ro4uWfSVNZTOQa5Kl1/img.png&quot; data-alt=&quot;폴더-파일 관계 와 Composite-Leaf 관계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/effnAt/btrXuaLnBFM/w2P1ro4uWfSVNZTOQa5Kl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeffnAt%2FbtrXuaLnBFM%2Fw2P1ro4uWfSVNZTOQa5Kl1%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; alt=&quot;Composite-pattern&quot; loading=&quot;lazy&quot; width=&quot;414&quot; height=&quot;300&quot; data-origin-width=&quot;414&quot; data-origin-height=&quot;300&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;폴더-파일 관계 와 Composite-Leaf 관계&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;복합체 패턴은 바로 이 폴더와 파일을 동일한 타입으로 취급하여 구현을 단순화 시키는 것이 목적이다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;폴더 안에는 파일 뿐만 아니라 서브 폴더가 올수 있고 또 서브 폴더안에 서브 폴더가 오고.. 이런식으로 계층 구조를 구현하다 보면, &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;자칫 복잡해 질 수 도 있는 복합 객체를 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;u&gt;&lt;b&gt;재귀 동작&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;을 통해 하위 객체들에게 작업을 위임한다. 그러면 복합 객체와 단일 객체를 대상으로 똑같은 작업을 적용할 수 있어 단일 / 복합 객체를 구분할 필요가 거의 없어진다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;윈도우에선 폴더와 파일은 엄연히 다른 놈이지만, 리눅스(Unix)에선 디렉토리와 파일은 모두 파일로 취급된다. 어찌보면 리눅스 OS가 복합체(Composite) 패턴을 통해 계층 구조를 구현하였다고 볼 수 있다. &lt;br /&gt;당연히 실제 설계는 다르겠지만, 복합체 패턴은 입문하기 어려운 패턴중 하나이기에 이러한 비유를 통해 좀더 패턴에 친숙하게 접근하도록 해보자.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하자면,&amp;nbsp;&lt;span style=&quot;color: #ee2323;&quot;&gt;Composite 패턴은&amp;nbsp;그릇과 내용물을 동일시해서 재귀적인 구조를 만들기 위한 디자인 패턴&lt;/span&gt;이라고 말할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Composite 패턴 구조&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;620&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cz8EwC/btrXtSLeMUg/kVboikXD5y70G2rOpTMWP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cz8EwC/btrXtSLeMUg/kVboikXD5y70G2rOpTMWP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cz8EwC/btrXtSLeMUg/kVboikXD5y70G2rOpTMWP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcz8EwC%2FbtrXtSLeMUg%2FkVboikXD5y70G2rOpTMWP0%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; alt=&quot;Composite-pattern&quot; loading=&quot;lazy&quot; width=&quot;1018&quot; height=&quot;620&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;620&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;Component&lt;/b&gt; : Leaf와 Compsite 를 묶는 공통적인 상위 인터페이스&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Composite&lt;/b&gt;&lt;/span&gt; : 복합 객체로서, Leaf 역할이나 Composite 역할을 넣어 관리하는 역할을 한다.&amp;nbsp;&amp;nbsp;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Component 구현체들을 &lt;span style=&quot;color: #ee2323;&quot;&gt;내부 리스트&lt;/span&gt;로 관리한다&lt;/li&gt;
&lt;li&gt;add 와 remove 메소드는 내부 리스트에 단일 / 복합 객체를 저장&lt;/li&gt;
&lt;li&gt;Component 인터페이스의 구현 메서드인 operation은 복합 객체에서 호출되면 &lt;span style=&quot;color: #ee2323;&quot;&gt;재귀&lt;/span&gt; 하여, 추가 단일 객체를 저장한 하위 복합 객체를 순회하게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt;Leaf&lt;/b&gt;&lt;/span&gt;: 단일 객체로서, 단순하게 내용물을 표시하는 역할을 한다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Component 인터페이스의 구현 메서드인&lt;span&gt;&amp;nbsp;&lt;/span&gt;operation은 단일 객체에서 호출되면 적절한 값만 반환한다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Client&lt;/b&gt; : 클라이언트는 Component를 참조하여 단일 / 복합 객체를 하나의 객체로서 다룬다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복합체 패턴의 핵심은 Composite 와 Leaf가 동시에 구현하는 &lt;s&gt;operation()&lt;/s&gt; 인터페이스 추상 메서드를 정의하고, Composite 객체의 &lt;s&gt;operation()&lt;/s&gt; 메서드는 자기 자신을 호출하는 재귀 형태로 구현하는 것이다. 왜냐하면 폴더 안에 폴더를 넣고, 그 안에 또 폴더를 넣고 파일을 넣는 트리 구조를 생각해보면, 재귀적으로 반복되는 형식이 나타나기 때문이다. 그래서&amp;nbsp;단일체와 복합체를 동일한 개체로 취급하여 처리하기 위해 재귀 함수 원리를 이용한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Composite 패턴&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;흐름&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;클래스 구성&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1675061674856&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface Component {
    void operation();
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1675061670688&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Leaf implements Component {

    @Override
    public void operation() {
        System.out.println(this + &quot; 호출&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1675061666141&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Composite implements Component {

    // Leaf 와 Composite 객체 모두를 저장하여 관리하는 내부 리스트
    List&amp;lt;Component&amp;gt; components = new ArrayList&amp;lt;&amp;gt;();

    public void add(Component c) {
        components.add(c); // 리스트 추가
    }

    public void remove(Component c) {
        components.remove(c); // 리스트 삭제
    }

    @Override
    public void operation() {
        System.out.println(this + &quot; 호출&quot;);
        
        // 내부 리스트를 순회하여, 단일 Leaf이면 값을 출력하고,
        // 또다른 서브 복합 객체이면, 다시 그 내부를 순회하는 재귀 함수 동작이 된다.
        for (Component component : components) {
            component.operation(); // 자기 자신을 호출(재귀)
        }
    }
    
    public List&amp;lt;Component&amp;gt; getChild() {
        return components;
    }
}&lt;/code&gt;&lt;/pre&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;클래스 흐름&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1675133409321&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Client {
    public static void main(String[] args) {
        // 1. 최상위 복합체 생성
        Composite composite1 = new Composite();

        // 2. 최상위 복합체에 저장할 Leaf와 또다른 서브 복합체 생성
        Leaf leaf1 = new Leaf();
        Composite composite2 = new Composite();

        // 3. 최상위 복합체에 개체들을 등록
        composite1.add(leaf1);
        composite1.add(composite2);

        // 4. 서브 복합체에 저장할 Leaf 생성
        Leaf leaf2 = new Leaf();
        Leaf leaf3 = new Leaf();
        Leaf leaf4 = new Leaf();

        // 5. 서브 복합체에 개체들을 등록
        composite2.add(leaf2);
        composite2.add(leaf3);
        composite2.add(leaf4);

        // 6. 최상위 복합체의 모든 자식 노드들을 출력
        composite1.operation();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;350&quot; data-origin-height=&quot;147&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VWgE0/btrXypbbtjU/LgKKOhdVdLbD8kAxGyn62k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VWgE0/btrXypbbtjU/LgKKOhdVdLbD8kAxGyn62k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VWgE0/btrXypbbtjU/LgKKOhdVdLbD8kAxGyn62k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVWgE0%2FbtrXypbbtjU%2FLgKKOhdVdLbD8kAxGyn62k%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; alt=&quot;Composite-pattern&quot; loading=&quot;lazy&quot; width=&quot;350&quot; height=&quot;147&quot; data-origin-width=&quot;350&quot; data-origin-height=&quot;147&quot;/&gt;&lt;/span&gt;&lt;/figure&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/mwlE5/btrXt9NhZTV/PB7CzfRcchVHY7Uzu12GJK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mwlE5/btrXt9NhZTV/PB7CzfRcchVHY7Uzu12GJK/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;376&quot; data-filename=&quot;img (1) (1).jpg&quot; style=&quot;width: 40.3643%; margin-right: 10px;&quot; data-widthpercent=&quot;40.84&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mwlE5/btrXt9NhZTV/PB7CzfRcchVHY7Uzu12GJK/img.jpg&quot; alt=&quot;Composite-pattern&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmwlE5%2FbtrXt9NhZTV%2FPB7CzfRcchVHY7Uzu12GJK%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;582&quot; height=&quot;376&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhJsTl/btrXrU3YluL/82UTNU2HXePat0gQT8p700/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhJsTl/btrXrU3YluL/82UTNU2HXePat0gQT8p700/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;454&quot; data-filename=&quot;img (2) (1).jpg&quot; style=&quot;width: 58.4729%;&quot; data-widthpercent=&quot;59.16&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhJsTl/btrXrU3YluL/82UTNU2HXePat0gQT8p700/img.jpg&quot; alt=&quot;Composite-pattern&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhJsTl%2FbtrXrU3YluL%2F82UTNU2HXePat0gQT8p700%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;1018&quot; height=&quot;454&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트에서 operation 메서드를 호출하게 되면, 단일체일 경우 값이 호출 되고, 복합체일 경우 자기 자신을 호출하는 재귀 함수에 의해 저장하고 있는 하위 Leaf 객체들을 순회하여 호출하게 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Composite 패턴&lt;span&gt;&amp;nbsp;특징&lt;/span&gt;&lt;/b&gt;&lt;/h3&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;패&lt;/b&gt;&lt;b&gt;턴 사용 시기&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터를 다룰때 &lt;b&gt;계층적&lt;/b&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;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;패턴 장점&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단일체와 복합체를 동일하게 여기기 때문에 묶어서 연산하거나 관리할 때 편리하다.&lt;/li&gt;
&lt;li&gt;다형성 재귀를 통해 복잡한 트리 구조를 보다 편리하게 구성 할 수 있다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;수평적, 수직적 모든 방향으로 객체를 확장할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;새로운 Leaf 클래스를 추가하더라도 클라이언트는 추상화된 인터페이스 만을 바라보기 때문에 &lt;a href=&quot;https://inpa.tistory.com/entry/OOP-%F0%9F%92%A0-%EC%95%84%EC%A3%BC-%EC%89%BD%EA%B2%8C-%EC%9D%B4%ED%95%B4%ED%95%98%EB%8A%94-OCP-%EA%B0%9C%EB%B0%A9-%ED%8F%90%EC%87%84-%EC%9B%90%EC%B9%99&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;개방 폐쇄 원칙(OCP)&lt;/a&gt;을 준수 한다. (단일 부분의 확장이 용이)&lt;/li&gt;
&lt;/ul&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;패턴 단점&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;재귀 호출 특징 상 트리의 깊이(depth)가 깊어질 수록 디버깅에 어려움이 생긴다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; letter-spacing: 0px;&quot;&gt;설계가 지나치게 범용성을 갖기 때문에 새로운 요소를 추가할 때 복합 객체에서 구성 요소에 제약을 갖기 힘들다.&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;예를들어, &lt;span&gt;계층형 구조에서 leaf 객체와 composite 객체들을 모두 동일한 인터페이스로 다루어야하는데, 이 공통 인터페이스 설계가 까다로울 수 있다.&lt;/span&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;span style=&quot;color: #000000;&quot;&gt;수평적 방향으로만 확장이 가능하도록 Leaf를 제한하는 Composite를 만들때&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;예제를 통해 알아보는&lt;span&gt;&lt;span&gt; Composite&lt;/span&gt;&lt;/span&gt;&lt;b&gt;&amp;nbsp;패턴&lt;/b&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;아이템을 담는 가방을 담은 가방&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복합(composite) 객체와 단일(leaf) 객체를 상자와 아이템으로 비유하자면 아래 그림과 같이 표현할 수 있을 것이다. 상자 안에 아이템을 넣고, 다시 그 상자를 상자 안에 넣는 식으로 말이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1145&quot; data-origin-height=&quot;695&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Dh7zg/btrXt88E60D/QNKKokivokMXBQFbPyEQvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Dh7zg/btrXt88E60D/QNKKokivokMXBQFbPyEQvk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Dh7zg/btrXt88E60D/QNKKokivokMXBQFbPyEQvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDh7zg%2FbtrXt88E60D%2FQNKKokivokMXBQFbPyEQvk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;750&quot; height=&quot;455&quot; data-origin-width=&quot;1145&quot; data-origin-height=&quot;695&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;다음 Item 클래스와 그를 담는 Bag 클래스가 있다고 하자. 가방안에 아이템을 담는 형식이니, Item 클래스는 Leaf가 되고 Bag 클래스는 Composite가 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드에서 우리가 구현하고 싶은 것은 Bag 속 리스트에 담겨진 Item 객체들의 총 가격(price) 값을 추출하고 싶다고 한다. 그런데 단순히 가방 안에 아이템들이 들어있을 뿐 아니라 복수의 아이템을 담은 또다른 가방들이 여러개 들어 있을 수 있고 그 가방안에 가방이 들어있을 수 있다. 그렇다면 루트 가방일 경우 하위 계층까지 순회하여 아이템의 price값을 합산해야 되는데 이러한 계층 트리 구조를 컴포지트 패턴으로 클래스를 구성을 하면 아래와 같이 되게 된다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Composite와 Leaf 객체를 공용으로 묶는 ItemComponent 인터페이스를 정의하고, Composite와 Leaf 객체에서 동시에 쓰이는 추상 메소드를 정의한다.&lt;/li&gt;
&lt;li&gt;Composite 객체인 Bag 클래스에서 &lt;span&gt;ItemComponent&lt;span&gt; 타입의 공용 아이템을 담는 내부 리스트를 정의한다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;Component 인터페이스의 공통적인 operation인 &lt;s&gt;getPrice()&lt;/s&gt; 메서드는 Item일 경우 그대로 price값을 반환하면 되지만, Bag일 경우 가방은 단순히 아이템을 담는 그릇일 뿐이기 때문에 자기 자신을 호출하여 현재 가방에 들어있는 Item을 순회하는 재귀 동작을 행하게 된다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;947&quot; data-origin-height=&quot;440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dPzftc/btrXBlMMz62/PbcupCOCCrj1XoJoG6MPU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dPzftc/btrXBlMMz62/PbcupCOCCrj1XoJoG6MPU1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dPzftc/btrXBlMMz62/PbcupCOCCrj1XoJoG6MPU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdPzftc%2FbtrXBlMMz62%2FPbcupCOCCrj1XoJoG6MPU1%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; alt=&quot;Composite-pattern&quot; loading=&quot;lazy&quot; width=&quot;947&quot; height=&quot;440&quot; data-origin-width=&quot;947&quot; data-origin-height=&quot;440&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1675054894015&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Component 인터페이스
interface ItemComponent {
    int getPrice();
    String getName();
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1675139838389&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Composite 객체
class Bag implements ItemComponent {
    // 아이템들과 서브 가방 모두를 저장하기 위해 인터페이스 타입 리스트로 관리
    List&amp;lt;ItemComponent&amp;gt; components = new ArrayList&amp;lt;&amp;gt;();

    String name; // 가방 이름

    public Bag(String name) {
        this.name = name;
    }

    // 리스트에 아이템 &amp;amp; 가방 추가
    public void add(ItemComponent item) {
        components.add(item); 
    }

    // 현재 가방의 내용물을 반환
    public List&amp;lt;ItemComponent&amp;gt; getComponents() {
        return components; 
    }

    @Override
    public int getPrice() {
        int sum = 0;

        for (ItemComponent component : components) {
            // 만일 리스트에서 가져온 요소가 Item이면 정수값을 받을 것이고, Bag이면 '재귀 함수' 동작이 되게 된다 ☆
            sum += component.getPrice(); // 자기 자신 호출(재귀)
        }

        return sum; // 그렇게 재귀적으로 돌아 하위 아이템들의 값을 더하고 반환하게 된다.
    }

    @Override
    public String getName() {
        return name;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1675059877996&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Leaf 객체
class Item implements ItemComponent {
    String name; // 아이템 이름
    int price; // 아이템 가격

    public Item(String name, int price) {
        this.name = name;
        this.price = price;
    }

    @Override
    public int getPrice() {
        return price;
    }

    @Override
    public String getName() {
        return name;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1675054915743&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Client {
    public static void main(String[] args) {

        // 1. 메인 가방 인스턴스 생성
        Bag bag_main = new Bag(&quot;메인 가방&quot;);

        // 2. 아이템 인스턴스 생성
        Item armor = new Item(&quot;갑옷&quot;, 250);
        Item sword = new Item(&quot;장검&quot;, 500);

        // 3. 메인 가방에는 모험에 필요한 무구 아이템만을 추가
        bag_main.add(armor);
        bag_main.add(sword);

        // 4. 서브 가방 인스턴스 생성
        Bag bag_food = new Bag(&quot;음식 가방&quot;);

        // 5. 아이템 인스턴스 생성
        Item apple = new Item(&quot;사과&quot;, 400);
        Item banana = new Item(&quot;바나나&quot;, 130);

        // 6. 서브 가방에는 음식 아이템만을 추가
        bag_food.add(apple);
        bag_food.add(banana);

        // 7. 서브 가방을 메인 가방에 넣음
        bag_main.add(bag_food);

        // ----------------------------------------------------- //

        Client client = new Client();

        // 가방 안에 있는 모든 아이템의 총 값어치를 출력 (가방안에 아이템 뿐만 아니라 서브 가방도 들어있음)
        client.printPrice(bag_main);

        // 서브 가방 안에 있는 모든 아이템의 총 값어치를 출력
        client.printPrice(bag_food);
    }

    public void printPrice(ItemComponent bag) {
        int result = bag.getPrice();
        System.out.println(bag.getName() + &quot;의 아이템 총합 : &quot; + result + &quot; 골드&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;282&quot; data-origin-height=&quot;54&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8xck4/btrXA96xOYO/rAkG6fq9SIB4FVztxJYF7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8xck4/btrXA96xOYO/rAkG6fq9SIB4FVztxJYF7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8xck4/btrXA96xOYO/rAkG6fq9SIB4FVztxJYF7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8xck4%2FbtrXA96xOYO%2FrAkG6fq9SIB4FVztxJYF7k%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; alt=&quot;Composite-pattern&quot; loading=&quot;lazy&quot; width=&quot;282&quot; height=&quot;54&quot; data-origin-width=&quot;282&quot; data-origin-height=&quot;54&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트에서는 ItemComponent 만을 사용하기 때문에 Item, Bag 구현체 상관 없이(전체, 개별 구분 없이) 구현 메소드만 호출하면 내부에서 정의된 구현에 따라 원하는 값을 얻을 수 있게 된다.&amp;nbsp;&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;파일 디렉토리 시스템 구현&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;414&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/effnAt/btrXuaLnBFM/w2P1ro4uWfSVNZTOQa5Kl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/effnAt/btrXuaLnBFM/w2P1ro4uWfSVNZTOQa5Kl1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/effnAt/btrXuaLnBFM/w2P1ro4uWfSVNZTOQa5Kl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeffnAt%2FbtrXuaLnBFM%2Fw2P1ro4uWfSVNZTOQa5Kl1%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; alt=&quot;Composite-pattern&quot; loading=&quot;lazy&quot; width=&quot;414&quot; height=&quot;300&quot; data-origin-width=&quot;414&quot; data-origin-height=&quot;300&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;본 포스팅 초반에 에시를 들었던 파일-폴더 시스템을 구현해보자.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;예를들어 리눅스에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;&lt;s&gt;ls&lt;/s&gt; 나 &lt;s&gt;tree&lt;/s&gt; 명령어를 쓰면 파일들이 일괄적으로 출력되는데 이 기능을 비슷하게 컴포지트 패턴을 통해 구성해보고자 한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;위에서 한번 연습했던 가방-아이템 구조를 폴더-파일 구조로 그대로 치환만 하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;758&quot; data-origin-height=&quot;538&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5iIYm/btrXByd9Gd2/mhmdsZQOc7MKPohJ23nAe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5iIYm/btrXByd9Gd2/mhmdsZQOc7MKPohJ23nAe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5iIYm/btrXByd9Gd2/mhmdsZQOc7MKPohJ23nAe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5iIYm%2FbtrXByd9Gd2%2FmhmdsZQOc7MKPohJ23nAe0%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; alt=&quot;Composite-pattern&quot; loading=&quot;lazy&quot; width=&quot;758&quot; height=&quot;538&quot; data-origin-width=&quot;758&quot; data-origin-height=&quot;538&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1675059853810&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Component
interface Node {
    // 계층 트리 출력
    void print();
    void print(String str);

    // 파일/폴더 용량 얻기
    int getSize();
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1675140840386&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Composite
class Folder implements Node {
    private String name; // 폴더 이름

    private ArrayList&amp;lt;Node&amp;gt; list;

    public Folder(String name) {
        this.name = name;
        list = new ArrayList&amp;lt;&amp;gt;();
    }

    // 리스트에 폴더, 파일 추가
    public void add(Node node) {
        list.add(node);
    }

    // 공백 indent 표현 처리를 위한 print 메서드 오버로딩
    public void print() {
        this.print(&quot;&quot;);
    }

    public void print(String str) {
        int size = getSize(); // 폴더가 담고 있는 모든 파일에 대한 용량 합산

        System.out.println(str + &quot;\uD83D\uDCC2&quot; + name + &quot; (&quot; + size + &quot;kb)&quot;);

        for (Node node : list) {
            // Folder 일 경우 재귀 동작
            node.print(str + &quot;    &quot;); // 인자로 공백문자를 할당하여 indent 처리
        }
    }

    // 각 파일의 용량(kb) 구하기
    public int getSize() {
        int sum = 0;
        for (Node node : list) {
            sum += node.getSize(); // print 로직과 똑같이 재귀 동작
        }
        return sum;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1675059871964&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Leaf
class File implements Node {
    private String name; // 파일 이름
    private int size; // 파일 사이즈

    public File(String name, int size) {
        this.name = name;
        this.size = size;
    }

    public void print() {
        this.print(&quot;&quot;);
    }

    public void print(String str) {
        System.out.println(str + &quot;\uD83D\uDCDC&quot; + name + &quot; (&quot; + size + &quot;kb)&quot;);
    }
    
    public int getSize() {
        return size;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1675059893921&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Client {
    public static void main(String[] args) {

        Folder root = new Folder(&quot;root&quot;);

        File file1 = new File(&quot;file1&quot;, 10);
        Folder sub1 = new Folder(&quot;sub1&quot;);
        Folder sub2 = new Folder(&quot;sub2&quot;);

        root.add(sub1);
        root.add(file1);
        root.add(sub2);

        File file11 = new File(&quot;file11&quot;, 10);
        File file12 = new File(&quot;file12&quot;, 10);

        sub1.add(file11);
        sub1.add(file12);

        File file21 = new File(&quot;file21&quot;, 10);

        sub2.add(file21);

        // 전체 dir 출력
        root.print();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;311&quot; data-origin-height=&quot;172&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lxl7R/btrXCojmSK5/WAFYakoYJbDzZpagn4whJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lxl7R/btrXCojmSK5/WAFYakoYJbDzZpagn4whJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lxl7R/btrXCojmSK5/WAFYakoYJbDzZpagn4whJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flxl7R%2FbtrXCojmSK5%2FWAFYakoYJbDzZpagn4whJK%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; alt=&quot;Composite-pattern&quot; loading=&quot;lazy&quot; width=&quot;311&quot; height=&quot;172&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;311&quot; data-origin-height=&quot;172&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;참고로 print 메서드를 두개로 오버로딩(overloading) 한게 이상할수도 있지만, 공백 indent를 표현하기 위한 기법 중 하나이다. 문자열 파라미터를 가진 print 메서드를 재귀적으로 호출하여 공백을 추가하는 식이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot; data-swap-href=&quot;https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4?inst=cf68b212&quot;&gt;코딩으로 학습하는 GoF의 디자인 패턴 - 백기선&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://www.youtube.com/watch?v=g96bJvVDZPs&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://refactoring.guru/design-patterns/composite&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://stacktraceguru.com/composite-design-pattern/&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;http://www-sop.inria.fr/axis/cbrtools/usermanual-eng/Patterns/Composite.html&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://springframework.guru/gang-of-four-design-patterns/composite-pattern/&lt;/p&gt;</description>
      <category>디자인 패턴/GOF</category>
      <category>Design Pattern</category>
      <category>tree</category>
      <category>디자인 패턴</category>
      <category>복합체</category>
      <category>컴포지트</category>
      <category>컴포짓</category>
      <category>트리</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1072</guid>
      <comments>https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EB%B3%B5%ED%95%A9%EC%B2%B4Composite-%ED%8C%A8%ED%84%B4-%EC%99%84%EB%B2%BD-%EB%A7%88%EC%8A%A4%ED%84%B0%ED%95%98%EA%B8%B0#entry1072comment</comments>
      <pubDate>Tue, 21 Mar 2023 08:26:43 +0900</pubDate>
    </item>
    <item>
      <title>  경량(Flyweight) 패턴 - 완벽 마스터하기</title>
      <link>https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-Flyweight-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img (84).webp&quot; data-origin-width=&quot;1237&quot; data-origin-height=&quot;660&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vo3UQ/btrVAKU5xWP/XwHdpjDkkC9i2nQ0o6V9D1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vo3UQ/btrVAKU5xWP/XwHdpjDkkC9i2nQ0o6V9D1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vo3UQ/btrVAKU5xWP/XwHdpjDkkC9i2nQ0o6V9D1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvo3UQ%2FbtrVAKU5xWP%2FXwHdpjDkkC9i2nQ0o6V9D1%2Fimg.webp&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; alt=&quot;gof-Flyweight-Pattern&quot; loading=&quot;lazy&quot; width=&quot;1237&quot; height=&quot;660&quot; data-filename=&quot;img (84).webp&quot; data-origin-width=&quot;1237&quot; data-origin-height=&quot;660&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;100&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c6x6Cb/btrVpZLGztF/fVlKwBVmsNpxMUW0wwX1pk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c6x6Cb/btrVpZLGztF/fVlKwBVmsNpxMUW0wwX1pk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c6x6Cb/btrVpZLGztF/fVlKwBVmsNpxMUW0wwX1pk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc6x6Cb%2FbtrVpZLGztF%2FfVlKwBVmsNpxMUW0wwX1pk%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; alt=&quot;gof-Flyweight-Pattern&quot; loading=&quot;lazy&quot; width=&quot;140&quot; height=&quot;100&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;100&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Flyweight Pattern&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플라이웨이트 패턴(Flyweight Pattern)은 재사용 가능한 객체 인스턴스를 공유시켜 메모리 사용량을 최소화하는 구조 패턴이다. 간단히 말하면 캐시(Cache) 개념을 코드로 패턴화 한것으로 보면 되는데, 자주 변화는 속성(extrinsit)과 변하지 않는 속성(intrinsit)을 분리하고 변하지 않는 속성을 캐시하여 재사용해 메모리 사용을 줄이는 방식이다. 그래서 동일하거나 유사한 객체들 사이에 가능한 많은 데이터를 서로 공유하여 사용하도록 하여 최적화를 노리는 &lt;b&gt;경량 패턴&lt;/b&gt;이라고도 불린다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Flyweight 단어 의미는 Fly(가벼운) + Weight(무게)를 뜻함으로써, 복싱의 체급에서 유래되었다. 이를 자바 프로그래밍에 접목해보면 모든 객체를 일일히 인스턴스화 하지않고 재사용할수 있는 객체는 재사용함으로써 메모리를 가볍게 만든다는 의미로서 쓰인다보 보면 된다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/C41Xj/btrVsKoyJgU/iFrXxQ8nlxhFsK26kuNSjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/C41Xj/btrVsKoyJgU/iFrXxQ8nlxhFsK26kuNSjK/img.png&quot; data-alt=&quot;객체를 일일히 생성한것과 객체를 공유 상태로 만든 것에 대한 메모리 무게 차이&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/C41Xj/btrVsKoyJgU/iFrXxQ8nlxhFsK26kuNSjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FC41Xj%2FbtrVsKoyJgU%2FiFrXxQ8nlxhFsK26kuNSjK%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; alt=&quot;gof-Flyweight-Pattern&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;400&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;객체를 일일히 생성한것과 객체를 공유 상태로 만든 것에 대한 메모리 무게 차이&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;플라이웨이트 패턴 구조&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;958&quot; data-origin-height=&quot;614&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cz6b7o/btrVukbE6St/QFbUXrz0CRCJztscABssQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cz6b7o/btrVukbE6St/QFbUXrz0CRCJztscABssQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cz6b7o/btrVukbE6St/QFbUXrz0CRCJztscABssQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcz6b7o%2FbtrVukbE6St%2FQFbUXrz0CRCJztscABssQ1%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; alt=&quot;gof-Flyweight-Pattern&quot; loading=&quot;lazy&quot; width=&quot;958&quot; height=&quot;614&quot; data-origin-width=&quot;958&quot; data-origin-height=&quot;614&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;Flyweight&lt;/b&gt; : 경량 객체를 묶는 인터페이스.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;ConcreteFlyweight&lt;/span&gt;&lt;/b&gt; : 공유 가능하여 재사용되는 객체 &lt;b&gt;(intrinsic state)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;UnsahredConcreteFlyweight&lt;/span&gt; &lt;/b&gt;: 공유 불가능한 객체&lt;b&gt; (extrinsic state)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;FlyweightFactory&lt;/span&gt;&lt;/b&gt; : 경량 객체를 만드는 공장 역할과 캐시 역할을 겸비하는 Flyweight 객체 관리 클래스
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GetFlyweight() 메서드는 팩토리 메서드 역할을 한다고 보면 된다.&lt;/li&gt;
&lt;li&gt;만일 객체가 메모리에 존재하면 그대로 가져와 반환하고, 없다면 새로 생성해 반환한다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Client&lt;/b&gt; : 클라이언트는 FlyweightFactory를 통해 Flyweight 타입의 객체를 얻어 사용한다.&lt;/li&gt;
&lt;/ul&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;intrinsic 와 extrinsic 상태&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플라이웨이트 패턴에서 가장 주의 깊게 보아야 할 점이 &lt;span style=&quot;color: #000000;&quot;&gt;바로&lt;b&gt; Intrinsic와 Extrinsic의 상태를 구분&lt;/b&gt;하는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;intrinsic&lt;/b&gt;란 '고유한, 본질적인' 이라는 의미를 가진다. 본질적인 상태란 인스턴스가 어떠한 상황에서도 변하지 않는 정보를 말한다. 그래서 값이 고정되어 있기에 충분히 언제 어디서 공유해도 문제가 없게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;extrinsic&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;intrinsic한 객체 : 장소나 상황에 의존하지 않기 때문에 값이 고정되어 &lt;span style=&quot;color: #009a87;&quot;&gt;공유할 수 있는 객체&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;extrinsic한 객체 : 장소나 상황에 의존하기 때문에 매번 값이 바뀌어 &lt;span style=&quot;color: #ee2323;&quot;&gt;공유할 수 없는 객체&lt;/span&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;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;무수히 떨어지는 폭탄 들은 모두 하나의 객체일 것인데, 이 폭탄 객체들을 일일히 new 를 통해 인스턴스화하면 객체를 생성 할때마다 메모리를 차지하게 되어 게임이 무거워질 것이다. 떨어지는 폭탄 객체는 어차피 모두 같기 때문에 이를 일일히 생성하는건 중복이기 때문이다. &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;따라서 이 폭탄을 플라이웨이트로 처리함으로써 폭탄 인스턴스는 하나만 만들고 공유하여 이를 가져와 화면에 흩뿌리면 될 일이다. &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;그런데 여기서 플라이웨이트 적용 기준이 되는 상태를 구분할 필요가 있다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 떨어지는 폭탄들이 가지고 있는 정보는 다음과 같다고 한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;폭탄 모양&lt;/li&gt;
&lt;li&gt;폭탄 색깔&lt;/li&gt;
&lt;li&gt;폭탄 x, y 좌표 위치&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;638&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfZa8E/btrVzl9YzKy/HRUZ7tUkKW0krXTeklh6c0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfZa8E/btrVzl9YzKy/HRUZ7tUkKW0krXTeklh6c0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfZa8E/btrVzl9YzKy/HRUZ7tUkKW0krXTeklh6c0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfZa8E%2FbtrVzl9YzKy%2FHRUZ7tUkKW0krXTeklh6c0%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; alt=&quot;intrinsic-extrinsic&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;638&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;638&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 폭탄 모양이나 색깔 값은 본질적인 폭탄 상태를 나타나기 때문에 캐시하여 여러 곳에 공유할 수 있다. 반면 폭탄 객체의 x, y 좌표 값은 실시간으로 변화하기 때문에 이를 캐시하여 공유할 수는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 폭탄 클래스 구조를 플라이웨이트 디자인 패턴으로 표현한다면, 폭탄의 형태나 색깔 같은 고정 정보를 포함하고 있는 객체는 ConcreteFlyweight로 구현 되고, 폭탄의 좌표값 같은 정보를 변화 정보를 포함하고 있는 객체는 UnsahredConcreteFlyweight로 구현 되게 된다. 그리고 이 폭탄 객체를 FlyweightFactory가 생성하고 캐싱하고 관리를 하는 것이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;플라이웨이트 패턴&lt;span&gt;&amp;nbsp;특징&lt;/span&gt;&lt;/b&gt;&lt;/h3&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;패&lt;/b&gt;&lt;b&gt;턴 사용 시기&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어플리케이션에 의해 생성되는 객체의 수가 많아 저장 비용이 높아질 때&lt;/li&gt;
&lt;li&gt;생성된 객체가 오래도록 메모리에 상주하며 사용되는 횟수가 많을때&lt;/li&gt;
&lt;li&gt;공통적인 인스턴스를 많이 생성하는 로직이 포함된 경우&lt;/li&gt;
&lt;li&gt;임베디드와 같이 메모리를 최소한으로 사용해야하는 경우에 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;ul id=&quot;0cd69626-f18a-499a-b43e-7d6ea285e245&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;/ul&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;패턴 장점&lt;/b&gt;&lt;/h4&gt;
&lt;ul id=&quot;8ab5faaf-a885-4d25-a052-f1de376c815e&quot; 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;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;new로 인스턴스화를 하면 데이터가 생성되고 메모리에 적재 되는 미량의 시간이 걸리게 된다.&lt;/li&gt;
&lt;li&gt;객체를 공유하면 인스턴스를 가져오기만 하면 되기 때문에 메모리 뿐만 아니라 속도도 향상시킬 수 있게 되는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;패턴 단점&lt;/b&gt;&lt;/h4&gt;
&lt;ul id=&quot;95caf9fc-c187-45ef-a4fb-516d7e983f92&quot; 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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;예제를 통해 알아보는&lt;span&gt;&lt;span&gt; Flyweight&lt;/span&gt;&lt;/span&gt;&lt;b&gt;&amp;nbsp;패턴&lt;/b&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;마인크래프트 필드에 나무 심기&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 사진과 같이 마인크래프트에 숲을 구현하기 위해 지형(Terrain)에 나무 객체들을 심으려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 나무(Tree) 객체에 대해 필요한 데이터는 다음과 같다고 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;나무 종류&lt;/li&gt;
&lt;li&gt;메시 폴리곤 (mesh)&lt;/li&gt;
&lt;li&gt;나무껍질 텍스쳐 (texture)&lt;/li&gt;
&lt;li&gt;잎사귀 텍스쳐 (texture)&lt;/li&gt;
&lt;li&gt;위치 매개변수&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나무에도 여러가지 종류의 나무가 있으며, 나무의 형태를 구현하는 mesh와 texture 그리고 나무가 어느 지형 좌표에 심어질지에 대한 x, y 위치 매개변수가 필요하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1366&quot; data-origin-height=&quot;705&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b36uhl/btrVxOpdpVS/XkVP2DUbAr6OgFDzvE7l10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b36uhl/btrVxOpdpVS/XkVP2DUbAr6OgFDzvE7l10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b36uhl/btrVxOpdpVS/XkVP2DUbAr6OgFDzvE7l10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb36uhl%2FbtrVxOpdpVS%2FXkVP2DUbAr6OgFDzvE7l10%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; alt=&quot;gof-Flyweight-Pattern&quot; loading=&quot;lazy&quot; width=&quot;1366&quot; height=&quot;705&quot; data-origin-width=&quot;1366&quot; data-origin-height=&quot;705&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DoszB/btrVq14921R/rEqh1ikVqVjE8dVQS98zxk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DoszB/btrVq14921R/rEqh1ikVqVjE8dVQS98zxk/img.jpg&quot; data-alt=&quot;나무 종류&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DoszB/btrVq14921R/rEqh1ikVqVjE8dVQS98zxk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDoszB%2FbtrVq14921R%2FrEqh1ikVqVjE8dVQS98zxk%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; alt=&quot;gof-Flyweight-Pattern&quot; loading=&quot;lazy&quot; width=&quot;854&quot; height=&quot;480&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;클린하지 않은 문제의 코드 ❌&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 심플한 방법은 Tree 객체를 new 생성자로 인스턴스화 하여 배치하는 것이다. 하지만 이렇게 하면 아마 메모리 부족으로 게임이 터져버릴 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 나무 객체가 포함하고 있는 메시와 텍스쳐의 데이터의 크기는 만만치 않으며, 이렇게 많은 나무 객체로 이루어진 숲 전체를 하나의 화면에 담아내기에는 무리이기 때문이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;331&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbF4Ae/btrVuAlUtFK/enJr7WB9SIIgkPEYJTxV9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbF4Ae/btrVuAlUtFK/enJr7WB9SIIgkPEYJTxV9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbF4Ae/btrVuAlUtFK/enJr7WB9SIIgkPEYJTxV9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbF4Ae%2FbtrVuAlUtFK%2FenJr7WB9SIIgkPEYJTxV9K%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; alt=&quot;gof-Flyweight-Pattern&quot; loading=&quot;lazy&quot; width=&quot;610&quot; height=&quot;331&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;331&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1673005212425&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Memory {
    public static long size = 0; // 메모리 사용량

    public static void print() {
        System.out.println(&quot;총 메모리 사용량 : &quot; + Memory.size + &quot;MB&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1672982662156&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Tree {
    long objSize = 100; // 100MB

    String type; // 나무 종류

    Object mesh; // 메쉬
    Object texture; // 나무 껍질 + 잎사귀 텍스쳐

    // 위치 변수
    double position_x;
    double position_y;

    public Tree(String type, Object mesh, Object texture, double position_x, double position_y) {
        this.type = type;
        this.mesh = mesh;
        this.texture = texture;
        this.position_x = position_x;
        this.position_y = position_y;

        // 나무 객체를 생성하였으니 메모리 사용 크기 증가
        Memory.size +=  this.objSize;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1672982673247&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Client
class Terrain {
    // 지형 타일 크기
    static final int CANVAS_SIZE = 10000;

    // 나무를 렌더릴
    public void render(String type, Object mesh, Object texture, double position_x, double position_y) {
        // 나무를 지형에 생성
        Tree tree = new Tree(
                type, // 나무 종류
                mesh, // mesh
                texture, // texture
                Math.random() * CANVAS_SIZE, // position_x
                Math.random() * CANVAS_SIZE // position_y
        );

        System.out.println(&quot;x:&quot; + tree.position_x + &quot; y:&quot; + tree.position_y + &quot; 위치에 &quot; + type + &quot; 나무 생성 완료&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1673000252075&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    // 지형 생성
    Terrain terrain = new Terrain();

    // 지형에 Oak 나무 5 그루 생성
    for (int i = 0; i &amp;lt; 5; i++) {
        terrain.render(
                &quot;Oak&quot;, // type
                new Object(), // mesh
                new Object(), // texture
                Math.random() * Terrain.CANVAS_SIZE, // position_x
                Math.random() * Terrain.CANVAS_SIZE // position_y
        );
    }

    // 지형에 Acacia 나무 5 그루 생성
    for (int i = 0; i &amp;lt; 5; i++) {
        terrain.render(
                &quot;Acacia&quot;, // type
                new Object(), // mesh
                new Object(), // texture
                Math.random() * Terrain.CANVAS_SIZE, // position_x
                Math.random() * Terrain.CANVAS_SIZE // position_y
        );
    }

    // 지형에 Jungle 나무 5 그루 생성
    for (int i = 0; i &amp;lt; 5; i++) {
        terrain.render(
                &quot;Jungle&quot;, // type
                new Object(), // mesh
                new Object(), // texture
                Math.random() * Terrain.CANVAS_SIZE, // position_x
                Math.random() * Terrain.CANVAS_SIZE // position_y
        );
    }

    // 총 메모리 사용률 출력
    Memory.print();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;585&quot; data-origin-height=&quot;372&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhnNx1/btrVz9neCjd/xovuOeLgKL8SFQ9SK04E5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhnNx1/btrVz9neCjd/xovuOeLgKL8SFQ9SK04E5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhnNx1/btrVz9neCjd/xovuOeLgKL8SFQ9SK04E5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhnNx1%2FbtrVz9neCjd%2FxovuOeLgKL8SFQ9SK04E5K%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; alt=&quot;gof-Flyweight-Pattern&quot; loading=&quot;lazy&quot; width=&quot;585&quot; height=&quot;372&quot; data-origin-width=&quot;585&quot; data-origin-height=&quot;372&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각 Oak 나무, Acacia 나무, Jungle 나무 5개씩 100MB를 곱하면 총 1500 MB 메모리가 사용되게 된다.&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;&lt;span&gt;플라이웨이트 패턴을 적용한 코드 ✔️&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최적화의 핵심은 나무가 수백 그루 넘게 있는다 해도 대부분 비슷하게 보인다는 것이다. 즉,&amp;nbsp;나무를 생성하는데 사용된 mesh와 texture를 재사용하여 표현해도 어차피 같은 나무이니 문제가 없는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 공통으로 사용되는 모델 데이터와 실시간으로 변하는 위치 매개변수를 분리하여 객체를 구성해주면, 지형에서 나무를 구현할때 나무 모델 인스턴스 하나를 공유받고 위치 매개변수만 다르게 설정해주면 메모리 사용량을 절반 이상을 줄일 수 있을 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w0obA/btrVvmgGsde/MHsehv13PthxZl8X6SpAS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w0obA/btrVvmgGsde/MHsehv13PthxZl8X6SpAS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w0obA/btrVvmgGsde/MHsehv13PthxZl8X6SpAS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw0obA%2FbtrVvmgGsde%2FMHsehv13PthxZl8X6SpAS1%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; alt=&quot;gof-Flyweight-Pattern&quot; loading=&quot;lazy&quot; width=&quot;610&quot; height=&quot;232&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;232&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;586&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baAt2a/btrVzUDWpuz/tK2GfRDmZcjl3HcYeoaZOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baAt2a/btrVzUDWpuz/tK2GfRDmZcjl3HcYeoaZOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baAt2a/btrVzUDWpuz/tK2GfRDmZcjl3HcYeoaZOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaAt2a%2FbtrVzUDWpuz%2FtK2GfRDmZcjl3HcYeoaZOk%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; alt=&quot;gof-Flyweight-Pattern&quot; loading=&quot;lazy&quot; width=&quot;996&quot; height=&quot;586&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;586&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;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;1.&lt;/span&gt; intrinsic 객체와 extrinsic 객체 쪼개기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마인크래프트 게임 내에서 똑같은 메시와 나무껍질 텍스쳐를 일일히 여러번 메모리에 올릴 이유가 없기 때문에 공유되는 나무 모델 객체를 기존 Tree 클래스에서 따로 빼준다. 그러면 아래와 같이 TreeModel 클래스는 ConcreteFlyweight가 되고 좌표값을 가지고 있는 기존 Tree 클래스는 UnsahredConcreteFlyweight 가 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1673005844769&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ConcreteFlyweight - 플라이웨이트 객체는 불변성을 가져야한다. 변경되면 모든 것에 영향을 주기 때문이다.
final class TreeModel {
    // 메시, 텍스쳐 총 사이즈
    long objSize = 90; // 90MB

    String type; // 나무 종류
    Object mesh; // 메쉬
    Object texture; // 나무 껍질 + 잎사귀 텍스쳐

    public TreeModel(String type, Object mesh, Object texture) {
        this.type = type;
        this.mesh = mesh;
        this.texture = texture;

        // 나무 객체를 생성하여 메모리에 적재했으니 메모리 사용 크기 증가
        Memory.size += this.objSize;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1673005850761&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// UnsahredConcreteFlyweight
class Tree {
    // 죄표값과 나무 모델 참조 객체 크기를 합친 사이즈
    long objSize = 10; // 10MB

    // 위치 변수
    double position_x;
    double position_y;

    // 나무 모델
    TreeModel model;

    public Tree(TreeModel model, double position_x, double position_y) {
        this.model = model;
        this.position_x = position_x;
        this.position_y = position_y;

        // 나무 객체를 생성하였으니 메모리 사용 크기 증가
        Memory.size +=  this.objSize;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 Tree 클래스와 TreeModel 간의 관계를 맺어주어야 하는데, 상속을 통해 해주어도 되고, 이 예제에서는 &lt;a href=&quot;https://inpa.tistory.com/entry/OOP-%F0%9F%92%A0-%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5%EC%9D%98-%EC%83%81%EC%86%8D-%EB%AC%B8%EC%A0%9C%EC%A0%90%EA%B3%BC-%ED%95%A9%EC%84%B1Composition-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;합성(Composition)&lt;/a&gt;을 통해 맺어주었다. 그리고 ConcreteFlyweight인 TreeModel 클래스를 final화 시켜 불변 객체로 만들어준다. 나무 모델은 중간에 메시와 텍스쳐가 변경될 일이 없기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;2.&lt;/span&gt; Flyweight 팩토리 만들기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나무 모델 객체에 플라이웨이트를 적용하였으니 이를 생성하고 관리하는 FlyweightFactory 클래스를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플라이웨이트 팩토리의 핵심은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Flyweight Pool : HashMap 컬렉션을 통해 키(key) 와 나무 모델 객체를 저장하는 캐시 저장소 역할&lt;/li&gt;
&lt;li&gt;getInstance 메서드 : Pool에 가져오고자 하는 객체가 있는지 검사를 하여 있으면 그대로 반환, 없으면 새로 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1673007736359&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// FlyweightFactory
class TreeModelFactory {
    // Flyweight Pool - TreeModel 객체들을 Map으로 등록하여 캐싱
    private static final Map&amp;lt;String, TreeModel&amp;gt; cache = new HashMap&amp;lt;&amp;gt;(); // static final 이라 Thread-Safe 함

    // static factory method
    public static TreeModel getInstance(String key) {
        // 만약 캐시 되어 있다면
        if(cache.containsKey(key)) {
            return cache.get(key); // 그대로 가져와 반환
        } else {
            // 캐시 되어있지 않으면 나무 모델 객체를 새로 생성하고 반환
            TreeModel model = new TreeModel(
                    key,
                    new Object(),
                    new Object()
            );
            System.out.println(&quot;-- 나무 모델 객체 새로 생성 완료 --&quot;);

            // 캐시에 적재
            cache.put(key, model);

            return model;
        }
    }
}&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;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;3.&lt;/span&gt; Client 최적화 하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 Terrain 클래스의 나무를 생성하는 render() 메서드의 내부 로직은 단순히 사용자로부터 매개변수를 받아 그대로 tree 객체를 생성할 뿐이었다. 그러나 기존 Tree 객체를 따로 TreeModel로 나누로, TreeModelFactory 까지 생성하였으니 이제 이들을 이용하는 클라이언트의 코드를 최적화 작업을 진행해 주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flyweight를 이용한 최적화 작업은 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;먼저 TreeModel 에서 공유되고 있는 나무 모델 객체를 가져온다. (없다면 새로 생성)&lt;/li&gt;
&lt;li&gt;가져온 나무 모델과 좌표값을 이용해 나무 객체를 새로 생성한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 2단계로 구성된것 뿐이지만, 공유 객체를 가져와 사용함으로써 쓸데없는 mesh와 texture 낭비를 방지한 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1673008525984&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Client
class Terrain {
    // 지형 타일 크기
    static final int CANVAS_SIZE = 10000;

    // 나무를 렌더릴
    public void render(String type, double position_x, double position_y) {
        // 1. 캐시 되어 있는 나무 모델 객체 가져오기
        TreeModel model = TreeModelFactory.getInstance(type);

        // 2. 재사용한 나무 모델 객체와 변화하는 속성인 좌표값으로 나무 생성
        Tree tree = new Tree(model, position_x, position_y);

        System.out.println(&quot;x:&quot; + tree.position_x + &quot; y:&quot; + tree.position_y + &quot; 위치에 &quot; + type + &quot; 나무 생성 완료&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1673008551804&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    // 지형 생성
    Terrain terrain = new Terrain();

    // 지형에 Oak 나무 5 그루 생성
    for (int i = 0; i &amp;lt; 5; i++) {
        terrain.render(
                &quot;Oak&quot;, // type
                Math.random() * Terrain.CANVAS_SIZE, // position_x
                Math.random() * Terrain.CANVAS_SIZE // position_y
        );
    }

    // 지형에 Acacia 나무 5 그루 생성
    for (int i = 0; i &amp;lt; 5; i++) {
        terrain.render(
                &quot;Acacia&quot;, // type
                Math.random() * Terrain.CANVAS_SIZE, // position_x
                Math.random() * Terrain.CANVAS_SIZE // position_y
        );
    }

    // 지형에 Jungle 나무 5 그루 생성
    for (int i = 0; i &amp;lt; 5; i++) {
        terrain.render(
                &quot;Jungle&quot;, // type
                Math.random() * Terrain.CANVAS_SIZE, // position_x
                Math.random() * Terrain.CANVAS_SIZE // position_y
        );
    }

    // 총 메모리 사용률 출력
    Memory.print();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;579&quot; data-origin-height=&quot;436&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dkhMqU/btrVxrJtVCM/RNKEZuCDmbAgvp3rKLQQuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dkhMqU/btrVxrJtVCM/RNKEZuCDmbAgvp3rKLQQuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dkhMqU/btrVxrJtVCM/RNKEZuCDmbAgvp3rKLQQuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdkhMqU%2FbtrVxrJtVCM%2FRNKEZuCDmbAgvp3rKLQQuk%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; alt=&quot;gof-Flyweight-Pattern&quot; loading=&quot;lazy&quot; width=&quot;579&quot; height=&quot;436&quot; data-origin-width=&quot;579&quot; data-origin-height=&quot;436&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패턴 적용 전에는 1500MB 메모리 사용량이 들었지만, 패턴 적용 후에는 메모리 사용량이 확연히 줄어들었음을 확인할 수 있다. 이전에는 통짜로 나무 객체를 생성함에 비해, 나무 모델을 따로 플라이웨이트로 분리함으로써 중복된 mesh, texture 사용을 공유시켜 메모리를 아낀 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 공유할수 있는 intrinsic(본질적) 상태의 데이터를 분간하여 캐싱함으로써 프로그램을 최적화를 추구하는것이 훌륭한 프로그래머의 자질이라고 할수 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;3-인스턴스와-garbage-collection&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;Garbage Collection 처리 주의사항  ️&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 구현한 TreeModelFactory에서는 HashMap을 이용해 TreeModel의 인스턴스를 캐싱하여 관리하고 있다. 이와 같이 '인스턴스를 관리' 하는 기능을 자바 프로그래밍에서 구현하여 사용할 때에는 반드시&lt;span style=&quot;color: #ee2323;&quot;&gt; '관리되고 있는 인스턴스는 GC(Garbage Collection) 처리되지 않는다'&lt;/span&gt; 라는 점을 주의해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 나무를 모두 렌더링을 완료하여 더이상 나무를 생성할 일이 없다라면, 반드시 TreeModelFactory에 잔존해있는 Flyweight Pool 을 비워줄 필요가 있는 것이다. 그래야 인스턴스에 대한 참조를 잃은 TreeModel 인스턴스들이 GC에 의해 메모리 청소가 되게 된다. 그렇지 않으면 더이상 나무를 생성할 일이 없는데도 TreeModel 데이터가 메모리에 쓸데없이 잔존하게 된다. 메모리 최적화를 위해 플라이웨이트 패턴을 썼는데 마지막 맺음에서 이렇게 허점이 생기면 뒤가 쓰린 법이다.&lt;/p&gt;
&lt;figure id=&quot;og_1672990431703&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;[JAVA] ☕ 가비지 컬렉션 동작 원리 &amp;amp; GC 종류   총정리&quot; data-og-description=&quot;가비지 컬렉션(GC) 란? 가비지 컬렉션(Garbage Collectoin, 이하 GC)은 자바의 메모리 관리 방법 중의 하나로 JVM(자바 가상 머신)의 Heap 영역에서 동적으로 할당했던 메모리 중 필요 없게 된 메모리 객체(&quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EA%B0%80%EB%B9%84%EC%A7%80-%EC%BB%AC%EB%A0%89%EC%85%98GC-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%F0%9F%92%AF-%EC%B4%9D%EC%A0%95%EB%A6%AC&quot; data-og-url=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EA%B0%80%EB%B9%84%EC%A7%80-%EC%BB%AC%EB%A0%89%EC%85%98GC-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%F0%9F%92%AF-%EC%B4%9D%EC%A0%95%EB%A6%AC&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bvTsE4/hyRbIz0paA/YKCw1dtUHNs0gVer3UxpDK/img.png?width=800&amp;amp;height=485&amp;amp;face=0_0_800_485,https://scrap.kakaocdn.net/dn/b10snx/hyRbxyvd4I/hNCkYYhDwgbiDqAKNDK8J1/img.png?width=800&amp;amp;height=485&amp;amp;face=0_0_800_485,https://scrap.kakaocdn.net/dn/mS8dx/hyRbD6uaAS/bjY00sODOr1X6tWrO6klOK/img.png?width=1200&amp;amp;height=728&amp;amp;face=0_0_1200_728&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EA%B0%80%EB%B9%84%EC%A7%80-%EC%BB%AC%EB%A0%89%EC%85%98GC-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%F0%9F%92%AF-%EC%B4%9D%EC%A0%95%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EA%B0%80%EB%B9%84%EC%A7%80-%EC%BB%AC%EB%A0%89%EC%85%98GC-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%F0%9F%92%AF-%EC%B4%9D%EC%A0%95%EB%A6%AC&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bvTsE4/hyRbIz0paA/YKCw1dtUHNs0gVer3UxpDK/img.png?width=800&amp;amp;height=485&amp;amp;face=0_0_800_485,https://scrap.kakaocdn.net/dn/b10snx/hyRbxyvd4I/hNCkYYhDwgbiDqAKNDK8J1/img.png?width=800&amp;amp;height=485&amp;amp;face=0_0_800_485,https://scrap.kakaocdn.net/dn/mS8dx/hyRbD6uaAS/bjY00sODOr1X6tWrO6klOK/img.png?width=1200&amp;amp;height=728&amp;amp;face=0_0_1200_728');&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;[JAVA] ☕ 가비지 컬렉션 동작 원리 &amp;amp; GC 종류   총정리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;가비지 컬렉션(GC) 란? 가비지 컬렉션(Garbage Collectoin, 이하 GC)은 자바의 메모리 관리 방법 중의 하나로 JVM(자바 가상 머신)의 Heap 영역에서 동적으로 할당했던 메모리 중 필요 없게 된 메모리 객체(&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;inpa.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;실무에서_찾아보는_전략_패턴&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;실무에서 찾아보는 Flyweight 패턴&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;Java&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Java&lt;/b&gt;&lt;/h3&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;String&amp;nbsp;Constant&amp;nbsp;Pool&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;String Constant Pool 개념이 바로 Flyweight Pool 개념이다.&lt;/li&gt;
&lt;li&gt;자바는 String 데이터에 대해 별도로 string constant pool 영역에 적재한다.&lt;/li&gt;
&lt;li&gt;그래서 같은 문자열 데이터 다시 사용될때 pool을 검사해 있다면 이를 공유한다.&lt;/li&gt;
&lt;li&gt;만일 pool에 없다면 새로 메모리를 할당하여 pool에 등록한 후 재사용한다.&lt;/li&gt;
&lt;li&gt;즉, String 클래스는 Flyweight 패턴을 통해 리터럴 문자열 데이터에 대한 캐싱을 하고 있는 것이다.&lt;/li&gt;
&lt;li&gt;또한 String 클래스는 불변(immutable) 객체 특성을 가지고 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1672988831225&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;[JAVA] ☕ String 타입 특징 이해하기 (String Pool &amp;amp; 문자열 비교)&quot; data-og-description=&quot;여타 대부분의 프로그래밍 언어에서 문자열 이라는 데이터를 저장하기 위해 string 이라는 데이터 타입을 사용한다. 이 string 데이터를 다루는데 있어 특별히 유의해야 할점은 없어보이지만, 자바&quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-String-%ED%83%80%EC%9E%85-%ED%95%9C-%EB%88%88%EC%97%90-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-String-Pool-%EB%AC%B8%EC%9E%90%EC%97%B4-%EB%B9%84%EA%B5%90&quot; data-og-url=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-String-%ED%83%80%EC%9E%85-%ED%95%9C-%EB%88%88%EC%97%90-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-String-Pool-%EB%AC%B8%EC%9E%90%EC%97%B4-%EB%B9%84%EA%B5%90&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eP0aD/hyRbvHwCS3/Bv0jbnvqPsm1bogkdBa6j1/img.jpg?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/bT27Hw/hyRbFXu0WU/bVR1k5RVOYN4A0KWKC7i4k/img.jpg?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/bU6KqZ/hyRbzQwu08/NmYxqMWNedxmgp73ZeDt6k/img.png?width=1170&amp;amp;height=644&amp;amp;face=0_0_1170_644&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-String-%ED%83%80%EC%9E%85-%ED%95%9C-%EB%88%88%EC%97%90-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-String-Pool-%EB%AC%B8%EC%9E%90%EC%97%B4-%EB%B9%84%EA%B5%90&quot; data-source-url=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-String-%ED%83%80%EC%9E%85-%ED%95%9C-%EB%88%88%EC%97%90-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-String-Pool-%EB%AC%B8%EC%9E%90%EC%97%B4-%EB%B9%84%EA%B5%90&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eP0aD/hyRbvHwCS3/Bv0jbnvqPsm1bogkdBa6j1/img.jpg?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/bT27Hw/hyRbFXu0WU/bVR1k5RVOYN4A0KWKC7i4k/img.jpg?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/bU6KqZ/hyRbzQwu08/NmYxqMWNedxmgp73ZeDt6k/img.png?width=1170&amp;amp;height=644&amp;amp;face=0_0_1170_644');&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;[JAVA] ☕ String 타입 특징 이해하기 (String Pool &amp;amp; 문자열 비교)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;여타 대부분의 프로그래밍 언어에서 문자열 이라는 데이터를 저장하기 위해 string 이라는 데이터 타입을 사용한다. 이 string 데이터를 다루는데 있어 특별히 유의해야 할점은 없어보이지만, 자바&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;inpa.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;pre id=&quot;code_1672988203957&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 문자열 리터럴은 String Pool에 저장되는 Flyweight 패턴을 이용한다.
String str1 = &quot;Hello&quot;; 
String str2 = &quot;Hello&quot;;
 
// 두 주소값이 같다는 말은 메모리에 하나의 데이터만 적재되고 이를 공유하고 있다는 반증
System.out.println(str1 == str2); // true&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1672988555214&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// new 연산자를 이용한 방식은 Flyweight 패턴을 적용하지 않는다.
String str3 = new String(&quot;Hello&quot;); 
String str4 = new String(&quot;Hello&quot;);

// 서로 다른 힙 메모리에 저장된 데이터이니 두 주솟값은 다르다
System.out.println(str3 == str4); // false&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;855&quot; data-origin-height=&quot;575&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZ6CH7/btrVzKU3lZr/rX1V3m1g1aEYvGAbfgn9Q0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZ6CH7/btrVzKU3lZr/rX1V3m1g1aEYvGAbfgn9Q0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZ6CH7/btrVzKU3lZr/rX1V3m1g1aEYvGAbfgn9Q0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZ6CH7%2FbtrVzKU3lZr%2FrX1V3m1g1aEYvGAbfgn9Q0%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; alt=&quot;flyweight String Constant Pool&quot; loading=&quot;lazy&quot; width=&quot;855&quot; height=&quot;575&quot; data-origin-width=&quot;855&quot; data-origin-height=&quot;575&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;&lt;b&gt;Integer의 valueOf()&lt;/b&gt;&lt;/h4&gt;
&lt;ul id=&quot;0ebb4620-6d49-4456-b141-5d63158bbb55&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자바의 Interger 클래스의 &lt;s&gt;valueOf()&lt;/s&gt; 메서드는 &lt;a href=&quot;https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EC%A0%95%EC%A0%81-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%83%9D%EC%84%B1%EC%9E%90-%EB%8C%80%EC%8B%A0-%EC%82%AC%EC%9A%A9%ED%95%98%EC%9E%90-%F0%9F%92%A1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;정적 팩토리 메서드&lt;/a&gt;로서, &lt;s&gt;valueOf()&lt;/s&gt; 메서드를 통해 값을 할당하면 Flyweight 패턴을 통해 캐시되게 된다.&lt;/li&gt;
&lt;li&gt;&lt;s&gt;valueOf()&lt;/s&gt; 메소드가 호출 되었을 때 요청된 Integer 값이 -128 ~ 127 범위의 정수라면 캐싱을 이용하도록 설계 되어있다.&lt;/li&gt;
&lt;li&gt;Integer 뿐만 아니라 Double이나 Boolean 같은 Wrapper 클래스들도 마찬가지로 적용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;499&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k5dvh/btrVyWv4wYp/eITQB3yuJIU9nJUPKz0QxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k5dvh/btrVyWv4wYp/eITQB3yuJIU9nJUPKz0QxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k5dvh/btrVyWv4wYp/eITQB3yuJIU9nJUPKz0QxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk5dvh%2FbtrVyWv4wYp%2FeITQB3yuJIU9nJUPKz0QxK%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; alt=&quot;valueOf-flyweight&quot; loading=&quot;lazy&quot; width=&quot;724&quot; height=&quot;499&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;499&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;675&quot; data-origin-height=&quot;414&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c32bJs/btrVzZSUVP2/zv64mV06bxDxiizuXnEJEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c32bJs/btrVzZSUVP2/zv64mV06bxDxiizuXnEJEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c32bJs/btrVzZSUVP2/zv64mV06bxDxiizuXnEJEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc32bJs%2FbtrVzZSUVP2%2Fzv64mV06bxDxiizuXnEJEk%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; alt=&quot;valueOf-flyweight&quot; loading=&quot;lazy&quot; width=&quot;675&quot; height=&quot;414&quot; data-origin-width=&quot;675&quot; data-origin-height=&quot;414&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1672989564391&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// valueOf 정적 팩토리 메서드로 수를 등록하면 캐시됨
// auto boxing으로 Integer i1 = 100; 으로 선언하여도 똑같음
Integer i1 = Integer.valueOf(100); 
Integer i2 = Integer.valueOf(100);

System.out.println(i1 == i2); // true

// 생성자로 수를 등록하면 캐시 안됨
Integer i3 = new Integer(100);
Integer i4 = new Integer(100);

System.out.println(i3 == i4); // false&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1672969366672&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// -128 ~ 127 는 캐시 됨
Integer i1 = Integer.valueOf(100);
Integer i2 = Integer.valueOf(100);

System.out.println(i1 == i2); // true

// -128 ~ 127 를 넘어선 수는 캐시 안됨
Integer i3 = Integer.valueOf(1000);
Integer i4 = Integer.valueOf(1000);

System.out.println(i3 == i4); // false&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot; data-swap-href=&quot;https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4?inst=cf68b212&quot;&gt;코딩으로 학습하는 GoF의 디자인 패턴 - 백기선&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://refactoring.guru/design-patterns/flyweight&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://www.hanbit.co.kr/channel/series/series_view.html?cms_code=CMS3043948395&amp;amp;hcs_idx=3&amp;nbsp;&lt;/p&gt;
&lt;div id=&quot;gtx-trans&quot; style=&quot;position: absolute; left: 140px; top: 6356.86px;&quot;&gt;
&lt;div class=&quot;gtx-trans-icon&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;</description>
      <category>디자인 패턴/GOF</category>
      <category>Design Pattern</category>
      <category>java cache</category>
      <category>Java Pool</category>
      <category>경량 객체</category>
      <category>경량 패턴</category>
      <category>디자인 패턴</category>
      <category>자바 pool</category>
      <category>자바 캐시</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1060</guid>
      <comments>https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-Flyweight-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90#entry1060comment</comments>
      <pubDate>Mon, 20 Mar 2023 09:05:59 +0900</pubDate>
    </item>
    <item>
      <title>  정적 팩토리 메서드 패턴 (Static Factory Method)</title>
      <link>https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EC%A0%95%EC%A0%81-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%83%9D%EC%84%B1%EC%9E%90-%EB%8C%80%EC%8B%A0-%EC%82%AC%EC%9A%A9%ED%95%98%EC%9E%90</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;maxresdefault (1).webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8A2jZ/btrVuHqirsx/gmLTAuDmDQQqtXer7aiAH0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8A2jZ/btrVuHqirsx/gmLTAuDmDQQqtXer7aiAH0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8A2jZ/btrVuHqirsx/gmLTAuDmDQQqtXer7aiAH0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8A2jZ%2FbtrVuHqirsx%2FgmLTAuDmDQQqtXer7aiAH0%2Fimg.webp&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; alt=&quot;static-factory-method-pattern&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;720&quot; data-filename=&quot;maxresdefault (1).webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Static Factory Method Pattern&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;정적 팩토리 메서드(Static Factory Method)&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;패턴은 개발자가 구성한 Static Method를 통해 간접적으로 생성자를 호출하는 객체를 생성하는 디자인 패턴이다. &lt;/span&gt;&lt;span&gt;&lt;span&gt;우리는 지금까지 객체를 인스턴스화 할때 직접적으로 생성자(Constructor)를 호출하여 생성하였는데, 별도의 &lt;b&gt;객체 생성의 역할을 하는 클래스 메서드&lt;/b&gt;를 통해 간접적으로 객체 생성을 유도하는 것이다. 그리고 이 정적 메서드를 통칭적으로 정적 팩토리 메서드 패턴이라고 부르는 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1672885535757&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Book {
    private String title;
    
    // 생성자를 private화 하여 외부에서 생성자 호출 차단
    private Book(String title) { this.title = title; }
    
    // 정적 팩토리 메서드
    public static Book titleOf(String title) {
        return new Book(title); // 메서드에서 생성자를 호출하고 리턴함
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1672892667584&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
	// 정적 메서드 호출을 통해 인스턴스화된 객체를 얻음
    Book book1 = Book.titleOf(&quot;어린왕자&quot;); 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;정적 팩토리 메서드는 이름 취지에서도 알 수 있듯이 GOF의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9CFactory-Method-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90&quot;&gt;팩토리 메서드&lt;/a&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EC%B6%94%EC%83%81-%ED%8C%A9%ED%86%A0%EB%A6%ACAbstract-Factory-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90&quot;&gt;추상 팩토리&lt;/a&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;패턴의 팩토리(Factory) 개념을 따와 심플하게 변형시킨 팩토리 변형 패턴 종류라고 보면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 왜 멀쩡한 생성자를 냅두고 번거롭게 한단계 거쳐 정적 팩토리 메서드를 통해 객체를 생성하는지에 대한 실용성에 대해 의문을 가질것이다. 그런데 실제로 정적 팩토리 메서드는 단순히 생성자의 역할을 대신 이행하는 것 뿐만 아니라 개발자가 좀 더 가독성 좋은 코드를 작성하고 객체 지향적으로 프로그래밍 할 수 있게 도와주기 때문에 실무에서도 심심치 않게 사용되고 있다. 이것은 생성자(Constructor)의 본질적인 문제점을 극복하기 위해서 이기도 한데, 지금부터 왜 정적 팩토리 메서드를 이용해야 되는지에 대한 이유를 알아보자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&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;span style=&quot;background-color: #ffffff; color: #303a3e;&quot;&gt;자바 프로그래밍의 간판이라 할수 있는, 조슈아 블로크(Joshua&lt;span style=&quot;background-color: #ffffff; color: #4d5156;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;J. Bloch&lt;/span&gt;)의 저서 [이펙티브 자바] 책에서 소개하는 아이템 중에서 첫 번째로 조언 하는것이 바로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;'생성자 대신 정적 팩토리 메서드를 고려하라'&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #303a3e;&quot;&gt;&lt;span&gt;&amp;nbsp;&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;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #303a3e;&quot;&gt;정적 팩토리 메서드 특징&lt;/span&gt;&lt;/b&gt;&lt;/h3&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;&lt;span style=&quot;color: #ef5369;&quot;&gt;1.&lt;/span&gt; 생성 목적에 대한 이름 표현이 가능하다&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 클래스를 설계할때 다양한 타입의 객체를 생성하기 위해, 생성 목적에 따라 생성자를 오버로딩하여 구분하여 사용해왔다. 하지만 문제는 이러한 객체를 new 키워드를 통해 생성자로 생성하려면, 개발자는 해당 생성자의 인자 순서와 내부 구조를 알고 있어야 목적에 맞게 객체를 생성할수가 있다는 번거로움이 있다.&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;예를들어 다음과 같이 Car 클래스는 브랜드명과 자동차 색깔을 정의하는 멤버를 가지고 있다고 하자. 브랜드명은 반드시 생성자를 통해 외부로부터 입력을 받아야 되지만, 자동차 색깔은 기본값이 '검정'이며 선택적으로 입력받을 수 있다고 한다. 즉, 객체 생성에 있어 필수 속성과 선택 속성이 나뉘게 되는데, 이를 생성자를 통해 구현하면 두가지 형식의 생성자 오버로딩으로 처리하고, 이를 호출하는 쪽에서 생성자의 인자 갯수를 다르게 할당함으로써 구현해야 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1672894567450&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Car {
    private String brand;
    private String color = &quot;black&quot;;

    public Car(String brand, String color) {
        this.brand = brand;
        this.color = color;
    }

    public Car(String brand) {
        this.brand = brand;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1672894850467&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    // 검정색 테슬라 자동차 
    Car teslaCar = new Car(&quot;Tesla&quot;);

    // 빨간색 BMW 자동차
    Car bmwRedCar = new Car(&quot;BMW&quot;, &quot;Red&quot;);
}&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;프로그래밍 할때 중요한 요소 중에 하나가 코드를 읽기 쉽도록 작성해야 한다는 점이다. 그런 의미에서 new 생성자 방법은 단지 매개변수의 유형과 개수를 제안할 뿐이지 어떠한 역할 표현이나 편의성을 제공하지 않는다. 즉, 생성자로 넘기는 매개변수 만으로는 &lt;b&gt;반환될 객체의 특성을 제대로 표현하기가 어렵다&lt;/b&gt;는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만일 나 자신이 클래스를 설계하고 오로지 나만 클래스를 이용한다고 가정한다면 이는 큰 문제가 되지 않는다. 하지만 패키지로 배포하여 남이 사용한다거나 협업을 해야된다면 말은 달라진다. 외부 사용자 입장에선 &lt;s&gt;new Car()&lt;/s&gt; 에 몇개를, 몇번째 인자에 어떤 타입을 할당해야 자신이 원하는 색깔의 브랜드 자동차를 생성할수 있는지에 대한 정보를 얻기 위해선 결국은 Car 클래스 내부 구조를 뜯어봐야 한다. 만일 클래스가 바이트로 컴파일된 상태라면 이를 디컴파일 해야하고 번거롭다.&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;pre id=&quot;code_1672896009036&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Car {
    private String brand;
    private String color;

    // private 생성자
    private Car(String brand, String color) {
        this.brand = brand;
        this.color = color;
    }

    // 정적 팩토리 메서드 (매개변수 하나는 from 네이밍)
    public static Car brandBlackFrom(String brand) {
        return new Car(brand, &quot;black&quot;);
    }

    // 정적 팩토리 메서드 (매개변수 여러개는 of 네이밍)
    public static Car brandColorOf(String brand, String color) {
        return new Car(brand, color);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1672896036951&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    // 검정색 테슬라 자동차 
    Car teslaCar = Car.brandBlackFrom(&quot;Tesla&quot;);

    // 빨간색 BMW 자동차
    Car bmwRedCar = Car.brandColorOf(&quot;BMW&quot;, &quot;Red&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 생성자 대신 정적 팩토리 메서드를 호출함으로써 생성될 객체의 특성에 대해 쉽게 묘사할 수 있다는 장점이 있어 코드의 가독성을 높여주게 된다. 생성자로 만드는 것보다 의미를 가진 메소드를 이용하면 객체 생성의 의미를 훨씬 파악하기 쉽기 때문이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;정적 팩토리 메서드를 구성하고자 한다면, 반드시 생성자에 private 접근 제어자를 두어 외부에서 new 키워드를 이용하여 객체를 생성하는 것을 잊지 말자!&lt;/blockquote&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;&lt;span style=&quot;color: #ef5369;&quot;&gt;2.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;인스턴스에 대해 통제 및 관리가 가능하다&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222;&quot;&gt;메서드를 통해 한단계 거쳐 간접적으로 객체를 생성하기 때문에, 기본적으로 전반적인 객체 생성 및 통제 관리를 할 수 있게 된다. 즉, &lt;span style=&quot;color: #222222;&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;a href=&quot;https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EC%8B%B1%EA%B8%80%ED%86%A4Singleton-%ED%8C%A8%ED%84%B4-%EA%BC%BC%EA%BC%BC%ED%95%98%EA%B2%8C-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Singleton 디자인 패턴&lt;/a&gt; 을 들 수 있는데, &lt;span style=&quot;color: #222222;&quot;&gt;getInstance() 라는 정적 팩토리 메서드를 사용해 오로지 하나의 객체만 반환하도록 하여 객체를 재사용해 메모리를 아끼도록 유도할 수 있다.&lt;/span&gt;&lt;span style=&quot;color: #222222;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1673067795771&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Singleton {
    private static Singleton instance;

    private Singleton() {}

    // 정적 팩토리 메서드
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1673068801913&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    Singleton s1 = Singleton.getInstance();
    Singleton s2 = Singleton.getInstance();
    Singleton s3 = Singleton.getInstance();

    System.out.println(s1);
    System.out.println(s2);
    System.out.println(s3);

    System.out.println(s1 == s2);
    System.out.println(s1 == s3);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;333&quot; data-origin-height=&quot;109&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBXTNN/btrVE2t0C1t/Ji3rMDNTywwQxevxPfWoyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBXTNN/btrVE2t0C1t/Ji3rMDNTywwQxevxPfWoyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBXTNN/btrVE2t0C1t/Ji3rMDNTywwQxevxPfWoyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBXTNN%2FbtrVE2t0C1t%2FJi3rMDNTywwQxevxPfWoyK%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; alt=&quot;static-factory-method-pattern&quot; loading=&quot;lazy&quot; width=&quot;333&quot; height=&quot;109&quot; data-origin-width=&quot;333&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;&lt;span style=&quot;color: #222222;&quot;&gt;또다른 예로는 &lt;/span&gt;인스턴스에 대한&lt;b&gt; &lt;span style=&quot;color: #ef6f53;&quot;&gt;캐싱(Caching)&lt;/span&gt;&lt;/b&gt; 절차 구조를 정적 팩토리 메서드로 구현할 수 있다. 인스턴스에 대해 캐싱을 한다면 필요한 인스턴스만 뽑아 재사용하여 메모리를 절약할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드를 살펴보면 요일 정보를 나타내는 Day 객체와, 이 Day 객체를 생성하는 DayFactory 팩토리 객체가 있다. 그중&amp;nbsp; 팩토리 객체를 보면 내부적으로 HashMap 자료구조를 이용하여 key값을 통해 인스턴스를 캐싱하여 관리하고 있는데, 정적 팩토리 메서드가 호출되면 먼저 캐시 저장소를 조회하여 기존 Day 객체가 있는지 검사하고, 있으면 그대로 반환하여 재사용을 유도하고, 없다면 새로 생성해서 캐싱해두게 된다.&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;span&gt; &lt;a href=&quot;https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-Flyweight-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90&quot;&gt;Flyweight 디자인 패턴&lt;/a&gt;의 &lt;/span&gt;근간이 되게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1673068576598&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Day {
    private String day;

    public Day(String day) { this.day = day; }

    public String getDay() { return day; }
}

// Day 객체를 생성하고 관리하는 Flyweight 팩토리 클래스
class DayFactory {

	// Day 객체를 저장하는 캐싱 저장소 역할
    private static final Map&amp;lt;String, Day&amp;gt; cache = new HashMap&amp;lt;&amp;gt;();
	
    // 자주 사용될것 같은 Day 객체 몇가지를 미리 등록한다
    static { 
    	cache.put(&quot;Monday&quot;, new Day(&quot;Monday&quot;)); 
        cache.put(&quot;Tuesday&quot;, new Day(&quot;Tuesday&quot;)); 
        cache.put(&quot;Wednesday&quot;, new Day(&quot;Wednesday&quot;)); 
    }

    // 정적 팩토리 메서드 (인스턴스에 대해 철저한 관리)
    public static Day from(String day) {

        if(cache.containsKey(day)) {
            // 캐시 되어있으면 그대로 가져와 반환
            System.out.println(&quot;해당 요일은 캐싱되어 있습니다.&quot;);
            return cache.get(day);
        } else {
            // 캐시 되어 있지 않으면 새로 생성하고 캐싱하고 반환
            System.out.println(&quot;해당 요일은 캐싱되어 있지 않아 새로 생성하였습니다.&quot;);
            Day d = new Day(day);
            cache.put(day, d);
            return d;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1673068583843&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    // 이미 등록된 요일 가져오기
    Day day = DayFactory.from(&quot;Monday&quot;);
    System.out.println(day.getDay());

    // 등록되지 않은 요일 가져오기
    day = DayFactory.from(&quot;Friday&quot;);
    System.out.println(day.getDay());
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;413&quot; data-origin-height=&quot;92&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkodKa/btrVzzf7IZH/a7n8HdFtUxEb8jheq6NSUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkodKa/btrVzzf7IZH/a7n8HdFtUxEb8jheq6NSUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkodKa/btrVzzf7IZH/a7n8HdFtUxEb8jheq6NSUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkodKa%2FbtrVzzf7IZH%2Fa7n8HdFtUxEb8jheq6NSUk%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; alt=&quot;static-factory-method-pattern&quot; loading=&quot;lazy&quot; width=&quot;413&quot; height=&quot;92&quot; data-origin-width=&quot;413&quot; data-origin-height=&quot;92&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이렇게 인스턴스의 생성에 관여하여, 생성되는 인스턴스의 수를 통제할 수 있는 클래스를 &lt;b&gt;인스턴스 통제 (instance-controlled) 클래스&lt;/b&gt;라고 한다.&lt;span&gt;&amp;nbsp;&lt;/span&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;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;3.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;하위 자료형 객체를 반환할 수 있다&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스의 다형성의 특징을 응용한 정적 팩토리 메서드 특징이다. 메서드 호출을 통해 얻을 객체의 인스턴스를 자유롭게 선택할수 있는 유연성을 갖는 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1673072040297&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface SmarPhone {}

class Galaxy implements SmarPhone {}
class IPhone implements SmarPhone {}
class Huawei implements SmarPhone {}

class SmartPhones {
    public static SmarPhone getSamsungPhone() {
        return new Galaxy();
    }

    public static SmarPhone getApplePhone() {
        return new IPhone();
    }

    public static SmarPhone getChinesePhone() {
        return new Huawei();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 아이디어는 인터페이스를 정적 팩터리 메서드의 반환 타입으로 사용하는 인터페이스 기반 프레임워클르 만드는 핵심이 된다. 대표적으로 자바의 컬렉션 프레임워크인 java.util.Collections 클래스를 들 수 있는데, 이 클래스는 Collection 인터페이스를 반환하는 여러 정적 팩토리 메서드를 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Collections 클래스와 Collection 인터페이스의 이름을 보면 &lt;span style=&quot;color: #009a87;&quot;&gt;'s'&lt;/span&gt; 를 붙인걸 볼 수 있는데, 이것을 &lt;span&gt;Collection&lt;span&gt; 인터페이스의&lt;b&gt; 동반 클래스(Companion Class)&lt;/b&gt; 라고 부른다. 위의 SmartPhones 예제 코드에도 컬렉션을 그대로 응용한 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 java 8 버전부터는 인터페이스가 정적 메서드를 가질수 있게 되어 동반 클래스 개념은 더이상 필요없어졌다. 즉, 인터페이스에 그냥 정적 팩토리 메서드를 선언하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1673072705474&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface SmarPhone {
    public static SmarPhone getSamsungPhone() {
        return new Galaxy();
    }

    public static SmarPhone getApplePhone() {
        return new IPhone();
    }

    public static SmarPhone getChinesePhone() {
        return new Huawei();
    }
}&lt;/code&gt;&lt;/pre&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;&lt;span style=&quot;color: #ef5369;&quot;&gt;4.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;인자에 따라 다른 객체를 반환하도록 분기할 수 있다&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드이니 매개변수를 받을수 있을테고, 메서드 블록 내에서 분기문을 통해 여러 자식 타입의 인스턴스를 반환하도록 응용 구성이 가능하다. 위의 3번에 대한 확장 예제라고 보면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1673073044121&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface SmarPhone {
    public static SmarPhone getPhone(int price) {
        if(price &amp;gt; 100000) {
            return new IPhone();
        }

        if(price &amp;gt; 50000) {
            return new Galaxy();
        }

        return new Huawei();
    }
}&lt;/code&gt;&lt;/pre&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;&lt;span style=&quot;color: #ef5369;&quot;&gt;5.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;객체 생성을 캡슐화 할 수 있다&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222;&quot;&gt;생성자를 사용하는 경우 외부에 내부 구현을 드러내야 하는데, &lt;/span&gt;&lt;span style=&quot;color: #222222;&quot;&gt;정적 팩토리 메서드는 구현부를 외부로 부터 숨길 수 있어 캡슐화(encapsulation) 및 정보 은닉(information hiding)을 할수 있다는 특징이 있다. 또한 노출하지 않는다는 특징은 &lt;span style=&quot;color: #ee2323;&quot;&gt;정보 은닉성&lt;/span&gt;을 가지기도 하지만 동시에 사용하고 있는 구현체를 숨겨&lt;span style=&quot;color: #ee2323;&quot;&gt; 의존성을 제거&lt;/span&gt;해주는 장점도 지니고 있다. 이 예제 역시 위의 3번의 확장 예이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222;&quot;&gt;아래 예제 코드를 보면, 메인 메서드에서 오로지 &lt;span style=&quot;color: #222222;&quot;&gt;GradeCalculator의 정적 팩토리 메서드 of() 를 호출하여 Grade 인터페이스 타입의 객체를 반환할 뿐이지, Grade 인터페이스의 구현체인 A ~ F 객체 존재에 대해서는 모르게 된다. 즉, 구현체를 생성해서 반환할 책임은 정적 팩토리 메서드를 가진 &lt;span style=&quot;color: #222222;&quot;&gt;GradeCalculator 이고, 클라이언트는 구현체를 신경쓸 필요없이 제공되는 메서드를 호출만 하면 되어 편리하게 사용이 가능해진다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1673073607069&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface Grade {
    String toText();
}

class A implements Grade {
    @Override
    public String toText() {return &quot;A&quot;;}
}

class B implements Grade {
    @Override
    public String toText() {return &quot;B&quot;;}
}

class C implements Grade {
    @Override
    public String toText() {return &quot;C&quot;;}
}

class D implements Grade {
    @Override
    public String toText() {return &quot;D&quot;;}
}

class F implements Grade {
    @Override
    public String toText() {return &quot;F&quot;;}
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1673069915429&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class GradeCalculator {
	// 정적 팩토리 메서드
    public static Grade of(int score) {
        if (score &amp;gt;= 90) {
            return new A();
        } else if (score &amp;gt;= 80) {
            return new B();
        } else if (score &amp;gt;= 70) {
            return new C();
        } else if (score &amp;gt;= 60) {
            return new D();
        } else {
            return new F();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1673073590240&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    String jeff_score = GradeCalculator.of(36).toText();
    String herryPorter_score = GradeCalculator.of(99).toText();

    System.out.println(jeff_score); // F
    System.out.println(herryPorter_score); // A
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&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;span style=&quot;color: #222222;&quot;&gt;정적 팩토리 메서드 와 다른 정적 메서드와 역할을 구분짓기 위해 독자적인 네이밍 컨벤션&lt;span style=&quot;color: #222222;&quot;&gt;(Convention)&lt;/span&gt;이 존재한다. 단, 정적 팩토리 메서드에서의 네이밍은 단순히 선호도 의미를 넘어서 거의 &lt;b&gt;법칙 정도로 사용&lt;/b&gt;되는 것이니, 각 네이밍의 역할에 대해 알아두는 것은 &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: #222222;&quot;&gt;정적 팩토리 메서드에서 사용되는 네이밍 단어 종류는 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;from&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 하나의 매개 변수를 받아서 객체를 생성&lt;/li&gt;
&lt;li&gt;of&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 여러개의 매개 변수를 받아서 객체를 생성&lt;/li&gt;
&lt;li&gt;getInstance | instance&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 인스턴스를 생성. 이전에 반환했던 것과 같을 수 있음&lt;/li&gt;
&lt;li&gt;newInstance | create&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 항상 새로운 인스턴스를 생성&lt;/li&gt;
&lt;li&gt;get[OrderType]&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 다른 타입의 인스턴스를 생성. 이전에 반환했던 것과 같을 수 있음&lt;/li&gt;
&lt;li&gt;new[OrderType]&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 항상 다른 타입의 새로운 인스턴스를 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1672898083458&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// from : 매개변수를 하나 받아서 해당 타입의 인스턴스를 반환하는 형변환 메서드
Date d = Date.from(instant);&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1672898236539&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// of : 여러 매개변수를 받아 적합한 타입의 인스턴스를 반환하는 집계 메서드
Set&amp;lt;Rank&amp;gt; faceCards = EnumSet.of(JACK, QUEEN, KING);&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1672898240491&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// valueOf : from과 of의 더 자세한 버전
BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1672898243763&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// instance 혹은 getInstance : (매개변수를 받는다면) 매개변수로 명시한 인스턴스를 반환하지만, 같은 인스턴스임을 보장하지는 않는다.
StackWalker luke = StackWalker.getInstance(options);

Calendar instance = Calendar.getInstance();&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1672898247542&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// create 혹은 newInstance : instance 혹은 getInstance와 같지만, 매번 새로운 인스턴스를 생성해 반환함을 보장한다.
Object newArray = Array.newInstance(classObject, arrayLen);&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1672898252815&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// getType : getInstance와 같으나, 생성할 클래스가 아닌 다른 클래스에 팩터리 메서드를 정의할 때 쓴다. Type은 팩터리 메서드가 반환할 객체의 타입이다.
FileStore fs = Files.getFileStore(path);&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1672898257883&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// newType : newInstance와 같으나, 생성할 클래스가 아닌 다른 클래스에 팩터리 메서드를 정의할 때 쓴다. Type은 팩터리 메서드가 반환할 객체의 타입이다.
BufferedReader br = Files.newBufferdReader(path);&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1672898260642&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// type : getType과 newType의 간결한 버전
List&amp;lt;Complaint&amp;gt; litany = Collections.list(legacyLitany);&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&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;span style=&quot;color: #222222;&quot;&gt;사실 우리는 자바에서 제공하는 여러 패키지의 클래스들을 가져와 사용하면서 우리들은 알기도 모르게 정적 팩토리 메서드를 사용해 왔다. 그저 지칭하는 단어만 모르고 있을 뿐 그에 대한 톡톡한 효과를 누리고 있었던 것이다.&amp;nbsp;&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;Optional의 of()&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222;&quot;&gt;자바 8버전 부터 지원하는 Optional 객체를 얻기 위해선 아래와 같이 new 키워드 대신 &lt;s&gt;of()&lt;/s&gt; 메서드를 이용해 객체를 얻도록 설계 되어 있다. 위에서 배운 정적 팩토리 메서드 네이밍 컨밴션을 잘 따른 예이기도 하다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1672898945856&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Optional&amp;lt;Integer&amp;gt; value = Optional.of(1000);&lt;/code&gt;&lt;/pre&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;List의 of()&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222;&quot;&gt;이부분은 하위 자식 객체 반환 및 캡슐화에 대한 실전 구현체 예이다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222;&quot;&gt;자바 8버전 부터 지원하는 List 인터페이스의 &lt;s&gt;of()&lt;/s&gt; 메서드는 인터페이스를 반환하는 정적 팩토리 메서드이다. 사용자&lt;/span&gt; 입장에선 반환되는 클래스가 어떤 타입인지 알 필요 없이, 그냥 &lt;s&gt;of()&lt;/s&gt; 메서드의 기능이 무엇인지만 알고 호출만 하면 되는 것이다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// 리터럴 값으로 List 타입 객체 생성
List&amp;lt;Integer&amp;gt; list = List.of(1, 2, 3);

// 배열 객체로 List 타입 객체 생성
Integer[] array = {1,2};
List&amp;lt;Integer&amp;gt; list = List.of(array);&lt;/code&gt;&lt;/pre&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;Integer의 valueOf()&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이부분은 정적 팩토리 메서드의 '인스턴스를 캐싱하여 관리가 가능하다'는 실제 자바 구현체 예이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 primitive 타입의 Wrapper 클래스들은 값을 캐싱(Caching)하여 자바 프로그램을 최적화하도록 설계 되어 있다. 예를들어 Intefer 클래스의 &lt;s&gt;valueOf()&lt;/s&gt; 메서드는 입력값이 -128 ~ 127 범위의 정수라면 캐싱을 이용하도록 설계 되어있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;499&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nQ0Kg/btrVE1u90x0/oD9kZpyyOg9ub6o4CbT0tK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nQ0Kg/btrVE1u90x0/oD9kZpyyOg9ub6o4CbT0tK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nQ0Kg/btrVE1u90x0/oD9kZpyyOg9ub6o4CbT0tK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnQ0Kg%2FbtrVE1u90x0%2FoD9kZpyyOg9ub6o4CbT0tK%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; alt=&quot;static-factory-method-pattern&quot; loading=&quot;lazy&quot; width=&quot;724&quot; height=&quot;499&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;499&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;675&quot; data-origin-height=&quot;414&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/elbC8I/btrVzJQmAhG/914vNUiB0qP1n1cKX2dPP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/elbC8I/btrVzJQmAhG/914vNUiB0qP1n1cKX2dPP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/elbC8I/btrVzJQmAhG/914vNUiB0qP1n1cKX2dPP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FelbC8I%2FbtrVzJQmAhG%2F914vNUiB0qP1n1cKX2dPP0%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; alt=&quot;static-factory-method-pattern&quot; loading=&quot;lazy&quot; width=&quot;675&quot; height=&quot;414&quot; data-origin-width=&quot;675&quot; data-origin-height=&quot;414&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 Interger.valueOf(int i) 메서드 시그니처를 보자면, 파라미터&lt;span&gt;&amp;nbsp;&lt;/span&gt;i&lt;span&gt;&amp;nbsp;&lt;/span&gt;가 캐싱된 상수 숫자의 범위내에 있다면 객체를 새로 생성하지 않고 배열에 미리 생성해 두었던 객체를 반환하는 걸 볼 수 있다. 범위를 벗어난 경우&lt;span&gt; 새로운&lt;/span&gt; 객체를 생성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1673067318173&quot; class=&quot;reasonml&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// -128 ~ 127 는 캐시 됨
Integer i1 = Integer.valueOf(100);
Integer i2 = Integer.valueOf(100);

System.out.println(i1 == i2); // true

// -128 ~ 127 를 넘어선 수는 캐시 안됨
Integer i3 = Integer.valueOf(1000);
Integer i4 = Integer.valueOf(1000);

System.out.println(i3 == i4); // false&lt;/code&gt;&lt;/pre&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;Lombok으로 정적 팩토리 메서드 만들기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;롬복을 이용해 간단하게 정적 팩토리 메서드를 지정해줄 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1673079680007&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RequiredArgsConstructor(staticName = &quot;of&quot;)
class Product {
    private Long id;
    private String name;
}

public class Main {
    public static void main(String[] args) {
        Product p = Product.of();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&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;생성자보단 정적 팩토리 메서드를 고려해야 하지만, 정적 팩토리 메서드도 단점이 존재하니 이를 잘 파악하고 사용하여야 한다.&amp;nbsp;&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;&lt;span style=&quot;color: #ef5369;&quot;&gt;1.&amp;nbsp;&lt;/span&gt;private&amp;nbsp;생성자일&amp;nbsp;경우&amp;nbsp;상속&amp;nbsp;불가능&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 팩토리 메서드로 클래스를 설계를 하면 생성자를 private 접근 제어자로 설정하게 된다. 따라서 정적 팩토리 메서드를 적용하는 경우에는 상속을 이용한 확장이 불가능해진다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;539&quot; data-origin-height=&quot;369&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZqeKn/btrVqhl6VXJ/tlMjsQileFrk9a6niyhko0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZqeKn/btrVqhl6VXJ/tlMjsQileFrk9a6niyhko0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZqeKn/btrVqhl6VXJ/tlMjsQileFrk9a6niyhko0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZqeKn%2FbtrVqhl6VXJ%2FtlMjsQileFrk9a6niyhko0%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; alt=&quot;정적 팩토리 메서드&quot; loading=&quot;lazy&quot; width=&quot;539&quot; height=&quot;369&quot; data-origin-width=&quot;539&quot; data-origin-height=&quot;369&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만, 이 부분은 단점이라기 보단 스펙에 가깝다고 얘기할수 있다.&lt;br /&gt;대표적인 예로 자바의 Collections 클래스를 보면 생성자가 private로 되어 있다. 이는 일부로 상속을 하게 하지 않기 위한 설계된건데, Collections는 자바의 컬렉션에 대한 헬퍼 제공 용도이지 상속을 통해 무언가를 확장하게 하기 위한 용도가 아니기 때문이다. 따라서 이러한 제약은 상속(Inheritance) 보단 합성(Composition)을 사용하도록 유도하게 하거나, 불변(Immutable) 객체로 만들고 싶을때 사용되는 하나의 코드 패턴이라고 보면 된다.&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;&lt;span style=&quot;color: #ef5369;&quot;&gt;2.&amp;nbsp;&lt;/span&gt;API&amp;nbsp;문서에서의&amp;nbsp;불편함&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자는 하나의 자바 프로그래밍 언어의 스펙이기 때문에 &lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;JavaDoc 같은 문서에서 상단에 정의되어 있기 때문에 빠르게 그에 대한 스펙 검색을 할수가 있다. 반면에 정적 팩토리 메서드는 개발자가 임의로 만든 메서드이기 때문에, 안그래도 그 많은 메서드들 중에서 정적 팩토리 역할을 하는 메서드를 뒤져서 찾아 이해해야 한다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;따라서 클래스 설계자는&lt;b&gt; API 문서를 깔끔하게 작성&lt;/b&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-origin-width=&quot;1609&quot; data-origin-height=&quot;781&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baYwYc/btrVsIXOeCG/5z1CLpHHgYb08p7hoSB8Kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baYwYc/btrVsIXOeCG/5z1CLpHHgYb08p7hoSB8Kk/img.png&quot; data-alt=&quot;이 많은 메서드 설명을 뒤지기 전에 네이밍 컨밴션으로 역할을 유추가 가능하다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baYwYc/btrVsIXOeCG/5z1CLpHHgYb08p7hoSB8Kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaYwYc%2FbtrVsIXOeCG%2F5z1CLpHHgYb08p7hoSB8Kk%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; alt=&quot;정적 팩토리 메서드&quot; loading=&quot;lazy&quot; width=&quot;1609&quot; height=&quot;781&quot; data-origin-width=&quot;1609&quot; data-origin-height=&quot;781&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;이처럼 정적 팩터리 메서드와 생성자는 각각 장단점을 이해하고 사용하는 것이 좋으나, 대부분 정적 팩토리를 사용하는 게 유리한 경우가 더 많다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://tecoble.techcourse.co.kr/post/2020-05-26-static-factory-method/&lt;/p&gt;</description>
      <category>디자인 패턴/GOF +</category>
      <category>java factory</category>
      <category>자바 팩토리</category>
      <category>정적 팩토리</category>
      <category>팩토리 생성자</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1058</guid>
      <comments>https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EC%A0%95%EC%A0%81-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%83%9D%EC%84%B1%EC%9E%90-%EB%8C%80%EC%8B%A0-%EC%82%AC%EC%9A%A9%ED%95%98%EC%9E%90#entry1058comment</comments>
      <pubDate>Fri, 17 Mar 2023 19:53:32 +0900</pubDate>
    </item>
    <item>
      <title>  Template Callback 디자인 패턴</title>
      <link>https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-Template-Callback-%EB%B3%80%ED%98%95-%ED%8C%A8%ED%84%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;maxresdefault (1).webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3bDJI/btrTa4B5Fth/kk0o5PUDQSabKtLJJDPbA0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3bDJI/btrTa4B5Fth/kk0o5PUDQSabKtLJJDPbA0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3bDJI/btrTa4B5Fth/kk0o5PUDQSabKtLJJDPbA0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3bDJI%2FbtrTa4B5Fth%2Fkk0o5PUDQSabKtLJJDPbA0%2Fimg.webp&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; alt=&quot;Template-Callbac-pattern&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;720&quot; data-filename=&quot;maxresdefault (1).webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Template Callback Pattern&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탬플릿 콜백 패턴(Template Callback Pattern)은 스프링 프레임워크에서 DI(Dependency injection) 의존성 주입에서 사용하는 특별한 &lt;a href=&quot;https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EC%A0%84%EB%9E%B5Strategy-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;전략 패턴&lt;/a&gt;이다. 스프링의 JdbcTemplate, RestTemplate, TransactionTemplate, RedisTemplate과 같은곳에 사용된다.&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한마디로 &lt;span style=&quot;color: #ee2323;&quot;&gt;GOF 디자인 패턴은 아니고 전략 패턴의 확장판&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;용어도 그냥 전략 패턴에서의 컨텍스트(Context)를 템플릿으로 치환한 것일 뿐이며 콜백은 익명 클래스를 만들어진 메서드를 칭하는 것이다.&lt;/span&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;콜백(Callback) 이란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;프로그래밍에서 콜백(callback)은 하나의 오브젝트를 다른 오브젝트의 메소드에 매개변수로 넘겨주는 실행 가능한 코드를 말한다.&lt;span&gt; 파라미터로 전달되지만 값을 넘겨주는게 아닌 특정 로직을 담은 일종의 함수를 넘겨서 실행시키기 위해 사용 된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, callback은 코드가 호출(call)은 되는데 코드를 넘겨준 곳의 뒤(back)에서 실행된다는 것이다. 콜백을 넘겨받는 함수는 이 콜백 함수를 필요에 따라 즉시 실행할 수도 있고, 아니면 나중에 실행할 수도 있다.&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;자바스크립트의 콜백&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트를 배운 독자분들이라면 콜백이라는 단어는 질리도록 들어봤을 것이다. 간단하게 함수를 변수에 할당하고 함수의 인자로 넘겨주면 그것이 콜백이다.&lt;/p&gt;
&lt;pre id=&quot;code_1669736211455&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 콜백 함수
const callback = function(num) {
	return ++num;
}

// 콜백을 함수의 매개변수로 넘김
function increaseNum(num, callback) {
	let result = callback(num);
    console.log(result);
}

increaseNum(1, callback); // 2&lt;/code&gt;&lt;/pre&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;자바의 콜백&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본래 자바의 메소드는 &lt;a href=&quot;https://inpa.tistory.com/entry/CS-%F0%9F%91%A8%E2%80%8D%F0%9F%92%BB-%EC%9D%BC%EA%B8%89-%EA%B0%9D%EC%B2%B4first-class-object&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;일급 객체&lt;/a&gt;가 아니기 때문에 콜백 행위는 불가능 했다. 하지만 자바8에서 등장한 람다함수를 통해 콜백을 구현할 수 있게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;템플릿 콜백 패턴은 이름만 되게 거창해 보이지 사실 아래 코드 모습과 별반 다르지 않다. 어렵게 생각할 필요가 전혀 없다.&lt;/p&gt;
&lt;pre id=&quot;code_1670496192013&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface IAdd {
    int add(int x, int y);
}
 
public class Main {
    public static void main(String[] args) {
    	int n = result( (x, y) -&amp;gt; x + y ); // 메소드의 매개변수에 람다식을 전달
        System.out.println(n); // 3
    }
    
    public static int result(IAdd lambda) {
    	return lambda.add(1,2);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&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;span&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;span&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;b&gt;&lt;span&gt;클래스 구성&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1670503502839&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 콜백
interface Callback {
    int execute(final int n);
}

// 템플릿
class Template {
    int workflow(Callback cb) {
        System.out.println(&quot;Workflow 시작&quot;);
        int num = 100;
        int result = cb.execute(num);
        return result;
    }
}

// 클라이언트
public class Client {
    public static void main(String[] args) {
        int x = 100;
        int y = 20;

        Template t = new Template();
        int result = t.workflow(new Callback() {
            @Override
            public int execute(final int n) {
                return n * n;
            }
        });
        System.out.println(result); // 100 * 100 = 10000
    }
}&lt;/code&gt;&lt;/pre&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;&lt;span&gt;클래스 흐름&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;/span&gt;&lt;b&gt;&lt;span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1216&quot; data-origin-height=&quot;547&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wxp8U/btrTa7FpnkE/3F0jaulsIar6dgILzMo22K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wxp8U/btrTa7FpnkE/3F0jaulsIar6dgILzMo22K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wxp8U/btrTa7FpnkE/3F0jaulsIar6dgILzMo22K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwxp8U%2FbtrTa7FpnkE%2F3F0jaulsIar6dgILzMo22K%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; alt=&quot;Template-Callbac-pattern&quot; loading=&quot;lazy&quot; width=&quot;1216&quot; height=&quot;547&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1216&quot; data-origin-height=&quot;547&quot;/&gt;&lt;/span&gt;&lt;/figure&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;span&gt;클라이언트의 역할은 템플릿 안에서 실행될 로직을 담은 콜백(Callback) 객체를 만들고, 콜백이 참조할 정보를 제공한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;만들어진 콜백은 클라이언트가 템플릿의 메소드를 호출할 때 파라미터로 전달된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;템플릿은 정해진 흐름을 따라 작업(Workflow)을 시작한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;내부에서 생성한 참조 정보(변수)를 생성한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;콜백 오브젝트의 메소드 호출하면서 입력값으로 참조 정보(변수)를 전달한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;콜백은 클라이언트 메소드에 있는 정보와 템플릿이 제공한 참조정보를 이용하여&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;메소드 작업을 수행한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;작업 수행한 후 그 결과값을 다시 템플릿에 반환한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;템플릿은 콜백이 돌려준 정보를 사용해서 작업(Workflow)을 마저 수행한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;작업이 마무리되면,&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;최종 결과에 따라 클라이언트에 다시 반환한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #37352f;&quot;&gt;템플릿 콜백 패턴 특징&lt;/span&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;전략 패턴과 스프링의 의존성 주입(DI)의 장점을 익명 내부 클래스 사용 전략과 결합해 독특하게 활용되는 패턴&lt;/li&gt;
&lt;/ul&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;&lt;span style=&quot;background-color: #ffffff; color: #37352f;&quot;&gt;패턴 장점&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전략패턴은 따로 전략 알고리즘을 정해놓은 별도의 전략 클래스가 필요했지만, 템플릿-콜백 패턴은 별도의 전략 클래스 없이, 전략을 사용하는 메소드에 매개변수값으로 전략 로직을 넘겨 실행하기 때문에 전략 객체를 일일히 만들 필요가 없다.&lt;/li&gt;
&lt;li&gt;외부에서 어떤 전략을 사용하는지 감추고 중요한 부분에 집중할 수 있다.&lt;/li&gt;
&lt;/ul&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;&lt;span style=&quot;background-color: #ffffff; color: #37352f;&quot;&gt;패턴 단점&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스프링 클라이언트에서 DI를 사용하지 않게 되면, Bean으로 등록되지 않아 싱글톤 객체가 되지 않게 된다.&lt;/li&gt;
&lt;li&gt;인터페이스를 사용하지만 실제 사용할 클래스를 직접 선언하기 때문에 결합도가 증가하게 된다. 다만, 그렇다고 해서 무리하게 결합도를 낮추는 행위를 할 필요는 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;전략 패턴 &amp;rarr; 템플릿 콜백 패턴 변환 예제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;기존 전략 패턴 코드&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 사칙 연산을 전략으로 취급하여 구성한 예제 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사직 연산을 전략 객체로 구현한 OperationStrategy 군과 이를 사용하는 OperationContext 객체가 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;711&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XJsZ9/btrTb1xRnLT/kAidcnnSPy8l7BoHH9K4Q1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XJsZ9/btrTb1xRnLT/kAidcnnSPy8l7BoHH9K4Q1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XJsZ9/btrTb1xRnLT/kAidcnnSPy8l7BoHH9K4Q1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXJsZ9%2FbtrTb1xRnLT%2FkAidcnnSPy8l7BoHH9K4Q1%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; alt=&quot;Template-Callbac-pattern&quot; loading=&quot;lazy&quot; width=&quot;711&quot; height=&quot;306&quot; data-origin-width=&quot;711&quot; data-origin-height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1670498361727&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface OperationStrategy {
    // (int x, int y) -&amp;gt; int
    int calculate(int x, int y);
}

class Plus implements OperationStrategy {
    public int calculate(int x, int y) {
        return x + y;
    }
}

class Sub implements OperationStrategy {
    public int calculate(int x, int y) {
        return x - y;
    }
}

class Multi implements OperationStrategy {
    public int calculate(int x, int y) {
        return x * y;
    }
}

class Divide implements OperationStrategy {
    public int calculate(int x, int y) {
        return x / y;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1670498356994&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class OperationContext {
    OperationStrategy cal;

    void setOpertaion(OperationStrategy cal) {
        this.cal = cal;
    }
    int calculate(int x, int y) {
    	System.out.println(&quot;연산 시작&quot;);
        int result = cal.calculate(x, y);
        System.out.println(&quot;연산 종료&quot;);
        return result;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1670498350319&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Client {
    public static void main(String[] args) {
        int x = 100;
        int y = 30;

        OperationContext cxt = new OperationContext();

        cxt.setOpertaion(new Plus());
        int result = cxt.calculate(x, y);
        System.out.println(result); // 130

        cxt.setOpertaion(new Sub());
        result = cxt.calculate(x, y);
        System.out.println(result); // 70

        cxt.setOpertaion(new Multi());
        result = cxt.calculate(x, y);
        System.out.println(result); // 3000

        cxt.setOpertaion(new Divide());
        result = cxt.calculate(x, y);
        System.out.println(result); // 3
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;템플릿 콜백 패턴으로 변환해주면, 클라이언트에서 바로 익명 클래스 혹은 람다함수로 알고리즘 로직 코드를 정의한뒤 매개변수로 주입만 시켜주면 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Context가 템플릿 역할&lt;/li&gt;
&lt;li&gt;Strategy 부분이 콜백(Callback)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1670498161927&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface OperationStrategy {
    // (int x, int y) -&amp;gt; int
    int calculate(int x, int y);
}

// Template
class OperationTemplate {
    int calculate(int x, int y, OperationStrategy cal) {
    	System.out.println(&quot;연산 시작&quot;);
        int result = cal.calculate(x, y);
        System.out.println(&quot;연산 종료&quot;);
        return result;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1670499927383&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Client {
    public static void main(String[] args) {
        int x = 100;
        int y = 30;

        OperationTemplate cxt = new OperationTemplate();

        int result = cxt.calculate(x, y, new OperationStrategy() {
        	// callback
            @Override
            public int calculate(int x, int y) {
                return x + y;
            }
        });
        System.out.println(result); // 130

        result = cxt.calculate(x, y, new OperationStrategy() {
        	// callback
            @Override
            public int calculate(int x, int y) {
                return x - y;
            }
        });
        System.out.println(result); // 70

        result = cxt.calculate(x, y, new OperationStrategy() {
        	// callback
            @Override
            public int calculate(int x, int y) {
                return x * y;
            }
        });
        System.out.println(result); // 3000

        result = cxt.calculate(x, y, new OperationStrategy() {
        	// callback
            @Override
            public int calculate(int x, int y) {
                return x / y;
            }
        });
        System.out.println(result); // 3
    }
}&lt;/code&gt;&lt;/pre&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;중복 코드 제거하기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 클라이언트(main 메소드)에서 전략을 주입하는 부분을 자세히 보면 무언가 반복적으로 익명 클래스를 정의해 주입하는 모습이 보인다. 이러한 부분도 결국 쓸데없는 코드 중복이기 마련이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만, 이 부분은 템플릿 콜백 패턴을 사용하는데 좀 더 최적화를 위해 리팩토링(Refactoring)을 진행할 수도 있다는 예시이지, 반드시 이행할 필요는 없으며, 전략에 따라 선택적으로 이행하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드 역시 명답은 아니며 다양한 전략에 따라 얼마든지 변화될 수 있다는 점은 숙지하자.&lt;/p&gt;
&lt;pre id=&quot;code_1670500954540&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class OperationContext {

    int calculate(int x, String operation, int y) {
        System.out.println(&quot;연산 시작&quot;);
        int result = execute(operation).calculate(x, y);
        System.out.println(&quot;연산 종료&quot;);
        return result;
    }

    // 익명 클래스를 Template 클래스 내에 아예 정의함으로써 클라이언트의 전략 주입 중복 코드를 줄임
    private OperationStrategy execute(final String oper) {

        return new OperationStrategy() {
            public int calculate(int x, int y) {
                int result = 0;
                switch(oper) {
                    case &quot;+&quot; : result = x + y; break;
                    case &quot;-&quot; : result = x - y; break;
                    case &quot;*&quot; : result = x * y; break;
                    case &quot;/&quot; : result = x / y; break;
                }
                return result;
            }
        };
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1670500960856&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Client {
    public static void main(String[] args) {
        int x = 100;
        int y = 20;

        OperationContext cxt = new OperationContext();

        int result = cxt.calculate(x, &quot;+&quot;, y);
        System.out.println(result); // 120

        result = cxt.calculate(x, &quot;-&quot;, y);
        System.out.println(result); // 80

        result = cxt.calculate(x, &quot;*&quot;, y);
        System.out.println(result); // 2000

        result = cxt.calculate(x, &quot;/&quot;, y);
        System.out.println(result); // 5
    }
}&lt;/code&gt;&lt;/pre&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;람다로 변환하기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹은 함수형 인터페이스 구성으로도 처리되는 간단한 전략 알고리즘이라면, 람다표현식을 통해 코드를 획기적으로 짧게 줄일수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1670499829936&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Client {
    public static void main(String[] args) {
        int x = 100;
        int y = 30;

        OperationContext cxt = new OperationContext();

        int result = cxt.calculate(x, y, (x1, y1) -&amp;gt; x1 + y1);
        System.out.println(result); // 130

        result = cxt.calculate(x, y, (x1, y1) -&amp;gt; x1 - y1);
        System.out.println(result); // 70

        result = cxt.calculate(x, y, (x1, y1) -&amp;gt; x1 * y1);
        System.out.println(result); // 3000

        result = cxt.calculate(x, y, (x1, y1) -&amp;gt; x1 / y1);
        System.out.println(result); // 3
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;템플릿 메서드 패턴 &amp;rarr; 템플릿 콜백 패턴 변환 예제&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;템플릿 메서드의 전략 알고리즘 부분은 추상 메소드로서 서브 클래스가 상속하여 개별로 정의하는데, 상속(Inheritance)는 객체 지향 프로그래밍에서 지양해야 할 부분이기 때문에 따로 전략 패턴으로 재구성을 하거나, 아니면 &lt;u&gt;&lt;b&gt;콜백을 이용해 상속 대신 위임&lt;/b&gt;&lt;/u&gt;으로 처리 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;기존 템플릿 메서드 패턴 코드&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들자면 위의 전략 패턴 예제와 같은 사칙 연산 예제를 그대로 가져와 템플릿 메서드 패턴으로 표현하자면 아래와 같이 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;671&quot; data-origin-height=&quot;205&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cex60J/btrTbkEDWOD/0gLcM4s0lywzSKBnXoJgUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cex60J/btrTbkEDWOD/0gLcM4s0lywzSKBnXoJgUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cex60J/btrTbkEDWOD/0gLcM4s0lywzSKBnXoJgUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcex60J%2FbtrTbkEDWOD%2F0gLcM4s0lywzSKBnXoJgUk%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; alt=&quot;Template-Callbac-pattern&quot; loading=&quot;lazy&quot; width=&quot;671&quot; height=&quot;205&quot; data-origin-width=&quot;671&quot; data-origin-height=&quot;205&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1670501822563&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;abstract class OperationTemplate {

    public final void templateMethod(int x, int y) {
        int num1 = 100;
        int num2 = calculate(x, y);
        System.out.println(num1 * num2);
    }

    abstract int calculate(int x, int y);
}

class Plus extends OperationTemplate {

    @Override
    int calculate(int x, int y) {
        return x + y;
    }
}

class Sub extends OperationTemplate {

    @Override
    int calculate(int x, int y) {
        return x - y;
    }
}

class Multi extends OperationTemplate {

    @Override
    int calculate(int x, int y) {
        return x * y;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1670501828771&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Client {
    public static void main(String[] args) {
        int x = 100;
        int y = 20;

        // 1. 템플릿 메서드가 실행할 구현화한 하위 알고리즘 클래스 생성하고 템플릿 실행
        OperationTemplate template = new Plus();
        template.templateMethod(x, y); // 12000

        template = new Sub();
        template.templateMethod(x, y); // 8000

        template = new Multi();
        template.templateMethod(x, y); // 200000
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추상 메서드인 &lt;s&gt;calculate()&lt;/s&gt;를 각 서브 클래스들이 오버라이딩하여 재정의하는 형태인데, 이 역시 간단한 전략인 경우 서브 전략 클래스들을 없애고 콜백으로 바꿔버릴 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&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;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;647&quot; data-origin-height=&quot;350&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xiGiR/btrTaDLLh9Z/b5sCUn9AVEeH5Y9R1OgxP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xiGiR/btrTaDLLh9Z/b5sCUn9AVEeH5Y9R1OgxP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xiGiR/btrTaDLLh9Z/b5sCUn9AVEeH5Y9R1OgxP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxiGiR%2FbtrTaDLLh9Z%2Fb5sCUn9AVEeH5Y9R1OgxP1%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; alt=&quot;Template-Callbac-pattern&quot; loading=&quot;lazy&quot; width=&quot;647&quot; height=&quot;350&quot; data-origin-width=&quot;647&quot; data-origin-height=&quot;350&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1670502131371&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface OperationStrategy {
    // (int x, int y) -&amp;gt; int
    int calculate(int x, int y);
}

class OperationTemplate {

    public final void templateMethod(int x, int y, OperationStrategy oper) {
        int num1 = 100;
        int num2 = oper.calculate(x, y);
        System.out.println(num1 * num2);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1670502140137&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Client {
    public static void main(String[] args) {
        int x = 100;
        int y = 20;

        OperationTemplate template = new OperationTemplate();
        
        template.templateMethod(x, y, (x1, y1) -&amp;gt; x1 + y1); // 12000

        template.templateMethod(x, y, (x1, y1) -&amp;gt; x1 - y1); // 8000

        template.templateMethod(x, y, (x1, y1) -&amp;gt; x1 * y1); // 200000
    }
}&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;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot; data-swap-href=&quot;http://www.yes24.com/Product/Goods/4020006&quot;&gt;토비의 스프링3&amp;nbsp;(이일민)&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://yongdev.tistory.com/142&lt;/p&gt;</description>
      <category>디자인 패턴/GOF +</category>
      <category>Design Pattern</category>
      <category>spring design pattern</category>
      <category>template callback</category>
      <category>디자인 패턴</category>
      <category>스프링 디자인 패턴</category>
      <category>콜백 패턴</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1031</guid>
      <comments>https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-Template-Callback-%EB%B3%80%ED%98%95-%ED%8C%A8%ED%84%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0#entry1031comment</comments>
      <pubDate>Fri, 17 Mar 2023 19:53:20 +0900</pubDate>
    </item>
    <item>
      <title>  빌더(Builder) 패턴 - 완벽 마스터하기</title>
      <link>https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EB%B9%8C%EB%8D%94Builder-%ED%8C%A8%ED%84%B4-%EB%81%9D%ED%8C%90%EC%99%95-%EC%A0%95%EB%A6%AC</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img (84).webp&quot; data-origin-width=&quot;1237&quot; data-origin-height=&quot;660&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HuGvz/btrVK0CSOOG/NtWXYOGuRQ97lgZIxyCbTk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HuGvz/btrVK0CSOOG/NtWXYOGuRQ97lgZIxyCbTk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HuGvz/btrVK0CSOOG/NtWXYOGuRQ97lgZIxyCbTk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHuGvz%2FbtrVK0CSOOG%2FNtWXYOGuRQ97lgZIxyCbTk%2Fimg.webp&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; alt=&quot;gof-builder-pattern&quot; loading=&quot;lazy&quot; width=&quot;1237&quot; height=&quot;660&quot; data-filename=&quot;img (84).webp&quot; data-origin-width=&quot;1237&quot; data-origin-height=&quot;660&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;100&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dgvhYi/btrU6X9jNzx/zUtUr7WBd60cNoYZJ2auBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dgvhYi/btrU6X9jNzx/zUtUr7WBd60cNoYZJ2auBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dgvhYi/btrU6X9jNzx/zUtUr7WBd60cNoYZJ2auBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdgvhYi%2FbtrU6X9jNzx%2FzUtUr7WBd60cNoYZJ2auBK%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; alt=&quot;gof-builder-pattern&quot; loading=&quot;lazy&quot; width=&quot;140&quot; height=&quot;100&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;100&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Builder Pattern&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌더 패턴(Builder Pattern)은 복잡한 객체의 생성 과정과 표현 방법을 분리하여 다양한 구성의 인스턴스를 만드는 생성 패턴이다. 생성자에 들어갈 매개 변수를 메서드로 하나하나 받아들이고 마지막에 통합 빌드해서 객체를 생성하는 방식이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이해하기 쉬운 사례로 수제 햄버거를 들 수 있다. 수제 햄버거를 주문할때 빵이나 패티 등 속재료들은 주문하는 사람이 마음대로 결정된다. 어느 사람은 치즈를 빼달라고 할수 있고 어느 사람은 토마토를 빼달라고 할수 있다. 이처럼 선택적 속재료들을 보다 유연하게 받아 다양한 타입의 인스턴스를 생성할수 있어, 클래스의 선택적 매개변수가 많은 상황에서 유용하게 사용된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;563&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Cf49Q/btrVAaGS9dC/DJJXwYUxxrkc0nxiAxTqAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Cf49Q/btrVAaGS9dC/DJJXwYUxxrkc0nxiAxTqAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Cf49Q/btrVAaGS9dC/DJJXwYUxxrkc0nxiAxTqAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCf49Q%2FbtrVAaGS9dC%2FDJJXwYUxxrkc0nxiAxTqAK%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; alt=&quot;gof-builder-pattern&quot; loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;563&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;563&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;style3&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;점층적 생성자 패턴&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;점층적 생성자 패턴(Telescoping Constructor Pattern)은 필수 매개변수와 함께 선택 매개변수를 0개, 1개, 2개 .. 받는 형태로, 우리가 다양한 매개변수를 입력받아 인스턴스를 생성하고 싶을때 사용하던 &lt;b&gt;생성자를 오버로딩&lt;/b&gt; 하는 방식이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1673080245130&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Hamburger {
    // 필수 매개변수
    private int bun;
    private int patty;

    // 선택 매개변수
    private int cheese;
    private int lettuce;
    private int tomato;
    private int bacon;

    public Hamburger(int bun, int patty, int cheese, int lettuce, int tomato, int bacon) {
        this.bun = bun;
        this.patty = patty;
        this.cheese = cheese;
        this.lettuce = lettuce;
        this.tomato = tomato;
        this.bacon = bacon;
    }

    public Hamburger(int bun, int patty, int cheese, int lettuce, int tomato) {
        this.bun = bun;
        this.patty = patty;
        this.cheese = cheese;
        this.lettuce = lettuce;
        this.tomato = tomato;
    }
    

    public Hamburger(int bun, int patty, int cheese, int lettuce) {
        this.bun = bun;
        this.patty = patty;
        this.cheese = cheese;
        this.lettuce = lettuce;
    }

    public Hamburger(int bun, int patty, int cheese) {
        this.bun = bun;
        this.patty = patty;
        this.cheese = cheese;
    }

    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1673080777973&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    // 모든 재료가 있는 햄버거
    Hamburger hamburger1 = new Hamburger(2, 1, 2, 4, 6, 8);

    // 빵과 패티 치즈만 있는 햄버거
    Hamburger hamburger2 = new Hamburger(2, 1, 1);

    // 빵과 패티 베이컨만 있는 햄버거
    Hamburger hamburger3 = new Hamburger(2, 0, 0, 0, 0, 6);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이러한 방식은 클래스 인스턴스 필드들이 많으면 많을 수록 생성자에 들어갈 인자의 수가 늘어나 몇번째 인자가 어떤 필드였는지 햇갈릴 경우가 생기게 된다. 만일 여러 종류의 햄버거를 생성하기 위해 Hamburger 생성자의 몇번째 인수가 양상추의 갯수인지 토마토의 갯수인지 파악할 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 매개변수 특성상 순서를 따라야 하기 때문에 위의 '빵과 베이컨만 있는 햄버거'를 원할경우 억지로 파라미터에 0을 전달해야 된다. 생성자로만으로는 필드를 선택적으로 생략할 수 있는 방법이 없기 때문이다.&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;자바 빈(Java Beans) 패턴&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 단점을 보완하기 위해 Setter 메소드를 사용한 자바 빈(Bean) 패턴이 고안 되었다. 매개변수가 없는 생성자로 객체 생성후 Setter 메소드를 이용해 클래스 필드의 초깃값을 설정하는 방식이다.&lt;/p&gt;
&lt;pre id=&quot;code_1672729625687&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Hamburger {
    // 필수 매개변수
    private int bun;
    private int patty;

    // 선택 매개변수
    private int cheese;
    private int lettuce;
    private int tomato;
    private int bacon;
    
    public Hamburger() {}

    public void setBun(int bun) {
        this.bun = bun;
    }

    public void setPatty(int patty) {
        this.patty = patty;
    }

    public void setCheese(int cheese) {
        this.cheese = cheese;
    }

    public void setLettuce(int lettuce) {
        this.lettuce = lettuce;
    }

    public void setTomato(int tomato) {
        this.tomato = tomato;
    }

    public void setBacon(int bacon) {
        this.bacon = bacon;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1672729630713&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    // 모든 재료가 있는 햄버거
    Hamburger hamburger1 = new Hamburger();
    hamburger1.setBun(2);
    hamburger1.setPatty(1);
    hamburger1.setCheese(2);
    hamburger1.setLettuce(4);
    hamburger1.setTomato(6);
    hamburger1.setBacon(8);

    // 빵과 패티 치즈만 있는 햄버거
    Hamburger hamburger2 = new Hamburger();
    hamburger2.setBun(2);
    hamburger2.setPatty(1);
    hamburger2.setCheese(2);

    // 빵과 패티 베이컨만 있는 햄버거
    Hamburger hamburger3 = new Hamburger();
    hamburger3.setBun(2);
    hamburger2.setPatty(1);
    hamburger3.setBacon(8);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 생성자 오버로딩에서 나타났던 가독성 문제점이 사라지고 선택적인 파라미터에 대해 해당되는 Setter 메서드를 호출함으로써 유연적으로 객체 생성이 가능해졌다. 하지만 이러한 방식은 객체 생성 시점에 모든 값들을 주입 하지 않아 &lt;b&gt;일관성(consistency)&lt;/b&gt; 문제와 &lt;b&gt;불변성(immutable)&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;size18&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;1) 일관성 문제&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필수 매개변수란 객체가 초기화될때 반드시 설정되어야 하는 값이다. 하지만 개발자가 깜빡하고 &lt;s&gt;setBun()&lt;/s&gt; 이나 &lt;s&gt;setPatty()&lt;/s&gt; 메서드를 호출하지 않았다면 이 객체는 일관성이 무너진 상태가 된다. 즉, 객체가 유효하지 않은 것이다. 만일 다른곳에서 햄버거 인스턴스를 사용하게 된다면 런타임 예외가 발생할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 객체를 생성하는 부분과 값을 설정하는 부분이 물리적으로 떨어져 있어서 발생하는 문제점이다. 물론 이는 어느정도 생성자(Constructor)와 결합하여 극복은 할 수 있다. 하지만 다음에 소개할 불변성의 문제 때문에 자바 빈즈 패턴은 지양해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;2) 불변성 문제&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바 빈즈 패턴의 Setter 메서드는 객체를 처음 생성할때 필드값을 설정하기 위해 존재하는 메서드이다. 하지만 객체를 생성했음에도 여전히 외부적으로 Setter 메소드를 노출하고 있으므로, 협업 과정에서 언제 어디서 누군가 Setter 메서드를 호출해 함부로 객체를 조작할수 있게 된다. 이것을 불변함을 보장할 수 없다고 얘기한다.&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;빌더(Builder) 패턴&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;빌더 패턴은 이러한 문제들을 해결하기 위해 &lt;span style=&quot;color: #ee2323;&quot;&gt;별도의 Builder 클래스를 만들어&lt;/span&gt; 메소드를 통해 &lt;span style=&quot;color: #ee2323;&quot;&gt;step-by-step &lt;/span&gt;으로 값을 입력받은 후에 최종적으로 &lt;s&gt;build()&lt;/s&gt; 메소드로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span&gt;하나의 인스턴스를 생성하여 리턴하는 패턴이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌더 패턴 사용법을 잠시 살펴보면, StudentBuilder 빌더 클래스의 메서드를 체이닝(Chaining) 형태로 호출함으로써 자연스럽게 인스턴스를 구성하고 마지막에&amp;nbsp;&lt;s&gt;build()&lt;/s&gt;&amp;nbsp;메서드를 통해 최종적으로 객체를 생성하도록 되어있음을 볼 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1672734403864&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {

    // 생성자 방식
    Hamburger hamburger = new Hamburger(2, 3, 0, 3, 0, 0);

    // 빌더 방식
    Hamburger hamburger = new Hamburger.Builder(10)
        .bun(2)
        .patty(3)
        .lettuce(3)
        .build();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span&gt;빌더 패턴을 이용하면 더이상 생성자 오버로딩 열거를 하지 않아도 되며, 데이터의 순서에 상관없이 객체를 만들어내 생성자 인자 순서를 파악할 필요도 없고 잘못된 값을 넣는 실수도 하지 않게 된다. &lt;span style=&quot;background-color: #ffffff; color: #212529;&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;빌더 패턴&lt;span&gt;&lt;span&gt;&lt;span&gt; 구조&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&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;background-color: #ffffff;&quot;&gt;예를들어 다음과 같은 Student 클래스에 대한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;객체 생성만을 담당하는 별도의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;빌더 클래스를 만들려고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1672732950746&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Student {
    private int id;
    private String name = &quot;아무개&quot;;
    private String grade = &quot;freshman&quot;;
    private String phoneNumber = &quot;010-0000-0000&quot;;

    public Student(int id, String name, String grade, String phoneNumber) {
        this.id = id;
        this.name = name;
        this.grade = grade;
        this.phoneNumber = phoneNumber;
    }
    
    @Override
    public String toString() {
        return &quot;Student { &quot; +
                &quot;id='&quot; + id + '\'' +
                &quot;, name=&quot; + name +
                &quot;, grade=&quot; + grade +
                &quot;, phoneNumber=&quot; + phoneNumber +
                &quot; }&quot;;
    }
}&lt;/code&gt;&lt;/pre&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;빌더 클래스 구현하기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span&gt;먼저 Builder 클래스를 만들고 필드 멤버 구성을 만들고자 하는 Student 클래스 멤버 구성과 똑같이 구성한다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1672732968318&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class StudentBuilder {
    private int id;
    private String name;
    private String grade;
    private String phoneNumber;
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그리고 각 맴버에대한 Setter 메서드를 구현해준다. 이때 가독성을 좋게 하면서도 기존 Setter와의 다른 특성을 가지고 있는 점을 알리기 위해서, set 단어는 빼주고 심플하게 멤버이름으로만 메서드명을 지어준다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1672733173577&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class StudentBuilder {
    private int id;
    private String name;
    private String grade;
    private String phoneNumber;

    public StudentBuilder id(int id) {
        this.id = id;
        return this;
    }

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

    public StudentBuilder grade(String grade) {
        this.grade = grade;
        return this;
    }

    public StudentBuilder phoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
        return this;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 주목할 부분은 각 Setter 함수 마지막 반환 구문인&amp;nbsp;&lt;s&gt;return this&lt;/s&gt;&amp;nbsp;부분이다. 여기서 &lt;s&gt;this&lt;/s&gt;란 StudentBuilder 객체 자신을 말한다. 즉, 빌더 객체 자신을 리턴함으로써 메서드 호출 후 연속적으로 빌더 메서드들을 체이닝(Chaining) 하여 호출할 수 있게 된다.&amp;nbsp;ex)&amp;nbsp;&lt;s&gt;new StudentBuilder().id(값).name(값)&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 빌더의 목표였던 최종 Student 객체를 만들어주는 build 메서드를 구성해준다. 빌더 클래스의 필드들을 Student 생성자의 인자에 넣어줌으로써 멤버 구성이 완료된 Student 인스턴스를 얻게 되는 것이다.&lt;span style=&quot;color: #000000;&quot;&gt;&lt;s&gt;&lt;/s&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1672733647146&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class StudentBuilder {
    private int id;
    private String name;
    private String grade;
    private String phoneNumber;

    public StudentBuilder id(int id) { ... }

    public StudentBuilder name(String name) { ... }

    public StudentBuilder grade(String grade) { ... }

    public StudentBuilder phoneNumber(String phoneNumber) { ... }

    public Student build() {
        return new Student(id, name, grade, phoneNumber); // Student 생성자 호출
    }
}&lt;/code&gt;&lt;/pre&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;빌더 클래스 실행하기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 구성한 빌더 객체를 실행하면 아래와 같은 성질의 코드가 구현되게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1672733984253&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {

    Student student = new StudentBuilder()
                .id(2016120091)
                .name(&quot;임꺽정&quot;)
                .grade(&quot;Senior&quot;)
                .phoneNumber(&quot;010-5555-5555&quot;)
                .build();

    System.out.println(student);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;458&quot; data-origin-height=&quot;48&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZ6iDj/btrVf8o5Kh2/VWJWahUOQbqKqPw0SgPHp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZ6iDj/btrVf8o5Kh2/VWJWahUOQbqKqPw0SgPHp1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZ6iDj/btrVf8o5Kh2/VWJWahUOQbqKqPw0SgPHp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZ6iDj%2FbtrVf8o5Kh2%2FVWJWahUOQbqKqPw0SgPHp1%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; alt=&quot;빌더 패턴&quot; loading=&quot;lazy&quot; width=&quot;458&quot; height=&quot;48&quot; data-origin-width=&quot;458&quot; data-origin-height=&quot;48&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;빌더 패턴&lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&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;span&gt;빌더 패턴의 멤버 설정 메서드 네이밍 방식에는 대표적으로 3가지 정도 존재한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;멤버이름()&lt;/li&gt;
&lt;li&gt;set멤버이름()&lt;/li&gt;
&lt;li&gt;with멤버이름()&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1672737284822&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Student student = new StudentBuilder(2016120091)
        .name(&quot;홍길동&quot;)
        .grade(&quot;freshman&quot;)
        .phoneNumber(&quot;010-5555-5555&quot;)
        .build();&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1672737284822&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Student student = new StudentBuilder(2016120091)
        .setName(&quot;홍길동&quot;)
        .setGrade(&quot;freshman&quot;)
        .setPhoneNumber(&quot;010-5555-5555&quot;)
        .build();&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1672737284822&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Student student = new StudentBuilder(2016120091)
        .withName(&quot;홍길동&quot;)
        .withGrade(&quot;freshman&quot;)
        .withPhoneNumber(&quot;010-5555-5555&quot;)
        .build();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&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;&lt;span&gt;&lt;span&gt;두번째 네이밍 방식은 정통적인 자바(Java) 스러운 네이밍 형식인데 일반 Setter 메소드와 햇깔릴 소지가 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;세번째는 Setter와 구분키 위해 'with' 라는 키워드를 사용한것이며, 빌더 지연 생성 방식에서 미리 빌더를 설정할때 쓰이기도 한다.&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;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;b&gt;Builder 패턴&lt;/b&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt; 장단점 총정리&amp;nbsp;&lt;/span&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;빌더 패턴&lt;span&gt;&lt;span&gt; 장점&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h3&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;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;1.&lt;/span&gt;&lt;/span&gt; 객체&amp;nbsp;생성 과정을 &lt;b&gt;일관된 프로세스로 &lt;/b&gt;표현&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;생성자 방식으로 객체를 생성하는 경우는 매개변수가 많아질수록 가독성이 급격하게 떨어진다. &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;클래스 변수가 4개 이상만 되어도 &lt;/span&gt;각 인자 순서 마다 이 값이 어떤 멤버에 해당되는지&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt; 바로 파악이 힘들다.&amp;nbsp;&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: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;반면 다음과 같이 빌더 패턴을 적용하면 직관적으로 어떤 데이터에 어떤 값이 설정되는지 한눈에 파악할 수 있게 된다. &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;특히 연속된 동일 타입의 매개 변수를 많이 설정할 경우에 발생할 수 있는 설정 오류와 같은 실수를 방지할 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1672717196539&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 생성자 방식
Student student1 = new Student(2016120091, &quot;홍길동&quot;, &quot;freshman&quot;, &quot;010-5555-5555&quot;);

// 빌더 방식
Student student2 = new StudentBuilder()
            .id(2016120091)
            .name(&quot;임꺽정&quot;)
            .grade(&quot;Senior&quot;)
            .phoneNumber(&quot;010-5555-5555&quot;)
            .build();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 빌더 패턴이 탄생하게된 옛 시대와 달리, 요즘에는 인텔리제이나 이클립스 같은 왠만한 IDE에선 아래와 같이 생성자 매개변수에 대한 &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-origin-width=&quot;909&quot; data-origin-height=&quot;76&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMtoau/btrVh5Y70lV/Ikxw91UbwMOTBE6Ega2a7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMtoau/btrVh5Y70lV/Ikxw91UbwMOTBE6Ega2a7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMtoau/btrVh5Y70lV/Ikxw91UbwMOTBE6Ega2a7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMtoau%2FbtrVh5Y70lV%2FIkxw91UbwMOTBE6Ega2a7K%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; alt=&quot;builder-pattern&quot; loading=&quot;lazy&quot; width=&quot;909&quot; height=&quot;76&quot; data-origin-width=&quot;909&quot; data-origin-height=&quot;76&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;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;2.&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;디폴트 매개변수 생략을 간접적으로 지원&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;본래 디폴트 매개변수라는 건 인자 값을 설정해줘도 되고 설정 안하고 생략해도 되는것을 말한다.&lt;span&gt; 그런데 &lt;/span&gt;&lt;/span&gt;파이썬이나 자바스크립트와 달리 자바 언어에선 기본적으로 메서드에 대한 &lt;span style=&quot;color: #ee2323;&quot;&gt;디폴트 매개변수를 지원하지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;166&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/65TjO/btrVjiquhgj/wcsgg3C7qSCxVuASKlMgx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/65TjO/btrVjiquhgj/wcsgg3C7qSCxVuASKlMgx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/65TjO/btrVjiquhgj/wcsgg3C7qSCxVuASKlMgx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F65TjO%2FbtrVjiquhgj%2Fwcsgg3C7qSCxVuASKlMgx1%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; alt=&quot;builder-pattern&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;166&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;166&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 디폴트 매개변수를 구현하기 위해선 클래스 필드 변수에 초깃값을 미리 세팅하고, 초깃값이 세팅된 필드 인자를 제외시킨 생성자를 따로 구현하는 식으로 설계해야 한다. &lt;span&gt;하지만 이는 결국 지나친 생성자 오버로딩 열거를 통한 본래의 문제점을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;회귀&lt;/b&gt;한 꼴이 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1672717196548&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Student {
    private int id;
    private String name;
    private String grade = &quot;freshman&quot;; // 디폴트 매개변수 역할
    private String phoneNumber;

    public Student(int id, String name, String grade, String phoneNumber) {
        ...
    }
	
    // 디폴트 매개변수를 제외한 인자들을 받는 생성자 오버로딩
    public Student(int id, String name, String phoneNumber) {
        ...
    }

    @Override
    public String toString() {
        return &quot;Student { &quot; +
                &quot;id='&quot; + id + '\'' +
                &quot;, name=&quot; + name +
                &quot;, grade=&quot; + grade +
                &quot;, phoneNumber=&quot; + phoneNumber +
                &quot; }&quot;;
    }
}&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 id=&quot;code_1672717196551&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class StudentBuilder {
    private int id;
    private String name;
    private String grade = &quot;freshman&quot;; // 디폴트 매개변수 역할
    private String phoneNumber;

    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1672717196553&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 디폴트 필드인 grade를 제외하고 빌더 구성 및 인스턴스화
Student student1 = new StudentBuilder(2016120091)
        .name(&quot;홍길동&quot;)
        .phoneNumber(&quot;010-5555-5555&quot;)
        .build();

System.out.println(student1);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;675&quot; data-origin-height=&quot;60&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lDMbG/btrVf8Wde5c/KC0LT8NJcB0EU8KPLQJd40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lDMbG/btrVf8Wde5c/KC0LT8NJcB0EU8KPLQJd40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lDMbG/btrVf8Wde5c/KC0LT8NJcB0EU8KPLQJd40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlDMbG%2FbtrVf8Wde5c%2FKC0LT8NJcB0EU8KPLQJd40%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; alt=&quot;builder-pattern&quot; loading=&quot;lazy&quot; width=&quot;675&quot; height=&quot;60&quot; data-origin-width=&quot;675&quot; data-origin-height=&quot;60&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;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;3.&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;필수 멤버와 선택적 멤버를 분리 가능&lt;/b&gt;&lt;/h4&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;만일 Student 클래스의 id 필드가 인스턴스화 할때 반드시 필수적으로 값을 지정해 주어야 하는 필수 멤버 변수라고 가정해보자. 이를 기존 생성자 방식으로 구현하려면 초기화가 필수인 멤버 변수만을 위한 생성자를 정의하고 선택적인 멤버 변수에 대응하는 생성자를 오버로딩을 통해 열거하거나, 혹은 전체 멤버를 인자로 받는 생성자만을 선언하고 매개변수에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;null&lt;/i&gt;을 받는식으로 구성하여야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1672730300240&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Student {
    // 초기화 필수 멤버
    private int id;

    // 초기화 선택적 멤버
    private String name;
    private String grade;
    private String phoneNumber;

    public Student(int id, String name, String grade, String phoneNumber) {
        this.id = id;
        this.name = name;
        this.grade = grade;
        this.phoneNumber = phoneNumber;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1672730306996&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Student student = new Student(2010234455, null, 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;따라서 빌더 클래스를 통해 &lt;span style=&quot;color: #ee2323;&quot;&gt;초기화가 필수인 멤버는 빌더의 생성자로 받게 하여&lt;/span&gt; &lt;span style=&quot;color: #ee2323;&quot;&gt;필수 멤버를 설정해주어야 빌더 객체가 생성되도록 유도&lt;/span&gt;하고, 선택적인 멤버는 빌더의 메서드로 받도록 하면, 사용자로 하여금 필수 멤버와 선택 멤버를 구분하여 객체 생성을 유도할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1672730568367&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class StudentBuilder {
    // 초기화 필수 멤버
    private int id;

    // 초기화 선택적 멤버
    private String name;
    private String grade;
    private String phoneNumber;

    // 필수 멤버는 빌더의 생성자를 통해 설정
    public StudentBuilder(int id) {
        this.id = id;
    }

    // 나머지 선택 멤버는 메서드로 설정
    public StudentBuilder name(String name) {
        this.name = name;
        return this;
    }

    public StudentBuilder grade(String grade) {
        this.grade = grade;
        return this;
    }

    public StudentBuilder phoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
        return this;
    }

    public Student build() {
        return new Student(id, name, grade, phoneNumber);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1672730561500&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Student student1 = 
        new StudentBuilder(2016120091) // 필수 멤버
        .name(&quot;홍길동&quot;) // 선택 멤버
        .build();

Student student2 = 
        new StudentBuilder(2016120091) // 필수 멤버
        .name(&quot;임꺽정&quot;) // 선택 멤버
        .grade(&quot;freshman&quot;) // 선택 멤버
        .build();

Student student3 = 
        new StudentBuilder(2016120091) // 필수 멤버
        .name(&quot;주몽&quot;) // 선택 멤버
        .grade(&quot;Senior&quot;) // 선택 멤버
        .phoneNumber(&quot;010-5555-5555&quot;) // 선택 멤버
        .build();&lt;/code&gt;&lt;/pre&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;&lt;span style=&quot;color: #006dd7;&quot;&gt;4.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;객체 생성 단계를 지연할 수 있음&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 생성을 단계별로 구성하거나 구성 단계를 지연하거나 재귀적으로 생성을 처리 할수 있다. 즉, 빌더를 재사용 함으로써 객체 생성을 주도적으로 지연할 수 있는 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1672727689982&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. 빌더 클래스 전용 리스트 생성
List&amp;lt;StudentBuilder&amp;gt; builders = new ArrayList&amp;lt;&amp;gt;();

// 2. 객체를 최종 생성 하지말고 초깃값만 세팅한 빌더만 생성
builders.add(
    new StudentBuilder(2016120091)
    .name(&quot;홍길동&quot;)
);

builders.add(
    new StudentBuilder(2016120092)
    .name(&quot;임꺽정&quot;)
    .grade(&quot;senior&quot;)
);

builders.add(
    new StudentBuilder(2016120093)
    .name(&quot;박혁거세&quot;)
    .grade(&quot;sophomore&quot;)
    .phoneNumber(&quot;010-5555-5555&quot;)
);

// 3. 나중에 빌더 리스트를 순회하여 최종 객체 생성을 주도
for(StudentBuilder b : builders) {
    Student student = b.build();
    System.out.println(student);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;492&quot; data-origin-height=&quot;79&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJYJwZ/btrVh7b74yl/QsEJkRXGIYGaMTykgLx7jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJYJwZ/btrVh7b74yl/QsEJkRXGIYGaMTykgLx7jk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJYJwZ/btrVh7b74yl/QsEJkRXGIYGaMTykgLx7jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJYJwZ%2FbtrVh7b74yl%2FQsEJkRXGIYGaMTykgLx7jk%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; alt=&quot;builder-pattern&quot; loading=&quot;lazy&quot; width=&quot;492&quot; height=&quot;79&quot; data-origin-width=&quot;492&quot; data-origin-height=&quot;79&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;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;6.&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;초기화 검증을 멤버별로 분리&lt;/b&gt;&lt;/h4&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;span style=&quot;color: #000000;&quot;&gt;생성자의 크기가 비대해지게 되는 결과를 낳게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1672730632481&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Student {
	...

	// 각 매개변수에 대한 검증을 하나의 생성자 모두 처리하고 있다
    public Student(int id, String name, String grade, String phoneNumber) {
        if (!grade.equals(&quot;freshman&quot;) &amp;amp;&amp;amp; !grade.equals(&quot;sophomore&quot;) &amp;amp;&amp;amp; !grade.equals(&quot;junior&quot;) &amp;amp;&amp;amp; !grade.equals(&quot;senior&quot;)) {
            throw new IllegalArgumentException(grade);
        }

        if (!phoneNumber.startsWith(&quot;010&quot;)) {
            throw new IllegalArgumentException(phoneNumber);
        }

        this.id = id;
        this.name = name;
        this.grade = grade;
        this.phoneNumber = phoneNumber;
    }
}&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;color: #000000;&quot;&gt;빌더를 이용하면 생성될 객체의 멤버 변수의 초기화와 검증을 각각의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;멤버&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;별로 분리해서 작성할 수 있다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;빌더의 각각의멤버 설정 메서드에서 검증 과정을 분담함으로써 유지 보수를 용이하게 하는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1672730632483&quot; class=&quot;cs&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class StudentBuilder {
	...

    public StudentBuilder(int id) {
        this.id = id;
    }

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

    public StudentBuilder grade(String grade) {
        if (!grade.equals(&quot;freshman&quot;) &amp;amp;&amp;amp; !grade.equals(&quot;sophomore&quot;) &amp;amp;&amp;amp; !grade.equals(&quot;junior&quot;) &amp;amp;&amp;amp; !grade.equals(&quot;senior&quot;)) {
            throw new IllegalArgumentException(grade);
        }
        this.grade = grade;
        return this;
    }

    public StudentBuilder phoneNumber(String phoneNumber) {
        if (!phoneNumber.startsWith(&quot;010&quot;)) {
            throw new IllegalArgumentException(phoneNumber);
        }
        this.phoneNumber = phoneNumber;
        return this;
    }

    public Student build() {
        return new Student(id, name, grade, phoneNumber);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;물론 이러한 형식은 흔히 Getter &amp;amp; Setter 형식에서도 많이 이용되는 패턴이기도 하다. 하지만 어느 클래스에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Setter 메서드를 구현한다는 말은 객체 멤버의 변경 가능성을 열어둔것&lt;span style=&quot;background-color: #ffffff; 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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;7.&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;멤버에 대한 변경 가능성 최소화를 추구&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 개발자들이 자바 프로그래밍을 하면서 멤버에 값을 할당할때 흔히 사용하는 것이 Setter 메서드인데, 그중&amp;nbsp;&lt;span style=&quot;color: #ee2323;&quot;&gt;클래스 맴버 초기화를 Setter을 통해 구성하는 것은&amp;nbsp;매우 좋지 않은 방법&lt;/span&gt;이다. 즉, 이 부분은 위의 빌더 패턴 탄생 배경에서 다루었던 Java Beans Pattern인 Setter 메서드를 통해 멤버 초기화를 하지 말아야 하는 이유에 대한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;&lt;b&gt;좀 더 고수준 적인 내용&lt;/b&gt;&lt;/u&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 프로그램을 개발하는데 있어 다른 사람과 협업할때 가장 중요시되는 점 중 하나가 바로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;불변(immutalbe) 객체&lt;/b&gt;이다. 불변 객체란 객체 생성 이후 내부의 상태가 변하지 않는 객체이다. 불변 객체는 오로지 읽기(get) 메소드만을 제공하며 쓰기(set)는 제공하지 않는다. 대표적으로 자바에서 final 키워드를 붙인 변수가 바로 불변이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현업에서 불변 객체를 이용해 개발해야 하는 이유로는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;불변 객체는 Thread-Safe 하여 동기화를 고려하지 않아도 된다&lt;/li&gt;
&lt;li&gt;만일 가변 객체를 통해 작업을 하는 도중 예외(Exception)가 발생하면 해당 객체가 불안정한 상태에 빠질 수 있어 또 다른 에러를 유발할 수 있는 위험성이 있기 때문이다.&lt;/li&gt;
&lt;li&gt;불변 객체로 구성하면 다른 사람이 개발한 함수를 위험없이 이용을 보장할 수 있어 협업에도 유지보수에도 유용하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 클래스들은 가변적 이여야 하는 매우 타당한 이유가 있지 않는 한 반드시 불변으로 만들어야 한다. &lt;span style=&quot;color: #ee2323;&quot;&gt;만약 클래스를 불변으로 만드는 것이 불가능하다면 가능한 변경 가능성을 최소화&lt;/span&gt;해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어 경우에 따라 변수에 final 키워드를 붙일수 없는 상황이 생길 수도 있다. 이때는 Setter 메서드 자체를 구현하지 않음으로서 불변 객체를 간접적으로 구성이 가능하다.그러면 결국은 돌도 돌아 생성자를 이용하라는 것인데 역시나 지나친 생성자 오버로딩 문제가 발생하게 된다. 그래서 연구된것이 빌더 클래스이다.&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;빌더 패턴&lt;span&gt;&lt;span&gt;&amp;nbsp;단점&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h3&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;&lt;span style=&quot;color: #ef5369;&quot;&gt;1.&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;코드 복잡성 증가&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 빌더 패턴을 적용하려면 N개의 클래스에 대해 N개의 새로운 빌더 클래스를 만들어야 해서, 클래수 수가 기하급수적으로 늘어나&lt;span&gt;&amp;nbsp;&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;&lt;span style=&quot;color: #ef5369;&quot;&gt;2.&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;생성자 보다는 성능은 떨어진다.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매번 메서드를 호출하여 빌더를 거쳐 인스턴스화 하기 때문에 어쩌면 당연한 말일지도 모른다. 비록 생성 비용 자체는 크지는 않지만, 어플리케이션의 성능을 극으로 중요시되는 상황이라면 문제가 될수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;3.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;지나친 빌더 남용은 금지&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스의 필드의 개수가 4개 보다 적고, 필드의 변경 가능성이 없는 경우라면 차라리 생성자나 &lt;a href=&quot;https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EC%A0%95%EC%A0%81-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%83%9D%EC%84%B1%EC%9E%90-%EB%8C%80%EC%8B%A0-%EC%82%AC%EC%9A%A9%ED%95%98%EC%9E%90&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;정적 팩토리 메소드&lt;/a&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 style=&quot;letter-spacing: 0px;&quot;&gt;다만 API 는 시간이 지날수록 많은 매개변수를 갖는 경향이 있기 때문에 애초에 빌더 패턴으로 시작하는 편이 나을 때가 많다고 말하는 경향도 있다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&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;빌더 패턴에는 여타 디자인 패턴과는 다르게 두가지 디자인 종류가 존재한다. GOF의 디자인 패턴에서 소개하는 빌더 패턴과 이펙티브 자바(Effective Java) 책에서 소개하는 빌더 패턴 구조가 서로 다르기 때문이다.&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;이펙티브 자바의 빌더 패턴 : 생성시 지정해야 할 인자가 많을때 사용. 객체의 일관성 불변성이 목적.&lt;/li&gt;
&lt;li&gt;GoF의 빌더 패턴 : 객체의 생성 단계 순서를 결정해두고 각 단계를 다양하게 구현하고 싶을때 사용.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 두 책의 관점이 다르고 GoF의 디자인 패턴은 1994년에 발매되었고 이펙티브 자바가 2001년에 나중에 나왔기 때문이라고 생각 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;심플 빌더 패턴 (&lt;b&gt;&lt;span&gt;Effective Java&lt;/span&gt;&lt;/b&gt;)&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 개발자들이 빌더 패턴을 말할 때 정의되는 것이 이펙티브 자바에서 소개한 빌더 패턴이다. GOF 빌더 패턴과 구분하기 위해 심플 빌더 패턴(Simple Builder Pattern) 이라고도 불리운다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심플 빌더 패턴은&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;&lt;b&gt; 생성자가 많을 경우&lt;/b&gt; 또는 &lt;b&gt;변경 불가능한 불변 객체&lt;/b&gt;가 필요한 경우 코드의 가독성과 일관성, 불변성을 유지하는 것에 중점을 둔다. &lt;/span&gt;심플 빌더 패턴은 위에서 우리가 배운 빌더 패턴과 차이가 거의 없다. 다만 빌더(Builder) 클래스가 구현할 클래스의 &lt;a href=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EB%82%B4%EB%B6%80-%ED%81%B4%EB%9E%98%EC%8A%A4Inner-Class-%EC%9E%A5%EC%A0%90-%EC%A2%85%EB%A5%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;정적 내부 클래스(Static Inner Class)&lt;/a&gt;로 구현된다는 점이 다르다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;552&quot; data-origin-height=&quot;640&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0JMa5/btrVAa1GQrs/oIctj57LSTGwDCdy0pxUy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0JMa5/btrVAa1GQrs/oIctj57LSTGwDCdy0pxUy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0JMa5/btrVAa1GQrs/oIctj57LSTGwDCdy0pxUy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0JMa5%2FbtrVAa1GQrs%2FoIctj57LSTGwDCdy0pxUy0%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; alt=&quot;simple-builder-pattern&quot; loading=&quot;lazy&quot; width=&quot;552&quot; height=&quot;640&quot; data-origin-width=&quot;552&quot; data-origin-height=&quot;640&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌더 클래스가 static inner class로 구현되는 이유로는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&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 style=&quot;color: #ee2323;&quot;&gt;두 번째&lt;/span&gt;&lt;span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;대상 객체는 오로지 빌더 객체에 의해 초기화 된다. 즉, 생성자를 외부에 노출시키면 안되기 때문에 생성자를 private로 하고, 내부 빌더 클래스에서 private 생성자를 호출함으로써&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: #ee2323;&quot;&gt;세 번째&lt;/span&gt;&lt;span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;inner class를 쓰면 좋은건 알겠는데 왜 하필 static 으로 선언해주어야 하냐면, &lt;/span&gt;정적 내부 클래스는 외부 클래스의 인스턴스 없이도 생성할 수 있는데, 만일 일반 내부 클래스로 구성한다면 내부 클래스를 생성하기도 전에 외부 클래스를 인스턴스화 해야 한다. &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;빌더가 최종적으로 생성할 클래스의 인스턴스를 먼저 생성해야 한다면 모순이 생기기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;네 번째&lt;/span&gt;, 메모리 누수 문제 때문에 static으로 내부 클래스를 정의해주어야 한다. 자세한건 &lt;a href=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EC%9E%90%EB%B0%94%EC%9D%98-%EB%82%B4%EB%B6%80-%ED%81%B4%EB%9E%98%EC%8A%A4%EB%8A%94-static-%EC%9C%BC%EB%A1%9C-%EC%84%A0%EC%96%B8%ED%95%98%EC%9E%90&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;다음 포스팅&lt;/a&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;b&gt;Simple 빌더 패턴 구현하기&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;218&quot; data-origin-height=&quot;294&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dIKTQ1/btrVAL1EP4f/LAVel9VWITC8MOI0H0qtVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dIKTQ1/btrVAL1EP4f/LAVel9VWITC8MOI0H0qtVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dIKTQ1/btrVAL1EP4f/LAVel9VWITC8MOI0H0qtVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdIKTQ1%2FbtrVAL1EP4f%2FLAVel9VWITC8MOI0H0qtVk%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; alt=&quot;simple-builder-pattern&quot; loading=&quot;lazy&quot; width=&quot;218&quot; height=&quot;294&quot; data-origin-width=&quot;218&quot; data-origin-height=&quot;294&quot;/&gt;&lt;/span&gt;&lt;/figure&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;span&gt;&lt;span&gt;빌더 클래스를 Static Nested Class로 정의한다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;빌더를 통해 인스턴스화 하기 때문에 대상 객체 생성자는 private로 정의한다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;빌더 클래스의 생성자는 public으로 하며, 필수 파라미터에 대해 생성자의 파라미터로 받는다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;선택적 파라미터에 대해서는 메소드로 제공한다. 이때 메소드의 반환값은 빌더 객체 자신(this) 이어야 한다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;마지막 단계로 최종 객체를 생성하는 &lt;s&gt;build()&lt;/s&gt; 메소드를 정의하여 클라이언트에게 최종 생성된 결과물을 제공한다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;이때 생성자의 인수로 빌더 인스턴스 자기자신을 전달하고, 대상 객체 생성자에서 빌더 인스턴스의 필드를 각각 대입하여 최종 완성본이 나오게 된다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1673167539741&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Person {
    // final 키워드로 필드들을 불변 객체로 만든다.
    private final String name;
    private final String age;
    private final String gender;
    private final String job;
    private final String birthday;
    private final String address;

    // 정적 내부 빌더 클래스
    public static class Builder {

        // 필수 파라미터
        private final String name;
        private final String age;

        // 선택 파라미터
        private String gender;
        private String job;
        private String birthday;
        private String address;

        // 필수 파라미터는 빌더 생성자로 받게 한다
        public Builder(String name, String age) {
            this.name = name;
            this.age = age;
        }

        // 선택 파라미터는 각 메서드를 통해 정의한다
        public Builder gender(String gender) {
            this.gender = gender;
            return this;
        }

        public Builder job(String job) {
            this.job = job;
            return this;
        }

        public Builder birthday(String birthday) {
            this.birthday = birthday;
            return this;
        }

        public Builder address(String address) {
            this.address = address;
            return this;
        }

        // 대상 객체의 private 생성자를 호출하여 최종 인스턴스화 한다
        public Person build() {
            return new Person(this); // 빌더 객체 자신을 넘긴다.
        }
    }

    // private 생성자 - 생성자는 외부에서 호출되는것이 아닌 빌더 클래스에서만 호출되기 때문에
    private Person(Builder builder) {
        this.name = builder.name;
        this.age = builder.age;
        this.gender = builder.gender;
        this.job = builder.gender;
        this.birthday = builder.birthday;
        this.address = builder.address;
    }

    @Override
    public String toString() {
        return &quot;Person{&quot; +
                &quot;name='&quot; + name + '\'' +
                &quot;, age='&quot; + age + '\'' +
                &quot;, gender='&quot; + gender + '\'' +
                &quot;, job='&quot; + job + '\'' +
                &quot;, birthday='&quot; + birthday + '\'' +
                &quot;, address='&quot; + address + '\'' +
                '}';
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1673167550665&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {

    Person person = new Person
            .Builder(&quot;홍길동&quot;, &quot;26&quot;) // static inner class 초기화 (필수 파라미터)
            .gender(&quot;man&quot;) // 선택 파라미터
            .job(&quot;Warrior&quot;)
            .birthday(&quot;1800.10.10&quot;)
            .address(&quot;조선&quot;)
            .build();

    System.out.println(person);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;777&quot; data-origin-height=&quot;58&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bh8u4w/btrVBQnRUDs/NSFQFC7oRuJiEzaPjgAkA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bh8u4w/btrVBQnRUDs/NSFQFC7oRuJiEzaPjgAkA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bh8u4w/btrVBQnRUDs/NSFQFC7oRuJiEzaPjgAkA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbh8u4w%2FbtrVBQnRUDs%2FNSFQFC7oRuJiEzaPjgAkA1%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; alt=&quot;simple-builder-pattern&quot; loading=&quot;lazy&quot; width=&quot;777&quot; height=&quot;58&quot; data-origin-width=&quot;777&quot; data-origin-height=&quot;58&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;디렉터 빌더 패턴 (&lt;b&gt;&lt;span&gt;GOF&lt;/span&gt;&lt;/b&gt;)&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GOF에서 정의하고 있는 디자인 패턴은 복잡한&amp;nbsp;&lt;b&gt;객체의 생성 알고리즘과 조립 방법을 분리&lt;/b&gt;하여 빌드 공정을 구축하는것이 목적이다. 빌더를 받아 조립 방법을 정의한 클래스를 Director라고 부른다.&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Director 빌더 패턴 구조&lt;/b&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2hP2C/btrVea7SOZS/jiQs0JWOUx0dcU6kaNwUe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2hP2C/btrVea7SOZS/jiQs0JWOUx0dcU6kaNwUe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2hP2C/btrVea7SOZS/jiQs0JWOUx0dcU6kaNwUe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2hP2C%2FbtrVea7SOZS%2FjiQs0JWOUx0dcU6kaNwUe0%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; alt=&quot;gof-builder&quot; loading=&quot;lazy&quot; width=&quot;1400&quot; height=&quot;480&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;Builder&lt;/b&gt; : 빌더 추상 클래스&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ConcreteBuilder&lt;/b&gt; : Builder의 구현체. Product 생성을 담당한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Director&lt;/b&gt; : Builder에서 제공하는 메서드들을 사용해 정해진 순서대로 Product 생성하는 프로세스를 정의&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Product&lt;/b&gt; : Director가 Builder로 만들어낸 결과물.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이러한 구조는 클라이언트가 직접 빌더의 모든 API를 사용하는 게 아닌, Director을 통해서 간단하게 인스턴스를 얻어올 수 있고 코드를 재사용할 수 있도록 한다.&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;Director 빌더 패턴 구현하기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 예제는 일반적인 자바 데이터를 저장하고 있는 Data 객체를 Builder 인터페이스를 통해 적절한 문자열 포맷으로 변환하는 예제이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;PlainTextBuilder : Data 인스턴스의 데이터들을 평이한 텍스트 형태로 만드는 API&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;JSONBuilder : Data 인스턴스의 데이터들을&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;JSON&lt;/span&gt;&lt;/span&gt;&amp;nbsp;&lt;/span&gt;형태로 만드는 API&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;XMLBuilder :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;Data 인스턴스의 데이터들을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;XML&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;형태로 만드는 API&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이때 각 문자열 포맷으로 변환하는 프로세스를 Director 객체가 담당하게 된다. 클라이언트는 Director를 이용하여 간단한 메서드 호출만으로 복잡한 빌드 공정 과정을 알필요없이 결과물을 얻을 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;925&quot; data-origin-height=&quot;479&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pJck7/btrVzJckk7P/k5QeDt2b5ezBU633OckBK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pJck7/btrVzJckk7P/k5QeDt2b5ezBU633OckBK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pJck7/btrVzJckk7P/k5QeDt2b5ezBU633OckBK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpJck7%2FbtrVzJckk7P%2Fk5QeDt2b5ezBU633OckBK0%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; alt=&quot;gof-builder&quot; loading=&quot;lazy&quot; width=&quot;925&quot; height=&quot;479&quot; data-origin-width=&quot;925&quot; data-origin-height=&quot;479&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1673177683397&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Data {
    private String name;
    private int age;

    public Data(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1673177125439&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;abstract class Builder {
    // 상속한 자식 클래스에서 사용하도록 protected 접근제어자 지정
    protected Data data;

    public Builder(Data data) {
        this.data = data;
    }

    // Data 객체의 데이터들을 원하는 형태의 문자열 포맷을 해주는 메서드들 (머리 - 중간 - 끝 형식)
    public abstract String head();
    public abstract String body();
    public abstract String foot();

}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1673177117365&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Data 데이터들을 평범한 문자열로 변환해주는 빌더
class PlainTextBuilder extends Builder {

    public PlainTextBuilder(Data data) {
        super(data);
    }

    @Override
    public String head() {
        return &quot;&quot;;
    }

    @Override
    public String body() {
        StringBuilder sb = new StringBuilder();

        sb.append(&quot;Name: &quot;);
        sb.append(data.getName());
        sb.append(&quot;, Age: &quot;);
        sb.append(data.getAge());

        return sb.toString();
    }

    @Override
    public String foot() {
        return &quot;&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1673177740452&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Data 데이터들을 JSON 형태의 문자열로 변환해주는 빌더
class JSONBuilder extends Builder {

    public JSONBuilder(Data data) {
        super(data);
    }

    @Override
    public String head() {
        return &quot;{\n&quot;;
    }

    @Override
    public String body() {
        StringBuilder sb = new StringBuilder();

        sb.append(&quot;\t\&quot;Name\&quot; : &quot;);
        sb.append(&quot;\&quot;&quot; + data.getName() + &quot;\&quot;,\n&quot;);
        sb.append(&quot;\t\&quot;Age\&quot; : &quot;);
        sb.append(data.getAge());

        return sb.toString();
    }

    @Override
    public String foot() {
        return &quot;\n}&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1673177745146&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Data 데이터들을 XML 형태의 문자열로 변환해주는 빌더
class XMLBuilder extends Builder {
    public XMLBuilder(Data data) {
        super(data);
    }

    @Override
    public String head() {
        StringBuilder sb = new StringBuilder();

        sb.append(&quot;&amp;lt;?xml version=\&quot;1.0\&quot; encoding=\&quot;UTF-8\&quot; ?&amp;gt;\n&quot;);
        sb.append(&quot;&amp;lt;DATA&amp;gt;\n&quot;);

        return sb.toString();
    }

    @Override
    public String body() {
        StringBuilder sb = new StringBuilder();

        sb.append(&quot;\t&amp;lt;NAME&amp;gt;&quot;);
        sb.append(data.getName());
        sb.append(&quot;&amp;lt;NAME&amp;gt;&quot;);
        sb.append(&quot;\n\t&amp;lt;AGE&amp;gt;&quot;);
        sb.append(data.getAge());
        sb.append(&quot;&amp;lt;AGE&amp;gt;&quot;);

        return sb.toString();
    }

    @Override
    public String foot() {
        return &quot;\n&amp;lt;/DATA&amp;gt;&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1673177676396&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 각 문자열 포맷 빌드 과정을 템플릿화 시킨 디렉터
class Director {
    private Builder builder;

    public Director(Builder builder) {
        this.builder = builder;
    }

    // 일종의 빌드 템플릿 메서드라 보면 된다
    public String build() {
        StringBuilder sb = new StringBuilder();

		// 빌더 구현체에서 정의한 생성 알고리즘이 실행됨
        sb.append(builder.head());
        sb.append(builder.body());
        sb.append(builder.foot());

        return sb.toString();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1673177093151&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    // 1. 포맷할 자바 데이터 생성
    Data data = new Data(&quot;홍길동&quot;, 44);

    // 2. 일반 텍스트로 포맷하여 출력하기
    Builder builder1 = new PlainTextBuilder(data);
    Director director1 = new Director(builder1);
    String result1 = director1.build();
    System.out.println(result1);

    // 3. JSON 형식으로 포맷하여 출력하기
    Builder builder2 = new JSONBuilder(data);
    Director director2 = new Director(builder2);
    String result2 = director2.build();
    System.out.println(result2);

    // 4. XML 형식으로 포맷하여 출력하기
    Builder builder3 = new XMLBuilder(data);
    Director director3 = new Director(builder3);
    String result3 = director3.build();
    System.out.println(result3);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;437&quot; data-origin-height=&quot;315&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cONeCL/btrVI7hTGyn/b8R94piJF1J3kpb0ZqrdMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cONeCL/btrVI7hTGyn/b8R94piJF1J3kpb0ZqrdMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cONeCL/btrVI7hTGyn/b8R94piJF1J3kpb0ZqrdMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcONeCL%2FbtrVI7hTGyn%2Fb8R94piJF1J3kpb0ZqrdMK%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; alt=&quot;gof-builder&quot; loading=&quot;lazy&quot; width=&quot;437&quot; height=&quot;315&quot; data-origin-width=&quot;437&quot; data-origin-height=&quot;315&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 Director는 템플릿화 한 메서드를 통해 일관된 프로세스로 인스턴스를 만드는 빌드 과정을 단순화 하고, 클라이언트 쪽에선 Director가 제공하는 메서드를 호출하므로써 코드를 재사용할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Builder 는 부품을 만들고, Director는 ㅠuilder가 만든 부품을 조합해 제품을 만든다고 할 수 있다.&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;GOF의 빌더 패턴은 여러 디자인 패턴의 짬뽕&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 까지 구현해본 GOF의 디렉터 빌더 패턴은 어찌 보면 빌드 과정을 추상화 및 단순화한 &lt;a href=&quot;https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%ED%8D%BC%EC%82%AC%EB%93%9CFacade-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Facade 패턴&lt;/a&gt;과 빌드 과정 코드를 템플릿화한 &lt;a href=&quot;https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%ED%85%9C%ED%94%8C%EB%A6%BF-%EB%A9%94%EC%86%8C%EB%93%9CTemplate-Method-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Template Method 패턴&lt;/a&gt; 그리고 원하는 문자열 형식의 각 빌드 전략 알고리즘을 정의한 &lt;a href=&quot;https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EC%A0%84%EB%9E%B5Strategy-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Strage 패턴&lt;/a&gt;을 짬뽕 시킨 디자인 패턴이라고 볼수도 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;925&quot; data-origin-height=&quot;520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfdDvj/btrVE0Kog6V/i4rnMRqNmkKNj2awORC9mk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfdDvj/btrVE0Kog6V/i4rnMRqNmkKNj2awORC9mk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfdDvj/btrVE0Kog6V/i4rnMRqNmkKNj2awORC9mk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfdDvj%2FbtrVE0Kog6V%2Fi4rnMRqNmkKNj2awORC9mk%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; alt=&quot;gof-builder&quot; loading=&quot;lazy&quot; width=&quot;925&quot; height=&quot;520&quot; data-origin-width=&quot;925&quot; data-origin-height=&quot;520&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Lombok의 @Builder&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자가 좀더 편하게 빌더 패턴을 이용하기 위헤 Lombok에서는 별도의 어노테이션을 지원한다. 클래스에 &lt;b&gt;&lt;b&gt;&lt;span&gt;@Builder&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;어노테이션&lt;/b&gt;&lt;/b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;만 붙여주면 &lt;span style=&quot;color: #000000;&quot;&gt;클래스를 컴파일 할 때 자동으로 클래스 내부에 빌더 API가 만들어진다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 롬복의 @Builder는 GOF의 디렉터 빌더가 아닌 심플 빌더 패턴을 다룬다는 점은 유의하자.&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;빌더 어노테이션 구현하기&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;@Builder :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;PersonBuilder 빌더 클래스와 이를 반환하는 builder() 메서드 생성&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;@AllArgsConstructor(access = AccessLevel.PRIVATE) : &lt;/b&gt;&lt;/span&gt;&lt;span&gt;@Builder 어노테이션을 선언하면 전체 인자를 갖는 생성자를 자동으로 만드는데, 이를 private 생성자로 설정&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;@ToString&lt;/b&gt; : toString() 메서드 자동 생성&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1673173897866&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.ToString;

@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@ToString
class Person {
    private final String name;
    private final String age;
    private final String gender;
    private final String job;
    private final String birthday;
    private final String address;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1673173914066&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    Person person = Person.builder()
            .name(&quot;홍길동&quot;)
            .age(&quot;26&quot;)
            .gender(&quot;man&quot;) // 선택 파라미터
            .job(&quot;Warrior&quot;)
            .birthday(&quot;1800.10.10&quot;)
            .address(&quot;조선&quot;)
            .build();
}&lt;/code&gt;&lt;/pre&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;필수 파라미터 빌더 구현하기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Builder 어노테이션으로 빌더 패턴을 구현하면 필수 파라미터 적용을 지정해줄수가 없다. 따라서 대상 객체 안에 별도의 &lt;s&gt;builder()&lt;/s&gt; 정적 메서드를 구현함으로써, 빌더 객체를 생성하기 전에 필수 파라미터를 설정하도록 유도할수 있고, 또한 파라미터 검증 로직도 추가해줄 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1673174584904&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@ToString
class Person {
    private final String name;
    private final String age;
    private final String gender;
    private final String job;
    private final String birthday;
    private final String address;

    // 필수 파라미터 빌더 메서드 구현
    public static PersonBuilder builder(String name, String age) {
        // 빌더의 파라미터 검증
        if(name == null || age == null)
            throw new IllegalArgumentException(&quot;필수 파라미터 누락&quot;);

        // 필수 파라미터를 미리 빌드한 빌더 객체를 반환 (지연 빌더 원리)
        return new PersonBuilder().name(name).age(age);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1673174594159&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {

    Person person = Person.builder(&quot;홍길동&quot;, &quot;26&quot;) // 필수 파라미터
            .gender(&quot;man&quot;) // 선택 파라미터
            .job(&quot;Warrior&quot;)
            .birthday(&quot;1800.10.10&quot;)
            .address(&quot;조선&quot;)
            .build();

    System.out.println(person);
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;실무에서_찾아보는_전략_패턴&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;실무에서 찾아보는 Builder 패턴&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;Java&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Java&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;java.lang.StringBuilder의 append()&lt;/li&gt;
&lt;li&gt;java.lang.StringBuffer&lt;span&gt;의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;append()&lt;/li&gt;
&lt;li&gt;java.nio.ByteBuffer&lt;span&gt;의&lt;span&gt; &lt;/span&gt;&lt;/span&gt;put() - CharBuffer,&lt;span&gt;&amp;nbsp;&lt;/span&gt;ShortBuffer,&lt;span&gt;&amp;nbsp;&lt;/span&gt;IntBuffer,&lt;span&gt;&amp;nbsp;&lt;/span&gt;LongBuffer,&lt;span&gt;&amp;nbsp;&lt;/span&gt;FloatBuffer,&lt;span&gt; &lt;/span&gt;DoubleBuffer 도 마찬가지&lt;/li&gt;
&lt;li&gt;javax.swing.GroupLayout.Group의 addComponent()&lt;/li&gt;
&lt;li&gt;java.lang.Appendable의 구현체&lt;/li&gt;
&lt;li&gt;java.util.stream.Stream.Builder&lt;/li&gt;
&lt;/ul&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;StringBuilder&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바의 문자열 클래스하면 배우는 StringBuilder가 이름에서 보듯이 그 예이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌더에 해당하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;StringBuilder를 생성하고, 빌더가 제공하는 append 메서드로 파라미터를 구성하고, 최종적으로 toString을 호출해서 String 객체를 생성하는 일련의 과정이 빌더 패턴이기 때문이다.&lt;/p&gt;
&lt;pre id=&quot;code_1672739375907&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    String result = new StringBuilder()
            .append(&quot;hello &quot;)
            .append(&quot;world!&quot;)
            .toString(); // build()

    System.out.println(result);
}&lt;/code&gt;&lt;/pre&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;StreamBuilder&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stream API가 제공하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;StreamBuilder도 마찬가지이다. StreamBuilder를 사용하면 Stream에 들어갈 요소를 add 할 수 있고, 최종적으로 build를 호출해서 stream 객체를 생성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1672739390629&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    Stream.Builder&amp;lt;String&amp;gt; stringBuilder = Stream.builder();
    Stream&amp;lt;String&amp;gt; stream = stringBuilder.add(&quot;hello&quot;).add(&quot;world!&quot;).add(&quot;bye..&quot;).build();

    stream.forEach(System.out::println);
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 id=&quot;Spring_Framework&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Spring Framework&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 프레임워크에선 UriComponents 인스턴스를&lt;span&gt;&amp;nbsp;&lt;/span&gt;Uricomponentsbuilder를 통해서 만들 수 있다. uri를 하드코딩해서 만드는 것보다 안전하게 만들 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1672739412841&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    UriComponents uriComponents = UriComponentsBuilder.newInstance()
            .scheme(&quot;https&quot;)
            .host(&quot;kangworld.tistory.com&quot;)
            .build();

    System.out.println(uriComponents);
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot; data-swap-href=&quot;http://www.yes24.com/Product/Goods/65551284&quot;&gt;이펙티브&amp;nbsp;자바&amp;nbsp;Effective&amp;nbsp;Java&amp;nbsp;3/E&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot; data-swap-href=&quot;https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4?inst=cf68b212&quot;&gt;코딩으로 학습하는 GoF의 디자인 패턴 - 백기선&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://refactoring.guru/design-patterns/builder&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://www.youtube.com/watch?v=_GCiJAFU2DU&lt;/p&gt;
&lt;div id=&quot;gtx-trans&quot; style=&quot;position: absolute; left: 449px; top: 33990.4px;&quot;&gt;
&lt;div class=&quot;gtx-trans-icon&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;</description>
      <category>디자인 패턴/GOF</category>
      <category>@Builder</category>
      <category>Builder</category>
      <category>Design Pattern</category>
      <category>디자인 패턴</category>
      <category>빌더</category>
      <category>생성 패턴</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1062</guid>
      <comments>https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EB%B9%8C%EB%8D%94Builder-%ED%8C%A8%ED%84%B4-%EB%81%9D%ED%8C%90%EC%99%95-%EC%A0%95%EB%A6%AC#entry1062comment</comments>
      <pubDate>Thu, 16 Mar 2023 08:06:38 +0900</pubDate>
    </item>
    <item>
      <title>  퍼사드(Facade) 패턴 - 완벽 마스터하기</title>
      <link>https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%ED%8D%BC%EC%82%AC%EB%93%9CFacade-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img (84).webp&quot; data-origin-width=&quot;1237&quot; data-origin-height=&quot;660&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEbe1H/btrVaSk97kZ/DIiuIptB539b3QMwXElzi0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEbe1H/btrVaSk97kZ/DIiuIptB539b3QMwXElzi0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEbe1H/btrVaSk97kZ/DIiuIptB539b3QMwXElzi0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEbe1H%2FbtrVaSk97kZ%2FDIiuIptB539b3QMwXElzi0%2Fimg.webp&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; alt=&quot;Facade-Pattern&quot; loading=&quot;lazy&quot; width=&quot;1237&quot; height=&quot;660&quot; data-filename=&quot;img (84).webp&quot; data-origin-width=&quot;1237&quot; data-origin-height=&quot;660&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;100&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IsUZX/btrThOFiO8Z/sCJxhwH5S6GeO46OgoDugk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IsUZX/btrThOFiO8Z/sCJxhwH5S6GeO46OgoDugk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IsUZX/btrThOFiO8Z/sCJxhwH5S6GeO46OgoDugk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIsUZX%2FbtrThOFiO8Z%2FsCJxhwH5S6GeO46OgoDugk%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; alt=&quot;Facade-Pattern&quot; loading=&quot;lazy&quot; width=&quot;140&quot; height=&quot;100&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;100&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Facade Pattern&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;퍼사드 패턴(Facade Pattern)&lt;/b&gt;은 사용하기 복잡한 클래스 라이브러리에 대해 &lt;span&gt;사용하기 편하게&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;간편한 인터페이스(API)를 구성하기 위한 구조 패턴 이다. 예를들어 라이브러리의 각 클래스와 메서드들이 어떤 목적의 동작인지 이해하기 어려워 바로 가져다 쓰기에는 난이도가 높을때, 이에 대한 적절한 네이밍과 정리를 통해 사용자로 하여금 쉽게 라이브러리를 다룰수 있도록 인터페이스를 만드는데, &lt;span style=&quot;color: #ee2323;&quot;&gt;우리가 교제를 보고 필기노트에 재정리를 하듯이 클래스를 재정리하는 행위&lt;/span&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-origin-width=&quot;490&quot; data-origin-height=&quot;190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Aj26g/btrUVjedoTD/fhXkGRWGOYqu0LGcJDJ6s1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Aj26g/btrUVjedoTD/fhXkGRWGOYqu0LGcJDJ6s1/img.png&quot; data-alt=&quot;고객은 복잡한 절차 지식없이 고객센터(창구)에 요구만 하면 결과를 얻는다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Aj26g/btrUVjedoTD/fhXkGRWGOYqu0LGcJDJ6s1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAj26g%2FbtrUVjedoTD%2FfhXkGRWGOYqu0LGcJDJ6s1%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; alt=&quot;Facade-Pattern&quot; loading=&quot;lazy&quot; width=&quot;490&quot; height=&quot;190&quot; data-origin-width=&quot;490&quot; data-origin-height=&quot;190&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;이처럼 퍼사드(Facade) 패턴은 복잡하게 얽혀 있는 것을 정리해서 사용하기 편한 인터페이스를 고객에게 제공한다고 보면된다. 그래서 고객은 복잡한 시스템을 알 필요없이 시스템의 외부에 대해서 단순한 인터페이스를 이용하기만 하면 된다. 퍼사드를 이용하면 &lt;span style=&quot;background-color: #fcfcfc;&quot;&gt;자칫 동작의 목적과 같은 중요한 사항을 놓치는 실수를 줄일 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Facade라는 단어의 뜻은 건축물의 정면을 의미한다. 건축물의 정면은 보통 건축물의 이미지와 건축 의도를 나타내기 때문에 오래 전부터 특별한 디자인을 적용하여 의미를 부여했다. 이처럼 건축물 정면만 봐도 이 건물이 어떤 목적을 하는지 단번에 알수 있다는 특징을 차용하여 명명 지은 것이다.&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;250&quot; data-origin-height=&quot;331&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbwDMb/btrUZYt14O5/fx3gJ2A6gXnkdFH33ZMsik/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbwDMb/btrUZYt14O5/fx3gJ2A6gXnkdFH33ZMsik/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbwDMb/btrUZYt14O5/fx3gJ2A6gXnkdFH33ZMsik/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbwDMb%2FbtrUZYt14O5%2Ffx3gJ2A6gXnkdFH33ZMsik%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; alt=&quot;Facade-Pattern&quot; loading=&quot;lazy&quot; width=&quot;250&quot; height=&quot;331&quot; data-origin-width=&quot;250&quot; data-origin-height=&quot;331&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;퍼사드 패턴 구조&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;560&quot; data-origin-height=&quot;380&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u36IP/btrTgQjz9tj/vPMaQnI04MHvhfVHVpE69k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u36IP/btrTgQjz9tj/vPMaQnI04MHvhfVHVpE69k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u36IP/btrTgQjz9tj/vPMaQnI04MHvhfVHVpE69k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu36IP%2FbtrTgQjz9tj%2FvPMaQnI04MHvhfVHVpE69k%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; alt=&quot;Facade-Pattern&quot; loading=&quot;lazy&quot; width=&quot;560&quot; height=&quot;380&quot; data-origin-width=&quot;560&quot; data-origin-height=&quot;380&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;Facade&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 서브시스템 기능을 편리하게 사용할 수 있도록 하기 위해 여러 시스템과 상호 작용하는 복잡한 로직을 재정리해서 높은 레벨의 인터페이스를 구성한다. Facade 역할은 서브 시스템의 많은 역할에 대해 &amp;lsquo;단순한 창구&amp;rsquo;가 된다. 클라이언트와 서브시스템이 서로 긴밀하게 연결되지 않도록 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Additional Facade&lt;/b&gt; : 퍼사드 클래스는 반드시 한개만 존재해야 한다는 규칙같은 건 없다. 연관 되지 않은 기능이 있다면 얼마든지 퍼사드 2세로 분리한다. 이 퍼사드 2세는 다른 퍼사드에서 사용할 수도 있고 클라이언트에서 직접 접근할 수도 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SubSystem(하위 시스템)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 수십 가지 라이브러리 혹은 클래스들&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Client&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 서브 시스템에 직접 접근하는 대신 Facade를 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퍼사드 패턴은 전략 패턴이나 팩토리 패턴과 같은 여타 다른 디자인 패턴과는 다르게 &lt;u&gt;&lt;b&gt;클래스 구조가 정형화 되지 않은 패턴&lt;/b&gt;&lt;/u&gt;이다. 반드시 클래스 위치는 어떻고 어떤 형식으로 위임을 해야되고 이런것이 없다. 그냥 퍼사드 클래스를 만들어 적절히 기능 집약화만 해주면 그게 디자인 패턴이 되는 것이다. (패턴이라기 보단 논리에 가깝다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C언어나 파이썬에서 어떠한 복잡한 로직의 코드가 있으면 이걸 main 함수에서 모두 실행하는 것이 아니라, 함수(function) 분리를 통해 main 함수의 코드를 심플하게 구성해본 경험이 있을 것이다. 이를 객체 지향 프로그래밍 관점으로 치환한 것이 퍼사드 패턴이다. 즉, 퍼사드는 복잡한 것(&lt;span&gt;내부에서 실행되고 있는 많은 클래스의 관계나 사용법&lt;/span&gt;)을 단순하게 보여주는 것에 초점을 둔다.&lt;span style=&quot;color: #ee2323;&quot;&gt; 클라이언트로 하여금 복잡한 것을 의식하지 않도록 해주는 것&lt;/span&gt;이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;410&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m9PZo/btrUWbmOmhE/o32D9ySNQIpOuUBve2BeF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m9PZo/btrUWbmOmhE/o32D9ySNQIpOuUBve2BeF0/img.png&quot; data-alt=&quot;Client들은 복잡한 Subsystem을 의식하지않고 Facade라는 창구를 통해 결과를 얻는다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m9PZo/btrUWbmOmhE/o32D9ySNQIpOuUBve2BeF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm9PZo%2FbtrUWbmOmhE%2Fo32D9ySNQIpOuUBve2BeF0%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; alt=&quot;Facade-Pattern&quot; loading=&quot;lazy&quot; width=&quot;860&quot; height=&quot;410&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;410&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Client들은 복잡한 Subsystem을 의식하지않고 Facade라는 창구를 통해 결과를 얻는다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;2-재귀적인-facade-패턴의-적용&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;재귀적인 Facade 패턴의 적용&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;재귀적 퍼사드&lt;/b&gt;란 위에서 언급한&amp;nbsp;&lt;b&gt;Additional Facade &lt;/b&gt;를 말하는 것이다. 예를들어 다수의 클래스, 다수의 패키지를 포함하고 있는 큰 시스템에 요소 요소 마다 Facade 패턴을 여기 저기 적용하고 다시 그 Facade를 합친 Facade를 만드는 식으로, 퍼사드를 재귀적으로 구성하면 시스템은 보다 편리하게 된다. 이처럼 퍼사드는 한개만 있으라는 법은 없으며 필요에 의하면 얼마든지 늘려 의존할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;372&quot; data-origin-height=&quot;320&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAu71c/btrUXosFEgJ/qUCZ79h4E7DPBWFZ45fX4k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAu71c/btrUXosFEgJ/qUCZ79h4E7DPBWFZ45fX4k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAu71c/btrUXosFEgJ/qUCZ79h4E7DPBWFZ45fX4k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAu71c%2FbtrUXosFEgJ%2FqUCZ79h4E7DPBWFZ45fX4k%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; alt=&quot;Facade-Pattern&quot; loading=&quot;lazy&quot; width=&quot;372&quot; height=&quot;320&quot; data-origin-width=&quot;372&quot; data-origin-height=&quot;320&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;퍼사드 패턴&lt;span&gt;&amp;nbsp;특징&lt;/span&gt;&lt;/b&gt;&lt;/h3&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;패&lt;/b&gt;&lt;b&gt;턴 사용 시기&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시스템이 너무 복잡할때&lt;/li&gt;
&lt;li&gt;그래서 간단한 인터페이스를 통해 복잡한 시스템을 접근하도록 하고 싶을때&lt;/li&gt;
&lt;li&gt;시스템을 사용하고 있는 외부와 결합도가 너무 높을 때 의존성 낮추기 위할때&lt;/li&gt;
&lt;/ul&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;패턴 장점&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하위 시스템의 복잡성에서 코드를 분리하여, 외부에서 시스템을 사용하기 쉬워진다.&lt;/li&gt;
&lt;li&gt;하위 시스템 간의 의존 관계가 많을 경우 이를 감소시키고 의존성을 한 곳으로 모을 수 있다.&lt;/li&gt;
&lt;li&gt;복잡한 코드를 감춤으로써, 클라이언트가 시스템의 코드를 모르더라도 Facade 클래스만 이해하고 사용 가능하다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;외부에서 내부 로직을 직접 사용하기 떄문에 내부 로직의 구조를 변경한다고 하거나 파라미터나 리턴값 등을 변경할 경우 직접적으로 영향을 받아 수정이힘들거나 불가능한 경우가 종종 있다. 하지만 중간에 매개체 역할을 해주는 퍼사드 객체가 있기 때문에 실제 내부 로직이 어떻게 변경이 되더라도 상관이 없어지므로 의존성이 감소된다.&lt;/blockquote&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;패턴 단점&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;span style=&quot;color: #555555;&quot;&gt;퍼사드가 앱의 모든 클래스에 결합된 God 객체가 될 수 있다&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;퍼사드 클래스 자체가 서브시스템에 대한 의존성을 가지게 되어 의존성을 완전히는 피할 수는 없다.&lt;/li&gt;
&lt;li&gt;어찌되었건 추가적인 코드가 늘어나는 것이기 때문에 유지보수 측면에서 공수가 더 많이 들게 된다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;따라서 추상화 하고자하는 시스템이 얼마나 복잡한지 퍼사드 패턴을 통해서 얻게 되는 이점과 추가적인 유지보수 비용을 비교해보며 결정하여야 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;예제를 통해 알아보는&lt;span&gt;&lt;span&gt; Facade&lt;/span&gt;&lt;/span&gt;&lt;b&gt;&amp;nbsp;패턴&lt;/b&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;복잡한 DBMS 시스템 간편하게 재구성&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스로부터 어떤 데이터를 조회해서 출력해주는 JDBC 비스무리한 자바 패키지가 있다고 하자. 우리는 이 라이브러리를 이용하여 데이터베이스로부터 값을 얻어오고 화면에 데이터를 파싱해서 예쁘게 출력하려는 프로그램을 만들려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패키지에는 총 4개의 Cache, DBMS, Row, Message 클래스가 존재한다. 그리고 아래 그림에서 볼 수 있듯이 각 행위에 대해 각 클래스들의 역할이 정해져 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;516&quot; data-origin-height=&quot;395&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uOvc3/btrU7I4jfNb/c6TqIX6EvOyyiPQy0XJoO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uOvc3/btrU7I4jfNb/c6TqIX6EvOyyiPQy0XJoO0/img.png&quot; data-alt=&quot;각 클래스들의 역할 (점선)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uOvc3/btrU7I4jfNb/c6TqIX6EvOyyiPQy0XJoO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuOvc3%2FbtrU7I4jfNb%2Fc6TqIX6EvOyyiPQy0XJoO0%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; alt=&quot;Facade-Pattern&quot; loading=&quot;lazy&quot; width=&quot;516&quot; height=&quot;395&quot; data-origin-width=&quot;516&quot; data-origin-height=&quot;395&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;그런데 이 라이브러리를 이용하는데 있어 데이터베이스를 조회해서 데이터를 가공하기 까지 다음과 같은 규칙이 존재한다고 한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;dbms를 바로 조회하기전에&lt;/li&gt;
&lt;li&gt;과거에 조회된 데이터인지를 캐시에서 먼저 조사를 하고&lt;/li&gt;
&lt;li&gt;캐시에 데이터가 있다면 이 캐시에 데이터를 가공하고 출력&lt;/li&gt;
&lt;li&gt;캐시에 데이터가 없다면 DBMS를 통해서 조회를 하고&lt;/li&gt;
&lt;li&gt;조회된 데이터를 가공하고 출력함과 동시에 캐시에 저장한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DBMS에 조회된 데이터는 성능을 위해 반드시 캐시에 저장해야 되는 것을 잊지 말아야하고, 또한 데이터를 가공하기 위해서는 Message 클래스를 써야 한다. 이 수칙을 따르지 않으면 라이브러리가 제대로 작동하지 않아 개발자는 위의 사항을 제대로 숙지한 상태에서 프로그래밍을 하여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 라이브러리의 각 클래스에 대한 은유적인 코드는 아래와 같다고 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1672565558229&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// DBMS에 저장된 데이터를 나타내는 클래스
class Row  {
    private String name;
    private String birthday;
    private String email;

    public Row(String name, String birthday, String email) {
        this.name = name;
        this.birthday = birthday;
        this.email = email;
    }

    public String getName() {
        return name;
    }

    public String getBirthday() {
        return birthday;
    }

    public String getEmail() {
        return email;
    }
}

// 데이터베이스 역할을 하는 클래스
class DBMS {
    private HashMap&amp;lt;String, Row&amp;gt; db = new HashMap&amp;lt;&amp;gt;();

    public void put(String name, Row row) {
        db.put(name, row);
    }

    // 데이터베이스에 쿼리를 날려 결과를 받아오는 메소드
    public Row query(String name) {
        try {
            Thread.sleep(500); // DB 조회 시간을 비유하여 0.5초대기로 구현
        } catch(InterruptedException e) {}

        return db.get(name.toLowerCase());
    }
}

// DBMS에서 조회된 데이터를 임시로 담아두는 클래스 (속도 향상)
class Cache {
    private HashMap&amp;lt;String, Row&amp;gt; cache = new HashMap&amp;lt;&amp;gt;();

    public void put(Row row) {
        cache.put(row.getName(), row);
    }

    public Row get(String name) {
        return cache.get(name);
    }
}

// Row 클래스를 보기좋게 출력하는 클래스
class Message {
    private Row row;

    public Message(Row row) {
        this.row = row;
    }

    public String makeName() {
        return &quot;Name : \&quot;&quot; + row.getName() + &quot;\&quot;&quot;;
    }

    public String makeBirthday() {
        return &quot;Birthday : &quot; + row.getBirthday();
    }

    public String makeEmail() {
        return &quot;Email : &quot; + row.getEmail();
    }
}&lt;/code&gt;&lt;/pre&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;클린하지 않은 문제의 코드 ❌&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제의 코드는 당연히 라이브러리의 코드를 그대로 메인 로직에 작성하여 구현하는 것이다. 데이터를 조회하고 출력되기 까지 여러개의 객체가 사용되고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 당장은 프로그램이 정상적으로 돌아가 서비스에는 문제가 없을지는 모르겠지만, 나중에 수정과 확장함에 있어 개발자가 위의 수칙들을 까먹고 &lt;span style=&quot;color: #ee2323;&quot;&gt;실수를 하여 서비스에 버그가 생길수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1672565394959&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Client {
    public static void main(String[] args) {
        // 1. 데이터베이스 생성 &amp;amp; 등록
        DBMS dbms = new DBMS();
        dbms.put(&quot;홍길동&quot;, new Row(&quot;홍길동&quot;, &quot;1890-02-14&quot;, &quot;honggildong@naver.com&quot;));
        dbms.put(&quot;임꺽정&quot;, new Row(&quot;임꺽정&quot;, &quot;1820-11-02&quot;, &quot;imgguckjong@naver.com&quot;));
        dbms.put(&quot;주몽&quot;, new Row(&quot;주몽&quot;, &quot;710-08-27&quot;, &quot;jumong@naver.com&quot;));

        // 2. 캐시 생성
        Cache cache = new Cache();

        // 3. 트랜잭션에 앞서 먼저 캐시에 데이터가 있는지 조회
        String name = &quot;홍길동&quot;;
        Row row = cache.get(name);

        // 4. 만약 캐시에 없다면
        if (row == null){
            row = dbms.query(name); // DB에 해당 데이터를 조회해서 row에 저장하고
            if(row != null) {
                cache.put(row); // 캐시에 저장
            }
        }

        // 5. dbms.query(name)에서 조회된 값이 있으면
        if(row != null) {
            Message message = new Message(row);

            System.out.println(message.makeName());
            System.out.println(message.makeBirthday());
            System.out.println(message.makeEmail());
        }
        // 6. 조회된 값이 없으면
        else {
            System.out.println(name + &quot; 가 데이터베이스에 존재하지 않습니다.&quot;);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;282&quot; data-origin-height=&quot;72&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/B6lsi/btrU374KOBS/sG4iDKEbUjwOl9XBkrbeEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/B6lsi/btrU374KOBS/sG4iDKEbUjwOl9XBkrbeEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/B6lsi/btrU374KOBS/sG4iDKEbUjwOl9XBkrbeEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FB6lsi%2FbtrU374KOBS%2FsG4iDKEbUjwOl9XBkrbeEk%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; alt=&quot;Facade-Pattern&quot; loading=&quot;lazy&quot; width=&quot;282&quot; height=&quot;72&quot; data-origin-width=&quot;282&quot; data-origin-height=&quot;72&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;&lt;b&gt;&lt;span&gt;퍼사드 패턴을 적용한 코드 ✔️&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;따라서 이러한 사항들을 개발자가 직접 기억해서 하나하나 따져가며 코드를 작성하는 것보다, 이 사항들을 묶은 클래스를 하나 추가해서 단순화된 인터페이스를 통해 서브 클래스를 다룸으로써 &lt;span style=&quot;color: #ee2323;&quot;&gt;개발자의 실수를 줄이고자&lt;/span&gt; 하는 것이 바로 퍼사드(Facade) 패턴이다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;마치 교제에 있는 풀어져 있는 내용들을 필기노트에 요약하듯이, 퍼사드 객체에 사용 로직을 묶어 재정리 함으로써 단순화 시켜 개발을 보다 용이하게 해주는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;582&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wxVOr/btrU2LgyWG4/XQOXkqTRJk4i626kkXLNVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wxVOr/btrU2LgyWG4/XQOXkqTRJk4i626kkXLNVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wxVOr/btrU2LgyWG4/XQOXkqTRJk4i626kkXLNVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwxVOr%2FbtrU2LgyWG4%2FXQOXkqTRJk4i626kkXLNVK%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; alt=&quot;Facade-Pattern&quot; loading=&quot;lazy&quot; width=&quot;580&quot; height=&quot;582&quot; data-origin-width=&quot;580&quot; data-origin-height=&quot;582&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 퍼사드 클래스를 생성해 주고 메인 메소드의 로직을 퍼사드 클래스의 메서드에 통짜로 넣는다. (분리가 필요하면 메서드를 더 늘린다)&lt;/p&gt;
&lt;pre id=&quot;code_1672566381705&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Facade {
    private DBMS dbms = new DBMS();
    private Cache cache = new Cache();

    public void insert() {
        dbms.put(&quot;홍길동&quot;, new Row(&quot;홍길동&quot;, &quot;1890-02-14&quot;, &quot;honggildong@naver.com&quot;));
        dbms.put(&quot;임꺽정&quot;, new Row(&quot;임꺽정&quot;, &quot;1820-11-02&quot;, &quot;imgguckjong@naver.com&quot;));
        dbms.put(&quot;주몽&quot;, new Row(&quot;주몽&quot;, &quot;710-08-27&quot;, &quot;jumong@naver.com&quot;));
    }

    public void run(String name) {
        Row row = cache.get(name);

        // 1. 만약 캐시에 없다면
        if (row == null){
            row = dbms.query(name); // DB에 해당 데이터를 조회해서 row에 저장하고
            if(row != null) {
                cache.put(row); // 캐시에 저장
            }
        }

        // 2. dbms.query(name)에서 조회된 값이 있으면
        if(row != null) {
            Message message = new Message(row);

            System.out.println(message.makeName());
            System.out.println(message.makeBirthday());
            System.out.println(message.makeEmail());
        }
        // 3. 조회된 값이 없으면
        else {
            System.out.println(name + &quot; 가 데이터베이스에 존재하지 않습니다.&quot;);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1672566379129&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Client {
    public static void main(String[] args) {

        // 1. 퍼사드 객체 생성
        Facade facade = new Facade();

        // 2. db 값 insert
        facade.insert();

        // 3. 퍼사드로 데이터베이스 &amp;amp; 캐싱 &amp;amp; 메세징 로직을 한번에 조회
        String name = &quot;홍길동&quot;;
        facade.run(name);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퍼사드 패턴을 적용하니 메인 로직이 엄청 심플해졌다. 이처럼 퍼사드의 핵심은 인터페이스(API)를 적게 구성하는 것이다. 라이브러리에서 제공하는 클래스나 메소드가 많이 보이면, 프로그래머는 무엇을 사용하면 좋을지 망설이게 되고 호출하는 순서도 doc을 살펴보며 주의해야만 한다. 주의해야 한다는 말은 다르게 말하면 틀리기 쉽다는 것이다. 따라서 퍼사드의 메서드를 가능한 적게 구성하는 것이 좋다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;오해하지 말아야 할 점은 퍼사드는 하위 시스템 클래스들을 캡슐화 하는 것이 아니다. 그냥 서브 시스템들을 사용할 간단한 인터페이스를 제공할 뿐 이다. 사용자가 서브 시스템 내부의 클래스를 직접 사용하는 것을 제한할 수는 없다. 그래서 오히려 추상화에 가깝다고 볼 수 있다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;대부분의 경우 퍼사드 객체는 하나만 있어도 충분하므로, 퍼사드 클래스를 &lt;a href=&quot;https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EC%8B%B1%EA%B8%80%ED%86%A4Singleton-%ED%8C%A8%ED%84%B4-%EA%BC%BC%EA%BC%BC%ED%95%98%EA%B2%8C-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;싱글톤&lt;/a&gt;으로 구성해주면 좋다.&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot; data-swap-href=&quot;https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4?inst=cf68b212&quot;&gt;코딩으로 학습하는 GoF의 디자인 패턴 - 백기선&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://youtu.be/mQlOqyFE3oI&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://refactoring.guru/design-patterns/facade&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;http://best-practice-software-engineering.ifs.tuwien.ac.at/patterns/facade.html&lt;/p&gt;</description>
      <category>디자인 패턴/GOF</category>
      <category>Design Pattern</category>
      <category>건물 패턴</category>
      <category>디자인 패턴</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1055</guid>
      <comments>https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%ED%8D%BC%EC%82%AC%EB%93%9CFacade-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90#entry1055comment</comments>
      <pubDate>Tue, 14 Mar 2023 08:55:10 +0900</pubDate>
    </item>
    <item>
      <title>  반복자(Iterator) 패턴 - 완벽 마스터하기</title>
      <link>https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EB%B0%98%EB%B3%B5%EC%9E%90Iterator-%ED%8C%A8%ED%84%B4-%EC%99%84%EB%B2%BD-%EB%A7%88%EC%8A%A4%ED%84%B0%ED%95%98%EA%B8%B0</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img (84).webp&quot; data-origin-width=&quot;1237&quot; data-origin-height=&quot;660&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpYESK/btrX2V7vcri/9YHttFSNKaxjkLq7ICHhz0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpYESK/btrX2V7vcri/9YHttFSNKaxjkLq7ICHhz0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpYESK/btrX2V7vcri/9YHttFSNKaxjkLq7ICHhz0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpYESK%2FbtrX2V7vcri%2F9YHttFSNKaxjkLq7ICHhz0%2Fimg.webp&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; alt=&quot;Iterator-pattern&quot; loading=&quot;lazy&quot; width=&quot;1237&quot; height=&quot;660&quot; data-filename=&quot;img (84).webp&quot; data-origin-width=&quot;1237&quot; data-origin-height=&quot;660&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;100&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SlKSn/btrXK8MJ1RF/zaRwZYB5j33gobr4GB3pWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SlKSn/btrXK8MJ1RF/zaRwZYB5j33gobr4GB3pWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SlKSn/btrXK8MJ1RF/zaRwZYB5j33gobr4GB3pWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSlKSn%2FbtrXK8MJ1RF%2FzaRwZYB5j33gobr4GB3pWK%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; alt=&quot;Iterator-pattern&quot; loading=&quot;lazy&quot; width=&quot;140&quot; height=&quot;100&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;100&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Iterator Pattern&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;반복자(Iterator) 패턴은 일련의 데이터 집합에 대하여 &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;background-color: #ffffff;&quot;&gt;데이터 집합이란 객체들을 그룹으로 묶어 자료의 구조를 취하는 컬렉션을 말한다. 대표적인 컬렉션으로 한번쯤은 들어본 리스트나 트리, 그래프, 테이블 ..등이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;490&quot; data-origin-height=&quot;100&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zUWzb/btrXUSDzBGt/v2hlfSM9B6ItuEfOscPeck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zUWzb/btrXUSDzBGt/v2hlfSM9B6ItuEfOscPeck/img.png&quot; data-alt=&quot;요소를 다양한 방법으로 저장하는 컬렉션&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zUWzb/btrXUSDzBGt/v2hlfSM9B6ItuEfOscPeck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzUWzb%2FbtrXUSDzBGt%2Fv2hlfSM9B6ItuEfOscPeck%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; alt=&quot;Iterator-pattern&quot; loading=&quot;lazy&quot; width=&quot;490&quot; height=&quot;100&quot; data-origin-width=&quot;490&quot; data-origin-height=&quot;100&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;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;보통 배열이나 리스트 같은 경우 순서가 연속적인 데이터 집합이기 때문에 간단한 for문을 통해 순회할수 있다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;그러나 해시, 트리와 같은 컬렉션은 데이터 저장 순서가 정해지지 않고 적재되기 때문에, 각 요소들을 어떤 기준으로 접근해야 할지 애매해진다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;예를들어 아래와 같이 트리 구조가 있다면 어떤 상황에선 깊이(세로)를 우선으로 순회 해야 할 수도 있고, 너비(가로)를 우선으로 순회할수도 있기 때문이다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SLac4/btrXOWN9VXe/WE4NJ4faGvEv34iabgGX40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SLac4/btrXOWN9VXe/WE4NJ4faGvEv34iabgGX40/img.png&quot; data-alt=&quot;어떤 기준으로 순회할지 컬렉션마다 다를수 있다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SLac4/btrXOWN9VXe/WE4NJ4faGvEv34iabgGX40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSLac4%2FbtrXOWN9VXe%2FWE4NJ4faGvEv34iabgGX40%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; alt=&quot;Iterator-pattern&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;160&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;160&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;이처럼 복잡하게 얽혀있는 자료 컬렉션들을 순회하는 알고리즘 전략을 정의하는 것을 이터레이터 패턴이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컬렉션 객체 안에 들어있는 모든 원소들에 대한 접근 방식이 공통화 되어 있다면 어떤 종류의 컬렉션에서도 이터레이터만 뽑아내면 여러 전략으로 순회가 가능해 보다 다형(多形) 적인 코드를 설계할 수 있게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;470&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cge0GC/btrX19rsato/G82z0okQmMsIxPypTAElMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cge0GC/btrX19rsato/G82z0okQmMsIxPypTAElMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cge0GC/btrX19rsato/G82z0okQmMsIxPypTAElMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcge0GC%2FbtrX19rsato%2FG82z0okQmMsIxPypTAElMK%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; alt=&quot;Iterator-pattern&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;470&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;470&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;자바의 컬렉션 프레임워크(JCF)에서 각종 컬렉션을 무리없이 순회할수 있는 것도 내부에 미리 이터레이터 패턴이 적용되어 있기 때문이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;이밖에도 이터레이터 패턴은 별도의 이터레이터 객체를 반환 받아 이를 이용해 순회하기 때문에, 집합체의 &lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;내부 구조를 노출하지 않고&lt;/span&gt; 순회 할 수 있다는 장점도 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;이터레이터 패턴 구조&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;727&quot; data-origin-height=&quot;417&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dbIYgp/btrXUWLY0t5/bV5lnB9KyfLa3Q6dsyNGIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dbIYgp/btrXUWLY0t5/bV5lnB9KyfLa3Q6dsyNGIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dbIYgp/btrXUWLY0t5/bV5lnB9KyfLa3Q6dsyNGIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdbIYgp%2FbtrXUWLY0t5%2FbV5lnB9KyfLa3Q6dsyNGIK%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; alt=&quot;Iterator-pattern&quot; loading=&quot;lazy&quot; width=&quot;727&quot; height=&quot;417&quot; data-origin-width=&quot;727&quot; data-origin-height=&quot;417&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li id=&quot;1-iterator반복자&quot;&gt;&lt;b&gt;Aggregate (인터페이스)&lt;/b&gt; : &lt;span&gt;ConcreateIterator&lt;span&gt; 객체를 반환하는 인터페이스를 제공한다.&lt;/span&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;iterator() : ConcreateIterator 객체를 만드는 팩토리 메서드&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li id=&quot;4-concreateaggregate구체적인-집합체&quot;&gt;&lt;b&gt;ConcreateAggregate (클래스)&lt;/b&gt; : &lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;여러 요소들이 이루어져 있는 데이터 집합체&lt;/span&gt;&lt;/li&gt;
&lt;li id=&quot;1-iterator반복자&quot;&gt;&lt;b&gt;Iterator (인터페이스)&lt;/b&gt; : &lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;집합체 내의 요소들을 순서대로 검색하기 위한 인터페이스를 제공한다.&lt;/span&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;hasNext() : 순회할 다음 요소가 있는지 확인 (true / false)&lt;/li&gt;
&lt;li&gt;next() : 요소를 반환하고 다음 요소를 반환할 준비를 하기 위해 커서를 이동시킴&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li id=&quot;2-concreateiterator구체적인-반복자&quot;&gt;&lt;b&gt;ConcreateIterator (클래스)&lt;/b&gt; : 반복자 객체
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;ConcreateAggregate가 구현한 메서드로부터 생성되며, &lt;span&gt;ConcreateAggregate&lt;span&gt; 의 컬렉션을 참조하여 순회한다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;어떤 전략으로 순회할지에 대한 로직을 구체화 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;이터레이터 패턴&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;흐름&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;클래스 구성&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1675314260291&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 집합체 객체 (컬렉션)
interface Aggregate {
    Iterator iterator();
}

class ConcreteAggregate implements Aggregate {
    Object[] arr; // 데이터 집합 (컬렉션)
    int index = 0;

    public ConcreteAggregate(int size) {
        this.arr = new Object[size];
    }

    public void add(Object o) {
        if(index &amp;lt; arr.length) {
            arr[index] = o;
            index++;
        }
    }

    // 내부 컬렉션을 인자로 넣어 이터레이터 구현체를 클라이언트에 반환
    @Override
    public Iterator iterator() {
        return new ConcreteIterator(arr);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1675314270149&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 반복체 객체
interface Iterator {
    boolean hasNext();
    Object next();
}

class ConcreteIterator implements Iterator {
    Object[] arr;
    private int nextIndex = 0; // 커서 (for문의 i 변수 역할)

    // 생성자로 순회할 컬렉션을 받아 필드에 참조 시킴
    public ConcreteIterator(Object[] arr) {
        this.arr = arr;
    }

    // 순회할 다음 요소가 있는지 true / false
    @Override
    public boolean hasNext() {
        return nextIndex &amp;lt; arr.length;
    }

    // 다음 요소를 반환하고 커서를 증가시켜 다음 요소를 바라보도록 한다.
    @Override
    public Object next() {
        return arr[nextIndex++];
    }
}&lt;/code&gt;&lt;/pre&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;클래스 흐름&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1675314282225&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    // 1. 집합체 생성
    ConcreteAggregate aggregate = new ConcreteAggregate(5);
    aggregate.add(1);
    aggregate.add(2);
    aggregate.add(3);
    aggregate.add(4);
    aggregate.add(5);

    // 2. 집합체에서 이터레이터 객체 반환
    Iterator iter = aggregate.iterator();

    // 3. 이터레이터 내부 커서를 통해 순회
    while(iter.hasNext()) {
        System.out.printf(&quot;%s &amp;rarr; &quot;, iter.next());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;266&quot; data-origin-height=&quot;29&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ecp533/btrXOQAaXJl/Mp3zrAxuKzddsHRlCxIk7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ecp533/btrXOQAaXJl/Mp3zrAxuKzddsHRlCxIk7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ecp533/btrXOQAaXJl/Mp3zrAxuKzddsHRlCxIk7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fecp533%2FbtrXOQAaXJl%2FMp3zrAxuKzddsHRlCxIk7K%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; alt=&quot;Iterator-pattern&quot; loading=&quot;lazy&quot; width=&quot;266&quot; height=&quot;29&quot; data-origin-width=&quot;266&quot; data-origin-height=&quot;29&quot;/&gt;&lt;/span&gt;&lt;/figure&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;이터레이터 패턴&lt;span&gt;&amp;nbsp;특징&lt;/span&gt;&lt;/b&gt;&lt;/h3&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;패&lt;/b&gt;&lt;b&gt;턴 사용 시기&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컬렉션에 상관없이 객체 접근 순회 방식을 통일하고자 할 때&lt;/li&gt;
&lt;li&gt;컬렉션을 순회하는 다양한 방법을 지원하고 싶을 때&lt;/li&gt;
&lt;li&gt;컬렉션의 복잡한 내부 구조를 클라이언트로 부터 숨기고 싶은 경우 (편의 + 보안)&lt;/li&gt;
&lt;li&gt;데이터 저장 컬렉션 종류가 변경&amp;nbsp;가능성이&amp;nbsp;있을&amp;nbsp;때
&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;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;패턴 장점&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일관된 이터레이터 인터페이스를 사용해 여러 형태의 컬렉션에 대해 동일한 순회 방법을 제공한다.&lt;/li&gt;
&lt;li&gt;컬렉션의 내부 구조 및 순회 방식을 알지 않아도 된다.&lt;/li&gt;
&lt;li&gt;집합체의 구현과 접근하는 처리 부분을 반복자 객체로 분리해 &lt;a href=&quot;https://inpa.tistory.com/entry/OOP-%F0%9F%92%A0-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EA%B2%B0%ED%95%A9%EB%8F%84-%EC%9D%91%EC%A7%91%EB%8F%84-%EC%9D%98%EB%AF%B8%EC%99%80-%EB%8B%A8%EA%B3%84-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-%EC%89%BD%EA%B2%8C-%EC%A0%95%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;결합도&lt;/a&gt;를 줄 일 수 있다.&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Client에서 iterator로 접근하기 때문에 ConcreteAggregate 내에 수정 사항이 생겨도 iterator에 문제가 없다면 문제가 발생하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;순회 알고리즘을 별도의 반복자 객체에 추출하여 각 클래스의 책임을 분리하여 &lt;a href=&quot;https://inpa.tistory.com/entry/OOP-%F0%9F%92%A0-%EC%95%84%EC%A3%BC-%EC%89%BD%EA%B2%8C-%EC%9D%B4%ED%95%B4%ED%95%98%EB%8A%94-SRP-%EB%8B%A8%EC%9D%BC-%EC%B1%85%EC%9E%84-%EC%9B%90%EC%B9%99&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;단일 책임 원칙(SRP)&lt;/a&gt;를 준수한다.&lt;/li&gt;
&lt;li&gt;데이터 저장 컬렉션 종류가 변경되어도 클라이언트 구현 코드는 손상되지 않아 수정에는 닫혀 있어 &lt;a href=&quot;https://inpa.tistory.com/entry/OOP-%F0%9F%92%A0-%EC%95%84%EC%A3%BC-%EC%89%BD%EA%B2%8C-%EC%9D%B4%ED%95%B4%ED%95%98%EB%8A%94-OCP-%EA%B0%9C%EB%B0%A9-%ED%8F%90%EC%87%84-%EC%9B%90%EC%B9%99&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;개방 폐쇄 원칙(OCP)&lt;/a&gt;를 준수한다.&lt;/li&gt;
&lt;/ul&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;패턴 단점&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;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;li&gt;구현 방법에 따라 캡슐화를 위배할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;예제를 통해 알아보는&lt;span&gt;&lt;span&gt; Iterator&lt;/span&gt;&lt;/span&gt;&lt;b&gt;&amp;nbsp;패턴&lt;/b&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;이터레이터로 순회 전략을 나누기&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;클린하지 않은 문제의 코드 ❌&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 게시글(Post)과 게시판(Borad)를 표현한 인스턴스이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게시글에는 게시글 제목 title과 게시글 발행 날짜 필드가 있다. (코드 간략화를 위해 게시글 내용은 생략하였다)&lt;/p&gt;
&lt;pre id=&quot;code_1675407924587&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 게시글
class Post {
    String title; // 게시글 제목
    LocalDate date; // 게시글 발행일

    public Post(String title, LocalDate date) {
        this.title = title;
        this.date = date;
    }
}

// 게시판
class Board {
    // 게시글을 리스트 집합 객체로 저장 관리
    List&amp;lt;Post&amp;gt; posts = new ArrayList&amp;lt;&amp;gt;();

    public void addPost(String title, LocalDate date) {
        this.posts.add(new Post(title, date));
    }

    public List&amp;lt;Post&amp;gt; getPosts() {
        return posts;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1675407918759&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    // 1. 게시판 생성
    Board board = new Board();

    // 2. 게시판에 게시글을 포스팅
    board.addPost(&quot;디자인 패턴 강의 리뷰&quot;, LocalDate.of(2020, 8, 30));
    board.addPost(&quot;게임 하실분&quot;, LocalDate.of(2020, 2, 6));
    board.addPost(&quot;이거 어떻게 하나요?&quot;, LocalDate.of(2020, 6, 1));
    board.addPost(&quot;이거 어떻게 하나요?&quot;, LocalDate.of(2021, 12, 22));

    List&amp;lt;Post&amp;gt; posts = board.getPosts();
    
    // 3. 게시글 발행 순서대로 조회하기
    for (int i = 0; i &amp;lt; posts.size(); i++) {
        Post post = posts.get(i);
        System.out.println(post.title + &quot; / &quot; + post.date);
    }

    // 4. 게시글 날짜별로 조회하기
    Collections.sort(posts, (p1, p2) -&amp;gt; p1.date.compareTo(p2.date)); // 집합체를 날짜별로 정렬
    for (int i = 0; i &amp;lt; posts.size(); i++) {
        Post post = posts.get(i);
        System.out.println(post.title + &quot; / &quot; + post.date);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;382&quot; data-origin-height=&quot;207&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsXyca/btrXXzFj0Wz/I6KZv82k2AWG9OZF57eYaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsXyca/btrXXzFj0Wz/I6KZv82k2AWG9OZF57eYaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsXyca/btrXXzFj0Wz/I6KZv82k2AWG9OZF57eYaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsXyca%2FbtrXXzFj0Wz%2FI6KZv82k2AWG9OZF57eYaK%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; alt=&quot;Iterator-pattern&quot; loading=&quot;lazy&quot; width=&quot;382&quot; height=&quot;207&quot; data-origin-width=&quot;382&quot; data-origin-height=&quot;207&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 for문을 돌려 집합체의 요소들을 순회하였다. 그러나 이러한 구성 방식은 Board에 들어간 Post를 순회할 때, Board가 어떠한 구조로 이루어져 있는지를 &lt;span style=&quot;color: #ee2323;&quot;&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;b&gt;&lt;span&gt;이터레이터 패턴을 적용한 코드 ✔️&lt;/span&gt;&lt;/b&gt;&lt;/h4&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;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;ListPostIterator : 저장&amp;nbsp;순서&amp;nbsp;이터레이터&lt;/li&gt;
&lt;li&gt;DatePostIterator : 날짜&amp;nbsp;순서&amp;nbsp;이터레이터&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 ListPostIterator 와 DatePostIterator 객체를 반환하는 팩토리 메서드를 Board 클래스에 추가만 해주면 완성 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;925&quot; data-origin-height=&quot;436&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caAP5J/btrX0LSMyFZ/AFo5jqInYrWRPCGBwL8qb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caAP5J/btrX0LSMyFZ/AFo5jqInYrWRPCGBwL8qb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caAP5J/btrX0LSMyFZ/AFo5jqInYrWRPCGBwL8qb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaAP5J%2FbtrX0LSMyFZ%2FAFo5jqInYrWRPCGBwL8qb1%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; alt=&quot;Iterator-pattern&quot; loading=&quot;lazy&quot; width=&quot;925&quot; height=&quot;436&quot; data-origin-width=&quot;925&quot; data-origin-height=&quot;436&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1675410848748&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 저장 순서 이터레이터
class ListPostIterator implements Iterator&amp;lt;Post&amp;gt; {
    private Iterator&amp;lt;Post&amp;gt; itr;

    public ListPostIterator(List&amp;lt;Post&amp;gt; posts) {
        this.itr = posts.iterator();
    }

    @Override
    public boolean hasNext() {
        return this.itr.hasNext(); // 자바 내부 이터레이터에 위임해 버림
    }

    @Override
    public Post next() {
        return this.itr.next(); // 자바 내부 이터레이터에 위임해 버림
    }
}

// 날짜 순서 이터레이터
class DatePostIterator implements Iterator&amp;lt;Post&amp;gt; {
    private Iterator&amp;lt;Post&amp;gt; itr;

    public DatePostIterator(List&amp;lt;Post&amp;gt; posts) {
        // 최신 글 목록이 먼저 오도록 정렬
        Collections.sort(posts, (p1, p2) -&amp;gt; p1.date.compareTo(p2.date));
        this.itr = posts.iterator();
    }

    @Override
    public boolean hasNext() {
        return this.itr.hasNext(); // 자바 내부 이터레이터에 위임해 버림
    }

    @Override
    public Post next() {
        return this.itr.next(); // 자바 내부 이터레이터에 위임해 버림
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1675410851380&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 게시판
class Board {
    // 게시글을 리스트 집합 객체로 저장 관리
    List&amp;lt;Post&amp;gt; posts = new ArrayList&amp;lt;&amp;gt;();

    public void addPost(String title, LocalDate date) {
        this.posts.add(new Post(title, date));
    }

    public List&amp;lt;Post&amp;gt; getPosts() {
        return posts;
    }

    // ListPostIterator 이터레이터 객체 반환
    public Iterator&amp;lt;Post&amp;gt; getListPostIterator() {
        return new ListPostIterator(posts);
    }

    // DatePostIterator 이터레이터 객체 반환
    public Iterator&amp;lt;Post&amp;gt; getDatePostIterator() {
        return new DatePostIterator(posts);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1675410862208&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    // 1. 게시판 생성
    Board board = new Board();

    // 2. 게시판에 게시글을 포스팅
    board.addPost(&quot;디자인 패턴 강의 리뷰&quot;, LocalDate.of(2020, 8, 30));
    board.addPost(&quot;게임 하실분&quot;, LocalDate.of(2020, 2, 6));
    board.addPost(&quot;이거 어떻게 하나요?&quot;, LocalDate.of(2020, 6, 1));
    board.addPost(&quot;이거 어떻게 하나요?&quot;, LocalDate.of(2021, 12, 22));

    // 게시글 발행 순서대로 조회하기
    print(board.getListPostIterator());

    // 게시글 날짜별로 조회하기
    print(board.getDatePostIterator());
}

public static void print(Iterator&amp;lt;Post&amp;gt; iterator) {
    Iterator&amp;lt;Post&amp;gt; itr = iterator;
    while(itr.hasNext()) {
        Post post = itr.next();
        System.out.println(post.title + &quot; / &quot; + post.date);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;382&quot; data-origin-height=&quot;207&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsXyca/btrXXzFj0Wz/I6KZv82k2AWG9OZF57eYaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsXyca/btrXXzFj0Wz/I6KZv82k2AWG9OZF57eYaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsXyca/btrXXzFj0Wz/I6KZv82k2AWG9OZF57eYaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsXyca%2FbtrXXzFj0Wz%2FI6KZv82k2AWG9OZF57eYaK%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; alt=&quot;Iterator-pattern&quot; loading=&quot;lazy&quot; width=&quot;382&quot; height=&quot;207&quot; data-origin-width=&quot;382&quot; data-origin-height=&quot;207&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 클라이언트는 게시글을 순회할 때 Board 내부가 어떤 집합체로 구현(Array, List, Tree, Queue ..등) 되어 있는지 알 수 없게 감추고 전혀 신경 쓸 필요가 없게 되었다. 그리고 순회 전략을 각 객체로 나눔으로써 때에 따라 적절한 이터레이터 객체만 받으면 똑같은 이터레이터 순회 코드로 다양한 순회 전략을 구사할 수 있게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;1-구현에-상관-없이-iterator를-사용할-수-있다&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;집합체 구현에 상관 없이 순회를 표현&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span&gt;✔️&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Iterator를 사용하는 또다른 이유는 이터레이터를 사용함으로써 집합체 구현과 분리할 수 있기 때문이다.&lt;/p&gt;
&lt;pre id=&quot;code_1675411456925&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Iterator&amp;lt;Post&amp;gt; itr = iterator;
while(itr.hasNext()) {
    Post post = itr.next();
    System.out.println(post.title + &quot; / &quot; + post.date);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이터레이터 객체를 반환하면 컬렉션을 순회할때 &lt;s&gt;hasNext()&lt;/s&gt; 와 &lt;s&gt;next()&lt;/s&gt; 라는 Iterator의 메소드만을 이용하기 때문에, 집합체인 Board의 내부 구성을 감출수 있게 된다. 즉, 위의 while문은 Board의 구현에 의존하지 않는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 말은 만일 추후에 Board의 집합체를 수정하더라도 Board 클래스가&amp;nbsp;올바른 Iterator만을 반환해 준다면 클라이언트의 코드(위의 while 루프)는 변경하지 않아도 되게 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;실무에서_찾아보는_전략_패턴&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;실무에서 찾아보는 Iterator 패턴&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;Java&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Java&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;java.util.Enumeration 과 java.util.Iterator&lt;/li&gt;
&lt;li&gt;Java StAX (Streaming API for XML)의 Iterator 기반 API
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;XmlEventReader, XmlEventWriter&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;java.util.Enumeration&lt;/b&gt;&lt;/h4&gt;
&lt;ul id=&quot;322283d4-f516-4440-8b3d-ac06e9d96ef3&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Iterator가 만들어지기 전 java 1.0부터 있었던 API&lt;/li&gt;
&lt;li&gt;과거의 클래스로서, 현재는 안쓰고 Iterator로 기능이 대체 되었다.&lt;/li&gt;
&lt;li&gt;java 9 에서는 Enumeration Iterator로 변환해주는 코드가 추가되었다. &lt;s&gt;asIterator()&lt;/s&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;table id=&quot;tablepress-1613&quot; style=&quot;border-collapse: collapse; width: 100%; height: 137px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt;Iterator&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt;Enumeration&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;height: 40px;&quot;&gt;요소를 반복하는 데 사용되는 일반 커서이며 모든 컬렉션 클래스에 적용할 수 있다.&lt;/td&gt;
&lt;td style=&quot;height: 40px;&quot;&gt;Vector와 같은 레거시 클래스에만 적용할 수 있으므로 일반 커서가 아니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;컬렉션 클래스에 대한 읽기 권한만 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;height: 40px;&quot;&gt;반복 메서드 : hasNext(), next()&lt;/td&gt;
&lt;td style=&quot;height: 40px;&quot;&gt;&lt;span&gt;반복 메서드 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;hasMoreElements(), nextElement()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;height: 40px;&quot;&gt;반복자를 사용하여 컬렉션의 요소를 제거할 수 있다.&lt;/td&gt;
&lt;td style=&quot;height: 40px;&quot;&gt;읽기 권한만 있기 때문에 열거를 사용하여 컬렉션의 요소를 제거할 수 없다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1675401812984&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    Vector&amp;lt;Integer&amp;gt; rollno = new Vector&amp;lt;&amp;gt;();
    rollno.add(1);
    rollno.add(2);
    rollno.add(3);
    rollno.add(4);
    rollno.add(5);
    
    Enumeration&amp;lt;Integer&amp;gt; classNine = rollno.elements();

    // hasMoreElements(), nextElement() 로 순회
    while (classNine.hasMoreElements()) {
        System.out.println(classNine.nextElement());
    }
}&lt;/code&gt;&lt;/pre&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;java.util.Iterator&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;hasNext() : 순회할 요소가 있는지 확인&lt;/li&gt;
&lt;li&gt;next() : 현재 커서의 요소를 출력하고 다음으로 커서를 이동&lt;/li&gt;
&lt;li&gt;remove()&lt;span&gt;&amp;nbsp;&lt;/span&gt;: Iterator에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;next()로 받았던 해당 엘리먼트를 삭제.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 이터레이터에서 다 지원하는 것은 아니다. UnsupportedOperationException()을 던지는 경우가 많다.&lt;/li&gt;
&lt;li&gt;보통 이 기능은 동시에 다발적으로 같은 명령을 수행해도 안전한 컬렉션에서 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;forEachRemaining() : 함수형 인터페이스를 통해 순회 코드를 심플화 해준다&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1675402833938&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    Set&amp;lt;Integer&amp;gt; aggregate = new TreeSet&amp;lt;&amp;gt;(List.of(1,2,3,4,5));

    // hasNext(), next(), remove()
    Iterator&amp;lt;Integer&amp;gt; itr = aggregate.iterator();
    while(itr.hasNext()) {
        System.out.printf(&quot;%d 삭제&quot;, itr.next());
        itr.remove();
    }

    System.out.println(aggregate); // []
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1675320334020&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    Set&amp;lt;Integer&amp;gt; aggregate = new TreeSet&amp;lt;&amp;gt;(List.of(1,2,3,4,5));

    // 이터레이터의 while 문 코드를 for문으로 축약할 수 도 있다.
    for (Iterator&amp;lt;Integer&amp;gt; i = aggregate.iterator(); i.hasNext();) {
        System.out.println(i.next());
    }

    // forEachRemaining()
    aggregate.iterator().forEachRemaining(System.out::println);
}&lt;/code&gt;&lt;/pre&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;Java StAX (Streaming API for XML)&lt;/b&gt;&lt;/h4&gt;
&lt;ul id=&quot;aef819fd-2d84-4335-8220-396f28f2c687&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;XML 파일 포맷을 읽거나 만들때 사용하는 자바 라이브러리&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;콘솔 기반의 API, 이터레이터 기반의 API를 제공한다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;이터레이터 기반의 API&lt;/b&gt; : xml의 Element마다 이벤트가 지나가면서 캡쳐하며 그 영역을 표현하는 XMLEvent라는 인스턴스가 새로 만들고 이를 이용하는 방식이다. (XmlEventReader,&amp;nbsp;XmlEventWriter)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;콘솔 기반의 API&lt;/b&gt; : 하나의 인스턴스가 Element를 지나가면서 직접 안의 내용들을 변경하는 방식이다. 그래서 메모리는 이터레이터 기반보다 효율적이지만 재사용, 변경 측면에서는 좋지 않아 이터레이터 기반의 API를 사용하는 것이 권장된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;비슷한 SAX(Simple API for XML)도 있지만, SAX는 XML을 읽기만 가능하다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이터레이터 기반의 API 코드 예제는 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1675320385860&quot; class=&quot;html xml&quot; data-ke-language=&quot;xml&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;books&amp;gt;
    &amp;lt;book title=&quot;오징어 게임&quot;/&amp;gt;
    &amp;lt;book title=&quot;숨바꼭질&quot;/&amp;gt;
    &amp;lt;book title=&quot;우리집에 왜 왔니&quot;/&amp;gt;
&amp;lt;/books&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1675320359352&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static void main(String[] args) throws FileNotFoundException, XMLStreamException {

    // 1. XMLEventReader 객체를 만드는 팩토리 객체를 얻는다.
    XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();

    // 2. XMLEventReader 객체를 만든다.
    XMLEventReader reader = xmlInputFactory.createXMLEventReader(new FileInputStream(&quot;book.xml&quot;));

    // 3. 이터레이터 순회한다.
    while(reader.hasNext()) {
        // &amp;lt;books&amp;gt; 엘리먼트를 캡쳐하여 그 영역을 표현하는 XMLEvent 인스턴스를 생성
        XMLEvent nextEvent = reader.nextEvent();

        // 엘리먼트에 자식 엘리먼트가 있을 경우
        if(nextEvent.isStartElement()) {
            StartElement startElement = nextEvent.asStartElement(); // &amp;lt;book&amp;gt;
            QName name = startElement.getName();
            if(name.getLocalPart().equals(&quot;book&quot;)) {
                // 엘리먼트의 속성을 얻는다. &amp;lt;book title=&quot;&quot;/&amp;gt;
                Attribute title = startElement.getAttributeByName(new QName(&quot;title&quot;));
                System.out.println(title.getValue());
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2021-12-26 오후 10.31.43.png&quot; data-origin-width=&quot;220&quot; data-origin-height=&quot;136&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ctr4bv/btrXSw13FrD/MiZ2z0fiNbFkbaHVy1Ndyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctr4bv/btrXSw13FrD/MiZ2z0fiNbFkbaHVy1Ndyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ctr4bv/btrXSw13FrD/MiZ2z0fiNbFkbaHVy1Ndyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fctr4bv%2FbtrXSw13FrD%2FMiZ2z0fiNbFkbaHVy1Ndyk%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; alt=&quot;Java StAX&quot; loading=&quot;lazy&quot; width=&quot;171&quot; height=&quot;136&quot; data-filename=&quot;스크린샷 2021-12-26 오후 10.31.43.png&quot; data-origin-width=&quot;220&quot; data-origin-height=&quot;136&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;style3&quot; /&gt;
&lt;h3 id=&quot;Spring_Framework&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Spring Framework&lt;/b&gt;&lt;/h3&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;CompositeIterator&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;기존의 Interator에 add 기능만 하나 추가한 것&lt;/li&gt;
&lt;li&gt;add() : 여러 Iterator들을 조합(Composite)해서 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1675310758945&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class IteratorInSpring {
 
    public static void main(String[] args) {
        CompositeIterator iterator;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot; data-swap-href=&quot;https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4?inst=cf68b212&quot;&gt;코딩으로 학습하는 GoF의 디자인 패턴 - 백기선&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://refactoring.guru/design-patterns/iterator&lt;/p&gt;
&lt;div id=&quot;gtx-trans&quot; style=&quot;position: absolute; left: 726px; top: 12979.6px;&quot;&gt;
&lt;div class=&quot;gtx-trans-icon&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;</description>
      <category>디자인 패턴/GOF</category>
      <category>Design Pattern</category>
      <category>iterater</category>
      <category>iterator</category>
      <category>디자인 패턴</category>
      <category>이터레이터</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1075</guid>
      <comments>https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EB%B0%98%EB%B3%B5%EC%9E%90Iterator-%ED%8C%A8%ED%84%B4-%EC%99%84%EB%B2%BD-%EB%A7%88%EC%8A%A4%ED%84%B0%ED%95%98%EA%B8%B0#entry1075comment</comments>
      <pubDate>Mon, 13 Mar 2023 09:43:50 +0900</pubDate>
    </item>
    <item>
      <title>  :before :after 에 text-decoration 효과 없애기</title>
      <link>https://inpa.tistory.com/entry/%F0%9F%9A%80-before-after-%EC%97%90-text-decoration-%ED%9A%A8%EA%B3%BC-%EC%97%86%EC%95%A0%EA%B8%B0</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;630&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d7bqXu/btr1YKINdIT/zI1jKWTek3iV7sryzhak41/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d7bqXu/btr1YKINdIT/zI1jKWTek3iV7sryzhak41/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d7bqXu/btr1YKINdIT/zI1jKWTek3iV7sryzhak41/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd7bqXu%2Fbtr1YKINdIT%2FzI1jKWTek3iV7sryzhak41%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; alt=&quot;text-decoration-before-after&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;630&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;630&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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;s&gt;&amp;lt;a&amp;gt;&lt;/s&gt; 링크 태그를 꾸미기 위해 &lt;s&gt;:before&lt;/s&gt; 나 &lt;s&gt;:after&lt;/s&gt; 에 아이콘을 넣는 경우가 종종 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 anchor 태그의 밑줄 효과인 &lt;s&gt;text-decoration&lt;/s&gt; 속성을 텍스트에만 적용하고 &lt;s&gt;content&lt;/s&gt; 속성의 아이콘 표시 부분은 적용하고 싶지 않을때가 있을텐데, 아무리 의사 선택자에 &lt;s&gt;text-decoration&lt;/s&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;속성을 &lt;s&gt;none&lt;/s&gt;으로 해도 밑줄이 지워지지 않은 현상 때문에 애로사항이 생겼을 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;해결방법은 의사 선택자의 &lt;s&gt;display&lt;/s&gt; 속성을 &lt;s&gt;inline-block&lt;/s&gt; 으로 설정해주면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1678067211105&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;a href=&quot;#&quot;&amp;gt;My link&amp;lt;/a&amp;gt;

&amp;lt;a class=&quot;no-underline&quot; href=&quot;#&quot;&amp;gt;My link&amp;lt;/a&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1678067239080&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;a {
    text-decoration: underline;
}

a:before {
    content: &quot;★&quot;;
}

a.no-underline:before {
    /* inline-block 설정하고 밑줄효과를 none으로 해주면 된다 */
    display: inline-block;
    text-decoration: none;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;eYLRoqd&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/eYLRoqd&quot;&gt; :before에 text-decoration 없애기&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Snippet/CSS 스니펫</category>
      <category>content 밑줄 효과</category>
      <category>text-decoration</category>
      <category>밑줄 효과 없애기</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1095</guid>
      <comments>https://inpa.tistory.com/entry/%F0%9F%9A%80-before-after-%EC%97%90-text-decoration-%ED%9A%A8%EA%B3%BC-%EC%97%86%EC%95%A0%EA%B8%B0#entry1095comment</comments>
      <pubDate>Sun, 12 Mar 2023 11:47:43 +0900</pubDate>
    </item>
    <item>
      <title>  텍스트 말줄임표(...) 간단 적용하기</title>
      <link>https://inpa.tistory.com/entry/%F0%9F%9A%80-%ED%85%8D%EC%8A%A4%ED%8A%B8-%EB%B0%91%EC%A4%84%EC%9E%84%ED%91%9C</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;630&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q2RBH/btr1vrJ0M6d/pd9ul3L7hrYZ7K8pTUz180/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q2RBH/btr1vrJ0M6d/pd9ul3L7hrYZ7K8pTUz180/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q2RBH/btr1vrJ0M6d/pd9ul3L7hrYZ7K8pTUz180/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq2RBH%2Fbtr1vrJ0M6d%2Fpd9ul3L7hrYZ7K8pTUz180%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; alt=&quot;css-ellipsis&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;630&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;630&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;글자&amp;nbsp;&lt;span style=&quot;color: #252525;&quot;&gt;ellipsis 적용하기&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일정한 영역 내에 문단을 넘치지 않으면서 생략 기호를 쓰고 싶어 말줄임표(...)를 적용하고 싶다면, CSS 속성의 &lt;s&gt;text-overflow: ellipsis&lt;/s&gt; 를 적용하면 된다. 다만 이 속성을 적용하기 위해선 &lt;b&gt;몇가지 사전 작업이 꼭 필요&lt;/b&gt;하다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;적용할 해당 요소는 display 속성이 block / inline-block 이어야 한다 (그래야 width를 적용할수가 있다)&lt;/li&gt;
&lt;li&gt;적용할 해당 요소는 width를 지정해주어야 한다. (그래야 넘치는 기준을 정하기 때문이다)&lt;/li&gt;
&lt;li&gt;적용할 해당 요소는 overflow가 hidden 이어야 한다. (그래야 넘치는 부분이 가려지기 때문이다)&lt;/li&gt;
&lt;li&gt;마지막으로 text-overflow를 ellipsis 로 처리하면 된다&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;싱글 라인 말줄임표 적용&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1677807177593&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.ellipsis {
    display: inline-block; /* 특정 너비를 가지도록 상황에 따라 block or inline-block 으로 변경 */
    width: 200px; /* inline-block처럼 콘텐츠에 따라 유동적인 너비를 가진다면 직접 너비를 설정 */
    overflow: hidden; /* 넘치는 부분 가리기 */
    text-overflow: ellipsis; /* ... 처리하기 */
    
    white-space: nowrap; /* 줄바꿈 안하기 (한 줄 밑줄임표 적용) */
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;멀티 라인 말줄임표 적용&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만일 한줄 줄임이 아닌 여러 줄을 적용하고 어느 라인 기전에 말줄임을 적용하고 싶다면 특별한 속성을 사용하여야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1677807824081&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.ellipsis-multi {
    display: block;
    width:22%;
    overflow:hidden;
    text-overflow:ellipsis;

	/* 여러 줄 밑줄임표 적용 */
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3; /* 3번째 줄까지만 글자를 표현하고 그 이후는 줄임 처리 */
}&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 400px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;400&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;yLxbape&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/yLxbape&quot;&gt; text ellipsis&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Snippet/CSS 스니펫</category>
      <category>말줄임표</category>
      <category>점 3개</category>
      <category>점 세개</category>
      <category>줄임표</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1094</guid>
      <comments>https://inpa.tistory.com/entry/%F0%9F%9A%80-%ED%85%8D%EC%8A%A4%ED%8A%B8-%EB%B0%91%EC%A4%84%EC%9E%84%ED%91%9C#entry1094comment</comments>
      <pubDate>Sat, 11 Mar 2023 16:30:51 +0900</pubDate>
    </item>
    <item>
      <title> ️ 웹문서에 최적화된 독특한 HTML 태그 10가지</title>
      <link>https://inpa.tistory.com/entry/HTML-%F0%9F%8F%B7%EF%B8%8F-%EC%B5%9C%EC%A0%81%ED%99%94-%ED%83%9C%EA%B7%B8</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;extra-transformed.webp&quot; data-origin-width=&quot;1680&quot; data-origin-height=&quot;960&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E2WEz/btr1dlPVeOv/mC4Lg9is6KBPqGvm1xTRc1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E2WEz/btr1dlPVeOv/mC4Lg9is6KBPqGvm1xTRc1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E2WEz/btr1dlPVeOv/mC4Lg9is6KBPqGvm1xTRc1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE2WEz%2Fbtr1dlPVeOv%2FmC4Lg9is6KBPqGvm1xTRc1%2Fimg.webp&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; alt=&quot;html-tags&quot; loading=&quot;lazy&quot; width=&quot;1680&quot; height=&quot;960&quot; data-filename=&quot;extra-transformed.webp&quot; data-origin-width=&quot;1680&quot; data-origin-height=&quot;960&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;figure &amp;amp; figcaption 태그&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 태그들은 이미지, 동영상 과 같은 멀티미디어 콘텐츠의 캡션 및 기타 메타데이터를 추가하는 방법을 제공한다. 물론 이미지, 동영상을 표현하는데 적합한 &lt;s&gt;&amp;lt;img&amp;gt;&lt;/s&gt; , &lt;s&gt;&amp;lt;video&lt;/s&gt;&amp;gt; 태그들이 존재하지만, 이들을 &lt;s&gt;&amp;lt;figure&amp;gt;&lt;/s&gt; 태그로 한번 더 감싸주면, &lt;span style=&quot;color: #ee2323;&quot;&gt;웹 페이지의 접근성과 검색 엔진 최적화가 개선&lt;/span&gt;된다고 한다. 멀티미디어 콘텐츠에 대한 컨텍스트 및 추가 정보를 제공하여 사용자가 이해하기 쉽고 검색 엔진이 웹 페이지를 색인화할 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;&amp;lt;figcaption&amp;gt;&lt;/s&gt; 태그는 소스에 대한 제목 또는 설명과 같은 추가 정보를 제공하는 캡션(caption)으로써, &amp;lt;figure&amp;gt; 태그 내부 하단에 배치된다.&lt;/p&gt;
&lt;pre id=&quot;code_1678082082544&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- 이미지에 대한 컨테이너 --&amp;gt;
&amp;lt;figure&amp;gt;
  &amp;lt;img src=https://bit.ly/2bgFrvL width=&quot;600px&quot;/&amp;gt;
  &amp;lt;figcaption&amp;gt;World Map&amp;lt;/figcaption&amp;gt; &amp;lt;!-- 이미지의 캡션을 정의 --&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;당장 이곳 티스토리 블로그도 개발자도구를 살펴보면 이미지를 포스팅에 표시할때 &lt;s&gt;&amp;lt;figure&amp;gt;&lt;/s&gt; 엘리먼트를 사용한다는 걸 알 수 있다.&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;346&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWdTXx/btr16jj6u3l/bA5XjBtgXAxhKnR47WFNM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWdTXx/btr16jj6u3l/bA5XjBtgXAxhKnR47WFNM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWdTXx/btr16jj6u3l/bA5XjBtgXAxhKnR47WFNM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWdTXx%2Fbtr16jj6u3l%2FbA5XjBtgXAxhKnR47WFNM1%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; alt=&quot;figure&quot; loading=&quot;lazy&quot; width=&quot;550&quot; height=&quot;346&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;346&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;mark 태그&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;s&gt;&amp;lt;mark&amp;gt;&lt;/s&gt; 태그를 사용하면 검색 엔진이 텍스트의 문맥과 관련성을 더 쉽게 이해할 수 있게 해주기 때문에 웹 페이지의 사용성과 접근성을 개선하는 데 도움이 될 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1678085705754&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;p&amp;gt;The quick &amp;lt;mark&amp;gt;brown fox&amp;lt;/mark&amp;gt; jumps over the lazy dog.&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 168.18182373046875px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;168.18182373046875&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;rNZzBKY&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/rNZzBKY&quot;&gt; Untitled&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;abbr 태그&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서에 '약어' 기능을 추가할 수 있다. 사용자가 약어 위로 마우스를 가져가면 전체 형식이 표시되는 일종의 툴팁이라 보면 된다. &lt;span style=&quot;background-color: #fefefe; color: #3a3a3a;&quot;&gt;스크린 리더, 맞춤법 검사기 및 검색 엔진에 이점이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;비슷한 약어 태그인 &lt;s&gt;&amp;lt;acronym&amp;gt;&lt;/s&gt; 도 있지만 HTML5에서 지원되지 않으며 현재 사용되지 않는다&lt;/blockquote&gt;
&lt;pre id=&quot;code_1678082785723&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;p&amp;gt;
    마우스 커서를 
    &amp;lt;abbr title=&quot;안녕하세요 인파입니다 방문해주셔서 감사해요&quot;&amp;gt;고유명사&amp;lt;/abbr&amp;gt; 
    에 올리면 설명 캡션이 생겨요!
&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;GRXEVaY&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/GRXEVaY&quot;&gt; abbr 태그&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;del &amp;amp; ins 태그&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흔히 글자 가운데 취소선을 긋기 위해 &lt;s&gt;&amp;lt;s&amp;gt;&lt;/s&gt; 태그를 이용하지만 &lt;s&gt;&amp;lt;del&amp;gt;&lt;/s&gt; 태그는 최신 시맨틱 태그로서 이것을 사용하기를 권장된다. 또한 &lt;s&gt;&amp;lt;ins&amp;gt;&lt;/s&gt; 태그와 결합하면 단순 취소선 표시 뿐만 아니라 수정된 테스트를 옆에 강조 표시로 나타내게 할 수도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1678085979459&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;p&amp;gt;Pluto &amp;lt;del&amp;gt;is&amp;lt;/del&amp;gt; &amp;lt;ins&amp;gt;isn&amp;rsquo;t&amp;lt;/ins&amp;gt; a planet.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;John &amp;lt;del&amp;gt;likes&amp;lt;/del&amp;gt; &amp;lt;ins&amp;gt;LOVES&amp;lt;/ins&amp;gt; his new iPod.&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 200.90908813476562px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;200.90908813476562&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;dyqzbga&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/dyqzbga&quot;&gt; del &amp;amp; ins 태그&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;ruby 태그&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #292929;&quot;&gt;단어의 위나 옆에 배치되어 발음을 나타내는 작은 문자 또는 기호인 루비 주석을 만드는 데 사용되는 태그이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1678085359955&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;p&amp;gt;
  The word 
  &amp;lt;ruby&amp;gt;안녕하세요&amp;lt;rt&amp;gt;annyeonghaseyo&amp;lt;/rt&amp;gt;&amp;lt;/ruby&amp;gt; &amp;lt;!-- 글자 위에 작은 주석이 표시--&amp;gt;
  means &quot;hello&quot; in Korean
&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 219.09091186523438px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;219.09091186523438&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;oNPevEa&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/oNPevEa&quot;&gt; ruby 태그&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;sup &amp;amp; sub 태그&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서에 위첨자(x&amp;sup2;) 및 아래첨자 (x₀) 를 문서에 추가해준다. 일일히 특수기호를 뒤지지 말고 간단한 태그로 구현이 가능한 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1678082442985&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;p&amp;gt;
  length&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt; +
  width&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt; =
  hypotenuseA&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;
&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;poOwMGo&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/poOwMGo&quot;&gt; sub &amp;amp; sup&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;wbr 태그&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 태그는 단어를 여러 줄로 나누는 위치를 나타내는 데 사용된다. 무슨 말이냐 하면, 가로폭을 줄일 경우&amp;nbsp;&lt;b&gt;텍스트가 영역을 오버플로하여 레이아웃 문제를&lt;/b&gt; 일으켜 단어 중간에서 끊어져 읽기 어렵게 만드는데 이를 방지하는데 사용된다고 보면 된다. 요소 영역에 &lt;span style=&quot;background-color: #fefefe; color: #3a3a3a;&quot;&gt;가로 스크롤 자체를 사용하지 않으려할때 유용히 쓰일 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제 에서 텍스트 박스 크기를 줄이면서 링크 문자열의 단어 개행이 어떤식으로 되는지 확인해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1678084799288&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;p&amp;gt;
  아래 링크 문자열은 강제로 개행할 경우 텍스트가 단어 중간에서 끊어져 읽기 어려운 문제를 일으키기 때문에 이를 방지 하기 위해 적절하게 단어대로 개행하도록 한다
  &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;
  https://&amp;lt;wbr&amp;gt;www&amp;lt;wbr&amp;gt;.creativebloq&amp;lt;wbr&amp;gt;.com/
&amp;lt;/p&amp;gt;
&amp;lt;!--
    강제로 개행해도 링크의 문자열이 단어 단위대로 끊어지도록 설정
    https://
    www
    .creativebloq
    .com/
--&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 620.0000305175781px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;620.0000305175781&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;OJogKKd&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/OJogKKd&quot;&gt; object 태그&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;time 태그&lt;/b&gt;&lt;/h2&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;표시하는 데 사용된다. &lt;s&gt;&amp;lt;time&amp;gt;&lt;/s&gt; 태그를 명시해주면, 기계가 읽을 수 있는 형식 으로 시간과 날짜를 지정할 수 있으므로 검색 엔진이 더 쉽게 이해할 수 있는 장점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용법은 아래 코드와 같이 날짜를 표현하는 단어에 &lt;s&gt;&amp;lt;time&amp;gt;&lt;/s&gt; 태그로 감싸주고 &lt;s&gt;datetime&lt;/s&gt; 속성을 주어서 &lt;span style=&quot;background-color: #ffffff; color: #292929;&quot;&gt;기계가 읽을 수 있는 형식을 지정한다. 이때 형식은 날짜 및 시간을 나타내는 표준화된 형식인 ISO 8601 표준이어야 한다. 그리고 사람이 읽을 수 있는 7월 7일 이라는 텍스트를 표현함으로써, &lt;span style=&quot;color: #ee2323;&quot;&gt;사람과 기계가 동시에 읽을 수 있는 time 표현&lt;/span&gt;을 문서에 그릴 수 있는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1678085148020&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;p&amp;gt;Posted: &amp;lt;time datetime=&quot;2020-07-07&quot;&amp;gt;7월 7일&amp;lt;/time&amp;gt;&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;address 태그&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왠만한 회사 웹사이트 하단(footer) 부분을 보면 이메일, 전화, 주소와 같은 비즈니스 정보를 기재해 둔다. 이때 무지성으로 &lt;s&gt;&amp;lt;div&amp;gt;&lt;/s&gt; 태그로 표현하지말고 좀더 시맨틱(sematic)한 주소 전용 태그인 &lt;s&gt;&amp;lt;address&amp;gt;&lt;/s&gt;로 감싸 표현해주면 html에서 의미론적으로 주소를 마크업 해줘 좋다.&lt;/p&gt;
&lt;pre id=&quot;code_1678152719527&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;address&amp;gt;    
   Written by &amp;lt;a href=&quot;mailto:info@rijsat.com&quot;&amp;gt;Rijwan Ansari&amp;lt;/a&amp;gt;.&amp;lt;br&amp;gt;     
   Visit us at:&amp;lt;br&amp;gt;    
   RijSat.com&amp;lt;br&amp;gt;    
   Buddhanagar, Kathmandu&amp;lt;br&amp;gt;    
   Nepal    
&amp;lt;/address&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;b38f&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;번역 방지 속성&lt;/b&gt;&lt;/h2&gt;
&lt;p id=&quot;db4c&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;태그의 속성을 &lt;s&gt;translate=&quot;no&quot;&lt;/s&gt;&amp;nbsp;로&amp;nbsp;설정하면 번역 변환이 자동으로 방지된다. 회사나 브랜드 로고나 이름과 같은 단어를 번역하지 않으려는 경우에 유용하다.&lt;/p&gt;
&lt;pre id=&quot;code_1678082654087&quot; class=&quot;xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;p translate=&quot;no&quot;&amp;gt;Brand name&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://javascript.plainenglish.io/9-unique-html-tags-you-need-to-know-5ceecfcf0095&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://blog.duda.co/8-cool-html-tags-you-didn-t-know-existed&lt;/p&gt;
&lt;div id=&quot;gtx-trans&quot; style=&quot;position: absolute; left: -24px; top: 6163.08px;&quot;&gt;
&lt;div class=&quot;gtx-trans-icon&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;</description>
      <category>Mark Up/HTML</category>
      <category>유니크 태그</category>
      <category>태그 종류</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1097</guid>
      <comments>https://inpa.tistory.com/entry/HTML-%F0%9F%8F%B7%EF%B8%8F-%EC%B5%9C%EC%A0%81%ED%99%94-%ED%83%9C%EA%B7%B8#entry1097comment</comments>
      <pubDate>Fri, 10 Mar 2023 07:46:07 +0900</pubDate>
    </item>
    <item>
      <title>  CSS 선택자를 모던하게 :where() / :is() / :has() / :not()</title>
      <link>https://inpa.tistory.com/entry/%F0%9F%8C%9F-css-where-is-has-not</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;t2aayy49dmh5u1qk1vhh.webp&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;420&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Y7aKy/btr1dlhxUjQ/03qpZ5FriITuGEaPLwJ9q1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Y7aKy/btr1dlhxUjQ/03qpZ5FriITuGEaPLwJ9q1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Y7aKy/btr1dlhxUjQ/03qpZ5FriITuGEaPLwJ9q1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FY7aKy%2Fbtr1dlhxUjQ%2F03qpZ5FriITuGEaPLwJ9q1%2Fimg.webp&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; alt=&quot;css-where-is-has-not&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;420&quot; data-filename=&quot;t2aayy49dmh5u1qk1vhh.webp&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;420&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;:where()&lt;/span&gt; 가상 선택자&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;:where&lt;/s&gt; 의사 클래스 선택자는 css 코딩할때 &lt;span style=&quot;background-color: #ffffff; color: #292929;&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: #292929;&quot;&gt;예를들어 다음과 같이 여러 엘리먼트 안에 있는 anchor 태그에 hover 효과를 주기위해선 각 선택자들을 콤마를 이어 나열하여 표현하여야 했었다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1678094542042&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;nav&amp;gt;
  &amp;lt;ul&amp;gt;
    &amp;lt;a href=&quot;#&quot;&amp;gt;element 1&amp;lt;/a&amp;gt;
  &amp;lt;/ul&amp;gt;
&amp;lt;/nav&amp;gt;

&amp;lt;footer&amp;gt;
  &amp;lt;ol&amp;gt;
    &amp;lt;a href=&quot;#&quot;&amp;gt;element 2&amp;lt;/a&amp;gt;
  &amp;lt;/ol&amp;gt;
&amp;lt;/footer&amp;gt;

&amp;lt;aside&amp;gt;
  &amp;lt;p&amp;gt;
    &amp;lt;a href=&quot;#&quot;&amp;gt;element 3&amp;lt;/a&amp;gt;
  &amp;lt;/p&amp;gt;
&amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1678094556115&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;nav &amp;gt; ul a:hover,
footer &amp;gt; ol a:hover,
aside &amp;gt; p a:hover {
  color: purple;
  text-decoration: underline wavy deeppink;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 코드를 보면 &lt;s&gt;a:hover&lt;/s&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;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;:where() 사용법&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 &lt;s&gt;:where()&lt;/s&gt; 선택자를 통해 보다 모던스럽게 css 코드를 구성할 수 있다. &lt;b&gt;불필요한 중복을 없애&lt;/b&gt; 코드 자체도 짧아지게 되고, 각 선택자를 &lt;b&gt;개별적으로 분석&lt;/b&gt;하기 때문에 만일 잘못된 선택자가 있어도 이해가 안되면 그대로 버려버려 확장성도 좋다.&lt;/p&gt;
&lt;pre id=&quot;code_1678094804983&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;:where(nav &amp;gt; ul, footer &amp;gt; ol, aside &amp;gt; p) a:hover {
  color: purple;
  text-decoration: underline wavy deeppink;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 316.3636474609375px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;316.3636474609375&quot; data-default-tab=&quot;css,result&quot; data-slug-hash=&quot;WNgEvxN&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/WNgEvxN&quot;&gt; :where 선택자&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&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;이중 :where() 처리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀 더 이 선택자의 강력한 효과를 확연히 느낄수 있게 좀 길다란 예시 코드를 가져와봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 선택자들을 보면 section, article, aside, nav 선택자와 그의 자손인 h1 ~ h6 선택자가 반복되는 걸 볼 수 있다. 이를 이중 &lt;s&gt;:where()&lt;/s&gt; 을 통해 획기적으로 줄일 수가 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1678095881401&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;section h1, section h2, section h3, section h4, section h5, section h6, 
article h1, article h2, article h3, article h4, article h5, article h6, 
aside h1, aside h2, aside h3, aside h4, aside h5, aside h6, 
nav h1, nav h2, nav h3, nav h4, nav h5, nav h6 {
  color: #BADA55;
}

/* &amp;darr; */

:where(section, article, aside, nav) :where(h1, h2, h3, h4, h5, h6) {
  color: #BADA55;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;:where() 한계점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 &lt;s&gt;::before&lt;/s&gt; 나 &lt;s&gt;::after&lt;/s&gt; 와 같은 의사 요소는 DOM에 있는 요소가 아니므로 선택이 불가능해 묶을수 없다는 한계가 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1678095071734&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* 동작하지 않음 */
a:where(::before, ::after) {
  ...
}

/* 어쩔수 없이 쉼표로 이어야 한다  */
a::before,
a::after {
  ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;:is()&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;가상 선택자&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 선택자는 위에서 배운 &lt;s&gt;:where&lt;/s&gt; 선택자와 &lt;u&gt;&lt;b&gt;동일하게 동작&lt;/b&gt;&lt;/u&gt;된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 유일한 차이점은 &lt;a href=&quot;https://inpa.tistory.com/entry/CSS-%F0%9F%93%9A-%EC%83%81%EC%86%8D-%EA%B0%9C%EB%85%90-%EC%A0%81%EC%9A%A9-%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;명시도&lt;/a&gt; 에 있다. id 선택자는 다른 선택자보다 우선순위가 가장 높고, css 코드가 아래일 수록 우선순위가 적용된다고 들어본적이 있을 것인데 이것을 말하는 것이다. 즉, &lt;s&gt;:where()&lt;/s&gt;는 명시도가 0이지만 &lt;s&gt;:is()&lt;/s&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;:where()와 :is() 차이점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222;&quot;&gt;아래 예제 코드를 보면 &lt;s&gt;&amp;lt;header&amp;gt;&lt;/s&gt; , &lt;s&gt;&amp;lt;main&amp;gt;&lt;/s&gt; , &lt;s&gt;&amp;lt;footer&amp;gt;&lt;/s&gt; 안의 글자의 색을 스타일링 하려 한다. 이때 각각 &lt;s&gt;:where()&lt;/s&gt; 와 &lt;s&gt;:is()&lt;/s&gt; 로 글자색을 지정하고 css 마지막에 전체 선택자로 글자색을 녹색으로 덮어 씌웠다. 결과는 어떻게 될까?&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1678095430847&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;article&amp;gt;
  &amp;lt;h2&amp;gt;:where()&amp;lt;/h2&amp;gt;
  &amp;lt;header class=&quot;where&quot;&amp;gt;
    &amp;lt;p&amp;gt;Section&amp;lt;/p&amp;gt;
  &amp;lt;/header&amp;gt;
  &amp;lt;main class=&quot;where&quot;&amp;gt;
    &amp;lt;p&amp;gt;Aside&amp;lt;/p&amp;gt;
  &amp;lt;main/&amp;gt;
  &amp;lt;footer class=&quot;where&quot;&amp;gt;
    &amp;lt;p&amp;gt;Footer&amp;lt;/p&amp;gt;
  &amp;lt;/footer&amp;gt;
&amp;lt;/article&amp;gt;

&amp;lt;article&amp;gt;
  &amp;lt;h2&amp;gt;:is()&amp;lt;/h2&amp;gt;
  &amp;lt;header class=&quot;is&quot;&amp;gt;
    &amp;lt;p&amp;gt;Section&amp;lt;/p&amp;gt;
  &amp;lt;/header&amp;gt;
  &amp;lt;main class=&quot;is&quot;&amp;gt;
    &amp;lt;p&amp;gt;Aside&amp;lt;/p&amp;gt;
  &amp;lt;/main&amp;gt;
  &amp;lt;footer class=&quot;is&quot;&amp;gt;
    &amp;lt;p&amp;gt;Footer&amp;lt;/p&amp;gt;
  &amp;lt;/footer&amp;gt;
&amp;lt;/article&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1678095722241&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* 맨 아래 정의된 선택자에 의해 무시된다 */
:where(header.where, main.where, footer.where) p {
  color: red;
}

/* 가장 명시도가 높아 밑에 뭐가 있던 무조건 적용된다 */
:is(header.is, main.is, footer.is) p {
  color: orange;
}

/* 전체 선택자 처리 */
header p,
main p,
footer p {
  color: green;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 440px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;440&quot; data-default-tab=&quot;css,result&quot; data-slug-hash=&quot;dyqzoJg&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/dyqzoJg&quot;&gt; :is() 선택자&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;:where()&lt;/s&gt; 은 명시도가 0이기 때문에 css 마지막에 선언된 전체 선택자에 의해 효과가 덮어씌워졌다. 그러나 &lt;s&gt;:is()&lt;/s&gt;는 명시도가 높아 가장 높은 우선순위를 가지기 때문에 뒤에 뭐가 오든 무조건 해당 스타일로 적용되게 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;:not()&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;가상 선택자&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 선택자는 이름 그대로&amp;nbsp;&lt;s&gt;:not()&lt;/s&gt; 괄호 안의 매개변수 요소를 제외한 모든 요소의 스타일을 지정하는 데 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;:not()&lt;/s&gt; 을 이용하면 솔루션에 대해서 좀더 심플하게 해결책을 구성할 수 가 있는데, 예를 들어 다음 리스트 박스 마다 간격을 주기 위해 &lt;s&gt;margin-bottom&lt;/s&gt;을 설정하였다고 하자.&lt;/p&gt;
&lt;pre id=&quot;code_1678098368137&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div class=&quot;card&quot;&amp;gt;
  &amp;lt;ul&amp;gt;
    &amp;lt;li&amp;gt;Item 1&amp;lt;/li&amp;gt;
    &amp;lt;li&amp;gt;Item 2&amp;lt;/li&amp;gt;
    &amp;lt;li&amp;gt;Item 3&amp;lt;/li&amp;gt;
    &amp;lt;li&amp;gt;Item 4&amp;lt;/li&amp;gt;
    &amp;lt;li&amp;gt;Item 5&amp;lt;/li&amp;gt;
  &amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1678098155206&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ul li {
  margin-bottom: 20px;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 428.18182373046875px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;428.18182373046875&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;yLxoYbK&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/yLxoYbK&quot;&gt; :not 선택자&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 모든 리스트 하단에 margin이 들어가 박스가 대칭이 이루어지지 않는 문제점이 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;395&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boIWfk/btr2vpbTLXU/76PKIjIGw1d0r7B1OI1ZP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boIWfk/btr2vpbTLXU/76PKIjIGw1d0r7B1OI1ZP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boIWfk/btr2vpbTLXU/76PKIjIGw1d0r7B1OI1ZP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboIWfk%2Fbtr2vpbTLXU%2F76PKIjIGw1d0r7B1OI1ZP1%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;730&quot; height=&quot;395&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;395&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;margin-top도 설정하는 법이나 강제로 li 요소 마지막은 margin을 설정하지 않도록 하는 방법도 있겠지만 좀더 심플하고 코드 한줄로 해결할 수가 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1678098505370&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* li 요소 마지막은 적용하지 않는다 */
ul li:not(:last-of-type) {
  margin-bottom: 20px;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 410px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;410&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;oNPejLr&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/oNPejLr&quot;&gt; Untitled&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&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;s&gt;!=&lt;/s&gt; not equals 연산자 기능을 구현할 수도 있다.&lt;/p&gt;
&lt;figure id=&quot;og_1678097395353&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;[CSS]   속성 선택자 not equal 구현하기 [attr!=value]&quot; data-og-description=&quot;Attribute Selector not equal 구현 CSS 속성(Attribute) 선택자 문법에는 equal 연산자와 함께 다양한 기호 문법을 지원한다. [attr=value] : attr 속성이 정확히 value인 요소를 선택 [attr~=value] : attr 속성이 정확히 v&quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/CSS-%F0%9F%9A%80-%EC%84%A0%ED%83%9D%EC%9E%90-not-equal-%EA%B5%AC%ED%98%84&quot; data-og-url=&quot;https://inpa.tistory.com/entry/CSS-%F0%9F%9A%80-%EC%84%A0%ED%83%9D%EC%9E%90-not-equal-%EA%B5%AC%ED%98%84&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c9Lw3D/hyRQqTAJEn/DclNKUOYJQ9X0bCdgwljs1/img.jpg?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420,https://scrap.kakaocdn.net/dn/dn1QAs/hyRRLaLnTf/LC6JcH98pfScLx9EoZIe11/img.jpg?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420,https://scrap.kakaocdn.net/dn/bCv5TQ/hyRQwGkUdI/Jt32obtPU5Wlk4eGjHa4LK/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/CSS-%F0%9F%9A%80-%EC%84%A0%ED%83%9D%EC%9E%90-not-equal-%EA%B5%AC%ED%98%84&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inpa.tistory.com/entry/CSS-%F0%9F%9A%80-%EC%84%A0%ED%83%9D%EC%9E%90-not-equal-%EA%B5%AC%ED%98%84&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c9Lw3D/hyRQqTAJEn/DclNKUOYJQ9X0bCdgwljs1/img.jpg?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420,https://scrap.kakaocdn.net/dn/dn1QAs/hyRRLaLnTf/LC6JcH98pfScLx9EoZIe11/img.jpg?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420,https://scrap.kakaocdn.net/dn/bCv5TQ/hyRQwGkUdI/Jt32obtPU5Wlk4eGjHa4LK/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[CSS]   속성 선택자 not equal 구현하기 [attr!=value]&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Attribute Selector not equal 구현 CSS 속성(Attribute) 선택자 문법에는 equal 연산자와 함께 다양한 기호 문법을 지원한다. [attr=value] : attr 속성이 정확히 value인 요소를 선택 [attr~=value] : attr 속성이 정확히 v&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;inpa.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;:has() &lt;/span&gt;가상 선택자&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;:has()&lt;/s&gt; 는 최근에 추가된 선택자이다. &lt;span&gt;이 선택자는 해당 부모 선택자가 특정 자식 선택자를 가지고 있을 경우 적용하는 이른바&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;css의 if문&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;버전이라 보면 된다.&lt;span&gt; 기존에는 이를 구현하려면 css로만은 문법적인 한계가 있어서 &lt;a href=&quot;https://inpa.tistory.com/entry/SCSS-%F0%9F%92%8E-SassSCSS-%EB%9E%80-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%BB%B4%ED%8C%8C%EC%9D%BC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SCSS&lt;/a&gt;나 자바스크립트를 통해서만 가능했다. 하지만 &lt;s&gt;:has()&lt;/s&gt; 개념은 &lt;span&gt;굉장히 유용하고 편리한 선택자 개념이라 jQuery에서 제이쿼리 전용 선택자으로만 쓰였지만, 얼마전 드디어 공식화 되었다.&lt;span&gt;&amp;nbsp;&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;s&gt;div.parent&lt;/s&gt; 가 만일 p 요소를 가지고 있는 경우에만 자신의 요소에 스타일을 적용하고 싶다면 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1678096230728&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div class=&quot;parent&quot;&amp;gt;
  &amp;lt;p&amp;gt;Child&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1678096273756&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* div 요소가 자식으로 p를 가지고 있을 경우 스타일 적용 */
div:has(&amp;gt; p) {
  background: red;
}&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;1023&quot; data-origin-height=&quot;390&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9gRh9/btr0KGH4KXD/IgDwdq9x0qmgirYzXS5cKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9gRh9/btr0KGH4KXD/IgDwdq9x0qmgirYzXS5cKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9gRh9/btr0KGH4KXD/IgDwdq9x0qmgirYzXS5cKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9gRh9%2Fbtr0KGH4KXD%2FIgDwdq9x0qmgirYzXS5cKk%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; alt=&quot;css-has&quot; loading=&quot;lazy&quot; width=&quot;1023&quot; height=&quot;390&quot; data-origin-width=&quot;1023&quot; data-origin-height=&quot;390&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;&lt;b&gt;:has() 선택자 조합 종류&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;:has()&lt;/s&gt; 괄호 안 매개변수로 선택자 문법을 넣음으로써 자식, 형제, 자손 선택자를 다양한 폭으로 지정해줄 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1678097220247&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* a 요소가 자손으로 img를 가지고 있을 경우 */
a:has(img) {
}

/* a 요소가 자식으로 img를 가지고 있을 경우 */
a:has(&amp;gt; img) {
}

/* h3 요소가 형제로 p를 가지고 있을 경우 */
h3:has(+ p) {
}

/* article 요소가 자손으로 h3 그리고 p를 가지고 있을 경우 (and 논리) */
article:has(h3, p) {
}&lt;/code&gt;&lt;/pre&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;:has()&amp;nbsp;와&amp;nbsp;:not()&amp;nbsp;조합&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번엔 약간 난이도 높은 조합을 가져와 보았다. &lt;s&gt;:has()&lt;/s&gt; 와 &lt;s&gt;:not()&lt;/s&gt; 가상 요소를 어떤 순서대로 조합하느냐에 따라 논리적으로 완전히 달라지기 때문에, 어떻게 되는지 코드를 보고 추론해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1678097211720&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* div 요소가 자손으로 h3을 가지고 있지 않을 경우 전체 칼라 적용 */
div.section1:not(:has(h3)) {
  color: red;
}

/* div 요소의 자손이 h3이 아닌 요소가 있을 경우 전체 칼라 적용 */
div.section2:has(:not(h3)) {
  color: blue;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;gOdxaba&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/gOdxaba&quot;&gt; :has() 와 :not() 조합&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&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;자바스크립트 셀렉터 조합&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;:has()&lt;/s&gt; 선택자는 스크립트로 동적으로 요소를 조작해야 할때 빛을 발한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;div.parent&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;가 만일 p 요소를 가지고 있는 경우에만 엘리먼트를 가져오고 싶다면, 기존에는 child 요소를 일일히 순회해서 탐색하며 분간해야 됬지만, 이 selector 하나로 곧바로 일치하는 요소를 가져올 수 있게 되었다.&lt;/p&gt;
&lt;pre id=&quot;code_1678097970648&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div class=&quot;parent&quot;&amp;gt;
  &amp;lt;input type=&quot;checkbox&quot; /&amp;gt;
  &amp;lt;p&amp;gt;Child&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;div class=&quot;parent&quot;&amp;gt;
  &amp;lt;input type=&quot;radio&quot; /&amp;gt;
  &amp;lt;p&amp;gt;Child&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;div class=&quot;parent&quot;&amp;gt;
  &amp;lt;p&amp;gt;Child&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1678098091734&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;document.querySelectorAll('.parent:has(p)')

/* 제이쿼리도 자체 지원한다. */
$('.parent:has(p)')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;브라우저가 :has() 를 지원하는지 테스트&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #292929;&quot;&gt;단, 최신 스펙인 만큼 아직 파이어폭스나 인터넷 익스플로러에서는 아직 사용할 수 없다는 단점이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;499&quot; data-origin-height=&quot;259&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmtn00/btr2rD93mp2/yO5mGu8dNlkvg3o8xk9tY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmtn00/btr2rD93mp2/yO5mGu8dNlkvg3o8xk9tY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmtn00/btr2rD93mp2/yO5mGu8dNlkvg3o8xk9tY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbmtn00%2Fbtr2rD93mp2%2FyO5mGu8dNlkvg3o8xk9tY0%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; alt=&quot;css-has&quot; loading=&quot;lazy&quot; width=&quot;499&quot; height=&quot;259&quot; data-origin-width=&quot;499&quot; data-origin-height=&quot;259&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만일 해당 브라우저가 이 선택자를 지원하는지 알아보고 우회 방법을 적용하고 싶다면 &lt;s&gt;@supports&lt;/s&gt; 를 통해 구성해주면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1678096585252&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@supports(selector(:has(p))) {
  /* Supported! */
}

@supports not (selector(:has(p))) {
  /* Not Supported! 우회코드 작성 */
}&lt;/code&gt;&lt;/pre&gt;
&lt;figure id=&quot;og_1678096582512&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;[CSS]   최신 CSS 기능 지원여부 확인   @supports&quot; data-og-description=&quot;@supports 최신 스펙의 css값을 사용할 때, 해당 css를 제공하는 브라우저에 따라 맞춤형으로 적용할 수 있게 css를 선언할 수 있다. 방법은 바로 @supports 문법(기능 쿼리, feature query)을 이용하는 것이&quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/CSS-%F0%9F%93%9A-%EC%B5%9C%EC%8B%A0-CSS-%EA%B8%B0%EB%8A%A5-%F0%9F%8E%A8-supports&quot; data-og-url=&quot;https://inpa.tistory.com/entry/CSS-%F0%9F%93%9A-%EC%B5%9C%EC%8B%A0-CSS-%EA%B8%B0%EB%8A%A5-%F0%9F%8E%A8-supports&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ofwGF/hyRQvm5cXJ/17kr6ciRdI9Ccit4jXVdg0/img.png?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420,https://scrap.kakaocdn.net/dn/i880e/hyRQj7ZOaQ/Ui9GJnd8mefkk7tQunBou0/img.png?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420,https://scrap.kakaocdn.net/dn/BKS69/hyRRCZbXCp/5w00ILP8aa6AWF5RJJFTI0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/CSS-%F0%9F%93%9A-%EC%B5%9C%EC%8B%A0-CSS-%EA%B8%B0%EB%8A%A5-%F0%9F%8E%A8-supports&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inpa.tistory.com/entry/CSS-%F0%9F%93%9A-%EC%B5%9C%EC%8B%A0-CSS-%EA%B8%B0%EB%8A%A5-%F0%9F%8E%A8-supports&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ofwGF/hyRQvm5cXJ/17kr6ciRdI9Ccit4jXVdg0/img.png?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420,https://scrap.kakaocdn.net/dn/i880e/hyRQj7ZOaQ/Ui9GJnd8mefkk7tQunBou0/img.png?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420,https://scrap.kakaocdn.net/dn/BKS69/hyRRCZbXCp/5w00ILP8aa6AWF5RJJFTI0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[CSS]   최신 CSS 기능 지원여부 확인   @supports&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;@supports 최신 스펙의 css값을 사용할 때, 해당 css를 제공하는 브라우저에 따라 맞춤형으로 적용할 수 있게 css를 선언할 수 있다. 방법은 바로 @supports 문법(기능 쿼리, feature query)을 이용하는 것이&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;inpa.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://junghan92.medium.com/%EB%B2%88%EC%97%AD-where-is-has-%EB%8B%B9%EC%8B%A0%EC%9D%98-%EC%82%B6%EC%9D%84-%EB%8D%94-%EC%89%BD%EA%B2%8C-%EB%A7%8C%EB%93%A4%EC%96%B4-%EC%A4%84-%EC%83%88%EB%A1%9C%EC%9A%B4-css-%EC%84%A0%ED%83%9D%EC%9E%90-ee44dd58aa3b&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://css-tricks.com/complete-guide-to-css-functions/&lt;/p&gt;</description>
      <category>Style Sheet/CSS</category>
      <category>css has</category>
      <category>css is</category>
      <category>css not</category>
      <category>css where</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1096</guid>
      <comments>https://inpa.tistory.com/entry/%F0%9F%8C%9F-css-where-is-has-not#entry1096comment</comments>
      <pubDate>Thu, 9 Mar 2023 09:28:51 +0900</pubDate>
    </item>
    <item>
      <title> ️ 스크립트 없이 구현 가능한 독특한 HTML 태그 9가지</title>
      <link>https://inpa.tistory.com/entry/%F0%9F%8F%B7%EF%B8%8F-%EB%8F%85%ED%8A%B9%ED%95%9C-%ED%83%9C%EA%B7%B8-%EC%86%8C%EA%B0%9C</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;tJ5CZETgyMTUqX9dJjAWrKzfDIzComXPaHKhW0V1.webp&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;630&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ce6bHk/btr0qrKftpa/h76YPRO5dUo6UMyaRNwXOK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ce6bHk/btr0qrKftpa/h76YPRO5dUo6UMyaRNwXOK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ce6bHk/btr0qrKftpa/h76YPRO5dUo6UMyaRNwXOK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fce6bHk%2Fbtr0qrKftpa%2Fh76YPRO5dUo6UMyaRNwXOK%2Fimg.webp&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; alt=&quot;html-tags&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;630&quot; data-filename=&quot;tJ5CZETgyMTUqX9dJjAWrKzfDIzComXPaHKhW0V1.webp&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;630&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅은 HTML5을 배우는데 있어 접하지 못한 독특한 엘리먼트들을 소개해 보는 시간을 가져볼 것이다. 기존의 태그와 자바스크립트를 조합하여 힘들게 구현해야 하는 컴포넌트들을 태그 하나로만 구현할수 있어, 비록 아주 자주 사용될 녀석들이 아니지만 한번 쯤 훑어보고 존재 정도만 머리에 각인시키고 있다면, 추후에 도움이 될 것이다. 각 요소의 속성 상세 스펙은 MDN을 참고하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;progress&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;progress 태그&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;progress 요소는 진행 정도를 나타내는 바를 만드는 태그이다. 주로 자신의 스킬 퍼센티지를 표기하거나 api 사용량을 표현하는데 자주 애용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엘리먼트의 max 에 따른 value 속성값에 따라 bar의 게이지가 포함되는 원리이다.&lt;/p&gt;
&lt;pre id=&quot;code_1677226250260&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;progress value=&quot;50&quot; max=&quot;100&quot;&amp;gt;&amp;lt;/progress&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;499&quot; data-origin-height=&quot;123&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/emQksJ/btr0DhucqSF/12Idfak3l3ZlbWQJKG9T7k/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/emQksJ/btr0DhucqSF/12Idfak3l3ZlbWQJKG9T7k/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/emQksJ/btr0DhucqSF/12Idfak3l3ZlbWQJKG9T7k/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/emQksJ/btr0DhucqSF/12Idfak3l3ZlbWQJKG9T7k/img.gif&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; alt=&quot;progress 태그&quot; loading=&quot;lazy&quot; width=&quot;499&quot; height=&quot;123&quot; data-origin-width=&quot;499&quot; data-origin-height=&quot;123&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 311.8182067871094px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;311.8182067871094&quot; data-default-tab=&quot;css,result&quot; data-slug-hash=&quot;poObmeB&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/poObmeB&quot;&gt; progress&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;meter&amp;nbsp;태그&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;meter 태그는 progress 요소와 같이 진행 정도 바를 표현하지만, progress는 값의 상태만 전달 할 수 있는 것에 비해 meter은 속성에 따라 값이 높고 낮을때 색상을 다르게 설정하여 작업의 진행 상태를 세세하게 표현이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;value, min, max, low, high, optimum 속성을 통해 진행정도에 따른 색상변경이 가능하다. 기본적으로 값의 포화도에 따라 초록색 &amp;rarr; 노란색 &amp;rarr; 빨간색으로 색상이 변경된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;value :&lt;span&gt;&amp;nbsp;&lt;/span&gt;현재 측정값 지정&lt;/li&gt;
&lt;li&gt;min : 범위의 하한을 지정&lt;/li&gt;
&lt;li&gt;max : 범위의 상한을 지정&lt;/li&gt;
&lt;li&gt;low : 낮은 값 범위 지정&lt;/li&gt;
&lt;li&gt;high : 높은 값 범위 지정&lt;/li&gt;
&lt;li&gt;optimum : 최상의 또는 최적의 숫자 범위를 지정&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1677226488898&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;meter value=&quot;15&quot; min=&quot;0&quot; max=&quot;100&quot; low=&quot;20&quot; high=&quot;65&quot; optimum=&quot;15&quot; id=&quot;meter&quot;&amp;gt;&amp;lt;/meter&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;502&quot; data-origin-height=&quot;214&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/x2M8y/btr0JOqI5Cc/zfdnHwTjmLi0T7ZMdpI8t0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/x2M8y/btr0JOqI5Cc/zfdnHwTjmLi0T7ZMdpI8t0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/x2M8y/btr0JOqI5Cc/zfdnHwTjmLi0T7ZMdpI8t0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fx2M8y%2Fbtr0JOqI5Cc%2FzfdnHwTjmLi0T7ZMdpI8t0%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; alt=&quot;meter 태그&quot; loading=&quot;lazy&quot; width=&quot;502&quot; height=&quot;214&quot; data-origin-width=&quot;502&quot; data-origin-height=&quot;214&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 370.9090576171875px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;370.9090576171875&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;zYJBQzv&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/zYJBQzv&quot;&gt; meter&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;map &amp;amp; area 태그&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;b&gt;세계지도에 국가 영토 위치별로 해당 링크 요소&lt;/b&gt;를 이미지에 여러개 위치 시켜주고 싶을때 이용된다고 보면 된다. 바로 아래 예제를 통해 한눈에 이해할 수 있을 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1678083832373&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;map name=&quot;continents_map&quot;&amp;gt;
  &amp;lt;area shape=&quot;circle&quot; coords=&quot;70,70,10&quot; href=&quot;#north_america&quot;&amp;gt; &amp;lt;!-- 이미지의 좌표 70,70,10 에 링크를 표시 --&amp;gt;
  &amp;lt;area shape=&quot;circle&quot; coords=&quot;133,185,10&quot; href=&quot;#south_america&quot;&amp;gt;
  &amp;lt;area shape=&quot;circle&quot; coords=&quot;270,137,10&quot; href=&quot;#africa&quot;&amp;gt;
  &amp;lt;area shape=&quot;circle&quot; coords=&quot;292,44,10&quot; href=&quot;#europe&quot;&amp;gt;
  &amp;lt;area shape=&quot;circle&quot; coords=&quot;469,201,10&quot; href=&quot;#australia&quot;&amp;gt;
  &amp;lt;area shape=&quot;circle&quot; coords=&quot;374,65,10&quot; href=&quot;#asia&quot;&amp;gt;
  &amp;lt;area shape=&quot;circle&quot; coords=&quot;340,267,10&quot; href=&quot;#antartica&quot;&amp;gt;
&amp;lt;/map&amp;gt;

&amp;lt;!-- usemap 속성을 통해 map &amp;amp; area 기능을 사용할 이미지를 상호작용한다 (continents_map) --&amp;gt;
&amp;lt;img usemap=&quot;#continents_map&quot; src=https://bit.ly/2bgFrvL width=&quot;600px&quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 540px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;540&quot; data-default-tab=&quot;result&quot; data-slug-hash=&quot;PqZNzG&quot; data-user=&quot;rpsthecoder&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/rpsthecoder/pen/PqZNzG&quot;&gt; Image map&lt;/a&gt; by Preethi Sam (&lt;a href=&quot;https://codepen.io/rpsthecoder&quot;&gt;@rpsthecoder&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;picture 태그&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;&amp;lt;img&amp;gt;&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;태그로 이미지를 문서에 표현 해왔겠지만,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;&amp;lt;picture&amp;gt;&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&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;반응형 홈페이지의 경우 최적화를 위해 모바일 이미지와 PC화면의 이미지를 다르게 설정하여 모바일 사용자가 쓸데없이 큰 이미지를 다운로드 받지 않게 하기 위해 이용된다고 보면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 설정하면 각 media 사이즈에서 각 해당 이미지 보여주게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1678083628992&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;picture&amp;gt;
    &amp;lt;source srcset=&quot;src/01.jpeg&quot; media=&quot;(min-width:1200px)&quot; type=&quot;image/jpeg&quot;/&amp;gt;
    &amp;lt;source srcset=&quot;src/02.jpeg&quot; media=&quot;(min-width:900px)&quot; type=&quot;image/jpeg&quot;/&amp;gt;
    &amp;lt;source srcset=&quot;src/03.jpeg&quot; media=&quot;(min-width:500px)&quot; type=&quot;image/jpeg&quot;/&amp;gt;
    &amp;lt;img src=&quot;src/04.jpeg&quot; /&amp;gt;
&amp;lt;/picture&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li id=&quot;98bd&quot; data-selectable-paragraph=&quot;&quot;&gt;&lt;s&gt;&amp;lt;picture&amp;gt;&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;요소 내의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;&amp;lt;source&amp;gt;&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;요소는 다양한 유형의 장치 또는 브라우저에 대해 다양한 버전의 이미지를 제공하는 데 사용된다&lt;/li&gt;
&lt;li id=&quot;ea7a&quot; data-selectable-paragraph=&quot;&quot;&gt;&lt;s&gt;&amp;lt;source&amp;gt;&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;요소의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;srcset&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;속성은 이미지의 소스를 지정한다.&lt;/li&gt;
&lt;li data-selectable-paragraph=&quot;&quot;&gt;&lt;s&gt;&amp;lt;source&amp;gt;&lt;/s&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;요소의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;s&gt;type&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;속성은 이미지 유형을 지정한다.&lt;/li&gt;
&lt;li id=&quot;4820&quot; data-selectable-paragraph=&quot;&quot;&gt;마지막&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;&amp;lt;img&amp;gt;&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;요소는 만일 브라우저가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;s&gt;&amp;lt;picture&amp;gt;&lt;/s&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;요소를 지원하지 않는 경우에 대한 방지책으로 넣어준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;detail &amp;amp; summary 태그&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;클릭으로 정보를 보여주고 숨기는 기능으로서, &lt;/span&gt;일종의 &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;background-color: #ffffff; color: #212529;&quot;&gt;&lt;s&gt;&amp;lt;summary&amp;gt;&lt;/s&gt; 태그 안에는 클릭 하기 전의 내용이 보여지게 되고, 클릭 시 펼쳐질 내용을 다음에 입력하면 된니다. css로는 &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;&lt;s&gt;[open]&lt;/s&gt; 이라는 CSS선택자를 통해 스타일 변경 가능 하다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1677566978675&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;details&amp;gt;
	&amp;lt;summary&amp;gt;클릭 전 볼 수 있는 영역&amp;lt;/summary&amp;gt;
	&amp;lt;span&amp;gt;클릭 후에만 표시되는 영역&amp;lt;/span&amp;gt;
&amp;lt;/details&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1677566972839&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;details[open] {
	background-color: sky;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;zYJBQEr&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/zYJBQEr&quot;&gt; Untitled&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;input 태그 속성&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;달력 날짜 속성&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;input 태그의 type 속성으로&lt;span&gt; 달력 및 일일, 주, 월별 그룹 지정도 가능하다. 브라우저 환경에 따라 달력 위젯의 형태는 달라질 수 있다&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1677567556025&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;input type=&quot;date&quot;/&amp;gt;
&amp;lt;input type=&quot;week&quot;/&amp;gt;
&amp;lt;input type=&quot;month&quot;/&amp;gt;
&amp;lt;input type=&quot;time&quot;/&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 363.6363525390625px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;363.6363525390625&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;MWqJyQz&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/MWqJyQz&quot;&gt; 달력, 날짜 선택기&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&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;b&gt;색상 선택기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만일 color picker 기능이 웹페이지에 필요하다면 무거운 라이브러리 없이 정말 간단하게 구현이 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1678086617017&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;input id=&quot;color_picker&quot; type=&quot;color&quot; onchange=&quot;showHex();&quot;&amp;gt; 
&amp;lt;h1&amp;gt;COLOR ME !&amp;lt;/h1&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1678086624697&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function showHex(){
  document.querySelector(&quot;h1&quot;).style.color = document.querySelector(&quot;#color_picker&quot;).value;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;yLxoBWo&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/yLxoBWo&quot;&gt; Untitled&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;5-datalist&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;datalist 태그&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;자바스크립트 없이&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;auto complete(자동 완성 &lt;b&gt;기능&lt;/b&gt;)&amp;nbsp;&lt;/b&gt; 를 &lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;사용 가능하게 해주는 요소이다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;또한 &lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&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: #212529;&quot;&gt;사용법은 아래와 같은데 이때 유의해야 할점은, &lt;span style=&quot;background-color: #ffffff; color: #212529;&quot;&gt;&lt;s&gt;&amp;lt;input&amp;gt;&lt;/s&gt;의 list 속성값과 &lt;s&gt;&amp;lt;datalist&amp;gt;&lt;/s&gt;의 id명을 똑같이 맞춰주어야 동작된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;745&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dCWyJ2/btr0WfwuE8y/ruwkcdd9kmpBQVSq3OQTJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dCWyJ2/btr0WfwuE8y/ruwkcdd9kmpBQVSq3OQTJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dCWyJ2/btr0WfwuE8y/ruwkcdd9kmpBQVSq3OQTJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdCWyJ2%2Fbtr0WfwuE8y%2Fruwkcdd9kmpBQVSq3OQTJ0%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; alt=&quot;html-datalist&quot; loading=&quot;lazy&quot; width=&quot;543&quot; height=&quot;175&quot; data-origin-width=&quot;745&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1677568000293&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- input의 list와 datalist의 id 명칭은 동일해야 된다 --&amp;gt;

&amp;lt;label for=&quot;movie&quot;&amp;gt;What is your favourite movie?&amp;lt;/label&amp;gt;
&amp;lt;input type=&quot;text&quot; list=&quot;movie-options&quot;/&amp;gt;

&amp;lt;datalist id=&quot;movie-options&quot;&amp;gt;
  &amp;lt;option value=&quot;Dune&quot;/&amp;gt;
  &amp;lt;option value=&quot;Dark waters&quot;/&amp;gt;
  &amp;lt;option value=&quot;The Artist&quot;/&amp;gt;
  &amp;lt;option value=&quot;The Avengers&quot;/&amp;gt;
  &amp;lt;option value=&quot;Iron Man&quot;/&amp;gt;
  &amp;lt;option value=&quot;Iron Man II&quot;/&amp;gt;
  &amp;lt;option value=&quot;Matrix&quot;/&amp;gt;
&amp;lt;/datalist&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;wvEgGEy&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/wvEgGEy&quot;&gt; datalist 태그&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;template 태그&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동적으로 복잡한 엘리먼트 요소를 DOM에 삽입할때 보통 문자열 형태로 만들고 innerHTML이나 제이쿼리 함수로 삽입 할 것이다. 나쁘지 않은 방법이지만 문자열은 하이라이트도 안되서 가독이 좋지 않는 다는 한계점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 극복하기 위해 아예 html에 &lt;s&gt;&amp;lt;template&amp;gt;&lt;/s&gt; 태그로 감싸면 이 요소는 문서 표시가 아닌 템플릿용으로 취급되게 하는 템플릿 태그가 추가 되었다. 템플릿 요소 내부의 모든 콘텐츠는 비활성화되며 렌더링 되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어&lt;span&gt; 아래&amp;nbsp;&lt;/span&gt;&lt;s&gt;&amp;lt;table&amp;gt;&lt;/s&gt; 행을 버튼을 누르면 스크립트로 동적으로 추가하고 싶다면,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;lt;template&amp;gt; 요소를 자바스크립트로 호출하여 템플릿 태그 내부에서 마크업을 복사하고 테이블 마크업에 추가하는 식으로 구성 할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1678089076047&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;table id=&quot;myTable&quot;&amp;gt;
    &amp;lt;tr&amp;gt;
        &amp;lt;th&amp;gt;Sl. No.&amp;lt;/th&amp;gt;
        &amp;lt;th&amp;gt;Input&amp;lt;/th&amp;gt;
    &amp;lt;/tr&amp;gt;
    &amp;lt;tr&amp;gt;
        &amp;lt;td&amp;gt;1.&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;
            &amp;lt;input type=&quot;text&quot; /&amp;gt;
        &amp;lt;/td&amp;gt;
    &amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;

&amp;lt;!-- 버튼 클릭하면 자바스크립트 실행 --&amp;gt;
&amp;lt;input type=&quot;button&quot; value=&quot;New Row&quot; onclick=&quot;addRow()&quot;&amp;gt;

&amp;lt;!-- 테이블에 추가할 테이블 내부 템플릿 --&amp;gt;
&amp;lt;template id=&quot;rowTemplate&quot;&amp;gt;
    &amp;lt;tr&amp;gt;
        &amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;
            &amp;lt;input type=&quot;text&quot; /&amp;gt;
        &amp;lt;/td&amp;gt;
    &amp;lt;/tr&amp;gt;
&amp;lt;/template&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1678089085474&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function addRow() {
  // 템플릿 요소 가져오기
  const template = document.querySelector('#rowTemplate');
  
  // 템플릿을 추가할 요소 가져오기
  const tbl = document.querySelector('#myTable');
  
  // 템플릿의 content 속성과 그의 자식 모든 요소를 복사
  const clone = template.content.cloneNode(true); 
  
  // 기존 요소에 복사한 템플릿을 추가
  tbl.appendChild(clone);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 450.90911865234375px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;450.90911865234375&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;WNgENdX&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/WNgENdX&quot;&gt; template 태그&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;optgroup 태그&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선택 상자를 구현하기 위해 &lt;s&gt;&amp;lt;select&amp;gt;&lt;/s&gt; 태그와 내부 리스트인 &lt;s&gt;&amp;lt;option&amp;gt;&lt;/s&gt; 태그를 사용할 텐데, 이 리스트를 그룹으로 묶어 표현할때 사용된다. 예를 들어 영화 목록을 시간별로 그룹화해야 하는 경우 다음과 같이 구성될 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1678151948475&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;label for=&quot;showtimes&quot;&amp;gt;Showtimes&amp;lt;/label&amp;gt;

&amp;lt;select id=&quot;showtimes&quot; name=&quot;showtimes&quot;&amp;gt; 
    &amp;lt;optgroup label=&quot;1PM&quot;&amp;gt;
        &amp;lt;option value=&quot;titanic&quot;&amp;gt;Twister&amp;lt;/option&amp;gt; 
        &amp;lt;option value=&quot;nd&quot;&amp;gt;Napoleon Dynamite&amp;lt;/option&amp;gt; 
        &amp;lt;option value=&quot;wab&quot;&amp;gt;What About Bob?&amp;lt;/option&amp;gt; 
    &amp;lt;/optgroup&amp;gt; 
    
    &amp;lt;optgroup label=&quot;2PM&quot;&amp;gt;
        &amp;lt;option value=&quot;bkrw&quot;&amp;gt;Be Kind Rewind&amp;lt;/option&amp;gt; 
        &amp;lt;option value=&quot;stf&quot;&amp;gt;Stranger Than Fiction&amp;lt;/option&amp;gt; 
    &amp;lt;/optgroup&amp;gt; 
&amp;lt;/select&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 320.90911865234375px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;320.90911865234375&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;WNgEyoe&quot; data-user=&quot;inpaSkyrim&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/inpaSkyrim/pen/WNgEyoe&quot;&gt; optgroup 태그&lt;/a&gt; by barzz12 (&lt;a href=&quot;https://codepen.io/inpaSkyrim&quot;&gt;@inpaSkyrim&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;# 참고자료&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot; data-swap-href=&quot;https://youtu.be/EMOlLLTAZMs&quot;&gt;노마드 코더 - 개발자가 모르는 신박한 HTML 태그 5개&lt;/p&gt;
&lt;p class=&quot;swap-href&quot; data-ke-size=&quot;size16&quot;&gt;https://velog.io/@jiseon-han/%EC%8B%A0%EA%B8%B0%ED%95%9C-HTML-%ED%83%9C%EA%B7%B8-5%EA%B0%9C&lt;/p&gt;</description>
      <category>Mark Up/HTML</category>
      <category>html tag</category>
      <category>HTML 요소</category>
      <category>html 태그</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1093</guid>
      <comments>https://inpa.tistory.com/entry/%F0%9F%8F%B7%EF%B8%8F-%EB%8F%85%ED%8A%B9%ED%95%9C-%ED%83%9C%EA%B7%B8-%EC%86%8C%EA%B0%9C#entry1093comment</comments>
      <pubDate>Wed, 8 Mar 2023 07:53:16 +0900</pubDate>
    </item>
    <item>
      <title>  브라우저 이미지 캐시 방지하기</title>
      <link>https://inpa.tistory.com/entry/%F0%9F%8C%90-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%BA%90%EC%8B%9C-%EB%B0%A9%EC%A7%80</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;how-to-clear-browser-cache-1.webp&quot; data-origin-width=&quot;1460&quot; data-origin-height=&quot;730&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vKXeW/btrZa3FbFz0/Wn8jwZXT833maHKarblK5k/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vKXeW/btrZa3FbFz0/Wn8jwZXT833maHKarblK5k/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vKXeW/btrZa3FbFz0/Wn8jwZXT833maHKarblK5k/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvKXeW%2FbtrZa3FbFz0%2FWn8jwZXT833maHKarblK5k%2Fimg.webp&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; alt=&quot;Disable Image Cache&quot; loading=&quot;lazy&quot; width=&quot;1460&quot; height=&quot;730&quot; data-filename=&quot;how-to-clear-browser-cache-1.webp&quot; data-origin-width=&quot;1460&quot; data-origin-height=&quot;730&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Disable Image Cache&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹페이지 속도 테스트나 혹은 이미지를 다루는 라이브러리 중에 이미지를 Ajax로 불러오다가 &lt;a href=&quot;https://inpa.tistory.com/entry/%F0%9F%8C%90-CORS-%EC%BA%90%EC%8B%9C-%EC%97%90%EB%9F%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;캐시에 의한 CORS 에러&lt;/a&gt; 문제 등 여러 이유들에 의해서 브라우저가 이미지를 캐싱 하지 않게 하고 싶은 때가 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 직접 서버에서 &lt;a href=&quot;https://inpa.tistory.com/entry/HTTP-%F0%9F%8C%90-%EC%9B%B9-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EC%9D%98-%EC%BA%90%EC%8B%9C-%EC%A0%84%EB%9E%B5-Cache-Headers-%EB%8B%A4%EB%A3%A8%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;캐시 관련 헤더&lt;/a&gt; 설정을 할수 없거나 다른 업체의 서버를 사용하는 경우 결국 프론트단에서 처리해야 한다. &lt;s&gt;ctrl + F5&lt;/s&gt; 강력 새로고침으로 임시적으로 해결할수는 있지만, 매번 이럴수도 없고 &lt;span&gt;무슨 방법이 없을까 고민하겠지만 의외로 방법은 간단하다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;354&quot; data-origin-height=&quot;109&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WP5Jc/btrZa3FbgM0/RDzMDnmu90q6t5uYNtwTy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WP5Jc/btrZa3FbgM0/RDzMDnmu90q6t5uYNtwTy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WP5Jc/btrZa3FbgM0/RDzMDnmu90q6t5uYNtwTy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWP5Jc%2FbtrZa3FbgM0%2FRDzMDnmu90q6t5uYNtwTy1%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; alt=&quot;Disable Image Cache&quot; loading=&quot;lazy&quot; width=&quot;354&quot; height=&quot;109&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;브라우저는 이미지를 완전히 같은 url 이름으로 불러들일때는 캐시한 이전 이미지를 불러온다. 이러한 특성을 이용하여 url의 쿼리스트링 값만 다르게 주는 것으로 캐시되지 않은 똑같은 이미지를 불러오도록 하는 편법을 쓸 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어 &lt;span style=&quot;color: #0593d3;&quot;&gt;picture.jpg?1222259157&lt;/span&gt; 와 같이 이미지 url &lt;s&gt;?&lt;/s&gt; 뒤의 쿼리스트링 값을 랜덤으로 설정해줘서 할당해주면 된다. 쿼리스트링 값이 달라져도 가져오는 리소스는 동일하니 문제 없는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 자바스크립트로 현재 날짜 시간값을 쿼리 스트링으로 할당한 예제이다. 이외에도 랜덤 함수로도 구현할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1676350658692&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;document.querySelector('img').src = `https://이미지경로?${new Date().getTime()}`&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Language/JavaScript (WEB)</category>
      <category>image cache</category>
      <category>이미지 캐시</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1087</guid>
      <comments>https://inpa.tistory.com/entry/%F0%9F%8C%90-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%BA%90%EC%8B%9C-%EB%B0%A9%EC%A7%80#entry1087comment</comments>
      <pubDate>Tue, 7 Mar 2023 13:00:50 +0900</pubDate>
    </item>
    <item>
      <title>  리소스 캐시로 인한 CORS 에러 현상 고찰</title>
      <link>https://inpa.tistory.com/entry/%F0%9F%8C%90-CORS-%EC%BA%90%EC%8B%9C-%EC%97%90%EB%9F%AC</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;CORS (1).webp&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pjldb/btrZzH1oB9D/2LOC1fkisC33bFI7brqnIk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pjldb/btrZzH1oB9D/2LOC1fkisC33bFI7brqnIk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pjldb/btrZzH1oB9D/2LOC1fkisC33bFI7brqnIk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpjldb%2FbtrZzH1oB9D%2F2LOC1fkisC33bFI7brqnIk%2Fimg.webp&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; alt=&quot;cors-image-cache&quot; loading=&quot;lazy&quot; width=&quot;1080&quot; height=&quot;540&quot; data-filename=&quot;CORS (1).webp&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;브라우저 캐시로 인한 CORS 문제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;CORS(Cross-Origin Resource Sharing)는 서로 다른 출처(Origin)의 리소스를 공유하고 싶을때 사용하는 정책을 말한다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;기본적으로 브라우저는 SOP(Same Origin Policy) 정책을 따르기 때문에 외부 리소스에 대해서 차단한다. 하지만 인터넷은 여러 사람들에게 오픈된 환경이고, 이런 환경에서 웹페이지에서 다른 출처에 있는 리소스를 가져와 사용하는 일은 매우 흔한 일이라 &lt;span style=&quot;color: #212529;&quot;&gt;모든 외부 리소스를&amp;nbsp; &lt;/span&gt;무턱대고 막으면 지금처럼 웹이 발전하지 않았을 것이다. 따라서 외부 리소스라도 허용 가능한 예외 사항을 두었는데 그것이 CORS 정책이다.&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1676541649161&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;[WEB]   악명 높은 CORS 개념 &amp;amp; 해결법 - 정리 끝판왕  &quot; data-og-description=&quot;악명 높은 CORS 에러 메세지 웹 개발을 하다보면 반드시 마주치는 멍멍 같은 에러가 바로 CORS 이다. 웹 개발의 신입 신고식이라고 할 정도로, CORS는 누구나 한 번 정도는 겪게 된다고 해도 과언이 &quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-CORS-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-%F0%9F%91%8F&quot; data-og-url=&quot;https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-CORS-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-%F0%9F%91%8F&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dsCPSk/hyRCWZFieU/IwSDPb3VxFj69SOyBCnDsk/img.png?width=800&amp;amp;height=466&amp;amp;face=0_0_800_466,https://scrap.kakaocdn.net/dn/dqEEIk/hyRD1LPmgO/YWeniTTKqsrkdRtNCBD2P1/img.png?width=800&amp;amp;height=466&amp;amp;face=0_0_800_466,https://scrap.kakaocdn.net/dn/bPRUXN/hyRCO1DhNn/gyYaW2oLEBDSrgb9fQ8ww0/img.png?width=1200&amp;amp;height=700&amp;amp;face=0_0_1200_700&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-CORS-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-%F0%9F%91%8F&quot; data-source-url=&quot;https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-CORS-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-%F0%9F%91%8F&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dsCPSk/hyRCWZFieU/IwSDPb3VxFj69SOyBCnDsk/img.png?width=800&amp;amp;height=466&amp;amp;face=0_0_800_466,https://scrap.kakaocdn.net/dn/dqEEIk/hyRD1LPmgO/YWeniTTKqsrkdRtNCBD2P1/img.png?width=800&amp;amp;height=466&amp;amp;face=0_0_800_466,https://scrap.kakaocdn.net/dn/bPRUXN/hyRCO1DhNn/gyYaW2oLEBDSrgb9fQ8ww0/img.png?width=1200&amp;amp;height=700&amp;amp;face=0_0_1200_700');&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]   악명 높은 CORS 개념 &amp;amp; 해결법 - 정리 끝판왕  &lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;악명 높은 CORS 에러 메세지 웹 개발을 하다보면 반드시 마주치는 멍멍 같은 에러가 바로 CORS 이다. 웹 개발의 신입 신고식이라고 할 정도로, CORS는 누구나 한 번 정도는 겪게 된다고 해도 과언이&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;inpa.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;요청 방식에 따라 다른 CORS 발생 여부&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이때 브라우저는 HTTP 요청에 대해서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;어떤 방식으로 요청을 하느냐에 따라 CORS를 자동으로 허용하기도 막기도 한다. 예를들어 &amp;lt;img&amp;gt; 태그와 같은 인라인으로 리소스를 요청하면 다른 출처의 리소스라도 검사 없이 자동 통과된다. 그러나 자바스크립트 Ajax 요청일 경우 어김없이 차단되어 버린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청 방식에 따라 다른 CORS 발생 여부를 좀 더 이해하기 쉽게 아래 html 코드를 직접 작성하고 테스트 해보자. 똑같은 서버 도메인으로 부터 check.svg 이미지를 가져오지만 결과는 다르게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;※&lt;/span&gt; &lt;span&gt;참고로 해당 호스팅 서버는 cors 설정이 되어있지 않는 서버라고 가정해보자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1676541080233&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;img src=&quot;https://third-party-test.glitch.me/check.svg&quot; alt=&quot;이미지&quot;&amp;gt;

&amp;lt;script&amp;gt;
    fetch('https://third-party-test.glitch.me/check.svg')
        .then(response =&amp;gt; response.blob())
        .then(imgBlob =&amp;gt; {
            const imageObjectURL = URL.createObjectURL(imgBlob); // 응답 받은 이미지를 blob 객체로 변환
            const img = document.createElement('img'); // 이미지 태그를 생성하고
            img.src = imageObjectURL; // 이미지 경로를 설정한뒤
            document.body.append(img); // html에 추가
        })
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지 결과를 보면 이미지가 하나만 나타나는 걸 볼 수 있을 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;345&quot; data-origin-height=&quot;143&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IcRoV/btrZCplrzCz/GNkcWt8i2RSMeZRzWCjzI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IcRoV/btrZCplrzCz/GNkcWt8i2RSMeZRzWCjzI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IcRoV/btrZCplrzCz/GNkcWt8i2RSMeZRzWCjzI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIcRoV%2FbtrZCplrzCz%2FGNkcWt8i2RSMeZRzWCjzI1%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; alt=&quot;cors-image-cache&quot; loading=&quot;lazy&quot; width=&quot;345&quot; height=&quot;143&quot; data-origin-width=&quot;345&quot; data-origin-height=&quot;143&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저 개발자 도구의 Network 창을 보면 check.svg 이미지에 대해서 두번 요청은 했지만 자바스크립트 fetch Type으로 요청한 것이 Status가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;CORS error&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;임을 볼수가 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;605&quot; data-origin-height=&quot;92&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DHPK9/btrZuHuR4fX/tvrJjNLzfnjhjyzvl0mYtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DHPK9/btrZuHuR4fX/tvrJjNLzfnjhjyzvl0mYtk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DHPK9/btrZuHuR4fX/tvrJjNLzfnjhjyzvl0mYtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDHPK9%2FbtrZuHuR4fX%2FtvrJjNLzfnjhjyzvl0mYtk%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; alt=&quot;cors-image-cache&quot; loading=&quot;lazy&quot; width=&quot;605&quot; height=&quot;92&quot; data-origin-width=&quot;605&quot; data-origin-height=&quot;92&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;807&quot; data-origin-height=&quot;544&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cStwDe/btrZtqmwmSY/4zQeNYIk7sFkbzDpZF5fck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cStwDe/btrZtqmwmSY/4zQeNYIk7sFkbzDpZF5fck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cStwDe/btrZtqmwmSY/4zQeNYIk7sFkbzDpZF5fck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcStwDe%2FbtrZtqmwmSY%2F4zQeNYIk7sFkbzDpZF5fck%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; alt=&quot;cors-image-cache&quot; loading=&quot;lazy&quot; width=&quot;807&quot; height=&quot;544&quot; data-origin-width=&quot;807&quot; data-origin-height=&quot;544&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #222222;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;fetch 요청 처리에 대한 헤더 목록을 보면 그 이유를 알 수 있는데, 클라이언트가 자신의 Origin을 요청 헤더에 넣어 서버에 전달했지만, 서버에선 별다른 액션 없이 &lt;s&gt;Access-Control-Allow-Origin&lt;/s&gt; 헤더를 응답해주지 않았고, 이를 브라우저가 감지하여 사단에 차단한 것이다. &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #222222;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;여기서 오해하지 말아야 할 점이 서버는 리소스를 정상 응답했지만 브라우저가 cors 관련 헤더 없다고 차단해 버린 것이다.&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;s&gt;&amp;lt;img&amp;gt;&lt;/s&gt;, &lt;s&gt;&amp;lt;video&amp;gt;&lt;/s&gt; 와 같은 일부 미디어 태그를 통해 리소스를 요청할 CORS를 제한하지 않는다.그래서 HTTP 요청시 Origin 헤더를 추가하지 않고, 브라우저도 별다른 검사 없이 통과하게 해주는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;834&quot; data-origin-height=&quot;649&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dtB1Vw/btrZA3pxB2i/gJMMEQ86K3fnSkUK39ewi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dtB1Vw/btrZA3pxB2i/gJMMEQ86K3fnSkUK39ewi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dtB1Vw/btrZA3pxB2i/gJMMEQ86K3fnSkUK39ewi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdtB1Vw%2FbtrZA3pxB2i%2FgJMMEQ86K3fnSkUK39ewi0%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; alt=&quot;cors-image-cache&quot; loading=&quot;lazy&quot; width=&quot;834&quot; height=&quot;649&quot; data-origin-width=&quot;834&quot; data-origin-height=&quot;649&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;style3&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;그런데 이 CORS를 제한하지 않고 받은 리소스를 브라우저가 로컬 캐시에 저장하고 재활용 할 경우 문제가 발생하게 된다.&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: #ef5369;&quot;&gt;※&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;참고로 해당 호스팅 서버는 cors 설정이 되어있는 서버이기 때문에 자바스크립트 ajax 요청에도 정상적으로 응답하게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1676537199705&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;img src=&quot;https://tistory4.daumcdn.net/tistory/5927418/skin/images/sample.png&quot; alt=&quot;이미지&quot;&amp;gt;

&amp;lt;script&amp;gt;
    fetch('https://tistory4.daumcdn.net/tistory/5927418/skin/images/sample.png', { mode: 'cors' })
        .then(response =&amp;gt; response.blob())
        .then(imgBlob =&amp;gt; {
            const imageObjectURL = URL.createObjectURL(imgBlob);
            const img = document.createElement('img');
            img.src = imageObjectURL;
            document.body.append(img);
        })
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;582&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLM8Hg/btrZzKRDmyu/ukKo3ssuES3I9ZjEZM5kEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLM8Hg/btrZzKRDmyu/ukKo3ssuES3I9ZjEZM5kEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLM8Hg/btrZzKRDmyu/ukKo3ssuES3I9ZjEZM5kEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLM8Hg%2FbtrZzKRDmyu%2FukKo3ssuES3I9ZjEZM5kEk%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; alt=&quot;cors-image-cache&quot; loading=&quot;lazy&quot; width=&quot;904&quot; height=&quot;582&quot; data-origin-width=&quot;904&quot; data-origin-height=&quot;582&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;541&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJcEFC/btrZzniii2W/yNK5j5XJI9KkB29H2B5kM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJcEFC/btrZzniii2W/yNK5j5XJI9KkB29H2B5kM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJcEFC/btrZzniii2W/yNK5j5XJI9KkB29H2B5kM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJcEFC%2FbtrZzniii2W%2FyNK5j5XJI9KkB29H2B5kM1%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; alt=&quot;cors-image-cache&quot; loading=&quot;lazy&quot; width=&quot;760&quot; height=&quot;541&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;541&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;이번에는 자바스크립트 fetch 요청을 3초 뒤에 하라고 &lt;s&gt;setTimeout()&lt;/s&gt; 메서드를 통해 설정해보자. 이렇게 하는 이유는 이미지 태그로 요청한 sample.png 이미지가 &lt;b&gt;브라우저 로컬 캐시에 저장&lt;/b&gt;하도록 시간을 주기 위해서 이다. 그러면 3초 뒤에 똑같은 이미지 URL을 요청할 경우 브라우저는 자동으로 최적화를 위해 서버에 요청을 때리는 것이 아닌 캐시 저장소에서 리소스를 가져오게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1676537390882&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;img src=&quot;https://tistory4.daumcdn.net/tistory/5927418/skin/images/sample.png&quot; alt=&quot;이미지&quot;&amp;gt;

&amp;lt;script&amp;gt;
    setTimeout(() =&amp;gt; {
        fetch('https://tistory4.daumcdn.net/tistory/5927418/skin/images/sample.png', { mode: 'cors' })
            .then(response =&amp;gt; response.blob())
            .then(imgBlob =&amp;gt; {
                const imageObjectURL = URL.createObjectURL(imgBlob);
                const img = document.createElement('img');
                img.src = imageObjectURL;
                document.body.append(img);
            })
    }, 3000);
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;82&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LURT8/btrZz07Sk5Q/K2M9u3tBhKlgkBRUZHLbK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LURT8/btrZz07Sk5Q/K2M9u3tBhKlgkBRUZHLbK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LURT8/btrZz07Sk5Q/K2M9u3tBhKlgkBRUZHLbK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLURT8%2FbtrZz07Sk5Q%2FK2M9u3tBhKlgkBRUZHLbK1%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; alt=&quot;cors-image-cache&quot; loading=&quot;lazy&quot; width=&quot;673&quot; height=&quot;82&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;82&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1039&quot; data-origin-height=&quot;377&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnRbRj/btrZB3JiFdl/xWZnEsTHG2qAthluLfJVR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnRbRj/btrZB3JiFdl/xWZnEsTHG2qAthluLfJVR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnRbRj/btrZB3JiFdl/xWZnEsTHG2qAthluLfJVR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnRbRj%2FbtrZB3JiFdl%2FxWZnEsTHG2qAthluLfJVR1%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; alt=&quot;cors-image-cache&quot; loading=&quot;lazy&quot; width=&quot;1039&quot; height=&quot;377&quot; data-origin-width=&quot;1039&quot; data-origin-height=&quot;377&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 결과는 CORS 에러가 뜬다. 분명 서버에서 모든 출처를 허용하는 &lt;s&gt;access-control-allow-origin: *&lt;/s&gt; 헤더를 설정하였고, &lt;s&gt;setTimeout()&lt;/s&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;&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;s&gt;&amp;lt;img&amp;gt;&lt;/s&gt; 태그로 불러오는 인라인 리소스 요청일 경우엔 요청 헤더에는 Origin 헤더가 없다. 따라서 서버에서도 Access-Control-Allow-Origin 헤더를 실어 보내지 않게되고, 이 상태 그대로 캐시에 적재되게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;964&quot; data-origin-height=&quot;692&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Frtqi/btrZuH2woub/WSaPlHuzgxGohTn1zlf1JK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Frtqi/btrZuH2woub/WSaPlHuzgxGohTn1zlf1JK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Frtqi/btrZuH2woub/WSaPlHuzgxGohTn1zlf1JK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFrtqi%2FbtrZuH2woub%2FWSaPlHuzgxGohTn1zlf1JK%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; alt=&quot;cors-image-cache&quot; loading=&quot;lazy&quot; width=&quot;964&quot; height=&quot;692&quot; data-origin-width=&quot;964&quot; data-origin-height=&quot;692&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 3초 뒤에 똑같은 리소스 URL을 요청했을때 캐시 저장소에 있는 리소스를 가져오는데, 응답 헤더에 &lt;s&gt;Access-Control-Allow-Origin&lt;/s&gt; 헤더가 없기 때문에 아래와 같이 &lt;span style=&quot;color: #ee2323;&quot;&gt;No '&amp;nbsp;Access-Control-Allow-Origin' header&lt;/span&gt; 라고 에러 메세지를 내뿜는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;82&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LURT8/btrZz07Sk5Q/K2M9u3tBhKlgkBRUZHLbK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LURT8/btrZz07Sk5Q/K2M9u3tBhKlgkBRUZHLbK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LURT8/btrZz07Sk5Q/K2M9u3tBhKlgkBRUZHLbK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLURT8%2FbtrZz07Sk5Q%2FK2M9u3tBhKlgkBRUZHLbK1%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; alt=&quot;cors-image-cache&quot; loading=&quot;lazy&quot; width=&quot;673&quot; height=&quot;82&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;82&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;815&quot; data-origin-height=&quot;579&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5RC2q/btrZxe0aHMk/wzZVT5o5GEbSnVeelKNO00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5RC2q/btrZxe0aHMk/wzZVT5o5GEbSnVeelKNO00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5RC2q/btrZxe0aHMk/wzZVT5o5GEbSnVeelKNO00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5RC2q%2FbtrZxe0aHMk%2FwzZVT5o5GEbSnVeelKNO00%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; alt=&quot;cors-image-cache&quot; loading=&quot;lazy&quot; width=&quot;815&quot; height=&quot;579&quot; data-origin-width=&quot;815&quot; data-origin-height=&quot;579&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;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;캐시로 인한 CORS 에러 해결방법&lt;/b&gt;&lt;/h2&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;crossorigin 속성 할당하여 강제 CORS 검사 행하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결 방법은 아래와 같이 &lt;s&gt;&amp;lt;img&amp;gt;&lt;/s&gt; 태그의 속성으로 &lt;span style=&quot;color: #ee2323;&quot;&gt;crossorigin&lt;/span&gt; 을 할당해 주면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1676538825809&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;img crossorigin src=&quot;https://tistory4.daumcdn.net/tistory/5927418/skin/images/sample.png&quot; alt=&quot;이미지&quot;&amp;gt;

&amp;lt;script&amp;gt;
    setTimeout(() =&amp;gt; {
        fetch('https://tistory4.daumcdn.net/tistory/5927418/skin/images/sample.png', { mode: 'cors' })
            .then(response =&amp;gt; response.blob())
            .then(imgBlob =&amp;gt; {
                const imageObjectURL = URL.createObjectURL(imgBlob);
                const img = document.createElement('img');
                img.src = imageObjectURL;
                document.body.append(img);
            })
    }, 3000);
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&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;crossorigin 속성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;&amp;lt;audio&amp;gt;&lt;/s&gt; , &lt;s&gt;&amp;lt;img&amp;gt;&lt;/s&gt; , &lt;s&gt;&amp;lt;link&amp;gt;&lt;/s&gt; , &lt;s&gt;&amp;lt;script&amp;gt;&lt;/s&gt; 및 &lt;s&gt;&amp;lt;video&amp;gt;&lt;/s&gt; 와 같은 미디어 요소 의 crossorigin 속성은 요소가 교차 출처 요청을 처리하는 방법을 정의하는 속성이다. 보통은 클라이언트에서 쿠키나 인증 헤더를 실어 서버에 보낼때 사용되는 속성이지만, 이것을 태그 속성으로 명시하면 인라인 리소스 요청이든 뭐든 강제적으로 CORS 정책을 따르게 하여 리소스에 대한 엑세스 권한을 요청하게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자 도구의 헤더 목록을 보면 더 감이 잡힐 것이다. 비록 인라인 리소스 요청이지만 요청 헤더에 Origin 헤더가 담긴걸 볼 수 있고 서버도 이에 반응하여 cors 헤더를 응답함을 볼 수 가 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;903&quot; data-origin-height=&quot;630&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5YRa8/btrZzMaUma2/eRu1gMd4jWzfMGyyV27yOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5YRa8/btrZzMaUma2/eRu1gMd4jWzfMGyyV27yOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5YRa8/btrZzMaUma2/eRu1gMd4jWzfMGyyV27yOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5YRa8%2FbtrZzMaUma2%2FeRu1gMd4jWzfMGyyV27yOK%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; alt=&quot;cors-image-cache&quot; loading=&quot;lazy&quot; width=&quot;903&quot; height=&quot;630&quot; data-origin-width=&quot;903&quot; data-origin-height=&quot;630&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 3초 뒤에 행해지만 자바스크립트 Ajax 요청 역시 캐시에 리소스를 가져오더라도, 이미 캐시 저장소에 있는 리소스는 &lt;s&gt;Access-Control-Allow-Origin&lt;/s&gt; 헤더가 설정된 리소스 이기 때문에 CORS 관문 검사에 통과되는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;877&quot; data-origin-height=&quot;614&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/I24JB/btrZAG2b2Z1/dKdjo7WkHxdXKo8omweBs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/I24JB/btrZAG2b2Z1/dKdjo7WkHxdXKo8omweBs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/I24JB/btrZAG2b2Z1/dKdjo7WkHxdXKo8omweBs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FI24JB%2FbtrZAG2b2Z1%2FdKdjo7WkHxdXKo8omweBs1%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; alt=&quot;cors-image-cache&quot; loading=&quot;lazy&quot; width=&quot;877&quot; height=&quot;614&quot; data-origin-width=&quot;877&quot; data-origin-height=&quot;614&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;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;crossorigin 속성이 있는데 서버 CORS 허용이 안될 경우&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 만일 서버에서 별다른 cors 헤더 설정을 하지 않았을 경우 &lt;s&gt;&amp;lt;img crossorigin src&amp;gt;&lt;/s&gt; 인라인 요청도 결국 cors 에러 로그가 나타나게 된다. 왜냐하면 crossorigin 속성은 강제로 CORS 검사 관문을 행하기 때문이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;910&quot; data-origin-height=&quot;645&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/28koZ/btrZz5afURR/0sV3hcwBOiz51uYVUggk3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/28koZ/btrZz5afURR/0sV3hcwBOiz51uYVUggk3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/28koZ/btrZz5afURR/0sV3hcwBOiz51uYVUggk3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F28koZ%2FbtrZz5afURR%2F0sV3hcwBOiz51uYVUggk3K%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; alt=&quot;cors-image-cache&quot; loading=&quot;lazy&quot; width=&quot;910&quot; height=&quot;645&quot; data-origin-width=&quot;910&quot; data-origin-height=&quot;645&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;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하자면, 서버에 Origin 허용이 된 상태에서 동일한 리소스에 대한 중복 요청을 cors를 통해 가져올 경우 위의 엇갈림을 조심하면 된다. 또한 브라우저 로컬 캐시 뿐만 아니라 이밖에도 프록시(Proxy)나 CloudFront와 같은 CDN 서비스를 이용할때도 CORS 캐시 문제가 따라오게 된다. 보통 클라우드의 CDN 서비스일 경우 이러한 경우를 대비해 별도로 Cache Key 설정을 제공하니 이에 대해서 검색해보길 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발 지식/WEB 지식</category>
      <category>cors cache</category>
      <category>cors 됬다 안됬다</category>
      <category>CORS 에러</category>
      <category>cors 캐시</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1086</guid>
      <comments>https://inpa.tistory.com/entry/%F0%9F%8C%90-CORS-%EC%BA%90%EC%8B%9C-%EC%97%90%EB%9F%AC#entry1086comment</comments>
      <pubDate>Mon, 6 Mar 2023 09:37:26 +0900</pubDate>
    </item>
    <item>
      <title>  마우스 wheel &amp;amp; scroll 이벤트 멈춤 감지하기</title>
      <link>https://inpa.tistory.com/entry/%F0%9F%9A%80-wheel-scroll-%EB%A9%88%EC%B6%A4-%EA%B0%90%EC%A7%80</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img_20(88).webp&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;630&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qU892/btrYphIPSwS/biKGv3ydsqkREWDuS8xeI0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qU892/btrYphIPSwS/biKGv3ydsqkREWDuS8xeI0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qU892/btrYphIPSwS/biKGv3ydsqkREWDuS8xeI0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqU892%2FbtrYphIPSwS%2FbiKGv3ydsqkREWDuS8xeI0%2Fimg.webp&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; alt=&quot;wheel-scroll-stop&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;630&quot; data-filename=&quot;img_20(88).webp&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;630&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;마우스 휠 &amp;amp; 스크롤 멈춤 감지 방법&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 문서를 스크롤 하면 이벤트를 발생시키는데, &lt;u&gt;&lt;b&gt;스크롤을 멈추면 이를 감지&lt;/b&gt;&lt;/u&gt;하여 스크립트 처리를 하고 싶은 상황이 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마우스 스크롤을 감지하는 이벤트는 두가지가 있는데, 문서를 스크롤하면 발생하는 'scroll' 이벤트와 그냥 마우스 가운데 휠 버튼을 내리면 발생하는 'wheel' 이벤트가 있다. 이&amp;nbsp;이벤트들에 &lt;s&gt;setTimeout()&lt;/s&gt; 메서드와 조합해서 종단 지점을 감지하는 로직을 구현 할수가 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1675731958480&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let scrolling; // setTimeout() 메서드를 할당하는 전역 변수

window.addEventListener('scroll', (e) =&amp;gt; {
    if (!scrolling) {
    	console.log('start scrolling!');
    }
    
    // 일정시간(250ms) 뒤에 스크롤 동작 멈춤을 감지
    clearTimeout(scrolling);
    scrolling = setTimeout(() =&amp;gt; {
        console.log('stop scrolling!');
        
        scrolling = undefined;
    }, 250);
})&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1675732980860&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let wheeling; // setTimeout() 메서드를 할당하는 전역 변수

window.addEventListener('wheel', (e) =&amp;gt; {
    if (!wheeling) {
    	console.log('start wheeling!');
    }

    // 일정시간(400ms) 뒤에 스크롤 동작 멈춤을 감지
    clearTimeout(wheeling);
    wheeling = setTimeout(() =&amp;gt; {
        console.log('stop wheeling!');
        
        wheeling = undefined;
    }, 400);
})&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;'mousewheel'은 deprecated 된 구형 이벤트로서 최신 이벤트 스펙인 'wheel' 사용을 권장한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Snippet/JS 스니펫</category>
      <category>scroll</category>
      <category>스크롤</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1079</guid>
      <comments>https://inpa.tistory.com/entry/%F0%9F%9A%80-wheel-scroll-%EB%A9%88%EC%B6%A4-%EA%B0%90%EC%A7%80#entry1079comment</comments>
      <pubDate>Sun, 5 Mar 2023 13:27:33 +0900</pubDate>
    </item>
    <item>
      <title>  말풍선 디자인 자동 만들기 사이트</title>
      <link>https://inpa.tistory.com/entry/%F0%9F%8E%A8-%EB%A7%90%ED%92%8D%EC%84%A0-%EB%94%94%EC%9E%90%EC%9D%B8</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;8109d9612f6acd06b78c9e0182e19dae-blue-speech-bubble.webp&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;1082&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bi6pxc/btr1lBq4ohq/S6FUgDNyyBzksBKnCTQkkK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bi6pxc/btr1lBq4ohq/S6FUgDNyyBzksBKnCTQkkK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bi6pxc/btr1lBq4ohq/S6FUgDNyyBzksBKnCTQkkK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbi6pxc%2Fbtr1lBq4ohq%2FS6FUgDNyyBzksBKnCTQkkK%2Fimg.webp&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; alt=&quot;speech-bubble&quot; loading=&quot;lazy&quot; width=&quot;1500&quot; height=&quot;1082&quot; data-filename=&quot;8109d9612f6acd06b78c9e0182e19dae-blue-speech-bubble.webp&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;1082&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;말풍선 꼬리표 위치(Side) 와 모양(&lt;span&gt;Pointer&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;triangle) 과 크기(Pointer size)를 지정해주고 옆의 css 코드를 붙여넣으면 완성된다.&lt;/p&gt;
&lt;pre id=&quot;code_1677564444990&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div class=&quot;speech-bubble&quot;&amp;gt;
	&amp;lt;h1&amp;gt;Bubbly&amp;lt;/h1&amp;gt;
	&amp;lt;h2&amp;gt;CSS speech bubbles made easy!&amp;lt;/h2&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;iframe src=&quot;https://projects.verou.me/bubbly/&quot; width=&quot;2000&quot; height=&quot;750&quot;&gt;&lt;/iframe&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1677564416120&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;Bubbly &amp;mdash; CSS speech bubbles made easy&quot; data-og-description=&quot;&quot; data-og-host=&quot;projects.verou.me&quot; data-og-source-url=&quot;https://projects.verou.me/bubbly/&quot; data-og-url=&quot;https://projects.verou.me/bubbly/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://projects.verou.me/bubbly/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://projects.verou.me/bubbly/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Bubbly &amp;mdash; CSS speech bubbles made easy&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;projects.verou.me&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>Style Sheet/스타일 요소 모음</category>
      <author>인파_</author>
      <guid isPermaLink="true">https://inpa.tistory.com/1092</guid>
      <comments>https://inpa.tistory.com/entry/%F0%9F%8E%A8-%EB%A7%90%ED%92%8D%EC%84%A0-%EB%94%94%EC%9E%90%EC%9D%B8#entry1092comment</comments>
      <pubDate>Sat, 4 Mar 2023 16:12:43 +0900</pubDate>
    </item>
  </channel>
</rss>