<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Duknock의 개발새발</title>
    <link>https://duknock.tistory.com/</link>
    <description>글 막 쓰는 개초보 개발자</description>
    <language>ko</language>
    <pubDate>Sat, 15 Aug 2026 05:16:30 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>duknock</managingEditor>
    <image>
      <title>Duknock의 개발새발</title>
      <url>https://tistory1.daumcdn.net/tistory/5329148/attach/a36fd48027ac43909dcd619e7bde18de</url>
      <link>https://duknock.tistory.com</link>
    </image>
    <item>
      <title>[자바스크립트] 이벤트 루프와 비동기 작업 (그리고 단어 설명)</title>
      <link>https://duknock.tistory.com/32</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;자바스크립트는 싱글스레드 환경에서 실행된다.&lt;br&gt;&amp;nbsp;&lt;br&gt;즉, 하나의 호출 스택을 통해 함수 호출을 처리하기 때문에, 한번에 하나의 작업만 처리할 수 있다.&lt;br&gt;&amp;nbsp;&lt;br&gt;하지만 우리는 이미 비동기 함수라는 것을 많이 사용하고 있다.(setTimeout, ajax(fetch), 웹 워커 등)&lt;br&gt;&amp;nbsp;&lt;br&gt;비동기 함수를 사용하면 여러개의 작업을 순차적으로 기다리지 않고 한번에 병렬적으로 처리 가능하다.&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;엥? 한번에 하나의 작업만 처리 가능하다면서 어떻게 비동기 작업을 처리함?&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;왜냐하면 자바스크립트에서는 비동기적인 작업은 메인스레드가 아닌 백그라운드 스레드로 보내서 처리시키기 때문이다.&lt;br&gt;(다른 놈[브라우저]한테 짬때리는 거다)&lt;br&gt;&amp;nbsp;&lt;br&gt;  이 때 주의할 점 하나는, 비동기 함수 자체를 백그라운드 스레드로 보내는 것이 아니다!&lt;br&gt;백그라운드 스레드로 보내는 것은 &lt;span style=&quot;background-color: #DDDDDD;&quot;&gt;비동기 작업&lt;/span&gt;이다.&lt;br&gt;setTimeout()과 같은 함수 자체는 동기적으로 처리된다.&lt;br&gt;&amp;nbsp;&lt;br&gt;예시) &lt;span style=&quot;background-color: #99CEFA;&quot;&gt;파란색은 동기적&lt;/span&gt;, &lt;span style=&quot;background-color: #FFC1C8;&quot;&gt;빨간색은 비동기적&lt;/span&gt; 업무 라고 보면 된다&lt;br&gt;&amp;nbsp;&lt;br&gt;상사가 &lt;span style=&quot;background-color: #99CEFA;&quot;&gt;업무메일을 받고&lt;/span&gt; &lt;span style=&quot;background-color: #FFC1C8;&quot;&gt;메일에 쓰여져 있는 업무&lt;/span&gt;를 후임한테 짬때린다.&lt;br&gt;상사는 &lt;span style=&quot;background-color: #FFC1C8;&quot;&gt;메일에 쓰여진 업무&lt;/span&gt;를 후임한테 시키는 것이지, &lt;span style=&quot;background-color: #99CEFA;&quot;&gt;메일을 받는 행위 자체&lt;/span&gt;를 후임한테 시키는 것이 아니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;그리고 이런 비동기 작업을 관리하는 것이 &lt;span style=&quot;background-color: #DDDDDD;&quot;&gt;이벤트 루프&lt;/span&gt;인데,&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;background-color: #DDDDDD;&quot;&gt;이벤트 루프&lt;/span&gt;란&amp;nbsp;호출스택, web API(백그라운드 스레드), 태스크 큐 등의 요소들을 모니터링하며&lt;span style=&quot;color: #0D0D0D;&quot;&gt; 비동기 작업을 관리하고 실행하는 메커니즘이다.&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;잠시만, 왜 갑자기 '큐', '스택'이니 '스레드'니 하면서 어려운 단어 씀?&lt;br&gt;난 컴공과 전공이 아니라서 그런 단어 몰라요&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;큐(Queue)&lt;/b&gt;&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;큐는&amp;nbsp;&lt;span style=&quot;color: #0D0D0D;&quot;&gt;선입선출(FIFO, First In, First Out) 원칙에 따라 동작하는 데이터 구조이다.&lt;/span&gt;&lt;span style=&quot;color: #0D0D0D;&quot;&gt;(물리적인 메모리가 아니다. 개념이다.)&lt;/span&gt;&lt;span style=&quot;color: #0D0D0D;&quot;&gt; &lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #0D0D0D;&quot;&gt;이는 먼저 추가된 요소가 먼저 제거되는 구조를 가지고 있다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #0D0D0D;&quot;&gt;편의점에서 냉장고에 음료수를 채울 때, 워크인 냉장고로부터 뒤에서부터 채우면 먼저 채워놓은것들이 앞으로 가는 원리와 같다.&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;스택(Stack)&lt;/b&gt;&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;스택은 &lt;span style=&quot;color: #0D0D0D;&quot;&gt;후입선출(LIFO, Last In, First Out) 원칙에 따라 동작하는 데이터 구조이다.(얘도 개념이다.)&lt;/span&gt;&lt;br&gt;예시를 들자면 상자에 물건을 쌓는 모습을 생각하면 된다.&lt;br&gt;마지막에 넣은 물건이 맨 위에 위치하고, 물건을 빼낼 때는 맨 위에서 부터 빼낸다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;스레드(Thread)&lt;/b&gt;&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;스레드는 &lt;span style=&quot;color: #0D0D0D;&quot;&gt;컴퓨터 프로그램이 동시에 여러 작업을 수행할 수 있도록 하는 실행 단위이다.&lt;/span&gt;&lt;br&gt;일반적으로 각 스레드는 하나의 호출 스택을 가지고 있다.&lt;br&gt;&lt;span style=&quot;color: #0D0D0D;&quot;&gt;호출 스택은 함수 호출의 순서를 기록하는 데이터 구조로, 현재 실행 중인 함수의 정보를 저장한다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #0D0D0D;&quot;&gt;스레드는 이 호출 스택을 사용하여 코드를 실행하고, 함수 호출이 발생할 때마다 호출 스택에 새로운 프레임을 추가하고, 함수가 완료되면 해당 프레임을 제거한다.&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style2&quot;&gt;우리는 지금부터 이벤트루프의 작동 과정을 로우레벨로 분석할 것이다.&lt;br&gt;솔직히 단순 코딩할 때는 몰라도 되는 부분이지만,&lt;br&gt;작동 원리를 알고 있으면 개발할 때 분명히 도움이 된다.&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;예시를 들어보자&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;setTimeout(()=&amp;gt;{
	console.log('hello');
}, 1000)
console.log('bye')&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;위 코드가 실행되면 어떻게 될까?&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;// 콘솔 출력 &quot;bye&quot;
// 1초 후 콘솔 출력 &quot;hello&quot;&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;뭐 당연히 이렇게 콘솔에 찍히겠지&lt;br&gt;&amp;nbsp;&lt;br&gt;근데 이 과정에서 브라우저 엔진 내에서는 어떻게 처리가 되는지 알아보면&lt;br&gt;&amp;nbsp;&lt;br&gt;1. &lt;span style=&quot;background-color: #DDDDDD;&quot;&gt;setTimeout()&lt;/span&gt; 함수가 호출스택에 쌓인다.&lt;br&gt;2. &lt;span style=&quot;background-color: #DDDDDD;&quot;&gt;setTimeout()&lt;/span&gt; 함수가 실행되면서 &lt;span style=&quot;background-color: #FFC1C8;&quot;&gt;백그라운드 스레드(타이머)&lt;/span&gt;를 부른다.(야 너 1초 세라!) &lt;br&gt;3. &lt;span style=&quot;background-color: #C0D1E7;&quot;&gt;이벤트 루프&lt;/span&gt;는 호출스택에서 &lt;span style=&quot;background-color: #DDDDDD;&quot;&gt;setTimeout()&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;함수에 할당된&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #DDDDDD;&quot;&gt;콜백 함수&amp;nbsp;()=&amp;gt;{console.log('hello')}&lt;/span&gt;를 꺼내서 &lt;span style=&quot;background-color: #FFC1C8;&quot;&gt;백그라운드 스레드(타이머)&lt;/span&gt;에게 넘겨준다.&lt;br&gt;4. &lt;span style=&quot;background-color: #FFC1C8;&quot;&gt;백그라운드 스레드(타이머)&lt;/span&gt;는&amp;nbsp;시간을 잰다.(1초 동안)&lt;br&gt;5. &lt;span style=&quot;background-color: #DDDDDD;&quot;&gt;setTimeout()&lt;/span&gt; 함수는 할일을 다 했으니 호출스택에서 제거된다.&lt;br&gt;6. &lt;span style=&quot;background-color: #DDDDDD;&quot;&gt;console.log('bye')&lt;/span&gt; 함수가 호출스택에 쌓인다.&lt;br&gt;7. &lt;span style=&quot;background-color: #DDDDDD;&quot;&gt;console.log('bye')&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;함수는&lt;/span&gt; 바로 실행되면서 호출스택에서 제거된다. (이제 호출스택이 완전히 비었다.)&lt;br&gt;8. 1초가 지난 후 &lt;span style=&quot;background-color: #FFC1C8;&quot;&gt;백그라운드 스레드(타이머)&lt;/span&gt;가 할일을 끝냈다고 감지한 &lt;span style=&quot;background-color: #C0D1E7;&quot;&gt;이벤트 루프&lt;/span&gt;가 &lt;span style=&quot;background-color: #DDDDDD;&quot;&gt;콜백 함수&lt;/span&gt;를 태스크큐로 옮긴다.&lt;br&gt;9. 호출스택이 비어있다고 감지한 이벤트 루프가 태스크큐에 있는 &lt;span style=&quot;background-color: #DDDDDD;&quot;&gt;콜백함수&lt;/span&gt;를 호출스택으로 옮긴다.&lt;br&gt;10. &lt;span style=&quot;background-color: #DDDDDD;&quot;&gt;콜백함수&lt;/span&gt;가 실행되며 호출스택에서 제거된다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;이렇게 복잡한 과정이 진행된다&lt;br&gt;&amp;nbsp;&lt;br&gt;글로 써놓으니 이해하기 어렵다면 아래 코드와 이미지를 참고하자&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;const foo = () =&amp;gt; console.log(&quot;First&quot;);
const bar = () =&amp;gt; setTimeout(() =&amp;gt; console.log(&quot;Second&quot;), 500);
const baz = () =&amp;gt; console.log(&quot;Third&quot;);

bar();
foo();
baz();&lt;/code&gt;&lt;/pre&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;405&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vzWo3/btsGZxVWn4o/P0kJIF28kkh6PWfC7rNGQ0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vzWo3/btsGZxVWn4o/P0kJIF28kkh6PWfC7rNGQ0/img.gif&quot; data-alt=&quot;출처 :https://dev.to/lydiahallie/javascript-visualized-event-loop-3dif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vzWo3/btsGZxVWn4o/P0kJIF28kkh6PWfC7rNGQ0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/vzWo3/btsGZxVWn4o/P0kJIF28kkh6PWfC7rNGQ0/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;720&quot; height=&quot;405&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;405&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 :https://dev.to/lydiahallie/javascript-visualized-event-loop-3dif&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;&amp;nbsp;요약하자면 이벤트 루프는 각 요소들을 &lt;span style=&quot;background-color: #99CEFA;&quot;&gt;계속 모니터링&lt;/span&gt; 하면서 비동기 함수 작업이 호출스택에 쌓이면 이를 Web API(백그라운드 스레드)로 옮기고&lt;br&gt;&lt;br&gt;작업이 완료되면 콜백을 태스크큐로 옮기고, 이를 다시 호출스택으로 &lt;span style=&quot;background-color: #99CEFA;&quot;&gt;옮기는 역할&lt;/span&gt;을 한다.&lt;br&gt;&lt;br&gt;이 작업은 끊임없이 반복되기 때문에 &lt;span style=&quot;background-color: #DDDDDD;&quot;&gt;이벤트'루프'&lt;/span&gt; 라고 하는 것이다.&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;/p&gt;</description>
      <category>개발/Javascript</category>
      <category>eventloop</category>
      <category>JavaScript</category>
      <category>js</category>
      <category>비동기</category>
      <category>이벤트루프</category>
      <category>자바스크립트</category>
      <category>프론트엔드</category>
      <author>duknock</author>
      <guid isPermaLink="true">https://duknock.tistory.com/32</guid>
      <comments>https://duknock.tistory.com/32#entry32comment</comments>
      <pubDate>Fri, 26 Apr 2024 19:01:01 +0900</pubDate>
    </item>
    <item>
      <title>1년 만에 티스토리 글쓰기</title>
      <link>https://duknock.tistory.com/31</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;티스토리 안 들어온지 어느새 1년이 다 되어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작년만 해도 &quot;나도 개발자다! 히히!&quot; 하면서 장황하게 새로 알게 된 지식들을 블로그에 싸질러 놨었는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 다시 내가 썼던 글을 읽어보니 아주 창피하기 짝이 없네^^&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 지금도 아무고토 모르는 주니어 개발자이지만, 적어도 작년보다는 아는게 많아지긴 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&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;그래서 1년 간 나름대로 쌓인 나의 조그마한... 지식들로 다시 포스팅을 시작해야겠음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안 써놓으면 다 까먹더라; 나도 가끔 내가 써놓은 글 참고해서 다시 코드 작성할 때 많다ㅋㅋ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;후... 이제 취직하고 2년 반이 다 되어 가는데, 이제서야 프롤로그를 끝내는 느낌이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간중간 나름 제대로 공부해보겠다고 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;프로그래머스&lt;/b&gt;&lt;/span&gt;에서 코딩테스트 예제도 풀어보고 했는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 코딩실력에 대해 알아보고는 까암짝 놀랐음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레벨 2를 푸는데 어렵더라;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;(물론 나는 웹 퍼블리셔로 시작했으니까 알고리즘 관련 지식은 전무하긴 하다만)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제는 프론트엔드와 더불어 웹서버쪽도 야금야금 공부하며 도전해보고 있는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이따위 실력으로는 다른 회사 가서 열심히 코드 짜서 커밋해봤자 &quot;니 코드 더러워&quot; 하면서 revert 할 게 뻔하다.&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;i&gt;깊은 인터넷 속 두낙의 블로그 누가 와서 보나요~&lt;/i&gt; 싶지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&quot;나 다시 포스팅 한다.&quot;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;근데 귀찮으니까 포스팅 주기는 랜덤임ㅎㅎㅎㅎ&lt;/p&gt;</description>
      <category>개발/기타 </category>
      <author>duknock</author>
      <guid isPermaLink="true">https://duknock.tistory.com/31</guid>
      <comments>https://duknock.tistory.com/31#entry31comment</comments>
      <pubDate>Tue, 27 Feb 2024 23:33:13 +0900</pubDate>
    </item>
    <item>
      <title>[자바스크립트] 화면(뷰포트)에 특정 요소가 떴을 때 감지하는 방법 - Intersection Observer</title>
      <link>https://duknock.tistory.com/30</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bENjl4/btsdXv6GlMX/udQydHzKE0kGo9WPowXlNK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bENjl4/btsdXv6GlMX/udQydHzKE0kGo9WPowXlNK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bENjl4/btsdXv6GlMX/udQydHzKE0kGo9WPowXlNK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bENjl4/btsdXv6GlMX/udQydHzKE0kGo9WPowXlNK/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;1200&quot; height=&quot;800&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Intersection Observer&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Intersection&amp;nbsp;Observer&lt;/b&gt;는&amp;nbsp;웹&amp;nbsp;브라우저에서&amp;nbsp;제공하는&amp;nbsp;API&amp;nbsp;중&amp;nbsp;하나로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;요소가 &lt;u&gt;&lt;b&gt;다른 요소나 브라우저 viewport와 교차하는지 여부를 감지하는 기능&lt;/b&gt;&lt;/u&gt;을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;예를 들어, 스크롤을 내렸을 때 화면에 보이는 이미지나 다른 요소를 로드하기 전에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Intersection&amp;nbsp;Observer를&amp;nbsp;사용하여&amp;nbsp;해당&amp;nbsp;요소가&amp;nbsp;브라우저&amp;nbsp;viewport&amp;nbsp;안에&amp;nbsp;있는지&amp;nbsp;감지하고,&lt;/p&gt;
&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;br /&gt;이를 통해 불필요한 리소스 로드를 줄일 수 있으며, 페이지 로딩 속도를 향상시킬 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;Intersection Observer는 요소의 가시성 여부를 지속적으로 감시하고, 이벤트를 발생시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트는 요소가 viewport에 나타날 때, 사라질 때, 일부분만 나타날 때 등 다양한 상황에서 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;Intersection Observer는 모바일 기기에서의 성능이 더욱 향상되며, 상대적으로 덜 복잡한 코드를 작성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;또한, &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;스크롤, 리사이즈,&lt;/b&gt; &lt;b&gt;CSS 애니메이션&lt;/b&gt;&lt;/span&gt; 등 다양한 이벤트와 함께 사용할 수 있어서 다양한 상황에서 활용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-size=&quot;size16&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;사용법&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;1. IntersectionObserver 객체 생성&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IntersectionObserver를 사용하기 위해서는 먼저 객체를 생성해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;객체를 생성할 때는 콜백 함수와 옵션을 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1683620547057&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const observer = new IntersectionObserver(callback, options);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;callback&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IntersectionObserver가 감지할 때 실행할 콜백 함수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콜백 함수에는 인자로 &lt;b&gt;entries&lt;/b&gt;와 &lt;b&gt;observer&lt;/b&gt;가 전달된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;entries - 관찰 대상 요소의 정보를 담고 있는 객체&lt;br /&gt;observer - IntersectionObserver 객체 자체&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;options&lt;/span&gt;&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;IntersectionObserver 설정을 지정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;root&lt;/b&gt;, &lt;b&gt;rootMargin&lt;/b&gt;, &lt;b&gt;threshold&lt;/b&gt; 등의 속성을 설정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;root: 관찰 대상 요소의 가시성을 판단할 때 기준이 되는 요소&lt;br /&gt;rootMargin: root 요소의 여백을 지정한다.&lt;br /&gt;threshold: 관찰 대상 요소의 가시성 비율을 지정한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;root&lt;/b&gt;와 &lt;b&gt;rootMargin&lt;/b&gt;은&amp;nbsp;둘&amp;nbsp;중&amp;nbsp;하나만&amp;nbsp;지정할&amp;nbsp;수&amp;nbsp;있다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;threshold&lt;/b&gt;는&amp;nbsp;배열&amp;nbsp;형태로&amp;nbsp;지정할&amp;nbsp;수&amp;nbsp;있다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;threshold&lt;/b&gt;의 기본값은 0이다.&lt;br /&gt;0은 관찰 대상 요소가 뷰포트에 1px이라도 들어오면 콜백&amp;nbsp;함수가&amp;nbsp;실행됨을&amp;nbsp;의미하고,&lt;br /&gt;1은&amp;nbsp;관찰&amp;nbsp;대상&amp;nbsp;요소가&amp;nbsp;뷰포트에&amp;nbsp;전부&amp;nbsp;들어와야&amp;nbsp;콜백&amp;nbsp;함수가&amp;nbsp;실행됨을&amp;nbsp;의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;2. 관찰 대상 요소 지정&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IntersectionObserver로&amp;nbsp;관찰할&amp;nbsp;대상&amp;nbsp;요소를&amp;nbsp;지정해야&amp;nbsp;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;b&gt;observe()&lt;/b&gt;&lt;/span&gt;&amp;nbsp;메소드를&amp;nbsp;사용하여&amp;nbsp;대상&amp;nbsp;요소를&amp;nbsp;지정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1683621433114&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const target = document.querySelector('.target');
observer.observe(target);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;3. 콜백 함수 작성&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IntersectionObserver가 감지할 때 실행할 콜백 함수를 작성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;콜백 함수에는 아까 위에 말했듯이&amp;nbsp;&lt;b&gt;entries&lt;/b&gt;와 &lt;b&gt;observer&lt;/b&gt;가 인자로 전달된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1683621508423&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function callback(entries, observer) {
  entries.forEach(entry =&amp;gt; {
    if (entry.isIntersecting) {
      console.log(`${entry.target} is intersecting`);
    } else {
      console.log(`${entry.target} is not intersecting`);
    }
  });
}&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;각 entry 객체에는 여러 정보들이 포함되어 있다.&lt;/p&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;entry.target&lt;/b&gt;: IntersectionObserver가 감지한 대상 요소&lt;br /&gt;&lt;b&gt;entry.isIntersecting&lt;/b&gt;: 대상 요소가 뷰포트에 들어온 경우 true가 반환&lt;br /&gt;&lt;b&gt;entry.intersectionRatio&lt;/b&gt;: 대상 요소가 뷰포트에 들어온 비율을 반환&lt;br /&gt;&lt;b&gt;entry.intersectionRect&lt;/b&gt;: 대상 요소가 뷰포트에 들어온 영역의 정보를 반환&lt;br /&gt;&lt;b&gt;entry.boundingClientRect&lt;/b&gt;: 대상 요소의 정보를 반환&lt;br /&gt;&lt;b&gt;entry.rootBounds&lt;/b&gt;: root 요소의 정보를 반환&lt;br /&gt;&lt;b&gt;entry.time&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;blockquote data-ke-style=&quot;style2&quot;&gt;4.&amp;nbsp;IntersectionObserver 객체 해제&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IntersectionObserver&amp;nbsp;객체를&amp;nbsp;해제할&amp;nbsp;때는&amp;nbsp;unobserve()&amp;nbsp;메소드를&amp;nbsp;사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1683621798609&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;observer.unobserve(target);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와&amp;nbsp;같이&amp;nbsp;IntersectionObserver를&amp;nbsp;사용하면&amp;nbsp;뷰포트에&amp;nbsp;나타나는&amp;nbsp;요소를&amp;nbsp;감지하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;콜백 함수를 통해 이벤트를 처리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/Javascript</category>
      <category>IntersectionObserver</category>
      <category>JavaScript</category>
      <category>js</category>
      <category>자바스크립트</category>
      <category>프론트엔드</category>
      <author>duknock</author>
      <guid isPermaLink="true">https://duknock.tistory.com/30</guid>
      <comments>https://duknock.tistory.com/30#entry30comment</comments>
      <pubDate>Tue, 9 May 2023 17:48:24 +0900</pubDate>
    </item>
    <item>
      <title>[자바스크립트] setInterval 대신 requestAnimationFrame 사용하기</title>
      <link>https://duknock.tistory.com/29</link>
      <description>&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;371&quot; data-origin-height=&quot;46&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dvWbQr/btscPTh7ySJ/hX5XrcvdTclSP2KP0zPU8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dvWbQr/btscPTh7ySJ/hX5XrcvdTclSP2KP0zPU8K/img.png&quot; data-alt=&quot;로그인 세션의 남은 시간을 나타내는 타이머&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dvWbQr/btscPTh7ySJ/hX5XrcvdTclSP2KP0zPU8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdvWbQr%2FbtscPTh7ySJ%2FhX5XrcvdTclSP2KP0zPU8K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;371&quot; height=&quot;46&quot; data-origin-width=&quot;371&quot; data-origin-height=&quot;46&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;로그인 세션의 남은 시간을 나타내는 타이머&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;보통 브라우저 화면에 타이머 같은것을 띄울 때, setInterval 함수를 사용한다.&lt;/p&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;pre id=&quot;code_1682556185082&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;html&amp;gt;

&amp;lt;head&amp;gt;
	&amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
	&amp;lt;title&amp;gt;타이머&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;

&amp;lt;body&amp;gt;
	&amp;lt;h1&amp;gt;타이머&amp;lt;/h1&amp;gt;
	&amp;lt;p id=&quot;timer&quot;&amp;gt;0초&amp;lt;/p&amp;gt;
	&amp;lt;button onclick=&quot;startTimer()&quot;&amp;gt;시작&amp;lt;/button&amp;gt;
	&amp;lt;button onclick=&quot;stopTimer()&quot;&amp;gt;정지&amp;lt;/button&amp;gt;
	&amp;lt;script&amp;gt;
		// 변수 선언
		let seconds = 0;
		let timerInterval;

		// 타이머 시작 함수
		function startTimer() {
			// 1초마다 seconds 변수 증가
			timerInterval = setInterval(function() {
				seconds++;
				// 타이머 렌더링
				document.getElementById(&quot;timer&quot;).innerHTML = seconds + &quot;초&quot;;
			}, 1000);
		}

		// 타이머 정지 함수
		function stopTimer() {
			// setInterval 함수 정지
			clearInterval(timerInterval);
			// seconds 변수 초기화
			seconds = 0;
			// 타이머 렌더링
			document.getElementById(&quot;timer&quot;).innerHTML = seconds + &quot;초&quot;;
		}
	&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;

&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 1초 (1000ms) 마다 함수를 실행하여 HTML 요소를 업데이트하는 방식으로 렌더링하게 되는데&lt;/p&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;고작 타이머 하나 보여주려고 백그라운드에서 계속 돌아가는 setinterval 함수를 사용하는 것은 아래와 같은 문제점들이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;1. 정확한 실행 시간 보장 불가능:&lt;br /&gt;setInterval 함수는 일정한 시간 간격으로 코드를 실행하는 것이 아니라, 이벤트 루프에서 실행 대기열에 추가된 시점부터 일정 시간이 지난 후 실행됩니다. 따라서, 코드 실행이 지연될 가능성이 있습니다.&lt;br /&gt;&lt;br /&gt;2. 코드 충돌 가능성:&lt;br /&gt;setInterval 함수를 사용하면서, 두 개 이상의 코드가 동시에 실행될 가능성이 있습니다. 이 경우, 코드 충돌이 발생하여 예상치 못한 결과가 발생할 수 있습니다.&lt;br /&gt;&lt;br /&gt;3. 반복 시간 간격의 불일치:&lt;br /&gt;setInterval 함수를 사용하여 일정 시간 간격으로 코드를 실행하면 시간 간격이 정확하게 유지되지 않을 수 있습니다. 이는 브라우저의 성능에 따라 달라질 수 있습니다.&lt;br /&gt;&lt;br /&gt;4. 메모리 사용:&lt;br /&gt;setInterval 함수를 사용하면 반복적으로 실행되는 코드가 메모리를 계속해서 사용하게 됩니다. 이는 메모리 누수를 유발할 수 있습니다.&lt;br /&gt;&lt;br /&gt;5. 중복 실행:&lt;br /&gt;setInterval 함수를 사용하면 이전 코드 실행이 완료되지 않은 상태에서 새로운 코드가 실행될 가능성이 있습니다. 이러한 경우, 코드 중복 실행이 발생할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;그래서 단순 HTML 렌더링을 위해서라면 setInterval 함수 대신 사용할 수 있는 좋은 함수가 있다.&lt;/p&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; requestAnimationFrame&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;requestAnimationFrame()&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&lt;b&gt;requestAnimationFrame&lt;/b&gt; 함수는 브라우저의 성능을 최적화하고 애니메이션을 부드럽게 처리하기 위한 자바스크립트 함수이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;이 함수는 브라우저의 화면 갱신 주기에 맞추어 애니메이션을 처리하므로, 애니메이션의 부드러운 움직임을 보장한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;이 함수가 setInterval 함수보다 나은 점은 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;1. 더 나은 성능:&amp;nbsp;&lt;br /&gt;requestAnimationFrame 함수는 브라우저의 화면 갱신 주기에 맞추어 호출되기 때문에, 브라우저가 새로운 프레임을 그리기 전까지 실행되지 않는다. 이를 통해, 애니메이션이 부드럽게 처리되고 브라우저의 성능을 최적화할 수 있다. setInterval 함수는 정해진 시간 간격으로 함수를 호출하는 방식으로 애니메이션을 처리하기 때문에 브라우저의 성능과 화면 갱신 주기와 일치하지 않을 수 있다.&lt;br /&gt;&lt;br /&gt;2. 메모리 절약:&lt;br /&gt;화면 갱신 주기에 맞추어 호출되기 때문에 불필요한 처리를 방지하고 메모리를 절약할 수 있다. 이는 모바일 기기에서 특히 중요하다. (많은 메모리의 사용은 배터리 수명과 직결되기 때문)&lt;br /&gt;&lt;br /&gt;3. 다른 탭과의 경쟁 회피:&lt;br /&gt;requestAnimationFrame 함수는 브라우저가 비활성화되거나 다른 탭으로 전환되어 있는 경우에는 호출되지 않는다. 이는 다른 탭과의 경쟁을 회피하고 브라우저의 성능을 유지하는 데 도움이 된다.&lt;br /&gt;&lt;br /&gt;4. 동일한 타이머 공유:&lt;br /&gt;여러개의 애니메이션을 생성해도, 각각의 타이머를 생성하는것이 아닌, 내부의 동일한 타이머를 참조하여 불필요한 메모리를 사용하지 않는다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;requestAnimationFrame 함수 예시&lt;/h4&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;한번 아래와 같이 1초 동안 콘솔에 계속 로그를 남겨보라고 하면 어떻게 될까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1682558476358&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let now = new Date();
let after1sec = new Date(now.getTime() + 1000);

function printTime() {
	let now = new Date();
	if (after1sec.getTime() - now.getTime() &amp;gt; 0) {
		console.log('record!');
		printTime();
	}
}
printTime();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;399&quot; data-origin-height=&quot;186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFaNLs/btscPcB7kaC/wxagHoL6yTWye3G3N8c8S1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFaNLs/btscPcB7kaC/wxagHoL6yTWye3G3N8c8S1/img.png&quot; data-alt=&quot;바로 스택 오버플로우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFaNLs/btscPcB7kaC/wxagHoL6yTWye3G3N8c8S1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFaNLs%2FbtscPcB7kaC%2FwxagHoL6yTWye3G3N8c8S1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;399&quot; height=&quot;186&quot; data-origin-width=&quot;399&quot; data-origin-height=&quot;186&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;바로 스택 오버플로우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연하게도 지나치게 많은 콜스택이 쌓여 에러가 난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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;그렇다면 이번엔 requestAnimationFrame을 이용하여 똑같이 1초동안 콘솔을 찍어보면 어떤 결과가 나올까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1682558594564&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let startTime = null;

function printTime(timestamp) {
	if (!startTime) startTime = timestamp;
	console.log('record!');
	if (timestamp - startTime &amp;lt; 1000) {
		requestAnimationFrame(printTime);
	}
}
requestAnimationFrame(printTime);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;455&quot; data-origin-height=&quot;60&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Bpbi3/btscRp1mHFA/OARnqdZus8yeFbbwFBCZBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Bpbi3/btscRp1mHFA/OARnqdZus8yeFbbwFBCZBk/img.png&quot; data-alt=&quot;음...60번이 아니라 61번이 찍히긴 했지만&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Bpbi3/btscRp1mHFA/OARnqdZus8yeFbbwFBCZBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBpbi3%2FbtscRp1mHFA%2FOARnqdZus8yeFbbwFBCZBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;455&quot; height=&quot;60&quot; data-origin-width=&quot;455&quot; data-origin-height=&quot;60&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;음...60번이 아니라 61번이 찍히긴 했지만&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;아까 위의 코드처럼 무제한 호출되는 것이 아닌, 1초에 60번의 호출만 발생하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;즉, 1/60으로 애니메이션이 제한되어 최적화된 속도로 부드러운 애니메이션을 표현하면서 성능은 최대한 확보할 수 있게 된 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;cancelAnimationFrame()&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setInterval 함수가 clearInterval 함수로 끝낼 수 있다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;requestAnimationFrame 함수는 cancelAnimationFrame 함수로 종료시킬 수 있다.&lt;/p&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;사용 방법은 clearInterval 함수와 똑같다.&lt;/p&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;pre id=&quot;code_1682559384950&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let requestId;

function draw() {
	// 애니메이션 그리기
	requestId = requestAnimationFrame(draw);
}

// 애니메이션 시작
requestId = requestAnimationFrame(draw);

// 5초 후 애니메이션 중지
setTimeout(() =&amp;gt; {
	cancelAnimationFrame(requestId);
}, 5000);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;requestAnimationFrame 함수를 변수에 할당하고&lt;/p&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;cancelAnimationFrame 함수를 실행 할 때 해당 변수를 인수로 건네주면 종료된다.&lt;/p&gt;</description>
      <category>개발/Javascript</category>
      <category>JavaScript</category>
      <category>js</category>
      <category>requestAnimationFrame</category>
      <category>VanillaJS</category>
      <category>바닐라JS</category>
      <category>자바스크립트</category>
      <category>프론트엔드</category>
      <author>duknock</author>
      <guid isPermaLink="true">https://duknock.tistory.com/29</guid>
      <comments>https://duknock.tistory.com/29#entry29comment</comments>
      <pubDate>Thu, 27 Apr 2023 10:38:18 +0900</pubDate>
    </item>
    <item>
      <title>[Chat GPT] 챗 GPT를 VS Code에서 사용하기</title>
      <link>https://duknock.tistory.com/28</link>
      <description>&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;Chat GPT를 써본 사람은 알겠지만&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이자식 사람보다 코딩을 잘한다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;심지어 코딩뿐만 아니라 모든 방면에서 지식이 사람보다 뛰어나다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 요즘 코드를 짜는 방식이 좀 변했는데,&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;내가 먼저 코드를 짜보고 GPT한테 내 코드를 보여주며 코드리뷰를 해달라고 한다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;그럼 &lt;b&gt;코딩 외길인생 개발의 달인(경력 30년)&lt;/b&gt; 양쪽 뺨 싸다구를 후려 갈길 정도로 리뷰를 잘써준다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;근데 매번 Open AI 페이지 들어가서 코드 복붙해서 리뷰해달라고 하기가 너무 귀찮으니&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;VS code 안에서 바로 Chat GPT를 사용할 수 있는 방법을 찾아봤는데&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;역시나 누군가 벌써 플러그인을 만들어놓았다. (심지어 한국인인듯)&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이번엔 VS code에서 Chat GPT를 사용하는 방법을 알아보자.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;우선 VS Code 에서 AICodeHelper 라고 검색한다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&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;587&quot; data-origin-height=&quot;176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6HbZT/btr7CV6iXt9/ek2b6p7HxcRZCTl4rYkNdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6HbZT/btr7CV6iXt9/ek2b6p7HxcRZCTl4rYkNdk/img.png&quot; data-alt=&quot;감사합니다 김승태씨&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6HbZT/btr7CV6iXt9/ek2b6p7HxcRZCTl4rYkNdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6HbZT%2Fbtr7CV6iXt9%2Fek2b6p7HxcRZCTl4rYkNdk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;587&quot; height=&quot;176&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;176&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;감사합니다 김승태씨&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 플러그인을 설치하고 나면 Chat GPT에서 API Key를 받아와서 적용을 해줘야한다.&lt;/p&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 Key는 &lt;a href=&quot;https://platform.openai.com/account/api-keys&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;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;780&quot; data-origin-height=&quot;361&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ctPVLE/btr7HiT5G5Y/mi8gB9ygCdJ0U7PnvUPbG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctPVLE/btr7HiT5G5Y/mi8gB9ygCdJ0U7PnvUPbG1/img.png&quot; data-alt=&quot;저기서 Create new secret key 버튼을 누르고 발급받은 key를 복사한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ctPVLE/btr7HiT5G5Y/mi8gB9ygCdJ0U7PnvUPbG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FctPVLE%2Fbtr7HiT5G5Y%2Fmi8gB9ygCdJ0U7PnvUPbG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;780&quot; height=&quot;361&quot; data-origin-width=&quot;780&quot; data-origin-height=&quot;361&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;저기서 Create new secret key 버튼을 누르고 발급받은 key를 복사한다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 후, VS code 로 돌아와서 alt + shif + ctrl + Q 를 동시에 누르면&lt;/p&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;597&quot; data-origin-height=&quot;99&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/T66SJ/btr7OOYTVvU/vN45igwNpaDlFPBtSoyEaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/T66SJ/btr7OOYTVvU/vN45igwNpaDlFPBtSoyEaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/T66SJ/btr7OOYTVvU/vN45igwNpaDlFPBtSoyEaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FT66SJ%2Fbtr7OOYTVvU%2FvN45igwNpaDlFPBtSoyEaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;597&quot; height=&quot;99&quot; data-origin-width=&quot;597&quot; data-origin-height=&quot;99&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 창이 뜨는데 여기에 복사해 둔 API Key를 넣으면 된다.&lt;/p&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;사용해보기&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용방법은 간단하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일에 아무 문장이나 적어서 선택한 다음 원하는 명령어 단축키를 누르면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령어 단축키는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;주석&amp;nbsp;남기기&amp;nbsp;=&amp;nbsp;ctrl&amp;nbsp;shift&amp;nbsp;alt&amp;nbsp;z&amp;nbsp; &lt;br /&gt;리팩토링&amp;nbsp;=&amp;nbsp;ctrl&amp;nbsp;shift&amp;nbsp;alt&amp;nbsp;r &lt;br /&gt;코드리뷰&amp;nbsp;=&amp;nbsp;ctrl&amp;nbsp;shift&amp;nbsp;alt&amp;nbsp;c &lt;br /&gt;일반요청&amp;nbsp;=&amp;nbsp;ctrl&amp;nbsp;shift&amp;nbsp;alt&amp;nbsp;m &lt;br /&gt;코드생성&amp;nbsp;=&amp;nbsp;ctrl&amp;nbsp;shift&amp;nbsp;alt&amp;nbsp;g&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;&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;816&quot; data-origin-height=&quot;223&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uLLXF/btr7LqYYw7A/NzfOzjFC0lNee2SIptKrtK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uLLXF/btr7LqYYw7A/NzfOzjFC0lNee2SIptKrtK/img.png&quot; data-alt=&quot;문장을 적어서 드래그로 선택한 다음&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uLLXF/btr7LqYYw7A/NzfOzjFC0lNee2SIptKrtK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuLLXF%2Fbtr7LqYYw7A%2FNzfOzjFC0lNee2SIptKrtK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;816&quot; height=&quot;223&quot; data-origin-width=&quot;816&quot; data-origin-height=&quot;223&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;문장을 적어서 드래그로 선택한 다음&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ctrl + shift +alt + G (코드 생성)를 누르면&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;631&quot; data-origin-height=&quot;371&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDelaN/btr7Hf366Ih/ZZuVMuBwUQCwxUQePXGWv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDelaN/btr7Hf366Ih/ZZuVMuBwUQCwxUQePXGWv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDelaN/btr7Hf366Ih/ZZuVMuBwUQCwxUQePXGWv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDelaN%2Fbtr7Hf366Ih%2FZZuVMuBwUQCwxUQePXGWv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;631&quot; height=&quot;371&quot; data-origin-width=&quot;631&quot; data-origin-height=&quot;371&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 코딩을 해준다!&lt;/p&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 플러그인의 진가는 바로 &lt;span style=&quot;background-color: #f3c000;&quot;&gt;&lt;b&gt;코드리뷰&lt;/b&gt;&lt;/span&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;1074&quot; data-origin-height=&quot;249&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zjFyR/btr7PjkcpLt/uuURC44K5dUPSQoomSmYO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zjFyR/btr7PjkcpLt/uuURC44K5dUPSQoomSmYO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zjFyR/btr7PjkcpLt/uuURC44K5dUPSQoomSmYO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzjFyR%2Fbtr7PjkcpLt%2FuuURC44K5dUPSQoomSmYO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1074&quot; height=&quot;249&quot; data-origin-width=&quot;1074&quot; data-origin-height=&quot;249&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 함수는 임의로 내가 제작한 함수인데,&lt;/p&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 요청이 오류가 났을 경우, 에러메세지를 모달창으로 띄워주는 함수이다.&lt;/p&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;ctrl + shift + alt + C (코드리뷰) 를 누르면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;1093&quot; data-origin-height=&quot;439&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dopaAK/btr7He5dypC/YZZV93LYlsK0m3VhKrKHok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dopaAK/btr7He5dypC/YZZV93LYlsK0m3VhKrKHok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dopaAK/btr7He5dypC/YZZV93LYlsK0m3VhKrKHok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdopaAK%2Fbtr7He5dypC%2FYZZV93LYlsK0m3VhKrKHok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1093&quot; height=&quot;439&quot; data-origin-width=&quot;1093&quot; data-origin-height=&quot;439&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 내가 만든 함수가 어떤 의도로 만든 것인지를 정확하게 파악한것 뿐만 아니라,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;modalShow 함수 또한 내가 임의로 제작한 함수인데 함수명만 보고 어떤 기능인지를 유추까지 해낸다.&lt;/p&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;&lt;span style=&quot;color: #ee2323;&quot;&gt;미.쳤.다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;물론 이거만 의존해서 코딩을 하면 AI를 이용하는게 아니라,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;내가 AI에 의해 행동하는 바보멍청이가 될 수 있으니, 너무 믿지는 말고 도움받는 정도로만 사용하시길...&lt;/span&gt;&lt;/p&gt;</description>
      <category>개발/기타 </category>
      <category>Ai</category>
      <category>ChatGPT</category>
      <category>vscode</category>
      <author>duknock</author>
      <guid isPermaLink="true">https://duknock.tistory.com/28</guid>
      <comments>https://duknock.tistory.com/28#entry28comment</comments>
      <pubDate>Mon, 3 Apr 2023 17:35:27 +0900</pubDate>
    </item>
    <item>
      <title>[네트워크] 포트포워딩(Port Fowarding) 이 뭘까</title>
      <link>https://duknock.tistory.com/27</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Port Fowarding&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포트 포워딩은 인터넷에 연결된 장치(예: 컴퓨터, 라우터, 모뎀 등)가 외부에서 접속하는 요청을 내부 네트워크의 특정 장치로 전달하는 과정을 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로&amp;nbsp;인터넷에서&amp;nbsp;특정&amp;nbsp;서비스(예:&amp;nbsp;웹&amp;nbsp;서버,&amp;nbsp;FTP&amp;nbsp;서버,&amp;nbsp;게임&amp;nbsp;서버&amp;nbsp;등)를&amp;nbsp;제공하려면&amp;nbsp;해당&amp;nbsp;서비스에&amp;nbsp;할당된&amp;nbsp;포트&amp;nbsp;번호를&amp;nbsp;외부에서&amp;nbsp;접속&amp;nbsp;가능하게&amp;nbsp;열어야&amp;nbsp;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이를&amp;nbsp;위해서는&amp;nbsp;먼저&amp;nbsp;라우터나&amp;nbsp;모뎀과&amp;nbsp;같은&amp;nbsp;게이트웨이&amp;nbsp;장치에게&amp;nbsp;해당&amp;nbsp;포트&amp;nbsp;번호로&amp;nbsp;들어오는&amp;nbsp;요청을&amp;nbsp;내부&amp;nbsp;네트워크의&amp;nbsp;서버나&amp;nbsp;컴퓨터로&amp;nbsp;전달하도록&amp;nbsp;설정해야&amp;nbsp;하는데,&amp;nbsp;이&amp;nbsp;설정&amp;nbsp;과정을&amp;nbsp;포트포워딩이라고&amp;nbsp;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;예를&amp;nbsp;들어,&amp;nbsp;인터넷에서&amp;nbsp;접속&amp;nbsp;가능한&amp;nbsp;웹&amp;nbsp;서버를&amp;nbsp;구축하려면&amp;nbsp;웹&amp;nbsp;서버에&amp;nbsp;할당된&amp;nbsp;포트&amp;nbsp;번호(일반적으로&amp;nbsp;80번&amp;nbsp;포트)를&amp;nbsp;외부에서&amp;nbsp;접속&amp;nbsp;가능하게&amp;nbsp;열고,&amp;nbsp;해당&amp;nbsp;포트로&amp;nbsp;들어오는&amp;nbsp;요청을&amp;nbsp;라우터나&amp;nbsp;모뎀과&amp;nbsp;같은&amp;nbsp;게이트웨이&amp;nbsp;장치에서&amp;nbsp;웹&amp;nbsp;서버로&amp;nbsp;포워딩해야&amp;nbsp;한다.&amp;nbsp;이러한&amp;nbsp;설정을&amp;nbsp;통해&amp;nbsp;인터넷에서&amp;nbsp;접속&amp;nbsp;가능한&amp;nbsp;웹&amp;nbsp;서버를&amp;nbsp;구축할&amp;nbsp;수&amp;nbsp;있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;681&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ofmVW/btr5iaiMKrR/58zqgCcd8qDm6kkZmsQ5SK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ofmVW/btr5iaiMKrR/58zqgCcd8qDm6kkZmsQ5SK/img.png&quot; data-alt=&quot;외부(주로 인터넷)에서 접속요청이 들어오면 게이트웨이 장치(공유기 등)는 해당하는 포트를 통해 웹서버로 요청을 전달한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ofmVW/btr5iaiMKrR/58zqgCcd8qDm6kkZmsQ5SK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FofmVW%2Fbtr5iaiMKrR%2F58zqgCcd8qDm6kkZmsQ5SK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;750&quot; height=&quot;681&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;681&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;style6&quot; /&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;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;내부 아이피와 외부 아이피&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;누구집&lt;/b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;인지만 말해도 충분히 찾아갈 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 같은 공유기를 쓰는 PC끼리 &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;내부 아이피&lt;/b&gt;&lt;/span&gt;로 해당 PC의 서버에 접속할 수 있는것과 같다.&lt;/p&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;763&quot; data-origin-height=&quot;457&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3T4G7/btr5dRR9yUX/PXfKIJStIradb0rEhS3Isk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3T4G7/btr5dRR9yUX/PXfKIJStIradb0rEhS3Isk/img.png&quot; data-alt=&quot;실제로는 IPv4의 주소는 192.168 로 시작한다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3T4G7/btr5dRR9yUX/PXfKIJStIradb0rEhS3Isk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3T4G7%2Fbtr5dRR9yUX%2FPXfKIJStIradb0rEhS3Isk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;763&quot; height=&quot;457&quot; data-origin-width=&quot;763&quot; data-origin-height=&quot;457&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실제로는 IPv4의 주소는 192.168 로 시작한다&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;&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;span style=&quot;background-color: #99cefa;&quot;&gt;누구씨 집&lt;/span&gt;&amp;nbsp;이라고만 해도 알아서 잘 찾아간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;물론 무조건 접속 가능한건 당연히 아니다. 방화벽이 있기때문에 방화벽에서 연결을 허용해줘야 접속이 가능하다.&lt;br /&gt;집 주소는 맞게 찾아갔지만 문 안열어주면 못 들어가는것과 같다&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전혀 다른동네에 사는 모르는 사람에게&amp;nbsp; &lt;span style=&quot;background-color: #99cefa; color: #333333; text-align: start;&quot;&gt;누구씨 집&lt;/span&gt;&amp;nbsp;으로 찾아오라고 하면 찾아올 수 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;687&quot; data-origin-height=&quot;459&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckvdTB/btr5deG5qIF/af1dx4wO0ZKxQ6GktFZrm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckvdTB/btr5deG5qIF/af1dx4wO0ZKxQ6GktFZrm0/img.png&quot; data-alt=&quot;응~ 죽으면 그만이야&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckvdTB/btr5deG5qIF/af1dx4wO0ZKxQ6GktFZrm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckvdTB%2Fbtr5deG5qIF%2Faf1dx4wO0ZKxQ6GktFZrm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;687&quot; height=&quot;459&quot; data-origin-width=&quot;687&quot; data-origin-height=&quot;459&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;응~ 죽으면 그만이야&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당장 와이파이 연결 안된 스마트폰으로 자기 PC의 내부 IP주소와 포트번호를 입력해서 접속해봐라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접속이 안된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부네트워크에서 접속요청을 할때에는 외부 아이피를 사용해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #99cefa;&quot;&gt;누구씨집(내부 IP)&lt;/span&gt; 이라고 하는게 아니라 &lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;ㅇㅇ시 ㅇㅇ구 ㅇㅇ동 ㅇㅇ아파트 ㅇㅇ동 ㅇㅇ호(외부 IP)&lt;/span&gt; 라고 말해야 정확하게 찾아갈 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-size=&quot;size14&quot; data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;포트번호 (Port)&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 &lt;b&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;포트번호&lt;/span&gt;&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;서버(Server)&lt;/span&gt;&lt;/b&gt;는 웹페이지를 보여주는 역할(웹 서버)만 할 수도 있고, FTP서버(PC와 서버와의 파일 송수신 프로그램)의 역할을 할 수도 있다. 아니면 도커를 이용하여 웹 서버를 여러개 가지고 있을 수도 있고ㅇㅇ&lt;/p&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;FTP 요청&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;바로 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;포트번호&lt;/span&gt;로 구분을 하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 아파트의 101동을 찾아와도 101호냐 102냐에 따라 다른 집이 나오는것 처럼&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버는 포트번호를 통해 웹서버 요청인지, FTP 요청인지를 구분할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;포트포워딩&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;게이트웨이 장치(공유기)를 설치하게 되면, 공유기와 연결된 서버(PC)들은 &lt;b&gt;192.168~ 로 시작하는 IP주소&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;그리고 공유기는 ISP 업체로부터 &lt;b&gt;123.456.789.00 이런식의 다른 IP주소&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;이 &lt;b&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;공유기가 받은 IP주소를 외부 아이피&lt;/span&gt;&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;공유기와 연결된 &lt;b&gt;&lt;span style=&quot;background-color: #99cefa;&quot;&gt;각 서버(PC)들의 IP주소를 내부 아이피&lt;/span&gt;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 공유기와 연결되지 않은 외부에서 내부 서버(PC)로 접속을 하려면 공유기의 주소(외부 아이피)와 어떤 서버로 접속할 것인지(포트번호)를 입력해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;ㅇㅇ시 ㅇㅇ구 ㅇㅇ동 ㅇㅇ아파트 ㅇㅇ동 &lt;span style=&quot;background-color: #99cefa;&quot;&gt;ㅇㅇ호&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;&lt;span style=&quot;background-color: #99cefa;&quot;&gt;&lt;span style=&quot;background-color: #ffc1c8;&quot;&gt;123.456.789.00&lt;/span&gt;:80&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 외부에서 80포트로의 요청이 오면 게이트웨이 장치(공유기)가 맨 처음 그 요청을 받게 되는데,&lt;/p&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1001&quot; data-origin-height=&quot;581&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NzQd5/btr5nH2w5UJ/EJGPSNcY2XeLbTHapx5MWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NzQd5/btr5nH2w5UJ/EJGPSNcY2XeLbTHapx5MWK/img.png&quot; data-alt=&quot;신입 게이트웨이 장치가 어리버리 타는 모습이다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NzQd5/btr5nH2w5UJ/EJGPSNcY2XeLbTHapx5MWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNzQd5%2Fbtr5nH2w5UJ%2FEJGPSNcY2XeLbTHapx5MWK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1001&quot; height=&quot;581&quot; data-origin-width=&quot;1001&quot; data-origin-height=&quot;581&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;신입 게이트웨이 장치가 어리버리 타는 모습이다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;게이트웨이 장치는 80포트로의 요청이 어느 서버로 연결해야 하는건지 모르는 상태이다!&lt;/p&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;size18&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이런 상태에서 게이트웨이 장치에게 &lt;b&gt;포트번호 마다 어디로 보내야 하는지 방향을 잡아주는 것&lt;/b&gt;이 &lt;span style=&quot;color: #ffffff; background-color: #8a3db6;&quot;&gt;&lt;b&gt;포트포워딩&lt;/b&gt;&lt;/span&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;101호로 요청이 오면 누구씨집으로 보내고, 102호로 요청이 오면 어쩌구씨 집으로 보내고...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 매뉴얼을 게이트웨이 장치에게 줘서, 외부네트워크에서 요청이 들어왔을 때 어느 내부 IP로 안내를 해야할지를 알게 해준다.&lt;/p&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;825&quot; data-origin-height=&quot;458&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/quZGO/btr5e5XE8zx/ZmKiirO0V0eJieayyre4e0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/quZGO/btr5e5XE8zx/ZmKiirO0V0eJieayyre4e0/img.png&quot; data-alt=&quot;매뉴얼(포트포워딩)을 받아 일을 잘하게 된 공유기의 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/quZGO/btr5e5XE8zx/ZmKiirO0V0eJieayyre4e0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FquZGO%2Fbtr5e5XE8zx%2FZmKiirO0V0eJieayyre4e0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;825&quot; height=&quot;458&quot; data-origin-width=&quot;825&quot; data-origin-height=&quot;458&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;매뉴얼(포트포워딩)을 받아 일을 잘하게 된 공유기의 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/기타 </category>
      <category>IP</category>
      <category>개발</category>
      <category>네트워크</category>
      <category>아이피</category>
      <category>포트포워딩</category>
      <author>duknock</author>
      <guid isPermaLink="true">https://duknock.tistory.com/27</guid>
      <comments>https://duknock.tistory.com/27#entry27comment</comments>
      <pubDate>Wed, 22 Mar 2023 15:13:23 +0900</pubDate>
    </item>
    <item>
      <title>[자바스크립트] 페이지네이션(Pagination) 구현</title>
      <link>https://duknock.tistory.com/26</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;s&gt;하 이건 진짜 하기 싫었는데 정리하긴 해야지...&lt;/s&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;게시판 같은거 보면 아래에 페이지 버튼이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p 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 style=&quot;text-align: left;&quot; 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;711&quot; data-origin-height=&quot;97&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byprAa/btr3dTSaYiX/wI421QFnGULuneKUD5cYik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byprAa/btr3dTSaYiX/wI421QFnGULuneKUD5cYik/img.png&quot; data-alt=&quot;네이버 메일의 페이지네이션 버튼&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byprAa/btr3dTSaYiX/wI421QFnGULuneKUD5cYik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyprAa%2Fbtr3dTSaYiX%2FwI421QFnGULuneKUD5cYik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;711&quot; height=&quot;97&quot; data-origin-width=&quot;711&quot; data-origin-height=&quot;97&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;네이버 메일의 페이지네이션 버튼&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얘는 &lt;b&gt;페이지네이션 (Pagination)&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;span style=&quot;background-color: #dddddd; color: #4d5156; text-align: left;&quot;&gt;콘텐츠를 여러 페이지로 나누어 다음 또는 이전 페이지로 이동하거나 특정 페이지로 이동할 수 있는 요소이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 얘를 만들어볼건데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&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;이번엔 자바스크립트의 Class 문법을 사용하여 만들어보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;설계&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 페이지 버튼을 누르면 해당 페이지가 URL 파라미터에 추가되며 페이지를 갱신한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;- (2 페이지를 클릭했을 경우 : duknock.tistory.com -&amp;gt; duknock.tistory.com?page=2)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. URL에 ?page=3 이런식으로 파라미터가 들어가있으면 버튼을 누르지 않아도 해당 페이지로 이동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; (1번과 같은 얘기)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 한 페이지에 보여줄 데이터의 양은 셀렉트 박스로 조절할 수 있다. 얘를 변경했을 때에도 페이지가 갱신된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - (10개씩보기, 20개씩보기 이런거)&lt;/p&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;4. &lt;b&gt;데이터를 한번에 모두 불러왔을때만 사용할 수 있게&lt;/b&gt; 설계했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - (보여줄 데이터의 총량을 알아야 페이지네이션 구현이 가능하다.)&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;물론 데이터 자체를 다 가져오지 않아도 데이터의 총 갯수만 따로 받을 수 있게 API를 구성하면 페이지네이션을 구현할 수 있다.&lt;br /&gt;그렇게 하려면 API를 요청할때 프론트단에서 page 값과 page에서 보여줄 데이터의 갯수 등의 정보를 파라미터에 담아서 보내면 백엔드단에서 요청에 맞게 데이터를 잘라서 보내주어야 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 페이지네이션 버튼은 한줄에 10개가 존재한다. (&amp;lt;&amp;lt;, &amp;gt;&amp;gt; 제외)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - (&amp;lt;&amp;lt;, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, &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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 현재 페이지가 항상 중간에 위치한다. 예를 들어 총 페이지수가 20페이지이고 현재페이지가 12일 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - (&amp;lt;&amp;lt;, 7, 8, 9, 10, 11, &lt;span style=&quot;color: #f3c000;&quot;&gt;12&lt;/span&gt;, 13, 14, 15, 16, &amp;gt;&amp;gt;) 이런식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 물론 현재 페이지 앞이나 뒤에 남은 페이지 수가 4장보다 적을 경우엔 그쪽으로 치우치게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 예시로 총 페이지수가 20페이지고 현재페이지가 18일 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;(&amp;lt;&amp;lt;, 12, 13, 14, 15,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;16&lt;/span&gt;, 17, &lt;span style=&quot;color: #f3c000;&quot;&gt;18&lt;/span&gt;, 19, 20, &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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Pagination.js&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1678619965392&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default class Pagination {
	constructor(data, itemsPerPage) {
		this.data = data;
		this.itemsPerPage = itemsPerPage;
		this.totalPages = Math.ceil(this.data.length / this.itemsPerPage);
		this.currentPage = 1;
		this.queryString = new URLSearchParams(window.location.search);
		if (this.queryString.has('page')) {
			this.currentPage = parseInt(this.queryString.get('page'));
		}
	}
}&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;일단 constructor 메서드로 클래스에 값을 할당해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;data&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 보여줄 데이터를 의미한다. 배열로 되어있으며, 게시판을 예시로 들자면 게시판의 글에 해당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;itemsPerPage&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 한 페이지에서 보여줄 데이터의 양을 의미한다. 아까 위에서 말한 셀렉트 박스의 value를 가져올것이다. (~개씩 보기)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;totalPages&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 총 페이지 수를 의미한다. &lt;b&gt;데이터의 수 / 페이지에 보여줄 데이터의 수&amp;nbsp;&lt;/b&gt;로 계산해서 올림한다. 예시로 데이터의 수가 52개이고 한 페이지에 10개씩 보여준다면 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;Math.ceil(52 / 10)&lt;/span&gt; 이 되어 총 6페이지가 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Math.ceil() 을 하는 이유는 데이터가 1개라도 있으면 페이지가 추가되기 때문&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;currentPage&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 현재 페이지를 의미한다. 디폴트로 1페이지가 설정되어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;queryString&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- URL 파라미터를 가져온다. 만약 URL 파라미터에 page가 있을 경우 &lt;b&gt;currentPage&lt;/b&gt;를 해당 page값으로 교체한다.&lt;/p&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;pre id=&quot;code_1678621795349&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;createPagination() {
		const paginationContainer = document.createElement('div');
		paginationContainer.classList.add('pagination');
		let startPage = 1;
		if (this.currentPage &amp;gt; 5) {
			startPage = this.currentPage - 5;
		}
		let endPage = startPage + 9;
		if (endPage &amp;gt; this.totalPages) {
			endPage = this.totalPages;
			startPage = this.totalPages - 9;
		}
		if (startPage &amp;lt; 1) {
			startPage = 1;
		}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이제 페이지네이션을 생성하는 메서드를 만들어보자! (은근 머리통 아프니 천천히 보자)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 페이지네이션 요소들을 담을 div를 생성하고&lt;/p&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;startPage(첫번째 페이지 버튼)&lt;/b&gt;를 1로 설정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 현재 페이지가 5보다 클 경우 startPage는 현재페이지 -5 로 변경한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(현재 페이지가 9일 경우 맨 첫번째버튼은 4가 된다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;endPage(마지막 페이지 버튼)&lt;/b&gt;는 startPage + 9 다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(그렇다면 아까 위에 말한것과 합쳐서 현재 페이지가 9일 경우 [4, 5, 6, 7, 8, &lt;span style=&quot;color: #f3c000;&quot;&gt;9&lt;/span&gt;, 10, 11, 12, 13] 이렇게 된다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, endPage&lt;span style=&quot;background-color: #dddddd;&quot;&gt;(startPage +9)&lt;/span&gt; 가 총 페이지 수보다 많을 경우! endPage는 총 페이지 수로 변경한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 startPage는 endPage -9 로 변경한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(현재페이지가 9이고 총 페이지 수가 11일 경우 [2, 3, 4, 5, 6, 7, 8, &lt;span style=&quot;color: #f3c000;&quot;&gt;9&lt;/span&gt;, 10, 11] 이렇게 된다.)&lt;/p&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;그리고 startPage가 1보다 작아지면 startPage는 1로 설정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(현재페이지가 3이고 전체 페이지 수가 5일 경우 [1, 2, &lt;span style=&quot;color: #f3c000;&quot;&gt;3&lt;/span&gt;, 4, 5] 이렇게 된다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;&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;pre id=&quot;code_1678622670736&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;		const goFirstButton = document.createElement('button');
		goFirstButton.innerText = '&amp;lt;&amp;lt;';
		goFirstButton.classList.add('page-btn');
		goFirstButton.disabled = this.currentPage === 1;
		goFirstButton.addEventListener('click', () =&amp;gt; {
			let url = new URL(window.location.href);
			url.searchParams.set('page', 1);
			window.location.href = url.toString();
		});
		paginationContainer.appendChild(goFirstButton);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맨 처음으로 가는 버튼을 만들어준다. [&amp;lt;&amp;lt;]&lt;/p&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;단, 이미 현재 페이지가 1이라면 disabled 처리된다.&lt;/p&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;이 버튼을 클릭하면 URL의 page 파라미터 값을 1로 설정하고 페이지를 갱신한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1678622826318&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for (let i = startPage; i &amp;lt;= endPage; i++) {
			const paginationButton = document.createElement('button');
			paginationButton.innerText = i;
			paginationButton.classList.add('page-btn');
			paginationButton.addEventListener('click', () =&amp;gt; {
				let url = new URL(window.location.href);
				let pageValue = i;
				let pageParam = 'page';
				if (!url.searchParams.has(pageParam)) {
					url.searchParams.append(pageParam, pageValue);
				} else {
					url.searchParams.set(pageParam, pageValue);
				}
				window.location.href = url.toString();
			});
			if (i === this.currentPage) {
				paginationButton.disabled = true;
			}
			paginationContainer.appendChild(paginationButton);
		}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아까 구한 startPage부터 endPage 까지 반복문을 돌린다.&lt;/p&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연히 해당 페이지 버튼들을 누르면 URL의 page 파라미터를 해당 페이지 값으로 바꾸고 페이지 갱신...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 URL에 page 라는 파라미터 자체가 없을 경우 page 파라미터를 추가한다.&lt;/p&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;그리고 currentPage가 해당 버튼의 값과 일치 할 경우, disabled 처리한다.&lt;/p&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;pre id=&quot;code_1678622995505&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const goLastButton = document.createElement('button');
		goLastButton.innerText = '&amp;gt;&amp;gt;';
		goLastButton.classList.add('page-btn');
		goLastButton.disabled = this.currentPage === this.totalPages;
		goLastButton.addEventListener('click', () =&amp;gt; {
			let url = new URL(window.location.href);
			url.searchParams.set('page', this.totalPages);
			window.location.href = url.toString();
		});
		paginationContainer.appendChild(goLastButton);

		return paginationContainer;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 페이지가 이미 총 페이지 수(즉, 마지막 페이지)와 같을 경우 disabled 처리한다.&lt;/p&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 생성한 요소들이 전부 paginationContainer 에 append 되었으니&lt;/p&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;마지막으로 paginationContainer를 return 하면 끝!&lt;/p&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;pre id=&quot;code_1678623147730&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default class Pagination {
	constructor(data, itemsPerPage) {
		this.data = data;
		this.itemsPerPage = itemsPerPage;
		this.totalPages = Math.ceil(this.data.length / this.itemsPerPage);
		this.currentPage = 1;
		this.queryString = new URLSearchParams(window.location.search);
		if (this.queryString.has('page')) {
			this.currentPage = parseInt(this.queryString.get('page'));
		}
	}

	createPagination() {
		const paginationContainer = document.createElement('div');
		paginationContainer.classList.add('pagination');
		let startPage = 1;
		if (this.currentPage &amp;gt; 5) {
			startPage = this.currentPage - 5;
		}
		let endPage = startPage + 9;
		if (endPage &amp;gt; this.totalPages) {
			endPage = this.totalPages;
			startPage = this.totalPages - 9;
		}
		if (startPage &amp;lt; 1) {
			startPage = 1;
		}

		const goFirstButton = document.createElement('button');
		goFirstButton.innerText = '&amp;lt;&amp;lt;';
		goFirstButton.classList.add('page-btn');
		goFirstButton.disabled = this.currentPage === 1;
		goFirstButton.addEventListener('click', () =&amp;gt; {
			let url = new URL(window.location.href);
			url.searchParams.set('page', 1);
			window.location.href = url.toString();
		});
		paginationContainer.appendChild(goFirstButton);

		for (let i = startPage; i &amp;lt;= endPage; i++) {
			const paginationButton = document.createElement('button');
			paginationButton.innerText = i;
			paginationButton.classList.add('page-btn');
			paginationButton.addEventListener('click', () =&amp;gt; {
				let url = new URL(window.location.href);
				let pageValue = i;
				let pageParam = 'page';
				if (!url.searchParams.has(pageParam)) {
					url.searchParams.append(pageParam, pageValue);
				} else {
					url.searchParams.set(pageParam, pageValue);
				}
				window.location.href = url.toString();
			});
			if (i === this.currentPage) {
				paginationButton.disabled = true;
			}
			paginationContainer.appendChild(paginationButton);
		}

		const goLastButton = document.createElement('button');
		goLastButton.innerText = '&amp;gt;&amp;gt;';
		goLastButton.classList.add('page-btn');
		goLastButton.disabled = this.currentPage === this.totalPages;
		goLastButton.addEventListener('click', () =&amp;gt; {
			let url = new URL(window.location.href);
			url.searchParams.set('page', this.totalPages);
			window.location.href = url.toString();
		});
		paginationContainer.appendChild(goLastButton);

		return paginationContainer;
	}
}&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;/p&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_1678623289231&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import Pagination from '/pagination.js';

let dataArray = [a,b,c,d,e,f,g,h....]; //대충 데이터 몇백개
let itemsPerPage = 10;
let paging = new Pagination(dataArray, itemsPerPage);
let pagination = paging.createPagination();
document.querySelector('.box').append(pagination);&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;import 로 아까 만들어둔 페이지네이션 모듈을 가져와서&lt;/p&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;new (생성자 메서드)로 Pagination을 생성해주고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.createPagination 메서드로 페이지네이션 요소를 생성해주면 끝!&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;683&quot; data-origin-height=&quot;120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dRcI0V/btr3iJgU5El/8fqdivBq5W9dNaWERUp2fK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dRcI0V/btr3iJgU5El/8fqdivBq5W9dNaWERUp2fK/img.png&quot; data-alt=&quot;이렇게 잘 만들어졌습니다~&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dRcI0V/btr3iJgU5El/8fqdivBq5W9dNaWERUp2fK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdRcI0V%2Fbtr3iJgU5El%2F8fqdivBq5W9dNaWERUp2fK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;683&quot; height=&quot;120&quot; data-origin-width=&quot;683&quot; data-origin-height=&quot;120&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이렇게 잘 만들어졌습니다~&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;802&quot; data-origin-height=&quot;225&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZcrNS/btr3fsNi4kf/ozgRCiMvi4UoIPDZ2kGnE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZcrNS/btr3fsNi4kf/ozgRCiMvi4UoIPDZ2kGnE1/img.png&quot; data-alt=&quot;5 페이지를 클릭했을 때 URL에도 page=5 로 잘 표시된다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZcrNS/btr3fsNi4kf/ozgRCiMvi4UoIPDZ2kGnE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZcrNS%2Fbtr3fsNi4kf%2FozgRCiMvi4UoIPDZ2kGnE1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;802&quot; height=&quot;225&quot; data-origin-width=&quot;802&quot; data-origin-height=&quot;225&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;5 페이지를 클릭했을 때 URL에도 page=5 로 잘 표시된다&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;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;마무리&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지네이션은 사용할 곳이 많으니 한번 제대로 만들어두고 계속 재활용하자~&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아, 페이지네이션 버튼 만드는 함수는 알려줬으니 해당 페이지에 데이터 보여주는건 알아서 하시길... &lt;/p&gt;</description>
      <category>개발/Javascript</category>
      <category>frontend</category>
      <category>JavaScript</category>
      <category>js</category>
      <category>pagination</category>
      <category>VanillaJS</category>
      <category>바닐라JS</category>
      <category>자바스크립트</category>
      <category>페이지네이션</category>
      <category>페이징</category>
      <category>프론트엔드</category>
      <author>duknock</author>
      <guid isPermaLink="true">https://duknock.tistory.com/26</guid>
      <comments>https://duknock.tistory.com/26#entry26comment</comments>
      <pubDate>Sun, 12 Mar 2023 21:32:49 +0900</pubDate>
    </item>
    <item>
      <title>[Node.js]node-ssh 모듈을 이용하여 리눅스 서버에 명령어 보내기</title>
      <link>https://duknock.tistory.com/25</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 진행중인 프로젝트에 &lt;b&gt;웹페이지에서 리눅스 서버로 명령어를 보내는 프롬프트 창&lt;/b&gt;을 만드는 과제가 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세상에 하다하다 웹페이지에서 리눅스서버로 명령어를 보내는것도 만들어야하다니... &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 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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&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;1. 웹페이지에서 Node.js 웹서버로 커맨드와 서버 url, 포트번호등을 담은 POST 요청을 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Node.js 웹서버가 ssh 접속 요청이 들어오면 따로 정의해둔 라우터로 라우팅한다. (다른 api 요청도 처리해야해서 분리함)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 라우터에서 &lt;b&gt;node-ssh 모듈&lt;/b&gt;을 이용하여 리눅스 서버와 연결 후 커맨드를 입력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 리눅스 서버가 응답을 보내면 그대로 웹서버로 전달해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 지금까지 구현해본건 API 요청을 라우팅하는 정도밖에 없어서 SSH 접속 요청도 API 요청 처리와 비슷하게 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;(Node.js 도 초보인데 리눅스는 만져본적도 없으니 그냥 되는대로 만들었다ㅎㅎ)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;웹페이지&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2880&quot; data-origin-height=&quot;603&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cDTDDW/btr1XyBnk1m/gWxkDnd5lh3rcXFnxq4zRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cDTDDW/btr1XyBnk1m/gWxkDnd5lh3rcXFnxq4zRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cDTDDW/btr1XyBnk1m/gWxkDnd5lh3rcXFnxq4zRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcDTDDW%2Fbtr1XyBnk1m%2FgWxkDnd5lh3rcXFnxq4zRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;2880&quot; height=&quot;603&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2880&quot; data-origin-height=&quot;603&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;pre id=&quot;code_1677997857430&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;document.querySelector('#sshTest').addEventListener('click', () =&amp;gt; {
	const cmd = document.querySelector('#sshCommand').value;
	const host = document.querySelector('#host').value;
	const userName = document.querySelector('#userName').value;
	const port = document.querySelector('#port').value;
	const logClearChecked = document.querySelector('#logClear').checked;
	if (logClearChecked) {
		document.querySelector('#result pre').innerHTML = '';
	}
	fetch(`/ssh/ssh`, {
		method: 'POST',
		headers: {
			'Content-Type': 'application/json',
		},
		body: JSON.stringify({
			'command': cmd,
			'host': host,
			'userName': userName,
			'port': port
		}),
	}).then((res) =&amp;gt; res.text())
	.then((data) =&amp;gt; {
		document.querySelector('#result pre').innerHTML += data;
		document.querySelector('#result pre').innerHTML += '\n-----------------------------------------------------------------\n';
	})
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 리눅스 서버로 요청을 보낼 수 도 있으니 &lt;b&gt;Host name, User Name, Port&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;SSH 테스트 버튼을 누르면&amp;nbsp; Input 데이터들을 객체로 묶어서 &quot;/ssh/ssh&quot; URL로 POST 요청을 보낸다.&lt;/p&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;div&gt;
&lt;div&gt;&lt;span style=&quot;color: #5fb3b3;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #eb606b;&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;color: #5fb3b3;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #bb80b3;&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color: #5fb3b3;&quot;&gt;=&quot;&lt;/span&gt;&lt;span style=&quot;color: #99c794;&quot;&gt;logClear&lt;/span&gt;&lt;span style=&quot;color: #5fb3b3;&quot;&gt;&quot; &lt;/span&gt;&lt;span style=&quot;color: #bb80b3;&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color: #5fb3b3;&quot;&gt;=&quot;&lt;/span&gt;&lt;span style=&quot;color: #99c794;&quot;&gt;checkbox&lt;/span&gt;&lt;span style=&quot;color: #5fb3b3;&quot;&gt;&quot;&amp;gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얘가 체크되어 있으면 &amp;lt;pre&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;응답이 오면 res.text() 메서드를 이용하여 응답데이터를 문자열로 받은 다음,&lt;/p&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;lt;pre&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Node.js 웹서버 (server.js)&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1677999741050&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const express = require('express');
const app = express();

app.use('/ssh/ssh', require('./ssh/ssh'));

...&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;모든 서버로의 요청을 처리하는 메인 server.js 파일이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;server.js 에 ssh 접속 요청을 처리하는 로직을 작성하면 &lt;span style=&quot;color: #ee2323;&quot;&gt;너무 코드가 길어지기 때문에&lt;/span&gt;, 라우팅을 이용하여 분리하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;/ssh/ssh&quot; URL로의 요청이 들어오면 라우팅하여 &quot;./ssh/ssh&quot; 경로의 파일이 처리하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&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;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;ssh.js (라우터)&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얘가 실제로 ssh 접속과 커맨드 입력 요청을 보내는 애다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1678000027248&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const express = require('express');
const router = express.Router();
const fs = require('fs');
router.use(express.json());
router.use(express.urlencoded({ extended: true }));

const { NodeSSH } = require('node-ssh');

router.post('/', function (req, res) {
	let body = req['body'];
	const keyPath = __dirname + `/secret/${body['userName']}.pem`;
	const privateKey = fs.readFileSync(keyPath, 'utf8');
	const ssh = new NodeSSH();
	ssh.connect({
		host: body['host'],
		username: body['userName'],
		port: body['port'],
		privateKey: privateKey,
		timeout: 5000, // ms
	})
		.then(() =&amp;gt; {
			console.log('connected');

			ssh.execCommand(body['command'], {}).then(function (result) {
				// console.log('결과: ' + result.stdout);
				// console.log('에러: ' + result.stderr);
				ssh.dispose(); //커넥션 종료
				res.send(result.stdout);
			});
		})
		.catch(function (error) {
			console.error('Connection failed: ', error);
			res.status(500);
			res.send();
		});
});

module.exports = router;&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;여기서 node-ssh 모듈을 이용하는데 얘는 미리 npm에서 설치해야한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;npm install node-ssh&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아까 server.js가 라우팅을 걸었으므로 해당 URL(&quot;/ssh/ssh&quot;)로 post 요청이 들어왔을 경우 처리할 로직을&lt;b&gt; router.post&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;여기서 keyPath, privateKey라는 이상한 놈이 있을텐데, 얘는 SSH 접속을 할때 필요한 프라이빗 키를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 물리서버를 이용한다면 PEM 키가 아닌 패스워드를 입력해서 로그인하겠지만, 클라우드서버를 이용한다면 아마 리눅스 인스턴스에 접속하기 위해 PEM 키를 사용할 것이다. 아님 말고^^&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;PEM 키란? &lt;br /&gt;SSH를 구성하는 가장 핵심적인 키워드는 &amp;lsquo;KEY(키, 열쇠)&amp;rsquo;입니다. 사용자(클라이언트)와 서버(호스트)는 각각의 키를 보유하고 있으며, 이 키를 이용해 연결 상대를 인증하고 안전하게 데이터를 주고 받게 됩니다. 여기서 키를 생성하는 방식이 두 가지가 있는데, 그것이 SSH를 검색했을 때 가장 쉽게 볼 수 있는 &amp;lsquo;대칭키&amp;rsquo;와 &amp;lsquo;비대칭키(또는 공개 키)&amp;rsquo; 방식입니다.&lt;br /&gt;&amp;ndash; 비대칭키 방식&lt;br /&gt;작동하는 순서를 하나하나씩 짚어보도록 하겠습니다. 가장 먼저 사용자와 서버가 서로의 정체를 증명해야 합니다. 이 시점에서 사용되는 것이 비대칭키 방식입니다. 비대칭키 방식에서는 서버 또는 사용자가 Key Pair(키 페어, 키 쌍)를 생성합니다. 키 페어는 공개 키와 개인 키의 두 가지로 이루어진 한 쌍을 뜻하며, 보통 공개 키의 경우 .pub, 개인 키의 경우 .pem의 파일 형식을 띄고 있습니다.&lt;/blockquote&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size14&quot;&gt;출처 : &lt;a href=&quot;https://library.gabia.com/contents/infrahosting/9002/&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭐 아무튼 보안을 위한 키인데, 얘를 같이 보내야 리눅스 서버에서 얘가 접속해도 되는 애인지 아닌지를 판단한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&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;아무튼 나는 이 PEM 키를 서버에 secret이라는 폴더에 올려놓고 라우터에서 불러와 사용하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 server.js 에서 static을 이용하여 secret이라는 폴더에는 임의로 접근할 수 없도록 설정해두었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;그래도 보안상 좋은 방법 같지는 않지만 다른 방법은 당장 생각나는게 없기도 하고, 내가 만드는 페이지는 회사 내부에서만 쓸거라....보안 이슈가 걱정된다면 다른 방법을 찾아보세용&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 &lt;b&gt;new NodeSSH()&lt;/b&gt; 로 ssh 인스턴스를 생성해주고, &lt;b&gt;ssh.connect()&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;옵션에는 host, username, port ,privateKey, timeout(ms 단위) 를 주고 요청을 보낸다.&lt;/p&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 메서드랑 비슷하다.&lt;/p&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;.then()&lt;/b&gt; 메서드가 실행되는데 (Promise 기반이라 then을 사용한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1678001765588&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ssh.execCommand(body['command'], {})
.then(function (result) {
	console.log('결과: ' + result.stdout);
	console.log('에러: ' + result.stderr);
	ssh.dispose(); //커넥션 종료
	res.send(result.stdout);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ssh.execCommand()&lt;/b&gt; 메서드로 아까 웹페이지가 보낸 command를 보낸다. 그럼 응답이 오겠지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답을 result 객체로 받아서 결과값이 나오면 &lt;b&gt;result.stdout&lt;/b&gt; 에 들어가있을거고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러가 났다면 &lt;b&gt;result.stderr&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;그리고 &lt;b&gt;ssh.dispose()&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;그리고 &lt;b&gt;res.send()&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;근데 이렇게 하면 웹페이지에서 커맨드 요청을 보낼때마다 접속하고 접속끊고를 반복해서, 연속적인 응답은 받을 수 없다.&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;ssh.dispose()&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;마무리&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 node-ssh 모듈을 사용하여 Node.js 서버에서 리눅스 인스턴스로 접속하여 커맨드를 보내보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여전히 위 코드에서도 해결할 점은 많지만 그래도 정상적으로 작동하니 다행....&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추후에는 PEM키를 서버에 올려버리는 보안적인 문제도 해결하고 SSH 커넥션을 지속적으로 유지하는 방법을 찾아봐야겠다.&lt;/p&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>개발/Node.js</category>
      <category>JavaScript</category>
      <category>node-ssh</category>
      <category>Nodejs</category>
      <category>SSH</category>
      <category>개발</category>
      <category>리눅스</category>
      <category>백엔드</category>
      <category>웹개발</category>
      <category>자바스크립트</category>
      <category>프론트엔드</category>
      <author>duknock</author>
      <guid isPermaLink="true">https://duknock.tistory.com/25</guid>
      <comments>https://duknock.tistory.com/25#entry25comment</comments>
      <pubDate>Sun, 5 Mar 2023 16:47:56 +0900</pubDate>
    </item>
    <item>
      <title>[자바스크립트] window.open()으로 새 창 열기</title>
      <link>https://duknock.tistory.com/24</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트로 새 창을 띄우는 방법에는 window.open() 메서드가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1665105451388&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;window.open(url, name, specs, replace)&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;pre id=&quot;code_1665105890737&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let url = 'https://duknock.tistory.com';
let name = 'popup';
let specs = 'width=720, height=500, top=100, left=200';
let replace = false;
window.open(url, name, specs, replace);&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;&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;size18&quot;&gt;&lt;b&gt;return&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;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1665105543037&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let newWindow = window.open(url, name, specs, replace);&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;이렇게 변수에 할당하면 자바스크립트에서 해당 창 객체를 제어할 수 있다. newWindow.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;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;URL&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&quot;&gt;window.open(&lt;span style=&quot;color: #333333; background-color: #99cefa;&quot;&gt;&lt;b&gt;url&lt;/b&gt;&lt;/span&gt;, name, specs, replace);&lt;/p&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;b&gt;url&lt;/b&gt;을 입력하면 해당 주소의 팝업이 뜬다. 선택적인 값으로, 비워두면 빈창(about:blank)이 뜬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;name&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&quot;&gt;window.open(&lt;span style=&quot;color: #333333;&quot;&gt;url&lt;/span&gt;, &lt;span style=&quot;color: #333333; background-color: #99cefa;&quot;&gt;&lt;b&gt;name&lt;/b&gt;&lt;/span&gt;, specs, replace);&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;새로 열릴 참의 속성 또는 창의 이름을 지정한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;입력 안 할 경우 기본값은 &quot;_blank&quot;&amp;nbsp; 이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;name&lt;/b&gt;이 &quot;_blank&quot;인 상태로 여러 번 open()을 하면 계속 또 다른 새 창이 열린다.&amp;nbsp;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;name값&lt;/b&gt;을 임의로 입력하고, 동일한 이름에 다시 open() 을 하면 기존 열린창의 내용이 바뀐다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;다른 이름을 사용하면 또다른 새창이 열린다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;specs&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&quot;&gt;window.open(url,&amp;nbsp;&lt;span style=&quot;color: #333333;&quot;&gt;name&lt;/span&gt;, &lt;span style=&quot;background-color: #99cefa;&quot;&gt;&lt;b&gt;specs&lt;/b&gt;&lt;/span&gt;, replace);&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;spec&lt;/b&gt;은 창의 너비, 높이, 화면상의 위치 등의 속성을 지정한다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;- width : 창의 너비 (px)&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;- height : 창의 높이 (px)&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;- left : 왼쪽 기준 새 창이 열릴 위치 (px)&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;- top : 위쪽 기준 새 창이 열릴 위치 (px)&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;replace&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;window.open(url,&amp;nbsp;name,&amp;nbsp;specs,&amp;nbsp;&lt;span style=&quot;background-color: #99cefa;&quot;&gt;replace&lt;/span&gt;)&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;replace는 히스토리에 새 항목을 만들지, 기존 항목을 대체할지 여부를 결정한다. 불리언 값을 넣는다. (true, false)&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;- true : 현재 히스토리를 대체한다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;- false : 새 히스토리 항목을 만든다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이렇게 window.open()으로 열린 창과 기존 창 간에는 계층구조가 생기게 되는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 창은 부모 창이 되고, 열린 창은 자식 창이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아까 window.open()을 변수에 할당해서 제어하는 방법이 &lt;b&gt;부모창에서 자식창을 제어&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;자식창에서는 &lt;b&gt;window.opener&lt;/b&gt; 속성을 이용하여 &lt;b&gt;자식창에서 부모창을 제어&lt;/b&gt;할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;(window는 생략하고 opener 만 써도 됨)&lt;/p&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;size18&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;b&gt;return&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;A&lt;/span&gt; 창에서 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;B&lt;/span&gt;를 열었다면 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;B.opener&lt;/span&gt; 는 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;A&lt;/span&gt;를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 창이 window.open() 으로 열린 창이 아니라면, &lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;null&lt;/span&gt;&lt;/b&gt;을 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/Javascript</category>
      <category>JavaScript</category>
      <category>js</category>
      <category>window.open</category>
      <category>바닐라JS</category>
      <category>새창</category>
      <category>자바스크립트</category>
      <category>팝업</category>
      <category>프론트엔드</category>
      <author>duknock</author>
      <guid isPermaLink="true">https://duknock.tistory.com/24</guid>
      <comments>https://duknock.tistory.com/24#entry24comment</comments>
      <pubDate>Fri, 7 Oct 2022 10:59:35 +0900</pubDate>
    </item>
    <item>
      <title>URL의 구조 (/, ?, # 이게 다 뭐야)</title>
      <link>https://duknock.tistory.com/23</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffc9af;&quot;&gt;https:&lt;/span&gt;//&lt;span style=&quot;background-color: #f6e199;&quot;&gt;duknock.tistory.com&lt;/span&gt;/&lt;span style=&quot;background-color: #9feec3;&quot;&gt;page.html&lt;/span&gt;?&lt;span style=&quot;background-color: #99cefa;&quot;&gt;type=post&lt;/span&gt;#&lt;span style=&quot;background-color: #c1bef9;&quot;&gt;top&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;background-color: #c1bef9;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt;Protocol&lt;/span&gt;://&lt;span style=&quot;background-color: #f6e199;&quot;&gt;Host&lt;/span&gt;/&lt;span style=&quot;background-color: #9feec3;&quot;&gt;Path&lt;/span&gt;?&lt;span style=&quot;background-color: #99cefa;&quot;&gt;QueryString&lt;/span&gt;#&lt;span style=&quot;background-color: #c1bef9;&quot;&gt;Fragment&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;주소창의 URL을 보면 이런식으로 되어있다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;1. 프로토콜&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;2. 호스트:포트번호&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;3. 경로&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;4. 쿼리스트링&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;5. 프래그먼트(앵커)&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p 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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;프로토콜&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #ffc9af;&quot;&gt;https:&lt;/span&gt;//duknock.tistory.com/page.html?type=post#top&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;프로토콜은 컴퓨터끼리 네트워크 통신을 하기 위한 규약이다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;우리가 웹 페이지의 주소를 표현할 때는 &lt;b&gt;http&lt;/b&gt; (&lt;b&gt;H&lt;/b&gt;yper&lt;b&gt;T&lt;/b&gt;ext &lt;b&gt;T&lt;/b&gt;ransfer &lt;b&gt;P&lt;/b&gt;rotocol)를 사용한다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;http에 보안이 추가된 &lt;b&gt;https&lt;/b&gt;도 있다. https나 http나 동일한 규격임ㅇㅇ&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;http, https 외에도 ftp(파일서버 접속)나 telnet(원격 접속)등이 있다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p 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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;호스트:포트번호&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;https:&lt;/span&gt;//&lt;span style=&quot;background-color: #f6e199;&quot;&gt;duknock.tistory.com&lt;/span&gt;/page.html?type=post#top&lt;/h2&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;span style=&quot;color: #666666;&quot;&gt;웹 페이지를 요청할 서버의 주소다. 사실 상 집 주소나 같은 것&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스트는 호스트명과 도메인으로 이루어져있는데, &lt;span style=&quot;background-color: #dddddd;&quot;&gt;duknock이 호스트&lt;/span&gt;이고 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;tistory가 도메인&lt;/span&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 아파트 동호수에 비유하자면, 도메인은 동을 뜻하고, 호스트는 호수에 비할 수 있겠다. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;(tistory동의 duknock호)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &lt;b&gt;포트번호&lt;/b&gt;라는게&amp;nbsp;있는데, &lt;span style=&quot;color: #1b1b1b;&quot;&gt;웹서버에서 자원을 접근하기 위해 사용하는 관문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #1b1b1b;&quot;&gt;가정집에 문 여러개 달아두고 하나는 정문, 하나는 차고로 가는 문, 하나는 반려동물용 문 뭐 이런식으로 나눠둔거랑 비슷하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #1b1b1b;&quot;&gt;근데 우리가 쓰는 웹 URL을 보면 포트번호가 보이지 않는다. 위의 예시만 봐도 포트번호는 눈을 씻고 찾아봐도 없다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;기본적으로 &lt;b&gt;포트번호를 입력하지 않았을 때는 프로토콜이 가지고 있는 기본 포트번호&lt;/b&gt;가 적용되어 생략 가능하기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;http의 경우 80번, https의 경우 443번의 포트번호가 기본으로 적용된다.&lt;/p&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;경로&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;https:&lt;/span&gt;//duknock.tistory.com/&lt;span style=&quot;background-color: #9feec3;&quot;&gt;page.html&lt;/span&gt;?type=post#top&lt;/h2&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;span style=&quot;background-color: #ffffff; color: #666666;&quot;&gt;웹서버 프로그램 내에 짜인 로직으로 가는 &lt;span style=&quot;background-color: #ffffff; color: #666666;&quot;&gt;자원에 대한 경로&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;로컬 컴퓨터의 경로와 똑같다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;예시로 경로가 /pages/page1.html 이면 page라는 폴더 안에 page1.html 페이지를 가리킨다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;쿼리스트링&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;https:&lt;/span&gt;//duknock.tistory.com/page.html?&lt;span style=&quot;background-color: #99cefa;&quot;&gt;type=post&lt;/span&gt;#top&lt;/h2&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;span&gt;쿼리스트링은 서버에 추가로 보내는&lt;span&gt; 파라미터다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;주로, 같은 경로라 하더라도&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;입력한 값&lt;/b&gt;에 따라 다른 결과를 보여줘야할 때 쿼리 문자열을 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;(검색엔진 등에서 검색 결과를 보여주는 경우에 검색어를 전달하는 용도로 많이 쓰임.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;만약 여러개의 데이터를 보낸다면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&amp;amp;&lt;/b&gt;문자로 구분한다. (?key1=value1&amp;amp;key2=value2)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;프래그먼트(앵커)&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;https:&lt;/span&gt;//duknock.tistory.com/page.html?type=post#&lt;span style=&quot;background-color: #c1bef9;&quot;&gt;top&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프래그먼트(앵커)는 &lt;span style=&quot;background-color: #ffffff; color: #666666;&quot;&gt;URL이 지정하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;자원의 세부 부분&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #666666;&quot;&gt;을 지정할때 쓰인다. 책갈피 같은거다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;얘는 없어도 페이지가 잘 나온다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 위키백과 같은 곳에서 특정 항목을 정확히 가리킬 때 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/URL#%ED%91%9C%ED%98%84%EB%B0%A9%EB%B2%95&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ko.wikipedia.org/wiki/URL#표현방법&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;위 링크를 들어가보면 URL 항목의 &lt;b&gt;표현방법 단락으로 바로 이동&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 외)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Q : URL이 막 https://ko.wikipedia.org/wiki/URL#%ED%91%9C%ED%98%84%EB%B0%A9%EB%B2%95&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; 이런식으로 나오는데 저 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;%랑 숫자랑 알파벳 조합들은 뭐에요?&lt;/b&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;A : 한글이 깨진겁니다. 인터넷을 통해 전송할 수 있는 문자는 오직 ASCII 문자밖에 없거든요. 그래서 강제로 인코딩 된겁니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;b&gt;끝!&lt;/b&gt;&lt;/p&gt;</description>
      <category>개발/기타 </category>
      <category>url</category>
      <category>웹</category>
      <category>웹주소</category>
      <category>인터넷</category>
      <category>쿼리스트링</category>
      <category>프로토콜</category>
      <category>호스트</category>
      <author>duknock</author>
      <guid isPermaLink="true">https://duknock.tistory.com/23</guid>
      <comments>https://duknock.tistory.com/23#entry23comment</comments>
      <pubDate>Wed, 5 Oct 2022 12:01:02 +0900</pubDate>
    </item>
  </channel>
</rss>