<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>g.hyxn1_</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Thu, 14 May 2026 10:35:03 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>g.hyxn1_</title>
            <url>https://velog.velcdn.com/images/mgang0_0/profile/4a4afb1b-f35e-4862-9116-0e723367ce4c/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. g.hyxn1_. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/mgang0_0" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[React Native!]]></title>
            <link>https://velog.io/@mgang0_0/React-Native</link>
            <guid>https://velog.io/@mgang0_0/React-Native</guid>
            <pubDate>Thu, 14 May 2026 10:35:03 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/mgang0_0/post/3b63a8a1-6349-4680-9435-e1a61db15a12/image.png" alt=""></p>
<h2 id="시작하며">시작하며</h2>
<p>이번 글에서는 React Native가 무엇인지, 장단점과 React와의 차이점을 정리해봤습니다.</p>
<hr>
<h2 id="react-native란">React Native란?</h2>
<p>React Native는 크로스플랫폼 모바일 앱 개발 프레임워크입니다. JavaScript와 React 문법으로 코드를 작성하면 iOS와 Android 앱을 동시에 만들 수 있습니다.</p>
<p>각 플랫폼의 <strong>실제 네이티브 컴포넌트</strong>를 렌더링하기 때문에 네이티브 앱에 가까운 사용자 경험을 제공합니다.</p>
<hr>
<h2 id="장점">장점</h2>
<p><strong>코드 재사용</strong> — 하나의 코드베이스로 iOS와 Android를 동시에 커버할 수 있습니다. 비즈니스 로직과 UI 대부분을 공유할 수 있어 개발 비용이 줄어듭니다.</p>
<p><strong>익숙한 구조</strong> — React를 알고 있다면 러닝커브가 낮습니다. JSX, 컴포넌트 구조, 상태 관리 방식이 거의 동일해서 웹 개발자가 모바일로 빠르게 넘어올 수 있습니다.</p>
<p><strong>빠른 개발 사이클</strong> — Fast Refresh 덕분에 코드 수정이 즉시 반영됩니다. 매번 빌드를 새로 돌릴 필요가 없습니다.</p>
<hr>
<h2 id="단점">단점</h2>
<p><strong>플랫폼별 예외 처리</strong> — iOS와 Android가 다르게 동작하는 부분에서는 따로 직접 분기 처리를 해야 합니다.</p>
<p><strong>라이브러리 호환성</strong> — React Native 버전 업데이트가 잦고, 특히 Expo SDK 버전과 맞춰야 할 때 호환성 문제가 종종 발생합니다.</p>
<hr>
<h2 id="react웹와-다른-점">React(웹)와 다른 점</h2>
<p>React를 알고 있다면 친숙하게 느껴지지만, 몇 가지는 확실히 다릅니다.</p>
<p>렌더링 대상 자체가 달라서 웹의 <code>&lt;div&gt;</code>, <code>&lt;span&gt;</code> 대신 <code>&lt;View&gt;</code>, <code>&lt;Text&gt;</code> 같은 네이티브 전용 컴포넌트를 사용합니다. 라우팅은 <code>react-router</code> 대신 <code>React Navigation</code>, 클릭 이벤트는 <code>onClick</code> 대신 <code>onPress</code>를 씁니다. 스크롤도 브라우저가 자동 처리해주는 것과 달리 <code>&lt;ScrollView&gt;</code>나 <code>&lt;FlatList&gt;</code>를 직접 사용해야 합니다.</p>
<p>스타일링은 기본적으로 <code>StyleSheet</code> API를 사용하지만, Tailwind CSS를 설치하여 문법을 그대로 쓸 수 있습니다. 다만 <code>display: block</code> 같은 개념이 없고 모든 View의 기본값이 <code>flex-direction: column</code>이라는 점은 알고 있어야 처음에 덜 헷갈립니다.</p>
<hr>
<h2 id="expo를-쓰는-이유">Expo를 쓰는 이유</h2>
<p>React Native를 처음 세팅하면 Xcode, Android Studio, JDK, CocoaPods 등 환경 설정만으로 하루가 날아가는 경우도 있습니다. Expo는 이 문제를 해결해줍니다.</p>
<p>Expo는 React Native 위에 올라가는 개발 플랫폼으로, <code>npx create-expo-app</code> 하나로 바로 시작할 수 있습니다. 카메라, 위치, 알림 등 자주 쓰는 기능이 SDK로 이미 제공되고, Expo Go 앱으로 QR 코드 하나만으로 실제 기기에서 바로 테스트할 수 있습니다.</p>
<hr>
<h2 id="마무리">마무리</h2>
<p>React Native는 웹 개발자가 모바일에 발을 들이기에 가장 좋은 선택지라고 생각합니다. 하나의 코드베이스로 두 플랫폼을 커버하면서 네이티브에 가까운 경험을 줄 수 있다는 점이 좋은 것 같습니다.</p>
<p>감사합니다!!!!!!!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spotify api 사용기]]></title>
            <link>https://velog.io/@mgang0_0/Spotify-api-%EC%82%AC%EC%9A%A9%EA%B8%B0</link>
            <guid>https://velog.io/@mgang0_0/Spotify-api-%EC%82%AC%EC%9A%A9%EA%B8%B0</guid>
            <pubDate>Thu, 09 Apr 2026 07:57:02 GMT</pubDate>
            <description><![CDATA[<h1 id="시작하며">시작하며</h1>
<p>이번 글에선 Spotify api를 사용하면서 어떤일이 일어났는지 또 어떻게 해결했는지 그리고 기본적인 사용법에 대해서 간단하게 적어 보겠습니다.</p>
<h1 id="사용법">사용법</h1>
<h2 id="앱-생성하기">앱 생성하기</h2>
<p>먼저 Spotify api를 사용 하려면 앱을 생성해야합니다. 
앱은 아래 보이는 링크인 spotify for developer 에서 생성할 수 있습니다.
<a href="https://developer.spotify.com">https://developer.spotify.com</a> 
<img src="https://velog.velcdn.com/images/mgang0_0/post/e50c81f2-96e6-43cd-840a-c66268007c04/image.png" alt=""></p>
<p>이런식으로 dashboard에 들어가서 앱을 생성할 수 있습니다.
앱을 생성할 때 앱 이름, 앱 설명, 웹사이트(필수 아님), 리디렉션 URL과 같은 정보들이 들어가게 됩니다 
<img src="https://velog.velcdn.com/images/mgang0_0/post/1ec37e0a-36eb-4ffb-8c1a-966a41831fcf/image.png" alt="">
앱을 생성 하면 이런식으로 Dashboard에 내가 생성한 앱이 뜹니다.
그리고 앱을 클릭하면 자신의 앱 정보와 client id, client secret 이 나오게 됩니다. 
이것을 환경 변수에 넣거나 하드 코딩해서 api를 불러와서 쓸 수 있습니다.</p>
<h2 id="주요-기능">주요 기능</h2>
<h3 id="검색">검색</h3>
<p>트랙, 아티스트, 앨범, 플레이리스트 등을 키워드로 검색할 수 있습니다.</p>
<pre><code>GET https://api.spotify.com/v1/search?q={검색어}&amp;type=track,artist,album</code></pre><h3 id="트랙-정보">트랙 정보</h3>
<p>특정 트랙의 제목, 아티스트, 앨범, 30초 미리듣기 URL, 커버 이미지 등을 가져올 수 있습니다.</p>
<pre><code>GET https://api.spotify.com/v1/tracks/{track_id}</code></pre><h3 id="아티스트-정보">아티스트 정보</h3>
<p>아티스트의 이름, 인기도(popularity) 등을 가져올 수 있습니다.</p>
<pre><code>GET https://api.spotify.com/v1/artists/{artist_id}
GET https://api.spotify.com/v1/artists/{artist_id}/top-tracks</code></pre><h3 id="앨범-정보">앨범 정보</h3>
<p>앨범에 포함된 트랙 목록, 발매일, 커버 이미지 등을 가져올 수 있습니다.</p>
<pre><code>GET https://api.spotify.com/v1/albums/{album_id}</code></pre><h3 id="플레이리스트">플레이리스트</h3>
<p>공개된 플레이리스트의 트랙 목록을 가져올 수 있습니다.</p>
<pre><code>GET https://api.spotify.com/v1/playlists/{playlist_id}/tracks</code></pre><h1 id="실제-사용기">실제 사용기</h1>
<p>저는 디코봇에서 !노추 !가수 와 같은 키워드로 노래 추천과 가수를 검색하는 기능을 구현하였고 또한 디코봇을 만드는 여러 방법 중에서 discord.js라는 라이브러리를 이용하여 작업하였습니다.</p>
<h2 id="access-token-발급">Access Token 발급</h2>
<p>위에서 설명했던 Spotify Api 앱 생성에서 받았던 client_id와 client_scret을 사용해야 합니다.
저는 유저 로그인이 필요 없는 Client Credentials Flow 방식을 사용했습니다.</p>
<pre><code class="language-js">async function getAccessToken() {
  const credentials = Buffer.from(
    `${process.env.SPOTIFY_CLIENT_ID}:${process.env.SPOTIFY_CLIENT_SECRET}`
  ).toString(&quot;base64&quot;);

  const response = await fetch(&quot;https://accounts.spotify.com/api/token&quot;, {
    method: &quot;POST&quot;,
    headers: {
      Authorization: `Basic ${credentials}`,
      &quot;Content-Type&quot;: &quot;application/x-www-form-urlencoded&quot;,
    },
    body: &quot;grant_type=client_credentials&quot;,
  });

  const data = await response.json();
  return data.access_token;
}</code></pre>
<p>토큰은 발급 후 약 1시간(3600초) 뒤에 만료되기 때문에, 매 요청마다 새로 발급받는 건 비효율적입니다.
그래서 토큰을 캐싱해두고 만료됐을 때만 새로 발급받는 방식으로 구현했습니다.</p>
<pre><code class="language-js">let cachedToken = null;
let tokenExpiresAt = 0;

async function getToken() {
  if (cachedToken &amp;&amp; Date.now() &lt; tokenExpiresAt) {
    return cachedToken;
  }
  const token = await getAccessToken();
  cachedToken = token;
  tokenExpiresAt = Date.now() + 3500 * 1000; // 여유 있게 3500초
  return token;
}</code></pre>
<h2 id="장르-검색-기능">장르 검색 기능</h2>
<p>처음에는 장르 검색 기능을 Spotify API의 기본 기능을 활용하여 구현하려고 했습니다.
하지만 확인해보니 API에서 장르 기반 검색을 직접 지원하지 않는다는 것을 알게 되었습니다.</p>
<p>물론 artist_id를 활용하여 간접적으로 구현할 수는 있었지만 이 방식은 구조가 복잡하고 실제로는 장르가 아닌 곡 제목 기반 검색으로 동작하는 문제가 있었습니다.
예를 들어 !노추 힙합과 같이 검색하면 장르와는 무관하게 “hip-hop”이라는 제목의 노래가 검색되는 이슈가 발생했습니다.</p>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/5ba7eb4b-9471-4901-8257-0a9d4fb7c2a4/image.png" alt=""></p>
<p>이 문제를 해결하기 위해, 클로드가 제안 해준 것을 참고하여 장르별 아티스트를 직접 정의하는 방식을 선택했습니다.</p>
<pre><code class="language-js">const GENRE_ARTISTS = {
  케이팝: [&quot;BTS&quot;, &quot;아이유&quot;, &quot;NewJeans&quot;, &quot;BLACKPINK&quot;, ...],
  팝: [&quot;Taylor Swift&quot;, &quot;Ariana Grande&quot;, ...],
  힙합: [&quot;Travis Scott&quot;, &quot;빈지노&quot;, ...],
  // ...
};</code></pre>
<p>이처럼 장르별로 아티스트를 하드코딩한 뒤,
예를 들어 !노추 인디와 같이 입력하면 해당 배열에 포함된 아티스트(검정치마, 잔나비 등)의 음악을 검색하는 방식으로 구현했습니다.</p>
<p>이 방법을 통해 장르 검색 기능의 문제는 해결할 수 있었지만,
등록된 아티스트에만 의존해야 한다는 한계가 있어 추천 범위가 제한적이라는 단점이 있습니다.</p>
<p>추후에 더 고민해보고 더 나은 방향이 있으면 수정할 예정입니다.</p>
<h2 id="구독-문제">구독 문제</h2>
<p>처음 앱을 생성하여 코드를 짜고 개인 서버에서 요청을 보냈는데 어떻게 해도 계속 오류가 떴습니다. 
저는 Spotify API를 무료 버전에서도 사용할 수 있을 것이라고 생각했지만  유료 구독이 필요했습니다. 따라서 아직 사용하지 않았던 3개월 무료 체험을 통해 구독을 진행했습니다.</p>
<p>하지만 또 다른 문제가 발생했습니다. 콘솔에서 유료 구독 정보가 서버에 반영되기까지 몇 시간이 소요될 수 있다는 메시지가 표시되었고 약 2시간 정도 기다렸음에도 정상적으로 적용되지 않았습니다.</p>
<p>따라서 해결하기 위해 기존에 생성했던 앱을 삭제하고 새로 생성하려 했지만 앱을 다시 만들기 위해서는 24시간을 기다려야 한다는 제한이 있어 예상보다 많은 시간을 소모하게 되었습니다.</p>
<h2 id="limit-에러">limit 에러</h2>
<p>처음에 limit을 20으로 설정했는데 계속 400 에러가 났습니다.
알고보니 Client Credentials 방식의 앱은 limit을 10 이하로만 설정해야 했습니다.
공식 문서에는 명확하게 나와있지 않아서 찾는 데 시간이 걸렸습니다.</p>
<pre><code class="language-js">// Client Credentials에서는 에러 발생
limit: 20

// 10 이하로 설정해야 정상 동작
limit: 10</code></pre>
<h2 id="url-인코딩-문제">URL 인코딩 문제</h2>
<p>처음에는 Node.js의 https 모듈을 직접 사용해서 API를 호출했습니다.
그런데 검색어에 한글이나 특수문자가 들어가면 URL 인코딩이 제대로 되지 않아 요청이 실패하는 문제가 있었습니다.
해결 방법은 클로드가 제안 해주었습니다. 
https 모듈 대신 Node.js 18+에 내장된 fetch를 사용하고,
URLSearchParams로 쿼리를 구성하면 인코딩을 자동으로 처리해줍니다.</p>
<pre><code class="language-js">// 인코딩 문제 발생 가능
const url = `https://api.spotify.com/v1/search?q=${query}&amp;type=track&amp;limit=20`;

// URLSearchParams로 안전하게 처리
const params = new URLSearchParams({ q: query, type: &quot;track&quot;, limit: 10 });
const url = `https://api.spotify.com/v1/search?${params}`;</code></pre>
<h2 id="마치며">마치며</h2>
<p>Spotify API 자체는 문서도 잘 되어있고 사용하기 어렵지 않았지만, 처음 사용해보기도 하고 여러 시행착오가 많았던 것 같습니다. 
그래도 좋은 경험 이었던 것 같고 앞으로도 이런 새로운 시도를 많이 해보고 싶습니다!!
글 읽어주셔서 감사합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[개학 한 달 차 회고]]></title>
            <link>https://velog.io/@mgang0_0/%EA%B0%9C%ED%95%99-%ED%95%9C-%EB%8B%AC-%EC%B0%A8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@mgang0_0/%EA%B0%9C%ED%95%99-%ED%95%9C-%EB%8B%AC-%EC%B0%A8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Thu, 02 Apr 2026 09:18:20 GMT</pubDate>
            <description><![CDATA[<h2 id="시작하며">시작하며</h2>
<p>이번 글에선 개학한지 1달이된 나의 모습, 좋았던점, 아쉬웠던점 그리고 앞으로의 계획을 정리해 보려고 합니다.</p>
<h2 id="바뀐점">바뀐점</h2>
<p>방학 때는 학교에서 자습과 과제를 하는 것이 일상이어서 개학을 해도 크게 달라지지 않을 것이라고 생각했습니다.<br>하지만 실제로는 생각보다 많은 부분이 달라졌고 또 달라져야한다고 느꼈습니다.</p>
<p>그중 가장 크게 바뀌어야하는건 <strong>책임감</strong>입니다.</p>
<p>후배들이 들어오고 멘토멘티를 진행하면서 단순히 배우는 입장이 아니라 알려주는 선배가 된 것을 체감하게 되었습니다.<br>또한 FE 스터디 부장과 여러 프로젝트를 통해 참여자가 아닌 이끄는 역할을 맡으며 책임감을 느끼게 되었습니다.</p>
<p>저는 스스로 책임감이 뛰어난 사람이라고 생각하지 않습니다.<br>그 만큼 실수도 많이 했습니다.
그래서 이러한 역할들이 부담되기도 하지만, 오히려 이를 통해 책임감을 키워야겠다고 생각하게 되었습니다.
앞으로의 상황을 대비하는 좋은 연습이라고 생각하고 있습니다.</p>
<h2 id="좋았던점">좋았던점</h2>
<p>최근에 책을 읽고 있습니다. <strong>역행자</strong>라는 자기계발서를 읽고 있는데 정말 좋은 책인것 같습니다.
<img src="https://velog.velcdn.com/images/mgang0_0/post/c8ad9c0d-b114-4d6c-87d6-d403d447aeff/image.png" alt=""></p>
<p>이 책에서 말하는 내용에서 <strong>자의식 해체</strong> 라는 말이 있었습니다. 자의식 해체는 자신을 제3자의 입장에서 객관적으로 보는것 입니다. 
예를들어 내가 실수한 상황일때 자의식 보호(자신을 보호하려고 하는 생각 즉 자기합리화)가 발동합니다. &quot;내 잘못은 없어 다 환경탓이야!&quot; 라는 착각이 무의식적으로 스쳐 지나간다고 합니다. 물론 좀 극단적인 예시를 들긴 했지만 인간의 본성이 자아를 손상시키는 것에 예민하기 때문에 저정돈 아니더라도 자의식을 보호하려고 온갖 생각이 스쳐 지나갈겁니다. </p>
<p>책에선 자의식 해체를 하는 <strong>탐색, 인정, 전환</strong> 이 3가지 방법을 소개 합니다. 
탐색은 내가 뭘 잘못했는지, 내 잘못이 무엇에서 비롯되었는지 탐색하는 것입니다. 
인정은 말 그대로 자신의 잘못을 알았다면 깔끔하게 인정 하는 것입니다. 
마지막으로 전환은 그러한 생각들을 실천으로 옮기는 것입니다. 
이러한 방법들이 한번에 실행으로 옮겨지진 않지만 이러한 상황이 일어날때 기억하고 실행으로 옮긴다면 성공입니다.</p>
<p>또한 이 책의 저자는 22전략을 소개 합니다. 22전략이란 하루 2시간 책읽기와 글쓰기를 하는것입니다. 솔직히 2시간 책읽기는 몰라도 2시간 글쓰기는 좀 어렵긴 합니다. 그 만큼 이 저자는 책읽기와 글쓰는 능력을 강조하는 것 같습니다, 책은 과거의 위인과 소통할수있는 좋은 수단이라고 설명하면서요.</p>
<p>넵 뭔가 책에대한 소개를 한것 같지만 아무튼 책읽는 좋은 습관이 들여진 것 같습니다. 전공책 뿐만 아니라 이런 자기개발서나 소설책을 읽는 습관은 좋은 것 같습니다!!</p>
<h2 id="아쉬웠던점">아쉬웠던점</h2>
<p>이번 한 달 동안 가장 아쉬웠던 점은<br>프로젝트 스프린트 기간을 맞추지 못한 것입니다.</p>
<p>저는 모든 일에서 <strong>약속을 지키는 것이 중요하다</strong>고 생각하지만
이번에는 스스로의 나태함으로 인해 기한을 지키지 못했습니다.</p>
<p>또한 일정이 늦어질 것 같았음에도<br>팀원들에게 미리 공유하지 못했고,<br>그로 인해 프로젝트 전체 흐름에도 영향을 주었다고 느꼈습니다.</p>
<h2 id="개선할점">개선할점</h2>
<p>앞으로는 맡은 역할에 대해 더 큰 책임감을 가지고<br>끝까지 포기하지 않고 수행하는 것이 중요하다고 생각합니다.</p>
<p>특히 일정에 문제가 생길 것 같을 때는<br>혼자 해결하려 하기보다, 팀원들과 <strong>미리 소통하는 것</strong>을  
가장 우선으로 개선해야 할 부분이라고 느꼈습니다.</p>
<h2 id="앞으로의-계획">앞으로의 계획</h2>
<p>이번 한 달을 돌아보며 단순한 노력보다<br><strong>방향성과 꾸준함이 더 중요하다</strong>는 것을 느꼈습니다.<br>앞으로는 아래와 같은 방향으로 개선해 나가려고 합니다.</p>
<h3 id="1-포트폴리오-초안-작성-해보기">1. 포트폴리오 초안 작성 해보기</h3>
<p>포트폴리오 초안을 작성 해보면서 내가 부족한 부분이 뭔지 앞으로 남은 2학년동안 어떤걸 더 공부하고 해보면 좋을지 알아가보려고 합니다.</p>
<h3 id="2-오픈소스-기여">2. 오픈소스 기여</h3>
<p>전부터 오픈소스 기여는 해보고 싶다고 생각 했었습니다.
하지만 실력도 아직 부족하고 어디서부터 시작해야 할지도 막막합니다.
그래도 찾아보면서 작은것부터 조금씩 도전 해보고 싶습니다.</p>
<h3 id="3-프로젝트-진행">3. 프로젝트 진행</h3>
<ul>
<li>광탈페</li>
<li>광산</li>
<li>gsmc v4 </li>
</ul>
<p>등등 여러 프로젝트를 진행 하면서 개선점이나 기여한 부분 이슈를 해결한 경험을 잘 정리해서 포트폴리오에 잘 정리하려고 합니다.</p>
<h3 id="4-사이드-프로젝트">4. 사이드 프로젝트</h3>
<p>아직 구체적인 계획이나 아이디어가 떠오르지 않았지만 크롬 확장자로 배포를 해서 사용해보고 싶은 마음은 있습니다. 
아이디어가 떠오르면 바로 계획하고 실행 하려고 합니다!</p>
<h2 id="마무리">마무리</h2>
<p>이번 글에선 개학하고 한달된  제 모습을 회고하면서 기록 하였습니다.
글을쓰며 아직 부족한점이 많고 해야할 것도 많다고 느꼈습니다.
앞으로 이러한 부족한점 개선 하며 성장해 나가도록 하겠습니다!
감사합니다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[캡챠 인증 의미가 있나요???]]></title>
            <link>https://velog.io/@mgang0_0/%EC%BA%A1%EC%B1%A0-%EC%9D%B8%EC%A6%9D-%EC%9D%98%EB%AF%B8%EA%B0%80-%EC%9E%88%EB%82%98%EC%9A%94</link>
            <guid>https://velog.io/@mgang0_0/%EC%BA%A1%EC%B1%A0-%EC%9D%B8%EC%A6%9D-%EC%9D%98%EB%AF%B8%EA%B0%80-%EC%9E%88%EB%82%98%EC%9A%94</guid>
            <pubDate>Thu, 12 Mar 2026 09:04:47 GMT</pubDate>
            <description><![CDATA[<h2 id="시작하며">시작하며</h2>
<p>이번 글에선 사이트에 들어가서 로그인 하거나 어떠한 절차를 거쳐야할때 봇검사를 하는데 이때 캡챠인증이라고 뜹니다. 많은 사람들을 귀찮게 하는 캡챠인증이 제대로 작동 하는지, 봇이 제대로 걸러지는지 확인 해보려고 합니다.</p>
<h2 id="캡챠captcha인증이란">캡챠(CAPTCHA)인증이란?</h2>
<p>CAPTCHA는 <strong>&quot;Completely Automated Public Turing test to tell Computers and Humans Apart&quot;</strong>의 약자로
쉽게 말하면 컴퓨터와 사람을 구별하기 위한 자동화된 테스트입니다.</p>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/c1d3f245-2983-41ef-91ce-1efb262e4448/image.png" alt=""></p>
<h2 id="캡챠-너-쓸모-있는거냐">캡챠 너 쓸모 있는거냐?...</h2>
<p>결론부터 말하면 어느 정도는 효과가 있다고 합니다!...
자 그럼 이제부터 캡챠가 효과적인 이유에 대해 설명 하도록 하겠습니다.</p>
<h3 id="기본적인-자동화-봇-차단">기본적인 자동화 봇 차단</h3>
<p>대부분의 단순한 봇은 다음과 같은 방식으로 동작합니다.</p>
<ul>
<li>HTTP 요청 자동 반복</li>
<li>스크립트를 통한 회원가입 자동 생성</li>
<li>댓글 스팸 자동 작성</li>
</ul>
<p>이러한 단순 스크립트 기반 봇은 캡챠에서 대부분 차단된다고 합니다.
특히 회원가입이나 로그인 시도에서 큰 효과가 있습니다.</p>
<h3 id="공격-비용-증가">공격 비용 증가</h3>
<p>캡챠는 공격을 완전히 막는 것이 아니라 비용을 증가시키는 역할을 한다고 합니다.</p>
<p>봇이 캡챠를 우회하려면 다음과 같은 방법이 필요합니다.</p>
<ul>
<li>OCR(문자인식) 기반 이미지 해석</li>
<li>머신러닝 모델 사용</li>
<li>캡챠 해결 서비스 이용 (사람이 대신 풀어주는 서비스)</li>
</ul>
<p>이 과정은 시간과 비용이 발생하기 때문에 대규모 공격이 어려워집니다.</p>
<h3 id="행동-기반-분석과-결합">행동 기반 분석과 결합</h3>
<p>최근 캡챠는 단순한 문제 풀이가 아니라 사용자 행동 패턴 분석을 함께 사용합니다.</p>
<p>예를 들어 다음과 같은 요소를 분석합니다.</p>
<ul>
<li>마우스 움직임</li>
<li>클릭 패턴</li>
<li>타이핑 속도</li>
<li>브라우저 환경</li>
</ul>
<p>이러한 방식은 대표적으로 Google <code>reCAPTCHA</code>에서 사용됩니다.
특히 <code>reCAPTCHA v3</code>는 사용자에게 문제를 거의 보여주지 않고도 봇 점수를 계산한다고 합니다.</p>
<h2 id="캡챠의-한계">캡챠의 한계</h2>
<p>하지만 캡챠가 완벽한 해결책은 아닙니다.</p>
<h3 id="ai가-캡챠를-풀-수-있음">AI가 캡챠를 풀 수 있음</h3>
<p>최근 AI 기술이 발전하면서 이미지 인식 모델이 기존 캡챠를 상당히 잘 풀 수 있게 되었습니다.</p>
<p>특히 다음과 같은 경우가 있습니다.</p>
<ul>
<li>OCR(문자인식) 모델로 문자 캡챠 해석</li>
<li>딥러닝으로 이미지 분류</li>
<li>자동 클릭 스크립트</li>
</ul>
<p>즉 기술적으로 완벽한 방어는 어렵습니다.</p>
<h3 id="캡챠-해결-서비스-존재">캡챠 해결 서비스 존재</h3>
<p>이번 글을 쓰며 찾아보면서  인터넷에는 캡챠를 대신 풀어주는 서비스도 존재한다고 합니다...</p>
<p>동작 방식은 다음과 같습니다.
봇이 캡챠 이미지를 전송하고 실제 사람이 문제 해결한 뒤 결과를 다시 봇에게 전달
이 방식은 매우 저렴한 비용으로 이용 가능하기 때문에 대규모 스팸 공격에 사용되기도 합니다.</p>
<h3 id="사용자-경험ux-저하">사용자 경험(UX) 저하</h3>
<p>캡챠의 가장 큰 문제 중 하나는 사용자 경험을 떨어뜨릴 수 있다는 점입니다.</p>
<p>예를 들어</p>
<ul>
<li>이미지가 너무 어려움</li>
<li>여러 번 반복 인증</li>
<li>모바일에서 불편함</li>
</ul>
<p>특히 접근성 측면에서도 문제가 발생할 수 있습니다.
매번 캡챠 인증 하기 너무 귀찮은 것 같습니다...</p>
<h2 id="마무리">마무리</h2>
<p>이번 글에선 캡챠가 도움이 되는지 실제로 봇은 잘 거르는지에 대해 알아보았습니다. 
캡챠인증이 정말 귀찮긴 하지만 도움이 된다고 합니다.
글 읽어주셔서 감사합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Of "CORS"?? CORS에 대해 알아보자~]]></title>
            <link>https://velog.io/@mgang0_0/Of-CORS-CORS%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@mgang0_0/Of-CORS-CORS%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Wed, 11 Mar 2026 05:20:29 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/mgang0_0/post/601f005f-8235-4dcc-a5d0-65787e4dd3ed/image.png" alt=""></p>
<h2 id="시작하며">시작하며</h2>
<p>개발을 하다보면 CORS에러가 뜬걸 많이 보셨을 겁니다.
이번 글에선 그런 CORS가 무엇인지, 왜 발생하는지, 어떻게 해결하는지 알아보겠습니다.</p>
<h2 id="cors란">CORS란?</h2>
<p>CORS(Cross-Origin Resource Sharing)는
다른 출처의 리소스를 요청할 수 있도록 허용하는 브라우저 보안 정책입니다.
쉽게 말하면 내 웹사이트가 다른 서버에 있는 데이터를 요청해도 되는지 검사하는 규칙입니다.</p>
<h2 id="origin이란">Origin이란?</h2>
<p>CORS를 이해하려면 먼저 Origin을 알아야 합니다.
Origin은 도메인, 프로토콜, 포트의 집합입니다.
예시로
<code>https://kanghyeon.com:3000</code> 이런 주소가 있다면 
프로토콜은 https
도메인은 kanghyeon.com
포트번호는 3000입니다.
<img src="https://velog.velcdn.com/images/mgang0_0/post/a4341f96-6a8f-4b5e-af33-a19bcc2af2e8/image.png" alt=""></p>
<h2 id="cors는-왜-발생할까">CORS는 왜 발생할까?</h2>
<p>기본적으로 브라우저에는 Same-Origin Policy(SOP, 동일 출처 정책) 이 있습니다.
이 정책은 같은 Origin에서만 리소스 요청을 허용하는 보안 정책입니다.</p>
<p>예를 들어</p>
<p><code>http://localhost:3001</code> 이러한 서버에서
<code>https://kanghyeon.com</code> 이곳으로 요청을 보내면 Origin이 다르기 때문에 브라우저가 요청을 차단합니다.</p>
<p>이때 등장하는 개념이 바로 CORS입니다.</p>
<p>위에서 설명한 것처럼 SOP가 서로 다른 출처일 때 리소스 요청과 응답을 차단하는 정책이라면,
CORS는 서로 다른 출처라도 리소스 요청과 응답을 허용할 수 있도록 하는 정책입니다.</p>
<p>그래서 우리가 만나는 에러도 대부분 CORS가 가능하도록 설정하라는 내용으로 이루어져 있습니다.</p>
<p>뒤에서 설명할 해결 방법에 등장하는 헤더인 <code>Access-Control-Allow-Origin</code>도
허용되는 출처에 대한 접근 제어라는 의미라고 이해할 수 있습니다.</p>
<h2 id="cors-에러-대응">CORS 에러 대응</h2>
<h3 id="서버에서-응답헤더-세팅하기">서버에서 응답헤더 세팅하기</h3>
<p>서버에서 Access-Control-Allow-Origin 헤더를 설정해서 수락할 출처를 명시적으로 지정할 수 있습니다. 
이 헤더를 세팅하면 출처가 다르더라도 리소스 요청이 허용됩니다.</p>
<p>예를들어</p>
<pre><code>Access-Control-Allow-Origin: https://kanghyeon.com</code></pre><p>이런식으로 특정 웹사이트 <code>https://kanghyeon.com</code> 에서만 오는 요청을 허용하게 설정할 수도 있고,</p>
<pre><code>Access-Control-Allow-Origin: *</code></pre><p>이런식으로 모든 사이트의 요청을 허용하게 설정할 수도 있습니다.</p>
<p>하지만 *를 사용하여 설정하게 된다면 모든 접근을 허용하기 때문에 보안에 취약해질 수 있습니다. 
따라서 <code>Access-Control-Allow-Origin: https://kanghyeon.com</code> 이와 같이 직접 출처를 세팅하는 방식이 더 좋습니다.</p>
<h3 id="proxy-서버-사용하기">proxy 서버 사용하기</h3>
<p>웹이 리소스를 직접적으로 요청하는 대신, 프록시 서버를 사용하여 웹에서 리소스로 요청을 전달하는 방법이 있습니다. 
이 방법을 사용한다면 웹이 리소스랑 동일한 출처에서 요청을 보내는 것처럼 보여서 CORS 에러를 방지 할 수 있습니다.</p>
<p>예를들어
<code>https://kanghyeon.com</code>에서 동작하는 웹이 
<code>https://api.kanghyeon.com</code>에서 데이터를 요청한다면 두 도메인이 다르기 때문에 CORS 설정을 제대로 하지 않으면 요청이 실패할 수도 있습니다.
이 문제를 해결하려면 웹에서 직접 <code>https://api.kanghyeon.com</code>에 api를 요청하는 대신, 같은 <code>https://kanghyeon.com</code>에 위치한 프록시 서버를 통해 api 요청을 중간에서 하도록 구성 해주면 됩니다. 
때문에 브라우저 입장에선 <code>https://kanghyeon.com</code>에서 요청한 것처럼 보이기 때문에 CORS 검사 없이 응답 받는게 가능 합니다.&#39;</p>
<p>흐름</p>
<pre><code>브라우저 → https://kanghyeon.com (프록시 서버) → https://api.kanghyeon.com</code></pre><h2 id="마치며">마치며</h2>
<p>이번 글에서는 CORS라는 개념에 대해 알아보았습니다.<br>개발을 하며 많이 접했던 오류여서 찾아보게 되었는데, 에러를 어떻게 대응하는지에 대한 부분이 특히 도움이 많이 되었던 것 같습니다.</p>
<p>이 밖에도 다양한 해결 방법이 있지만, 이번 글에서는 가장 기본적인 개념과 대표적인 해결 방법 위주로 정리해보았습니다.<br>글 읽어 주셔서 감사합니다!!</p>
<p>출처: <a href="https://docs.tosspayments.com/resources/glossary/cors#%EC%84%9C%EB%B2%84%EC%97%90%EC%84%9C-access-control-allow-origin-%EC%9D%91%EB%8B%B5-%ED%97%A4%EB%8D%94-%EC%84%B8%ED%8C%85%ED%95%98%EA%B8%B0">https://docs.tosspayments.com/resources/glossary/cors#%EC%84%9C%EB%B2%84%EC%97%90%EC%84%9C-access-control-allow-origin-%EC%9D%91%EB%8B%B5-%ED%97%A4%EB%8D%94-%EC%84%B8%ED%8C%85%ED%95%98%EA%B8%B0</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[GSM 1학년 회고록]]></title>
            <link>https://velog.io/@mgang0_0/GSM-1%ED%95%99%EB%85%84-%ED%9A%8C%EA%B3%A0%EB%A1%9D</link>
            <guid>https://velog.io/@mgang0_0/GSM-1%ED%95%99%EB%85%84-%ED%9A%8C%EA%B3%A0%EB%A1%9D</guid>
            <pubDate>Mon, 05 Jan 2026 09:16:19 GMT</pubDate>
            <description><![CDATA[<h2 id="시작하며">시작하며</h2>
<p>올 한 해는 저에게 있어 굉장히 의미 있는 해였던 것 같습니다. 얻은 것도, 잃은 것도, 배운 것도 많았다고 생각합니다. 최근 주변 선배들이나 블로그 글을 보며 아직 많이 노력해야겠다는 생각이 들었습니다. 이 글을 적으며 앞으로 2026년을 어떻게 살아가야 할지 방향을 잡아보고자 합니다.</p>
<hr>
<h2 id="마이스터고등학교에-지원했던-배경과-동기">마이스터고등학교에 지원했던 배경과 동기</h2>
<p>저는 중학교 2학년 초반까지만 하더라도 마이스터고라는 것이 있는지도 몰랐습니다. 그러다 5기 선배님과 부모님들끼리 아는 사이여서 GSM에 대해 알게 되었습니다. 중학교 2학년 때 학교 설명회에도 갔었고, 자마고와 공동으로 진행한 설명회도 있어서 함께 다녀왔습니다.</p>
<p>두 학교의 설명을 모두 들어보니, 자마고보다는 평소 컴퓨터를 좋아하고 관심도 많았던 저에게 소마고가 더 잘 맞을 것 같다고 생각했습니다. 학교에 들어오기 전에는 기숙사 생활에 대한 로망도 조금 있었습니다. 친구들과 함께 지내며 학교생활을 하면 재미있을 것 같다는 생각도 들었습니다.</p>
<p>그리고 가장 결정적인 이유는 취업을 빨리 해서 돈을 벌고 싶었기 때문입니다. 물론 4년제 대학을 나와 취업하는 방법도 있지만, 고등학교 때 내신을 챙기며 교과 공부를 하고 야자와 학원까지 다니는 생활은 저와 정말 맞지 않다고 느꼈습니다. 그래서 이러한 이유들로 마이스터고를 선택하게 되었습니다.</p>
<hr>
<h2 id="어떠한-개발자가-되고-싶은지">어떠한 개발자가 되고 싶은지</h2>
<p>저는 배우는 것을 즐기는 개발자가 되고 싶습니다. 제가 선택한 전공인 프론트엔드는 새로운 기술과 트렌드가 빠르게 변화하는 분야라고 생각합니다. 그만큼 배워야 할 것도 많고, 꾸준한 학습이 중요하다고 느꼈습니다.</p>
<p>상태 관리 라이브러리만 봐도 zustand, redux, recoil, jotai 등 다양한 것들이 있고, 제가 아직 사용해 보지 못한 기술들도 훨씬 더 많습니다. 저는 이런 기술들을 단순히 알고 넘어가는 것이 아니라, 직접 사용해 보고 비교해 보며 상황에 맞게 실제 프로젝트에서 활용할 수 있는 개발자가 되고 싶습니다.</p>
<p>앞으로도 새로운 기술들을 계속해서 배우며, 배운 내용을 실제 프로젝트에 적용해 성장해 나가는 개발자가 되고 싶습니다.</p>
<hr>
<h2 id="개발-공부를-어떤-방식으로-하는지-그-방식이-나에게-맞는지">개발 공부를 어떤 방식으로 하는지, 그 방식이 나에게 맞는지</h2>
<p>저는 최근 프로젝트를 진행하면서 공부를 병행해 왔습니다. 친구들과 함께 프로젝트를 하며<br>“여기에는 이 기술이 더 좋을까?”,<br>“상태 관리 라이브러리를 추가해 볼까?”<br>와 같은 고민을 하면서 기술을 하나씩 배워 나갔던 것 같습니다. 이러한 방식은 배운 내용을 바로 프로젝트에 적용해 볼 수 있어서 습득력이 확실히 빠르다고 느꼈습니다.</p>
<p>또한 AI를 활용하며 공부했습니다. AI가 작성해 준 코드를 그대로 사용하는 것이 아니라, 코드를 분석하며 어떤 방식으로 사용되는지, 왜 이렇게 작성해야 하는지를 찾아보고 직접 AI에게 질문하면서 공부했습니다. 물론 시간에 쫓겨 코드를 충분히 확인하지 못한 채 프로젝트에 사용했던 경험도 있었습니다. 하지만 시간이 부족하더라도 이런 방식으로 천천히 코드를 분석하면서 공부하면 블로그를 찾아보거나 강의를 보는 것보다 빠르게 배울 수 있었고, 나중에 비슷한 상황이 왔을 때도 이러한 경험들이 도움이 될 것이라고 생각합니다.</p>
<p>다가오는 겨울방학에는 이론 공부를 조금 더 탄탄히 하며 내년 2학년을 준비하면 좋을 것 같습니다. 또한 이러한 지식을 바탕으로 내년에 10기가 들어오게 된다면, 직접 가르쳐 주면서 복습하고, 제가 잘 기억나지 않거나 처음 접하는 개념이 나온다면 함께 공부하면 도움이 많이 될 것 같습니다!</p>
<hr>
<h2 id="지금까지-개발한-프로젝트-경험-정리">지금까지 개발한 프로젝트 경험 정리</h2>
<p>올해 마무리한 프로젝트는 3개 정도가 있는 것 같습니다.</p>
<h3 id="hcbc">HCBC</h3>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/e11a126b-e862-4d0d-85da-6b0029ae68a0/image.png" alt=""></p>
<p>HCBC는 incube 동아리에서 팀을 꾸려서 완료한 프로젝트입니다.<br>랜덤 채팅 프로그램으로, 웹소켓 기반의 실시간 채팅이 구현되어 있는 프로젝트입니다.</p>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/894f8410-643a-4a5e-8d80-afe56b26c543/image.png" alt="">
<img src="https://velog.velcdn.com/images/mgang0_0/post/83747f69-6666-4f28-aba3-e8457bbde114/image.png" alt=""></p>
<p>이 프로젝트에서 좋았던 점은 첫 프로젝트 치고 나쁘지 않은 경험이었다는 점입니다. 이때만 해도 AI를 많이 썼던 것 같습니다. 물론 AI를 써서 개발하는 것이 나쁘지는 않지만, AI에 거의 의존해서 개발했던 점이 조금 아쉬웠습니다.<br>그리고 첫 프로젝트인 만큼 소통도 잘되지 않았고, 그로 인해 충돌도 많이 났습니다. API 통신도 처음 해봐서 오류도 굉장히 많이 나왔고, 여러 가지 면에서 아쉬운 점이 많았습니다.</p>
<p>HCBC 프론트 레포: <a href="https://github.com/HelloCarBotCube/HCBC-Front">https://github.com/HelloCarBotCube/HCBC-Front</a></p>
<hr>
<h3 id="gsmc">GSMC</h3>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/73ce5988-831c-4a53-8cb1-bee369b7beb7/image.png" alt=""></p>
<p>GSMC는 저희 학교인 GSM의 인증제를 관리하는 서비스입니다.<br>저는 이 프로젝트에서 얻어간 점이 많다고 생각합니다. 처음 했던 프로젝트인 HCBC와는 다르게 PR 템플릿, 라벨, Gemini 코드 리뷰 등 선배님들과 함께한 프로젝트인 만큼 프로젝트 세팅부터 남달랐습니다.</p>
<p>저는 이 프로젝트에서 클라이언트 부분은 자주 묻는 질문을 담당했고,</p>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/9e934eb5-ce73-446b-9059-87bbe7494369/image.png" alt=""></p>
<p>어드민에서는 학생 필터, 학생 부분 점수 조회, 심사 요청 조회, 심사 요청 자세히 보기 페이지를 개발하였습니다.</p>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/e684ca73-115e-40d8-aab3-f0586b2ae5f7/image.png" alt="">
<img src="https://velog.velcdn.com/images/mgang0_0/post/02f71589-ebf6-4149-a31d-3f84c08b5497/image.png" alt="">
<img src="https://velog.velcdn.com/images/mgang0_0/post/2e34654e-39f6-46ea-b554-e4e3836a06bb/image.png" alt="">
<img src="https://velog.velcdn.com/images/mgang0_0/post/b2d86123-f76d-4cbe-a6d7-3f524e4a58e4/image.png" alt=""></p>
<p>이 프로젝트에서 좋았던 점은 선배님들과 같이 프로젝트를 하며 협업에 대해 배우게 되었다는 점입니다.<br>아쉬웠던 점은 제 전공 실력이 부족해서 많은 부분에 기여하지 못했던 것입니다. 만약 기회가 되어 다음 버전을 릴리즈하게 된다면 더 열심히 하고 싶습니다!</p>
<hr>
<h3 id="gami">GAMI</h3>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/0d6718a3-3307-4f4d-8174-deb3341328c9/image.png" alt=""></p>
<p>GAMI는 멘토링을 도와주는 서비스입니다.<br>웹소켓을 사용하여 실시간 채팅을 구현했고, 익명 게시판으로 자유롭게 자신의 생각을 올릴 수 있습니다.</p>
<p>저는 이 프로젝트에서 첫 프로젝트였던 HCBC 때보다 발전했다는 것이 느껴졌던 것 같습니다.<br>이 프로젝트에서는 회원가입, 익명 게시판, 마이 페이지, 어드민 페이지를 개발하였습니다.</p>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/23eb7ff7-02ef-48e0-a1c3-ffc1c9a45eaf/image.png" alt="">
<img src="https://velog.velcdn.com/images/mgang0_0/post/c1b0c2ed-60a5-4442-b833-68490d3ec257/image.png" alt="">
<img src="https://velog.velcdn.com/images/mgang0_0/post/9b5ef090-0d8f-460e-85ef-0826fd615f96/image.png" alt="">
<img src="https://velog.velcdn.com/images/mgang0_0/post/ad395eb4-db06-4039-bddd-cb04aa8ed1b6/image.png" alt=""></p>
<p>이 프로젝트에서 좋았던 점은 소통이 나름 잘됐다는 것입니다. 하루에 할 일을 todo로 적어서 올리고, PR을 올리면 바로바로 확인해 주는 등 소통이 잘 이루어져 프로젝트가 원활하게 진행되었습니다.<br>아쉬웠던 점은 명세서를 확실하게 잡아놓지 않아서 API가 명세서나 디자인과 다르게 개발된 경우가 있었다는 것입니다. 아이디어 페스티벌 특성상 프로젝트 기간이 길다 보니 이런 실수가 있었지만, 이를 통해 프로젝트를 처음 시작할 때 기능을 세세하게 잘 정리해 두는 것이 중요하다고 느꼈습니다.</p>
<p>프로젝트 레포: <a href="https://github.com/CKOLBo/GAMI-Client">https://github.com/CKOLBo/GAMI-Client</a></p>
<hr>
<h2 id="올해-목표">올해 목표</h2>
<p>저는 단순히 프로젝트만 하는 것이 아니라, 저만의 개발 성향과 스타일을 정립하고 이를 바탕으로 포트폴리오를 준비하고 싶습니다. 2025년에 마무리했던 프로젝트들은 대부분 개발 → 릴리즈 → 수정의 과정에서 끝났습니다. 하지만 올해부터는 트러블슈팅 과정이나 성능을 개선했던 부분 등을 따로 정리하는 습관을 기르고 싶습니다.</p>
<p>또한 작년을 돌아보며 후회한 점이 많았던 만큼, 올해는 정말 후회 없이 최선을 다하고 싶습니다. 사람이 하루아침에 바뀌지는 않겠지만, 당장 해볼 수 있는 것부터 실천해 나가고 싶습니다. 예를 들어 TIL을 하루에 하나씩 작성하는 것처럼, 작은 습관들을 하나씩 바꿔 간다면 올해는 후회 없이 보낼 수 있을 것이라 생각합니다.</p>
<p>마지막으로 오픈소스에 기여하거나 직접 라이브러리를 만들어 보고 싶다는 목표도 가지고 있습니다. 지금 당장은 쉽지 않겠지만, 앞서 세운 목표인 ‘후회 없이 열심히 살아가기’를 꾸준히 실천해 나간다면 분명히 해낼 수 있을 것이라고 생각합니다.</p>
<hr>
<h2 id="마치며">마치며</h2>
<p>이번 글은 저에게 굉장히 많은 도움이 된 것 같습니다. 앞으로 올 한 해를 어떻게 살아가야 할지, 작년에 잘한 점은 무엇인지, 고칠 점은 무엇인지 다시 한 번 되돌아볼 수 있었습니다. 앞으로도 꾸준히 성장하는 프론트엔드 개발자가 되기 위해 열심히 하겠습니다!!! 감사합니다!!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AI가 초보 개발자에게 미치는 영향]]></title>
            <link>https://velog.io/@mgang0_0/AI%EA%B0%80-%EC%B4%88%EB%B3%B4-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%97%90%EA%B2%8C-%EB%AF%B8%EC%B9%98%EB%8A%94-%EC%98%81%ED%96%A5</link>
            <guid>https://velog.io/@mgang0_0/AI%EA%B0%80-%EC%B4%88%EB%B3%B4-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%97%90%EA%B2%8C-%EB%AF%B8%EC%B9%98%EB%8A%94-%EC%98%81%ED%96%A5</guid>
            <pubDate>Wed, 03 Dec 2025 14:11:55 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/mgang0_0/post/719cf05f-d549-4d45-bee2-7c5e1cf9effc/image.png" alt=""></p>
<h2 id="시작하며">시작하며</h2>
<p>이번 글에서는 ai가 초보 개발자에게 어떠한 영향을 미치는지 그리고 초보 개발자인 제가 어떤영향을 받았는지 알아보도록 하겠습니다.</p>
<h2 id="ai-너-뭐-되냐">AI 너 뭐 되냐?</h2>
<p>사실 전 중학교 때까지만 해도 AI를 잘 쓰지 않았습니다.
그냥 검색하고 직접 찾아보는 게 더 익숙했고, AI를 굳이 써야겠다는 필요성도 못 느꼈습니다.</p>
<p>하지만 고등학교에 올라오고 시간이 지나면서 ChatGPT, Gemini 같은 여러 AI 기술들이 엄청나게 발전하기 시작했습니다.
요즘 구글검색을 해도 gemini가 브라우저 맨위에서 검색에 대한 답을 해줍니다.
<img src="https://velog.velcdn.com/images/mgang0_0/post/3b34284c-e044-4f3c-9421-111e0239d239/image.png" alt=""></p>
<p>물론 아직 부족한 점도 많고 가끔 틀린 정보를 알려줄 때도 있습니다.
하지만 예전과 비교하면 그런 오류도 확실히 줄었고 앞으로 더 발전해서 거의 완벽에 가까워진다면 진짜 무서울 것 같다는 생각도 듭니다.</p>
<p>결론: ai 뭐 된다 편리하다</p>
<h2 id="ai가-초보-개발자에게-미치는-긍정적-영향">ai가 초보 개발자에게 미치는 긍정적 영향</h2>
<p>다시 본론으로 돌아 와서 ai가 초보자에게 미치는 긍정적 영향에 대해 알아보겠습니다. </p>
<h3 id="학습-속도-향상">학습 속도 향상</h3>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/2949e282-b98d-4e0e-8efe-f3585399ce63/image.png" alt=""></p>
<p>초보 개발자가 어려움을 느끼는 지점은 대부분 검색해도 답이 나오지 않을때 입니다.
AI는 이런 시간 낭비를 줄여 줄 수 있습니다.
모르는 개념을 설명해달라고 하면 예제, 비유, 맥락까지 정리해서 알려줍니다.</p>
<h3 id="디버깅-난이도-낮춤">디버깅 난이도 낮춤</h3>
<p>초보가 가장 스트레스를 많이 받는 건 디버깅이라고 생각 합니다.
오류 메시지를 읽어도 무슨 말인지 모르고 어디서부터 손대야 할지도 모를 수 있습니다.</p>
<ul>
<li>오류 원인</li>
<li>의심되는 부분</li>
<li>해결 가능한 여러 접근 방식을 단계별로 설명해준다.</li>
</ul>
<p>이를 통해 디버깅의 감을 익힐 수 있습니다.
ai가 고쳐주는 게 아니라 어떻게 고쳐지는지 보는 과정이 공부가 될 수 있습니다.</p>
<h2 id="ai가-초보-개발자에게-미치는-부정적-영향">ai가 초보 개발자에게 미치는 부정적 영향</h2>
<h3 id="문제-해결-능력-저하">문제 해결 능력 저하</h3>
<p>원래 왜 이 코드가 오류가 날까?를 스스로 고민하며 성장해야 합니다.
그런데 ai는 오류 메시지를 던지면 바로 해결책을 내놓습니다.
그러다 보면 자연스럽게 원인을 파악하는 과정이 생략되고, 문제를 직접 추적하는 능력이 약해집니다.</p>
<p>예를들어
오류 로그를 보기도 전에 &quot;ai야 이거 고쳐줘!!!&quot; &quot;이거 이런식으로 수정 해줘&quot; 라고 한다면 스스로 생각 할 능력을 잃습니다.</p>
<h3 id="ai를-전적으로-믿음">ai를 전적으로 믿음</h3>
<p>초보때는 개발 실력이 없으니까 ai가 틀린 코드를 작성해도 무조건 믿고 바로 적용하는 안좋은 습관이있습니다.(저 포함..)
하지만 실제로는</p>
<ul>
<li>프로젝트 구조와 안 맞는 코드일 수 있고</li>
<li>비효율적인 방식일 수도 있고</li>
<li>심지어 틀린 내용일 때도 있음</li>
</ul>
<p>초보는 그걸 구분하기 어려워서 잘못된 지식을 그대로 학습할 위험이 있습니다.</p>
<h3 id="자기-코드에-대한-책임감이-약해짐">자기 코드에 대한 책임감이 약해짐</h3>
<p>ai가 코드를 많이 짜주면 자신이 만든 코라는 느낌이 줄어듭니다.
그러면 자연스럽게</p>
<ul>
<li>디버깅이 어려워지고</li>
<li>유지보수도 어려워지며</li>
<li>코드에 대한 주인의식도 낮아진다.</li>
</ul>
<p>특히 팀 프로젝트에서는 큰 문제입니다.
AI가 만들어준 코드라 내가 깊게 이해한 게 아니면 나중에 팀원이 질문했을 때 설명도 못 합니다.</p>
<h3 id="의존도가-높아져서-스스로-생각하는-시간이-부족">의존도가 높아져서 스스로 생각하는 시간이 부족</h3>
<p>ai가 편하다고 해서</p>
<ul>
<li>구조 설계</li>
<li>코드 작성</li>
<li>디버깅</li>
<li>글 정리</li>
</ul>
<p>모든 걸 떠넘기게 되면, 초보 개발자에게 가장 중요한
사고하는 힘이 약해집니다.</p>
<p>이건 시간이 지나면 큰 차이를 만듭니다.
ai 없이 백지 상태에서 코드를 짜기 어렵습니다.
이 능력에서 초보 개발자들의 격차가 생깁니다.</p>
<h2 id="ai를-잘-쓰는-방법">ai를 잘 쓰는 방법?</h2>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/0d8b5f59-7dd0-49a9-a4bc-4b76e95136de/image.png" alt="">
저도 물론 잘 못하지만 이번에 제가 곰곰히 생각해보며 전공 동아리 프로젝트와는 다르게 ai 의존도를 낮춰보려고 노력하면서 적용했던점과 이번에 블로그를 쓰며 조사했던것을 담은 내용이라는 점 참고 해주시면 감사하겠습니다 더 좋은 방법이 있으시다면 피드백 해주세요!</p>
<h3 id="고민하고-질문하기">고민하고 질문하기</h3>
<p>바로 묻는 건 가장 안좋은 습관입니다.
3분만이라도 스스로 원인을 추측해보면 사고력이 엄청 자랄 수 있을거라고 생각됩니다.</p>
<p>예를 들어
“이 오류 메시지가 말하는 게 뭐지?”
“어디까지 내가 확인해봤지?”</p>
<p>이런 질문을 스스로 해보고 ai에게 물어보면 사고력을 기를 수 있을 것 같습니다.</p>
<h3 id="ai의-답-의심해보기">ai의 답 의심해보기</h3>
<p>ai에 의존도가 높을수록 중요한 습관입니다.</p>
<ul>
<li>왜 그렇게 해야 하는지 이유까지 물어보기</li>
<li>코드 실행해보고 실제 동작 확인</li>
<li>문서나 공식 레퍼런스와 비교</li>
</ul>
<p>이 과정을 통해 ai가 준 정보는 정답이 아니라 참고 자료라고 생각을 해야 합니다.</p>
<h3 id="ai를-선생님이라고-생각하기">ai를 선생님이라고 생각하기</h3>
<p>ai는 사실 코드보다 설명에 더 강하다고 합니다.
따라서 ai에게 무작정 코드를 짜라고 시키는것 보다 설명을 모르는 개념에 대해 설명을 해달라고 해서 이해하는게 훨씬 더 좋다고 생각합니다.</p>
<p>예를들어</p>
<ul>
<li>“왜 이 Hook은 이렇게 동작해?”</li>
<li>“여기서 왜 useState를 안쓰고 useRef를 써?”</li>
<li>“이 패턴의 단점은 뭐야?”</li>
</ul>
<p>이런 식으로 개념, 원리, 이유를 물어보는게 저는 굉장히 도움이 많이 되었습니다.</p>
<h3 id="ai가-짜준-코드-정리-해보기">ai가 짜준 코드 정리 해보기</h3>
<p>단순히 “AI에게 답을 받았다”로 끝내지 말고</p>
<ul>
<li>모르는 내용 찾아보기</li>
<li>노션 정리</li>
<li>개념 요약</li>
<li>스스로 다시 설명해보기</li>
</ul>
<p>이 과정을 거쳐서
ai를 코드 짜는 기계가아닌 학습도구로 활용해야 합니다.</p>
<h2 id="마치며">마치며</h2>
<p>이번 글에서는 ai가 초보 개발자에게 미치는 영향에 대해서 알아보았습니다.
개발에 대한 지식이 없는 상태에서 무작정 ai 한테 코드를 짜달라고 한다면 자신의 실력도 제대로 늘지 않을 뿐더러 코드이해 조차 힘듭니다.
따라서 저도 이번 글을 작성하며 많은 깨닳음을 얻은 것 같습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[메모리 구조]]></title>
            <link>https://velog.io/@mgang0_0/%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B5%AC%EC%A1%B0</link>
            <guid>https://velog.io/@mgang0_0/%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B5%AC%EC%A1%B0</guid>
            <pubDate>Tue, 25 Nov 2025 08:19:31 GMT</pubDate>
            <description><![CDATA[<h2 id="시작하며">시작하며</h2>
<p>이 글에서는 메모리 구조에 대해 알아보도록 하겠습니다.
프로그램이 실행될 때 메모리가 어떻게 나누어지는지, 그리고 각 영역이 어떤 역할을 하는지 알아보도록 하겠습니다.</p>
<h2 id="메모리-구조가-뭘까용">메모리 구조가 뭘까용??</h2>
<p>프로그램은 실행 될때 운영체제로 부터 메모리 공간을 할당 받습니다. 그리고 이 메모리는 크게 4가지로 나뉩니다</p>
<ul>
<li><strong>코드</strong></li>
<li><strong>데이터</strong></li>
<li><strong>힙</strong></li>
<li><strong>스택</strong> </li>
</ul>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/55e88e05-08a5-47cd-af8c-898c71a56ab9/image.png" alt=""></p>
<h2 id="코드-영역">코드 영역</h2>
<p>프로그램을 실행시키기 위해 구성되는 것들이 저장되는 영역입니다. 한마디로 명령문들이 저장되는 것인데, 제어문, 함수, 상수들이 이 영역에 저장됩니다. 
CPU가 이 영역의 코드를 하나씩 읽어 실행합니다.
쉽게 말해 코드가 실행되기 위해 저장되어있는 영역입니다.</p>
<h3 id="특징">특징</h3>
<ul>
<li>CPU가 실행할 명령어가 들어 있음</li>
<li>읽기 전용이기 때문에 실행 중 수정 불가</li>
<li>프로그램 전체에서 공유됨</li>
<li>예를 들어 C에서 작성한 <code>int main() {}</code> 함수의 컴파일 결과도 여기 저장됩니다.</li>
</ul>
<hr>
<h2 id="데이터-영역">데이터 영역</h2>
<p>데이터 영역은 우리가 작성한 코드에서 전역변수, 정적변수 등이 저장되는 공간입니다.
또한 데이터영역은 크게 두가지로 나뉩니다.</p>
<h3 id="초기화된-데이터-영역">초기화된 데이터 영역</h3>
<pre><code class="language-c">int a = 10; // 초기값 있음</code></pre>
<h3 id="초기화되지-않은-데이터-영역">초기화되지 않은 데이터 영역</h3>
<pre><code class="language-c">static int count; // 초기값 없음</code></pre>
<h3 id="특징-1">특징</h3>
<ul>
<li>프로그램 시작 시 함께 생성</li>
<li>프로그램 종료까지 메모리에 남아 있음</li>
<li>전역 상태를 저장하는 공간</li>
</ul>
<hr>
<h2 id="힙-영역">힙 영역</h2>
<p>사용자에 의해 관리되는 영역입니다. 흔히 동적으로 할당 할 변수들이 여기에 저장된다고 보시면 됩니다.
필요할 때 메모리를 요청하고, 사용이 끝나면 다시 해제해야 합니다.</p>
<h3 id="예시">예시</h3>
<h4 id="c언어">C언어</h4>
<pre><code class="language-c">int* p = malloc(sizeof(int) * 10);</code></pre>
<h4 id="js">JS</h4>
<p>자바스크립트 객체/배열도 대부분 힙에 저장
예)</p>
<ul>
<li>JS의 객체/배열/함수 → 모두 힙에 저장</li>
<li>number, string등 원시타입들은 스택에 저장됨<pre><code class="language-js">const obj = { name: &quot;강현&quot; }; // 객체는 힙에 저장
</code></pre>
</li>
</ul>
<pre><code>
#### JAVA
```java
new String(&quot;Hello&quot;); // 힙에 저장</code></pre><h3 id="특징-2">특징</h3>
<ul>
<li>필요할때 동적으로 생성</li>
<li>개발자가 직접 관리(할당/해제)해야 함</li>
<li>크기가 자유롭고 유연함</li>
<li>하지만 잘못 사용하면 메모리 누수 발생</li>
<li>객체나 동적 배열처럼 크기가 유동적인 데이터가 저장됨</li>
</ul>
<hr>
<h2 id="스택-영역">스택 영역</h2>
<p>스택 영역은 함수를 호출 할 때 지역변수와 매개변수가 저장되는 공간입니다. stack pop 과 같이 함수가 종료되면 해당 함수에 할당된 변수들을 메모리에서 해제시킵니다.</p>
<h3 id="예">예</h3>
<pre><code class="language-c">void test() {
    int x = 3; // 스택에 저장
}</code></pre>
<h3 id="특징-3">특징</h3>
<ul>
<li>함수가 호출될 때 push, 종료될 때 pop</li>
<li>매우 빠른 접근 속도</li>
<li>자동으로 메모리 관리됨</li>
<li>크기는 제한적(overflow 가능)</li>
</ul>
<p>그래서 아래와 같은 코드는 스택 오버플로우를 일으킵니다:</p>
<pre><code class="language-c">int arr[10000000]; // 너무 큰 배열 → 스택 오버플로우</code></pre>
<h2 id="마치며">마치며</h2>
<p>이번 글에서는 프로그램이 실행될 때 메모리가 어떻게 나뉘는지 간단하게 정리해보았습니다.
처음 봤을때는 생각 보다 쉬워 보였지만 자료를 찾다 보니 생각 보다 깊은 내용도 많고 많이 어려워서 내용 보완 할 점이 많다고 생각 합니다.
글 읽어 주셔서 감사합니다</p>
<p>출처:
<a href="https://st-lab.tistory.com/198">https://st-lab.tistory.com/198</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[useRef란 무엇일까...]]></title>
            <link>https://velog.io/@mgang0_0/useRef%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C</link>
            <guid>https://velog.io/@mgang0_0/useRef%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C</guid>
            <pubDate>Wed, 19 Nov 2025 04:39:21 GMT</pubDate>
            <description><![CDATA[<h2 id="시작하며">시작하며</h2>
<p>오늘은 전부터 헷갈리고 잘 사용해보지 못했던 useRef에 대해 알아보도록 하겠습니다.</p>
<h2 id="useref-그래서-넌-뭐냐">useRef 그래서 넌 뭐냐</h2>
<p>useState는 값이 변경되면 즉시 컴포넌트를 다시 렌더링하기 위한 용도이고,
useRef는 렌더링과 무관하게 유지해야 하는 값을 저장하는 데 사용됩니다.</p>
<ul>
<li>useRef는 반환된 객체의 current 프로퍼티로 값에 접근하고 수정할 수 있습니다.</li>
<li>useRef는 값이 바뀌어도 컴포넌트가 다시 렌더링되지 않습니다.</li>
</ul>
<p>여기까지만 들었을때 저는 왜 굳이 쓸까? 어디에 쓸까? useState쓰면 되는거 아닌가 라는 고민이 되었습니다. </p>
<h2 id="사용법-부터-알아보자">사용법 부터 알아보자~</h2>
<h3 id="기본-사용법">기본 사용법</h3>
<ul>
<li><p>먼저 import로 useRef를 불러옵니다:
  <code>import { useRef } from &#39;react&#39;;</code></p>
</li>
<li><p>초기값을 넣어 변수에 할당합니다:
  <code>const inputRef = useRef(null);</code></p>
</li>
<li><p>필요할 경우 JSX에서 ref 속성으로 연결:
  <code>&lt;input ref={inputRef} type=&quot;text&quot; /&gt;</code></p>
</li>
<li><p>JavaScript에서 <code>.current</code> 속성으로 참조하거나 값을 변경할 수 있습니다:
<code>inputRef.current.focus();</code>와 같이 사용하면 함수에서 해당 input에 포커스를 줄 수 있습니다.</p>
<h3 id="렌더링-없이-값-저장하기">렌더링 없이 값 저장하기</h3>
<pre><code class="language-ts">import { useRef } from &#39;react&#39;;
</code></pre>
</li>
</ul>
<p>export default function Counter() {
  const countRef = useRef<number>(0);</p>
<p>  const increase = () =&gt; {
    countRef.current += 1;
    console.log(countRef.current); // UI는 안 바뀌지만 값은 증가
  };</p>
<p>  return <button onClick={increase}>카운트 증가</button>;
}</p>
<pre><code>![](https://velog.velcdn.com/images/mgang0_0/post/5b46ad8c-8eaf-40c9-828f-33059cb8c01c/image.png)

- `useRef(0)`
    `useRef`를 호출하면 `{ current: 초기값 }` 객체를 반환한다. 여기서는 `countRef.current`가 0으로 시작합니다
    이때 countRef 자체는 컴포넌트가 재렌더링되어도 동일한 객체를 가리킵니다

- `countRef.current += 1`
    `.current` 값만 변경되므로 `React`가 리렌더링을 트리거하지 않는다.
    콘솔에서 값은 바뀌지만 화면에는 반영되지 않습니다

### 돔 직접 접근
```ts
import { useRef } from &#39;react&#39;;

export default function InputFocus() {
  const inputRef = useRef&lt;HTMLInputElement&gt;(null);

  const handleFocus = () =&gt; {
    inputRef.current?.focus();
  };

  return (
    &lt;&gt;
      &lt;input ref={inputRef} /&gt;
      &lt;button onClick={handleFocus}&gt;포커스 이동&lt;/button&gt;
    &lt;/&gt;
  );
}
</code></pre><p><img src="https://velog.velcdn.com/images/mgang0_0/post/19dbdb92-2b76-448c-a61c-dc9f42db483d/image.gif" alt=""></p>
<ul>
<li><p><code>const inputRef = useRef(null);</code>
useRef에 초기값을 null로 전달하면 처음 렌더링 시 <code>inputRef.current</code>는 null 상태입니다. 이후 React가 <code>&lt;input ref={inputRef} /&gt;</code>를 렌더링하면서 해당 DOM 요소를 <code>inputRef.current</code>에 자동으로 넣어줍니다. 즉, 아직 참조할 대상이 없다 → 렌더링 후 DOM을 가지게 됩니다</p>
</li>
<li><p><code>&lt;input ref={inputRef} /&gt;</code>
ref 속성에 useRef로 만든 ref 객체를 전달하면, React가 렌더링 후 해당 DOM 노드를 <code>inputRef.current</code>에 할당하게 됩니다.
개발자는 DOM을 직접 찾을 필요 없이 <code>ref.current</code>를 통해 바로 접근할 수 있습니다.</p>
</li>
<li><p><code>inputRef.current?.focus()</code>
ref를 통해 얻은 실제 DOM 노드에 접근해, 브라우저의 기본 DOM API인 <code>.focus()</code>를 호출하게 됩니다.
이를 통해 버튼 클릭 시 input 요소로 포커스가 곧바로 이동하게 됩니다.</p>
</li>
</ul>
<h2 id="어디에-쓸까">어디에 쓸까?</h2>
<h3 id="메시지-도착-시-스크롤을-맨-아래로-내려야-할때">메시지 도착 시 스크롤을 맨 아래로 내려야 할때</h3>
<p>채팅 앱을 사용해본 사람이라면 누구나 익숙한 기능이 있습니다.
새로운 메시지가 생기면 자동으로 스크롤이 아래로 내려가는 것
이걸 구현하려면 마지막 메시지 엘리먼트를 직접 선택해서 스크롤을 이동시켜야 합니다.
그래서 이렇게 ref를 만들었습니다.</p>
<pre><code class="language-js">const messagesEndRef = useRef(null);</code></pre>
<p>그리고 메시지가 업데이트될 때 실행해줍니다.</p>
<pre><code class="language-js">messagesEndRef.current?.scrollIntoView({ behavior: &#39;smooth&#39; });</code></pre>
<ul>
<li>state로 DOM을 직접 선택할 수 없다</li>
<li>state는 값의 변경 → 렌더링이 목적</li>
<li>그러나 스크롤처럼 DOM 조작은 렌더링과 무관한 영역</li>
</ul>
<p>따라서 이러한 경우에는 useRef를 사용해야합니다.</p>
<h3 id="스크롤을-움직임을-감지-할때">스크롤을 움직임을 감지 할때</h3>
<p>채팅에 자동 스크롤이 있다고 해서 항상 스크롤을 내려가면 안 됩니다.
사용자가 과거 메시지를 읽으려고 위로 올렸는데
아래 메시지가 올 때마다 자동으로 내려가 버리면 안됩니다.</p>
<p>그래서 실시간으로 사용자가 스크롤을 올렸는지를 감지해야 했는데,
또 DOM 요소에 직접 이벤트를 달아야 했습니다.</p>
<hr>
먼저 채팅 메시지 전체를 감싸는 DOM 요소를 ref로 가져옵니다.

<pre><code class="language-js">const messagesContainerRef = useRef(null);</code></pre>
<p>이제 이 DOM 요소에 직접 scroll 이벤트를 붙입니다.</p>
<pre><code class="language-js">useEffect(() =&gt; {
  const handleScroll = () =&gt; {
    if (!messagesContainerRef.current) return;

    const { scrollTop, scrollHeight, clientHeight } = messagesContainerRef.current;

    // 사용자가 맨 아래에 있는지 여부
    const isAtBottom = scrollHeight - scrollTop - clientHeight &lt; 50;

    setIsUserScrolling(!isAtBottom);
  };

  const container = messagesContainerRef.current;
  if (container) {
    container.addEventListener(&#39;scroll&#39;, handleScroll);
    return () =&gt; {
      container.removeEventListener(&#39;scroll&#39;, handleScroll);
    };
  }
}, []);</code></pre>
<table>
<thead>
<tr>
<th>속성</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td><code>scrollTop</code></td>
<td>현재 얼마나 내려왔는지 (위로 갈수록 0)</td>
</tr>
<tr>
<td><code>scrollHeight</code></td>
<td>전체 스크롤 가능한 높이</td>
</tr>
<tr>
<td><code>clientHeight</code></td>
<td>현재 보이는 영역의 높이</td>
</tr>
</tbody></table>
<h4 id="사용자가-맨-아래에-있는지-확인하는-공식">사용자가 맨 아래에 있는지 확인하는 공식</h4>
<pre><code class="language-js">scrollHeight - scrollTop - clientHeight &lt; 50</code></pre>
<p>scrollHeight - clientHeight → 스크롤 가능한 가장 아래 위치</p>
<p>거기서 현재 scrollTop을 빼서
얼마나 아래에 가까운지를 측정</p>
<p>50px 정도 여유를 둔 이유는
미세한 스크롤 변화에도 갑자기 자동 스크롤이 꺼지거나 켜지는 걸 방지하기 위함입니다.</p>
<h4 id="최종-결정">최종 결정</h4>
<pre><code class="language-js">useEffect(() =&gt; {
  if (messages.length &gt; previousMessagesLengthRef.current &amp;&amp; !isUserScrolling) {
    messagesEndRef.current?.scrollIntoView({ behavior: &#39;smooth&#39; });
  }

  previousMessagesLengthRef.current = messages.length;
}, [messages, isUserScrolling]);</code></pre>
<h4 id="새-메시지가-추가됐는지-체크">새 메시지가 추가됐는지 체크</h4>
<pre><code class="language-js">messages.length &gt; previousMessagesLengthRef.current</code></pre>
<ul>
<li>이전 렌더 때 저장해둔 메시지 개수(previousMessagesLengthRef.current)보다
지금 메시지 개수가 많으면 → 새 메시지가 도착한 상황이라고 판단.</li>
</ul>
<h4 id="사용자가-스크롤을-위로-올려서-읽고-있는-중인지-체크">사용자가 스크롤을 위로 올려서 읽고 있는 중인지 체크</h4>
<pre><code class="language-js">!isUserScrolling</code></pre>
<ul>
<li>사용자가 과거 메시지 읽으려고 스크롤을 위쪽에 두고 있는 중이면 자동 스크롤하면 안 됨.</li>
<li>따라서 사용자가 아래쪽(최신 위치)에 있을 때만 자동 스크롤 가능.</li>
</ul>
<h4 id="조건-충족-시-자동-스크롤-실행">조건 충족 시 자동 스크롤 실행</h4>
<pre><code class="language-js">messagesEndRef.current?.scrollIntoView({ behavior: &#39;smooth&#39; });</code></pre>
<ul>
<li>새 메시지가 추가되었고
사용자가 현재 스크롤 맨 아래에 있다면
→ 부드럽게(smooth) 맨 아래로 자동 이동.</li>
</ul>
<h4 id="현재-메시지-개수를-ref에-저장">현재 메시지 개수를 ref에 저장</h4>
<pre><code class="language-js">previousMessagesLengthRef.current = messages.length;</code></pre>
<ul>
<li>다음 렌더 때 “이전 메시지 개수”로 사용하기 위해 업데이트해 둠.</li>
<li>ref는 값이 바뀌어도 리렌더링이 일어나지 않음 → 이 용도에 최적.</li>
</ul>
<h2 id="마치며">마치며</h2>
<p>오늘은 리액트 훅중 하나인 useRef에 대해 알아보았습니다. useRef에대해 개념정도는 알고 있었지만 왜 쓰는지 또 어디에 쓰는지 잘 알지 못하였지만 이 블로그를 쓰면서, 프로젝트에도 적용을 해보면서 잘 알게 된 것 같습니다.
글 읽어 주셔서 감사합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Zustand 너 뭐 됨?]]></title>
            <link>https://velog.io/@mgang0_0/Zustand-%EB%84%88-%EB%AD%90-%EB%90%A8</link>
            <guid>https://velog.io/@mgang0_0/Zustand-%EB%84%88-%EB%AD%90-%EB%90%A8</guid>
            <pubDate>Mon, 17 Nov 2025 06:45:57 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/mgang0_0/post/a8a3a156-c99a-45c8-9289-77cc25e9eec2/image.png" alt=""></p>
<h1 id="시작하며">시작하며</h1>
<p>오늘은 리액트의 상태관리 라이브러리 중 하나인 Zustand에 대해 알아보도록 하겠습니다!</p>
<h2 id="상태관리-너-뭔디">상태관리...? 너 뭔디</h2>
<p>Zustand에 대해 알아보기 전에 상태관리에 대해 알아 보도록 하겠습니다.
상태(state)는 리액트 컴포넌트속 데이터를 의미 합니다.
이러한 상태는 시간에 따라, 사용자와의 상호작용에 따라 업데이트되며, 이렇게 변화하는 상태를 올바르게 반응하게 해주는 관리를 상태 관리라고 합니다.</p>
<h3 id="왜-필요함">왜 필요함?</h3>
<ul>
<li>복잡한 시스템 관리
프로젝트 규모가 커질수록 각 상태를 개별적으로 추적하고 관리하는 것은 매우 비효율적입니다. 상태 관리는 여러 컴포넌트에서 공유되는 데이터를 중앙에서 일관성 있게 관리하게 해줍니다.</li>
<li>성능 최적화
리액트는 상태가 변경되면 해당 컴포넌트와 그 하위 컴포넌트를 리렌더링합니다. 적절한 상태 관리는 상태 변경에 따라 필요한 컴포넌트만 선택적으로 리렌더링하도록 제어하여 성능 저하를 막아줍니다.</li>
<li>Props Driling 문제 해결
부모 컴포넌트에서 자식컴포넌트로 props를 전달 할때 코드가 복잡해지면서 Props Driling 현상이 발생하는데 이때 상태관리 라이브러리를 사용 하면 직접 접근을 하여 이러한 현상을 막을 수 있다.</li>
</ul>
<h2 id="zustand">Zustand</h2>
<p>리액트에서 사용 할 수 있는 가볍고 빠른 상태관리 라이브러리 입니다.
<img src="https://velog.velcdn.com/images/mgang0_0/post/46f0cd14-9f99-4a02-8d12-77489f24b1e8/image.png" alt="">
현재 까진 리덕스보다 다운로드 수가 더 적지만, 최근 1년으로 본다면 Zustand가 급격히 성장하고 있습니다.
왜 그럴까요? 너 뭐 되냐 Zustand?!!</p>
<h2 id="그래서-왜씀">그래서 왜씀?</h2>
<p>결론부터 말하면 뭐 됩니다...ㄷㄷ</p>
<ul>
<li>설치용량 작음<br>가볍습니다 다른 라이브러리에 비해 설치 용량이 적음 1.1kb입니다 </li>
<li>불필요한 리렌더링이 적어서 빠름</li>
<li>사용법이 쉬움</li>
<li>리액트 밖에서도 사용가능
Redux는 리액트에 묶여있는 느낌이지만 Zustand는 순수 js 라이브러리여서 리액트 밖에서도 사용 가능 합니다.</li>
<li>코드가 깔끔해짐
전역 상태 로직이 하나의 store 안에 정리되기 때문에
프로젝트 규모가 커져도 유지보수가 쉬워집니다</li>
</ul>
<p>이때 store는 애플리케이션의 여러 상태(State)를 중앙에서 관리하는 패턴을 말합니다
<img src="https://velog.velcdn.com/images/mgang0_0/post/e191842a-f504-4327-b8e4-a5417a94a295/image.png" alt=""></p>
<h2 id="기본-사용법">기본 사용법</h2>
<h3 id="설치">설치</h3>
<pre><code>npm install zustand</code></pre><h3 id="store-만들기">store 만들기</h3>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/4c48f53d-92cf-4cf3-8b01-9be3eba0faec/image.png" alt=""></p>
<p>Zustand는 create() 함수를 사용해 전역 상태 store를 만듭니다.</p>
<pre><code class="language-js">import { create } from &quot;zustand&quot;;

const useCounterStore = create((set) =&gt; ({
  count: 0,
  increase: () =&gt; set((state) =&gt; ({ count: state.count + 1 })),
  decrease: () =&gt; set((state) =&gt; ({ count: state.count - 1 })),
}));</code></pre>
<ul>
<li><code>create()</code> : Zustand store를 만드는 함수</li>
<li><code>set</code> : 상태를 업데이트할 때 사용하는 함수</li>
<li><code>count</code> : 전역 상태로 관리할 값</li>
<li><code>increase</code>, <code>decrease</code> : 상태를 변경하는 액션 함수</li>
</ul>
<p>즉, 전역에서 공유 가능한 useState라고 보면 이해가 쉽습니다.</p>
<h3 id="컴포넌트에서-사용하기">컴포넌트에서 사용하기</h3>
<pre><code class="language-js">function Counter() {
  const { count, increase, decrease } = useCounterStore();

  return (
    &lt;div&gt;
      &lt;h1&gt;{count}&lt;/h1&gt;
      &lt;button onClick={increase}&gt;+&lt;/button&gt;
      &lt;button onClick={decrease}&gt;-&lt;/button&gt;
    &lt;/div&gt;
  );
}</code></pre>
<ul>
<li><p><code>useCounterStore()</code>를 호출하면 store에 있는 상태와 함수를 가져올 수 있습니다.</p>
</li>
<li><p><code>count</code> 값이 바뀌면 이 컴포넌트만 리렌더링됩니다.
불필요한 렌더링을 막아 성능이 좋습니다.</p>
<h3 id="selector">selector</h3>
<pre><code class="language-js">function Counter() {
const count = useCounterStore((state) =&gt; state.count);
const increase = useCounterStore((state) =&gt; state.increase);

return (
  &lt;div&gt;
    &lt;h1&gt;{count}&lt;/h1&gt;
    &lt;button onClick={increase}&gt;+&lt;/button&gt;
  &lt;/div&gt;
);
}</code></pre>
</li>
<li><p>Zustand는 selector를 사용해 “필요한 상태만” 구독 가능</p>
</li>
<li><p>따라서 count만 바뀌면 count를 구독하는 컴포넌트만 리렌더링</p>
</li>
<li><p>불필요한 렌더링 최소화</p>
</li>
</ul>
<h1 id="마치며">마치며</h1>
<p>오늘은 상태관리 라이브러리중 하나인 Zustand에 알아보았습니다.
Zustand는 작은 프로젝트뿐 아니라, 중간 규모 이상의 프로젝트에서도 큰 힘을 발휘합니다.
글 읽어 주셔서 감사합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[그래프 ]]></title>
            <link>https://velog.io/@mgang0_0/%EA%B7%B8%EB%9E%98%ED%94%84</link>
            <guid>https://velog.io/@mgang0_0/%EA%B7%B8%EB%9E%98%ED%94%84</guid>
            <pubDate>Tue, 11 Nov 2025 02:41:35 GMT</pubDate>
            <description><![CDATA[<h2 id="그래프란">그래프란?</h2>
<p>그래프는 정점(Vertex) 과 간선(Edge) 의 집합으로 표현됩니다.</p>
<ul>
<li><strong>정점</strong>
여러가지 특성을 가질 수 있는 객체(식별이 가능한 특성)
노드라고도 불림</li>
<li><strong>간선</strong>
정점들 사이의 관계
링크라고도 불림</li>
</ul>
<p>예를 들어,</p>
<pre><code>V(G1) = {0, 1, 2, 3}
E(G1) = {(0,1), (0,2), (0,3), (1,2)}</code></pre><p>이렇게 표현할 수 있습니다.
여기서 V(G)는 정점들의 집합, E(G)는 간선들의 집합을 의미합니다.</p>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/710d9435-36d9-473c-b045-33ec6244fdee/image.png" alt=""></p>
<h2 id="무방향-그래프-vs-방향-그래프">무방향 그래프 vs 방향 그래프</h2>
<p>그래프는 간선의 방향성 유무에 따라 두 가지로 나뉩니다.</p>
<h3 id="무방향-그래프">무방향 그래프</h3>
<ul>
<li>간선에 방향이 없는 그래프</li>
<li>A—B가 있다면 B—A도 자동으로 연결된 상태입니다.</li>
<li>친구 관계, 도로 지도 등 양방향 관계를 표현할 때 사용<h4 id="특징">특징</h4>
</li>
<li>A에서 B로 갈 수 있으면 B에서 A로도 가능</li>
<li>간선 수는 중복 없이 한 번만 기록 (예: A-B 한 줄)</li>
</ul>
<h3 id="방향-그래프-directed-graph">방향 그래프 (Directed Graph)</h3>
<ul>
<li>간선에 방향이 있는 그래프</li>
<li>A → B는 가능하지만, B → A는 아닐 수도 있습니다.</li>
<li>인스타그램 팔로우, 웹페이지 링크 등 일방향 관계를 표현할 때 사용<h4 id="특징-1">특징</h4>
</li>
<li>간선이 방향성을 가지므로, 연결 구조가 복잡할 수 있음</li>
<li>사이클(순환)이 생기면 특정 정점으로 되돌아오는 경우도 존재
<img src="https://velog.velcdn.com/images/mgang0_0/post/730238e7-d32d-4abd-ab2a-4e77c353adc3/image.png" alt=""></li>
</ul>
<h2 id="네트워크weighted-graph">네트워크(Weighted Graph)</h2>
<p>각 간선에 가중치(weight) 가 부여된 그래프를 네트워크 또는 가중치 그래프라고 합니다.
예를 들어 도로의 거리, 통신 비용, 연결 속도 등을 표현할 때 사용됩니다.</p>
<h2 id="정점의-차수-degree">정점의 차수 (Degree)</h2>
<p>그래프에서 인접 정점은 간선으로 직접 연결된 정점을 말합니다.
무방향 그래프에서 정점의 <strong>차수</strong> 는 인접한 정점의 수입니다.
모든 정점의 차수를 합하면 간선 수의 두 배가 됩니다.
방향 그래프에서는 들어오는 간선의 수를 <strong>진입 차수</strong>,
나가는 간선의 수를 <strong>진출 차수</strong> 라고 합니다.</p>
<h2 id="경로와-사이클">경로와 사이클</h2>
<ul>
<li>경로(Path): 한 정점에서 다른 정점으로 이동하는 순서의 나열</li>
<li>단순 경로(Simple Path): 같은 간선을 반복하지 않는 경로</li>
<li>사이클(Cycle): 시작과 끝이 같은 단순 경로</li>
</ul>
<h2 id="연결-그래프와-완전-그래프">연결 그래프와 완전 그래프</h2>
<ul>
<li>연결 그래프: 모든 정점 쌍 사이에 경로가 존재하는 그래프</li>
<li>비연결 그래프: 일부 정점이 다른 정점과 연결되어 있지 않은 그래프</li>
<li>완전 그래프: 모든 정점이 서로 직접 연결된 그래프</li>
</ul>
<p>정점이 n개일 때 간선의 개수 = n × (n - 1) / 2</p>
<h2 id="그래프의-표현-방식">그래프의 표현 방식</h2>
<h3 id="인접-행렬">인접 행렬</h3>
<h3 id="인접-행렬-adjacency-matrix">인접 행렬 (Adjacency Matrix)</h3>
<p>인접 행렬(adjacency matrix)은 <strong>2차원 배열을 사용하여 그래프를 표현하는 방법</strong>입니다.  </p>
<p>그래프의 정점 수가 <code>n</code>이라면, <code>n × n</code> 크기의 2차원 배열 <code>M</code>을 생성하고,<br>정점 <code>i</code>와 <code>j</code>를 연결하는 간선이 존재할 경우 <code>M[i][j]</code> 값을 <strong>1</strong>로 설정합니다.  </p>
<h4 id="특징-2">특징</h4>
<ul>
<li><strong>자기 자신과의 간선(자가 간선)</strong>은 허용하지 않으므로,<br>인접 행렬의 <strong>대각선 성분은 모두 0</strong>으로 표시됩니다.  </li>
<li><strong>무방향 그래프</strong>의 경우,<br>정점 <code>i</code>와 <code>j</code>가 연결되면 <code>M[i][j]</code>와 <code>M[j][i]</code>가 <strong>대칭적으로 1</strong>이 됩니다.  </li>
<li><strong>방향 그래프</strong>의 경우,<br>정점 <code>i</code>에서 <code>j</code>로의 간선이 있을 때만 <code>M[i][j] = 1</code>로 표시되며,<br>일반적으로 <strong>대칭이 아닙니다.</strong></li>
</ul>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/af81540c-29ec-4f3e-baec-1467a16bd2c9/image.png" alt=""></p>
<h3 id="인접-리스트">인접 리스트</h3>
<p>인접 리스트(adjacency list)는 <strong>그래프를 효율적으로 표현하는 방법</strong>으로,<br>각 정점에 인접한 정점들을 <strong>연결 리스트 형태로 저장하는 방식</strong>입니다.  </p>
<h4 id="특징-3">특징</h4>
<ul>
<li>각 정점은 <strong>헤더 노드</strong>를 가지며, 이 헤더 노드들은 <strong>배열로 구성</strong>되어 있습니다.  </li>
<li>정점 번호를 배열의 인덱스로 사용하면, 해당 정점의 연결 리스트에 <strong>쉽게 접근할 수 있습니다.</strong>  </li>
<li><strong>무방향 그래프</strong>의 경우, 간선 (i, j)는  <ul>
<li>정점 i의 연결 리스트에 <strong>j</strong>,  </li>
<li>정점 j의 연결 리스트에 <strong>i</strong><br>를 각각 추가하여 <strong>양방향으로 표현됩니다.</strong>  </li>
</ul>
</li>
<li>인접 정점이 연결 리스트에 추가되는 <strong>순서에 따라 리스트 내 정점의 순서가 달라질 수 있습니다.</strong></li>
</ul>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/21bc7495-4392-45dd-9bcb-5fd466682e97/image.png" alt=""></p>
<h2 id="그래프-탐색-알고리즘">그래프 탐색 알고리즘</h2>
<p>그래프 탐색은 모든 정점을 한 번씩 방문하는 과정입니다.
대표적인 두 가지 방법은 DFS(깊이 우선 탐색) 과 BFS(너비 우선 탐색) 입니다.</p>
<h3 id="dfs-depth-first-search">DFS (Depth-First Search)</h3>
<p>그래프의 시작 정점에서 출발하여 시작 정점 <code>v</code>를 방문했다고 표시합니다.<br>그 다음, <code>v</code>에 인접한 정점들 중 아직 방문하지 않은 정점 <code>u</code>를 선택합니다.  </p>
<ul>
<li>만약 방문하지 않은 인접 정점이 없다면 탐색을 종료합니다.  </li>
<li>방문하지 않은 정점 <code>u</code>가 있다면, 그 정점을 새로운 시작 정점으로 하여 DFS를 재귀적으로 수행합니다.  </li>
<li>탐색이 끝나면 다시 이전 정점으로 돌아와,<br>인접한 정점들 중 방문하지 않은 정점이 있는지 확인합니다.<br>없으면 종료하고, 있다면 그 정점을 시작 정점으로 하여 DFS를 다시 수행합니다.  </li>
</ul>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/ebd4dff4-e6ea-4f26-a245-62c2c32cd09d/image.png" alt=""></p>
<h3 id="bfs-breadth-first-search">BFS (Breadth-First Search)</h3>
<p>너비 우선 탐색(BFS)은 시작 정점으로부터 가까운 정점을 먼저 방문하고,<br>멀리 떨어져 있는 정점을 나중에 방문하는 그래프 순회 방법입니다.  </p>
<h4 id="탐색-과정">탐색 과정</h4>
<ol>
<li>시작 정점을 큐(Queue)에 넣고, 방문 표시를 합니다.  </li>
<li>큐에서 하나의 정점을 꺼내어, 그 정점에 인접한 정점들 중 방문하지 않은 정점을 모두 큐에 넣습니다.  </li>
<li>큐가 비어 있을 때까지 이 과정을 반복합니다.  </li>
</ol>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/9466d6eb-9056-41f6-aed9-cf65ff8cd0c8/image.png" alt=""></p>
<h2 id="마무리">마무리</h2>
<p>그래프는 단순한 자료구조 같지만, 지도 탐색, SNS 친구 추천, 네트워크 연결, 인공지능 경로 탐색 등
수많은 실제 서비스의 기반이 됩니다.</p>
<p>출처:
신민기씨 블로그
<a href="https://velog.io/@minki091212/%EA%B7%B8%EB%9E%98%ED%94%84Graph">https://velog.io/@minki091212/%EA%B7%B8%EB%9E%98%ED%94%84Graph</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[크리스마스 "트리"?]]></title>
            <link>https://velog.io/@mgang0_0/%ED%81%AC%EB%A6%AC%EC%8A%A4%EB%A7%88%EC%8A%A4-%ED%8A%B8%EB%A6%AC</link>
            <guid>https://velog.io/@mgang0_0/%ED%81%AC%EB%A6%AC%EC%8A%A4%EB%A7%88%EC%8A%A4-%ED%8A%B8%EB%A6%AC</guid>
            <pubDate>Mon, 10 Nov 2025 14:45:55 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/mgang0_0/post/ebc75c74-9bab-43f3-9aae-6741520256dc/image.png" alt=""></p>
<h1 id="시작하며">시작하며</h1>
<p>오늘은 자료구조 중 하나인 트리(Tree) 에 대해 알아보겠습니다.</p>
<h2 id="트리-너-뭥미">트리 너 뭥미</h2>
<p>트리는 노드(Node)들이 마치 나무 가지처럼 계층적으로 연결되어 있는 <strong>비선형 자료구조</strong>입니다.</p>
<h3 id="비선형-자료구조">비선형 자료구조?</h3>
<p>비선형 자료구조란 데이터를 순서대로 나열하지 않고, 서로의 관계를 통해 구조적으로 표현하는 방식을 말합니다.
예를 들어 트리와 그래프가 있습니다.
<img src="https://velog.velcdn.com/images/mgang0_0/post/301c6a62-9290-43d7-916f-a91273b737d1/image.png" alt="">
이런식으로 생겼습니다.</p>
<h3 id="선형-자료구조">선형 자료구조?</h3>
<p>그렇다면 선형 자료구조란 무엇일까요? 
선형 자료구조란 데이터가 한 줄로 차례차례 정렬되어있는 구조 입니다
각 데이터 요소는 바로 이전 요소와 바로 다음 요소와만 직접적으로 관계를 가집니다.
예를 들어 배열, 연결리스트, 스택, 큐와 같은 것 들이 있습니다.
<img src="https://velog.velcdn.com/images/mgang0_0/post/7f8ac019-cb38-4d96-a873-3b3fcafef20d/image.png" alt="">
이런 식으로 생겼습니다.</p>
<h3 id="트리의-기본-용어">트리의 기본 용어</h3>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/a731aeda-133b-445f-8b20-f382cda36a4e/image.png" alt=""></p>
<ul>
<li><strong>루트(root)</strong>
트리의 맨 꼭대기, 최상위노드
예) 저 사진의 트리의 루트는 A</li>
<li><strong>노드(node)</strong>
트리의 기본 단위(데이터)</li>
<li><strong>단말 노드</strong>
자식이 없는 노드(차수가 0인 노드)</li>
<li><strong>비단말 노드</strong>
적어도 하나의 자식을 갖는 노드</li>
<li><strong>간선</strong>
노들들을 잇는 선</li>
<li><strong>서브트리</strong>
하나의 노드와 그 노드들의 자손들로 이루어져있는 것</li>
<li><strong>차수</strong>
노드가 가지고 있는 자식 노드이 개수
예) D의 차수는 2</li>
<li><strong>레벨</strong>
각층의 번호
예) B의 레벨은 1 </li>
<li><strong>높이</strong>
어떤 노드부터 가장 깊은 리프 노드까지 경로 길이
예) 저 사진에 나와있는 트리의 높이는 3<h2 id="이진-트리">이진 트리</h2>
이진 트리는 단말노드를 제외한 각 노드의 차수가 2개인 것
특징으로는 구현이 편하고 서브트리간 순서가 존재 합니다.</li>
<li><blockquote>
<p>왼쪽 자식노드 오른쪽 자식노드
그래서 같은 루트에 같은 자식노드 하나를 가지고 있어도 자식노드의 위치가 각각 왼쪽과 오른쪽으로 다르다면 그 두 트리는 서로 다른 트리가 됩니다.
<img src="https://velog.velcdn.com/images/mgang0_0/post/b0be4411-95df-428c-9f16-7116f06be170/image.png" alt=""></p>
</blockquote>
</li>
</ul>
<p>최대 2개이기 때문에 자식이 1개 있을 수도 있고, 없을 수도 있습니다.</p>
<h4 id="이진-트리의-장점">이진 트리의 장점</h4>
<p>차수가 2개라 구현이 편하다
서브트리간 순서가 존재</p>
<h4 id="포화-이진-트리">포화 이진 트리</h4>
<p>노드가 더이상 들어가지 않는 이진트리의 포화상태 입니다.
<img src="https://velog.velcdn.com/images/mgang0_0/post/6818e4e9-93be-4cd9-896a-06e6980a8cb7/image.png" alt=""></p>
<h4 id="완전-이진-트리">완전 이진 트리</h4>
<p>왼쪽부터 값이 차례대로 노드에 값이 차있는 상태입니다.
<img src="https://velog.velcdn.com/images/mgang0_0/post/98331db4-64dc-48ec-9012-aa1b498c673b/image.png" alt=""></p>
<h3 id="간선-개수-구하기">간선 개수 구하기</h3>
<p>노그의 개수가 n이면 간선의 개수 n-1
루트는 부모로 가는 간선이 없음 따라서 간선의 개수는 n-1</p>
<h3 id="노드-개수-구하기">노드 개수 구하기</h3>
<p>높이가 h인 이진트리 경우 최소 h의 노드를 가짐
최대 2^h-1개의 노드를 가짐
<img src="https://velog.velcdn.com/images/mgang0_0/post/229e2000-6456-4fec-b997-2e7897a580fa/image.png" alt=""></p>
<pre><code class="language-py">class Node:
    def __init__(self, data):
        self.data = data
        self.left = None
        self.right = None

def count_nodes(root):
    if root is None:
        return 0
    return 1 + count_nodes(root.left) + count_nodes(root.right)

root = Node(1)
root.left = Node(2)
root.right = Node(3)
root.left.left = Node(4)

print(count_nodes(root)) 
</code></pre>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/2fc38f74-c929-46c7-af7e-5e39112d21d5/image.png" alt=""></p>
<ul>
<li>Node 클래스: 트리의 기본 구조</li>
<li>count_nodes() 함수: 재귀로 왼쪽 + 오른쪽 + 현재 노드 수를 합함</li>
</ul>
<h2 id="이진-탐색-트리-binary-search-tree-bst">이진 탐색 트리 (Binary Search Tree, BST)</h2>
<h3 id="정의">정의</h3>
<p>이진 탐색 트리(BST)는 왼쪽 서브트리에는 작은 값, 오른쪽 서브트리에는 큰 값이 저장되는 이진 트리(Binary Tree) 구조입니다.
이 규칙을 이용하면 탐색, 삽입, 삭제 연산을 효율적으로 수행할 수 있습니다.</p>
<h3 id="주요-연산">주요 연산</h3>
<h4 id="삽입-insert">삽입 (Insert)</h4>
<p>이진 탐색 트리는 왼쪽 &lt; 루트 &lt; 오른쪽 규칙을 유지해야 합니다.
따라서 새 값을 넣을 때, 현재 노드 값과 비교하면서
왼쪽 또는 오른쪽으로 이동하며 삽입한다.</p>
<pre><code class="language-py">def insert(tree, i, key):
    while len(tree) &lt;= i:
        tree.append(None)
    if tree[i] is None:
        tree[i] = key
    elif key &lt; tree[i]:
        insert(tree, 2*i+1, key)   # 왼쪽으로 이동
    elif key &gt; tree[i]:
        insert(tree, 2*i+2, key)   # 오른쪽으로 이동
</code></pre>
<h4 id="삭제-delete">삭제 (Delete)</h4>
<p>노드를 삭제할 때는 세 가지 경우를 고려해야 합니다:</p>
<ul>
<li><strong>단말노드인 경우</strong> 
그냥 삭제</li>
<li><strong>자식이 하나인 경우</strong> 
부모랑 자식이랑 이어줌</li>
<li><strong>자식이 둘인 경우</strong> 
오른쪽 서브트리의 가장 작은 값 즉 자신 바로 다음으로 큰 값으로 대체</li>
</ul>
<p>하지만 배열 기반 트리에서는
링크를 직접 이어주는 게 어렵기 때문에,
단순히 해당 값 위치를 None으로 바꾸는 방식으로 구현하였습니다.</p>
<pre><code class="language-py">def delete(tree, i, key):
    if i &gt;= len(tree) or tree[i] is None:
        return
    if tree[i] == key:
        tree[i] = None
    elif key &lt; tree[i]:
        delete(tree, 2*i+1, key)
    else:
        delete(tree, 2*i+2, key)
</code></pre>
<h4 id="실행-코드">실행 코드</h4>
<pre><code class="language-py">def insert(tree, i, key):
    while len(tree) &lt;= i:
        tree.append(None)
    if tree[i] is None:
        tree[i] = key
    elif key &lt; tree[i]:
        insert(tree, 2*i+1, key)
    elif key &gt; tree[i]:
        insert(tree, 2*i+2, key)

def delete(tree, i, key):
    if i &gt;= len(tree) or tree[i] is None:
        return
    if tree[i] == key:
        tree[i] = None
    elif key &lt; tree[i]:
        delete(tree, 2*i+1, key)
    else:
        delete(tree, 2*i+2, key)

# 실행
t = []
for x in [50, 30, 70, 20, 40, 60, 80]:
    insert(t, 0, x)
print(&quot;삽입 후:&quot;, t)

delete(t, 0, 70)
print(&quot;삭제 후:&quot;, t)</code></pre>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/647a82d7-f5e7-4a21-83ca-788f18b7ad0d/image.png" alt=""></p>
<h2 id="트리의-순회">트리의 순회</h2>
<p>트리의 모든 노드를 일정한 규칙에 따라 방문하는 과정을 “순회”라고 합니다.
크게 전위, 중위, 후위, 레벨 순회가 있습니다.</p>
<h3 id="전위-순회-vlr">전위 순회 (VLR)</h3>
<p>루트 → 왼쪽 → 오른쪽
트리 구조를 복사하거나 표현할 때 유용합니다.</p>
<pre><code class="language-py">def preorder(i):
    if i &gt;= len(tree) or tree[i] is None:
        return
    print(f&quot;[{tree[i]}]&quot;, end=&#39; &#39;)
    preorder(2*i + 1)
    preorder(2*i + 2)</code></pre>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/93a9fb82-0987-4aef-8a36-d3cd0834b806/image.png" alt=""></p>
<h3 id="중위-순회-lvr">중위 순회 (LVR)</h3>
<p>왼쪽 → 루트 → 오른쪽
이진 탐색 트리에서 오름차순 정렬된 결과를 얻을 수 있습니다.</p>
<pre><code class="language-py">def inorder(i):
    if i &gt;= len(tree) or tree[i] is None:
        return
    inorder(2*i + 1)
    print(f&quot;[{tree[i]}]&quot;, end=&#39; &#39;)
    inorder(2*i + 2)</code></pre>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/b6ad441c-1e31-4794-8496-a2395f0e8418/image.png" alt=""></p>
<h3 id="후위-순회-lrv">후위 순회 (LRV)</h3>
<p>왼쪽 → 오른쪽 → 루트
트리를 삭제하거나 메모리 해제할 때 주로 사용됩니다.</p>
<pre><code class="language-py">def postorder(i):
    if i &gt;= len(tree) or tree[i] is None:
        return
    postorder(2*i + 1)
    postorder(2*i + 2)
    print(f&quot;[{tree[i]}]&quot;, end=&#39; &#39;)</code></pre>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/4ef2fe07-3600-45cb-9ab6-384e37673169/image.png" alt=""></p>
<pre><code class="language-py"># 트리를 배열로 표현
tree = [1, 2, 3, 4, 5, None, 6]

# 전위 순회
def preorder(i):
    if i &gt;= len(tree) or tree[i] is None:
        return
    print(f&quot;[{tree[i]}]&quot;, end=&#39; &#39;)
    preorder(2*i + 1)
    preorder(2*i + 2)

# 중위 순회
def inorder(i):
    if i &gt;= len(tree) or tree[i] is None:
        return
    inorder(2*i + 1)
    print(f&quot;[{tree[i]}]&quot;, end=&#39; &#39;)
    inorder(2*i + 2)

# 후위 순회
def postorder(i):
    if i &gt;= len(tree) or tree[i] is None:
        return
    postorder(2*i + 1)
    postorder(2*i + 2)
    print(f&quot;[{tree[i]}]&quot;, end=&#39; &#39;)


# 실행
print(&quot;preorder : &quot;, end=&#39;&#39;)
preorder(0)
print(&quot;\ninorder  : &quot;, end=&#39;&#39;)
inorder(0)
print(&quot;\npostorder: &quot;, end=&#39;&#39;)
postorder(0)</code></pre>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/6d9024e0-6a6c-419a-a683-776210a58485/image.png" alt=""></p>
<h3 id="레벨-순회-level-order-traversal">레벨 순회 (Level-order Traversal)</h3>
<p>레벨 순회는 각 노드를 레벨순으로 검사하는 순회 방법입니다. 루트 노드의 레벨이 1이고 아래로 내려갈수록 레벨은 증가합니다. 동일한 레벨의 경우 왼쪽에서 오른쪽으로 방문합니다.
<img src="https://velog.velcdn.com/images/mgang0_0/post/922c0a11-4f5c-48da-9b33-8005d2052ed2/image.png" alt="">
레벨 순회 코드는 큐에 하나라도 노드가 있으면 계속 반복하는 코드로 이루어져 있습니다. 먼저 큐에 있는 노드를 꺼내어 방문한 다음 그 노드의 자식 노드를 큐에 삽입하는 것으로 한번의 반복을 끝냅니다. 이러한 반복을 큐에 더 이상의 노드가 없을 때까지 계속합니다.</p>
<pre><code class="language-py">class Node:
    def __init__(self, data):
        self.data = data
        self.left = None
        self.right = None

def level(root):
    if root is None:
        return
    q = [root]
    while q:
        n = q.pop(0)
        print(n.data, end=&#39; &#39;)
        if n.left:
            q.append(n.left)
        if n.right:
            q.append(n.right)

root = Node(1)
root.left = Node(2)
root.right = Node(3)
root.left.left = Node(4)
root.left.right = Node(5)

level(root)</code></pre>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/d612cb27-17a1-4d66-91cf-a4524315b99e/image.png" alt=""></p>
<ul>
<li>배열 q[]를 큐처럼 사용</li>
<li>루트부터 꺼내서 출력하고</li>
<li>왼쪽→오른쪽 자식을 차례로 넣음</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[axios 너 일로 와바]]></title>
            <link>https://velog.io/@mgang0_0/axios-%EB%84%88-%EC%9D%BC%EB%A1%9C-%EC%99%80%EB%B0%94</link>
            <guid>https://velog.io/@mgang0_0/axios-%EB%84%88-%EC%9D%BC%EB%A1%9C-%EC%99%80%EB%B0%94</guid>
            <pubDate>Mon, 10 Nov 2025 07:45:57 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/mgang0_0/post/520253b4-6e4e-456c-8279-9b4d74361896/image.png" alt=""></p>
<h2 id="시작하며">시작하며</h2>
<p>오늘은 제가 프로젝트를 만들면서 자주 사용하게 될 axios에 대해 공부한 내용을 정리해보려고 합니다.
처음 날씨 api를 불러와 통신할 때는 fetch를 사용했지만
이번에는 보다 간결하고 다루기 쉬운 axios를 적용해보기로 했습니다.</p>
<h2 id="axios란">axios란?</h2>
<p>axios는 Node.js와 브라우저 모두에서 사용할 수 있는 Promise 기반의 HTTP 요청 라이브러리입니다.
간단히 말해, axios는 서버에 데이터를 요청하거나, 서버가 보낸 응답을 받아오는 도구라고 생각하면 됩니다.</p>
<p><strong>용도</strong>
서버에 요청 보냄 -&gt; 서버로부터 응답 받아옴
<strong>예시</strong>
로그인 요청, 유저 목록 가져오기, 게시글 등록, 클릭한 상품 </p>
<p>이 모든 과정에서 axios를 사용할 수 있습니다.
프론트엔드와 백엔드가 데이터를 주고받을 때 필수적으로 활용되는 도구 중 하나입니다.</p>
<h2 id="axios를-사용하는-이유">axios를 사용하는 이유</h2>
<ul>
<li><p><strong>코드가 간결하다</strong>
같은 작업을 fetch로 구현하려면 then, JSON 변환, 에러 처리까지 많은 코드를 작성해야 하지만, axios는 비교적 단순하고 직관적인 문법을 제공합니다.</p>
</li>
<li><p><strong>자동 JSON 변환</strong>
서버에서 JSON 데이터를 주고받을 때 별도의 변환 과정 없이 자동으로 처리됩니다.</p>
</li>
<li><p><strong>에러 처리 용이</strong>
요청 실패 시 에러 상태 코드, 메시지 등 상세 정보를 쉽게 확인할 수 있어 디버깅할 때 유용합니다.</p>
</li>
<li><p><strong>인터셉터 지원</strong>
요청 전/후에 특정 로직을 공통으로 넣을 수 있습니다. (예: 토큰 추가, 에러 처리 등)</p>
</li>
<li><p><strong>타임아웃 설정</strong>
요청에 시간 제한을 걸어두어 일정 시간 내에 응답이 없으면 자동으로 오류 처리가 가능합니다.</p>
</li>
</ul>
<h4 id="기본적인-사용법">기본적인 사용법</h4>
<pre><code class="language-javascript">import axios from &#39;axios&#39;

// GET 요청 (데이터 조회)
axios.get(&#39;/api/users&#39;)
  .then((response) =&gt; {
    console.log(&#39;유저 목록:&#39;, response.data)
  })
  .catch((error) =&gt; {
    console.error(&#39;에러 발생:&#39;, error)
  })

// POST 요청 (데이터 전송)
axios.post(&#39;/api/login&#39;, {
  id: &#39;testUser&#39;,
  password: &#39;1234&#39;
})
  .then((response) =&gt; {
    console.log(&#39;로그인 성공:&#39;, response.data)
  })
  .catch((error) =&gt; {
    console.error(&#39;로그인 실패:&#39;, error)
  })</code></pre>
<p>axios는 Promise를 기반으로 하므로 .then()과 .catch()로 결과를 다룰 수 있습니다.
하지만 async/await 문법으로 조금 더 깔끔하게 작성할 수도 있습니다.</p>
<h4 id="asyncawait-문법으로-작성하기">async/await 문법으로 작성하기</h4>
<pre><code class="language-javascript">import axios from &#39;axios&#39;

async function fetchUserList() {
  try {
    const response = await axios.get(&#39;/api/users&#39;)
    console.log(&#39;유저 목록:&#39;, response.data)
  } catch (error) {
    console.error(&#39;에러 발생:&#39;, error)
  }
}

fetchUserList()</code></pre>
<p>async/await을 사용하면 코드 흐름이 동기적으로 보이기 때문에 가독성이 좋아지고 에러 처리 구조도 단순해집니다.
요즘은 대부분 async/await 방식으로 axios를 사용하는 경우가 많습니다.</p>
<h2 id="기본-설정-axios-인스턴스">기본 설정 (axios 인스턴스)</h2>
<p>프로젝트가 커질수록 매번 주소를 적는 것이 번거로워집니다.
이럴 때는 axios 인스턴스를 만들어 baseUrl등 여러설정들을 등록해두면 좋습니다.</p>
<pre><code class="language-javascript">import axios from &#39;axios&#39;

const api = axios.create({
  baseURL: &#39;https://my-project-server.com/api&#39;,
  timeout: 5000, // 요청 제한 시간 (ms)
  headers: {
    &#39;Content-Type&#39;: &#39;application/json&#39;,
  },
})

export default api</code></pre>
<p>이렇게 만들어둔 인스턴스를 사용하면</p>
<pre><code class="language-javascript">import api from &#39;./api&#39;

// api.get(&#39;/users&#39;)
// api.post(&#39;/login&#39;)</code></pre>
<p>처럼 간단하게 호출할 수 있습니다. 코드 중복이 줄고 유지보수도 쉬워집니다.</p>
<h2 id="프로젝트-적용-중-생긴-문제">프로젝트 적용 중 생긴 문제</h2>
<p>백엔드에서는 카테고리 값을 영문 Enum 형태로 관리하고 있었고 프론트엔드는 사용자가 보기 쉽게 한글 카테고리명을 보여주고 있었습니다.
그래서 프론트에서 선택한 한글 카테고리를 영어 코드로 변환해 전송해야 했습니다.</p>
<p>아래는 당시 프론트 쪽 코드의 일부입니다.</p>
<pre><code class="language-javascript">const categories = [
  &#39;운동&#39;, &#39;맛집&#39;, &#39;동물&#39;, &#39;여행&#39;, &#39;영화&#39;,
  &#39;게임&#39;, &#39;독서&#39;, &#39;공부&#39;, &#39;음악&#39;, &#39;🔞&#39;,
  &#39;웹툰&#39;, &#39;외향형&#39;, &#39;내향형&#39;, &#39;애니메이션&#39;
];

const categoryMap = {
  운동: &#39;EXERCISE&#39;,
  맛집: &#39;RESTAURANT&#39;,
  동물: &#39;ANIMAL&#39;,
  여행: &#39;TRIP&#39;,
  영화: &#39;MOVIE&#39;,
  게임: &#39;GAME&#39;,
  독서: &#39;READING&#39;,  // 문제의 원인
  공부: &#39;STUDY&#39;,
  음악: &#39;MUSIC&#39;,
  &#39;🔞&#39;: &#39;SEXUAL_PLEASURE&#39;,
  웹툰: &#39;WEBTOON&#39;,
  외향형: &#39;EXTROVERT&#39;,
  내향형: &#39;INTROVERT&#39;,
  애니메이션: &#39;ANIMATION&#39;,
};</code></pre>
<p>그런데 회원가입 요청을 보낼 때 독서를 선택한 사용자만 회원가입이 실패하는 현상이 발생했습니다.
콘솔에서 확인해보니 API 응답이 500에러를 반환하고 있었습니다.
<img src="https://velog.velcdn.com/images/mgang0_0/post/d526ea02-1e4a-41f2-91fc-57774a211a19/image.png" alt=""></p>
<p>원인 분석
백엔드에서 사용하는 Enum 이름은 LEADING이었는데 프론트엔드에서는 READING으로 백엔드에서 오타가 나서 잘못 매핑되어 있었습니다.</p>
<p>프론트엔드와 백엔드 코드에 다음과 같은 차이가 있었습니다.</p>
<h4 id="백엔드">백엔드</h4>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/5c51952c-99ea-48ba-8cf7-4a9ab5e317d7/image.png" alt=""></p>
<h4 id="프론트엔드">프론트엔드</h4>
<pre><code class="language-javascript">const categoryMap = {
  운동: &#39;EXERCISE&#39;,
  맛집: &#39;RESTAURANT&#39;,
  동물: &#39;ANIMAL&#39;,
  여행: &#39;TRIP&#39;,
  영화: &#39;MOVIE&#39;,
  게임: &#39;GAME&#39;,
  독서: &#39;READING&#39;,
  공부: &#39;STUDY&#39;,
  음악: &#39;MUSIC&#39;,
  &#39;🔞&#39;: &#39;SEXUAL_PLEASURE&#39;,
  웹툰: &#39;WEBTOON&#39;,
  외향형: &#39;EXTROVERT&#39;,
  내향형: &#39;INTROVERT&#39;,
  애니메이션: &#39;ANIMATION&#39;,
};</code></pre>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/b2601141-d808-4b55-acf8-abc9783f3f6d/image.png" alt=""></p>
<p>이러한 불일치는 회원가입 시 유효성 검사 단계에서 거부되어 에러로 이어졌습니다.
눈으로 보면 금방 알 수 있지만 실제 프로젝트에서는 이런 부분이 쉽게 놓치기 쉽습니다.</p>
<h3 id="문제-해결">문제 해결</h3>
<p>우선 백엔드 Enum 리스트를 정확히 확인한 뒤 프론트에서 사용하는 categoryMap을 수정했습니다.</p>
<p>수정 후 다시 회원가입을 진행하니 문제가 완벽히 해결되었습니다.
회원가입 요청이 정상적으로 서버에 전달되고 선택한 카테고리들도 올바르게 저장되었습니다.</p>
<h3 id="느낀-점">느낀 점</h3>
<p>이번 문제를 통해 깨달은 건 프론트엔드와 백엔드 간 데이터 명세 일치가 얼마나 중요한가 또 소통의 중요성을 다시한번 느꼈습니다.
백엔드에서 Enum 값을 조금만 바꿔도 프론트엔드 로직이 바로 깨질 수 있기 때문에 api 명세서가 정확히 정리돼 있어야 합니다.</p>
<h2 id="마치며">마치며</h2>
<p>이번에 axios를 직접 적용해보면서 단순한 API 요청뿐 아니라 인터셉터, 인스턴스 설정, 에러 처리 등 다양한 기능이 있다는 걸 알게 되었습니다.
처음에는 fetch로도 충분하다고 생각했지만 실제 프로젝트에서는 axios의 편리함이 훨씬 크게 느껴졌습니다.</p>
<p>앞으로 axios 인스턴스와 인터셉터를 좀 더 적극적으로 활용하여 프로젝트를 잘 마무리 하도록 하겠습니다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[컴포넌트 상태 관리 및 생명주기]]></title>
            <link>https://velog.io/@mgang0_0/%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC-%EB%B0%8F-%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0</link>
            <guid>https://velog.io/@mgang0_0/%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC-%EB%B0%8F-%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0</guid>
            <pubDate>Wed, 22 Oct 2025 10:41:02 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/mgang0_0/post/db62f8d8-2063-46a1-ba42-98b624048daf/image.png" alt=""></p>
<h2 id="상태관리란">상태관리란?</h2>
<p>컴포넌트마다 데이터를 직접 관리하는 번거러움을 피하고자 데이터를 리액트에서 중요한 데이터를 자동으로 관리하기 위한 데이터 관리를 말합니다.</p>
<p>간단하게 알아보자면</p>
<h3 id="로컬-상태">로컬 상태</h3>
<p>특정 컴포넌트 내에서만 데이터를 관리 hook과 props를 사용해 관리합니다. 하지만 어플리케이션의 크기가 점점 커질수록 반복 작업이 늘어나 유지보수가 어려워지는 단점이 있습니다.</p>
<h3 id="전역-상태">전역 상태</h3>
<p>프로젝트 전체의 상태관리를 총괄하는 방법입니다 대표적으로 Redux, Zustand가 있습니다 전역에서 관리하기 때문에 컴포넌트 상태 값 반영하고 변경하는 것이 가능 합니다 props 과정이 생략돼 작업이 적고 유지보수가 간편해 지는 장점이 있습니다 하지만 상태관리 오류시 나타나는 사이드 이펙트가 커지는 단점이 존재 합니다.</p>
<h3 id="zustand">Zustand</h3>
<p>가장 많이 쓰이는 zustand에 대해 알아 보겠습니다. zustand를 사용하면 전역 상태를 저장하는 ‘스토어’를 생성하고, 이 스토어의 상태를 컴포넌트에 필요한 만큼만 가져와서 사용합니다.</p>
<h2 id="생명주기">생명주기</h2>
<p>리액트에서 생명 주기란 특정 컴포넌트의 생성 변경 소멸의 과정을 의미합니다. 생성-mount 변경-update 소멸-unmount 라고 지칭 합니다. 이러한 생명주기 이벤트에 연결할 수 있는 함수들을 생명주기 함수 라고 합니다.</p>
<h3 id="mount-컴포넌트-생성-시">Mount (컴포넌트 생성 시)</h3>
<p>컴포넌트가 처음 만들어질 때 실행됩니다.
초기 state 값을 설정하고, 필요한 이벤트를 등록합니다.
전달받은 props 값에 따라 state를 업데이트하거나 초기 렌더링을 처리합니다.
외부 API를 호출해 데이터를 가져오고 state에 저장할 수도 있습니다.</p>
<h3 id="update-컴포넌트-업데이트-시">Update (컴포넌트 업데이트 시)</h3>
<p>props나 state가 변경되면 실행됩니다.
변경된 부분만 다시 렌더링하여 화면을 갱신합니다.
불필요한 연산을 줄이기 위해, 특정 state가 바뀔 때만 관련된 함수가 실행되도록 처리합니다.</p>
<h3 id="unmount-컴포넌트-제거-시">Unmount (컴포넌트 제거 시)</h3>
<p>컴포넌트가 화면에서 사라질 때 실행됩니다.
타이머나 인터벌 같은 비동기 작업을 정리해 불필요한 연산을 없앱니다.
window 등 전역 객체에 등록된 이벤트 리스너를 제거합니다.
컴포넌트가 사라질 때 애니메이션이나 전환 효과를 처리할 수도 있습니다.</p>
<p>컴포넌트가 생성, 업데이트, 소멸될 때마다 개발자가 직접 로직을 처리할 수도 있지만, 이런 작업들을 컴포넌트의 생명주기 이벤트에 미리 등록해두면, 각 단계에 맞는 기능이 자동으로 실행됩니다.
매번 이벤트를 직접 관리하면 비효율적이기 때문에, React의 생명주기 함수를 잘 활용해 코드를 작성하는 것이 좋습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[props???]]></title>
            <link>https://velog.io/@mgang0_0/props</link>
            <guid>https://velog.io/@mgang0_0/props</guid>
            <pubDate>Wed, 15 Oct 2025 09:31:16 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/mgang0_0/post/a53e5bec-9b49-485b-8f26-500e632e7b7a/image.png" alt=""></p>
<h2 id="시작하며">시작하며</h2>
<p>리액트를 공부하면서 컴포넌트와 props에 대해 많이 접하게 되었습니다. 그래서 제가 배운 내용을 글로 적어 보려고 합니다!</p>
<h2 id="props란">Props란?</h2>
<p>props는 properties(속성)의 약자입니다.
우리가 어떠한 값을 컴포넌트에게 전달해줘야 할 때 props를 사용합니다.</p>
<pre><code class="language-js">function Welcome(props) {
  return &lt;h1&gt;안녕, {props.name}!&lt;/h1&gt;;
}

function App() {
  return &lt;Welcome name=&quot;강현&quot; /&gt;;
}</code></pre>
<p>Welcome 컴포넌트에서 name값을 사용하고 싶을때 어떻게 할까요?
컴포넌트에게 전달되는 props는 파라미터를 통하여 조회 할 수 있습니다. props는 객체 형태로 전달되며 만약 <code>name</code> 값을 조회하고 싶다면 <code>props.name</code>을 사용하여 조회 할 수 있습니다.</p>
<h2 id="여러-개의-props">여러 개의 props</h2>
<p>props는 여러 개 전달이 가능합니다. 예를 들어 color라는 값을 추가로 전달하면, 다음과 같이 사용할 수 있습니다.</p>
<pre><code class="language-jsx">&lt;Hello name=&quot;react&quot; color=&quot;red&quot; /&gt;</code></pre>
<p>컴포넌트에서 props를 반복해서 사용하다 보면, 비구조화 할당 문법이 훨씬 코드를 간결하게 만들어줍니다. 함수의 파라미터에서 props를 구조 분해하면 아래와 같습니다.</p>
<pre><code class="language-jsx">function Hello({ name, color }) {
  return &lt;div style={{ color }}&gt;{name}&lt;/div&gt;;
}</code></pre>
<p>이렇게 하면 <code>props.name</code>과 <code>props.color</code> 대신, 바로 <code>name</code>, <code>color</code>를 사용할 수 있어 코드가 더욱 읽기 쉬워집니다.</p>
<h2 id="props는-읽기-전용">props는 읽기 전용</h2>
<p>props는 부모에서 자식으로만 흐르는 데이터 입니다.
즉 자식 컴포넌트는 props를 변경할 수 없습니다.
만약 데이터를 수정해야 한다면,
부모에서 state를 관리하고 props로 다시 내려주는 방식으로 처리해야 합니다.</p>
<pre><code class="language-jsx">function CounterDisplay({ count }) {
  return &lt;h2&gt;현재 카운트: {count}&lt;/h2&gt;;
}

function Counter() {
  const [count, setCount] = useState(0);

  return (
    &lt;&gt;
      &lt;CounterDisplay count={count} /&gt;
      &lt;button onClick={() =&gt; setCount(count + 1)}&gt;+1&lt;/button&gt;
    &lt;/&gt;
  );
}</code></pre>
<p><code>count</code>는 부모인 <code>Counter</code>에서 관리됨
<code>CounterDisplay</code>는 단지 그 값을 받아서 보여주기만 함</p>
<h2 id="props의-구조-분해-할당">props의 구조 분해 할당</h2>
<p><code>props.name</code>, <code>props.age</code> 처럼 매번  <code>props.</code>을 쓰는 것은 귀찮을 수 있습니다
이럴땐 구조 분해 할당을 사용하면 깔끔 해집니다.</p>
<pre><code class="language-jsx">function UserCard({ name, age }) {
  return (
    &lt;div&gt;
      &lt;h3&gt;{name}&lt;/h3&gt;
      &lt;p&gt;{age}살&lt;/p&gt;
    &lt;/div&gt;
  );
}

function App() {
  return &lt;UserCard name=&quot;강현&quot; age={17} /&gt;;
}</code></pre>
<h2 id="props-state-차이">props state 차이</h2>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/dd9b3c3d-8136-4f3b-91c2-cbb7b423c06d/image.png" alt=""></p>
<p>props는 읽기 전용입니다 상위에서 하위로만 전달 가능 합니다. 하지만 state는 컴포넌트 내부에서 변경이 가능합니다.
props는 상위 컴포넌트에서 관리 합니다. 하지만 state는 해당 컴포넌트 내부에서 관리 합니다.
props의 용도는 컴포넌트 간 데이터 전달이고 state의 용도는는 UI의 상태를 동적으로 변경합니다.</p>
<h2 id="props-기본값-defaultprops">props 기본값 defaultProps</h2>
<p>props가 전달되지 않았을 때 사용할 기본 값을 저장할 수 있습니다.</p>
<pre><code class="language-jsx">function Hello({ name = &quot;게스트&quot; }) {
  return &lt;h1&gt;안녕, {name}!&lt;/h1&gt;;
}
Hello.defaultProps = {
  name: &quot;게스트&quot;,
};</code></pre>
<p>이 코드에서는 props를 전달하지 않아도 에러 없이 기본값이 표시됩니다.</p>
<h2 id="children-props">children props</h2>
<p>컴포넌트 태그 사이에 넣은 내용을 참조 해야 할 떄는 props.children을 활용합니다. 예를 들어 Wrapper 컴포넌트 내부에 자식들을 렌더링하려면 아래와 같이 작성합니다.</p>
<pre><code class="language-jsx">function Wrapper({ children }) {
  const style = {
    border: &#39;2px solid black&#39;,
    padding: &#39;16px&#39;,
  };
  return &lt;div style={style}&gt;{children}&lt;/div&gt;;
}</code></pre>
<p>이 Wrapper 컴포넌트로 감싼 자식들의 내용을 모두 받아 렌더링 합니다. 이를 활용해 여러 컴포넌트를 그룹화 하고 레이아웃을 간편히 구성 가능 합니다.</p>
<h2 id="마무리">마무리</h2>
<p>오늘은 props의 개념과 state와의 차이점을 간단하게 알아 보았습니다. React에서 컴포넌트 간의 데이터 전달과 내부 상태관리를 효과적으로 할 수 있습니다. 앞으로 저도 프로젝트에 잘 적용하며 써보도록 하겠습니다. 감사합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[비동기 프로그래밍]]></title>
            <link>https://velog.io/@mgang0_0/%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</link>
            <guid>https://velog.io/@mgang0_0/%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</guid>
            <pubDate>Mon, 13 Oct 2025 09:00:31 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/mgang0_0/post/8cce07de-dbea-47ec-81c0-a70937bb56e0/image.png" alt=""></p>
<h2 id="시작하며">시작하며</h2>
<p>오늘은 동기,비동기에 대해 알아보겠습니다.</p>
<h2 id="동기란">동기란?</h2>
<p>한번에 하나씩 순서대로 수행하는 것입니다.
한마디로 번호표라고 생각 하시면 편합니다.
예를 들어 병원에서 번호표를 뽑고 진료를 기다리고 있을 때 자기 차례가 오기 전까지 앞 번호를 뽑은 사람들이 차례대로 한명씩 진료를 받는 방법이 동기라고 생각하시면 됩니다
주로 티케팅 예매 사이트에서 순차대로 먼저 클릭한사람 먼저 서버에 요청을 보낸사람 순으로 처리해줄때 사용합니다
그렇다면 <strong>동기 프로그래밍</strong>은 무엇일까요 간단한 예를 들어 보겠습니다</p>
<pre><code class="language-js">console.log(&quot;작업 1&quot;);
console.log(&quot;작업 2&quot;);
console.log(&quot;작업 3&quot;);</code></pre>
<p>이런식으로 간단한 코드를 본다면 
console.log를 작업1 ~ 작업3 까지 차례대로 하나씩 코드를 실행 합니다.</p>
<h2 id="비동기란">비동기란?</h2>
<p>비동기란 동시에 여러 작업을 수행하는 것입니다.
예를 들자면 동시에 여러 손님을 받는 식당입니다. 
손님이 동시에 여러개의 주문을 넣는 다면 동시에 여러 요리를 해야하기 떄문에 비동기 작업이라고 볼 수 있습니다.
주로 로그인기능을 구현할때 순서 상관없이 처리해줄 때 사용합니다
그렇다면 <strong>비동기 프로그래밍</strong>은 무엇일까요 간단한 예를 들어보겠습니댜.</p>
<pre><code class="language-js">console.log(&#39;1&#39;);

setTimeout(() =&gt; {
    console.log(&#39;2&#39;);
}, 2000);</code></pre>
<p>이 코드는 1이 실행된 이후에 2초뒤에 콘솔창에서 2가 출력되는 코드 입니다.</p>
<pre><code class="language-js">setTimeout(() =&gt; {
    console.log(&#39;2&#39;);
}, 2000);

console.log(&#39;1&#39;);</code></pre>
<p>여기서 위 코드 처럼 함수 선언을 먼저 한다면 
실행 결과가 2가 먼저 출력되고 2초뒤에 1이 출력될 것 같지만
setTimeout이 비동기 함수이기 때문에 2초가 지나기 전에 먼저 1이 출력되고 2초후 2가 출력됩니다.
한마디로 비동기 작업이기 때문에 타이머가 끝나길 기다리지 않고 먼저 다른 값을 출력합니다.</p>
<h2 id="비동기-콜백">비동기 콜백</h2>
<p>콜백함수란 다른 함수의 인자로 전달되는 함수</p>
<pre><code class="language-js">function main(callback) {
  callback();
}

main(() =&gt; {});</code></pre>
<p>위 코드 처럼 함수를 main에서 호출할 때 인자로 전달 돼는 함수를 콜백함수라고 합니다.
콜백 함수를 전달 받는 함수는 전달 받은 콜백을 함수 내부에서 호출 하도록 구현 되어있습니다.</p>
<h3 id="콜백지옥">콜백지옥</h3>
<p>콜백함수를 쓴다면 비동기 작업도 순차적으로 실행 가능 합니다</p>
<pre><code class="language-js">// 1.로그인
function login(username, callback) {
  setTimeout(() =&gt; {
    callback(username);
  }, 1000);
}

// 2. 장바구니에 넣기
function addToCart(product, callback) {
  setTimeout(() =&gt; {
    callback(product);
  }, 1000);
}

// 3.결제하기
function makePayment(cardNumber, product, callback) {
  setTimeout(() =&gt; {
    callback(cardNumber, product);
  }, 1000);
}

login(&quot;홍길동&quot;, (username) =&gt; {
  console.log(`${username}님 안녕하세요`);
  addToCart(&quot;시계&quot;, (product) =&gt; {
    console.log(`${product}를 장바구니에 담았습니다`);
    makePayment(&quot;0000000000000000&quot;, product, (cardNumber, item) =&gt; {
      console.log(`${cardNumber.slice(0, 6)}로 ${product}를 구매했습니다`);
    });
  });
});
</code></pre>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/7192aa38-412b-47f0-990d-ef83d786c996/image.gif" alt=""></p>
<blockquote>
<p>다만 이런식으로 코드를 짜게 된다면 가독성도 안좋을 뿐만 아니라 콜백함수를 계속 호출하는 <strong>콜백지옥</strong>에 빠지게 됩니다 또한 유지보수도 힘들어지게 됩니다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/30d342a9-d8de-47fa-ac93-6776018e7d30/image.png" alt=""></p>
<p>그렇다면 이러한 문제를 어떻게 해결 할까요?
바로 Promise를 사용하면 됩니다.</p>
<h2 id="promise">Promise</h2>
<h3 id="정의">정의</h3>
<p>Promise는 비동기 작업의 상태(진행중, 성공, 실패)를 나타내는 객체입니다.
비유하자면 비동기 작업이 끝나면 결과값이나 에러가 들어가는 상자와 같습니다.
세 가지 상태(pending, fulfilled, rejected)가 있으며 작업의 결과에 따라 상태와 값이 결정됩니다.
pending은 대기 상태 result는 undefined
fulfillde는 성공했을 때 완료상태 result는 결과값
rejected는 실패했을 때 오류상태를 나타냄 result는 error</p>
<h3 id="사용법">사용법</h3>
<p>new Promise(익시큐터 함수)로 생성할 수 있습니다.
익시큐터 함수는 두 개의 인자(resolve, reject)를 받으며 비동기 작업이 성공하면 resolve, 실패하면 reject를 호출하면 됩니다.
생성 즉시 익시큐터 함수가 실행됩니다.</p>
<p>익시큐터 함수는 new Promise()에 괄호 안에있는 함수를 뜻합니다</p>
<pre><code class="language-js">const promise = new Promise((resolve, reject) =&gt; {
  setTimeout(() =&gt; {
    resolve({ name: &#39;철수&#39; });
  }, 1000);
});</code></pre>
<h3 id="비동기-함수로-활용">비동기 함수로 활용</h3>
<p>비동기 작업마다 Promise를 반환하는 함수로 만들고 원하는 시점에 호출해 비동기 작업을 실행할 수 있습니다.
함수 내부에서 new Promise로 작업을 변환하고 return하면 됩니다.</p>
<h3 id="then-catch-finally">then, catch, finally</h3>
<p>then: Promise가 성공(fulfilled)하면 실행되는 콜백을 등록합니다.
catch: 실패(rejected) 시 에러를 처리합니다.
finally: 작업 성공/실패와 관계없이 무조건 실행되는 마지막 후처리 콜백을 지정할 수 있습니다.</p>
<pre><code class="language-js">getData()
  .then((data) =&gt; { console.log(data); })
  .catch((err) =&gt; { console.log(err); })
  .finally(() =&gt; { console.log(&#39;작업 완료&#39;); });</code></pre>
<h3 id="promise-chaining">Promise Chaining</h3>
<p>then의 반환값이 또 다른 Promise라면 다음 then에서 결과가 이어받아집니다.
여러 비동기 작업을 순차적으로 이어붙여 관리할 수 있어 &#39;콜백 지옥&#39;을 예방합니다.</p>
<pre><code class="language-js">getData()
  .then((data) =&gt; getMoreData(data))
  .then((moreData) =&gt; processData(moreData))
  .catch((err) =&gt; handleError(err));</code></pre>
<h3 id="fetch-api와-promise">Fetch API와 Promise</h3>
<p>fetch란 JavaScript에서 서버와 데이터를 주고받기 위해 사용된다.
fetch 등 많은 현대 웹 API는 Promise를 반환합니다.
then/catch/finally와 chaining으로 비동기 데이터 요청 및 처리 로직을 간결하게 구성할 수 있습니다.
예시코드</p>
<pre><code class="language-js">fetch(&quot;https://jsonplaceholder.typicode.com/users&quot;)
  .then((response) =&gt; {
    return response.json();
  })
  .then((data) =&gt; {
    console.log(data);
  })
  .catch((error) =&gt; {
    console.log(&quot;에러발생&quot;);
  })
  .finally(() =&gt; {
    console.log(&quot;마무리 작업&quot;);
  });</code></pre>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/bca6fbea-fb25-4b52-8556-fb22d67521f8/image.png" alt="">
사진 차럼 fetch로 링크에있는 test서버의 데이터들을 불러오고 콘솔에서 출력 혹시 오류가 발생한다면 <code>.catch</code>로 에러 검출 finally로 꼭 실행되야하는 후처리 작업</p>
<h3 id="promise로-가독성-좋은-코드-짜기">Promise로 가독성 좋은 코드 짜기</h3>
<h4 id="promise-1">promise</h4>
<pre><code class="language-js">// 1.로그인
function login(username) {
  return new Promise((resolve, reject) =&gt; {
    setTimeout(() =&gt; {
      if (username) {
        resolve(username);
      } else {
        reject(new Error(&quot;아이디를 입력해주세요&quot;));
      }
    }, 1000);
  });
}

// 2.장바구니에 넣기
function addToCart(product) {
  return new Promise((resolve, reject) =&gt; {
    setTimeout(() =&gt; {
      if (product) {
        resolve(product);
      } else {
        reject(new Error(&quot;장바구니에 넣을 상품이 없어요&quot;));
      }
    }, 1000);
  });
}

// 3.결제하기
function makePayment(cardNumber, product) {
  return new Promise((resolve, reject) =&gt; {
    setTimeout(() =&gt; {
      if (cardNumber !== 16) {
        reject(new Error(&quot;잘못된 카드 번호 입니다.&quot;));
        return;
      }

      if (!product) {
        reject(new Error(&quot;결제할 상품을 넣어 주세요&quot;));
        return;
      }
      resolve(product);
    }, 1000);
  });
}

login(&quot;홍길동&quot;)
  .then((username) =&gt; {
    console.log(`${username}님 환영합니다`);
    return addToCart(&quot;시계&quot;);
  })
  .then((product) =&gt; {
    console.log(`${product}를 장바구니에 넣었어요`);
    return makePayment(&quot;0000000000000000&quot;, product);
  })
  .then((product) =&gt; {
    console.log(`${product}를 구매했습니다`);
  });</code></pre>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/f0228fd3-c4e2-4539-b5a1-d2405d877238/image.png" alt=""></p>
<h4 id="비동기-콜백-1">비동기 콜백</h4>
<pre><code class="language-js">// 1.로그인
function login(username, callback) {
  setTimeout(() =&gt; {
    callback(username);
  }, 1000);
}

// 2. 장바구니에 넣기
function addToCart(product, callback) {
  setTimeout(() =&gt; {
    callback(product);
  }, 1000);
}

// 3.결제하기
function makePayment(cardNumber, product, callback) {
  setTimeout(() =&gt; {
    callback(cardNumber, product);
  }, 1000);
}

login(&quot;홍길동&quot;, (username) =&gt; {
  console.log(`${username}님 안녕하세요`);
  addToCart(&quot;시계&quot;, (product) =&gt; {
    console.log(`${product}를 장바구니에 담았습니다`);
    makePayment(&quot;0000000000000000&quot;, product, (cardNumber, item) =&gt; {
      console.log(`${cardNumber.slice(0, 6)}로 ${product}를 구매했습니다`);
    });
  });
});
</code></pre>
<p>쇼핑몰 로그인 → 장바구니 담기 → 결제 등 순차 작업에 Promise chaning을 사용하면 가독성 좋은 코드로 쉽게 관리할 수 있습니다.
각 단계에서 발생 가능한 에러마다 catch를 중간중간 넣어 유연하게 에러를 처리할 수도 있습니다.</p>
<h2 id="async--await">Async &amp; Await</h2>
<h3 id="async란">Async란?</h3>
<p>async 키워드는 함수를 비동기 함수로 만들어 줍니다.
async가 붙은 함수는 항상 Promise가 반환됩니다.
함수 내부에서 일반 값을 반환하면 그 값이 자동으로 Promise로 변환되어 반환됩니다.
내부에서 직접 Promise를 반환하면 중첩되지 않고 그대로 반환됩니다.
예시</p>
<pre><code class="language-js">async function getUser() {
  return &#39;안녕&#39;;
}</code></pre>
<p>→ 내부적으로 Promise.resolve(&#39;안녕&#39;) 형태로 반환됩니다.</p>
<h3 id="await란">Await란?</h3>
<p>await는 Promise가 완료될 때까지 기다리는 키워드입니다.
반드시 async 함수 내부에서만 사용할 수 있습니다.
해당 Promise가 resolve될 때까지 코드를 일시 중단하고 결과값을 받아 변수에 담습니다.
예시</p>
<pre><code class="language-js">function networkRequest() {
  return new Promise((resolve) =&gt; {
      setTimeout(() =&gt; {
      console.log(&#39;데이터를 받아왔습니다&#39;);
      resolve(&#39;서버1&#39;);
    },2000);
  })    
}

async function getUser() {
  const result = await networkRequest();
  console.log(result);
}

const user = getUser();
user.then((name) =&gt; console.log(name));</code></pre>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/78dc8584-3907-4ff3-9c93-13c46a25521d/image.png" alt=""></p>
<p>await은 비동기 작업이 완료될 때까지 기다려서 코드가 마치 동기적으로 실행되는 것처럼 보입니다.</p>
<h3 id="에러-처리">에러 처리</h3>
<p>try...catch 문을 활용하면 비동기 함수 내부의 에러를 쉽게 처리할 수 있습니다.
await으로 호출되는 함수에서 throw가 발생하면 catch 블록으로 전달됩니다.
예시</p>
<pre><code class="language-js">async function getData() {
  try {
    const data = await fetchData();
    console.log(data);
  } catch (error) {
    console.error(&#39;에러 발생:&#39;, error.message);
  }
}</code></pre>
<p>에러가 발생하면 코드가 멈추지 않고 안전하게 처리됩니다.</p>
<h3 id="fetch-api와-awaitasync">fetch api와 await&amp;async</h3>
<h4 id="awaitasync">await&amp;async</h4>
<pre><code class="language-js">async function fetchData() {
  const response = await fetch(&quot;https://jsonplaceholder.typicode.com/users&quot;)
  const data = await response.json();
  console.log(response);
}  

fetchData();</code></pre>
<h4 id="promise-2">promise</h4>
<pre><code class="language-js">fetch(&quot;https://jsonplaceholder.typicode.com/users&quot;)
  .then((response) =&gt; {
    return response.json();
  })
  .then((data) =&gt; {
    console.log(data);
  })
  .catch((error) =&gt; {
    console.log(&quot;에러발생&quot;);
  })
  .finally(() =&gt; {
    console.log(&quot;마무리 작업&quot;);
  });</code></pre>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/984ac39d-a9c8-4928-9809-69be004578a4/image.png" alt="">
이 코드는 URL에서 사용자 데이터 JSON을 비동기적으로 받아와서 데이터가 완전히 로드된 후 콘솔에 출력하는 기능을 합니다.
이처럼 async와 await를 쓰면 복잡한 Promise 체인 없이도 마치 동기 처리하듯 깔끔하고 직관적인 비동기 코드 작성이 가능합니다.</p>
<h2 id="마치며">마치며</h2>
<p>오늘은 비동기,동기 , 콜백함수, promise, async, await에 대해 알아보았습니다.
promise개념이 조금 어려웠지만 여러 영상이나 블로그 찾아보면서 공부 해보았습니다.
promise와 async,await을 잘 사용하면 비동기 프로그래밍을 효율적이게 구현할 수 있을것 같습니다.
글 읽어주셔서 감사합니다 피드백 많이 해주세요!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Function VS Arrow Function]]></title>
            <link>https://velog.io/@mgang0_0/Function-VS-Arrow-Function</link>
            <guid>https://velog.io/@mgang0_0/Function-VS-Arrow-Function</guid>
            <pubDate>Thu, 18 Sep 2025 15:32:52 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/mgang0_0/post/79ab516c-cfac-4e83-8698-aea771b96ceb/image.jpg" alt=""></p>
<h2 id="시작하며">시작하며</h2>
<p>자바스크립트에서 함수를 선언하는 방식은 다양하지만 특히 일반 함수와 화살표 함수는 자주 비교되는 개념입니다.
이 글에선 두가지의 차이점과 화살표 함수를 사용하는 이유를 정리해보겠습니다. </p>
<h2 id="함수-선언-방식">함수 선언 방식</h2>
<h3 id="일반-함수">일반 함수</h3>
<pre><code class="language-js">const add = function(a, b) {
    return a + b;
};</code></pre>
<ul>
<li><code>function</code> 키워드를 사용해 선언</li>
<li>함수 이름을 가질 수도 있고, 익명 함수로 표현식에 할당할 수도 있음<h3 id="화살표-함수">화살표 함수</h3>
</li>
</ul>
<pre><code class="language-js">const add = (a, b) =&gt; {
    return a + b;
};</code></pre>
<ul>
<li>function 키워드 대신 <code>=&gt;</code>기호를 사용하여 함수를 간결 하고 간단하게 작성 가능</li>
<li>한줄 반환 시 return과 {} 생략 가능<pre><code class="language-js">const greet = (name) =&gt; `Hello, ${name}!`;</code></pre>
위 코드 처럼 <code>return</code> 키워드를 생략할 수 있고 이때 중괄호{} 는 생략해야 합니다.
또, 중복된 매개변수 이름을 선언 할 수 없습니다.<h2 id="화살표-함수와-일반-함수의-차이">화살표 함수와 일반 함수의 차이</h2>
화살표 함수와 일반 함수의 차이에 대해 알아보겠습니다.</li>
</ul>
<h3 id="this-바인딩-차이">this 바인딩 차이</h3>
<p>먼저 바인딩이란 쉽게 말해서 변수, 함수, 객체 등이 어떤 값이 연결되는 것을 의미합니다.
즉 함수가 실행될 때 <code>this</code>가 무엇을 가리키는지를 결정하는 연결 과정을 바인딩이라고 합니다.</p>
<h4 id="일반함수">일반함수</h4>
<pre><code class="language-js">window.a = 10;

function foo() {
  console.log(this.a);
}

foo(); // 10</code></pre>
<p>기본 바인딩이 적용될 경우 this는 전역 객체에 바인딩된다.</p>
<h4 id="화살표-함수-1">화살표 함수</h4>
<pre><code class="language-js">const obj = {
  name: &quot;JS&quot;,
  arrow: () =&gt; console.log(this.name),
};

obj.arrow(); // undefined (전역 this)</code></pre>
<p>화살표 함수는 자신만의 this를 가지지 않고 정의된 위치의 this를 그대로 사용합니다.
따라서 eventListener나 콜백에서 this를 헷갈리지 않고 사용할 수 있습니다.</p>
<h3 id="arguments-객체">arguments 객체</h3>
<p>arguments란 프로그래밍에서 함수가 호출될 때 함수에 전달되는 실제 값을 의미합니다.</p>
<h4 id="일반-함수-1">일반 함수</h4>
<pre><code class="language-js">function normalFunc(a, b) {
  console.log(arguments); // [1, 2, 3]
}
normalFunc(1, 2, 3);</code></pre>
<h4 id="화살표-함수-2">화살표 함수</h4>
<pre><code class="language-js">const arrowFunc = (...args) =&gt; {
  console.log(args);  // {0: 1, 1: 2, 2: 3}
};
arrowFunc(1, 2, 3);</code></pre>
<blockquote>
<p>일반 함수는 함수 안에서 전달된 모든 인수를 <code>arguments</code> 객체로 확인할 수 있습니다.
반면 화살표함수는 <code>arguments</code>가 없고 대신 ``...args`나머지 매개변수를 사용해야 합니다.</p>
</blockquote>
<h3 id="생성자-함수">생성자 함수</h3>
<p>생성자 함수란 <code>new</code>키워드를 사용해 객체를 만들어주는 함수입니다.</p>
<h4 id="일반-함수-2">일반 함수</h4>
<pre><code class="language-js">function Person(name, age) {
  this.name = name;
  this.age = age;
}

const p1 = new Person(&quot;Tom&quot;, 20);
console.log(p1.name); // &quot;Tom&quot;</code></pre>
<blockquote>
<p>이름 첫 글자를 대부분 대문자로 씁니다.
코드 안에서 <code>this</code>는 새로 만들어질 객체를 가리킵니다.</p>
</blockquote>
<h4 id="화살표-함수-3">화살표 함수</h4>
<pre><code class="language-js">const PersonArrow = (name, age) =&gt; {
  this.name = name;
  this.age = age;
};

const p2 = new PersonArrow(&quot;Tom&quot;, 20); // 에러</code></pre>
<blockquote>
<p>화살표 함수에는 자체적인 <code>this</code>가 없기 때문에 <code>new</code>와 함께 쓰면 에러가 납니다.</p>
</blockquote>
<h2 id="화살표-함수-장점">화살표 함수 장점</h2>
<h3 id="문법이-간결하다">문법이 간결하다</h3>
<p>가장 큰 이유 중 하나는 짧은 문법입니다.
위에서 설명 한것 처럼 function 키워드가 필요없고
한 줄로 반환할 때 {} 와 return 도 생략 가능 합니다.</p>
<h3 id="콜백-함수에서-깔끔">콜백 함수에서 깔끔</h3>
<p><code>map</code>, <code>filter</code>, <code>forEach</code> 같은 배열 매서드에서 특히 유용합니다.</p>
<pre><code class="language-js">const numbers = [1, 2, 3];
const doubled = numbers.map(n =&gt; n * 2);
console.log(doubled); // [2, 4, 6]</code></pre>
<p>반복되는 짧은 콜백 함수는 화살표 함수로 쓰면 가독성이 더 좋아집니다.</p>
<h2 id="마치며">마치며</h2>
<p>오늘은 화살표 함수와 일반함수의 차이점 그리고 왜 쓰는지에 대해 알아보았습니다. 화살표 함수는 짧고 직관적입니다 따라서 상황에 맞게 잘 쓰면 가독성을 정말 좋아지게 할 수 있습니다. 글 읽어주셔서 감사합니다. 피드백 많이 해주시면 감사하겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS 유용한 함수들]]></title>
            <link>https://velog.io/@mgang0_0/JS-%EC%9C%A0%EC%9A%A9%ED%95%9C-%ED%95%A8%EC%88%98%EB%93%A4</link>
            <guid>https://velog.io/@mgang0_0/JS-%EC%9C%A0%EC%9A%A9%ED%95%9C-%ED%95%A8%EC%88%98%EB%93%A4</guid>
            <pubDate>Mon, 08 Sep 2025 08:54:41 GMT</pubDate>
            <description><![CDATA[<h2 id="시작하며">시작하며</h2>
<p>오늘은 React를 배우며 아직 기본기가 제대로 안잡힌거 같은 JS복습 할겸 많이 쓰이는 JS 함수들에 대해서 알아보겠습니다</p>
<h2 id="함수란">함수란?</h2>
<p>함수는 특정 작업을 수행하도록 묶어 놓은 코드 블록입니다.
쉽게 말하면 필요할 때 불러 쓸 수 있는 코드 묶음 이라고 생각 하시면 됩니다.
함수는 코드 재사용, 가독성 향상, 유지보수를 위해 없어서는 안 되는 존재입니다.</p>
<p>다음은 기본적인 함수 예시입니다</p>
<pre><code class="language-js">// 함수 선언
function sayHello(name) {
  return `hello ${name}`;
}

//함수 호출
console.log(sayHello(&quot;function&quot;)); //  hello function</code></pre>
<p><code>function</code> 함수 선언
<code>name</code> 매개변수 -&gt; 호출할때 전달할 값
<code>return</code> 함수가 돌려주는 결과값</p>
<h2 id="함수-선언-방법">함수 선언 방법</h2>
<p>대표적으로 3가지 소개해 보겠습니다</p>
<h3 id="1-선언식-함수">1. 선언식 함수</h3>
<pre><code class="language-js">function add(a, b) {
  return a + b;
}</code></pre>
<ul>
<li>function 키워드로 생성</li>
<li>hoisting이 적용돼 어디서든 호출 가능
호이스팅이란 함수, 변수, 클래스 또는 임포트(import)의 선언문을 해당 범위의 맨 위로 끌어올리는 것처럼 보이는 현상입니다<h3 id="2-표현식-함수">2. 표현식 함수</h3>
<pre><code class="language-js">const add = function(a, b) {
return a + b;
};</code></pre>
</li>
<li>변수에 함수를 담는 방식</li>
<li>선언 이후에만 호출 가능</li>
</ul>
<h3 id="3-화살표-함수">3. 화살표 함수</h3>
<pre><code class="language-js">const add = (a, b) =&gt; a + b;</code></pre>
<p>-&gt; 이런식으로 짧고 간결하게 작성 가능 하다는 장점이 있습니다.</p>
<hr>

<h2 id="자주-쓰이는-함수">자주 쓰이는 함수</h2>
<h3 id="문자열-관련-함수">문자열 관련 함수</h3>
<h4 id="trim"><code>trim()</code></h4>
<p>문자열 양쪽끝에 공백을 제거</p>
<pre><code class="language-js">let str = &quot;   hello world   &quot;;
console.log(str.trim()); // 출력: &quot;hello world&quot;</code></pre>
<p>사용 에 : 로그인 입력 창에서 사용자가 실수로 공백을 넣었을때</p>
<br>

<h4 id="touppercasetolowercase"><code>toUpperCase()</code>/<code>toLowerCase()</code></h4>
<p>문자열을 모두 대문자/소문자로 변환</p>
<pre><code class="language-js">let word = &quot;JavaScript&quot;;
console.log(word.toUpperCase()); // 출력: &quot;JAVASCRIPT&quot;
console.log(word.toLowerCase()); // 출력: &quot;javascript&quot;</code></pre>
<p>사용 예 : 검색 기능에서 대소문자 구분없이 검색할 때</p>
<br>

<h4 id="includes"><code>includes</code></h4>
<p>문자열에 특정 값이 포함 돼 있는지 확인</p>
<pre><code class="language-js">let sentence = &quot;I love JavaScript!&quot;;
console.log(sentence.includes(&quot;love&quot;)); // true
console.log(sentence.includes(&quot;hate&quot;)); // false</code></pre>
<p>사용 예 : 키워드 검색, 비밀번호 검증(특정 문자 포함 여부)</p>
<br>

<h4 id="slicestart-end"><code>slice(start, end)</code></h4>
<p>문자열의 일부를 잘라서 반환</p>
<pre><code class="language-js">let text = &quot;Hello JavaScript&quot;;
console.log(text.slice(0, 5)); // 실행: &quot;Hello&quot;
console.log(text.slice(6));    // 실행: &quot;JavaScript&quot;</code></pre>
<p>사용 예: 문자열 일부만 보여주고 싶을 때 게시글 미리보기 기능</p>
<hr>

<h3 id="배열-관련-함수">배열 관련 함수</h3>
<h4 id="foreach"><code>forEach()</code></h4>
<p>배열의 각 요소를 순회 하며 실행</p>
<pre><code class="language-js">let arr = [1, 2, 3];
arr.forEach(num =&gt; console.log(num));
// 실행
// 1
// 2
// 3</code></pre>
<p>사용 예: 단순 반복작업 유용</p>
<h4 id="map"><code>map()</code></h4>
<p>배열의 각 요소를 변환하여 새로운 배열을 반환</p>
<pre><code class="language-js">let nums = [1, 2, 3];
let doubled = nums.map(n =&gt; n * 2);
console.log(doubled); // 실행: [2, 4, 6]</code></pre>
<p>사용 예: 데이터를 변환해서 새로운 리스트를 만들 때</p>
<h4 id="filter"><code>filter()</code></h4>
<p>조건에 맞는 요소만 걸러서 새로운 배열을 반환</p>
<pre><code class="language-js">let nums = [1, 2, 3, 4, 5];
let even = nums.filter(n =&gt; n % 2 === 0);
console.log(even); // 실행: [2, 4]</code></pre>
<h4 id="reduce"><code>reduce()</code></h4>
<p>배열을 순회하며 누적 값을 계산</p>
<pre><code class="language-js">let nums = [1, 2, 3, 4, 5];
let sum = nums.reduce((acc, cur) =&gt; acc + cur, 0);
console.log(sum); //실행: 15</code></pre>
<p>사용 예: 합계,평균,누적 계산 할 때</p>
<h4 id="find"><code>find()</code></h4>
<p>조건을 만족하는 첫번째 요소 반환</p>
<pre><code class="language-js">let users = [
  { id: 1, name: &quot;JS&quot; },
  { id: 2, name: &quot;함수&quot; }
];
console.log(users.find(user =&gt; user.id === 2));
// 실행: { id: 2, name: &quot;함수&quot; }</code></pre>
<p>사용 예: 특정 아이디, 특정 조건에 맞는 첫 데이터를 가져올 때</p>
<hr>

<h3 id="수학-관련-함수">수학 관련 함수</h3>
<h4 id="mathfloor-mathceil-mathround"><code>Math.floor(), Math.ceil(), Math.round()</code></h4>
<ul>
<li>floor(): 내림</li>
<li>ceil(): 올림</li>
<li>round(): 내림<pre><code class="language-js">console.log(Math.floor(4.9)); // 4
console.log(Math.ceil(4.1));  // 5
console.log(Math.round(4.5)); // 5</code></pre>
</li>
</ul>
<h4 id="mathrandom"><code>Math.random()</code></h4>
<p>0 이상 1 미만의 난수를 반환</p>
<pre><code class="language-js">console.log(Math.random()); // 예: 0.2743...
console.log(Math.floor(Math.random() * 10) + 1); // 1~10 사이 랜덤 숫자</code></pre>
<p>사용 예: 랜덤 추첨, 주사위 게임 등에 활용됩니다</p>
<h4 id="mathmax--mathmin"><code>Math.max() / Math.min()</code></h4>
<p>배열이나 여러 숫자 중 최대값/최솟값을 구합니다</p>
<pre><code class="language-js">console.log(Math.max(3, 10, 7)); // 실행: 10
console.log(Math.min(3, 10, 7)); // 실행: 3</code></pre>
<hr>

<h3 id="날짜-관련-함수">날짜 관련 함수</h3>
<h4 id="date">Date()</h4>
<ul>
<li><code>getFullYear()</code> : 연도 가져옴</li>
<li><code>getMonth()</code> : 달 가져옴</li>
<li><code>getDate()</code> : 일 가져옴</li>
<li><code>getDay()</code> : 요일 가져옴</li>
<li><code>toLocaleDateString()</code> : 몇년 몇월 며칠 가져옴<pre><code class="language-js">let now = new Date();
</code></pre>
</li>
</ul>
<p>console.log(now.getFullYear());  // 2025
console.log(now.getMonth() + 1); // 9 (월은 0부터 시작)
console.log(now.getDate());      // 6
console.log(now.getDay());       // 요일 (0=일요일 ~ 6=토요일)
console.log(now.toLocaleDateString()); // &quot;2025. 9. 6.&quot;</p>
<p>```
사용 예: 블로그 게시글 날짜, 예약시스템, 채팅 타임스탬프 등 활용</p>
<h2 id="마치며">마치며</h2>
<p>오늘은 함수 선언방법 그리고 어떤 종류가 있는지 간단하게 알아보았습니다.
이렇게 내장함수를 잘 활용하면 복잡한 반복문을 줄이고 코드 가독성을 크게 높일 수 있습니다.
여기에 적지 못한 수많은 함수가 있습니다 그건 제가 또 공부해서 점차 추가해보도록 하겠습니다.
부족한 점 있다면 피드백 많이 해주세요!!
글 읽어주셔서 감사합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Git Flow 너 누구임?]]></title>
            <link>https://velog.io/@mgang0_0/Git-Flow-%EB%84%88-%EB%88%84%EA%B5%AC%EC%9E%84</link>
            <guid>https://velog.io/@mgang0_0/Git-Flow-%EB%84%88-%EB%88%84%EA%B5%AC%EC%9E%84</guid>
            <pubDate>Tue, 02 Sep 2025 00:25:32 GMT</pubDate>
            <description><![CDATA[<h2 id="시작하며">시작하며</h2>
<p>안녕하세요 오늘은 깃 브랜치 전략 <strong>&#39;Git Flow&#39;</strong>에 대해 알아 보겠습니다. 
그전에 먼저 깃 브랜치 전략은 뭘까요? 바로 알아보도록 하겠습니다.</p>
<h2 id="git-branch-전략">Git Branch 전략?</h2>
<p>여러 개발자가 더 효율적이고 효과적으로 협업 하기위해 
git branch에 규칙을 정하고 
저장소를 잘 활용하기 위한 workflow를 정의하는 것 입니다. 
workflow란 특정 순서에 따라 발생하는 
반복적인 프로세스와 작업을 관리하는 시스템입니다.</p>
<p>그럼 왜 git branch 전략을 쓸까요?
여러 개발자가 협업할때 중요합니다.
각자 다른 기능을 담당하는 브랜치를 사용하여 작업하면,
개발 중인 기능이나 수정사항이 서로 독립적이게 되며 
영향을 주지 않고 동시에 진행될 수 있습니다.</p>
<h2 id="git-flow-전략">Git Flow 전략</h2>
<p>그럼 이제 git branch 전략에 가장 핵심 가장 많이 쓰는 
git flow 전략을 알아볼게요.</p>
<p>브랜치 종류는 5가지가 있습니다.</p>
<ul>
<li>master: 제품 출시 버전을 관리하는 메인 브랜치</li>
<li>develop: 다음 출시 버전을 위해 개발하는 브랜치</li>
<li>feature: 새로운 기능을 개발하는 브랜치</li>
<li>release: 다음 출시 버전을 준비하는 브랜치</li>
<li>hotfix: 출시된 제품의 버그를 고치기 위한 브랜치</li>
</ul>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/009fb56c-dfa2-4eec-8963-15d0d07b3a8b/image.png" alt=""></p>
<p>여러 사람이 함께 협업할때는 이러한 git flow 전략이 중요 합니다.
자세히 살펴보면 master branch가 사용자들이 쓰는 버전?main?이라고 생각 하시면 편합니다.
master에서 develop 브랜치를 파서 거기서 feature 브랜치를 팝니다.
그리고 feature에서 기능 개발을 하다 완성 되었으면 
develop으로 push후 1차 test를 합니다.
이상이없다면 바로 master로 merge해도 됩니다.
하지만 정말 큰 프로젝트라면 눈에 안보이는 작은 오류가 있을 수도 있습니다.
그렇다면 develop에서 release브랜치를 팝니다.
마지막으로 최종 test를 한뒤 안전하게 master로 push 하면 됩니다.
하지만 master로 merge 한 후에 충돌이나 버그가 났다면 
이때는 긴급하게 바로 고쳐야하기 때문에 
복잡한 develop feature release 과정을 거치지 않고 
바로 hotfix브랜치를 파서 고친후 master로 merge 합니다.</p>
<h1 id="마무리">마무리</h1>
<p>오늘은 개발자가 협업을 할때 꼭 알아야하는 git branch 전략 그중에서도 git flow에 대해 알아 보았습니다. 
git branch 전략에는 github flow, gitlab flow등 여러가지 전략이 있습니다.
이것은 제가 더 공부를 열심히 한 뒤 꼭 추가하고 또 협업할때 꼭 유용하 써보겠습니다. 
글 읽어주셔서 감사합니다.</p>
<p>출처 : <a href="https://devocean.sk.com/blog/techBoardDetail.do?ID=165571&amp;boardType=techBlog">Git Branch 전략 비교 - Git Flow vs GitHub Flow</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[돔(DOM)이란 무엇일까?]]></title>
            <link>https://velog.io/@mgang0_0/%EB%8F%94DOM%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C</link>
            <guid>https://velog.io/@mgang0_0/%EB%8F%94DOM%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C</guid>
            <pubDate>Mon, 25 Aug 2025 14:24:30 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/mgang0_0/post/4ea06bed-db0e-48b3-aa08-d117f260b588/image.png" alt=""></p>
<h2 id="시작하며">시작하며</h2>
<p>저는 오늘 DOM에 대한 개념을 또 VDOM은 뭔지 간략하게 소개 해보려고 합니다.
부족한 점이 많을 수도 있지만 그런점 피드백 많이 해주시면 감사하겠습니다.</p>
<h2 id="dom-이란">DOM 이란?</h2>
<p>문서 객체 모델 DOM은 *<em>HTML,XML 문서 구조를 객체로 표현한 것입니다.
*</em> DOM의 풀네임은 Document Object Model 이라고 합니다.</p>
<p>그럼 &#39;문서 구조&#39;는 무엇일까요?
HTML은 head body등 여러 태그가 문서구조를 이루고 있습니다. 
HTML 요소의 계층을 반영해서 만든 객체가 DOM입니다.
HTML 문서를 객체로 표현하면 JavaScript와 같은 스크립트 및 프로그래밍 언어가 웹페이지에 접근할 수 있습니다. 
즉 DOM은 HTML로 구성된페이지와 스크립트와 같은 프로그래밍 언어를 연결시켜 주는 역할을 합니다.</p>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/a0c7dd2a-7651-4d8b-ba87-9ae98c92a19e/image.png" alt=""></p>
<h2 id="dom-접근">DOM 접근</h2>
<p>각 언어마다 DOM에 접근하는 방법은 다 다릅니다. 
java script에서는 <code>document.getElementByld(&quot;#id&quot;)</code>매서드가 있습니다.
html 코드에서 id선택자를 사용해 불러오는 것입니다.</p>
<h4 id="예시코드">예시코드</h4>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;title&gt;Document&lt;/title&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;darkMode.css&quot; /&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;안녕하세요&lt;/h1&gt;
    &lt;ol&gt;
      &lt;li&gt;Item 1&lt;/li&gt;
      &lt;li&gt;Item 2&lt;/li&gt;
      &lt;li&gt;Item 3&lt;/li&gt;
    &lt;/ol&gt;
    &lt;h2&gt;자바스크립트 연습중&lt;/h2&gt;

    &lt;input type=&quot;button&quot; value=&quot;다크모드&quot; id=&quot;darkModeBtn&quot; /&gt;
    &lt;input type=&quot;button&quot; value=&quot;라이트모드&quot; id=&quot;lightModeBtn&quot; /&gt;
  &lt;/body&gt;
  &lt;script src=&quot;darkMode.js&quot;&gt;&lt;/script&gt;
&lt;/html&gt;</code></pre><hr>
<pre><code class="language-javascript">const darkModeBtn = document.getElementById(&quot;darkModeBtn&quot;);
const lightModeBtn = document.getElementById(&quot;lightModeBtn&quot;);
const body = document.querySelector(&quot;body&quot;);

darkModeBtn.addEventListener(&quot;click&quot;, function () {
  body.style.backgroundColor = &quot;black&quot;;
  body.style.color = &quot;white&quot;;
});

lightModeBtn.addEventListener(&quot;click&quot;, function () {
  body.style.backgroundColor = &quot;white&quot;;
  body.style.color = &quot;black&quot;;
});</code></pre>
<p>첫번째 코드는 html 이고 두번째는 js 입니다.
js,html 코드를 살펴 보시면 html 코드에 
<code>input type=&quot;button&quot;</code>의 id인 <code>darkModeBtn</code>을 <code>getElementById</code>로 
js에 가져와서 click 이벤트로 버튼이 클릭시 
바탕화면 폰트 컬러가 각각 다크 모드에 맞게 변하는 코드 입니다.</p>
<h2 id="dom의-계층-구조">DOM의 계층 구조</h2>
<p>DOM은 HTML 문서의 구조의 계층을 표현하는 객체로 
&#39;tree(트리)구조&#39;를 가집니다.
이때 트리구조란 노드들이 나무 가지처럼 연결된 자료구조입니다.
트리구조는 나무를 거꾸로 뒤집어 놓은 모양과 유사합니다.
<img src="https://velog.velcdn.com/images/mgang0_0/post/48f97ff4-0175-4124-91ba-ba257cb98c0d/image.png" alt="">
예시코드와 함께 설명을 해보겠습니다.</p>
<pre><code class="language-&lt;html&gt;">  &lt;body&gt;
    &lt;div&gt;
      &lt;h1&gt;Hello&lt;/h1&gt;
      &lt;p&gt;World&lt;/p&gt;
    &lt;/div&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/fcee3461-f56e-4a9e-bfc1-7dabfbc475e7/image.png" alt=""></p>
<p>예시 코드를 트리구조 처럼 표현 하면 다음과 같습니다.</p>
<ul>
<li>html은 document의 자식</li>
<li>body는 html의 자식</li>
<li>div는 body의 자식</li>
<li>h1과 p는 div의 자식, 서로 형제
이런식으로 구성 됩니다.</li>
</ul>
<h2 id="브라우저에서의-작동">브라우저에서의 작동</h2>
<p>브라우저는 HTML 코드를 해석해서 
요소들을 트리 형태로 구조화해 표현하는 문서(데이터)를 생성 합니다. 
브라우저는 DOM을 통해 화면에 웹 콘텐츠를 랜더링 합니다.
또한, JavaScript를 사용해 웹 화면에 콘텐츠를 추가, 수정, 삭제하고 
이벤트를 처리할 수 있도록 프로그래밍 인터페이스 제공을 합니다.
<img src="https://velog.velcdn.com/images/mgang0_0/post/68e9e442-3ef9-461c-8645-a683455b245a/image.png" alt=""></p>
<h2 id="vitural-domvdom-뭘까">Vitural DOM(VDOM) 뭘까??</h2>
<p><img src="https://velog.velcdn.com/images/mgang0_0/post/8ae23bb8-88db-4036-a09c-0e6aaec3efff/image.png" alt=""></p>
<p>가상 DOM(Virtual DOM)은 웹 개발에서 
UI 업데이트 성능을 향상시키기 위해 사용되는 기술입니다. 
실제 DOM의 복사본을 메모리에 유지하고 변경 사항이 있을 때마다 전체 DOM을 다시 렌더링하는 대신 
가상 DOM을 비교하여 변경된 부분만 
실제 DOM에 반영하는 방식으로 작동합니다
이 방식 덕분에 전체 DOM을 매번 새로 고치지 않고, 필요한 부분만 효율적으로 업데이트할 수 있습니다.</p>
<h2 id="virtural-dom은-왜-쓸까요">Virtural DOM은 왜 쓸까요?</h2>
<p>일반 DOM은 직접 수정하는게 느립니다
예를 들어 글자 하나 바꾸려고 해도 브라우저는 레이아웃 다시 계산하고 화면을 다시 그려야 해서 성능이 떨어질 수 있습니다</p>
<p>리액트는 이런 문제를 줄이려고 <strong>Virtual DOM (VDOM)</strong>이라는 방식을 씁니다.</p>
<h2 id="react에서-dom-동작">React에서 DOM 동작</h2>
<p>리액트 컴포넌트에서 데이터가 변하면 어떻게 될까요?
보통이라면 브라우저는 바로 DOM을 수정해야 하지만 
리액트는 그렇게 하지 않습니다.
리액트는 먼저 <strong>Virtual DOM(가상 DOM)</strong>이라는 걸 업데이트해요.
DOM을 바로 건드리지 않고 메모리 속에서 
가상의 DOM을 만들어놓고 거기에 변화를 적용하는 겁니다.
또한 리액트는 바뀐 부분만 DOM에 반영합니다.
글자 하나가 달라졌다면 그 글자만 고치고
버튼 색상만 변했다면 버튼 색상 부분만 수정합니다.
전체 화면을 다 새로 그리는 게 아니라 꼭 필요한 부분만 살짝 고치는 방식으로 동작하는 겁니다.</p>
<h2 id="마치며">마치며</h2>
<p>오늘은 프론트엔드개발자라면 한번쯤은 들어봤을 DOM 그리고 VDOM에 대해 알아보았습니다. 아직 부족한 점, 부실한 점 많지만 앞으로 많은 피드백 받으며 성장하는 개발자 되도록 하겠습니다.</p>
<p>출처 : <a href="https://docs.tosspayments.com/resources/glossary/dom">https://docs.tosspayments.com/resources/glossary/dom</a>
출처 : <a href="https://www.youtube.com/watch?v=zyz1eJJjsNE">유노코딩 DOM이란 뭘까요? 초 짧은 설명..!</a></p>
]]></description>
        </item>
    </channel>
</rss>