<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>프라이Develog(❁&amp;acute;◡`❁)</title>
    <link>https://friedegg556.tistory.com/</link>
    <description>틀린내용 정정 및 개선사항은 언제든지 댓글 달아주세요 :D</description>
    <language>ko</language>
    <pubDate>Wed, 15 Apr 2026 16:56:35 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>프라이D</managingEditor>
    <image>
      <title>프라이Develog(❁&amp;acute;◡`❁)</title>
      <url>https://tistory1.daumcdn.net/tistory/2789392/attach/89a134f7779942a5853a8ce6f61c9475</url>
      <link>https://friedegg556.tistory.com</link>
    </image>
    <item>
      <title>[Electron.js] electron 의 rebuild 란?</title>
      <link>https://friedegg556.tistory.com/387</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;electron 의 rebuid 라는 커맨드는, 현재 설치된 Electron 버전에 대해 네이티브 Node.js 모듈을 재 구축하기 위해 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일전에 사내 프로젝트에서 electron 과 serialport 라이브러리를 사용할 때, 이 rebuild 커맨드에 대해 알지 못해 상당히 해멨던 적이 있는데... 간단하게나마 정리해 보고자 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSb0SJ/btsE1DYfZJT/DWxlKK3AnHwRJHwqZUXQp0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSb0SJ/btsE1DYfZJT/DWxlKK3AnHwRJHwqZUXQp0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSb0SJ/btsE1DYfZJT/DWxlKK3AnHwRJHwqZUXQp0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSb0SJ%2FbtsE1DYfZJT%2FDWxlKK3AnHwRJHwqZUXQp0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;720&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 reubild 와 같은 작업이 필요하냐? rebuild - 그러니까 재컴파일 과정이 없으면 일렉트론 앱과 네이티브 Node.js 모듈, 그러니까 파일 시스템, 운영체제 등 저수준 기능에 접근하는 모듈간에 문제가 발생할 수 있다. 왜냐면, 일렉트론의 Node.js 는 표준 Node.js 환경과 다른 버전을 사용하기 때문이다... 알다시피 일렉트론은 크로미움 + Node.js 의 결합이기 때문에 표준 V8과 다른 버전의 노드를 내부적으로 사용하고 있다. 따라서 표준 노드 버전에 맞도록 컴파일된 모듈은 rebuild 를 거쳐 Electron 용으로 재 컴파일을 해야할 필요가 있다. &lt;a href=&quot;https://www.electronjs.org/docs/latest/tutorial/using-native-node-modules&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명에 따르면, Electron 은 표준 Node.js 환경과 다른 ABI (Application Binary Interface) 를 사용한다고 하는데... ABI 란... 응용 프로그램과 운영체제 사이 혹은 다른 프로그램 모듈 간의 인터페이스를 의미한다. 그러니까 ABI는 소프트웨어간 혹은 소프트웨어와 운영 체제 간 어떻게 통신을 할 것인지, 함수의 호출 방법이나 데이터 구조/해석 방법등을 정의한 저수준 인터페이스이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 프로젝트 개발 의존성에 새 모듈을 추가할 때 혹은 프로젝트를 처음 셋팅한 이후 electron-rebuild 명령어를 추가 실행해 주는 것이 맞고, 이를 사용하기 위해선 &lt;a href=&quot;https://www.npmjs.com/package/@electron/rebuild&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;@electron/rebuild&lt;/a&gt; 를 의존성에 추가해 커맨드를 실행하면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;electron-forge vs electron-builder&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 사내 프로젝트에선 환경에 맞게 빌드를 할 때마다 rebuild 커맨드를 실행해야 한다는 사실을 몰라서... 빌드 툴 선정시 serial port 가 정상 동작 되었던 electron-forge 를 택했다. electron forge 에서만 정상 동작했던 이유가, 개발모드 그리고 배포판 빌드를 할 때 rebuild 커맨드를 자동으로 실행했기 때문이고... 이 문제가 아니었다면 여러 상황을 고려했을 때 자료가 상대적으로 많은 electron-builder 를 사용했어도 됬겠다는 생각이 든다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1706&quot; data-origin-height=&quot;1036&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rPRWh/btsE3oT4FMc/rgBH1BRRJeilL0DgIMg7SK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rPRWh/btsE3oT4FMc/rgBH1BRRJeilL0DgIMg7SK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rPRWh/btsE3oT4FMc/rgBH1BRRJeilL0DgIMg7SK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrPRWh%2FbtsE3oT4FMc%2FrgBH1BRRJeilL0DgIMg7SK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1706&quot; height=&quot;1036&quot; data-origin-width=&quot;1706&quot; data-origin-height=&quot;1036&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 자료는 forge, builder 그리고 packager 세 가지 메인(?) 빌드 툴의 다운로드 횟수를 비교한 것인데, 최근엔 forge 도 많이 사용하고 있는 것으로 나타나긴 한다. 참고로 electron-forge 는 electron 이 메인테이너이고, electron-builder 는 electron-userland 라는 커뮤니티 기반으로 유지보수가 이루어 지고 있다. 또 일렉트론 공식 문서를 보면 빌드와 배포 설명에 대해 forge를 기반으로 하고 있기도 함...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;electron-forge 를 선택하고 s3 기반 auto update 를 하기 위해 많은 삽질을 했었는데... 예전에 자료 조사 했던 것에 따르면 electron-builder 관련 업데이트 자료도 많고 잘 되어 있어서... 상황에 맞는 툴을 선택해 사용하면 될 것 같다.&amp;nbsp;&lt;/p&gt;</description>
      <category>Stacks</category>
      <author>프라이D</author>
      <guid isPermaLink="true">https://friedegg556.tistory.com/387</guid>
      <comments>https://friedegg556.tistory.com/387#entry387comment</comments>
      <pubDate>Sun, 18 Feb 2024 22:51:47 +0900</pubDate>
    </item>
    <item>
      <title>[Node.js] Node의 require module 은 어떻게 동작하는가?</title>
      <link>https://friedegg556.tistory.com/386</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;CommonJS 모듈 시스템&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;하나의 자바스크립트 파일은 분리된 모듈로서 취급된다.&lt;/li&gt;
&lt;li&gt;그리고 nodejs는 CommonJS 시스템을 쓴다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;require()&lt;/code&gt; 그리고 &lt;code&gt;exports&lt;/code&gt; 혹은 &lt;code&gt;module.exports&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;프론트에서 쓰이는 &lt;code&gt;import/export&lt;/code&gt; 는 ES Module 시스템&lt;/li&gt;
&lt;li&gt;mjs 를 사용하면 Node 에서도 ES 모듈 시스템을 쓸 수 있다. 하지만 대중적인 pick 은 아닌 것 같음.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;모듈을 require 하면 무슨 일이 일어나느냐?&lt;/h3&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;const http = require('http');
const fs = require('fs');&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아래와 같은 단계를 거친다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Resolving &amp;amp; Loading&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모듈의 주어진 경로를 해석하고 로딩한다.&lt;/li&gt;
&lt;li&gt;모듈의 종류는 크게 세 가지가 있음.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;http 와 같은 Node 의 core 모듈&lt;/li&gt;
&lt;li&gt;개발자가 직접 쓴 모듈 파일&lt;/li&gt;
&lt;li&gt;외부 3rd-part 모듈 (npm에서 받은거)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;먼저 core 모듈을 찾고, 만약 / 로 시작한다면 해당 폴더를 찾는다.&lt;/li&gt;
&lt;li&gt;두 경우 모두 없다면 node_modules 경로에서 모듈을 찾으려고 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Wrapping&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모듈 코드를 특정 함수 스코프로 감싸서, 모듈 코드가 전역 스코프에서 직접 실행되는 것을 방지한다. + 네임스코프 오염 및 충돌 방지 등&amp;hellip;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(function(exports, require, module, __filename, __dirname) {
// 모듈의 코드
});&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;이 과정에서 exports 나 require 같은 함수를 사용할 수 있게 되는 것.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Execution&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그리고 감싸진 wrapper 함수가 실행된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Returning Exports&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;require 함수는 모듈을 exports 한다.&lt;/li&gt;
&lt;li&gt;모듈이 하나의 값이나 객체를 내보내는 경우 module.exports 를 사용해 단일 클래스, 함수등을 내보낼 수 있음.&lt;/li&gt;
&lt;li&gt;exports 의 경우 여러개의 함수, 객체를 모듈 속성으로 내보낼 때 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Caching&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;require 을 처음 실행한 경우 캐싱이 되기 때문에, 여러번 require 을 호출해도 처음 한 번만 실행되는 것과 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>Stacks</category>
      <author>프라이D</author>
      <guid isPermaLink="true">https://friedegg556.tistory.com/386</guid>
      <comments>https://friedegg556.tistory.com/386#entry386comment</comments>
      <pubDate>Sun, 4 Feb 2024 23:26:33 +0900</pubDate>
    </item>
    <item>
      <title>[Node.js] Streams 에 대해서 알아봐요.</title>
      <link>https://friedegg556.tistory.com/385</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Streams 란&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터를 작은 조각(chunk) 로 나누어서 처리하는 방식&lt;/li&gt;
&lt;li&gt;전체 데이터를 읽거나 쓰지 않고, 메모리에 저장도 하지 않는다.&lt;/li&gt;
&lt;li&gt;따라서 큰 데이터를 효율적으로 다룰 수 있고, 메모리 절약 및 더 빠른 I/O 작업 처리가 가능해진다.&lt;/li&gt;
&lt;li&gt;Streams 또한 Event Emitter 의 인스턴스&lt;/li&gt;
&lt;li&gt;따라서 Event 를 emit 하고 그에 대한 리스너 &amp;amp; 핸들러에 의해 이벤트 루프를 통해 핸들링된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4가지 종류의 Streams&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Readable&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;말 그대로 데이터를 읽을 수 있는 stream&lt;/li&gt;
&lt;li&gt;http request, fs read streams 가 있다.&lt;/li&gt;
&lt;li&gt;발생시키는 주요 이벤트로는 data, end&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Wrieable&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;말 그대로 데이터를 쓸 수 있는 stream&lt;/li&gt;
&lt;li&gt;http response, fs write streams&lt;/li&gt;
&lt;li&gt;drain, finish 와 같은 이벤트가 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Duplex&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Read &amp;amp; write 가 동시에 가능한 stream&lt;/li&gt;
&lt;li&gt;net web socket 모듈이 예시&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Transform&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Duplex &amp;amp;&amp;amp; 데이터 변환&lt;/li&gt;
&lt;li&gt;예시로는 zlip&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;간단한 코드 예시&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. fs.readFile 을 사용해 파일을 한 번에 읽어오기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1706449273182&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 전체 파일을 모두 읽어와야 하므로 느린 동작 &amp;amp; 메모리에 이 데이터를 저장해야함. (data 변수)
server.on('request', (req, res) =&amp;gt; {
  fs.readFile('test-file.txt', (err, data) =&amp;gt; {
    if (err) console.log(err);

    res.end(data);
  });
});&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버에 요청이 들어오면, fs 모듈을 통해 txt 파일을 읽어온다.&lt;/li&gt;
&lt;li&gt;이 작업은 요청에 의해서 비동기 적으로 실행되지만, 만약&amp;nbsp; 읽어오는 파일의 크기가 크다면, 이를 다 읽어올 때까지 기다렸다가 콜백이 실행되기 때문에, 파일 크기가 적을 때에나 적합한 작업이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. readable &amp;amp; writable stream 을 사용해보자&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1706449443973&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server.on('request', (req, res) =&amp;gt; {
    // redable stream 생성
    const readable = fs.createReadStream('test-file.txt');
    
    // 파일 읽기가 시작되면, 작은 데이터 조각 (chunk) 로 읽혀짐
    readable.on('data', (chunk) =&amp;gt; {
      // http response 응답 객체 (res) 에 write 를 호출해 chunk 를 클라이언트에 전달
      res.write(chunk);
    });
    
    // 파일 읽기가 완료되면, http 응답을 완료한다.
    readable.on('end', () =&amp;gt; {
      res.end();
    });
    
    readable.on('error', (err) =&amp;gt; {
      console.log(err);

      res.statusCode(500);
      res.end('File not found');
    });
});&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위 방식에서는 backpressure 현상이 발생할 수 있다.&lt;/li&gt;
&lt;li&gt;즉, 데이터 생성 속도 &amp;gt;&amp;gt; 데이터 소비 속도, 네트워크가 느린 경우 네트워크 버퍼가 금방 차 시스템 메모리를 차지하고 이로 인해 성능 저하가 발생할 수 있음.&lt;/li&gt;
&lt;li&gt;이럴 땐 데이터의 흐름을 관리해주는 방법이 필요함.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. pipe 메서드 사용하기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1706450208734&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server.on('request', (req, res) =&amp;gt; {
  const readable = fs.createReadStream('test-file.txt');
  readable.pipe(res);
})&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;pipe 메소드는 redable 스트립과 writable 스트림(res 응답객체) 을 자동으로 연결해준다.&lt;/li&gt;
&lt;li&gt;위 과정에서 redable 스트림의 읽기 속도를 제어해 데이터 흐름을 관리해줌.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아 뭔가 벌써 어려운데.. 일단 계속 해보겠음...&lt;/p&gt;</description>
      <category>Stacks</category>
      <author>프라이D</author>
      <guid isPermaLink="true">https://friedegg556.tistory.com/385</guid>
      <comments>https://friedegg556.tistory.com/385#entry385comment</comments>
      <pubDate>Sun, 28 Jan 2024 22:58:54 +0900</pubDate>
    </item>
    <item>
      <title>[TIL] 2024-0123 : this 바인딩</title>
      <link>https://friedegg556.tistory.com/384</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Findings&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 자바스크립트의 this 문제 : 취준때 공부했던 내용을 실무에서 마주하다니...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 일반적인 함수선언 방식에서 this 는 호출 시점에 결정.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 화살표 함수 방식에서 this 는 선언 시점에 결정.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 클래스의 메서드로 일반 함수를 정의해두고 외부에서 호출한다면 그 호출 시점에 결정이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 함수 문법을 사용한다면...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 객체의 메서드로 호출시, 해당 메서드를 호출한 객체를 가리킴&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 단독 함수로 호출될 시, 전역객체 혹은 undefined&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 생성자 함수로 사용이 된다면 새로 생긴 객체 인스턴스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘 내 코드의 경우엔, 다른 클래스에서 this를 사용해 메서드를 정의해두고 그 메서드를 또 다른 클래스에서 호출했기 때문에, 그 시점의 this 를 가리켜 의도치 않게 동작을 한 것이다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화살표 함수의 this 는 자기 자신의 this를 갖지 않고 상위 컨텍스트의 this 를 가리킨다고만 알고 있었는데, 아예 선언 시점에 this 값을 캡쳐해서 가지고 있기 때문에 호출 컨텍스트에 상관 없이 동일한 this를 가리키게 된다. 일관적으로...&amp;nbsp;&lt;/p&gt;</description>
      <category>TIL</category>
      <author>프라이D</author>
      <guid isPermaLink="true">https://friedegg556.tistory.com/384</guid>
      <comments>https://friedegg556.tistory.com/384#entry384comment</comments>
      <pubDate>Wed, 24 Jan 2024 09:38:58 +0900</pubDate>
    </item>
    <item>
      <title>[Electron.js] 일렉트론에 대해서 알아보자!</title>
      <link>https://friedegg556.tistory.com/383</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;최근 사내에서 진행하는 키오스크 프로젝트에서 일렉트론을 사용해 데스크탑 앱을 만들어보고 있다.&lt;/p&gt;
&lt;figure id=&quot;og_1705842678135&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;Introduction | Electron&quot; data-og-description=&quot;Welcome to the Electron documentation! If this is your first time developing an Electron app, read through this Getting Started section to get familiar with the basics. Otherwise, feel free to explore our guides and API documentation!&quot; data-og-host=&quot;www.electronjs.org&quot; data-og-source-url=&quot;https://electronjs.org/docs/latest&quot; data-og-url=&quot;https://electronjs.org/docs/latest/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://electronjs.org/docs/latest&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://electronjs.org/docs/latest&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;Introduction | Electron&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Welcome to the Electron documentation! If this is your first time developing an Electron app, read through this Getting Started section to get familiar with the basics. Otherwise, feel free to explore our guides and API documentation!&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.electronjs.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일렉트론이란 웹에서 사용하는 기술, HTML, CSS, JS 를 가지고 데스크탑 프로그램을 개발할 수 있는 프레임워크이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 프론트 기술을 활용할 수 있어서, 리액트 혹은 Next.js 와 함께 사용도 가능하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;도입 계기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계기는 내부적으로 조금 촉박하게 키오스크 프로그램을 개발해야하는 상황이었다. 처음에는 Next.js 를 사용해서 웹과 키오스크 펌웨어간 통신을 계획했는데, 만약 기기가 여러대가 생긴다면... 서버의 부담도 생기고 관리 방식도 엄청 복잡해질 것이란 우려가 나와 지속가능성이 떨어진다고 판단했다. 그래서 Electron에서 로컬 서버를 띄우고 private IP를 활용해 http 통신 방식으로 펌웨어 - 일렉트론 - 웹 키오스크 UI 이런 방식으로 변경하게 되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상황에서 만약 일렉트론으로 UI 와 펌웨어 통신을 모두 담당하게 된다면 두 개의 별도 프로젝트가 아닌 하나의 프로젝트, 하나의 프로그램으로 개발하는 것도 가능하다는 판단이 들어 내부적으로 계속 개발을 진행중이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 말했듯 웹 프론트엔드 개발자가 가장 접근하기 쉬운 기술인 HTML,CSS,JS 를 기반으로 하기 때문에 러닝커브가 적었고, 그래서 기존에 웹으로 띄우던 UI 를 긁어와 하나의 데스크탑 프로그램으로 통합하는 작업을 진행하게 되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;찾아보니, Vscode, Slack, Discord 등 자주 사용하는 서비스가 일렉트론을 기반으로 개발이 되었기도 하고, 이 분야에서 현재 일렉트론만큼 넓은 점유율을 보유한 기술도 없는 것 같아 선택하게 되었다. &lt;s&gt;그래서 개발을 하다가 막히면, '그래.. 슬랙과 디스코드도 일렉트론으로 개발했는데 나라고 못할거 없지...' 란 생각이 들면서 위안이 된다...&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;겪은 어려움&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 데스크탑 앱인 만큼, 브라우저에서 너무 당연하게 핸들링 해주던 많은 부분을 직접 핸들링 해야한다는 점이 어려웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 보안에 많은 신경을 써야해서 - 유저의 파일 시스템 등에 직접 접근이 가능하기 때문에 - 유저와 직접 맞닿는 UI 부분은 Renderer 프로세스가, 이외에는 Main 프로세스가 담당한다. 이 둘 간의 통신 방식을 이해하는 것이 중요했는데, 처음엔 좀 어려웠음. 그리고 사실 아직도 완벽하게 이해를 한 것은 아니라 많은 공부가 필요하다. &lt;s&gt;만약 일렉트론을 계속 사용할거라면...&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1143&quot; data-origin-height=&quot;940&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q1U0Q/btsDJYCYshS/c8zgyXd92VkVXiwBscm19k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q1U0Q/btsDJYCYshS/c8zgyXd92VkVXiwBscm19k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q1U0Q/btsDJYCYshS/c8zgyXd92VkVXiwBscm19k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq1U0Q%2FbtsDJYCYshS%2Fc8zgyXd92VkVXiwBscm19k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;493&quot; data-origin-width=&quot;1143&quot; data-origin-height=&quot;940&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; contextBridge를 사용해보아요~&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 종류의 프로세스로 구성된 일렉트론에서, 프로세스간 통신을 위해서 사용하는 것이 바로 IPC 모듈이다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1705844652062&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  appWindow = new BrowserWindow({
    width: 800,
    height: 600,
    backgroundColor: '#202020',
    show: false,
    autoHideMenuBar: true,
    frame: false,
    titleBarStyle: 'hidden',
    icon: path.resolve('assets/images/appIcon.ico'),
    webPreferences: {
      nodeIntegration: false,
      contextIsolation: true,
      nodeIntegrationInWorker: false,
      nodeIntegrationInSubFrames: false,
      preload: APP_WINDOW_PRELOAD_WEBPACK_ENTRY,
      sandbox: false,
    },
  });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 메인 프로세스에서 프로그램 윈도우 인스턴스를 생성하는 코드이다. webPreferences 속성에 중 특히 봐야하는게 nodeIntergration 그리고 contextIsolation 이다. 위 두 옵션은 프로세스간 통신에서 보안을 강화하기 위한 옵션인데, 현재 설정과 같이 되어있다면 렌더러 프로세스에서 Node 모듈 접근을 할 수 없고, 두 프로세스간 격리된 컨텍스트를 생성하게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 보안이 중요한 이유는, 만약 외부의 콘텐츠를 렌더링하는 상황이 발생한다면, 두 컨텍스트는 격리되어 있기 때문에 노드 모듈에 접근하지 못하게 방지하는 역할을 하기 때문이다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적으로 preload 경로도 있는데, preload 스크립트 파일의 위치를 의미하는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;preload 스크립트란 웹 콘텐츠를 렌더링 하기 전에, 렌더링 프로세스에서 실행되는 코드를 포함하는 스크립트 파일이다. 렌더러 프로세스 내에서 실행되지만, Node 모듈에 대한 접근 권한을 갖기 때문에, 만약 contextIsolation 옵션이 true 로 활성화 되어 있다면, 이 preload 스크립트에서 contextBridge 를 활용해 각 프로세스간 통신을 제어할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 코드를 보면서 다시 살펴보자...&lt;/p&gt;
&lt;pre id=&quot;code_1705845051081&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 메인 프로세스와 통신할 명령어
export const serialBridge = {
  motorDown: () =&amp;gt; {
    ipcRenderer.send('motor-down', '모터 다운이다옹...');
  },
};

// preload 파일 내에서 contextBridge를 통해 등록
contextBridge.exposeInMainWorld('serial', serialBridge);
  
// 실제 사용
window.serial.motorDown();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 통신을 할 때 ipc 모듈을 활용한다고 했는데, 렌더러 프로세스에서는 ipcRenderer 모듈을, 메인 프로세스에서는 ipcMain 모듈을 활용해 메시지를 주고받게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 렌더러 프로세스에 등록된 위 요청을 메인 프로세스에서 어떻게 처리하는지 보여주는 코드 조각이다.&lt;/p&gt;
&lt;pre id=&quot;code_1705845239815&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 메인 프로세스...
export function registerPortCommands() {
  const port = initSerialPort();

  ipcMain.on('motor-down', () =&amp;gt; {
    port.write(Buffer.from(KIOSK_CMD.motorDown), function (error) {
      if (error) {
        console.error(`Error on serial`, error.message);
      }
      `motor-down 입니다.`;
    });

    port.flush();
  });
 ...
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 serialport 라이브러리를 사용해 기기 펌웨어와 통신을 하기 때문에, UI 에서 명령 요청 - 메인 프로세스에서 - 시리얼 포트에 접근해 핸들링 하는 방식이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 외에도, 외부 api 로의 http 요청도 메인 프로세스에서 핸들링하도록 되어있다. contextBridge 를 쓰면 이에 대한 모든 명령을 preload 에 등록하고, 만약 타입스크립트를 사용한다면 이에 대한 타입도 선언을 해주어야 하는 귀찮음이 존재한다. 만약 복잡한 로직이라면 관리하기가 상당히 까다로울 것 같다는 생각도 들고... 어떤 구조로 가져가야 유지보수하기 편할지 고민도 된다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재는 s3를 사용한 update 기능까지 구현이 된 상태인데, 동작이 완벽하게 되는지는 지속적인 테스트가 필요하다. 사실 계속 일렉트론으로 키오스크 프로그램을 개발할지도 미지수이지만... 이렇게 시도해볼 수 있을 때 해보는 것도 경험이고 그게 스타트업의 장점이 아닌가 싶기도... 다음주부턴 기존 소스 개선 및 신규 개발건이 있어서 당분간 일렉트론을 많이 하진 않을 것 같다..&lt;/p&gt;</description>
      <category>Stacks</category>
      <author>프라이D</author>
      <guid isPermaLink="true">https://friedegg556.tistory.com/383</guid>
      <comments>https://friedegg556.tistory.com/383#entry383comment</comments>
      <pubDate>Sun, 21 Jan 2024 22:59:01 +0900</pubDate>
    </item>
    <item>
      <title>[Node.js] Node란 무엇이냐..이벤트 루프란..?</title>
      <link>https://friedegg556.tistory.com/382</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;최근에 Nest 를 공부하기 시작했는데, 뭔가 더 기본적인 개념을 알고 싶어서 먼저 Node.js 에 대해 정리를 해보려고 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Node.js 란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립 런타임 환경, 자바스크립트로 서버 환경을 개발할 수 있는 런타임이다. 비동기 이벤트 기반의 런타임이라고 하는데... 그래서 이벤트 루프가 주요한 개념으로 포함된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노드의 구성 요소로는 크게 V8 엔진과 libuv 라이브러리가 있다. V8 엔진은 구글 크롬의 브라우저 엔진과 동일한 것이고, 인터프리터를 기반으로 자바스크립트 코드를 기계어로 변환하는 역할을 함. 그리고 libuv 라이브러리는 비동기 I/O를 담당한다. 이벤트 루프 및 비동기식 태스크 처리, 파일시스템 접근 및 네트워크 등... 서버사이드 개발을 위해 필요한 동작을 libuv 라이브러리를 통해 지원받는다. 이 외에 http-parser, c-ares, OpenSSL, zlib 등의 라이브러리 등으로 구성된다고 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;V8 엔진은 자바스크립트 + C++ 그리고 libuv 라이브러리는 C++ 로 작성되었다. 따라서 노드는 C++ 그리고 자바스크립트 기반으로 구성되었지만 우리가 자바스크립트 코드로 노드 서버를 프로그래밍 할 수 있도록 레이어 역할을 한다고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;630&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvy8Sy/btsDrgYalbU/dHlNAMoZiaXsRcyuHAK7f1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvy8Sy/btsDrgYalbU/dHlNAMoZiaXsRcyuHAK7f1/img.png&quot; data-alt=&quot;출처 :&amp;amp;nbsp;https://www.simform.com/blog/build-real-time-apps-node-js/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvy8Sy/btsDrgYalbU/dHlNAMoZiaXsRcyuHAK7f1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcvy8Sy%2FbtsDrgYalbU%2FdHlNAMoZiaXsRcyuHAK7f1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;630&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;630&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 :&amp;nbsp;https://www.simform.com/blog/build-real-time-apps-node-js/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;프로세스 &amp;amp; 쓰레드 그리고 쓰레드 풀&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 용어 정리... 프로세스는 실행중인 프로그램 그 자체(인스턴스) 를 의미하고, 쓰레드란 프로세스 내에서 실행되는 실행의 흐름..&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 자바스크립트는 싱글 스레드 언어로 익히 알려져 있다. 싱글 스레드 기반에서 어떻게 동시성을 관리하느냐 하면, 브라우저와 마찬가지로 이벤트 루프를 기반으로 동작한다. 이벤트 루프가 동작하기 전, Node 의 싱글 스레드에선...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램 초기화 -&amp;gt; 최상단 코드 실행 -&amp;gt; 모듈 가져오기 -&amp;gt; 이벤트 콜백 등록 과 같은 과정을 거친 뒤 이벤트 루프를 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 싱글스레드 내의 이벤트 루프에서 실행되기에 너무 무거운 작업이라면 - 예를 들어 블록킹을 유발하는 I/O 작업이라던지... 그럴 땐 Node.js 가 libuv 라이브러리를 통해 제공하는 쓰레드 풀에 해당 작업의 처리를 맡길 수 있다. 쓰레드 풀은 기본 4개의 쓰레드를 가지고, 최대 128개 까지의 쓰레드로 쪼갤 수도 있다고 함. 그리고 이 쓰레드들은 메인 프로세스의 싱글 스레드와 구별된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싱글 스레드에서 쓰레드 풀로의 위임은, 개발자가 직접 핸들링 한다기 보단 Node.js 내부에서 이루어지며, 쓰레드 풀을 활용하는 API 를 사용하면 된다. 예를 들면 fs 모듈... 파일시스템, 네트워킹과 같은 I/O 작업이나 CPU 집약적인 작업을 할 때 내부적으로 이런 방식으로 동작한다고 이해하면 될 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;이벤트 루프&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 잠깐 노드는 비동기 이벤트 기반의 런타임이다 - 라는 문장을 썼는데, 그렇게 동작할 수 있도록 하는 핵심은 바로 이벤트 루프이다. 이벤트 기반 아키텍쳐에서는 서버가 발생하는 모든 사건을 이벤트로 간주하고 이에 대응을 하기 위해 콜백 함수를 실행한다. 그 사건이란 예를들면 HTTP 요청의 도착, 파일 읽기의 완료 등... 그러면 노드는 해당 이벤트에 대해 미리 등록된 콜백을 비동기적으로 처리하고, 이런 비동기 처리 덕분에 동시에 발생하는 여러 요청을 효과적으로 처리할 수 있다. 그리고 이런 효율적인 처리 덕분에 당근 서버의 성능과 자원을 최적화 할 수 있는 아키텍쳐를 가지고 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;암튼 그래서 이벤트 루프는 이벤트에 대응하는 콜백 함수를 실행시키는 역할을 담당하는데, 여기에도 각 단계? 순서가 있다... 그리고 각 phase 는 해당하는 별도의 콜백 큐를 가지고 있다. 각각의 콜백 큐에 이벤트에 해당하는 콜백을 쌓아놓고 실행하는 것이다...! 가장 주요한 phase 로는 크게 아래와 같다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Timers : setTimeout 이나 setInterval 에 의해서 지정된 콜백을 실행하는 단계&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. I/O Callbacks : 대부분의 비동기 I/O 작업의 콜백을 이 단계에서 처리한다. (파일시스템, 네트워크 작업 등..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. I/O Polling : 새로운 I/O 이벤트를 폴링하고 이와 관련된 콜백을 실행한다. 그러니까 새로운 단계의 I/O 작업을 대기하거나 감지하는 역할을 한다고 함... 뭔가 둘의 차이가 뭔지 아직 잘 모르겠지만 일단 넘어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. Check : SetImmediate&amp;nbsp; 함수에 대한 콜백이 이 단계에서 실행된다. Node.js 에서 제공되는 비동기 함수인데, 이벤트 루프의 한 사이클이 완료 되면 그 완료된 직후에 실행할 콜백 함수를 예약하는 역할을 한다고 함.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. Close Callbacks : 일부 close 이벤트가 이 단계에서 실행!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 각 phase 의 콜백 큐 이외에도 process.nextTick 큐와 resolved 된 프로미스들을 위한 마이크로 태스크 큐가 존재하는데, 만약 이 두 큐에 작업이 하나라도 있다면, 각 phase 의 실행 직후에 실행된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 루프에서 타이머를 기다리거나, I/O 를 기다리는 일이 없다면, 프로그램을 종료한다. I/O 를 기다린다는 것은 예를들면 HTTP 요청을 기다린다는 의미...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;자바 vs Node&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 알고 있는 백엔드 기술 두 가지... 사실 우리 회사 백엔드에서 대부분의 프로젝트를 Nest 로 작성했다가 최근들어 마이그레이션 까진 아니지만 암튼 자바 스프링을 사용한 프로젝트들이 좀 있어서 과연 두 기술간 차이는 뭘까 궁금해졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간략히 설명하면 자바는 멀티스레드 환경에서 동기적으로 작업을 처리하고, 객체 지향 설계 원칙에 따라서 대규모 서비스를 안정적으로 구축하기에 적합한 언어라고 할 수 있겠다. 하지만 멀티 스레드이기 때문에 더 많은 자원을 소모한다는...&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 노드는, 앞서 설명한 바와 같이 싱글스레드 + 이벤트 기반의 설계로 동시성 문제를 해결하고 있어 조금 더 가볍다는 장점이 있다. 그리고 프론트엔드 개발자로서는 자바스크립트를 기반으로 하기 때문에 언어에 대한 러닝커브가 낮다는 점, 풀스택으로 개발하기엔 이만한 기술이 없다는 점...? 다만 싱글 스레드의 한계로 아주 무겁고 대규모 서비스를 만들기에는 부적합한 면도 있을 것이라 생각된다...!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 잘 해보자..~&lt;/p&gt;</description>
      <category>Stacks</category>
      <author>프라이D</author>
      <guid isPermaLink="true">https://friedegg556.tistory.com/382</guid>
      <comments>https://friedegg556.tistory.com/382#entry382comment</comments>
      <pubDate>Sun, 14 Jan 2024 17:20:34 +0900</pubDate>
    </item>
    <item>
      <title>[TIL] 2024-0110</title>
      <link>https://friedegg556.tistory.com/381</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Facts&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 어제에 이어 여전히 electron 을 파헤치는 중...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Feelings&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 어쩌다보니 키오스크 개발을 웹 프론트에서 핸들링 하게 되었다. (사실 내가 할 수 있다고! 좋다고 했음..~)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 처음엔 Next.js 로 빌드해서 웹 서버로 띄웠었는데, 기기 펌웨어와 요러쿵 죠러쿵 통신을 해야하는 부분이 있어서 일렉트론으로 로컬 서버를 띄운 뒤에 private Ip 로 post 요청을 보내는 방식으로 했다가...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 동료 개발자분께서 여러 날의 디깅을 해주신 끝에 자동 업데이트 기능이 완성되어서, 본격적으로 일렉트론으로 코드를 옮겨 수정하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 일단 전에 하던 것에 비해 새롭게 배우고 부딪혀야 할 것이 많긴 한데, 그게 꽤 재밌다. 웹 환경이 아닌 네이티브 환경이다보니 IPC 라던지, 보안으로 인해 고안된 (나에겐) 새로운 방식 이라던지 배울 것이 많다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 그리고 삽질할 때도 많은데 어제 오후부터 어제 저녁, 밤, 오늘 점심 이후까지 거의 반나절 이상을 엄청 간단한 문제 때문에 디깅하고 헤맸는데... 또 그 과정에서 일렉트론과 쬐끔 더 친해질 수 있었다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 하다가.. 과연 이게 될까..? 라는 생각이 들면, 이 프레임웤으로 슬랙도 만들고 디스코드도 만들었다는 것을 생각하면... 이 정도 새발의 피는 당연히 가능하지... 라고도 생각할 수 있다...&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Findings&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- electron 에서는 카메라를 쓰려면 이것도 뭔가 ipc 모듈을 활용해 이루어져야 한다고 생각했는데, 그게 아니고 그냥 기기에서 카메라 권한을 주면 된다. 문제는 앱을 깔았을 때 유저에게 권한을 켤 수 있도록 노티를 주던지 해야하는데... 이건 좀이따 더 찾아봐야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- electron 에서 env 환경변수 사용방법...! 일단 renderer 프로세스에서 직접 접근은 안되는 것 같다.. 그러면 메인 프로세스쪽 node(...? 이게 맞는 표현인지는 잘 모르겠습니다...) 에 접근할 수 있도록 BrowserWindow 생성시에 webPreferences 옵션에서 nodeIntegration 을 true 로 바꿔주고, contextIsolation 을 false로 바꿔주는 방법도 있는데, 이걸 막아둔 이유는 역시 보안상의 이유이므로 다른 우회법을 찾아서 핸들링 하도록 해야겠다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 근데 electron 에서 http 요청을 보내는 방법을 찾다보니, 어차피 이런 요청을 메인 프로세스에서 핸들링 하도록 되어 있으므로... 서버 url 을 환경변수로 관리한다면 어차피 메인 프로세스에서는 process 에 접근이 가능하니 굳이 위 방법으로 하지 않아도 될 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Feedback&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;오늘의 참고자료&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=CUbysreXVqw&amp;amp;t=374s&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=CUbysreXVqw&amp;amp;t=374s&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>TIL</category>
      <author>프라이D</author>
      <guid isPermaLink="true">https://friedegg556.tistory.com/381</guid>
      <comments>https://friedegg556.tistory.com/381#entry381comment</comments>
      <pubDate>Thu, 11 Jan 2024 09:19:41 +0900</pubDate>
    </item>
    <item>
      <title>[TIL] 2024-0109 electron IPC 모듈로 통신하기</title>
      <link>https://friedegg556.tistory.com/380</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Facts&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Next.js 로 웹서버에서 띄워주던 UI 를 Electron 내의 renderer 로 옮기는 통합 작업 진행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Findings&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Electron 에서 로컬 경로의 이미지를 로드하려면, custom protocol 을 설정해주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 일렉트론이 chronium 과 node를 기반으로 하는데 이 두 환경 사이의 상호작용에서 보안 이슈가 발생할 가능성이 있기 때문에 그렇다고 한다. (어떻게 동작하길래 보안 이슈가 발생할 수 있는거지..?)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- electron 에서는 main 프로세스와 UI를 띄워주는(브라우저 같은..) renderer 프로세스를 갖는다. 이 두 프로세스 간 통신을 위해서는 IPC 모듈을 사용해야 하는데, 렌더러 프로세스에서 사용할 수 있는 IpcRenderer 와, 메인 프로세스의 IpcMain이 존재한다. 처음에 그냥 아무 렌더러 쪽 코드에서 IpcRender 를 호출했더니, fs 모듈을 읽어올 수 없다는 에러가 발생했다. 그래서 찾아보니, preload 스크립트 파일을 사용해 renderer 실행 전 명령어를 window 에 등록해주고 이를 활용해야 한다. 이 때 preload 에서 contextBridge 를 사용하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1704794121260&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// preload.ts
const registerIPC = () =&amp;gt; {
  contextBridge.exposeInMainWorld('serial', {
    register: () =&amp;gt; {
      ipcRenderer.send('register', '레지스터 해보겠다옹~');
    },
  });
};

// main.ts 
// 아래 함수를 윈도우가 로드되기 이전 시점에 호출해서 등록...
function registerMainIPC(port: SerialPort) {
  ipcMain.on('register', () =&amp;gt; {
    port.write(Buffer.from(LALALOOP_DISH_CMD.motorDown), function (error) {
      if (error) {
        console.error(`Error on serial`, error.message);
      }
      `레지스터다옹~.`;
    });

    port.flush();
  });
}

// renderer 내에서...
onRegister = async () =&amp;gt; {
window.serial.onRegister();
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 카메라(디바이스)는 어떻게 연동해야 하는가...? 그건 내일 다시 알아보자...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Feedback&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 회사에서는 감정이 드러나지 않게 잘 조절합시다... 이미 잘 하고 있다고 생각하지만...&lt;/p&gt;</description>
      <category>TIL</category>
      <author>프라이D</author>
      <guid isPermaLink="true">https://friedegg556.tistory.com/380</guid>
      <comments>https://friedegg556.tistory.com/380#entry380comment</comments>
      <pubDate>Tue, 9 Jan 2024 20:30:17 +0900</pubDate>
    </item>
    <item>
      <title>file, blob 객체의 차이점</title>
      <link>https://friedegg556.tistory.com/379</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;최근 사내 프로젝트를 진행하던 도중, ai 인식을 위해 키오스크 기기에서 촬영한 이미지 파일을 서버로 보내야 하는 상황이 있었다. 이미지 파일을 blob 형식으로 보냈는데, 서버에서 정상 인식이 되지 않는 상황이 발생해 디깅을 하다, File 형식으로 보내야 한다는 것을 깨닫고...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 형식의 차이점이 무엇인지 궁금해졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론부터 말하자면, File 객체는 Blob 의 확장이다. 만약 input 을 통해 file 을 업로드 하면, 이 데이터의 형식이 바로 File 형식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트를 위해 임시로 input 을 통해 file 을 받아 업로드 했을 때 정상 동작했던게 바로 이런 차이가 있기 때문이다...&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 Blob 객체란 무엇이냐?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Blob 은 Binary Large Object 의 약자로, 이진 데이터의 원시 형식을 의미한다. 큰 파일 데이터를 이진 데이터로 효율적으로 처리하기 위해 사용하는 데이터 타입이다. Blob 객체는 이미지 뿐만 아니라 텍스트, 사운드, 비디오 같은 다양한 형식의 데이터를 나타낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 File 형식 데이터가 Blob의 확장이라고 했는데, 그럼 어떤 차이점이 있을까? File 객체는 메타 데이터를 포함한다. 메타 데이터에는 파일명, 수정 시간 등이 포함된다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어쨌든 이미지를 주고받기 위해서 Blob 이든 File 이든 어떤 형식이든 상관 없었겠지만 서버에서 어떤 타입의 데이터를 필요로 하냐에 달렸기 때문에 그에 맞는 형식을 주어야 한다..&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로, 나는 canvas 를 통해 video 태그의 이미지를 추출해 이 데이터를 Blob 으로 변환하고 다시 File 형식으로 변환해 서버로 전송하는 아래와 같은 로직을 작성할 수 있었다... 참고로 canvas 로 추출한 데이터는 ArrayBuffer 형식으로 들어온다.&lt;/p&gt;
&lt;pre id=&quot;code_1704639887021&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  const captureAsFile = async (): Promise&amp;lt;File | null&amp;gt; =&amp;gt; {
    const canvas = canvasRef.current;
    const context = canvas?.getContext('2d');

    if (!canvas || !context || !videoRef.current) return null;

    context.drawImage(videoRef.current, 0, 0, canvas.width, canvas.height);

    const blob = await createBlobFromCanvas(canvas);

    if (blob) {
      const file = new File([blob], 'captured_image.png', {
        type: 'image/png',
      });
      return file;
    } else {
      return null;
    }
  };

  const createBlobFromCanvas = async (
    canvas: HTMLCanvasElement,
  ): Promise&amp;lt;Blob | null&amp;gt; =&amp;gt; {
    return new Promise((resolve) =&amp;gt; {
      canvas.toBlob(resolve, 'image/png');
    });
  };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 로직을 통해 얻은 파일 데이터는 formData 형식으로 서버에 전송할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1704639965729&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const imageFileToFormData = (image: File) =&amp;gt; {
  const formData = new FormData();
  formData.append('file', image, '테스트용 이미지');

  return formData;
};&lt;/code&gt;&lt;/pre&gt;</description>
      <category>프로젝트/work</category>
      <author>프라이D</author>
      <guid isPermaLink="true">https://friedegg556.tistory.com/379</guid>
      <comments>https://friedegg556.tistory.com/379#entry379comment</comments>
      <pubDate>Mon, 8 Jan 2024 00:06:11 +0900</pubDate>
    </item>
    <item>
      <title>151. Reverse Words in a String</title>
      <link>https://friedegg556.tistory.com/378</link>
      <description>&lt;figure id=&quot;og_1704289936079&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;Reverse Words in a String - LeetCode&quot; data-og-description=&quot;Can you solve this real interview question? Reverse Words in a String - Given an input string s, reverse the order of the words. A word is defined as a sequence of non-space characters. The words in s will be separated by at least one space. Return a strin&quot; data-og-host=&quot;leetcode.com&quot; data-og-source-url=&quot;https://leetcode.com/problems/reverse-words-in-a-string/?envType=study-plan-v2&amp;amp;envId=leetcode-75&quot; data-og-url=&quot;https://leetcode.com/problems/reverse-words-in-a-string/description&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bFbHSR/hyUXUcA4o8/fgVInRfq89zOg6aERAY3O1/img.png?width=500&amp;amp;height=260&amp;amp;face=0_0_500_260&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/reverse-words-in-a-string/?envType=study-plan-v2&amp;amp;envId=leetcode-75&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://leetcode.com/problems/reverse-words-in-a-string/?envType=study-plan-v2&amp;amp;envId=leetcode-75&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bFbHSR/hyUXUcA4o8/fgVInRfq89zOg6aERAY3O1/img.png?width=500&amp;amp;height=260&amp;amp;face=0_0_500_260');&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;Reverse Words in a String - LeetCode&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Can you solve this real interview question? Reverse Words in a String - Given an input string s, reverse the order of the words. A word is defined as a sequence of non-space characters. The words in s will be separated by at least one space. Return a strin&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;leetcode.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;내 풀이&lt;/h3&gt;
&lt;pre id=&quot;code_1704289923271&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/*
 [문제]
 - 문자열 s가 주어졌을 때, 단어들의 순서를 뒤집어라
 - 단어들은 공백을 기준으로 나뉘고, 공백을 기점으로 뒤집어야함
 - s가 주어질 때 단어 사이에 여러개의 공백이 있을 수 있는데, 
 - 앞,뒤 공백도 다 없애고 단어는 무조건 하나의 공백으로만 구분지어야 함
  */
var reverseWords = function reverseWords(s) {
  // 단어를 돌면서 공백이 나오기 직전까지 단어를 임시 저장해두었다가
  // 공백이 나오는 시점에 해당 문자열을 배열에 push
  // 그런담에 순서를 뒤집어서 join 하면 될 것 같음

  const words = [];
  let temp = '';

  for (let i = 0; i &amp;lt; s.length; i++) {
    const curr = s[i];
    const isCurrSpace = curr === ' ';

    if (!isCurrSpace) {
      temp += s[i];
    }

    if ((isCurrSpace || i === s.length - 1) &amp;amp;&amp;amp; temp) {
      words.push(temp);
      temp = '';
    }
  }

  return words.reverse().join(' ');
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;더 나은 풀이&lt;/h3&gt;
&lt;pre id=&quot;code_1704289870289&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var reverseWords = function(s) {
    let words = s.trim().split(/\s+/).filter(word =&amp;gt; word !== ''); 
    const reverseword = words.reverse();
    return reverseword.join(' ');
};&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm &amp;amp; 자료구조/알고리즘 w.JavaScript</category>
      <author>프라이D</author>
      <guid isPermaLink="true">https://friedegg556.tistory.com/378</guid>
      <comments>https://friedegg556.tistory.com/378#entry378comment</comments>
      <pubDate>Wed, 3 Jan 2024 22:53:39 +0900</pubDate>
    </item>
  </channel>
</rss>