<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Joonas' Note</title>
    <link>https://joonas.tistory.com/</link>
    <description>알고리즘, Computer Science, 머신러닝, 딥러닝, 강화학습 등 공부한 내용과 개발하면서 생긴 일들 정리하는 메모장</description>
    <language>ko</language>
    <pubDate>Thu, 18 Jun 2026 02:21:57 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>joonas</managingEditor>
    <image>
      <title>Joonas' Note</title>
      <url>https://tistory1.daumcdn.net/tistory/2753505/attach/629a089c2b214eb3bdb5a63f906af758</url>
      <link>https://joonas.tistory.com</link>
    </image>
    <item>
      <title>다가온 인공지능 시대에 대한 블로거의 감상</title>
      <link>https://joonas.tistory.com/279</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오랜만에 포스트를 적는 이유는, 여가 시간의 대부분을 여행으로 보내면서, 기술적으로 작성해야 할 내용이 있을 만큼의 공부가 없었기 때문이다. 하지만 최근 몇 년 사이에 LLM 서비스들이 안정적으로 결과를 생성하게 되면서, 그리고 그러한 서비스들(ChatGPT, Gemini 등)을 사용하면서 느끼는 점 역시 더 이상 글을 적지 않는 것도 한 이유이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이버 블로그를 시작으로, 본격적인 기술 블로그는 Google Blogger를 거쳐 (&lt;a href=&quot;https://blog.joonas.io/250&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://blog.joonas.io/250&lt;/a&gt; 참고), 티스토리를 플랫폼으로 삼아 도메인만 호스팅하고 있다. 그리고 글의 작성 목적이 나를 위한 글임은 여전하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 기반의 질의(Q/A)와 검색 결과(아마도 RAG?)가 제공되기 이전에는 나의 글을 찾는 것이 쉬웠다.&lt;br /&gt;즉, 나의 언어로 인터넷에 질문을 던졌을 때, 나를 위한 대답을 과거의 내 포스트로부터 찾을 수 있었다.&lt;br /&gt;그리고 이건 내 주변의 사람들에게 설명할 때도 많은 도움이 됐다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2350&quot; data-origin-height=&quot;1464&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvlVen/dJMcagTblw1/ALpFc8Im2uZfZ00tiVtc11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvlVen/dJMcagTblw1/ALpFc8Im2uZfZ00tiVtc11/img.png&quot; data-alt=&quot;'중국인의 나머지 정리' 구글 검색 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvlVen/dJMcagTblw1/ALpFc8Im2uZfZ00tiVtc11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcvlVen%2FdJMcagTblw1%2FALpFc8Im2uZfZ00tiVtc11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;2350&quot; height=&quot;1464&quot; data-origin-width=&quot;2350&quot; data-origin-height=&quot;1464&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;'중국인의 나머지 정리' 구글 검색 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 사례로, 중국인의 나머지 정리를 검색하면 두 개의 위키 사이트를 제외하면 상단에 이 블로그가 나온다.&lt;br /&gt;하지만 최근 활성화 된 구글의 AI Overview 에는 조금 다른 출처들이 사용되고 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/od0De/dJMcagTblZ4/OetkNBCPnJa9346KKmU5wk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/od0De/dJMcagTblZ4/OetkNBCPnJa9346KKmU5wk/img.png&quot; data-origin-width=&quot;1396&quot; data-origin-height=&quot;1190&quot; data-is-animation=&quot;false&quot; style=&quot;width: 57.3478%; margin-right: 10px;&quot; data-widthpercent=&quot;58.02&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/od0De/dJMcagTblZ4/OetkNBCPnJa9346KKmU5wk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fod0De%2FdJMcagTblZ4%2FOetkNBCPnJa9346KKmU5wk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1396&quot; height=&quot;1190&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbgW9Z/dJMcajvz4pN/wVH22BW1pIzsowPKnkMkdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbgW9Z/dJMcajvz4pN/wVH22BW1pIzsowPKnkMkdK/img.png&quot; data-origin-width=&quot;920&quot; data-origin-height=&quot;1084&quot; data-is-animation=&quot;false&quot; width=&quot;342&quot; height=&quot;403&quot; style=&quot;width: 41.4894%;&quot; data-widthpercent=&quot;41.98&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbgW9Z/dJMcajvz4pN/wVH22BW1pIzsowPKnkMkdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbgW9Z%2FdJMcajvz4pN%2FwVH22BW1pIzsowPKnkMkdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;920&quot; height=&quot;1084&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Google Search - AI Overview 검색 출처&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 느끼는 바는 크게 두 가지이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나는, 나를 위해 작성되었던 글과 그림 그리고 작성 당시의 기억들은 찾기 어려워졌다.&lt;br /&gt;다른 하나는 위에서 이어지는 생각으로, &lt;b&gt;글로 작성할 만큼 내가 정리할 필요가 없어졌다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어딘가에서 우스갯소리이면서 동시에 괴담으로써 죽은 인터넷 이론이 종종 언급되고는 한다. 그 진위를 떠나서 내용과 결과만큼은 생각해 볼 만한 주제이다. 컴퓨터 공학에서는 &quot;Garbage In, Garbage Out&quot; 라는 유명하고 뼈 있는 격언이 있다. 쓸데없는 입력은 쓸데없는 출력을 만들 수밖에 없다는 이 문구를, 인터넷에서 정보를 소비(조회)만 하고 생산자가 더 이상 존재하지 않는 세상을 생각해 보면, 죽은 인터넷 이론은 마냥 이론으로 남지 않을 수 있다는 생각이 든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거에 우연히 하나의 글을 읽었는데, 블로그에 글을 작성하는 이유로 &quot;본인 역시 &lt;b&gt;인터넷에서 받았으니 인터넷에 돌려주는&lt;/b&gt;&quot; 정보의 선순환을 언급하신 글이 있었다. 이 문장은 지금도 크게 공감하고 있는 문구이다. 하지만 다가온 시대에 던져지는 물음이기도 한 것 같다.&lt;/p&gt;</description>
      <category>후기</category>
      <author>joonas</author>
      <guid isPermaLink="true">https://joonas.tistory.com/279</guid>
      <comments>https://joonas.tistory.com/279#entry279comment</comments>
      <pubDate>Mon, 25 May 2026 18:20:29 +0900</pubDate>
    </item>
    <item>
      <title>nvidia-smi 명령어 정리</title>
      <link>https://joonas.tistory.com/278</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;GPU 전체 상태 보기&lt;/h3&gt;
&lt;pre id=&quot;code_1762145975560&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;nvidia-smi&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;특정 GPU 상태 보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자는 gpu id (UUID 또는 PIC bus ID) 를 입력하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1762146095788&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; nvidia-smi -i 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 개를 한 번에 출력할 수 도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1762146158578&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ nvidia-smi -i 0,3
Mon Nov  3 14:02:05 2025
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 470.141.10   Driver Version: 470.141.10   CUDA Version: 11.4     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  Tesla V100-DGXS...  On   | 00000000:07:00.0  On |                    0 |
| N/A   48C    P0    41W / 300W |    218MiB / 16155MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
|   3  Tesla V100-DGXS...  On   | 00000000:0F:00.0 Off |                    0 |
| N/A   64C    P0    43W / 300W |     10MiB / 16158MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|    0   N/A  N/A      3174      G   /usr/lib/xorg/Xorg                102MiB |
|    0   N/A  N/A      4054      G   /usr/lib/xorg/Xorg                 67MiB |
|    0   N/A  N/A      4253      G   /usr/bin/gnome-shell               26MiB |
|    3   N/A  N/A      3174      G   /usr/lib/xorg/Xorg                  4MiB |
|    3   N/A  N/A      4054      G   /usr/lib/xorg/Xorg                  4MiB |
+-----------------------------------------------------------------------------+&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;GPU 전체 리스트로 출력&lt;/h3&gt;
&lt;pre id=&quot;code_1762146023467&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;nvidia-smi -L&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1762146033509&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GPU 0: Tesla V100-DGXS-16GB (UUID: GPU-4a5696d4-12ff-d8a3-f604-d25020c46dc9)
GPU 1: Tesla V100-DGXS-16GB (UUID: GPU-036e8961-7968-e8ed-05db-3ed1117387ab)
Unable to determine the device handle for gpu 0000:0E:00.0: Unknown Error
GPU 3: Tesla V100-DGXS-16GB (UUID: GPU-4ce0c45f-8be0-d20f-db73-e6e0e254b51f)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과부하 걸렸거나 온도가 높아서 죽어버린 GPU 도 위처럼 볼 수 있음&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;GPU 최대 전력 제한&lt;/h3&gt;
&lt;pre id=&quot;code_1762146299747&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ nvidia-smi -pm 1

$ nvidia-smi -pl 220
Power limit for GPU 00000000:07:00.0 was set to 220.00 W from 300.00 W.
Power limit for GPU 00000000:08:00.0 was set to 220.00 W from 300.00 W.
Power limit for GPU 00000000:0E:00.0 was set to 220.00 W from 300.00 W.
Power limit for GPU 00000000:0F:00.0 was set to 220.00 W from 300.00 W.
All done.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;옵션 전체 보기 (공식 문서)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.nvidia.com/deploy/nvidia-smi/index.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.nvidia.com/deploy/nvidia-smi/index.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1762146377049&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;https://docs.nvidia.com/deploy/nvidia-smi/index.html&quot; data-og-description=&quot;Operating state of the PSU. The power supply state can be any of the following: &amp;quot;Normal&amp;quot;, &amp;quot;Abnormal&amp;quot;, &amp;quot;High voltage&amp;quot;, &amp;quot;Fan failure&amp;quot;, &amp;quot;Heatsink temperature&amp;quot;, &amp;quot;Current limit&amp;quot;, &amp;quot;Voltage below UV alarm threshold&amp;quot;, &amp;quot;Low-voltage&amp;quot;, &amp;quot;I2C remote off command&amp;quot;, &amp;quot;MOD_&quot; data-og-host=&quot;docs.nvidia.com&quot; data-og-source-url=&quot;https://docs.nvidia.com/deploy/nvidia-smi/index.html&quot; data-og-url=&quot;https://docs.nvidia.com/deploy/nvidia-smi/index.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.nvidia.com/deploy/nvidia-smi/index.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.nvidia.com/deploy/nvidia-smi/index.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;https://docs.nvidia.com/deploy/nvidia-smi/index.html&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Operating state of the PSU. The power supply state can be any of the following: &quot;Normal&quot;, &quot;Abnormal&quot;, &quot;High voltage&quot;, &quot;Fan failure&quot;, &quot;Heatsink temperature&quot;, &quot;Current limit&quot;, &quot;Voltage below UV alarm threshold&quot;, &quot;Low-voltage&quot;, &quot;I2C remote off command&quot;, &quot;MOD_&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.nvidia.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발</category>
      <author>joonas</author>
      <guid isPermaLink="true">https://joonas.tistory.com/278</guid>
      <comments>https://joonas.tistory.com/278#entry278comment</comments>
      <pubDate>Mon, 3 Nov 2025 14:05:06 +0900</pubDate>
    </item>
    <item>
      <title>FormData 전송할 때 fetch vs. axios</title>
      <link>https://joonas.tistory.com/277</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;서버에 파일을 업로드하는 함수를 구현하던 중, axios를 fetch 로 변경하였는데 서버쪽에서 500 에러가 발생했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 서버에 도달한 요청 데이터에 RequestBody가 사라진 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 두 함수를 비교하면, 전혀 문제될 것이 없어보인다.&lt;br /&gt;먼저, axios를 사용하고 있던 기존의 함수 로직이다.&lt;/p&gt;
&lt;pre id=&quot;code_1744730147860&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;axios
  .post(ENDPOINT, formData, {
    headers: {
      'Content-Type': 'multipart/form-data',
    },
  })
  .then((data) =&amp;gt; console.log(data))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 fetch로 변경한 함수 로직이다.&lt;/p&gt;
&lt;pre id=&quot;code_1744730221501&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fetch(ENDPOINT, {
  method: 'POST',
  headers: {
    'Content-Type': 'multipart/form-data',
  },
  body: formData,
})
  .then((data) =&amp;gt; console.log(data))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;결론부터 말하자면,&lt;/b&gt; 아래와 같이 고치면 정상적으로 동작한다.&lt;/p&gt;
&lt;pre id=&quot;code_1744730623401&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fetch(ENDPOINT, {
  method: 'POST',
  body: formData,
})
  .then((data) =&amp;gt; console.log(data))&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style4&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유를 알 수 없어서 여러 방법으로 분석해보았다.&lt;br /&gt;서버쪽 로그도 찍어보고, Postman으로도 전송해보았는데 서버 문제는 아니었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 axios와 fetch의 동작에 차이가 있다는 것으로 보여서 네트워크로 전송되는 데이터를 비교해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 axios를 통해 전송되는 데이터이다.&lt;/p&gt;
&lt;pre id=&quot;code_1744730389652&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;POST /upload HTTP/1.1
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: ko
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 235
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary1x8F0uD1L2QhdmuW
DNT: 1
Host: localhost:3000
Origin: http://localhost:3001
Pragma: no-cache
Referer: http://localhost:3001/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36
sec-ch-ua: &quot;Google Chrome&quot;;v=&quot;135&quot;, &quot;Not-A.Brand&quot;;v=&quot;8&quot;, &quot;Chromium&quot;;v=&quot;135&quot;
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: &quot;Windows&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 fetch를 통해 전송되는 데이터이다.&lt;/p&gt;
&lt;pre id=&quot;code_1744730499627&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;POST /upload HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: ko
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 235
Content-Type: multipart/form-data
DNT: 1
Host: localhost:3000
Origin: http://localhost:3001
Pragma: no-cache
Referer: http://localhost:3001/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36
sec-ch-ua: &quot;Google Chrome&quot;;v=&quot;135&quot;, &quot;Not-A.Brand&quot;;v=&quot;8&quot;, &quot;Chromium&quot;;v=&quot;135&quot;
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: &quot;Windows&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 데이터에서 유일하게 다른 부분은 Content-Type 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도대체 왜 fetch 에서는 boundary 가 사라진 것일까?&lt;br /&gt;그렇다면 boundary 를 붙이고 내용물을 동일하게 가공하여 전송하면 제대로 동작할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(일단 되는지 확인하기 위해서) 아래처럼 FormData 를 쪼개서 전송 폼에 맞게 만들어보았다.&lt;/p&gt;
&lt;pre id=&quot;code_1744730932124&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const boundary = '----WebKitFormBoundary' + 'caKRudE3BYlaVvsk';

const fields = Array.from(formData).map(([key, value], index) =&amp;gt; {
  // 먼저 string 타입만 전송 테스트
  return `Content-Disposition: form-data; name=&quot;${key}&quot;\n\n${value}\n${boundary}`;
});
const encodedBody = `--${boundary}\n${fields.join('\n')}--`;
console.log(encodedBody);

fetch(ENDPOINT, {
  method: 'POST',
  headers: {
    'Content-Type': 'multipart/form-data; boundary=' + boundary,
  },
  body: encodedBody,
})
  .then((data) =&amp;gt; console.log(data))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안타깝게도 세상은 그렇게 호락호락하지 않다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;295&quot; data-origin-height=&quot;147&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckkXBG/btsNl8igEbU/anYv0ldubjNEKXsvuNpz21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckkXBG/btsNl8igEbU/anYv0ldubjNEKXsvuNpz21/img.png&quot; data-alt=&quot;multipart/form-data payload (fake)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckkXBG/btsNl8igEbU/anYv0ldubjNEKXsvuNpz21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckkXBG%2FbtsNl8igEbU%2FanYv0ldubjNEKXsvuNpz21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;295&quot; height=&quot;147&quot; data-origin-width=&quot;295&quot; data-origin-height=&quot;147&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;multipart/form-data payload (fake)&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;내용물을 동일하게 만들었다하더라도, 파라미터로 넘기는 객체가 FormData 타입이 아니기때문에 Content-Length 도 다르고 fetch 함수 내부에서 다르게 처리되는 것인지 올바르지 않은 포맷으로 전송되고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/axios/axios&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;axios&lt;/a&gt;는 &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/XMLHttpRequest&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;XMLHttpRequest&lt;/a&gt; 객체를 사용하는 라이브러리이다. 개인적으로 XMLHttpRequest 의 사용 방법이 너무나도 불편했고 axios는 물론 과거 ajax 역시 초기 세팅 등 사용하기에 편하지는 않았다. 그래서 &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/Fetch_API&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Fetch API&lt;/a&gt;가 등장한 이후로는 너무 유용하게 사용하고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼 이런 차이때문인지는 몰라도, &lt;b&gt;fetch에서는 multipart/form-data의 경우&lt;/b&gt;에는 &lt;b&gt;Content-Type을 지정하지 않아야&lt;/b&gt; boundary가 자동으로 붙어서 전송되어 정상적으로 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택오버플로우의 답변으로부터 약 10년이 지난 지금까지도 유효한 방법이다 (...)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style4&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&gt;
&lt;figure id=&quot;og_1744729858283&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;fetch - Missing boundary in multipart/form-data POST&quot; data-og-description=&quot;I want to send a new FormData() as the body of a POST request using the fetch api The operation looks something like this: var formData = new FormData() formData.append('myfile', file, 'someFileNam...&quot; data-og-host=&quot;stackoverflow.com&quot; data-og-source-url=&quot;https://stackoverflow.com/questions/39280438/fetch-missing-boundary-in-multipart-form-data-post&quot; data-og-url=&quot;https://stackoverflow.com/questions/39280438/fetch-missing-boundary-in-multipart-form-data-post&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/duFXlU/hyYId55ts3/Y82cfz7zKN6fbfmuy3EFO1/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/39280438/fetch-missing-boundary-in-multipart-form-data-post&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stackoverflow.com/questions/39280438/fetch-missing-boundary-in-multipart-form-data-post&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/duFXlU/hyYId55ts3/Y82cfz7zKN6fbfmuy3EFO1/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;fetch - Missing boundary in multipart/form-data POST&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;I want to send a new FormData() as the body of a POST request using the fetch api The operation looks something like this: var formData = new FormData() formData.append('myfile', file, 'someFileNam...&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;figure id=&quot;og_1744731590204&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 boundary in multipart/form-data?&quot; data-og-description=&quot;I want to ask a question about the multipart/form-data. In the HTTP header, I find that the Content-Type: multipart/form-data; boundary=???. Is the ??? free to be defined by the user? Or is it&quot; data-og-host=&quot;stackoverflow.com&quot; data-og-source-url=&quot;https://stackoverflow.com/questions/3508338/what-is-the-boundary-in-multipart-form-data&quot; data-og-url=&quot;https://stackoverflow.com/questions/3508338/what-is-the-boundary-in-multipart-form-data&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/O7Czd/hyYG8qDoc2/mEGe3nL8rzxwXVjSIZpZJ1/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/3508338/what-is-the-boundary-in-multipart-form-data&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stackoverflow.com/questions/3508338/what-is-the-boundary-in-multipart-form-data&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/O7Czd/hyYG8qDoc2/mEGe3nL8rzxwXVjSIZpZJ1/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 boundary in multipart/form-data?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;I want to ask a question about the multipart/form-data. In the HTTP header, I find that the Content-Type: multipart/form-data; boundary=???. Is the ??? free to be defined by the user? Or is it&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;</description>
      <category>개발/Javascript</category>
      <category>axios</category>
      <category>Fetch</category>
      <category>JavaScript</category>
      <author>joonas</author>
      <guid isPermaLink="true">https://joonas.tistory.com/277</guid>
      <comments>https://joonas.tistory.com/277#entry277comment</comments>
      <pubDate>Wed, 16 Apr 2025 00:47:27 +0900</pubDate>
    </item>
    <item>
      <title>[React] useDeepMemo</title>
      <link>https://joonas.tistory.com/276</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;primitive 하게 비교되지 않는 object들은 의존성 배열에서 같은 값으로 인식하지 않는다.&lt;br /&gt;이건 javascript 의 비교 연산자가 얕은 비교를 하기 때문이다. 아래는 대표적인 사례.&lt;/p&gt;
&lt;pre id=&quot;code_1744643480007&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{} == {}
// output: false&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 아래의 memo는 전혀 캐싱되지 않기 때문에 어떤 스노우볼을 굴릴 지 모른다.&lt;/p&gt;
&lt;pre id=&quot;code_1744643403706&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const complexObject = {a:1, b:2, c:&quot;xyz&quot;}
// 의존성 배열은 항상 다른 값으로 인식된다.
const complexMemo = useMemo(() =&amp;gt; complexObject, [complextObject])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 비교 대상을 문자열로 변경해서 해결하는 방법도 있는데, 보기에도 느껴지지만 그렇게 권장되는 방법은 아니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744643578685&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const complexObject = {a:1, b:2, c:&quot;xyz&quot;}
const complexMemo = useMemo(() =&amp;gt; complexObject, [JSON.stringify(complextObject)])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 방법과 크게 다르지 않지만, 핵심은 의존성 배열 내의 객체를 비교해야 하는 것이므로, 구조적으로 변경할 수 있게 아래처럼 작성해서 사용하고 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1744643277375&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// useDeepHooks.ts
import { useRef, useEffect, useMemo } from 'react'

const serialize = (obj: any) =&amp;gt; {
  if (typeof obj === 'object') {
    return JSON.stringify(obj)
  }
  return obj
}

export function useDeepMemo(callback: () =&amp;gt; any, dependencies: any[]) {
  const depsStringified = useMemo(
    () =&amp;gt; dependencies.map(serialize),
    dependencies,
  )
  return useMemo(() =&amp;gt; callback(), depsStringified)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트 개발자 중 한 명은 이 외에도 &lt;a href=&quot;https://github.com/facebook/react/issues/14476#issuecomment-471199055&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2가지 방법&lt;/a&gt;을 더 제시하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 외에도 ref 를 사용해서 상태를 들고 있는 방법도 있는데, Object 비교에서 실수한건지 제대로 동작하진 않았다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고&lt;/h3&gt;
&lt;figure id=&quot;og_1744643913157&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;When to useMemo and useCallback&quot; data-og-description=&quot;Stay up to date Stay up to date All rights reserved &amp;copy; Kent C. Dodds 2025&quot; data-og-host=&quot;kentcdodds.com&quot; data-og-source-url=&quot;https://kentcdodds.com/blog/usememo-and-usecallback&quot; data-og-url=&quot;https://kentcdodds.com/blog/usememo-and-usecallback&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/gGD4N/hyYCezkALZ/l0NoyzOQzgmQD3HEIwSHx1/img.png?width=2400&amp;amp;height=1256&amp;amp;face=165_746_1964_1061,https://scrap.kakaocdn.net/dn/czEmBZ/hyYB8lAHqh/S7tAYA5DLoFbbvZrqWv910/img.jpg?width=1517&amp;amp;height=1011&amp;amp;face=730_621_817_715,https://scrap.kakaocdn.net/dn/zBLnQ/hyYEIz8evq/bOBxfmKcFuohWfGycHKh2k/img.jpg?width=955&amp;amp;height=1273&amp;amp;face=0_0_955_1273&quot;&gt;&lt;a href=&quot;https://kentcdodds.com/blog/usememo-and-usecallback&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://kentcdodds.com/blog/usememo-and-usecallback&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/gGD4N/hyYCezkALZ/l0NoyzOQzgmQD3HEIwSHx1/img.png?width=2400&amp;amp;height=1256&amp;amp;face=165_746_1964_1061,https://scrap.kakaocdn.net/dn/czEmBZ/hyYB8lAHqh/S7tAYA5DLoFbbvZrqWv910/img.jpg?width=1517&amp;amp;height=1011&amp;amp;face=730_621_817_715,https://scrap.kakaocdn.net/dn/zBLnQ/hyYEIz8evq/bOBxfmKcFuohWfGycHKh2k/img.jpg?width=955&amp;amp;height=1273&amp;amp;face=0_0_955_1273');&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;When to useMemo and useCallback&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Stay up to date Stay up to date All rights reserved &amp;copy; Kent C. Dodds 2025&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;kentcdodds.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1744643948160&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;useCallback/useEffect support custom comparator &amp;middot; Issue #14476 &amp;middot; facebook/react&quot; data-og-description=&quot;Currently we can pass an array as second argument when using useCallback or useEffect like below: useCallback(()=&amp;gt; { doSth(a, b) }, [a, b]) // how to do deep equal if a is an object ? The problem i...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/facebook/react/issues/14476#issuecomment-471199055&quot; data-og-url=&quot;https://github.com/facebook/react/issues/14476&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/DsZPR/hyYFAgsxVG/Pk4v1jehSe8FPKOIjVcFn0/img.png?width=1200&amp;amp;height=600&amp;amp;face=965_99_1086_232,https://scrap.kakaocdn.net/dn/OVVPZ/hyYChCSdOR/OTdeM1xTbktvBoVYdz27F0/img.png?width=1200&amp;amp;height=600&amp;amp;face=965_99_1086_232&quot;&gt;&lt;a href=&quot;https://github.com/facebook/react/issues/14476#issuecomment-471199055&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/facebook/react/issues/14476#issuecomment-471199055&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/DsZPR/hyYFAgsxVG/Pk4v1jehSe8FPKOIjVcFn0/img.png?width=1200&amp;amp;height=600&amp;amp;face=965_99_1086_232,https://scrap.kakaocdn.net/dn/OVVPZ/hyYChCSdOR/OTdeM1xTbktvBoVYdz27F0/img.png?width=1200&amp;amp;height=600&amp;amp;face=965_99_1086_232');&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;useCallback/useEffect support custom comparator &amp;middot; Issue #14476 &amp;middot; facebook/react&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Currently we can pass an array as second argument when using useCallback or useEffect like below: useCallback(()=&amp;gt; { doSth(a, b) }, [a, b]) // how to do deep equal if a is an object ? The problem i...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/Javascript</category>
      <category>Deep</category>
      <category>hooks</category>
      <category>React</category>
      <author>joonas</author>
      <guid isPermaLink="true">https://joonas.tistory.com/276</guid>
      <comments>https://joonas.tistory.com/276#entry276comment</comments>
      <pubDate>Tue, 15 Apr 2025 00:22:57 +0900</pubDate>
    </item>
    <item>
      <title>로또가 의심돼! (feat. 당첨 숫자가 하나도 없을 확률)</title>
      <link>https://joonas.tistory.com/275</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;로또는 우리 인생에서 확률이라는 보이지 않는 수를 몸으로 체감할 수 있는 좋은 기회다.&lt;br /&gt;하지만 늘 조작이라는 의심이 따르는 편이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 문득 로또를 구매하다가 역대 당첨 기록으로부터 조작의 증거를 발견할 수 있을까 하는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다. 혹시 &lt;a href=&quot;https://ko.wikipedia.org/wiki/%EB%B2%A4%ED%8F%AC%EB%93%9C%EC%9D%98_%EB%B2%95%EC%B9%99&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;벤포드의 법칙&lt;/a&gt;이 통할 것인가 궁금해서 확인해보려다가, 조금 더 생각해보니까 아니라는 생각이 들었다.&lt;br /&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;각 회차별로 등장하는 당첨 숫자는 7개(보너스 숫자도 포함)이다. 그럼 변수가 7개이므로 각 \(p(X_1)\) 부터 \(p(X_7)\) 에 대한 &lt;a href=&quot;https://en.wikipedia.org/wiki/Joint_probability_distribution&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;결합 확률 분포(Joint probability distribution)&lt;/a&gt;를 계산해야하나 싶었다. 근데 또 각 변수가 독립은 아닌데.... (모르겠다 너무 어렵다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1부터 45까지 중에서 뽑은 7개의 숫자 합은 최소 28(1+2+3+4+5+6+7), 최대 294(39+40+41+42+43+44+45)이다.&lt;br /&gt;한 회차에서 숫자들의 등장 확률은 조합이므로 정규 분포를 따른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 각 회차에서 등장하는 숫자들의 합들의 분포를 찍어봐도 정규 분포를 따라야하지 않을까?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;633&quot; data-origin-height=&quot;413&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgBl7H/btsM11j2QJr/FbdmoJoifCY8BdJWMqg30K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgBl7H/btsM11j2QJr/FbdmoJoifCY8BdJWMqg30K/img.png&quot; data-alt=&quot;각 회차별 당첨 숫자합에 대한 분포표&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgBl7H/btsM11j2QJr/FbdmoJoifCY8BdJWMqg30K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgBl7H%2FbtsM11j2QJr%2FFbdmoJoifCY8BdJWMqg30K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;499&quot; height=&quot;326&quot; data-origin-width=&quot;633&quot; data-origin-height=&quot;413&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;각 회차별 당첨 숫자합에 대한 분포표&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;X축은 각 회차에서 등장한 당첨 숫자들의 합, Y축은 그러한 합이 등장할 확률이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엄밀하게 확인하려던 게 아니라 추이만 보려고 한 것이라 이 정도에서 마친다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;로또 하나도 안 맞을 확률&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;435&quot; data-origin-height=&quot;668&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjBatJ/btsM2xioEl1/9XGki54Di1ENbnnbHVUTok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjBatJ/btsM2xioEl1/9XGki54Di1ENbnnbHVUTok/img.png&quot; data-alt=&quot;당첨 번호를 모두 피한 5개의 자동 추첨&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjBatJ/btsM2xioEl1/9XGki54Di1ENbnnbHVUTok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjBatJ%2FbtsM2xioEl1%2F9XGki54Di1ENbnnbHVUTok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;435&quot; height=&quot;668&quot; data-origin-width=&quot;435&quot; data-origin-height=&quot;668&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;당첨 번호를 모두 피한 5개의 자동 추첨&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 문제를 맞힌 사람 역시 대단하지만, 모든 문제를 틀린 사람도 대단하다고 할 수 있다.&lt;br /&gt;보너스 숫자를 제외하고 6개의 번호가 일치하는 확률은 814만분의 1이지만, 이 경우에는 확률이 어떻게 되는 지 궁금해졌다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;532&quot; data-origin-height=&quot;312&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccMWOn/btsM2xbEbLf/iq9KGOi95Ukr5OIpDhkso1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccMWOn/btsM2xbEbLf/iq9KGOi95Ukr5OIpDhkso1/img.png&quot; data-alt=&quot;여기 한 사람 더 있었다. (출처: 홍차넷)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccMWOn/btsM2xbEbLf/iq9KGOi95Ukr5OIpDhkso1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FccMWOn%2FbtsM2xbEbLf%2Fiq9KGOi95Ukr5OIpDhkso1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;377&quot; height=&quot;221&quot; data-origin-width=&quot;532&quot; data-origin-height=&quot;312&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;여기 한 사람 더 있었다. (출처: 홍차넷)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;45개의 숫자 중에서 당첨 숫자인 6개를 제외한 39개 중에서 6개의 숫자를 뽑을 확률을 구하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(\frac{39}{45} \cdot \frac{38}{44} \cdot \frac{37}{43} \cdot \frac{36}{42} \cdot \frac{35}{41} \cdot \frac{34}{40}~=~\frac{2~349~088~560}{5~864~443~200}~=0.4005646\) 로, 약 40% 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 5개의 줄을 샀으므로, 서로 독립인 5개의 줄에서 모두 등장하지 않으려면, 40%가 5번 동시에 일어나야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\({0.4005646}^5~=~0.01031247\) 로, 약 1%의 확률이다. 이것도 쉽지 않은 확률인데, 당첨 확률은 이것보다 8만배 높다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고한 글&lt;/h3&gt;
&lt;figure id=&quot;og_1743343207668&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;로또 회차별(1회~1165회) 당첨번호&quot; data-og-description=&quot;로또 회차별 당첨번호&amp;nbsp;1회 ~ 1165회&amp;nbsp;&amp;nbsp;*로또 당첨시간 토요일 8시 35분경으로 변경(방송사 :MBC)&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &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; data-og-host=&quot;signalfire85.tistory.com&quot; data-og-source-url=&quot;https://signalfire85.tistory.com/28&quot; data-og-url=&quot;https://signalfire85.tistory.com/28&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/MHKhN/hyYvuWxmi3/hQpIZFrIlc6CUVscflneL1/img.jpg?width=320&amp;amp;height=425&amp;amp;face=0_0_320_425,https://scrap.kakaocdn.net/dn/bmPihX/hyYyMH8aB3/0muSDp8sg5n4B9EVt23NZk/img.jpg?width=320&amp;amp;height=425&amp;amp;face=0_0_320_425,https://scrap.kakaocdn.net/dn/bZlcyh/hyYviBVkH7/O76pb5Jo0v1Uo4UyUb1shK/img.jpg?width=320&amp;amp;height=425&amp;amp;face=193_145_254_206&quot;&gt;&lt;a href=&quot;https://signalfire85.tistory.com/28&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://signalfire85.tistory.com/28&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/MHKhN/hyYvuWxmi3/hQpIZFrIlc6CUVscflneL1/img.jpg?width=320&amp;amp;height=425&amp;amp;face=0_0_320_425,https://scrap.kakaocdn.net/dn/bmPihX/hyYyMH8aB3/0muSDp8sg5n4B9EVt23NZk/img.jpg?width=320&amp;amp;height=425&amp;amp;face=0_0_320_425,https://scrap.kakaocdn.net/dn/bZlcyh/hyYviBVkH7/O76pb5Jo0v1Uo4UyUb1shK/img.jpg?width=320&amp;amp;height=425&amp;amp;face=193_145_254_206');&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;로또 회차별(1회~1165회) 당첨번호&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;로또 회차별 당첨번호&amp;nbsp;1회 ~ 1165회&amp;nbsp;&amp;nbsp;*로또 당첨시간 토요일 8시 35분경으로 변경(방송사 :MBC)&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &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 class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;signalfire85.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1743343249994&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;확알못입니다. 따끈따끈한 이번주 로또 결과인데요. 로또가 사진처럼 하나도 안 맞을 확률이 어떻게 되나요? 확잘알님들 도와주세요 흐흐 1등을 바랬는데 뒤에서 1등을 할 줄이야... ㅠㅠ&quot; data-og-host=&quot;redtea.kr&quot; data-og-source-url=&quot;https://redtea.kr/qna/1041&quot; data-og-url=&quot;https://redtea.kr/qna/1041&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/r9qBU/hyYxExjvCm/dPoQm4RY7sA1cCcgU2rg2k/img.png?width=1440&amp;amp;height=2560&amp;amp;face=0_0_1440_2560,https://scrap.kakaocdn.net/dn/fphPg/hyYxIGuUO2/WNP46BSfL4XsJR7aCSbylk/img.png?width=1440&amp;amp;height=2560&amp;amp;face=0_0_1440_2560&quot;&gt;&lt;a href=&quot;https://redtea.kr/qna/1041&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://redtea.kr/qna/1041&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/r9qBU/hyYxExjvCm/dPoQm4RY7sA1cCcgU2rg2k/img.png?width=1440&amp;amp;height=2560&amp;amp;face=0_0_1440_2560,https://scrap.kakaocdn.net/dn/fphPg/hyYxIGuUO2/WNP46BSfL4XsJR7aCSbylk/img.png?width=1440&amp;amp;height=2560&amp;amp;face=0_0_1440_2560');&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;확알못입니다. 따끈따끈한 이번주 로또 결과인데요. 로또가 사진처럼 하나도 안 맞을 확률이 어떻게 되나요? 확잘알님들 도와주세요 흐흐 1등을 바랬는데 뒤에서 1등을 할 줄이야... ㅠㅠ&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;redtea.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>수학</category>
      <category>확률</category>
      <category>확률밀도함수</category>
      <category>확률분포</category>
      <author>joonas</author>
      <guid isPermaLink="true">https://joonas.tistory.com/275</guid>
      <comments>https://joonas.tistory.com/275#entry275comment</comments>
      <pubDate>Sun, 30 Mar 2025 23:51:50 +0900</pubDate>
    </item>
    <item>
      <title>[Windows 10] pytorch3d 설치 중 트러블 슈팅</title>
      <link>https://joonas.tistory.com/274</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;환경 세팅&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프록시 설정 때문에 pip install pytorch3d 는 불가한 상황이었고, &lt;a href=&quot;https://github.com/facebookresearch/pytorch3d/blob/main/INSTALL.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식 설치 문서&lt;/a&gt;를 참고해서 직접 패키지를 설치해야했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 conda 를 잘 사용하지 않기도 하고, conda 세팅도 불가한 상황이라 virtualenv 로 실행 환경을 구축한 상황이다.&lt;/p&gt;
&lt;pre id=&quot;code_1742509169901&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;python -m virtualenv .venv
source .venv/Scripts/activate
python -m pip install pytorch torchvision pytorch-cuda&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;CL 명령어를 못 찾음&lt;/h2&gt;
&lt;pre id=&quot;code_1742288381194&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
copying pytorch3d\datasets\shapenet\shapenet_synset_dict_v2.json -&amp;gt; build\lib.win-amd64-cpython-311\pytorch3d\datasets\shapenet
running build_ext
D:\MLTest\.venv\Lib\site-packages\torch\utils\cpp_extension.py:380: UserWarning: Error checking compiler version for cl: [WinError 2] 지정된 파일을 찾을 수 없습니다
  warnings.warn(f'Error checking compiler version for {compiler}: {error}')
error: [WinError 2] 지정된 파일을 찾을 수 없습니다&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio Tools 에 있는 cl.exe 를 실행할 수 있도록 환경 변수 추가&lt;/p&gt;
&lt;pre id=&quot;code_1742288419260&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export PATH=$PATH:/d/Programs/Visual\ Studio/VC/Tools/MSVC/14.36.32532/bin/Hostx64/x64&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고&lt;/h3&gt;
&lt;figure id=&quot;og_1742288602999&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;Installed Visual Studio 2022 but 'cl' is not recognized as an internal or external command&quot; data-og-description=&quot;None of my terminals on Windows 10 recognize cl 'as an internal or external command'. I have Visual Studio 2022 installed and I've tried it on every terminal, including terminals in the Visual Studio&quot; data-og-host=&quot;stackoverflow.com&quot; data-og-source-url=&quot;https://stackoverflow.com/a/71201077&quot; data-og-url=&quot;https://stackoverflow.com/questions/70840683/installed-visual-studio-2022-but-cl-is-not-recognized-as-an-internal-or-extern&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/vrMiC/hyYukY5xJG/BxF6yY3IlAOADbPHUIz1z0/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316,https://scrap.kakaocdn.net/dn/czUFQ0/hyYp9LM93N/ZXxIr10eeohmTGjgcxbkp0/img.png?width=1779&amp;amp;height=653&amp;amp;face=0_0_1779_653&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/a/71201077&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stackoverflow.com/a/71201077&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/vrMiC/hyYukY5xJG/BxF6yY3IlAOADbPHUIz1z0/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316,https://scrap.kakaocdn.net/dn/czUFQ0/hyYp9LM93N/ZXxIr10eeohmTGjgcxbkp0/img.png?width=1779&amp;amp;height=653&amp;amp;face=0_0_1779_653');&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;Installed Visual Studio 2022 but 'cl' is not recognized as an internal or external command&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;None of my terminals on Windows 10 recognize cl 'as an internal or external command'. I have Visual Studio 2022 installed and I've tried it on every terminal, including terminals in the Visual Studio&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;build_ext 이후 에러&lt;/h2&gt;
&lt;pre id=&quot;code_1742288476417&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;adding license file 'LICENSE-3RD-PARTY'
writing manifest file 'pytorch3d.egg-info\SOURCES.txt'
installing library code to build\bdist.win-amd64\egg
running install_lib
running build_py
running build_ext
error: [WinError 2] 지정된 파일을 찾을 수 없습니다&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CUDA_HOME 또는 CUDA_PATH 환경 변수 설정&lt;/p&gt;
&lt;pre id=&quot;code_1742288753215&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export CUDA_HOME=/d/Programs/NVIDIA\ Toolkits/CUDA/v12.4&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경 변수 설정하고 다시 실행해보면 다음 단계인 building 'pytorch3d._C' extension 를 시작한다.&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;a href=&quot;https://stackoverflow.com/a/77463619&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://stackoverflow.com/a/77463619&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1742288777815&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;running build_ext gives &amp;#96;[WinError 2] The system cannot find the file specified&amp;#96; while building detectron2&quot; data-og-description=&quot;I'm currently trying to build detectron2 in environment in Anaconda. Below is the packages that I'm using in my env: windows 11 conda package python 3.8 cuda 11.3 pytorch 1.11 torchaudio 0.11.0&quot; data-og-host=&quot;stackoverflow.com&quot; data-og-source-url=&quot;https://stackoverflow.com/a/77463619&quot; data-og-url=&quot;https://stackoverflow.com/questions/76488233/running-build-ext-gives-winerror-2-the-system-cannot-find-the-file-specified&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bZjKVD/hyYuq55nPN/vcvxlgTNzuJ2kq0P8p15v1/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/a/77463619&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stackoverflow.com/a/77463619&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bZjKVD/hyYuq55nPN/vcvxlgTNzuJ2kq0P8p15v1/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;running build_ext gives `[WinError 2] The system cannot find the file specified` while building detectron2&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;I'm currently trying to build detectron2 in environment in Anaconda. Below is the packages that I'm using in my env: windows 11 conda package python 3.8 cuda 11.3 pytorch 1.11 torchaudio 0.11.0&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;</description>
      <category>개발/python</category>
      <category>pytorch3d</category>
      <author>joonas</author>
      <guid isPermaLink="true">https://joonas.tistory.com/274</guid>
      <comments>https://joonas.tistory.com/274#entry274comment</comments>
      <pubDate>Fri, 21 Mar 2025 07:19:54 +0900</pubDate>
    </item>
    <item>
      <title>사과 게임 헬퍼 만들어보기</title>
      <link>https://joonas.tistory.com/273</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;게임&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.gamesaien.com/game/fruit_box_a/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.gamesaien.com/game/fruit_box_a/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1741505083842&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;画面上をマウスでドラッグして、数字の合計が10になるようにリンゴを囲むパズルゲームです。(説明) iPhone,iPadやAndroidでも動作します。&quot; data-og-host=&quot;www.gamesaien.com&quot; data-og-source-url=&quot;https://www.gamesaien.com/game/fruit_box_a/&quot; data-og-url=&quot;https://www.gamesaien.com/game/fruit_box_a/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.gamesaien.com/game/fruit_box_a/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.gamesaien.com/game/fruit_box_a/&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;画面上をマウスでドラッグして、数字の合計が10になるようにリンゴを囲むパズルゲームです。(説明) iPhone,iPadやAndroidでも動作します。&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.gamesaien.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;요약&lt;/h2&gt;
&lt;div id=&quot;code_1741505108587&quot; data-ke-type=&quot;html&quot; data-source=&quot;&amp;lt;iframe width=&amp;quot;560&amp;quot; height=&amp;quot;315&amp;quot; src=&amp;quot;https://www.youtube.com/embed/Y9KoMT_X3jw?si=iJSeyh7aoA3OuqrQ&amp;quot; title=&amp;quot;YouTube video player&amp;quot; frameborder=&amp;quot;0&amp;quot; allow=&amp;quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&amp;quot; referrerpolicy=&amp;quot;strict-origin-when-cross-origin&amp;quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/Y9KoMT_X3jw?si=iJSeyh7aoA3OuqrQ&quot; width=&quot;560&quot; height=&quot;315&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전에 참 재밌게 했었던 게임인데, 오랜만에 다시 하려니 심신이 피로하고 무척 귀찮아서 차라리 사각형을 보여주는 걸 코딩해보면 재밌겠다는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플래시 게임 수준의 1인용 로컬 게임이기도 하니, 핵이나 매크로를 만든다고 해서 피해가 생기진 않을테고...&lt;br /&gt;어디서 사용한다고 쳐도 너무 눈에 띄는 수준이라 만들고 공개도 해볼만하다고 생각해서 진행했다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style4&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;구현&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;게임 동작 방식&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;470&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cknCO3/btsMDSIPaZX/JxZ6IrkToRFt5oUrVpncKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cknCO3/btsMDSIPaZX/JxZ6IrkToRFt5oUrVpncKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cknCO3/btsMDSIPaZX/JxZ6IrkToRFt5oUrVpncKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcknCO3%2FbtsMDSIPaZX%2FJxZ6IrkToRFt5oUrVpncKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;375&quot; height=&quot;245&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;470&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가로로 17개, 세로로 10개의 사과가 있고, 각 사과에는 1부터 9 까지의 숫자가 적혀있다.&lt;br /&gt;아래와 같이 숫자의 합이 10이 되는 사각형을 찾아서 드래그하면 점수를 얻는다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;162&quot; data-origin-height=&quot;96&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btdqQ3/btsMEOet0D9/Bt601T5RuKLMX9D2eI4Sd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btdqQ3/btsMEOet0D9/Bt601T5RuKLMX9D2eI4Sd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btdqQ3/btsMEOet0D9/Bt601T5RuKLMX9D2eI4Sd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtdqQ3%2FbtsMEOet0D9%2FBt601T5RuKLMX9D2eI4Sd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;162&quot; height=&quot;96&quot; data-origin-width=&quot;162&quot; data-origin-height=&quot;96&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;326&quot; data-origin-height=&quot;25&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BlpB9/btsMD43kyKa/i8myMkdo09TltIDEWEuLek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BlpB9/btsMD43kyKa/i8myMkdo09TltIDEWEuLek/img.png&quot; data-alt=&quot;게임판에서 적당히 20x20 크기로 잘라낸 숫자들&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BlpB9/btsMD43kyKa/i8myMkdo09TltIDEWEuLek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBlpB9%2FbtsMD43kyKa%2Fi8myMkdo09TltIDEWEuLek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;326&quot; height=&quot;25&quot; data-origin-width=&quot;326&quot; data-origin-height=&quot;25&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;게임판에서 적당히 20x20 크기로 잘라낸 숫자들&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다행히도 HTML5 canvas 기반이라서 쉽게 이미지를 가져올 수 있었다. 적당히 기준점을 (75, 80) 위치로 잡고 33 x 33 크기로 잘라봤는데 모든 사과들의 위치가 규칙적이라, 이미지를 얻는 데 큰 문제는 없었다.&lt;/p&gt;
&lt;pre id=&quot;code_1741507219634&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const getImageData = (x, y) =&amp;gt; {
  return ctx.getImageData(75 + x * 33, 80 + y * 33, 20, 20);
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진지하게 숫자 인식을 사용해볼까하다가 tensorflow.js 같은 외부 라이브러리를 쓰는 수고가 더 들 것 같았다.&lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;어차피 재미로 짜보는 코드인데,&lt;span&gt; 완성도는 나중에 생각하고 로직을 먼저 완성해보는 것이 맞아보인다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;642&quot; data-origin-height=&quot;321&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dk559G/btsMFwc3u5a/J040lKolkT5cYigKAR9bCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dk559G/btsMFwc3u5a/J040lKolkT5cYigKAR9bCk/img.png&quot; data-alt=&quot;대충 모든 숫자를 겹쳐 본 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dk559G/btsMFwc3u5a/J040lKolkT5cYigKAR9bCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdk559G%2FbtsMFwc3u5a%2FJ040lKolkT5cYigKAR9bCk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;474&quot; height=&quot;237&quot; data-origin-width=&quot;642&quot; data-origin-height=&quot;321&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;대충 모든 숫자를 겹쳐 본 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어차피 9개의 숫자가 서로 구분만 되면 되니까, 숫자별로 위치가 다르게 나올 수 밖에 없는 조합을 골라서 해싱으로 비교했다.&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;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ezl3Hq/btsME5tt4Fd/GkYMUMwXZikJOvEEy6yJ30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ezl3Hq/btsME5tt4Fd/GkYMUMwXZikJOvEEy6yJ30/img.png&quot; data-alt=&quot;노란색 박스 안에서 적당히 6개의 픽셀 선정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ezl3Hq/btsME5tt4Fd/GkYMUMwXZikJOvEEy6yJ30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fezl3Hq%2FbtsME5tt4Fd%2FGkYMUMwXZikJOvEEy6yJ30%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;240&quot; data-origin-width=&quot;240&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;노란색 박스 안에서 적당히 6개의 픽셀 선정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;숫자 인식에 대한 자세한 내용 보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 설명은 생략한다.&lt;/p&gt;
&lt;pre id=&quot;code_1741508621285&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 20x20 pixels =&amp;gt; 6 pixels X 3(rgb) =&amp;gt; 18개
const pca = ({data}) =&amp;gt; {
  const loc = (x, y) =&amp;gt; 4 * (y * 20 + x);
  const rgb = (x, y) =&amp;gt; {
    const i = loc(x, y);
    const s = data.slice(i, i + 4);
    return [s[0], s[1], s[2]];
  };
  return [rgb(5, 5), rgb(12, 5), rgb(5, 9), rgb(12, 9), rgb(5, 13), rgb(12, 13)];
};
const hash = (imgData) =&amp;gt; pca(imgData).flat().reduce((a, c) =&amp;gt; (a * 256 + c) % 213, 0);
const answers = [37, 48, 145, 205, 175, 146, 59, 139, 70];
const getNumber = (imgData) =&amp;gt; answers.indexOf(hash(imgData)) + 1;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;414&quot; data-origin-height=&quot;239&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/doflCN/btsMGgtZSd1/fq6b1yPioui97QMOoNfNHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/doflCN/btsMGgtZSd1/fq6b1yPioui97QMOoNfNHk/img.png&quot; data-alt=&quot;첫 번째 스크린샷과 비교하면 성공적으로 숫자 인식됨&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/doflCN/btsMGgtZSd1/fq6b1yPioui97QMOoNfNHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdoflCN%2FbtsMGgtZSd1%2Ffq6b1yPioui97QMOoNfNHk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;414&quot; height=&quot;239&quot; data-origin-width=&quot;414&quot; data-origin-height=&quot;239&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;첫 번째 스크린샷과 비교하면 성공적으로 숫자 인식됨&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 캔버스의 크기나 주변 픽셀 등의 영향으로 rgb 색상이 조금이라도 바뀐다면 동작하지 않는 코드지만, 아래의 짤로 마무리하겠다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;217&quot; data-origin-height=&quot;189&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pb8Fm/btsMEUeC1Wm/I7zeBV0MKb16oqkLtlO861/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pb8Fm/btsMEUeC1Wm/I7zeBV0MKb16oqkLtlO861/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pb8Fm/btsMEUeC1Wm/I7zeBV0MKb16oqkLtlO861/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpb8Fm%2FbtsMEUeC1Wm%2FI7zeBV0MKb16oqkLtlO861%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;217&quot; height=&quot;189&quot; data-origin-width=&quot;217&quot; data-origin-height=&quot;189&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;합이 10인 사각형 찾기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사과 게임 이미지로부터 숫자를 해석하고 이제 처리할 준비는 되었는데, 어떤 알고리즘으로 사각형을 찾아줄 것인지 고민할 차례이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2차원 배열에서 합이 10이 되는 사각형을 찾는 알고리즘은, prefix sum(누적 합)으로 계산할 수 있다. 적절한 연습 문제로는 &lt;a href=&quot;https://www.acmicpc.net/problem/2167&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;BOJ 2167 - 2차원 배열의 합&lt;/a&gt;이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 모든 경우의 수를 탐색하면서 하나씩 전부 찾아도 되긴 하지만, 시간복잡도가 \(O(n^6)\) 이다. 물론 \(n \le 17\)&amp;nbsp; 이라서 허용할 수 있는 정도지만.. 일단 이런 naive한 방식의 구현은 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드는 사각형의 네 꼭짓점을 찾고, 네 꼭짓점 안에 존재하는 모든 숫자를 더하는 방법이다.&lt;/p&gt;
&lt;pre id=&quot;code_1741505973858&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function getSum(g, sx, sy, ex, ey) {
  let sum = 0;
  for (let i = sx; i &amp;lt;= ex; i++)
    for (let j = sy; j &amp;lt;= ey; j++)
      sum += g[i][j];
  return sum;
}

function getBoxes() {
  const g = getGrid();
  const boxes = [];
  // O(n^6)
  for (let sy = 0; sy &amp;lt; ROWS; sy++)
    for (let sx = 0; sx &amp;lt; COLS; sx++)
      for (let ey = sy; ey &amp;lt; ROWS; ey++)
        for (let ex = sx; ex &amp;lt; COLS; ex++)
          if (getSum(g, sx, sy, ex, ey) === 10)
            boxes.push({sx, sy, ex, ey});
  return boxes;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;약간 최적화&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 누적합 배열을 두어서, \(O(n^2)\) 시간에 미리 초기화를 해두고, 어떤 사각형을 만드는 네 꼭짓점이 주어지면 \(O(1)\) 시간에 사각형 내부의 합계를 알 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1741506227885&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// O(1)
function getPrefixSum(sum, sx, sy, ex, ey) {
  return sum[ey+1][ex+1] - sum[ey+1][sx] - sum[sy][ex+1] + sum[sy][sx];
}

function getBoxes() {
  const g = getGrid();
  const boxes = [];
  const sum = Array.from({length: ROWS + 1}, () =&amp;gt; Array(COLS + 1).fill(0));
  
  // O(n^2); init prefix sum array
  for (let y = 1; y &amp;lt;= ROWS; ++y)
    for (let x = 1; x &amp;lt;= COLS; ++x)
      sum[y][x] = g[y-1][x-1] + sum[y-1][x] + sum[y][x-1] - sum[y-1][x-1];
  
  // O(n^4)
  for (let sy = 0; sy &amp;lt; ROWS; sy++)
    for (let sx = 0; sx &amp;lt; COLS; sx++)
      for (let ey = sy; ey &amp;lt; ROWS; ey++)
        for (let ex = sx; ex &amp;lt; COLS; ex++)
          if (getPrefixSum(sum, sx, sy, ex, ey) === 10)
            boxes.push({sx, sy, ex, ey});
  return boxes;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별거 아닌 것 같지만, 가로 17, 세로 10인 이 게임의 경우에는 위 코드보다 170배 빠르게 계산하는 코드이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;468&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nLKFG/btsMFhm2jOk/k9hVIpxgo0DTzkQ1UtEyk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nLKFG/btsMFhm2jOk/k9hVIpxgo0DTzkQ1UtEyk1/img.png&quot; data-alt=&quot;잘 나온다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nLKFG/btsMFhm2jOk/k9hVIpxgo0DTzkQ1UtEyk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnLKFG%2FbtsMFhm2jOk%2Fk9hVIpxgo0DTzkQ1UtEyk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;568&quot; height=&quot;372&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;468&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;잘 나온다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전체 코드&lt;/h2&gt;
&lt;figure id=&quot;og_1741505203545&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;Fruit box game visualizer&quot; data-og-description=&quot;Fruit box game visualizer. GitHub Gist: instantly share code, notes, and snippets.&quot; data-og-host=&quot;gist.github.com&quot; data-og-source-url=&quot;https://gist.github.com/joonas-yoon/d88b3dd9e8d7dd19d6099ada90a4da48&quot; data-og-url=&quot;https://gist.github.com/joonas-yoon/d88b3dd9e8d7dd19d6099ada90a4da48&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bemguf/hyYmIAufrX/lJ4y4GdoDmG8WoaBFSbr5K/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640,https://scrap.kakaocdn.net/dn/bfF4tK/hyYm6VGfHa/hbJb4Cu0aTLk4sL5vLxrnK/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640&quot;&gt;&lt;a href=&quot;https://gist.github.com/joonas-yoon/d88b3dd9e8d7dd19d6099ada90a4da48&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://gist.github.com/joonas-yoon/d88b3dd9e8d7dd19d6099ada90a4da48&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bemguf/hyYmIAufrX/lJ4y4GdoDmG8WoaBFSbr5K/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640,https://scrap.kakaocdn.net/dn/bfF4tK/hyYm6VGfHa/hbJb4Cu0aTLk4sL5vLxrnK/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640');&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;Fruit box game visualizer&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Fruit box game visualizer. GitHub Gist: instantly share code, notes, and snippets.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;gist.github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/Javascript</category>
      <category>Algorithm</category>
      <category>JavaScript</category>
      <category>prefixsum</category>
      <category>사과게임</category>
      <author>joonas</author>
      <guid isPermaLink="true">https://joonas.tistory.com/273</guid>
      <comments>https://joonas.tistory.com/273#entry273comment</comments>
      <pubDate>Sun, 9 Mar 2025 12:10:26 +0900</pubDate>
    </item>
    <item>
      <title>git hook 설정할 때 scp connection 오류 해결법</title>
      <link>https://joonas.tistory.com/272</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;gerrit 을 사용한다면 git clone 후에 scp 로 훅을 아래와 같이 설정할 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1726127425262&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ scp -p -P 29418 username@gerrit.example.com:hooks/commit-msg .git/hooks&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 29418번 포트는 gerrit 포트 기본값이다.&lt;/p&gt;
&lt;figure id=&quot;og_1726127480439&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;Gerrit Code Review - Uploading Changes&quot; data-og-description=&quot;As Gerrit implements the entire SSH and Git server stack within its own process space, Gerrit maintains complete control over how the repository is updated, and what responses are sent to the git push client invoked by the end-user, or by repo upload. This&quot; data-og-host=&quot;gerrit-review.googlesource.com&quot; data-og-source-url=&quot;https://gerrit-review.googlesource.com/Documentation/user-upload.html&quot; data-og-url=&quot;https://gerrit-review.googlesource.com/Documentation/user-upload.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://gerrit-review.googlesource.com/Documentation/user-upload.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://gerrit-review.googlesource.com/Documentation/user-upload.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;Gerrit Code Review - Uploading Changes&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;As Gerrit implements the entire SSH and Git server stack within its own process space, Gerrit maintains complete control over how the repository is updated, and what responses are sent to the git push client invoked by the end-user, or by repo upload. This&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;gerrit-review.googlesource.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;pre id=&quot;code_1726127523722&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Cloning into 'MyRepo'...
remote: Total 5216 (delta 0), reused 5216 (delta 0)
Receiving objects: 100% (5216/5216), 453.29 MiB | 30.66 MiB/s, done.
Resolving deltas: 100% (1773/1773), done.
Updating files: 100% (271/271), done.
subsystem request failed on channel 0
scp: Connection closed&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전에 Stackoverflow 에서 해결 방법을 찾았는데, 출처는 기억이 안나지만 메모할 겸 글로 남긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SFTP 프로토콜이 아닌 SCP 프로토콜을 사용하도록, &lt;b&gt;대문자 O 옵션&lt;/b&gt;을 추가하면 해결된다.&lt;/p&gt;
&lt;figure id=&quot;og_1726127655002&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;scp(1) - Linux manual page&quot; data-og-description=&quot;&quot; data-og-host=&quot;man7.org&quot; data-og-source-url=&quot;https://man7.org/linux/man-pages/man1/scp.1.html&quot; data-og-url=&quot;https://man7.org/linux/man-pages/man1/scp.1.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://man7.org/linux/man-pages/man1/scp.1.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://man7.org/linux/man-pages/man1/scp.1.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;scp(1) - Linux manual page&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;man7.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;pre id=&quot;code_1726127700608&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ scp -O -p -P 29418 username@gerrit.example.com:hooks/commit-msg .git/hooks/
commit-msg                                          100% 1790   378.4KB/s   00:00&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발</category>
      <author>joonas</author>
      <guid isPermaLink="true">https://joonas.tistory.com/272</guid>
      <comments>https://joonas.tistory.com/272#entry272comment</comments>
      <pubDate>Thu, 12 Sep 2024 19:55:29 +0900</pubDate>
    </item>
    <item>
      <title>git push 할 때 TLS certificate verification 생략하기</title>
      <link>https://joonas.tistory.com/271</link>
      <description>&lt;pre id=&quot;code_1725416282669&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git push origin
warning: ----------------- SECURITY WARNING ----------------
warning: | TLS certificate verification has been disabled! |
warning: ---------------------------------------------------
warning: HTTPS connections may not be secure. See https://aka.ms/gcm/tlsverify for more information.
warning: ----------------- SECURITY WARNING ----------------
warning: | TLS certificate verification has been disabled! |
warning: ---------------------------------------------------
warning: HTTPS connections may not be secure. See https://aka.ms/gcm/tlsverify for more information.
warning: ----------------- SECURITY WARNING ----------------
warning: | TLS certificate verification has been disabled! |
warning: ---------------------------------------------------
warning: HTTPS connections may not be secure. See https://aka.ms/gcm/tlsverify for more information.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;git push 는 정상적으로 동작하는데, Security warning 검사때문에 push 가 굉장히 지연되는 경우가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Android Studio 로 git push 했을 때에는 위와 같은 절차가 없이 빠르게 push 되길래 커맨드를 확인해봤더니 아래와 같았다.&lt;/p&gt;
&lt;pre id=&quot;code_1725416384339&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git push --progress --porcelain origin --no-verify&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--no-verify 옵션을 붙여서 SSL 검사를 생략해버리면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 push 하려는 remote url 이 평소에 사용하던 곳이고 신뢰하는 곳일때, 또는 급하게 push 해야하는 경우에 사용하는 것이 좋을 듯하다.&lt;/p&gt;</description>
      <category>개발</category>
      <category>Git</category>
      <category>Push</category>
      <category>SSL</category>
      <category>tls certificate verification</category>
      <author>joonas</author>
      <guid isPermaLink="true">https://joonas.tistory.com/271</guid>
      <comments>https://joonas.tistory.com/271#entry271comment</comments>
      <pubDate>Wed, 4 Sep 2024 20:21:15 +0900</pubDate>
    </item>
    <item>
      <title>[Browser] pageX/screenX/clientX/offsetX 비교</title>
      <link>https://joonas.tistory.com/270</link>
      <description>&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;812&quot; data-origin-height=&quot;688&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MZVfm/btsJfpwaZF9/gll0U81NH7bbNUkAKf2x4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MZVfm/btsJfpwaZF9/gll0U81NH7bbNUkAKf2x4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MZVfm/btsJfpwaZF9/gll0U81NH7bbNUkAKf2x4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMZVfm%2FbtsJfpwaZF9%2Fgll0U81NH7bbNUkAKf2x4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;812&quot; height=&quot;688&quot; data-origin-width=&quot;812&quot; data-origin-height=&quot;688&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://stackoverflow.com/questions/6073505/what-is-the-difference-between-screenx-y-clientx-y-and-pagex-y&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://stackoverflow.com/questions/6073505/what-is-the-difference-between-screenx-y-clientx-y-and-pagex-y&lt;/a&gt;&lt;/p&gt;</description>
      <category>개발/Javascript</category>
      <author>joonas</author>
      <guid isPermaLink="true">https://joonas.tistory.com/270</guid>
      <comments>https://joonas.tistory.com/270#entry270comment</comments>
      <pubDate>Mon, 26 Aug 2024 23:59:54 +0900</pubDate>
    </item>
  </channel>
</rss>