<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>murpin_kim.log</title>
        <link>https://velog.io/</link>
        <description>모든 것에 배움을 얻고자합니다</description>
        <lastBuildDate>Sun, 02 Feb 2025 14:43:23 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>murpin_kim.log</title>
            <url>https://velog.velcdn.com/images/muman_kim/profile/923447cb-6f8d-47f5-b5b9-fb56657ec868/image.JPG</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. murpin_kim.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/muman_kim" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[글또 10기-한 걸음(One Step)]]></title>
            <link>https://velog.io/@muman_kim/%EA%B8%80%EB%98%90-10%EA%B8%B0-%ED%95%9C-%EA%B1%B8%EC%9D%8COne-Step</link>
            <guid>https://velog.io/@muman_kim/%EA%B8%80%EB%98%90-10%EA%B8%B0-%ED%95%9C-%EA%B1%B8%EC%9D%8COne-Step</guid>
            <pubDate>Sun, 02 Feb 2025 14:43:23 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가며">들어가며</h2>
<p>최근 면접을 본 회사에서 합격 소식을 듣고 서울로 올라오게 되었습니다. 이제 한 명의 개발자로 커리어를 시작하게 되었다는 사실에 기쁘면서도 한편으로는 실감이 나지 않습니다. 마치 정신없이 계단을 한 걸음씩 올라가다 보니 어느새 목표했던 위치에 도달한 듯한 기분입니다.
<img src="https://velog.velcdn.com/images/muman_kim/post/a3adad1b-7ff1-44d4-8aa4-f6ab6a692438/image.png" alt=""></p>
<p>그래서 이번 글에서는 제가 개발자로 성장하기까지 어떤 과정을 거쳤는지 돌아보고, 앞으로의 목표(Next Step)에 대해 고민하는 시간을 가져보려 합니다.</p>
<h2 id="개발자로의-첫-걸음">개발자로의 첫 걸음</h2>
<p>처음부터 &quot;개발자가 되겠다!&quot;는 확고한 목표를 갖고 시작한 것은 아니었습니다. 대학교에 입학해 컴퓨터 관련 동아리에 들어가면서 자연스럽게 개발을 접하게 되었습니다. 1학년 때 웹 개발을 처음 알게 되었고, HTML, CSS부터 JavaScript를 학습한 후 Vue를 통해 협업 프로젝트를 경험했습니다.
<img src="https://velog.velcdn.com/images/muman_kim/post/dde2190f-1edb-45c1-9376-a86396f8b215/image.png" alt=""></p>
<p>그때는 단순히 &quot;아무 동아리나 가입해서 열심히 해보자&quot;는 생각이었지만, 만약 이 선택을 하지 않았다면 지금의 저는 개발자가 되지 않았을지도 모릅니다. 결국 이 첫 걸음이 저를 개발자로 이끌었습니다.</p>
<p>이후 동아리 활동을 하면서 선배들에게 개발을 배우고, 후배들에게는 제가 익힌 기술을 가르치면서 개발자로서의 기본기를 다졌습니다. 특히 팀원들과 함께 프로젝트를 만들고, 결과를 되돌아보며 더 나은 결과물을 고민하는 과정에서 개발에 대한 흥미를 느꼈고, 본격적으로 개발자가 되기로 결심했습니다.</p>
<blockquote>
<p>여러분은 어떻게 개발을 시작하게 되었나요?</p>
</blockquote>
<h2 id="개발-경험을-쌓기-위한-한-걸음">개발 경험을 쌓기 위한 한 걸음</h2>
<p>처음에는 개발 자체가 재미있었지만, 점차 &quot;실제 회사에서는 어떤 개발을 하게 될까?&quot;, &quot;나는 어떤 개발자가 되어야 할까?&quot;라는 고민이 들기 시작했습니다.</p>
<p>마침 운 좋게 스타트업에서 프론트엔드 인턴 기회를 얻었고, 이를 놓치지 않고 도전했습니다. 처음에는 회사라는 조직에서 돈을 받고 일한다는 부담감이 있었고, 스프린트마다 마감 기한을 맞춰야 하는 것에 대한 두려움도 컸습니다. 하지만 회사 동료들의 도움을 받아 적응하면서 점점 회사의 흐름을 이해하게 되었고, 결국은 한 명의 개발자로서 1인분을 해낼 수 있게 되었습니다.
(인턴 시절의 나)
<img src="https://velog.velcdn.com/images/muman_kim/post/8b6e2cdb-69b1-4c8f-8f4b-f874d76c5f01/image.png" alt=""></p>
<p>특히 동료 개발자들과 협업하면서 회사에서 어떻게 일해야 하는지를 배웠고, 다른 팀원들에게도 많은 도움을 받으며 성장할 수 있었습니다. 최근 레퍼런스 체크를 위해 오랜만에 연락을 드렸을 때 따뜻한 답변을 받으며 다시 한 번 좋은 동료들의 소중함을 느꼈습니다. 회사에 적응하고 여유가 생기면 꼭 찾아뵙고 인사드리고 싶습니다.</p>
<p>좋은 동료들과 함께 일할 때 가장 즐거웠고, 나 또한 이런 따뜻한 동료가 되어야겠다는 생각을 하게 되었습니다.</p>
<blockquote>
<p>여러분에게도 회사에서 함께 성장했던 좋은 동료들이 있나요?</p>
</blockquote>
<h2 id="개발자로-일하기-위한-한-걸음">개발자로 일하기 위한 한 걸음</h2>
<p>인턴을 마친 후, 저는 대학교 3학년으로 복학하여 졸업을 준비해야 했습니다. 하지만 단순히 학업을 마치는 것이 아니라, &quot;진짜 직원이 되기 위해 무엇을 해야 할까?&quot;를 고민하며 다양한 개발 프로젝트를 진행했습니다.
<img src="https://velog.velcdn.com/images/muman_kim/post/5a496a56-60d6-498f-99cc-1747db90aa36/image.png" alt=""></p>
<p>이때부터는 단순한 개발이 아니라 &quot;왜 이렇게 개발했는가&quot;를 고민하며 기술을 학습하고, 각 기술이 프로젝트에 적합한지를 판단하는 습관을 들였습니다. 이러한 고민의 흔적을 정리하며 포트폴리오를 만들었고, 이 경험이 이번 면접에서도 긍정적인 인상을 주는 데 도움이 되었다고 생각합니다.</p>
<p>개발자는 단순히 코드를 짜는 사람이 아니라, 하나의 제품을 기술적으로 디자인하는 엔지니어라고 생각합니다. 단순한 기능 구현을 넘어, 실제 문제를 해결하는 개발자가 되기 위해 고민하는 것이 중요하다고 느꼈습니다.</p>
<blockquote>
<p>여러분이 생각하는 개발자의 역할은 무엇인가요?</p>
</blockquote>
<h2 id="이제는-next-step">이제는 Next Step</h2>
<p>이러한 과정을 거치며 개발자로서의 방향성을 어느 정도 구체화했지만, 아직 저는 신입일 뿐입니다. 이제는 회사에서 어떻게 성장할지를 고민해야 합니다.
<img src="https://velog.velcdn.com/images/muman_kim/post/6b18b5e1-7669-4199-88b6-93fb1f01facd/image.png" alt=""></p>
<p>가장 먼저 해야 할 일은 새로운 환경에 적응하는 것입니다. 회사에서 어떤 방식으로 일하는지 파악하고, 내가 어떤 기여를 할 수 있을지를 고민해야 합니다. 그리고 궁극적으로는 회사에 어떻게 기여하고, 어떤 성과를 낼 수 있을지를 생각해야 합니다.</p>
<p>더 나아가 앞으로의 커리어를 위해 어떤 목표를 설정할지도 중요합니다. 저는 <code>&quot;단순히 좋은 기업에 다니는 것&quot;</code>보다 <code>&quot;잘하는 개발자&quot;</code>가 되는 것을 목표로 삼고 싶습니다. 인턴 시절 함께 일했던 뛰어난 개발자 동료분들은 자신이 맡은 프로젝트를 깊이 이해하고, 유연하게 해결해 나가는 능력을 갖추고 있었습니다. 저도 그런 개발자가 되고 싶다는 꿈을 키워가고 있습니다.</p>
<blockquote>
<p>&quot;실력 있는 개발자가 좋은 회사를 간다&quot;는 말도 있지만, 저는 &quot;잘하는 개발자는 어디서든 빛난다&quot;고 생각합니다.</p>
</blockquote>
<h2 id="마무리하며">마무리하며</h2>
<p>이번 글을 통해 저의 성장 과정을 돌아보고, 앞으로의 방향성을 다시 한번 정리해보았습니다. 이제 막 개발자로 첫 걸음을 뗀 주니어이지만, 더 잘해지고 싶다는 열망은 모든 주니어 개발자들이 공유하는 꿈일 것입니다.</p>
<p>스스로의 부족함을 인식하고, 무엇을 해야 할지 고민하며 실행한다면 어느 순간 내가 바라보던 목표에 도달할 수 있을 것입니다. 물론 그 과정이 쉽지 않고, 계단이 높고 길게 느껴질 수도 있습니다. 하지만 한 걸음씩 꾸준히 나아간다면 결국 목표를 이룰 수 있다고 믿습니다.
<img src="https://velog.velcdn.com/images/muman_kim/post/79019f8d-6ac6-4d61-9af4-2be2e60a6974/image.png" alt=""></p>
<p>모든 주니어 개발자분들을 응원합니다. 함께 화이팅합시다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[글또 10기 - 서버 2개로 원격 통신이 가능하다고?]]></title>
            <link>https://velog.io/@muman_kim/%EA%B8%80%EB%98%90-10%EA%B8%B0-%EC%84%9C%EB%B2%84-2%EA%B0%9C%EB%A1%9C-%EC%9B%90%EA%B2%A9-%ED%86%B5%EC%8B%A0%EC%9D%B4-%EA%B0%80%EB%8A%A5%ED%95%98%EB%8B%A4%EA%B3%A0</link>
            <guid>https://velog.io/@muman_kim/%EA%B8%80%EB%98%90-10%EA%B8%B0-%EC%84%9C%EB%B2%84-2%EA%B0%9C%EB%A1%9C-%EC%9B%90%EA%B2%A9-%ED%86%B5%EC%8B%A0%EC%9D%B4-%EA%B0%80%EB%8A%A5%ED%95%98%EB%8B%A4%EA%B3%A0</guid>
            <pubDate>Sun, 12 Jan 2025 14:16:44 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가며">들어가며</h2>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/41fea82f-a1f5-444c-a4f0-8c52e36501d5/image.png" alt=""></p>
<p>최근 면접이나 다양한 기술 이야기를 나누면서 제 졸업 작품에 대한 관심을 많이 받게 되었습니다. 이 작품에서 사용된 핵심 기술 중 하나는 바로 <strong>WebRTC</strong>를 활용한 원격 화상 통화 시스템이었습니다. 당시 프로젝트를 진행하며 P2P 통신을 구현했었는데, 그 과정에서 STUN과 TURN 서버를 접하게 되었고, 기술적으로 많은 것을 배우는 계기가 되었습니다.</p>
<p>이 글에서는 당시의 경험을 돌아보며 제가 <strong>왜 STUN과 TURN 서버를 선택했고, 어떤 한계와 고민이 있었는지</strong>를 정리하려 합니다. 추가로, &quot;이런 식으로 했다면 어땠을까?&quot;라는 개선 아이디어도 함께 담아보았습니다. 이를 통해 WebRTC 실시간 통신을 더 깊이 이해하는 데 도움이 되길 바랍니다.</p>
<hr>
<h2 id="프로젝트-시나리오">프로젝트 시나리오</h2>
<p>제가 만든 프로젝트의 시나리오는 간단히 말해 <strong>원격 의료 진단</strong>입니다. 환자와 의사가 원격으로 화상 통화를 하며 진단을 진행할 수 있는 시스템을 구상했습니다.</p>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/7e889bd9-491e-4a09-b89d-9cb13e409d04/image.png" alt=""></p>
<p>(당시 시나리오 만들 때 그림)</p>
<p>당시 제 접근 방식은 간단했습니다. <strong>WebRTC에서 제공하는 ICE(Interactive Connectivity Establishment)</strong> 를 활용해 자동으로 연결을 추적하고 P2P 통신을 설정하면 충분하다고 생각했습니다. 그러나 실제로 구현하면서 <strong>NAT(Network Address Translation) 문제와 방화벽</strong>이라는 현실적 제약에 부딪히게 되었고, 이를 해결하기 위해 STUN과 TURN 서버를 알게 되었는데 이에 대한 내용을 설명해보겠습니다.</p>
<h2 id="stun-서버-nat-뒤에서도-p2p-연결-지원">STUN 서버: NAT 뒤에서도 P2P 연결 지원</h2>
<p>STUN(Session Traversal Utilities for NAT) 서버는 WebRTC에서 기본적으로 사용되는 서버로, 클라이언트가 자신의 <strong>공인 IP와 포트</strong>를 파악할 수 있도록 돕습니다. STUN 서버는 클라이언트 간 직접 연결(P2P)을 지원하며, NAT(Network Address Translation) 뒤에 있어도 네트워크 경로를 설정할 수 있게 해줍니다.</p>
<p><strong>초기 접근 방식</strong>으로는 STUN 서버만으로 충분하다고 생각했습니다. Google에서 제공되는 STUN 서버(예시: <code>stun:stun.l.google.com:19302</code>)를 사용해 P2P 연결을 설정하려 했고, 테스트도 진행했습니다.</p>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/b98410c1-0717-432b-a1b2-f12cb9870ed9/image.png" alt=""></p>
<p>실제로 학교 내부에서 저의 컴퓨터 2대로 정상 작동을 확인했습니다만, 중요한 것이 외부에 있는 친구나 다른 네트워크에서 이동하려니 제대로 되지 않았습니다.</p>
<h3 id="문제-발생-왜-외부-네트워크에서는-연결되지-않을까">문제 발생: 왜 외부 네트워크에서는 연결되지 않을까?</h3>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/2d97723f-1afa-4885-8cc6-f370ce226453/image.png" alt=""></p>
<p>학교 내에서 테스트했을 때는 클라이언트 간 연결이 문제없이 이루어졌습니다. 그러나 집에 있는 친구와 테스트를 진행했을 때, 통신이 아예 이루어지지 않는 상황이 발생했습니다.</p>
<p>처음에는 코드나 WebRTC 설정에 문제가 있다고 생각했지만, <strong>네트워크 환경의 차이</strong> 때문이라는 것을 알게 되었습니다. 자세히 살펴보니 다음과 같은 이유로 P2P 연결이 실패하고 있었습니다:</p>
<ol>
<li><strong>Symmetric NAT 문제</strong><br>집에서 사용하는 인터넷 환경이 <strong>Symmetric NAT</strong>를 사용하고 있어 STUN 서버를 통해 공인 IP를 파악해도 P2P 연결이 불가능했습니다.</li>
</ol>
<ol start="2">
<li><strong>학교 네트워크의 UDP 스트림 차단</strong><br> 학교 네트워크는 UDP 통신을 제한하지 않아 STUN 서버만으로도 통신이 가능했지만, 다른 네트워크 환경(특히 가정용 인터넷)에서는 UDP 스트림이 차단되거나 제대로 작동하지 않았습니다.</li>
</ol>
<hr>
<h2 id="turn-서버의-필요성-데이터를-중계하여-문제-해결">TURN 서버의 필요성: 데이터를 중계하여 문제 해결</h2>
<p>STUN 서버만으로는 P2P 연결이 어려운 환경(Symmetric NAT, UDP 제한)이 있다는 것을 깨닫고, 이를 해결할 방법을 찾기 시작했습니다. 그 과정에서 WebRTC에서 사용하는 <strong>TURN 서버(Traversal Using Relays around NAT)</strong>를 알게 되었습니다.</p>
<p>TURN 서버는 클라이언트 간 데이터를 <strong>중계</strong>하여 직접 연결이 불가능한 상황에서도 안정적인 통신을 보장합니다. 특히, Symmetric NAT와 강력한 방화벽 환경에서 필수적인 역할을 합니다.
<img src="https://velog.velcdn.com/images/muman_kim/post/6b49b06b-b630-4dd1-a649-99253a296a54/image.png" alt=""></p>
<hr>
<h3 id="coturn을-활용한-turn-서버-도입">Coturn을 활용한 TURN 서버 도입</h3>
<p>TURN 서버를 직접 구축하기 위해 AWS EC2에 <strong>Coturn</strong>을 설치했습니다. Coturn은 오픈소스로 제공되는 TURN 서버 구현체로, 간단한 설정만으로 TURN 서버를 실행할 수 있었습니다.</p>
<p><strong>Coturn 서버 설정 예시</strong>:</p>
<pre><code># EC2에서 Coturn 설치 및 실행 
sudo apt-get install coturn 
sudo turnserver -a -o -v -n --realm myrealm.com --user username:password</code></pre><p><strong>WebRTC 설정</strong>:</p>
<pre><code>const peerConnection = new RTCPeerConnection({
  iceServers: [
    { urls: &#39;stun:stun.l.google.com:19302&#39; }, // STUN 서버
    { urls: &#39;turn:ec2-xx-xx-xx-xx.compute.amazonaws.com:3478&#39;, 
      username: &#39;username&#39;, 
      credential: &#39;password&#39; 
    } // Coturn TURN 서버
  ]
});
</code></pre><h2 id="프로젝트-회고와-아쉬운-점">프로젝트 회고와 아쉬운 점</h2>
<h3 id="turn-서버-도입의-의의">TURN 서버 도입의 의의</h3>
<p>STUN 서버만으로는 해결할 수 없던 네트워크 문제를 TURN 서버를 도입함으로써 해결할 수 있었습니다. Coturn을 EC2에 설치하면서 엄청 많은 문제가 되지 않았습니다. 실제로 시연날에는 서울에 있는 팀원과 정상적으로 통신되는 결과까지 얻을 수 있었습니다.</p>
<p>또한 졸업작품을 만들면서 시간도 들인 노력도 있지만, 0부터 설계하고 쌓아 올렸다는 것에 많은 걸 경험했던 것 같습니다.</p>
<h3 id="아쉬운-점-인코딩-및-바이너리-데이터-처리의-부족">아쉬운 점: 인코딩 및 바이너리 데이터 처리의 부족</h3>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/4305072a-b368-4ce8-af7f-1c5d38bbac7b/image.png" alt=""></p>
<p>프로젝트에서는 서버 설계와 네트워크 연결에 대부분의 노력을 기울였지만, <strong>실제 영상 및 오디오 데이터의 인코딩과 처리</strong>등 성능과 관련해서는 깊이 다루지 못했습니다. 어떻게 보면 웹 프론트엔지니어로서 가장 많이 봐야하는 부분이었는데 제가 잘 분석하지 못한 이유도 여기에 있을 수도 있겠다는 생각이 듭니다. WebRTC의 통신에서의 가장 중요한 인코딩 부분이 성능 최적화에 직접적인 영향을 미치기 때문에 이를 더 자세히 알아야할 필요가 있다고 생각합니다. 그래서 다음 글에서는 이 부분을 더 깊이 다뤄보고자 합니다.</p>
<hr>
<h2 id="마치며">마치며</h2>
<p>STUN과 TURN 서버는 WebRTC의 핵심 기술로, 각각 P2P 연결을 지원하거나 데이터 중계를 통해 네트워크 문제를 해결합니다. 이번 프로젝트를 통해 두 기술의 역할과 한계를 명확히 이해할 수 있었습니다. 특히 TURN 서버를 도입한 경험은 실무에서도 충분히 적용 가능한 교훈을 남겼습니다.</p>
<p>다만, 영상 및 오디오 데이터의 처리와 인코딩 최적화에 대해 더 깊이 고민하지 못한 점은 아쉬움으로 남습니다. 앞으로 이 주제를 심화 연구해 글로 공유할 예정이니, 관심 있게 지켜봐 주시길 바랍니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[24년도 하반기 회고: 꽁꽁 얼어붙은 취업길 위로 개발자가 걸어갑니다]]></title>
            <link>https://velog.io/@muman_kim/24%EB%85%84%EB%8F%84-%ED%95%98%EB%B0%98%EA%B8%B0-%ED%9A%8C%EA%B3%A0-%EA%BD%81%EA%BD%81-%EC%96%BC%EC%96%B4%EB%B6%99%EC%9D%80-%EC%B7%A8%EC%97%85%EA%B8%B8-%EC%9C%84%EB%A1%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EA%B1%B8%EC%96%B4%EA%B0%91%EB%8B%88%EB%8B%A4</link>
            <guid>https://velog.io/@muman_kim/24%EB%85%84%EB%8F%84-%ED%95%98%EB%B0%98%EA%B8%B0-%ED%9A%8C%EA%B3%A0-%EA%BD%81%EA%BD%81-%EC%96%BC%EC%96%B4%EB%B6%99%EC%9D%80-%EC%B7%A8%EC%97%85%EA%B8%B8-%EC%9C%84%EB%A1%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EA%B1%B8%EC%96%B4%EA%B0%91%EB%8B%88%EB%8B%A4</guid>
            <pubDate>Sun, 22 Dec 2024 14:27:33 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가며">들어가며</h2>
<p>2024년 12월 19일을 끝으로 4학년 2학기를 마무리했습니다. 마지막 학기를 다니며 여러 추억을 쌓았고, 동시에 불안함과 다짐이 뒤섞인 마음으로 취업을 준비하고 있습니다. 종강이 다가오는 시점, 술자리 참석을 위해 밖으로 나갔다가 매서운 추위가 몸을 파고드는 경험을 했습니다. 그 순간, 문득 지금의 제 취업 준비 과정이 한강 추위와 같다는 생각이 들었습니다.
<img src="https://velog.velcdn.com/images/muman_kim/post/87941832-01ee-4ea5-b370-3342bb34984a/image.png" alt=""></p>
<p>요즘 &quot;춥다 춥다&quot;라는 말이 절로 나오지만, 저는 제가 할 수 있는 모든 것을 다해볼 생각입니다. 개발자로서 커리어를 시작하는 것도, 이를 위해 준비하는 것도 결국 저의 몫이기 때문입니다. 취업문이 좁다고 하지만, 그 문을 통과할 수 있는 개발자가 되기 위해 2024년 상반기 회고록에 이어 이번에는 하반기 회고록을 작성하려 합니다. 이번 회고에서도 이전과 마찬가지로 월별로 돌아보는 방식을 택했습니다.</p>
<h2 id="하반기-월별-회고">하반기 월별 회고</h2>
<p>하반기 동안 취업을 위해 많은 시도를 했습니다. 하지만 솔직히 결과보다는 경험을 쌓는 데 그쳤습니다. 흔들림도 많았고, 꿈을 향한 집중력이 떨어진 순간도 있었지만, 앞으로의 길을 위해 마음을 다잡고 내년을 준비해야겠다고 결심했습니다. 이번 하반기에 제가 무엇을 했는지 정확히 파악하고, 부족한 점을 알아가는 과정이 필요하다고 생각합니다.</p>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/18cd7499-9fa7-4c69-87c7-53528553e1e6/image.png" alt=""></p>
<h3 id="7월">7월</h3>
<p>6월에는 네이버 부스트캠프와 당근마켓 여름 인턴십에 지원했습니다. 또한 학교 계절학기를 진행하며 바쁜 시간을 보냈습니다. 네이버 부스트캠프는 열심히 준비했지만 아쉽게도 불합격이라는 결과를 받았습니다. 비록 합격하지는 못했지만, 외부 인원들과 소통하며 코드를 처음부터 다시 돌아보고 문제를 해결하려고 노력한 경험은 큰 배움이 되었습니다.</p>
<p>당근마켓의 경우 서류에서 탈락했지만, 주변에서도 모두 서류 탈락이라는 소식을 듣고 이곳이 정말 치열한 경쟁을 뚫어야 하는 곳임을 실감했습니다.
<img src="https://velog.velcdn.com/images/muman_kim/post/ffc65ca5-5de8-43bc-a9e2-d80f117859ac/image.png" alt="">
<strong>(떨어졌지만, 내 할 일을 한다...)</strong></p>
<p>결과적으로 조금 멘탈이 흔들리긴 했지만, 시간 낭비 없이 정보처리기사 시험 공부와 여름 자바스크립트 스터디를 조직하며 시간을 효율적으로 활용했습니다. 개인적인 공부 시간과 기사 자격증 준비에 집중하며 보람찬 한 달을 보냈습니다.</p>
<h3 id="8월">8월</h3>
<p>8월에는 조금 색다른 도전을 해보았습니다. 친구들과 함께 &#39;비판적 사고와 프론트엔드&#39;라는 주제로 스터디 및 세미나를 기획했습니다. 세미나는 역삼에 위치한 회사 세미나실에서 진행했으며, 개발 관련 주제와 각자의 경험을 공유하는 자리로 꾸며졌습니다. 남에게 제 경험을 전달하기 위해 자료를 준비하는 과정에서 큰 성취감을 느낄 수 있었습니다. 혹시 궁금하신 분들은 <a href="https://www.youtube.com/watch?v=W-t4yDOYz_I">여기</a>서 구경하실 수 있습니다.(저의 긴장된 모습이 너무 들어나는 발표...)
<img src="https://velog.velcdn.com/images/muman_kim/post/ace7c68c-00ba-4326-88d1-a37fe6410f11/image.png" alt=""></p>
<p>한편, 막학기를 앞두고 친구들과 여름 여행을 다녀오며 학기의 시작을 준비했습니다. 짧은 여행이었지만, 새로운 에너지를 얻는 데 큰 도움이 되었습니다.</p>
<h3 id="9월">9월</h3>
<p>9월부터는 드디어 마지막 학기가 시작되었습니다. 본격적으로 취업 전략을 고민하며 이력서를 보강했고, 그 결과 몇몇 기업에서 서류 통과라는 쾌거를 이루었습니다. 하지만 중간 면접에서 부족함을 느끼거나 코딩 테스트에서 만족스러운 결과를 얻지 못해 최종 합격에는 이르지 못했습니다.</p>
<p>이때 정보처리기사 시험 결과가 발표되었고, 합격이라는 좋은 소식으로 자격증을 취득할 수 있었습니다.
<img src="https://velog.velcdn.com/images/muman_kim/post/95788341-1e11-4cf7-bde7-a0d4b675c4cf/image.png" alt=""></p>
<p>특히, 토스의 코딩 테스트는 제 기준으로 매우 어려웠습니다. 문제를 풀 수는 있었지만, 각 케이스를 해결하기 위한 경험이 부족하다 보니 시간이 오래 걸렸습니다. &quot;이것이 주니어의 기준인가?&quot;라는 생각에 한동안 흔들렸지만, 토스와 같은 회사가 빠른 기획을 소화하기 위해 경험 많은 사람을 원한다는 점을 인정하고, 저 또한 이를 목표로 더 준비하려고 다짐했습니다.</p>
<h3 id="10월">10월</h3>
<p>10월은 학기 중간고사와 함께 더욱 바쁜 나날이었습니다. 마지막 학기이기에 다양한 기술을 배워야겠다는 결심으로 과목을 많이 신청했는데, 조금 후회도 되었습니다.
<img src="https://velog.velcdn.com/images/muman_kim/post/66891d23-745a-4425-884d-140d42ff07dc/image.png" alt=""></p>
<p>특히, 인공지능과 Unity를 활용한 팀 프로젝트가 많아 시간 관리가 쉽지 않았습니다. 팀플, 동아리 개발, 이력서 작성 및 취업 준비를 병행하면서 시간이 빠르게 흘렀습니다. 바쁜 와중에도 동아리 회식과 MT에 참여하며 마지막 대학교 생활의 청춘을 만끽했습니다.</p>
<h3 id="11월">11월</h3>
<p>11월에는 중간 이력서 제출 결과들이 하나둘 도착하기 시작했습니다. 몇몇 기업에서는 면접 기회가 주어졌고, 서울을 오가며 바쁘게 움직였습니다.
<img src="https://velog.velcdn.com/images/muman_kim/post/139045f8-fe4d-4f68-aa23-36ee13be5811/image.png" alt=""></p>
<p>면접 준비와 함께 이력서를 다시 다듬으며 자아 성찰의 시간을 가졌습니다. 더 나은 결과를 위해 노력하며 팀 프로젝트를 잠시 미뤄야 했던 순간도 있었지만, 취업을 향한 집중력을 키울 수 있었습니다. 이 기간은 제게 &quot;노력한 만큼 기회가 온다&quot;는 사실을 다시금 깨닫게 해준 시간이었습니다.</p>
<h3 id="12월">12월</h3>
<p>12월에도 결과 소식이 나오지 않은 경우가 있어 아쉬웠지만, 언제나 다음 스텝을 생각하며 준비하고 있습니다. 종강 후 친구들과 함께 마지막 종강 파티를 열며 대학교 생활을 마무리했습니다.</p>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/76cd17ee-3b4e-44a2-8f32-9da908cc54d2/image.png" alt=""></p>
<p>성적도 만족스러운 결과로 마무리되었고, 졸업을 위한 영어 성적 준비를 위해 영어 공부를 시작했습니다. 요즘 세상의 흐름을 보니 영어 실력은 필수라는 생각이 강해졌습니다.</p>
<p>또한, 올해 면접 경험이 많지 않아 아쉬웠지만, 저의 경험을 어필할 수 있는 이력서를 다듬고 기술 면접에서 자신감을 키우기 위해 꾸준히 노력하고 있습니다.</p>
<h2 id="하반기-총평">하반기 총평</h2>
<p>상반기는 계획대로 흘러갔지만, 하반기는 기대했던 만큼 결과를 내지 못해 아쉬움이 많았습니다. 하지만 서류 통과율이 점점 높아지고, 면접 경험을 쌓아가면서 다음 스텝으로 나아가는 것을 느낄 수 있었습니다.
<img src="https://velog.velcdn.com/images/muman_kim/post/e2af795c-0d6a-40c1-800c-7a955526dc05/image.png" alt=""></p>
<p>결과를 기다리는 기업도 있지만, 항상 다음 기회를 생각하며 대비하고 있습니다. 취업 시장이 얼어붙어 있지만, 저는 차갑게 얼어붙은 길 위에서도 스케이트를 타듯 유연하게 나아가려고 합니다. 이번 하반기는 부족했던 점을 채우는 데 집중했고, 앞으로의 길을 더 단단히 준비하겠다는 결심을 다진 시간이었습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[글또 10기 - Suspense가 뭐길래 useSuspenseQuery까지 나옴?]]></title>
            <link>https://velog.io/@muman_kim/%EA%B8%80%EB%98%90-10%EA%B8%B0-Suspense%EA%B0%80-%EB%AD%90%EA%B8%B8%EB%9E%98-useSuspenseQuery%EA%B9%8C%EC%A7%80-%EB%82%98%EC%98%B4</link>
            <guid>https://velog.io/@muman_kim/%EA%B8%80%EB%98%90-10%EA%B8%B0-Suspense%EA%B0%80-%EB%AD%90%EA%B8%B8%EB%9E%98-useSuspenseQuery%EA%B9%8C%EC%A7%80-%EB%82%98%EC%98%B4</guid>
            <pubDate>Sun, 24 Nov 2024 06:11:51 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가며">들어가며</h2>
<p>최근에 한기대 코인 프로젝트를 진행하며 API 로직에 대한 변화가 있었습니다. tanStack-Query를 통해 useQuery로 API를 관리하고 있었는데, useSuspenseQuery로 변경하는 작업을 진행하고 있습니다.
처음에는 이 useSuspenseQuery가 뭐지에 대해 고민하다 Suspense를 제가 실제로 많이 다룬 경험이 없더군요. 그래서 한번 Suspense 깔끔하게 정리하면서 useQuery보다 useSuspenseQuery를 도입하게 된 이유를 서술하면서 이번 글을 마무리해보려 합니다.</p>
<h2 id="what-is-suspense">What is Suspense?</h2>
<blockquote>
<p><code>&lt;Suspense&gt;</code> 는 자식 요소가 로드되기 전까지 화면에 대체 UI를 보여줍니다. 
from. <a href="https://ko.react.dev/reference/react/Suspense">React 공식 문서</a></p>
</blockquote>
<pre><code class="language-javascript">&lt;Suspense fallback={&lt;Loading /&gt;}&gt;  
    &lt;SomeComponent /&gt;  
&lt;/Suspense&gt;</code></pre>
<p>React의 <strong>Suspense</strong>는 데이터 로딩, 코드 분할 등과 같은 비동기 작업을 보다 직관적이고 깔끔하게 처리하기 위해 고안된 기능입니다. 기존의 로딩 상태 관리 방식은 종종 복잡한 조건문과 state 관리를 요구했으나, Suspense는 이러한 문제를 단순화합니다.</p>
<p>쉽게 위 코드를 해석하면 <code>&lt;Suspense&gt;</code> 태그에 감싸진 자식 요소에 필요한 모든 코드와 데이터가 로드될 때까지 fallback에 선언된 컴포넌트를 보여주게 됩니다.
이 Suspense의 기능을 요약하면 다음과 같습니다.</p>
<ul>
<li><p><strong>Suspense의 주요 역할</strong></p>
<ol>
<li>컴포넌트가 비동기 작업을 완료할 때까지 UI 렌더링을 유예.</li>
<li>로딩 상태를 컴포넌트 수준에서 간결하게 처리 가능.</li>
<li>다양한 데이터 소스 및 동적 import를 한 곳에서 관리.</li>
</ol>
</li>
<li><p><strong>기존 방식과의 차이점</strong><br>  기존에는 <code>isLoading</code>이나 <code>isFetching</code> 같은 상태 플래그를 사용해 데이터가 준비되었는지 확인하고 UI를 조건부 렌더링했습니다. 반면, Suspense는 이 모든 과정을 단순화해 fallback 컴포넌트를 통해 로딩 상태를 처리할 수 있습니다.</p>
</li>
</ul>
<h2 id="suspense가-갖는-현대-웹에서의-의미">Suspense가 갖는 현대 웹에서의 의미</h2>
<p><code>Suspense</code> 가 도입된 것은 18년도 처음으로 리액트 16에서 시범적인 기능으로 들어오게 되었지만, 리액트 18버전으로 넘어오면서 정식 기능으로 출시되었습니다. 이 기능이 갖게 되는 의미를 좀 생각해보면 저는 비동기 처리에 대해서 선언적인 기능이 추가되었다는 좋은 기능이라고 생각합니다.</p>
<h3 id="비동기-처리에-대한-선언적-처리">비동기 처리에 대한 선언적 처리</h3>
<p>Suspense는 복잡한 비동기 데이터 처리를 더 선언적이고 간단한 방식으로 관리할 수 있도록 도와줍니다.
이전에 데이터를 처리하기 위해서는 <code>useEffect</code>나 <code>try..catch</code>와 같은 비동기 작업 중간에 제어할 수 있는 기능을 통해 로직을 선언적으로 처리하여 좀 더 관리가 편해졌다고 생각합니다.</p>
<p>아래에 코드를 통해 2개의 차이를 보시면 좋을 것 같습니다.</p>
<pre><code class="language-javascript">import { useQuery } from &quot;react-query&quot;;

async function fetchData() {
  const response = await fetch(&quot;https://api.example.com/data&quot;);
  if (!response.ok) throw new Error(&quot;Failed to fetch data&quot;);
  return response.json();
}

function MyComponent() {
  const { data, isLoading, isError, error } = useQuery(&quot;fetchData&quot;, fetchData);

  if (isLoading) return &lt;div&gt;Loading...&lt;/div&gt;; // 로딩 상태 처리
  if (isError) return &lt;div&gt;Error: {error.message}&lt;/div&gt;; // 에러 상태 처리

  return &lt;div&gt;Data: {data.title}&lt;/div&gt;; // 데이터 렌더링
}

function MySuspenseComponent() { 
  const { data } = useQuery(&quot;fetchData&quot;, fetchData, { suspense: true }); 
  return &lt;div&gt;Data: {data.title}&lt;/div&gt;;
}</code></pre>
<p>ErrorBoundary와 Suspense를 통해 데이터를 받고 로딩과 에러 처리까지 선언적 처리가 아래와 같이 가능해집니다.</p>
<pre><code class="language-javascript">function App() { 
    return ( 
        &lt;ErrorBoundary FallbackComponent={({ error }) =&gt; &lt;div&gt;Error: {error.message}&lt;/div&gt;}&gt; 
        &lt;React.Suspense fallback={&lt;div&gt;Loading...&lt;/div&gt;}&gt; 
            &lt;MySuspenseComponent /&gt; 
        &lt;/React.Suspense&gt; 
    &lt;/ErrorBoundary&gt; 
    ); 
}</code></pre>
<p>이렇게 선언적 처리를 통해 에러 처리가 가능해진다면, 비동기 데이터를 처리하면서 발생할 수 있는 불필요한 로딩 지연이나 깜박임(FLIP 현상)을 효과적으로 줄여준다는 사용자 경험적으로도 이점이 있습니다.</p>
<h2 id="suspense-도입의-고려-사항">Suspense 도입의 고려 사항</h2>
<p>Suspense가 좋다고 생각하거나 위와 같은 고민을 하셔서 도입하려면 기존 코드베이스에 몇 가지 중요한 변경 작업이 필요합니다.</p>
<ol>
<li><p><strong>React 18 버전 이상</strong>
  기본적으로 Suspense 로직이 React 18버전 이상부터 도입되었기에 이를 사용하기 위해서는 React 18이 필수적입니다. 혹여나 레거시 프로젝트에 적용한다면 먼저 React를 올려둬야합니다.</p>
</li>
<li><p><strong>ErrorBoundary 설정</strong><br> Suspense는 비동기 작업에서 에러를 처리하기 위해 반드시 ErrorBoundary와 함께 사용해야 합니다. 이를 통해 비동기 작업 실패 시 graceful fallback을 제공할 수 있습니다.</p>
</li>
<li><p><strong>Suspense fallback 구성</strong><br> 로딩 중 상태를 보여주기 위한 fallback UI를 정의해야 합니다. 예를 들어, <code>&lt;Suspense fallback={&lt;LoadingSpinner /&gt;}&gt;</code>를 사용하여 로딩 상태를 명시적으로 표현합니다.</p>
</li>
<li><p><strong>성능 및 사용자 경험</strong><br> Suspense를 사용하면 비동기 로직이 간소화되지만, fallback UI를 적절히 설계하지 않으면 사용자 경험에 부정적인 영향을 줄 수 있습니다. 이를 위해 성능 최적화 및 로딩 시간을 최소화하는 작업이 필요합니다.</p>
</li>
</ol>
<h2 id="이번-글을-쓰며">이번 글을 쓰며</h2>
<p>작업을 진행하며 <code>useQuery</code>만 사용하던 기존 방식에서 <code>useSuspenseQuery</code>를 도입하게 된 것은 개인적으로도 큰 변화였습니다. <code>&lt;Suspense&gt;</code>를 활용해 선언적으로 비동기 데이터를 처리하는 경험은 단순히 새로운 API를 적용하는 것을 넘어, 현대적인 웹 개발의 시점에서 이를 되돌아 보았습니다.
아직  <code>&lt;Suspense&gt;</code>가 익숙하지 않은 부분도 있지만, 이번 프로젝트를 통해 현대적인 비동기 처리 방식에 대한 이해를 확장할 수 있었던 만큼, 앞으로 프로젝트에서 기능의 목적을 생각해 가장 잘 활용할 수 있는 접근 방식을 찾아봐야겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[글또 10기 - 캐시 써도 되는거죠? 지금 써야되지 않을까요?]]></title>
            <link>https://velog.io/@muman_kim/%EA%B8%80%EB%98%90-10%EA%B8%B0-%EC%BA%90%EC%8B%9C-%EC%8D%A8%EB%8F%84-%EB%90%98%EB%8A%94%EA%B1%B0%EC%A3%A0-%EC%A7%80%EA%B8%88-%EC%8D%A8%EC%95%BC%EB%90%98%EC%A7%80-%EC%95%8A%EC%9D%84%EA%B9%8C%EC%9A%94</link>
            <guid>https://velog.io/@muman_kim/%EA%B8%80%EB%98%90-10%EA%B8%B0-%EC%BA%90%EC%8B%9C-%EC%8D%A8%EB%8F%84-%EB%90%98%EB%8A%94%EA%B1%B0%EC%A3%A0-%EC%A7%80%EA%B8%88-%EC%8D%A8%EC%95%BC%EB%90%98%EC%A7%80-%EC%95%8A%EC%9D%84%EA%B9%8C%EC%9A%94</guid>
            <pubDate>Sat, 09 Nov 2024 14:22:30 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가며">들어가며</h2>
<p>저번에 최적화에 대한 이야기를 하면서 캐싱이라는 단어를 언급한 적이 있었습니다. 그런데 문득 “과연 나는 캐싱을 제대로 알고 있을까?”라는 생각이 들었고, 지금까지 웹 개발을 하며 캐싱과 관련된 다양한 경험들을 되짚어보기로 했습니다. React Query 같은 라이브러리에서도 캐싱을 자주 접하게 되는데, 이 기회를 통해 <strong>프론트엔드에서 캐싱이 어떤 방식으로 활용되는지</strong> 면밀하게 분석하고, 그 내용을 여러분께도 공유하고자 합니다. 이번 글이 캐싱에 대한 실질적인 이해와 도움을 줄 수 있기를 바라면서 시작합니다.</p>
<h2 id="캐싱이란">캐싱이란?</h2>
<p><strong>캐싱(Cache)</strong>은 자주 접근하는 데이터를 미리 저장해 두고, 다시 필요할 때 빠르게 가져오는 방식입니다. 네트워크 요청을 매번 새로 하지 않고, 이전 데이터를 재사용함으로써 성능을 최적화하고 응답 속도를 향상시키는 것이 주요 목적입니다. 캐싱은 데이터 소모를 줄여주고, 특히 사용자 경험을 높이는 데 큰 역할을 합니다. 예를 들어, 매번 같은 이미지를 로드하기보다 캐시에 저장해 두었다가 재사용하면 훨씬 빠르게 화면을 렌더링할 수 있습니다.</p>
<p>캐싱이 필요한 이유는 크게 두 가지로 요약됩니다:</p>
<ol>
<li><strong>빠른 데이터 접근</strong>: 자주 사용하는 데이터를 미리 준비해 두어 불필요한 요청을 줄임.</li>
<li><strong>비용 절감 및 리소스 최적화</strong>: 데이터 요청 빈도를 줄여 서버와 네트워크 자원을 아끼고, 사용자의 로딩 대기 시간을 줄임.</li>
</ol>
<h2 id="frontend에서의-캐싱">FrontEnd에서의 캐싱</h2>
<p>프론트엔드 개발자가 캐싱을 이해하고 사용할 때 중요한 점은, <strong>캐싱의 범위와 목적을 고려하여 필요한 캐싱 전략을 선택하는 것</strong>입니다. 데이터가 어떤 특성을 가지고 있고, 어디에 저장되느냐에 따라 캐싱 방식이 달라지기 때문입니다.</p>
<p>프론트엔드에서 캐싱을 분류하는 방법으로는 <strong>상태 기반 캐싱, 웹 캐싱, 외부 서버 캐싱</strong>의 세 가지가 있습니다. 각 방식은 서로 다른 특성을 가지며, 특정 상황에서 효과적으로 활용할 수 있습니다.</p>
<ol>
<li><strong>상태 기반 캐싱</strong>은 애플리케이션에서 자주 호출되는 데이터를 상태로 관리하면서, 중복 요청을 줄여 컴포넌트 간 효율적인 데이터 흐름을 유지합니다.</li>
<li><strong>웹 캐싱</strong>은 브라우저와 서버의 상호작용을 최적화하는 것으로, 페이지를 미리 렌더링하거나 브라우저 캐시를 활용해 불필요한 네트워크 요청을 줄이는 방식입니다.</li>
<li><strong>외부 서버 캐싱</strong>은 대규모 애플리케이션에서 빠른 데이터 접근을 보장하고, 여러 지역에 분산된 사용자에게 빠르게 서비스를 제공할 수 있게 해 줍니다.</li>
</ol>
<p>이제, 각각의 캐싱 방식이 어떤 역할을 하고 어떻게 활용되는지 자세히 살펴보겠습니다.</p>
<h2 id="1-상태-기반에서의-캐싱">1. 상태 기반에서의 캐싱</h2>
<p>상태 기반 캐싱은 애플리케이션에서 자주 요청하는 API 데이터나 컴포넌트 상태를 효율적으로 관리하기 위한 방식입니다. 예를 들어, <code>React Query</code>와 같은 라이브러리를 사용하면 서버 데이터를 상태로 관리하면서도, <strong>캐싱을 통해 불필요한 네트워크 요청을 줄일 수 있습니다</strong>. 캐싱된 데이터는 일정 시간 이후 자동으로 갱신되거나, 사용자 인터랙션이 발생할 때 갱신될 수 있어 매우 유연하게 동작합니다.</p>
<h3 id="react-query--swr">React Query / SWR</h3>
<p>클라이언트에서 서버 데이터를 캐싱하고 필요한 경우만 다시 요청하여 성능을 높입니다.
사용자 프로필 데이터를 React Query로 관리하면 컴포넌트가 언마운트된 후에도 데이터가 캐시에 남아, 다른 컴포넌트에서 중복된 API 호출을 방지합니다.</p>
<pre><code class="language-javascript">import { useQuery } from &#39;react-query&#39;;

function UserProfile({ userId }) {
  const { data: user, isLoading } = useQuery([&#39;user&#39;, userId], () =&gt;
    fetch(`/api/users/${userId}`).then((res) =&gt; res.json())
  );

  if (isLoading) return &lt;p&gt;Loading...&lt;/p&gt;;
  return &lt;div&gt;{user.name}&lt;/div&gt;;
}</code></pre>
<p><a href="https://tanstack.com/query/latest/docs/framework/react/guides/caching#caching-examples">Tanstack Query Documentation - Caching</a>
<a href="https://swr.vercel.app/ko/docs/advanced/understanding.ko#fetch-and-revalidate">SWR Documentation - Fetch and Revalidation</a></p>
<h2 id="2-웹에서의-캐싱">2. 웹에서의 캐싱</h2>
<p>웹 캐싱은 <strong>브라우저 캐싱</strong>과 <strong>서버에서의 미리 렌더링된 캐싱</strong>으로 나눌 수 있습니다. 웹에서의 캐싱은 주로 네트워크 요청을 줄이고 로딩 시간을 단축하여 <strong>사용자 경험을 개선</strong>하는 데 중점을 둡니다. 특히 <strong>Next.js의 프리렌더링(SSG, ISR)과 프리페칭/프리로딩</strong>은 각각 다른 방식으로 웹 성능 최적화를 달성하며, 넓은 의미에서 캐싱과 유사한 효과를 낼 수 있습니다.</p>
<h3 id="브라우저-캐싱">브라우저 캐싱</h3>
<p>브라우저는 Cache-Control 헤더와 ETag를 통해 캐싱 정책을 설정합니다. 예를 들어, Cache-Control 헤더를 통해 리소스의 만료 시간을 지정하여, 일정 시간 동안 리소스를 캐싱할 수 있습니다.</p>
<p><strong>예시</strong>: 웹 서버의 정적 자원에 <code>Cache-Control: max-age=3600</code>을 설정하여 한 시간 동안 브라우저 캐시에 유지할 수 있습니다.</p>
<pre><code class="language-JS">Cache-Control: max-age=3600
ETag: &quot;abc123&quot; // 브라우저가 리소스를 다시 요청할 때, 서버와 비교하여 최신 여부를 판단합니다.</code></pre>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control">MDN - Cache-Control</a><br><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag">MDN - ETag</a></p>
<h3 id="프리페칭과-프리로딩">프리페칭과 프리로딩</h3>
<p><strong>프리페칭(Prefetching)</strong>은 <strong>다음에 필요할 리소스를 미리 가져오는 방식</strong>입니다. 
예를 들어, Next.js의 <code>Link</code> 컴포넌트에서 <code>prefetch</code> 속성을 설정하면, 사용자가 현재 페이지에 있을 때 다음 페이지에 필요한 리소스를 미리 로드합니다. 이를 통해 <strong>빠른 페이지 전환</strong>이 이루어질 수 있습니다.</p>
<pre><code class="language-html">&lt;Link href=&quot;/profile&quot; prefetch={true}&gt; // 다음 페이지 리소스를 미리 로드
  &lt;a&gt;Go to Profile&lt;/a&gt;
&lt;/Link&gt;</code></pre>
<p><strong>프리로딩(Preloading)</strong>은 <strong>현재 페이지의 초기 로딩 속도를 높이기 위해 중요한 리소스를 미리 로드하는 방식</strong>입니다. 크리티컬 CSS, 자바스크립트 파일, 이미지 등을 프리로딩하면, 페이지가 완전히 렌더링되기 전에 필요한 자원들을 미리 확보하여 <strong>빠르게 화면을 표시</strong>할 수 있습니다.</p>
<p><a href="https://nextjs.org/docs/app/building-your-application/routing/linking-and-navigating#2-prefetching">Next.js Documentation - Prefetching</a><br><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload">MDN - Resource Hints (preload)</a></p>
<h3 id="nextjs의-프리렌더링-ssg-isr">Next.js의 프리렌더링 (SSG, ISR)</h3>
<p>Next.js에서는 <strong>프리렌더링을 통해 페이지를 미리 생성</strong>하여 사용자에게 빠르게 제공합니다. 이 방식은 초기 로딩 속도를 높이는 데 매우 효과적이며, 캐싱과 비슷한 역할을 합니다.</p>
<p><strong>SSG (Static Site Generation)</strong>은 <strong>빌드 시점에 HTML을 미리 생성</strong>하여 CDN에 저장하고, 모든 사용자에게 동일한 정적 콘텐츠를 제공합니다. 주로 자주 변경되지 않는 콘텐츠에 적합하며, 서버의 부하를 줄이고 로딩 속도를 극대화할 수 있습니다.</p>
<p><strong>예시</strong>: 블로그 글처럼 고정된 콘텐츠는 SSG를 통해 미리 생성해 두고, 사용자에게 빠르게 전달할 수 있습니다.</p>
<pre><code class="language-javascript">export async function getStaticProps() {
  const res = await fetch(&#39;https://api.example.com/posts&#39;);
  const posts = await res.json();

  return {
    props: { posts },
  };
}</code></pre>
<p><strong>ISR (Incremental Static Regeneration)</strong>은 SSG의 변형으로, <strong>정적 페이지를 주기적으로 갱신</strong>하는 방식입니다. 일정 시간이 지나면 서버가 페이지를 다시 생성하여 최신 데이터를 반영합니다. 이는 실시간 데이터가 필요한 페이지에서도 정적 페이지의 속도를 유지할 수 있게 합니다.</p>
<p><strong>예시</strong>: 뉴스 사이트의 헤드라인처럼 데이터가 자주 변경되지만 매번 실시간 데이터를 불러올 필요가 없는 경우 ISR을 사용해 성능을 최적화할 수 있습니다.</p>
<pre><code class="language-javascript">export async function getStaticProps() {
  const res = await fetch(&#39;https://api.example.com/posts&#39;);
  const posts = await res.json();

  return {
    props: { posts },
    revalidate: 60, // 매 60초마다 페이지를 갱신
  };
}</code></pre>
<p><a href="https://nextjs.org/docs/app/api-reference/next-config-js/staticGeneration">Next.js Documentation - Static Generation</a><br><a href="https://nextjs.org/docs/app/building-your-application/data-fetching/incremental-static-regeneration">Next.js Documentation - Incremental Static Regeneration</a></p>
<h2 id="3-외부-서버를-통한-캐싱">3. 외부 서버를 통한 캐싱</h2>
<p>외부 서버 캐싱은 <strong>CDN이나 Redis</strong>와 같은 외부 서버를 사용해 데이터를 캐싱하여 전체 애플리케이션 성능을 최적화하는 방식입니다. 특히 대규모 트래픽이 발생하는 애플리케이션에서는 CDN과 Redis를 함께 사용하여 <strong>정적 콘텐츠와 동적 데이터 모두에서 고속의 응답</strong>을 제공할 수 있습니다. 이를 통해 서버 부하를 줄이고 사용자에게 빠른 데이터를 전달할 수 있습니다.</p>
<h3 id="cdn-content-delivery-network">CDN (Content Delivery Network)</h3>
<p><strong>CDN</strong>은 전 세계 여러 위치의 엣지 서버에 <strong>정적 자원(이미지, CSS, JavaScript 파일 등)을 분산</strong>하여 캐싱하는 방식입니다. 사용자는 <strong>자신과 가까운 위치의 서버에서 리소스를 제공받기 때문에, 지연 시간이 줄어들고 빠른 응답이 가능</strong>해집니다.</p>
<p><strong>예시</strong>: 글로벌 커머스 웹사이트에서 제품 이미지와 CSS 파일을 CDN에 캐싱합니다. 사용자가 웹사이트를 방문하면, CDN 서버에서 가까운 위치의 이미지와 스타일 시트를 제공받아 페이지 로딩 속도가 빨라집니다.</p>
<p><strong>사용 코드 예시</strong> (Next.js와 CDN을 연동해 정적 파일을 제공):</p>
<pre><code class="language-javascript">// next.config.js
module.exports = {
  images: {
    domains: [&#39;cdn.example.com&#39;], // CDN 도메인 등록
  },
};

// 페이지 컴포넌트에서 이미지 사용
import Image from &#39;next/image&#39;;

function ProductPage() {
  return (
    &lt;div&gt;
      &lt;Image
        src=&quot;https://cdn.example.com/product-image.jpg&quot;
        alt=&quot;Product Image&quot;
        width={500}
        height={500}
      /&gt;
    &lt;/div&gt;
  );
}</code></pre>
<h3 id="redis를-통한-데이터베이스-캐싱">Redis를 통한 데이터베이스 캐싱</h3>
<p><strong>Redis</strong>는 <strong>동적 데이터</strong>나 <strong>실시간 API 응답</strong>을 캐싱하는 데 적합합니다. Redis는 메모리 기반 데이터 저장소이므로, 데이터베이스에 접근하지 않고도 메모리에서 즉시 데이터를 제공하여 <strong>반복적인 데이터베이스 요청을 줄일 수 있습니다</strong>.</p>
<p>외부 캐시 서버로 Redis를 선택하는 이유가 많더군요? 실제 제가 개발한 경험이 없어 코드 예시는 의미가 없다고 생각해서 Redis가 선택되는 이유에 대해 몇가지 조사를 해봤습니다.</p>
<blockquote>
<ul>
<li><p><strong>고속 데이터 접근</strong>: Redis는 메모리 기반 데이터 저장소이므로, 디스크 기반 데이터베이스보다 <strong>훨씬 빠르게 데이터에 접근</strong>할 수 있습니다. 이를 통해 API 응답 시간을 줄이고, 사용자에게 실시간 데이터를 제공하는 데 유리합니다.</p>
</li>
<li><p><strong>데이터베이스 부하 감소</strong>: 자주 요청되는 데이터를 Redis에 캐싱하면 데이터베이스에 대한 요청 수를 줄일 수 있어, <strong>데이터베이스의 부하가 줄어들고 안정성</strong>이 높아집니다.</p>
</li>
<li><p><strong>간단한 데이터 구조 지원</strong>: Redis는 문자열, 리스트, 셋, 해시 등 다양한 데이터 구조를 지원합니다. </p>
</li>
<li><p><strong>만료 시간 설정</strong>: Redis는 데이터를 일정 시간 동안만 유지하도록 설정할 수 있어, <strong>세션 관리나 캐시 데이터의 만료</strong>를 비교적 쉽게 관리할 수 있습니다. 이는 실시간 데이터가 빈번히 갱신될 때 유용합니다.</p>
</li>
</ul>
</blockquote>
<p>다소 얕은 지식으로 조사한 내용이기에 혹시나 제가 잘못 조사한 부분이 있다면 댓글로 알려주세요!</p>
<h2 id="캐싱에-대한-생각-캐싱은-결국-비용">캐싱에 대한 생각: 캐싱은 결국 비용</h2>
<p>캐싱 전략은 성능 최적화에 큰 효과가 있지만, 이를 위한 <strong>비용</strong>을 이해하고 관리하는 것이 필수적입니다. 캐싱이 불러오는 <strong>저장 공간, 관리 복잡성, 무효화 비용</strong> 등의 문제는 단순히 캐시를 사용한다고 해서 해결되지 않습니다. 따라서 다음과 같은 비용 요소를 신중히 고려해야 합니다.</p>
<ol>
<li><strong>저장 공간 비용</strong>: 캐싱할 데이터의 양이 많아질수록 캐시에 필요한 저장 공간이 늘어나며, 특히 서버 캐시를 사용할 때는 비용이 증가합니다.</li>
<li><strong>무효화 비용</strong>: 캐시된 데이터가 오래될 경우, 이를 갱신하거나 무효화해야 하며, 이 과정에서 원본 데이터베이스 접근 비용이 발생합니다.</li>
<li><strong>복잡성 증가</strong>: 캐시 시스템이 추가되면 데이터 일관성을 유지하기 위한 로직과 테스트가 필요해 코드 관리가 복잡해집니다.</li>
</ol>
<p>캐싱의 효과를 극대화하면서도 비용을 줄일 수 있는 최적의 전략을 찾는 것이 중요합니다. 
프론트엔드 개발자들이 캐싱을 무작정 사용하기 보다는 실제 캐싱의 성능과 비용을 균형 있게 고려하고 사용할 수 있는데에 도움이 되었으면 합니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 최적화의 빨간 약: 메모이제이션과 클로저]]></title>
            <link>https://velog.io/@muman_kim/React-%EC%B5%9C%EC%A0%81%ED%99%94%EC%9D%98-%EB%B9%A8%EA%B0%84-%EC%95%BD-%EB%A9%94%EB%AA%A8%EC%9D%B4%EC%A0%9C%EC%9D%B4%EC%85%98%EA%B3%BC-%ED%81%B4%EB%A1%9C%EC%A0%80</link>
            <guid>https://velog.io/@muman_kim/React-%EC%B5%9C%EC%A0%81%ED%99%94%EC%9D%98-%EB%B9%A8%EA%B0%84-%EC%95%BD-%EB%A9%94%EB%AA%A8%EC%9D%B4%EC%A0%9C%EC%9D%B4%EC%85%98%EA%B3%BC-%ED%81%B4%EB%A1%9C%EC%A0%80</guid>
            <pubDate>Wed, 23 Oct 2024 00:44:35 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가며">들어가며...</h2>
<p>리액트를 사용할 때, 성능 최적화는 중요한 과제 중 하나입니다. 작은 규모의 애플리케이션에서는 크게 체감되지 않겠지만, 컴포넌트 구조가 복잡해지고 데이터가 자주 변동하는 상황에서는 불필요한 렌더링이 사용자 경험에 영향을 미칠 수 있습니다. 이런 이유로 성능 최적화가 필요한 시점을 잘 판단하고, 적절한 기법을 사용하는 것이 중요합니다. </p>
<p>그렇다면 여러분들은 어떻게 최적화 하시나요? 여러분들의 머릿속에 위와 같은 상황에서 사용할만한 방식들은 여러가지일 수 있지만, 저는 리액트와 관련되어서 최적화에 대한 진실을 배우고 이에 대한 실제로 어떤 진실이 있는지 빨간약을 먹기 위해 글을 작성해보곘습니다.</p>
<h3 id="리액트에서-최적화가-필요한-경우">리액트에서 최적화가 필요한 경우</h3>
<p>리액트 애플리케이션에서 최적화가 필요한 대표적인 상황은 다음과 같습니다:</p>
<ul>
<li><strong>상위 컴포넌트의 상태 변화</strong>로 인해 하위 컴포넌트가 불필요하게 다시 렌더링될 때.</li>
<li><strong>비용이 많이 드는 계산</strong>(예: 복잡한 연산, API 호출)이나 데이터 처리가 자주 발생할 때.</li>
<li>대량의 데이터를 <strong>렌더링하거나 필터링</strong>하는 경우 성능 저하가 발생할 때.</li>
</ul>
<p>이런 상황에서 자주 사용하는 것이 바로 <code>useCallback</code>과 <code>useMemo</code>입니다.</p>
<p>21년도 React Conference에서 소개된 테마 색상을 변경할 수 있는 기능이 추가된 간단한 Todo List에서의 경우를 보여드리겠습니다.</p>
<p><img src="https://i.imgur.com/gJUQahL.png" alt=""></p>
<p>위의 코드에서 themeColor가 드래그를 통해 변경하면 themeColor를 상속 받고 있는 TodoList가 불필요하게 계속해서 재렌더링되는 성능 이슈가 발생합니다.
이에 대해서 <code>memoization</code>이나 <code>debounce</code>등의 테크닉을 활용하는게 좋아 보인다고 생각하고 우리는 열심히 구현할 것입니다.</p>
<p>구현 예시 : <a href="https://www.youtube.com/watch?v=lGEMwh32soc">React Conf: React without memo</a></p>
<h3 id="최적화를-위한-usecallback과-usememo">최적화를 위한 useCallback과 useMemo</h3>
<p>React로 개발하면서 위에 제시된 최적화가 필요한 상황을 맞이하였을 때, 생각할 수 있는 것은 여러가지가 있을 수 있습니다. <code>Throttle</code>, <code>debounce</code> 같은 처리방식도 있고, React 환경에서는 <code>useCallback</code>, <code>useMemo</code>와 같은 메모이제이션 훅을 통해 성능을 향상시킬 수 있습니다.</p>
<p>간단하게 두 개의 훅이 어떤 것인지 정리해보겠습니다.</p>
<blockquote>
<ol>
<li>useCallback
<code>useCallback</code>은 <strong>함수를 메모이제이션</strong>하여, 의존성 배열이 변경되지 않는 한 동일한 함수 객체를 반환합니다. 주로 자식 컴포넌트에 함수를 props로 넘길 때, 불필요한 리렌더링을 방지하기 위해 사용합니다</li>
</ol>
</blockquote>
<pre><code class="language-js">const memoizedCallback = useCallback(() =&gt; {
  doSomething(a, b);
}, [a, b]);</code></pre>
<blockquote>
<ol start="2">
<li>useMemo
<code>useMemo</code>는 <strong>값을 메모이제이션</strong>하여, 의존성 배열이 변경되지 않는 한 계산을 다시 하지 않고 저장된 값을 반환합니다. 주로 <strong>비용이 많이 드는 계산</strong>을 반복하지 않도록 할 때 사용합니다.</li>
</ol>
</blockquote>
<pre><code class="language-js">const memoizedValue = useMemo(() =&gt; computeExpensiveValue(a, b), [a, b]);</code></pre>
<p>둘 다 성능 최적화 용도로 메모이제이션을 활용해서 사용되지만, <strong>useCallback은 함수 재생성 방지</strong>에, <strong>useMemo는 값 재계산 방지</strong>에 초점이 맞춰져 있습니다.</p>
<h2 id="메모이제이션의-숨은-비용-클로저와-메모리">메모이제이션의 숨은 비용: 클로저와 메모리</h2>
<p>메모이제이션은 성능 최적화를 위해 자주 사용되는 기술이지만, 그 이면에는 메모리 사용과 관련된 숨은 비용이 존재합니다. 특히 클로저와 메모리 관리 측면에서 주의할 필요가 있습니다.</p>
<h3 id="클로저와-메모이제이션의-관계">클로저와 메모이제이션의 관계</h3>
<p>React의 <code>useMemo</code>와 <code>useCallback</code> 훅은 컴포넌트가 <strong>재렌더링될 때마다 불필요한 재계산을 방지</strong>하기 위해 메모이제이션을 사용합니다. 이를 위해 클로저를 활용해 이전 값을 기억하고, 필요 시 저장된 값을 다시 사용할 수 있게 합니다.</p>
<p>아래 <code>useMemo</code>의 React 내부 구현 예시에서 클로저가 어떻게 작동하는지 살펴보겠습니다.</p>
<pre><code class="language-ts">function mountMemo&lt;T&gt;(
  nextCreate: () =&gt; T, // 메모이제이션할 값을 생성하는 함수
  deps: Array&lt;mixed&gt; | void | null, // 의존성 배열
): T {
  const hook = mountWorkInProgressHook(); 
  // 현재 훅의 상태를 가져옵니다. 이 훅의 상태는 클로저로 묶인 데이터입니다.

  const nextDeps = deps === undefined ? null : deps; 
  // 의존성 배열을 처리합니다. 이 배열은 클로저 안에서 기억되고, 렌더링마다 참조됩니다.

  const nextValue = nextCreate(); 
  // `nextCreate`는 클로저로 감싸져 이전 환경에 있는 값들에 접근할 수 있습니다. 
  // 여기서 새로운 값을 계산합니다.

  hook.memoizedState = [nextValue, nextDeps]; 
  // 새로 계산된 값(nextValue)과 의존성 배열(nextDeps)을 저장하여, 다음 렌더링 시 사용합니다.
  return nextValue; 
}</code></pre>
<h3 id="클로저와-메모리의-관계">클로저와 메모리의 관계</h3>
<p>위 코드에서 <code>nextCreate</code> 함수가 클로저를 통해 의존성 배열과 현재 상태를 <strong>기억</strong>합니다. 그 결과, <strong>값과 의존성 배열은 메모리에 저장</strong>되고, 이후 렌더링에서도 동일한 값을 사용하거나, 의존성 배열이 변할 때만 재계산을 수행합니다.</p>
<p>클로저는 함수가 선언된 환경을 기억하기 때문에, React의 훅 시스템에서 메모이제이션할 값이나 함수를 기억하고 그 값들이 다시 필요할 때 사용될 수 있습니다.</p>
<pre><code class="language-ts">function updateMemo&lt;T&gt;(
  nextCreate: () =&gt; T, // 메모이제이션할 값을 생성하는 함수
  deps: Array&lt;mixed&gt; | void | null, // 의존성 배열
): T {
  const hook = updateWorkInProgressHook(); 
  // 현재 훅의 상태를 가져옵니다. 클로저를 통해 이전 값과 의존성 배열을 참조할 수 있습니다.

  const nextDeps = deps === undefined ? null : deps; 
  // 새로운 의존성 배열이 전달되었는지 확인합니다.

  const prevState = hook.memoizedState; 
  // 이전에 메모이제이션된 값과 의존성 배열을 가져옵니다.

  if (nextDeps !== null) {
    const prevDeps: Array&lt;mixed&gt; | null = prevState[1]; 
    if (areHookInputsEqual(nextDeps, prevDeps)) {
      return prevState[0]; 
      // 클로저를 통해 이전 값과 의존성 배열을 기억하고, 값이 변경되지 않았으면 메모이제이션된 값을 반환합니다.
    }
  }

  const nextValue = nextCreate(); 
  // 의존성 배열이 변경되었으므로 새로운 값을 계산합니다.

  hook.memoizedState = [nextValue, nextDeps]; 
  // 새로 계산된 값과 새로운 의존성 배열을 메모이제이션합니다.
  return nextValue;
}
</code></pre>
<h3 id="메모리-사용-측면에서의-문제">메모리 사용 측면에서의 문제</h3>
<p>이처럼 클로저는 메모이제이션된 값과 그 값이 사용된 환경을 메모리에 계속해서 저장합니다. 의존성 배열이 변경되지 않으면 이 값들이 계속 메모리에 남아 있게 되므로, <strong>메모리 사용량이 증가</strong>할 수 있습니다.</p>
<p>이러한 동작은 특히 다음과 같은 상황에서 문제가 될 수 있습니다:</p>
<ul>
<li><strong>불필요한 메모이제이션</strong>: 자주 변하지 않는 값에 대해 메모이제이션을 남용할 경우, 메모리만 차지하고 성능 개선 효과는 미미할 수 있습니다.</li>
<li><strong>큰 데이터나 복잡한 객체를 메모이제이션할 때</strong>: 메모리에 저장된 값이 클 경우, 메모리 누수가 발생할 가능성이 높아집니다.</li>
</ul>
<p>따라서 메모이제이션을 사용할 때는 <strong>클로저가 불필요한 데이터를 메모리에 계속 유지하는 상황</strong>을 방지하기 위해, 의존성 배열과 계산 비용을 신중하게 고려해야 합니다.</p>
<h2 id="그렇다면-usememo는-언제-사용하는게-좋을까">그렇다면 useMemo는 언제 사용하는게 좋을까?</h2>
<p>위 내용을 통해 useMemo의 메모이제이션을 활용하게 되면 불필요한 데이터 유지를 통해 메모리 누수가 발생할 수 있다는 것을 알았습니다. 이후 또 하나의 의구심으로  <code>useMemo</code>를 사용할 때 성능 이점을 얻으려면 데이터가 얼마나 복잡하거나 커야 <code>useMemo</code>를 사용하는 이점이 있을까? 를 고민해보았습니다. 
구글링을 통해  발견한 <code>useMemo</code>에 대한 벤치마크를 테스트한 <a href="https://medium.com/@minjae_lee/memoization-and-react-forget-2003214d9dff">블로그</a>를 약간의 소개를 드리고 저의 생각을 적어보겠습니다.</p>
<p><a href="https://medium.com/@minjae_lee/memoization-and-react-forget-2003214d9dff">블로그</a>에서 한 테스트를 아래와 같이 요약했습니다.</p>
<blockquote>
<h4 id="블로그-테스트-요약">블로그 테스트 요약</h4>
<ul>
<li>컴포넌트가 다시 렌더링될 때 이전에 계산된 값을 재사용함으로써 불필요한 재계산을 방지
실험 내용</li>
<li>처리할 데이터의 복잡도<code>(n)</code>에 따라 <code>useMemo</code>가 언제 효과적인지 실험
실험결과</li>
<li><strong>복잡도 n = 1</strong>: <code>useMemo</code>가 성능에 별다른 이점을 제공하지 못하고, 오히려 초기 렌더링이 19% 느려졌습니다.</li>
<li><strong>복잡도 n = 100</strong>: 초기 렌더링이 62% 느려지지만, 후속 렌더링에서 거의 성능 차이가 없거나 약간 빠릅니다.</li>
<li><strong>복잡도 n = 1000</strong>: 초기 렌더링이 183% 느려졌으나, 후속 렌더링은 37% 더 빠릅니다.</li>
<li><strong>복잡도 n = 5000</strong>: 초기 렌더링이 545% 느려지지만, 후속 렌더링에서 최대 609%까지 성능이 향상됩니다.</li>
</ul>
</blockquote>
<p>useMemo 훅에 대한 렌더링 최적화의 기능은 1000번 이상의 복잡한 연산이 필요할 때 부터 유의미한 성능을 보일 수 있다는 것을 알 수 있었습니다.</p>
<p>그래서 저는 &quot;useMemo 사용해야할까?&quot;에 대한 질문에 이렇게 말하는게 좋을 것 같습니다.</p>
<blockquote>
<p>useMemo... 정말 필요할까?</p>
</blockquote>
<p>실제로 로직을 짜면서 1000번 이상의 반복적인 연산이 필요하다는 것을 자각했다면, 이 코드는 무엇인가 잘못되었거나 대처가 필요하다는 생각을 하는 것이 중요하다고 생각합니다.
<code>useMemo</code>에 대해서 단순히 쓰면 어떨까로 시작하기 보다는 지금 있는 로직에 <code>useMemo</code>를 쓸 필요가 있을지를 연산 횟수가 나의 머릿속에서 반복되는 것을 알고 사용해야하지 않을까 합니다.
단순히 &quot;여러번 호출&quot;이라는 애매모호한 기준보다는 실제로 높은 횟수의 렌더링에서 효과적일 것이라고 생각합니다.</p>
<p>혹시라도 여러분들이 <code>useCallback</code>과 <code>useMemo</code>에 대해 많은 고민을 하고 계신다면, 조금 더 기다려보시면 좋을 것도 같습니다. <code>React Forget</code>이라는 자동적인 메모이제이션을 도입해주는 기능을 현재 Meta에서 개발 중이라고 합니다. 물론 완성도와 모든 개별적 상황에 대해 어떻게 처리할지는 고민이 많아지지만, 잘 지켜보는게 좋을 것 같습니다.</p>
<h2 id="마치며">마치며...</h2>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/f45b17eb-65f3-4484-a5bd-979a3c978c0e/image.png" alt=""></p>
<p>이번 글을 쓰기 위해 다양한 글을 찾아보고 메모이제이션 동작과 최적화의 빨간약을 먹어보았습니다. 실제 제가 진행하는 프로젝트에서 <code>useMemo</code>와 <code>useCallback</code> 을 통해 최적화가 필요한게 보이지 않더라고요. 정말 특수한 상황에 필요하다는 것을 알게 되었습니다. 위 두 개의 훅은 마법으로 최적화가 되는 것이 아닌 실제로 메모리라는 비용을 지불해서 만들어진 기술임을 인지하게된 좋은 탐구였습니다.(딥다이브 정도는 아니였지만 가볍게 들어갔다 나왔습니다.)</p>
<p>그리고 위에 나온 블로그의 테스트를 보면서 생각했지만 저정도의 렌더링에서 발생하게 되는 초기 렌더링 비용이 무지막지하게 높더군요? 과연 저런 초기렌더링도 잡는 것은 결국 어떤식으로 대처할지에 대해 조금 찾아보니 NextJS에서 초기 렌더링을 최적화하는 방식은 없을지 (결국 캐시로 귀결되지 않을까 싶지만) 좀 더 패턴을 찾아보고 다음 글 주제로 가지 않을까 합니다.
아래 제가 이 글을 쓰기 위해 참고자료가 너무 좋았던 기억에 들고왔으니 혹시라도 저의 글이 부족하다고 느껴지시다면 아래에서 더 가져가시면 좋을 것 같습니다.</p>
<blockquote>
<p>참고자료
<a href="https://www.schiener.io/2024-03-03/react-closures">useCallback과 closure에 대한 더 깊은 이해를 원하신다면?</a>
<a href="https://github.com/yeonjuan/dev-blog/blob/master/JavaScript/should-you-really-use-usememo.md">useMemo 벤치마크 테스트</a>
<a href="https://jser.dev/react/2022/01/11/how-react-memo-works">useMemo에 대한 딥다이브를 원하신다면?</a>
<a href="https://github.com/facebook/react/blob/65a56d0e99261481c721334a3ec4561d173594cd/packages/react-reconciler/src/ReactFiberHooks.js#L2856-L2900">useMemo 소스코드가 궁금하다면?</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[글또 10기를 시작하며 - 나는 무슨 생각으로 글을 쓸까?]]></title>
            <link>https://velog.io/@muman_kim/%EA%B8%80%EB%98%90-10%EA%B8%B0%EB%A5%BC-%EC%8B%9C%EC%9E%91%ED%95%98%EB%A9%B0-%EB%82%98%EB%8A%94-%EB%AC%B4%EC%8A%A8-%EC%83%9D%EA%B0%81%EC%9C%BC%EB%A1%9C-%EA%B8%80%EC%9D%84-%EC%93%B8%EA%B9%8C</link>
            <guid>https://velog.io/@muman_kim/%EA%B8%80%EB%98%90-10%EA%B8%B0%EB%A5%BC-%EC%8B%9C%EC%9E%91%ED%95%98%EB%A9%B0-%EB%82%98%EB%8A%94-%EB%AC%B4%EC%8A%A8-%EC%83%9D%EA%B0%81%EC%9C%BC%EB%A1%9C-%EA%B8%80%EC%9D%84-%EC%93%B8%EA%B9%8C</guid>
            <pubDate>Mon, 07 Oct 2024 03:11:57 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가며">들어가며</h2>
<p>이전에 친구가 &quot;글또&quot;라는 글쓰기 모임을 매우 추천해줘서 이번 10기가 마지막이라는 소식에 후다닥 지원하게 되었습니다. 글쓰기를 잘하기 위한 나만의 강제성을 부여하고, 글을 써서 나의 지식을 잘 설명해볼 수 있는 경험이 될 수 있기리를 희망하고 있습니다.
이번 첫 글을 작성하기 이전에 토스 NEXT 챌린지에 도전했지만, 너무나도 어려운 난이도에 그만 정신을 잃었지만, 이를 계기로 더 공부해야겠다고 생각했습니다.
<img src="https://velog.velcdn.com/images/muman_kim/post/4ad62e34-6311-47a1-83a0-d9e9ddb147e9/image.png" alt=""></p>
<p>이젠 정말 공부뿐이고 그 공부를 위해 글쓰기를 열심히 해야겠습니다. 좌절할 시간도 아까운 현대사회에서 내가 몰랐던게 뭔지를 다시 한번 리뷰하고 나를 잘 표현할 수 있는 글을 써보는 시간이 될 수 있도록 노력해야겠습니다.</p>
<h2 id="내가-글을-쓰는-이유">내가 글을 쓰는 이유</h2>
<blockquote>
<p>저는 옵시디언과 PARA 기법을 통해 지식을 문서화를 하고 있습니다. 배우는 지식을 기록으로 남김으로 지식을 다시 복기할 수 있도록 합니다. 또한 다른 사람에게 쉽게 읽히고 좋은 설명이 되는 문서화를 고민합니다.</p>
</blockquote>
<p>확실히 문서화를 통해 지식을 정리하면 언젠가 다시 읽어보았을때나 기억이 쉽게 납니다. 하지만, 지식을 설명하기 위한 글은 정리하는 글과는 다르게 작성되어야합니다. </p>
<h3 id="지식에-대한-정리하는-글과-설명하는-글의-차이">지식에 대한 정리하는 글과 설명하는 글의 차이</h3>
<p>지식을 정리하는 글은 나 중심적 사고입니다. <code>나는 ~~ 상황에 처했고, 이를 대처하기 위해 ~~ 을 해서 ~~ 하는 결과를 얻었습니다.</code> 식의 형식으로 작성됩니다.
물론 이러한 경험을 작성하는 글이 나로 한정해서는 굉장히 좋은 글입니다. 나의 상황을 다른 사람에게 전달함으로 나의 경험을 잘 알려줄 수 있습니다. 또한 그 글에서 기술적인 요점을 작성하며 글의 기술력 또한 올릴 수 있습니다.</p>
<p>하지만 지식을 설명하는 글의 경우에는 조금 다릅니다. 나의 상황에 한정하게 된다면 나와 동일 or 비슷한 상황의 사람에게는 도움이 될 수 있지만, 기반된 지식이 다른 사람들이나 나의 해결과정만을 가져가는 것은 읽는 사람의 역량에 따라 받아들이는 정보의 한도가 다르기에 저의 기준에서는 설명글이라고 보기에는 아쉽다고 생각합니다.</p>
<p>그래서 저는 설명하는 글에서는 나의 상황을 모두에게 적용할 수 있는 상황으로 일반화를 하고 이를 해결하기 위한 방식을 나열하고 이 중에 이것이 가장 적합하다라는 것을 증명하는 과정을 잘 서술하고 그에 대한 생각을 적어서 나의 생각을 넣어두는 것까지로 생각하고 있습니다.</p>
<p>정리하면 다음과 같습니다.</p>
<ul>
<li>지식을 정리하는 글<ul>
<li>나 중심적 사고 : 나는 이래서 이렇게 했어!</li>
<li>나만의 상황과 나만의 해결책을 작성, 이를 해결하기 위한 나의 생각</li>
</ul>
</li>
<li>지식을 설명하는 글<ul>
<li>일반화된 상황에 대한 사고 : 만약 이런 상황이 온다면? (나도 다른 사람도 겪을 만한 상황, 혹은 특정한 상황)</li>
<li>해결하기 위한 방법을 작성하고 이를 선택하게된 이유</li>
</ul>
</li>
</ul>
<h3 id="차이점은-상황의-일반화">차이점은 상황의 일반화</h3>
<p>이 둘이 비슷할 수도 있다고 생각하지만 이 둘의 가장 차이점은 <code>지식의 일반화</code>입니다. 내 생각을 잘 정리한다는 내가 처했던 상황과 해결한 기술만을 설명할 수 있습니다. 하지만 이런 글은 상황이 지엽적이기에 특정 사람들에게만 도움이 되는 글이 될 수 있습니다. 
하지만 상황을 일반화 하여, 누구에게나 통용될 수 있는 상황을 제시하거나 이를 해결하기 위한 방식을 다양하게 설명하게 된다면 여러 사람들에게 기술에 대한 더 넓은 해석의 기회를 줄 수 있습니다.</p>
<h2 id="그렇다면-나의-글쓰기-수준은">그렇다면 나의 글쓰기 수준은?</h2>
<p>솔직히 나의 글쓰기 수준이 어느정도인가? 라고 한다면 나의 생각을 잘 정리해서 나의 생각은 잘 표현한다고 생각합니다. 하지만 남에게 이해시키는 능력은 조금 부족한 것 같다는 생각이 듭니다. 나와 비슷한 생각을 가진 사람에게는 충분히 도움이 될 수 있는 글을 작성할 수 있지만, 전혀 다른 3자에게 나의 지식을 설명하기에는 설명하는 레벨이나 상황에 대한 공감이 되지 않아 글이 잘 정리되지 않았습니다.</p>
<p>이전에 썼던 블로그글도 누군가 읽어보면 단순 정보 전달 글 위에 말했던 나 중심적 사고의 글 뿐, 지식을 설명하고 있다는 느낌은 아니라고 생각합니다. </p>
<h2 id="이번-글또에서-시도해볼-것">이번 글또에서 시도해볼 것</h2>
<p>그래서 저는 이번 글또에서 지식을 설명하는 글을 어떻게 하면 잘 쓸지를 고민해 볼 것입니다. 그러면서 이제 적어볼 나만의 소재를 적어보겠습니다.</p>
<h3 id="1-이전에-내가-학습했던-기술">1. 이전에 내가 학습했던 기술</h3>
<p>개발 공부를 시작하면서 배워온 React, NextJS 등 기술에 대한 이해를 하고 있는지를 다시 한번 검토하고 기술을 잘 설명해볼 생각입니다. 또한 이 기술을 사용했던 경험도 있으니 이를 잘 일반화해서 사람들에게 잘 전달해볼 생각입니다.</p>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/ad55cada-f28e-45c9-9c14-26c8165e4965/image.png" alt=""></p>
<h3 id="2-기술-딥다이브">2. 기술 딥다이브</h3>
<p>이번 토스 NEXT 챌린지를 경험하면서 주니어 개발자의 컷이 정말 높구나라는 생각을 했습니다. 그렇기에 기술에 대한 더 깊은 이해를 위해 내가 쓰는 것에 대해 더 자세히 들어가보려고 합니다. React와 TypeScript도 있지만, 웹 엔진과 렌더링에 대해서도 해서 총 4가지로 좀 정리해보고 싶습니다.
<img src="https://velog.velcdn.com/images/muman_kim/post/02e050b2-07a4-4a7f-9a28-723527bb93a5/image.png" alt=""></p>
<h3 id="3-뻘짓-해보기">3. 뻘짓(?) 해보기</h3>
<p>예전에 React.memo 100번 쓰기 같은 이런건 이렇게 쓰면 안된다는데 한번 극단적인 상황에서는 어떤 상황이 벌어질까에 대해 상상해보고 시도해본 적이 있습니다. 그리고 해당 문제에 대해서 성능이나 실질적인 결과를 측정해서 앞으로 제가 추구하는 성능 최적화를 위해 많은 고민을 해보는 과정을 생각해보고 이를 잘 설명해보고 싶습니다.
<img src="https://velog.velcdn.com/images/muman_kim/post/50303e9b-b957-486d-81aa-7b9e7bf71a9d/image.png" alt=""></p>
<h2 id="글또를-참여하는-마음가짐">글또를 참여하는 마음가짐</h2>
<p>모두가 글을 쓰는 목적이 다 다를 수 있지만, 제가 글을 쓰는 목적은 목적은 효율적인 소통을 위한 도구입니다. 지식을 정리하는 것은 과거의 나와의 소통, 지식을 설명하는 것은 다른사람들과의 소통이기 때문입니다.
그렇기에 이번 글또에서 많은 사람들과 소통도 해보면서 나의 글쓰기를 점검하고 더 성장하는 계기가 될 수 있도록 노력할 것입니다.
글은 많이 쓰는 것도 좋지만, 글을 잘 써서 나의 강점으로 더 성장할 수 있도록 이번 글또 10기에 전심전력으로 도전해보겠습니다. 결국 세상은 글쓰는 또라이가 바꿉니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[24년 상반기 회고 : 그대들은 어떻게 살 것인가?]]></title>
            <link>https://velog.io/@muman_kim/24%EB%85%84-%EC%83%81%EB%B0%98%EA%B8%B0-%ED%9A%8C%EA%B3%A0-%EA%B7%B8%EB%8C%80%EB%93%A4%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%82%B4-%EA%B2%83%EC%9D%B8%EA%B0%80</link>
            <guid>https://velog.io/@muman_kim/24%EB%85%84-%EC%83%81%EB%B0%98%EA%B8%B0-%ED%9A%8C%EA%B3%A0-%EA%B7%B8%EB%8C%80%EB%93%A4%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%82%B4-%EA%B2%83%EC%9D%B8%EA%B0%80</guid>
            <pubDate>Fri, 28 Jun 2024 15:37:13 GMT</pubDate>
            <description><![CDATA[<h2 id="간단한-고찰">간단한 고찰</h2>
<p>벌써 24년도 상반기도 지나가 버렸습니다. 대학교 4학년 1학기 졸업작품을 마무리(<del>꺄르륵</del>)한게 너무 기분이가 좋기도 하면서도, 대학을 떠나면 무엇을 할지에 대해 많은 고민에 빠져있습니다. 결국 나는 개발자로 취준을 해야하는데 아직 취준의 불안감은 여전합니다. 그러면 내가 뭘하고 있었는지 어떻게 준비하면 좋은지 나에게 물어봅시다. </p>
<blockquote>
<p><code>그래서 뭐함?</code></p>
</blockquote>
<p>지금 시작합니다.</p>
<p align="center">
<img src="https://velog.velcdn.com/images/muman_kim/post/9a15b2be-4565-4d73-acdc-3ae8e9bd1b22/image.png" width="50%" height="50%" />
</p>

<h2 id="회고-방식">회고 방식</h2>
<p>전체적인 회고 방식은 각 월마다 무엇을 했고 어떤 목적을 가지고 살았는지, 당시 플래너가 없어서, 핸드폰 갤러리와 계좌이체기록을 보며 무엇을 했는지 추적했습니다.
<img src="https://velog.velcdn.com/images/muman_kim/post/e93e3e14-c75d-4590-9e49-f942ec5a2a4e/image.png" alt=""></p>
<blockquote>
<p><code>돈을 어떻게 쓴거야?</code>
여기서 하나의 회고
먹고 살자고 일하는 건 맞지만, 저축은 좀 해야겠다. 너무 즉흥적으로 쓴다.</p>
</blockquote>
<h2 id="월별-회고">월별 회고</h2>
<p>내가 매월 무슨 짓을 했는지 사진을 보며 많은 생각을 했지만, 가장 많이 든 생각은 누군가와 식사를 하고 남긴 사진을 보며,<code>맞아! 이때는 누구랑 이거 먹으러 갔었지~</code> 하면서 새로운 약속을 잡아버린 것도 있었지만, 역시 한국인 밥넥팅이 맞는 것 같습니다.
그러면서도 직장 다니시는 인생 선배님들께 참 많이도 얻어먹었다는 생각도 하게 되었구요. 얼른 취직해서 갚아야지요 <del>은혜는 2배로, 원한은 100배로</del></p>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/65598239-def1-4289-b04d-4d9e6706d0ca/image.png" alt=""></p>
<h3 id="1월">1월</h3>
<p>24년도 시작하는 1월을 시작하기 앞서 12월 종강 이후 연말에 친구들과 놀러다니다가 1월 시작과 비슷하게 이전에 인턴으로 활동했던 회사에서 방학간 개발 아르바이트를 했습니다. 당시 대표님이 제가 서울 살지 않는 것도 감안해주시고, 많은 배려를 해주셔서 도와드리러 갈 수 밖에 없더군요! 1월에 받은 월급으로 트랙패드(<del>버티컬을 샀어야지</del>)를 구매했고, 컴퓨터에 너무 앉아있음에 건강 걱정에 헬스도 다녔습니다.
그리고 4학년 1학기를 대비 피부 AI 학습 관련해서 데이터 센터를 많이 돌아다녔습니다.</p>
<p>
<img src="https://velog.velcdn.com/images/muman_kim/post/0926515e-5e7c-4b0b-b799-cd436328d727/image.png" width="50%" height="50%" />
  </p>

<h3 id="2월">2월</h3>
<p>이때 저에게 가장 큰 이슈는 <code>의사파업</code>이었습니다. 제가 졸업작품으로 학습시키려던 피부 AI 이미지를 관련해서 의사의 허가가 필요했는데, 이게 한번 허가가 끝이 아니라 졸업작품 제작기간 중 2~3번 정도 더 받았어야했는데 (최대 기간 3개월), 그리고 데이터 센터 이전으로 내 예상보다 시간이 촉박해지는 이슈가 있었습니다. 그래서 많은 고민을 하며 졸업작품 아이디어를 변경을 고민했었습니다.</p>
<p>개발 아르바이트를 하면서 평소에 일상을 유지했습니다.
이번년도부터 기사 시험을 볼 수 있어 필기시험을 지원하고 학교에서 친구와 같이 맛있는 저녁을 먹었습니다~</p>
<h3 id="3월">3월</h3>
<p>2월간의 개발아르바이트를 마치고, 개강하는 3월. 강하지 못하면 살아남지 못하는 대학교 4학년 졸업작품 기간에 돌입했습니다. </p>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/bba637de-0337-4452-9e7a-170201f07a6f/image.png" alt="">
(대4이어인)</p>
<p>졸업작품은 원래 피부였었지만, 동물 피부로 변경했습니다. 다행히 큰 문제없이 주제 변경을 하게 되었고, 남은거 교수님 눈치보기뿐이었죠.</p>
<p>
<img src="https://velog.velcdn.com/images/muman_kim/post/61aca2a8-6b4c-4c85-9ead-ef03896ee5ea/image.png" width="50%" height="50%" />
</p>
(주제 변경한다고 방학 때 말하지 개강하고 1주 뒤에 온 것을 지켜보고 있는 교수님)

<p>아무튼 이렇게 잘 변경해서 열심히 EC2 배포와 Express 서버 만들기, Python Flask 등 여러 기술을 활용해서 졸업작품에 들어갔습니다.</p>
<p>
<img src="https://velog.velcdn.com/images/muman_kim/post/30cf5ade-3d2b-41d2-b514-ec02a7ad951b/image.png" width="50%" height="50%" />
</p>

<h3 id="4월">4월</h3>
<p>동아리에서 활동하는<code>코인 커뮤니티</code>에서 팀을 새롭게 개편하면서 학교에서 보여주는 상점 서비스를 각 상점 사장님들이 관리할 수 있는 비즈니스 기능을 추가해보고 있습니다.
직접 사장님을 찾아가 필요한 기능을 청취하고 개발해보는 재밌는 경험을 하는게 보람찼던 것 같습니다!</p>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/19cd4f9d-95d3-4014-80cd-aff23f3dd7b7/image.png" alt=""></p>
<p>그리고 긴장되었던 졸업작품 중간발표 엄청 긴장했지만, 무사히 통과하고 좋은 피드백도 들으며 졸업작품이 알맞게 개발되는 것도 느꼈습니다. 하지만 여기서 또 미친 것처럼 직접 외주까지 받아가며 통나무 들고 개발근을 기르기 위해 열심히 달려 나갔네요.(<del>이때 헬스 멈춰서 실근육은...</del>)</p>
<p align="center">
<img src="blob:https://velog.io/bb89b395-02f7-4196-b18a-82db11ed734b" width="50%" height="50%" />
</p>

<h3 id="5월">5월</h3>
<p>5월은 예비군도 다녀오고, 축제도 거치면서 24년도 마지막 대학 축제를 친구들과 즐겼었네요. 그리고 졸업작품도 완료해서 최종 발표까지 잘 마무했던 5월이었던 것 같습니다.
<img src="https://velog.velcdn.com/images/muman_kim/post/d580b778-7669-43bc-ad0c-136ab1b44fe7/image.png" alt=""></p>
<p>(마지막 학생 예비군과 학교 축제를 즐기는 4학년 대학생)</p>
<p>또한 색다른 경험으로 IPP 멘토링을 진행했습니다. 많은 학생들이 우리 학교에 졸업조건인 IPP 인턴쉽을 무조건 가야하는데, 이 과정에서 많은 학생들이 취준의 어려움을 느끼기도 하고, 자신이 지금까지 무엇을 했는지 모르는 경우가 많더군요.
결국 자신이 위기감을 느껴야 준비하는 것도 많는 것 같습니다. 그래서 최대한 제가 도움이 될 수 있는 조언을 많이 해봤습니다.</p>
<p align="center">
<img src="https://velog.velcdn.com/images/muman_kim/post/d165eae0-8aa9-4cb3-af1b-a62af1e9d164/image.png" width="50%" height="50%"/>
</p>

<p>(4학년에게, 3학년때 IPP 간 경험을 말하는 4학년이 되는 모습)</p>
<h3 id="6월">6월</h3>
<p>대망의 마지막 6월에서는 졸업작품 발표회로 한 학기를 마쳤습니다. 이제 다음학기 마지막을 위해 졸업조건을 마무리하고, 취업 이력서도 정비하면서 새로운 환경을 찾아 나아가고 있습니다. (네이버 부스트 캠프, 여러회사 인턴 이력서 빌넣빌넣)
<img src="https://velog.velcdn.com/images/muman_kim/post/f6768eaf-8abe-437b-bde1-1d93c1ea07d9/image.png" width="50%" height="50%" />
(직장은 많은데 나의 직장은 없구나를 생각하는 개발자 김모씨)</p>
<h2 id="상반기-총평">상반기 총평</h2>
<p>생각보다 많은 일을 이루었고 결과도 게획과 에상범위 안에서 모든 일이 진행되었습니다. 하지만 역시 장점이 곧 단점이 되었든 너무 예상 범위안의 성과만 있었다고 생각합니다. 뭐가 예상을 벗어나는 기교를 갖는 사람이 되고 싶은데 아직 부족한 것 같기도 합니다. 앞으로 어떻게 살아가는지는 제가 만들어가 듯이 예상을 벗어나는 결과를 만들기 위해 노력보다는 기교를 통해 뛰어넘는 제가 되어봐야겠군요. 그렇게 대학교 이후에 정착할 수 있는 새로운 환경을 찾아 노력해보겠습니다!
<img src="https://velog.velcdn.com/images/muman_kim/post/5fe9d46d-7641-4dd9-bb85-5c0213cc81bd/image.gif" alt=""></p>
<p>(언제나 기교 넘치는 나!)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[1. 한기대 코인 리코드  리뷰:  useState 사용 줄이기]]></title>
            <link>https://velog.io/@muman_kim/%ED%95%9C%EA%B8%B0%EB%8C%80-%EC%BD%94%EC%9D%B8-%EB%A6%AC%EC%BD%94%EB%93%9C-%EB%A6%AC%EB%B7%B0-useState-%EC%82%AC%EC%9A%A9-%EC%A4%84%EC%9D%B4%EA%B8%B0</link>
            <guid>https://velog.io/@muman_kim/%ED%95%9C%EA%B8%B0%EB%8C%80-%EC%BD%94%EC%9D%B8-%EB%A6%AC%EC%BD%94%EB%93%9C-%EB%A6%AC%EB%B7%B0-useState-%EC%82%AC%EC%9A%A9-%EC%A4%84%EC%9D%B4%EA%B8%B0</guid>
            <pubDate>Thu, 20 Jun 2024 06:53:03 GMT</pubDate>
            <description><![CDATA[<h2 id="서론">서론</h2>
<p>동아리에서 부원들끼리 소그룹을 만들어서 개발과 관련된 소규모 스터디를 진행하였습니다. 제가 선택한 그룹은 레거시 그룹이라는 우리가 이전에 했던 프로젝트에 어떤 코드를 썼는지를 리뷰하고 레거시로 남게 되거나 기능 개선이 필요한 경우에 대해서 정보를 공유하고 코드를 개선하는 그룹입니다. 
그래서 그룹활동에 취지에 맞게 이전에 코인 리코드 프로젝트에서 제가 신경써서 작업했던 state를 필요한 곳에서만 적재적소하게 사용하기 위해 했던 노력을 공개하고 실제 어떤 개선이었는지에 대해서 소개하겠습니다.
state와 관련되어서 어떤 것이 문제인지 간단한 예제를 제시해보겠습니다.
<img src="https://velog.velcdn.com/images/muman_kim/post/248e7cb0-ec0c-497b-9a8b-a952097e0ca5/image.png" alt=""></p>
<p align="center">
(비장한 소그룹)
</p>

<h2 id="문제제기">문제제기</h2>
<p>우리는 코인 리코드 작업 중에 어떤 문제가 있는지 탐색하던 중 로그인 화면에서 문제를 찾을 수 있었습니다.
해당 아래 이미지를 확인해주세요</p>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/bd6feaf6-154d-431d-96b1-5a4a38583545/image.gif" alt=""></p>
<p>위의 코드에서 <code>state</code>를 통해 ID와 패스워드의 <code>value</code>를 추적하게 되고 입력 하나마다 이를 검사하고 리렌더링이 일어나는 것을 chrome의 <code>react-devTool-extension</code>을 통해 알 수 있습니다.
이런 현상이 일어나는지에 대한 내용을 한번 예시를 통해 소개해드리겠습니다.</p>
<pre><code class="language-jsx">import React, { useState } from &#39;react&#39;;

export default function MyApp() {
  const [name, setName] = useState(&#39;&#39;);
  const [address, setAddress] = useState(&#39;&#39;);
  return (
    &lt;&gt;
      &lt;label&gt;
        Name{&#39;: &#39;}
        &lt;input value={name} onChange={(e) =&gt; setName(e.target.value)} /&gt;
      &lt;/label&gt;
      &lt;br /&gt;
      &lt;label&gt;
        Address{&#39;: &#39;}
        &lt;input value={address} onChange={(e) =&gt; setAddress(e.target.value)} /&gt;
      &lt;/label&gt;
      &lt;Greeting name={name} /&gt;
    &lt;/&gt;
  );
}

const Greeting = function Greeting({ name }) {
  console.log(&#39;Greeting was rendered at&#39;, new Date().toLocaleTimeString());
  return (
    &lt;h3&gt;
      Hello{name &amp;&amp; &#39;, &#39;}
      {name}!
    &lt;/h3&gt;
  );
};
</code></pre>
<p><a href="https://react-i8mtvg.stackblitz.io">실행 예제</a></p>
<p>해당 입력 폼에서 입력할 때 마다 <code>console.log</code>가 발생하면서 리렌더링이 발생하는 상황을 알 수 있습니다.
해당 폼에 적용된 로직은 위에서 보게된 이미지와 동일하게 작성되었습니다.</p>
<p><code>state</code>는 리액트 렌더링을 발생시키기 됩니다. 공식문서에 따르면</p>
<blockquote>
<p><code>useState</code> 훅은 두 가지를 제공합니다:</p>
<p>렌더링 사이에 데이터를 유지하는 상태 변수.
변수를 업데이트하고 React가 컴포넌트를 다시 렌더링하도록 트리거하는 상태 설정 함수.</p>
<p>reference: <a href="https://react.dev/learn/state-a-components-memory">https://react.dev/learn/state-a-components-memory</a></p>
</blockquote>
<p>useState 훅을 사용할 때 setState 함수를 호출하면 해당 컴포넌트는 상태가 변경되었다고 판단하여 리렌더링이 발생합니다. 이는 React의 기본 동작 방식 중 하나입니다. (물론 리액트에서 렌더링 일으키는 건 더 있습니다)</p>
<p>그렇다면 이것을 극복할 방법은 없는 것일까요?
저희는 이 해결 방안에 대해 총 2가지를 논의하였습니다.</p>
<ol>
<li><a href="https://react.dev/reference/react/memo">React.memo</a>를 통한 리렌더링 방지</li>
<li><a href="https://react.dev/reference/react/useRef#useref">useRef</a>와 <a href="https://react.dev/reference/react/useImperativeHandle">useImpretiveHandle</a>을 활용한 컨트롤</li>
</ol>
<p>그렇다면 위의 예제 코드를 2가지 방식에 맞게 개선해보았습니다.</p>
<p>1번 방법인 memo를 통해 리렌더링을 방지할 수 있었습니다.</p>
<pre><code class="language-jsx">import React, { memo, useState } from &#39;react&#39;;

export default function MyApp() {
  const [name, setName] = useState(&#39;&#39;);
  const [address, setAddress] = useState(&#39;&#39;);
  return (
    &lt;&gt;
      &lt;label&gt;
        이름 {&#39;: &#39;}
        &lt;input value={name} onChange={(e) =&gt; setName(e.target.value)} /&gt;
      &lt;/label&gt;
      &lt;br /&gt;
      &lt;label&gt;
        Address{&#39;: &#39;}
        &lt;input value={address} onChange={(e) =&gt; setAddress(e.target.value)} /&gt;
      &lt;/label&gt;
      &lt;Greeting name={name} /&gt;
    &lt;/&gt;
  );
}

const Greeting = memo(function Greeting({ name }) {
  console.log(&#39;Greeting was rendered at&#39;, new Date().toLocaleTimeString());
  return (
    &lt;h3&gt;
      Hello{name &amp;&amp; &#39;, &#39;}
      {name}!
    &lt;/h3&gt;
  );
});</code></pre>
<p><a href="https://react-m6yd1u.stackblitz.io">실행예제</a></p>
<h3 id="reactmemo란">React.memo란?</h3>
<p><code>React.memo</code>는 React의 렌더링 최적화를 위해 설계된 고차 컴포넌트<code>(Higher-Order Component, HOC)</code>입니다</p>
<p><code>React.memo</code>는 컴포넌트에 전달되는 <code>props</code>가 변경되었는지 확인하기 위해 얕은 비교를 수행합니다. 얕은 비교는 객체의 최상위 레벨의 값들만을 비교하는 방법입니다. 즉, <code>props</code> 객체나 그 내부의 객체가 가리키는 값이나 참조가 이전 렌더링과 비교해 변했는지 확인합니다.</p>
<p>예제 코드에서는 <code>React.memo</code>를 사용함으로써, <code>name</code> 값에 변화가 없다면 <code>&lt;Greeting /&gt;</code> 컴포넌트의 재렌더링을 건너뛰게 됩니다.</p>
<p>2번 방법인 useRef와 useImpretive를 활용한 리렌더링 방지입니다.</p>
<pre><code class="language-jsx">import React, {
  useState,
  useRef,
  useImperativeHandle,
  forwardRef,
} from &#39;react&#39;;

export default function MyApp() {
  const [name, setName] = useState(&#39;&#39;);
  const passwordRef = useRef();

  return (
    &lt;&gt;
      &lt;label&gt;
        Name{&#39;: &#39;}
        &lt;input value={name} onChange={(e) =&gt; setName(e.target.value)} /&gt;
      &lt;/label&gt;
      &lt;br /&gt;
      &lt;PasswordInput ref={passwordRef} /&gt;
      &lt;Greeting name={name} /&gt;
    &lt;/&gt;
  );
}

const PasswordInput = forwardRef((props, ref) =&gt; {
  // input 요소에 대한 ref 생성
  const inputRef = useRef();

  useImperativeHandle(ref, () =&gt; ({
    // 외부에서 접근할 수 있도록 getPassword 함수 제공
    getPassword: () =&gt; inputRef.current.value,
  }));

  return (
    &lt;label&gt;
      Password{&#39;: &#39;}
      &lt;input
        type=&quot;password&quot;
        ref={inputRef} // input 요소에 ref 연결
        defaultValue=&quot;&quot; // controlled 대신 uncontrolled 컴포넌트로 사용
      /&gt;
    &lt;/label&gt;
  );
});

const Greeting = ({ name }) =&gt; {
  console.log(&#39;Greeting was rendered at&#39;, new Date().toLocaleTimeString());
  return (
    &lt;h3&gt;
      Hello{name &amp;&amp; &#39;, &#39;}
      {name}!
    &lt;/h3&gt;
  );
};</code></pre>
<p><a href="https://react-ffejjd.stackblitz.io">실행예제</a></p>
<h3 id="useimpretivehandle과-forwardref">useImpretiveHandle과 forwardRef</h3>
<p>useImpretiveHandle과 forewardRef를 활용하게 되는데, useImpretiveHandle은 리액트 훅 중 하나로, 부모 컴포넌트에게 노출할 ref 핸들을 사용자가 직접 정의할 수 있게 (ref로 노출 시키는 노드의 일부 메서드만 노출할 수 있게) 해주는 훅입니다.
forwardRef는 사용하면 구성 요소가 참조를 사용하여 DOM 노드를 상위 구성 요소에 노출할 수 있습니다.</p>
<h3 id="useref를-활용한-최적화">useRef를 활용한 최적화</h3>
<p><code>useRef</code> 훅은 React 컴포넌트에서 참조<code>(ref)</code>를 생성하고 접근할 수 있게 해줍니다. 이 훅이 반환하는 객체(<code>ref</code> 객체)는 <code>.current</code> 프로퍼티를 통해 참조된 DOM 요소나 React 엘리먼트에 직접 접근할 수 있게 해줍니다. 여기서는 <code>passwordRef</code>를 생성하여 <code>PasswordInput</code> 컴포넌트에 전달하고 있습니다. 이를 통해 부모 컴포넌트<code>(MyApp)</code>가 자식 컴포넌트<code>(PasswordInput)</code> 내부의 함수에 접근할 수 있습니다.</p>
<p><code>useState</code>의 경우에는 상태가 변경되면 컴포넌트가 리렌더링되지만, <code>.current</code> 프로퍼티를 활용함을 통해  직접 접근하여 변경을 일으키기 때문에 리렌더링을 발생 시키지 않습니다.</p>
<h2 id="그렇다면-어떤-것이-좋을까">그렇다면 어떤 것이 좋을까?</h2>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/6b88c61c-787a-4ba2-b3d1-6bb2a1573978/image.png" alt=""></p>
<p>1번과 2번 방법 둘 다 최적화라는 방식에서 결과가 개선된 것을 확인했습니다. 두 기능의 결과에 대한 차별점을 확인해보겠습니다.</p>
<ul>
<li>1번의 경우 <ul>
<li>React.memo의 경우에는 <code>React.memo</code>를 사용함으로써, <code>name</code> 값에 변화가 없다면 <code>&lt;Greeting /&gt;</code> 컴포넌트의 재렌더링을 건너뛰게 됩니다. 이의 경우에는 Address가 변경되어도 name의 상태가 유지됩니다.</li>
</ul>
</li>
<li>2번의 경우<ul>
<li>반환하는 객체(<code>ref</code> 객체)는 <code>.current</code> 프로퍼티를 통해 참조된 DOM 요소나 React 엘리먼트에 직접 접근할 수 있게 해줍니다. 여기서는 <code>passwordRef</code>를 생성하여 <code>PasswordInput</code> 컴포넌트에 전달하면서 최적화 합니다.</li>
</ul>
</li>
</ul>
<p>그 중, React 공식문서에서<a href="https://react.dev/reference/react/memo#usage"> React.memo의 사용법</a>과 관련해서 DeepDive 내용에서 이러한 내용이 있었습니다</p>
<blockquote>
<p>If your app is like this site, and most interactions are coarse (like replacing a page or an entire section), memoization is usually unnecessary. On the other hand, if your app is more like a drawing editor, and most interactions are granular (like moving shapes), then you might find memoization very helpful.</p>
<p>&quot;만약 당신의 앱이 이 사이트처럼 대부분의 상호작용이 페이지를 교체하거나 전체 섹션을 대체하는 것과 같은 대략적인(interaction) 형태라면, 메모이제이션은 보통 불필요합니다. 반면에, 당신의 앱이 드로잉 에디터와 같고 대부분의 상호작용이 도형을 이동하는 것과 같이 세밀한(granular) 형태라면, 메모이제이션이 매우 유용할 수 있습니다.&quot;</p>
</blockquote>
<h2 id="결론">결론</h2>
<p align="center">
<img src="https://velog.velcdn.com/images/muman_kim/post/dd60d1d2-1b93-4b3a-a898-d1fb98376070/image.png" width="30%" height="30%">
 </p>
메모이제이션을 적용할지 여부는 애플리케이션의 상호작용 방식에 따라 달라집니다. 
여기서 우리가 적용하려는 경우에서는 로그인 페이지에서의`input` value에 대한 state 변화를 하는 경우이기 때문에 컴포넌트 자체가 단순했기에 Ref를 선택하여 최적화를 진행하였습니다.
하지만 이런 결론을 산출하는 과정에서 여러가지 의구심을 남겼습니다.
> React.memo는 언제 써야 베스트 케이스일까?
> 어디에 메모이제이션되는지?
> 어떤 성능 문제가 발생하는지?

<p>다음 글에서는 memo와 관련된 내용에 대해 다이브 해보는 경험을 작성해보겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[블로깅을 못(안)했던 이유(부제: 부(不)의 감정)]]></title>
            <link>https://velog.io/@muman_kim/%EB%B8%94%EB%A1%9C%EA%B9%85%EC%9D%84-%EB%AA%BB%EC%95%88%ED%96%88%EB%8D%98-%EC%9D%B4%EC%9C%A0%EB%B6%80%EC%A0%9C-%EB%B6%80%E4%B8%8D%EC%9D%98-%EA%B0%90%EC%A0%95</link>
            <guid>https://velog.io/@muman_kim/%EB%B8%94%EB%A1%9C%EA%B9%85%EC%9D%84-%EB%AA%BB%EC%95%88%ED%96%88%EB%8D%98-%EC%9D%B4%EC%9C%A0%EB%B6%80%EC%A0%9C-%EB%B6%80%E4%B8%8D%EC%9D%98-%EA%B0%90%EC%A0%95</guid>
            <pubDate>Sun, 11 Jun 2023 13:00:51 GMT</pubDate>
            <description><![CDATA[<h1 id="이-글을-쓰게-된-이유">이 글을 쓰게 된 이유</h1>
<p>최근 나의 가장 소중한 사람이 하늘의 별이 되었습니다. 갑자기 가신 것은 아니고 3월 이후로 안 좋은 소식이 들려온 이후로 매주 주말마다 가서 인사드리고 하고 싶은 말, 자랑하고 싶은 이야기를 나누며 3개월을 보내왔습니다. 항상 만날 때마다 이상한 기분이 들었습니다. 만나러 가는 시간과 만난 이후 재미있게 이야기를 나누었지만, 나올 때는 항상 마음이 무거워졌습니다.
항상 내가 뭘 할 수 있을까를 생각해 보았지만, 항상 시간 날 때 전화나 문자를 하고 주말마다 만나러 가는 것 이외에는 아무것도 할 수 없었습니다.</p>
<blockquote>
<p>그래서 내가 뭘 할 수 있지?</p>
</blockquote>
<p>이 생각이 항상 저를 갉아 먹었던 것 같습니다. 결국은 그렇게 그분과의 마지막을 가족과 함께 지켜보고 회사에 해당 내용을 전달했습니다. 그리고 3일 이후에 마지막 날 그분이 담긴 함을 들고 산소로 가는 와중 그 함이 얼마나 따뜻했는지 모르겠습니다. 그저 마지막 온기를 느끼고 작별 인사를 마무리하였습니다.</p>
<h2 id="3월-소식-이후">3월 소식 이후</h2>
<p>3월에 처음으로 갑자기 쓰러지시던 상황 속에서 감정이 매우 낮아졌습니다. 연락받는연락을 받는 순간 회사였는데, 혼자 나와 있던 동료분께서 무슨 일 있냐면서 물어보셨습니다. 표정 속에 있던 근심을 알아봐 주신 게 하나의 감정으로 고마웠지만, 미안했습니다. 내가 괜히 회사 분위기를 해치는 것은 아닐까 하고는 말이죠
<strong>자신만의 부정적인 문제를 남에게 표출하여 남에게도 부정적인 문제점을 만들게 하는 감정</strong> 
이러한 감정들을 저는 <code>부(不)의 감정</code>이라고 생각했습니다.</p>
<blockquote>
<p>감정은 감염되는 것이다.</p>
</blockquote>
<p>항상 느껴왔지만, 사람들이 느끼는 감정들은 전달이 된다고 생각되었습니다. 아기들이 한 명 울면 옆에 아기가 울듯이 감정이란 감염되는 것으로 생각했습니다.
그래서 내가 심각한 상황임을 표정이나 행동에서 조금 드러난다면, 이것은 전체 분위기를 휩쓴다고 생각했습니다.
내가 괜히 회사 분위기를 안 좋게 이끄는 원인이 되지 않았나 죄송스러웠습니다.
평소에 거짓말이나 표정 관리 잘 못하는 것이 저의 장점이나 단점이었는데, 이번 순간은 매우 큰 단점으로 와닿았던 것 같았습니다.</p>
<h2 id="36월-사이">3~6월 사이</h2>
<p>매주 회사를 출퇴근 시간이 1시간에서 1시간 30분 사이인데 이러한 교통 시간 이후 자취방에서 식사 이후 운동 2시간을 했었습니다. 이 시기에는 PT 기간이라 쉬지 않고 나갔었는데 항상 식사 이후에 아무것도 하지 못하는 무력감에 빠져들었습니다. 주말에는 항상 본가에 내려가기 위해 짐을 싸고 내려갔습니다. 그때도 아무것도 하기 싫어서 누워서 오전이나 오후를 날려 먹고 본가에 내려갔습니다. 뭔가 내가 한심하게 느껴지기도 하아고 많은 생각 휩싸였지만 가장 많이 든 생각은</p>
<blockquote>
<p>동아리 작업 도와줘야하는데, 공부 조금씩 해야하는데...
<em>뭐라도 해야하는데...</em></p>
</blockquote>
<p>오늘도 아무것도 하지 않았구나라는 나에 대한 자조적인 생각, 나 자신을 깍아먹는 생각들이 더 힘들게 느껴졌습니다. 이를 극복하기 위해 조금 익숙해지기도 하는 방향으로 생각을 바꿨습니다.
<img src="https://velog.velcdn.com/images/muman_kim/post/1aa8467d-e42a-4a98-849d-105714176c17/image.png" alt=""></p>
<blockquote>
<p>&quot;아 오늘도 아무것도 하지 않았네, 그래서 어쩌라고 오늘은 좀 쉴거야. 내 생각도 정리하고 뭐라도 내 심신의 안정이 필요해&quot;</p>
</blockquote>
<p>모든 것은 나를 위해서라는 생각으로 조금은 이기적인 마인드였지만, <code>부(不)의 감정</code>들을 극복하는 기간에는 필요했던 마인드였습니다. </p>
<h2 id="6월의-마지막과-이번-글을-쓰면서">6월의 마지막과 이번 글을 쓰면서</h2>
<p>그렇게 6월에 들어서 그분과 마지막까지 자리를 지켜서 좋은 곳에 보내드린 이후 잘 돌아왔습니다.
<code>부(不)의 감정</code>이 결코 잘못된 감정은 아니라고 생각합니다. 어떻게 사람이 항상 행복하고 좋은 일만 있을 수 있을까요. 당연히 안좋은 일도 있고, 불행한 일도 있을 겁니다.
<img src="https://velog.velcdn.com/images/muman_kim/post/9561354c-e11b-4be4-ab67-2c576d69d052/image.png" alt=""></p>
<p>하지만 이 감정들을 내가 남에게 최소한 필요한 정도로만 뿜어내고, 나의 몸속에서 정제하는 것이 가장 중요한 태도가 아닐까 생각합니다.
이번 경험에서 사회 생활하면서 힘들었던 기간이었지만, 최소한 남에게 피해주지 않기 위해 많은 생각의 시간을 보냈던 것 같습니다.
그러면서 이제 내일부터는 조금 정신차려서 살기로 하고 반년회고를 생각했습니다.
시작이 반이고, 지금이 반입니다. 아직 내가 버려버린 3개월을 극복하기에 충분한 시간이 남았습니다.
남은 올해 그분과 약속한 더 멋진 삶을 살테니 지켜봐달라는 약속을 지켜야겠습니다.
앞으로 개인공부하면서 좋은 내용의 블로깅으로 찾아뵙겠습니다. 감사합니다.
<img src="https://velog.velcdn.com/images/muman_kim/post/d3da7815-6610-4c0b-a036-e17d0ff1551d/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JavaScript] 가비지 컬렉터에서의 메모리 누수는 언제 일어날까?(Garbage Collector)]]></title>
            <link>https://velog.io/@muman_kim/JavaScript-%EA%B0%80%EB%B9%84%EC%A7%80-%EC%BB%AC%EB%A0%89%ED%84%B0%EC%97%90%EC%84%9C%EC%9D%98-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%88%84%EC%88%98%EB%8A%94-%EC%96%B8%EC%A0%9C-%EC%9D%BC%EC%96%B4%EB%82%A0%EA%B9%8CGarbage-Collector</link>
            <guid>https://velog.io/@muman_kim/JavaScript-%EA%B0%80%EB%B9%84%EC%A7%80-%EC%BB%AC%EB%A0%89%ED%84%B0%EC%97%90%EC%84%9C%EC%9D%98-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%88%84%EC%88%98%EB%8A%94-%EC%96%B8%EC%A0%9C-%EC%9D%BC%EC%96%B4%EB%82%A0%EA%B9%8CGarbage-Collector</guid>
            <pubDate>Sat, 04 Mar 2023 14:39:20 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가면서">들어가면서..</h2>
<p>최근 회사 인턴으로 합류하면서 열심히 FrontEnd 개발자로 일하고 있다. 첫 회사여서 부담감이 크지만, 성실히 잘 수행해 나가고 있고, 계속해서 웹 트래픽이 늘고 있으면 기분이가 좋아진다.
아무튼 그래서 그 중에 코드 PR리뷰를 하게 되면서 어떠한 반복문에서 사수님의 지적이 있었다.</p>
<blockquote>
<p>이 반복문에서 메모리 회수 안될 것 같은데?</p>
</blockquote>
<p>에? 그럴리가.. 를 생각했지만, 한번 알아보라는 사수님의 말에 공부하게 되었고, 한번 쭉 정리를 해보려고 한다.</p>
<h2 id="what-is-gc">What is GC?</h2>
<p>자바스크립트는 메모리를 자동으로 관리하는 <code>Garbage Collector</code>(가비지 컬렉터)를 가지고 있습니다. 가비지 컬렉터는 메모리에 할당된 객체 중에서 더 이상 사용되지 않는 객체, 즉 Garbage(쓰레기)를 자동으로 탐지하고 제거하는 역할을 합니다.</p>
<p>JS에서는 개발자가 직접 메모리 할당과 해제를 하지 않아도 됩니다. 객체를 생성하면 자동으로 메모리에 할당되며, 해당 객체를 더 이상 사용하지 않을 때는 개발자가 직접 할당 해제를 하지 않아도, <code>Garbage Collector</code> 객체를 제거해줍니다. 이러한 기능을 통해 개발자는 코드에 집중할 수 있으며, 메모리 관리에 대한 부담을 줄일 수 있습니다.</p>
<p><code>Garbage Collector</code>는 주기적으로 메모리를 스캔하여 더 이상 참조되지 않는 객체를 제거합니다. 이 때, 참조되지 않는 객체를 어떻게 판단하는가에 따라 <code>Garbage Collector</code>의 성능이 좌우됩니다. </p>
<p>대부분의 JS 엔진에서는 <code>Mark-and-Sweep</code> 알고리즘을 사용하여 가비지 컬렉션을 수행합니다. </p>
<p>이 알고리즘은 Root Set으로부터 시작하여 도달 가능한 객체를 마킹하고, 마킹되지 않은 객체를 <code>Garbage</code>로 판단하여 제거합니다.</p>
<p>가비지 컬렉션은 자동으로 이루어지지만, 가끔씩 개발자가 <code>메모리 누수</code>(memory leak)를 일으키는 경우가 있습니다.</p>
<blockquote>
<p>아니!! 그러면 메모리 회수가 안되는 건 저 알고리즘 탓 아닌교!!!!
<img src="https://velog.velcdn.com/images/muman_kim/post/e5751e8c-8c7f-4aa6-8f5a-5dab20a113c0/image.png" alt=""></p>
</blockquote>
<p>라면서 굉장히 그럼 잘 만들어진 걸 주던가라는 말을 할 수 도 있지만, 세상에 코드의 정답은 없고, 버그는 많다라는 말과 함께 우리 JS 개발자는 이를 극복해야합니다. </p>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/fbd52822-0fb6-4251-9fe2-bbf78e1b0fe6/image.gif" alt=""></p>
<p>메모리 누수는 개발자가 의도하지 않은 객체의 참조를 유지하게 되는 경우를 말하며, 이러한 경우에는 <code>Garbage Collector</code>가 객체를 제거하지 못하고 메모리를 계속 점유하게 되는 현상을 말합니다.</p>
<p>그렇다면 코드를 어떻게 쓰면 메모리누수가 발생되는 예시를 보여드리겠습니다.
<br><br></p>
<h3 id="1-이벤트-리스너-등록-후-제거하지-않는-경우">1. 이벤트 리스너 등록 후 제거하지 않는 경우</h3>
<pre><code class="language-javaScript">function handleClick() {
  console.log(&quot;Button clicked&quot;);
}

const button = document.querySelector(&quot;#myButton&quot;);
button.addEventListener(&quot;click&quot;, handleClick);
// ...
// 이벤트 리스너를 제거하지 않았으므로, 메모리 누수가 발생할 가능성이 있음
</code></pre>
<br>

<p>위 코드에서 button 요소에 클릭 이벤트 리스너를 등록하고 있습니다. 하지만 이벤트 리스너를 제거하지 않으면 해당 요소가 제거될 때까지 이벤트 리스너가 메모리에 남아있게 됩니다.</p>
<p>해결책으로는, 이벤트 리스너를 등록할 때 리스너 함수를 변수에 저장하고, 이벤트 리스너를 제거할 때 해당 변수를 사용하여 제거해주는 것입니다.
<br></p>
<pre><code class="language-javaScript">function handleClick() {
  console.log(&quot;Button clicked&quot;);
}

const button = document.querySelector(&quot;#myButton&quot;);
button.addEventListener(&quot;click&quot;, handleClick);

// ...

button.removeEventListener(&quot;click&quot;, handleClick);</code></pre>
<p><br><br></p>
<h3 id="2-setinterval-사용-후-clearinterval-호출하지-않는-경우">2. setInterval() 사용 후 clearInterval() 호출하지 않는 경우</h3>
<pre><code class="language-javaScript">let count = 0;

setInterval(() =&gt; {
  console.log(count++);
}, 1000);
// ...
// clearInterval() 함수를 호출하지 않았으므로, 메모리 누수가 발생할 가능성이 있음</code></pre>
<br>
위 코드에서 setInterval() 함수를 사용하여 1초마다 count 값을 출력하도록 설정하고 있습니다. 하지만 clearInterval() 함수를 호출하지 않으면 setInterval() 함수가 계속해서 실행되며, count 변수와 함께 메모리에 계속해서 남아있게 됩니다.

<p>해결책으로는, setInterval() 함수를 호출할 때 반환되는 타이머 ID 값을 변수에 저장하고, clearInterval() 함수를 호출할 때 해당 변수를 사용하여 타이머를 제거해주는 것입니다.
<br></p>
<pre><code class="language-javaScript">let count = 0;
let intervalId = setInterval(() =&gt; {
  console.log(count++);
}, 1000);
// ...
clearInterval(intervalId);</code></pre>
<br>

<blockquote>
<p>이제 이해가 되셨나요? 한마디로 자신이 등록해놓은 변수에 대해서 회수를 하지 않게 된다면 이러한 메모리 누수를 야기할 수 있다는 사실!
하지만 이러한 상황이 아닌 단순한 순수 JS 코드에서는 메모리 누수가 일어나는 경우도 있습니다.</p>
</blockquote>
<h2 id="순수js-코드에서의-메모리누수">순수JS 코드에서의 메모리누수</h2>
<br>

<pre><code class="language-javaScript">function addButtons() {
  const buttonsContainer = document.querySelector(&quot;#buttons&quot;);

  for (var i = 0; i &lt; 5; i++) {
    const button = document.createElement(&quot;button&quot;);
    button.textContent = &quot;Button &quot; + i;
    button.addEventListener(&quot;click&quot;, function() {
      console.log(&quot;Button &quot; + i + &quot; clicked&quot;);
    });
    buttonsContainer.appendChild(button);
  }
}

addButtons();</code></pre>
<br>

<p>위 코드에서 addButtons() 함수 내부에서 for 반복문을 사용하여 버튼을 생성하고, 각 버튼에 이벤트 리스너를 등록하고 있습니다. 이벤트 리스너 함수는 클로저 함수로 정의되어 있으며, 버튼이 클릭될 때 해당 버튼의 인덱스를 출력하고 있습니다.</p>
<p>하지만 for 반복문에서 사용되는 i 변수는 var 키워드를 사용하여 선언되었기 때문에 함수 스코프를 가지게 됩니다. 이러한 이유로 클로저 함수 내에서 i 변수를 참조하면 반복문이 종료된 이후에도 변수의 값이 계속해서 참조되므로, 원치 않는 결과가 발생할 수 있습니다.</p>
<p>해결책으로는, for 반복문에서 let 키워드를 사용하여 변수를 선언하는 것입니다. let으로 변수를 선언하면 블록 스코프를 가지게 되므로, 클로저 함수 내부에서 해당 변수를 참조하더라도 원하는 값이 출력됩니다.</p>
<br>

<pre><code class="language-javaScript">function addButtons() {
  const buttonsContainer = document.querySelector(&quot;#buttons&quot;);

  for (let i = 0; i &lt; 5; i++) {
    const button = document.createElement(&quot;button&quot;);
    button.textContent = &quot;Button &quot; + i;
    button.addEventListener(&quot;click&quot;, function() {
      console.log(&quot;Button &quot; + i + &quot; clicked&quot;);
    });
    buttonsContainer.appendChild(button);
  }
}

addButtons();</code></pre>
<br>

<p>위와 같이 let을 사용하여 변수를 선언하면, 클로저 함수 내부에서 i 변수를 참조할 때 각각의 버튼이 클릭될 때 해당 버튼의 인덱스가 출력됩니다.</p>
<h2 id="이번-학습을-통해-느꼈던-점">이번 학습을 통해 느꼈던 점</h2>
<p>이번 학습에서 자바스크립트의 <code>Garbage Collector</code>에서 일어날 수 있는 메모리 누수에 대해서 한번 학습한 내용을 정리해보았습니다. 이렇게 학습하고 보니 그때 지적받았던 코드는 크게 메모리 누수와 상관 없었던 것 같기도 하다는 생각을 했습니다. 다음번에도 GC에 대한 내용이 거론되면 뜨거운 개발 토의 현장을 만들 수 있다는 자신감이 차오르게 되었습니다.</p>
<p>(사수님의 표정)
<img src="https://velog.velcdn.com/images/muman_kim/post/cf16bf00-471e-4241-8c59-e7aaf8c286db/image.png" alt=""></p>
<p>언제까지고 뒤쳐져서 살 수 없습니닷!
한번 그때를 기대하면서 다음 블로깅으로 돌아오겠습니다. 감사합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[신년계획 30일차 검토 블로깅]]></title>
            <link>https://velog.io/@muman_kim/%EC%8B%A0%EB%85%84%EA%B3%84%ED%9A%8D-30%EC%9D%BC%EC%B0%A8-%EA%B2%80%ED%86%A0-%EB%B8%94%EB%A1%9C%EA%B9%85</link>
            <guid>https://velog.io/@muman_kim/%EC%8B%A0%EB%85%84%EA%B3%84%ED%9A%8D-30%EC%9D%BC%EC%B0%A8-%EA%B2%80%ED%86%A0-%EB%B8%94%EB%A1%9C%EA%B9%85</guid>
            <pubDate>Tue, 07 Feb 2023 15:31:54 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/muman_kim/post/bea53b66-194b-4ef1-856d-c4234dd76861/image.png" alt=""></p>
<h2 id="글머리">글머리</h2>
<p>23년도가 시작된지 벌써 12분의 1이 지난 오늘 새벽에 인생이라는 무엇인가에 고민하면서 뭘해야될까를 써보다가  작년 회고를 보면서 한번 나의 23년에 지금 얼마나 지키나 보게 검토하자라는 생각이 들었습니다.</p>
<ul>
<li>꾸준한 TIL (내용: JS 메서드, React Road맵을 따른 스터디 내용 올리기)</li>
<li>프로젝트 포스팅(코인 리코드, 코알라 Tistory에 써둔거 옮기기!)</li>
<li>1 일 1 알고리즘 (주변 카카오 간 사람들의 조언)</li>
<li>계획된 헬스 프로젝트 진행하기(나도… 솔로 탈출)</li>
<li>Todo mate로 빡세게 관리하기
그리고 23년 계획이라고 저런걸 작성해둔 나를 볼 수 있었습니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/9ae06c82-92d3-40c9-ad06-df2016e8a629/image.jpg" alt=""></p>
<p>대표사진을 통해 알 수 있지만 전체적으로 검토하면서 핑계 좀 대보겠습니다.</p>
<h3 id="꾸준한-til">꾸준한 TIL..</h3>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/1073e8eb-e601-4b53-a2fa-93d699aa392d/image.png" alt=""></p>
<p>현재 저의 깃허브입니다.. 사막화가 진행되고 있습니다. 학교를 다니면서도 열심히 했다고 생각했는데 아무래도 현재 회사 인턴생활을 하면서 정신이 없다보니 TIL도 제대로 진행하지 못하고 있는 것 같습니다. 회사 생활에 얼른 익숙해져서 집와도 TIL 쓸 수 있는 시간을 마련해야겠습니다.</p>
<blockquote>
<p>나에게 하는 조언
게임도 줄이고!! 일찍 자고 일찍 일어나야해!</p>
</blockquote>
<h3 id="프로젝트-포스팅">프로젝트 포스팅</h3>
<p>현재 코인 리코드를 진행을 하고 있습니다만, 아직도 포스팅을 안하다닛!!!.
요런 포스팅은 너무 크니 주말마다 끊어서 해야하는데 1월에는 서울 이사다 뭐다 하면서 바빴습니다. 한번 이거는 잘 생각해봐야겠네요</p>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/9c87e542-9c67-4d66-8f51-304832b06f2c/image.jpg" alt=""></p>
<blockquote>
<p>나에게 하는 조언
주말에 놀지말고 카페라도 가서 이런거 좀 쓰자!</p>
</blockquote>
<h3 id="1-일-1-알고리즘">1 일 1 알고리즘</h3>
<p>허.... 이거는 조금 알고리즘 문제를 어떻게 풀지가 중요할 것 같습니다. 아무래도 리트코드를 선택할 생각인데, 평범하게 알고리즘 공부에 관련된 내용을 정리해서 이번 주말에 작성해두어야겠습니다.</p>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/6bbb910e-04a7-410b-bab5-3e0c3ccf62fa/image.jpg" alt=""></p>
<blockquote>
<p>나에게 하는 조언
이거는 진짜 끈기와 노력이 필요 잘 정해보기. 이런거는 중간중간 보고하듯이 포스팅하는게 좋을 듯?</p>
</blockquote>
<h3 id="헬스-프로젝트">헬스 프로젝트</h3>
<p>이거 하나만큼은 잘 지키고 있습니다.
1월 중순에 서울 경기에 정착한 이후 1년 헬스장을 끊었습니다. 현재 3주차 정도 다니는데 가족행사나 회식 이슈가 아니라면 꼭 가서 주 5~6일은 꼭 가고 있습니다.
최근에는 다른 사람들과 함께 하는 챌린지를 진행하면서 3주간 추가로 오운완하고 있습니다!</p>
<p><a href="https://velog.velcdn.com/images/muman_kim/post/bddf9458-1155-433f-a8c8-26d272867425/image.jpg"></a></p>
<blockquote>
<p>나에게 하는 조언
넌 잘하고 있어, 헷갈리때면 여태 그랬던 것처럼 그냥 고</p>
</blockquote>
<h3 id="todo-mate-빡세게-활용">TODO Mate 빡세게 활용</h3>
<p>엄... 요즘 회사 생활이나 운동에 아직 벅찬데 이런 거라도 쓰면서 까먹지 말아야하는데...
아마 더 좋은 방향의 구글캘린더도 있지만 Todo Mate 써야겠지
작년까지만 해도 ENTJ 나왔는데 이제 INTP 나올 것 같다.</p>
<blockquote>
<p>나에게 하는 조언
출퇴근을 활용해서 꼭 써보자 웹툰 좀 그만보자</p>
</blockquote>
<h2 id="그-결과">그 결과</h2>
<p>아무튼 현재 진행되고 있는 결과에 대한 점수는 20점이라고 할 수 있습니다.
회사 생활에 적응도 힘들지만 최대한 23년도의 나를 성장할 수 있는 기회라고 생각되며 더 빡빡하게 한번 나의 삶을 운영해보는 기회라고 생각합니다. 
나는 아직 젊고 더 많은 것을 경험할 거야!</p>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/9ad6f541-f86b-4000-9a10-82522f13467f/image.jpg" alt="">
(운영의 신 진양철 회장님.. 저에게 힘을 주세요)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Merge 전략에 대해서]]></title>
            <link>https://velog.io/@muman_kim/Merge-%EC%A0%84%EB%9E%B5%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C</link>
            <guid>https://velog.io/@muman_kim/Merge-%EC%A0%84%EB%9E%B5%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C</guid>
            <pubDate>Wed, 11 Jan 2023 15:46:45 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가기에-앞서">들어가기에 앞서…</h2>
<p>이번에 회사에서 인턴을 경험하면서 사수분께 많은 것을 배우고 있습니다.
(항상 감사합니다)
<img src="https://velog.velcdn.com/images/muman_kim/post/7fed99e9-655d-4681-8dae-951ac53b9801/image.png" alt=""></p>
<p>그래서 숙제로 스쿼시 머지라는 것에 대해서 한번 알아보라고 하셨는데, 앗! 시마타! 분명 들은 적있는데....
예전에 간단하게 언급된 내용이었는데 머리를 치며 이번에 새벽을 새더라도 공부를 해보려고 합니다.</p>
<blockquote>
<p>사전 개념</p>
<p>Merge란?
gitFlow 전략을 예로 설명하면 <code>develop</code>브런치에 대해서 기능을 구현할때 <code>feature/~~</code>라는 새로운 브랜치로 작업을 진행한 이후 <code>develop</code>브런치에 다시 붙이는 과정을 Merge라고 한다.</p>
<p>그리고 이러한 Merge 방식에 대해서는 3가지 종류가 있다.</p>
<ol>
<li>Merge</li>
<li>Squash and Merge</li>
<li>Rebase and Merge</li>
</ol>
</blockquote>
<p>예시는 모두 <code>develop</code>에서 <code>feature/~~</code> 브런치를 생성한 후 <code>develop</code>으로 붙이려는 상황으로 생각해주면 더 잘 이해가 되실겁니다.</p>
<h2 id="merge">Merge</h2>
<p><strong>간단하게 사전 개념에서의 내용 그대로 한번 읽어주시면 감사하겠습니다.</strong>
사진을 통해 보면
<img src="https://velog.velcdn.com/images/muman_kim/post/928509d1-a13c-4c1a-9531-a3327771cf60/image.png" alt="">
분류되었던 브런치에 대한 모든 <code>commit</code>기록들이 살아 있는 상태로 develop에 붙게 됩니다.
해당 <code>Merge</code> 방식의 장점으로는 다른 분기의 브런치들의 <code>commit</code>기록을 통해서 작업의 수정자와 작업한 모든 순간을 확인할 수 있다는 것입니다.</p>
<p>장점이 곧 단점으로 모든 <code>commit</code>기록을 확인하기 때문에 쓸데없이 오타를 지우거나 변수명을 변경하는 별거 없는 변경에 대해서도 기록이 남기에 원하는 변경사항을 찾는데 시간이 오래 걸릴 수 도 있습니다.</p>
<h2 id="squash-and-merge">Squash and Merge</h2>
<p><code>Squash and Merge</code> 방식도 똑같이 <code>feature/~~</code> 를 예시로 들 수 있는데 최종 머지하는 방식에서 개발자가 feature에서 작업한 모든 <code>commit</code> 기록들이 하나가 되어 머지가 됩니다.
<img src="https://velog.velcdn.com/images/muman_kim/post/eed7139f-e6a5-4a31-884d-30f26120976b/image.png" alt=""></p>
<p>Squash 방식의 장점으로는 쓸데없는 <code>commit</code> 기록을 보지 않고 오로지 내가 분리한 기능에 대해서 붙이는 것을 확인할 수 있기에 merge된 것에 집중할 수 있습니다.
단점은 세세한 <code>commit</code> 기록을 볼 수 없기에 만약 한명이 아닌 두 명 이상이 작업한 브런치인 경우 작업한 기록이나 사람을 특정할 수 없다는 단점이 있습니다.</p>
<h2 id="rebase-and-merge">Rebase and Merge</h2>
<p><code>Rebase and Merge</code> 방식 또 결국은 같지만 결과를 보았을 때 <code>feature/</code>에 대한 commit 기록에 <code>develop</code> 앞에 붙게 되어 모든 작업이 develop에서만 이루어진 것처럼 branch를 관리할 수 있습니다.
<img src="https://velog.velcdn.com/images/muman_kim/post/84b3ca81-3ad2-4d92-9b0f-9d771f3257a5/image.png" alt=""></p>
<p>장점으로는 develop 브런치의 commit기록을 남김으로써 코드의 변화를 빠르게 확인할 수 있습니다.
단점으로는 하나의 브런치에 모든 commit 기록이 뭉쳐있어 찾고 싶은 commit 기록이 있는 경우 탐색에 어려움이 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2022년 회고 - 처음 뒤돌아보는 1년(부제. 스칼라보단 벡터)]]></title>
            <link>https://velog.io/@muman_kim/2022%EB%85%84-%ED%9A%8C%EA%B3%A0-%EC%B2%98%EC%9D%8C-%EB%92%A4%EB%8F%8C%EC%95%84%EB%B3%B4%EB%8A%94-1%EB%85%84feat.-%EC%8A%A4%EC%B9%BC%EB%9D%BC%EB%B3%B4%EB%8B%A8-%EB%B2%A1%ED%84%B0</link>
            <guid>https://velog.io/@muman_kim/2022%EB%85%84-%ED%9A%8C%EA%B3%A0-%EC%B2%98%EC%9D%8C-%EB%92%A4%EB%8F%8C%EC%95%84%EB%B3%B4%EB%8A%94-1%EB%85%84feat.-%EC%8A%A4%EC%B9%BC%EB%9D%BC%EB%B3%B4%EB%8B%A8-%EB%B2%A1%ED%84%B0</guid>
            <pubDate>Thu, 22 Dec 2022 11:52:21 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h2 id="들어가기에-앞서">들어가기에 앞서</h2>
<p>최근 학교 수업에서 나의 자서전에 대해 쓰는 과제를 받아서 어떻게 써야할지를 고민해봤습니다. 막상 앉아서 무엇에 쓸지를 기억에 의존하여 쭉 나열도 해보고 과거 연락했던 친구들의 이름도 적어보니 뭔가 뒤돌아볼 시간 없이 앞만 보고 달려왔다는 생각이 들었습니다. 
그리고 주변 친구들의 1년을 되돌아보는 회고를 블로깅하면서 앞으로의 길을 생각하는 것을 보며, 나아가기 위해 뒤를 돌아보는 이번 1년에 대한 회고록을 작성해보려고 합니다.</p>
</blockquote>
<p>(이제는 뒤를 돌아보며 나아가자)</p>
<h2 id="13월">1~3월</h2>
<p>작년 전역하고 12월 넘어서는 시기에 시작한 KOALA 프로젝트를 진행하고 있을 시간이다. 코알라 프로젝트에서는 팀리더가 되어 이끄는 경험은 정말 힘들었지만 많은 것을 경험하게 되었다.
다양한 사람들에게 일을 분배하기 위한  Github issue를 통한 작업 분류를 기본으로, 코드 컨벤션을 통한 사람들의 브랜치 관리를 좀 더 수월하게 관리했습니다.</p>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/e2698866-1a82-43c0-b729-365482085c37/image.png" alt=""></p>
<blockquote>
<p>돌이켜보면
그때 해본 협업에 대한 경험은 매우 중요하기도 하고 나의 포트폴리오에 대한 장점, 나만의 경험한 내용이기에 그에 대한 포폴을 준비하도록 하는 시간을 가져야겠다.</p>
</blockquote>
<h2 id="46월">4~6월</h2>
<p>이 시기에 KOALA 프로젝트가 기능의 범위를 감당하기에 너무 많은 시간이 흘러서 결국 해
산되어서 나의 방향성을 잃어버렸었다.
그래서 이 시기에는 작업보다는 동아리 운영과 나의 개인역량 숙달을 더 신경썼습니다.
블로그 포스팅을 하기 위해 Tistroy블로그를 생성하고 포스팅 하였습니다.</p>
<p><a href="https://murpin-tech.tistory.com/">카카오 티스토리 링크</a></p>
<p>또한 코로나 이후 학교가 대면으로 수업방식을 변경하게 되어 그에 대한 동아리 행사에 적응하느라 시간을 많이 쏟게 되었던 것도 일이었습니다.
선배들과 힘을 합쳐 동아리 행사인 BCSD Conference를 잘 진행하게 되었습니다.
<img src="https://velog.velcdn.com/images/muman_kim/post/db992221-25dd-495b-94e9-60b2249c3203/image.jpg" alt=""></p>
<p>또한 오픈소스 컨트리뷰션에 지원하였지만 이 부분에 대해서도 불발되어 여름방학에 갈피를 잃어서 많이 방황하게 되었습니다.</p>
<blockquote>
<p>돌이켜보면
이 시기에 번아웃 아닌 번아웃이 오게되어 github에 신경을 쓰지 못하여 많은 커밋을 날리지 못하였다. 평소 잔디밭을 비우는 일이 없도록 꾸준한 TIL 작성에 노력을 기해야겠다.</p>
</blockquote>
<h2 id="79월">7~9월</h2>
<p>여름방학 기간에 오픈소스 컨트리뷰션을 진행하려하였지만 탈락하게 되어 방학기간 뭘하지에 고민하게 되었고 친구들과 프로젝트 리코드 계획을 진행하게 되었습니다.
리코드를 기획하면서 해당 부분에 대한 내용들을 정리도 해보고 앞으로 올해 안에 진행할 프로젝트를 기획하는 기간을 가졌습니다.</p>
<p><a href="https://velog.io/@muman_kim/0.-%ED%95%9C%EA%B8%B0%EB%8C%80%EC%82%AC%EB%9E%8C%EB%93%A4-%EC%BD%94%EC%9D%B8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%9A%B0%EB%A6%AC-%EB%A6%AC%EC%BD%94%EB%93%9C%ED%96%88%EC%96%B4%EC%9A%94">코인 리코드 블로깅</a>
<a href="https://github.com/BCSDLab/KOIN_WEB_RECODE">코인리코드 레포</a>
<img src="https://velog.velcdn.com/images/muman_kim/post/500b1b5d-fb56-4932-9802-0da3a95ed61b/image.png" alt=""></p>
<p>그때부터 제대로된 프로젝트 진행을 위한 ESLint와 StyleLint를 적용하여 
코드의 통일성과 가독성을 높혀 앞으로의 프로젝트의 레퍼런스로 만드는 것을 목적으로 진행하였습니다.</p>
<h2 id="911월">9~11월</h2>
<p>학교에서 2학기를 시작하게 되며 다시 행사와 코인 리코드라는 새로운 프로젝트를 시작하였습니다. 
이때 몸의 건강이 안좋아지기 시작해서 영양제를 챙겨먹기 시작했습니다. 여러분도 꼭 챙겨드세요.
각종 영양제</p>
<p>22년도 2학기를 장식할 새로운 BCSD-Conference!
<img src="https://velog.velcdn.com/images/muman_kim/post/49d715b7-aa41-4dce-a52f-f5b2140ee51e/image.png" alt=""></p>
<p>자세한 내용은 밑에 유튜브 링크를 참고해주세요!
<a href="https://www.youtube.com/watch?v=4dEh1NlU-iI">22-2 B-CON 발표 영상</a></p>
<p>또한 우아한테크코스 프리코스라는 새로운 방식의 우테코 캠프가 있어 이를 지원하고 많이 진행하였습니다. 
<img src="https://velog.velcdn.com/images/muman_kim/post/956a8214-2410-4fab-9057-5ae3c26bad9b/image.png" alt=""></p>
<p><a href="https://velog.io/@muman_kim/series/%EC%9A%B0%ED%85%8C%EC%BD%94%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4">우테코 회고록 블로그</a></p>
<p>그리고 리액트 로드맵이라는 것을 보고 공부하려고 지금 많이 작성해뒀는데 글이 정리되지 않아 많이 포스팅 못하고 있습니다. 이 또한 진행해야할 것 같습니다.</p>
<p><a href="https://roadmap.sh/react">React Developer Roadmap: Learn to become a React developer</a></p>
<p>지켜봐주세요!</p>
<blockquote>
<p>돌이켜보면
아직 작성하지 못한 부분도 올해를 기점으로 작성해두고 차근차근 올려봐야곘습니다.
아직 못 올린 부분은 </p>
<ul>
<li>createPortal을 활용한 Modal 창</li>
<li>로그인 로직에 대한 고찰</li>
<li>useParams란 무엇이고 사용하는 이유, 사용시 조심해야할 것</li>
<li>지도앱을 활용한 복덕방</li>
<li>식단표 만들기
우테코 프리코스에 대한 회고록을 다시보니 JS 메서드와 그에 대한 순수함수를 작성하는 방식에 대한 많은 고찰을 했었습니다. 이를 잊지않고 내년 학습 목표로 삼을 생각입니다.</li>
</ul>
</blockquote>
<h2 id="12월">12월</h2>
<p>이렇게 마지막 12월까지 달려왔습니다. 12월에는 수많은 텀프와 팀플에 치였지만 꺽이지 않는 마음으로 종강을 맞이하였습니다.</p>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/ace88f02-aa7d-4913-aca3-2d4785eb227e/image.png" alt=""></p>
<p>또한 이번 우테코 프리코스를 탈락하기도 해서 선배의 추천으로 한 스타트업 기업에 인턴에 대한 면접을 지원하였습니다.
<img src="https://velog.velcdn.com/images/muman_kim/post/e8182427-8ab0-4fa3-8fca-c20d5d9d45dd/image.png" alt=""></p>
<p>이 글을 쓸때는 이제 크리스마스인데… 오늘도 솔크네요. 언젠간 저도 남들과 같은 따뜻한 크리스마스를 보내는 날이 오겠지요? 
저는 크리스마스를 대비해서 github페이지를 한번 꾸며봤습니다!</p>
<blockquote>
<p>돌이켜보면
12월에 정말 많은 일이 있었던 것 같지만 이렇게 큰 사건만 적어보니 아쉽게 결과가 아쉽네요 내년에는 결과가 좋은 1년을 보내고 싶습니다.
진짜 이렇게 살면 안되는데 과정도 과정이지만 결과가 그를 뒷받침한다고 생각합니다.</p>
</blockquote>
<h2 id="인생은">인생은…</h2>
<blockquote>
<p>인생은 스칼라보다는 벡터가 중요한 삶을 살아야한다.</p>
</blockquote>
<p>과거 중학교 2학년 때  국어 선생님이 소개팅 남자가 인생에 대한 생각이라고 했다. 정말 이과스러운 시각에서는 감동적인 말이었지만, 문과인 선생님에게는 들을 때는 별로지만 의미를 알고나니 정말 좋았다고 한다. (결국 국어 선생님은 다른 사람과 결혼하셨다 ^^)
<img src="https://velog.velcdn.com/images/muman_kim/post/1fc8f5ea-78e8-42ec-a351-ff9aebd08602/image.jpeg" alt=""></p>
<p>(힘내요 우리는 최고 공돌이!)</p>
<p>그렇게 제가 내년 23년부터 행하기로 정한 벡터는</p>
<ul>
<li>꾸준한 TIL (내용: JS 메서드, React Road맵을 따른 스터디 내용 올리기)</li>
<li>프로젝트 포스팅(코인 리코드, 코알라 Tistory에 써둔거 옮기기!)</li>
<li>1일 1 알고리즘 (주변 카카오 간 사람들의 조언)</li>
<li>계획된 헬스 프로젝트 진행하기(나도… 솔로 탈출)</li>
<li>Todo mate로 빡세게 관리하기</li>
</ul>
<p>여러분들도 새해계획 생각하시면 좋을 것 같습니다. 이렇게 돌아보니 또 색다르군요
내일의 나는 오늘의 나보다 더 앞서가도록 사는 사람이자 개발자가 되도록 노력하겠습니다.
읽어주셔서 감사합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ [우테코 프리코스] FE 김머핀의 4주차 회고록 - 이러다 다 죽어~]]></title>
            <link>https://velog.io/@muman_kim/%EC%9A%B0%ED%85%8C%EC%BD%94-%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4-FE-%EA%B9%80%EB%A8%B8%ED%95%80%EC%9D%98-4%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0%EB%A1%9D-%EC%9D%B4%EB%9F%AC%EB%8B%A4-%EB%8B%A4-%EC%A3%BD%EC%96%B4</link>
            <guid>https://velog.io/@muman_kim/%EC%9A%B0%ED%85%8C%EC%BD%94-%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4-FE-%EA%B9%80%EB%A8%B8%ED%95%80%EC%9D%98-4%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0%EB%A1%9D-%EC%9D%B4%EB%9F%AC%EB%8B%A4-%EB%8B%A4-%EC%A3%BD%EC%96%B4</guid>
            <pubDate>Mon, 28 Nov 2022 03:04:35 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/muman_kim/post/8eb8ff65-f0f2-408d-bb6a-81ee82b5977c/image.png" alt=""></p>
<h2 id="서론">서론</h2>
<p>이번 화요일 제출을 마지막으로 4주차로 프리코스를 마무리하였다. 아직 어떤 식으로 더 진행하겠다에 대해서는 아직 나온 내용은 없지만, 다들 열정적으로 하는 사람들도 많고 그들에게 배울 수 있는 점이 많다고 생각한다.
프리코스 1차 합격자는 대학 종강 이후이니 그전까지는 복습이나 테스트코드 Jest연습을 이어나가기로 결심하였다.
이번에 4주차를 쓰면서는 내가 파일을 분류했던 과정과 내가 생각하는 이번 우테코 코드를 평가해보고 느낀점으로 마무히라 것이다 :+1</p>
<p><del>제발 1차 합격 기원!!! 다들 살아서 만납시다!!! 이러다 다 죽으면 안돼</del>
<img src="https://velog.velcdn.com/images/muman_kim/post/1f758b02-615d-47b5-adfc-80dacd914d55/image.png" alt=""></p>
<p>(과연 나는 살아남을 수 있을까…)</p>
<h2 id="새로운-코드-대면">새로운 코드 대면</h2>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/a36d3353-1198-4cf5-8a36-edf35db1000c/image.png" alt=""></p>
<p>다리게임을 보니 넷플릭스에서 대흥행을 이끈 오징어게임 최종라운드의 게임이다.
이번에는 App 하나만 주는 것이 아닌 다양한 파일을 제안함으로써 답과 구조를 어느정도 의도해준 느낌이 들었다. 처음에 했을 때 너무 양식을 내가 따라가는 것이 아닌가도 많이 느꼈기에 이번에는 더 많은 변화를 주려고 노력하였다.</p>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/4a6a262e-2ac1-436b-8067-d5689cb4f6b3/image.png" alt=""></p>
<h3 id="코드-분할">코드 분할</h3>
<p>처음으로 이상하게 생각했던 것은 BridgeGame과 App만 클래스이고 나머지는 함수 객체로 이루어져있음을 확인하였다.
클래스인줄 알고 BridgeGame.move() 해보신 분은 개추해주세요~</p>
<p>그렇기에 일단은 두 가지 클래스와 나머지 파일들의 의미를 확실히 하기 위해서 그림을 정리해 보았다.</p>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/cd3b2c65-c1dc-4bf2-80df-aaf975e16ecd/image.png" alt=""></p>
<p>이렇게 나는 View와 Class 두가지로 분할하였고 그에 대한 나의 해석을 보여줄 수 있을 것이다.</p>
<h2 id="app에-대한-고찰">App에 대한 고찰</h2>
<p>항상 App에 대해서는 많은 생각이 들게 한다. 이곳에서 입력을 어떻게 부터 시작해야 하는 것인가에서 많은 코드를 수정하게 된다.
하지만 결국 나는 App이란 것이 결국 호출에 있어서는 최상위 계층이라고 생각한다.
무조건 출발은 이곳에서부터라는 인지를 한다.</p>
<p>물론 이렇게 틀에 가두는 생각이 좋은 방향은 아니지만, 결국 테스트 코드는 <code>app.play()</code>로 시작하기에 나쁘지 않은 방향성이다.</p>
<p>그래서 결국 이 게임에 대한 전반적인 흐름 속에서 이 코드가 무조건적으로 가지고 있어야할 것은 결국 다리의 상태이다.</p>
<p>게임을 보면 알겠지만 게임 한번에 이루어지는 다리의 상태는 한번이다.
게임이 종료되면 다시 시작할 수 있는 문구에 대한 조건이 없었다고는 하지만 있다고 하여도 재시작을 하게 된다면 다리의 상태를 관리하는 것이 더 편리해질 것이라고 생각하였다. </p>
<h2 id="class-vs-const--">Class vs const = {};</h2>
<p>이번 과제에서 분할에 대한 고찰에 접근하면서 가장 크게 느낀 것은 이 2가지의 차이가 무엇일까이다.</p>
<p>이 문제를 너무 복잡하게 가져가지 말자. 나는 복잡하게 생각할지 언정 읽는 사람조차 복잡하게 느껴서는 안된다. 최대한 가독성을 높히고, 이유있는 코드여야만 한다.</p>
<p>물론 나의 잣대이니 너무 깊게 생각하지 않아도 좋다.
나는 Class는 JS 관점에서 바라보았을때는 단순한 <code>Syntactic sugar</code>적인 면이 강하다고 생각하는 사람으로써 결국에 이는 하나의 분류를 간단하게 하기 위해서라고 생각한다.</p>
<p><code>Class</code>라는 결국 JS 입장에서는 함수에 지나지 않지만, <code>BridgeGame</code>이라는 하나의 클래스를 만들고 다리게임을 실행하기 위한 메서드가 하나의 목적의 통일성이 존재한다면은  <code>Class</code>를 사용하는 것이 좋은 방식이라고 생각한다.</p>
<p>그렇기에 나는 이렇게 주어진 코드에서 따로 <code>Class</code>를 더 분류하는 것보다는 구조를 그대로 가져가는 것이 좋다고 생각했다.</p>
<h2 id="view">View</h2>
<p>이번에 폴더구조에 너무 많이 시간 쓰지말라고 일부러 이렇게 나누어줬던 것 같다. 
<code>View</code>는 두 가지, 입력과 출력이다. 
여기서 더 나누어야할까? 굳이?
나는 언제나 코딩을 하면서, 결국은 내가 읽기 편해야 다른 사람도 읽기 편한 것이라고 생각하는 입장에서 출력을 정리하면 아래와 같다.</p>
<blockquote>
<p><strong>입력</strong></p>
<ol>
<li>사용자의 다리길이 입력</li>
<li>다리 움직이는 방향 입력</li>
<li>게임 재시작 혹은 종료 입력</li>
</ol>
<p><strong>출력</strong></p>
<ol>
<li>다리 상태 출력</li>
<li>최종 결과 메세지 출력</li>
</ol>
</blockquote>
<p>정말 많지 않다. 이것을 과연 내가 클래스로 병합하거나 하나의 폴더로 묶는 것이 큰 의미가 있을까?
단순히 가독성만 해칠 뿐이다. 그렇기에 나는 이 둘을 하나의 사용자의 시선에 노출되는 부분인 View에 해당하다고 생각하여 <code>View</code> 폴더에 분할하였다.</p>
<h2 id="constant상수">constant(상수)</h2>
<p>상수에 대해서는 저번에 우테코 피드백에 대해서 있던 내용을 많이 생각하였다.
<img src="https://velog.velcdn.com/images/muman_kim/post/4e70c5b4-e27e-492c-95fc-affe4f8c80c5/image.png" alt=""></p>
<p>그렇게 하다가 가끔씩 둘러보다보면 뭔가 이상한 상수 변수명들을 보며, 변수명의 중요성을 느꼈다.</p>
<pre><code>...SIGNAL.O: ‘O’,</code></pre><p>음... 어....
<img src="https://velog.velcdn.com/images/muman_kim/post/6ce379f1-5541-4001-9d8e-f5f1e511004d/image.png" alt=""></p>
<p>그래서 변수명에 대해서는 확실한 분리를 이용했다. 간단하게 View에서 활용하는 것과 BridgeGame에서 활용되는 내용으로 분리하여 아래 사진과 같이 완성하였다.</p>
<h2 id="느낀-점">느낀 점</h2>
<p>4주간의 JS만을 활용해서 과제를 수행하는 것은 알고리즘 스터디 이례로 처음이다. 실제로 로직을 작성하면서 JS 몰랐던 메서드들을 배워가면서 학습한 것이 JS에서의 많은 활용을 배웠던 것 같다. 프론트엔드 개발에 있어서 현재 JS는 핵심 코어인데, 리액트만 하면서 그 본질을 놓쳐서는 안된다는 것을 마음 속에 새기게 되었던 것 같다.
나중에 한번 메서드 공부도 해보면 좋을 듯!?
다들 1차합격도 하고! 최종코테도 합격하고! 우테코 정식 5기로 만났으면 좋겠습니다. 감사합니다.</p>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/0bd42dde-c779-4819-96cc-51ae5d8483a7/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Object.freeze()와 Object.seal()은 무엇인가?]]></title>
            <link>https://velog.io/@muman_kim/Object.freeze%EC%99%80-Object.seal%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</link>
            <guid>https://velog.io/@muman_kim/Object.freeze%EC%99%80-Object.seal%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</guid>
            <pubDate>Tue, 22 Nov 2022 02:46:49 GMT</pubDate>
            <description><![CDATA[<p>refrence : <a href="https://ui.toast.com/posts/ko_20220420">https://ui.toast.com/posts/ko_20220420</a></p>
<p>우테코 프리코스를 하며 사람들의 코드를 둘러보며 리뷰하더 도중 이러한 코드를 발견할 수 있었다.</p>
<pre><code class="language-jsx">cosnt 상수 = Object.freeze({
  a: &#39;A&#39;,
    ...//
})</code></pre>
<h2 id="서론">서론</h2>
<p>그래서 상수처리하는데 왜 저게 필요한 것이지? 라는 생각과 왜 저런 코드를 작성할까를 궁금해서 탐색을 하게 되었다.</p>
<p>먼저 <code>Object.freeze()</code> 란 무엇인가에 대해 알아보았는데</p>
<blockquote>
<p>The <strong><code>Object.freeze()</code></strong>method <em>freezes</em> an object. Freezing an object <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/preventExtensions">prevents extensions</a>
 and makes existing properties non-writable and non-configurable. A frozen object can no longer be changed: new properties cannot be added, existing properties cannot be removed, their enumerability, configurability, writability, or value cannot be changed, and the object&#39;s prototype cannot be re-assigned. <code>freeze()</code>
 returns the same object that was passed in.
reference: <a href="%5Bhttps://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze%5D(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze)">MDN-Object.freeze</a></p>
</blockquote>
<p>해석하면</p>
<p><strong><code>Object.freeze()</code> 메서드는 객체를 고정합니다. 
⇒</strong> 즉, 개발자가 생성한 상수 객체를 고정하겠다고 선언하는 과정,</p>
<p>**개체를 고정하면 확장이 방지되고 기존 속성을 쓸 수 없고 구성할 수 없게 됩니다. 고정된 개체는 더 이상 변경할 수 없습니다. 새 속성을 추가할 수 없고 기존 속성을 제거할 수 없으며 열거 가능성, 구성 가능성, 쓰기 가능성 또는 값을 변경할 수 없으며 개체의 프로토타입을 다시 할당할 수 없습니다.
⇒ 자바스크립트에서 객체의 경우에는 <code>const</code>를 해도 변경이 가능합니다.</p>
<ul>
<li>이유는** <code>const</code> 에 <code>object</code> 를 할당할 경우, memory heap 에 object 를 위한 메모리를 만들고 해당 주소값을 const 식별자에 할당합니다. 즉, 해당 주소값은 변하진 않지만 참조하고 있는 object 는 값을 변화시킬 수 있습니다.</li>
</ul>
<p>한마디로 자바스크립트 객체에서의 상수이지만 값이 변경되는 경우를 염려하여 이와 같은 freeze를 활용하여 처리할 수 있다는 것을 알 수 있었다</p>
<p>아하 그렇구나 이해하고 넘어가려던 중!
TOAST UI 팀에서 작성된 <code>Object.freeze()</code> 와 <code>Object.seal()</code> 이라는 내용의 대결을 보고서는 이에 대해 나의 지식으로 정리하고자 이렇게 글을 쓰게 됩니다. (출처는 상단에 표시하였습니다.)</p>
<h2 id="objectseal">Object.seal()?</h2>
<p>seal이라는 의미는 편지를 봉한다는 의미를 지니는데, 전달 받은 객체의 모든 속성을 구성할 수 없게(<code>non-configurable</code>) 만든다</p>
<pre><code class="language-jsx">const obj = { a: 100 };
Object.getOwnPropertyDescriptors(obj);
/* {
 *   a: {
 *        configurable: true,
 *        enumerable: true,
 *        value: 100,
 *        writable: true
 *      }
 * }
 *
 */

Object.seal(obj);
Object.getOwnPropertyDescriptors(obj);

/* {
 *   a: {
 *        configurable: false,
 *        enumerable: true,
 *        value: 100,
 *        writable: true
 *      }
 * }
 *
 */</code></pre>
<p>위의 예시해서 보면 <code>configurable</code>이 false로 변경된 것을 확인할 수 있다.</p>
<p>그렇다면 seal을 해두면은 개발자는 무엇이 제한되는 것인가?</p>
<pre><code class="language-jsx">// 1. 프로퍼티를 변경하는 경우
obj.a = 200;
console.log(obj.a);
// 200

// 2. 프로퍼티를 제거하는 경우
delete obj.a;
console.log(obj.a);
// 200

// 3. 새로운 프로퍼티를 추가하는 경우
obj.b = 500;
console.log(obj.b);
// undefined</code></pre>
<p>위의 1번을 제외한 나머지 동작들은 실행되지 않고 1번만 동작하게 되었다.</p>
<p>그렇다면 왜 1번만 이루어진 것인가?</p>
<p>봉인된 객체의 <code>configurable</code>이 <code>false</code>가 되어도 기존 속성값은 <code>200</code>으로 변경되게 된다. 앞서 설명했듯 <code>configurable</code>
을 <code>false</code>로 설정하면 속성을 쓸 수 없도록 만들어지지만 <code>writable</code>이 명시적으로 <code>true</code>면 큰 상관 없이 변경이 가능하다.</p>
<h2 id="그렇다면-이-둘의-차이는">그렇다면 이 둘의 차이는?</h2>
<p>위의 내용들을 정리하면 <code>Object.freeze()</code> 와 <code>Object.seal()</code>의 기능상 목적은 동일하다. 그렇다면 왜 굳이 2개가?</p>
<p>이 둘의 차이점을 설명해보겠다.</p>
<p><code>Object.freeze</code>와 <code>Object.seal</code> 중 <code>Object.freeze</code>가 보다 제한이 더 크게 걸린다.</p>
<pre><code class="language-jsx">const obj = { a: 100 };
Object.getOwnPropertyDescriptors(obj);
/* {
 *   a: {
 *        configurable: true,
 *        enumerable: true,
 *        value: 100,
 *        writable: true
 *      }
 * }
 *
 */
Object.freeze(obj);
Object.getOwnPropertyDescriptors(obj);
/* {
 *   a: {
 *        configurable: false,
 *        enumerable: true,
 *        value: 100,
 *        writable: false
 *      }
 * }
 *
 */</code></pre>
<p>위의 내용에서 보면 <code>configurable</code> 뿐만아니라 <code>writable</code>이 <code>false</code>로 변경된 것을 확인할 수 있다.</p>
<pre><code class="language-jsx">// 1. 프로퍼티를 변경하는 경우
obj.a = 200;
console.log(obj.a);
// 100

// 2. 프로퍼티를 제거하는 경우
delete obj.a;
console.log(obj.a);
// 100

// 3. 새로운 프로퍼티를 추가하는 경우
obj.b = 500;
console.log(obj.b);
// undefined</code></pre>
<p>물론 기능 예시의 기능의 경우를 보면 위에서 1번을 포함한 모든 기능이 작동하지 않는다. 이 결과를 통해 <code>Object.freeze()</code>를 <code>writable</code> 속성을 <code>false</code>로 변경시키기 때문에 <code>seal</code> 보다 더 엄격하다고 할 수 있다.</p>
<h2 id="objectfreeze와-objectseal-정리"><code>Object.freeze</code>와 <code>Object.seal</code> 정리</h2>
<h3 id="공통점">공통점</h3>
<ol>
<li>전달된 객체들은 확장할 수 없게 되어 새로운 속성을 추가할 수 없다.</li>
<li>전달된 객체 내부의 모든 요소들은 구성 불가능하게 되어 삭제할 수 없다.</li>
<li>스트릭트 모드에서 두 메서드를 사용한 객체 모두 <code>obj.b = 500</code> 같이 프로퍼티를 추가하는 명령어의 경우에는 <code>undefined</code>성립하지 않는다. </li>
</ol>
<h3 id="차이점">차이점</h3>
<ol>
<li><code>Object.seal</code>은 프로퍼티를 수정할 수 있지만 <code>Object.freeze</code>는 그렇지 않다.</li>
</ol>
<h3 id="결함">결함</h3>
<p>이 두가지 메서드에서 목적과 맞지 않게 결함이 존재하는데, 바로 중첩된 객체의 경우에 제대로 이루어지지 않는다는 것이다.</p>
<pre><code class="language-jsx">const obj = {
  foo: {
    bar: 10
  }
};

Object.seal(obj);</code></pre>
<p>위 와 같은 중첩된 객체를 seal을 통해 잠금했을 때 서술자는 이렇게 변경된다.</p>
<pre><code class="language-jsx">Object.getOwnPropertyDescriptors(obj);
/* {
 *   foo: {
 *        configurable: false,
 *        enumerable: true,
 *        value: {bar: 10},
 *        writable: false
 *      }
 * }
 */

Object.getOwnPropertyDescriptors(obj.foo);
/* {
 *   bar: {
 *        configurable: true,
 *        enumerable: true,
 *        value: 10,
 *        writable: true
 *      }
 * }
 */</code></pre>
<p>이 차이를 이해하셨나요? 그렇습니다. 개발자의 의도에서는obj를 seal로 봉하려 했지만 객체 내부에 있는 객체는 아무런 변화가 없음을 알 수 있다.</p>
<p>위와 같은 문제를 해결하기 위해 deepFreeze라는 방식을 추천하는데 코드는 아래와 같다.</p>
<pre><code class="language-jsx">function deepFreeze(object) {

  // 객체에 정의된 속성의 이름을 조회한다.
  var propNames = Object.getOwnPropertyNames(object);

  // 자신을 프리징하기 전 속성을 프리징한다.
  for (let name of propNames) {
    let value = object[name];

    if(value &amp;&amp; typeof value === &quot;object&quot;) {
      deepFreeze(value);
    }
  }

  return Object.freeze(object);
}</code></pre>
<h3 id="결론">결론</h3>
<p><code>Object.freeze</code>와 <code>Object.seal</code>은 확실히 객체를 봉인하여 불변성을 지켜준다는 장점이 있어 상수와 같이 <code>const</code>에서 객체의 불안함을 해결할 수 있는 새로운 메서드가 될 것이다. ****</p>
<p>하지만 중첩된 객체를 동결하기 위해선 <code>deepFreeze</code>를 사용해야 한다는 점을 반드시 고려해야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ [우테코 프리코스] FE 김머핀의 3주차 회고록 - 로또]]></title>
            <link>https://velog.io/@muman_kim/%EC%9A%B0%ED%85%8C%EC%BD%94-%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4-FE-%EA%B9%80%EB%A8%B8%ED%95%80%EC%9D%98-3%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0%EB%A1%9D-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BD%94%EB%93%9C</link>
            <guid>https://velog.io/@muman_kim/%EC%9A%B0%ED%85%8C%EC%BD%94-%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4-FE-%EA%B9%80%EB%A8%B8%ED%95%80%EC%9D%98-3%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0%EB%A1%9D-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BD%94%EB%93%9C</guid>
            <pubDate>Mon, 21 Nov 2022 13:28:00 GMT</pubDate>
            <description><![CDATA[<h2 id="서론">서론</h2>
<p>최근 동아리에서 2학기 MT 및 컨퍼런스의 진행을 하게 되면서 굉장히 바쁜 나날을 보냈다. 진행자체가 주말이어서 평일엔 컨퍼런스, MT 준비를 하고 주말에 진행하느라 우테코 과제에 쏟는 시간이 적었다. 
하지만! 행사 끝나고 바로 작업에 들어가서 어찌저찌 리팩토링도 하고 다했지만, 가장 막혔던 부분은 테스트 코드이다. 
테스트를 작성해야한다라… 일단은 많은 부분으로 테스트를 해야할 부분은 생각하고 예외사항을 추가했다.
이번 과제를 수행했던 나의 과거와 코드를 찾아보겠다.</p>
<h2 id="문제에-대한-고찰">문제에 대한 고찰</h2>
<p>일단 처음 문제를 보았을 때 로또에 대해서 많은 에러처리가 필요해 보였다.</p>
<ol>
<li>중복되지 않은 6자리의 수를 입력해야하며, 보너스 숫자라는 또 다른 입력이 존재하여 굉장히 테스트를 많이 해보기 좋은 문제였다.</li>
<li>로또 생성은 랜덤이지만 그에 대한 당첨번호는 사용자의 입력임을 알고 그에 대한 에러처리를 하고 테스트코드를 통해 이를 더 많이 해결해보자</li>
</ol>
<blockquote>
<p>1장에 1000원, 로또를 구매하여 자동으로 뽑기를 진행한다.
그리고 자동으로 뽑힌 로또에 대한 결과를 보여준다.
그리고 당첨번호는 사용자가 정한다. 
여기서 당첨번호 6개와 보너스 번호 1개가 존재한다.
그리고 당첨을 확인하고 당첨된 현황을 보여주고
복권구매비용과 복권당첨금액에 대한 수익률을 보여준다.</p>
</blockquote>
<h2 id="1-로또">1. 로또</h2>
<p>일단은 구매하는 로또는 하나당 1000원이다.
그렇다면 1500원을 내면 당연하게도 한 장만 구매 가능하다.
하지만 이렇게 1000원으로 나누어 떨어지지 않는 경우를 예외사항, 즉 에러로 분류한다.
그렇다면 어떻게 해야하나. 당연한게도 1000원으로 나누어 떨어지지 않음을 확인해서 이를 해결 가능할 것이다.</p>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/6b7e0756-8511-4e0b-8144-864818d736ce/image.png" alt=""></p>
<p>이렇게 다행하게 구매를 했다면 이제 자동으로 뽑힌 복권을 보여줘야한다.
다행히도 이 부분에 대해서는 우테코에서 지원해준 라이브러리에 아주 좋은 메서드가 존재했다.</p>
<pre><code class="language-js">val numbers = Randoms.pickUniqueNumbersInRange(1, 45, 6)</code></pre>
<p>위의 코드를 통해서 1~45의 숫자 중 서로 다른 6개의 수를 출력하는 기능이다.
그렇다면 이렇게 여러개의 로또를 생성하는 경우를 생각해보면,</p>
<p>만약 로또가 똑같은 것이 나온다면?
로또 1등이 여러명을 나온 것을 생각한다면, 굳이 생각하지 않아도 된다.</p>
<p>로또 생성에 대한 유효성을 정리하면</p>
<ol>
<li>로또를 구매하는 금액은 반드시 1000원 단위로 구매하여야한다.</li>
<li>로또의 구매는 제공되는 메서드로 생성하니 크게 상관하지 않아도 된다.
이  두 가지가 될 것이다.</li>
</ol>
<h2 id="2--사용자의-당첨번호-입력">2 . 사용자의 당첨번호 입력</h2>
<p>이렇게 로또가 생성된 후 사용자는 당첨번호 6개와 보너스 번호 1개를 입력해야한다.
이떄 입력하는 당첨번호의 양식은 <code>,</code>를 활용하여 입력된다.
여기서 예외 처리는 <code>,</code>를 활용하지 않는 입력, 중복되는 당첨번호,  1~45를 벗어나는 경우가 예외가 될 것이다.</p>
<p>그리고 보너스 번호의 경우에는
당첨번호와 중복되는 경우와, 1~45를 벗어나는 경우, 숫자가 아닌 경우 일 것이다.</p>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/5de8f200-8217-4249-bf92-f866e328eda7/image.png" alt=""></p>
<p>당첨번호와 보너스 입력의 예외를 정리하면</p>
<ol>
<li>입력이 반드시 숫자여야하며, 당첨 번호는 <code>,</code>를 통해 6개가 입력되어야한다.</li>
<li>1~45의 범위의 숫자여야 한다.</li>
<li>당첨번호 6개와 보너스 번호 1개는 서로 중복이 있어서는 안된다.
이 세가지가 될 것이다.</li>
</ol>
<h2 id="3-당첨-내역-확인하기">3. 당첨 내역 확인하기</h2>
<p>사용자가 정상적으로 입력한 당첨번호와 보너스 번호의 당첨을 선정해야한다.
당첨 조건은 아래와 같다.</p>
<pre><code>3개 일치 (5,000원) - 1개
4개 일치 (50,000원) - 0개
5개 일치 (1,500,000원) - 0개
5개 일치, 보너스 볼 일치 (30,000,000원) - 0개
6개 일치 (2,000,000,000원) - 0개</code></pre><p>먼저 3개 이상부터 당첨이며, 이때 중요한 것은 보너스 번호의 첨부이다.
만약 로또가 당첨번호 2개와 보너스 번호 1개와 일치하는 경우에는 3개로 인정하지 않는다. 
이는 로또의 규칙이니 이를 생각한다.
그렇다면 보너스 번호는 무엇이냐?
보너스 번호의 경우에는 당첨번호 5개가 일치한 경우 보너스 번호를 추가로 검사하여 보너스 번호가 있을 시 5개 보다 더 높은 금액을 받을 수 있다.
보너스 번호의 사용처는 5개만 있을 때 비교하여도 좋다.</p>
<p>여기서는 예외 사항보다는 로또의 규칙이다.</p>
<ol>
<li>보너스 번호는 5개를 일치시킨 로또만 비교한다.</li>
</ol>
<h2 id="4-수익률-계산">4. 수익률 계산</h2>
<p>위와 같이 당첨된 로또를 계산했다면, 총 당첨금액을 출력하는 것이 아닌 수익률을 계산해야한다. 
수익률 계산의 경우에는
<code>(총 당첨 금액) / (로또 구매 금액)</code>으로 계산된다.
이때 만약 수익률이 소수점으로 내려갈 경우를 어떻게 처리할 것인지 혹은 그 이상으로 올라간다면 어떻게 표현할지를 생각하는게 좋을 것 같다.</p>
<ol>
<li>수익률에 대한 퍼센트 숫자 표시에 대한 처리</li>
</ol>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/dca73142-3bf5-41e4-93eb-4b8e28d8c725/image.png" alt=""></p>
<h2 id="이번-문제를-풀며">이번 문제를 풀며…</h2>
<p>너무 늦게 시작해서인지 급한 마음에 코드를 작성한게 너무 보였다.
예외처리에 대해서 테스트코드를 작성하였지만, 원래 있던 부분을 조금씩만 수정해서 간단하게 작성하였다.
하지만 위에서의 예외사항을 분석하여 나만의 해결 로직을 생성했던 것이 많은 도움이 되었던 것 같다.
동아리 활동도, 학업도, 개인 개발에, TIL!
솔직히 동아리 행사 준비랑 행사진행이 걸려서 깊게 파지 못한 것은 너무 아쉬웠지만 4주차를 빠르게 마무리하고 리팩토링도 현재 마쳤다. 4주차는 게으르게 하지 말자!</p>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/8e730b1c-5d89-45b9-98b0-c4a10029d9d3/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[우테코 프리코스] FE 김머핀의 리마인드와 2주차 회고록]]></title>
            <link>https://velog.io/@muman_kim/%EC%9A%B0%ED%85%8C%EC%BD%94-FE-%EA%B9%80%EB%A8%B8%ED%95%80%EC%9D%98-%EB%A6%AC%EB%A7%88%EC%9D%B8%EB%93%9C%EC%99%80-2%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0%EB%A1%9D</link>
            <guid>https://velog.io/@muman_kim/%EC%9A%B0%ED%85%8C%EC%BD%94-FE-%EA%B9%80%EB%A8%B8%ED%95%80%EC%9D%98-%EB%A6%AC%EB%A7%88%EC%9D%B8%EB%93%9C%EC%99%80-2%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0%EB%A1%9D</guid>
            <pubDate>Wed, 16 Nov 2022 13:16:20 GMT</pubDate>
            <description><![CDATA[<h2 id="1주차에-이은-2주차">1주차에 이은 2주차…</h2>
<p>1주차를 마치고 리뷰할 시간도 없이 2주차를 시작하게 되었습니다.
이번 2주차에서는 특이하게 TDD를 공부해보세요! 라는 내용이 있었습니다.
처음에은 ‘에이 그냥 npm test를 돌리면 맞겠지~’ 라고 생각한 나의 안일한 판단 때문에 코드를 지우게 되었습니다. 그에 대한 내용으로 제가 경험한 2주차의 경험을 적어보겠습니다.</p>
<blockquote>
<p>숫자야구게임 만들기
컴퓨터가 랜덤한 반환한 서로 다른 3자리 숫자를 사용자가 3자리 숫자를 입력해서
입력한 숫자에서 해당 숫자+위치가 일치한 경우에는 <code>스트라이크</code>
숫자는 들어있지만 위치가 다른 경우 <code>볼</code>
스트라이크와 볼이 해당하는 경우가 없을 때는 <code>낫싱</code>을 출력한다.</p>
</blockquote>
<h2 id="어-뭔가-잘-안되는데">어 뭔가 잘 안되는데…?</h2>
<p>먼저 저는 해당 문제를 접근하면서 코딩테스트 느낌대로 뭐 입력 받고 그것을 돌리면 되겠지라고 생각하면 1주차 보다 간단한 문제이지 않을까 생각했습니다.
금방 끝나겠지라고 생각한 저를 보니 인터스텔라의 “그 장면”이 떠오르더군요.</p>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/02491437-8122-446e-a489-a86fec17ad1a/image.png" alt=""></p>
<pre><code class="language-js">  play() {
    this.printStartSentence();
    this.getRandomNumber();
    console.log(this.randomNumber);
    do {
      this.inputLine(&#39;숫자를 입력해주세요&#39;);
      //계산중....
    } while (this.input);
  }</code></pre>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/8c992f7f-72d7-4b7c-b14c-b58e1203833c/image.gif" alt=""></p>
<p>무엇인가 이상한 결과를 받았다. 어째서 저 반복문을 돌지 않을까?</p>
<h2 id="왜-이럴까">왜 이럴까?</h2>
<p>일단은 어떻게 이렇게될까를 생각해봤는데 내가 생각하는 scanf와 좀 다른 것 같다고 생각했습니다.</p>
<p>일단 처음 이에 대한 흐름을 예측해보았을 때 readLine의 경우 사용자가 입력하는 과정을 기다리지 않고 바로 다음 줄을 해석합니다.
인터프리터 언어에서 한 줄씩 읽어나간다고 가정했을 때 안에 함수 값을 저장하고 가는 것이 아닌 바로 함수를 실행하고 입력 전에 undefined로 계속해서 들어가게 되는 현상을 확인할 수 있었습니다.
정확히는 입력과정 전에 바로 다음 줄이 실행되기에 이런 현상이 발생한다고 추측하였습니다.</p>
<p>또한 이 과정에서 Missionutil을 확인하였을 때도 readLine은 동기처리로 되어있기에 제가 알고리즘처럼 풀던 방식은 안된다는 것을 깨달았습니다.</p>
<h2 id="그러면-어떻게-해야할까">그러면 어떻게 해야할까?</h2>
<p>일단 처음 떠오른 생각은 비동기 처리였습니다.
입력과 계산 값을 기다려주고 결과가 처리되면 바로 다음을 읽어나는 async , await과 Promise를 활용하여 비동기 처리를 통해 지원하였다.</p>
<p>그래서 각 계산을 담당하는 함수를 Promise로 감싸서 문제를 해결하려했습니다.</p>
<pre><code class="language-js">function inputLineSync(questionText) {
  return new Promise((resolve, reject) =&gt; {
    Console.readLine(questionText, (input) =&gt; resolve(input));
  });
}</code></pre>
<p>출력은 정상적으로 나오고 있었습니다.</p>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/a41e23d8-45cd-4bcd-a6da-bcbdc0a6bb26/image.png" alt=""></p>
<p>이렇게 저는 완벽하게 해결하였군! 생각했지만 어림도 없지!</p>
<h2 id="테스트-넌-뭐니">테스트 넌 뭐니?</h2>
<p>바로 테스트 코드에서 절대 통과가 불가능 했습니다. 왜냐하면 테스트 코드를 읽으면서 분석하였는데, 테스트 코드는 동기 상태이기에 아직 입력이 다 들어가지 않은 상태에서 출력이 나왔는지를 검사가 이루어져서 테스트가 통과할 수 없는 구조였다.</p>
<p><del>테스트 코드 Jest 분석에 대해서는 다다음 포스팅에서</del></p>
<p>그러니 테스트를 통과하기 위해서는 동기적인 코드를 생각해서 작성해야한다.</p>
<h2 id="동기적으로-하기-위해서는">동기적으로 하기 위해서는…</h2>
<p>그렇다면 일단 반복문은 불가능하다는 것을 깨달았으니, 이제 이걸 어떻게 해야하나라는 고민에 빠졌습니다.</p>
<p>과연 어떻게 할것인가에 대한 고민 속에서 다시 한번 과정을 생각합니다.</p>
<p>결국에는 readLine이 먼저 실행되는 구조 속에서 연산을 진행해야한다.
그렇다면 연산하는 과정 속에서 함수 호출이 이루어져야하지 않을까라는 생각이 들었고 </p>
<blockquote>
<p>다시 입력해야하는 경우 자기 자신을 불러오는 구조?
그렇게 생각하여 콜백형식의 구조로 작성하게 되었습니다.</p>
</blockquote>
<pre><code class="language-js">  inputUserNumber (questionText) {
    Console.readLine(questionText, (input) =&gt; {
      this.isValidateInputNumber(input);

      this.userNumber = stringToArray(input);
      let result = compareNumber(this.userNumber, this.randomNumber);

      if (result) {
        this.gameOption(&#39;게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.\n&#39;);
      } else {
        this.inputUserNumber(questionText);
      }
    })
  }
</code></pre>
<p>상기하면서 다시 쓰고 있지만 역시 좀 구조가 좋지는 않아 보입니다. 허허…</p>
<h3 id="리마인드">리마인드..</h3>
<p>현재 3주차가 마무리된 지금 아직 4주차와 최종코테를 남겨두고 있습니다,
2주차를 리마인드하는 과정이기에 현재 3주차도 4주차와 더불어 진행하고 최종코테 이전에 한번 4주차와 함께 정리해볼 예정입니다.</p>
<blockquote>
<p>느낀점
2주차에서 가장 크게 문제를 생각하며 보게 된 것은 위와 같은 readLine의 활용이 어렵다는 것입니다. 여러번 반복 입력에 대한 처리를 접근하는 방식을 알 수 있는 프리코스 과정이었습니다.
처음에는 어 이게 안되네. 아 이거 누가만든거야 하면서 짜증이 나기도 하였습니다. 
언제나 문제는 바로 <code>나</code>! 의심은 언제나 나에게로 향해야한다는 것을 깨닫고 문제를 접근하였습니다.
어려운 과제라고 해도 문제요소를 하나씩 지워간다면 해결할 수 있을테니, 급한 마음은 접어두고 왜이러지? 왜이럴까? 라는 의문으로써 문제를 접근해야한다고 느꼈습니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[우테코 프리코스] FE  김머핀의  1주차 회고록 ]]></title>
            <link>https://velog.io/@muman_kim/FE-%EA%B9%80%EB%A8%B8%ED%95%80%EC%9D%98-%EC%9A%B0%ED%85%8C%EC%BD%94-%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4-1%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0%EB%A1%9D</link>
            <guid>https://velog.io/@muman_kim/FE-%EA%B9%80%EB%A8%B8%ED%95%80%EC%9D%98-%EC%9A%B0%ED%85%8C%EC%BD%94-%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4-1%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0%EB%A1%9D</guid>
            <pubDate>Mon, 07 Nov 2022 16:34:39 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/muman_kim/post/fc5d7ddc-d15e-46c1-86eb-72ada08eb234/image.png" alt=""></p>
<p>이번에 처음 지원해보는 우아한 테크 코스에서 프리코스를 참여하게 되었습니다.</p>
<h2 id="온보딩-미션-수행">[온보딩] 미션 수행</h2>
<p>먼저 우아한테크코스 github에 초대되어 매 주차마다 미션을 진행하는데 1주차는 간단하게 문제를 7개를 풀어서 제출하는 것으로 진행하였다.
그리고 해당 문제를 풀면서 내가 함수 분할을 하거나 변수명을 결정하는 그런 방식에 대해서 나의 생각을 되짚어 보고 복기해보는 시간을 가져보았고 이를 정리하여 이번 글을 쓰게 됩니다.</p>
<h2 id="문제-1">문제 1</h2>
<hr>
<h3 id="🚀-기능-요구-사항">🚀 기능 요구 사항</h3>
<blockquote>
<p>포비와 크롱이 페이지 번호가 1부터 시작되는 400 페이지의 책을 주웠다. 책을 살펴보니 왼쪽 페이지는 홀수, 오른쪽 페이지는 짝수 번호이고 모든 페이지에는 번호가 적혀있었다. 책이 마음에 든 포비와 크롱은 페이지 번호 게임을 통해 게임에서 이긴 사람이 책을 갖기로 한다. 페이지 번호 게임의 규칙은 아래와 같다.</p>
<ol>
<li>책을 임의로 펼친다.</li>
<li>왼쪽 페이지 번호의 각 자리 숫자를 모두 더하거나, 모두 곱해 가장 큰 수를 구한다.</li>
<li>오른쪽 페이지 번호의 각 자리 숫자를 모두 더하거나, 모두 곱해 가장 큰 수를 구한다.</li>
<li>2~3 과정에서 가장 큰 수를 본인의 점수로 한다.</li>
<li>점수를 비교해 가장 높은 사람이 게임의 승자가 된다.</li>
<li>시작 면이나 마지막 면이 나오도록 책을 펼치지 않는다.</li>
</ol>
<p>포비와 크롱이 펼친 페이지가 들어있는 배열 pobi와 crong이 주어질 때, 포비가 이긴다면 1, 크롱이 이긴다면 2, 무승부는 0, 예외사항은 -1로 return 하도록 solution 메서드를 완성하라.</p>
</blockquote>
<h3 id="문제-정의">문제 정의</h3>
<p>첫번째 문제에서는  각 pobi와 crong에 대하여 길이가 2인 배열 두개를 입력 받는데 이 값은 연속적이어야한다. (책을 생각했을때 왼쪽 페이지가 5이면 오른쪽은 6일 것이다)</p>
<p>그리고 이 두 개의 값에 대하여 연산의 횟수를 생각해보았을 때, 요구사항에서 2번과 3번은 각 페이지 값에 대하여 곱셈값과 덧셈값을 해주어야 한다.
왼쪽 페이지 연산 2+
오른족 페이지 연산 2+
그렇다면 유저 한 명에게는 선택할 수 있는 숫자의 경우가 4가지인가? 아니다.
2번과 3번을 자세히 읽어 보면 가장 큰 수를 구하는 것이므로 
왼쪽에서 곱셈과 덧셈 값 중 가장 큰 수 하나
오른족에서 곱셈과 덧셈값 중 가장 큰 수 하나를 구하고서 비교한 이후 더 큰 값을 반환하면 된다.</p>
<p>처음 문제를 해결할 때 조건문을 써보니 depth가 굉장히 심해져 가독성이 떨어짐을 느꼈고 해당 부분을 함수로 분할하였다.</p>
<h3 id="함수-정의">함수 정의</h3>
<p>처음에는 페이지에서 곱셈 값과 덧셈 값을 계산해주고 더 큰 값을 반환하는 함수
<code>calcPageLargerValue</code></p>
<p>다음은 왼쪽과 오른쪽 페이지의 최댓값을 비교하여 유저의 최댓값을 반환하는 함수 
 <code>getUserMaxScore</code></p>
<p>이 두 가지로 분할하여 최댓값을 계산하는 과정에서 단순히 <code>&gt;</code>, <code>&lt;</code> 부등호등을 사용하는 것이 아닌 자바스크립트에 <code>Math.max(...)</code>를 활용하였다.
해당 함수는 괄호 안에 입력받는 숫자 중 가장 큰 수를 반환합니다. </p>
<ul>
<li><code>Math.max(1 , 2)</code>인 경우에는 2를 반환</li>
</ul>
<p>그리고 최종적으로 결과 값에 대해서는 <code>if … else</code> 를 활용하여 결과값을 출력하였습니다.</p>
<p>함수로 분할할까 하였지만 결과 값에 대한 연산을 분리하는 것에 대해서 오히려 단순 조건문으로 이루어진 것인데 함수 분할의 의미의 필요성을 느끼지 못하여 그대로 작성하였습니다.</p>
<h3 id="느낀-점">느낀 점</h3>
<p>1번 문제에 대해서는 큰 막힘 없이 풀었고 함수를 분할하면서 분할하는 기준을 세워보는 것도 좋을 것 같다. 단순한 필요성을 느끼지 못했다 보다는 명확한 근거가 있다면 좋을 지도?!</p>
<h2 id="문제-2">문제 2</h2>
<hr>
<h3 id="🚀-기능-요구-사항-1">🚀 기능 요구 사항</h3>
<blockquote>
<p>암호문을 좋아하는 괴짜 개발자 브라운이 이번에는 중복 문자를 이용한 새로운 암호를 만들었다. 예를 들어 “browoanoommnaon”이라는 암호문은 다음과 같은 순서로 해독할 수 있다.</p>
<ol>
<li>“browoanoommnaon”</li>
<li>“browoannaon”</li>
<li>“browoaaon”</li>
<li>“browoon”</li>
<li>“brown”</li>
</ol>
<p>임의의 문자열 cryptogram이 매개변수로 주어질 때, 연속하는 중복 문자들을 삭제한 결과를 return 하도록 solution 메서드를 완성하라.</p>
</blockquote>
<h3 id="문제-정의-1">문제 정의</h3>
<p>cryptogram이란 암호문이 존재하며,
그 암호문은 2번 이상 반복되는 문자열들을 제거하고 붙이고,
또 다시 2번이상 반복되는 문자열이 존재하지 않을 때까지 반복하면, 결국은 단일로 이루어진 단어가 나올 것이며 해당 단어를 출력하면 된다.</p>
<h3 id="함수-정의-1">함수 정의</h3>
<blockquote>
<p>결국에는 두 번 이상 반복되는 것을 찾아서 공백으로 만들 수 있는 함수가 있다면 해당 함수를 반복을 돌리면서 없을 때 종료하는 것으로 문제를 해결할 수 있을 것 같았다.</p>
</blockquote>
<p>처음에 생각한 것은 while문을 통해 반복문처리도 가능할 것 같다고 생각했지만, 자바스크립트 메서드를 활용하는 방식으로 더 생각해보았다.</p>
<p>그렇게 생각해낸 것이 정규표현식이다.</p>
<p>정규표현식을 통해 만약  a~z중 2번 이상 반복되는 문자열을 확인한다면, 해당 부분을  <code>replace()</code>를 통해 공백으로 바꿔주는 것을 반복하여 반복이 없어진 경우에 대해 최종 값을 반환하는 형태를 생각했다.</p>
<h3 id="느낀점">느낀점</h3>
<p>정규표현식을 사용하니 굉장히 짧고 간단하게 작성할 수 있었다. 다음부터 문자열에 대한 자세를 정규표현식으로 접근해야겠다는 나름의 솔루션을 찾게 되었다.
하나 생긴 의문으로는 정규표현식의 경우에는 어떻게 탐색하는 탐색 방식에 대해 궁금증이 생겼다. (좋은 블로깅의 주제가 될 것 같다.)</p>
<h2 id="문제-3">문제 3</h2>
<hr>
<h3 id="🚀-기능-요구-사항-2">🚀 기능 요구 사항</h3>
<blockquote>
<p>배달이가 좋아하는 369게임을 하고자 한다. 놀이법은 1부터 숫자를 하나씩 대면서, 3, 6, 9가 들어가는 숫자는 숫자를 말하는 대신 3, 6, 9의 개수만큼 손뼉을 쳐야 한다.</p>
<p>숫자 number가 매개변수로 주어질 때, 1부터 number까지 손뼉을 몇 번 쳐야 하는지 횟수를 return 하도록 solution 메서드를 완성하라.</p>
</blockquote>
<h3 id="문제-정의-2">문제 정의</h3>
<p>간단하게 369 게임이며, 만약 해당 숫자까지 1부터 올라가면서 3, 6, 9 가 해당 문자에 포함되어있는 횟수를 계산하여 반환하는 문제이다.</p>
<h3 id="함수-정의-2">함수 정의</h3>
<p>결국은 입력받은 숫자만큼 반복은 해야할 것이다.
그러한 반복이 되는 것에 대해서 3, 6, 9 가 포함된 횟수를 정규표현식을 통해 포함된 경우에 대해서만 3,6,9의 갯수를 계산하도록 하였다.
갯수 계산에 대해서는 <code>match().length</code>를 통해 계산하였다.</p>
<p>또한 계산에 있어서 1이나 2에서는 굳이 검사할 필요가 없기에 3부터 반복문을 검사하였다.</p>
<h3 id="느낀점-1">느낀점</h3>
<p>정규표현식을 활용하여 강력한 문제 풀이가 가능했다. 번거롭게 더 함수를 작성할 생각이 들지 않았다. 하지만 정규표현식에 대한 초기 정의에서는 꽤나 번거롭기도 하였고 학습을 요구한다는 것을 느꼈다.</p>
<h2 id="문제-4">문제 4</h2>
<hr>
<h3 id="🚀-기능-요구-사항-3">🚀 기능 요구 사항</h3>
<blockquote>
<p>어느 연못에 엄마 말씀을 좀처럼 듣지 않는 청개구리가 살고 있었다. 청개구리는 엄마가 하는 말은 무엇이든 반대로 말하였다.</p>
<p>엄마 말씀 word가 매개변수로 주어질 때, 아래 청개구리 사전을 참고해 반대로 변환하여 return 하도록 solution 메서드를 완성하라.
| A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z |
| — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
| Z | Y | X | W | V | U | T | S | R | Q | P | O | N | M | L | K | J | I | H | G | F | E | D | C | B | A |</p>
</blockquote>
<h3 id="문제-정의-3">문제 정의</h3>
<p>영어 문장이 주워지면 모든 문자에 대해 문자를 변경하는데 
변경하는 대상은 영어 소문자와 대문자이며, A ~ Z 를 Z ~ A 형식으로 변경하는 작업을 진행한다.
그리고 입력 받는 문장에 대해 영어에서만 처리가 이루어져야 한다.</p>
<h3 id="함수-정의-3">함수 정의</h3>
<p>먼저 알파벳인 영어에서 A~Z a-z 에 대한 탐색이 이루어져야하므로 탐색 정규표현식
<code>ALPHABETICAL_REG_EXP</code></p>
<p>탐색된 모든 수에 대하여 청개구리식으로 변경하는 함수 <code>frogTranslation</code></p>
<p>이 두 개를 활용하여 계산하기로 하였습니다.
문자열에 대한 탐색으로는 정규표현식을 통해 하였는데 그렇다면 변경을 어떻게 할지를 고민하였고,  변경에 대한 <code>replace()</code>를 더 학습하였습니다
그리고  <code>replace()</code>에 두 번째 인자로 익명함수를 통하여 선택되는 문자에 대한 정보를 받을 수 있는 기능을 통하여 각 영어 문자에 변화를 주었습니다.
그리고 청개구리식 변환에서 아스키코드를 활용하여 계산을 하였습니다.
계산 과정에서 착각할 뻔 했던 부분은 영어의 아스키코드는 대문자와 소문자가 연속해 있지 않으며, 코드 변환 이후 다시 글자로 반환해주는 과정을 거쳐야 한다는 것이었습니다.
A~Z : 65 ~ 90
a-z: 97 ~ 122
91 ~ 96은 다른 문자</p>
<h3 id="느낀점-2">느낀점</h3>
<p>문자 변형에 대해서 아스키코드를 사용해서 변경하였는데 이 것 말고는 딱히 다른 풀이법이 떠오르지 않았다. 그리고 또 이 문제에서 정규표현식이 활약하였다.
나름 깔끔한 풀이가 지속되고 있어서 기분은 좋았다.</p>
<h2 id="문제-5">문제 5</h2>
<hr>
<h3 id="🚀-기능-요구-사항-4">🚀 기능 요구 사항</h3>
<blockquote>
<p>계좌에 들어있는 돈 일부를 은행에서 출금하고자 한다. 돈 담을 지갑이 최대한 가볍도록 큰 금액의 화폐 위주로 받는다.</p>
<p>돈의 액수 money가 매개변수로 주어질 때, 오만 원권, 만 원권, 오천 원권, 천 원권, 오백원 동전, 백원 동전, 오십원 동전, 십원 동전, 일원 동전 각 몇 개로 변환되는지 금액이 큰 순서대로 배열에 담아 return 하도록 solution 메서드를 완성하라.</p>
</blockquote>
<h3 id="문제-정의-4">문제 정의</h3>
<p>결국에는 들어오는 금액에 대해서 돈의 액수에서 가장 큰 지폐권부터 가질 수 있는 최대를 가지면서 계산하면 자연스럽게 최소의 지폐와 동전의 갯수를 가지게 될 것이다.</p>
<h3 id="함수-정의-4">함수 정의</h3>
<p>가장 먼저 떠오른 것은 해당 지폐권에 대한 정보를 담고 있는 정적인 데이터가 필요하다고 판단하여 해당 상수를 선언하여 사용하였다.
<code>const STATIC_MONEY = [50000, 10000, 5000, 1000, 500, 100, 50, 10, 1];</code></p>
<p>두 번째로는 상수로 선언한 단위는 큰 순서대로 선언되었으니,  50000원부터 입력 받은 값을 나누어준 값을 answer 배열에 push하는 형태로 구현하였습니다.
결과적으로 큰 수를 먼저 나누게 되고, 마지막 1까지 나눈 수를 계산하는 것으로 결과가 마무리 됩니다.</p>
<h3 id="느낀점-3">느낀점</h3>
<p>지폐의 단위를 먼저 선언하여 해당 값을 순서대로 계산을 먼저 생각해서 하였지만 마지막 1계산 이후 return을 0으로 설정하였는데 뭔가 쓰레기 값을 사용하게 된 것 같아 불편했다. 한번 더 함수를 깔끔하게 할 수 있도록 노력해야겠다.</p>
<h2 id="문제-6">문제 6</h2>
<hr>
<h3 id="🚀-기능-요구-사항-5">🚀 기능 요구 사항</h3>
<blockquote>
<p>우아한테크코스에서는 교육생(이하 크루) 간 소통 시 닉네임을 사용한다. 간혹 비슷한 닉네임을 정하는 경우가 있는데, 이러할 경우 소통할 때 혼란을 불러일으킬 수 있다.</p>
<p>혼란을 막기 위해 크루들의 닉네임 중 <strong><em>같은 글자가 연속적으로 포함</em></strong> 될 경우 해당 닉네임 사용을 제한하려 한다. 이를 위해 같은 글자가 연속적으로 포함되는 닉네임을 신청한 크루들에게 알려주는 시스템을 만들려고 한다.</p>
<p>신청받은 닉네임 중 <strong><em>같은 글자가 연속적으로 포함</em></strong> 되는 닉네임을 작성한 지원자의 이메일 목록을 return 하도록 solution 메서드를 완성하라.</p>
</blockquote>
<h3 id="문제-정의-5">문제 정의</h3>
<p>입력의 경우에 대해서 닉네임과 이메일이 입력된다
입력된 사용자의 이름과 이메일이 저장되고 입력된 사용자들의 닉네임 중 2글자가 같은 경우에는 해당 유저의 이메일과 닉네임을 반환해야한다. 
중복된 인원 두 명 모두 나와야한다.</p>
<h3 id="함수-정의-5">함수 정의</h3>
<p>먼저 닉네임에 대한 내용을 2글자씩 나누어서 저장해주는 함수 -<code>setNicknameDirectory</code>
검색 중인 닉네임이 저장된 nickNameDirectory에 있는 글자 중 2글자가 중복이 존재하는 지를 파악하는 함수 -<code>isDuplicateNickname</code></p>
<p>현재 검색 중인 닉네임에 대한 중복이 있는지를 검사하는 함수 - <code>isDuplicateNowNickname</code> </p>
<ul>
<li>이 함수의 경우에는 예외 상황을 생각했던 예를 들어 닉네임이 “김머판머판” 인 경우 닉네임을 2글자로 나누어서 저장하는 과정에서 중복으로 반환하는 경우가 있다.</li>
<li>그래서 이를 방지하고자 넣은 함수이다.</li>
</ul>
<p>중복이 일어난 닉네임과 이메일을 정답배열에 넣는 함수 - <code>setAnswer</code></p>
<h3 id="느낀점-4">느낀점</h3>
<p>여기서부터 조금 많이 구현에 대한 생각이 많아지고 브루트포스와 같은 무식한 방식으로 했다면 굉장히 오랜 시간이 걸렸을 것 같다. 마침 JS Object를 통해 키값을 통한 value 접근의 시간을 줄임으로써 실행시간에서 많은 손해를 보지 않고 해결할 수 있었던 문제 였다.</p>
<h2 id="문제-7">문제 7</h2>
<hr>
<h3 id="🚀-기능-요구-사항-6">🚀 기능 요구 사항</h3>
<blockquote>
<p>레벨 2의 팀 프로젝트 미션으로 SNS(Social Networking Service)를 만들고자 하는 팀이 있다. 팀에 속한 크루 중 평소 알고리즘에 관심이 많은 미스터코는 친구 추천 알고리즘을 구현하고자 아래와 같은 규칙을 세웠다.</p>
<ul>
<li>사용자와 함께 아는 친구의 수 = 10점 </li>
<li>사용자의 타임 라인에 방문한 횟수 = 1점</li>
</ul>
<p>사용자 아이디 user와 친구 관계를 담은 이차원 배열 friends, 사용자 타임 라인 방문 기록 visitors가 매개변수로 주어질 때, 미스터코의 친구 추천 규칙에 따라 점수가 가장 높은 순으로 정렬하여 최대 5명을 return 하도록 solution 메서드를 완성하라. 이때 추천 점수가 0점인 경우 추천하지 않으며, 추천 점수가 같은 경우는 이름순으로 정렬한다.</p>
</blockquote>
<h3 id="문제-정의-6">문제 정의</h3>
<p>먼저 user를 중심으로 생각해야한다. 중요한 것은 user와 관련이 있는자를 구분하는 것으로 시작한다.</p>
<p>점수를 주는 기준</p>
<ul>
<li>user의 친구(friend)의 친구(other friend)이지만 user와 친구(friend)가 아닌 경우 10점</li>
<li>user의 친구가 아닌 경우, user의 타임라인 방문 회수 당 1점</li>
</ul>
<p>탐색 순서의 경우에는 먼저 user의 친구(friend)들을 한명씩 살펴야한다.
그리고 user의 친구(friend)들의 친구들(other friend) 중에 user의 친구가 아닌 친구들은 모두 10점씩 부여해 준다.
10점이 부여되는 친구들은 타임라인에 몇회 방문했는지 계산하고 방문 횟수당 1점을 추가로 부여한다. 
그렇게 최종적으로 결정된 인원은 정답 배열에 넣게되는데 
만약 배열이 비어있는 경우에는 그대로 집어넣는다.
만약 숫자가 하나라도 있다면 그와 점수를 비교하고 더 높을경우 자리를 교환하고 낮을 경우 다음 자리로 이동한다.</p>
<p>그렇게 탐색과 정렬이 완성되면 결과값을 반환한다.</p>
<h3 id="함수-정의-6">함수 정의</h3>
<p>form에 입력되는 친구들에 대한 관계를 저장한다. 키값이 user에게 value로 친구들의 이름이 담긴 리스트를 갖게하고, 나머지 친구와 친구리스트 형태로 저장하는 함수 - <code>updateFriendsList</code></p>
<p>만약 추천 리스트에 중복된 인원의 이름이 들어오는 지를 확인하는 함수 - <code>isDuplicateRecommendFriendList</code></p>
<p>추천 리스트에 들어가는 인원들을 반환하는 함수 - <code>getRecommendFriendList</code></p>
<p>추천 리스트에 들어가는 인원들의 순위를 정렬하고 저장하는 함수 - <code>setRecommendFriendList</code></p>
<h3 id="느낀점-5">느낀점</h3>
<p>개인적으로 가장 구현이 어려웠다. 마지막 문제여서 그런지 생각 보다 시간도 많이 쓰고 코드가 길어지게 된 부분이 컸다. 또한 인수들에 대한 네이밍도 중요하겠다고 생각하여 다양한 방식으로 시도를 해보았다. 시도는 좋았지만 그닥 퀄리티가 좋아보이지 않았다.
아무래도 더 좋은 구조를 생각할 수 있었을 텐데 너무 아쉽다. 앞으로 남은 기간 더 열심히 배우는 자세로 임해야겠다.</p>
<h2 id="잎으로의-다짐">잎으로의 다짐</h2>
<hr>
<p>1주차에 대한 회고가 많이 늦어지고 있다. 좀 더 나의 성장을 위해 투자해야할 때이지 않은가 싶다. 이번 년도 연말 나의 학구열을 불태워서 이번 우테코 프리코스 기간 동안 성장할 것이다. 만약 합격 된다면 좋겠지만, 되지 않더라도 이 경험들이 나의 밑거름이 되어 더 멋있는 개발자로 성장하는 내가 될 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React를 찾기 위한 여정: create-react-app]]></title>
            <link>https://velog.io/@muman_kim/React%EB%A5%BC-%EC%B0%BE%EA%B8%B0-%EC%9C%84%ED%95%9C-%EC%97%AC%EC%A0%95-create-react-app</link>
            <guid>https://velog.io/@muman_kim/React%EB%A5%BC-%EC%B0%BE%EA%B8%B0-%EC%9C%84%ED%95%9C-%EC%97%AC%EC%A0%95-create-react-app</guid>
            <pubDate>Tue, 01 Nov 2022 17:41:28 GMT</pubDate>
            <description><![CDATA[<h1 id="react를-찾기-위한-여정">React를 찾기 위한 여정</h1>
<p><a href="https://roadmap.sh/react">React Developer Roadmap: Learn to become a React developer</a>
최근 리액트 스터디 로드맵이 있었는데 한번 해볼 가치가 있다고 여겨져 시작하게 되었다.
2학기가 끝나고 겨울방학이 끝나기 전까지를 목표로 잡고 꾸준히 해보겠다.
물론 하면서 머릿속에서 복습하는 과정도 거치면서 천천히 진행해보겠다 아자!</p>
<h1 id="react를-찾기-위한-여정-create-react-app">React를 찾기 위한 여정: create-react-app</h1>
<p>우리가 처음 리액트를 시작할 때 사용하는  <code>create-react-app</code> 이라는 명령어를 사용한다 과연 이 명령어는 무엇일까?</p>
<p><code>create-react-app</code> 은 공식적으로 지원되는 단일 페이지 React 애플리케이션을 만드는 방법입니다. 구성이 없는 최신 빌드 설정을 제공합니다.</p>
<p>명령을 통해 선언하게 된다면 아래와 같은 디렉토리가 생성됩니다.</p>
<blockquote>
<p>npx create-react-app cra-example
cd cra-example
npm start</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/muman_kim/post/0e74f488-2dee-432b-ac63-8ff73d1c9b7e/image.png" alt=""></p>
<p>위에서 하나부터 폴더를 설명해보겠다.</p>
<h2 id="node_modules">node_modules</h2>
<blockquote>
<p>node_modules
실제 라이브러리가 설치되는 디렉토리이다.</p>
</blockquote>
<p>가끔 이 노드 모듈도 github에 올려야되는지를 묻는 질문이나 올리는 실수를 하기도 하지만 이 디렉토리는 원격 저장소에 저장할 필요가 없다.
왜냐하면 위의 설명처럼 실제 라이브러리가 설치되는 디렉토리이다.</p>
<blockquote>
<p>그렇다면 필요한 것 아닌가요?</p>
</blockquote>
<p>우리는 <code>package.json</code>이라는 파일에 내가 프로젝트에 사용한 모든 라이브러리들에 대한 종류와 버전 정보를 담고 있기 때문에 해당 파일만 원격저장소에 올려두면, 다른 로컬환경에서 <code>npm install</code>을 통해 <code>package.json</code>에 작성되어 있는 그대로 라이브러리를 추가하여 실행할 수 있다.</p>
<blockquote>
<p>-g 옵션은 무엇인가요?
global 라이브러리에 설치한다는 의미로 전역 파일에 설치됩니다.
이 파일의 위치는 root -g 를 통해 알 수 있습니다.
일반적으로 해당 파일에 대해 찾기도 어렵기에 권장하지 않는 옵션입니다.
만약 사용하게 된다면 root node_modules 폴더에 추가되어서 모든 프로젝트에 접근이 가능해지기에 보통 개발 과정 중 터미널에서 실행하는 독립적인 성격의 도구들이 해당됩니다.</p>
</blockquote>
<h2 id="public">public</h2>
<p>일반적으로 폰트나 이미지, styleSheet와 같은 static file들을 보관하는 장소로 사용합니다. 
<code>public</code>폴더는 webpack에 의해 번들링 되지 않고 그래도 build폴더로 들어가게 됩니다.
또한 해당 <code>public</code> 폴더의 경우에는 따로 import를 활용하지 않고 접근이 가능합니다.
주요한 파일로 index.html이 있습니다.</p>
<blockquote>
<p><em>index.html</em>
public 폴더에 있으며, 메인 프로그램인 index.js에 대응되는 파일로 HTML 템플릿 파일이다. 이 파일이 직접 표시되는 것은 아니고, index.js에 의해 일어난 내용에 대해 렌더링된 결과가 표시됩니다.</p>
</blockquote>
<h2 id="src">src</h2>
<p>소스코드라는 이름의 약자로 되어 있으며, 주요하게 있는 파일은 index.js와 App.js가 있습니다</p>
<blockquote>
<p><em>index.js</em>
메인 프로그램이라고 할 수 있습니다.
 여기에서 HTML 템플릿 및 JavaScript의 컴포넌트를 조합하여 렌더링하고 실제 표시를 담당입니다.</p>
</blockquote>
<blockquote>
<p><em>App.js</em>
이것은 컴포넌트를 정의하는 프로그램입니다. 실제로 화면에 표시되는 내용 등은 여기에서 정의된다.</p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>