<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>pinkcamel 님의 블로그</title>
    <link>https://pinkcamel.tistory.com/</link>
    <description>pinkcamel 님의 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Wed, 15 Jul 2026 12:06:48 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>pinkcamel</managingEditor>
    <item>
      <title>다섯 번째 사담콘 참여후기</title>
      <link>https://pinkcamel.tistory.com/6</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2803&quot; data-origin-height=&quot;1383&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9xKwm/dJMcaiquwhO/4Nb8Sq4v7c3C1ECbTVbXxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9xKwm/dJMcaiquwhO/4Nb8Sq4v7c3C1ECbTVbXxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9xKwm/dJMcaiquwhO/4Nb8Sq4v7c3C1ECbTVbXxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9xKwm%2FdJMcaiquwhO%2F4Nb8Sq4v7c3C1ECbTVbXxk%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;2803&quot; height=&quot;1383&quot; data-origin-width=&quot;2803&quot; data-origin-height=&quot;1383&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;거제 ㅇ...가 아니라&lt;br /&gt;&lt;br /&gt;사담콘~ 야호~&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1254&quot; data-origin-height=&quot;1254&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dHlIkG/dJMcabZhGQ1/kKmcLxVhX3r1lPQFXDEock/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dHlIkG/dJMcabZhGQ1/kKmcLxVhX3r1lPQFXDEock/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dHlIkG/dJMcabZhGQ1/kKmcLxVhX3r1lPQFXDEock/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdHlIkG%2FdJMcabZhGQ1%2FkKmcLxVhX3r1lPQFXDEock%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;1254&quot; height=&quot;1254&quot; data-origin-width=&quot;1254&quot; data-origin-height=&quot;1254&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역시 개발자는 파라파라 추지말고 말고 파라미터나 줘야지~&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI AI~ 오이데 ~ 오이데 ~&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bErxsZ/dJMcabkGpzC/Hm15lZQIB9OXOocLeqr3Lk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bErxsZ/dJMcabkGpzC/Hm15lZQIB9OXOocLeqr3Lk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bErxsZ/dJMcabkGpzC/Hm15lZQIB9OXOocLeqr3Lk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbErxsZ%2FdJMcabkGpzC%2FHm15lZQIB9OXOocLeqr3Lk%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;960&quot; height=&quot;540&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://5th-sadamcon.sipe.team/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://5th-sadamcon.sipe.team/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1784036364814&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;SIPE 다섯 번째 컨퍼런스 - 사담콘 야호, 파라미터나 줘야지&quot; data-og-description=&quot;2026년 7월 11일 토요일, 서울 마포구 마포대로 122 5층 박병원홀에서 열리는 SIPE 다섯 번째 컨퍼런스 사담콘에 참여하세요.&quot; data-og-host=&quot;5th-sadamcon.sipe.team&quot; data-og-source-url=&quot;https://5th-sadamcon.sipe.team/&quot; data-og-url=&quot;https://5th-sadamcon.sipe.team&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/FokkL/dJMb896f34X/WHJ9Fup2rJJFjarHLf3d1K/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/ctr4zU/dJMb87giQPa/jqUGQ3XguqjwCdGScwSfA0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/ZWC9n/dJMb86Pd5FV/we91U1LUjgBiBBxuXfpQK0/img.jpg?width=1200&amp;amp;height=1200&amp;amp;face=609_388_717_506&quot;&gt;&lt;a href=&quot;https://5th-sadamcon.sipe.team/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://5th-sadamcon.sipe.team/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/FokkL/dJMb896f34X/WHJ9Fup2rJJFjarHLf3d1K/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/ctr4zU/dJMb87giQPa/jqUGQ3XguqjwCdGScwSfA0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/ZWC9n/dJMb86Pd5FV/we91U1LUjgBiBBxuXfpQK0/img.jpg?width=1200&amp;amp;height=1200&amp;amp;face=609_388_717_506');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;SIPE 다섯 번째 컨퍼런스 - 사담콘 야호, 파라미터나 줘야지&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;2026년 7월 11일 토요일, 서울 마포구 마포대로 122 5층 박병원홀에서 열리는 SIPE 다섯 번째 컨퍼런스 사담콘에 참여하세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;5th-sadamcon.sipe.team&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 주말 사담콘이라는 개발자 IT 동아리의 세미나에 참석했다.&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;s&gt;이 정도는 현대 시대의 필수 교양으로 안원잘부를 보고 꼭 배워야한다고 생각한다.&lt;/s&gt;&lt;/p&gt;
&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;일단 내가 개발자를 하는 가장 큰 이유 중에 하나는 &quot;서로 함께 성장하는 문화&quot;이다.&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;그리고 결정적으로 3개의 발표가 나의 호기심을 강하게 자극했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1594&quot; data-origin-height=&quot;1285&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dne3by/dJMb991rKIw/nbxKV65k5JS6bhkoxApI6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dne3by/dJMb991rKIw/nbxKV65k5JS6bhkoxApI6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dne3by/dJMb991rKIw/nbxKV65k5JS6bhkoxApI6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdne3by%2FdJMb991rKIw%2FnbxKV65k5JS6bhkoxApI6K%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;1594&quot; height=&quot;1285&quot; data-origin-width=&quot;1594&quot; data-origin-height=&quot;1285&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 &lt;b&gt;우창완&lt;/b&gt;님의 &quot;모던 프론트 엔드 라이브러리 패키징 가이드&quot;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 JS를 제대로 배울 때 토스팀에서 아래와 같은 블로그를 읽었었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://toss.tech/article/commonjs-esm-exports-field&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://toss.tech/article/commonjs-esm-exports-field&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 어떻게 CommonJS와 ESM 모두에 대응하는 라이브러리를 개발하는지에 대해서 조금 이해할 수 있었는데 창완님도 토스팀 소속이셔서 여기서 또 다른 내용을 다뤄주시려나? 하는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 &lt;b&gt;김진욱&lt;/b&gt;님의 &quot;CRUD에서 복잡한 아키텍처로: AI시대의 서버 엔지니어링 의사결정&quot;이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요즘 백엔드 개발을 하다보면 CRUD는 말그대로 AI 딸깍이다.&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;b&gt;이성태&lt;/b&gt;님의 &quot;끊기는 AI채팅을 안정화하기까지:SSE -&amp;gt; WebSocket 전환기&quot;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전에 SSE와 Socket을 비교하면서 같은 서비스에 두 기술 모두를 적용하면서 맛보기는 했었는데&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;h4 data-ke-size=&quot;size20&quot;&gt;사담콘 입장&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20260714_223232957_02.jpg&quot; data-origin-width=&quot;1050&quot; data-origin-height=&quot;1400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sDbqT/dJMb99UCK0X/bfyDIsTy5Kd8tBOU9FdKD0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sDbqT/dJMb99UCK0X/bfyDIsTy5Kd8tBOU9FdKD0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sDbqT/dJMb99UCK0X/bfyDIsTy5Kd8tBOU9FdKD0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsDbqT%2FdJMb99UCK0X%2FbfyDIsTy5Kd8tBOU9FdKD0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1050&quot; height=&quot;1400&quot; data-filename=&quot;KakaoTalk_20260714_223232957_02.jpg&quot; data-origin-width=&quot;1050&quot; data-origin-height=&quot;1400&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 이렇게 의자가 많나 했더니 오늘 참석자가 무려 150명가량 된다고 한다 ㅎㄷㄷ&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20260714_223232957_03.jpg&quot; data-origin-width=&quot;1050&quot; data-origin-height=&quot;1400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnhRkG/dJMcabroJXm/xZt4EkjnKMusaKrGmjX54k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnhRkG/dJMcabroJXm/xZt4EkjnKMusaKrGmjX54k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnhRkG/dJMcabroJXm/xZt4EkjnKMusaKrGmjX54k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnhRkG%2FdJMcabroJXm%2FxZt4EkjnKMusaKrGmjX54k%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1050&quot; height=&quot;1400&quot; data-filename=&quot;KakaoTalk_20260714_223232957_03.jpg&quot; data-origin-width=&quot;1050&quot; data-origin-height=&quot;1400&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 사담콘 영상을 다시볼 수 있는 NFC 굿즈와 각종 스티커들도 받았다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 바로 인상적이었던 발표들을 듣고 느낀 소감에 대해서 적어보려고한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;발표를 듣고 느낀점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;모던 프론트 엔드 라이브러리 패키징 가이드&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20260714_233801622.jpg&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;1050&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9STJc/dJMcag7gVDU/pnAEj5IBglnFbnTPmGrwf1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9STJc/dJMcag7gVDU/pnAEj5IBglnFbnTPmGrwf1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9STJc/dJMcag7gVDU/pnAEj5IBglnFbnTPmGrwf1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9STJc%2FdJMcag7gVDU%2FpnAEj5IBglnFbnTPmGrwf1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1400&quot; height=&quot;1050&quot; data-filename=&quot;KakaoTalk_20260714_233801622.jpg&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;1050&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;일단 제일 공감가는 부분은 SSOT의 부제... 지금 가장 최근에 겪은 문제 중에 하나가 소스 코드 + tsconfig가 SSOT인데 dist에 캐시된 부분을 그대로 사용해서 실행이 안되었던 기억이있다.(근데 이건 SSOT는 이미 존재하는데 나의 휴먼 이슈로 내가 설정을 잘못한거긴하다...) 추가적인 내용들은 조금씩 기억이 나지만 역시 인간은 망각의 동물인지라... 잘 기억이 안난다. 되게 궁금했던 발표라서 필기도 열심히했는데 집에와서보니 노트를 잃어버렸다... 창완님께 사진 사용 허가도 받았는데... 죄송합니다...! 사담콘 영상이 올라오면 다시 기억을 되살려서 글을 업데이트 하겠습니닷...!&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CRUD에서 복잡한 아키텍처로: AI시대의 서버 엔지니어링 의사결정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진욱님의 발표는 발표도 정말 좋았지만 끝나고 잠시 나눴던 대화 시간에서 좋은 말씀들을 많이 해주셨다. 가장 핵심은 서버구조를 처음부터 확정하지 말고 &quot;내 서버구조를 내가 &lt;b&gt;스스로 측정하고 평가&lt;/b&gt;하고 필요하면 그 때 &lt;b&gt;구조를 추가&lt;/b&gt;하는게 가장 좋다. 처음부터 모든 것을 다 고려한 서버구조는 오버엔지니어링이다.&quot; 이를 위해서 우리가 갖추어야할 역량은 다양한 서버구조를 직접 다 만들어보는게 아니라 &quot;필&lt;b&gt;요한 기술을 그 때 사용할 수 있게 끔 0에서 1&lt;/b&gt;&quot;로 만들어두기라고 하셨다. 이 말씀을 듣고 요즘 대규모 시스템 설계(모두가 아는 그 책)을 정말 열심히 재밌게 읽고있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;끊기는 AI채팅을 안정화하기까지:SSE -&amp;gt; WebSocket 전환기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성태님의 발표는 발표 내내 너무 궁금한 점이 있었다. 왜 처음부터 WebSocket을 안썼는가!&lt;br /&gt;그리고 이 질문이 나만 생각났던게 아니였다. 다른 분도 똑같은 질문을 해주셨고 그 질문에 대한 답은 &quot;이미 쓰고 있었습니다.&quot;였었는데 모두가 웃음으로 반응했었다. 내 추측으로는 성태님이 다루시는 서버구조가 AI 챗봇 서비스이다보니 AI가 응답을 만드는 시간 동안 기다리고 그 후에 이벤트를 발행하면 비동기적 흐름이 자연스러워서가 아닐까? 라고 추측했는데 생각해보면 Socket도 이건 똑같이 가능하긴하다... 혹시 이전 전임자 분 왜 SSE를 쓰셨는지 제 글을 본다면 댓글을 부탁드려도 될지...ㅎㅎ&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;경품 당첨&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20260714_232033545_01.jpg&quot; data-origin-width=&quot;1050&quot; data-origin-height=&quot;1400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c327Rv/dJMcahSBDZy/235Kz1HXkZD407Z6LUNgCK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c327Rv/dJMcahSBDZy/235Kz1HXkZD407Z6LUNgCK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c327Rv/dJMcahSBDZy/235Kz1HXkZD407Z6LUNgCK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc327Rv%2FdJMcahSBDZy%2F235Kz1HXkZD407Z6LUNgCK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1050&quot; height=&quot;1400&quot; data-filename=&quot;KakaoTalk_20260714_232033545_01.jpg&quot; data-origin-width=&quot;1050&quot; data-origin-height=&quot;1400&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘 행사에는 경품 추첨이있었는데 나는 올 빙고는 아니고 빙고 3개를 만들어서 경품 추첨권 3개를 얻을 수 있었다. 그런데 경품이 거의 50개인데 경품 추첨에 참여한 사람이 100명 밖에 되지 않았다. 그래서 속으로 아주 큰 기대를 하고 있었는데...&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_20260714_232033545.jpg&quot; data-origin-width=&quot;1050&quot; data-origin-height=&quot;1400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rzVp0/dJMcaf8klCl/LLQxoRyfXOtdxCCzyWwIjK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rzVp0/dJMcaf8klCl/LLQxoRyfXOtdxCCzyWwIjK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rzVp0/dJMcaf8klCl/LLQxoRyfXOtdxCCzyWwIjK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrzVp0%2FdJMcaf8klCl%2FLLQxoRyfXOtdxCCzyWwIjK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1050&quot; height=&quot;1400&quot; data-filename=&quot;KakaoTalk_20260714_232033545.jpg&quot; data-origin-width=&quot;1050&quot; data-origin-height=&quot;1400&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;야호 &quot;클린 아키텍처&quot; 당첨~ 요즘 서버구조, 아키텍처에 대해서 한참 관심이 많은데 너무 좋은책을 경품으로 받은 것 같다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;전체적인 소감&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 모임의 전체적인 후기는 나도 내가 배우고 느낀 점을 타인과 공유하는 발표자가 되어보고 싶다는 생각이 들었다. 다음에는 Listener가 아닌 Seapker로서 여러분들께 후기를 들려드릴 수 있기를 바라면서 긴 글 읽어주신 분들께 감사의 말씀을!&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;추가&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 사담콘 영상이 올라오고 다면 내용들이 조금 더 업데이트 될 수 있습니다!&lt;/p&gt;</description>
      <category>모임 &amp;amp; 세미나 &amp;amp; 컨퍼런스</category>
      <category>사담콘 #개발자세미나 #개발자컨퍼런스 #IT세미나 #IT컨퍼런스 #개발자 #네트워킹 #SIPE</category>
      <author>pinkcamel</author>
      <guid isPermaLink="true">https://pinkcamel.tistory.com/6</guid>
      <comments>https://pinkcamel.tistory.com/6#entry6comment</comments>
      <pubDate>Tue, 14 Jul 2026 23:29:19 +0900</pubDate>
    </item>
    <item>
      <title>모던 자바스크립트 Deep Dive 3장 자바스크립트 개발 환경과 실행 방법</title>
      <link>https://pinkcamel.tistory.com/5</link>
      <description>&lt;p data-sourcepos=&quot;3:1-3:163;23-185&quot; data-ke-size=&quot;size16&quot;&gt;자바스크립트를 공부하기 시작하면 가장 먼저 마주치는 질문이 있다. &quot;그래서 이 코드를 어디서 실행하지?&quot; 자바스크립트는 브라우저에서도 돌아가고 Node.js에서도 돌아가는데, 둘은 비슷해 보이지만 분명히 다르다. 이번 글에서는 두 실행 환경의 차이와 실제로 코드를 실행하는 방법을 정리한다.&lt;/p&gt;
&lt;h2 data-sourcepos=&quot;5:1-5:33;187-219&quot; data-ke-size=&quot;size26&quot;&gt;자바스크립트 엔진: 브라우저와 Node.js의 공통점&lt;/h2&gt;
&lt;p data-sourcepos=&quot;7:1-7:106;221-326&quot; data-ke-size=&quot;size16&quot;&gt;모든 브라우저는 자바스크립트를 해석하고 실행할 수 있는 &lt;b&gt;자바스크립트 엔진&lt;/b&gt;을 내장하고 있다. 대표적인 것이 구글이 만든 &lt;b&gt;V8 엔진&lt;/b&gt;으로, Chrome과 Edge 등에서 사용된다.&lt;/p&gt;
&lt;p data-sourcepos=&quot;9:1-9:133;328-460&quot; data-ke-size=&quot;size16&quot;&gt;Node.js 역시 이 V8 엔진을 그대로 내장해서 만든 **자바스크립트 런타임(실행 환경)**이다. V8이라는 부품(엔진) 위에 파일 시스템, 네트워크 같은 기능을 얹어서, 브라우저 밖에서도 자바스크립트를 실행할 수 있게 만든 것이다.&lt;/p&gt;
&lt;p data-sourcepos=&quot;11:1-11:48;462-509&quot; data-ke-size=&quot;size16&quot;&gt;즉, 브라우저와 Node.js는 같은 엔진을 공유하지만 그 위에 얹힌 기능이 다르다.&lt;/p&gt;
&lt;h2 data-sourcepos=&quot;13:1-13:15;511-525&quot; data-ke-size=&quot;size26&quot;&gt;하지만 용도는 다르다&lt;/h2&gt;
&lt;div data-sourcepos=&quot;15:1-18:126;527-763&quot;&gt;구분브라우저Node.js
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;주된 목적&lt;/td&gt;
&lt;td&gt;HTML/CSS/JS를 해석해 화면에 &lt;b&gt;렌더링&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;브라우저 밖에서의 &lt;b&gt;자바스크립트 실행 환경&lt;/b&gt; 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;제공하는 API&lt;/td&gt;
&lt;td&gt;DOM API, BOM, Canvas, fetch 등&lt;br /&gt;(클라이언트 사이드 Web API)&lt;/td&gt;
&lt;td&gt;File System(fs), HTTP 서버, process 등&lt;br /&gt;(Node.js 고유 API)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-sourcepos=&quot;20:1-20:22;765-786&quot; data-ke-size=&quot;size16&quot;&gt;여기서 중요한 개념 하나를 짚고 가자.&lt;/p&gt;
&lt;blockquote data-sourcepos=&quot;22:1-23:40;788-882&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-sourcepos=&quot;22:3-23:40;790-882&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ECMAScript(언어 코어)는 브라우저와 Node.js 어디서든 동일하게 동작한다.&lt;/b&gt; 다만 각 &lt;b&gt;호스트 환경&lt;/b&gt;이 제공하는 추가 API가 다를 뿐이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-sourcepos=&quot;25:1-25:215;884-1098&quot; data-ke-size=&quot;size16&quot;&gt;변수, 함수, 객체, 반복문 같은 언어의 핵심 문법(ECMAScript)은 어느 환경에서든 똑같이 쓸 수 있다. 하지만 브라우저에서는 fs.readFile(파일 읽기)을 쓸 수 없고, Node.js에서는 document.querySelector(DOM 조작)를 쓸 수 없다. 각 API가 &quot;호환되지 않는&quot; 것이 아니라, 애초에 &lt;b&gt;그 환경에만 존재하는 API&lt;/b&gt;이기 때문이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-sourcepos=&quot;27:1-28:53;1100-1198&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;27:1-27:46;1100-1145&quot;&gt;브라우저는 화면을 그리는 것이 목적이므로 DOM을 조작하는 API를 제공하고,&lt;/li&gt;
&lt;li data-sourcepos=&quot;28:1-28:53;1146-1198&quot;&gt;Node.js는 서버 등 범용 실행 환경이 목적이므로 파일을 읽고 쓰는 API를 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-sourcepos=&quot;30:1-30:33;1200-1232&quot; data-ke-size=&quot;size26&quot;&gt;브라우저에서 자바스크립트 실행하기: 개발자 도구 콘솔&lt;/h2&gt;
&lt;p data-sourcepos=&quot;32:1-32:50;1234-1283&quot; data-ke-size=&quot;size16&quot;&gt;가장 간단한 실행 방법은 브라우저 &lt;b&gt;개발자 도구의 Console&lt;/b&gt;을 이용하는 것이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-sourcepos=&quot;34:1-35:26;1285-1349&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;34:1-34:39;1285-1323&quot;&gt;Windows: F12 또는 Ctrl + Shift + I&lt;/li&gt;
&lt;li data-sourcepos=&quot;35:1-35:26;1324-1349&quot;&gt;Mac: Cmd + Option + I&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-sourcepos=&quot;37:1-37:61;1351-1411&quot; data-ke-size=&quot;size16&quot;&gt;개발자 도구를 열고 &lt;b&gt;Console 탭&lt;/b&gt;으로 이동하면 자바스크립트 코드를 바로 입력하고 실행할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;982&quot; data-origin-height=&quot;637&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3nGXc/dJMcab5UzxX/3A97jFgBweLi3slt0LqxB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3nGXc/dJMcab5UzxX/3A97jFgBweLi3slt0LqxB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3nGXc/dJMcab5UzxX/3A97jFgBweLi3slt0LqxB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3nGXc%2FdJMcab5UzxX%2F3A97jFgBweLi3slt0LqxB0%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;982&quot; height=&quot;637&quot; data-origin-width=&quot;982&quot; data-origin-height=&quot;637&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;div data-sourcepos=&quot;41:1-43:4;1459-1503&quot;&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;javascript&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;javascript&quot; style=&quot;color: #eaecf0;&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;console.log(&quot;자바스크립트야 안녕?&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-sourcepos=&quot;45:1-45:58;1505-1562&quot; data-ke-size=&quot;size16&quot;&gt;실행하면 콘솔에 자바스크립트야 안녕?이 출력되고, 그 아래에 undefined가 함께 표시된다.&lt;/p&gt;
&lt;h3 data-sourcepos=&quot;47:1-47:23;1564-1586&quot; data-ke-size=&quot;size23&quot;&gt;잠깐, undefined는 뭐지?&lt;/h3&gt;
&lt;p data-sourcepos=&quot;49:1-49:101;1588-1688&quot; data-ke-size=&quot;size16&quot;&gt;처음 보면 에러인가 싶지만 &lt;b&gt;에러가 아니다.&lt;/b&gt; 콘솔은 REPL(Read-Eval-Print Loop) 환경이라서, 입력한 표현식의 &lt;b&gt;평가 결과(반환값)&lt;/b&gt; 를 항상 보여준다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-sourcepos=&quot;51:1-52:42;1690-1830&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;51:1-51:99;1690-1788&quot;&gt;console.log()는 문자열을 &lt;b&gt;출력&lt;/b&gt;하는 일은 하지만, 함수 자체는 아무것도 &lt;b&gt;반환&lt;/b&gt;하지 않는다. 그래서 반환값이 undefined로 표시되는 것이다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;52:1-52:42;1789-1830&quot;&gt;반면 1 + 2를 입력하면 표현식의 평가 결과인 3이 표시된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-sourcepos=&quot;54:1-54:73;1832-1904&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;출력&quot;과 &quot;반환값&quot;은 다르다&lt;/b&gt;는 이 구분은 나중에 함수의 반환값 개념을 배울 때 그대로 이어지므로 지금 기억해 두면 좋다.&lt;/p&gt;
&lt;h2 data-sourcepos=&quot;56:1-56:14;1906-1919&quot; data-ke-size=&quot;size26&quot;&gt;그 밖의 실행 방법&lt;/h2&gt;
&lt;p data-sourcepos=&quot;58:1-58:35;1921-1955&quot; data-ke-size=&quot;size16&quot;&gt;콘솔 외에도 자바스크립트를 실행하는 방법은 여러 가지가 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-sourcepos=&quot;60:1-62:59;1957-2180&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-sourcepos=&quot;60:1-60:69;1957-2025&quot;&gt;&lt;b&gt;HTML 파일에 &amp;lt;script&amp;gt; 태그로 포함&lt;/b&gt;해서 브라우저로 열기 &amp;mdash; 실제 웹 페이지가 동작하는 방식과 동일&lt;/li&gt;
&lt;li data-sourcepos=&quot;61:1-61:96;2026-2121&quot;&gt;&lt;b&gt;터미널에서 node 파일명.js&lt;/b&gt; 로 실행 &amp;mdash; Node.js가 설치되어 있다면 파일 단위로 실행 가능 (node만 입력하면 Node.js REPL 진입)&lt;/li&gt;
&lt;li data-sourcepos=&quot;62:1-62:59;2122-2180&quot;&gt;&lt;b&gt;VS Code + Code Runner 확장&lt;/b&gt; 또는 내장 터미널 활용 &amp;mdash; 에디터에서 바로 실행&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-sourcepos=&quot;64:1-64:6;2182-2187&quot; data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-sourcepos=&quot;66:1-68:123;2189-2486&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;66:1-66:89;2189-2277&quot;&gt;브라우저와 Node.js는 모두 자바스크립트 엔진(V8)을 내장하지만, &lt;b&gt;브라우저는 렌더링&lt;/b&gt;, &lt;b&gt;Node.js는 범용 실행 환경 제공&lt;/b&gt;이 목적이다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;67:1-67:86;2278-2363&quot;&gt;&lt;b&gt;ECMAScript(언어 코어)는 어디서든 동일&lt;/b&gt;하고, 호스트 환경이 제공하는 API(Web API vs Node.js 고유 API)만 다르다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;68:1-68:123;2364-2486&quot;&gt;가장 간단한 실행 방법은 &lt;b&gt;개발자 도구 콘솔&lt;/b&gt;이며, 콘솔은 REPL 환경이라 출력값과 별개로 &lt;b&gt;표현식의 반환값&lt;/b&gt;을 보여준다. console.log() 뒤의 undefined는 에러가 아니라 반환값이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-sourcepos=&quot;70:1-70:29;2488-2516&quot; data-ke-size=&quot;size16&quot;&gt;다음 글에서는 변수와 데이터 타입을 다룰 예정이다.&lt;/p&gt;</description>
      <category>Javascript</category>
      <author>pinkcamel</author>
      <guid isPermaLink="true">https://pinkcamel.tistory.com/5</guid>
      <comments>https://pinkcamel.tistory.com/5#entry5comment</comments>
      <pubDate>Tue, 7 Jul 2026 22:48:46 +0900</pubDate>
    </item>
    <item>
      <title>모던 자바스크립트 Deep Dive 2장 자바스크립트란?</title>
      <link>https://pinkcamel.tistory.com/4</link>
      <description>&lt;h1&gt;자바스크립트의 역사 &amp;mdash; 모카에서 멀티 패러다임 언어까지&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;자바스크립트가 어떻게 지금의 모습이 되었는지 정리해 보았다. 단순한 브라우저 스크립트 언어가 어떻게 웹 전체를 지배하는 언어가 되었는지, 그 흐름을 따라가 보자.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;자바스크립트의 탄생 (1995)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트는 1995년 넷스케이프 커뮤니케이션즈(Netscape Communications)의 **브렌던 아이크(Brendan Eich)**가 개발했다. 처음부터 &quot;자바스크립트&quot;라는 이름이었던 것은 아니고, 다음과 같은 순서로 이름이 바뀌었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;모카(Mocha) &amp;rarr; 라이브스크립트(LiveScript) &amp;rarr; 자바스크립트(JavaScript)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당시 큰 인기를 얻고 있던 자바(Java)의 유명세에 편승하기 위한 마케팅 목적의 개명이었기 때문에, 이름과 달리 자바스크립트와 자바는 전혀 다른 언어다. 그래서 나는 개발자와의 대화에서 근본 없는 언어라고 말을 할 때가 많다.(아는 분은 아시겠지만 신기한 코드들이 종종 작동한다.)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;표준화의 시작 &amp;mdash; ECMAScript&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저마다 자바스크립트가 서로 다르게 동작하는 &lt;b&gt;크로스 브라우징 이슈&lt;/b&gt;가 심각해지면서, 자바스크립트를 표준화해야 한다는 수요가 생겼다. 이에 표준화 기구인 &lt;b&gt;ECMA 인터내셔널&lt;/b&gt;이 자바스크립트의 표준 사양인 &lt;b&gt;ECMAScript&lt;/b&gt;를 제정하게 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ES6 (ECMAScript 2015) &amp;mdash; 가장 큰 변화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 2015년에 공개된 **ES6(ECMAScript 2015)**는 자바스크립트 역사상 가장 큰 변화를 가져온 버전이다. 이때 추가된 기능들은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;let / const&lt;/li&gt;
&lt;li&gt;화살표 함수(Arrow Function)&lt;/li&gt;
&lt;li&gt;클래스(Class)&lt;/li&gt;
&lt;li&gt;스프레드 문법(Spread Syntax)&lt;/li&gt;
&lt;li&gt;심벌(Symbol)&lt;/li&gt;
&lt;li&gt;프로미스(Promise)&lt;/li&gt;
&lt;li&gt;Map / Set&lt;/li&gt;
&lt;li&gt;모듈 시스템 (import / export)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 우리가 &quot;모던 자바스크립트&quot;라고 부르는 코드 스타일의 대부분이 이 버전에서 시작되었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ES8 (ECMAScript 2017)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ES8에서는 비동기 처리를 동기 코드처럼 작성할 수 있게 해주는 **async / await**가 도입되어, 프로미스 기반 비동기 코드의 가독성이 크게 향상되었다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Ajax &amp;mdash; 웹의 동작 방식을 바꾸다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;**Ajax(Asynchronous JavaScript and XML)**는 서버와 브라우저가 &lt;b&gt;비동기 방식으로 데이터를 교환&lt;/b&gt;하며 통신하는 기능이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ajax 이전에는 페이지가 조금만 바뀌어도 서버가 HTML 전체를 다시 내려주고 브라우저가 전체 페이지를 다시 렌더링해야 했다. Ajax의 등장으로 &lt;b&gt;필요한 데이터만 주고받고, 브라우저는 변경된 부분만 렌더링&lt;/b&gt;하는 것이 가능해졌다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  개인 생각: 이 지점이 CSR(Client Side Rendering)의 시작 가능성을 열어준 순간이라고 생각한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;jQuery &amp;mdash; DOM 조작의 대중화&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;jQuery&lt;/b&gt;는 복잡하고 브라우저마다 달랐던 DOM 조작을 훨씬 손쉽게 할 수 있게 해준 라이브러리다. 크로스 브라우징 문제를 상당 부분 해소해 주면서 오랫동안 프론트엔드 개발의 표준처럼 사용되었다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;V8 엔진 &amp;mdash; 자바스크립트의 성능 혁명 (2008)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;V8&lt;/b&gt;은 2008년 구글이 발표한 고성능 자바스크립트 엔진이다. V8의 등장으로 자바스크립트의 실행 속도가 비약적으로 빨라졌고, 무거운 로직을 클라이언트에서 처리하는 것이 현실적인 선택지가 되면서 프론트엔드 개발이 본격적으로 주목받는 계기가 되었다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  개인 생각: V8의 성능 향상이 있었기에 CSR이 실제로 쓸 만한 수준이 될 수 있었다고 본다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Node.js &amp;mdash; 브라우저 밖으로 나온 자바스크립트 (2009)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Node.js&lt;/b&gt;는 2009년 **라이언 달(Ryan Dahl)**이 발표한 자바스크립트 런타임으로, V8 엔진을 기반으로 빌드되었다. Node.js의 등장으로 자바스크립트는 프론트엔드뿐만 아니라 &lt;b&gt;백엔드 개발에서도 주력으로 사용되는 언어&lt;/b&gt;로 자리 잡게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node.js의 특징을 정리하면 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTTP 등 서버 개발에 필요한 &lt;b&gt;빌트인 API가 내장&lt;/b&gt;되어 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;싱글 스레드 이벤트 루프&lt;/b&gt; 기반으로 동작하기 때문에 I/O 집약적인 작업에는 강하다.&lt;/li&gt;
&lt;li&gt;반대로 같은 이유로 &lt;b&gt;CPU 집약적인 작업에는 권장되지 않는다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SPA 프레임워크 &amp;mdash; CSR의 대중화&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;**SPA(Single Page Application)**는 하나의 페이지에서 필요한 부분만 동적으로 갱신하며 동작하는 애플리케이션 구조다. 이를 구현하기 위한 프레임워크&amp;middot;라이브러리로 &lt;b&gt;Angular, React, Vue.js&lt;/b&gt; 등이 등장했고, 이들이 CSR을 대중화시킨 주역이라 할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;자바스크립트와 ECMAScript의 관계&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 용어는 자주 혼용되지만 엄밀히는 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;자바스크립트 = ECMAScript + 클라이언트 사이드 Web API&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ECMAScript&lt;/b&gt;: 언어의 핵심 문법과 동작을 정의한 표준 사양&lt;/li&gt;
&lt;li&gt;&lt;b&gt;클라이언트 사이드 Web API&lt;/b&gt;: DOM, fetch, Canvas 등 브라우저가 제공하는 기능으로, ECMAScript와 별도로 **W3C(월드 와이드 웹 컨소시엄)**에서 사양을 관리한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;자바스크립트는 어떤 언어인가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 자바스크립트라는 언어 자체의 특징을 정리해 보면 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;웹 브라우저에서 동작하는 사실상 유일한 프로그래밍 언어&lt;/b&gt;다.&lt;/li&gt;
&lt;li&gt;전통적으로 &lt;b&gt;인터프리터 언어&lt;/b&gt;로 분류되지만, 현대의 자바스크립트 엔진은 컴파일러의 장점을 결합하여 인터프리터 언어의 단점을 상당 부분 해결했다. 이에 대해 더 알고 싶다면 &lt;b&gt;JIT(Just-In-Time) 컴파일&lt;/b&gt;을 찾아보면 좋다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;명령형, 함수형, 프로토타입 기반 객체 지향&lt;/b&gt;을 모두 지원하는 &lt;b&gt;멀티 패러다임 프로그래밍 언어&lt;/b&gt;다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마치며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하고 보니 자바스크립트의 역사는 곧 웹의 역사와 맞닿아 있다. 크로스 브라우징 문제에서 표준화가 나왔고, Ajax에서 CSR의 가능성이 열렸으며, V8과 Node.js가 그 가능성을 현실로 만들었다. 2장에 대해서 완벽하게 동일한 정리는 아니고 필자가 중요하다고 생각하는 부분위주로 적었으니 자세한 내용은 책을 꼭 확인해보시길 권장한다.&lt;/p&gt;</description>
      <category>Javascript</category>
      <category>모던 자바스크립트 DeepDive #Javascript #Node.js #V8</category>
      <author>pinkcamel</author>
      <guid isPermaLink="true">https://pinkcamel.tistory.com/4</guid>
      <comments>https://pinkcamel.tistory.com/4#entry4comment</comments>
      <pubDate>Sun, 5 Jul 2026 23:58:22 +0900</pubDate>
    </item>
    <item>
      <title>내가 생각하는 하네스의 단점과 프롬프트의 중요성</title>
      <link>https://pinkcamel.tistory.com/3</link>
      <description>&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-filename=&quot;ChatGPT Image 2026년 7월 3일 오전 12_00_46.png&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;1086&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blmS2l/dJMcaf8b46u/mdADxt0G30Qzwl2yann6tK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blmS2l/dJMcaf8b46u/mdADxt0G30Qzwl2yann6tK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blmS2l/dJMcaf8b46u/mdADxt0G30Qzwl2yann6tK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblmS2l%2FdJMcaf8b46u%2FmdADxt0G30Qzwl2yann6tK%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;1448&quot; height=&quot;1086&quot; data-filename=&quot;ChatGPT Image 2026년 7월 3일 오전 12_00_46.png&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;1086&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;0번을 제외하고 요약하자면 notion에 있는 api 명세를 불러와서 설계 후 구현을 하는데 각 과정을 서로 다른 에이전트가 진행하고 이를 오케스트레이터가 총괄하도록 하며 마지막에 verify.sh를 실행해서 코드를 테스트하는 하네스이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적으로 프로젝트의 정보에 대해서 단일 진심 공급원도 제공하고 있어서 프롬프트는 너무 정교하게 작성하지 않아도 Agent가 잘 구현해주지 않을까라는 생각을 가지고 있었다.&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 구현에서 프롬프트의 중요성에 대해서 다시 한 번 생각하게 되었다.&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;bandid로 band를 불러오는 GET 요청에 대한 api를 구현하는 것이었다. 나는 아주 단순 구현이라고 생각이 들어서&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;bandid 기준으로 band를 조회하는 api를 구현해줘 notion 명세 부터 작성하자&quot;&lt;/b&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;1030&quot; data-origin-height=&quot;780&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LCSEy/dJMcadJhdzA/o6wPvZP4C8Z3KfG0DMHBHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LCSEy/dJMcadJhdzA/o6wPvZP4C8Z3KfG0DMHBHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LCSEy/dJMcadJhdzA/o6wPvZP4C8Z3KfG0DMHBHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLCSEy%2FdJMcadJhdzA%2Fo6wPvZP4C8Z3KfG0DMHBHk%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;1030&quot; height=&quot;780&quot; data-origin-width=&quot;1030&quot; data-origin-height=&quot;780&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;지금 현재 notion에 api는 다음과 같은 형태로 정리되어있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부가 설명을 좀 하자면 #뒤에 번호는 api의 고유 넘버링으로 ai와의 원활한 소통을 위해서 붙여놨다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 나는 당연히 ai가 새로운 api의 번호는 현재 존재하는 api의 마지막 번호인 69다음인 70을 붙여서 api 명세를 작성할 것으로 생각했는데 현재 band api의 13번 다음인 14번으로 api를 만들었다.&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;b&gt;&quot;notion&amp;nbsp;명세에는&amp;nbsp;왜&amp;nbsp;적어두지&amp;nbsp;않는거야? &lt;/b&gt;&lt;br /&gt;&lt;b&gt;user는&amp;nbsp;다&amp;nbsp;적었잖아 &lt;/b&gt;&lt;br /&gt;&lt;b&gt;그리고 api 이름에 수정한 다음 오늘날짜로 바꿔&quot;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라는 프롬프트를 적었더니 AI가 모든 notion의 명세를 검토하고 동기화하는 작업을 시작했다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;난 분명 band에서 bandid로 get하는 api만을 명세화 하기를 바란건데 여기서도 나의 하네스의 허점을 발견했다.&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;1. 명세 확인 2. 명세 동기화 3. 설계&amp;nbsp; 4. 설계 보완 5.구현 6.QA&lt;/p&gt;
&lt;p data-ke-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;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 한 번 하네스를 구축하고나면 강제적으로 해당 하네스에 맞춰서 모든 작업이 진행 되기 때문에 항상 그 무거운 워크 플로우를 다 따라가야한다는 단점도 느꼈다. 오늘의 경험을 통해서 크게 느낀 점은 2가지 인 것 같다.&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;2. 프롬프트를 입력할 때 하네스가 잘 보완해주겠지라는 믿음을 가지지 말고 여전히 최대한 잘 기술하는게 중요한 것 같다. 물론 내가 아직 부족해서 그럴 수도 있지만 앞으로는 프롬프트를 조금 더 신중히 작성해야겠다.&lt;/p&gt;</description>
      <category>Claude</category>
      <author>pinkcamel</author>
      <guid isPermaLink="true">https://pinkcamel.tistory.com/3</guid>
      <comments>https://pinkcamel.tistory.com/3#entry3comment</comments>
      <pubDate>Fri, 3 Jul 2026 00:26:32 +0900</pubDate>
    </item>
    <item>
      <title>서드파티 액션은 커밋 SHA로 고정해야할까?</title>
      <link>https://pinkcamel.tistory.com/2</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;Github actions를 활용해서 CI/CD 파이프라인을 구축하던 중에 이전에는 받지 못했던 리뷰를 받았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 env설정은 secret으로 설정을 해두었고 서버에도 이미 둔 상태라서 바로 resolve하면 되는데&amp;nbsp;&lt;br /&gt;서드파티 액션을 커밋 SHA로 고정하라는게 무슨 말인지 알아보기로했다.&lt;/p&gt;
&lt;p data-ke-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;1204&quot; data-origin-height=&quot;985&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PxADl/dJMcaiKhKad/VXoB9YtK5pm6oKIeEOraeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PxADl/dJMcaiKhKad/VXoB9YtK5pm6oKIeEOraeK/img.png&quot; data-alt=&quot;토끼가 준 링크의 핵심 내용&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PxADl/dJMcaiKhKad/VXoB9YtK5pm6oKIeEOraeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPxADl%2FdJMcaiKhKad%2FVXoB9YtK5pm6oKIeEOraeK%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;1204&quot; height=&quot;985&quot; data-origin-width=&quot;1204&quot; data-origin-height=&quot;985&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1336&quot; data-origin-height=&quot;378&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YHgSy/dJMcagFNDpA/p1Wwo2gWZXU5L5ksP96pM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YHgSy/dJMcagFNDpA/p1Wwo2gWZXU5L5ksP96pM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YHgSy/dJMcagFNDpA/p1Wwo2gWZXU5L5ksP96pM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYHgSy%2FdJMcagFNDpA%2Fp1Wwo2gWZXU5L5ksP96pM1%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;1336&quot; height=&quot;378&quot; data-origin-width=&quot;1336&quot; data-origin-height=&quot;378&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 서드 파티에 대해서 짧게 설명하면&lt;br /&gt;퍼스트 파티 - 개발자(나)&lt;br /&gt;세컨드 파티 - 사용자&lt;br /&gt;서드 파티 - 그외 다른 사람이 만든 앱&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 다시 리뷰와 토끼가 준 글로 돌아가면 checkout은 github에서 공식으로 지원하는 action이지만 개발자인 내가 만든 앱은 아니다. 그래서 코드래빗은 GitHub Actions 문서를 참고해서 checkout을 태그로 사용하는 방식이 아닌 내가 실제로 사용하고자 하는 버전의 commit을 사용할 것을 권장하는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 권장하는 이유는 v4라는 태그를 사용하게 되면 checkout@v4가 만약 업데이트 되면 내 ci/cd 파이프 라인에 checkout 업데이트라는 변수 즉, 백도어의 가능성이 생길 수 있기 때문이다.&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;처음에는 checkout은 github 공식 액션인데 너무 과한 거 아닌가?라는 생각이 들었는데&lt;br /&gt;문득 최근 발생했던 axios 취약점이 생각났다. (Node 생태계에서 아주 많이 쓰이는 비동기 라이브러리)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 npm에서도 아주 간단한 라이브러리 삭제로 인한 파급 효과도 생각이 났고...&lt;br /&gt;&lt;br /&gt;특히나 AI가 코드를 대신 작성해주는 시대이다보니 조심해서 나쁠거는 없다는 생각도 들었다.&lt;br /&gt;그럼 이제 버전 태그 대신 SHA commit으로 교체를 어떻게하는지 살펴보자&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2662&quot; data-origin-height=&quot;1441&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cINLrO/dJMcaiQ5fc2/oE73XBSHV0sobnD5FTwZb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cINLrO/dJMcaiQ5fc2/oE73XBSHV0sobnD5FTwZb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cINLrO/dJMcaiQ5fc2/oE73XBSHV0sobnD5FTwZb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcINLrO%2FdJMcaiQ5fc2%2FoE73XBSHV0sobnD5FTwZb1%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;2662&quot; height=&quot;1441&quot; data-origin-width=&quot;2662&quot; data-origin-height=&quot;1441&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 actions/checkout 레포지토리를 가면 배포된 버전들 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2440&quot; data-origin-height=&quot;1272&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dbQPnh/dJMcaaeuAxV/v8znOnMFXsZyteEPKR5H0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dbQPnh/dJMcaaeuAxV/v8znOnMFXsZyteEPKR5H0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dbQPnh/dJMcaaeuAxV/v8znOnMFXsZyteEPKR5H0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdbQPnh%2FdJMcaaeuAxV%2Fv8znOnMFXsZyteEPKR5H0k%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;2440&quot; height=&quot;1272&quot; data-origin-width=&quot;2440&quot; data-origin-height=&quot;1272&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;최신 버전은 항상 무서우니 4.0 버전을 사용하겠다.&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;2356&quot; data-origin-height=&quot;462&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5Jodj/dJMcaicx4ia/ZKHVksuakWQGSUoAcmiXc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5Jodj/dJMcaicx4ia/ZKHVksuakWQGSUoAcmiXc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5Jodj/dJMcaicx4ia/ZKHVksuakWQGSUoAcmiXc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5Jodj%2FdJMcaicx4ia%2FZKHVksuakWQGSUoAcmiXc1%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;2356&quot; height=&quot;462&quot; data-origin-width=&quot;2356&quot; data-origin-height=&quot;462&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;그런데 v4랑 v4.3.1이랑 commit sha가 같다?&lt;br /&gt;알고보니 v4는 v4버전의 가장 최신 릴리즈 버전을 따라가는 태그였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 보니깐 내 프로젝트의 배포 과정에서 다른 서드파티(checkout)의 v4 최신 버전 릴리즈에 따라서 바뀔 수도 있겠다는 생각이 들었다.&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;그러면서 동시에 github 공식 action인데...하는 마음도 있지만 일단은 sha를 고정으로 하는 방식으로 가려고한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 외에도 다른 actions들도 sha고정 방식으로 변경하였다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1233&quot; data-origin-height=&quot;289&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNNJQr/dJMcaalcJK7/RQ41KXKWOw6u7qIWHOOdj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNNJQr/dJMcaalcJK7/RQ41KXKWOw6u7qIWHOOdj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNNJQr/dJMcaalcJK7/RQ41KXKWOw6u7qIWHOOdj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNNJQr%2FdJMcaalcJK7%2FRQ41KXKWOw6u7qIWHOOdj1%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;1233&quot; height=&quot;289&quot; data-origin-width=&quot;1233&quot; data-origin-height=&quot;289&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 토끼의 리뷰는 resolve 처리 되었습니다.&lt;/p&gt;</description>
      <category>GitHub Actions</category>
      <category>#서드파티 #sha #GitHub Actions #CI/CD #actions</category>
      <author>pinkcamel</author>
      <guid isPermaLink="true">https://pinkcamel.tistory.com/2</guid>
      <comments>https://pinkcamel.tistory.com/2#entry2comment</comments>
      <pubDate>Fri, 5 Jun 2026 01:40:26 +0900</pubDate>
    </item>
    <item>
      <title>[Nestjs] NestJs란 무엇인가요?</title>
      <link>https://pinkcamel.tistory.com/1</link>
      <description>&lt;h1&gt;NestJS란?&lt;/h1&gt;
&lt;h2&gt;서버사이드 애플리케이션&lt;/h2&gt;
&lt;p&gt;우리가 어떤 웹 사이트에 접속하면 서버로부터 HTML, CSS, JavaScript 같은 파일을 받아 브라우저에 화면을 표시하게 된다.&lt;/p&gt;
&lt;p&gt;하지만 모든 사용자에게 항상 같은 화면만 보여주는 것은 아니다. 로그인한 사용자와 로그인하지 않은 사용자가 보는 화면은 다를 수 있고, 사용자의 권한이나 요청한 데이터에 따라서 응답 결과도 달라질 수 있다.&lt;/p&gt;
&lt;p&gt;예를 들어 게시판 서비스를 생각해보자.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;로그인하지 않은 사용자는 글 목록만 볼 수 있다.&lt;/li&gt;
&lt;li&gt;로그인한 사용자는 글을 작성할 수 있다.&lt;/li&gt;
&lt;li&gt;관리자는 다른 사용자의 글을 삭제할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이처럼 사용자의 상태, 권한, 요청 내용에 따라 다른 데이터를 만들어 응답하는 과정을 서버에서 처리한다. 이러한 역할을 담당하는 애플리케이션을 보통 &lt;strong&gt;서버사이드 애플리케이션&lt;/strong&gt;, &lt;strong&gt;백엔드 애플리케이션&lt;/strong&gt;, 또는 간단히 &lt;strong&gt;서버&lt;/strong&gt;라고 부른다.&lt;/p&gt;
&lt;p&gt;Node.js 런타임에서도 아주 간단한 서버사이드 애플리케이션을 만들 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// server.js
const http = require(&amp;#39;http&amp;#39;);

const server = http.createServer((req, res) =&amp;gt; {
  // 요청 정보 확인
  console.log(`${req.method} ${req.url}`);

  // 응답 헤더 설정
  res.writeHead(200, {
    &amp;#39;Content-Type&amp;#39;: &amp;#39;text/plain; charset=utf-8&amp;#39;,
  });

  // 응답 본문 전송
  res.end(&amp;#39;Hello Node.js Server!&amp;#39;);
});

server.listen(3000, () =&amp;gt; {
  console.log(&amp;#39;서버가 http://localhost:3000 에서 실행 중입니다.&amp;#39;);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 코드를 실행한 뒤 브라우저에서 &lt;code&gt;localhost:3000&lt;/code&gt;에 접속하면 다음과 같은 응답을 확인할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Hello Node.js Server!&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 코드는 별도의 프레임워크 없이 Node.js의 기본 &lt;code&gt;http&lt;/code&gt; 모듈만 사용해서 동작한다.&lt;/p&gt;
&lt;p&gt;요청이 들어오면 서버는 요청 정보를 확인하고, 응답 헤더를 설정한 뒤, 클라이언트에게 응답 본문을 전송한다.&lt;/p&gt;
&lt;p&gt;즉, 아주 단순하게 보면 서버사이드 애플리케이션은 다음과 같은 흐름으로 동작한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;클라이언트 요청
  ↓
서버에서 요청 처리
  ↓
필요한 데이터 생성 또는 조회
  ↓
클라이언트에게 응답 반환&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;프레임워크&lt;/h2&gt;
&lt;p&gt;앞에서 본 것처럼 Node.js만으로도 서버를 만들 수 있다.&lt;/p&gt;
&lt;p&gt;그렇다면 사람들은 왜 Express나 NestJS 같은 프레임워크를 사용할까?&lt;/p&gt;
&lt;p&gt;간단한 서버라면 Node.js의 기본 기능만으로도 충분하다. 하지만 실제 서비스는 단순히 문자열 하나를 응답하는 수준에서 끝나지 않는다.&lt;/p&gt;
&lt;p&gt;보통 서버에서는 다음과 같은 일들을 처리해야 한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;요청 URL에 따라 다른 로직 실행&lt;/li&gt;
&lt;li&gt;HTTP 메서드별 처리&lt;/li&gt;
&lt;li&gt;요청 데이터 검증&lt;/li&gt;
&lt;li&gt;로그인 여부 확인&lt;/li&gt;
&lt;li&gt;사용자 권한 확인&lt;/li&gt;
&lt;li&gt;에러 처리&lt;/li&gt;
&lt;li&gt;공통 응답 형식 관리&lt;/li&gt;
&lt;li&gt;데이터베이스 연결&lt;/li&gt;
&lt;li&gt;비즈니스 로직 분리&lt;/li&gt;
&lt;li&gt;테스트하기 쉬운 구조 설계&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;프레임워크 없이 이런 기능을 모두 직접 구현하면 코드가 점점 복잡해진다.&lt;/p&gt;
&lt;p&gt;예를 들어 &lt;code&gt;/users&lt;/code&gt;, &lt;code&gt;/posts&lt;/code&gt;, &lt;code&gt;/comments&lt;/code&gt; 같은 API가 늘어나면 요청 URL을 직접 비교하면서 분기해야 한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;if (req.url === &amp;#39;/users&amp;#39; &amp;amp;&amp;amp; req.method === &amp;#39;GET&amp;#39;) {
  // 사용자 목록 조회
} else if (req.url === &amp;#39;/posts&amp;#39; &amp;amp;&amp;amp; req.method === &amp;#39;GET&amp;#39;) {
  // 게시글 목록 조회
} else if (req.url === &amp;#39;/comments&amp;#39; &amp;amp;&amp;amp; req.method === &amp;#39;POST&amp;#39;) {
  // 댓글 작성
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;처음에는 간단해 보이지만 기능이 많아질수록 코드가 길어지고, 어디에 어떤 로직을 둬야 할지 애매해진다.&lt;/p&gt;
&lt;p&gt;이 문제를 해결하기 위해 프레임워크를 사용한다.&lt;/p&gt;
&lt;p&gt;프레임워크는 서버 애플리케이션을 만들 때 자주 필요한 구조와 기능을 미리 제공해준다. 개발자는 모든 것을 처음부터 직접 만들지 않고, 프레임워크가 정해둔 방식에 맞춰 필요한 기능을 구현하면 된다.&lt;/p&gt;
&lt;p&gt;즉, 프레임워크는 다음과 같은 역할을 한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;애플리케이션의 기본 구조를 제공한다.&lt;/li&gt;
&lt;li&gt;반복적으로 필요한 기능을 제공한다.&lt;/li&gt;
&lt;li&gt;코드 작성 방식에 일관성을 준다.&lt;/li&gt;
&lt;li&gt;유지보수하기 쉬운 구조를 만들 수 있게 도와준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;NestJS란?&lt;/h2&gt;
&lt;p&gt;NestJS는 효율적이고 확장 가능한 Node.js 서버사이드 애플리케이션을 만들기 위한 프레임워크다.&lt;/p&gt;
&lt;p&gt;NestJS는 내부적으로 Express를 기본 HTTP 서버 프레임워크로 사용한다. 필요하다면 Fastify로 변경해서 사용할 수도 있다.&lt;/p&gt;
&lt;p&gt;즉, NestJS는 Express 위에서 동작하면서 더 구조적인 애플리케이션 설계 방식을 제공하는 프레임워크라고 볼 수 있다.&lt;/p&gt;
&lt;p&gt;Express가 자유도가 높은 프레임워크라면, NestJS는 정해진 구조를 제공하는 프레임워크에 가깝다.&lt;/p&gt;
&lt;p&gt;NestJS에서는 애플리케이션을 보통 다음과 같은 단위로 나누어 작성한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Module&lt;/li&gt;
&lt;li&gt;Controller&lt;/li&gt;
&lt;li&gt;Provider&lt;/li&gt;
&lt;li&gt;Service&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 구조 덕분에 요청을 받는 부분, 비즈니스 로직을 처리하는 부분, 데이터에 접근하는 부분을 명확히 분리할 수 있다.&lt;/p&gt;
&lt;h2&gt;NestJS의 특징&lt;/h2&gt;
&lt;h3&gt;1. TypeScript 기반&lt;/h3&gt;
&lt;p&gt;NestJS는 TypeScript를 기본으로 지원한다.&lt;/p&gt;
&lt;p&gt;TypeScript를 사용하면 변수, 함수, 객체의 타입을 명확하게 표현할 수 있다. 덕분에 코드 작성 단계에서 실수를 줄일 수 있고, 프로젝트 규모가 커져도 코드의 의도를 파악하기 쉬워진다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;@Get()
findAll(): string {
  return &amp;#39;Hello NestJS&amp;#39;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 코드에서 &lt;code&gt;findAll&lt;/code&gt; 메서드는 문자열을 반환한다는 것을 타입으로 명확히 표현하고 있다.&lt;/p&gt;
&lt;p&gt;백엔드 애플리케이션에서는 요청 데이터, 응답 데이터, 데이터베이스 모델 등 다양한 데이터 구조를 다루기 때문에 TypeScript의 장점이 크게 드러난다.&lt;/p&gt;
&lt;h3&gt;2. Module 기반 구조&lt;/h3&gt;
&lt;p&gt;NestJS 애플리케이션은 Module을 중심으로 구성된다.&lt;/p&gt;
&lt;p&gt;Module은 관련된 기능을 하나의 단위로 묶는 역할을 한다. 예를 들어 사용자 기능은 &lt;code&gt;UsersModule&lt;/code&gt;, 게시글 기능은 &lt;code&gt;PostsModule&lt;/code&gt;처럼 나눌 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { Module } from &amp;#39;@nestjs/common&amp;#39;;
import { UsersController } from &amp;#39;./users.controller&amp;#39;;
import { UsersService } from &amp;#39;./users.service&amp;#39;;

@Module({
  controllers: [UsersController],
  providers: [UsersService],
})
export class UsersModule {}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 코드에서 &lt;code&gt;UsersModule&lt;/code&gt;은 사용자 기능과 관련된 &lt;code&gt;UsersController&lt;/code&gt;와 &lt;code&gt;UsersService&lt;/code&gt;를 하나로 묶고 있다.&lt;/p&gt;
&lt;p&gt;이렇게 기능 단위로 모듈을 나누면 프로젝트가 커져도 각 기능의 책임을 분리하기 쉽다.&lt;/p&gt;
&lt;h3&gt;3. Controller&lt;/h3&gt;
&lt;p&gt;Controller는 클라이언트의 요청을 받는 역할을 한다.&lt;/p&gt;
&lt;p&gt;예를 들어 사용자가 &lt;code&gt;/users&lt;/code&gt; 경로로 요청을 보내면, 해당 요청을 어떤 메서드가 처리할지 Controller에서 정의한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { Controller, Get } from &amp;#39;@nestjs/common&amp;#39;;
import { UsersService } from &amp;#39;./users.service&amp;#39;;

@Controller(&amp;#39;users&amp;#39;)
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Get()
  findAll() {
    return this.usersService.findAll();
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 코드에서 &lt;code&gt;@Controller(&amp;#39;users&amp;#39;)&lt;/code&gt;는 &lt;code&gt;/users&lt;/code&gt; 경로의 요청을 처리한다는 의미다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;@Get()&lt;/code&gt;은 HTTP GET 요청을 처리한다는 의미다.&lt;/p&gt;
&lt;p&gt;즉, 클라이언트가 &lt;code&gt;GET /users&lt;/code&gt; 요청을 보내면 &lt;code&gt;findAll()&lt;/code&gt; 메서드가 실행된다.&lt;/p&gt;
&lt;p&gt;하지만 Controller에서 모든 로직을 처리하지는 않는다. Controller는 요청을 받고, 실제 비즈니스 로직은 Service에게 위임하는 것이 일반적이다.&lt;/p&gt;
&lt;h3&gt;4. Provider와 Service&lt;/h3&gt;
&lt;p&gt;NestJS에서 Provider는 의존성 주입이 가능한 객체를 의미한다.&lt;/p&gt;
&lt;p&gt;대표적인 Provider가 바로 Service다.&lt;/p&gt;
&lt;p&gt;Service는 주로 비즈니스 로직을 처리하는 역할을 한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { Injectable } from &amp;#39;@nestjs/common&amp;#39;;

@Injectable()
export class UsersService {
  findAll() {
    return [&amp;#39;user1&amp;#39;, &amp;#39;user2&amp;#39;];
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;@Injectable()&lt;/code&gt; 데코레이터는 이 클래스가 NestJS의 의존성 주입 시스템에서 관리될 수 있음을 의미한다.&lt;/p&gt;
&lt;p&gt;Controller는 Service를 직접 생성하지 않고 생성자를 통해 주입받는다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;constructor(private readonly usersService: UsersService) {}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 하면 Controller는 요청 처리에 집중하고, Service는 비즈니스 로직에 집중할 수 있다.&lt;/p&gt;
&lt;p&gt;역할이 분리되기 때문에 코드가 더 명확해지고 테스트하기도 쉬워진다.&lt;/p&gt;
&lt;h2&gt;NestJS 요청 처리 흐름&lt;/h2&gt;
&lt;p&gt;NestJS의 요청은 보통 다음과 같은 흐름으로 처리된다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Client
  ↓
Middleware
  ↓
Guard
  ↓
Interceptor
  ↓
Pipe
  ↓
Controller
  ↓
Service
  ↓
Repository / ORM
  ↓
Database&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;각 단계의 역할은 다음과 같다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;단계&lt;/th&gt;
&lt;th&gt;역할&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Middleware&lt;/td&gt;
&lt;td&gt;요청 로깅, 공통 전처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Guard&lt;/td&gt;
&lt;td&gt;인증, 인가 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interceptor&lt;/td&gt;
&lt;td&gt;요청과 응답을 가로채서 공통 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pipe&lt;/td&gt;
&lt;td&gt;요청 데이터 검증 및 변환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Controller&lt;/td&gt;
&lt;td&gt;요청을 받아 적절한 로직 호출&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service&lt;/td&gt;
&lt;td&gt;비즈니스 로직 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repository / ORM&lt;/td&gt;
&lt;td&gt;데이터베이스 접근&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;데이터 저장 및 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;예를 들어 사용자가 게시글 목록을 조회하는 요청을 보냈다고 생각해보자.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;GET /posts&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그러면 NestJS에서는 대략 다음과 같은 일이 일어난다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1. 클라이언트가 GET /posts 요청을 보낸다.
2. Middleware에서 요청 로그를 남긴다.
3. Guard에서 사용자의 인증 여부를 확인한다.
4. Pipe에서 요청 데이터가 올바른지 검증한다.
5. Controller에서 요청을 받는다.
6. Controller는 Service에게 게시글 목록 조회를 요청한다.
7. Service는 Repository 또는 ORM을 통해 DB에서 데이터를 조회한다.
8. 조회한 데이터를 Controller로 반환한다.
9. Interceptor가 응답 형식을 가공할 수 있다.
10. 최종 응답이 클라이언트에게 전달된다.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이처럼 NestJS는 요청이 들어왔을 때 어떤 순서로 처리되는지에 대한 구조를 제공한다.&lt;/p&gt;
&lt;p&gt;덕분에 인증은 Guard에서, 데이터 검증은 Pipe에서, 비즈니스 로직은 Service에서 처리하는 식으로 책임을 분리할 수 있다.&lt;/p&gt;
&lt;h2&gt;NestJS 앱 만들어보기&lt;/h2&gt;
&lt;p&gt;NestJS 프로젝트는 Nest CLI를 사용하면 쉽게 생성할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm i -g @nestjs/cli&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nest CLI를 설치한 뒤 다음 명령어로 프로젝트를 생성한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;nest new hello-nest&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;프로젝트를 생성한 뒤 실행한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cd hello-nest
npm run start:dev&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;기본적으로 NestJS 서버는 &lt;code&gt;localhost:3000&lt;/code&gt;에서 실행된다.&lt;/p&gt;
&lt;p&gt;브라우저나 API 테스트 도구에서 다음 주소로 요청을 보내면 기본 응답을 확인할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;http://localhost:3000&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;NestJS의 기본 애플리케이션 구조는 대략 다음과 같다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;src
 ├── app.controller.ts
 ├── app.service.ts
 ├── app.module.ts
 └── main.ts&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;각 파일의 역할은 다음과 같다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;파일&lt;/th&gt;
&lt;th&gt;역할&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;main.ts&lt;/td&gt;
&lt;td&gt;애플리케이션을 실행하는 진입점&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;app.module.ts&lt;/td&gt;
&lt;td&gt;루트 모듈&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;app.controller.ts&lt;/td&gt;
&lt;td&gt;요청을 받는 컨트롤러&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;app.service.ts&lt;/td&gt;
&lt;td&gt;비즈니스 로직을 처리하는 서비스&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;main.ts&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { NestFactory } from &amp;#39;@nestjs/core&amp;#39;;
import { AppModule } from &amp;#39;./app.module&amp;#39;;

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}

bootstrap();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;main.ts&lt;/code&gt;는 NestJS 애플리케이션을 실행하는 시작점이다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;NestFactory.create(AppModule)&lt;/code&gt;을 통해 루트 모듈인 &lt;code&gt;AppModule&lt;/code&gt;을 기반으로 애플리케이션을 생성한다.&lt;/p&gt;
&lt;h3&gt;app.module.ts&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { Module } from &amp;#39;@nestjs/common&amp;#39;;
import { AppController } from &amp;#39;./app.controller&amp;#39;;
import { AppService } from &amp;#39;./app.service&amp;#39;;

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;AppModule&lt;/code&gt;은 애플리케이션의 루트 모듈이다.&lt;/p&gt;
&lt;p&gt;여기서 사용할 Controller와 Provider를 등록한다.&lt;/p&gt;
&lt;h3&gt;app.controller.ts&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { Controller, Get } from &amp;#39;@nestjs/common&amp;#39;;
import { AppService } from &amp;#39;./app.service&amp;#39;;

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;AppController&lt;/code&gt;는 클라이언트의 요청을 받는다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;@Get()&lt;/code&gt; 데코레이터가 붙은 &lt;code&gt;getHello()&lt;/code&gt; 메서드는 GET 요청을 처리한다.&lt;/p&gt;
&lt;h3&gt;app.service.ts&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { Injectable } from &amp;#39;@nestjs/common&amp;#39;;

@Injectable()
export class AppService {
  getHello(): string {
    return &amp;#39;Hello NestJS!&amp;#39;;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;AppService&lt;/code&gt;는 실제 응답으로 보낼 문자열을 반환한다.&lt;/p&gt;
&lt;p&gt;지금 예제는 매우 단순하지만, 실제 애플리케이션에서는 이 Service에 비즈니스 로직이 들어간다.&lt;/p&gt;
&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;Node.js만으로도 서버사이드 애플리케이션을 만들 수 있다. 하지만 실제 서비스에서는 라우팅, 인증, 검증, 에러 처리, 데이터베이스 접근, 비즈니스 로직 분리 등 다양한 기능이 필요하다.&lt;/p&gt;
&lt;p&gt;프레임워크는 이런 기능을 더 쉽게 구현할 수 있도록 기본 구조와 도구를 제공한다.&lt;/p&gt;
&lt;p&gt;NestJS는 Node.js 서버사이드 애플리케이션을 구조적으로 만들 수 있게 도와주는 프레임워크다. Module, Controller, Provider, Service 같은 개념을 통해 역할을 분리하고, TypeScript 기반으로 안정적인 백엔드 애플리케이션을 만들 수 있다.&lt;/p&gt;
&lt;p&gt;특히 프로젝트 규모가 커질수록 NestJS의 장점은 더 분명해진다.&lt;/p&gt;
&lt;p&gt;요청을 받는 Controller, 비즈니스 로직을 처리하는 Service, 기능을 묶는 Module 구조 덕분에 코드의 책임이 명확해지고 유지보수하기 쉬운 서버 애플리케이션을 만들 수 있다.&lt;/p&gt;
&lt;h2&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;NestJS 공식 문서&lt;/li&gt;
&lt;li&gt;NestJS 한글 문서: &lt;a href=&quot;https://nestjs.burt.pe.kr/&quot;&gt;https://nestjs.burt.pe.kr/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Nest</category>
      <category>nestjs</category>
      <author>pinkcamel</author>
      <guid isPermaLink="true">https://pinkcamel.tistory.com/1</guid>
      <comments>https://pinkcamel.tistory.com/1#entry1comment</comments>
      <pubDate>Fri, 22 May 2026 23:08:15 +0900</pubDate>
    </item>
  </channel>
</rss>