<?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>Fri, 10 Apr 2026 20:48:30 +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>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>
    <item>
      <title>[Ubuntu] 디스크 용량이 남았는데 No space left 오류인 경우</title>
      <link>https://joonas.tistory.com/269</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;최근 회사에서 우분투로 프로젝트를 빌드해야하는 일이 생겼는데, 파일 시스템과 관련하여 학부생때 공부했던 이론이 문제 해결에 도움이 된 경험이 신기해서 글로 남겨본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이론과 실습, 특히나 다양한 프로그램을 주로 사용하는 우분투 환경에서는 OS, 컴퓨터 구조와 같은 수업에서 배우는 이론적인 부분은 실제 개발하는 중에 크게 마주할 일이 없을거라고 생각했다. 하지만 그런 일이 생겼다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;발단&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동료의 컴퓨터에서 빌드 도중 No space left on device 와 같은 이유로 빌드가 중단되었다. 하지만 디스크 용량을 확인해봤을때, 용량은 분명 40GB 정도 남아있었다. (아래는 당시 상황을 재연한 모습이다.)&lt;/p&gt;
&lt;pre id=&quot;code_1723297662102&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        49G   24G   25G  49% /
tmpfs            64M     0   64M   0% /dev
/dev/root        49G   24G   25G  49% /pid2
/dev/sda2       256G  6.0M   32G   1% /tmp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분명 디스크 용량이 남아있었기 때문에, touch a.txt 와 같은 명령어로 빈 파일을 생성하려고 했는데 그마저도 막혔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 문제의 원인은 하나밖에 없다는 생각이 머리를 스쳤다.&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;파일을 더 생성하지 못한다는거는, inode 개수를 모두 사용한 것이 아닐까?&quot; 라는 생각이었다.&lt;/p&gt;
&lt;pre id=&quot;code_1723298164148&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ df -ih
Filesystem    Inodes IUsed IFree IUse% Mounted on
/dev/sda1       6.2M  6.2M     0   99% /
tmpfs           7.9M    16  7.9M    1% /dev
/dev/root       6.2M  666K  5.6M   11% /pid2
/dev/sda2       800K   21K  780K    3% /io&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 시스템의 디스크 사용량을 확인하는 df 명령어에는 inode 를 확인하는 -i 옵션이 있다.&lt;br /&gt;위처럼 inode 를 모두 사용한 것을 확인하고는 학교에서 배우는 내용이 이렇게 쓰일 줄은 몰랐다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 우분투는 보통 ext4 파일 시스템을 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1723296903917&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ mount -l | grep ^/dev
/dev/root on / type ext4 (ro,relatime,discard,errors=remount-ro)
/dev/root on /nix type ext4 (ro,relatime,discard,errors=remount-ro)
/dev/nbd47 on /repl type btrfs (rw,noatime,nobarrier,ssd,space_cache=v2,subvolid=256,subvol=/working_subv)
/dev/root on /etc/localtime type ext4 (ro,relatime,discard,errors=remount-ro)
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ext3는 하나의 디렉토리에 최대 6만개 정도의 파일을, ext4는 최대 3만개의 파일을 가질 수 있는 것으로 기억한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번을 기회에 오랜만에 inode 구조에 대해서 다시 공부해보았다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;456&quot; data-origin-height=&quot;286&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mNHkT/btsI1QTeaf5/6d096YbV5Dd9di1L1hBMaK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mNHkT/btsI1QTeaf5/6d096YbV5Dd9di1L1hBMaK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mNHkT/btsI1QTeaf5/6d096YbV5Dd9di1L1hBMaK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/mNHkT/btsI1QTeaf5/6d096YbV5Dd9di1L1hBMaK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;456&quot; height=&quot;286&quot; data-origin-width=&quot;456&quot; data-origin-height=&quot;286&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;inode 구조체는 아래 링크에서 직접 확인해볼 수 있다.&lt;/p&gt;
&lt;figure id=&quot;og_1723299262368&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;fs.h - include/linux/fs.h -  Linux source code v6.5-rc1 - Bootlin&quot; data-og-description=&quot;/ include / linux / fs.h&quot; data-og-host=&quot;elixir.bootlin.com&quot; data-og-source-url=&quot;https://elixir.bootlin.com/linux/v6.5-rc1/source/include/linux/fs.h#L608&quot; data-og-url=&quot;https://elixir.bootlin.com/linux/v6.5-rc1/source/include/linux/fs.h#L608&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://elixir.bootlin.com/linux/v6.5-rc1/source/include/linux/fs.h#L608&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://elixir.bootlin.com/linux/v6.5-rc1/source/include/linux/fs.h#L608&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;fs.h - include/linux/fs.h - Linux source code v6.5-rc1 - Bootlin&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;/ include / linux / fs.h&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;elixir.bootlin.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림을 참고해보면 inode 는 실제로 디스크에 저장되는 block 을 포인터 배열로 들고 있고, 부족한 경우에는 이중/삼중 포인터로도 들고 있지만, ext4 파일 시스템의 경우 용량에 따라 사용 가능한 inode 의 개수가 이미 정해져있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 파일은 하나의 inode를 차지한다. 여기서 파일이란 디렉토리를 포함한다.&lt;br /&gt;여담으로, 디렉토리 역시 파일인데, vi 같은 편집기로 디렉토리를 열어보면 하위 파일의 경로가 적혀있는 텍스트 파일임을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 이유로, inode 를 확보하는 방법은 &quot;최대한 많은 파일을 제거하는 것&quot;이다.&lt;br /&gt;사용되고 있지 않는데 용량이 작은 파일을 최대한 많이 삭제하는 방법, 가장 대표적으로 캐시 파일을 지우는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 방법으로는 파일 시스템을 변경하는 법이다. ext4는 구조상 ext3 에 비해 1/2배의 inode만 사용할 수 있다.&lt;br /&gt;그렇다는것은, ext3 파일 시스템으로 디스크를 새로 포맷하면 2배의 inode 를 사용할 수 있다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 방법은, 포맷이 필요하기 때문에 파일을 전부 지워야하는 사태가 발생해서 실제로 적용해보기는 어려운 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FAT, NTFS 등의 파일 시스템은 inode 개수가 없는 것으로 알고 있는데, Ubuntu OS 와의 호환성이 좋지는 않은 것으로 알고 있어서 속도가 빠르지는 않을 것으로 예상된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발</category>
      <category>Ext</category>
      <category>inode</category>
      <category>nfts</category>
      <category>OS</category>
      <category>ubuntu</category>
      <category>파일시스템</category>
      <author>joonas</author>
      <guid isPermaLink="true">https://joonas.tistory.com/269</guid>
      <comments>https://joonas.tistory.com/269#entry269comment</comments>
      <pubDate>Sat, 10 Aug 2024 23:23:36 +0900</pubDate>
    </item>
  </channel>
</rss>