<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>대체로 맑음</title>
    <link>https://29223.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Thu, 11 Jun 2026 10:24:15 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>단진</managingEditor>
    <image>
      <title>대체로 맑음</title>
      <url>https://tistory1.daumcdn.net/tistory/5009202/attach/a54d3d8309374951a0886485bb24a16b</url>
      <link>https://29223.tistory.com</link>
    </image>
    <item>
      <title>[2025년] 2025년 회고</title>
      <link>https://29223.tistory.com/180</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2025년.png&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oDdJB/dJMcahXFuGj/heONpKbNyPEvN0nWKTIaq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oDdJB/dJMcahXFuGj/heONpKbNyPEvN0nWKTIaq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oDdJB/dJMcahXFuGj/heONpKbNyPEvN0nWKTIaq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoDdJB%2FdJMcahXFuGj%2FheONpKbNyPEvN0nWKTIaq1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1100&quot; height=&quot;600&quot; data-filename=&quot;2025년.png&quot; data-origin-width=&quot;1100&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;약간 늦은 2025년 회고다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2025년에는 교환학생, 취업, 이직까지 모두 경험한 한 해가 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;교환학생&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2025년 상반기에 약 5개월 동안 미국으로 교환학생을 다녀오게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;갈수록 취업은 어려워지고 있는 상황에 교환학생을 가는 것이 좋은 선택일지 계속해서 고민했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다고 준비를 대충하지는 않았고 제출해야 하는 서류나 인터뷰 등에서는 최선을 다하기는 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과 교환학생에 선발되어 미국으로 다녀올 수 있게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미국에서의 삶은 즐거웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 수강 과목들은 이미 한국에서 들었던 과목들이라 새로운 내용은 많지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 내 목표는 두 가지 였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(수업의 내용은 이미 대부분 수강했기 때문에) 영어에 익숙해지기 + 취업 전 마지막 휴가 즐기기(?)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 목표 모두 제대로 달성한 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언어와 문화가 낯선 곳에 처음 갔던 만큼 처음에는 힘들기도 했으나 사람들은 매우 친절했고 다양한 인종의 사람들과 함께 어울릴 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 학교 앞에 있던 로컬 바가 기억에 남는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동네 주민들을 포함하여 학교 학생들도 자주 방문하던 바였고 전형적인 미국 영화에서 볼 수 있을 것 같은 바 중에 하나였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그곳에 앉아 있으면 종종 지역 주민으로 보이는 분들이 말을 거는 경우가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 분들과 대화하며 정말 다양한 주제로 대화해볼 수 있었고 이 과정에서 미국인들의 (모든 미국인을 대변하지는 않겠지만) 사고 방식과 자신 또는 타인을 대하는 태도 등에 대해 알 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학기가 끝난 뒤 미국 서부를 여행하며 그랜드 캐니언과 같이 뛰어난 자연 경관도 볼 수 있었고 실리콘 밸리를 방문해서 구글, 애플의 본사도 구경할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 기회로 구글 내부에 있는 식당에서 밥을 먹을 수 있었는데 내부에 정말 다양한 인종의 사람들이 어울려 밥을 먹고 다양한 이야기를 하고 있었다. (음식도 맛있었다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나도 언젠가는 뛰어난 개발자가 되어(?) 이 곳에 속해보고 싶다는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;취업&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(회사 내부에서 진행했던 프로젝트에 관한 자세한 내용을 최대한 기술하지 않으려 함)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 미국에 가기 전에 이미 취업이 결정되어 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에 6월부터 출근하기 시작했으며 원래 다니던 회사였기 때문에 업무 적응에는 오랜 시간이 필요하지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 업무들을 했으나 가장 기억에 남는 것은 AI를 활용해 회사에서 다양한 제품을 만드는데 활용할 수 있는 기반 기술을 만드는 스쿼드의 업무였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우연한 기회로 이 스쿼드에 합류하게 되었으며 기존에 없던 개념을 새롭게 만들고 고도화 하는 과정이 어렵기도 했으나 그 과정은 재미있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어찌 보면 2024년에 인턴 재직 당시 했던 프로젝트의 연장선인 것 같아 보이기도 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 나는 규칙 기반으로 문제를 해결하려 했으나 규칙에는 한계가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람의 주관이 개입되는 디자인의 특성상 다양한 예외들이 있었으며 이 모든 것을 규칙으로 정의할 수는 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이번 스쿼드에서는 이 문제를 규칙으로 해결하려 시도하는 대신, 새로운 자료구조를 정의하고 LLM을 통해 새롭게 정의한 자료구조로 변환할 수 있도록 구성했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 자료구조를 만들게 된 후 검색, 대치 등 다양한 목적으로 응용이 가능하며 우리의 다음 목표는 이를 고도화 하는 것이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 사용하던 방식에서는 정확도가 아쉬웠으나 이를 해결하기 위해 LLM을 한 번 호출하지 않고 여러 번 호출하는 것으로 이 문제를 해결했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 정확도를 검사하기 위해서는 사람이 직접 육안으로 검사를 해야 하는 한계가 있었으나 팀원들 모두 열정적으로 참여해 만족스러운 결과를 얻어낼 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말 바빴을 때는 야근도 하고 주말에도 근무를 하며 목표를 달성하고자 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 몸은 힘들었을지라도 과정 자체는 즐거웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점점 결과가 좋아지는 것이 보이고 팀원들과 함께 저녁도 먹으면서 작업하는 것이 즐거웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 프로젝트에 참여하면서 Cursor, Claude Code, ChatGPT 등과 같이 단순하게 AI를 하나의 제품이나 보조도구로 사용하는 방식을 넘어 이를 어떻게 응용해서 서비스에 녹여낼 수 있는지 배울 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인공지능 활용 능력이 갈수록 중요해진다고 하는데 이렇게 깊은 수준으로 AI를 다뤄본 경험이 앞으로도 내게 많은 도움이 될 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이직&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2025년의 막바지 쯤에 (최종 결과는 1월 첫 주에 나오기는 했다) 이직이 확정되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추석 전에 서류를 제출했으니 최종 발표까지 제법 많은 시간이 걸렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 합류하게 된 곳은 버킷플레이스(오늘의집)다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 곳에서 커머스 팀의 프론트엔드 엔지니어로 업무를 하게 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘의집이라는 서비스는 기존에도 알고 있었으며 이사를 할 때마다 한 번씩은 들어가서 보게 되는 서비스였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한가지 특이했던 것은 커머스가 활성화 된 이후 커뮤니티가 생기지 않고 오히려 커뮤니티 기반으로 출발한 뒤 커머스를 도입했다는 점이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분이 다른 커머스 플랫폼 들과는 차별점이라는 생각이 들었고 충성 고객이 많다는 점 또한 매력적이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(실제로 주변 지인들도 가구는 오늘의집에서 구매하는 경우가 많다고 한다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때마침 채용 공고가 나왔고 좋은 결과가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전 직장에 대해 특별한 불만을 갖거나 하지 않았기 때문에 시원섭섭한 기분도 든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 더 큰 조직과 유저 수, 다른 도메인에 대한 도전 등이 계속해서 마음속에 남아있었고 새로운 경험을 통해 한 층 더 성장할 수 있을 것이라는 기대감에 이직을 결정하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;앞으로&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 오늘의집에 출근한지 일주일이 되었기 때문에 구체적으로 어떤 업무를 수행할지, 업무 방식은 어떻게 되는지, 조직은 어떻게 굴러가는지 등에 대한 세부적인 파악이 덜 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(일주일 밖에 안되었지만 동료들이 정말 친절하고 열정적이라는 것은 느낄 수 있었다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입사 후 리더분과 1on1을 하며 내가 갖고 있는 목표에 대해 서로 이야기 하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이곳에서 이루고 싶은 목표가 있냐는 질문에 &quot;커머스 팀에 왔으니 당연히 매출쪽에서 임팩트를 내고 싶다. 그리고 커머스가 아니더라도 회사 전체에 어떤 방면으로던지 임팩트를 낼 수 있는 사람이 되고 싶다. 어떤 중요한 일이 있을 때 ~한테 시키면 되는거 아니냐는 말이 나올 수 있도록 하고 싶다&quot;라고 답변드렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 사람이 될 수 있도록, 믿고 맡길 수 있는 사람이라는 것을 보여주기 위해서는 우선 주어진 업무를 처리하며 신뢰도를 쌓아야 할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영어 이름으로 불리는 것이 아직은 어색하기도 하고 (나를 불러도 내가 못 알아듣는 경우도 있다) 새로운 것에 점차 익숙해지고 이 조직에 스며들기 위해 정신이 없을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아마 이번 상반기 목표는 수습 기간을 잘 넘기고 회사에 잘 적응하면서 보여줄 수 있는 최선을 보여주는 것이 될 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 전 직장에서 같은 스쿼드에 있던 분들과 사이드 프로젝트를 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI를 활용해서 다양한 시도를 하고 있으며 어느정도 윤곽이 잡힌 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;약 6개월 정도 함께 업무를 수행했던 사람들이라 그런지 별다른 얘기가 없어도 알아서 일이 잘 진행되는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기술적 도전의 경우 이미 경험했기 때문에 이 사람들이 얼마나 창의적으로 생각하는지, 그리고 그것이 당연히 구현 가능하다는 것을 알고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이 프로젝트가 성공적으로 마무리 되는 것 또한 이번 상반기의 목표로 삼고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 역시 중요한 것은 설계 능력과 설계를 따라가는 구현이라는 것을 다시 한 번 깨닫고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 등장하고 코드를 작성하는 시간은 점점 줄어들지만 사람이 이해하는 코드의 양도 그 만큼 줄어드는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 아직 클로드 코드를 써본적이 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cursor만 사용해왔으며 업무를 작은 단위로 나누고 어떤 것을 어떻게 구현해야 할 지는 내가 스스로 생각하고자 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI를 도구로 보고 내가 생각한 구현 방법을 단순히 코드로 옮기는 데만 사용하고자 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과 코드 수준에서는 내부가 어떻게 되어있는지 정확히는 몰라도 최소한 그 함수가 어떤 역할을 하고 전체 프로세스가 어떻게 흘러가는지, 문제가 발생했을 때 어느 지점을 봐야 하는지 등은 내가 이해하고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 상반기에 위의 작업 방식이 유효한지 스스로 검증 해봐야 할 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;생각을 AI에게 의존하지 말자&quot;는 것이 이번 상반기에 갖고 있을 마음가짐이 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/회고</category>
      <author>단진</author>
      <guid isPermaLink="true">https://29223.tistory.com/180</guid>
      <comments>https://29223.tistory.com/180#entry180comment</comments>
      <pubDate>Mon, 16 Feb 2026 00:19:24 +0900</pubDate>
    </item>
    <item>
      <title>브라우저 탭 사이의 통신하기 (postMessage)</title>
      <link>https://29223.tistory.com/179</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;JS.png&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpxXmM/btsQc5qNptr/LmMN1AIvHKRymZPfSesB71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpxXmM/btsQc5qNptr/LmMN1AIvHKRymZPfSesB71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpxXmM/btsQc5qNptr/LmMN1AIvHKRymZPfSesB71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpxXmM%2FbtsQc5qNptr%2FLmMN1AIvHKRymZPfSesB71%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1100&quot; height=&quot;600&quot; data-filename=&quot;JS.png&quot; data-origin-width=&quot;1100&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저에는 탭이라는 개념이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 탭에 서로 다른 페이지를 띄울 수 있으며 탭 사이에는 어떠한 연관성이 없어 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저 탭 사이에 데이터를 주고 받는게 가능할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;브라우저 탭&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저에 있는 탭은 어떤 특징을 갖고 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 브라우저에 있을 뿐 실제로 별개로 동작하는 것일까?&lt;/p&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://developer.mozilla.org/ko/docs/Glossary/Browsing_context&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.mozilla.org/ko/docs/Glossary/Browsing_context&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1756644226362&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;브라우징 맥락&quot; data-og-description=&quot;&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/ko/docs/Glossary/Browsing_context&quot; data-og-url=&quot;https://developer.mozilla.org/ko/docs/Glossary/Browsing_context&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Glossary/Browsing_context&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/ko/docs/Glossary/Browsing_context&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;브라우징 맥락&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;developer.mozilla.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mdn의 문서에 따르면 다음과 같은 내용이 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;브라우징&amp;nbsp;맥락은&amp;nbsp;브라우저가&amp;nbsp;Document를&amp;nbsp;표시하는&amp;nbsp;환경을&amp;nbsp;말합니다.&amp;nbsp;오늘날에는&amp;nbsp;보통&amp;nbsp;탭이지만,&amp;nbsp;브라우저&amp;nbsp;창이나&amp;nbsp;페이지&amp;nbsp;내의&amp;nbsp;프레임도&amp;nbsp;가능합니다.&lt;br /&gt;&lt;br /&gt;각&amp;nbsp;브라우징&amp;nbsp;맥락은&amp;nbsp;특정&amp;nbsp;출처,&amp;nbsp;활성화된&amp;nbsp;문서의&amp;nbsp;출처,&amp;nbsp;그리고&amp;nbsp;표시했던&amp;nbsp;모든&amp;nbsp;문서의&amp;nbsp;방문&amp;nbsp;기록을&amp;nbsp;가집니다.&lt;br /&gt;&lt;br /&gt;브라우징&amp;nbsp;맥락&amp;nbsp;간&amp;nbsp;통신은&amp;nbsp;엄격히&amp;nbsp;제한됩니다.&amp;nbsp;같은&amp;nbsp;출처를&amp;nbsp;가진&amp;nbsp;브라우징&amp;nbsp;맥락끼리는&amp;nbsp;BroadcastChannel을&amp;nbsp;열어&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/span&gt;&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;mdn에 따르면 각 탭은 브라우징 맥락(Browsing context)을 갖는 것으로 이해할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 이 브라우징 맥락 사이의 통신은 엄격히 제한되며 같은 출처를 가진 브라우징 맥락인 경우만 BroadcastChannel을 사용해 통신할 수 있다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저의 탭은 최상위 브라우징 컨텍스트로 각 탭은 자체 Document, Window, History, FrameTree, 자바스크립트 글로벌 객체를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;격리 환경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 각 탭은 각자가 필요한 것을 스스로 갖고 있는 완전히 격리된 것으로 보인다.&lt;/p&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;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;여러&amp;nbsp;웹사이트의&amp;nbsp;페이지가&amp;nbsp;항상&amp;nbsp;서로&amp;nbsp;다른&amp;nbsp;프로세스에&amp;nbsp;배치되며,&amp;nbsp;각&amp;nbsp;프로세스는&amp;nbsp;프로세스에서&amp;nbsp;수행할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;작업을&amp;nbsp;제한하는&amp;nbsp;샌드박스에서&amp;nbsp;실행됩니다.&amp;nbsp;또한&amp;nbsp;프로세스가&amp;nbsp;다른&amp;nbsp;사이트에서&amp;nbsp;특정&amp;nbsp;유형의&amp;nbsp;민감한&amp;nbsp;정보를&amp;nbsp;수신하지&amp;nbsp;못하도록&amp;nbsp;차단합니다&lt;/span&gt;&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 프로세스 단위로 탭을 분리하게 되면 특정 탭이 다른 탭의 힙 메모리 등에 접근할 수 없게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 각 탭은 완전한 격리 환경을 구성할 수 있으며 서로가 서로에게 영향을 줄 수 없기 때문에 안전한 브라우저 사용이 가능한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;엔진의 격리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 브라우저 크롬은 V8 엔진을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;V8엔진에 따르면 엔진또한 VM 인스턴스로 격리되며 각각의 인스턴스는 개별 힙을 갖고 있다고 한다.&lt;/p&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://html.spec.whatwg.org/multipage/webappapis.html#event-loops&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://html.spec.whatwg.org/multipage/webappapis.html#event-loops&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 이 문서에 따르면 각각의 Agent(에이전트는 ECMAScript 실행 컨텍스트, 실행 컨텍스트 스택, 실행 중인 실행 컨텍스트, 에이전트 레코드 및 실행 스레드 집합으로 구성된다. 실행 스레드를 제외한 에이전트의 구성 요소는 해당 에이전트에 독점적으로 속한다)들은 각각의 이벤트 루프를 갖는다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 엔진의 격리가 의미하는 것은 GC, 마이크로태스크 큐, 이벤트 루프가 컨텍스트마다 분리되어 동작한다는 것을 의미할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇듯 각 탭은 완전히 격리 되어있기 때문에 아래와 같은 것들이 가능한 것이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브라우저의 탭이 비활성화(숨김)된 경우 타이머가 완벽하게 동작하지 않음 (CPU 및 메모리 효율성을 위함)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/blog/timer-throttling-in-chrome-88?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.chrome.com/blog/timer-throttling-in-chrome-88?hl=ko&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 브라우저의 뒤로가기/앞으로 가기를 하려 할 때 페이지를 삭제하는 것이 아니라 임시로 남겨두되 실행중이던 JS의 실행을 중지하는 것. 다시 해당 페이지에 접근할 경우 중지되었던 부분에서 재시작
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/articles/bfcache?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://web.dev/articles/bfcache?hl=ko&lt;/a&gt;&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 탭에서 JS의 실행이 중지되건, 타이머를 분당 한 번 확인하건 다른 탭에는 영향이 가지 않을 수 있는 이유다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;그렇다면 탭 사이의 통신이 가능할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론은 &quot;가능하다&quot;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 알아본 내용에 따르면 각 탭들은 완벽한 격리 환경이므로 둘 사이에 &quot;절대&quot; 영향을 주고받을 수 없을 것 처럼 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 예상과 다르게 각 탭 사이에서 데이터를 주고 받을 수 있는 방법은 여러 개가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 왜 우리는 탭 사이의 통신이 불가능 하다고 느끼는 것일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 대한 여러 이유가 있으며 대표적으로 아래와 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;암묵적 공유가 없다: 탭은 서로의 힙에 손댈 수 없다. 공유를 원하면 반드시 저장소나 메시징 같은 &amp;ldquo;명시적 채널&amp;rdquo;을 열어야 한다.&lt;/li&gt;
&lt;li&gt;보안 모델: SOP와 프로세스 격리는 우발적&amp;middot;악의적 접근을 차단한다.&lt;/li&gt;
&lt;li&gt;스케줄링/생명주기 차이: 각 탭의 이벤트 루프&amp;middot;GC&amp;middot;BFCache&amp;middot;백그라운드 스로틀링이 달라 &amp;ldquo;동시에 같은 값을 본다&amp;rdquo;는 보장이 없다.&lt;/li&gt;
&lt;li&gt;프라이버시 강화 흐름: 저장소/캐시 파티셔닝으로 &amp;ldquo;같은 사용자 프로필&amp;rdquo;이라도 공유 범위가 줄어드는 추세.&lt;/li&gt;
&lt;li&gt;세션 단위 상태: sessionStorage, 페이지 내부 상태, window.name 등은 본질적으로 탭 한정.&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;위에서 언급한 &quot;명시적 채널&quot;을 사용한다면 통신이 가능하며 이는 대표적으로 로컬 스토리지, BroadcastChannel, postMessage 등이 있다.&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;localStorage + storage 이벤트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 스토리지에 값을 넣고 해당 값이 변경되는 경우 이벤트 핸들러를 통해 새로 저장된 값을 꺼내올 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 쪽에서 로컬 스토리지에 setItem, removeItem, clear를 하게 되면 다른 쪽에서는 storage 이벤트를 수신할 수 있다. (발신 탭에는 발생하지 않음)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 동일 출처(same-origin)를 갖는 모든 곳에서 접근할 수 있으며 간단한 상태를 브로드캐스팅 하기에는 적절한 선택이 될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 문자열 데이터만 저장 가능하다는 단점이 있으며 (JSON 직렬화) 동기적 I/O API라 잦은 쓰기는 메인 스레드에 부담이 될 수 있다는 단점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;BroadcastChannel&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/BroadcastChannel&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.mozilla.org/ko/docs/Web/API/BroadcastChannel&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1756646572567&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;BroadcastChannel&quot; data-og-description=&quot;BroadcastChannel() 명명된 채널에 연결되는 객체를 생성합니다. 이 인터페이스는 부모인 EventTarget의 속성도 상속합니다. BroadcastChannel.name 읽기 전용 채널 이름 문자열을 반환합니다. 이 인터페이스는&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/API/BroadcastChannel&quot; data-og-url=&quot;https://developer.mozilla.org/ko/docs/Web/API/BroadcastChannel&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/BroadcastChannel&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/API/BroadcastChannel&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;BroadcastChannel&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;BroadcastChannel() 명명된 채널에 연결되는 객체를 생성합니다. 이 인터페이스는 부모인 EventTarget의 속성도 상속합니다. BroadcastChannel.name 읽기 전용 채널 이름 문자열을 반환합니다. 이 인터페이스는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.mozilla.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;new BroadcastChannel('name')로 참가할 수 있으며 channel.postMessage(data)로 모든 참가자에 전달할 수 있다.&lt;/p&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;middot;워커(Service/Dedicated/Shared) 간 &amp;ldquo;채널명&amp;rdquo; 단위 브로드캐스트다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞선 예시에서 볼 수 있듯이 동일 출처를 갖는 다수의 탭이 같은 채널에 참가한다면 단일 발신자가 복수의 탭에 메시지를 전달할 수 있다는 장점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 비교적 가볍다는 장점이 있으며 실시간 브로드캐스트에 적합하다는 장점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 지원되지 않는 브라우저가 있을 수 있으며 동일 출처만 참가할 수 있다는 단점(높은 보안성을 의미하므로 단점으로 보기는 어려울 수 있음)이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;postMessage&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 postMessage 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;postMessage 메소드는 window 객체에 포함되어 있기 때문에 window.postMessage와 같이 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식의 경우 참조 가능한 윈도우/프레임 간 P2P 통신(팝업 window.open, 부모-자식 iframe, opener/parent/top)이며 출처 상관없이 사용 가능하다는 장점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 동일 출처 제약이 없기 때문에 반드시 targetOrigin을 특정하고, 수신 측에서 event.origin 검증 필요하기 때문에 '*'를 남용하지 않도록 주의해야 한다는 단점이 있다.&lt;/p&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;ldquo;임의의 다른 탭&amp;rdquo;에는 직접 보낼 수 없으며(윈도우 참조가 있어야 함) 전역 브로드캐스트 용도엔 부적절하다는 단점도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식의 경우 수명 결합의 개념이 있기 때문에 팝업/iframe이 닫히면 통신이 종료된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그럼에도 불구하고&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단점이 많음에도 불구하고 postMessage를 사용하는 이유는 결국 동일 출처 조건을 만족하지 않아도 쉽게 다른 탭과 데이터를 주고 받을 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-size=&quot;size16&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;일반적으로,&amp;nbsp;다른&amp;nbsp;페이지&amp;nbsp;간의&amp;nbsp;스크립트는&amp;nbsp;각&amp;nbsp;페이지가&amp;nbsp;같은&amp;nbsp;프로토콜,&amp;nbsp;포트&amp;nbsp;번호와&amp;nbsp;호스트을&amp;nbsp;공유하고&amp;nbsp;있을&amp;nbsp;때에(&quot;동일&amp;nbsp;출처&amp;nbsp;정책&quot;으로도&amp;nbsp;불려&amp;nbsp;집니다.)&amp;nbsp;서로&amp;nbsp;접근할&amp;nbsp;수&amp;nbsp;있습니다.&amp;nbsp;window.postMessage()&amp;nbsp;는&amp;nbsp;이&amp;nbsp;제약&amp;nbsp;조건을&amp;nbsp;안전하게&amp;nbsp;우회하는&amp;nbsp;기능을&amp;nbsp;제공합니다.&lt;/span&gt;&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;mdn에 따르면 위와 postMessage 방식의 경우 동일 출처가 아니더라도 (호스트, 포트, 프로토콜 중 하나라도 다른 경우 동일 출처가 아님) 서로 메시지를 주고 받는 것이 가능하다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 postMessage 또한 탭에 대한 참조가 있어야 하며 자식 탭의 경우는 자신의 부모에게 온 메시지 라는 것을 확인하지 않으면 보안에 문제가 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제를 확인해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1756989408027&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useEffect, useRef, useState } from 'react';

function App() {
  const [childMessage, setChildMessage] = useState('');
  const childRef = useRef&amp;lt;Window | null&amp;gt;(null);

  const receiver = (event: MessageEvent&amp;lt;{ type: string; text: string }&amp;gt;) =&amp;gt; {
    if (event.data.type === 'childMessage') {
      setChildMessage(event.data.text);
    }
  };

  useEffect(() =&amp;gt; {
    window.addEventListener('message', receiver);

    return () =&amp;gt; {
      window.removeEventListener('message', receiver);
    };
  }, []);

  return (
    &amp;lt;&amp;gt;
      &amp;lt;h1&amp;gt;This is Mother&amp;lt;/h1&amp;gt;
      &amp;lt;button
        onClick={() =&amp;gt; {
          const child = window.open('http://localhost:3001');
          childRef.current = child;
        }}
      &amp;gt;
        call child
      &amp;lt;/button&amp;gt;
      &amp;lt;button
        onClick={() =&amp;gt; {
          childRef.current?.postMessage({ type: 'motherMessage', text: 'Hello from Mother' }, '*');
        }}
      &amp;gt;
        send message to child
      &amp;lt;/button&amp;gt;
      &amp;lt;p&amp;gt;{childMessage}&amp;lt;/p&amp;gt;
    &amp;lt;/&amp;gt;
  );
}

export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 구성된 간단한 부모 페이지(포트 3000)가 있다고 가정해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;window.open의 경우 생성된 window의 참조를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 참조를 사용해서 대상 윈도우에 메시지를 보낼 수 있는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1756989491384&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useEffect, useState } from 'react';

function App() {
  const [motherMessage, setMotherMessage] = useState('');

  const receiver = (event: MessageEvent&amp;lt;{ type: string; text: string }&amp;gt;) =&amp;gt; {
    if (event.data.type === 'motherMessage') {
      setMotherMessage(event.data.text);
    }
  };

  useEffect(() =&amp;gt; {
    window.addEventListener('message', receiver);

    return () =&amp;gt; {
      window.removeEventListener('message', receiver);
    };
  }, []);

  return (
    &amp;lt;&amp;gt;
      &amp;lt;h1&amp;gt;This is child&amp;lt;/h1&amp;gt;
      &amp;lt;p&amp;gt;{motherMessage}&amp;lt;/p&amp;gt;
      &amp;lt;button
        onClick={() =&amp;gt; {
          window.opener.postMessage({ type: 'childMessage', text: 'Hello from Child' });
        }}
      &amp;gt;
        call mother
      &amp;lt;/button&amp;gt;
    &amp;lt;/&amp;gt;
  );
}

export default App;&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;위와 같이 구성된 자식 탭이 있을 때 (포트 3001) 이 탭에서는 window.opener를 통해 자신을 open한 window에 접근할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 탭은 포트가 서로 다르기 때문에 동일 출처 정책을 위반한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3248&quot; data-origin-height=&quot;2112&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4aPYq/btsQl6Wj2BE/GjjN4EF8dcXTH8QNP0SkHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4aPYq/btsQl6Wj2BE/GjjN4EF8dcXTH8QNP0SkHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4aPYq/btsQl6Wj2BE/GjjN4EF8dcXTH8QNP0SkHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4aPYq%2FbtsQl6Wj2BE%2FGjjN4EF8dcXTH8QNP0SkHk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3248&quot; height=&quot;2112&quot; data-origin-width=&quot;3248&quot; data-origin-height=&quot;2112&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부모 탭에서 call child 버튼을 클릭하면 새로운 탭이 열리게 된다. (3001 포트)&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;3248&quot; data-origin-height=&quot;2112&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Kszcu/btsQkYdIQ5F/C9k5pYwby1TioqrkgJpfx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Kszcu/btsQkYdIQ5F/C9k5pYwby1TioqrkgJpfx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Kszcu/btsQkYdIQ5F/C9k5pYwby1TioqrkgJpfx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKszcu%2FbtsQkYdIQ5F%2FC9k5pYwby1TioqrkgJpfx1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3248&quot; height=&quot;2112&quot; data-origin-width=&quot;3248&quot; data-origin-height=&quot;2112&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 3001 포트인 자식 페이지가 열리게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 부모 탭에서 send message to child 버튼을 클릭하면 자신의 자식에게 메시지를 보낼 수 있게 된다.&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;3248&quot; data-origin-height=&quot;2112&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LqKNi/btsQklfVSEU/v4BuJcyBUA8AVjp8kKBK81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LqKNi/btsQklfVSEU/v4BuJcyBUA8AVjp8kKBK81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LqKNi/btsQklfVSEU/v4BuJcyBUA8AVjp8kKBK81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLqKNi%2FbtsQklfVSEU%2Fv4BuJcyBUA8AVjp8kKBK81%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3248&quot; height=&quot;2112&quot; data-origin-width=&quot;3248&quot; data-origin-height=&quot;2112&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;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 window.opener를 통해 부모 window에 접근할 수 있으며 window.opener.postMessage와 같이 메시지를 전송할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 자식 탭에서 call mother 버튼을 클릭해도 부모 탭에서는 아무런 변화를 볼 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;postMessage는 아래와 같이 다른 인자를 받을 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1756990025797&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;targetWindow.postMessage(message, targetOrigin, [transfer]);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 두 번째 인자에 해당하는 targetOrigin에 어느 위치로 데이터를 보낼지 명시해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서 부모 탭의 경우 postMessage의 두 번째 인자로 '*' 와일드 카드를 설정했기 때문에 문제 없이 보낼 수 있었던 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1756990186921&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;window.opener.postMessage({ type: 'childMessage', text: 'Hello from Child' }, 'http://localhost:3000');&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;자식 페이지도 이와 같이 자신의 부모 즉 자신을 open한 오리진 정보가 있어야 제대로 메시지를 보낼 수 있다.&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;3248&quot; data-origin-height=&quot;2112&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bawMza/btsQmYXzbzG/OyP9OEhKYsRjXmplnKxGkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bawMza/btsQmYXzbzG/OyP9OEhKYsRjXmplnKxGkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bawMza/btsQmYXzbzG/OyP9OEhKYsRjXmplnKxGkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbawMza%2FbtsQmYXzbzG%2FOyP9OEhKYsRjXmplnKxGkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;3248&quot; height=&quot;2112&quot; data-origin-width=&quot;3248&quot; data-origin-height=&quot;2112&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;origin을 잘 설정한다면 위와 같이 부모 탭에서 메시지를 수신할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 postMessage에서 targetWindow의 스키마, 호스트이름, 포트가 targetOrigin과 다르다면 메시지가 전송되지 않는다.&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;postMessage 안전하게 사용하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;postMessage는 동일 출처 정책을 우회할 수 있지만 최소한의 보안 조치는 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 postMessage 자체가 제공하는 보안 조치도 있지만 이벤트 핸들러 단위의 보안 조치도 적절히 요구된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MessageEvent 내부에는 origin이라는 값이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 값을 통해 이 이벤트가 어떤 origin에서 전송된 것인지 알 수 있다.&lt;/p&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_1756990633936&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const receiver = (event: MessageEvent&amp;lt;{ type: string; text: string }&amp;gt;) =&amp;gt; {
  if (event.origin !== 'http://localhost:3001') return;
  if (event.data.type === 'childMessage') {
    setChildMessage(event.data.text);
  }
};&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;이렇게 한다면 자신의 자식인 origin이 아닌 경우는 핸들러가 실행되지 않을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 자식의 이벤트 핸들러에도 자신의 부모로 부터 온 이벤트인지 확인하는 과정이 필요할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;추가 - 전송 가능한 데이터 타입&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;postMessage 예제를 보면 일반적인 객체를 그대로 보내고 있는 것 처럼 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 객체는 localStorage와 달리 직렬화가 필요 없는 것인가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정은 postMessage가 자동으로 하기 때문에 우리가 JSON.stringify()와 같은 방법으로 직접 직렬화를 해주지 않아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/Web_Workers_API/Structured_clone_algorithm&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.mozilla.org/ko/docs/Web/API/Web_Workers_API/Structured_clone_algorithm&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;structured clone algorithm이라는 기술이 적용되어 있으며 기회가 된다면 다시 다루도록 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론은 postMessage의 경우 위의 알고리즘이 적용되어 있어 데이터를 그냥 넣어도 문제가 없다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저의 탭은 완전히 격리된 환경이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 격리된 탭 사이에 데이터를 주고 받을 수 있는 방법이 여러 개 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분은 동일 출처 정책을 만족해야 하지만 postMessage의 경우는 동일 출처 정책을 우회할 수 있다.&lt;/p&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;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[참고자료]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Glossary/Browsing_context&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.mozilla.org/ko/docs/Glossary/Browsing_context&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://html.spec.whatwg.org/multipage/document-sequences.html#browsing-context&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://html.spec.whatwg.org/multipage/document-sequences.html#browsing-context&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.chrome.com/blog/site-isolation?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.chrome.com/blog/site-isolation?hl=ko&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://v8.dev/docs/embed#isolate&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://v8.dev/docs/embed#isolate&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/Window/postMessage&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.mozilla.org/ko/docs/Web/API/Window/postMessage&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/Security/Same-origin_policy&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.mozilla.org/ko/docs/Web/Security/Same-origin_policy&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/MessageEvent&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.mozilla.org/ko/docs/Web/API/MessageEvent&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/JavaScript</category>
      <author>단진</author>
      <guid isPermaLink="true">https://29223.tistory.com/179</guid>
      <comments>https://29223.tistory.com/179#entry179comment</comments>
      <pubDate>Sun, 7 Sep 2025 12:11:16 +0900</pubDate>
    </item>
    <item>
      <title>구조 분해 할당 (feat. ES6, 단점)</title>
      <link>https://29223.tistory.com/178</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;JS.png&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HtqNL/btsPM13paUY/oxmQ12TIQkwJNWYCHSc6Xk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HtqNL/btsPM13paUY/oxmQ12TIQkwJNWYCHSc6Xk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HtqNL/btsPM13paUY/oxmQ12TIQkwJNWYCHSc6Xk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHtqNL%2FbtsPM13paUY%2FoxmQ12TIQkwJNWYCHSc6Xk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1100&quot; height=&quot;600&quot; data-filename=&quot;JS.png&quot; data-origin-width=&quot;1100&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;배열에서 두 요소를 바꾸는 경우 아래와 같이 작성하곤 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1754911809029&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const arr = [1, 2, 3, 4, 5];

[arr[1], arr[2]] = [arr[2], arr[1]]

console.log(arr); // [1, 3, 2, 4, 5]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와&amp;nbsp;같이&amp;nbsp;작성할&amp;nbsp;수도&amp;nbsp;있지만&amp;nbsp;아래와&amp;nbsp;같이&amp;nbsp;작성할&amp;nbsp;수도&amp;nbsp;있다.&lt;/p&gt;
&lt;pre id=&quot;code_1754911794341&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const arr = [1, 2, 3, 4, 5];

const tmp = arr[1];
arr[1] = arr[2];
arr[2] = tmp;

console.log(arr); // [1, 3, 2, 4, 5]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;구조 분해 할당&lt;/h2&gt;
&lt;pre id=&quot;code_1754911866240&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const [state, setState] = useState();

console.log(state);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;리액트에서 많이 사용하는 useState의 사용 예제는 위와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1754911881805&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const tmp = useState();

console.log(tmp[0])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이렇게&amp;nbsp;사용하는&amp;nbsp;것과&amp;nbsp;차이가&amp;nbsp;없다.&lt;br /&gt;&lt;br /&gt;그런데 배열의 길이가 길어지게 되고 그 배열 속에서 특정 위치에 있는 값을 변수에 할당하려 한다면 많은 코드가 필요하다는 문제가 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1754911892230&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const arr = [1, 2, 3, 4, ..., 1000000];

const first = arr[0];
const second = arr[1];
...
const million = arr[999999];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;ES6에&amp;nbsp;도입된&amp;nbsp;구조분해&amp;nbsp;할당을&amp;nbsp;사용한다면&amp;nbsp;위의&amp;nbsp;코드를&amp;nbsp;아래처럼&amp;nbsp;변경할&amp;nbsp;수&amp;nbsp;있다.&lt;/p&gt;
&lt;pre id=&quot;code_1754911935002&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const arr = [1, 2, 3, 4, ..., 1000000]; 
const [first, second, ..., million] = arr;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 구조 분해 할당 구문은 배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 JavaScript 표현식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구조 분해 할당이 가능한 이유&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 문법은 다들 알고 있다.&lt;br /&gt;&lt;br /&gt;하지만&amp;nbsp;저&amp;nbsp;기능이&amp;nbsp;ES6에서&amp;nbsp;사용&amp;nbsp;가능한&amp;nbsp;이유는&amp;nbsp;무엇이며&amp;nbsp;단순히&amp;nbsp;새로운&amp;nbsp;문법이라고&amp;nbsp;생각해도&amp;nbsp;되는&amp;nbsp;것일까?&lt;br /&gt;&lt;br /&gt;결론부터&amp;nbsp;알아보면&amp;nbsp;이는&amp;nbsp;이터레이터를&amp;nbsp;활용하고&amp;nbsp;있기&amp;nbsp;때문이다.&lt;br /&gt;&lt;br /&gt;배열의&amp;nbsp;경우&amp;nbsp;배열/이터러블&amp;nbsp;구조분해에&amp;nbsp;해당한다.&amp;nbsp;(객체의&amp;nbsp;경우는&amp;nbsp;구조분해에&amp;nbsp;이터레이터를&amp;nbsp;쓰지&amp;nbsp;않는다)&lt;br /&gt;&lt;br /&gt;이터레이터를&amp;nbsp;사용하고&amp;nbsp;있기에&amp;nbsp;이터레이터가&amp;nbsp;도입된&amp;nbsp;ES6&amp;nbsp;버전&amp;nbsp;이후에서&amp;nbsp;사용&amp;nbsp;가능한&amp;nbsp;것이다.&lt;br /&gt;&lt;br /&gt;또한, 구조 분해 할당을 하기 위해서는 좌변에서 구조 분해 할당이 적용되어 있다는 것을 JS 엔진이 판단해야 하는데 이 부분이 ES6 이후에 도입되었다.&lt;br /&gt;(객체&amp;nbsp;구조&amp;nbsp;분해&amp;nbsp;할당도&amp;nbsp;ES6&amp;nbsp;이후에&amp;nbsp;지원되는&amp;nbsp;이유)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;배열의 구조 분해 할당에서 이터레이터를 활용하는 것은 아래와 같은 코드에서 확인할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1754911983962&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const [a, b] = 1;&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;실제로 위와 같은 코드를 실행한다면 에러 문구는 Uncaught TypeError: 1 is not iterable로 출력된다.&lt;/p&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;br /&gt;구조 분해 할당은 좌변에 있는 타입에 따라 요구하는 데이터가 달라진다.&lt;br /&gt;&lt;br /&gt;좌변이&amp;nbsp;배열인&amp;nbsp;경우&amp;nbsp;우변은&amp;nbsp;이터러블,&amp;nbsp;좌변이&amp;nbsp;객체인&amp;nbsp;경우&amp;nbsp;우변이&amp;nbsp;객체로&amp;nbsp;변환&amp;nbsp;가능한&amp;nbsp;값이여야&amp;nbsp;한다.&lt;br /&gt;&lt;br /&gt;이는&amp;nbsp;좌변에&amp;nbsp;올&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;것은&amp;nbsp;배열/객체&amp;nbsp;패턴이라는&amp;nbsp;의미이며&amp;nbsp;내부의&amp;nbsp;타겟은&amp;nbsp;변수,&amp;nbsp;obj.prop,&amp;nbsp;arr[i]와&amp;nbsp;같이&amp;nbsp;할당&amp;nbsp;가능한&amp;nbsp;위치가&amp;nbsp;올&amp;nbsp;수&amp;nbsp;있다는&amp;nbsp;것이다.&lt;br /&gt;&lt;br /&gt;이터러블의 예시로는 Set, 문자열, Map 등이 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1754912018426&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const [a, b, c] = 'abc'; // 가능
const [a, b, c] = new Set([1, 2, 3]); // 가능

const test = {};
[test.a, test.b] = '으악';
console.log(test.a); // '으'

const { test1, test2 } = { test1: 123, test2: 456 };
const { length } = 'abc'; // 3
const { x } = null; // TypeError
const { 0: first } = [10, 20]; // 가능. first = 10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;/h4&gt;
&lt;pre id=&quot;code_1754912034086&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const arr = [1, 2, 3];
const [test1, test2, test3] = arr;&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;위의 코드를 본다면 결과는 예상하듯이 test1에 1이 할당될 것이다.&lt;br /&gt;&lt;br /&gt;이를 풀어서 다시 써보면&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;const [test1, test2, test3] = arr;
&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;현재 렉시컬 환경에 test1, test2, test3 바인딩을 만들되 아직 초기화는 되지 않은 상태&lt;/li&gt;
&lt;li&gt;배열이므로 이터레이터 기반의 구조 분해라는 것을 판단&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;우변 평가
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;arr 평가를 통해 1번 줄에서 만든 배열의 객체 참조를 획득&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이터레이터 획득
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배열은 이터러블이므로 arr[Symbol.iterator]()를 호출하여 ArrayIterator 생성&lt;/li&gt;
&lt;li&gt;이 이터레이터는 내부적으로 인덱스 순서로 값을 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;좌변에 할당 과정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫 번째 변수 - test1
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;iter.next() = { value: 1, done: false }&lt;/li&gt;
&lt;li&gt;InitializeBinding(test1, 1) &amp;rarr; test1 = 1&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;두 번째 변수 - test2
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;iter.next() = { value: 2, done: false }&lt;/li&gt;
&lt;li&gt;InitializeBinding(test2, 2) &amp;rarr; test2 = 2&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;세 번째 변수 - test3
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;iter.next() = { value: 3, done: false }&lt;/li&gt;
&lt;li&gt;InitializeBinding(test3, 3) &amp;rarr; test3 = 3&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;네 번째
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;{ value: undefined, done: true } (요청 시)&lt;/li&gt;
&lt;/ul&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;객체 구조 분해 할당 과정&lt;/h4&gt;
&lt;pre id=&quot;code_1754912142679&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const obj = { test1: 1, test2: 2, test3: 3 };
const { test1, test2, test3 } = obj;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;const { test1, test2, test3 } = obj;
&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;현재 렉시컬 환경에 test1, test2, test3 바인딩을 만들되 아직 초기화는 되지 않은 상태&lt;/li&gt;
&lt;li&gt;객체이므로 객체 구조 분해라는 것을 판단&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;우변 평가
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;obj를 평가하여 1번줄에서 선언한 객체의 참조를 획득&lt;/li&gt;
&lt;li&gt;ToObject 수행 (null / undefined면 TypeError)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;좌변 key 처리 (좌 &amp;rarr; 우)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫 번째 key - test1
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Get(obj, &quot;test1&quot;) &amp;rarr; 1&lt;/li&gt;
&lt;li&gt;InitializeBinding(test1, 1) &amp;rarr; test1 = 1&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;두 번째 key - test2
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Get(obj, &quot;test2&quot;) &amp;rarr; 2&lt;/li&gt;
&lt;li&gt;InitializeBinding(test2, 2) &amp;rarr; test2 = 2&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;세 번째 key - test3
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Get(obj, &quot;test3&quot;) &amp;rarr; 3&lt;/li&gt;
&lt;li&gt;InitializeBinding(test3, 3) &amp;rarr; test3 = 3&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 기본값이 설정되는 경우 값이 undefined인 경우만 기본값이 할당된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;null인 경우는 기본값이 적용되지 않는다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;위 과정은 JS 엔진이 담당하고 있으며 자세한 스펙은 아래 문서에서 확인할 수 있다.&lt;br /&gt;&lt;a href=&quot;https://tc39.es/ecma262/#sec-runtime-semantics-destructuringassignmentevaluation&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://tc39.es/ecma262/#sec-runtime-semantics-destructuringassignmentevaluation&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;결론적으로&amp;nbsp;&amp;ldquo;구조분해가&amp;nbsp;적용되었으니&amp;nbsp;이터레이터를&amp;nbsp;가져와서&amp;nbsp;순차적으로&amp;nbsp;할당&amp;nbsp;하거나&amp;nbsp;프로퍼티에&amp;nbsp;접근해서&amp;nbsp;할당&amp;rdquo;이라는&amp;nbsp;판단은&amp;nbsp;JS&amp;nbsp;엔진이&amp;nbsp;한다는&amp;nbsp;것이다.&lt;br /&gt;&lt;br /&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;h3 data-ke-size=&quot;size23&quot;&gt;장점만 있는가?&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;벤치마크 결과&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;benchmark.js를 사용한 벤치마크 결과는 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1754912287328&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import Benchmark from &quot;benchmark&quot;;

let sink = 0;

function arraySuite(n) {
  const arr = Array.from({ length: n }, (_, i) =&amp;gt; i + 1);

  return new Benchmark.Suite(`array_${n}`)
    .add(&quot;indexing&quot;, function () {
      if (n === 5) {
        const a = arr[0],
          b = arr[1],
          c = arr[2],
          d = arr[3],
          e = arr[4];
        sink += a + b + c + d + e;
      } else if (n === 3) {
        const a = arr[0],
          b = arr[1],
          c = arr[2];
        sink += a + b + c;
      }
    })
    .add(&quot;destructure&quot;, function () {
      if (n === 5) {
        const [a, b, c, d, e] = arr;
        sink += a + b + c + d + e;
      } else if (n === 3) {
        const [a, b, c] = arr;
        sink += a + b + c;
      }
    })
    .on(&quot;cycle&quot;, function (event) {
      console.log(String(event.target));
    })
    .on(&quot;complete&quot;, function () {
      console.log(`array_${n} fastest &amp;rarr; ${this.filter(&quot;fastest&quot;).map(&quot;name&quot;)}`);
      console.log(&quot;sink:&quot;, sink);
      sink = 0;
    });
}

function objectSuite(n) {
  const obj = {};
  for (let i = 0; i &amp;lt; n; i++) obj[String.fromCharCode(97 + i)] = i + 1;

  return new Benchmark.Suite(`object_${n}`)
    .add(&quot;dot access&quot;, function () {
      if (n === 5) {
        const a = obj.a,
          b = obj.b,
          c = obj.c,
          d = obj.d,
          e = obj.e;
        sink += a + b + c + d + e;
      } else if (n === 3) {
        const a = obj.a,
          b = obj.b,
          c = obj.c;
        sink += a + b + c;
      }
    })
    .add(&quot;destructure&quot;, function () {
      if (n === 5) {
        const { a, b, c, d, e } = obj;
        sink += a + b + c + d + e;
      } else if (n === 3) {
        const { a, b, c } = obj;
        sink += a + b + c;
      }
    })
    .on(&quot;cycle&quot;, function (event) {
      console.log(String(event.target));
    })
    .on(&quot;complete&quot;, function () {
      console.log(
        `object_${n} fastest &amp;rarr; ${this.filter(&quot;fastest&quot;).map(&quot;name&quot;)}`
      );
      console.log(&quot;sink:&quot;, sink);
      sink = 0;
    });
}

function runAll() {
  [arraySuite(5), arraySuite(3), objectSuite(5), objectSuite(3)].forEach(
    (suite, i) =&amp;gt; {
      global.gc?.();
      suite.run({ async: false });
      if (i !== 3) console.log(&quot;---&quot;);
    }
  );
}

runAll();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1754912312153&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;indexing x 169,216,580 ops/sec &amp;plusmn;1.35% (92 runs sampled)
destructure x 115,857,537 ops/sec &amp;plusmn;0.32% (99 runs sampled)
array_5 fastest &amp;rarr; indexing
sink: 22583285190
---
indexing x 171,998,591 ops/sec &amp;plusmn;0.86% (93 runs sampled)
destructure x 132,465,584 ops/sec &amp;plusmn;1.79% (97 runs sampled)
array_3 fastest &amp;rarr; indexing
sink: 9928435962
---
dot access x 170,665,770 ops/sec &amp;plusmn;1.08% (93 runs sampled)
destructure x 169,491,900 ops/sec &amp;plusmn;1.53% (95 runs sampled)
object_5 fastest &amp;rarr; dot access
sink: 26993263200
---
dot access x 175,556,332 ops/sec &amp;plusmn;0.82% (90 runs sampled)
destructure x 169,800,189 ops/sec &amp;plusmn;0.88% (98 runs sampled)
object_3 fastest &amp;rarr; dot access
sink: 11177306760&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;(결과에서 sink의 경우 최적화 방지용으로 출력하고 있을 뿐 성능 등의 의미있는 값이 아님)&lt;/p&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;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ops/sec: 초당 수행 횟수(클수록 빠름)&lt;/li&gt;
&lt;li&gt;&amp;plusmn;%: 상대 오차(RME). 차이가 이 범위 수준이면 &amp;ldquo;유의미하지 않을 수 있음&amp;rdquo;&lt;/li&gt;
&lt;li&gt;배열
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;길이 5: indexing 169.2M vs destructure 115.9M &amp;rarr; indexing가 약 46% 빠름&lt;/li&gt;
&lt;li&gt;길이 3: indexing 172.0M vs destructure 132.5M &amp;rarr; indexing가 약 30% 빠름&lt;/li&gt;
&lt;li&gt;해석: 배열에서는 구조분해가 반복마다 패턴 바인딩/이터레이터(엔진 내부 처리) 등 오버헤드가 커서 인덱스 접근이 확실히 우세. 구조분해는 바인딩 개수가 줄면 비용도 함께 줄어드는 경향이 보임.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;객체
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;5개: dot 170.7M vs destructure 169.5M &amp;rarr; dot가 약 0.7% 빠름(&amp;plusmn;1.1~1.5% 오차 범위와 비슷해 사실상 비슷)&lt;/li&gt;
&lt;li&gt;3개: dot 175.6M vs destructure 169.8M &amp;rarr; dot가 약 3.4% 빠름(유의미하나 격차는 작음)&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;(벤치마크를 여러 번 수행하면 오차 발생 가능)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조 분해 할당의 경우 간결하게 표현할 수 있다는 장점이 있으나 좌변의 패턴 파악, 우변의 평가 등 (약간의) 성능 오버헤드가 있을 수 있으며 rest를 사용하는 경우 O(n)의 추가 메모리와 시간이 필요하기 때문에 GC에 영향을 줄 수 있다. 또한, 좌변에 따라 우변에 올 수 있는 타입이 제대로 전달되었는지 신경써야 하며 구버전에 호환되지 않는다는 문제점이 있다.&lt;/p&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;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼에도 불구하고 배열 구조분해를 사용하는 이유는 가독성이 좋으며 반복적인 코드를 제거할 수 있다는 장점이 있기 때문이다.&lt;br /&gt;&lt;br /&gt;또한&amp;nbsp;rest를&amp;nbsp;통해&amp;nbsp;나머지&amp;nbsp;값을&amp;nbsp;한&amp;nbsp;번에&amp;nbsp;가져올&amp;nbsp;수&amp;nbsp;있다는&amp;nbsp;장점이&amp;nbsp;있으며&amp;nbsp;함수의&amp;nbsp;파라미터에서&amp;nbsp;필요한&amp;nbsp;필드만&amp;nbsp;받고&amp;nbsp;기본&amp;nbsp;값까지&amp;nbsp;한&amp;nbsp;번에&amp;nbsp;선언할&amp;nbsp;수&amp;nbsp;있다는&amp;nbsp;장점이&amp;nbsp;있다.&lt;br /&gt;&lt;br /&gt;위에서&amp;nbsp;기존의&amp;nbsp;방식보다&amp;nbsp;오버헤드가&amp;nbsp;발생할&amp;nbsp;수&amp;nbsp;있다고&amp;nbsp;했으나&amp;nbsp;요즘은&amp;nbsp;엔진&amp;nbsp;차원의&amp;nbsp;최적화가&amp;nbsp;이루어지기도&amp;nbsp;하며&amp;nbsp;저&amp;nbsp;정도의&amp;nbsp;성능을&amp;nbsp;개선하는&amp;nbsp;것&amp;nbsp;보다는&amp;nbsp;다른&amp;nbsp;성능&amp;nbsp;문제를&amp;nbsp;해결하는게&amp;nbsp;먼저일&amp;nbsp;것이다.&lt;br /&gt;&lt;br /&gt;물론 마이크로 최적화를 하고 싶다면 위의 방법이 도움이 될 수 있으나 대부분의 경우는 이를 고려하지 않아도 아무런 문제가 없을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조 분해 할당은 ES6에서 새롭게 도입된 문법이며 좌변에 따라 우변의 이터레이터를 활용하거나 객체로 변환하여 값에 접근 및 좌변에 할당한다.&lt;br /&gt;&lt;br /&gt;이&amp;nbsp;과정은&amp;nbsp;엔진에&amp;nbsp;의해&amp;nbsp;수행되기&amp;nbsp;때문에&amp;nbsp;자세한&amp;nbsp;구조를&amp;nbsp;몰라도&amp;nbsp;사용하는데&amp;nbsp;문제가&amp;nbsp;없다.&lt;br /&gt;&lt;br /&gt;성능&amp;nbsp;등의&amp;nbsp;단점이&amp;nbsp;없지는&amp;nbsp;않으나&amp;nbsp;가독성과&amp;nbsp;같은&amp;nbsp;장점도&amp;nbsp;있기&amp;nbsp;때문에&amp;nbsp;자신이&amp;nbsp;속한&amp;nbsp;집단의&amp;nbsp;컨벤션에&amp;nbsp;따르면&amp;nbsp;될&amp;nbsp;것이다.&lt;br /&gt;&lt;br /&gt;일반적으로&amp;nbsp;권장되는&amp;nbsp;경우는&amp;nbsp;소수의&amp;nbsp;값&amp;nbsp;추출,&amp;nbsp;옵션&amp;nbsp;기본값&amp;nbsp;설정,&amp;nbsp;API&amp;nbsp;응답&amp;nbsp;등에서&amp;nbsp;일부만&amp;nbsp;사용,&amp;nbsp;파라미터&amp;nbsp;선언,&amp;nbsp;간단한&amp;nbsp;스왑&amp;nbsp;등이&amp;nbsp;있으며&amp;nbsp;지양되는&amp;nbsp;경우는&amp;nbsp;성능에&amp;nbsp;민감한&amp;nbsp;루프,&amp;nbsp;대용량&amp;nbsp;전개&amp;nbsp;또는&amp;nbsp;복사,&amp;nbsp;복잡한&amp;nbsp;중첩&amp;nbsp;패턴&amp;nbsp;남용&amp;nbsp;등이&amp;nbsp;있기&amp;nbsp;때문에&amp;nbsp;성능&amp;nbsp;최적화가&amp;nbsp;최우선인&amp;nbsp;구간이&amp;nbsp;아니라면&amp;nbsp;명료성과&amp;nbsp;유지보수성&amp;nbsp;측면에서&amp;nbsp;유용하다.&lt;br /&gt;&lt;br /&gt;ES6&amp;nbsp;이하의&amp;nbsp;버전에서&amp;nbsp;사용하기&amp;nbsp;위해서는&amp;nbsp;이&amp;nbsp;새로운&amp;nbsp;문법을&amp;nbsp;파싱하기&amp;nbsp;위한&amp;nbsp;트랜스파일&amp;nbsp;과정이&amp;nbsp;추가로&amp;nbsp;필요하다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[참고문서]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Destructuring&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Destructuring&lt;/a&gt;&amp;nbsp;&lt;br /&gt;&lt;a href=&quot;https://tc39.es/ecma262/#sec-runtime-semantics-destructuringassignmentevaluation&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://tc39.es/ecma262/#sec-runtime-semantics-destructuringassignmentevaluation&lt;/a&gt;&amp;nbsp;&lt;br /&gt;&lt;a href=&quot;https://tc39.es/ecma262/#sec-destructuring-assignment&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://tc39.es/ecma262/#sec-destructuring-assignment&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/개념</category>
      <author>단진</author>
      <guid isPermaLink="true">https://29223.tistory.com/178</guid>
      <comments>https://29223.tistory.com/178#entry178comment</comments>
      <pubDate>Mon, 11 Aug 2025 20:43:35 +0900</pubDate>
    </item>
    <item>
      <title>[Node.js 기여하기] 4. os.networkInterfaces의 서브넷마스크 조건 수정하기</title>
      <link>https://29223.tistory.com/177</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Nodejs.png&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEEJeg/btsNt2i17b0/gpG1jxSIO69ZHjeBhFEn00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEEJeg/btsNt2i17b0/gpG1jxSIO69ZHjeBhFEn00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEEJeg/btsNt2i17b0/gpG1jxSIO69ZHjeBhFEn00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEEJeg%2FbtsNt2i17b0%2FgpG1jxSIO69ZHjeBhFEn00%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1100&quot; height=&quot;600&quot; data-filename=&quot;Nodejs.png&quot; data-origin-width=&quot;1100&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;&lt;a href=&quot;https://github.com/nodejs/node/pull/57324&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/nodejs/node/pull/57324&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1745263744070&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;os: fix netmask format check condition in getCIDR function by HBSPS &amp;middot; Pull Request #57324 &amp;middot; nodejs/node&quot; data-og-description=&quot;Modified to check the format of the netmask instead of just checking that each part of the netmask is even number. (Ref: https://www.rfc-editor.org/rfc/rfc1878) The result of the previous run was l...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/nodejs/node/pull/57324&quot; data-og-url=&quot;https://github.com/nodejs/node/pull/57324&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/byOZGR/hyYFEYa3C9/kdz4MvXknnSOvR36EtvwGk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/rCr59/hyYH9pIpXP/Em3fJQOSw4BFZWefPXL120/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/nodejs/node/pull/57324&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/nodejs/node/pull/57324&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/byOZGR/hyYFEYa3C9/kdz4MvXknnSOvR36EtvwGk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/rCr59/hyYH9pIpXP/Em3fJQOSw4BFZWefPXL120/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;os: fix netmask format check condition in getCIDR function by HBSPS &amp;middot; Pull Request #57324 &amp;middot; nodejs/node&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Modified to check the format of the netmask instead of just checking that each part of the netmask is even number. (Ref: https://www.rfc-editor.org/rfc/rfc1878) The result of the previous run was l...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 성능 개선이 아니라 Node.js에서 올바르지 않게 동작하던 부분을 수정할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정 전 코드는 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1745263789326&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function networkInterfaces() {
  const data = getInterfaceAddresses();
  const result = {};

  if (data === undefined)
    return result;
  for (let i = 0; i &amp;lt; data.length; i += 7) {
    const name = data[i];
    const entry = {
      address: data[i + 1],
      netmask: data[i + 2],
      family: data[i + 3],
      mac: data[i + 4],
      internal: data[i + 5],
      cidr: getCIDR(data[i + 1], data[i + 2], data[i + 3]),
    };
    const scopeid = data[i + 6];
    if (scopeid !== -1)
      entry.scopeid = scopeid;

    const existing = result[name];
    if (existing !== undefined)
      ArrayPrototypePush(existing, entry);
    else
      result[name] = [entry];
  }

  return result;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1745263809320&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function getCIDR(address, netmask, family) {
  let ones = 0;
  let split = '.';
  let range = 10;
  let groupLength = 8;
  let hasZeros = false;
  let lastPos = 0;

  if (family === 'IPv6') {
    split = ':';
    range = 16;
    groupLength = 16;
  }

  for (let i = 0; i &amp;lt; netmask.length; i++) {
    if (netmask[i] !== split) {
      if (i + 1 &amp;lt; netmask.length) {
        continue;
      }
      i++;
    }
    const part = StringPrototypeSlice(netmask, lastPos, i);
    lastPos = i + 1;
    if (part !== '') {
      if (hasZeros) {
        if (part !== '0') {
          return null;
        }
      } else {
        const binary = NumberParseInt(part, range);
        const binaryOnes = countBinaryOnes(binary);
        ones += binaryOnes;
        if (binaryOnes !== groupLength) {
          if ((binary &amp;amp; 1) !== 0) {
            return null;
          }
          hasZeros = true;
        }
      }
    }
  }

  return `${address}/${ones}`;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nodejs.org/docs/latest/api/os.html#osnetworkinterfaces&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://nodejs.org/docs/latest/api/os.html#osnetworkinterfaces&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1745263852039&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;OS | Node.js v23.11.0 Documentation&quot; data-og-description=&quot;Source Code: lib/os.js The node:os module provides operating system-related utility methods and properties. It can be accessed using: import os from 'node:os';const os = require('node:os');copy os.EOL# Added in: v0.7.8 The operating system-specific end-of-&quot; data-og-host=&quot;nodejs.org&quot; data-og-source-url=&quot;https://nodejs.org/docs/latest/api/os.html#osnetworkinterfaces&quot; data-og-url=&quot;https://nodejs.org/docs/latest/api/os.html#osnetworkinterfaces&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://nodejs.org/docs/latest/api/os.html#osnetworkinterfaces&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nodejs.org/docs/latest/api/os.html#osnetworkinterfaces&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;OS | Node.js v23.11.0 Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Source Code: lib/os.js The node:os module provides operating system-related utility methods and properties. It can be accessed using: import os from 'node:os';const os = require('node:os');copy os.EOL# Added in: v0.7.8 The operating system-specific end-of-&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;nodejs.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드의 경우 OS 모듈에 포함된 메소드 중 하나로 실행 결과는 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1745263874148&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  lo: [
    {
      address: '127.0.0.1',
      netmask: '255.0.0.0',
      family: 'IPv4',
      mac: '00:00:00:00:00:00',
      internal: true,
      cidr: '127.0.0.1/8'
    },
    {
      address: '::1',
      netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
      family: 'IPv6',
      mac: '00:00:00:00:00:00',
      scopeid: 0,
      internal: true,
      cidr: '::1/128'
    }
  ],
  eth0: [
    {
      address: '192.168.1.108',
      netmask: '255.255.255.0',
      family: 'IPv4',
      mac: '01:02:03:0a:0b:0c',
      internal: false,
      cidr: '192.168.1.108/24'
    },
    {
      address: 'fe80::a00:27ff:fe4e:66a1',
      netmask: 'ffff:ffff:ffff:ffff::',
      family: 'IPv6',
      mac: '01:02:03:0a:0b:0c',
      scopeid: 1,
      internal: false,
      cidr: 'fe80::a00:27ff:fe4e:66a1/64'
    }
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 출력 예시에서 볼 수 있듯이 단순히 현재 시스템의 네트워크 정보를 가져오는 함수다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개선점 찾기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 함수는 인자가 없으며 내부적으로 시스템의 정보를 가져와 반환하는 것으로 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 전체 네트워크 정보들을 순회하며 정해진 데이터를 만들어 반환하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 저 부분에서는 별도로 개선할 점이 없어보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 해당 함수 내부에서 사용하는 getCIDR 함수다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 함수의 경우 당시까지 networkInterfaces 메소드 내부에서만 사용하고 있었으며 CIDR을 생성해 반환하는 함수다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 개의 데이터를 인자로 받으며 각각 IP, 서브넷마스크, family (IPv4인지 IPv6인지)로 구성되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 함수의 최종 반환 결과는 127.0.0.1/8과 같이 CIDR 형식의 문자열이다.&lt;/p&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_1745264097334&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function getCIDR(address, netmask, family) {
  let ones = 0;
  let split = '.';
  let range = 10;
  let groupLength = 8;
  let hasZeros = false;
  let lastPos = 0;

  if (family === 'IPv6') {
    split = ':';
    range = 16;
    groupLength = 16;
  }

  for (let i = 0; i &amp;lt; netmask.length; i++) {
    if (netmask[i] !== split) {
      if (i + 1 &amp;lt; netmask.length) {
        continue;
      }
      i++;
    }
    const part = StringPrototypeSlice(netmask, lastPos, i);
    lastPos = i + 1;
    if (part !== '') {
      if (hasZeros) {
        if (part !== '0') {
          return null;
        }
      } else {
        const binary = NumberParseInt(part, range);
        const binaryOnes = countBinaryOnes(binary);
        ones += binaryOnes;
        if (binaryOnes !== groupLength) {
          if ((binary &amp;amp; 1) !== 0) {
            return null;
          }
          hasZeros = true;
        }
      }
    }
  }

  return `${address}/${ones}`;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 문제가 되는 부분은 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1745264161858&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 생략

if (part !== '') {
  if (hasZeros) {
    if (part !== '0') {
      return null;
    }
  } else {
    const binary = NumberParseInt(part, range);
    const binaryOnes = countBinaryOnes(binary);
    ones += binaryOnes;
    if (binaryOnes !== groupLength) {
      if ((binary &amp;amp; 1) !== 0) {
        return null;
      }
      hasZeros = true;
    }
  }
}

// 생략&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래와 같은 경우가 있다고 해보자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ip: 127.0.0.1&lt;/li&gt;
&lt;li&gt;netmask: 255.242.0.0&lt;/li&gt;
&lt;li&gt;family: IPv4&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;(split에 해당하는 .을 만날 때 까지 netmask를 한 자리씩 읽기 순서로 탐색. 최종적으로 part는 255가 된 상태에서 if문 진입)&lt;/li&gt;
&lt;li&gt;part가 비어있지 않으므로 (255) else문 실행&lt;/li&gt;
&lt;li&gt;binary = 255 (int) / binaryOnes = 8 / ones = 0 + 8&lt;/li&gt;
&lt;li&gt;binaryOnes와 groupLength가 8로 동일하므로 이후 코드는 실행되지 않음&lt;/li&gt;
&lt;li&gt;두 번째 part는 242이므로 else문 실행&lt;/li&gt;
&lt;li&gt;binary = 242 (int) / binaryOnes = 5 (11110010) / ones = 8 + 5&lt;/li&gt;
&lt;li&gt;binaryOnes와 groupLength가 8로 동일하지 않으므로 if문 실행&lt;/li&gt;
&lt;li&gt;(binary &amp;amp; 1 = 0)이므로 조건문이 참이 됨&lt;/li&gt;
&lt;li&gt;hasZero가 true로 변경&lt;/li&gt;
&lt;li&gt;이후 두 번의 반복에 대해 netmask가 비어있지 않고 hasZero가 true며 각 part가 0이기 때문에 null이 반환되지 않음&lt;/li&gt;
&lt;li&gt;최종 결과는 127.0.0.1/13&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 본인의 경우 학교 수업을 들으며 서브넷마스크의 경우 연속된 1로 구성되어 있으며 한 번 0이 나온 뒤에는 다시 1이 나와선 안된다고 배웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 다양한 RFC 문서에서 이를 언급하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc1519#section-4&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://datatracker.ietf.org/doc/html/rfc1519#section-4&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1745265218554&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;RFC 1519: Classless Inter-Domain Routing (CIDR): an Address Assignment and Aggregation Strategy&quot; data-og-description=&quot;This memo discusses strategies for address assignment of the existing IP address space with a view to conserve the address space and stem the explosive growth of routing tables in default-route-free routers. [STANDARDS-TRACK]&quot; data-og-host=&quot;datatracker.ietf.org&quot; data-og-source-url=&quot;https://datatracker.ietf.org/doc/html/rfc1519#section-4&quot; data-og-url=&quot;https://datatracker.ietf.org/doc/html/rfc1519&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc1519#section-4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://datatracker.ietf.org/doc/html/rfc1519#section-4&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;RFC 1519: Classless Inter-Domain Routing (CIDR): an Address Assignment and Aggregation Strategy&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;This memo discusses strategies for address assignment of the existing IP address space with a view to conserve the address space and stem the explosive growth of routing tables in default-route-free routers. [STANDARDS-TRACK]&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;datatracker.ietf.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc4632#section-5.1&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://datatracker.ietf.org/doc/html/rfc4632#section-5.1&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1745265244596&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;RFC 4632: Classless Inter-domain Routing (CIDR): The Internet Address Assignment and Aggregation Plan&quot; data-og-description=&quot;This memo discusses the strategy for address assignment of the existing 32-bit IPv4 address space with a view toward conserving the address space and limiting the growth rate of global routing state. This document obsoletes the original Classless Inter-dom&quot; data-og-host=&quot;datatracker.ietf.org&quot; data-og-source-url=&quot;https://datatracker.ietf.org/doc/html/rfc4632#section-5.1&quot; data-og-url=&quot;https://datatracker.ietf.org/doc/html/rfc4632&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ecUVyg/hyYJBsYMgi/azjIkFdBClq2L2hv7Q0s21/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc4632#section-5.1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://datatracker.ietf.org/doc/html/rfc4632#section-5.1&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ecUVyg/hyYJBsYMgi/azjIkFdBClq2L2hv7Q0s21/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;RFC 4632: Classless Inter-domain Routing (CIDR): The Internet Address Assignment and Aggregation Plan&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;This memo discusses the strategy for address assignment of the existing 32-bit IPv4 address space with a view toward conserving the address space and limiting the growth rate of global routing state. This document obsoletes the original Classless Inter-dom&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;datatracker.ietf.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;An&amp;nbsp;implementation&amp;nbsp;following&amp;nbsp;these&amp;nbsp;rules&amp;nbsp;should&amp;nbsp;also&amp;nbsp;be&amp;nbsp;generalized,&amp;nbsp;&amp;nbsp;so&amp;nbsp;that&amp;nbsp;an&amp;nbsp;arbitrary&amp;nbsp;network&amp;nbsp;number&amp;nbsp;and&amp;nbsp;mask&amp;nbsp;are&amp;nbsp;accepted&amp;nbsp;for&amp;nbsp;all&amp;nbsp;routing&amp;nbsp;destinations.&amp;nbsp;&amp;nbsp;The&amp;nbsp;only&amp;nbsp;outstanding&amp;nbsp;constraint&amp;nbsp;is&amp;nbsp;that&amp;nbsp;the&amp;nbsp;mask&amp;nbsp;must&amp;nbsp;be&amp;nbsp;left&amp;nbsp;contiguous.&lt;br /&gt;&lt;br /&gt;이러한&amp;nbsp;규칙을&amp;nbsp;따르는&amp;nbsp;구현도&amp;nbsp;일반화하여&amp;nbsp;모든&amp;nbsp;라우팅&amp;nbsp;대상에&amp;nbsp;대해&amp;nbsp;임의의&amp;nbsp;네트워크&amp;nbsp;번호와&amp;nbsp;마스크가&amp;nbsp;허용되도록&amp;nbsp;해야&amp;nbsp;합니다.&amp;nbsp;&amp;nbsp;유일한&amp;nbsp;제약&amp;nbsp;조건은&amp;nbsp;마스크가&amp;nbsp;연속적으로&amp;nbsp;유지되어야&amp;nbsp;한다는&amp;nbsp;것입니다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개선 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재의&amp;nbsp;조건문은&amp;nbsp;단순히&amp;nbsp;해당&amp;nbsp;숫자가&amp;nbsp;짝수인지&amp;nbsp;확인만&amp;nbsp;하고&amp;nbsp;있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 연속된 1이 아닌 서브넷 마스크가 허용된다고 하더라도 단순히 짝수인지 검사하는 조건은 필요 없을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면 이러한 경우 모든 형태의 서브넷 마스크가 허용되므로 홀수 서브넷 마스크만 허용되지 않을 이유가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 두 가지 방법이 있다.&lt;/p&gt;
&lt;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;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 내가 배웠던 내용과 함께 RFC 문서라는 근거가 뒷받침 되기 때문에 2번을 선택했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서브넷 마스크가 유효한지 찾기 위해 선택한 방법은 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1745265447986&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (StringPrototypeIncludes(binary.toString(2), '01')) {
  return null;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 숫자를 2진수 문자열로 변환한 뒤 문자열 01이 포함되는지 확인하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 한 번 0이 나온 뒤 다시 1이 나올 수 없다는 서브넷 마스크의 조건을 만족하는지 검사하는 가장 쉬운 방법이라 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1차 리뷰 - 테스트 파일 추가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때까지의 기여와는 다르게 벤치마크 결과도 없으며 이 변경이 승인되기 위해서는 기술적으로 설득할 수 있어야 한다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 위에서 언급한 두 가지 방법을 모두 제시한 뒤 의견이 다른 경우 그 의견을 따르고자 했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1820&quot; data-origin-height=&quot;1406&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpl9Qb/btsNtYOnmBi/Z2ibm7zGrw1LyaeVayQvQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpl9Qb/btsNtYOnmBi/Z2ibm7zGrw1LyaeVayQvQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpl9Qb/btsNtYOnmBi/Z2ibm7zGrw1LyaeVayQvQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbpl9Qb%2FbtsNtYOnmBi%2FZ2ibm7zGrw1LyaeVayQvQ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1820&quot; height=&quot;1406&quot; data-origin-width=&quot;1820&quot; data-origin-height=&quot;1406&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 승인을 받는데 큰 어려움은 없었으나 새로운 변경을 요구받았다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1842&quot; data-origin-height=&quot;442&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxQXRd/btsNum8YBjV/LT1DCIT7dgVXl5xSmm3EK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxQXRd/btsNum8YBjV/LT1DCIT7dgVXl5xSmm3EK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxQXRd/btsNum8YBjV/LT1DCIT7dgVXl5xSmm3EK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxQXRd%2FbtsNum8YBjV%2FLT1DCIT7dgVXl5xSmm3EK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1842&quot; height=&quot;442&quot; data-origin-width=&quot;1842&quot; data-origin-height=&quot;442&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 기존 getCIDR의 문제가 되는 조건문은 테스트 커버리지에 포함되지 않고 있었다.&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;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 함수는 networkInterfaces 내부에서만 사용되고 있었으며 networkInterfaces의 경우 인자가 없고 시스템 정보를 활용하기 때문에 테스트 파일을 작성하는데 어려움이 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 생각한 부분은 mocking이었으나 시스템 내부의 정보를 가져오는 것은 internal 부분에 포함된 코드인 것으로 보이기 때문에 이것을 mocking 하는데 어려움이 있을 것으로 보였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 방법으로 getCIDR 함수의 경우 순수함수기 때문에 이 함수 자체를 바로 테스트 할 수 있는 방법을 찾고자 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 파일의 경우도 networkInterfaces와 같은 파일에 작성되어 있었으며 내보내고 있지 않았기 때문에 require를 통해 불러올 수 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결 - 함수를 다른 파일로 이동&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해 우선 첫 번째 리뷰를 해주신 분께 도움을 요청드렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 두 번째 방법이 조금 더 합리적이라는 것을 알고는 있으나 기존의 코드를 다른 파일로 이동하는 것과 새로운 파일을 생성하는 것에 대해 확실한 답변을 받는 것이 나을 것으로 생각했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1818&quot; data-origin-height=&quot;498&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brwLDb/btsNu5yqK5v/2JUOfbZbizgMBF4Tm73nx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brwLDb/btsNu5yqK5v/2JUOfbZbizgMBF4Tm73nx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brwLDb/btsNu5yqK5v/2JUOfbZbizgMBF4Tm73nx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrwLDb%2FbtsNu5yqK5v%2F2JUOfbZbizgMBF4Tm73nx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1818&quot; height=&quot;498&quot; data-origin-width=&quot;1818&quot; data-origin-height=&quot;498&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;1824&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cp6agn/btsNu5yqK7t/t3MiN5b62YzNBCmAKZr1Sk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cp6agn/btsNu5yqK7t/t3MiN5b62YzNBCmAKZr1Sk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cp6agn/btsNu5yqK7t/t3MiN5b62YzNBCmAKZr1Sk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcp6agn%2FbtsNu5yqK7t%2Ft3MiN5b62YzNBCmAKZr1Sk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1824&quot; height=&quot;300&quot; data-origin-width=&quot;1824&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;답변에 따르면 새로운 파일을 생성하여 이동하는 것이 가능했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 기존에 있던 getCIDR 함수와 내부에서만 사용하는 다양한 함수들을 internal/util.js로 이동했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;internal 내부에 있는 함수들은 내보내기를 해도 사용자가 일반적인 방법으로 require 할 수 없도록 되어있다. (말 그대로 내부에서 사용하기 위한 함수들의 모음)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부적으로는 이 함수를 require하는데 문제가 없다.&lt;/p&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_1745266283321&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Flags: --expose-internals
'use strict';
require('../common');

// These are tests that verify that the subnetmask is used
// to create the correct CIDR address.
// Tests that it returns null if the subnetmask is not in the correct format.
// (ref: https://www.rfc-editor.org/rfc/rfc1878)

const assert = require('node:assert');
const { getCIDR } = require('internal/util');

assert.strictEqual(getCIDR('127.0.0.1', '255.0.0.0', 'IPv4'), '127.0.0.1/8');
assert.strictEqual(getCIDR('127.0.0.1', '255.255.0.0', 'IPv4'), '127.0.0.1/16');

// 242 = 11110010(2)
assert.strictEqual(getCIDR('127.0.0.1', '242.0.0.0', 'IPv4'), null);

assert.strictEqual(getCIDR('::1', 'ffff:ffff:ffff:ffff::', 'IPv6'), '::1/64');
assert.strictEqual(getCIDR('::1', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', 'IPv6'), '::1/128');

// ff00:ffff = 11111111 00000000 : 11111111 11111111(2)
assert.strictEqual(getCIDR('::1', 'ffff:ff00:ffff::', 'IPv6'), null);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결과&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2472&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/owobU/btsNt0FuABc/6MlwPFaXtDzuk4Uu78ajHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/owobU/btsNt0FuABc/6MlwPFaXtDzuk4Uu78ajHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/owobU/btsNt0FuABc/6MlwPFaXtDzuk4Uu78ajHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FowobU%2FbtsNt0FuABc%2F6MlwPFaXtDzuk4Uu78ajHk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;2472&quot; height=&quot;306&quot; data-origin-width=&quot;2472&quot; data-origin-height=&quot;306&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;테스트 파일도 작성해볼 수 있었고 모르는 부분에 대해 도움도 받을 수 있는 PR이라 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모르는 것이 있거나 그들의 컨벤션을 잘 따르기 위해서 질문을 한다면 보다 수준이 높은 기여를 할 수 있을 것이라 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;여담&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node.js의 내부에는 다양한 내부 모듈들이 있으며 이들의 목표는 내부적으로 활용하기 위한 모듈들을 담는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 사용자는 이 모듈을 사용할 일이 없으며 내부에 있는 모듈들은 위와 같이 언제든 바뀔 수 있기 때문에 이를 직접 사용하지 않는 것을 추천한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/nodejs/node/blob/main/lib/internal/README.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/nodejs/node/blob/main/lib/internal/README.md&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1745266685111&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;node/lib/internal/README.md at main &amp;middot; nodejs/node&quot; data-og-description=&quot;Node.js JavaScript runtime ✨ ✨. Contribute to nodejs/node development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/nodejs/node/blob/main/lib/internal/README.md&quot; data-og-url=&quot;https://github.com/nodejs/node/blob/main/lib/internal/README.md&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dk00je/hyYIhnKOKK/NuPx9mnbscckrJB0kAsusK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/ed7sZx/hyYH7yF8PF/N8rbE2iQDw3ODJOgCMQ1ck/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/nodejs/node/blob/main/lib/internal/README.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/nodejs/node/blob/main/lib/internal/README.md&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dk00je/hyYIhnKOKK/NuPx9mnbscckrJB0kAsusK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/ed7sZx/hyYH7yF8PF/N8rbE2iQDw3ODJOgCMQ1ck/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;node/lib/internal/README.md at main &amp;middot; nodejs/node&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Node.js JavaScript runtime ✨ ✨. Contribute to nodejs/node development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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;node 명령어에 --expose-internal을 사용한다면 내부 모듈을 사용할 수는 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(호기심 많은 사람이라면 직접 사용해볼 수 있다. 단, 실제 프로젝트가 저 부분에 의존해서는 안된다)&lt;/p&gt;</description>
      <category>개발/개발과정</category>
      <author>단진</author>
      <guid isPermaLink="true">https://29223.tistory.com/177</guid>
      <comments>https://29223.tistory.com/177#entry177comment</comments>
      <pubDate>Tue, 22 Apr 2025 05:19:53 +0900</pubDate>
    </item>
    <item>
      <title>모든 브라우저는 gzip을 자동으로 해제할 수 있는가?</title>
      <link>https://29223.tistory.com/176</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;gzip.jpg&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RBIum/btsM3Xn1lDo/qkHSkMPyUqc0zogQzkdkv0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RBIum/btsM3Xn1lDo/qkHSkMPyUqc0zogQzkdkv0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RBIum/btsM3Xn1lDo/qkHSkMPyUqc0zogQzkdkv0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRBIum%2FbtsM3Xn1lDo%2FqkHSkMPyUqc0zogQzkdkv0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;720&quot; data-filename=&quot;gzip.jpg&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&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;웹 기반의 서비스의 성능을 최적화 하는 방법에는 무엇이 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 본인이 시도했던 것은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;번들 크기 줄이기&lt;/li&gt;
&lt;li&gt;코드 스플리팅&lt;/li&gt;
&lt;li&gt;이미지 최적화(webp를 사용)&lt;/li&gt;
&lt;li&gt;기타&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 위에서 했던 다양한 방법들의 궁극적인 목표는 무엇일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램에 사용되지 않는 코드를 제외하고 각 페이지에서 필요한 코드만 가져올 수 있도록 lazy loading을 적용하거나 차세대 이미지 형식인 webp를 사용하여 이미지의 해상도를 유지하며 용량을 줄이는 방법 모두 전송되는 데이터의 크기를 감소시키는데 목표를 두고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;최적화의 목표?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 최적화 중 일부는 더 작은 데이터 크기를 만드는 것을 목표로 하고 있는 것을 확인했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 더 작은 데이터가 의미하는 것은 무엇일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 작은 데이터 = 더 빠른 전송 = 더 좋은 사용자 경험을 의미할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇듯 더 작은 데이터는 결국 더 좋은 사용자 경험을 제공하기 위한 좋은 방법이 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그래서 어쩌라고?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gzip에 관한 글인데 왜 최적화 이야기만 하는가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얼마 전 개인적으로 프로젝트를 진행하던 중 신기한 현상을 발견했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(물론 당시의 본인 기준이므로 사람에 따라 당연한 내용일 수도 있습니다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당시 아래와 같은 상황이었다.&lt;/p&gt;
&lt;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;여기서 공공데이터 포털을 통해 받아온 데이터를 저장하는 과정에서 다음과 같은 생각을 했다.&lt;/p&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; 공공데이터 포털의 API 월별 호출 횟수 제한이 있기 때문에 이를 프론트엔드 코드에 직접 넣고 싶었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트 프로젝트의 경우 public 디렉토리 하위에 정적 리소스를 넣을 수 있었으며 vercel 또는 gitbub pages를 사용한다면 이 정적 리소스를 함께 업로드 할 수 있을 것이라 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러던 중 아래와 같은 문제가 발생했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-31 오후 10.00.59.png&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;1016&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbLzFA/btsM3Wv1ajP/WJc9Q8CVHCJWLMDvQilNY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbLzFA/btsM3Wv1ajP/WJc9Q8CVHCJWLMDvQilNY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbLzFA/btsM3Wv1ajP/WJc9Q8CVHCJWLMDvQilNY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbLzFA%2FbtsM3Wv1ajP%2FWJc9Q8CVHCJWLMDvQilNY1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;445&quot; height=&quot;600&quot; data-filename=&quot;스크린샷 2025-03-31 오후 10.00.59.png&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;1016&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 json 파일의 개수는 2800개 였으며 이 모든 파일의 용량은 1.17GB를 차지했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 데이터들을 vercel 또는 github pages에 업로드 하기에도 무리가 있다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러던 중 예전 vite를 사용할 때 gzip이라는 키워드를 볼 수 있었으며 이를 활용하면 용량을 줄일 수 있을 것으로 기대했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결과&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;용량은 줄었으며 vercel에 업로드 할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터가 준비 되었으며 업로드가 성공적인 것을 확인했으니 public에 있는 정적 리소스를 잘 가져오는지 확인하고자 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 브라우저에서 압축과 관련된 문제가 발생하지 않고 데이터의 압축이 자동으로 해제된 것 처럼 보였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1066&quot; data-origin-height=&quot;826&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bckOU0/btsM3W3PUMO/iZNoZgK103un5kKbNVr1t0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bckOU0/btsM3W3PUMO/iZNoZgK103un5kKbNVr1t0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bckOU0/btsM3W3PUMO/iZNoZgK103un5kKbNVr1t0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbckOU0%2FbtsM3W3PUMO%2FiZNoZgK103un5kKbNVr1t0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;445&quot; height=&quot;345&quot; data-origin-width=&quot;1066&quot; data-origin-height=&quot;826&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;분명 확장자에는 .gz라고 쓰여있지만 네트워크 탭에서 압축된 데이터를 읽을 수 있으며 이 데이터를 그대로 사용하는데 문제가 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 데이터는 .gz 확장자를 갖는 압축된 파일이지만 브라우저에서 그대로 사용할 수 있었으며 이것이 가능한 이유가 무엇인지 궁금했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한,&amp;nbsp;프론트엔드&amp;nbsp;영역의&amp;nbsp;최적화&amp;nbsp;기법중&amp;nbsp;gzip&amp;nbsp;압축을&amp;nbsp;통한&amp;nbsp;용량을&amp;nbsp;감소시키는&amp;nbsp;경우도&amp;nbsp;있으니&amp;nbsp;이에&amp;nbsp;대해 궁금했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 본인은 크롬을 사용하고 있으며 크롬에서만 저 기능이 작동하는 것인지, 모든 브라우저에서 저 동작이 기본으로 지원되는 것인지 알아보기로 했다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;gzip?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.gzip.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.gzip.org/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1743477150560&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;The gzip home page&quot; data-og-description=&quot;Intro Welcome to this momentary pit stop on the road to finding what you need concerning gzip! gzip is a single-file/stream lossless data compression utility, where the resulting compressed file generally has the suffix .gz. gzip also refers to the associa&quot; data-og-host=&quot;www.gzip.org&quot; data-og-source-url=&quot;https://www.gzip.org/&quot; data-og-url=&quot;https://www.gzip.org/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.gzip.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.gzip.org/&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;The gzip home page&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Intro Welcome to this momentary pit stop on the road to finding what you need concerning gzip! gzip is a single-file/stream lossless data compression utility, where the resulting compressed file generally has the suffix .gz. gzip also refers to the associa&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.gzip.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;gzip&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;is a single-file/stream lossless data compression utility, where the resulting compressed file generally has the suffix&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;.gz&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;.&lt;/span&gt;&lt;br /&gt;gzip&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;also refers to the associated compressed data format used by the utility.&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;gzip은 단일 파일/스트림 무손실 데이터 압축 유틸리티로, 결과 압축 파일에는 일반적으로 .gz라는 접미사가 붙습니다.&lt;br /&gt;gzip은 유틸리티에서 사용하는 관련 압축 데이터 형식을 의미하기도 합니다.&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;gzip 공식 문서에서 gzip은 데이터 압축 유틸리티라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 gzip에 관한 구조나 알고리즘을 다루지 않을 예정이기에(추후 기회가 된다면 다시 다루도록 하겠다) 아래 문서들을 참고해 gzip의 구조 또는 특징에 대해 확인하기를 추천한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://old-projects.andromedarabbit.net/src/zip/GzipFileFormat.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://old-projects.andromedarabbit.net/src/zip/GzipFileFormat.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1743477319346&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;GZIP 파일의 구조&quot; data-og-description=&quot;압축이 일어난 파일 시스템을 알려준다. 텍스트 파일의 End-of-Line을 구별하는 데에 유용하다. 0 FAT file system (MS-DOS, OS/2, NT/Win32) 1 Amiga 2 VMS or Open VMS 3 Unix 4 VM/CMS 5 Atari TOS 6 HPFS file system (OS/2, NT) 7 Maci&quot; data-og-host=&quot;old-projects.andromedarabbit.net&quot; data-og-source-url=&quot;https://old-projects.andromedarabbit.net/src/zip/GzipFileFormat.html&quot; data-og-url=&quot;https://old-projects.andromedarabbit.net/src/zip/GzipFileFormat.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://old-projects.andromedarabbit.net/src/zip/GzipFileFormat.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://old-projects.andromedarabbit.net/src/zip/GzipFileFormat.html&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;GZIP 파일의 구조&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;압축이 일어난 파일 시스템을 알려준다. 텍스트 파일의 End-of-Line을 구별하는 데에 유용하다. 0 FAT file system (MS-DOS, OS/2, NT/Win32) 1 Amiga 2 VMS or Open VMS 3 Unix 4 VM/CMS 5 Atari TOS 6 HPFS file system (OS/2, NT) 7 Maci&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;old-projects.andromedarabbit.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://products.aspose.com/zip/ko/most-common-archives/what-is-gzip/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://products.aspose.com/zip/ko/most-common-archives/what-is-gzip/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1743477487232&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;GZIP 파일 형식 - 알아야 할 모든 것&quot; data-og-description=&quot;GZIP은 파일 압축 및 압축 해제에 사용되는 널리 사용되는 파일 형식이자 소프트웨어 응용 프로그램입니다. 1990년대 초 Jean-Loup Gailly와 Mark Adler가 무료 오픈 소스 압축 알고리즘으로 개발했습니다&quot; data-og-host=&quot;products.aspose.com&quot; data-og-source-url=&quot;https://products.aspose.com/zip/ko/most-common-archives/what-is-gzip/&quot; data-og-url=&quot;https://products.aspose.com/zip/ko/most-common-archives/what-is-gzip/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/HZafl/hyYBcM4cAA/sG0Tp8F6VR2alPqK1Hpapk/img.png?width=496&amp;amp;height=354&amp;amp;face=0_0_496_354&quot;&gt;&lt;a href=&quot;https://products.aspose.com/zip/ko/most-common-archives/what-is-gzip/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://products.aspose.com/zip/ko/most-common-archives/what-is-gzip/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/HZafl/hyYBcM4cAA/sG0Tp8F6VR2alPqK1Hpapk/img.png?width=496&amp;amp;height=354&amp;amp;face=0_0_496_354');&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;GZIP 파일 형식 - 알아야 할 모든 것&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;GZIP은 파일 압축 및 압축 해제에 사용되는 널리 사용되는 파일 형식이자 소프트웨어 응용 프로그램입니다. 1990년대 초 Jean-Loup Gailly와 Mark Adler가 무료 오픈 소스 압축 알고리즘으로 개발했습니다&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;products.aspose.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 gzip의 압축 효과는 얼마나 좋을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 데이터를 단순히 gzip으로 압축하는 경우 결과는 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;1016&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btmsWC/btsM1K4Bv4Q/bfFjz5mBiviBP8wag5z7a1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btmsWC/btsM1K4Bv4Q/bfFjz5mBiviBP8wag5z7a1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btmsWC/btsM1K4Bv4Q/bfFjz5mBiviBP8wag5z7a1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtmsWC%2FbtsM1K4Bv4Q%2FbfFjz5mBiviBP8wag5z7a1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;445&quot; height=&quot;600&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;1016&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1743478326415&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { existsSync, mkdirSync, readdir, createReadStream, createWriteStream } from 'fs';
import { createGzip } from 'zlib';
import { resolve, join, extname } from 'path';

// 원본 파일이 있는 디렉토리
const rawDataDirectory = resolve('./rawData');

// 압축된 파일을 저장할 디렉토리
const compressedDataDirectory = resolve('./data');

// 압축된 파일을 저장할 디렉토리가 존재하지 않는 경우 생성
if (!existsSync(compressedDataDirectory)) {
  mkdirSync(compressedDataDirectory);
}

// 파일 목록을 읽어오기
readdir(rawDataDirectory, (err, files) =&amp;gt; {
  if (err) {
    console.error('Error reading directory:', err);
    return;
  }

  files.forEach((file) =&amp;gt; {
    const filePath = join(rawDataDirectory, file);

    // 파일이 JSON 파일인지 확인
    if (extname(file) === '.json') {
      const gzip = createGzip();
      const input = createReadStream(filePath);
      const output = createWriteStream(join(compressedDataDirectory, `${file}.gz`));

      // 파일을 읽고 gzip으로 압축한 후 저장
      input.pipe(gzip).pipe(output);

      output.on('finish', () =&amp;gt; {
        console.log(`Compressed ${filePath} to ${join(compressedDataDirectory, `${file}.gz`)}`);
      });
    }
  });
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(예제코드)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;node.js의 내장 모듈을 활용해서 gzip 압축을 할 수 있으며 공식 문서에서 확인한 것과 같이 모든 파일들에 .gz 확장자가 붙은 채로 압축되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 압축된 경우 VSCode를 통해 내용을 확인할 수 없으며 별도의 압축 해제 과정이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;브라우저는 알아서 해제 하던데요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 이 글의 핵심이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 gz 데이터를 사용하기 위해 별도의 압축 해제가 필요할 것으로 예상했지만 브라우저는 알아서 이 데이터를 압축 해제 하고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 브라우저는 기본적으로 gzip 압축을 해제하는 기능을 내장하고 있는 것일까?&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;RFC&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.ietf.org/process/rfcs/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.ietf.org/process/rfcs/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1743479740965&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;About RFCs&quot; data-og-description=&quot;RFC documents contain technical specifications and organizational notes for the Internet and are the core output of the IETF.&quot; data-og-host=&quot;www.ietf.org&quot; data-og-source-url=&quot;https://www.ietf.org/process/rfcs/&quot; data-og-url=&quot;https://www.ietf.org/process/rfcs/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/HcQ9y/hyYyKDWDEI/hIkg7gdyaddWcoP5U22Fc1/img.png?width=280&amp;amp;height=149&amp;amp;face=0_0_280_149,https://scrap.kakaocdn.net/dn/eRzK4/hyYyQjRpxw/Na23dDhhVOnijCHjRGZlSk/img.png?width=280&amp;amp;height=149&amp;amp;face=0_0_280_149&quot;&gt;&lt;a href=&quot;https://www.ietf.org/process/rfcs/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.ietf.org/process/rfcs/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/HcQ9y/hyYyKDWDEI/hIkg7gdyaddWcoP5U22Fc1/img.png?width=280&amp;amp;height=149&amp;amp;face=0_0_280_149,https://scrap.kakaocdn.net/dn/eRzK4/hyYyQjRpxw/Na23dDhhVOnijCHjRGZlSk/img.png?width=280&amp;amp;height=149&amp;amp;face=0_0_280_149');&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;About RFCs&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;RFC documents contain technical specifications and organizational notes for the Internet and are the core output of the IETF.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.ietf.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #333741; text-align: start;&quot;&gt;Software developers, hardware manufacturers, and network operators around the world voluntarily implement and adopt the technical specifications and best practices described by RFCs.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;전&amp;nbsp;세계의&amp;nbsp;소프트웨어&amp;nbsp;개발자,&amp;nbsp;하드웨어&amp;nbsp;제조업체,&amp;nbsp;네트워크&amp;nbsp;운영자는&amp;nbsp;RFC에서&amp;nbsp;설명하는&amp;nbsp;기술&amp;nbsp;사양과&amp;nbsp;모범&amp;nbsp;사례를&amp;nbsp;자발적으로&amp;nbsp;구현하고&amp;nbsp;채택하고&amp;nbsp;있습니다.&lt;/span&gt;&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;RFC는 문서로 쉽게 생각하면 인터넷의 표준 정도 되는 문서다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 표준을 지키도록 구현함으로써 상호 운용성 보장, 보안 강화, 시스템 신뢰성 향상 등의 이점을 얻을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc2026&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://datatracker.ietf.org/doc/html/rfc2026&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1743479938857&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;RFC 2026: The Internet Standards Process -- Revision 3&quot; data-og-description=&quot;This memo documents the process used by the Internet community for the standardization of protocols and procedures. It defines the stages in the standardization process, the requirements for moving a document between stages and the types of documents used &quot; data-og-host=&quot;datatracker.ietf.org&quot; data-og-source-url=&quot;https://datatracker.ietf.org/doc/html/rfc2026&quot; data-og-url=&quot;https://datatracker.ietf.org/doc/html/rfc2026&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/7rpZG/hyYA49kO7G/SAbr4nLiIK8eT78I2vkHc0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc2026&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://datatracker.ietf.org/doc/html/rfc2026&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/7rpZG/hyYA49kO7G/SAbr4nLiIK8eT78I2vkHc0/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;RFC 2026: The Internet Standards Process -- Revision 3&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;This memo documents the process used by the Internet community for the standardization of protocols and procedures. It defines the stages in the standardization process, the requirements for moving a document between stages and the types of documents used&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;datatracker.ietf.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 RFC 문서에는 RFC의 목적, 중요성, 표준화 과정 등에 대한 내용등을 담고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주의점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 생각해서 RFC가 인터넷의 표준을 제시한다고 했지만 실제로 모든 RFC 문서가 인터넷 표준을 의미하지는 않는다.&lt;/p&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://datatracker.ietf.org/doc/html/rfc1796&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://datatracker.ietf.org/doc/html/rfc1796&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1744072759615&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;RFC 1796: Not All RFCs are Standards&quot; data-og-description=&quot;This document discusses the relationship of the Request for Comments (RFCs) notes to Internet Standards. This memo provides information for the Internet community. This memo does not specify an Internet standard of any kind.&quot; data-og-host=&quot;datatracker.ietf.org&quot; data-og-source-url=&quot;https://datatracker.ietf.org/doc/html/rfc1796&quot; data-og-url=&quot;https://datatracker.ietf.org/doc/html/rfc1796&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc1796&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://datatracker.ietf.org/doc/html/rfc1796&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;RFC 1796: Not All RFCs are Standards&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;This document discusses the relationship of the Request for Comments (RFCs) notes to Internet Standards. This memo provides information for the Internet community. This memo does not specify an Internet standard of any kind.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;datatracker.ietf.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문서의 핵심은 결국 모든 RFC들이 인터넷 표준은 아니라는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RFC 문서들은 다수의 등급을 가지고 있으며 그 중에서 인터넷 표준으로 선택된 RFC들이 STD라는 새로운 번호를 부여받고 인터넷 표준으로 취급된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.rfc-editor.org/standards&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.rfc-editor.org/standards&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1744072866683&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;Official Internet Protocol Standards &amp;raquo; RFC Editor&quot; data-og-description=&quot;&quot; data-og-host=&quot;www.rfc-editor.org&quot; data-og-source-url=&quot;https://www.rfc-editor.org/standards&quot; data-og-url=&quot;https://www.rfc-editor.org/standards&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.rfc-editor.org/standards&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.rfc-editor.org/standards&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;Official Internet Protocol Standards &amp;raquo; RFC Editor&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;www.rfc-editor.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 주소에서 STD 번호를 부여받은 RFC 문서들의 목록을 볼 수 있으며 이 문서에 기술된 내용들은 인터넷 표준으로 받아들여지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;혹시 브라우저도?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RFC가 인터넷 표준을 제시하고 개발자 등은 이를 따라야 한다면 브라우저도 이 표준을 따르고 있지는 않을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 gzip에 관한 RFC 문서는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc1952&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://datatracker.ietf.org/doc/html/rfc1952&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1743527639419&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;RFC 1952: GZIP file format specification version 4.3&quot; data-og-description=&quot;This specification defines a lossless compressed data format that is compatible with the widely used GZIP utility. This memo provides information for the Internet community. This memo does not specify an Internet standard of any kind.&quot; data-og-host=&quot;datatracker.ietf.org&quot; data-og-source-url=&quot;https://datatracker.ietf.org/doc/html/rfc1952&quot; data-og-url=&quot;https://datatracker.ietf.org/doc/html/rfc1952&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc1952&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://datatracker.ietf.org/doc/html/rfc1952&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;RFC 1952: GZIP file format specification version 4.3&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;This specification defines a lossless compressed data format that is compatible with the widely used GZIP utility. This memo provides information for the Internet community. This memo does not specify an Internet standard of any kind.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;datatracker.ietf.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 이 규격의 목적은 다음과 같다.&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;CPU 유형, 운영 체제, 파일 시스템, 문자 집합에 독립적이므로 상호 교환에 사용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;무작위로 액세스할 수 있는 파일이 아닌 데이터 스트림을 압축 또는 압축 해제하여 선험적으로 제한된 양의 중간 저장 공간만 사용하여 다른 데이터 스트림을 생성할 수 있으므로 데이터 통신 또는 Unix 필터와 같은 유사한 구조에 사용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;현재 사용 가능한 최고의 범용 압축 방법과 비슷한 효율로 데이터를 압축하며, 특히 'compress' 프로그램보다 훨씬 우수합니다.&lt;/li&gt;
&lt;li&gt;특허의 적용을 받지 않는 방식으로 쉽게 구현할 수 있으므로 자유롭게 실행할 수 있습니다.&lt;/li&gt;
&lt;li&gt;현재 널리 사용되는 gzip 유틸리티에서 생성된 파일 형식과 호환되며 호환되는 압축 해제 프로그램은 기존 gzip 압축기에서 생성된 데이터를 읽을 수 있습니다.&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;또한, 1.4. Compliance (규정)에는 아래와 같이 명시되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;아래에 달리 명시되지 않는 한, 호환 압축기는 여기에 제시된 모든 사양을 준수하는 모든 파일을 수용하고 압축을 해제할 수 있어야 하며, 호환 압축기는 여기에 제시된 모든 사양을 준수하는 파일들을 생성해야 합니다&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문서에 따르면 문서에 정의된 gzip 형식을 수용하고 압축 및 해제를 할 수 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것은 인터넷 표준을 따르는 브라우저들도 준수해야 할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;gzip과 관련된 STD 문서는?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gzip이 어떤 것인지 알아봤으니 인터넷 표준으로 분류된 문서에서 gzip과 관련된 부분이 있는지 찾아봐야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc9110.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.rfc-editor.org/rfc/rfc9110.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1744073610657&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;RFC 9110: HTTP Semantics&quot; data-og-description=&quot;The Hypertext Transfer Protocol (HTTP) is a stateless application-level protocol for distributed, collaborative, hypertext information systems. This document describes the overall architecture of HTTP, establishes common terminology, and defines aspects of&quot; data-og-host=&quot;www.rfc-editor.org&quot; data-og-source-url=&quot;https://www.rfc-editor.org/rfc/rfc9110.html&quot; data-og-url=&quot;https://www.rfc-editor.org/rfc/rfc9110.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc9110.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.rfc-editor.org/rfc/rfc9110.html&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;RFC 9110: HTTP Semantics&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The Hypertext Transfer Protocol (HTTP) is a stateless application-level protocol for distributed, collaborative, hypertext information systems. This document describes the overall architecture of HTTP, establishes common terminology, and defines aspects of&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.rfc-editor.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;STD 97 (RFC 9110) HTTP Semantics 문서에서 이에 관한 내용을 찾을 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 중 8.4. Content-Encoding 섹션을 보면 다음과 같은 문장이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc9110.html#section-8.4&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.rfc-editor.org/rfc/rfc9110.html#section-8.4&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1744146473844&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;RFC 9110: HTTP Semantics&quot; data-og-description=&quot;The Hypertext Transfer Protocol (HTTP) is a stateless application-level protocol for distributed, collaborative, hypertext information systems. This document describes the overall architecture of HTTP, establishes common terminology, and defines aspects of&quot; data-og-host=&quot;www.rfc-editor.org&quot; data-og-source-url=&quot;https://www.rfc-editor.org/rfc/rfc9110.html#section-8.4&quot; data-og-url=&quot;https://www.rfc-editor.org/rfc/rfc9110.html#section-8.4&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc9110.html#section-8.4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.rfc-editor.org/rfc/rfc9110.html#section-8.4&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;RFC 9110: HTTP Semantics&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The Hypertext Transfer Protocol (HTTP) is a stateless application-level protocol for distributed, collaborative, hypertext information systems. This document describes the overall architecture of HTTP, establishes common terminology, and defines aspects of&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.rfc-editor.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(RFC 7231 3.1.2.2. Content-Encoding과 같은 내용)&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;The &quot;Content-Encoding&quot; header field indicates what content codings have been applied to the representation, beyond those inherent in the media type, and thus what decoding mechanisms have to be applied in order to obtain data in the media type referenced by the Content-Type header field. Content-Encoding is primarily used to allow a representation's data to be compressed without losing the identity of its underlying media type.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;Content-Encoding&quot;&amp;nbsp;헤더&amp;nbsp;필드는&amp;nbsp;미디어&amp;nbsp;유형에&amp;nbsp;내재된&amp;nbsp;것&amp;nbsp;외에&amp;nbsp;어떤&amp;nbsp;콘텐츠&amp;nbsp;코딩이&amp;nbsp;표현에&amp;nbsp;적용되었는지,&amp;nbsp;따라서&amp;nbsp;Content-Type&amp;nbsp;헤더&amp;nbsp;필드에서&amp;nbsp;참조하는&amp;nbsp;미디어&amp;nbsp;유형의&amp;nbsp;데이터를&amp;nbsp;얻기&amp;nbsp;위해&amp;nbsp;어떤&amp;nbsp;디코딩&amp;nbsp;메커니즘을&amp;nbsp;적용해야&amp;nbsp;하는지를&amp;nbsp;나타냅니다.&amp;nbsp;콘텐츠&amp;nbsp;인코딩은&amp;nbsp;주로&amp;nbsp;기본&amp;nbsp;미디어&amp;nbsp;유형의&amp;nbsp;정체성을&amp;nbsp;잃지&amp;nbsp;않고&amp;nbsp;표현의&amp;nbsp;데이터를&amp;nbsp;압축할&amp;nbsp;수&amp;nbsp;있도록&amp;nbsp;하는&amp;nbsp;데&amp;nbsp;사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(중략)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Unlike Transfer-Encoding (Section 6.1 of [HTTP/1.1]), the codings listed in Content-Encoding are a characteristic of the representation; the representation is defined in terms of the coded form, and all other metadata about the representation is about the coded form unless otherwise noted in the metadata definition. Typically, the representation is only decoded just prior to rendering or analogous usage.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전송-인코딩([HTTP/1.1]&amp;nbsp;6.1절)과&amp;nbsp;달리&amp;nbsp;Content-Encoding에&amp;nbsp;나열된&amp;nbsp;코딩은&amp;nbsp;표현의&amp;nbsp;특징입니다;&amp;nbsp;표현은&amp;nbsp;코딩된&amp;nbsp;형식의&amp;nbsp;관점에서&amp;nbsp;정의되며,&amp;nbsp;메타데이터&amp;nbsp;정의에&amp;nbsp;달리&amp;nbsp;명시되지&amp;nbsp;않는&amp;nbsp;한&amp;nbsp;표현에&amp;nbsp;대한&amp;nbsp;다른&amp;nbsp;모든&amp;nbsp;메타데이터는&amp;nbsp;코딩된&amp;nbsp;형식에&amp;nbsp;관한&amp;nbsp;것입니다.&amp;nbsp;일반적으로&amp;nbsp;표현은&amp;nbsp;렌더링&amp;nbsp;또는&amp;nbsp;이와&amp;nbsp;유사한&amp;nbsp;사용&amp;nbsp;직전에만&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;이 문서를 포함한 다른 문서에서 &quot;반드시 gzip과 같은 압축을 해제해야 한다&quot;라는 문구를 찾을 수는 없었으나 위의 내용을 바탕으로 원하는 답을 얻을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콘텐츠 인코딩 = 기본 미디어 유형의 정체성을 잃지 않고 데이터를 압축할 수 있도록 하는 데 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Content-Encoding 필드 = 미디어 유형의 데이터를 얻기 위해 어떤 디코딩 메커니즘을 적용해야 하는지 나타냄&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디코딩 시점 = 렌더링 또는 이와 유사한 사용 직전에만 디코딩&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 결론을 내릴 수 있을 것으로 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Content-Encoding 필드는 원하는 데이터 압축한 뒤 이 데이터를 디코딩 하기 위해서 어떤 메커니즘을 적용해야 하는지 알려주는 필드이며 이 필드를 참고하여 렌더링과 같이 실제 그 데이터를 사용하는 시점에 정해진 메커니즘으로 디코딩하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RFC 문서와 인터넷 표준 문서를 &quot;반드시&quot; 준수해야 하는 것은 아니지만 앞서 언급했듯이 표준을 따르며 다양한 이점이 따라오기 때문에 표준을 준수하는 것이 좋을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 다양한 요소들이 종합되어 브라우저는 gzip의 압축을 자동으로 헤제할 수 있도록 만들어 진 것으로 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 위에서 확인했던 .gz 데이터를 수신할 당시의 헤더는 다음과 같다.&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;122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKWVra/btsNdKI8pH1/KeH2MiXqXvNYK8apjCpoEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKWVra/btsNdKI8pH1/KeH2MiXqXvNYK8apjCpoEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKWVra/btsNdKI8pH1/KeH2MiXqXvNYK8apjCpoEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKWVra%2FbtsNdKI8pH1%2FKeH2MiXqXvNYK8apjCpoEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;122&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;122&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;위의 내용대로 해석해보면 Content-Type에 해당하는 application/json 파일을 원하고 있으며 이 데이터는 gzip 방식으로 인코딩 되어있다는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 말해 브라우저에서 기대했던 데이터는 json이며 이는 gzip 방식으로 압축되어 있다고 서버에서 응답하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저는 자신이 원하는 json을 얻기 위해서는 이를 직접 사용하는 시점(렌더링 등)에 gzip 방식의 디코딩 메커니즘을 사용할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저가 압축을 해제해야 한다는 명확한 문구는 찾을 수 없었으나 Content-Encoding 필드와 gzip이 관련되어 있다는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 인터넷 표준이라는 특성상 법적으로 이 표준의 준수가 강제되지 않지만 이를 따르게 되면서 오는 다양한 이점 때문에 이를 준수하고자 노력하고 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 현재 운영중인 브라우저들이 모두 웹 표준을 준수하고 있다고 한다면 gzip으로 압축된 데이터가 그 브라우저들에 의해 자동으로 압축이 해제될 것으로 기대해도 될 것으로 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 서비스 최적화 기법 중 gzip을 사용하더라도 대부분의 브라우저가 이를 자동으로 해제할 수 있음을 기대해도 되는 것이다.&lt;/p&gt;</description>
      <category>개발/개념</category>
      <author>단진</author>
      <guid isPermaLink="true">https://29223.tistory.com/176</guid>
      <comments>https://29223.tistory.com/176#entry176comment</comments>
      <pubDate>Wed, 9 Apr 2025 06:26:09 +0900</pubDate>
    </item>
    <item>
      <title>[2024년 하반기] 2024 하반기 회고</title>
      <link>https://29223.tistory.com/175</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2024 하반기.png&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJXcdo/btsLAB8UYxX/zn1UqvCJgGwMyNPClFrhR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJXcdo/btsLAB8UYxX/zn1UqvCJgGwMyNPClFrhR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJXcdo/btsLAB8UYxX/zn1UqvCJgGwMyNPClFrhR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJXcdo%2FbtsLAB8UYxX%2Fzn1UqvCJgGwMyNPClFrhR1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1100&quot; height=&quot;600&quot; data-filename=&quot;2024 하반기.png&quot; data-origin-width=&quot;1100&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2024년이 끝났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2024년은 유난히 긴 한 해가 되었던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;아쉬운 점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상반기에는 프로젝트가 무산되는 등 힘든 일이 많았지만 하반기에는 그래도 좋은 일이 많이 있었던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선, 현장실습 인턴으로 미리디에 입사하여 약 4개월간 서비스 유지보수 및 고도화 작업을 할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 함께 상반기부터 하던 공모전과 오픈소스 컨트리뷰션도 함께 참여했기 때문에 바쁜 하반기가 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 하반기에 아쉬웠던 점으로는 체력 관리와 규칙적인 생활 패턴을 유지하지 못했던 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8시부터 11시 사이에 출근하면 되는 자율 출퇴근제를 운영하는 회사였지만 나는 일찍 퇴근하는 것이 좋아 8시 30분에 출근을 하고자 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되면 퇴근은 5시 30분에 하기 때문에 집에 와서는 개인적인 공부와 다른 작업들을 할 수 있을 것이라 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 점점 시간이 갈 수록 내 출근 시간은 9시 30분에 가까워졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퇴근하면 무언가를 할 수 있을 것이라 기대했지만 생각보다 낮에 많은 에너지가 소모되었고 집에 와서는 거의 아무것도 할 수 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계획했던 것들을 제대로 할 수 없었으며 날씨가 추워지면서 출근 시간이 점점 늦어지게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퇴근하고 남는 시간에 뭐든지 할 수 있도록 규칙적인 생활과 체력 관리의 중요성을 다시 한 번 느끼게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;잘한 점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 업무를 수행할 때 기존에 습관처럼 하던 문서화가 많은 도움이 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서화라고 해서 거창한 것이 아니라 문제를 정의하고 해결 방법에 대한 가설을 세우고 이에 대해 실험한 뒤 결론을 도출하는 일종의 연구노트와 더 가까운 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 문서화를 하게 되면 내가 어떤 생각을 갖고 어떤 행동을 했는지 조금 더 기억하기 쉬울 뿐만 아니라 다른 사람이 이 작업의 히스토리를 파악하는 과정이 좀 더 수월할 것이라 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;업무적&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업무적으로 위와 같은 문서화 뿐만 아니라 기존에 알고 있던 CS 지식들을 활용하여 문제를 해결할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간이 오래 걸리기는 했으나 결코 깊이가 얕다고 볼 수 없는 문제였다는 피드백을 들을 수 있었으며 이를 해결했을 때 엄청난 성취감을 느꼈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제의 핵심은 JS에 있는 이벤트 루프의 내용 중 마이크로 태스크 큐와 매크로 태스크 큐의 우선순위와 관련되어 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Promise의 경우 마이크로 태스크 큐에 포함되며 setTImeout은 매크로 태스크 큐에 포함되는 개념이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 두 비동기 방식은 모두 콜스택이 비어있을 때 실행된다는 특징이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 여러 종류의 비동기 방식을 처리하기 위해 JS의 이벤트 루프에서는 각 단계에 우선순위를 두고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마이크로 태스크 큐가 매크로 태스크 큐 보다 우선순위가 높다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제가 발생한 부분은 매크로 태스크 큐에 있는 콜백함수가 실행되기 전 마이크로 태스크 큐에 있는 콜백함수에 의해 특정 값을 참조하기 때문에 발생한 문제였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해 다시 한 번 이벤트 루프에 대해 배울 수 있었고 코드 베이스의 실행 흐름을 도표로 나타내는 과정을 통해 코드 베이스에 대해 보다 더 깊은 이해를 할 수 있었던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로 문서화를 통해 동료 개발자에게 이 문제에 대해 쉽게 설명할 수 있었고 이 문제의 근본적인 해결 방법을 제시할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 언급한 내용 이외에도 다양한 문제들을 마주할 수 있었고 서비스의 특성상 CS 지식이 받쳐주지 않는다면 어떻게 해결할 수 있을지 막막했을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 한 번 기초가 중요함을 깨닫는 계기가 된 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;업무 외&lt;/h3&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;오픈소스 컨트리뷰션의 경우 성공적으로 발표까지 할 수 있었으며 Node.js 팀은 전체 프로젝트 중 1위(장관상)를 수상할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 받아보는 장관상이었으며 몇 개월의 노력으로 배운 점도 많았지만 그 노력들이 결실을 맺을 수 있었기에 더 보람찼던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 상반기부터 진행했던 공모전에서도 최우수 상을 받을 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업무 관리를 위해 노력했으며 주말을 이용해 밀린 작업들을 할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 성공적으로 서비스를 출시할 수 있었고 심사위원들 앞에서 발표할 수 있는 기회도 갖게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전 산업보안 논문 공모전을 제외한 첫 개발 관련 공모전에서 수상을 할 수 있어 좋았던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 공모전을 하면서 React Native에 조금 더 다뤄볼 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React와는 다른 React Native 만의 매력이 있는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람들의 일상에 빠질 수 없는 것이 모바일 기기인데 조금 더 많은 사람들에게 내 서비스가 닿게 하기 위해서는 모바일 서비스 개발을 하는 것도 좋은 방법이 될 수 있을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;앞으로&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현장 실습생일 때 회사에서 수습기간 리뷰를 한 적이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;담당 멘토분과 서로에 대해 피드백하고 앞으로 어떤 것을 새롭게 시도해볼 수 있을지 서로 이야기 하는 자리였다.&lt;/p&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;blockquote data-ke-style=&quot;style3&quot;&gt;이들의 &quot;동료&quot;가 되고자 노력해보면 좋을 것 같다.&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인턴 등으로 새로운 조직에 들어가면 자신을 증명하기 위해 노력하곤 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자신의 실력을 뽐내고자 하는 사람도 있을 것이고, 많은 시간을 회사에서 보내며 스스로의 열정을 보여주는 사람도 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나 또한 주어진 업무를 성공적으로 끝내기 위한 마음가짐을 갖고 있는 상태였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저 말을 들은 후 나름의 충격(?)을 받았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별거 아닌 말이라 생각할 수 있지만 최소한 나는 어떤 조직에서 조직원이 되기 위해 가져야 할 진짜 마음 가짐이 저것이라 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 구인 공고를 보면 &quot;협업 경험이 있는 사람&quot;, &quot;다양한 직군과 원활한 소통이 가능한 사람&quot; 등의 조건을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 말하는 것이 단순히 뛰어난 소통 능력을 요구하는 것일 수도 있지만 한 조직에 어떻게 녹아들고 서로를 동료로 의식할 수 있도록 만드는 능력을 요구하는 것일수도 있다는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저 말을 들은 이후 나는 좀 더 많은 질문을 하고자 했으며 모르는 부분이 있거나 내 해결 방법에 대해 의문이 드는 경우 스스로 생각하기 보다는 주변의 동료에게 도움을 요청하고 의견을 듣고자 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정에서 느낀 것은 코드를 스스로 읽고 이해하는데 5일의 시간이 걸린다면 이 코드를 작성한 동료를 찾아가거나 이 작업에 참여한 동료를 찾아간다면 5분만에 끝나는 경우도 있다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동료도 모든 것을 기억할 수는 없지만 최소한 참고할 만한 파일, 코드, 문서 등에 대한 정보를 얻을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;질문이 문제를 빠르게 해결하는 방법이 될 수도 있지만, 동료들과 더 많은 얘기를 할 수도 있고 보다 더 가까워 질 수 있는 방법이라 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자신의 실력을 보여주는 것도 중요하지만 그와 동시에 개개인이 모여 시너지를 낼 수 있는 방법을 찾는 것도 좋은 방법이 될 수 있을 것이라는 생각을 갖게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 생각들을 바탕으로 내년에 어떤 조직에 포함되건 그들의 동료가 되기 위해 노력할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2025년이 시작하자 마자 미국으로 교환학생을 갈 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미국으로 가서 한 학기를 보내는 것이 나름 도전이 될 수 도 있을 것이라 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 사람을 만나고 새로운 환경에서 낯선 사람들과 한국어가 아닌 언어로 대화한다는 것이 부담이 될 수 있으나 이 또한 배울 점이 있을 것이라 기대한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내년 한 해는 취업 전 마지막 장기 휴가(?)가 될 것 같기도 하고 나름의 배울점도 있을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2025년에는 조금 더 시간 관리, 건강 관리를 잘 할 수 있도록 노력해야겠다고 다짐한다.&lt;/p&gt;</description>
      <category>개발/회고</category>
      <author>단진</author>
      <guid isPermaLink="true">https://29223.tistory.com/175</guid>
      <comments>https://29223.tistory.com/175#entry175comment</comments>
      <pubDate>Tue, 31 Dec 2024 21:38:27 +0900</pubDate>
    </item>
    <item>
      <title>[Node.js] Node.js와 V8 (with. ECMAScript)</title>
      <link>https://29223.tistory.com/174</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Nodejs.png&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ur6Yg/btsJ4a4HqmZ/eEAnB2F3i29F4K4lxbdch1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ur6Yg/btsJ4a4HqmZ/eEAnB2F3i29F4K4lxbdch1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ur6Yg/btsJ4a4HqmZ/eEAnB2F3i29F4K4lxbdch1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUr6Yg%2FbtsJ4a4HqmZ%2FeEAnB2F3i29F4K4lxbdch1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1100&quot; height=&quot;600&quot; data-filename=&quot;Nodejs.png&quot; data-origin-width=&quot;1100&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;Node.js 뿐만 아니라 자바스크립트에서 가장 중요한 개념 중 JS 엔진이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JS 엔진 중 가장 유명하고 자주 사용되는 것이 V8 엔진이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;V8 엔진과 Node.js의 관계, 그리고 ECMAScript와 JavaScript의 관계가 어떻게 되는 것일까?&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;ECMAScript vs JavaScript&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 일반적으로 JavaScript를 사용해서 프로그래밍 한다고 표현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScript를 다루다 보면 ES6, ES2024와 같은 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로 ES6의 경우 일반적으로 JavaScript에서 많이 사용되는 const, let 등이 추가된 버전이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 ECMAScript와 JavaScript의 관계는 어떻게 될까?&lt;br /&gt;&lt;br /&gt;ECMAScript의 경우 JavaScript의 표준이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ES6의 경우 2015년에 도입된 JavaScript의 6번째 표준안이라 ES6라는 이름을 갖는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ECMAScript와 JavaScript는 같은 것일까?&lt;br /&gt;&lt;br /&gt;둘의 관계는 &quot;표준어&quot;와 &quot;방언&quot;의 관계로 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ECMAScript는 표준을 제시하고 JavaScript는 이를 따르고 있다. (ECMA-262)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, JavaScript는 ECMAScript의 표준을 따르는 대표적인 언어로 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트 언어들의 표준을 제시하기에 이 표준을 따르는 새로운 언어를 만들 수 있으며 JavaScript는 그 중 대표적인 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일종의 레시피로 볼 수 있으며 JavaScript는 이 레시피를 기반으로 만든 음식에 비유할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/912479/what-is-the-difference-between-javascript-and-ecmascript&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://stackoverflow.com/questions/912479/what-is-the-difference-between-javascript-and-ecmascript&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1728714191418&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;What is the difference between JavaScript and ECMAScript?&quot; data-og-description=&quot;What's the difference between ECMAScript and JavaScript? From what I've deduced, ECMAScript is the standard and JavaScript is the implementation. Is this correct?&quot; data-og-host=&quot;stackoverflow.com&quot; data-og-source-url=&quot;https://stackoverflow.com/questions/912479/what-is-the-difference-between-javascript-and-ecmascript&quot; data-og-url=&quot;https://stackoverflow.com/questions/912479/what-is-the-difference-between-javascript-and-ecmascript&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/botsBt/hyXhJ6LMJ9/neCA8zkxXvOO69BsYCIj9k/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/912479/what-is-the-difference-between-javascript-and-ecmascript&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stackoverflow.com/questions/912479/what-is-the-difference-between-javascript-and-ecmascript&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/botsBt/hyXhJ6LMJ9/neCA8zkxXvOO69BsYCIj9k/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316');&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;What is the difference between JavaScript and ECMAScript?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;What's the difference between ECMAScript and JavaScript? From what I've deduced, ECMAScript is the standard and JavaScript is the implementation. Is this correct?&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;stackoverflow.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람들이 레시피를 보고 음식을 만들 때 자신만의 재료를 추가하거나 조리 방식을 달리 하는 등 자신의 입맛에 맞게 이를 변형하곤 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 레시피를 따르며 음식을 한다면 음식의 본질이 바뀌지는 않을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(새우튀김이 오징어튀김이 되어 버린다거나)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 JavaScript는 ECMAScript를 준수하는 가장 대표적인 스크립트 언어로 볼 수 있으며 ECMAScript의 표준을 따르면서 다양한 환경에서 사용될 수 있는 기능들을 담고 있는 언어인 셈이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScript는 ECMAScript라는 뼈대를 갖고 있으며 클라이언트 사이트 Web API (DOM, fetch 등)을 포함하고 있는 개념이다.&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;V8&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 V8을 설명하기 위해 왜 ECMAScript가 필요한 것인가?&lt;/p&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://v8.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://v8.dev/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1728714647318&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;V8 JavaScript engine&quot; data-og-description=&quot;&quot; data-og-host=&quot;v8.dev&quot; data-og-source-url=&quot;https://v8.dev/&quot; data-og-url=&quot;https://v8.dev/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://v8.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://v8.dev/&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;V8 JavaScript engine&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;v8.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 v8 공식 문서를 보면 아래와 같은 내용이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;V8은 C++로 작성된 Google의 오픈 소스 고성능 자바스크립트 및 웹어셈블리 엔진입니다. Chrome과 Node.js 등에서 사용됩니다. ECMAScript 및 WebAssembly를 구현하며 x64, IA-32 또는 ARM 프로세서를 사용하는 Windows, macOS 및 Linux 시스템에서 실행됩니다. V8은 모든 C++ 애플리케이션에 임베드할 수 있습니다.&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;V8 엔진의 경우 자바스크립트 엔진이면서 ECMAScript를 구현하고 있다고 한다.&lt;/p&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://v8.dev/blog/modern-javascript#measuring-conformance&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://v8.dev/blog/modern-javascript#measuring-conformance&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1728714935781&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;ES2015, ES2016, and beyond &amp;middot; V8&quot; data-og-description=&quot;The V8 team places great importance on the evolution of JavaScript into an increasingly expressive and well-defined language that makes writing fast, safe, and correct web applications easy. In June 2015, the ES2015 specification was ratified by the TC39 s&quot; data-og-host=&quot;v8.dev&quot; data-og-source-url=&quot;https://v8.dev/blog/modern-javascript#measuring-conformance&quot; data-og-url=&quot;https://v8.dev/blog/modern-javascript#measuring-conformance&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ITmlj/hyXhSby71R/sFe49mTIMJJexk132DuMAk/img.png?width=652&amp;amp;height=241&amp;amp;face=0_0_652_241&quot;&gt;&lt;a href=&quot;https://v8.dev/blog/modern-javascript#measuring-conformance&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://v8.dev/blog/modern-javascript#measuring-conformance&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ITmlj/hyXhSby71R/sFe49mTIMJJexk132DuMAk/img.png?width=652&amp;amp;height=241&amp;amp;face=0_0_652_241');&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;ES2015, ES2016, and beyond &amp;middot; V8&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The V8 team places great importance on the evolution of JavaScript into an increasingly expressive and well-defined language that makes writing fast, safe, and correct web applications easy. In June 2015, the ES2015 specification was ratified by the TC39 s&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;v8.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 문서를 보면 V8은 2016년 기준 ES2015의 98%, ES2016의 100%를 지원한다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;V8 supports compliance with the &amp;ldquo;continually maintained draft future ECMAScript standard&amp;rdquo;!&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;또한, 위와 같이 V8은 향후 ECMAScript 표준도 준수할 것이라 작성되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 V8 엔진을 JavaScript 엔진 중 하나로 알고 있는데 V8은 스스로를 ECMAScript 표준을 준수한 엔진이라 설명하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ECMAScript와 JavaScript의 관계를 본다면 왜 V8은 JavaScript 엔진인데 스스로는 ECMAScript를 준수한다고 작성했는지 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;V8은 JavaScript 엔진이며 ECMAScript 표준을 준수한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScript는 ECMAScript를 준수하는 스크립트 언어다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 V8은 ECMAScript 표준을 준수하는 JavaScript 엔진이다라고 생각해도 이상한 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;브라우저&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node.js는 JavaScript 런타임이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 브라우저에서 사용하던 JavaScript를 실행하기 위한 또 다른 런타임으로 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 브라우저에서만 사용하던 JavaScript를 사용해 서버를 구축하는 등의 작업을 수행할 수 있게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;V8을 포함한 브라우저와 Node.js의 차이점은 무엇일까?&lt;br /&gt;&lt;br /&gt;앞서 JavaScript는 ECMAScript 표준을 준수하는 스크립트 언어라고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 브라우저도 ECMAScript 표준을 준수하고 있고 Node.js는 내부적으로 v8을 포함하고 있으며 v8 또한 ECMAScript를 준수하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘의 관계를 확인해보면 아래와 같은 모습이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1062&quot; data-origin-height=&quot;791&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/phry6/btsJ3YKabew/CXBmgrAdpQWFxaBOCpQfk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/phry6/btsJ3YKabew/CXBmgrAdpQWFxaBOCpQfk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/phry6/btsJ3YKabew/CXBmgrAdpQWFxaBOCpQfk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fphry6%2FbtsJ3YKabew%2FCXBmgrAdpQWFxaBOCpQfk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1062&quot; height=&quot;791&quot; data-origin-width=&quot;1062&quot; data-origin-height=&quot;791&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저와 Node.js는 모두 ECMAScript 표준을 준수하고 있으며 각 환경에 맞는 API들을 추가로 제공하고 있음을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 path 모듈의 경우 브라우저에서는 지원하지 않는 Node.js의 모듈이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전 Node.js 기여 과정에서 볼 수 있듯이 path 모듈의 내부는 JavaScript로 작성되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 순수 JavaScript로 작성된 path 모듈의 구현체는 Node.js에서 제공하는 여러 모듈 중 하나이며 이는 개발 과정의 편의를 위한 모듈 중 하나인 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순수한 JavaScript로 작성되어 있기에 ECMAScript를 준수하고 있다고 볼 수 있으며 이는 V8에서 실행하는데 문제가 없다고 볼 수 있다.&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;V8&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;V8은 결국 JavaScript를 실행하기 위한 엔진이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정을 위해 구문 분석을 통해 AST를 생성하는 등 일련의 과정을 거친다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약, 새로운 문법이 ECMAScript에 새롭게 추가된다면 V8 엔진은 이 문법을 읽을 수 있도록 업데이트 되어야 하며 이것이 결국 V8이 ECMAScript 표준을 따른다고 볼 수 있는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;const a = new Class()와 같이 작성한다면 a에 할당되는 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;이때 힙 영역에 해당 데이터를 저장하는 것을 V8에서 담당한다고 보면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 Node.js는 V8과 동일하다고 할 수는 없지만 (Node.js는 V8엔진을 포함한 다른 여러 라이브러리를 포함하고 있는 런타임 환경이므로) Node.js가 지원하는 ECMAScript 버전은 V8에 종속적이라 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node.js는 JavaScript를 위한 하나의 런타임을 제공하고 있는 것이며 내부적으로 다양한 라이브러리를 포함하고 있지만 결국 Node.js의 JavaScript를 실행하는 것은 V8엔진이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node.js 내부 코드를 보면 C++로 작성된 부분도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 C++로 작성된 코드들은 바인딩 되어 JS에서 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;V8 엔진은 JavaScript를 실행하는 과정에서 C++로 작성된 코드가 포함된 경우 해당 C++ 모듈도 호출하여 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행한 C++ 코드의 결과는 다시 JavaScript 환경으로 반환되어 JavaScript 환경에서 사용할 수 있게 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 Node.js와 V8엔진은 ECMAScript라는 표준에 의해 연결되어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 V8의 역할을 이해한다면 Node.js의 코드에 왜 C++ 코드들이 포함되어 있는지 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;V8의 경우 강력한 JavaScript 엔진이며 ECMAScript 표준을 준수하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 사용하는 JavaScript는 ECMAScript 표준을 준수하는 스크립트 언어다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node.js에서 V8의 역할은 우리가 ECMAScript를 준수하는 JavaScript를 사용할 수 있음을 보장하는 것이며 Node.js는 브라우저 밖에서 JavaScript를 실행할 수 있는 런타임이자 서버사이드 개발에 필요한 다양한 모듈을 제공하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 Node.js에서 사용하는 다양한 모듈들은 JavaScript (또는, C++)로 구성되어 있으며 이는 순수한 ECMAScript를 준수하기 때문에 V8 엔진에서 실행할 수 있는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 웹 표준과 관련이 있을 수도 있지만 Node.js에서 제공하는 여러 유틸리티로 보는 것이 적절하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Node.js가 준수하는 웹 표준 이외의 다른 사용자 친화적인 모듈이 있을 수 있다는 것이다.&lt;/p&gt;</description>
      <category>개발/개념</category>
      <author>단진</author>
      <guid isPermaLink="true">https://29223.tistory.com/174</guid>
      <comments>https://29223.tistory.com/174#entry174comment</comments>
      <pubDate>Sat, 12 Oct 2024 17:40:56 +0900</pubDate>
    </item>
    <item>
      <title>[Node.js 기여하기] 3. path 모듈 resolve 함수 성능 올리기</title>
      <link>https://29223.tistory.com/173</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Nodejs.png&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ndtAZ/btsJt9T91Kb/UcmNbbzhgWaXGbccwIgjPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ndtAZ/btsJt9T91Kb/UcmNbbzhgWaXGbccwIgjPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ndtAZ/btsJt9T91Kb/UcmNbbzhgWaXGbccwIgjPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FndtAZ%2FbtsJt9T91Kb%2FUcmNbbzhgWaXGbccwIgjPK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1100&quot; height=&quot;600&quot; data-filename=&quot;Nodejs.png&quot; data-origin-width=&quot;1100&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;&lt;a href=&quot;https://github.com/nodejs/node/pull/54835&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/nodejs/node/pull/54835&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1725777848962&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;path: remove repetitive conditional operator in &amp;#96;posix.resolve&amp;#96; by HBSPS &amp;middot; Pull Request #54835 &amp;middot; nodejs/node&quot; data-og-description=&quot;confidence improvement accuracy (*) (**) (***) path/resolve-posix.js n=100000 paths='' *** 7.32 % &amp;plusmn;2.03% &amp;plusmn;2.70% &amp;plusmn;3.51% path/resolve-po...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/nodejs/node/pull/54835&quot; data-og-url=&quot;https://github.com/nodejs/node/pull/54835&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bIfkcB/hyW208lXBm/KWKKCm3o9KKRxrkgl7fvJk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/nodejs/node/pull/54835&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/nodejs/node/pull/54835&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bIfkcB/hyW208lXBm/KWKKCm3o9KKRxrkgl7fvJk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;path: remove repetitive conditional operator in `posix.resolve` by HBSPS &amp;middot; Pull Request #54835 &amp;middot; nodejs/node&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;confidence improvement accuracy (*) (**) (***) path/resolve-posix.js n=100000 paths='' *** 7.32 % &amp;plusmn;2.03% &amp;plusmn;2.70% &amp;plusmn;3.51% path/resolve-po...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저번과 유사하게 path 모듈의 성능을 올릴 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떻게 보면 첫 번째 PR과 유사한 방식으로 개선한 것 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1725777944353&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;resolve(...args) {
    let resolvedPath = '';
    let resolvedAbsolute = false;

    for (let i = args.length - 1; i &amp;gt;= -1 &amp;amp;&amp;amp; !resolvedAbsolute; i--) {
      const path = i &amp;gt;= 0 ? args[i] : posixCwd();
      validateString(path, `paths[${i}]`);

      // Skip empty entries
      if (path.length === 0) {
        continue;
      }

      resolvedPath = `${path}/${resolvedPath}`;
      resolvedAbsolute =
        StringPrototypeCharCodeAt(path, 0) === CHAR_FORWARD_SLASH;
    }

    // At this point the path should be resolved to a full absolute path, but
    // handle relative paths to be safe (might happen when process.cwd() fails)

    // Normalize the path
    resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute, '/',
                                   isPosixPathSeparator);

    if (resolvedAbsolute) {
      return `/${resolvedPath}`;
    }
    return resolvedPath.length &amp;gt; 0 ? resolvedPath : '.';
},&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 함수는 path 모듈 중에서도 posix의 resolve 함수다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 함수의 자세한 사용 방법은 아래 공식문서를 참고하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nodejs.org/api/path.html#pathresolvepaths&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://nodejs.org/api/path.html#pathresolvepaths&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1725778133689&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;Path | Node.js v22.8.0 Documentation&quot; data-og-description=&quot;Path# Source Code: lib/path.js The node:path module provides utilities for working with file and directory paths. It can be accessed using: const path = require('node:path'); copy Windows vs. POSIX# The default operation of the node:path module varies base&quot; data-og-host=&quot;nodejs.org&quot; data-og-source-url=&quot;https://nodejs.org/api/path.html#pathresolvepaths&quot; data-og-url=&quot;https://nodejs.org/api/path.html#pathresolvepaths&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://nodejs.org/api/path.html#pathresolvepaths&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nodejs.org/api/path.html#pathresolvepaths&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;Path | Node.js v22.8.0 Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Path# Source Code: lib/path.js The node:path module provides utilities for working with file and directory paths. It can be accessed using: const path = require('node:path'); copy Windows vs. POSIX# The default operation of the node:path module varies base&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;nodejs.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개선점 찾기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 위의 함수가 하는 역할에 대해 알아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 함수는 join과 유사하게 동작하지만 인자로 전달된 경로 조각들 중 가장 마지막에 있는 절대경로를 기준으로 경로를 합치게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약, 인자에 절대 경로가 포함되어 있지 않다면 process.cwd()를 기준으로 반환하게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1725778273564&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for (let i = args.length - 1; i &amp;gt;= -1 &amp;amp;&amp;amp; !resolvedAbsolute; i--) {
  const path = i &amp;gt;= 0 ? args[i] : posixCwd();
  validateString(path, `paths[${i}]`);

  // Skip empty entries
  if (path.length === 0) {
    continue;
  }

  resolvedPath = `${path}/${resolvedPath}`;
  resolvedAbsolute =
    StringPrototypeCharCodeAt(path, 0) === CHAR_FORWARD_SLASH;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 for loop를 거꾸로 돌면서 가장 마지막의 절대 경로를 찾는 작업을 하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약, i가 -1이라면(인자 중 절대 경로가 포함되지 않은 경우) cwd를 기준으로 반환하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 위의 for loop에서 불필요한 부분이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;path 변수는 조건에 args[i] 또는 posixCwd (실제로는 process.cwd()와 동일)로 설정된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 posixCwd를 사용하는 경우는 모든 순회가 끝난 뒤 마지막 한 번 밖에 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 path 변수를 정하기 위해 삼항 연산자가 계속 실행될 필요가 없다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개선 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 개선하기 위해서는 불필요하게 반복되는 삼항 연산자를 제거하면 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로 아래 흐름과 같이 구성한다면 불필요한 연산을 줄일 수 있을 것으로 기대했다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;path 변수는 항상 args[i]가 된다.&lt;/li&gt;
&lt;li&gt;반복문은 args 갯수만큼 수행한다.&lt;/li&gt;
&lt;li&gt;반복문이 종료되었지만 절대 경로가 없는 경우 최종 경로 문자열 앞에 posixCwd를 추가한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 흐름을 코드로 나타내면 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1725778768401&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;resolve(...args) {
    let resolvedPath = '';
    let resolvedAbsolute = false;

    for (let i = args.length - 1; i &amp;gt;= 0 &amp;amp;&amp;amp; !resolvedAbsolute; i--) {
      const path = args[i];
      validateString(path, `paths[${i}]`);

      // Skip empty entries
      if (path.length === 0) {
        continue;
      }
      resolvedPath = `${path}/${resolvedPath}`;
      resolvedAbsolute =
        StringPrototypeCharCodeAt(path, 0) === CHAR_FORWARD_SLASH;
    }

    if (!resolvedAbsolute) {
      const cwd = posixCwd();
      resolvedPath = `${cwd}/${resolvedPath}`;
      resolvedAbsolute =
        StringPrototypeCharCodeAt(cwd, 0) === CHAR_FORWARD_SLASH;
    }

    // At this point the path should be resolved to a full absolute path, but
    // handle relative paths to be safe (might happen when process.cwd() fails)

    // Normalize the path
    resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute, '/',
                                   isPosixPathSeparator);
    if (resolvedAbsolute) {
      return `/${resolvedPath}`;
    }
    return resolvedPath.length &amp;gt; 0 ? resolvedPath : '.';
},&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로 path를 설정하기 위한 삼항 연산자를 제거하고 마지막에 절대 경로를 포함했는지 여부를 확인하여 posixCwd를 추가하도록 수정했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시행착오&lt;/h3&gt;
&lt;pre id=&quot;code_1725779196296&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (!resolvedAbsolute) {
  const cwd = posixCwd();
  resolvedPath = `${cwd}/${resolvedPath}`;
  resolvedAbsolute =
    StringPrototypeCharCodeAt(cwd, 0) === CHAR_FORWARD_SLASH;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분의 코드를 작성하며 시행착오를 겪었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 if문의 역할은 args 중 절대 경로가 없었다면 cwd를 resolvedPath에 추가하는 부분이다.&lt;/p&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_1725779260775&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (!resolvedAbsolute) {
  const cwd = posixCwd();
  resolvedPath = `${cwd}/${resolvedPath}`;
  resolvedAbsolute = true;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;posix에서 cwd는 절대 경로로 반환되기에 첫 글자를 확인할 필요 없이 resolvedAbsolute는 true가 된다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(맞는 말이긴 하다. process.cwd()를 실행 한다면 /Users/어쩌구와 같이 반환되기 때문이다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 작성하고 테스트를 돌린 결과 한 테스트 케이스를 통과하지 못했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 케이스만 통과하지 못했다면 수정하기 쉬울 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정한 코드가 제대로 된 기능을 하지 못한다면 해당 함수를 사용하는 다른 부분의 테스트도 함께 통과하지 못하는 경우가 많기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 통과하지 못한 테스트가 일부이며 해당 모듈과 직접적으로 관련된 (해당 모듈에 대한) 테스트일 경우는 특수한 케이스에 대해 실패했다고 볼 수 있을 것이다.&lt;/p&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_1725779474773&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (!common.isWindows) {
  // Test handling relative paths to be safe when process.cwd() fails.
  process.cwd = () =&amp;gt; '';
  assert.strictEqual(process.cwd(), '');
  const resolved = path.resolve();
  const expected = '.';
  assert.strictEqual(resolved, expected);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 마지막 assert를 통과하지 못했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;통과하지 못한 이유를 분석해보면 의도적으로 process.cwd()의 반환 값을 빈 문자열로 변경했음을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음과 같이 resolvedAbsolute를 단순히 true로 바꾸게 되면 위와 같이 process.cwd()가 기대한 반환값을 반환하지 못하는 경우에 문제가 될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 process.cwd()를 사용하더라도 첫 번째 글자를 확인해야 한다.&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;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저번 primordials와 for...of에 이어 이번에도 pollution에 관한 내용이 포함되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 pollution 관련 내용을 의도적으로 다루는 것은 아니지만 계속해서 관련된 내용이 나오는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 테스트 케이스를 통해 prototype pollution에 이어 함수의 반환값 자체를 바꿔버리는 것에 대해서도 대비를 하고 있는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 단순히 Node.js 안에서 원하는 기능을 꺼내 사용하기만 하면 되지만 이를 안정적으로 제공하기 위해 얼마나 많은 노력을 하고 있는지 알 수 있는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;벤치마크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유의미한 개선이 있는지 확인하기 위해 벤치마크를 수행한 결과는 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1725778939165&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;                                                                          confidence improvement accuracy (*)   (**)  (***)
path/resolve-posix.js n=100000 paths=''                                          ***      7.32 %       &amp;plusmn;2.03% &amp;plusmn;2.70% &amp;plusmn;3.51%
path/resolve-posix.js n=100000 paths='|'                                         ***      6.23 %       &amp;plusmn;0.74% &amp;plusmn;0.98% &amp;plusmn;1.28%
path/resolve-posix.js n=100000 paths='a/b/c/|../../..'                           ***      2.96 %       &amp;plusmn;0.55% &amp;plusmn;0.73% &amp;plusmn;0.95%
path/resolve-posix.js n=100000 paths='foo/bar|/tmp/file/|..|a/../subfile'                -0.08 %       &amp;plusmn;0.56% &amp;plusmn;0.75% &amp;plusmn;0.98%&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_1725778963813&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;                                                                          confidence improvement accuracy (*)   (**)  (***)
path/resolve-posix.js n=100000 paths=''                                          ***      9.61 %       &amp;plusmn;1.14% &amp;plusmn;1.51% &amp;plusmn;1.97%
path/resolve-posix.js n=100000 paths='|'                                         ***      8.55 %       &amp;plusmn;1.22% &amp;plusmn;1.62% &amp;plusmn;2.11%
path/resolve-posix.js n=100000 paths='a/b/c/|../../..'                           ***      3.15 %       &amp;plusmn;1.04% &amp;plusmn;1.39% &amp;plusmn;1.82%
path/resolve-posix.js n=100000 paths='foo/bar|/tmp/file/|..|a/../subfile'                -0.39 %       &amp;plusmn;0.90% &amp;plusmn;1.20% &amp;plusmn;1.57%&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Node.js Jenkins CI)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 환경 모두에서 유의미한 성능의 차이가 나타났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;PR&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 벤치마크 결과를 바탕으로 PR을 올릴 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1810&quot; data-origin-height=&quot;520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9axBM/btsJur72wqC/BJuAKVOIkbcnhyfxpapPCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9axBM/btsJur72wqC/BJuAKVOIkbcnhyfxpapPCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9axBM/btsJur72wqC/BJuAKVOIkbcnhyfxpapPCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9axBM%2FbtsJur72wqC%2FBJuAKVOIkbcnhyfxpapPCK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1810&quot; height=&quot;520&quot; data-origin-width=&quot;1810&quot; data-origin-height=&quot;520&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;벤치마크가 있는 함수 등을 수정하는 경우라면 벤치마크 결과가 가장 확실하게 이 코드를 설명할 수 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(개인적으로 성능을 개선하는데 관심이 많아 성능의 개선을 쉽게 확인할 수 있는 벤치마크가 포함된 함수 개선을 선호하고 있다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로는 세 개의 Approve를 받을 수 있었으며 로컬보다 Jenkins CI의 벤치마크 결과가 더 좋았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;우여곡절&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 PR이 merge 되기까지 약 10일이 걸렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 다른 PR에 비해 더 많은 시간이 걸렸으며 CI를 통과하지 못하는 경우도 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것은 이 PR을 올릴 즈음에 Node.js Jenkins CI에서 문제가 발생했기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 내가 할 수 있는 것은 없으며 CI가 수정되어 누군가 CI를 다시 돌려주기를 기다리는 방법밖에 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 기간이 체감상 길게 느껴졌으며 코멘트를 통해 Approve를 준 사람들을 언급하는 등 이 PR에 다시 관심을 갖도록 하기 위해 노력했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;931&quot; data-origin-height=&quot;756&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbowro/btsJD8G7iqK/0ZAk3KFMV56zXCQQLJmb81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbowro/btsJD8G7iqK/0ZAk3KFMV56zXCQQLJmb81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbowro/btsJD8G7iqK/0ZAk3KFMV56zXCQQLJmb81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcbowro%2FbtsJD8G7iqK%2F0ZAk3KFMV56zXCQQLJmb81%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;931&quot; height=&quot;756&quot; data-origin-width=&quot;931&quot; data-origin-height=&quot;756&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로 CI가 고쳐졌는지 CI를 통과하고 merge 될 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결과&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1266&quot; data-origin-height=&quot;155&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ebwVP6/btsJDHwuCmi/jqsGCkoh4HX6oFrAeWylR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ebwVP6/btsJDHwuCmi/jqsGCkoh4HX6oFrAeWylR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ebwVP6/btsJDHwuCmi/jqsGCkoh4HX6oFrAeWylR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FebwVP6%2FbtsJDHwuCmi%2FjqsGCkoh4HX6oFrAeWylR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1266&quot; height=&quot;155&quot; data-origin-width=&quot;1266&quot; data-origin-height=&quot;155&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오랜 시간이 걸렸지만 PR이 머지되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것으로 세 번째 기여를 할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의도치 않게 path 모듈에만 두 번의 성능 개선을 하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;path만 하고자 하는 것은 아니었으나 path 모듈의 코드를 가장 오랫동안 봤기에 개선점을 찾기가 더 쉬웠던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node.js의 성능을 올리는 것은 생각보다 재미있고 Node.js의 벤치마크가 잘 구성되어 있다는 것을 느낄 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 기여한 내용들은 22버전에 포함되어 있기 때문에 아직은 많은 사람들이 이 부분을 사용하지 않을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 이후 lts 버전들에는 이 코드가 포함 될 것이고 시간이 지날수록 더 많은 사람들이 이 코드를 사용하게 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(물론 직접 이 코드를 사용할 일은 없고 모듈을 불러와서 사용하는 것은 여전할테지만)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누군가가 사용하는 함수의 성능을 올린다는 것에 보람을 느끼기도 하고 코드의 구조를 파악하는 과정이 어렵지만 재미있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그를 보면 Node.js path 모듈의 resolve와 join을 비교하는 글들이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비슷한듯 다른 결과를 내놓는 두 함수의 차이점은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;resolve: 가장 마지막에 있는 절대경로를 기준으로 인자를 합한 경로를 반환 (인자에 절대경로가 없다면 cwd를 기준으로 반환)&lt;/li&gt;
&lt;li&gt;join: 절대경로에 상관없이 왼쪽 인자부터 마지막 인자까지 합한 경로를 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같은 내용의 새로운 블로그 글이 작성될 때 마다 읽어보는 재미도 있을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(내가 기여한 함수들과 관련된 블로그 글)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수의 동작을 이해하는 가장 좋은 방법은 그 함수의 내부를 들여다 보는 것도 방법이 될 수 있다는 생각을 갖게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/개발과정</category>
      <author>단진</author>
      <guid isPermaLink="true">https://29223.tistory.com/173</guid>
      <comments>https://29223.tistory.com/173#entry173comment</comments>
      <pubDate>Wed, 18 Sep 2024 22:46:41 +0900</pubDate>
    </item>
    <item>
      <title>[Node.js 기여하기] 2. for...of 대신 인덱스 사용하기</title>
      <link>https://29223.tistory.com/172</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Nodejs.png&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/panF4/btsJfDzOK30/gRS4o2d75SRVZGk2ukAkdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/panF4/btsJfDzOK30/gRS4o2d75SRVZGk2ukAkdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/panF4/btsJfDzOK30/gRS4o2d75SRVZGk2ukAkdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpanF4%2FbtsJfDzOK30%2FgRS4o2d75SRVZGk2ukAkdk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1100&quot; height=&quot;600&quot; data-filename=&quot;Nodejs.png&quot; data-origin-width=&quot;1100&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;&lt;a href=&quot;https://github.com/nodejs/node/pull/54474&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/nodejs/node/pull/54474&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1724565668884&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;stream: change stream to use index instead of &amp;#96;for...of&amp;#96; by HBSPS &amp;middot; Pull Request #54474 &amp;middot; nodejs/node&quot; data-og-description=&quot;Refs: https://github.com/nodejs/node/blob/main/doc/contributing/primordials.md#unsafe-array-iteration confid...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/nodejs/node/pull/54474&quot; data-og-url=&quot;https://github.com/nodejs/node/pull/54474&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/rK37W/hyWV08FAMX/nmhgD9Y0W5KzXa4vgSrdoK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/nodejs/node/pull/54474&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/nodejs/node/pull/54474&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/rK37W/hyWV08FAMX/nmhgD9Y0W5KzXa4vgSrdoK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;stream: change stream to use index instead of `for...of` by HBSPS &amp;middot; Pull Request #54474 &amp;middot; nodejs/node&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Refs: https://github.com/nodejs/node/blob/main/doc/contributing/primordials.md#unsafe-array-iteration confid...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저번에 이어 이번이 두 번째 기여가 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(4번의 시도 중 두 번이 merge 되었다)&lt;/p&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_1724565738093&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for (const key of ObjectKeys(streamReturningOperators)) {
  const op = streamReturningOperators[key];
  function fn(...args) {
    if (new.target) {
      throw new ERR_ILLEGAL_CONSTRUCTOR();
    }
    return Stream.Readable.from(ReflectApply(op, this, args));
  }
  ObjectDefineProperty(fn, 'name', { __proto__: null, value: op.name });
  ObjectDefineProperty(fn, 'length', { __proto__: null, value: op.length });
  ObjectDefineProperty(Stream.Readable.prototype, key, {
    __proto__: null,
    value: fn,
    enumerable: false,
    configurable: true,
    writable: true,
  });
}
for (const key of ObjectKeys(promiseReturningOperators)) {
  const op = promiseReturningOperators[key];
  function fn(...args) {
    if (new.target) {
      throw new ERR_ILLEGAL_CONSTRUCTOR();
    }
    return ReflectApply(op, this, args);
  }
  ObjectDefineProperty(fn, 'name', { __proto__: null, value: op.name });
  ObjectDefineProperty(fn, 'length', { __proto__: null, value: op.length });
  ObjectDefineProperty(Stream.Readable.prototype, key, {
    __proto__: null,
    value: fn,
    enumerable: false,
    configurable: true,
    writable: true,
  });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분은 stream과 관련된 부분이며 관련 객체를 설정하는 부분이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개선점 찾기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 구조적으로 수정할 수 있는 부분은 따로 없어보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;primordials를 잘 사용하고 있으며 성능상 크게 문제 되는 부분도 없는 것으로 보인다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;성능을 올릴 마땅한 방법은 없어 보이지만 이 코드에도 수정할 수 있는 지점이 존재한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;primordials의 문서를 읽어보면 아래와 같은 부분이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;a href=&quot;https://github.com/nodejs/node/blob/main/doc/contributing/primordials.md#unsafe-array-iteration&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/nodejs/node/blob/main/doc/contributing/primordials.md#unsafe-array-iteration&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1724565899145&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;node/doc/contributing/primordials.md at main &amp;middot; nodejs/node&quot; data-og-description=&quot;Node.js JavaScript runtime ✨ ✨. Contribute to nodejs/node development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/nodejs/node/blob/main/doc/contributing/primordials.md#unsafe-array-iteration&quot; data-og-url=&quot;https://github.com/nodejs/node/blob/main/doc/contributing/primordials.md&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dJsJ3Y/hyWSjvhhvk/WYjeARQsccgBZMXkkaJkF0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/nodejs/node/blob/main/doc/contributing/primordials.md#unsafe-array-iteration&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/nodejs/node/blob/main/doc/contributing/primordials.md#unsafe-array-iteration&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dJsJ3Y/hyWSjvhhvk/WYjeARQsccgBZMXkkaJkF0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;node/doc/contributing/primordials.md at main &amp;middot; nodejs/node&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Node.js JavaScript runtime ✨ ✨. Contribute to nodejs/node development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1029&quot; data-origin-height=&quot;784&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CFQWj/btsJdPveKN8/P8lZBfR4dkrpmMzdPbzJkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CFQWj/btsJdPveKN8/P8lZBfR4dkrpmMzdPbzJkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CFQWj/btsJdPveKN8/P8lZBfR4dkrpmMzdPbzJkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCFQWj%2FbtsJdPveKN8%2FP8lZBfR4dkrpmMzdPbzJkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1029&quot; height=&quot;784&quot; data-origin-width=&quot;1029&quot; data-origin-height=&quot;784&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;이 문서에 의하면 for...of의 경우 안전하지 않은 배열 이터레이션이라 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분이 수정할 지점이 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개선 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 수정하는 것은 크게 어렵지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 for...of를 사용하는 대신 Index 기반의 for loop를 만들면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1724566048981&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const streamKeys = ObjectKeys(streamReturningOperators);
for (let i = 0; i &amp;lt; streamKeys.length; i++) {
  const key = streamKeys[i];
  const op = streamReturningOperators[key];
  function fn(...args) {
    if (new.target) {
      throw new ERR_ILLEGAL_CONSTRUCTOR();
    }
    return Stream.Readable.from(ReflectApply(op, this, args));
  }
  ObjectDefineProperty(fn, 'name', { __proto__: null, value: op.name });
  ObjectDefineProperty(fn, 'length', { __proto__: null, value: op.length });
  ObjectDefineProperty(Stream.Readable.prototype, key, {
    __proto__: null,
    value: fn,
    enumerable: false,
    configurable: true,
    writable: true,
  });
}
const promiseKeys = ObjectKeys(promiseReturningOperators);
for (let i = 0; i &amp;lt; promiseKeys.length; i++) {
  const key = promiseKeys[i];
  const op = promiseReturningOperators[key];
  function fn(...args) {
    if (new.target) {
      throw new ERR_ILLEGAL_CONSTRUCTOR();
    }
    return ReflectApply(op, this, args);
  }
  ObjectDefineProperty(fn, 'name', { __proto__: null, value: op.name });
  ObjectDefineProperty(fn, 'length', { __proto__: null, value: op.length });
  ObjectDefineProperty(Stream.Readable.prototype, key, {
    __proto__: null,
    value: fn,
    enumerable: false,
    configurable: true,
    writable: true,
  });
}&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;위와 같이 수정한다면 index를 사용해서 for loop를 사용할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(다른 부분의 코드는 수정하지 않았다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서에서 추천하는 방법으로 개선을 했지만 이에 대해 성능상 문제가 없는지 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(테스트는 모두 통과한다는 가정)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;벤치마크&lt;/h2&gt;
&lt;pre id=&quot;code_1724566153543&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;                                                                                         confidence improvement accuracy (*)   (**)   (***)
streams/compose.js n=1000                                                                               -0.19 %       &amp;plusmn;0.55% &amp;plusmn;0.73%  &amp;plusmn;0.95%
streams/creation.js kind='duplex' n=50000000                                                            -0.66 %       &amp;plusmn;3.62% &amp;plusmn;4.82%  &amp;plusmn;6.28%
streams/creation.js kind='readable' n=50000000                                                          -0.43 %       &amp;plusmn;5.08% &amp;plusmn;6.78%  &amp;plusmn;8.86%
streams/creation.js kind='transform' n=50000000                                                          0.30 %       &amp;plusmn;6.37% &amp;plusmn;8.48% &amp;plusmn;11.05%
streams/creation.js kind='writable' n=50000000                                                          -2.93 %       &amp;plusmn;5.08% &amp;plusmn;6.81%  &amp;plusmn;8.96%
streams/destroy.js kind='duplex' n=1000000                                                               1.12 %       &amp;plusmn;1.55% &amp;plusmn;2.07%  &amp;plusmn;2.72%
streams/destroy.js kind='readable' n=1000000                                                      *      1.50 %       &amp;plusmn;1.47% &amp;plusmn;1.97%  &amp;plusmn;2.59%
streams/destroy.js kind='transform' n=1000000                                                            0.63 %       &amp;plusmn;1.10% &amp;plusmn;1.47%  &amp;plusmn;1.91%
streams/destroy.js kind='writable' n=1000000                                                             0.75 %       &amp;plusmn;1.79% &amp;plusmn;2.38%  &amp;plusmn;3.10%
streams/pipe-object-mode.js n=5000000                                                                   -1.15 %       &amp;plusmn;2.70% &amp;plusmn;3.63%  &amp;plusmn;4.82%
streams/pipe.js n=5000000                                                                                0.73 %       &amp;plusmn;1.18% &amp;plusmn;1.57%  &amp;plusmn;2.05%
streams/readable-async-iterator.js sync='no' n=100000                                             *     -0.69 %       &amp;plusmn;0.54% &amp;plusmn;0.72%  &amp;plusmn;0.94%
streams/readable-async-iterator.js sync='yes' n=100000                                                  -0.89 %       &amp;plusmn;1.40% &amp;plusmn;1.87%  &amp;plusmn;2.45%
streams/readable-bigread.js n=1000                                                                      -0.59 %       &amp;plusmn;0.68% &amp;plusmn;0.90%  &amp;plusmn;1.17%
streams/readable-bigunevenread.js n=1000                                                                -0.04 %       &amp;plusmn;0.60% &amp;plusmn;0.80%  &amp;plusmn;1.04%
streams/readable-boundaryread.js type='buffer' n=2000                                                    0.18 %       &amp;plusmn;1.02% &amp;plusmn;1.36%  &amp;plusmn;1.77%
streams/readable-boundaryread.js type='string' n=2000                                                   -0.64 %       &amp;plusmn;0.89% &amp;plusmn;1.19%  &amp;plusmn;1.54%
streams/readable-from.js type='array' n=10000000                                                         1.66 %       &amp;plusmn;7.10% &amp;plusmn;9.45% &amp;plusmn;12.31%
streams/readable-from.js type='async-generator' n=10000000                                               0.27 %       &amp;plusmn;1.07% &amp;plusmn;1.42%  &amp;plusmn;1.85%
streams/readable-from.js type='sync-generator-with-async-values' n=10000000                             -1.30 %       &amp;plusmn;1.72% &amp;plusmn;2.28%  &amp;plusmn;2.98%
streams/readable-from.js type='sync-generator-with-sync-values' n=10000000                              -0.29 %       &amp;plusmn;1.54% &amp;plusmn;2.05%  &amp;plusmn;2.67%
streams/readable-readall.js n=5000                                                                      -0.20 %       &amp;plusmn;0.36% &amp;plusmn;0.49%  &amp;plusmn;0.63%
streams/readable-uint8array.js kind='encoding' n=1000000                                        ***      2.43 %       &amp;plusmn;0.79% &amp;plusmn;1.05%  &amp;plusmn;1.37%
streams/readable-uint8array.js kind='read' n=1000000                                                    -0.96 %       &amp;plusmn;1.77% &amp;plusmn;2.35%  &amp;plusmn;3.06%
streams/readable-unevenread.js n=1000                                                                    0.04 %       &amp;plusmn;0.51% &amp;plusmn;0.67%  &amp;plusmn;0.88%
streams/writable-manywrites.js len=1024 callback='no' writev='no' sync='no' n=100000                    -0.30 %       &amp;plusmn;0.89% &amp;plusmn;1.19%  &amp;plusmn;1.55%
streams/writable-manywrites.js len=1024 callback='no' writev='no' sync='yes' n=100000             *     -0.70 %       &amp;plusmn;0.68% &amp;plusmn;0.90%  &amp;plusmn;1.18%
streams/writable-manywrites.js len=1024 callback='no' writev='yes' sync='no' n=100000            **     -0.71 %       &amp;plusmn;0.53% &amp;plusmn;0.70%  &amp;plusmn;0.91%
streams/writable-manywrites.js len=1024 callback='no' writev='yes' sync='yes' n=100000                  -0.42 %       &amp;plusmn;0.82% &amp;plusmn;1.09%  &amp;plusmn;1.42%
streams/writable-manywrites.js len=1024 callback='yes' writev='no' sync='no' n=100000                    0.65 %       &amp;plusmn;1.28% &amp;plusmn;1.71%  &amp;plusmn;2.22%
streams/writable-manywrites.js len=1024 callback='yes' writev='no' sync='yes' n=100000                   1.10 %       &amp;plusmn;2.19% &amp;plusmn;2.94%  &amp;plusmn;3.90%
streams/writable-manywrites.js len=1024 callback='yes' writev='yes' sync='no' n=100000            *     -0.89 %       &amp;plusmn;0.83% &amp;plusmn;1.10%  &amp;plusmn;1.43%
streams/writable-manywrites.js len=1024 callback='yes' writev='yes' sync='yes' n=100000                 -0.14 %       &amp;plusmn;1.42% &amp;plusmn;1.89%  &amp;plusmn;2.46%
streams/writable-manywrites.js len=32768 callback='no' writev='no' sync='no' n=100000                   -0.04 %       &amp;plusmn;0.98% &amp;plusmn;1.31%  &amp;plusmn;1.70%
streams/writable-manywrites.js len=32768 callback='no' writev='no' sync='yes' n=100000            *     -0.56 %       &amp;plusmn;0.53% &amp;plusmn;0.71%  &amp;plusmn;0.92%
streams/writable-manywrites.js len=32768 callback='no' writev='yes' sync='no' n=100000                   0.24 %       &amp;plusmn;0.87% &amp;plusmn;1.17%  &amp;plusmn;1.53%
streams/writable-manywrites.js len=32768 callback='no' writev='yes' sync='yes' n=100000                  0.27 %       &amp;plusmn;0.73% &amp;plusmn;0.98%  &amp;plusmn;1.27%
streams/writable-manywrites.js len=32768 callback='yes' writev='no' sync='no' n=100000                  -0.03 %       &amp;plusmn;2.01% &amp;plusmn;2.69%  &amp;plusmn;3.51%
streams/writable-manywrites.js len=32768 callback='yes' writev='no' sync='yes' n=100000                 -0.36 %       &amp;plusmn;1.21% &amp;plusmn;1.62%  &amp;plusmn;2.12%
streams/writable-manywrites.js len=32768 callback='yes' writev='yes' sync='no' n=100000                 -0.26 %       &amp;plusmn;0.94% &amp;plusmn;1.26%  &amp;plusmn;1.65%
streams/writable-manywrites.js len=32768 callback='yes' writev='yes' sync='yes' n=100000                -0.30 %       &amp;plusmn;0.72% &amp;plusmn;0.96%  &amp;plusmn;1.25%
streams/writable-uint8array.js kind='object-mode' n=50000000                                            -0.78 %       &amp;plusmn;1.82% &amp;plusmn;2.45%  &amp;plusmn;3.23%
streams/writable-uint8array.js kind='write' n=50000000                                                  -2.21 %       &amp;plusmn;2.45% &amp;plusmn;3.30%  &amp;plusmn;4.36%
streams/writable-uint8array.js kind='writev' n=50000000                                                 -0.14 %       &amp;plusmn;1.22% &amp;plusmn;1.62%  &amp;plusmn;2.12%&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stream과 관련된 부분의 모든 벤치마크를 돌려보면 위와 같은 결과가 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 경우는 개인 로컬 환경에서 수행한 벤치마크이며 Node.js의 Jenkins CI를 사용한 벤치마크 결과는 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1724566200394&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;                                                                                         confidence improvement accuracy (*)   (**)  (***)
streams/compose.js n=1000                                                                               -0.08 %       &amp;plusmn;0.54% &amp;plusmn;0.71% &amp;plusmn;0.93%
streams/creation.js kind='duplex' n=50000000                                                             1.11 %       &amp;plusmn;2.20% &amp;plusmn;2.93% &amp;plusmn;3.81%
streams/creation.js kind='readable' n=50000000                                                           1.09 %       &amp;plusmn;1.39% &amp;plusmn;1.85% &amp;plusmn;2.41%
streams/creation.js kind='transform' n=50000000                                                          0.75 %       &amp;plusmn;2.55% &amp;plusmn;3.40% &amp;plusmn;4.43%
streams/creation.js kind='writable' n=50000000                                                          -0.77 %       &amp;plusmn;1.35% &amp;plusmn;1.80% &amp;plusmn;2.34%
streams/destroy.js kind='duplex' n=1000000                                                              -0.06 %       &amp;plusmn;0.53% &amp;plusmn;0.71% &amp;plusmn;0.92%
streams/destroy.js kind='readable' n=1000000                                                             0.41 %       &amp;plusmn;0.74% &amp;plusmn;0.98% &amp;plusmn;1.27%
streams/destroy.js kind='transform' n=1000000                                                           -0.04 %       &amp;plusmn;0.44% &amp;plusmn;0.58% &amp;plusmn;0.76%
streams/destroy.js kind='writable' n=1000000                                                            -0.50 %       &amp;plusmn;0.87% &amp;plusmn;1.16% &amp;plusmn;1.51%
streams/pipe-object-mode.js n=5000000                                                                   -0.20 %       &amp;plusmn;0.28% &amp;plusmn;0.38% &amp;plusmn;0.50%
streams/pipe.js n=5000000                                                                        **      0.39 %       &amp;plusmn;0.28% &amp;plusmn;0.37% &amp;plusmn;0.49%
streams/readable-async-iterator.js sync='no' n=100000                                                   -0.19 %       &amp;plusmn;0.61% &amp;plusmn;0.82% &amp;plusmn;1.06%
streams/readable-async-iterator.js sync='yes' n=100000                                                  -0.38 %       &amp;plusmn;0.62% &amp;plusmn;0.83% &amp;plusmn;1.08%
streams/readable-bigread.js n=1000                                                                      -0.16 %       &amp;plusmn;0.73% &amp;plusmn;0.97% &amp;plusmn;1.28%
streams/readable-bigunevenread.js n=1000                                                                 0.11 %       &amp;plusmn;0.35% &amp;plusmn;0.46% &amp;plusmn;0.61%
streams/readable-boundaryread.js type='buffer' n=2000                                                   -0.40 %       &amp;plusmn;0.70% &amp;plusmn;0.94% &amp;plusmn;1.22%
streams/readable-boundaryread.js type='string' n=2000                                                    0.33 %       &amp;plusmn;0.68% &amp;plusmn;0.90% &amp;plusmn;1.18%
streams/readable-from.js type='array' n=10000000                                                         0.39 %       &amp;plusmn;2.50% &amp;plusmn;3.32% &amp;plusmn;4.32%
streams/readable-from.js type='async-generator' n=10000000                                              -0.60 %       &amp;plusmn;0.73% &amp;plusmn;0.97% &amp;plusmn;1.26%
streams/readable-from.js type='sync-generator-with-async-values' n=10000000                              0.22 %       &amp;plusmn;0.35% &amp;plusmn;0.47% &amp;plusmn;0.61%
streams/readable-from.js type='sync-generator-with-sync-values' n=10000000                              -0.06 %       &amp;plusmn;0.20% &amp;plusmn;0.26% &amp;plusmn;0.34%
streams/readable-readall.js n=5000                                                                       2.01 %       &amp;plusmn;2.23% &amp;plusmn;2.97% &amp;plusmn;3.86%
streams/readable-uint8array.js kind='encoding' n=1000000                                                 0.01 %       &amp;plusmn;0.55% &amp;plusmn;0.73% &amp;plusmn;0.95%
streams/readable-uint8array.js kind='read' n=1000000                                                     0.45 %       &amp;plusmn;2.24% &amp;plusmn;2.98% &amp;plusmn;3.88%
streams/readable-unevenread.js n=1000                                                                   -0.08 %       &amp;plusmn;0.27% &amp;plusmn;0.36% &amp;plusmn;0.47%
streams/writable-manywrites.js len=1024 callback='no' writev='no' sync='no' n=100000                     0.15 %       &amp;plusmn;0.94% &amp;plusmn;1.24% &amp;plusmn;1.62%
streams/writable-manywrites.js len=1024 callback='no' writev='no' sync='yes' n=100000                   -0.32 %       &amp;plusmn;1.14% &amp;plusmn;1.52% &amp;plusmn;1.98%
streams/writable-manywrites.js len=1024 callback='no' writev='yes' sync='no' n=100000                    0.09 %       &amp;plusmn;0.51% &amp;plusmn;0.68% &amp;plusmn;0.88%
streams/writable-manywrites.js len=1024 callback='no' writev='yes' sync='yes' n=100000                  -0.01 %       &amp;plusmn;1.21% &amp;plusmn;1.60% &amp;plusmn;2.09%
streams/writable-manywrites.js len=1024 callback='yes' writev='no' sync='no' n=100000                   -0.25 %       &amp;plusmn;1.40% &amp;plusmn;1.86% &amp;plusmn;2.42%
streams/writable-manywrites.js len=1024 callback='yes' writev='no' sync='yes' n=100000                   0.10 %       &amp;plusmn;0.80% &amp;plusmn;1.07% &amp;plusmn;1.39%
streams/writable-manywrites.js len=1024 callback='yes' writev='yes' sync='no' n=100000                   0.53 %       &amp;plusmn;0.60% &amp;plusmn;0.80% &amp;plusmn;1.04%
streams/writable-manywrites.js len=1024 callback='yes' writev='yes' sync='yes' n=100000                 -0.64 %       &amp;plusmn;1.03% &amp;plusmn;1.37% &amp;plusmn;1.78%
streams/writable-manywrites.js len=32768 callback='no' writev='no' sync='no' n=100000                    0.72 %       &amp;plusmn;1.66% &amp;plusmn;2.23% &amp;plusmn;2.95%
streams/writable-manywrites.js len=32768 callback='no' writev='no' sync='yes' n=100000                  -0.57 %       &amp;plusmn;1.19% &amp;plusmn;1.58% &amp;plusmn;2.06%
streams/writable-manywrites.js len=32768 callback='no' writev='yes' sync='no' n=100000                  -0.57 %       &amp;plusmn;0.75% &amp;plusmn;1.00% &amp;plusmn;1.30%
streams/writable-manywrites.js len=32768 callback='no' writev='yes' sync='yes' n=100000                 -0.11 %       &amp;plusmn;1.20% &amp;plusmn;1.61% &amp;plusmn;2.11%
streams/writable-manywrites.js len=32768 callback='yes' writev='no' sync='no' n=100000                  -0.93 %       &amp;plusmn;1.22% &amp;plusmn;1.63% &amp;plusmn;2.15%
streams/writable-manywrites.js len=32768 callback='yes' writev='no' sync='yes' n=100000                 -0.49 %       &amp;plusmn;0.92% &amp;plusmn;1.22% &amp;plusmn;1.59%
streams/writable-manywrites.js len=32768 callback='yes' writev='yes' sync='no' n=100000                  0.17 %       &amp;plusmn;0.54% &amp;plusmn;0.72% &amp;plusmn;0.95%
streams/writable-manywrites.js len=32768 callback='yes' writev='yes' sync='yes' n=100000                -0.47 %       &amp;plusmn;0.89% &amp;plusmn;1.18% &amp;plusmn;1.54%
streams/writable-uint8array.js kind='object-mode' n=50000000                                             0.00 %       &amp;plusmn;0.04% &amp;plusmn;0.06% &amp;plusmn;0.08%
streams/writable-uint8array.js kind='write' n=50000000                                                   0.09 %       &amp;plusmn;0.83% &amp;plusmn;1.10% &amp;plusmn;1.44%
streams/writable-uint8array.js kind='writev' n=50000000                                                  0.52 %       &amp;plusmn;0.95% &amp;plusmn;1.26% &amp;plusmn;1.64%&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬의 경우 confidence가 높은 벤치마크들이 다수 존재하지만 Jenkins의 경우는 하나의 케이스만 confidence 케이스로 분류되었다.&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;Jenkins를 기존으로 본다면 유의할 만한 성능 감소는 보이지 않았으며 유의미한 차이의 경우도 성능 저하가 보이지는 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 바탕으로 성능에 하락이 없으며 Node.js의 컨벤션을 따르도록 수정했다는 것을 보여주며 PR을 열게 되었다.&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;Unsafe Array Iteration?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 주목해야 하는 점은 Unsafe라는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for...of 문은 어떤 이유에서 index 기반의 for loop보다 안전하지 않다고 하는 것일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for...of 문은 이터러블을 순회하면서 이터러블 내부의 요소를 변수에 할당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 내부적으로 이터레이터의 next 문을 호출하여 이터러블을 순회하며 next 메소드가 반환한 이터레이터 결과 객체의 value 프로퍼티의 값을 for...of 문의 변수에 할당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 이터레이터 결과 객체의 done 프로퍼티가 true인 경우 이터러블의 순회를 중단하게 된다.&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;Iteration&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이터레이션은 ES6에 도입되었으며 순회 가능한 자료구조를 만들기 위한 규칙이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 다시 생각해보면 ES6 도입 이전에는 순회 가능한 자료구조들은 통일된 규격이 없었으며 for, for...in, forEach등을 사용하여 순회하고 있었다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ES6에서는 이러한 순회 가능 자료구조들을 이터레이션 프로토콜을 준수하는 이터러블로 통일하여 for...of, 스프레드 문법, 배열 구조분해 할당의 대상으로 사용할 수 있도록 한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 이터러블 프로토콜과 이터레이터 프로토콜이라는 개념이 나오게 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이터러블 프로토콜: Symbol.iterator를 프로퍼티의 키로 사용한 메소드를 직접 구현하거나 프로토타입 체인을 통해 상속받은 Symbol.iterator 메소드를 호출하는 경우 이터레이터 프로토콜을 준수한 이터레이터를 반환하게 되며 이러한 프로토콜을 따르는 객체를 이터러블이라 한다.&lt;/li&gt;
&lt;li&gt;이터레이터 프로토콜: 이터레이터의 Symbol.iterator 메소드를 호출하면 이터레이터 프로토콜을 준수한 이터레이터를 반환한다.&lt;/li&gt;
&lt;li&gt;이터러블: 이터러블 프로토콜을 준수한 객체.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 이터러블 프로토콜은 이터러블 객체가 따라야 할 규칙을 정의한 것이며, 이터레이터 프로토콜의 경우 이터레이터가 따라야 할 규칙을 정의한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 말해, 이터러블 객체의 Symbol.iterator 메소드를 호출하면 이터레이터 객체가 생성되는 것이며 이터레이터 객체의 경우는 next메소드와 결과 객체를 갖게 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 어떤 객체가 이터러블 프로토콜과 이터레이터 프로토콜을 제대로 따르고 있다면 이 객체는 Symbol.iterator 프로퍼티를 갖고 있으며(이터러블 프로토콜) Symbol.iterator는 next 메소드와 done, value를 포함한 결과 객체를 반환할 것이다(이터레이터 프로토콜).&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1278&quot; data-origin-height=&quot;354&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJrXoz/btsJeVVvD4H/eeyeD742SW6CvnkJMIFJk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJrXoz/btsJeVVvD4H/eeyeD742SW6CvnkJMIFJk1/img.png&quot; data-alt=&quot;모던 자바스크립트 Deep Dive (이웅모)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJrXoz/btsJeVVvD4H/eeyeD742SW6CvnkJMIFJk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJrXoz%2FbtsJeVVvD4H%2FeeyeD742SW6CvnkJMIFJk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1278&quot; height=&quot;354&quot; data-origin-width=&quot;1278&quot; data-origin-height=&quot;354&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;모던 자바스크립트 Deep Dive (이웅모)&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;일반 배열을 예시로 보면 다음과 같이 Symbol.iterator 프로퍼티를 갖고 있는 것을 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;1002&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vFVx5/btsJfMJ4xop/DTZtQjhO076Dd96SokDbM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vFVx5/btsJfMJ4xop/DTZtQjhO076Dd96SokDbM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vFVx5/btsJfMJ4xop/DTZtQjhO076Dd96SokDbM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvFVx5%2FbtsJfMJ4xop%2FDTZtQjhO076Dd96SokDbM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;716&quot; height=&quot;1002&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;1002&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;이를 바탕으로 Symbol.iterator를 보면 다음과 같은 결과를 얻을 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;240&quot; data-origin-height=&quot;265&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbxiQw/btsJePOtWJu/KMbiPAtdKtm9vXDQo0BKS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbxiQw/btsJePOtWJu/KMbiPAtdKtm9vXDQo0BKS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbxiQw/btsJePOtWJu/KMbiPAtdKtm9vXDQo0BKS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbxiQw%2FbtsJePOtWJu%2FKMbiPAtdKtm9vXDQo0BKS1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;240&quot; height=&quot;265&quot; data-origin-width=&quot;240&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;앞에서 봤던 내용을 바탕으로 이터러블 프로토콜을 준수한다면 Symbol.iterator는 메소드이므로 호출이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(이터러블 프로토콜을 준수하기 위해서는 Symbol.iterator를 프로퍼티 키로 사용한 메소드가 있어야 하며 이 메소드를 호출하면 이터레이터를 반환해야 한다. 위의 경우 b 변수에는 이터레이터가 할당될 것이다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 호출한다면 위와 같은 결과를 얻을 수 있으며 next 메소드를 호출하는 경우 value와 done을 포함한 결과 객체를 반환하는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(이터레이터 프로토콜을 준수하기 위해서는 next 메소드를 갖고 있으며 결과 객체를 반환해야 한다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, done이 true가 된 경우는 순회가 종료된 것으로 판단하며 value의 경우는 존재하지 않기 때문에 undefined가 되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 바탕으로 배열은 이터러블이라는 결론을 내릴 수 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;결론&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 본론으로 돌아와서 for...of가 안전하지 않은 이유에 대해 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 for...of는 ES6에서 도입된 이터레이션 프로토콜을 활용한 문법이라는 부분을 언급했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열의 경우 Array.prototype의 Symbol.iterator 메소드를 상속받는 이터러블이기 때문에 for...of로 순회가 가능한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 안전하지 않다고 하는 것은 primordials를 사용하는 이유와 같은 이유일 것으로 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 프로토타입 내부에 Iterator가 정의되어 있으며 이는 사용자 측에서 수정이 가능하기 때문이다.&lt;/p&gt;
&lt;pre id=&quot;code_1724568176830&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// User-land
Array.prototype[Symbol.iterator] = () =&amp;gt; ({
  next: () =&amp;gt; ({ done: true }),
});

// Core
let forOfLoopBlockExecuted = false;
let forLoopBlockExecuted = false;
const array = [1, 2, 3];
for (const item of array) {
  forOfLoopBlockExecuted = true;
}
for (let i = 0; i &amp;lt; array.length; i++) {
  forLoopBlockExecuted = true;
}
console.log(forOfLoopBlockExecuted); // false
console.log(forLoopBlockExecuted); // true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 예시가 있을 때, 이터레이터의 경우 done이라는 값을 갖는 결과 객체를 반환하며 이를 바탕으로 순회의 종료 여부를 판단한다고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약, 사용자가 Array 객체의 프로토타입에 직접 접근하여 Symbol.iterator의 행위를 수정하는 경우를 가정해보면 위와 같은 문제가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for...of를 사용하게 된다면 해당 loop가 시작되자 마자 done이 true이므로 loop가 종료된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그와 반대로 index를 사용하는 전통적인 방식의 경우 이터레이터에 의존하지 않기 때문에 prototype pollution에 보다 더 안전하다는 결론을 내릴 수 있다.&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;PR&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서에 언급한 대로 수정하는 간단한 작업이었기에 별다른 코멘트는 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;벤치마크를 통해 성능 저하가 없음을 보여줬기 때문에 더 언급할 부분은 없다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로는 아래와 같이 간단하게 위 문서에 대한 Refs를 걸고 벤치마크 결과와 함께 PR을 올리게 되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;905&quot; data-origin-height=&quot;998&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqKOzr/btsJequCF2V/co3asmsmGdr7N8BkDkcBVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqKOzr/btsJequCF2V/co3asmsmGdr7N8BkDkcBVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqKOzr/btsJequCF2V/co3asmsmGdr7N8BkDkcBVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqKOzr%2FbtsJequCF2V%2Fco3asmsmGdr7N8BkDkcBVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;905&quot; height=&quot;998&quot; data-origin-width=&quot;905&quot; data-origin-height=&quot;998&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;stream과 관련된 부분이라 그런지 많은 분들이 검토하고 가신 것 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;327&quot; data-origin-height=&quot;209&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCJ7QB/btsJdIbPhjV/9gEUjDGbFSbh9eH1uyUmM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCJ7QB/btsJdIbPhjV/9gEUjDGbFSbh9eH1uyUmM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCJ7QB/btsJdIbPhjV/9gEUjDGbFSbh9eH1uyUmM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCJ7QB%2FbtsJdIbPhjV%2F9gEUjDGbFSbh9eH1uyUmM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;327&quot; height=&quot;209&quot; data-origin-width=&quot;327&quot; data-origin-height=&quot;209&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;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-08-23 오후 4.43.32.png&quot; data-origin-width=&quot;1258&quot; data-origin-height=&quot;397&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cA2t9A/btsJeOvhkcl/R4TBrfUvQDqGfO2Uiv1Sy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cA2t9A/btsJeOvhkcl/R4TBrfUvQDqGfO2Uiv1Sy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cA2t9A/btsJeOvhkcl/R4TBrfUvQDqGfO2Uiv1Sy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcA2t9A%2FbtsJeOvhkcl%2FR4TBrfUvQDqGfO2Uiv1Sy1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1258&quot; height=&quot;397&quot; data-filename=&quot;스크린샷 2024-08-23 오후 4.43.32.png&quot; data-origin-width=&quot;1258&quot; data-origin-height=&quot;397&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-08-23 오후 4.43.47.png&quot; data-origin-width=&quot;921&quot; data-origin-height=&quot;192&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PpHJZ/btsJe9lAnTV/mhFWkxIiJpQaIN52uAAwj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PpHJZ/btsJe9lAnTV/mhFWkxIiJpQaIN52uAAwj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PpHJZ/btsJe9lAnTV/mhFWkxIiJpQaIN52uAAwj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPpHJZ%2FbtsJe9lAnTV%2FmhFWkxIiJpQaIN52uAAwj1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;921&quot; height=&quot;192&quot; data-filename=&quot;스크린샷 2024-08-23 오후 4.43.47.png&quot; data-origin-width=&quot;921&quot; data-origin-height=&quot;192&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별 다른 문제 없이 merge 되었고 이것으로 두 번째 기여를 할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성능의 개선이 아니라 아쉽긴 하지만 이번 경험을 바탕으로 prototype pollution에 대한 Node.js 팀의 생각을 볼 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 이터레이터에 대해서 조금 더 deep dive 할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새롭게 도입된 기술들에 대해 편리함을 느끼면서도 그 내부에는 어떤 개념이 포함되어 있으며 기존 방식과의 차이점이 무엇인지, 어떤 잠재적 문제가 있을지 생각해볼 수 있는 기회였다고 생각한다.&lt;/p&gt;</description>
      <category>개발/개발과정</category>
      <author>단진</author>
      <guid isPermaLink="true">https://29223.tistory.com/172</guid>
      <comments>https://29223.tistory.com/172#entry172comment</comments>
      <pubDate>Sun, 25 Aug 2024 15:52:54 +0900</pubDate>
    </item>
    <item>
      <title>[Node.js 기여하기] 1. path 모듈 join 함수 성능 올리기</title>
      <link>https://29223.tistory.com/171</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Nodejs.png&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxwnXQ/btsI3XmxhWc/kOTVGoa2P46YyR9NdGGYfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxwnXQ/btsI3XmxhWc/kOTVGoa2P46YyR9NdGGYfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxwnXQ/btsI3XmxhWc/kOTVGoa2P46YyR9NdGGYfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxwnXQ%2FbtsI3XmxhWc%2FkOTVGoa2P46YyR9NdGGYfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1100&quot; height=&quot;600&quot; data-filename=&quot;Nodejs.png&quot; data-origin-width=&quot;1100&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;&lt;a href=&quot;https://github.com/nodejs/node/pull/54331&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/nodejs/node/pull/54331&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1723729826703&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;path: change &amp;#96;posix.join&amp;#96; to use array by HBSPS &amp;middot; Pull Request #54331 &amp;middot; nodejs/node&quot; data-og-description=&quot;Change posix.join to use array.join instead of additional assignment. confidence improvement accuracy (*) (**) (***) path/join-posi...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/nodejs/node/pull/54331&quot; data-og-url=&quot;https://github.com/nodejs/node/pull/54331&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/O4KzN/hyWOfsExn6/MGBusAjK8yzlAb2lY41k31/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/nodejs/node/pull/54331&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/nodejs/node/pull/54331&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/O4KzN/hyWOfsExn6/MGBusAjK8yzlAb2lY41k31/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;path: change `posix.join` to use array by HBSPS &amp;middot; Pull Request #54331 &amp;middot; nodejs/node&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Change posix.join to use array.join instead of additional assignment. confidence improvement accuracy (*) (**) (***) path/join-posi...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 Node.js에 첫 기여를 해볼 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 확인하던 중 아래와 같은 부분이 있었다.&lt;/p&gt;
&lt;pre id=&quot;code_1723730008557&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
  * @param {...string} args
  * @returns {string}
  */
  join(...args) {
    if (args.length === 0)
      return '.';
    let joined;
    for (let i = 0; i &amp;lt; args.length; ++i) {
      const arg = args[i];
      validateString(arg, 'path');
      if (arg.length &amp;gt; 0) {
        if (joined === undefined)
          joined = arg;
        else
          joined += `/${arg}`;
      }
    }
    if (joined === undefined)
      return '.';
    return posix.normalize(joined);
  },&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;(path.join의 사용 방법에 대해서는 아래 문서를 참고하면 된다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nodejs.org/api/path.html#pathjoinpaths&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://nodejs.org/api/path.html#pathjoinpaths&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1723730051310&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;Path | Node.js v22.6.0 Documentation&quot; data-og-description=&quot;Path# Source Code: lib/path.js The node:path module provides utilities for working with file and directory paths. It can be accessed using: const path = require('node:path'); copy Windows vs. POSIX# The default operation of the node:path module varies base&quot; data-og-host=&quot;nodejs.org&quot; data-og-source-url=&quot;https://nodejs.org/api/path.html#pathjoinpaths&quot; data-og-url=&quot;https://nodejs.org/api/path.html#pathjoinpaths&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://nodejs.org/api/path.html#pathjoinpaths&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nodejs.org/api/path.html#pathjoinpaths&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;Path | Node.js v22.6.0 Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Path# Source Code: lib/path.js The node:path module provides utilities for working with file and directory paths. It can be accessed using: const path = require('node:path'); copy Windows vs. POSIX# The default operation of the node:path module varies base&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;nodejs.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개선점 찾기&lt;/h2&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;(인자가 있다면) 각 args에 대해 빈 문자열이 아닌 경우 결과에 문자열을 더한다.&lt;/li&gt;
&lt;li&gt;연결된 문자열이 빈 문자열이라면 .을 반환한다.&lt;/li&gt;
&lt;li&gt;(연결된 문자열이 있다면) normalize에 전달해 normalizing(상대 경로를 해결하고 경로 구분자를 os에 맞게 변환)한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 해결할 수 있는 부분은 2번에 해당한다.&lt;/p&gt;
&lt;pre id=&quot;code_1723730292558&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;join('foo', '..', 'bar');&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 코드가 있다고 할 때 최종적으로 normalize 함수에 전달되는 인자는 다음과 같이 구성될 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1723730330565&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;'foo/../bar'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 알 수 있는 것은 인자로 전달된 여러 경로 중 비어있지 않은 인자들을 /를 이용해 연결하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 해당하는 부분은 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1723730732021&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let joined;
for (let i = 0; i &amp;lt; args.length; ++i) {
  const arg = args[i];
  validateString(arg, 'path');
  if (arg.length &amp;gt; 0) {
    if (joined === undefined)
      joined = arg;
    else
      joined += `/${arg}`;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;joined가 undefined인 경우(값을 할당하지 않았으니 기본적으로 joined는 undefined로 초기화 됨) arg를 그냥 더하게 되며 이후 부터는 joined가 존재하기 때문에 /과 함께 arg를 더해주게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로 위와 같이 비어있는 문자열을 제외한 모든 args를 하나의 문자열로 만들게 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(상대 경로를 해결하고 경로를 normalizing하는 것은 normalize 함수에서 수행하기 때문에 이 부분에서 자세히 알 필요는 없다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개선 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 개선하기 위해 if문을 줄이려 시도했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;joined는 한 번 문자열이 추가되면 다시 undefined가 되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, joined가 한 번 변경된 뒤부터 해당 if문은 필요가 없게 되며 모든 arg가 해당 if문 안에 들어가기 때문에 불필요한 연산이라 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해 생각했던 방법은 배열을 활용하는 것이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 형태에서 볼 수 있듯이 빈 문자열이 아닌 args를 모두 배열에 넣은 뒤 array.join을 사용한다면 쉽게 만들 수 있을 것으로 생각했다.&lt;/p&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_1723731499232&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
  * @param {...string} args
  * @returns {string}
  */
  join(...args) {
    if (args.length === 0)
      return '.';

    const path = [];
    for (let i = 0; i &amp;lt; args.length; ++i) {
      const arg = args[i];
      validateString(arg, 'path');
      if (arg.length &amp;gt; 0) {
        path.push(arg);
      }
    }

    if (path.length === 0)
      return '.';

    return posix.normalize(ArrayPrototypeJoin(path, '/'));
  },&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;primordials?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;위의 코드를 보면 ArrayPrototypeJoin이라는 것을 볼 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;이것은 primordials라는 것중 하나로 이름에 알 수 있듯이 array.join과 같은 역할을 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;a href=&quot;https://github.com/nodejs/node/blob/main/doc/contributing/primordials.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/nodejs/node/blob/main/doc/contributing/primordials.md&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1723732536025&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;node/doc/contributing/primordials.md at main &amp;middot; nodejs/node&quot; data-og-description=&quot;Node.js JavaScript runtime ✨ ✨. Contribute to nodejs/node development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/nodejs/node/blob/main/doc/contributing/primordials.md&quot; data-og-url=&quot;https://github.com/nodejs/node/blob/main/doc/contributing/primordials.md&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/uDrGn/hyWOlTVRED/DPK51YCagPAl3fYPmBN8Kk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/nodejs/node/blob/main/doc/contributing/primordials.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/nodejs/node/blob/main/doc/contributing/primordials.md&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/uDrGn/hyWOlTVRED/DPK51YCagPAl3fYPmBN8Kk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;node/doc/contributing/primordials.md at main &amp;middot; nodejs/node&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Node.js JavaScript runtime ✨ ✨. Contribute to nodejs/node development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;그렇다면 Node.js에서 array.join 대신 위와 같이 복잡하게 작성하는 이유는 무엇일까?&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1723731794738&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const array = [1, 2, 3];
array.push(4); // Here `push` refers to %Array.prototype.push%.
console.log(JSON.stringify(array)); // [1,2,3,4]

// %Array.prototype%.push is modified in userland.
Array.prototype.push = function push(val) {
  return this.unshift(val);
};

array.push(5); // Now `push` refers to the modified method.
console.log(JSON.stringify(array)); // [5,1,2,3,4]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 push 함수 등 JS 빌트인 객체의 경우 사용자가 직접 prototype을 수정하여 결과를 다르게 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(prototype pollution)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 방지하고자 Node.js에서는 &lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;primordials라는 것을 사용하여 프로토타입 오염에 대비하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;좋은 목적을 갖고 있지만 성능에서 아쉬운 부분도 있다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #1f2328; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ArrayPrototypePush&lt;/li&gt;
&lt;li&gt;ArrayPrototypePop&lt;/li&gt;
&lt;li&gt;ArrayPrototypeShift&lt;/li&gt;
&lt;li&gt;ArrayPrototypeUnshift&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러가지 함수들이 있지만 대표적으로 (일반적으로 자주 사용하게 될) 위의 &lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;primordials의 경우는 성능에 이슈가 있다고 알려져 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;a href=&quot;https://github.com/nodejs/node/blob/main/doc/contributing/primordials.md#primordials-with-known-performance-issues&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/nodejs/node/blob/main/doc/contributing/primordials.md#primordials-with-known-performance-issues&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1723732524177&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;node/doc/contributing/primordials.md at main &amp;middot; nodejs/node&quot; data-og-description=&quot;Node.js JavaScript runtime ✨ ✨. Contribute to nodejs/node development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/nodejs/node/blob/main/doc/contributing/primordials.md#primordials-with-known-performance-issues&quot; data-og-url=&quot;https://github.com/nodejs/node/blob/main/doc/contributing/primordials.md&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bZ7kgJ/hyWOj9D2R5/9flsFeKKLBQVXa7y4Nkqh0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/nodejs/node/blob/main/doc/contributing/primordials.md#primordials-with-known-performance-issues&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/nodejs/node/blob/main/doc/contributing/primordials.md#primordials-with-known-performance-issues&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bZ7kgJ/hyWOj9D2R5/9flsFeKKLBQVXa7y4Nkqh0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;node/doc/contributing/primordials.md at main &amp;middot; nodejs/node&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Node.js JavaScript runtime ✨ ✨. Contribute to nodejs/node development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 성능상 이점을 택할 것인지 안정성을 택할 것인지 고민해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node.js 내부적으로도 &lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;primordials에 대해 찬성하는 입장과 반대하는 입장이 공존하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;이에 대한 논의는 계속되고 있으며 과거 모든 JS 연산에 대해 기본적으로 적용하던 &lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;primordials를 성능과 가독성 등의 문제로 제거하는 PR도 볼 수 있다.&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: #1f2328; text-align: start;&quot;&gt;&lt;a href=&quot;https://github.com/nodejs/node/pull/53699&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/nodejs/node/pull/53699&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1723732869310&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;tls: remove prototype primordials by aduh95 &amp;middot; Pull Request #53699 &amp;middot; nodejs/node&quot; data-og-description=&quot;Yagiz and I paired to remove primordials in TLS files, as those are really just a JS lib that happens to be in core, not something for which prototype pollution is a big concern.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/nodejs/node/pull/53699&quot; data-og-url=&quot;https://github.com/nodejs/node/pull/53699&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bbKdVN/hyWOkHusl5/8OQz7N7djNQglHKKjS9Qk1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/nodejs/node/pull/53699&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/nodejs/node/pull/53699&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bbKdVN/hyWOkHusl5/8OQz7N7djNQglHKKjS9Qk1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;tls: remove prototype primordials by aduh95 &amp;middot; Pull Request #53699 &amp;middot; nodejs/node&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Yagiz and I paired to remove primordials in TLS files, as those are really just a JS lib that happens to be in core, not something for which prototype pollution is a big concern.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 대해 현재 구체적인 &lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;primordials 사용의 구체적인 사용 사례를 도출하는 논의가 진행중이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;a href=&quot;https://github.com/nodejs/TSC/issues/1439&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/nodejs/TSC/issues/1439&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/nodejs/primordials-use-cases&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/nodejs/primordials-use-cases&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;벤치마크&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 실제로 유의미한 성능의 차이가 있는지 확인해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node.js 디렉토리에는 benchmark라는 디렉토리가 있으며 주요한 기능들에 대한 벤치마크가 위치하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 수정한 코드는 path.join에 해당하며 그 중에서도 posix(리눅스 또는 맥)의 join만 해당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 array.push를 사용한 경우 아래와 같은 결과가 나온다.&lt;/p&gt;
&lt;pre id=&quot;code_1723733561148&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;                                                               confidence improvement accuracy (*)   (**)  (***)
path/join-posix.js n=100000 paths='/foo|bar||baz/asdf|quux|..'        ***      5.95 %       &amp;plusmn;1.42% &amp;plusmn;1.90% &amp;plusmn;2.49%&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;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;primordials에 해당하는 ArrayPrototypePush를 사용한 결과는 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1723733594937&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;                                                               confidence improvement accuracy (*)   (**)  (***)
path/join-posix.js n=100000 paths='/foo|bar||baz/asdf|quux|..'                 0.20 %       &amp;plusmn;0.63% &amp;plusmn;0.84% &amp;plusmn;1.09%&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;h2 data-ke-size=&quot;size26&quot;&gt;PR&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로 PR에는 일반적인 push를 사용한 것을 올리게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성능적으로 유의미한 차이가 있으며 &lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;ArrayPrototypePush를 사용한 경우 성능상에 이점이 없기 때문에 의미있는 개선이라 할 수 없을 것 같았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;최근 &lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;primordials에 대한 논의가 계속되고 있으며 성능이 개선되었기 때문에 반대할 이유는 크게 없다고 생각했다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역시 &lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;primordials과 관련된 comment가 있었으며 최종적으로 이 코드에서는 필요가 없다는 확인을 받았다.&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;913&quot; data-origin-height=&quot;735&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YO82f/btsI4oxsAy1/RgJSdPmSSvLK8OWgkTqaM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YO82f/btsI4oxsAy1/RgJSdPmSSvLK8OWgkTqaM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YO82f/btsI4oxsAy1/RgJSdPmSSvLK8OWgkTqaM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYO82f%2FbtsI4oxsAy1%2FRgJSdPmSSvLK8OWgkTqaM0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;913&quot; height=&quot;735&quot; data-origin-width=&quot;913&quot; data-origin-height=&quot;735&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;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node.js에서 PR이 merge되기 위해서는 두 개 이상의 approve가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공교롭게도 이 PR에 approve를 준 두 사람이 각각&amp;nbsp;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;primordials 제거에 대해 제안하는 issue를 발행한 사람과 해당 issue에서 &lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;primordials의 효용성에 대해 주장하던 사람이라는 점이 흥미롭다.&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: #1f2328; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;a href=&quot;https://github.com/nodejs/TSC/issues/1438&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/nodejs/TSC/issues/1438&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1723734836652&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;Removing &amp;#96;primordials&amp;#96; from Node.js project &amp;middot; Issue #1438 &amp;middot; nodejs/TSC&quot; data-og-description=&quot;History Starting from November 2019, we migrated core modules to primordials Ref: nodejs/node#30697 On February 2022, TSC took a vote and came to a conclusion to Use primordials in the error path o...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/nodejs/TSC/issues/1438&quot; data-og-url=&quot;https://github.com/nodejs/TSC/issues/1438&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://github.com/nodejs/TSC/issues/1438&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/nodejs/TSC/issues/1438&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;Removing `primordials` from Node.js project &amp;middot; Issue #1438 &amp;middot; nodejs/TSC&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;History Starting from November 2019, we migrated core modules to primordials Ref: nodejs/node#30697 On February 2022, TSC took a vote and came to a conclusion to Use primordials in the error path o...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;결론적으로 한 명은 &lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;primordials 제거에 찬성하고 다른 한 명은 &lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;primordials 제거에 반대하는 사람이라는 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;해당 issue 이후 어떤 일이 있었길래 서로 상반된 생각을 가진 두 사람이 이 PR을 approve 했는지는 모르겠다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;오히려 &lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;primordials 제거에 반대한 사람이 먼저 approve를 눌렀다(?)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;결과&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-08-14 오후 8.21.29.png&quot; data-origin-width=&quot;908&quot; data-origin-height=&quot;441&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6VJ2s/btsI4cquhxm/LkKDzGWDrpHO21hQr8KFVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6VJ2s/btsI4cquhxm/LkKDzGWDrpHO21hQr8KFVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6VJ2s/btsI4cquhxm/LkKDzGWDrpHO21hQr8KFVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6VJ2s%2FbtsI4cquhxm%2FLkKDzGWDrpHO21hQr8KFVk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;908&quot; height=&quot;441&quot; data-filename=&quot;스크린샷 2024-08-14 오후 8.21.29.png&quot; data-origin-width=&quot;908&quot; data-origin-height=&quot;441&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-08-14 오후 8.20.43.png&quot; data-origin-width=&quot;934&quot; data-origin-height=&quot;633&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFHRQV/btsI5v983u5/yHt4iNu35VIzmWE1LlZaW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFHRQV/btsI5v983u5/yHt4iNu35VIzmWE1LlZaW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFHRQV/btsI5v983u5/yHt4iNu35VIzmWE1LlZaW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFHRQV%2FbtsI5v983u5%2FyHt4iNu35VIzmWE1LlZaW0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;934&quot; height=&quot;633&quot; data-filename=&quot;스크린샷 2024-08-14 오후 8.20.43.png&quot; data-origin-width=&quot;934&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;결과적으로 두 개의 승인을 받고 Node.js main 브랜치에 merge 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(두 개의 approve를 받아도 PR이 열린지 최소 48시간이 있어야 merge 된다 - 해당 시간 동안 또 다른 의견 또는 논의가 있을 수 있기 때문)&lt;/p&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;(개발자들이 path.join을 얼마나 사용할지는 모르겠다...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정을 통해 Node.js라는 대형 프로젝트에 기여하는 경험을 할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node.js 빌드, 수정, 벤치마크, 테스트, 커밋 메시지 형식, PR을 이용한 소통 등 기여를 위해 필요한 단계를 모두 경험할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node의 빌드는 생각보다 오래 걸리며 테스트는 수천개가 넘고 벤치마크를 통한 성능 측정까지 할 수 있도록 모두 준비되어 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 것들이 모두 오픈소스 기여자들이 만든 것임을 새삼 깨닫게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아주 단순한 수정이었지만 성능에 변화가 있다는 사실에 놀랐으며 이 주제가 현재 Node.js 내부에서도 활발히 논의중인 내용이라는 것이 놀라웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 작성한 코드를 남들이 모두 사용할 수 있음에 기대가 되는 한편 내가 작성한 코드를 남들이 모두 사용할 수 있기에 더 조심하게 되는 것 같다는 생각이 든다.&lt;/p&gt;</description>
      <category>개발/개발과정</category>
      <author>단진</author>
      <guid isPermaLink="true">https://29223.tistory.com/171</guid>
      <comments>https://29223.tistory.com/171#entry171comment</comments>
      <pubDate>Fri, 16 Aug 2024 00:32:48 +0900</pubDate>
    </item>
  </channel>
</rss>