<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>hogu__giriboy.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Thu, 14 May 2026 05:51:52 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>hogu__giriboy.log</title>
            <url>https://velog.velcdn.com/images/hogu__giriboy/profile/60156fb4-7b41-4db4-a57d-a5a1c350c754/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. hogu__giriboy.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/hogu__giriboy" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[인증의 진화, 그리고 정착]]></title>
            <link>https://velog.io/@hogu__giriboy/%EC%9D%B8%EC%A6%9D%EC%9D%98-%EC%A7%84%ED%99%94-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%A0%95%EC%B0%A9</link>
            <guid>https://velog.io/@hogu__giriboy/%EC%9D%B8%EC%A6%9D%EC%9D%98-%EC%A7%84%ED%99%94-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%A0%95%EC%B0%A9</guid>
            <pubDate>Thu, 14 May 2026 05:51:52 GMT</pubDate>
            <description><![CDATA[<p>로그인 기능은 겉으로 보면 단순해 보인다.</p>
<p>입력값을 받고, 기존 데이터와 동일할 경우
로그인 성공 처리를 해주면 끝나는 것처럼 보인다.</p>
<p>그런데 여기서 더 깊게 생각해보면 미처 생각하지 못한 부분이 생긴다.</p>
<blockquote>
<p>&quot;로그인 성공 이후에는 어떻게 로그인 상태를 유지시키지?&quot;</p>
</blockquote>
<p>로그인 요청을 계속 보내는 건 불친절한 기능이다.
한 번의 요청으로도 서비스는 계속 나를
로그인한 사용자로 알아봐야 한다.</p>
<p>그럼 서버가 어떻게 사용자를 기억하게 만들어야 할까?</p>
<p>웹의 기본 통신 방식인 HTTP는
기본적으로 이전 요청을 기억하지 못한다.</p>
<p>즉, 요청 하나가 끝나면
다음 요청은 완전히 새로운 요청처럼 처리된다.</p>
<p>그렇다면 로그인 상태를 유지하기 위해서는
사용자를 기억하기 위한 별도의 방법이 필요하다.</p>
<p>그리고 이 문제를 해결하기 위해 등장한 대표적인 방식이
바로 세션(Session)과 토큰(Token) 기반 인증이다.</p>
<hr>
<h2 id="1-원래-상태를-기억-못하는-http">1. 원래 상태를 기억 못하는 HTTP</h2>
<p>로그인 상태를 유지하는 방식을 이해하려면
우선 HTTP의 기본 구조부터 알아야 한다.</p>
<p>웹에서 클라이언트와 서버는
HTTP 요청(Request)와 응답(Response)을 주고받는다.</p>
<p>브라우저가 서버에게 요청을 보내면,
서버는 그에 맞는 응답을 반환한다.</p>
<p>그런데 여기서 중요한 특징이 하나 있다.</p>
<p>HTTP는 기본적으로 상태(State)를 기억하지 않는다.</p>
<p>즉, 요청 하나와 응답 하나가 끝나면 연결은 죵료되고,
다음 요청이 들어왔을 때 서버는
그 요청이 이전 요청과 같은 사용자의 요청인지 알 수 없다.</p>
<p>예를 들어</p>
<ol>
<li>사용자가 로그인 요청을 보낸다.</li>
<li>서버가 로그인 성공 응답을 보낸다.</li>
<li>이후 사용자가 다시 API 요청을 보낸다.</li>
</ol>
<p>사용자 입장에서는
&quot;아까 로그인했으니까 당연히 로그인된 상태겠지&quot;라고 생각할 수 있지만,
서버 입장에서는 아니라는 말이다.</p>
<p>새로운 요청이 들어왔을 뿐이고,
그 요청이 이전에 로그인했던 사용자의 요청인지
자동으로 알 수 있는 방법이 없다.</p>
<p>즉, HTTP만으로는
로그인 상태를 유지할 수 없는 구조라는 뜻이다.</p>
<p>이런 특징을 상태(State)를 저장하지 않는다는 의미로 Stateless라고 부른다.</p>
<p>그래서 웹 서비스는
사용자를 기억하기 위한 별도의 방법이 필요했고,</p>
<p>그 과정에서 등장한 대표적인 방식이
세션(Session)과 토큰(Token) 기반 인증이다.</p>
<hr>
<h2 id="2-를-해결하고자-등장한-세션-기반-인증">2. 를 해결하고자 등장한 세션 기반 인증</h2>
<p>앞서 말했듯, HTTP는 기본적으로 상태를 기억하지 못한다.</p>
<p>즉, 로그인 요청이 끝난 이후에는
서버가 사용자를 계속 기억할 방법이 없다.</p>
<p>그래서 등장한 방식이
바로 세션(Session) 기반 인증이다.</p>
<p>세션 방식의 핵심은 단순하다.</p>
<blockquote>
<p>서버가 직접 사용자의 로그인 상태를 기억하는 것</p>
</blockquote>
<p>로그인 요청이 성공하면
서버 내부에 사용자의 로그인 정보를 저장한다.</p>
<p>예를 들어</p>
<ul>
<li>어떤 사용자인지</li>
<li>언제 로그인했는지</li>
<li>어떤 권한을 가지고 있는지</li>
</ul>
<p>같은 정보들을 서버가 보관하게 된다.</p>
<p>그리고 이때 서버는
해당 사용자를 구분하기 위한 고유한 ID도 함께 만든다.</p>
<p>이 ID를 세션 ID(Session ID)라고 한다.</p>
<p>그런데 문제는 여기서 끝나지 않는다.</p>
<p>서버는 세션 정보를 저장했지만,
다음 요청에서 어떤 사용자가 어떤 세션의 주인인지 다시 알아야 한다.</p>
<p>그래서 브라우저가 세션 ID를 가지고 다니게 된다.</p>
<p>이때 사용되는 것이 쿠키(Cookie)다.</p>
<p>서버는 로그인 성공 이후
세션 ID를 쿠키에 담아 브라우저로 전달하고,
브라우저는 이후 요청마다 해당 쿠키를 함께 전송한다.</p>
<p>그러면 서버는</p>
<ol>
<li>전달받은 세션 ID를 확인하고</li>
<li>저장된 세션 정보와 비교한 뒤</li>
<li>로그인된 사용자임을 판단하게 된다.</li>
</ol>
<p>즉 세션은 서버에 저장되고 쿠키는 세션 ID를 전달하는 역할을 한다.</p>
<p>쿠키와 세션은 같은 개념이 아니라,
쿠키는 단순한 저장 공간이고,
세션은 서버가 사용자를 기억하기 위한 인증 방식에 가깝다.</p>
<p>이 부분이 핵심이자, 많이 헷갈리는 부분이라고 한다.</p>
<p>이렇게 세션 기반 인증은
HTTP가 상태를 기억하지 못한다는 문제를 해결하기 시작했다.</p>
<hr>
<h2 id="3-의-점차-다가오는-한계">3. 의 점차 다가오는 한계</h2>
<p>세션 기반 인증은
HTTP가 상태를 기억하지 못한다는 문제를 해결해줬다.</p>
<p>하지만 서비스 규모가 커지기 시작하면서
세션 방식도 점점 한계를 드러내기 시작한다.</p>
<p>가장 큰 이유는 세션 정보가 서버에 저장된다는 점이었는데,
사용자가 늘어날수록 서버가 저장해야 하는 세션 데이터도 함께 증가하게 된다.</p>
<p>즉, 로그인한 사용자가 많아질수록
서버는 더 많은 상태를 직접 관리해야 했다.</p>
<p>처음에는 큰 문제가 아니었지만
서비스 규모가 커지고 트래픽이 많아지면서
상황이 많이 달라지기 시작했다.
특히 서버를 여러 대 운영하기 시작하면 문제가 더 복잡해진다.</p>
<p>예를 들어 A 서버에서 로그인했는데,
다음 요청은 B 서버로 들어가는 상황이 생길 수도 있었다.
그런데 세션 정보가 A 서버에만 저장되어 있다면,
B 서버는 해당 사용자가 로그인한 상태인지 알 수 없다.</p>
<p>결국 여러 서버가 같은 세션 정보를 공유해야 하는 문제가 생긴다.</p>
<p>그래서 세션 저장소를 따로 두거나
Redis 같은 외부 저장소를 사용하거나
세션 클러스터링을 구성하는 방식 등이 등장하게 된다.</p>
<p>즉, 세션 방식을 통해 해결된 인증 자체보다
그 세션들을 관리하기 위한 추가적인 구조가 필요해지기 시작한 것이다.</p>
<p>그리고 웹 환경 자체도 점점 변하기 시작했다.</p>
<p>과거에는 서버가 화면까지 함께 렌더링하는 구조가 많았다면,
점점 프론트엔드와 백엔드가 분리되기 시작했고,</p>
<p>SPA(Single Page Application)나
모바일 앱 환경도 늘어나기 시작했다.</p>
<p>이런 환경에서는 서버가 상태를 직접 기억하는 방식보다,
클라이언트가 직접 인증 정보를 들고 다니는 방식이
더 유리해지기 시작했다.</p>
<p>그래서 이런 흐름 속에서 등장한 방식이
바로 토큰(Token) 기반 인증이다.</p>
<hr>
<h2 id="4-를-해결하고자-등장한-토큰-기반-인증">4. 를 해결하고자 등장한 토큰 기반 인증</h2>
<p>세션 방식은 서비스 규모가 커지고, 서버가 여러 대로 분산되고, 프론트와 백엔드가 분리되기 시작하면서
점점 서버가 상태를 직접 저장하는 방식에 부담이 생기기 시작했다.</p>
<p>그래서 등장한 방식이 바로 토큰(Token) 기반 인증이다.</p>
<p>토큰 방식의 핵심은 세션과 반대로,
세션은 서버가 사용자를 기억했다면,
토큰 방식은 클라이언트가 인증 정보를 직접 들고 다닌다.</p>
<p>로그인 요청이 들어오면 서버는 먼저 사용자의 정보를 확인한다.
그리고 로그인이 성공하면 사용자를 증명할 수 있는 토큰(Token)을 발급한다.</p>
<p>브라우저는 이 토큰을 저장하고, 이후 요청마다 함께 전달한다.</p>
<p>대표적으로 Authorization 헤더, Bearer Token 방식 등을 사용한다.</p>
<p>예를 들어 이런 형태다.</p>
<pre><code class="language-http">Authorization: Bearer eyJhbGciOi...</code></pre>
<p>그러면 서버는 요청이 들어올 때마다 토큰이 유효한지 검증하고,
검증이 완료되면 해당 사용자가 로그인된 사용자라고 판단하게 된다.</p>
<p>즉, 세션 방식처럼 서버가 로그인 상태를 직접 저장하지 않아도 되는 구조다.</p>
<p>특히 서버 입장에서는 세션 저장소를 계속 관리할 필요가 줄어들기 때문에
서버 확장이나 프론트/백엔드 분리 구조에서 더 유리해지기 시작했다.</p>
<p>그리고 이런 흐름 속에서
대표적인 토큰 기반 인증 방식으로 떠오른 것이 JWT(JSON Web Token)다.</p>
<p>JWT는 사용자 정보와 만료 시간 등을 포함한 데이터를
하나의 토큰 형태로 만들어 전달하는 방식이다.</p>
<p>덕분에 서버는 별도의 세션 저장소 없이도
토큰 자체를 검증하는 것만으로도 사용자를 확인할 수 있게 되었다.</p>
<p>하지만 토큰 방식 역시 완벽한 방식은 아니었고,
편리한 만큼 새로운 보안 문제와 관리 문제도 함께 가져오게 된다.</p>
<hr>
<h2 id="5-의-점차-보이는-단점">5. 의 점차 보이는 단점</h2>
<p>토큰 기반 인증은 세션 방식의 한계를 해결한 건 사실이다.
하지만 이것 역시도 시간이 지나면서 여러 단점이 드러나기 시작했다.</p>
<p>가장 대표적인 문제는 토큰이 탈취되었을 때의 위험성이다.</p>
<p>세션 방식은 서버가 상태를 직접 관리하기 때문에
특정 세션을 서버에서 강제로 만료시키는 것이 비교적 쉽다.</p>
<p>하지만 JWT는 클라이언트가 토큰 자체를 들고 다니는 구조다.</p>
<p>즉, 토큰이 유효한 동안에는 서버 입장에서 해당 사용자를 정상 사용자로 판단하게 된다.</p>
<p>만약 토큰이 외부에 노출되거나 탈취된다면,
다른 사용자가 그대로 인증된 사용자처럼 동작할 수도 있다는 뜻이다.</p>
<p>그리고 JWT는 흔히 오해하는 것처럼 데이터를 암호화하는 방식이 아니다.</p>
<p>JWT의 Payload는 Base64 방식으로 인코딩되어 있을 뿐이며,
토큰 자체를 열어보는 것은 어렵지 않다.</p>
<p>이는 곧 민강한 정보를 토큰 내부에 그대로 담는 것은 위험할 수 도 있다는 문제로 직결된다.</p>
<p>또 다른 문제는 로그아웃 처리다.</p>
<p>세션 방식은 서버에서 세션을 제거하면 즉시 로그아웃 상태로 만들 수 있다.
하지만 JWT는 이미 클라이언트에게 발급된 토큰이다.</p>
<p>서버가 토큰을 강제로 회수하기 어렵기 때문에,
만료 시간이 끝나기 전까지는 계속 사용할 수 있는 문제가 생긴다.</p>
<p>그래서 등장한 개념이 바로 Access Token과 Refresh Token 구조다.</p>
<p>Access Token은 짧은 시간만 사용하고, Refresh Token으로 새로운 토큰을 재발급받는 방식이다.</p>
<p>즉, 토큰 기반 인증은 세션 방식의 한계를 해결하기 위해 등장했지만,
그 과정에서 또 다른 보안 문제와 관리 문제를 함께 안게 된 것이다.</p>
<p>결국 인증 방식은 &quot;무조건 더 좋은 방식&quot;이 등장하는 흐름이라기보다,
환경 변화에 따라 장단점이 다른 방식들이 계속 발전해온 과정에 가깝다고 볼 수 있다.</p>
<hr>
<h2 id="6-세션과-토큰의-차이">6. 세션과 토큰의 차이</h2>
<p>세션과 토큰은 결국엔 둘 다
HTTP가 상태를 기억하지 못한다는 문제를 해결하기 위해 등장한 방식이다.</p>
<p>세션의 어느 문제가 있고, 토큰에 어느 문제가 있고,
결국 인증의 역할을 똑똑히 하고 있다.</p>
<p>하지만 분명한 차이점이 있다.
먼저 사용자를 기억하는 방식에서부터 차이가 있다.</p>
<p>세션 방식은 서버가 사용자의 로그인 상태를 직접 저장한다.
브라우저는 세션 ID만 전달하고, 실제 로그인 정보는 서버 내부에서 관리된다.</p>
<p>반면 토큰 방식은 클라이언트가 인증 정보를 직접 들고 다닌다.
서버는 별도의 로그인 상태를 저장하기보다, 전달받은 토큰이 유효한지만 검증한다.</p>
<table>
<thead>
<tr>
<th>세션 기반 인증</th>
<th>토큰 기반 인증</th>
</tr>
</thead>
<tbody><tr>
<td>서버가 상태 저장</td>
<td>클라이언트가 토큰 저장</td>
</tr>
<tr>
<td>서버 메모리/저장소 필요</td>
<td>Stateless 구조에 유리</td>
</tr>
<tr>
<td>강제 로그아웃 처리 쉬움</td>
<td>토큰 만료 전까지 유지 가능</td>
</tr>
<tr>
<td>서버 확장 시 관리 필요</td>
<td>분산 환경에 유리</td>
</tr>
<tr>
<td>전통적인 웹 구조에 강함</td>
<td>SPA / 모바일 환경에 강함</td>
</tr>
</tbody></table>
<p>이렇게 각각 장단점이 다르기 때문에 서비스 구조에 따라 더 잘 어울리는 환경도 달라진다.</p>
<p>세션 기반 인증은 아래와 같은 서비스에서 여전히 잘 쓰이고 있다.</p>
<ul>
<li>전통적인 서버 렌더링 기반 웹 서비스</li>
<li>관리자 페이지</li>
<li>금융 서비스</li>
<li>보안이 중요한 내부 시스템</li>
</ul>
<p>그런 전통적인 서비스에서 쓰이는 반면 토큰 기반 인증은 비교적 최신 서비스에 쓰인다.</p>
<ul>
<li>SPA(Single Page Application)</li>
<li>모바일 앱</li>
<li>프론트엔드 / 백엔드 분리 구조</li>
<li>MSA(Micro Service Architecture)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[여러가지 색깔의 DataBase]]></title>
            <link>https://velog.io/@hogu__giriboy/%EC%97%AC%EB%9F%AC%EA%B0%80%EC%A7%80-%EC%83%89%EA%B9%94%EC%9D%98-DataBase</link>
            <guid>https://velog.io/@hogu__giriboy/%EC%97%AC%EB%9F%AC%EA%B0%80%EC%A7%80-%EC%83%89%EA%B9%94%EC%9D%98-DataBase</guid>
            <pubDate>Thu, 07 May 2026 09:50:25 GMT</pubDate>
            <description><![CDATA[<p>이번 초급 프로젝트에서는 PostgreSQL 을 채택해서 진행했다.</p>
<p>그런데 프로젝트와 별개로 진행하던 스프린트 미션에서는 MongoDB 를 사용하고 있었다.</p>
<p>멘토님은 스프린트 미션 코드리뷰도 함께 진행해주시고, 초급 프로젝트 방향까지 같이 봐주시는데,
그러다 보니 자연스럽게 “초급 프로젝트도 MongoDB를 쓰고 있겠구나” 하고 잠깐 오해하신 상황이 있었다.</p>
<p>그 작은 해프닝을 계기로 이런 궁금증이 생겼다.</p>
<ul>
<li>MongoDB와 PostgreSQL은 정확히 뭐가 다를까?</li>
<li>어떤 상황에서 서로 다른 DB를 선택하게 될까?</li>
<li>그리고 DB를 공부한다면, 결국 무엇을 깊게 파야 할까?</li>
</ul>
<p>팀원분도 비슷한 질문을 멘토님께 드렸고, 돌아온 답변은 꽤 인상적이었다.</p>
<blockquote>
<p>현업에서는 하나만 정답처럼 쓰이지 않는다.
서비스 성격과 상황에 따라 정말 다양한 DB가 사용된다.</p>
</blockquote>
<p>그 말을 듣고 나니 단순히 “MongoDB vs PostgreSQL” 비교를 넘어서,
세상에는 어떤 종류의 데이터베이스가 있고, 각각 어디에 쓰이는지 정리해보고 싶어졌다.</p>
<p>그래서 이번 글에서는 다양한 데이터베이스의 종류와 특징, 그리고 어떤 상황에서 선택되는지를 중심으로 정리해보려고 한다.</p>
<hr>
<h2 id="1-다양한-종류의-데이터베이스-왜">1. 다양한 종류의 데이터베이스, 왜?</h2>
<p>처음 데이터베이스를 접했을 때는
그냥 “데이터를 저장하는 공간” 정도로만 생각했었다.</p>
<p>그래서 왜 스프린트 미션에서는 MongoDB 를 사용하고,
초급 프로젝트에서는 PostgreSQL 를 사용하는지 크게 의문을 가지지도 않았다.</p>
<p>그런데 이번 기회에 조금 더 찾아보면서
데이터베이스는 생각보다 훨씬 다양한 종류가 존재한다는 걸 알게 됐다.</p>
<h3 id="모든-데이터가-같은-모습일까">모든 데이터가 같은 모습일까?</h3>
<p>아니다.
이게 어쩌면 이번 섹션에 핵심으로 작용된다.</p>
<p>예를 들어 데이터를 보면</p>
<ul>
<li>사용자 정보처럼 구조가 명확한 데이터</li>
<li>게시글처럼 자유롭게 형태가 바뀌는 데이터</li>
<li>친구 관계처럼 연결 구조가 중요한 데이터</li>
<li>로그처럼 계속해서 대량으로 쌓이는 데이터</li>
</ul>
<p>등이 존재한다.</p>
<p>즉, 데이터마다 다루는 <strong>방식</strong>과 <strong>성격</strong> 자체가 다르다는 뜻이다.</p>
<h3 id="하나의-방식에서-생기는-한계">하나의 방식에서 생기는 한계</h3>
<p>이런 데이터를 모두 하나의 방식으로 처리하려고 하면 분명 문제가 생긴다.</p>
<p>어떤 경우에는 구조가 지나치게 엄격해지고,
어떤 경우에는 성능이 떨어지며,
어떤 경우에는 데이터 관리 자체가 복잡해진다.</p>
<p>결국 데이터의 성격에 따라
더 적합한 저장 방식이 필요해지기 시작한 것이다.</p>
<p>그래서 등장한 것이 바로
<strong>각각 다른 방식</strong>으로 <strong>데이터를 저장</strong>하는 다양한 데이터베이스들이다.</p>
<h3 id="데이터베이스를-나누는-대표적인-기준">데이터베이스를 나누는 대표적인 기준</h3>
<p>정답은 바로 위에서 다뤘다.</p>
<blockquote>
<p><strong>데이터를 어떤 형태로 저장하느냐</strong></p>
</blockquote>
<p>이 기준에 따라 데이터베이스는</p>
<ul>
<li>테이블 형태로 정리해서 저장하는 방식</li>
<li>JSON 문서 형태로 묶어서 저장하는 방식</li>
<li>key-value 형태로 빠르게 저장하는 방식</li>
</ul>
<p>등으로 나뉘게 된다.</p>
<p>그리고 이 저장 방식의 차이가
각 데이터베이스의 특징과 사용 목적을 결정하게 된다.</p>
<hr>
<h2 id="2-데이터를-저장하는-방식">2. 데이터를 저장하는 방식</h2>
<p>앞에서 말했듯이
데이터베이스를 이해하는 가장 큰 기준 중 하나는</p>
<blockquote>
<p>데이터를 어떤 방식으로 저장하느냐</p>
</blockquote>
<p>이다.</p>
<p>이 기준으로 보면
데이터베이스 종류들이 왜 나뉘는지 조금 더 명확하게 보이기 시작한다.</p>
<h3 id="데이터를-정리해서-저장하기">데이터를 &quot;정리해서&quot; 저장하기</h3>
<p>가장 익숙한 방식은
데이터를 정해진 구조 안에서 관리하는 방식이다.</p>
<p>데이터를 <strong>정해진 구조 안에 넣어서 관리하는 방식</strong>이다.</p>
<p>이 방식이 바로 관계형 데이터베이스(RDBMS)이며,
대표적으로 이번 초급 프로젝트에서 채택했던 PostgreSQL, MySQL이 있다.</p>
<h4 id="선-구조-후-데이터">선 구조 후 데이터</h4>
<p>관계형 데이터베이스는 흐름이 비교적 명확하다.</p>
<ol>
<li>테이블 구조를 먼저 정의</li>
<li>정의된 구조에 맞게 데이터 저장</li>
</ol>
<p>예를 들어</p>
<ul>
<li>사용자 테이블</li>
<li>습관 테이블</li>
<li>기록 테이블</li>
</ul>
<p>처럼 데이터를 역할별로 나누고,
각 테이블을 관계로 연결해서 관리한다.</p>
<h4 id="장점">장점</h4>
<p>관계형 데이터베이스의 가장 큰 특징은
데이터의 <strong>정확성</strong>과 <strong>일관성</strong>을 유지하기 쉽다는 점이다.</p>
<p>예를 들어</p>
<ul>
<li>존재하지 않는 사용자에 대한 기록 생성</li>
<li>잘못된 타입의 데이터 입력</li>
<li>관계가 맞지 않는 데이터 저장</li>
</ul>
<p>같은 문제들을 데이터베이스 단계에서 방지할 수 있다.</p>
<p>즉, 데이터 구조를 엄격하게 관리하는 대신
안정성을 높이는 방식이라고 볼 수 있다.</p>
<h4 id="단점">단점</h4>
<p>다만 구조를 엄격하게 관리하는 만큼
설계와 관리가 상대적으로 복잡해진다.</p>
<p>테이블을 나누고,
관계를 연결하고,
여러 데이터를 함께 고려해야 하기 때문이다.</p>
<p>그래서 데이터를 저장하거나 수정하는 과정이
비교적 무겁게 느껴질 수 있다.</p>
<h3 id="데이터를-묶어서-저장하기">데이터를 &quot;묶어서&quot; 저장하기</h3>
<p>반대로 데이터를 <strong>하나의 문서처럼 저장하는 방식</strong>도 존재한다.</p>
<p>이 방식이 바로 문서형 데이터베이스이며,
대표적으로 MongoDB 가 있다.</p>
<h4 id="선-데이터-후-구조">선 데이터 후 구조</h4>
<p>문서형 데이터베이스는 관계형 데이터베이스보다 훨씬 유연하다.</p>
<p>예를 들면 아래처럼 JSON 형태에 가까운 구조 자체를 그대로 저장할 수 있다.</p>
<pre><code class="language-json">{
  &quot;user&quot;: &quot;명곤&quot;,
  &quot;habits&quot;: [{ &quot;name&quot;: &quot;공부&quot;, &quot;done&quot;: false }]
}</code></pre>
<p>하나의 문서 안에 필요한 데이터를 함께 넣어서 관리하는 방식이다.</p>
<h4 id="장점-1">장점</h4>
<p>문서형 데이터베이스는
구조를 엄격하게 미리 정의하지 않아도 된다는 특징이 있다.</p>
<p>그래서</p>
<ul>
<li>빠르게 개발 가능</li>
<li>구조 변경에 유연함</li>
<li>데이터를 한 번에 저장 가능</li>
</ul>
<p>같은 장점을 가진다.</p>
<p>특히 데이터 구조가 자주 바뀌거나
빠른 개발 속도가 중요한 서비스에서 자주 사용된다.</p>
<h4 id="단점-1">단점</h4>
<p>다만 유연한 만큼
데이터 관리 책임이 개발자 쪽으로 많이 넘어온다.</p>
<p>관계형 데이터베이스처럼
강하게 관계를 검증하지 않기 때문에</p>
<ul>
<li>데이터 중복</li>
<li>일관성 문제</li>
<li>복잡한 관계 관리 어려움</li>
</ul>
<p>같은 문제가 발생하기 쉽다.</p>
<p>특히 규모가 커질수록
같은 데이터가 여러 곳에 퍼지면서
어떤 값이 실제 기준 데이터인지 헷갈리는 상황도 생길 수 있다.</p>
<h3 id="sql-vs-mongodb">SQL vs MongoDB</h3>
<p>MongoDB와 SQL 계열 데이터베이스를 비교할 때 자주 나오는 말이 있다.</p>
<blockquote>
<p>MongoDB는 쓰기가 빠르고, SQL은 느리다.</p>
</blockquote>
<p>하지만 이 표현은 조금 단순화된 이야기라고 한다.</p>
<p>관계형 데이터베이스는 데이터를 저장할 때</p>
<ul>
<li>관계 확인</li>
<li>데이터 검증</li>
<li>제약 조건 검사</li>
</ul>
<p>등을 함께 수행한다.</p>
<p>반면 문서형 데이터베이스는
상대적으로 자유로운 구조 안에서 데이터를 저장한다.</p>
<p>그래서 체감상 문서형 데이터베이스가 더 가볍고 빠르게 느껴질 수 있는 것이다.</p>
<blockquote>
<p>MongoDB가 무조건 더 뛰어난 성능을 가진다기보다는
저장 방식 자체가 다르기 때문에 체감 차이가 발생하는 것에 가깝다.</p>
</blockquote>
<p>결국 중요한 건
어떤 데이터와 어떤 서비스 구조에 더 잘 맞느냐이다.</p>
<hr>
<h2 id="3-속도를-위해-태어난-데이터베이스">3. 속도를 위해 태어난 데이터베이스</h2>
<p>앞에서 본 관계형 데이터베이스와 문서형 데이터베이스는
결국 데이터를 저장하고 관리하는 것이 핵심 목적이었다.</p>
<ul>
<li>관계형 데이터베이스 → 구조를 나눠서 저장</li>
<li>문서형 데이터베이스 → 데이터를 묶어서 저장</li>
</ul>
<p>그런데 서비스 규모가 커지면
단순히 “저장”만 잘한다고 끝나지 않는다.</p>
<p>예를 들어 서비스에서는 아래 같은 요청이 정말 자주 발생한다.</p>
<ul>
<li>로그인 상태 확인</li>
<li>오늘의 습관 조회</li>
<li>토글 상태 확인</li>
<li>자주 조회되는 데이터 불러오기</li>
</ul>
<p>이런 요청마다 매번 메인 데이터베이스에 접근하게 되면
점점 성능 부담이 커지기 시작한다.</p>
<p>그래서 등장한 방식이 바로</p>
<blockquote>
<p>데이터를 엄청 빠르게 꺼내는 데 집중한 데이터베이스</p>
</blockquote>
<p>로, 대표적으로 Redis가 있다.</p>
<h3 id="key-value-방식">Key-Value 방식</h3>
<p>이 방식은 구조 자체가 굉장히 단순하다.</p>
<pre><code>key → value</code></pre><p>예를 들면</p>
<ul>
<li>user:1 → 명곤</li>
<li>habit:3 → false</li>
</ul>
<p>처럼 key 하나에 value 하나를 연결해서 저장한다.</p>
<p>구조가 극단적으로 단순한 이유는 하나다.</p>
<blockquote>
<p>오직 <strong>속도</strong>를 위해서다.</p>
</blockquote>
<h3 id="어떻게-이런-속도가">어떻게 이런 속도가?</h3>
<p>핵심은 크게 두 가지다.</p>
<h4 id="1-메모리-기잔-저장">1. 메모리 기잔 저장</h4>
<p>일반적인 관계형 데이터베이스는
주로 디스크 기반으로 데이터를 저장한다.</p>
<p>반면 Redis는 데이터를 메모리(RAM)에 저장한다.</p>
<ul>
<li>디스크 → 상대적으로 느림</li>
<li>메모리 → 매우 빠름</li>
</ul>
<p>즉, 물리적인 접근 속도 자체에서 차이가 발생한다.</p>
<h4 id="2-구조-자체가-단순함">2. 구조 자체가 단순함</h4>
<p>Redis는 복잡한 관계를 거의 고려하지 않는다.</p>
<ul>
<li>조인 없음</li>
<li>관계 관리 없음</li>
<li>복잡한 쿼리 최소화</li>
</ul>
<p>그냥 key 하나로 바로 데이터를 찾는다.
“빠르게 찾기”에만 집중한 구조인 것인데, 어떻게 느릴 수 있겠는가 ...</p>
<h3 id="빠른-속도가-필요한-사용처">빠른 속도가 필요한 사용처</h3>
<p>중요한 건 Redis가 보통 <strong>메인 데이터베이스</strong> 역할을 하지는 않는다는 점이다.</p>
<p>대신 아래 같은 곳에서 많이 사용된다.</p>
<ul>
<li>로그인 세션 저장</li>
<li>캐시(Cache) 저장</li>
<li>조회 결과 임시 저장</li>
<li>실시간 데이터 처리</li>
</ul>
<p>예를 들어 오늘의 습관 조회 결과를 Redis에 저장해두면,</p>
<ol>
<li>첫 요청 → 메인 DB 조회</li>
<li>조회 결과를 Redis에 저장</li>
<li>다음 요청 → Redis에서 바로 반환</li>
</ol>
<p>처럼 동작할 수 있다.</p>
<p>즉, 매번 데이터베이스를 조회하지 않아도 되기 때문에
서비스 속도가 훨씬 빨라질 수 있다.</p>
<p>실제로 서비스 고도화 과정에서
캐시 전략으로 자주 고려되는 방식이라고 한다.</p>
<h3 id="단점-2">단점</h3>
<p>물론 빠르다고 해서 모든 상황에 적합한 건 아니다.</p>
<p>Redis 같은 Key-Value 방식은</p>
<ul>
<li>데이터 유실 가능성</li>
<li>복잡한 관계 표현 어려움</li>
<li>데이터 일관성 관리 한계</li>
</ul>
<p>같은 단점이 존재한다.</p>
<p>그래서 보통은</p>
<blockquote>
<p>메인 데이터베이스를 보조하는 역할</p>
</blockquote>
<p>로 많이 사용된다.</p>
<hr>
<h2 id="4-관계를-저장하는-데이터베이스">4. 관계를 저장하는 데이터베이스</h2>
<p>앞에서 다룬 관계형 데이터베이스는
테이블을 나누고, 관계를 연결한 뒤 필요한 순간에 JOIN으로 데이터를 가져오는 방식으로,
<strong>관계를 간접적으로 표현하는 구조</strong>에 가깝다.</p>
<p>그런데 세상에는 관계 자체가 핵심인 데이터들도 존재한다.</p>
<p>예를 들면</p>
<ul>
<li>친구의 친구 추천</li>
<li>팔로우 관계</li>
<li>추천 시스템</li>
<li>네트워크 분석</li>
</ul>
<p>같은 경우들이다.</p>
<p>이런 데이터들은 연결이 계속 늘어나고,
관계 구조도 점점 복잡해진다.</p>
<h3 id="관계형-데이터베이스의-한계">관계형 데이터베이스의 한계</h3>
<p>관계형 데이터베이스에서도 이런 구조를 구현할 수는 있다.</p>
<p>다만 관계가 많아질수록</p>
<ul>
<li>테이블 수 증가</li>
<li>JOIN 증가</li>
<li>쿼리 복잡도 증가</li>
</ul>
<p>같은 문제가 발생하기 시작한다.</p>
<p>특히 “연결을 따라가며 탐색”하는 작업이 많아질수록
성능과 관리 복잡도가 함께 커질 수 있다.</p>
<p>그래서 등장한 방식이 바로</p>
<blockquote>
<p>관계 자체를 중심으로 저장하는 데이터베이스</p>
</blockquote>
<p>이다.</p>
<p>대표적으로 Neo4j 가 있다.</p>
<h3 id="그래프-데이터베이스">그래프 데이터베이스</h3>
<p>그래프 데이터베이스는 데이터를 아래처럼 표현한다.</p>
<ul>
<li>노드(Node) → 데이터</li>
<li>엣지(Edge) → 관계</li>
</ul>
<p>여기서 중요한 건</p>
<blockquote>
<p>관계 자체가 하나의 <strong>핵심 데이터</strong>로 다뤄진다는 점</p>
</blockquote>
<p>이다.</p>
<p>예를 들어 관계형 데이터베이스에서는</p>
<ul>
<li>유저 테이블</li>
<li>팔로우 테이블</li>
</ul>
<p>처럼 관계를 별도 테이블로 관리한다.</p>
<p>반면 그래프 데이터베이스는 연결 자체가 구조가 된다.</p>
<pre><code>A --- 친구 --- B
|
팔로우
|
C</code></pre><blockquote>
<p>“누가 누구와 연결되어 있는가”</p>
</blockquote>
<p>를 굉장히 자연스럽게 표현하는 방식으로 볼 수 있다.</p>
<h3 id="장점-2">장점</h3>
<p>그래프 데이터베이스의 가장 큰 장점은
<strong>관계 탐색에 특화</strong>되어 있다는 점이다.</p>
<p>예를 들어</p>
<p>“A의 친구의 친구 찾기”</p>
<p>같은 작업을 수행한다고 하면,</p>
<p>관계형 데이터베이스에서는</p>
<ul>
<li>여러 JOIN 수행</li>
<li>복잡한 쿼리 작성</li>
<li>관계 깊어질수록 성능 부담 증가</li>
</ul>
<p>같은 문제가 생길 수 있다.</p>
<p>반면 그래프 데이터베이스는</p>
<ul>
<li>연결된 노드 따라가기</li>
</ul>
<p>방식으로 탐색이 가능하다.</p>
<p><strong>관계를 따라 이동하는 작업 자체에 최적화된 구조</strong>인 것이다.</p>
<h3 id="관계-탐색이-필요한-사용처">관계 탐색이 필요한 사용처</h3>
<p>그래프 데이터베이스는 보통</p>
<ul>
<li>친구 추천 시스템(SNS)</li>
<li>콘텐츠 추천 알고리즘</li>
<li>네트워크 분석</li>
<li>경로 탐색</li>
</ul>
<p>처럼 “연결 관계”가 핵심인 서비스에서 많이 사용된다.</p>
<h3 id="단점-3">단점</h3>
<p>다만 모든 상황에서 좋은 건 아니다.</p>
<p>그래프 데이터베이스는</p>
<ul>
<li>일반적인 CRUD 작업에는 비효율적일 수 있고</li>
<li>단순 데이터 저장에는 과한 구조가 될 수 있으며</li>
<li>학습 난이도와 설계 난이도가 높은 편</li>
</ul>
<p>이라는 특징도 존재한다.</p>
<p>그래서 보통은</p>
<blockquote>
<p>관계 탐색이 핵심인 특정 문제를 해결하기 위해 사용하는 데이터베이스</p>
</blockquote>
<p>라고 볼 수 있다.</p>
<h2 id="5-데이터를-분석하기-위한-데이터베이스">5. 데이터를 분석하기 위한 데이터베이스</h2>
<p>앞에서 살펴본 데이터베이스들은 공통적으로</p>
<blockquote>
<p>서비스를 운영하기 위한 데이터 저장</p>
</blockquote>
<p>에 초점이 맞춰져 있었다.</p>
<p>예를 들면</p>
<ul>
<li>사용자 정보 저장</li>
<li>습관 데이터 저장</li>
<li>로그인 상태 관리</li>
</ul>
<p>같은 작업들이다.</p>
<p>그런데 서비스 규모가 커지면
단순히 데이터를 저장하는 것을 넘어
데이터를 분석하려는 요구가 생기기 시작한다.</p>
<p>예를 들어</p>
<ul>
<li>“유저들이 어떤 기능을 가장 많이 사용할까?”</li>
<li>“하루 평균 습관 완료율은 얼마일까?”</li>
<li>“이번 주 활동량은 지난주보다 늘었을까?”</li>
</ul>
<p>같은 질문들이다.</p>
<p><strong>데이터를 저장</strong>하는 것을 넘어
<strong>데이터를 통해 흐름과 패턴을 분석</strong>하려는 단계로 넘어가는 것이다.</p>
<h3 id="운영-데이터베이스로-분석하면">운영 데이터베이스로 분석하면?</h3>
<p>물론 기존의 PostgreSQL 같은 관계형 데이터베이스에서도 분석 자체는 가능하다.</p>
<p>하지만 데이터 규모가 커질수록</p>
<ul>
<li>JOIN 증가</li>
<li>복잡한 집계 쿼리 증가</li>
<li>대량 데이터 조회 발생</li>
</ul>
<p>같은 문제가 생기기 쉽다.</p>
<p>그리고 운영 중인 데이터베이스에서
무거운 분석 작업까지 함께 수행하면
실제 서비스 성능에도 영향을 줄 수 있다.</p>
<p>그래서 대규모 서비스에서는 종종</p>
<blockquote>
<p>운영용 데이터베이스와 분석용 데이터베이스를 분리</p>
</blockquote>
<p>해서 사용하기도 한다.</p>
<h3 id="컬럼-기반으로-저장하는-방식">컬럼 기반으로 저장하는 방식</h3>
<p>이런 분석 작업에 특화된 방식 중 하나가 바로</p>
<blockquote>
<p>컬럼형(Column-oriented) 데이터베이스</p>
</blockquote>
<p>이다.</p>
<p>대표적으로 Apache Cassandra 같은 계열이 자주 언급된다.</p>
<h3 id="기존-데이터베이스와의-차이점">기존 데이터베이스와의 차이점</h3>
<p>기존 관계형 데이터베이스는 보통 행(Row) 단위로 저장된다.</p>
<pre><code>id | name | age | habit</code></pre><p>즉, 한 줄 안에 하나의 데이터 정보가 모두 들어간다.</p>
<p>반면 컬럼형 데이터베이스는
같은 컬럼끼리 묶어서 저장한다.</p>
<pre><code>id   → [1, 2, 3]
name → [A, B, C]
age  → [20, 25, 30]</code></pre><p>같은 종류의 데이터들을 연속적으로 저장하는 구조인 것이다.</p>
<h3 id="장점-3">장점</h3>
<p>이 방식은 특정 데이터만 대량으로 분석할 때 굉장히 강력하다.</p>
<p>예를 들어
“전체 유저 평균 나이 계산” 같은 작업을 수행한다고 하면,</p>
<p>행 기반 저장에서는
모든 row를 읽으면서 필요한 데이터를 꺼내야 한다.</p>
<p>반면 컬럼형 데이터베이스는
age 컬럼만 읽기만 하면 된다.</p>
<p>분석에 필요한 데이터만 빠르게 가져올 수 있기 때문에
대규모 집계와 통계 작업에 정말 유리하다.</p>
<h3 id="분석이-필요한-사용처">분석이 필요한 사용처</h3>
<p>이런 데이터베이스는 보통</p>
<ul>
<li>로그 분석</li>
<li>통계 처리</li>
<li>데이터 시각화</li>
<li>BI(Business Intelligence)</li>
<li>대규모 집계 시스템</li>
</ul>
<p>같은 분야에서 사용된다.</p>
<p>서비스 기능 자체보다는
<strong>데이터를 기반으로 의사결정을 내리기 위한 영역</strong>에서 많이 활용된다.</p>
<h3 id="단점-4">단점</h3>
<p>물론 일반 서비스 운영에는 잘 맞지 않는 부분도 존재한다.</p>
<ul>
<li>실시간 CRUD 작업에는 상대적으로 약하고</li>
<li>일반적인 서비스 구조와는 사용 방식이 다르며</li>
<li>데이터 수정이 잦은 환경에는 비효율적일 수 있다.</li>
</ul>
<p>그래서 운영 DB랑 분리해서 사용해야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[협업 쉽게 해버리기]]></title>
            <link>https://velog.io/@hogu__giriboy/%ED%98%91%EC%97%85-%EC%89%BD%EA%B2%8C-%ED%95%B4%EB%B2%84%EB%A6%AC%EA%B8%B0</link>
            <guid>https://velog.io/@hogu__giriboy/%ED%98%91%EC%97%85-%EC%89%BD%EA%B2%8C-%ED%95%B4%EB%B2%84%EB%A6%AC%EA%B8%B0</guid>
            <pubDate>Tue, 28 Apr 2026 16:06:46 GMT</pubDate>
            <description><![CDATA[<p>초급 팀 프로젝트를 진행하고 팀장들끼리 모여 회고 하던 중,
다른 팀장님께서 여러 협업 도구들을 알려주셨다.</p>
<p>슬랙, 지라, 리니어 같은 작업 관리 도구부터
프리티어, 린트, 허스키처럼 코드 스타일과 규칙을 관리하는 도구까지.</p>
<p>사실 나는 거의 처음 들어보는 것들이었다.
프리티어 정도만 &quot;저장하면 코드 예쁘게 정리해주는 도구&quot;로만 알고 있었을 뿐이다.</p>
<p>솔직히 충격이었다.</p>
<blockquote>
<p>“PR 형식을 자동으로 맞출 수 있다”
“컨벤션이 안 맞으면 push 자체가 막힌다”</p>
</blockquote>
<p>컨벤션이 안 맞으면 push 자체가 막힌다니.
우린 코드리뷰에서 하나하나 확인하고 알려주기로 했었는데 ...</p>
<p>물론 코드리뷰가 단순히 컨벤션만 보는 과정은 아니지만,
이 부분을 자동으로 처리할 수 있다는 건 꽤 큰 차이라고 느껴졌다.</p>
<p>특히 “push를 막는다”는 표현이 인상적이었다.
단순히 권장하는 수준이 아니라, 아예 지키지 않으면 다음 단계로 못 넘어가게 만든다는 의미니까.</p>
<p>그러던 중 또 다른 팀장님이 이런 말씀을 하셨다.</p>
<blockquote>
<p>“나는 자동사냥처럼 돌아가는 구조를 만들어두는 걸 좋아한다”</p>
</blockquote>
<p>사실 이번에 처음 듣는 말은 아닌데,
지금 다루려는 협업 도구들이랑 꽤 어울리는 말 같아서 인용해왔다.</p>
<p>나 또한 그렇다.
자동화가 가능하면 몸이 덜 고생하지 않겠는가 !</p>
<p>그런 나에게 이런 &quot;자동으로&quot; PR 형식을 맞춘다거나,
컨벤션이 안 맞으면 &quot;자동으로&quot; push 자체를 막는다거나
정말 매혹적인 말이 아닐 수가 없다.</p>
<p>그래서 그런 도구가 어떤 도구인지 살펴보고
그 외에 또 어떤 도구가 있나도 살펴보고 싶어서
이렇게 블로그 주제로 잡아왔다.</p>
<hr>
<h2 id="1-작업-흐름을-보이게-만드는-도구">1. 작업 흐름을 보이게 만드는 도구</h2>
<p>이번 프로젝트에서는
역할을 비교적 명확하게 나누고,
매일 데일리 스크럼을 진행하면서 작업 상황을 공유했다.</p>
<p>덕분에 협업이 크게 꼬이는 경험은 하지 않았다.</p>
<p>다만 느낀 건,
이 흐름을 사람이 계속 직접 관리하고 있었다는 점이다.</p>
<p>누가 어떤 작업을 하고 있는지,
어디까지 진행됐는지,
막힌 부분은 없는지
이런 것들을 매번 대화를 통해 확인해야 했다.</p>
<p>협업 도구는 이 과정을
<strong>시스템</strong>으로 대신해준다.</p>
<p>작업을 이슈 단위로 나누고,
진행 상태를 시각적으로 보여주고,
누가 무엇을 맡고 있는지 한눈에 파악할 수 있게 해준다.</p>
<p>즉, 매번 묻고 확인하지 않아도
<strong>흐름 자체가 자연스럽게 공유되는 구조</strong>가 된다.</p>
<p>물론 소통을 통한 방식도 충분히 협업을 굴릴 수 있고,
팀 분위기를 만드는 데에도 중요한 역할을 한다.</p>
<p>다만 이 흐름을 전적으로 사람에게만 의존하게 되면
상황에 따라 관리가 어려워질 수 있다.</p>
<p>그래서 이런 과정을 도구가 함께 담당해준다면
더 안정적이고 효율적인 협업이 가능해진다.</p>
<p>결국 협업 도구는
단순한 편의 기능이 아니라,</p>
<p>작업 흐름 자체를 관리해주는 역할을 한다.</p>
<h3 id="github-issues">GitHub Issues</h3>
<p><img src="https://velog.velcdn.com/images/hogu__giriboy/post/6662e370-4bac-4b11-bb8a-10002aa31e4f/image.png" alt="github issues"></p>
<p>이번 프로젝트에서는 GitHub의 Issues 기능을 활용해서 작업을 관리했다.</p>
<p>각자 해야 할 일을 이슈 단위로 정리하고,
관련된 PR을 연결하는 방식으로 작업을 진행했다.</p>
<p>코드를 관리하는 GitHub 안에서 바로 사용할 수 있다는 점이 편리했고,
이슈와 PR이 자연스럽게 연결되기 때문에
작업 흐름을 따라가기도 쉬웠다.</p>
<p>다만 첫 프로젝트였던 만큼
이 기능을 충분히 활용하지는 못했던 것 같다.</p>
<p>작업 흐름을 관리하기보다는
칸반 보드처럼 간단히 정리하거나
기록을 남겨두는 용도로 사용하는 데에 더 집중했던 느낌이었다.</p>
<p>다음은 보다 체계적으로 작업 흐름을 관리할 수 있는 도구들을 다뤘다.</p>
<h3 id="jira">Jira</h3>
<p><img src="https://velog.velcdn.com/images/hogu__giriboy/post/0bcc056c-a4e4-4bd0-b970-a9024e1c1462/image.png" alt="Jira"></p>
<p>지라는 이러한 이슈 기반 작업 관리를
더 체계적으로 확장한 도구다.</p>
<p>작업을 단순히 나열하는 것이 아니라,
진행 상태를 단계별로 나누고 보드 형태로 시각화해서
팀 전체 흐름을 한눈에 파악할 수 있게 해준다.</p>
<p>또한 스프린트 관리나 자동화 기능 등
협업을 위한 다양한 기능을 제공한다는 점에서
보다 구조적인 작업 관리 도구라고 볼 수 있다.</p>
<h3 id="linear">Linear</h3>
<p><img src="https://velog.velcdn.com/images/hogu__giriboy/post/0669627e-e63f-46b9-9513-261920be6197/image.png" alt="Linear"></p>
<p>리니어 역시 이슈 기반으로 작업을 관리하는 도구다.</p>
<p>지라와 유사한 역할을 하지만,
보다 가볍고 직관적인 사용성을 강조한 도구로 알려져 있다.</p>
<p>복잡한 설정 없이도 빠르게 작업을 관리할 수 있어서
소규모 팀이나 스타트업에서 많이 사용되는 편이다.</p>
<p>결국 지라와 리니어는
<strong>작업을 ‘흐름’으로 관리한다는 점</strong>에서는 동일한 도구들이다.</p>
<h3 id="slack">Slack</h3>
<p><img src="https://velog.velcdn.com/images/hogu__giriboy/post/1640c5c3-c696-4608-8c4c-26b8af78b15d/image.png" alt="slack"></p>
<p>이슈 기반으로 작업을 관리하는 것과 별개로,
팀원 간의 소통을 담당하는 도구도 필요하다.</p>
<p>슬랙은 채널을 통해 대화를 주제별로 나눌 수 있어
공지, 작업 공유, 질문 등을 구분해서 관리할 수 있다.</p>
<p>이 덕분에 대화가 뒤섞이지 않고,
필요한 정보를 빠르게 찾을 수 있다.</p>
<p>결국 슬랙은
<strong>팀의 소통을 구조화해주는 도구</strong>라고 볼 수 있다.</p>
<hr>
<h2 id="2-코드-스타일을-맞춰주는-도구">2. 코드 스타일을 맞춰주는 도구</h2>
<p>협업을 하다 보면
생각보다 자주 부딪히는 문제가 하나 있는데,
바로 코드 스타일이다.</p>
<p>같은 기능을 구현하더라도
들여쓰기, 세미콜론, 따옴표, 변수명 등
사람마다 코드 스타일이 조금씩 다르다.</p>
<p>이 차이가 쌓이면
코드를 읽기 어려워지고,
리뷰 과정에서도 불필요한 피드백이 반복되게 된다.</p>
<p>물론 컨벤션을 정해서 맞출 수는 있고,
우리 또한 이러한 코드 컨벤션을 정해놓고
프로젝트를 시작했다.</p>
<p>하지만 문제는
서론에서도 말했듯이
이걸 사람이 계속 지켜야 한다는 점이다.</p>
<p>실수로 놓칠 수도 있고,
사람마다 기준이 다르게 적용될 수도 있다.</p>
<p>그래서 등장하는 것이
코드 스타일을 자동으로 맞춰주는 도구다.</p>
<h3 id="prettier">Prettier</h3>
<p><img src="https://velog.velcdn.com/images/hogu__giriboy/post/db633429-67ca-44e1-801b-7e544cb35e34/image.png" alt="Prettier"></p>
<p>프리티어는 코드 포맷터다.</p>
<p>쉽게 말하면
코드를 ‘자동으로 정리해주는 도구’다.</p>
<p>저장을 하거나 명령을 실행하면
들여쓰기, 줄바꿈, 따옴표 스타일 등을
미리 정해둔 규칙에 맞게 자동으로 수정해준다.</p>
<p>덕분에 사람이 일일이 신경 쓰지 않아도
코드 스타일이 항상 동일하게 유지된다.</p>
<h3 id="eslint">ESLint</h3>
<p><img src="https://velog.velcdn.com/images/hogu__giriboy/post/9d7ddda6-5c46-4f1e-8d44-58b42eca2d7c/image.png" alt="ESLint"></p>
<p>ESLint는 코드 검사 도구다.</p>
<p>단순히 스타일을 맞추는 것을 넘어서
코드가 정해진 규칙을 지키고 있는지 확인해준다.</p>
<p>예를 들어
사용하지 않는 변수, 잘못된 문법, 위험한 코드 패턴 등을
미리 잡아낼 수 있다.</p>
<p>즉, 프리티어가
코드를 ‘보기 좋게’ 만들어준다면,</p>
<p>ESLint는
코드를 ‘올바르게’ 유지하도록 도와준다.</p>
<hr>
<h2 id="3-규칙을-지키게-만드는-도구">3. 규칙을 ‘지키게 만드는’ 도구</h2>
<p>드디어 이 파트다.
사실 이 파트가 이번 포스팅의 메인이다.</p>
<p>앞에서 Prettier와 ESLint를 통해
코드 스타일과 규칙을 자동으로 맞출 수 있다는 걸 알게 됐다.</p>
<p>이제 더 나아가
그걸 항상 지키게 만들어야 할 때다.</p>
<p>아무리 좋은 규칙이 있어도
사람이 실수로 놓치거나,
검사를 건너뛰고 넘어가는 상황은 충분히 발생할 수 있다.</p>
<p>결국 중요한 건
규칙을 만드는 것보다, 지키게 만드는 것이다.</p>
<p>이 역할을 해주는 도구가 바로 Husky다.</p>
<h3 id="husky">Husky</h3>
<p><img src="https://velog.velcdn.com/images/hogu__giriboy/post/5fe4eeae-836c-48ba-a625-470f7ffb04f0/image.png" alt="Husky"></p>
<p>Husky는 Git hook을 활용해서
특정 시점에 원하는 작업을 자동으로 실행할 수 있게 해주는 도구다.</p>
<p>예를 들어
코드를 push 하기 전에</p>
<ul>
<li>Prettier 실행</li>
<li>ESLint 검사</li>
</ul>
<p>같은 작업을 자동으로 돌릴 수 있다.</p>
<p>그리고 만약 이 과정에서
컨벤션이 맞지 않거나 오류가 발생하면</p>
<p><strong>push 자체를 막을 수 있다 !</strong></p>
<p>즉, 단순히 “검사해주는 도구”가 아니라
규칙을 지키지 않으면 다음 단계로 못 넘어가게 만드는 장치다.</p>
<hr>
<h2 id="4-더보기">4. 더보기</h2>
<p>앞에서 살펴본 도구들은
협업에서 자주 사용되는 기본적인 도구들이다.</p>
<p>하지만 찾아보면
이 외에도 협업을 더 효율적으로 만들어주는 도구들이 정 말 많다.</p>
<h3 id="github-actions">GitHub Actions</h3>
<p>GitHub Actions는
코드를 push 하거나 PR을 생성했을 때</p>
<ul>
<li>테스트 실행</li>
<li>빌드</li>
<li>배포</li>
</ul>
<p>같은 작업을 자동으로 수행할 수 있게 해주는 도구다.</p>
<p>즉, 사람이 직접 실행하지 않아도
코드가 올라가는 순간 자동으로 검증과 배포가 이루어진다.</p>
<p>이 도구와 밑에서 다룰 Commitlint는 다른 팀장님이 알려줬던 도구는 아닌데,
이 도구가 정말 너무 충격적이었어서 어떤 식으로 흘러가는 건지 더 알아봤다.</p>
<ol>
<li>이벤트 발생</li>
</ol>
<p>먼저 코드 push 혹은 PR 생성과 같은 상황이 생기면 시작된다.</p>
<ol start="2">
<li>가상 컴퓨터를 하나 띄우기</li>
</ol>
<p>GitHub가 아무것도 없는 깨끗한 상태의
&quot;임시 서버&quot;를 하나 만들어준다.</p>
<ol start="3">
<li>코드 가져오기</li>
</ol>
<p>말 그대로 내 레포 코드를 그 서버에 복사한다.</p>
<ol start="4">
<li>테스트 / 검사</li>
</ol>
<p>여기서 패키지 설치, ESLint 검사, 테스트 실행이 이뤄진다.
하나도 실패하면 워크플로우가 중단되고,
PR에 빨간 X 표시가 된다.</p>
<ol start="5">
<li>빌드</li>
</ol>
<p>React는 정적 파일로 변환하고,
Next는 서버 코드와 번들을 생성한다.</p>
<p>즉 서비스로 돌릴 수 있는 형태로 만드는 과정이다.</p>
<ol start="6">
<li>배포 (배포 플랫폼 호출 / 서버에 직접 업로드)</li>
</ol>
<p>이건 방식이 두 가지가 있는데,</p>
<p>Actions가 명령 실행하면 배포 플랫폼이 받아서 배포하는 방식과
직접 서버에 파일 던지는 방식으로 진행된다.</p>
<h3 id="commitlint">Commitlint</h3>
<p>Commitlint는
커밋 메시지가 정해진 규칙을 따르고 있는지 검사하는 도구다.</p>
<p>예를 들어
feat: 로그인 기능 추가
같은 형식을 강제할 수 있다.</p>
<p>이렇게 하면 커밋 로그만 봐도
어떤 작업이 이루어졌는지 한눈에 파악할 수 있다.</p>
<hr>
<h2 id="5-협업-자동화-돌리기">5. 협업: 자동화 돌리기</h2>
<p>앞에서 살펴본 도구들을
실제로 어떻게 연결되는지
얼마나 자동화로 돌려먹을 수 있는지 한 번 정리해보면 이렇다.</p>
<ol>
<li>작업 정의</li>
</ol>
<p>GitHub Issues 생성해서
작업 단위를 나눈다.</p>
<ol start="2">
<li>개발</li>
</ol>
<p>브랜치를 생성하고 코드 작성한다.</p>
<ol start="3">
<li>commit 단계 (1차 필터)</li>
</ol>
<p>Husky를 실행하여 Prettier와 ESLint 자동 실행한다.</p>
<p>문제 있으면 commit 자체가 안 된다.</p>
<ol start="4">
<li>PR 생성</li>
</ol>
<p>협업을 시작한다.</p>
<ol start="5">
<li>GitHub Actions (2차 필터)</li>
</ol>
<p>GitHub Actions 실행하여</p>
<ul>
<li>패키지 설치</li>
<li>lint 검사</li>
<li>테스트 실행</li>
<li>build</li>
</ul>
<p>같은 작업이 자동으로 수행한다.</p>
<p>이 과정에서 하나라도 실패하면
PR에 오류가 표시되고 머지를 할 수 없다.</p>
<ol start="6">
<li>자동 머지 (옵션)</li>
</ol>
<p>조건을 만족하면 자동 merge 가능하다.</p>
<ol start="7">
<li>자동 배포</li>
</ol>
<p>Vercel / Render 연결하여 완전 자동으로 배포하거나
GitHub Actions 안에서 배포까지 진행해 서버까지 직접 제어한다.</p>
<p>협업 도구를 제대로 구성하면,
개발자는 코드 작성과 PR 생성만 하면 되고 나머지 과정은 모두 자동으로 처리된다.</p>
<p>commit 단계에서 한 번,
GitHub Actions에서 한 번 검증을 거친 뒤,
문제가 없을 경우 자동으로 배포까지 이어지는 구조다.</p>
<hr>
<h2 id="6-협업은-결국-흐름을-만드는-일이다">6. 협업은 결국 ‘흐름’을 만드는 일이다</h2>
<p>이번에 협업 도구들을 살펴보면서 느낀 건
협업은 단순히 같이 일하는 것이 아니라,
하나의 흐름을 만드는 과정이라는 점이었다.</p>
<p>이슈로 작업을 나누고,
코드 스타일을 맞추고,
규칙을 자동으로 검사하고,
필요한 순간에 자동으로 배포까지 이루어지는 구조.</p>
<p>이 모든 과정이 연결되면서
하나의 자연스러운 흐름이 만들어진다.</p>
<p>그리고 협업 도구들은
이 흐름을 사람이 아닌
시스템이 관리할 수 있도록 도와준다.</p>
<p>물론 도구만으로 협업이 완성되는 것은 아니다.</p>
<p>하지만 최소한
사람이 계속 확인하고 관리해야 하는 부담을 줄여주고,
더 안정적인 협업 구조를 만들 수 있게 해준다.</p>
<p>우린 이러한 관리 외에도
집중해야 할 일이 많으니까 !</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Promise로 병렬 처리하기]]></title>
            <link>https://velog.io/@hogu__giriboy/Promise%EB%A1%9C-%EB%B3%91%EB%A0%AC-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@hogu__giriboy/Promise%EB%A1%9C-%EB%B3%91%EB%A0%AC-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 22 Apr 2026 13:28:27 GMT</pubDate>
            <description><![CDATA[<h1 id="1-병렬-처리하기">1. 병렬 처리하기</h1>
<p>리스트를 다루는 기능을 구현하면서
생성, 수정, 삭제 요청을 각각 나눠서 처리해야 하는 상황이 있었다.</p>
<p>처음에는 단순하게 생각했다.
요청을 하나씩 순서대로 보내는 것보다,
한 번에 묶어서 처리하면 더 깔끔하고 효율적일 것 같았다.</p>
<p>특히 생성, 수정, 삭제가 모두 비동기 요청이었기 때문에
이걸 병렬로 처리하면 속도도 더 빨라질 거라고 생각했다.</p>
<p>그래서 자연스럽게
여러 작업을 동시에 처리할 수 있는 방법을 찾게 되었고,
그 과정에서 Promise를 떠올리게 됐다.</p>
<p>예전에 한 번 간단하게 배워본 적은 있었지만,
실제 기능 구현에 제대로 써본 건 이번이 처음이었다.</p>
<p>그래서 이번 기회에 생각나서
생성, 수정, 삭제를 모두 Promise로 묶어서 처리했었고,
하면서 발생했던 일들과 함께
Promise를 다시 한 번 짚고 가려고 한다.</p>
<hr>
<h1 id="2-promise-되짚기">2. Promise 되짚기</h1>
<p>Promise는 비동기 작업의 결과를 다루기 위한 객체다.</p>
<p>지금 당장은 결과를 알 수 없지만,
나중에 완료될 작업의 상태를 가지고 있다고 보면 이해하기 편하다.</p>
<p>이 작업은 크게 세 가지 상태로 나뉜다.</p>
<ul>
<li>pending: 아직 처리 중인 상태</li>
<li>fulfilled: 작업이 성공한 상태</li>
<li>rejected: 작업이 실패한 상태</li>
</ul>
<p>이 상태에 따라 이후 실행 흐름이 결정된다.</p>
<p>그래서 Promise를 사용한다는 건 단순히
비동기 코드를 작성하는 게 아니라</p>
<p><strong>작업이 끝난 이후 흐름을 어떻게 이어갈지를 정의하는 것</strong>에 가깝다.</p>
<p>여기까지는 기본적인 개념이고,
내가 Promise를 떠올리게 된 이유는 조금 달랐다.</p>
<p>나는 이걸 “비동기 처리”보다는
여러 요청을 한 번에 처리할 수 있는 방법으로 먼저 인식했다.</p>
<p>생성, 수정, 삭제 요청을 각각 분기해서 진행해야 하니까
한 번에 묶어서 처리하면 더 효율적일 것 같았고,
그 과정에서 자연스럽게 Promise를 사용하게 됐다.</p>
<p>즉, 처음에는 Promise를
병렬 처리를 위한 도구로 생각하고 접근했던 것이다.</p>
<hr>
<h1 id="3-promise와-생긴-일">3. Promise와 생긴 일</h1>
<p>Promise를 사용해서
생성, 수정, 삭제 요청을 한 번에 묶어서 처리했다.</p>
<p>처음에는 별생각 없었다.
코드도 깔끔히 설계했던 대로 분기해서 처리하고,
처리 속도도 병렬로 처리하니까
빨라졌을 것이다.</p>
<p>그런데 이상한 문제가 발생하기 시작했다.</p>
<p>분명 내가 입력한 순서대로 생성 요청을 보냈는데,
실제로 반영된 결과를 보면
생성 순서가 매번 뒤죽박죽으로 섞여 있었다.</p>
<p>처음에는 단순히 정렬 문제라고 생각했다.</p>
<p>그래서 데이터를 가져온 뒤에
sort를 적용해서 순서를 맞춰보기도 했고,
다른 방식으로 정렬 기준을 바꿔보기도 했다.</p>
<p>하지만 아무리 정렬을 바꿔봐도
근본적인 문제는 해결되지 않았다.</p>
<blockquote>
<p>정렬의 문제가 아니라, 생성 자체가 꼬이고 있었던 것이다.</p>
</blockquote>
<p>그렇게 거의 두 시간을 헤매면서,
이게 왜 안 되는지 정확한 원인을 잡지 못한 채
로그만 계속 찍어내고 있었다.
근데 코드를 좀 멀리서 보기 시작하면서 문제가 보였다.</p>
<p>세부적인 로직에서의 문제가 아닌
이걸 어떻게 처리를 하냐로 시야를 멀리 잡았을 때,</p>
<p>그때 문득 떠올랐던 게 Promise였다.</p>
<p>여러 요청을 Promise로 묶어서 실행하면
각 요청이 동시에 실행되고,
먼저 끝난 요청이 먼저 반영되는 구조가 된다.</p>
<p>즉, 내가 보낸 순서와는 상관없이
서버에서 처리되는 순서가 달라질 수 있다는 뜻이었다.</p>
<p>결국 문제는 여기였다.</p>
<p>나는 단순히 요청을 “같이 보냈다”고 생각했지만,
실제로는 각 요청이 독립적으로 경쟁하면서 처리되고 있었던 것이다.</p>
<p>그래서 확인을 위해
생성만 Promise에서 제외하고,
수정과 삭제만 Promise로 처리하도록 구조를 바꿔봤다.</p>
<p>그 결과는 바로 드러났다.</p>
<p>이전까지 계속 꼬이던 생성 순서가
정상적으로 유지되기 시작했다.</p>
<p>그제서야 확실하게 이해할 수 있었다.</p>
<p><strong>생성은 순서가 중요한 작업</strong>이었고,
그걸 병렬로 처리하면서 문제가 발생했던 것이다.</p>
<hr>
<h1 id="4-수정과-삭제와는-다른-생성">4. 수정과 삭제와는 다른 생성</h1>
<p>생성만 Promise에서 제외하고 나서야
왜 문제가 발생했는지가 조금 더 명확하게 보이기 시작했다.</p>
<p>겉으로 보면 생성, 수정, 삭제 모두
같은 “비동기 요청”처럼 보였지만,
실제로는 성격이 완전히 다른 작업이었다.</p>
<p>먼저, 생성은 순서 자체가 의미를 가진다.</p>
<p>사용자가 입력한 순서대로 데이터가 쌓이길 기대하기 때문에
이 순서가 깨지면 결과가 어색해질 수밖에 없다.</p>
<p>반면 수정과 삭제는 다르다.</p>
<p>이미 존재하는 데이터를 대상으로
값을 바꾸거나 제거하는 작업이기 때문에
처리 순서가 크게 중요하지 않다.</p>
<p>즉,
생성은 순서가 중요하고,
수정과 삭제는 이미 순서가 정해져 있으니 크게 중요하지 않았다.</p>
<p>이 차이를 고려하지 않고
모든 요청을 동일하게 Promise로 묶어버린 것이 문제였다.</p>
<hr>
<h1 id="5-이번에-알게-된-점">5. 이번에 알게 된 점</h1>
<p>이번 경험을 통해 하나 분명해진 게 있다.</p>
<blockquote>
<p>Promise는 병렬로 처리해준다고 사용할 도구가 아니라
작업의 성격을 고려해서 선택해야 하는 도구라는 점이다.</p>
</blockquote>
<p>정리해보면 기준은 이렇게 나눌 수 있다.</p>
<p><strong>순서가 중요한 작업</strong>은 <strong>순차적으로 처리</strong>하고
<strong>서로 독립적인 작업</strong>은 <strong>병렬로 처리</strong>한다.</p>
<p>그리고 가장 크게 느꼈던 건 이거였다.
사실 어떻게 보면 간단한 문제다.</p>
<p>그저 생성에서 순서까지는 미처 생각하지 못해
일어난 문제였다.</p>
<p>아무튼 이번에 나에게 있어 Promise는
코드를 병렬로 처리할 수 있도록 하는 도구가 아니라,
실행 흐름을 어떻게 설계할지 결정하는 도구였다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[vite.config와 proxy 설정]]></title>
            <link>https://velog.io/@hogu__giriboy/vite.config%EC%99%80-proxy-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@hogu__giriboy/vite.config%EC%99%80-proxy-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Wed, 15 Apr 2026 14:40:34 GMT</pubDate>
            <description><![CDATA[<h2 id="1-viteconfig를-만나게-되다">1. vite.config를 만나게 되다.</h2>
<p>우선 팀 프로젝트에서 API base URL를 정했는데,
우리 팀은 이 URL이 <code>/api/~</code> 로 시작한다.</p>
<p>그래서 fetch를 받을 때 아무 생각도 없이 그냥 <code>/api/~</code> 로 받아버렸다.</p>
<p>연결을 시도해보니 문제가 발생했다.
API 요청을 보냈는데, 원하는 JSON 데이터가 아니라 전혀 다른 응답이 돌아온 거다.</p>
<p>처음에는 백엔드 문제라고 생각했다.</p>
<p>서버가 켜져 있는지 확인하고,
라우팅이 잘못된 건 아닌지 확인해보고.</p>
<p>하지만 서버도 정상 실행에
해당 API 라우트가 제대로 정의되어 있었고
Postman으로 직접 요청을 보내서 응답도 확인해봐도
Postman에서는 정상적으로 JSON 응답이 잘 내려오고 있었다.</p>
<p>그럼에도 불구하고 프론트에서 요청을 보내면 정상적인 응답이 오지 않았다.
네트워크 탭을 열어서 응답을 확인해보니,
내가 받고 있던 건 API 응답이 아니라 HTML 파일이었다.</p>
<p>이 부분에서 해결 방법이 뭔지 찾아보다가,
vite.config 파일과 proxy 설정을 건들게 되었다.</p>
<hr>
<h2 id="2-proxy-설정-알고-계셨나요">2. proxy 설정, 알고 계셨나요?</h2>
<p>Vite 개발 서버는 기본적으로 프론트 요청을 처리하기 때문에,
/api처럼 백엔드로 보내고 싶은 요청은 별도로 전달해주는 설정이 필요하다.</p>
<p>그게 바로 proxy였다.</p>
<pre><code class="language-js">server: {
  proxy: {
    &#39;/api&#39;: &#39;http://localhost:8080&#39;,
  }
}</code></pre>
<p>설명을 보면, <strong>/api로 시작하는 요청을
지정한 백엔드 서버(localhost:8080)로 전달해준다는 의미</strong>였다.</p>
<p>정확한 동작 원리를 이해한 상태는 아니었지만,
“이걸 적용하면 해결된다”는 흐름을 보고 그대로 적용해봤다.</p>
<p>결과는 바로 나타났다.</p>
<p>이전까지 HTML이 돌아오던 요청이
정상적으로 JSON 데이터를 반환하기 시작한 것이다.</p>
<p>이때는 단순히 문제가 해결됐으니 넘어가자는 생각 뿐이었다.</p>
<p>코드 리뷰를 통해 이 부분에 대한 질문을 받으면서,
그제서야 내가 적용한 방식에 대해 다시 생각해보게 됐다.</p>
<p>사실 우리가 지금까지 학습해온 방식은 풀 URL을 사용하는 방식이었고,
현재 팀 프로젝트 환경에서도 proxy 설정 없이 충분히 동작하는 구조였다.</p>
<p>결국 나는 팀 기준에 맞춰 다시 풀 URL 방식으로 코드를 수정했다.</p>
<p>이 과정을 통해 느낀 건,
proxy 설정이 잘못된 접근이었다기보다는
상황에 따라 선택할 수 있는 방식 중 하나였다는 점이다.</p>
<p>지금은 풀 URL을 사용하고 있지만,
이번 경험 덕분에 proxy 설정, 그리고 vite.config에 관해 찾아보게 됐다.</p>
<hr>
<h2 id="3-proxy가-필요한-상황">3. proxy가 필요한 상황</h2>
<p>그럼 proxy 설정은 언제 사용하는 걸까?</p>
<p>이걸 이해하려면 요청이 어떻게 흐르는지를 먼저 생각해봐야 한다.</p>
<p>프론트는 Vite 개발 서버 위에서 동작하고 있고,
내가 보내는 API 요청도 일단 이 서버를 거치게 된다.</p>
<p>이때 /api처럼 상대 경로로 요청을 보내면,
그 요청은 기본적으로 백엔드가 아니라
프론트 개발 서버로 먼저 전달된다.</p>
<p>그리고 Vite는 이 요청을 백엔드로 넘겨주지 않는다.
별도의 설정이 없다면, 해당 요청을 프론트에서 처리하려고 시도하게 된다.</p>
<p>이 상황에서 등장하는 게 proxy 설정이다.</p>
<p>proxy는 특정 경로(/api)로 들어온 요청을
백엔드 서버로 전달해주는 역할을 한다.</p>
<p>즉, </p>
<ul>
<li>/api/... 요청 → 프론트 서버에서 받음</li>
<li>proxy 설정 → 백엔드(localhost:8080)로 전달</li>
</ul>
<p>이 흐름이 만들어지는 것이다.</p>
<p>그래서 proxy는 다음과 같은 상황에서 유용하다.</p>
<ul>
<li>프론트와 백엔드를 각각 다른 서버에서 개발할 때</li>
<li>상대 경로(/api)를 유지하면서 API를 호출하고 싶을 때</li>
<li>개발 환경에서 CORS 문제를 우회하고 싶을 때</li>
</ul>
<p>반대로,</p>
<ul>
<li>풀 URL(<a href="http://localhost:8080/...)%EB%A1%9C">http://localhost:8080/...)로</a> 직접 요청을 보내는 경우라면</li>
<li>proxy 없이도 충분히 동작할 수 있다</li>
</ul>
<p>결국 proxy는 필수 설정이 아니라,
개발 환경에 따라 선택할 수 있는 방식 중 하나라고 볼 수 있다.</p>
<hr>
<h2 id="4-viteconfig에서-추가로-다루는-설정들">4. vite.config에서 추가로 다루는 설정들</h2>
<p>proxy 설정을 적용하면서 처음으로 vite.config 파일을 제대로 찾아보게 됐는데,
생각보다 다양한 설정들을 만질 수 있었다.</p>
<p>그중에서 몇 가지 설정만 간단히 정리해보면 다음과 같다.</p>
<p>먼저, 개발 서버 포트를 지정하는 설정이다.</p>
<pre><code class="language-js">server: {
  port: 5173,
}</code></pre>
<p>기본 포트가 정해져 있긴 하지만,
여러 프로젝트를 동시에 실행할 경우 포트를 직접 지정해야 할 상황이 생길 수 있다.</p>
<p>다음은 서버 실행 시 브라우저를 자동으로 열어주는 설정이다.</p>
<pre><code class="language-js">server: {
  open: true,
}</code></pre>
<p>개발할 때 매번 주소를 입력하는 번거로움을 줄일 수 있어서,
작지만 편의성을 높여주는 설정이다.</p>
<p>또 하나는 import 경로를 정리할 수 있는 alias 설정이다.</p>
<pre><code class="language-js">resolve: {
  alias: {
    &#39;@&#39;: &#39;/src&#39;,
  },
}</code></pre>
<p>상대 경로를 계속 이어서 쓰는 대신,
@ 같은 별칭을 사용해서 경로를 더 깔끔하게 관리할 수 있다.</p>
<p>마지막으로, 배포 시 경로를 설정하는 base 옵션이 있다.</p>
<pre><code class="language-js">base: &#39;/my-app/&#39;,</code></pre>
<p>로컬에서는 크게 체감되지 않지만,
배포 환경에서는 리소스 경로를 맞추기 위해 필요한 설정이다.</p>
<p>이처럼 vite.config는 단순한 설정 파일이 아니라,
개발 환경 전반을 조정하는 역할을 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[페어 프로그래밍]]></title>
            <link>https://velog.io/@hogu__giriboy/%ED%8E%98%EC%96%B4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</link>
            <guid>https://velog.io/@hogu__giriboy/%ED%8E%98%EC%96%B4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</guid>
            <pubDate>Tue, 07 Apr 2026 04:45:30 GMT</pubDate>
            <description><![CDATA[<p>이번에 스프린트에서 프로젝트를 진행한다.
계획서를 자잘자잘하게 쓰고 프로젝트를 진행하게 되는데,
거기서 팀장을 맡아버렸다.</p>
<p>그런 나서는 사람이 아니라 굉장한 부담이 함께하는데,
이 또한 성장 중 하나려니 싶어서
부담이고 자시고 모두 안고 가려고 한다.</p>
<p>본론으로 돌아가서 일정 및 R&amp;R을 비롯한 계획서를 작성하고
멘토님께 피드백을 받았는데
멘토님께서 페어 프로그래밍에 대해 언급해주셨다.</p>
<p>그래서 주제로 잡고 한 발자국 나아가려고 한다!</p>
<hr>
<h2 id="1-혼자-개발하는-방식에서-벗어나기">1. 혼자 개발하는 방식에서 벗어나기</h2>
<h3 id="혼자-개발할-때의-한계">혼자 개발할 때의 한계</h3>
<p>처음엔 강의도 혼자 듣고, 과제도 혼자 풀면서
당연히 혼자 개발하는 게 기본이다.</p>
<p>근데 어느 순간부터 이상한 느낌이 온다.</p>
<ul>
<li>이 코드가 맞는지 확신이 없음</li>
<li>더 좋은 방법이 있는지 모르겠음</li>
<li>그냥 &quot;돌아가니까 OK&quot; 상태가 됨</li>
</ul>
<p>이게 반복되면 뭐가 생기냐면
<strong>성장이 멈춘 느낌</strong>을 받게 된다.</p>
<h3 id="한계의-이유">한계의 이유</h3>
<p>혼자 개발하게 되면 아래와 같은 구조를 갖게 된다.</p>
<ol>
<li>내가 생각</li>
<li>내가 작성</li>
<li>내가 검증</li>
</ol>
<p>이게 문제로 작용한다.</p>
<p>누가 틀렸는지 체크해주는 사람이 없고,
다른 시각이 들어올 틈이 없다.</p>
<p>비유해보자면
혼자 시험 보고, 혼자 채점하는 상황이다.
틀려도 모르고, 맞아도 왜 맞는지 모른다.</p>
<p>그래서 등장한 방식이 <strong>페어 프로그래밍</strong>이다.</p>
<p>혼자 하는 게 아니라
<strong>처음부터 같이 생각하고 같이 만드는 방식</strong>이다.</p>
<hr>
<h2 id="2-같이-코딩처럼-보이는-오해">2. 같이 코딩처럼 보이는 오해</h2>
<p>처음 들으면 그냥 이렇게 생각하기 쉽다</p>
<blockquote>
<p>둘이 앉아서 같이 코딩하는 거 아닌가?</p>
</blockquote>
<p>둘이 같은 내용을 각자 나눠서 코딩하는 방식이라면
그건 오히려 비효율적인 중복 작업에 가깝다.</p>
<p>실제로 모습만 보면 비슷하게 오해할 만하다.</p>
<p>하지만 서로의 역할이 확실하게 구분된다.</p>
<p>페어 프로그래밍은 한 명이 키보드를 잡고 코드를 작성하고,
다른 한 명이 옆에서 화면을 보며 함께 진행하는 구조다.</p>
<p>그냥 같이 있는 게 아니라 역할이 나뉘어 있다는 것이 포인트다.</p>
<h3 id="driver--navigator-역할">Driver / Navigator 역할</h3>
<p>페어 프로그래밍은 기본적으로 역할이 두 개다.</p>
<ul>
<li>Driver → 직접 코드 작성하는 사람</li>
<li>Navigator → 옆에서 방향 잡는 사람</li>
</ul>
<p>여기서 많이 오해하는 게 있는데</p>
<p>Driver는 단순히 코드만 작성하는 사람이 아니라</p>
<blockquote>
<p><strong>왜 이렇게 작성하는지 설명하면서</strong> 코드를 짜야 한다.</p>
</blockquote>
<p>Navigator도 그냥 구경하는 사람이 아니고</p>
<ul>
<li>이 방식이 맞는지</li>
<li>더 좋은 방법이 있는지</li>
<li>놓친 부분이 없는지</li>
</ul>
<p>이걸 계속 생각해야 하는 역할이다.</p>
<h3 id="중요한-건-역할이-아니라-생각의-흐름">중요한 건 역할이 아니라 &quot;생각의 흐름&quot;</h3>
<p>여기서 진짜 중요한 포인트가 하나 있다.</p>
<p>둘이 코드를 나눠서 만드는 게 아니라</p>
<blockquote>
<p><strong>생각을 같이 끌어내는 구조</strong>다.</p>
</blockquote>
<p>혼자 할 때는 머릿속에서만 생각하고 끝났던 것들이
말로 설명되고 바로 피드백 받고
다시 수정되는 흐름이 생긴다.</p>
<p>이게 핵심이다.</p>
<h3 id="이런-구조로-인한-변화">이런 구조로 인한 변화</h3>
<p>이 구조 때문에 생기는 변화가 있다.</p>
<ul>
<li>애매하게 알고 있던 게 바로 들킨다.</li>
<li>그냥 감으로 짜던 코드가 설명 가능한 코드로 바뀐다.</li>
<li>선택 하나하나에 이유가 붙기 시작한다.</li>
</ul>
<blockquote>
<p>결국 코드 퀄리티보다 먼저
<strong>생각하는 방식 자체가 바뀐다.</strong></p>
</blockquote>
<hr>
<h2 id="3-실제-진행-방식">3. 실제 진행 방식</h2>
<h3 id="한-기능을-같이-끝까지">한 기능을 같이 끝까지</h3>
<p>페어 프로그래밍은
중간에 나눠서 하는 방식이 아니다.</p>
<blockquote>
<p>하나의 기능을 처음부터 끝까지 같이 만든다.</p>
</blockquote>
<p>예를 들어
상품 등록 기능을 하나 만든다면
API 설계부터 UI까지
전부 같이 진행하는 구조다.
이 과정 자체가 페어 프로그래밍의 핵심이다.</p>
<h3 id="시작은-항상-같이-생각부터">시작은 항상 &quot;같이 생각&quot;부터</h3>
<p>바로 코드부터 치지 않는다.</p>
<p>먼저 어떻게 만들지, 어떤 방식이 좋을지
같이 이야기하면서 방향을 잡는다.</p>
<p>이 과정이 없으면
그냥 둘이 앉아 있는 혼자 개발이랑 다를 게 없다.</p>
<h3 id="코드-작성도-혼자가-아니라-같이">코드 작성도 혼자가 아니라 같이</h3>
<p>코드를 작성하는 순간에도</p>
<blockquote>
<p>한 명만 생각하는 게 아니다.</p>
</blockquote>
<p>코드를 작성하면서
왜 이렇게 짜는지 설명하고
옆에서는 계속 질문하고 체크한다.</p>
<p>이게 계속 반복된다.</p>
<h3 id="중간중간-계속-수정되는-방향">중간중간 계속 수정되는 방향</h3>
<p>혼자 개발할 때는
한 번 정한 방향을 끝까지 밀고 가는 경우가 많다.</p>
<p>근데 페어 프로그래밍은 다르다.</p>
<ul>
<li>더 좋은 방법이 나오면 바로 바꾸고</li>
<li>놓친 부분이 있으면 바로 잡는다.</li>
</ul>
<p>즉
<strong>계속 방향을 조정하면서 진행한다.</strong></p>
<h3 id="처음-하면-느껴지는-특징">처음 하면 느껴지는 특징</h3>
<p>처음 하면 대부분 이렇게 느낀다.</p>
<ul>
<li>느리다</li>
<li>답답하다</li>
<li>혼자 하는 게 더 빠르다.</li>
</ul>
<p>근데 이게 정상이다.</p>
<p>왜 이렇게 만드는지 이해하고
그 과정을 공유하는 게 목적이다.</p>
<p>그래서 결과적으로는
코드를 만드는 과정 자체가 달라진다.</p>
<hr>
<h2 id="4-해보면-얻을-수-있는-것들">4. 해보면 얻을 수 있는 것들</h2>
<h3 id="강제로-넓어지는-시야">강제로 넓어지는 시야</h3>
<p>혼자 개발할 때는
내가 아는 방식 안에서만 선택하게 된다.</p>
<p>근데 페어 프로그래밍을 하면
다른 사람이 생각하는 방식을 계속 보게 된다.</p>
<ul>
<li>같은 문제를 다르게 접근하는 방식</li>
<li>내가 생각 못한 구조</li>
<li>더 단순한 해결 방법</li>
</ul>
<p>이게 계속 들어온다.</p>
<p>그래서 자연스럽게
<strong>&quot;내 방식 하나&quot;에서 &quot;여러 선택지&quot;로 바뀐다.</strong></p>
<h3 id="바로-들춰지는-애매한-이해">바로 들춰지는 애매한 이해</h3>
<p>혼자 할 때는</p>
<ul>
<li>대충 아는 상태</li>
<li>감으로 짠 코드</li>
</ul>
<p>이게 그냥 넘어간다.</p>
<p>근데 페어 프로그래밍은</p>
<ul>
<li>왜 이렇게 짰는지</li>
<li>이게 어떻게 동작하는지</li>
</ul>
<p>이걸 말로 풀어야 한다.</p>
<p>즉 설명을 해야 한다.</p>
<p>이 순간
<strong>모르면 바로 티가 난다.</strong></p>
<h3 id="코드에-이유가-생기기-시작한다">코드에 이유가 생기기 시작한다.</h3>
<p>혼자 개발하면
&quot;일단 되게 만드는 코드&quot;가 많다.</p>
<p>근데 페어로 하면</p>
<ul>
<li>이 구조 왜 썼는지</li>
<li>이 선택이 맞는지</li>
</ul>
<p>계속 확인을 받는다.</p>
<p>그래서 모든 코드에 이유가 붙기 시작한다.</p>
<h3 id="줄어드는-실수">줄어드는 실수</h3>
<p>혼자 개발하면
내가 놓친 건 끝까지 놓친다.</p>
<p>근데 둘이 보면
한 명이 놓친 걸 다른 한 명이 잡는다.</p>
<p>이게 생각보다 크다.</p>
<h3 id="결국-바뀌는-건-코드가-아닌-사고방식">결국 바뀌는 건 코드가 아닌 사고방식</h3>
<p>페어 프로그래밍을 하면
단순히 코드 퀄리티가 좋아지는 게 아니라
<strong>생각하는 방식 자체가 바뀐다.</strong></p>
<p>설명 가능한 코드만 쓰게 되고
선택에 근거를 붙이게 되고
더 나은 방법을 계속 고민하게 된다.</p>
<p>이게 핵심이다.</p>
<hr>
<h2 id="5-페어-프로그래밍-시작하기">5. 페어 프로그래밍 시작하기</h2>
<h3 id="처음부터-완벽-x">처음부터 완벽 X</h3>
<p>페어 프로그래밍이라고 해서
규칙 완벽하게 지키고
역할 정확하게 나누고
이상적인 방식으로 시작하려고 하면</p>
<p>시작도 못한다.</p>
<p>처음은 그냥
<strong>&quot;같이 해본다&quot; 수준이면 충분하다.</strong></p>
<h3 id="기능-하나-잡고-같이-해보는-게-시작">기능 하나 잡고 같이 해보는 게 시작</h3>
<p>가장 현실적인 시작 방법은 이거다.</p>
<ul>
<li>작은 기능 하나 정한다.</li>
<li>둘이 같이 붙어서 구현해본다.</li>
</ul>
<p>예를 들어</p>
<ul>
<li>상품 목록 가져오기</li>
<li>검색 기능</li>
<li>간단한 폼 처리</li>
</ul>
<p>이런 것부터 시작하면 된다.</p>
<h3 id="처음엔-어색한-게-정상">처음엔 어색한 게 정상</h3>
<p>처음 하면 무조건 이상하다.</p>
<p>말하면서 코딩하는 게 어색하고
옆에서 보는 것도 어색하고
서로 타이밍도 안 맞는다.</p>
<p>이게 정상이다.</p>
<h3 id="속도보다-같이-이해하는-경험에-집중">속도보다 &quot;같이 이해하는 경험&quot;에 집중</h3>
<p>혼자 하면 더 빠른 건 사실이다.</p>
<p>근데 페어 프로그래밍은
<strong>빠르게 만드는 게 목적이 아니다.</strong></p>
<p>왜 이렇게 만드는지
서로 어떻게 생각하는지
이걸 맞춰가는 과정이 핵심이다.</p>
<h3 id="부담을-가지는-순간-오히려-어려워진다">부담을 가지는 순간 오히려 어려워진다.</h3>
<p>많이들 여기서 막힌다.</p>
<ul>
<li>&quot;내가 못하면 어떡하지?&quot;</li>
<li>&quot;민폐 아닐까?&quot;</li>
</ul>
<p>근데 이 구조 자체가
서로 부족한 걸 드러내는 구조다.</p>
<p>그래서 오히려
모르는 걸 말하는 게 맞는 방향이다.</p>
<h3 id="결국-중요한-건-경험">결국 중요한 건 경험</h3>
<p>페어 프로그래밍은</p>
<blockquote>
<p>설명으로 이해하는 게 아니라
해봐야 이해된다.</p>
</blockquote>
<p>한 번만 제대로 해보면
왜 느린지 왜 필요한지
뭐가 달라지는지 바로 체감된다.</p>
<hr>
<p>실제로 스프린트 강사님이 따라오기 어려운 인원들을 모아
각자의 화면을 공유하면서 함께 실습을 진행하시는데</p>
<p>나는 내 모습이 드러나는 게 부담스러워 쉽게 참여하지 못했다.
그런데 돌아보니 그 방식이
페어 프로그래밍과 꽤 비슷했다.</p>
<p>결국에 내가 애매하게 알고 있는 모습들을
다 들춰내기 위한 방식이었고
내 애매함이 들춰지면 그건 또 성장으로 이어질 것이다.</p>
<p>그래서 멘토님이 페어 프로그래밍 방식을 추천하셨던 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[useState vs useSearchParams]]></title>
            <link>https://velog.io/@hogu__giriboy/useState-vs-useSearchParams</link>
            <guid>https://velog.io/@hogu__giriboy/useState-vs-useSearchParams</guid>
            <pubDate>Tue, 31 Mar 2026 02:39:24 GMT</pubDate>
            <description><![CDATA[<p>이번에 스프린트 미션으로 마켓 상품 정렬 및 검색 기능을 다뤘는데,
useState를 주로 써서 useState를 활용해서 작성했다.</p>
<p>그러던 와중에 궁금한 점이 하나 생겼다.</p>
<pre><code class="language-javascript">...
const [page, setPage] = useState(1);
const [orderBy, setOrderBy] = useState(&quot;recent&quot;);
const [keyword, setKeyword] = useState(&quot;&quot;);

useEffect(() =&gt; {
  async function loadProducts() {
    const res = await fetch(
      `https://.../products?page=${page}&amp;pageSize=${pageSize}&amp;orderBy=${orderBy}&amp;keyword=${keyword}`
    );
  }

  loadProducts();
}, [page, pageSize, orderBy, keyword]);
...</code></pre>
<p>위 코드는 useState를 활용한 코드다.
근데 이제 위 코드를
useSearchParams를 활용한 코드로 바꿔보면</p>
<pre><code class="language-javascript">...
const [searchParams, setSearchParams] = useSearchParams();

const page = Number(searchParams.get(&quot;page&quot;)) || 1;
const orderBy = searchParams.get(&quot;orderBy&quot;) || &quot;recent&quot;;
const keyword = searchParams.get(&quot;keyword&quot;) || &quot;&quot;;

useEffect(() =&gt; {
  async function loadProducts() {
    const res = await fetch(
      `/products?page=${page}&amp;orderBy=${orderBy}&amp;keyword=${keyword}`
    );
  }

  loadProducts();
}, [page, orderBy, keyword]);
...</code></pre>
<p>이런 식으로 쓰이고,
<code>fetch</code> 요청 형식이 똑같다.
그래서 의문이 들었던 게 있었다.</p>
<blockquote>
<p>이러면 useSearchParams는 굳이 왜 있는거지?</p>
</blockquote>
<p>그래서 옳거니 하고 바로 블로그 주제로 잡았다.</p>
<hr>
<h2 id="1-요청-코드가-아니라-상태의-위치-차이">1. 요청 코드가 아니라 상태의 위치 차이</h2>
<p>useState를 사용하든,
useSearchParams를 사용하든,
결국 fetch 요청을 보면 이렇게 된다.</p>
<pre><code class="language-js">/products?page=${page}&amp;orderBy=${orderBy}&amp;keyword=${keyword}</code></pre>
<p>서버에서 보내는 요청 형식은 똑같다.
그래서 처음에는 이렇게 생각했다.</p>
<blockquote>
<p>둘 다 결국 같은 요청을 보내는 거면
그냥 useState만 써도 되는 거 아닌가?</p>
</blockquote>
<p>그런데 여기서 중요한 건
<strong>요청 형식이 아니라 값이 어디에서 관리되고 있느냐</strong>였다.</p>
<h3 id="usestate로-관리하는-경우">useState로 관리하는 경우</h3>
<pre><code class="language-js">const [page, setPage] = useState(1);
const [orderBy, setOrderBy] = useState(&quot;recent&quot;);
const [keyword, setKeyword] = useState(&quot;&quot;);</code></pre>
<p>이 값들은 어디에 저장되어 있을까?</p>
<p>정답은 <strong>컴포넌트 내부 상태</strong>다.</p>
<p>즉 이 값들은 이 컴포넌트가 살아있는 동안만 존재하고
페이지를 새로고침하면 초기화되고
URL 주소에는 아무 정보도 남지 않는다.</p>
<p>이 상태는 <strong>컴포넌트 내부에서만 유지되는 상태</strong>다.</p>
<h3 id="usesearchparams로-관리하는-경우">useSearchParams로 관리하는 경우</h3>
<pre><code class="language-js">const [searchParams, setSearchParams] = useSearchParams();

const page = Number(searchParams.get(&quot;page&quot;)) || 1;
const orderBy = searchParams.get(&quot;orderBy&quot;) || &quot;recent&quot;;
const keyword = searchParams.get(&quot;keyword&quot;) || &quot;&quot;;</code></pre>
<p>이번에는 값이 어디에 있을까?</p>
<p>이 값들은 <strong>컴포넌트 내부 상태가 아니라 URL 쿼리스트링에 저장</strong>되어 있다.</p>
<p>주소창을 보면 이렇게 바뀐다.</p>
<pre><code class="language-text">/products?page=2&amp;orderBy=price&amp;keyword=phone</code></pre>
<p>즉 상태가 <strong>컴포넌트가 아니라 URL에 존재</strong>하게 된다.</p>
<p>정리하면 차이는 fetch 요청 코드가 아니라 상태가 어디에 저장되는지에 있다.</p>
<ul>
<li>useState → 상태가 컴포넌트 안에서 관리됨</li>
<li>useSearchParams → 상태가 URL 쿼리스트링에서 관리됨</li>
</ul>
<p>이 차이 때문에 페이지 동작 방식이 완전히 달라진다.</p>
<ul>
<li>새로고침</li>
<li>링크 공유</li>
<li>뒤로가기</li>
<li>앞으로가기</li>
<li>북마크</li>
<li>탭 복사</li>
</ul>
<p>이 모든 동작이 <strong>URL을 기준으로 동작하기 때문</strong>이다.</p>
<p>그래서 검색, 정렬, 페이지 번호 같은 값들은
컴포넌트 상태보다 URL 쿼리스트링으로 관리하는 것이 더 자연스럽다.</p>
<hr>
<h2 id="2-컴포넌트-안에만-있으면-생기는-문제">2. 컴포넌트 안에만 있으면 생기는 문제</h2>
<p>앞에서 정리한 것처럼</p>
<ul>
<li>useState → 상태가 컴포넌트 안에서 관리됨</li>
<li>useSearchParams → 상태가 URL 쿼리스트링에서 관리됨</li>
</ul>
<p>여기까지 보면 단순히 저장 위치만 다른 것처럼 보일 수 있다.
그런데 이 저장 위치 차이가 실제 동작에서 큰 차이를 만든다.</p>
<p>특히 검색, 정렬, 페이지 번호 같은 값들은
컴포넌트 안에만 있으면 여러 문제가 생긴다.</p>
<h3 id="새로고침하면-사라지는-상태">새로고침하면 사라지는 상태</h3>
<p>예를 들어</p>
<ol>
<li>검색어 입력</li>
<li>정렬 선택</li>
<li>3페이지까지 이동</li>
<li>새로고침</li>
</ol>
<p>useState로 관리하고 있었다면 상태는 전부 초기화된다.</p>
<ul>
<li>검색어 → 초기화</li>
<li>정렬 → 초기화</li>
<li>페이지 → 1페이지로 돌아감</li>
</ul>
<p>왜냐하면 useState는 <strong>컴포넌트 내부 메모리에 있는 상태</strong>라서
페이지가 새로 로드되면 다시 처음부터 시작하기 때문이다.</p>
<p>반대로 URL에 이렇게 남아 있다면</p>
<pre><code class="language-text">/products?page=3&amp;orderBy=price&amp;keyword=phone</code></pre>
<p>새로고침을 해도
브라우저는 같은 URL을 다시 요청하고,
컴포넌트는 URL에서 값을 다시 읽어오면 된다.</p>
<p>즉 상태가 유지된다.</p>
<h3 id="링크-공유-불가">링크 공유 불가</h3>
<p>이건 실제 서비스에서 중요한 부분이다.</p>
<p>예를 들어 어떤 상품을 검색해서
필터 + 정렬 + 페이지 이동까지 한 상태라고 해보자.</p>
<pre><code class="language-text">검색어: phone
정렬: 가격순
페이지: 3</code></pre>
<p>이 화면을 다른 사람에게 보여주고 싶어서
주소를 복사해서 보내면 어떻게 될까?</p>
<ul>
<li>useState → 상대방은 기본 화면이 뜬다</li>
<li>useSearchParams → 같은 검색 결과 화면이 뜬다</li>
</ul>
<p>왜냐하면 상태가 컴포넌트 안에 있느냐,
URL에 있느냐의 차이이기 때문이다.</p>
<p>그래서 실제 쇼핑몰, 게시판, 검색 페이지 같은 곳에서는
검색 조건, 정렬, 페이지 번호를 URL 쿼리스트링으로 관리하는 경우가 많다.</p>
<h3 id="뒤로가기--앞으로가기-동작-불가">뒤로가기 / 앞으로가기 동작 불가</h3>
<p>페이지 이동 흐름을 생각해보자</p>
<ul>
<li>1페이지 + 2페이지 → 3페이지</li>
</ul>
<p>이 상태에서 뒤로가기를 누르면
사용자는 보통 이렇게 기대한다.</p>
<ul>
<li>3페이지 → 뒤로가기 → 2페이지</li>
</ul>
<p>그런데 상태가 useState에만 있으면
브라우저는 상태 변화를 기억하지 못한다.</p>
<p>왜냐하면 브라우저는 <strong>URL 이동 기록</strong>만 기억하기 때문이다.</p>
<p>반대로 URL이 이렇게 바뀌고 있었다면</p>
<pre><code class="language-text">/products?page=1
/products?page=2
/products?page=3</code></pre>
<p>뒤로가기를 누르면 URL이 바뀌고,
우리는 URL에서 page 값을 다시 읽으면 된다.</p>
<p>그래서 뒤로가기와 앞으로가기 동작이 자연스럽게 동작한다.</p>
<p>검색, 정렬, 페이지 번호 같은 값들은
단순히 화면을 위한 상태가 아니라
현재 어떤 페이지 상태를 보고 있는지를 설명하는 상태다.</p>
<p>그래서 이 값들은 컴포넌트 상태가 아니라 페이지 상태라고 볼 수 있다.</p>
<p>그리고 페이지 상태는
컴포넌트 안이 아니라
<strong>URL과 함께 움직이는 것이 더 자연스럽다.</strong></p>
<p>그래서 여기서 useSearchParams를 사용하게 되는 거다.</p>
<p>즉 useSearchParams는 단순히 값을 저장하는 도구가 아니라,
페이지 상태를 URL과 동기화하기 위한 도구라고 볼 수 있다.</p>
<hr>
<h2 id="3-usesearchparams의-존재-이유">3. useSearchParams의 존재 이유</h2>
<p>앞에서 정리한 내용을 한 번 이어서 생각해보자.</p>
<p>useState로 관리해도 fetch 요청은 만들 수 있다.
그래서 처음에는 useSearchParams가 왜 필요한지 잘 느껴지지 않을 수 있다.
그런데 검색, 정렬, 페이지 번호는 화면 상태가 아니라 페이지 상태다.
페이지 상태는 URL과 함께 관리되는 것이 더 자연스럽다.
그래서 이런 페이지 상태를 관리할 때 useSearchParams를 사용한다.</p>
<p>여기서 중요한 건</p>
<blockquote>
<p>useSearchParams는 fetch 요청을 쉽게 만들기 위한 훅이 아니다.
URL에 상태를 저장하기 위한 훅이다.</p>
</blockquote>
<p>이게 거의 이 글의 핵심이다.</p>
<h3 id="usestate-vs-usesearchparams">useState vs useSearchParams</h3>
<table>
<thead>
<tr>
<th>구분</th>
<th>useState</th>
<th>useSearchParams</th>
</tr>
</thead>
<tbody><tr>
<td>상태 저장 위치</td>
<td>컴포넌트 내부 상태</td>
<td>URL 쿼리스트링</td>
</tr>
<tr>
<td>새로고침</td>
<td>상태 사라짐</td>
<td>상태 유지</td>
</tr>
<tr>
<td>링크 공유</td>
<td>불가능</td>
<td>가능</td>
</tr>
<tr>
<td>뒤로가기/앞으로가기</td>
<td>상태 이동 안 됨</td>
<td>상태 이동</td>
</tr>
<tr>
<td>사용 용도</td>
<td>UI 상태</td>
<td>페이지 상태</td>
</tr>
</tbody></table>
<p>여기서 가장 중요한 차이는
useState는 화면(UI)을 위한 상태를 저장하고,
useSearchParams는 페이지 상태를 저장한다는 점이다.</p>
<h3 id="언제-무엇을-쓸까">언제 무엇을 쓸까</h3>
<h4 id="usestate를-쓰는-경우">useState를 쓰는 경우</h4>
<ul>
<li>모달 열림 / 닫힘</li>
<li>드롭다운 열림 / 닫힘</li>
<li>input 입력값</li>
<li>탭 선택</li>
<li>체크박스</li>
<li>토글 버튼</li>
<li>hover 상태</li>
</ul>
<p><strong>→ 화면(UI) 안에서만 필요한 상태</strong></p>
<h4 id="usesearchparams를-쓰는-경우">useSearchParams를 쓰는 경우</h4>
<ul>
<li>검색어</li>
<li>정렬 방식</li>
<li>페이지 번호</li>
<li>필터 조건</li>
<li>카테고리 선택</li>
<li>보기 방식 (grid/list)</li>
</ul>
<p><strong>→ 현재 어떤 페이지 상태를 보고 있는지 설명하는 상태</strong></p>
<p>여기서 기준 문장 하나로 정리하면 좋다.</p>
<blockquote>
<p>이 값은 화면 안에서만 필요한 값인가?
아니면 현재 페이지를 설명하는 값인가?</p>
</blockquote>
<p>이 질문으로 대부분의 경우 어떤 상태 관리 방법을 써야 할지 결정할 수 있다.</p>
<p>useState와 useSearchParams의 차이는 기능의 차이가 아니라 상태를 저장하는 위치의 차이다.</p>
<p>컴포넌트 내부 상태는 useState로 관리하고,
페이지 상태는 useSearchParams로 관리한다.</p>
<hr>
<p>처음에는 useState로도 충분히 동작하는 것처럼 보여서 주제를 잡았다.
fetch 요청만 보면 useState와 useSearchParams는 큰 차이가 없어 보이기도 한다.</p>
<p>하지만 차이는 요청 방식이 아니라 상태가 어디에 저장되는지에 있었다.
검색, 정렬, 페이지 번호 같은 값들은 단순히 화면을 위한 상태가 아니라
현재 어떤 페이지 상태를 보고 있는지를 설명하는 상태였다.</p>
<p>그래서 이런 값들은 컴포넌트 안이 아니라 URL과 함께 관리되어야 했고,
그 역할을 하는 것이 useSearchParams였다.</p>
<p>결국 React에서 상태를 관리할 때 중요한 것은
어떤 훅을 사용할 것인가가 아니라
이 상태를 어디에 저장해야 하는지를 먼저 생각하는 것이었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AI "잘" 쓰기]]></title>
            <link>https://velog.io/@hogu__giriboy/AI-%EC%9E%98-%EC%93%B0%EA%B8%B0</link>
            <guid>https://velog.io/@hogu__giriboy/AI-%EC%9E%98-%EC%93%B0%EA%B8%B0</guid>
            <pubDate>Mon, 23 Mar 2026 06:17:12 GMT</pubDate>
            <description><![CDATA[<p>&quot;</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[DOM을 직접 바꾸지 않는 이유: Virtual DOM]]></title>
            <link>https://velog.io/@hogu__giriboy/DOM%EC%9D%84-%EC%A7%81%EC%A0%91-%EB%B0%94%EA%BE%B8%EC%A7%80-%EC%95%8A%EB%8A%94-%EC%9D%B4%EC%9C%A0-Virtual-DOM</link>
            <guid>https://velog.io/@hogu__giriboy/DOM%EC%9D%84-%EC%A7%81%EC%A0%91-%EB%B0%94%EA%BE%B8%EC%A7%80-%EC%95%8A%EB%8A%94-%EC%9D%B4%EC%9C%A0-Virtual-DOM</guid>
            <pubDate>Sat, 21 Mar 2026 08:40:02 GMT</pubDate>
            <description><![CDATA[<h2 id="1-렌더링-이후에-남는-문제">1. 렌더링 이후에 남는 문제</h2>
<p>이전 글에서 살펴봤듯이
React에서 렌더링은 <strong>컴포넌트 함수가 다시 실행되는 과정</strong>이고,
이 자체는 생각보다 큰 비용이 들지 않는 작업이다.</p>
<p>그렇다면 여기서 자연스럽게 하나의 질문이 생긴다.</p>
<blockquote>
<p>그럼 실제로 성능에 영향을 주는 건 무엇일까?</p>
</blockquote>
<p>핵심은 렌더링 자체가 아니라,
<strong>렌더링 이후 실제 화면을 업데이트하는 과정,</strong>
즉 <strong>DOM을 다루는 작업</strong>이다.</p>
<h3 id="dom이-왜-문제일까">DOM이 왜 문제일까</h3>
<p>JavaScript에서 변수 하나 바꾸는 건 가볍다.
하지만 DOM은 다르다.</p>
<p>DOM을 변경하는 순간 브라우저는 단순히 값만 바꾸는 게 아니라
<strong>화면을 다시 계산하는 과정</strong>을 거친다.</p>
<p>예를 들어 생각해보면 이런 느낌이다.</p>
<ul>
<li>JS 연산 → 계산만 하고 끝</li>
<li>DOM 변경 → 계산 + 화면 다시 그리기</li>
</ul>
<p>즉 DOM을 건드린다는 것은 단순히 값을 바꾸는 것이 아니라,
<strong>브라우저에게 화면을 다시 계산하고 그리도록 요청하는 것</strong>과 같다.</p>
<h3 id="그래서-생기는-문제">그래서 생기는 문제</h3>
<p>만약 DOM을 계속 직접 조작하게 되면 어떻게 될까?</p>
<pre><code class="language-javascript">element.textContent = &quot;A&quot;;
element.textContent = &quot;B&quot;;
element.textContent = &quot;C&quot;;</code></pre>
<p>이렇게 여러 번 바꾸면
브라우저는 그때마다 화면을 다시 계산하려고 한다.</p>
<p>결과적으로 불필요한 작업이 계속 발생한다.</p>
<p>그럼 단순하게 DOM을 덜 건드리면 되는 게 아닌가 싶은 생각이 들 수 있다.
그래서 이 지점에서 React의 방식이 등장한다.</p>
<hr>
<h2 id="2-그래서-등장한-virtual-dom">2. 그래서 등장한: Virtual DOM</h2>
<p>앞에서 본 것처럼
문제는 렌더링이 아니라 <strong>DOM을 직접 자주 변경하는 것</strong>이었다.</p>
<p>그렇다면 해결 방법은 단순하다.</p>
<blockquote>
<p><strong>DOM을 바로 건드리지 말고, 한 번 모아서 처리하자</strong></p>
</blockquote>
<p>이 아이디어에서 등장한 것이 바로 <strong>Virtual DOM</strong>이다.</p>
<h3 id="virtual-dom">Virtual DOM?</h3>
<p>Virtual DOM은 말 그대로
<strong>실제 DOM이 아니라, 메모리 위에 만들어진 DOM의 표현(추상화된 구조)</strong>다.</p>
<p>React는 상태가 변경되면
새로운 Virtual DOM을 생성하고 이전 Virtual DOM과 비교하여 <strong>변경 사항을 계산</strong>한다.</p>
<p>구조를 보면 다음과 같다.</p>
<ol>
<li>컴포넌트 실행</li>
<li>새로운 Virtual DOM 생성</li>
<li>이전 Virtual DOM과 비교</li>
<li>바뀐 부분만 실제 DOM에 반영</li>
</ol>
<h3 id="이-구조의-존재-가치">이 구조의 존재 가치</h3>
<p>핵심은 하나다.</p>
<blockquote>
<p>비싼 작업은 최소화하고, 싼 작업은 여러 번 해도 된다</p>
</blockquote>
<ul>
<li>Virtual DOM 비교 → JS 연산 (가벼움)</li>
<li>실제 DOM 변경 → 브라우저 작업 (무거움)</li>
</ul>
<p>그래서 React는 Virtual DOM을 통해 변경 사항을 여러 번 계산하고
실제 DOM 변경은 필요한 부분에만 최소한으로 적용하는 방식을 선택한 것이다.</p>
<p>이번에도 공사로 비유할 수 있다.</p>
<p>직접 DOM을 건드리는 방식은 수정할 때마다 바로 공사하는 느낌이다.
벽 하나 바뀌면 공사, 창문 하나 바뀌면 또 공사하는 방식이라면,</p>
<p>Virtual DOM은 <strong>설계도를 먼저 수정한 뒤, 필요한 부분만 실제 공사에 반영하는 방식</strong>이다.
설계도에서 바뀐 부분을 정리하고 실제 공사는 필요한 부분만 진행하는 방식이다.</p>
<p>즉 Virtual DOM은 화면을 그리기 위한 것이 아니라,
변경 사항을 계산하기 위한 중간 단계라고 볼 수 있다.</p>
<hr>
<h2 id="3-react는-어떻게-최적화하는가">3. React는 어떻게 최적화하는가</h2>
<p>Virtual DOM을 쓴다는 건 알았다.
그럼 중요한 건 이거다.</p>
<blockquote>
<p>어떻게 비교하고, 어떻게 최소 변경만 반영할까?</p>
</blockquote>
<h3 id="이전-결과-vs-새로운-결과">이전 결과 vs 새로운 결과</h3>
<p>React는 상태(state)나 props가 변경되어 렌더링이 발생할 때
새로운 Virtual DOM을 만든다.</p>
<p>그리고 그 이전 Virtual DOM과 새로운 Virtual DOM을
비교해서 <strong>달라진 부분을 찾는다.</strong></p>
<p>이 과정을 보통 <strong>diffing</strong>이라고 한다.</p>
<h3 id="diffing">diffing?</h3>
<p>비교 과정에서 React는 단순히 전체를 바꾸지 않고,
<strong>어디가 바뀌었는지</strong>를 찾는다.</p>
<p>예를 들어</p>
<pre><code class="language-javascript">&lt;h1&gt;Hello&lt;/h1&gt;</code></pre>
<p>에서</p>
<pre><code class="language-javascript">&lt;h1&gt;Hello World&lt;/h1&gt;</code></pre>
<p>로 바뀌었다면</p>
<p><code>&lt;h1&gt;</code> 요소 자체를 교체하는 것이 아니라
<strong>내용만 변경한다고 판단한다.</strong></p>
<h3 id="실제-dom-반영-reconciliation">실제 DOM 반영 (Reconciliation)</h3>
<p>diffing으로 변경된 부분을 찾았으면
이제 그걸 실제 DOM에 반영한다.</p>
<p>이 과정을 <strong>Reconciliation</strong>이라고 한다.</p>
<p>핵심은 <strong>필요한 부분만 업데이트</strong>한다는 것이다.
요소 타입이 같으면 내부 내용과 속성을 업데이트하고,
요소 타입이 다르면 기존 요소를 제거하고 새로 생성한다.</p>
<p>이 방식 때문에 React는
전체를 다시 그리지 않고 필요한 부분만 변경한다.</p>
<p>즉 다음과 같은 구조가 된다.</p>
<ol>
<li>렌더링 (계산)</li>
<li>Virtual DOM 비교</li>
<li>변경된 부분만 실제 DOM 반영</li>
</ol>
<p>이게 가능한 이유는
렌더링 과정은 대부분 JavaScript 계산 작업으로 비교적 가볍고,
실제 DOM 변경은 브라우저의 레이아웃과 화면 업데이트 과정이 포함되어
비용이 크기 때문이다.</p>
<p>그래서 React는 렌더링을 다시 수행하여 Virtual DOM을 만들고
변경 사항을 비교하는 과정은 여러 번 수행하되,
실제 DOM 변경은 필요한 부분에만 최소한으로 적용하는 전략을 선택했다.</p>
<p>결국 React는 화면을 바로 변경하는 방식이 아니라,
먼저 <strong>Virtual DOM을 통해 변경 사항을 계산</strong>하고,
이전 결과와 비교하여 <strong>바뀐 부분만 실제 DOM에 반영하는 방식</strong>으로
화면을 업데이트한다.</p>
<p>즉 React의 렌더링 구조는
<strong>렌더링 → 비교 → 실제 DOM 반영</strong>
이라는 단계로 이루어져 있다고 볼 수 있다.</p>
<hr>
<h2 id="4-핵심-정리">4. 핵심 정리</h2>
<ul>
<li>DOM을 직접 자주 변경하면 브라우저가 레이아웃 계산과 화면 업데이트를 반복하게 되어 성능 비용이 커진다.</li>
<li>React는 실제 DOM을 바로 수정하지 않고, <strong>Virtual DOM이라는 메모리 위의 DOM 구조</strong>를 사용해 변경 사항을 먼저 계산한다.</li>
<li>상태(state)나 props가 변경되면 새로운 Virtual DOM이 생성되고, 이전 Virtual DOM과 비교하여 달라진 부분을 찾는다.</li>
<li>이 비교 과정은 <strong>diffing</strong>, 변경된 부분을 실제 DOM에 반영하는 과정은 <strong>reconciliation</strong>이라고 한다.</li>
<li>React는 전체 화면을 다시 그리는 것이 아니라 <strong>변경된 부분만 실제 DOM에 반영</strong>하는 방식으로 동작한다.</li>
<li>React의 핵심 전략은 <strong>렌더링 계산은 여러 번 수행하되, 실제 DOM 변경은 최소화하는 것</strong>이다.</li>
<li>정리하면 React의 화면 업데이트 과정은 <strong>렌더링 → Virtual DOM 비교 → 실제 DOM 반영</strong> 단계로 이루어진다.</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[렌더링 ≠ 화면 변경: 결과 계산]]></title>
            <link>https://velog.io/@hogu__giriboy/%EB%A0%8C%EB%8D%94%EB%A7%81-%ED%99%94%EB%A9%B4-%EB%B3%80%EA%B2%BD-%EA%B2%B0%EA%B3%BC-%EA%B3%84%EC%82%B0</link>
            <guid>https://velog.io/@hogu__giriboy/%EB%A0%8C%EB%8D%94%EB%A7%81-%ED%99%94%EB%A9%B4-%EB%B3%80%EA%B2%BD-%EA%B2%B0%EA%B3%BC-%EA%B3%84%EC%82%B0</guid>
            <pubDate>Fri, 20 Mar 2026 07:47:57 GMT</pubDate>
            <description><![CDATA[<h2 id="1-렌더링이란">1. &quot;렌더링&quot;이란</h2>
<h3 id="흔히-아는-렌더링">흔히 아는 렌더링</h3>
<p>보통 &quot;렌더링&quot;이라고 하면
<strong>화면을 다시 그리는 것</strong>이라고 생각하는 경우가 많다.</p>
<p>예를 들어
버튼을 눌렀더니, 화면이 바뀌고
뭔가 새로 그려지는 느낌이다.</p>
<p>그래서 자연스럽게
<strong>렌더링은 화면이 바뀐다</strong>라고 받아들이게 된다.</p>
<h3 id="react에서의-렌더링">React에서의 렌더링</h3>
<p>React에서의 렌더링은 조금 다르게 정의해야 한다.</p>
<blockquote>
<p>컴포넌트 함수가 다시 실행되는 것</p>
</blockquote>
<p>예를 들어 이런 코드가 있다고 해보자.</p>
<pre><code class="language-javascript">function App() {
  console.log(&quot;렌더링 실행&quot;);

  return &lt;h1&gt;Hello&lt;/h1&gt;;
}</code></pre>
<p>이 컴포넌트는 처음 실행될 때 한 번 실행되고 끝나는 것이 아니라,</p>
<blockquote>
<p><strong>상태가 바뀔 때마다 다시 실행된다.</strong></p>
</blockquote>
<p>결과적으로는 <strong>화면이 바뀌는 것</strong>처럼 보이기 때문에
렌더링을 <strong>화면을 다시 그리는 것 정도로만</strong> 오해하기 쉽다.</p>
<p>하지만 React 내부에서는</p>
<ol>
<li>함수 실행</li>
<li>결과 생성</li>
<li>(이후 과정 존재)</li>
</ol>
<p>이 흐름을 먼저 가져간다.</p>
<p>렌더링을 <strong>설계도를 만드는 과정</strong>으로 생각하면 훨씬 쉬워진다.</p>
<ul>
<li>컴포넌트 실행 = 설계도 다시 만듦</li>
<li>화면 변경 = 설계도 실제 반영</li>
</ul>
<p>즉 렌더링은 <strong>화면을 바꾸는 단계</strong>가 아니라
<strong>결과를 만드는 단계</strong>다.</p>
<hr>
<h2 id="2-react는-언제-다시-실행될까">2. React는 언제 다시 실행될까</h2>
<p>렌더링이 <strong>컴포넌트 함수 실행</strong>이라는 것을 알았으니
중요한 질문이 하나 남는다.</p>
<blockquote>
<p>그래서 <strong>컴포넌트는 언제 다시 실행될까</strong></p>
</blockquote>
<h3 id="state가-바뀌면">state가 바뀌면</h3>
<p>가장 기본이자 핵심이다.</p>
<pre><code class="language-javascript">const [count, setCount] = useState(0);</code></pre>
<p>여기서</p>
<pre><code class="language-javascript">setCount(1);</code></pre>
<p>이 순간 <strong>컴포넌트 함수가 다시 실행</strong>된다.</p>
<p>state를 <strong>화면을 결정하는 재료</strong>라고 생각하면 편하다.
<strong>재료가 바뀌게 된다면 결과를 다시 계산해야 한다.</strong></p>
<p>그래서 화면을 결정하는 <strong>state가 바뀌면</strong>
<strong>컴포넌트 함수가 다시 실행</strong>된다.</p>
<h3 id="props가-바뀌면">props가 바뀌면</h3>
<p>부모에서 자식으로 데이터를 내려줄 때</p>
<pre><code class="language-javascript">&lt;Child value={count} /&gt;</code></pre>
<p>부모에서 count가 바뀌면 <strong>새로운 props가 전달되면서</strong> 자식 컴포넌트도 다시 실행된다.</p>
<p>구조적으로 이런 식이 된다.</p>
<ol>
<li>부모 실행</li>
<li>새로운 props 전달</li>
<li>자식도 다시 실행</li>
</ol>
<h3 id="부모가-렌더링되면-자식도-같이">부모가 렌더링되면 자식도 같이</h3>
<p>꼭 props가 바뀌지 않아도 실행된다.</p>
<p>예를 들어</p>
<pre><code class="language-javascript">function Parent() {
  const [count, setCount] = useState(0);

  return &lt;Child /&gt;;
}</code></pre>
<p>여기서 setCount 호출하면
Parent가 다시 실행되고, 그 과정에서 Child도 함께 다시 실행된다.</p>
<p>왜냐하면 React 입장에서는</p>
<blockquote>
<p>Parent를 다시 실행되면
그 안에 있는 Child도 다시 실행된다.</p>
</blockquote>
<p>이렇게 보는 것이다.</p>
<p>정리하자면 렌더링 트리거는 다음 세 가지로 볼 수 있다.</p>
<ul>
<li>state 변경</li>
<li>props 변경</li>
<li>부모 컴포넌트 렌더링</li>
</ul>
<hr>
<h2 id="3-렌더링이-발생하면-벌어지는-일">3. 렌더링이 발생하면 벌어지는 일</h2>
<p>컴포넌트 함수는 결국 <strong>JSX를 반환한다.</strong></p>
<pre><code class="language-javascript">function App() {
  return &lt;h1&gt;Hello&lt;/h1&gt;;
}</code></pre>
<p>이 코드를 실행하면 결과로 <strong>JSX 형태의 구조</strong>가 만들어진다.</p>
<p>JSX는 <strong>UI를 선언적으로 표현하기 위한 문법</strong>이다.</p>
<p>이렇게 보면 된다.</p>
<pre><code class="language-javascript">&lt;h1&gt;Hello&lt;/h1&gt;</code></pre>
<p>이건 &quot;이런 UI를 만들겠다&quot;라는
<strong>설계도에 가까운 표현</strong>이다.</p>
<p>이 과정을 정리해보면 다음과 같다.</p>
<ol>
<li>컴포넌트 실행</li>
<li>새로운 JSX가 생성됨</li>
</ol>
<hr>
<h2 id="4-화면을-업데이트하는-방식">4. 화면을 업데이트하는 방식</h2>
<p>우선 다시 떠올려야 할 것이 있다.</p>
<ul>
<li>렌더링 ≠ 화면 변경</li>
<li>렌더링 = 결과 계산</li>
</ul>
<p>즉 state가 변경되어 컴포넌트가 실행되고,
JSX가 생성된 지금 상태는 다음과 같다고 말할 수 있다.</p>
<blockquote>
<p>새로운 UI 설계도는 만들어졌는데
아직 실제 화면에는 반영 안 된 상태</p>
</blockquote>
<h3 id="필요한-부분만">필요한 부분만</h3>
<p>React는 새로운 결과를 바탕으로 <strong>실제 화면을 업데이트한다.</strong>
여기서 중요한 건 <strong>전체를 다 갈아엎지 않는다</strong>는 것이다.</p>
<p>우리가 생각하는 렌더링은 화면 전체를 다시 그리는 방식이다.</p>
<p>하지만 React에서의 렌더링은 결과를 만든 뒤 <strong>필요한 부분만</strong> 반영하는 과정에 가깝다.</p>
<p>설계도로 다시 비유하자면</p>
<ul>
<li>렌더링 = 새 설계도 작성</li>
<li>화면 변경 = 공사</li>
</ul>
<p>근데 공사를 할 때
건물 전체를 부수는 게 아니라 바뀐 부분만 공사한다.</p>
<p>React도 같은 방식으로
렌더링 이후 <strong>이전 결과와 비교해 바뀐 부분만 화면에 반영된다.</strong></p>
<hr>
<h2 id="5-렌더링이-많으면-문제가-될까">5. 렌더링이 많으면 문제가 될까?</h2>
<p>많은 사람들이 헷갈리는 부분이다.</p>
<blockquote>
<p>렌더링이 많이 발생하면 성능이 나쁜 것 아닐까?</p>
</blockquote>
<p>직관적으로는 맞는 말 같다.</p>
<p>자주 실행되고, 많이 반복되면
비효율적이라고 오해하기 쉽다.</p>
<h3 id="react-기준에서-보기">React 기준에서 보기</h3>
<p>React 기준에서 렌더링 자체는 <strong>큰 비용이 드는 작업이 아닐 수 있다.</strong></p>
<p>왜냐하면 렌더링은 기본적으로 <strong>컴포넌트 함수 실행 과정</strong>이기 때문이다.</p>
<p>진짜 비용이 큰 건 실제 화면(DOM)을 바꾸는 작업으로,
이게 훨씬 무겁다.</p>
<p>그래서 구조는 다음과 같이 봐야 한다.</p>
<ul>
<li>렌더링 (가벼움)</li>
<li>실제 화면 변경 (무거움)</li>
</ul>
<p>그래서 React는 렌더링은 다시 수행하되,
<strong>실제 DOM 변경은 최소화하는 방향</strong>으로 동작한다.</p>
<hr>
<h2 id="6-핵심-정리">6. 핵심 정리</h2>
<ul>
<li>렌더링은 컴포넌트 함수가 다시 실행되는 것이다.</li>
<li>state, props, 부모 렌더링에 의해 발생한다.</li>
<li>렌더링 결과로 새로운 JSX 구조가 생성된다.</li>
<li>이 시점에서는 아직 화면이 바뀌지 않는다.</li>
<li>이후 실제 화면이 업데이트되는 과정이 따로 존재한다.</li>
<li>렌더링 자체보다 중요한 것은 <strong>실제 화면 변경 비용</strong>이다.</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[jQuery의 사용률은 70%에 가까운데, 왜 React를 배울까?]]></title>
            <link>https://velog.io/@hogu__giriboy/jQuery%EC%9D%98-%EC%82%AC%EC%9A%A9%EB%A5%A0%EC%9D%80-70%EC%97%90-%EA%B0%80%EA%B9%8C%EC%9A%B4%EB%8D%B0-%EC%99%9C-React%EB%A5%BC-%EB%B0%B0%EC%9A%B8%EA%B9%8C</link>
            <guid>https://velog.io/@hogu__giriboy/jQuery%EC%9D%98-%EC%82%AC%EC%9A%A9%EB%A5%A0%EC%9D%80-70%EC%97%90-%EA%B0%80%EA%B9%8C%EC%9A%B4%EB%8D%B0-%EC%99%9C-React%EB%A5%BC-%EB%B0%B0%EC%9A%B8%EA%B9%8C</guid>
            <pubDate>Tue, 17 Mar 2026 14:41:16 GMT</pubDate>
            <description><![CDATA[<p>벨로그엔 정말 다양한 게시글들이 있고,
이목을 끄는 글들도 많다.</p>
<p>이번 글의 시작은
&quot;대회에서 멘헤라야차서비스 만든 썰&quot; 라는 게시글이었다.</p>
<p>주제부터 너무 유쾌하고 신박해서 쭉 보다가,
우연히 jQuery와 React를 비교하는 글을 보게 됐다.</p>
<p>사실 아직 나는 개발을 막 시작한 입장에서
두 기술 모두 익숙하지 않다.</p>
<p>jQuery는 스프린트 팀에서 현업 경험이 있으신 분이
주로 사용하셨다고 해서 &quot;이런 게 있구나&quot; 정도로 알고 있었고,</p>
<p>React는 요즘 웹 개발에서 많이 사용되는 UI 라이브러리이며,
Next.js와 같은 프레임워크와 함께 사용되는 경우가 많다는 정도만 알고 있었다.</p>
<p><img src="https://velog.velcdn.com/images/hogu__giriboy/post/eac0305d-5c43-4d53-9e53-67566de53243/image.png" alt="JavaScript Libraries">
(해당 자료는 <a href="https://w3techs.com/">해당 링크</a>에서)</p>
<p>근데 이제 통계 자료를 봐도
2026년 1월 1일 기준으로</p>
<p>jQuery는 약 70%,
React는 약 6.2%의 웹사이트에서 사용되고 있었다.</p>
<p>물론 React는 상승세이고,
jQuery를 포함한 일부 라이브러리는 사용 비율이 감소하는 추세지만 ...</p>
<p>이 숫자만 보면서 자연스럽게 한 가지 의문이 생겼다.</p>
<blockquote>
<p>그럼 React보다 jQuery가 더 중요한 기술 아닌가?</p>
</blockquote>
<p>이러한 의문이 들어서 두 라이브러리를
조금 더 깊게 알아보게 되었다.</p>
<hr>
<h2 id="1-사용률이라는-숫자의-의미">1. 사용률이라는 숫자의 의미</h2>
<h3 id="얼마나-쓰이냐-x-얼마나-깔려있냐-o">얼마나 쓰이냐 X 얼마나 깔려있냐 O</h3>
<p>우리가 처음 통계를 봤을 때는 이렇게 생각하기 쉽다.</p>
<blockquote>
<p>jQuery가 70%니까 지금 제일 많이 쓰는 기술이네?</p>
</blockquote>
<p>사실 나도 이렇게 생각해서 이 주제를 잡게 됐지만
사실 이 해석은 살짝 빗나갔다.</p>
<p>여기서 말하는 <strong>사용률</strong>은
<strong>현재 개발에서 얼마나 쓰이는가</strong>가 아니라
<strong>웹사이트에 포함되어 있는가</strong>를 의미한다.</p>
<h3 id="왜-이런-차이가-생기는가">왜 이런 차이가 생기는가</h3>
<p>웹 서비스는 다른 소프트웨어 분야보다 <strong>상대적으로 유지 기간이 긴 편</strong>이다.</p>
<p>예를 들어 하나의 사이트가 만들어지면
1~2년 쓰고 버리는 게 아니라
<strong>5년, 10년 이상 유지되는 경우가 많다.</strong></p>
<p>특히</p>
<ul>
<li>기업 홈페이지</li>
<li>쇼핑몰</li>
<li>워드프레스 사이트</li>
</ul>
<p>이런 것들은 <strong>한 번 만들면 계속 유지, 부분 수정</strong>하는 구조를 갖는다.</p>
<h3 id="계속-남게-되는-jquery">계속 남게 되는 jQuery</h3>
<p>과거 웹 개발은 거의 <code>HTML + CSS + jQuery</code>와 같은 구조였다.</p>
<p>이 시기에 만들어진 사이트들이 아직도 살아있기 때문에
jQuery는 계속 통계에 잡힌다.</p>
<p>반대로 React는 비교적 최근에 등장한 기술이기 때문에,
과거에 만들어진 웹사이트에는 포함되어 있지 않은 경우가 많다.</p>
<p>중요한 건 지금 활발하게 쓰고 있어서가 아니라
<strong>이미 만들어져 있어서 남아있는 것</strong> 이라는 점이다.</p>
<p>비유로 보면 이런 느낌이다.</p>
<pre><code class="language-text">도로 위 자동차 통계

2005년 아반떼 → 아직도 엄청 많음
최신 전기차 → 점점 늘어나는 중</code></pre>
<p>그래서 통계를 보면 옛날 차가 훨씬 많다고 착각할 수 있다.</p>
<p>하지만 실제 흐름은 <strong>신규 프로젝트에서 최신 기술이 주로 선택된다는 점</strong>이다.</p>
<h3 id="실제-개발에서는">실제 개발에서는</h3>
<p>지금 새로 만드는 웹 서비스는 대부분 이렇게 간다.</p>
<ul>
<li>React</li>
<li>Next.js</li>
<li>Vue</li>
</ul>
<p>jQuery로 새 프로젝트를 시작하는 경우는 거의 없다.</p>
<p>즉</p>
<ul>
<li>jQuery → &quot;남아있는 기술&quot;</li>
<li>React → &quot;현재 신규 프로젝트에서 주로 선택되는 기술&quot;</li>
</ul>
<p>이렇게 보는 게 맞다.</p>
<p><img src="https://velog.velcdn.com/images/hogu__giriboy/post/97be3535-46ff-4d6f-be65-f3b3af470272/image.png" alt="npm packege download">
(해당 자료는 <a href="https://npmtrends.com/jquery-vs-react">해당 링크</a>에서)</p>
<p>이건 또다른 통계자료로 npm 패키지 다운로드 수를 비교한 자료인데,
React 관련 패키지의 다운로드 수가 더 높은 흐름을 보이고 있다.</p>
<hr>
<h2 id="2-숫자가-다르게-보이는-이유">2. 숫자가 다르게 보이는 이유</h2>
<p>글을 마무리하고 업로드하려는 과정에서
통계를 보면서 한 가지 더 궁금한 점이 생겨
내용을 보완하기 위해 중간에 섹션을 추가했다.</p>
<p>npm 패키지 다운로드 수를 보면 React가 훨씬 높고,
jQuery는 오히려 그보다 낮은 경우가 많다.</p>
<p>그런데 왜 웹사이트 사용률에서는
jQuery가 압도적으로 높게 나오는 걸까?</p>
<p>이건 두 지표가 서로 다른 기준을 가지고 있기 때문이다.</p>
<p>jQuery는 npm을 사용하지 않고도 사용할 수 있다.
HTML 파일에서 <code>&lt;script&gt;</code> 태그로 불러오기만 해도 바로 사용할 수 있으며,
이 방식은 과거부터 지금까지 계속 유지되고 있다.</p>
<p>그래서 오래된 웹사이트나 워드프레스 같은 환경에서는
npm을 사용하지 않고도 jQuery가 포함된 경우가 많다.</p>
<p>반면 React는 일반적으로 npm 기반으로 사용된다.
프로젝트를 생성할 때부터 설치가 필요하고,
빌드 과정에서도 계속 사용되기 때문에 다운로드 수가 크게 증가한다.</p>
<p>결국 npm 다운로드 수는
지금 <strong>얼마나 활발하게 개발에 사용되고 있는지</strong>를 보여주고,</p>
<p>웹사이트 사용률은
이미 웹에 <strong>얼마나 많이 포함되어 있는지</strong>를 보여주는 지표다.</p>
<p>이 차이를 이해하고 나니
두 통계가 왜 다르게 보이는지 납득할 수 있었다.</p>
<p>따라서 사실상 두 라이브러리를 동일한 기준으로 직접 비교하기는 어렵다.
하지만 웹이 변화하면서 그에 맞는 개발 방식이 바뀌고 있다는 건 뚜렷하기에,
React의 사용 흐름이 증가하고 있다는 점은 확인할 수 있다.</p>
<hr>
<h2 id="3-지금-만들어지는-웹">3. 지금 만들어지는 웹</h2>
<p>아까까지는
웹 전체를 기준으로 통계를 봤다.</p>
<p>그래서 jQuery가 훨씬 많이 쓰이는 것처럼 보였다.</p>
<p>그런데 기준을
<strong>지금 새로 만들어지는 웹</strong>으로 바꾸면
완전히 다른 흐름이 보이기 시작한다.</p>
<h3 id="예전-웹-vs-지금-웹">예전 웹 vs 지금 웹</h3>
<p>과거의 웹은 지금처럼 복잡하지 않았다.</p>
<p>페이지를 이동하면서 내용을 보여주고,
필요할 때 일부 DOM을 수정하는 정도였다.</p>
<p>하지만 지금의 웹은 완전히 다르다.</p>
<p>버튼을 누르면 일부 UI만 바뀌고,
데이터가 바뀌면 화면이 갱신되며,
상태에 따라 화면이 계속 변한다.</p>
<p>이제 웹은 단순한 페이지가 아니라
<strong>하나의 애플리케이션처럼 동작한다.</strong></p>
<p>이러한 변화는 개발 방식 자체에도 영향을 주었다.</p>
<h3 id="예전-웹jquery-vs-지금-웹react">예전 웹(jQuery) vs 지금 웹(React)</h3>
<p>필요한 부분만 직접 찾아서 수정하는 jQuery 방식은
예전 웹에선 크게 문제가 없었다.</p>
<p>하지만 웹의 구조가 점점 변화하면서
jQuery처럼 DOM을 하나씩 직접 수정하는 방식은
수정할 곳이 계속 늘어나고 상태를 관리하기 어려워지고
코드가 점점 복잡해진다.</p>
<p>즉 규모가 커질수록 관리가 어려워진다.</p>
<p>그래서 등장한 방식이 React다.</p>
<p>React는 개발자가 직접 DOM을 조작하기보다는,
<strong>현재 상태를 기준</strong>으로 UI를 선언적으로 표현하고
상태 변화에 따라 필요한 부분만 업데이트한다.</p>
<p>이때 중요한 것은 상태(state)를 중심으로 UI를 관리한다는 점이다.</p>
<p>즉 상태가 바뀌면,
그 변화에 맞게 UI가 다시 계산되어 화면이 업데이트된다.</p>
<p>정리하면
jQuery는 <strong>DOM을 직접 조작하는 방식</strong>이고,
React는 <strong>상태를 기반으로 UI를 선언적으로 구성하는 방식</strong>이다.</p>
<hr>
<h2 id="4-핵심-정리">4. 핵심 정리</h2>
<ul>
<li>jQuery의 높은 사용률은 현재 활발히 사용된다는 의미가 아니라, 과거에 만들어진 웹이 계속 유지되고 있기 때문이다.</li>
<li>사용률 통계는 <strong>얼마나 많이 쓰이는가</strong>보다 <strong>얼마나 포함되어 있는가</strong>에 가까운 지표다.</li>
<li>npm 다운로드 수와 웹사이트 사용률은 서로 다른 기준을 가진 지표이며, 각각 <strong>현재 개발 흐름</strong>과 <strong>누적된 사용 현황</strong>을 보여준다.</li>
<li>기준을 <strong>웹 전체</strong>가 아니라 <strong>지금 새로 만들어지는 웹</strong>으로 바꿔보면 흐름이 완전히 다르게 보인다.</li>
<li>React는 상태 기반으로 UI를 관리하는 방식이며, 복잡한 인터페이스를 다루기 위해 등장했다.</li>
<li>현재 웹 개발은 React와 같은 상태 기반 UI 방식으로 발전하는 흐름을 보이고 있다.</li>
<li>결국 기술을 선택할 때는 단순한 사용률이 아니라, 현재의 흐름과 방향을 기준으로 판단해야 한다.</li>
</ul>
<hr>
<p>어떻게 보면 당연한 흐름이었다.</p>
<p>기술이 발전하면서 웹사이트에서 요구되는 사항이 많아졌을 것이고,
이에 따라 기존에 사용되던 기술들도 확장이나 변화가 필요해졌을 것이다.</p>
<p>jQuery도 꾸준히 업데이트되고 있지만,
기존 방식의 한계가 있었을 것이다.</p>
<p>React처럼 기존 방식과는 다른 접근이 필요했기에
이러한 변화가 나타나지 않았을까 싶다.</p>
<p>하지만 jQuery의 높은 사용률 통계에 이목이 끌렸고,
이 주제를 깊게 살펴보지 않았다면,
사용률만 보고선 왜 React가 현재 주요 선택지인지 이해하기 어려웠을 것이다.</p>
<p>두 기술을 잘 모르는 상태에서 비교해보는 과정 자체가
의미 있는 학습이 되었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[서버와 데이터를 주고받는 코드: fetch와 API]]></title>
            <link>https://velog.io/@hogu__giriboy/%EC%84%9C%EB%B2%84%EC%99%80-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EC%A3%BC%EA%B3%A0%EB%B0%9B%EB%8A%94-%EC%BD%94%EB%93%9C-fetch%EC%99%80-API</link>
            <guid>https://velog.io/@hogu__giriboy/%EC%84%9C%EB%B2%84%EC%99%80-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EC%A3%BC%EA%B3%A0%EB%B0%9B%EB%8A%94-%EC%BD%94%EB%93%9C-fetch%EC%99%80-API</guid>
            <pubDate>Mon, 16 Mar 2026 09:29:57 GMT</pubDate>
            <description><![CDATA[<p>블로그를 어떻게 해야 잘 재밌게 쓸 수 있을까 고민하다가
어떤 글을 봤더니 적절히 밈을 섞어서 유쾌하게 풀어냈길래
바로 저번 글에 한 번 소제목에 밈을 섞어봤었다.</p>
<p>근데 영 아닌 거 같아서 이번 글부터 바로 철회했다 ... ...</p>
<hr>
<h2 id="1-브라우저가-요청을-보내는-방법">1. 브라우저가 요청을 보내는 방법</h2>
<p>앞선 글에서는 웹이 <strong>요청(Request)과 응답(Response)</strong> 구조로 동작한다는 것과
브라우저와 서버가 <strong>HTTP를 통해 데이터를 주고받는다</strong>는 흐름을 살펴봤다.</p>
<p>하지만 그 설명은 어디까지나 <strong>통신 구조</strong>에 대한 이야기였다.</p>
<p>이제는 한 단계 더 들어가서
<strong>JavaScript 코드로 실제 서버 요청을 보내는 방법</strong>을 알아볼 차례다.</p>
<p>브라우저에서 서버에 요청을 보내는 방법은 여러 가지가 있지만
현대 웹 환경에서는 <strong>fetch API가 기본적인 방식으로 사용된다.</strong></p>
<pre><code class="language-javascript">fetch(&quot;https://example.com/data&quot;);</code></pre>
<p>이 코드 한 줄이 의미하는 것은 단순하다.</p>
<blockquote>
<p>이 주소로 요청을 보내라.</p>
</blockquote>
<p>브라우저는 이 코드를 실행하면 다음과 같은 일을 한다.</p>
<ol>
<li>JavaScript</li>
<li>브라우저가 HTTP 요청 생성</li>
<li>서버로 요청 전송</li>
<li>서버가 응답 반환</li>
</ol>
<p>즉 <code>fetch</code>는 단순한 함수처럼 보이지만
실제로는 <strong>브라우저에게 서버 요청을 보내라고 지시하는 명령</strong>이다.</p>
<p>여기서 중요한 점 하나가 있다.</p>
<p><code>fetch</code>는 <strong>요청을 보내는 것까지만 담당한다.</strong></p>
<p>서버가 어떤 데이터를 보내는지,
응답이 성공인지 실패인지,
데이터를 어떻게 읽어야 하는지는
<strong>그 다음 단계에서 처리해야 한다.</strong></p>
<p>그래서 <code>fetch</code>를 사용하면
다음과 같은 흐름이 자연스럽게 이어진다.</p>
<ol>
<li>요청 보내기</li>
<li>응답 받기</li>
<li>응답 데이터 읽기</li>
<li>필요한 형태로 사용</li>
</ol>
<p>다음 파트에서는
<code>fetch</code>가 서버 응답을 <strong>어떤 형태로 반환하는지</strong>를 살펴보자.</p>
<p>바로 <strong>Response 객체</strong> 이야기다.</p>
<hr>
<h2 id="2-fetch는-데이터를-바로-주지-않는다">2. fetch는 데이터를 바로 주지 않는다</h2>
<p><code>fetch</code>를 실행하면 서버로 요청이 보내진다.
그렇다면 서버가 데이터를 보내주면 바로 사용할 수 있을까?</p>
<p>실제로는 그렇지 않다.</p>
<pre><code class="language-javascript">const data = fetch(&quot;https://example.com/data&quot;);

console.log(data);</code></pre>
<p>이 코드를 실행하면 우리가 기대하는 <strong>데이터</strong>가 아니라
다음과 같은 값이 출력된다.</p>
<pre><code class="language-text">Promise { &lt;pending&gt; }</code></pre>
<p>여기서 중요한 사실 하나가 있다.</p>
<blockquote>
<p><code>fetch</code>는 데이터를 바로 반환하지 않는다.</p>
</blockquote>
<p>대신 <strong>Promise 객체</strong>를 반환한다.</p>
<p>이 말은 즉, <code>fetch</code>는 <strong>서버 요청을 보내고 Promise를 반환하는 비동기 작업</strong>이라는 뜻이다.</p>
<p>그래서 <code>fetch</code>의 기본 구조는 보통 이렇게 사용된다.</p>
<pre><code class="language-javascript">fetch(&quot;https://example.com/data&quot;).then((response) =&gt; {
  console.log(response);
});</code></pre>
<p>여기서 <code>response</code>는 서버가 반환한 <strong>응답 객체(Response)</strong>다.</p>
<p>하지만 여기서 또 하나 헷갈리는 부분이 있다.</p>
<p><code>response</code> 안에는 우리가 원하는 <strong>데이터가 바로 들어있지 않다.</strong></p>
<p>예를 들어 서버가 다음과 같은 JSON 데이터를 보냈다고 가정해보자.</p>
<pre><code class="language-json">{
  &quot;name&quot;: &quot;Alice&quot;,
  &quot;age&quot;: 25
}</code></pre>
<p>하지만 <code>response</code>를 그대로 출력하면
이 데이터가 바로 보이지 않는다.</p>
<p>그 이유는 서버 응답이 <strong>Response 객체 형태로 전달되기 때문</strong>이다.</p>
<p>즉 구조는 이렇게 된다.</p>
<ol>
<li>fetch</li>
<li>Promise 반환</li>
<li>Response 객체 도착</li>
<li>JSON 데이터 추출</li>
</ol>
<p>그래서 우리는 한 번 더 작업을 해야 한다.</p>
<p>바로 <strong>응답 데이터를 JSON으로 변환하는 과정</strong>이다.</p>
<pre><code class="language-javascript">fetch(&quot;https://example.com/data&quot;)
  .then((response) =&gt; response.json())
  .then((data) =&gt; {
    console.log(data);
  });</code></pre>
<p>여기서 중요한 포인트가 하나 있다.</p>
<p><code>response.json()</code> 역시 <strong>Promise를 반환한다.</strong></p>
<p>그래서 구조는 사실 이렇게 동작한다.</p>
<ol>
<li>fetch 요청</li>
<li>Response 객체 도착</li>
<li>JSON 파싱</li>
<li>실제 데이터</li>
</ol>
<p>그래서 코드에서는 <code>.then()</code>이 두 번 등장한다.</p>
<pre><code class="language-javascript">fetch(url)
  .then((response) =&gt; response.json())
  .then((data) =&gt; {
    // 실제 데이터 사용
  });</code></pre>
<p>처음 보면 이 구조가 조금 이상하게 느껴질 수 있다.</p>
<p>왜냐하면 우리는 보통 이렇게 생각하기 때문이다.</p>
<blockquote>
<p>요청 → 데이터</p>
</blockquote>
<p>하지만 실제로는 다음과 같은 단계가 존재한다.</p>
<ol>
<li>요청</li>
<li>응답 객체</li>
<li>데이터 파싱</li>
<li>사용</li>
</ol>
<p>이 구조를 이해하면 <code>fetch</code>를 사용할 때
왜 <code>.then()</code>이 여러 번 등장하는지 자연스럽게 이해할 수 있다.</p>
<hr>
<h2 id="3-데이터를-가져오는-가장-기본적인-요청-get-요청">3. 데이터를 가져오는 가장 기본적인 요청: GET 요청</h2>
<p>서버와 통신할 때 가장 먼저 사용하게 되는 요청은
바로 <strong>데이터를 가져오는 요청</strong>이다.</p>
<p>REST API에서는 이 역할을 <strong>GET 요청</strong>이 담당한다.</p>
<p>예를 들어 게시글 목록을 가져오는 API가 있다고 가정해보자.</p>
<pre><code class="language-text">https://example.com/posts</code></pre>
<p>이 주소로 요청을 보내면 서버는 게시글 데이터를 반환한다.</p>
<p>이 요청을 JavaScript 코드로 작성하면 다음과 같다.</p>
<pre><code class="language-javascript">fetch(&quot;https://example.com/posts&quot;)
  .then((response) =&gt; response.json())
  .then((data) =&gt; {
    console.log(data);
  });</code></pre>
<p>이 코드의 흐름을 보면 다음과 같다.</p>
<ol>
<li>fetch 요청 전송</li>
<li>Response 객체 반환</li>
<li>JSON 데이터로 변환</li>
<li>데이터 사용</li>
</ol>
<p>여기서 중요한 점은 <strong>GET 요청은 별도의 옵션 없이 사용할 수 있다는 것</strong>이다.</p>
<pre><code class="language-javascript">fetch(&quot;https://example.com/posts&quot;);</code></pre>
<p>이렇게 작성해도 기본적으로 <strong>GET 요청</strong>이 보내진다.</p>
<p>즉 <code>fetch</code>의 기본 요청 방식은 <strong>GET</strong>이다.</p>
<p>그래서 데이터를 조회할 때는 보통 다음과 같은 구조를 사용하게 된다.</p>
<pre><code class="language-javascript">async function getPosts() {
  const response = await fetch(&quot;https://example.com/posts&quot;);
  const data = await response.json();

  return data;
}</code></pre>
<p>이렇게 함수를 만들어 두면
다른 코드에서는 다음처럼 사용할 수 있다.</p>
<pre><code class="language-javascript">const posts = await getPosts();
console.log(posts);</code></pre>
<p>즉 <strong>데이터 조회 요청을 하나의 함수로 정리</strong>하면
코드를 훨씬 깔끔하게 관리할 수 있다.</p>
<p>하지만 실제 애플리케이션에서는
<strong>여러 개의 요청을 동시에 보내야 하는 상황</strong>이 자주 발생한다.</p>
<p>예를 들어</p>
<ul>
<li>게시글 목록</li>
<li>사용자 정보</li>
<li>댓글 목록</li>
</ul>
<p>같은 데이터를 <strong>동시에 가져와야 할 수도 있다.</strong></p>
<p>이때 사용하는 방법이 바로 다음 파트에서 알아볼 <strong>Promise.all</strong>이다.</p>
<hr>
<h2 id="4-여러-요청을-함께-처리하기-promiseall">4. 여러 요청을 함께 처리하기: Promise.all</h2>
<p>앞에서 살펴본 것처럼 <code>fetch</code>는 <strong>Promise를 반환하는 비동기 작업</strong>이다.
그래서 서버 요청이 하나일 때는 비교적 단순하게 처리할 수 있다.</p>
<p>하지만 실제 애플리케이션에서는
<strong>여러 데이터를 동시에 가져와야 하는 상황</strong>이 자주 발생한다.</p>
<p>예를 들어 페이지를 구성하기 위해 다음과 같은 데이터를 가져와야 할 수도 있다.</p>
<ul>
<li>게시글 목록</li>
<li>사용자 정보</li>
<li>댓글 목록</li>
</ul>
<p>이때 요청을 하나씩 순서대로 보내면
각 요청이 끝날 때까지 다음 요청을 기다리게 된다.</p>
<pre><code class="language-javascript">const posts = await fetch(&quot;/posts&quot;).then((res) =&gt; res.json());
const users = await fetch(&quot;/users&quot;).then((res) =&gt; res.json());
const comments = await fetch(&quot;/comments&quot;).then((res) =&gt; res.json());</code></pre>
<p>이 코드는 동작하지만 실제로는 다음과 같은 흐름으로 실행된다.</p>
<ol>
<li>posts 요청 → 응답 대기</li>
<li>users 요청 → 응답 대기</li>
<li>comments 요청 → 응답 대기</li>
</ol>
<p>즉 요청이 <strong>순차적으로 실행된다.</strong></p>
<p>하지만 이 세 요청은 서로 의존하지 않는다.
그렇다면 굳이 순서대로 기다릴 필요가 없다.</p>
<p>이럴 때 사용하는 것이 <strong>Promise.all</strong>이다.</p>
<pre><code class="language-javascript">const [posts, users, comments] = await Promise.all([
  fetch(&quot;/posts&quot;).then((res) =&gt; res.json()),
  fetch(&quot;/users&quot;).then((res) =&gt; res.json()),
  fetch(&quot;/comments&quot;).then((res) =&gt; res.json()),
]);</code></pre>
<p>이 코드의 실행 흐름은 다음과 같다.</p>
<ol>
<li>posts, users, comments 요청 동시에 시작</li>
<li>모든 요청이 완료될 때까지 대기</li>
<li>모든 응답이 도착하면 결과 반환</li>
</ol>
<p>즉 <code>Promise.all</code>은
<strong>여러 비동기 작업을 동시에 실행하고, 모든 작업이 완료되면 결과를 반환</strong>한다.</p>
<p>그래서 서로 의존하지 않는 요청이라면
<strong>순차 요청보다 훨씬 빠르게 데이터를 가져올 수 있다.</strong></p>
<p>여기서 <code>Promise.all</code>의 결과는
<strong>배열 형태</strong>로 반환된다.</p>
<p>그래서 보통 다음과 같이 <strong>구조 분해 할당</strong>을 사용해 값을 꺼낸다.</p>
<pre><code class="language-javascript">const [posts, users, comments] = ...</code></pre>
<p>이렇게 하면 각각의 데이터에 바로 접근할 수 있다.</p>
<hr>
<h2 id="5-서버에-새로운-데이터를-보내기-post">5. 서버에 새로운 데이터를 보내기: POST</h2>
<p>앞에서는 서버에서 <strong>데이터를 가져오는 요청(GET)</strong>을 살펴봤다.
하지만 실제 서비스에서는 데이터를 조회하는 것만으로는 충분하지 않다.</p>
<p>예를 들어 다음과 같은 상황을 생각해보자.</p>
<ul>
<li>새로운 게시글 작성</li>
<li>회원가입</li>
<li>댓글 작성</li>
</ul>
<p>이런 기능들은 모두 <strong>서버에 새로운 데이터를 보내는 작업</strong>이다.</p>
<p>이때 사용하는 요청이 바로 <strong>POST 요청</strong>이다.</p>
<p><code>fetch</code>로 POST 요청을 보내려면
단순히 URL만 전달하는 것이 아니라 <strong>옵션 객체</strong>를 함께 전달해야 한다.</p>
<pre><code class="language-javascript">fetch(&quot;https://example.com/posts&quot;, {
  method: &quot;POST&quot;,
});</code></pre>
<p>여기서 <code>method</code>는 <strong>HTTP 요청 방식</strong>을 의미한다.</p>
<p>하지만 실제로는 새로운 데이터를 생성해야 하기 때문에
<strong>서버로 데이터를 함께 보내야 한다.</strong></p>
<p>그래서 보통 다음과 같은 형태로 작성한다.</p>
<pre><code class="language-javascript">fetch(&quot;https://example.com/posts&quot;, {
  method: &quot;POST&quot;,
  headers: {
    &quot;Content-Type&quot;: &quot;application/json&quot;,
  },
  body: JSON.stringify({
    title: &quot;새로운 게시글&quot;,
    content: &quot;내용입니다&quot;,
  }),
});</code></pre>
<h3 id="method">method</h3>
<pre><code class="language-javascript">method: &quot;POST&quot;;</code></pre>
<p>요청의 종류를 <strong>POST 요청</strong>으로 지정한다.</p>
<h3 id="headers">headers</h3>
<pre><code class="language-javascript">headers: {
  &quot;Content-Type&quot;: &quot;application/json&quot;
}</code></pre>
<p>서버에게 <strong>어떤 형식의 데이터를 보내는지</strong> 알려주는 부분이다.</p>
<p>여기서는 <strong>JSON 형식으로 데이터를 보낸다</strong>는 의미다.</p>
<h3 id="body">body</h3>
<pre><code class="language-javascript">body: JSON.stringify({...})</code></pre>
<p>실제로 서버로 전달되는 데이터다.</p>
<p>여기서 <code>JSON.stringify()</code>를 사용하는 이유는
JavaScript 객체를 <strong>JSON 문자열 형태로 변환하기 위해서</strong>다.</p>
<p>즉 GET 요청이 <strong>데이터 조회</strong>라면
POST 요청은 <strong>데이터 생성</strong>을 담당한다.</p>
<p>하지만 데이터를 생성하는 것만으로는 충분하지 않다.</p>
<p>이미 존재하는 데이터를
<strong>수정하거나 업데이트해야 하는 상황</strong>도 있다.</p>
<hr>
<h2 id="6-데이터를-수정하는-요청-put과-patch">6. 데이터를 수정하는 요청: PUT과 PATCH</h2>
<p>서버에 데이터를 생성할 때는 <strong>POST 요청</strong>을 사용한다.
하지만 이미 존재하는 데이터를 <strong>수정해야 하는 상황</strong>도 자주 발생한다.</p>
<p>예를 들어 다음과 같은 경우다.</p>
<ul>
<li>게시글 내용 수정</li>
<li>사용자 정보 변경</li>
<li>할 일 목록 상태 변경</li>
</ul>
<p>이처럼 기존 데이터를 업데이트할 때 사용하는 요청이
<strong>PUT</strong>과 <strong>PATCH</strong>다.</p>
<p>두 요청이 모두 <strong>데이터 수정</strong>이라는 목적은 같지만
<strong>수정 방식</strong>에서 차이가 있다.</p>
<h3 id="전체를-교체하는-put">전체를 교체하는 PUT</h3>
<p>PUT 요청은 <strong>기존 데이터를 새로운 데이터로 전체 교체</strong>하는 방식이다.</p>
<p>예를 들어 서버에 다음과 같은 데이터가 있다고 가정해보자.</p>
<pre><code class="language-json">{
  &quot;id&quot;: 1,
  &quot;title&quot;: &quot;게시글 제목&quot;,
  &quot;content&quot;: &quot;내용&quot;
}</code></pre>
<p>이 데이터를 PUT 요청으로 수정하면
기존 데이터를 <strong>완전히 새로운 데이터로 덮어쓴다.</strong></p>
<pre><code class="language-javascript">fetch(&quot;https://example.com/posts/1&quot;, {
  method: &quot;PUT&quot;,
  headers: {
    &quot;Content-Type&quot;: &quot;application/json&quot;,
  },
  body: JSON.stringify({
    title: &quot;수정된 제목&quot;,
    content: &quot;수정된 내용&quot;,
  }),
});</code></pre>
<p>이 경우 서버는 <strong>기존 데이터를 새로운 데이터로 전체 교체</strong>하게 된다.</p>
<h3 id="일부만-수정하는-patch">일부만 수정하는 PATCH</h3>
<p>PATCH 요청은 <strong>데이터의 일부만 수정</strong>할 때 사용한다.</p>
<p>예를 들어 제목만 수정하고 싶다면
모든 데이터를 다시 보낼 필요는 없다.</p>
<pre><code class="language-javascript">fetch(&quot;https://example.com/posts/1&quot;, {
  method: &quot;PATCH&quot;,
  headers: {
    &quot;Content-Type&quot;: &quot;application/json&quot;,
  },
  body: JSON.stringify({
    title: &quot;수정된 제목&quot;,
  }),
});</code></pre>
<p>이 경우 서버는 기존 데이터에서
<code>title</code>만 수정하고 나머지 값은 그대로 유지한다.</p>
<h3 id="put과-patch의-차이">PUT과 PATCH의 차이</h3>
<p>정리하면 다음과 같다.</p>
<ul>
<li>PUT → 전체 교체</li>
<li>PATCH → 일부 수정</li>
</ul>
<p>그래서 API 설계에 따라
<strong>PUT만 사용하는 경우도 있고, PATCH를 함께 사용하는 경우도 있다.</strong></p>
<p>마지막으로 남은 것은
데이터를 <strong>삭제하는 요청</strong>이다.</p>
<hr>
<h2 id="7-데이터를-삭제하는-요청-delete">7. 데이터를 삭제하는 요청: DELETE</h2>
<p>애플리케이션에서는 더 이상 필요하지 않은 데이터를
<strong>삭제해야 하는 상황</strong>도 자주 발생한다.</p>
<p>예를 들어 다음과 같은 경우다.</p>
<ul>
<li>게시글 삭제</li>
<li>댓글 삭제</li>
<li>할 일 목록 삭제</li>
</ul>
<p>이때 사용하는 요청이 바로 <strong>DELETE 요청</strong>이다.</p>
<p><code>fetch</code>로 DELETE 요청을 보내는 방식은 비교적 단순하다.</p>
<pre><code class="language-javascript">fetch(&quot;https://example.com/posts/1&quot;, {
  method: &quot;DELETE&quot;,
});</code></pre>
<p>이 요청은 다음과 같은 의미를 가진다.</p>
<pre><code class="language-text">/posts/1 데이터 삭제 요청</code></pre>
<p>서버는 이 요청을 받으면
해당 ID의 데이터를 찾아 <strong>삭제 처리</strong>를 하게 된다.</p>
<p>DELETE 요청은 보통 <strong>삭제할 데이터의 식별자(ID)</strong>를
URL에 포함하는 방식으로 사용된다.</p>
<p>예를 들어</p>
<pre><code class="language-text">/posts/1</code></pre>
<p>이라는 주소는 &quot;ID가 1인 게시글&quot;을 의미한다.</p>
<p>그래서 삭제 요청은 보통 이런 형태로 작성된다.</p>
<pre><code class="language-javascript">async function deletePost(id) {
  await fetch(`https://example.com/posts/${id}`, {
    method: &quot;DELETE&quot;,
  });
}</code></pre>
<p>이렇게 함수를 만들어 두면
다른 코드에서는 다음과 같이 사용할 수 있다.</p>
<pre><code class="language-javascript">await deletePost(1);</code></pre>
<p>즉 서버에게 &quot;ID가 1인 데이터 삭제&quot; 라는 요청을 보내는 것이다.</p>
<p>여기까지 살펴보면
우리는 서버 데이터를 다루는 기본적인 작업을 모두 이해하게 된다.</p>
<ul>
<li>GET → 데이터 조회</li>
<li>POST → 데이터 생성</li>
<li>PUT → 전체 수정</li>
<li>PATCH → 부분 수정</li>
<li>DELETE → 데이터 삭제</li>
</ul>
<p>이 다섯 가지 요청은
서버 데이터를 관리하는 가장 기본적인 패턴이며
보통 <strong>CRUD 작업</strong>이라고 부른다.</p>
<p>하지만 실제 서버 통신에서는
요청이 항상 성공하는 것은 아니다.</p>
<p>네트워크 문제나 서버 오류로 인해
요청이 실패하는 상황도 충분히 발생할 수 있다.</p>
<hr>
<h2 id="8-요청이-항상-성공하는-것은-아니다-에러-처리">8. 요청이 항상 성공하는 것은 아니다: 에러 처리</h2>
<p>지금까지는 서버 요청이 <strong>정상적으로 동작하는 상황</strong>을 기준으로 살펴봤다.
하지만 실제 네트워크 통신에서는 요청이 항상 성공하는 것은 아니다.</p>
<p>예를 들어 다음과 같은 상황이 발생할 수 있다.</p>
<ul>
<li>존재하지 않는 주소로 요청한 경우</li>
<li>네트워크 연결이 끊어진 경우</li>
<li>서버 내부 오류가 발생한 경우</li>
</ul>
<p>이처럼 요청이 실패할 가능성이 있기 때문에
서버 통신 코드를 작성할 때는 <strong>에러 처리를 함께 고려해야 한다.</strong></p>
<p><code>fetch</code>를 사용할 때 가장 기본적인 방법은
<code>try...catch</code>를 사용하는 것이다.</p>
<pre><code class="language-javascript">async function getPosts() {
  try {
    const response = await fetch(&quot;https://example.com/posts&quot;);
    const data = await response.json();

    return data;
  } catch (error) {
    console.error(&quot;요청 중 오류가 발생했습니다:&quot;, error);
  }
}</code></pre>
<p>이 코드에서 <code>try</code> 블록은
<strong>정상적인 요청 흐름</strong>을 담당한다.</p>
<ol>
<li>fetch 요청</li>
<li>응답 받기</li>
<li>JSON 변환</li>
<li>데이터 반환</li>
</ol>
<p>만약 이 과정에서 오류가 발생하면
<code>catch</code> 블록이 실행된다.</p>
<p>하지만 여기서 한 가지 더 중요한 점이 있다.</p>
<p><code>fetch</code>는 <strong>HTTP 오류를 자동으로 에러로 처리하지 않는다.</strong></p>
<p>예를 들어 서버가 <strong>404 Not Found</strong>나
<strong>500 Internal Server Error</strong>를 반환해도
<code>fetch</code> 자체는 <strong>성공한 요청으로 처리된다.</strong></p>
<p>그래서 응답 상태를 직접 확인해야 한다.</p>
<pre><code class="language-javascript">async function getPosts() {
  const response = await fetch(&quot;https://example.com/posts&quot;);

  if (!response.ok) {
    throw new Error(&quot;서버 요청 실패&quot;);
  }

  const data = await response.json();
  return data;
}</code></pre>
<p>여기서 <code>response.ok</code>는
응답 상태 코드가 <strong>성공 범위(200~299)</strong>인지 확인하는 속성이다.</p>
<p>즉 구조는 이렇게 된다.</p>
<ol>
<li>요청 전송</li>
<li>응답 도착</li>
<li>응답 상태 확인</li>
<li>데이터 파싱</li>
<li>사용</li>
</ol>
<p>이 과정을 통해
요청 실패 상황을 보다 안정적으로 처리할 수 있다.</p>
<hr>
<h2 id="9-반복되는-fetch를-정리하면-api-함수">9. 반복되는 fetch를 정리하면: API 함수</h2>
<p>지금까지 <code>fetch</code>를 사용해 다양한 서버 요청을 살펴봤다.</p>
<p>하지만 실제 코드에서 매번 이런 요청을 직접 작성하면
코드가 금방 복잡해질 수 있다.</p>
<p>예를 들어 게시글 데이터를 가져오는 코드가
여러 곳에서 필요하다고 가정해보자.</p>
<pre><code class="language-javascript">const response = await fetch(&quot;https://example.com/posts&quot;);
const data = await response.json();</code></pre>
<p>이 코드를 페이지 곳곳에서 계속 작성하게 되면
다음과 같은 문제가 생긴다.</p>
<ul>
<li>같은 코드가 여러 곳에 반복된다</li>
<li>API 주소가 바뀌면 모든 코드를 수정해야 한다</li>
<li>요청 로직을 관리하기 어려워진다</li>
</ul>
<p>그래서 보통은 서버 요청 코드를
<strong>하나의 함수로 정리해서 사용한다.</strong></p>
<pre><code class="language-javascript">async function getPosts() {
  const response = await fetch(&quot;https://example.com/posts&quot;);
  const data = await response.json();

  return data;
}</code></pre>
<p>이렇게 정리해두면
다른 코드에서는 훨씬 간단하게 사용할 수 있다.</p>
<pre><code class="language-javascript">const posts = await getPosts();</code></pre>
<p>이 방식의 장점은 명확하다.</p>
<p>먼저 <strong>코드 중복을 줄일 수 있다.</strong></p>
<p>서버 요청 로직을 한 곳에 모아두면
같은 코드를 반복해서 작성할 필요가 없다.</p>
<p>또한 <strong>유지보수가 쉬워진다.</strong></p>
<p>만약 API 주소가 변경되더라도
함수 내부만 수정하면 된다.</p>
<p>마지막으로 <strong>코드 구조가 훨씬 명확해진다.</strong></p>
<p>데이터를 가져오는 코드와
화면을 렌더링하는 코드를 분리할 수 있기 때문이다.</p>
<p>그래서 실제 프로젝트에서는
이런 서버 요청 함수들을 모아서
보통 <strong>API 모듈</strong> 형태로 관리한다.</p>
<pre><code class="language-text">api
⎿ getPosts
⎿ createPost
⎿ updatePost
⎿ deletePost</code></pre>
<p>이렇게 요청 로직을 정리하면
애플리케이션 전체에서
<strong>일관된 방식으로 서버 통신을 관리할 수 있다.</strong></p>
<hr>
<h2 id="10-핵심-정리">10. 핵심 정리</h2>
<ul>
<li>브라우저는 <code>fetch</code>를 사용해 서버에 요청을 보낼 수 있다.</li>
<li><code>fetch</code>는 데이터를 바로 반환하지 않고 <strong>Promise</strong>를 반환한다.</li>
<li>서버 응답은 <strong>Response 객체</strong> 형태로 전달되며 <code>response.json()</code>으로 데이터를 읽을 수 있다.</li>
<li>데이터를 조회할 때는 <strong>GET 요청</strong>을 사용한다.</li>
<li>여러 요청을 동시에 처리할 때는 <strong>Promise.all</strong>을 사용할 수 있다.</li>
<li>데이터를 생성할 때는 <strong>POST 요청</strong>을 사용한다.</li>
<li>데이터를 수정할 때는 <strong>PUT(전체 수정)</strong>과 <strong>PATCH(부분 수정)</strong>을 사용한다.</li>
<li>데이터를 삭제할 때는 <strong>DELETE 요청</strong>을 사용한다.</li>
<li>서버 요청은 실패할 수 있으므로 <strong>에러 처리</strong>가 필요하다.</li>
<li>반복되는 <code>fetch</code> 코드는 <strong>API 함수로 정리하면 관리하기 쉬워진다.</strong></li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript - 변수 선언의 진화: var, let, const]]></title>
            <link>https://velog.io/@hogu__giriboy/JavaScript-%EB%B3%80%EC%88%98-%EC%84%A0%EC%96%B8%EC%9D%98-%EC%A7%84%ED%99%94-var-let-const</link>
            <guid>https://velog.io/@hogu__giriboy/JavaScript-%EB%B3%80%EC%88%98-%EC%84%A0%EC%96%B8%EC%9D%98-%EC%A7%84%ED%99%94-var-let-const</guid>
            <pubDate>Mon, 16 Mar 2026 05:17:39 GMT</pubDate>
            <description><![CDATA[<h2 id="1-javascript의-변수-선언-방식">1. JavaScript의 변수 선언 방식</h2>
<p>JavaScript에서 값을 저장하려면 <strong>변수에 값을 할당(assignment)</strong>해야 하며,
이를 위해 먼저 <strong>변수를 선언(declaration)</strong>한다.
그리고 JavaScript에는 변수를 선언하는 세 가지 방법이 있다.</p>
<ul>
<li>var</li>
<li>let</li>
<li>const</li>
</ul>
<p>지금은 대부분 <code>let</code>과 <code>const</code>를 사용하지만
JavaScript가 처음 등장했을 때는 <code>var</code> <strong>하나만 존재했다.</strong></p>
<p>즉 초기 JavaScript에서는 모든 변수를 이렇게 선언했다.</p>
<pre><code class="language-javascript">var name = &quot;Alice&quot;;
var age = 20;</code></pre>
<p>당시에는 문제가 없어 보였지만
프로그램 규모가 커지면서 <code>var</code>가 여러 문제를 만들기 시작했다.</p>
<h3 id="javascript-초창기-var">JavaScript 초창기: <code>var</code></h3>
<p>JavaScript는 원래 <strong>웹페이지에 동적인 동작을 추가하는 것</strong>이었다.
웹페이지에 간단한 동작을 추가하는 것이 목적이었기 때문에
변수 선언 방식도 단순하게 설계되었다.</p>
<p>그래서 변수 선언은 사실상 <code>var</code> <strong>하나로 통일</strong>되어 있었다.</p>
<pre><code class="language-javascript">var count = 1;
var message = &quot;hello&quot;;</code></pre>
<p>하지만 시간이 지나면서 JavaScript는
단순한 스크립트 언어를 넘어 <strong>대규모 애플리케이션을 만드는 언어</strong>로 발전했다.</p>
<p>이 과정에서 <code>var</code>의 설계가 여러 문제를 만들기 시작했다.</p>
<h3 id="es6에서-등장한-let과-const">ES6에서 등장한 <code>let</code>과 <code>const</code></h3>
<p>이 문제를 해결하기 위해
<strong>ES6(ECMAScript 2015)</strong>에서 새로운 변수 선언 방식이 추가된다.</p>
<ul>
<li>let</li>
<li>const</li>
</ul>
<p>이 두 가지는 단순한 문법 추가가 아니라
<strong>스코프와 변수 재선언 문제를 개선하기 위해 도입된 새로운 변수 선언 방식이다.</strong></p>
<p>정리하면 현재 JavaScript에는 다음 세 가지 선언 방식이 존재한다.</p>
<ul>
<li>var → 과거 방식</li>
<li>let → 재할당 가능한 변수</li>
<li>const → 재할당 불가능한 변수</li>
</ul>
<p>그리고 현대 JavaScript에서는 대부분 다음과 같은 방식으로 사용된다.</p>
<pre><code class="language-javascript">const name = &quot;Alice&quot;;
let count = 0;</code></pre>
<p>즉 <strong>기본은 <code>const</code></strong>,
값이 바뀌어야 할 때만 <strong><code>let</code></strong>을 사용한다.</p>
<p><code>var</code>는 과거 코드에서만 볼 수 있는 경우가 많고
새로운 코드에서는 거의 사용하지 않는다.</p>
<h3 id="변수-선언-방식의-변화-이유">변수 선언 방식의 변화 이유</h3>
<p>핵심 이유는 하나다.</p>
<p><code>var</code>가 <strong>예측하기 어려운 동작을 만들었기 때문</strong>이다.</p>
<p>특히 다음과 같은 문제들이 있었다.</p>
<ul>
<li>함수 스코프</li>
<li>중복 선언 가능</li>
<li>호이스팅</li>
</ul>
<p>이 문제들이 실제 개발에서 <strong>버그의 원인</strong>이 되는 경우가 많았고
이를 해결하기 위해 <code>let</code>과 <code>const</code>가 등장하게 된다.</p>
<hr>
<h2 id="2-var가-만들었던-문제들">2. <code>var</code>가 만들었던 문제들</h2>
<p>초창기 JavaScript에서 <code>var</code>는 유일한 변수 선언 방식이었지만
프로그램 규모가 커지면서 여러 <strong>예측하기 어려운 동작</strong>을 만들기 시작했다.</p>
<p>대표적으로 다음과 같은 특징들이 이해를 어렵게 만들 수 있다.</p>
<ul>
<li>함수 스코프</li>
<li>중복 선언 가능</li>
<li>호이스팅</li>
</ul>
<p>이 세 가지 때문에 코드의 동작을 <strong>직관적으로 이해하기 어려워졌다.</strong></p>
<h3 id="블록이-아닌-함수-기준으로-동작하는-스코프">블록이 아닌 함수 기준으로 동작하는 스코프</h3>
<p>많은 현대 프로그래밍 언어에서는 <code>{}</code> 블록 단위로 스코프가 결정되는
<strong>블록 스코프(block scope)</strong>를 사용한다.</p>
<p>하지만 <code>var</code>는 그렇지 않다.</p>
<p><code>var</code>는 <strong>블록이 아니라 함수 기준으로 스코프가 결정된다.</strong></p>
<pre><code class="language-javascript">if (true) {
  var message = &quot;hello&quot;;
}

console.log(message); // hello</code></pre>
<p><code>if</code> 블록 안에서 선언했는데도
블록 밖에서 변수에 접근할 수 있다.</p>
<p>왜냐하면 <code>var</code>는 <strong>블록 스코프가 아니라 함수 스코프</strong>이기 때문이다.</p>
<p>그래서 코드가 길어질수록
<strong>변수가 어디서 생성된 것인지 파악하기 어려워지는 문제</strong>가 생긴다.</p>
<h3 id="같은-변수를-여러-번-선언할-수-있다">같은 변수를 여러 번 선언할 수 있다.</h3>
<p>또 하나의 문제는 <strong>중복 선언이 가능하다는 것</strong>이다.</p>
<pre><code class="language-javascript">var name = &quot;Alice&quot;;
var name = &quot;Bob&quot;;

console.log(name); // Bob</code></pre>
<p>같은 이름의 변수를 다시 선언해도
에러가 발생하지 않는다.</p>
<p>결과적으로 기존 변수는 그냥 덮어쓰이게 된다.</p>
<p>이런 상황에서는 실수로 변수를 다시 선언해도 프로그램이 그대로 실행된다.</p>
<p>즉 <strong>버그가 발생해도 바로 알아차리기 어려워진다.</strong></p>
<h3 id="코드-위로-끌어올려지는-호이스팅">코드 위로 끌어올려지는 호이스팅</h3>
<p><code>var</code>의 또 다른 특징은 <strong>호이스팅(hoisting)</strong>이다.</p>
<p>호이스팅은 <strong>변수 선언이 실제 코드 위치와 관계없이</strong>
<strong>해당 스코프의 시작 부분에서 선언된 것처럼 동작하는 JavaScript의 동작 방식</strong>이다.</p>
<pre><code class="language-javascript">console.log(count);

var count = 10;</code></pre>
<p>이 코드는 에러가 나지 않는다.</p>
<p>실제로 JavaScript 엔진이 내부적으로
코드를 다음처럼 처리하기 때문이다.</p>
<pre><code class="language-javascript">var count;

console.log(count);

count = 10;</code></pre>
<p>그래서 결과는 <code>undefined</code>가 나온다.</p>
<p>변수가 선언되기 전에 사용했는데도
에러가 발생하지 않고 <code>undefined</code>가 출력된다.</p>
<p>이런 동작은 코드의 실행 흐름을 <strong>직관적으로 이해하기 어렵게 만든다.</strong></p>
<h3 id="점차-드물어지는-var">점차 드물어지는 <code>var</code></h3>
<p>정리하면 <code>var</code>는 다음과 같은 문제를 가지고 있었다.</p>
<ul>
<li>블록이 아닌 <strong>함수 기준 스코프</strong></li>
<li><strong>중복 선언 가능</strong></li>
<li><strong>호이스팅으로 인한 예측 어려움</strong></li>
</ul>
<p>JavaScript가 점점 <strong>대규모 애플리케이션을 만드는 언어</strong>로 발전하면서
이 문제들은 실제 개발에서 큰 혼란을 만들었다.</p>
<p>그래서 ES6에서는 이러한 특성으로 인한 혼란을 줄이기 위해
<strong><code>let</code>과 <code>const</code>라는 새로운 변수 선언 방식이 도입되었다.</strong></p>
<hr>
<h2 id="3-let-블록-스코프의-등장">3. <code>let</code>: 블록 스코프의 등장</h2>
<p><code>var</code>의 특징 중 하나는
<strong>스코프가 블록이 아니라 함수 단위로 결정된다는 것</strong>이다.</p>
<p>이 문제를 해결하기 위해 ES6에서 등장한 것이
바로 <code>let</code>이다.</p>
<p><code>let</code>의 가장 중요한 특징은 <strong>블록 스코프(block scope)</strong>다.</p>
<h3 id="블록-단위로-관리되는-변수">블록 단위로 관리되는 변수</h3>
<p>블록 스코프란
<code>{}</code> 블록 안에서 선언된 변수는 <strong>그 블록 안에서만 사용할 수 있는 것</strong>을 의미한다.</p>
<pre><code class="language-javascript">if (true) {
  let message = &quot;hello&quot;;
}

console.log(message);</code></pre>
<p>이 코드는 <strong>에러가 발생한다.</strong></p>
<pre><code class="language-text">ReferenceError: message is not defined</code></pre>
<p>왜냐하면 <code>message</code>는 <code>if</code> <strong>블록 안에서만 존재하는 변수</strong>이기 때문이다.</p>
<p>이것이 <code>var</code>와 가장 큰 차이다.</p>
<h3 id="var와-let의-스코프-차이"><code>var</code>와 <code>let</code>의 스코프 차이</h3>
<p>두 코드를 비교해 보면 차이가 더 분명해진다.</p>
<pre><code class="language-javascript">if (true) {
  var a = 10;
}

console.log(a); // 10</code></pre>
<p><code>var</code>는 블록을 무시하고
<strong>함수 전체에서 접근 가능하다.</strong></p>
<p>반면 <code>let</code>은 다음과 같다.</p>
<pre><code class="language-javascript">if (true) {
  let b = 10;
}

console.log(b); // ReferenceError</code></pre>
<p><code>let</code>은 <strong>블록을 벗어나면 접근할 수 없다.</strong></p>
<p>즉 변수의 <strong>사용 범위를 더 안전하게 제한할 수 있다.</strong></p>
<h3 id="재할당은-가능하지만-재선언은-불가능">재할당은 가능하지만 재선언은 불가능</h3>
<p><code>let</code>은 값을 <strong>다시 할당하는 것은 가능</strong>하지만
같은 이름으로 <strong>다시 선언하는 것은 불가능하다.</strong></p>
<pre><code class="language-javascript">let count = 1;
count = 2; // 가능</code></pre>
<p>하지만 다음 코드는 에러가 발생한다.</p>
<pre><code class="language-javascript">let count = 1;
let count = 2; // SyntaxError</code></pre>
<p>이 덕분에 실수로 같은 변수를 다시 선언하는 문제를
미리 막을 수 있다.</p>
<h3 id="let이-해결한-문제"><code>let</code>이 해결한 문제</h3>
<p><code>let</code>은 <code>var</code>의 설계로 인해
발생할 수 있는 혼란을 줄이기 위해 도입된 변수 선언 방식이다.</p>
<ul>
<li>블록 스코프 지원</li>
<li>중복 선언 방지</li>
<li>더 예측 가능한 코드 구조</li>
</ul>
<p>그래서 현대 JavaScript에서는
새로운 코드에서 <code>var</code> 대신 <strong><code>let</code>과 <code>const</code>를 사용하는 것이 일반적인 관례</strong>가 되었다.</p>
<p>하지만 여기서 한 가지 질문이 생긴다.</p>
<blockquote>
<p>재할당이 가능한 변수라면
값이 바뀌지 않는 변수는 어떻게 관리해야 할까?</p>
</blockquote>
<p>이 질문에 대한 답이 바로 <strong><code>const</code></strong>다.</p>
<hr>
<h2 id="4-const-변하지-않는-변수">4. <code>const</code>: 변하지 않는 변수</h2>
<p><code>let</code>이 등장하면서 <code>var</code>의 많은 문제가 해결되었지만
여전히 한 가지 고민이 남아 있었다.</p>
<blockquote>
<p>값이 절대 바뀌지 않는 변수는 어떻게 표현할까?</p>
</blockquote>
<p>이 문제를 해결하기 위해 ES6에서는
<strong><code>const</code></strong>라는 새로운 변수 선언 방식이 추가되었다.</p>
<h3 id="const는-재할당이-불가능하다"><code>const</code>는 재할당이 불가능하다</h3>
<p><code>const</code>는 <strong>한 번 값을 할당하면 다시 재할당할 수 없는 변수</strong>다.</p>
<pre><code class="language-javascript">const name = &quot;Alice&quot;;

name = &quot;Bob&quot;; // 에러 발생</code></pre>
<p>이 코드는 다음과 같은 에러가 발생한다.</p>
<pre><code class="language-text">TypeError: Assignment to constant variable</code></pre>
<p>즉 <code>const</code>는 <strong>재할당이 불가능한 변수</strong>다.</p>
<h3 id="선언과-동시에-값-할당">선언과 동시에 값 할당</h3>
<p><code>const</code>는 선언할 때 반드시 <strong>초기값을 함께 작성해야 한다.</strong></p>
<pre><code class="language-javascript">const count = 10;</code></pre>
<p>다음 코드는 에러가 발생한다.</p>
<pre><code class="language-javascript">const count; // SyntaxError</code></pre>
<p>왜냐하면 <code>const</code>는 <strong>재할당이 불가능한 변수</strong>이기 때문에
선언 시점에 초기값이 반드시 필요하기 때문이다.</p>
<h3 id="객체와-배열에서는-조금-다르게-동작한다">객체와 배열에서는 조금 다르게 동작한다</h3>
<p>여기서 많은 사람들이 헷갈리는 부분이 하나 있다.</p>
<p><code>const</code>로 선언한 <strong>객체나 배열의 프로퍼티나 요소는 변경할 수 있다.</strong></p>
<pre><code class="language-javascript">const user = {
  name: &quot;Alice&quot;,
};

user.name = &quot;Bob&quot;; // 가능</code></pre>
<p>이 코드에는 에러가 발생하지 않는다.</p>
<p>이유는 <code>const</code>가 <strong>객체 내부 값까지 보호하는 것이 아니라</strong>
<strong>변수의 참조 자체를 변경하지 못하게 하는 것</strong>이기 때문이다.</p>
<p>즉 다음 코드는 에러가 발생한다.</p>
<pre><code class="language-javascript">const user = { name: &quot;Alice&quot; };

user = { name: &quot;Bob&quot; }; // 에러</code></pre>
<p>객체 <strong>자체를 새로운 객체로 바꾸는 것</strong>은 불가능하다.</p>
<h3 id="기본이-되어버린-const">기본이 되어버린 <code>const</code></h3>
<p>현대 JavaScript에서는 변수를 선언할 때
다음과 같은 <strong>사용 패턴이 널리 사용된다.</strong></p>
<ul>
<li><strong>기본은 <code>const</code></strong></li>
<li>값이 바뀌어야 할 때만 <code>let</code></li>
<li><strong><code>var</code>는 거의 사용하지 않음</strong></li>
</ul>
<pre><code class="language-javascript">const name = &quot;Alice&quot;;
let count = 0;</code></pre>
<p>이 방식은 코드의 의도를 더 명확하게 만든다.</p>
<p><code>const</code>로 선언된 변수를 보면
<strong>&quot;이 값은 바뀌지 않는다&quot;</strong>는 것을 바로 알 수 있기 때문이다.</p>
<hr>
<h2 id="5-실제로-쓰이는-방식">5. 실제로 쓰이는 방식</h2>
<p>지금까지 <code>var</code>, <code>let</code>, <code>const</code>의 특징을 살펴봤다면
다음으로 중요한 것은 <strong>실제 코드에서 어떻게 사용하는가</strong>다.</p>
<p>현재 JavaScript에서는 보통 다음과 같은 기준을 사용한다.</p>
<ul>
<li>기본은 <code>const</code></li>
<li>값이 바뀌면 <code>let</code></li>
<li><code>var</code>는 사용하지 않음</li>
</ul>
<p>즉 변수 선언의 기본 출발점은 항상 <strong><code>const</code></strong>다.</p>
<h3 id="기본은-const">기본은 <code>const</code></h3>
<p>대부분의 변수는 한 번 값이 정해지면
프로그램 실행 동안 <strong>다시 바뀌지 않는 경우가 많다.</strong></p>
<p>예를 들어 이런 값들이다.</p>
<pre><code class="language-javascript">const API_URL = &quot;https://api.example.com&quot;;
const MAX_COUNT = 10;
const userName = &quot;Alice&quot;;</code></pre>
<p>이런 값들은 프로그램 중간에 변경될 필요가 없다.</p>
<p>그래서 처음부터 <strong><code>const</code>로 선언하는 것이 기본적인 방식</strong>이다.</p>
<h3 id="값이-바뀌어야-하면-let">값이 바뀌어야 하면 <code>let</code></h3>
<p>반대로 값이 바뀌는 경우에는 <code>let</code>을 사용한다.</p>
<p>예를 들어 카운트 값이나 반복문 변수 같은 경우다.</p>
<pre><code class="language-javascript">let count = 0;

count = count + 1;
count = count + 1;</code></pre>
<p>또는 반복문에서도 자주 사용된다.</p>
<pre><code class="language-javascript">for (let i = 0; i &lt; 5; i++) {
  console.log(i);
}</code></pre>
<p>이처럼 <strong>값이 변하는 변수</strong>는 <code>let</code>을 사용한다.</p>
<h3 id="var는-사실상-사용하지-않는다"><code>var</code>는 사실상 사용하지 않는다</h3>
<p>현대 JavaScript에서는 새로운 코드에서 <code>var</code>를 사용하는 경우가 거의 없다.
이유는 이미 살펴본 것처럼 <code>var</code>의 특성이 코드 이해를 어렵게 만들 수 있기 때문이다.</p>
<ul>
<li>블록 스코프가 없다</li>
<li>중복 선언이 가능하다</li>
<li>호이스팅으로 예측이 어렵다</li>
</ul>
<p>그래서 대부분의 코드 스타일 가이드에서는
<strong><code>var</code> 사용을 금지하거나 권장하지 않는다.</strong></p>
<h3 id="현재-javascript의-변수-선언-기준">현재 JavaScript의 변수 선언 기준</h3>
<p>정리하면 현재 JavaScript에서 변수 선언은
다음과 같은 기준으로 사용된다.</p>
<pre><code class="language-javascript">const userName = &quot;Alice&quot;; // 기본
let count = 0; // 값이 바뀌는 경우</code></pre>
<p>즉 개발자는 보통 이렇게 생각한다.</p>
<blockquote>
<p>이 값이 바뀔까?</p>
</blockquote>
<ul>
<li><strong>바뀌지 않는다 → <code>const</code></strong></li>
<li><strong>바뀐다 → <code>let</code></strong></li>
</ul>
<p>이 방식이 코드의 의도를 가장 명확하게 표현할 수 있기 때문이다.</p>
<hr>
<h2 id="6-핵심-정리">6. 핵심 정리</h2>
<ul>
<li>JavaScript에는 <strong>세 가지 변수 선언 방식</strong>이 있다: <code>var</code>, <code>let</code>, <code>const</code></li>
<li>초기 JavaScript에서는 <strong><code>var</code>만 존재</strong>했지만 여러 <strong>설계 특성으로 인해</strong> 현대 코드에서는 사용이 줄어들었다</li>
<li><code>var</code>는 <strong>함수 스코프, 중복 선언 가능, 호이스팅</strong> 등의 특징 때문에 예측하기 어려운 코드를 만들 수 있다</li>
<li>ES6에서 등장한 <code>let</code>과 <code>const</code>는 이러한 <strong>특성으로 인해 발생할 수 있는 혼란을 줄이기 위해</strong> 추가되었다</li>
<li><code>let</code>은 <strong>블록 스코프를 가지며 재할당은 가능하지만 재선언은 불가능</strong>하다</li>
<li><code>const</code>는 <strong>재할당이 불가능하며 선언과 동시에 초기화가 필요하다</strong></li>
<li>객체나 배열을 <code>const</code>로 선언해도 <strong>프로퍼티나 요소 수정은 가능하지만 참조 자체를 바꾸는 것은 불가능하다</strong></li>
<li>현대 JavaScript에서는 <strong>기본적으로 <code>const</code>를 사용하고, 값이 변경될 경우에만 <code>let</code>을 사용한다</strong></li>
<li><code>var</code>는 과거 코드에서 주로 볼 수 있으며 <strong>새로운 코드에서는 거의 사용하지 않는다</strong></li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript – 복사했는데 왜 같이 바뀔까?: 얕은 복사와 깊은 복사]]></title>
            <link>https://velog.io/@hogu__giriboy/JavaScript-%EB%B3%B5%EC%82%AC%ED%96%88%EB%8A%94%EB%8D%B0-%EC%99%9C-%EA%B0%99%EC%9D%B4-%EB%B0%94%EB%80%94%EA%B9%8C-%EC%96%95%EC%9D%80-%EB%B3%B5%EC%82%AC%EC%99%80-%EA%B9%8A%EC%9D%80-%EB%B3%B5%EC%82%AC</link>
            <guid>https://velog.io/@hogu__giriboy/JavaScript-%EB%B3%B5%EC%82%AC%ED%96%88%EB%8A%94%EB%8D%B0-%EC%99%9C-%EA%B0%99%EC%9D%B4-%EB%B0%94%EB%80%94%EA%B9%8C-%EC%96%95%EC%9D%80-%EB%B3%B5%EC%82%AC%EC%99%80-%EA%B9%8A%EC%9D%80-%EB%B3%B5%EC%82%AC</guid>
            <pubDate>Mon, 16 Mar 2026 05:16:38 GMT</pubDate>
            <description><![CDATA[<h2 id="1-그대로-복사했지만-바뀌는-건-원본">1. 그대로 복사했지만, 바뀌는 건 원본</h2>
<p>JavaScript를 공부하다 보면 한 번쯤 이런 상황을 만나게 된다.</p>
<pre><code class="language-javascript">const user = { name: &quot;Alice&quot; };

const copy = user;

copy.name = &quot;Bob&quot;;

console.log(user.name); //Bob</code></pre>
<p>코드를 보면 <code>user</code>를 <code>copy</code>에
<strong>복사한 것처럼 보이지만 실제로는 같은 객체를 참조하게 된다.</strong></p>
<p>그래서 많은 사람들이 이렇게 생각한다.</p>
<blockquote>
<p>copy를 수정했으니까 copy만 바뀌겠지?</p>
</blockquote>
<p>하지만 실제 결과는 그렇지 않다.</p>
<p><code>copy.name</code>을 <code>&quot;Bob&quot;</code>으로 바꾸면
같은 객체를 참조하고 있기 때문에 <strong>원본인 <code>user.name</code>도 <code>&quot;Bob&quot;</code>으로 바뀐다.</strong></p>
<p>즉 이런 상황이 된다.</p>
<ul>
<li>user → name: Bob</li>
<li>copy → name: Bob</li>
</ul>
<p>여기서 자연스럽게 이런 질문이 생긴다.</p>
<blockquote>
<p>왜 복사했는데 같이 바뀔까?</p>
</blockquote>
<blockquote>
<p>JavaScript에서 복사는 어떻게 동작하는 걸까?</p>
</blockquote>
<p>이 질문의 답을 이해하려면
먼저 <strong>JavaScript에서 데이터가 어떻게 저장되는지</strong>를 알아야 한다.</p>
<p>특히 중요한 포인트는 하나다.</p>
<p>JavaScript에서 객체 변수에는 <strong>객체 자체가 저장되는 것이 아니라</strong>
<strong>객체를 가리키는 참조 값이 저장된다.</strong></p>
<p>이 개념을 이해하면
왜 이런 일이 발생하는지 바로 보이기 시작한다.</p>
<hr>
<h2 id="2-javascript의-객체-저장법">2. JavaScript의 객체 저장법</h2>
<p>앞에서 봤던 예제를 다시 보면 이런 코드였다.</p>
<pre><code class="language-javascript">const user = { name: &quot;Alice&quot; };
const copy = user;

copy.name = &quot;Bob&quot;;</code></pre>
<p>겉으로 보면 <code>user</code>가 <code>copy</code>로 <strong>복사된 것처럼 보인다.</strong>
하지만 JavaScript 내부에서는 실제로 <strong>복사가 일어나지 않는다.</strong></p>
<p>왜냐하면 <strong>객체와 배열은 변수에 객체 자체가 저장되는 것이 아니라</strong>
<strong>객체를 가리키는 참조 값이 저장되기 때문</strong>이다.</p>
<h3 id="원시-타입은-값이-복사된다">원시 타입은 값이 복사된다</h3>
<p>먼저 <strong>원시 타입</strong>을 보자.</p>
<pre><code class="language-javascript">let a = 10;
let b = a;

b = 20;

console.log(a); //10</code></pre>
<p>이 경우에는 <code>a</code>의 <strong>값 자체가 복사된다.</strong></p>
<p>즉 메모리에서는 이렇게 된다.</p>
<ul>
<li>a → 10</li>
<li>b → 10</li>
</ul>
<p>그래서 <code>b</code>를 바꿔도 <code>a</code>는 영향을 받지 않는다.</p>
<h3 id="객체는-참조가-복사된다">객체는 참조가 복사된다</h3>
<p>하지만 <strong>객체나 배열은 다르게 동작한다.</strong></p>
<pre><code class="language-javascript">const user = { name: &quot;Alice&quot; };
const copy = user;</code></pre>
<p>이때 실제 구조는 이런 느낌이다.</p>
<pre><code class="language-text">user ─┐
      ├──&gt; { name: &quot;Alice&quot; }
copy ─┘</code></pre>
<p><code>user</code>와 <code>copy</code>가 <strong>같은 객체를 가리키고 있는 상태</strong>다.</p>
<p>그래서 <code>copy</code>를 수정하면</p>
<pre><code class="language-javascript">copy.name = &quot;Bob&quot;;</code></pre>
<p>실제로는 <strong>같은 객체를 수정하는 것</strong>이 된다.</p>
<pre><code class="language-text">user ─┐
      ├──&gt; { name: &quot;Bob&quot; }
copy ─┘</code></pre>
<p>여기서 중요한 포인트는
객체나 배열에서 <code>=</code>로 대입하면
<strong>객체 자체가 복사되는 것이 아니라 같은 객체를 가리키는 참조 값이 복사된다.</strong></p>
<p>그래서 개발에서는
<strong>객체를 &quot;진짜로 복사&quot;하는 방법</strong>이 필요해진다.</p>
<hr>
<h2 id="3-겉만-복사되는-얕은-복사">3. 겉만 복사되는 얕은 복사</h2>
<p>앞에서 봤듯이 객체를 단순히 대입하면</p>
<pre><code class="language-javascript">const copy = user;</code></pre>
<p>이건 <strong>복사가 아니라 같은 객체를 가리키는 것</strong>이었다.</p>
<p>그래서 개발에서는 <strong>객체를 새로 만들어 값을 복사하는 방법</strong>을 사용한다.
대표적인 방법이 바로 <strong>Spread 문법(<code>...</code>)</strong>이다.</p>
<pre><code class="language-javascript">const user = { name: &quot;Alice&quot; };

const copy = { ...user };

copy.name = &quot;Bob&quot;;

console.log(user.name); // Alice</code></pre>
<p>이번에는 <code>copy</code>를 수정해도
<strong>원본인 <code>user</code>는 바뀌지 않는다.</strong></p>
<p>즉 이번에는 <strong>정말로 복사가 된 것처럼 보인다.</strong></p>
<p>하지만 여기서 중요한 포인트가 있다.</p>
<p>Spread로 복사한 것은 <strong>완전한 복사가 아니라 &quot;얕은 복사&quot;</strong>다.</p>
<h3 id="중첩-객체에서-발생하는-문제">중첩 객체에서 발생하는 문제</h3>
<p>객체 안에 <strong>객체가 들어 있는 경우</strong>를 보자.</p>
<pre><code class="language-javascript">const user = {
  name: &quot;Alice&quot;,
  address: {
    city: &quot;Seoul&quot;,
  },
};

const copy = { ...user };

copy.address.city = &quot;Busan&quot;;

console.log(user.address.city); // Busan</code></pre>
<p>이번에는 다시 <strong>원본이 같이 바뀌어 버린다.</strong></p>
<p>왜 이런 일이 생길까?</p>
<p>Spread는 <strong>객체의 최상위 프로퍼티만 복사하기 때문</strong>이다.</p>
<p>즉 구조는 이렇게 된다.</p>
<pre><code class="language-text">user
 ├ name → &quot;Alice&quot;
 └ address ─┐
            └ { city: &quot;Seoul&quot; }

copy
 ├ name → &quot;Alice&quot;
 └ address ─┘ (같은 객체)</code></pre>
<p><code>name</code> 같은 <strong>원시 값은 새로운 값으로 복사되지만</strong>
<code>address</code> 같은 <strong>객체는 참조 값이 복사된다.</strong></p>
<p>그래서 <code>copy.address.city</code>를 바꾸면
<strong>같은 객체를 보고 있는 <code>user</code>도 같이 바뀐다.</strong></p>
<p>이처럼 <strong>객체의 최상위 프로퍼티만 복사되고 내부 객체는 참조가 공유되는 방식</strong>을
<strong>얕은 복사 (Shallow Copy)</strong>라고 한다.</p>
<hr>
<h2 id="4-완전히-분리되는-깊은-복사">4. 완전히 분리되는 깊은 복사</h2>
<p>앞에서 본 <strong>얕은 복사</strong>는 객체의 <strong>최상위 프로퍼티만 복사</strong>된다.
그래서 내부에 객체가 있으면 여전히 <strong>같은 데이터를 공유하게 된다.</strong></p>
<p>이 문제를 해결하려면 <strong>내부 객체까지 전부 새로 복사</strong>해야 한다.
이것을 <strong>깊은 복사(Deep Copy)</strong>라고 한다.</p>
<p>깊은 복사는 <strong>객체 내부에 있는 모든 중첩 객체까지</strong>
<strong>재귀적으로 복사하여 완전히 새로운 객체 구조를 만드는 것</strong>이다.</p>
<h3 id="json을-이용한-깊은-복사">JSON을 이용한 깊은 복사</h3>
<p>가장 흔히 사용되는 방법 중 하나는 JSON을 이용하는 방식이다.</p>
<pre><code class="language-javascript">const user = {
  name: &quot;Alice&quot;,
  address: {
    city: &quot;Seoul&quot;,
  },
};

const copy = JSON.parse(JSON.stringify(user));

copy.address.city = &quot;Busan&quot;;

console.log(user.address.city); // Seoul</code></pre>
<p>이 방식은 객체를 JSON 문자열로 변환한 뒤 다시 객체로 변환한다.
이 과정에서 <strong>직렬화 가능한 데이터만을 기준으로 새로운 객체가 생성된다.</strong></p>
<p>그래서 이제 구조는 이렇게 된다.</p>
<pre><code class="language-text">user ──&gt; { name: &quot;Alice&quot;, address: { city: &quot;Seoul&quot; } }

copy ──&gt; { name: &quot;Alice&quot;, address: { city: &quot;Seoul&quot; } }</code></pre>
<p>두 객체가 <strong>완전히 분리된 상태</strong>다.</p>
<p>그래서 <code>copy</code>를 수정해도
<code>user</code>에는 영향을 주지 않는다.</p>
<h3 id="structuredclone">structuredClone</h3>
<p>최근에는 JavaScript에서 <strong>깊은 복사를 위한 내장 함수</strong>도 제공한다.</p>
<pre><code class="language-javascript">const copy = structuredClone(user);</code></pre>
<p>이 함수는 객체 구조를 <strong>깊게 복사하도록 설계된 API</strong>다.</p>
<p>그래서 JSON 방식보다 <strong>더 다양한 데이터 타입을 지원하며</strong>
<strong>깊은 복사를 수행할 수 있다.</strong></p>
<p>여기까지 정리해봤을 때 흐름이 이렇게 된다.</p>
<ul>
<li>단순 대입 → <strong>참조 공유</strong></li>
<li>Spread → <strong>얕은 복사</strong></li>
<li>structuredClone/JSON 방식 → <strong>깊은 복사</strong></li>
</ul>
<h2 id="5-실제-개발에서-복사의-중요성">5. 실제 개발에서 복사의 중요성</h2>
<p>얕은 복사와 깊은 복사는 단순히 문법 문제가 아니라
<strong>데이터의 참조 관계와 변경 방식</strong>과 연결된 개념이다.</p>
<p>개발을 하다 보면 객체나 배열의 <strong>데이터를 변경해야 하는 상황</strong>이 자주 생긴다.</p>
<p>예를 들어 어떤 사용자 데이터를 다룬다고 해보자.</p>
<pre><code class="language-javascript">const user = {
  name: &quot;Alice&quot;,
  age: 25,
};</code></pre>
<p>이 데이터를 수정하면서도 <strong>원본 데이터를 유지해야 하는 상황</strong>이 생길 수 있다.</p>
<p>그래서 보통은 <strong>기존 데이터를 복사한 뒤 후 복사본을 수정</strong>한다.</p>
<pre><code class="language-javascript">const updatedUser = { ...user };

updatedUser.age = 26;</code></pre>
<p>이렇게 하면 <strong>원본 데이터는 그대로 두고</strong>
새로운 데이터를 만들어 사용할 수 있다.</p>
<p>이 방식은 특히 다음과 같은 상황에서 중요해진다.</p>
<h3 id="상태-관리">상태 관리</h3>
<p>프론트엔드에서는 상태(state)를 관리할 때
<strong>기존 데이터를 직접 수정하지 않는 방식</strong>을 많이 사용한다.</p>
<p>대신 기존 데이터를 복사하고 필요한 부분만 수정한 뒤 새로운 상태로 교체한다.</p>
<p>이때 <strong>얕은 복사/깊은 복사 개념을 이해하지 못하면</strong>
의도하지 않게 <strong>원본 데이터까지 바뀌는 버그</strong>가 생길 수 있다.</p>
<h3 id="데이터-수정-과정">데이터 수정 과정</h3>
<p>객체 안에 객체가 있는 구조에서는
얕은 복사로 인해 <strong>중첩 데이터가 함께 변경되는 문제</strong>가 자주 발생한다.</p>
<p>그래서 데이터를 다룰 때는 <strong>얕은 복사로 충분한지,</strong>
<strong>내부 객체까지 복사하는 깊은 복사가 필요한지</strong> 상황에 맞게 판단해야 한다.</p>
<hr>
<h2 id="6-핵심-정리">6. 핵심 정리</h2>
<ul>
<li>JavaScript에서 <strong>객체와 배열 변수에는 객체 자체가 아니라 객체를 가리키는 참조 값이 저장</strong>된다.</li>
<li>객체나 배열에서 <code>=</code>로 대입하면 <strong>객체 자체가 복사되는 것이 아니라 같은 객체를 가리키는 참조 값이 복사된다.</strong></li>
<li>Spread 문법(<code>...</code>)은 <strong>얕은 복사</strong>로, 객체의 <strong>최상위 프로퍼티만 복사</strong>된다.</li>
<li>객체 안에 객체가 있는 경우 <strong>내부 객체는 여전히 같은 참조를 공유</strong>한다.</li>
<li>내부 데이터까지 완전히 분리하려면 <strong>깊은 복사</strong>가 필요하다.</li>
<li><code>JSON.parse(JSON.stringify())</code>나 <code>structuredClone()</code> 같은 방법을 사용하면 <strong>중첩 객체까지 새로 복사</strong>할 수 있다.</li>
<li>데이터를 수정할 때 <strong>원본을 유지해야 하는 상황</strong>에서는 복사 방식에 대한 이해가 중요하다.</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[요청과 응답으로 움직이는 웹: HTTP와 REST API]]></title>
            <link>https://velog.io/@hogu__giriboy/%EC%9A%94%EC%B2%AD%EA%B3%BC-%EC%9D%91%EB%8B%B5%EC%9C%BC%EB%A1%9C-%EC%9B%80%EC%A7%81%EC%9D%B4%EB%8A%94-%EC%9B%B9-HTTP%EC%99%80-REST-API</link>
            <guid>https://velog.io/@hogu__giriboy/%EC%9A%94%EC%B2%AD%EA%B3%BC-%EC%9D%91%EB%8B%B5%EC%9C%BC%EB%A1%9C-%EC%9B%80%EC%A7%81%EC%9D%B4%EB%8A%94-%EC%9B%B9-HTTP%EC%99%80-REST-API</guid>
            <pubDate>Sun, 15 Mar 2026 06:47:18 GMT</pubDate>
            <description><![CDATA[<h2 id="1-브라우저만으로는-아무고또-모타죠">1. 브라우저만으로는 아무고또 모타죠</h2>
<p>처음 JavaScript를 배우면 보통 이런 것들을 만든다.</p>
<ul>
<li>버튼 클릭 이벤트</li>
<li>DOM 수정</li>
<li>애니메이션</li>
<li>입력값 처리</li>
</ul>
<p>이걸 보면 자연스럽게
JavaScript는 화면을 움직이는 언어라고 생각하기 쉽다.</p>
<p>하지만 실제 웹 서비스는 전혀 다른 구조 위에서 움직인다.</p>
<p>예를 들어 이런 기능들을 생각해보자.</p>
<ul>
<li>로그인</li>
<li>게시글 목록</li>
<li>댓글 작성</li>
<li>좋아요</li>
<li>상품 주문</li>
</ul>
<p>이 데이터들은 <strong>브라우저 안에 존재하지 않고</strong>
모두 <strong>서버에 저장되어 있다.</strong></p>
<p>그래서 브라우저는 항상 이런 행동을 한다.</p>
<pre><code class="language-text">서버에게 요청
→ 데이터 받아오기
→ 화면에 표시</code></pre>
<p>즉 브라우저는 사실 <strong>데이터를 가져오는 창구</strong>에 가깝다.</p>
<p>조금 다른 비유로 생각해보면 이해가 쉽다.
브라우저를 <strong>식당 손님</strong>이라고 생각해보자.</p>
<pre><code class="language-text">손님(브라우저)
→ 주문 요청
→ 주방(서버)
→ 요리 제작
→ 음식 전달</code></pre>
<p>손님은 요리를 만들 수 없고,
<strong>주문하는 것</strong>만 할 수 있다.</p>
<p>웹도 똑같다.
브라우저는 데이터를 만들지도 못하고,
저장하지도 못하고,
관리하지도 못한다.
이 역할은 모두 <strong>서버</strong>가 담당한다.</p>
<p>예를 들어 우리가 어떤 사이트에서 게시글 목록을 본다고 생각해보자.</p>
<p>브라우저는 서버에게 이런 요청을 보낸다.</p>
<pre><code class="language-text">GET /posts</code></pre>
<p>그러면 서버는 이런 데이터를 반환한다.</p>
<pre><code class="language-json">[
  { &quot;id&quot;: 1, &quot;title&quot;: &quot;Hello&quot; },
  { &quot;id&quot;: 2, &quot;title&quot;: &quot;World&quot; }
]</code></pre>
<p>그리고 브라우저는 이 데이터를 받아서 <strong>화면에 렌더링</strong>한다.</p>
<p>결국 웹은 우리가 처음 생각했던 것처럼</p>
<pre><code class="language-text">화면 → 기능</code></pre>
<p>이 구조가 아니라</p>
<pre><code class="language-text">요청 → 응답 → 화면</code></pre>
<p>이 구조로 움직인다.</p>
<p>그래서 웹을 이해할 때는 <strong>화면보다 먼저 통신 구조를 이해하는 것</strong>이 중요하다.</p>
<p>이제 자연스럽게 다음 질문이 생긴다.</p>
<blockquote>
<p>브라우저는 어떤 방식으로 서버에게 요청을 보내는 걸까?</p>
</blockquote>
<p>이 질문의 답이 바로 다음에서 등장하는 <strong>HTTP</strong>다.</p>
<hr>
<h2 id="2-웹은-결국-요청과-응답으로-움직인다">2. 웹은 결국 요청과 응답으로 움직인다</h2>
<p>앞에서 브라우저는 데이터를 직접 만들거나 저장하지 못한다고 이야기했다.
그래서 브라우저는 항상 <strong>서버에게 요청을 보내고, 응답을 받는 방식</strong>으로 동작한다.</p>
<p>이때 등장하는 것이 바로 <strong>HTTP</strong>다.</p>
<p>HTTP는 <strong>HyperText Transfer Protocol</strong>의 약자로
브라우저와 서버가 데이터를 주고받을 때 사용하는 <strong>통신 규칙</strong>이다.</p>
<p>조금 쉽게 말하면 브라우저와 서버가
서로 대화할 때 사용하는 <strong>약속된 방식</strong>이라고 볼 수 있다.</p>
<p>웹에서 어떤 일이 일어나든 결국 이 구조 안에서 움직인다.</p>
<pre><code class="language-text">브라우저 → 요청(Request)
서버 → 처리
서버 → 응답(Response)
브라우저 → 화면 표시</code></pre>
<p>이 흐름은 우리가 웹에서 하는 거의 모든 행동에 적용된다.</p>
<ul>
<li>페이지를 열 때</li>
<li>데이터를 조회할 때</li>
<li>게시글을 작성할 때</li>
<li>댓글을 수정하거나 삭제할 때</li>
</ul>
<p>이 모든 과정은 결국 <strong>요청과 응답의 반복</strong>이다.</p>
<p>그래서 웹을 이해할 때는
페이지나 화면보다 먼저 <strong>통신 구조</strong>를 이해하는 것이 중요하다.</p>
<p>그리고 여기서 한 가지 질문이 자연스럽게 생긴다.</p>
<p>브라우저는 서버에게 단순히 요청만 보내는 것이 아니라
<strong>여러 종류의 행동</strong>을 요청할 수 있다.</p>
<p>예를 들어</p>
<ul>
<li>데이터를 가져오는 요청</li>
<li>새로운 데이터를 만드는 요청</li>
<li>데이터를 수정하는 요청</li>
<li>데이터를 삭제하는 요청</li>
</ul>
<p>같은 것들이다.</p>
<p>그렇다면 브라우저는 이런 <strong>다양한 행동을 어떻게 구분해서 요청할까?</strong></p>
<p>이 질문의 답이 바로 다음에서 등장하는 <strong>HTTP 메서드</strong>다.</p>
<hr>
<h2 id="3-같은-주소-다른-행동-http-메서드">3. 같은 주소 다른 행동: HTTP 메서드</h2>
<p>웹에서 서버에게 요청을 보낼 때는 항상 <strong>URL</strong>이 존재한다.</p>
<p>예를 들어 이런 주소가 있다고 가정해보자.</p>
<pre><code class="language-text">/posts</code></pre>
<p>이 주소는 게시글과 관련된 자원을 의미한다.</p>
<p>하지만 우리는 이 주소에 대해 <strong>여러 가지 행동</strong>을 할 수 있다.</p>
<p>게시글 목록을 조회할 수도 있고
새로운 게시글을 만들 수도 있고
게시글을 수정할 수도 있고
게시글을 삭제할 수도 있다.</p>
<p>이때 등장하는 것이 바로 <strong>HTTP 메서드</strong>다.</p>
<p>HTTP 메서드는 같은 주소라도
<strong>어떤 행동을 요청하는지 구분하는 역할</strong>을 한다.</p>
<h3 id="get-데이터를-가져오는-요청">GET: 데이터를 가져오는 요청</h3>
<p>GET은 <strong>데이터를 조회할 때 사용하는 메서드</strong>다.</p>
<p>예를 들어 게시글 목록을 가져오는 요청은 이렇게 표현된다.</p>
<pre><code class="language-text">GET /posts</code></pre>
<p>이 요청의 의미는 단순하다.</p>
<pre><code class="language-text">/posts에 있는 데이터를 보내주세요</code></pre>
<p>GET 요청은 <strong>서버의 데이터를 변경하지 않고</strong>
데이터를 <strong>조회할 때 사용</strong>된다.</p>
<h3 id="post-새로운-데이터를-만드는-요청">POST: 새로운 데이터를 만드는 요청</h3>
<p>POST는 <strong>새로운 데이터를 생성할 때 사용하는 메서드</strong>다.</p>
<p>예를 들어 새로운 게시글을 작성할 때는 이런 요청이 만들어진다.</p>
<pre><code class="language-text">POST /posts</code></pre>
<p>이 요청은 보통 <strong>본문(body)</strong>에 데이터를 담아 서버로 보낸다.</p>
<pre><code class="language-json">{
  &quot;title&quot;: &quot;Hello&quot;,
  &quot;content&quot;: &quot;First post&quot;
}</code></pre>
<p>서버는 이 데이터를 받아서 <strong>새로운 게시글을 생성</strong>한다.</p>
<h3 id="put과-patch-데이터를-수정하는-요청">PUT과 PATCH: 데이터를 수정하는 요청</h3>
<p>데이터를 수정할 때는 <strong>PUT</strong>과 <strong>PATCH</strong>를 사용한다.</p>
<p>두 메서드는 비슷해 보이지만 역할이 다르다.</p>
<ul>
<li><strong>PUT</strong> → 데이터를 전체 교체</li>
<li><strong>PATCH</strong> → 일부만 수정</li>
</ul>
<p>예를 들어 게시글을 수정하는 요청은 이렇게 보낼 수 있다.</p>
<pre><code class="language-text">PATCH /posts/1</code></pre>
<p>이 요청은 <strong>게시글 일부 정보를 수정</strong>한다는 의미다.</p>
<h3 id="delete-데이터를-삭제하는-요청">DELETE: 데이터를 삭제하는 요청</h3>
<p>DELETE는 <strong>데이터를 삭제할 때 사용하는 메서드</strong>다.</p>
<p>예를 들어 게시글 하나를 삭제하는 요청은 이렇게 표현된다.</p>
<pre><code class="language-text">DELETE /posts/1</code></pre>
<p>이 요청의 의미는 간단하다.</p>
<pre><code class="language-text">id가 1인 게시글을 삭제해주세요</code></pre>
<p>이처럼 HTTP 메서드는 같은 URL이라도
<strong>어떤 행동을 할 것인지 구분하는 역할</strong>을 한다.</p>
<p>정리하면 다음과 같다.</p>
<ul>
<li>GET: 조회</li>
<li>POST: 생성</li>
<li>PUT: 전체 수정</li>
<li>PATCH: 부분 수정</li>
<li>DELETE: 삭제</li>
</ul>
<p>그래서 웹 API를 설계할 때는
<strong>URL + HTTP 메서드</strong> 조합으로 동작을 정의한다.</p>
<p>이제 여기서 또 하나의 궁금증이 생긴다.</p>
<blockquote>
<p>우리가 요청을 보냈을 때
서버는 <strong>요청이 성공했는지 실패했는지 어떻게 알려줄까?</strong></p>
</blockquote>
<p>이 역할을 하는 것이 바로 다음에서 등장하는 <strong>HTTP 상태 코드</strong>다.</p>
<hr>
<h2 id="4-열려라-상태창-http-상태-코드">4. 열려라 상태창: HTTP 상태 코드</h2>
<h3 id="요청의-결과를-알려주는-숫자">요청의 결과를 알려주는 숫자</h3>
<p>브라우저가 서버에게 요청을 보내면
서버는 단순히 데이터만 보내는 것이 아니라 <strong>요청의 처리 결과</strong>도 함께 알려준다.</p>
<p>이때 사용하는 것이 <strong>HTTP 상태 코드</strong>다.</p>
<p>HTTP 상태 코드는 요청이
<strong>성공했는지, 문제가 있었는지, 혹은 다른 처리가 필요한지</strong>를 나타내는 숫자다.</p>
<p>브라우저는 이 코드를 보고 <strong>요청 결과를 판단</strong>한다.</p>
<h3 id="대표적인-상태-코드">대표적인 상태 코드</h3>
<p>예를 들어 어떤 페이지를 요청했을 때
서버는 이런 응답을 보낼 수 있다.</p>
<pre><code class="language-text">200 OK</code></pre>
<p>이 코드는 요청이 <strong>정상적으로 처리되었다는 의미다.</strong></p>
<p>하지만 항상 성공하는 것은 아니다.
요청한 데이터가 없거나, 서버에 문제가 생길 수도 있다.</p>
<p>예를 들어 이런 경우가 있다.</p>
<pre><code class="language-text">404 Not Found</code></pre>
<p>이 코드는 요청한 자원을 <strong>서버에서 찾을 수 없을 때</strong> 반환된다.</p>
<p>또는 서버 내부에서 오류가 발생하면 이런 응답이 돌아올 수도 있다.</p>
<pre><code class="language-text">500 Internal Server Error</code></pre>
<p>HTTP 상태 코드는
<strong>요청이 어떻게 처리되었는지 알려주는 신호</strong> 역할을 한다.</p>
<p>상태 코드는 보통 <strong>첫 번째 숫자</strong>로 큰 의미를 구분한다.</p>
<ul>
<li>2xx → 요청 성공</li>
<li>4xx → 클라이언트 요청 문제</li>
<li>5xx → 서버 오류</li>
</ul>
<p>그래서 브라우저나 JavaScript 코드에서는
이 상태 코드를 확인해서 <strong>성공 처리나 오류 처리를 분기</strong>하게 된다.</p>
<p>웹 개발을 하다 보면 이 숫자들을 굉장히 자주 보게 된다.
특히 <strong>개발자 도구의 Network 탭</strong>에서 쉽게 확인할 수 있다.</p>
<p>그렇다면 서버와 통신할 때
<strong>URL은 어떤 기준으로 설계해야 할까?</strong></p>
<p>이 질문에서 등장하는 개념이 바로 <strong>REST API</strong>다.</p>
<hr>
<h2 id="5-로마에선-로마법-url에선-rest-api">5. 로마에선 로마법, URL에선 REST API</h2>
<h3 id="규칙-없는-url-복잡복잡의-지름길">규칙 없는 URL, 복잡복잡의 지름길</h3>
<p>서버와 통신할 때는 항상 <strong>URL</strong>을 사용한다.</p>
<p>예를 들어 게시글과 관련된 기능을 만든다고 가정해보자.</p>
<p>규칙 없이 API를 만든다면 URL이 이런 식으로 만들어질 수도 있다.</p>
<pre><code class="language-text">/getPosts
/createPost
/updatePost
/deletePost</code></pre>
<p>이 방식도 동작은 하지만
URL이 <strong>행동 중심으로 설계되어 있어 구조가 점점 복잡해질 수 있다.</strong></p>
<p>기능이 많아질수록</p>
<pre><code class="language-text">/getUserPosts
/createUserPost
/deleteUserPost</code></pre>
<p>처럼 URL이 계속 늘어나게 된다.</p>
<p>그래서 웹에서는 API를 설계할 때
<strong>일관된 규칙을 사용하는 접근 방식</strong>이 필요해졌다.</p>
<p>여기서 등장한 개념이 바로 <strong>REST</strong>다.</p>
<h3 id="자원을-기준으로-url을-설계하는-방식-rest">자원을 기준으로 URL을 설계하는 방식: REST</h3>
<p>REST는 <strong>Representational State Transfer</strong>의 약자로
웹에서 API를 설계할 때 사용하는 <strong>규칙</strong>이다.</p>
<p>REST의 핵심 아이디어는 단순하다.</p>
<p><strong>URL은 자원을 표현한다.</strong></p>
<p>예를 들어 게시글 API라면 URL은 이렇게 표현된다.</p>
<pre><code class="language-text">/posts
/posts/1</code></pre>
<p>여기서 <code>/posts</code>는 게시글이라는 <strong>자원(resource)</strong>을 의미하고
<code>/posts/1</code>은 <strong>id가 1인 게시글</strong>을 의미한다.</p>
<p>즉 REST에서는 <strong>URL이 데이터의 대상을 표현</strong>한다.</p>
<h3 id="행동은-url이-아니라-http-메서드가-담당">행동은 URL이 아니라 HTTP 메서드가 담당</h3>
<p>REST 구조에서는 <strong>행동을 URL에 넣지 않는다.</strong></p>
<p>대신 <strong>HTTP 메서드</strong>가 그 역할을 담당한다.</p>
<p>예를 들어 게시글 API는 이렇게 표현할 수 있다.</p>
<pre><code class="language-text">GET /posts
POST /posts
PATCH /posts/1
DELETE /posts/1</code></pre>
<p>여기서 <code>/posts</code>는 <strong>게시글이라는 자원</strong>을 나타내고
각 요청의 행동은 <strong>HTTP 메서드</strong>가 결정한다.</p>
<p>그래서 REST API는 보통 이런 구조로 정리한다.</p>
<ul>
<li>URL → 자원</li>
<li>HTTP 메서드 → 행동</li>
</ul>
<p>이 방식의 장점은 API 구조가 <strong>일관되고 이해하기 쉬워진다</strong>는 점이다.</p>
<p>그래서 대부분의 웹 서비스는
이와 같은 <strong>REST 방식으로 API를 설계</strong>한다.</p>
<p>그렇다면 서버는 이런 요청에 대해
<strong>어떤 형식으로 데이터를 응답할까?</strong></p>
<p>이때 등장하는 것이 바로 <strong>JSON</strong>이다.</p>
<hr>
<h2 id="6-서버와-브라우저의-papago-json">6. 서버와 브라우저의 PAPAGO: JSON</h2>
<h3 id="서로-다른-환경이-데이터를-주고받는-문제">서로 다른 환경이 데이터를 주고받는 문제</h3>
<p>브라우저와 서버는 서로 <strong>다른 환경</strong>에서 동작한다.</p>
<ul>
<li>브라우저 → JavaScript</li>
<li>서버 → 다양한 언어 (Java, Python, Node.js 등)</li>
</ul>
<p>서로 사용하는 언어가 다르기 때문에
데이터를 그대로 보내면 <strong>서로 이해하지 못할 가능성</strong>이 생긴다.</p>
<p>예를 들어 JavaScript 객체는 이렇게 생겼다.</p>
<pre><code class="language-javascript">const post = {
  id: 1,
  title: &quot;Hello&quot;,
};</code></pre>
<p>하지만 이 객체 구조는 <strong>JavaScript 문법</strong>이다.</p>
<p>서버가 다른 언어로 만들어져 있다면
이 객체를 그대로 이해하기 어렵다.</p>
<p>그래서 웹에서는 데이터를 주고받을 때
<strong>모두가 이해할 수 있는 공통 형식</strong>을 사용한다.</p>
<p>이 역할을 하는 것이 바로 <strong>JSON</strong>이다.</p>
<h3 id="데이터를-표현하는-공통-형식-json">데이터를 표현하는 공통 형식: JSON</h3>
<p>JSON은 <strong>JavaScript Object Notation</strong>의 약자로
데이터를 표현하기 위한 <strong>텍스트 기반 데이터 형식</strong>이다.</p>
<p>JSON의 구조는 JavaScript 객체와 매우 비슷하다.</p>
<p>예를 들어 이런 JSON 데이터가 있을 수 있다.</p>
<pre><code class="language-json">{
  &quot;id&quot;: 1,
  &quot;title&quot;: &quot;Hello&quot;
}</code></pre>
<p>이 구조는 언어와 상관없이
<strong>텍스트 형태로 데이터를 표현</strong>한다.</p>
<p>그래서 브라우저든 서버든
같은 방식으로 데이터를 읽을 수 있다.</p>
<p>즉 JSON은 웹에서 데이터를 주고받을 때 사용하는
<strong>공통 언어</strong>라고 볼 수 있다.</p>
<h3 id="서버-응답은-대부분-json">서버 응답은 대부분 JSON</h3>
<p>웹 API를 호출하면
서버는 보통 <strong>JSON 형식으로 데이터를 응답</strong>한다.</p>
<p>예를 들어 게시글 목록 요청을 보내면
이런 응답이 돌아올 수 있다.</p>
<pre><code class="language-json">[
  { &quot;id&quot;: 1, &quot;title&quot;: &quot;Hello&quot; },
  { &quot;id&quot;: 2, &quot;title&quot;: &quot;World&quot; }
]</code></pre>
<p>브라우저는 이 JSON 데이터를 받아서
JavaScript에서 사용할 수 있는 형태로 변환한 뒤
화면에 렌더링한다.</p>
<p>그렇다면 여기서 한 가지 궁금증이 생긴다.</p>
<p>JSON은 <strong>텍스트 기반 데이터 형식</strong>이다.</p>
<p>하지만 JavaScript에서는 보통 <strong>객체 형태</strong>로 데이터를 다룬다.</p>
<p>그렇다면 우리는 객체와 JSON 변환을 어떻게 해야 할까?</p>
<p>이 역할을 하는 것이 바로 다음에서 등장하는
<strong>JSON.stringify와 JSON.parse</strong>다.</p>
<hr>
<h2 id="7-객체와-문자열-사이를-오가는-두-개의-문">7. 객체와 문자열 사이를 오가는 두 개의 문</h2>
<p>앞에서 JSON은 <strong>텍스트 기반 데이터 형식</strong>이라고 했다.</p>
<p>하지만 JavaScript에서는 보통 데이터를 <strong>객체 형태</strong>로 다룬다.</p>
<p>예를 들어 이런 데이터가 있다고 해보자.</p>
<pre><code class="language-javascript">const post = {
  id: 1,
  title: &quot;Hello&quot;,
};</code></pre>
<p>이 객체는 JavaScript에서는 바로 사용할 수 있지만
서버와 통신할 때는 <strong>JSON 문자열 형태로 변환</strong>해야 한다.</p>
<p>반대로 서버에서 받은 JSON 데이터는
JavaScript에서 사용하려면 <strong>객체 형태로 변환</strong>해야 한다.</p>
<p>이 변환을 담당하는 것이 바로
<strong>JSON.stringify</strong>와 <strong>JSON.parse</strong>다.</p>
<h3 id="객체를-json-문자열로-jsonstringify">객체를 JSON 문자열로: JSON.stringify</h3>
<p><code>JSON.stringify</code>는 JavaScript 객체를
<strong>JSON 문자열로 변환하는 함수</strong>다.</p>
<p>예를 들어 이런 객체가 있다고 해보자.</p>
<pre><code class="language-javascript">const post = {
  id: 1,
  title: &quot;Hello&quot;,
};</code></pre>
<p>이 객체를 JSON 문자열로 변환하면 이렇게 된다.</p>
<pre><code class="language-javascript">JSON.stringify(post);</code></pre>
<p>결과는 다음과 같다.</p>
<pre><code class="language-json">{ &quot;id&quot;: 1, &quot;title&quot;: &quot;Hello&quot; }</code></pre>
<p>이 문자열은 <strong>JSON 형식의 데이터</strong>이기 때문에
서버로 전송할 수 있다.</p>
<p>그래서 새로운 데이터를 서버로 보낼 때
보통 이런 코드가 등장한다.</p>
<pre><code class="language-javascript">fetch(&quot;/posts&quot;, {
  method: &quot;POST&quot;,
  body: JSON.stringify(post),
});</code></pre>
<p>즉 <strong>객체 → JSON 문자열</strong> 변환을 담당하는 것이
<code>JSON.stringify</code>다.</p>
<h3 id="json-문자열을-객체로-바꾸는-jsonparse">JSON 문자열을 객체로 바꾸는 JSON.parse</h3>
<p>반대로 서버에서 데이터를 받으면
JSON 문자열을 <strong>JavaScript 객체로 변환</strong>해야 한다.</p>
<p>이때 사용하는 것이 <code>JSON.parse</code>다.</p>
<p>예를 들어 이런 JSON 데이터가 있다고 해보자.</p>
<pre><code class="language-json">{ &quot;id&quot;: 1, &quot;title&quot;: &quot;Hello&quot; }</code></pre>
<p>이 데이터를 JavaScript에서 사용하려면
이렇게 변환한다.</p>
<pre><code class="language-javascript">JSON.parse(`{&quot;id&quot;:1,&quot;title&quot;:&quot;Hello&quot;}`);</code></pre>
<p>그러면 결과는 다시 <strong>객체 형태</strong>가 된다.</p>
<pre><code class="language-javascript">{
  id: 1,
  title: &quot;Hello&quot;
}</code></pre>
<p>그래서 <code>JSON.parse</code>는
<strong>JSON 문자열 → JavaScript 객체</strong> 변환을 담당한다.</p>
<h3 id="두-함수의-역할-정리">두 함수의 역할 정리</h3>
<p>정리하면 이 두 함수는 서로 반대 역할을 한다.</p>
<ul>
<li>JSON.stringify : 객체 → JSON 문자열</li>
<li>JSON.parse : JSON 문자열 → 객체</li>
</ul>
<p>웹 통신에서는 <strong>객체와 JSON 사이의 변환이 반복적으로 발생</strong>한다.</p>
<ul>
<li>데이터를 <strong>보낼 때</strong> → <code>JSON.stringify</code></li>
<li>데이터를 <strong>받을 때</strong> → <code>JSON.parse</code></li>
</ul>
<p>이 과정을 이해하면
브라우저와 서버 사이의 데이터 흐름이 훨씬 명확해진다.</p>
<p>이제 실제 통신이 어떻게 이루어지는지
<strong>브라우저 개발자 도구</strong>에서 직접 확인해보자.</p>
<hr>
<h2 id="8-진짜-통신은-network-탭에">8. 진짜 통신은 Network 탭에</h2>
<p>지금까지 HTTP, REST API, JSON 같은 개념을 살펴봤다.
하지만 웹에서 실제 통신이 어떻게 이루어지는지는 <strong>브라우저 개발자 도구</strong>에서 직접 확인할 수 있다.</p>
<h3 id="요청과-응답을-직접-확인하는-곳">요청과 응답을 직접 확인하는 곳</h3>
<p>브라우저에는 <strong>개발자 도구(Developer Tools)</strong>라는 기능이 있다.
여기에는 웹 페이지의 동작을 확인할 수 있는 여러 탭이 있는데, 그중 하나가 <strong>Network 탭</strong>이다.</p>
<p>Network 탭은 브라우저가 서버와 주고받는 <strong>모든 요청과 응답을 기록</strong>한다.</p>
<p>예를 들어 어떤 웹 페이지를 열면 브라우저는 자동으로 여러 요청을 보낸다.</p>
<ul>
<li>HTML 요청</li>
<li>CSS 요청</li>
<li>JavaScript 요청</li>
<li>이미지 요청</li>
<li>API 요청</li>
</ul>
<p>이 요청들은 모두 Network 탭에 기록된다.</p>
<h3 id="요청과-응답의-정보가-모두-보인다">요청과 응답의 정보가 모두 보인다.</h3>
<p>Network 탭에서 하나의 요청을 선택하면
서버와 통신하면서 오간 여러 정보를 확인할 수 있다.</p>
<p>대표적으로 다음과 같은 것들이 보인다.</p>
<ul>
<li>요청 URL</li>
<li>HTTP 메서드</li>
<li>HTTP 상태 코드</li>
<li>응답 데이터</li>
</ul>
<p>예를 들어 API 요청을 확인하면
서버가 반환한 <strong>JSON 데이터</strong>도 직접 볼 수 있다.</p>
<p>그래서 웹 개발을 하다 보면 Network 탭은
<strong>가장 자주 확인하게 되는 도구 중 하나</strong>가 된다.</p>
<h3 id="지금까지-살펴본-것들의-연결고리">지금까지 살펴본 것들의 연결고리</h3>
<p>지금까지 살펴본 개념들은 사실 모두 Network 탭에서 확인할 수 있다.</p>
<p>예를 들어 어떤 API 요청을 보면 다음과 같은 정보가 보인다.</p>
<pre><code class="language-text">GET /posts
Status: 200
Response: JSON 데이터</code></pre>
<p>여기에서 우리가 앞에서 배운 것들이 모두 등장한다.</p>
<ul>
<li>HTTP 메서드</li>
<li>HTTP 상태 코드</li>
<li>JSON 응답</li>
</ul>
<p>즉 Network 탭은 <strong>웹 통신이 실제로 어떻게 이루어지는지 보여주는 창</strong>이라고 볼 수 있다.</p>
<p>이제 우리는</p>
<ul>
<li>웹이 요청과 응답 구조로 움직인다는 것</li>
<li>HTTP 메서드로 요청의 행동을 구분한다는 것</li>
<li>상태 코드로 요청 결과를 확인한다는 것</li>
<li>JSON으로 데이터를 주고받는다는 것</li>
</ul>
<p>까지 살펴봤다.</p>
<h2 id="9-핵심정리">9. 핵심정리</h2>
<p>웹은 단순히 <strong>화면을 보여주는 기술</strong>이 아니라
브라우저와 서버가 <strong>요청과 응답을 주고받는 통신 구조</strong> 위에서 동작한다.</p>
<p>브라우저는 서버에게 요청을 보내고
서버는 요청을 처리한 뒤 응답을 반환한다.</p>
<p>이때 브라우저와 서버가 통신할 때 사용하는 규칙이 <strong>HTTP</strong>다.</p>
<p>HTTP에서는 같은 URL이라도
<strong>HTTP 메서드</strong>를 통해 요청의 행동을 구분한다.</p>
<p>대표적으로 다음과 같은 메서드들이 있다.</p>
<ul>
<li>GET → 데이터 조회</li>
<li>POST → 데이터 생성</li>
<li>PUT → 전체 수정</li>
<li>PATCH → 부분 수정</li>
<li>DELETE → 데이터 삭제</li>
</ul>
<p>요청이 처리되면 서버는 <strong>HTTP 상태 코드</strong>를 통해
요청 결과가 성공인지 실패인지 알려준다.</p>
<p>대표적인 상태 코드 범주는 다음과 같다.</p>
<ul>
<li>2xx → 요청 성공</li>
<li>4xx → 클라이언트 요청 오류</li>
<li>5xx → 서버 오류</li>
</ul>
<p>또한 웹에서는 데이터를 주고받을 때
<strong>JSON이라는 텍스트 기반 데이터 형식</strong>을 사용한다.</p>
<p>JavaScript에서는 JSON 데이터를 다루기 위해
다음 두 가지 변환을 사용한다.</p>
<ul>
<li>JSON.stringify: JavaScript 객체 → JSON 문자열</li>
<li>JSON.parse: JSON 문자열 → JavaScript 객체</li>
</ul>
<p>마지막으로 브라우저의 <strong>Network 탭</strong>을 사용하면
이러한 통신 과정을 실제로 확인할 수 있다.</p>
<ul>
<li>요청 URL</li>
<li>HTTP 메서드</li>
<li>HTTP 상태 코드</li>
<li>JSON 응답</li>
</ul>
<p>즉 웹은 <code>요청 → 처리 → 응답</code> 이라는 흐름 위에서 움직이며
HTTP, REST API, JSON 같은 개념들은 모두
이 통신 구조를 이해하기 위한 핵심 요소들이다.</p>
<p>이 구조를 이해하면 브라우저와 서버 사이에서
<strong>데이터가 어떻게 오가는지</strong> 훨씬 명확하게 보이기 시작한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트 - 비동기의 진화: 콜백에서 async/await까지]]></title>
            <link>https://velog.io/@hogu__giriboy/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%9D%98-%EC%A7%84%ED%99%94-%EC%BD%9C%EB%B0%B1%EC%97%90%EC%84%9C-asyncawait%EA%B9%8C%EC%A7%80</link>
            <guid>https://velog.io/@hogu__giriboy/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%9D%98-%EC%A7%84%ED%99%94-%EC%BD%9C%EB%B0%B1%EC%97%90%EC%84%9C-asyncawait%EA%B9%8C%EC%A7%80</guid>
            <pubDate>Thu, 12 Mar 2026 05:31:45 GMT</pubDate>
            <description><![CDATA[<h2 id="1-자바스크립트-멈춰-">1. 자바스크립트: 멈춰 !!</h2>
<h3 id="동기-실행이란">동기 실행이란</h3>
<p>자바스크립트의 기본 실행 방식은 <strong>동기 실행(synchronous)</strong>다.
여기서 동기 실행은 <strong>코드가 작성된 순서대로 하나씩 실행되는 방식</strong>을
말한다.</p>
<p>즉 구조는 이렇게 생각하면 된다.</p>
<ol>
<li>작업 1 끝</li>
<li>작업 2 시작</li>
<li>작업 2 끝</li>
<li>작업 3 시작</li>
</ol>
<p>앞에 있는 작업이 끝나야 <strong>다음 코드가 실행</strong>된다.</p>
<pre><code class="language-javascript">console.log(&quot;1번 작업&quot;);
console.log(&quot;2번 작업&quot;);
console.log(&quot;3번 작업&quot;);</code></pre>
<p>코드의 <strong>작성 순서는 실행 순서</strong>다.
이것이 바로 <strong>동기 실행의 가장 큰 특징</strong>이다.</p>
<h3 id="동기-실행의-특징">동기 실행의 특징</h3>
<h4 id="직관적인-흐름">직관적인 흐름</h4>
<p>코드를 읽는 순서 그대로 실행되기 때문에 <strong>이해하기 쉽다</strong></p>
<pre><code class="language-javascript">step1();
step2();
step3();</code></pre>
<p>이걸 보면</p>
<ol>
<li>step1</li>
<li>step2</li>
<li>step3</li>
</ol>
<p>순서가 <strong>100% 보장</strong>된다.</p>
<h4 id="쉬운-디버깅">쉬운 디버깅</h4>
<p>코드가 순서대로 실행되기 때문에
문제가 생겨도 <strong>어디서 문제가 생겼는지</strong> 찾기 쉽다.</p>
<h3 id="웹에서-동기-방식이-가지는-문제">웹에서 동기 방식이 가지는 문제</h3>
<p>문제는 <strong>시간이 오래 걸리는 작업</strong>이 등장할 때 발생한다.</p>
<p>웹에서는 이런 작업이 굉장히 많은데, 예를 들어</p>
<ul>
<li>서버 요청 (API)</li>
<li>파일 읽기</li>
<li>타이머</li>
<li>사용자 입력</li>
</ul>
<p>이런 작업들은 <strong>몇 ms ~ 몇 초</strong>가 걸릴 수 있다.</p>
<p>예를 들어 서버 데이터를 가져온다고 해보자.</p>
<pre><code class="language-javascript">const data = fetch(&quot;https://api.example.com/data&quot;);
console.log(data);</code></pre>
<p>만약 이런 작업이 <strong>동기 방식</strong>으로 실행된다면
API 요청을 보낸 뒤 응답이 올 때까지 기다려야 한다.</p>
<p>그동안 다른 코드는 실행되지 못한다.</p>
<p>즉 이런 상태가 된다.</p>
<ul>
<li>버튼 클릭 안됨</li>
<li>스크롤 안됨</li>
<li>화면 멈춤</li>
</ul>
<p>이처럼 하나의 작업이 전체 실행 흐름을 막아버리는 상황을
<strong>블로킹(blocking)</strong>이라고 한다.</p>
<p>웹은 항상 <strong>사용자와 상호작용해야 하는 환경</strong>이다.</p>
<ul>
<li>클릭</li>
<li>스크롤</li>
<li>입력</li>
<li>애니메이션</li>
<li>API 요청</li>
</ul>
<p>이런 것들이 동시에 발생하는데
동기 방식만 사용하면 <strong>브라우저가 계속 멈추는 상황</strong>이 발생한다.</p>
<p>그래서 자바스크립트는
시간이 오래 걸리는 작업은 따로 처리하고 끝나고 알려주는
<strong>비동기(asynchronous)</strong> 방식을 사용한다.</p>
<p>그렇다면 여기서 하나의 질문이 생긴다.</p>
<blockquote>
<p>자바스크립트는 한 번에 하나만 실행한다면서
어떻게 비동기가 가능한 걸까?</p>
</blockquote>
<hr>
<h2 id="2-한-번에-하나만-실행하는-자바스크립트">2. 한 번에 하나만 실행하는 자바스크립트</h2>
<h3 id="싱글-스레드-언어">싱글 스레드 언어</h3>
<p>자바스크립트는 <strong>싱글 스레드 언어</strong>다.
여기서 스레드는 쉽게 말해 <strong>작업을 처리하는 흐름</strong>이라고 볼 수 있다.</p>
<p>즉 싱글 스레드는 <strong>한 번에 하나의 작업만 실행할 수 있다</strong>는 의미다.</p>
<p>예를 들어 이런 코드가 있다고 해보자.</p>
<pre><code class="language-javascript">function step1() {
  console.log(&quot;step1&quot;);
}

function step2() {
  console.log(&quot;step2&quot;);
}

function step3() {
  console.log(&quot;step3&quot;);
}

step1();
step2();
step3();</code></pre>
<p>실행 순서는 항상 아래와 같다.</p>
<ol>
<li>step1</li>
<li>step2</li>
<li>step3</li>
</ol>
<p>왜냐면 자바스크립트는 <strong>동시에 여러 작업을 실행하지 못하기 때문</strong>이다.
앞에 있는 작업이 끝나야 다음 작업이 실행된다.</p>
<p>그렇다면 앞서 언급했듯이 이런 의문이 생긴다.</p>
<blockquote>
<p>자바스크립트는 한 번에 하나만 실행한다면서
어떻게 비동기가 가능한 걸까?</p>
</blockquote>
<p>이걸 이해하려면 <strong>자바스크립트 실행 구조</strong>를 알아야 한다.</p>
<h3 id="call-stack-콜-스택">Call Stack (콜 스택)</h3>
<p>Call Stack은 <strong>현재 실행 중인 함수들이 쌓이는 공간</strong>이다.</p>
<p>여기서 스택은 &quot;Last In, First Out&quot; 구조를 갖는다.
쉽게 말하면 <strong>나중에 들어온 것이 먼저 나가는 구조</strong>다.</p>
<p>예를 들어 이런 코드가 있다.</p>
<pre><code class="language-javascript">function first() {
  second();
}

function second() {
  console.log(&quot;hello&quot;);
}

first();</code></pre>
<p>실행 흐름은 다음과 같다.</p>
<ol>
<li>first()</li>
<li>second()</li>
<li>console.log()</li>
</ol>
<p>그리고 실행이 끝나면 <strong>역순으로 빠져나온다.</strong></p>
<ol>
<li>console.log 종료</li>
<li>second 종료</li>
<li>first 종료</li>
</ol>
<p>Call Stack은 항상
함수가 들어오고 실행된 뒤 빠져나가는 구조로 동작한다.</p>
<p>여기까지는 모두 <strong>동기적인 실행 구조</strong>다.</p>
<p>그런데 자바스크립트에서는
이 흐름을 깨는 것처럼 보이는 코드가 등장한다.</p>
<pre><code class="language-javascript">console.log(&quot;1&quot;);

setTimeout(() =&gt; {
  console.log(&quot;2&quot;);
}, 0);

console.log(&quot;3&quot;);</code></pre>
<p>실행 결과는 <code>1 → 3 → 2</code>를 갖는다.</p>
<p>코드를 보면 <code>1 → 2 → 3</code> 순서로 실행될 것 같은데
실제로는 <strong>3이 먼저 실행된다.</strong></p>
<p>왜 이런 일이 발생하는지,
여기서 등장하는 게 <strong>Web API</strong>다.</p>
<h3 id="web-api">Web API</h3>
<p><code>setTimeout</code> 같은 기능은
<strong>자바스크립트 엔진이 직접 처리하는 게 아니다.</strong></p>
<p>브라우저가 대신 처리한다.</p>
<p>흐름은 다음과 같이 진행된다.</p>
<ol>
<li>setTimeout이 Call Stack에서 실행된다</li>
<li>타이머 작업을 Web API에게 맡긴다</li>
<li>타이머가 끝나면 콜백이 Queue로 이동한다</li>
</ol>
<p>즉 자바스크립트는
&quot;이거 오래 걸리는 작업이니, 브라우저가 대신 하고 끝나면 알려줘&quot;
라고 말하고 있는 것이다.</p>
<h3 id="callback-queue">Callback Queue</h3>
<p>타이머가 끝나면 브라우저는 <strong>콜백 함수를 Queue에 넣는다.</strong></p>
<p>이 공간을 <strong>Callback Queue</strong>라고 한다.</p>
<pre><code class="language-text">Callback Queue

console.log(&quot;2&quot;)</code></pre>
<p>근데 여기서 중요한 점이 하나 있는데,
콜백 함수는 <strong>바로 실행되지 못한다.</strong>
왜냐면 자바스크립트는 <strong>한 번에 하나만 실행</strong>하기 때문이다.
그래서 대기하고 있다.</p>
<h3 id="event-loop">Event Loop</h3>
<p>여기서 등장하는 게 <strong>Event Loop</strong>다.</p>
<p>Event Loop는 다음 과정을 반복한다.</p>
<ol>
<li>Call Stack 비어있는지 확인</li>
<li>비어있으면 Queue 확인</li>
<li>콜백을 Stack으로 이동</li>
<li>실행</li>
</ol>
<p>그래서 아까 코드가 이런 식으로 실행됐던 것이다.</p>
<ol>
<li>1 출력</li>
<li>setTimeout → Web API로 이동</li>
<li>3 출력</li>
<li>Call Stack 비어 있음</li>
<li>Callback Queue 확인</li>
<li>2 실행</li>
</ol>
<p>그래서 결과가 <code>1 → 3 → 2</code>가 된다.</p>
<hr>
<h2 id="3-콜백-함수의-등장">3. 콜백 함수의 등장</h2>
<h3 id="콜백-함수">콜백 함수</h3>
<p>콜백 함수는 <strong>다른 함수에 전달되는 함수</strong>다.</p>
<p>즉 함수가 <strong>값처럼 전달되는 상황</strong>이라고 보면 된다.</p>
<p>예를 들어 이런 코드가 있다.</p>
<pre><code class="language-javascript">function greet(name) {
  console.log(&quot;Hello &quot; + name);
}

function processUser(callback) {
  const user = &quot;Alice&quot;;
  callback(user);
}

processUser(greet);</code></pre>
<p>실행 흐름을 보면 이렇게 된다.</p>
<ol>
<li>processUser 실행</li>
<li>greet 함수 전달됨</li>
<li>callback(user) 실행</li>
<li>greet(&quot;Alice&quot;) 실행</li>
</ol>
<p>즉 <code>greet</code>함수는 <strong>직접 호출된 것이 아니라</strong>
<strong>다른 함수에 전달된 뒤 실행된 것</strong>이다.</p>
<p>이게 바로 <strong>콜백 함수</strong>다.</p>
<h3 id="콜백의-핵심-개념-제어권-전달">콜백의 핵심 개념: 제어권 전달</h3>
<p>콜백에서 가장 중요한 개념은 <strong>제어권 전달</strong>이다.</p>
<p>일반 함수는 우리가 직접 실행한다.</p>
<pre><code class="language-javascript">greet(&quot;Alice&quot;);</code></pre>
<p>하지만 콜백 함수는 우리가 실행하지 않는다.</p>
<pre><code class="language-javascript">processUser(greet);</code></pre>
<p>여기서는 <code>processUser</code>가 <strong>언제 실행할지 결정</strong>한다.</p>
<p>즉 함수를 넘겨주면
실행 타이밍은 다른 함수가 결정한다.</p>
<p>이렇게 실행 제어가 다른 함수로 넘어가는 것을
<strong>제어권이 넘어갔다</strong>고 말한다.</p>
<h3 id="비동기에서-콜백이-필요한-이유">비동기에서 콜백이 필요한 이유</h3>
<p>비동기에서는 <strong>작업이 언제 끝날지 모른다.</strong></p>
<p>예를 들어 이런 작업들이 있다.</p>
<ul>
<li>API 요청</li>
<li>파일 읽기</li>
<li>타이머</li>
</ul>
<p>이런 작업들은 <strong>몇 ms ~ 몇 초</strong> 걸릴 수도 있다.</p>
<p>그래서 자바스크립트는
&quot;이 작업 끝나면 이 함수 실행해줘&quot;
라고 생각한다.</p>
<p>이런 코드를 예로 들 수 있다.</p>
<pre><code class="language-javascript">setTimeout(() =&gt; {
  console.log(&quot;2초 후 실행&quot;);
}, 2000);</code></pre>
<p>여기서</p>
<pre><code class="language-text">() =&gt; {
  console.log(&quot;2초 후 실행&quot;);
};</code></pre>
<p>이 함수가 바로 <strong>콜백 함수</strong>다.</p>
<p>즉 &quot;2초 타이머 끝나면 이 함수 실행&quot; 이라는 의미를 담는다.</p>
<h3 id="결과-전달과-콜백-패턴">결과 전달과 콜백 패턴</h3>
<p>비동기 작업은 보통 <strong>결과를 콜백으로 전달</strong>한다.</p>
<pre><code class="language-javascript">function getData(callback) {
  const data = &quot;server data&quot;;
  callback(data);
}

getData(function (result) {
  console.log(result);
});</code></pre>
<p>실행 흐름은 이렇게 된다.</p>
<ol>
<li>getData 실행</li>
<li>데이터 생성</li>
<li>callback(data)</li>
<li>전달된 함수 실행</li>
<li>result 출력</li>
</ol>
<p>그래서 비동기 코드에서는</p>
<p>작업 실행 → 작업 완료 → 콜백 실행</p>
<p>이런 구조가 자주 나타난다.</p>
<p>하지만 비동기 작업이 여러 개 이어지기 시작하면
코드는 점점 복잡해진다.</p>
<hr>
<h2 id="4-어서-오게-콜백-지옥에">4. 어서 오게, 콜백 지옥에</h2>
<h3 id="비동기-작업의-순서-문제">비동기 작업의 순서 문제</h3>
<p>비동기 작업은 앞서 꾸준히 언급했듯이 <strong>언제 끝날지 알 수 없다.</strong></p>
<p>예를 들어 이런 작업들이 있다고 생각해보자.</p>
<ul>
<li>사용자 정보 가져오기</li>
<li>게시글 가져오기</li>
<li>댓글 가져오기</li>
</ul>
<p>이 작업들은 각각 <strong>서버 요청</strong>이기 때문에
완료되는 시간이 서로 다를 수 있다.</p>
<p>하지만 실제 프로그램에서는 이런 식으로 <strong>순서가 필요할 때</strong>가 많다.</p>
<ol>
<li>사용자 정보 가져오기</li>
<li>게시글 가져오기</li>
<li>댓글 가져오기</li>
</ol>
<p>즉 <strong>앞 작업이 끝난 뒤 다음 작업이 실행되어야 한다.</strong></p>
<p>그래서 보통 이렇게 코드를 작성하게 된다.</p>
<h3 id="콜백으로-순서-제어하기">콜백으로 순서 제어하기</h3>
<p>콜백을 사용하면 <strong>작업이 끝난 뒤 다음 작업을 실행</strong>할 수 있다.</p>
<p>예를 들어 이런 코드다.</p>
<pre><code class="language-javascript">getUser(function (user) {
  getPosts(user.id, function (posts) {
    getComments(posts[0].id, function (comments) {
      console.log(comments);
    });
  });
});</code></pre>
<p>실행 흐름은 이렇게 된다.</p>
<ol>
<li>getUser 실행</li>
<li>사용자 데이터 도착</li>
<li>getPosts 실행</li>
<li>게시글 데이터 도착</li>
<li>getComments 실행</li>
<li>댓글 데이터 도착</li>
</ol>
<p>즉 <strong>작업이 끝날 때마다 다음 작업을 콜백 안에서 실행</strong>하는 구조다.</p>
<h3 id="콜백-지옥-callback-hell">콜백 지옥 (Callback Hell)</h3>
<p>문제는 작업이 많아질 때 발생한다.</p>
<p>콜백 안에 또 콜백이 들어가고
그 안에 또 콜백이 들어간다.</p>
<p>작업이 하나라면 괜찮지만,
작업이 계속 이어지기 시작하면 코드 구조가 점점 복잡해진다.</p>
<p>코드는 이렇게 변한다.</p>
<pre><code class="language-javascript">getUser(function (user) {
  getPosts(user.id, function (posts) {
    getComments(posts[0].id, function (comments) {
      getLikes(comments[0].id, function (likes) {
        getNotifications(likes.userId, function (notifications) {
          console.log(notifications);
        });
      });
    });
  });
});</code></pre>
<p>코드를 보면 구조가 계속 오른쪽으로 밀리는 특징을 확인할 수 있다.
그래서 이 구조를 <strong>피라미드 형태</strong>라고 부르기도 한다.</p>
<pre><code class="language-text">콜백
⎿ 콜백
    ⎿ 콜백
        ⎿ 콜백
            ⎿ 콜백</code></pre>
<p>이렇게 코드가 점점 깊어지는 구조를
<strong>Callback Hell (콜백 지옥)</strong>이라고 한다.</p>
<h3 id="콜백-방식의-한계">콜백 방식의 한계</h3>
<p>콜백 지옥이 발생하면 여러 문제가 생긴다.</p>
<h4 id="가독성-문제">가독성 문제</h4>
<p>코드가 오른쪽으로 계속 밀려서
<strong>읽기가 매우 어려워진다.</strong></p>
<h4 id="유지보수-문제">유지보수 문제</h4>
<p>콜백 구조가 깊어지면
<strong>어떤 작업이 언제 실행되는지 파악하기 어렵다.</strong></p>
<h4 id="에러-처리-문제">에러 처리 문제</h4>
<p>비동기 콜백이 많아질수록
<strong>에러 처리 구조도 복잡해진다.</strong></p>
<p>그래서 자바스크립트에서는
이 문제를 해결하기 위한 새로운 방식이 등장한다.</p>
<hr>
<h2 id="5-해결사-promise">5. 해결사 Promise</h2>
<h3 id="promise">Promise</h3>
<p>콜백 지옥이 발생하는 이유는
<strong>비동기 작업의 흐름을 콜백으로 계속 연결해야 하기 때문</strong>이다.</p>
<p>즉 작업이 많아질수록 코드는 이렇게 된다.</p>
<pre><code class="language-javascript">getUser(function (user) {
  getPosts(user.id, function (posts) {
    getComments(posts[0].id, function (comments) {
      console.log(comments);
    });
  });
});</code></pre>
<p>코드가 계속 <strong>오른쪽으로 밀리는 구조</strong>가 된다.</p>
<p>그래서 자바스크립트에서는
이 문제를 해결하기 위해 <strong>Promise</strong>라는 개념이 등장했다.</p>
<p>Promise는 <strong>비동기 작업의 결과를 표현하는 객체</strong>다.</p>
<p>즉 &quot;미래에 완료될 작업&quot;을 <strong>객체 형태로 표현</strong>하는 방식이다.</p>
<h3 id="promise의-세-가지-상태">Promise의 세 가지 상태</h3>
<p>Promise는 항상 <strong>세 가지 상태 중 하나</strong>를 가진다.</p>
<ol>
<li>Pending</li>
</ol>
<p>아직 작업이 완료되지 않은 상태</p>
<ol start="2">
<li>Fulfilled</li>
</ol>
<p>작업이 성공적으로 완료된 상태</p>
<ol start="3">
<li>Rejected</li>
</ol>
<p>작업이 실패한 상태</p>
<p>즉 Promise는 Pending 상태에서 Fulfilled, 또는 Rejected 상태로 변경된다.</p>
<h3 id="resolve와-reject">resolve와 reject</h3>
<p>Promise 내부에서는 <code>resolve</code> 함수와 <code>reject</code> 함수를 사용한다.</p>
<p>예를 들어 이런 코드다.</p>
<pre><code class="language-javascript">const promise = new Promise((resolve, reject) =&gt; {
  const success = true;

  if (success) {
    resolve(&quot;작업 성공&quot;);
  } else {
    reject(&quot;작업 실패&quot;);
  }
});</code></pre>
<p>여기서 resolve는 작업 성공을,
reject는 작업 실패를 의미한다.</p>
<h3 id="thencatchfinally">then/catch/finally</h3>
<p>Promise는 작업이 끝났을 때 실행할 함수를 연결할 수 있다.</p>
<pre><code class="language-javascript">promise
  .then((result) =&gt; {
    console.log(result);
  })
  .catch((error) =&gt; {
    console.log(error);
  })
  .finally(() =&gt; {
    console.log(&quot;작업 완료&quot;);
  });</code></pre>
<p>각각의 역할은 다음과 같다.</p>
<ul>
<li>then → 성공 처리</li>
<li>catch → 실패 처리</li>
<li>finally → 성공/실패 상관없이 실행</li>
</ul>
<h3 id="promise-체이닝">Promise 체이닝</h3>
<p>Promise의 가장 큰 장점은
<strong>비동기 작업을 순서대로 연결할 수 있다는 점</strong>이다.</p>
<pre><code class="language-javascript">getUser()
  .then((user) =&gt; {
    return getPosts(user.id);
  })
  .then((posts) =&gt; {
    return getComments(posts[0].id);
  })
  .then((comments) =&gt; {
    console.log(comments);
  })
  .catch((error) =&gt; {
    console.error(error);
  });</code></pre>
<p>이 코드는 아까 콜백 코드와 같은 작업을 한다.</p>
<p>하지만 코드 구조는 오른쪽으로 밀리는 구조에서
<strong>왼쪽 정렬 상태</strong>를 유지한다.</p>
<p>그래서 Promise는
<strong>콜백 지옥을 해결하는 방법</strong>으로 등장했다.</p>
<h3 id="콜백-vs-promise">콜백 vs Promise</h3>
<h4 id="에러-처리-문제-1">에러 처리 문제</h4>
<pre><code class="language-javascript">getUser(function (err, user) {
  if (err) {
    console.error(err);
    return;
  }

  getPosts(user.id, function (err, posts) {
    if (err) {
      console.error(err);
      return;
    }

    getComments(posts[0].id, function (err, comments) {
      if (err) {
        console.error(err);
        return;
      }

      console.log(comments);
    });
  });
});</code></pre>
<p>여기서 특징이 하나 보인다.</p>
<pre><code class="language-text">if (err)
if (err)
if (err)</code></pre>
<p>에러 처리가 <strong>계속 반복된다.</strong></p>
<p>그런데 Promise에서는 이런식으로 바뀐다.</p>
<pre><code class="language-javascript">getUser()
  .then((user) =&gt; getPosts(user.id))
  .then((posts) =&gt; getComments(posts[0].id))
  .then((comments) =&gt; console.log(comments))
  .catch((error) =&gt; console.error(error));</code></pre>
<p>여기서는 catch 하나로 에러를 처리할 수 있다.</p>
<p>즉 콜백은 에러 처리를 반복하고,
Promise는 에러 처리를 중앙화 하는 차이가 생긴다.</p>
<h4 id="코드-흐름-읽기">코드 흐름 읽기</h4>
<p>콜백 코드는 읽을 때 위에서 안으로 파고드는 느낌을 갖는 반면,
Promise는 위에서 아래로 흐르는 느낌을 갖는다.</p>
<p>즉 콜백은 구조를 따라 읽어야 하고,
Promise는 흐름을 따라 읽으면 되는 차이점도 있다.</p>
<p>이러한 차이를 정리하면
Promise의 장점은 다음과 같다.</p>
<ul>
<li>코드가 오른쪽으로 밀리지 않는다</li>
<li>에러 처리가 단순해진다</li>
<li>비동기 흐름을 읽기 쉬워진다</li>
</ul>
<p>하지만 Promise 역시 완벽한 해결책은 아니었다.</p>
<hr>
<h2 id="6-이-지옥을-끝내러-왔다-asyncawait">6. 이 지옥을 끝내러 왔다: async/await</h2>
<h3 id="promise의-한계">Promise의 한계</h3>
<p>Promise는 콜백 지옥을 해결해 주었다.
비동기 작업을 <strong>체이닝 방식으로 연결</strong>할 수 있었기 때문이다.</p>
<pre><code class="language-javascript">getUser()
  .then((user) =&gt; getPosts(user.id))
  .then((posts) =&gt; getComments(posts[0].id))
  .then((comments) =&gt; console.log(comments))
  .catch((error) =&gt; console.error(error));</code></pre>
<p>콜백처럼 코드가 오른쪽으로 밀리지는 않는다.</p>
<p>하지만 Promise에도 아쉬운 점은 있었다.</p>
<ul>
<li><code>.then()</code>이 계속 이어지면 <strong>가독성이 떨어진다.</strong></li>
<li>동기 코드처럼 읽기 어렵다</li>
</ul>
<p>그래서 등장한 문법이 <strong>async/await</strong>이다.</p>
<h3 id="async-함수">async 함수</h3>
<p><code>async</code>는 <strong>비동기 함수를 선언할 때 사용하는 키워드다.</strong></p>
<pre><code class="language-javascript">async function example() {
  return &quot;hello&quot;;
}</code></pre>
<p>여기서 중요한 특징이 하나 있다.</p>
<p><code>async</code> 함수는 항상 Promise를 반환한다.</p>
<p>예를 들어</p>
<pre><code class="language-javascript">async function example() {
  return &quot;hello&quot;;
}</code></pre>
<p>이 코드는 실제로는 이렇게 동작한다.</p>
<pre><code class="language-javascript">function example() {
  return Promise.resolve(&quot;hello&quot;);
}</code></pre>
<p>즉 <code>async</code> 함수는 <strong>내부적으로 Promise를 반환하는 함수</strong>다.</p>
<h3 id="await-키워드">await 키워드</h3>
<p><code>await</code>는 <strong>Promise가 완료될 때까지 기다리는 키워드</strong>다.</p>
<p>예를 들어 Promise 코드가 이렇게 있다고 해보자.</p>
<pre><code class="language-javascript">getUser()
  .then((user) =&gt; getPosts(user.id))
  .then((posts) =&gt; console.log(posts));</code></pre>
<p>이 코드를 <code>async/await</code>으로 바꾸면 이렇게 된다.</p>
<pre><code class="language-javascript">async function run() {
  const user = await getUser();
  const posts = await getPosts(user.id);

  console.log(posts);
}</code></pre>
<p>코드를 보면 <strong>비동기 코드인데 동기 코드처럼</strong> 읽히는 특징이 있다.
이게 바로 <code>async/await</code>의 가장 큰 장점이다.</p>
<h3 id="asyncawait-에러-처리">async/await 에러 처리</h3>
<p>Promise에서는 보통 이렇게 에러를 처리했다.</p>
<pre><code class="language-javascript">getUser()
  .then((user) =&gt; getPosts(user.id))
  .catch((error) =&gt; console.error(error));</code></pre>
<p>하지만 async/await에서는 <strong>try/catch</strong>를 사용할 수 있다.</p>
<pre><code class="language-javascript">async function loadData() {
  try {
    const user = await getUser();
    const posts = await getPosts(user.id);

    console.log(posts);
  } catch (error) {
    console.error(error);
  }
}</code></pre>
<p>그래서 에러 처리도 <strong>동기 코드처럼 작성할 수 있다.</strong></p>
<h3 id="병렬-실행-promiseall">병렬 실행 (Promise.all)</h3>
<p><code>await</code>는 기본적으로 <strong>순차 실행</strong>을 만든다.</p>
<ol>
<li>getUser 실행</li>
<li>getUser 완료</li>
<li>getPosts 실행</li>
</ol>
<p>하지만 작업을 <strong>동시에 실행</strong>하고 싶다면,
<code>Promise.all</code>을 사용할 수 있다.</p>
<pre><code class="language-javascript">const [user, posts] = await Promise.all([getUser(), getPosts()]);</code></pre>
<p>이렇게 하면 두 작업이 <strong>동시에 실행</strong>된다.</p>
<hr>
<h2 id="7-핵심-정리">7. 핵심 정리</h2>
<p>자바스크립트의 기본 실행 방식은 <strong>동기 실행</strong>이다.
즉 코드는 <strong>위에서 아래로 순서대로 실행</strong>된다.</p>
<p>하지만 웹 환경에서는</p>
<ul>
<li>API 요청</li>
<li>타이머</li>
<li>파일 처리</li>
<li>사용자 입력</li>
</ul>
<p>처럼 <strong>시간이 오래 걸리는 작업</strong>이 자주 발생한다.</p>
<p>이런 작업이 동기 방식으로 실행되면
하나의 작업이 전체 실행 흐름을 멈추게 된다.</p>
<p>그래서 자바스크립트는
<strong>Event Loop 구조를 통해 비동기 작업의 실행 흐름을 관리</strong>한다.</p>
<p>이 과정에서 등장한 흐름은 다음과 같다.</p>
<ol>
<li>동기 실행</li>
<li>비동기 처리 구조 (Event Loop)</li>
<li>콜백 함수</li>
<li>콜백 지옥</li>
<li>Promise</li>
<li>async/await</li>
</ol>
<p>콜백은 비동기 작업이 끝난 뒤
다음 작업을 실행하기 위한 방법이었지만
콜백이 중첩되면서 <strong>콜백 지옥</strong> 문제가 발생했다.</p>
<p>이 문제를 해결하기 위해
비동기 작업의 결과를 객체로 표현하는 <strong>Promise</strong>가 등장했다.</p>
<p>그리고 Promise를 <strong>더 읽기 쉽고 직관적으로 작성하기 위해</strong>
<code>async/await</code> 문법이 추가되었다.</p>
<p>그래서 현재 자바스크립트 비동기 코드는 보통 다음 방식으로 작성된다.</p>
<pre><code class="language-text">Promise + async/await</code></pre>
<p>이는 <strong>비동기 코드를 동기 코드처럼 읽을 수 있게 만드는 방식</strong>이다.</p>
<p>현재 대부분의 자바스크립트 비동기 코드는
Promise와 async/await를 중심으로 작성된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[클로저(Closure) 이해하기: 함수가 스코프를 기억하는 이유]]></title>
            <link>https://velog.io/@hogu__giriboy/%ED%81%B4%EB%A1%9C%EC%A0%80Closure-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-%ED%95%A8%EC%88%98%EA%B0%80-%EC%8A%A4%EC%BD%94%ED%94%84%EB%A5%BC-%EA%B8%B0%EC%96%B5%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0</link>
            <guid>https://velog.io/@hogu__giriboy/%ED%81%B4%EB%A1%9C%EC%A0%80Closure-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-%ED%95%A8%EC%88%98%EA%B0%80-%EC%8A%A4%EC%BD%94%ED%94%84%EB%A5%BC-%EA%B8%B0%EC%96%B5%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0</guid>
            <pubDate>Mon, 09 Mar 2026 10:07:08 GMT</pubDate>
            <description><![CDATA[<h2 id="1-클로저가-등장하는-이유">1. 클로저가 등장하는 이유</h2>
<p>클로저는 <strong>갑자기 등장한 개념이 아니다.</strong>
보통 <strong>하나의 의문에서 시작된다.</strong></p>
<p>다음 코드를 보자.</p>
<pre><code class="language-javascript">function outer() {
  const message = &quot;hello&quot;;

  function inner() {
    console.log(message);
  }

  return inner;
}

const fn = outer();
fn();</code></pre>
<p>코드의 실행 흐름을 보면 다음과 같다.</p>
<pre><code class="language-text">outer 실행
→ inner 반환
→ outer 종료
→ fn 실행</code></pre>
<p>여기서 중요한 질문이 하나 생긴다.</p>
<p><code>outer()</code> 함수는 이미 <strong>실행이 끝났다.</strong>
그런데 <code>inner()</code> 함수는 <strong>message 변수에 계속 접근한다.</strong></p>
<p>즉 이런 의문이 생긴다.</p>
<blockquote>
<p>이미 종료된 함수의 변수에 어떻게 접근할 수 있을까?</p>
</blockquote>
<p>보통 함수가 끝나면
그 함수 내부의 변수도 <strong>사라지는 것이 정상</strong>이다.</p>
<p>하지만 위 코드에서는
<code>message</code>가 계속 유지된다.</p>
<p>이 현상을 설명하기 위해 등장하는 개념이 <strong>클로저(Closure)</strong>다.</p>
<hr>
<h2 id="2-렉시컬-스코프와-클로저">2. 렉시컬 스코프와 클로저</h2>
<p>클로저를 이해하려면 먼저 <strong>렉시컬 스코프</strong>를 떠올려야 한다.</p>
<p>JavaScript에서는
함수의 스코프가 <strong>호출 위치가 아니라 선언 위치</strong>에 의해 결정된다.</p>
<p>예제를 보자.</p>
<pre><code class="language-javascript">function outer() {
  const a = 10;

  function inner() {
    console.log(a);
  }

  inner();
}

outer();</code></pre>
<p>여기서 <code>inner()</code> 함수는
<code>outer()</code> 함수 안에서 선언되어 있다.</p>
<p>그래서 <code>inner()</code>는
자신이 선언된 스코프에 있는 변수 <code>a</code>에 접근할 수 있다.</p>
<p>이 규칙이 바로 이전 글에서 다뤘던 <strong>렉시컬 스코프</strong>다.</p>
<p>구조를 보면 이렇게 된다.</p>
<pre><code class="language-text">global scope
⎿ outer
    ⎿ inner</code></pre>
<p>하위 스코프는
항상 <strong>상위 스코프의 변수에 접근할 수 있다.</strong></p>
<p>여기까지는 사실 <strong>클로저가 아니다.</strong></p>
<p>단순히 <strong>스코프 규칙</strong>일 뿐이며 이전 글의 복습에 가깝다.</p>
<p>클로저는 <strong>조금 다른 상황에서 등장한다.</strong></p>
<p>외부 함수는 종료됐지만 내부 함수가 외부 변수를 계속 사용하는 상황이다.</p>
<p>즉 핵심 상황은 다음과 같다.</p>
<p><code>outer</code> 함수는 이미 끝났지만
<code>inner</code> 함수는 여전히 <code>outer</code>의 변수를 사용한다.</p>
<p>이때 JavaScript는
외부 스코프를 <strong>그대로 유지</strong>한다.</p>
<p>이 상태를 <strong>클로저</strong>라고 한다.</p>
<hr>
<h2 id="3-클로저의-개념">3. 클로저의 개념</h2>
<p>보통 클로저는 다음처럼 설명된다.</p>
<blockquote>
<p>클로저는 함수와 그 함수가 선언될 당시의 렉시컬 스코프가 결합된 구조다.</p>
</blockquote>
<p>즉 클로저는 단순히 <strong>함수 하나</strong>를 의미하는 것이 아니다.</p>
<p>구조적으로 보면 다음과 같다.</p>
<pre><code class="language-text">함수
+
함수가 선언될 당시의 스코프
=
클로저</code></pre>
<p>이 말은 JavaScript에서 함수는 실행될 때만 동작하는 것이 아니라
자신이 선언된 환경을 기억한다는 의미다.</p>
<p>그래서 내부 함수는
<strong>자신이 선언된 위치의 스코프에 계속 접근할 수 있다.</strong></p>
<hr>
<h2 id="4-클로저의-동작-구조">4. 클로저의 동작 구조</h2>
<p>이제 처음 던졌던 질문으로 다시 돌아가 보자.</p>
<pre><code class="language-javascript">function outer() {
  const message = &quot;hello&quot;;

  function inner() {
    console.log(message);
  }

  return inner;
}

const fn = outer();
fn();</code></pre>
<p>코드의 흐름은 다음과 같다.</p>
<pre><code class="language-text">outer 실행
→ inner 반환
→ outer 종료
→ fn 실행</code></pre>
<p>일반적으로 함수가 끝나면
그 함수 내부 변수는 <strong>메모리에서 제거된다.</strong></p>
<p>하지만 위 코드에서는
<code>message</code>가 계속 유지된다.</p>
<p>그 이유는 <code>inner</code> 함수가
<strong>message 변수에 계속 접근해야 하기 때문이다.</strong></p>
<p>그래서 JavaScript 엔진은
<code>outer</code>의 렉시컬 환경을 바로 제거하지 않는다.</p>
<p>대신 <strong>inner 함수와 함께 유지한다.</strong></p>
<p>구조적으로 보면 이렇게 된다.</p>
<pre><code class="language-text">inner 함수
+
outer의 스코프(message)</code></pre>
<p>이처럼 <strong>함수와 외부 스코프가 함께 유지되는 상태</strong>를
<strong>클로저(Closure)</strong>라고 한다.</p>
<hr>
<h2 id="5-클로저-활용-패턴">5. 클로저 활용 패턴</h2>
<p>클로저는 단순히 개념으로만 존재하는 것이 아니라
<strong>실제 코드에서 특정 패턴을 만들기 위해 자주 사용된다.</strong></p>
<p>대표적인 활용 방식은 크게 두 가지다.</p>
<h3 id="상태-유지-state">상태 유지 (State)</h3>
<p>클로저를 사용하면 <strong>함수 내부 상태를 유지</strong>할 수 있다.</p>
<pre><code class="language-javascript">function counter() {
  let count = 0;

  return function () {
    count++;
    console.log(count);
  };
}

const increase = counter();

increase(); // 1
increase(); // 2
increase(); // 3</code></pre>
<p>이 코드에서 중요한 점은 다음과 같다.</p>
<ul>
<li><code>count</code> 변수는 <strong>counter 함수 내부에 존재한다</strong></li>
<li>외부에서는 <code>count</code>에 <strong>직접 접근할 수 없다</strong></li>
<li>하지만 반환된 내부 함수는 <code>count</code>에 계속 접근할 수 있다</li>
</ul>
<p>즉 클로저를 통해 <strong>함수 내부 상태가 계속 유지된다.</strong></p>
<h3 id="데이터-은닉-encapsulation">데이터 은닉 (Encapsulation)</h3>
<p>클로저는 <strong>외부에서 직접 접근할 수 없는 데이터를 만드는 데</strong>도 사용된다.</p>
<pre><code class="language-javascript">function createUser(name) {
  let _name = name;

  return {
    getName() {
      return _name;
    },
  };
}

const user = createUser(&quot;Alice&quot;);
console.log(user.getName()); // Alice</code></pre>
<p>여기서 <code>_name</code> 변수는</p>
<ul>
<li>외부에서 직접 접근할 수 없고</li>
<li>내부 함수만 접근할 수 있다</li>
</ul>
<p>이처럼 클로저는 <strong>데이터를 보호하는 구조</strong>를 만들 때도 활용된다.</p>
<hr>
<h2 id="6-핵심-정리">6. 핵심 정리</h2>
<ul>
<li>클로저는 <strong>함수와 선언 당시의 스코프가 결합된 구조</strong>다</li>
<li>함수는 <strong>자신이 선언된 환경을 기억한다</strong></li>
<li>내부 함수가 외부 변수를 사용하면 <strong>해당 스코프는 유지된다</strong></li>
<li>클로저는 <strong>상태 유지와 데이터 은닉을 구현할 때 활용된다</strong></li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript 렉시컬 스코프와 스코프 체인 이해하기]]></title>
            <link>https://velog.io/@hogu__giriboy/JavaScript-%EB%A0%89%EC%8B%9C%EC%BB%AC-%EC%8A%A4%EC%BD%94%ED%94%84%EC%99%80-%EC%8A%A4%EC%BD%94%ED%94%84-%EC%B2%B4%EC%9D%B8-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@hogu__giriboy/JavaScript-%EB%A0%89%EC%8B%9C%EC%BB%AC-%EC%8A%A4%EC%BD%94%ED%94%84%EC%99%80-%EC%8A%A4%EC%BD%94%ED%94%84-%EC%B2%B4%EC%9D%B8-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 07 Mar 2026 09:56:15 GMT</pubDate>
            <description><![CDATA[<h2 id="1-스코프scope">1. 스코프(Scope)</h2>
<p>스코프(Scope)는 <strong>변수에 접근할 수 있는 범위</strong>를 의미한다.</p>
<p>JavaScript에서 변수는 프로그램 어디에서든 사용할 수 있는 것이 아니라
<strong>변수가 선언된 위치에 따라 접근 가능한 영역이 결정된다.</strong></p>
<p>즉 코드에는 다음과 같은 <strong>접근 범위 구조</strong>가 존재한다.</p>
<pre><code class="language-text">전역 스코프
⎿ 함수 스코프
    ⎿ 내부 함수 스코프</code></pre>
<p>상위 스코프에 선언된 변수는 하위 스코프에서 접근할 수 있지만
하위 스코프에서 선언된 변수는 상위 스코프에서 접근할 수 없다.</p>
<pre><code class="language-javascript">let a = 10;

function test() {
  let b = 20;
  console.log(a);
}

test();</code></pre>
<p>이 코드에서 변수의 스코프는 다음과 같다.</p>
<ul>
<li>a → 전역 스코프</li>
<li>b → test 함수 내부 스코프</li>
</ul>
<p>따라서 <code>test</code> 함수 내부에서는 전역 변수 <code>a</code>에 접근할 수 있지만
전역에서는 <code>b</code>에 접근할 수 없다.</p>
<pre><code class="language-javascript">console.log(b); // ReferenceError</code></pre>
<p>이처럼 변수의 접근 가능 범위를 결정하는 개념을 <strong>스코프(Scope)</strong>라고 한다.</p>
<h2 id="2-렉시컬-스코프-lexical-scope">2. 렉시컬 스코프 (Lexical Scope)</h2>
<p>JavaScript에서는 스코프가 <strong>코드가 작성된 위치</strong>에 의해 결정된다.
이러한 스코프 결정 방식을 <strong>렉시컬 스코프(Lexical Scope)</strong> 라고 한다.</p>
<p>여기서 <strong>Lexical</strong>은 &quot;코드의 문자적 위치&quot;를 의미한다.</p>
<p>즉 JavaScript는 함수가 <strong>어디서 호출되었는지</strong>가 아니라
함수가 <strong>어디에서 선언되었는지</strong>에 따라 스코프가 결정된다.</p>
<pre><code class="language-javascript">let x = 10;

function outer() {
  let x = 20;

  function inner() {
    console.log(x);
  }

  inner();
}

outer();</code></pre>
<p>이 코드의 실행 결과는 <code>20</code>이다.</p>
<p>그 이유는 <code>inner</code> 함수가 <strong>outer 함수 내부에서 선언되었기 때문</strong>이다.</p>
<p>코드의 구조를 보면 다음과 같다.</p>
<pre><code class="language-text">global
⎿ outer
    ⎿ inner</code></pre>
<p>JavaScript에서는 함수가 선언될 때
<strong>자신이 선언된 위치의 스코프를 기준으로 변수에 접근한다.</strong></p>
<p>따라서 <code>inner</code> 함수는 <code>outer</code>의 스코프에 접근할 수 있고
<code>outer</code>에 선언된 <code>x = 20</code>을 참조하게 된다.</p>
<p>이처럼 <strong>함수가 선언된 위치를 기준으로 스코프가 결정되는 규칙</strong>을
렉시컬 스코프라고 한다.</p>
<hr>
<h2 id="3-스코프-체인-scope-chain">3. 스코프 체인 (Scope Chain)</h2>
<p>JavaScript에서 변수를 사용할 때
엔진은 해당 변수를 <strong>현재 스코프에서 먼저 찾는다.</strong></p>
<p>만약 현재 스코프에 해당 변수가 없다면
<strong>상위 스코프로 올라가면서 변수를 탐색</strong>한다.</p>
<p>이 과정을 <strong>스코프 체인(Scope Chain)</strong> 이라고 한다.</p>
<p>즉 변수 탐색은 다음과 같은 순서로 이루어진다.</p>
<pre><code class="language-text">현재 스코프
→ 부모 스코프
→ 전역 스코프</code></pre>
<p>앞서 살펴본 예제를 다시 보자.</p>
<pre><code class="language-javascript">let x = 10;

function outer() {
  let x = 20;

  function inner() {
    console.log(x);
  }

  inner();
}

outer();</code></pre>
<p>이 코드의 스코프 구조는 다음과 같다.</p>
<pre><code class="language-text">global
⎿ outer
    ⎿ inner</code></pre>
<p><code>inner</code> 함수에서 <code>x</code>를 찾는 과정은 다음과 같다.</p>
<pre><code class="language-text">1. inner 스코프 확인
2. outer 스코프 확인
3. global 스코프 확인</code></pre>
<p><code>inner</code> 스코프에는 <code>x</code>가 존재하지 않기 때문에
상위 스코프인 <code>outer</code>로 올라가게 된다.</p>
<p>그 결과 <code>outer</code>에 선언된 <code>x = 20</code>을 찾게 되고
<code>console.log(x)</code>의 결과는 <code>20</code>이 된다.</p>
<p>JavaScript는 현재 스코프에서 시작해 상위 스코프로 이동하며 변수를 탐색한다.
이러한 탐색 구조를 <strong>스코프 체인(Scope Chain)</strong>이라고 한다.</p>
<hr>
<h2 id="4-렉시컬-스코프의-특징">4. 렉시컬 스코프의 특징</h2>
<h3 id="스코프는-코드-구조로-결정된다">스코프는 코드 구조로 결정된다.</h3>
<p>JavaScript에서 스코프는 <strong>코드가 작성된 위치</strong>,
즉 <strong>함수가 선언된 위치</strong>에 의해 결정된다.</p>
<p>따라서 함수가 어디에서 실행되었는지가 아니라
<strong>어디에서 선언되었는지가 변수 접근 범위를 결정</strong>한다.</p>
<h3 id="함수-호출-위치는-스코프에-영향을-주지-않는다">함수 호출 위치는 스코프에 영향을 주지 않는다.</h3>
<p>렉시컬 스코프에서는 <strong>함수가 호출된 위치는 중요하지 않다.</strong></p>
<p>함수는 실행되는 위치와 관계없이
<strong>자신이 선언된 위치의 스코프를 기준으로 변수에 접근</strong>한다.</p>
<p>예를 들어 함수가 다른 스코프에서 실행되더라도
자신이 <strong>정의된 환경의 스코프</strong>를 기준으로 동작한다.</p>
<h3 id="함수는-자신이-선언된-환경을-따른다">함수는 자신이 선언된 환경을 따른다.</h3>
<p>JavaScript의 함수는 선언될 때
<strong>자신이 선언된 스코프를 함께 기억한다.</strong></p>
<p>그래서 함수가 다른 곳에서 실행되더라도
원래 선언되었던 스코프의 변수에 접근할 수 있다.</p>
<p>이러한 특성은 이후 <strong>클로저(Closure)</strong>라는 개념으로 이어진다.</p>
<hr>
<h2 id="5-렉시컬-스코프와-this의-차이">5. 렉시컬 스코프와 this의 차이</h2>
<p>JavaScript에서 <strong>렉시컬 스코프와 <code>this</code>는 기준이 서로 다르다.</strong></p>
<p>렉시컬 스코프는 <strong>함수가 선언된 위치</strong>를 기준으로 결정되지만
<code>this</code>는 <strong>함수가 호출되는 방식</strong>에 따라 결정된다.</p>
<p>즉 두 개념은 다음과 같은 차이를 가진다.</p>
<ul>
<li>렉시컬 스코프 → 선언 위치 기준</li>
<li>this → 호출 방식 기준</li>
</ul>
<p>예를 들어 다음 코드를 보자.</p>
<pre><code class="language-javascript">const person = {
  name: &quot;홍길동&quot;,
  greet() {
    console.log(this.name);
  },
};

person.greet();</code></pre>
<p>이 코드에서 <code>this</code>는 <code>person</code>을 가리킨다.</p>
<p>그 이유는 <code>greet</code> 함수가 <strong>person 객체를 통해 호출되었기 때문</strong>이다.</p>
<p>즉 <code>this</code>는 <strong>함수가 어디에서 선언되었는지</strong>가 아니라
<strong>어떤 방식으로 호출되었는지</strong>에 따라 결정된다.</p>
<p>반면 렉시컬 스코프는
<strong>함수가 선언된 위치를 기준으로 스코프가 결정</strong>된다.</p>
<p>이처럼 JavaScript에서는
선언 위치를 기준으로 하는 렉시컬 스코프와
호출 방식을 기준으로 하는 this 규칙이 동시에 존재한다.</p>
<p>이 두 개념은 모두 함수와 관련된 개념이지만
<strong>동작 기준이 전혀 다르기 때문에 자주 혼동되는 개념</strong>이다.</p>
<hr>
<h2 id="6-핵심-정리">6. 핵심 정리</h2>
<ul>
<li>스코프는 <strong>변수에 접근할 수 있는 범위</strong>를 의미한다.</li>
<li>JavaScript는 <strong>렉시컬 스코프 규칙</strong>을 사용한다.</li>
<li>스코프는 <strong>함수가 선언된 위치(코드 구조)</strong>에 의해 결정된다.</li>
<li>변수는 <strong>현재 스코프에서 시작해 상위 스코프로 이동하며 탐색되는 스코프 체인 구조</strong>를 따른다.</li>
<li>렉시컬 스코프는 <strong>선언 위치 기준</strong>, <code>this</code>는 <strong>호출 방식 기준</strong>이라는 차이를 가진다.</li>
</ul>
<p>이로써 JavaScript에서 <strong>변수의 접근 범위가 어떻게 결정되고 탐색되는지</strong>를 이해할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트 - DOM으로 웹 페이지를 조작하는 방법]]></title>
            <link>https://velog.io/@hogu__giriboy/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-DOM%EC%9C%BC%EB%A1%9C-%EC%9B%B9-%ED%8E%98%EC%9D%B4%EC%A7%80%EB%A5%BC-%EC%A1%B0%EC%9E%91%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@hogu__giriboy/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-DOM%EC%9C%BC%EB%A1%9C-%EC%9B%B9-%ED%8E%98%EC%9D%B4%EC%A7%80%EB%A5%BC-%EC%A1%B0%EC%9E%91%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Fri, 06 Mar 2026 04:43:48 GMT</pubDate>
            <description><![CDATA[<h2 id="1-javascript와-dom">1. JavaScript와 DOM</h2>
<p><strong>JavaScript가 웹 페이지에서 실제로 무엇을 하는지 이해하는 것</strong>이
이번 파트의 목적이다.</p>
<p>많은 사람들이 처음 웹을 배울 때 이렇게 생각한다.</p>
<ul>
<li>HTML → 화면을 만든다</li>
<li>CSS → 디자인을 한다</li>
<li>JavaScript → 기능을 만든다</li>
</ul>
<p>이 설명 자체는 틀리지 않지만,
조금 더 정확하게 말하면
<strong>JavaScript는 DOM을 통해 HTML을 조작</strong>한다.</p>
<p>즉 구조는 이렇게 된다.</p>
<pre><code class="language-text">HTML → 브라우저가 읽는다
→ DOM 생성
→ JavaScript가 DOM을 조작한다</code></pre>
<p>여기서 등장하는 개념이 바로 <strong>DOM</strong>이다.</p>
<h3 id="dom이란">DOM이란</h3>
<p>DOM은 <strong>Document Object Model</strong>의 약자다.</p>
<p>쉽게 말하면 <strong>HTML 문서를 JavaScript가 다룰 수 있는 객체 구조로 변환한 것</strong>이다.</p>
<p>예를 들어 이런 HTML이 있다고 가정해보자.</p>
<pre><code class="language-html">&lt;body&gt;
  &lt;h1&gt;제목&lt;/h1&gt;
  &lt;p&gt;문단&lt;/p&gt;
&lt;/body&gt;</code></pre>
<p>브라우저는 이 코드를 그대로 사용하는 것이 아니라
내부적으로 <strong>트리 구조의 객체</strong>로 변환한다.</p>
<p>구조는 이렇게 된다.</p>
<pre><code class="language-text">document
⎿ body
    ⎿ h1
    ⎿ p</code></pre>
<p>이 구조를 <strong>DOM Tree</strong>라고 부른다.</p>
<p>그리고 JavaScript는 이 트리 구조를 통해
HTML 요소에 접근하고 수정할 수 있다.</p>
<h3 id="javascript는-무엇을-하는가">JavaScript는 무엇을 하는가</h3>
<p>JavaScript는 이 DOM을 이용해 웹 페이지를 <strong>동적으로 변경</strong>한다.</p>
<p>대표적인 예는 다음과 같다.</p>
<h4 id="내용-변경">내용 변경</h4>
<pre><code class="language-javascript">document.querySelector(&quot;h1&quot;).textContent = &quot;새 제목&quot;;</code></pre>
<p>페이지에 있는 <code>&lt;h1&gt;</code> 요소의 내용을 변경한다.</p>
<h4 id="스타일-변경">스타일 변경</h4>
<pre><code class="language-javascript">document.querySelector(&quot;button&quot;).style.color = &quot;red&quot;;</code></pre>
<p>버튼의 색상을 바꾼다.</p>
<h4 id="요소-추가">요소 추가</h4>
<pre><code class="language-javascript">const newDiv = document.createElement(&quot;div&quot;);
document.body.appendChild(newDiv);</code></pre>
<p>새로운 요소를 만들어 페이지에 추가한다.</p>
<h4 id="이벤트-처리">이벤트 처리</h4>
<pre><code class="language-javascript">document.querySelector(&quot;button&quot;).addEventListener(&quot;click&quot;, () =&gt; {
  alert(&quot;클릭됨!&quot;);
});</code></pre>
<p>사용자의 <strong>클릭 행동에 반응</strong>하도록 만들 수 있다.</p>
<h3 id="브라우저가-하는-일">브라우저가 하는 일</h3>
<p>웹 페이지가 열리면 브라우저 내부에서는 다음 과정이 일어난다.</p>
<ol>
<li>HTML 다운로드</li>
<li>HTML 파싱</li>
<li>DOM 생성</li>
<li>JavaScript 실행</li>
<li>화면 렌더링</li>
</ol>
<p>즉 흐름을 정리하면 다음과 같다.</p>
<pre><code class="language-text">HTML → DOM 생성 → JavaScript가 DOM을 조작 → 화면 변경</code></pre>
<hr>
<h2 id="2-dom-선택">2. DOM 선택</h2>
<p>JavaScript는 DOM을 조작할 수 있지만
<strong>바로 수정할 수는 없다.</strong></p>
<p>반드시 <strong>DOM을 먼저 선택한 뒤 조작해야 한다.</strong></p>
<p>즉 JavaScript는 먼저 <strong>어떤 요소를 바꿀지 찾은 뒤</strong>
그 요소를 수정한다.</p>
<p>예를 들어 페이지에 이런 HTML이 있다고 가정해보자.</p>
<pre><code class="language-html">&lt;h1 id=&quot;title&quot;&gt;안녕하세요&lt;/h1&gt;
&lt;button class=&quot;btn&quot;&gt;버튼&lt;/button&gt;</code></pre>
<p>JavaScript가 <code>&lt;h1&gt;</code>을 수정하려면
먼저 해당 요소를 찾아야 한다.</p>
<pre><code class="language-javascript">const title = document.querySelector(&quot;#title&quot;);

title.textContent = &quot;Hello&quot;;</code></pre>
<p>여기서 <code>querySelector</code>가 바로 <strong>DOM 선택 메서드(Selection Method)</strong>다.</p>
<h3 id="dom-선택의-기본-구조">DOM 선택의 기본 구조</h3>
<p>DOM을 선택할 때는 항상 <code>document</code> 객체를 사용한다.</p>
<pre><code class="language-javascript">document.선택메서드();</code></pre>
<p><code>document</code>는 <strong>현재 HTML 문서 전체</strong>를 의미한다.</p>
<p>즉 DOM 선택은 기본적으로
&quot;document에서 원하는 요소 찾기&quot;라는
의미를 가진다.</p>
<h4 id="getelementbyid">getElementById</h4>
<p>가장 기본적인 DOM 선택 방법이다.</p>
<pre><code class="language-javascript">document.getElementById(&quot;title&quot;);</code></pre>
<p>특징은 다음과 같다.</p>
<ul>
<li>id 기준으로 요소를 선택한다</li>
<li><strong>하나의 요소만 반환</strong>한다</li>
<li><code>#</code>를 붙이지 않는다</li>
</ul>
<p>예</p>
<pre><code class="language-javascript">const title = document.getElementById(&quot;title&quot;);</code></pre>
<h4 id="getelementsbyclassname">getElementsByClassName</h4>
<p>class 기준으로 요소를 선택한다.</p>
<pre><code class="language-javascript">document.getElementsByClassName(&quot;btn&quot;);</code></pre>
<p>특징</p>
<ul>
<li>같은 class를 가진 <strong>여러 요소를 반환</strong></li>
<li>반환 타입은 <strong>HTMLCollection</strong></li>
</ul>
<p>예</p>
<pre><code class="language-javascript">const buttons = document.getElementsByClassName(&quot;btn&quot;);</code></pre>
<h4 id="getelementsbytagname">getElementsByTagName</h4>
<p>태그 이름 기준으로 선택한다.</p>
<pre><code class="language-javascript">document.getElementsByTagName(&quot;p&quot;);</code></pre>
<p>예</p>
<pre><code class="language-javascript">const paragraphs = document.getElementsByTagName(&quot;p&quot;);</code></pre>
<p>이 메서드 역시 <strong>여러 요소를 반환</strong>한다.</p>
<h4 id="queryselector">querySelector</h4>
<p>현대 JavaScript에서 가장 많이 사용하는 방식이다.</p>
<p>CSS 선택자를 그대로 사용할 수 있다.</p>
<pre><code class="language-javascript">document.querySelector(&quot;h1&quot;);
document.querySelector(&quot;.btn&quot;);
document.querySelector(&quot;#title&quot;);</code></pre>
<p>특징</p>
<ul>
<li><strong>첫 번째 요소만 반환</strong></li>
<li>CSS 선택자 사용 가능</li>
</ul>
<p>예</p>
<pre><code class="language-javascript">const button = document.querySelector(&quot;.btn&quot;);</code></pre>
<h4 id="queryselectorall">querySelectorAll</h4>
<p>여러 요소를 선택할 때 사용한다.</p>
<pre><code class="language-javascript">document.querySelectorAll(&quot;.btn&quot;);</code></pre>
<p>예</p>
<pre><code class="language-javascript">const buttons = document.querySelectorAll(&quot;.btn&quot;);</code></pre>
<p>특징</p>
<ul>
<li>여러 요소 반환</li>
<li>반환 타입은 <strong>NodeList</strong></li>
</ul>
<h3 id="htmlcollection과-nodelist">HTMLCollection과 NodeList</h3>
<p>DOM 선택 메서드는 배열처럼 보이는 객체를 반환한다.</p>
<p>대표적으로 두 가지가 있다.</p>
<table>
<thead>
<tr>
<th>타입</th>
<th>생성 메서드</th>
</tr>
</thead>
<tbody><tr>
<td>HTMLCollection</td>
<td>getElements 계열</td>
</tr>
<tr>
<td>NodeList</td>
<td>querySelectorAll</td>
</tr>
</tbody></table>
<p>두 객체 모두 <strong>배열처럼 보이지만 배열은 아니다.</strong></p>
<p>하지만 차이가 하나 있다.</p>
<ul>
<li>HTMLCollection → live collection</li>
<li>NodeList → static collection</li>
</ul>
<hr>
<h2 id="3-dom-트리와-탐색">3. DOM 트리와 탐색</h2>
<p>DOM을 선택했다면
이제 다음 단계는 <strong>DOM 구조를 이해하는 것</strong>이다.</p>
<h3 id="dom-트리란">DOM 트리란</h3>
<p>HTML 문서는 단순히 위에서 아래로 나열된 구조가 아니라
<strong>트리(Tree)</strong> 구조로 이루어져 있다.</p>
<p>예를 들어 다음 HTML이 있다고 가정해보자.</p>
<pre><code class="language-html">&lt;body&gt;
  &lt;div class=&quot;container&quot;&gt;
    &lt;h1&gt;제목&lt;/h1&gt;
    &lt;p&gt;문단&lt;/p&gt;
  &lt;/div&gt;
&lt;/body&gt;</code></pre>
<p>브라우저는 이 코드를 그대로 사용하는 것이 아니라
다음과 같은 <strong>DOM 트리 구조</strong>로 변환한다.</p>
<pre><code class="language-text">document
⎿ body
    ⎿ div
        ⎿ h1
        ⎿ p</code></pre>
<p>이처럼 HTML 요소들은 서로 <strong>계층 구조</strong>를 가진다.</p>
<h3 id="부모자식형제-관계">부모/자식/형제 관계</h3>
<p>DOM 트리에서는 각 요소가 서로 관계를 가진다.</p>
<p>대표적인 관계는 다음과 같다.</p>
<ul>
<li>부모 요소 (parent)</li>
<li>자식 요소 (children)</li>
<li>형제 요소 (siblings)</li>
</ul>
<p>예를 들어 위 구조에서는 다음과 같은 관계가 된다.</p>
<pre><code class="language-text">div → h1의 부모
div → p의 부모

h1 → div의 자식
p → div의 자식

h1과 p → 서로 형제</code></pre>
<p>이 관계를 이용하면 DOM 안에서
<strong>요소 간 이동이 가능하다.</strong></p>
<h3 id="탐색-속성들">탐색 속성들</h3>
<p>DOM에서는 부모・자식・형제 관계를 이용해
<strong>요소를 탐색하는 속성</strong>들이 존재한다.</p>
<h4 id="parentelement">parentElement</h4>
<p>현재 요소의 <strong>부모 요소</strong>를 찾는다.</p>
<pre><code class="language-javascript">const title = document.querySelector(&quot;h1&quot;);

console.log(title.parentElement);</code></pre>
<p>결과</p>
<pre><code class="language-text">&lt;div class=&quot;container&quot;&gt;</code></pre>
<h4 id="children">children</h4>
<p>현재 요소의 <strong>자식 요소(element)</strong> 들을 가져온다.
텍스트 노드나 공백 노드는 포함되지 않는다.</p>
<pre><code class="language-javascript">const container = document.querySelector(&quot;.container&quot;);

console.log(container.children);</code></pre>
<p>결과</p>
<pre><code class="language-text">[h1, p]</code></pre>
<h4 id="firstelementchild">firstElementChild</h4>
<p>첫 번째 자식 요소를 가져온다.</p>
<pre><code class="language-javascript">container.firstElementChild;</code></pre>
<p>결과</p>
<pre><code class="language-text">&lt;h1&gt;</code></pre>
<h4 id="lastelementchild">lastElementChild</h4>
<p>마지막 자식 요소를 가져온다.</p>
<pre><code class="language-javascript">container.lastElementChild;</code></pre>
<p>결과</p>
<pre><code class="language-text">&lt;p&gt;</code></pre>
<h4 id="nextelementsibling">nextElementSibling</h4>
<p>다음 형제 요소를 가져온다.</p>
<pre><code class="language-javascript">const title = document.querySelector(&quot;h1&quot;);

title.nextElementSibling;</code></pre>
<p>결과는</p>
<pre><code class="language-text">&lt;p&gt;</code></pre>
<h4 id="previouselementsibling">previousElementSibling</h4>
<p>이전 형제 요소를 가져온다.</p>
<pre><code class="language-javascript">const paragraph = document.querySelector(&quot;p&quot;);

paragraph.previousElementSibling;</code></pre>
<p>결과</p>
<pre><code class="language-text">&lt;h1&gt;</code></pre>
<hr>
<h2 id="4-dom-조작">4. DOM 조작</h2>
<h3 id="내용-변경-1">내용 변경</h3>
<p>DOM을 선택하고 탐색했다면
이제 다음 단계는 <strong>DOM의 내용을 실제로 변경하는 것</strong>이다.</p>
<p>JavaScript는 DOM 객체를 조작하여
웹 페이지의 내용을 <strong>동적으로 변경</strong>할 수 있다.</p>
<p>예를 들어 다음과 같은 HTML이 있다고 가정해보자.</p>
<pre><code class="language-html">&lt;h1 id=&quot;title&quot;&gt;제목&lt;/h1&gt;</code></pre>
<p>JavaScript는 DOM을 통해 이 내용을 <strong>변경</strong>할 수 있다.</p>
<h4 id="textcontent">textContent</h4>
<p><code>textContent</code>는 <strong>요소 안의 텍스트 내용을 변경</strong>한다.</p>
<pre><code class="language-javascript">const title = document.querySelector(&quot;#title&quot;);

title.textContent = &quot;새로운 제목&quot;;</code></pre>
<p>결과</p>
<pre><code class="language-html">&lt;h1 id=&quot;title&quot;&gt;새로운 제목&lt;/h1&gt;</code></pre>
<p><code>textContent</code>는 <strong>텍스트만 변경</strong>하며
문자열 안의 HTML 태그는 해석하지 않는다.</p>
<p>예를 들어 다음 코드를 실행하면</p>
<pre><code class="language-javascript">title.textContent = &quot;&lt;span&gt;제목&lt;/span&gt;&quot;;</code></pre>
<p>화면에는 실제 태그가 아니라
문자 그대로 다음과 같이 출력된다.</p>
<pre><code class="language-text">&lt;span&gt;제목&lt;/span&gt;</code></pre>
<h4 id="innerhtml">innerHTML</h4>
<p><code>innerHTML</code>은 <strong>요소 내부의 HTML 구조 자체를 변경</strong>한다.</p>
<pre><code class="language-javascript">title.innerHTML = &quot;&lt;span&gt;새 제목&lt;/span&gt;&quot;;</code></pre>
<p>결과</p>
<pre><code class="language-html">&lt;h1 id=&quot;title&quot;&gt;
  &lt;span&gt;새 제목&lt;/span&gt;
&lt;/h1&gt;</code></pre>
<p><code>innerHTML</code>은 문자열을 <strong>HTML로 해석하여 DOM 구조를 변경</strong>한다.</p>
<h4 id="innertext">innerText</h4>
<p><code>innerText</code>는 <strong>브라우저 화면에 실제로 보이는 텍스트</strong>를 기준으로 동작한다.</p>
<p>예를 들어 CSS로 숨겨진 요소가 있다면
<code>innerText</code>는 그 내용을 포함하지 않는다.</p>
<pre><code class="language-javascript">element.innerText;</code></pre>
<h4 id="textcontent-vs-innerhtml-vs-innertext">textContent vs innerHTML vs innerText</h4>
<ul>
<li>textContent: 텍스트만 변경</li>
<li>innerHTML: HTML 구조까지 변경</li>
<li>innerText: 화면에 보이는 텍스트 기준</li>
</ul>
<p>일반적으로 <strong>단순한 텍스트 변경에는 <code>textContent</code>를 사용하는 것이 권장된다.</strong></p>
<hr>
<h2 id="5-요소-생성과-추가삭제">5. 요소 생성과 추가/삭제</h2>
<h3 id="요소-생성">요소 생성</h3>
<p>DOM을 조작할 때는
기존 요소의 내용을 바꾸는 것뿐만 아니라
<strong>새로운 요소를 만들 수도 있다.</strong></p>
<p>이때 사용하는 메서드가 <code>createElement</code>이다.</p>
<pre><code class="language-javascript">const div = document.createElement(&quot;div&quot;);</code></pre>
<p>이 코드는 <strong>새로운 <code>&lt;div&gt;</code> 요소를 생성</strong>한다.</p>
<p>하지만 이 상태에서는 아직 <strong>화면에 나타나지 않는다.</strong></p>
<p>이유는 간단하다.</p>
<ul>
<li>요소 생성 ≠ DOM에 추가</li>
</ul>
<p>즉 요소는 만들어졌지만
<strong>DOM 트리에 아직 연결되지 않은 상태</strong>다.</p>
<h3 id="요소-추가-1">요소 추가</h3>
<p>생성한 요소를 화면에 보이게 하려면
DOM 트리에 <strong>추가</strong>해야 한다.</p>
<p>대표적으로 사용하는 메서드는 <code>appendChild</code>다.</p>
<pre><code class="language-javascript">document.body.appendChild(div);</code></pre>
<p>그러면 DOM 구조는 다음처럼 변경된다.</p>
<pre><code class="language-text">document
⎿ body
    ⎿ div</code></pre>
<p>즉 <strong>DOM 트리에 연결되면서 화면에도 나타난다.</strong></p>
<h3 id="요소-삭제">요소 삭제</h3>
<p>DOM 요소는 제거할 수도 있다.</p>
<p>이때 사용하는 메서드는 <code>remove</code>다.</p>
<pre><code class="language-javascript">const title = document.querySelector(&quot;#title&quot;);

title.remove();</code></pre>
<p>이 코드를 실행하면 <code>&lt;h1&gt;</code> 요소가 <strong>DOM에서 삭제된다.</strong></p>
<p>즉 구조는 다음처럼 바뀐다.</p>
<pre><code class="language-text">삭제 전
&lt;h1 id=&quot;title&quot;&gt;제목&lt;/h1&gt;

삭제 후
(요소 없음)</code></pre>
<hr>
<h2 id="6-html-속성과-스타일-조작">6. HTML 속성과 스타일 조작</h2>
<h3 id="html-속성-조작">HTML 속성 조작</h3>
<p>DOM을 통해 요소의 <strong>내용이나 구조</strong>뿐만 아니라
<strong>HTML 속성(attribute)도 조작</strong>할 수 있다.</p>
<p>예를 들어 다음과 같은 HTML이 있다고 가정해보자.</p>
<pre><code class="language-html">&lt;a id=&quot;link&quot; href=&quot;https://google.com&quot;&gt;이동&lt;/a&gt;</code></pre>
<p>JavaScript를 사용하면 이 속성을 수정할 수 있다.</p>
<p>대표적으로 사용하는 메서드는 다음과 같다.</p>
<ul>
<li><code>getAttribute</code></li>
<li><code>setAttribute</code></li>
</ul>
<h4 id="getattribute">getAttribute</h4>
<p><code>getAttribute</code>는 <strong>요소의 속성 값을 가져온다.</strong></p>
<pre><code class="language-javascript">const link = document.querySelector(&quot;#link&quot;);

console.log(link.getAttribute(&quot;href&quot;));</code></pre>
<p>결과</p>
<pre><code class="language-text">https://google.com</code></pre>
<h4 id="setattribute">setAttribute</h4>
<p><code>setAttribute</code>는 <strong>속성 값을 변경하거나 추가</strong>한다.</p>
<pre><code class="language-javascript">link.setAttribute(&quot;href&quot;, &quot;https://naver.com&quot;);</code></pre>
<p>결과</p>
<pre><code class="language-html">&lt;a id=&quot;link&quot; href=&quot;https://naver.com&quot;&gt;이동&lt;/a&gt;</code></pre>
<h3 id="class-조작">class 조작</h3>
<p>HTML 요소의 <code>class</code>는
JavaScript에서 <code>classList</code> 객체를 통해 다룰 수 있다.</p>
<h4 id="class-추가">class 추가</h4>
<pre><code class="language-javascript">element.classList.add(&quot;active&quot;);</code></pre>
<h4 id="class-제거">class 제거</h4>
<pre><code class="language-javascript">element.classList.remove(&quot;active&quot;);</code></pre>
<h4 id="class-토글">class 토글</h4>
<pre><code class="language-javascript">element.classList.toggle(&quot;active&quot;);</code></pre>
<p><code>toggle</code>은 class가 있으면 제거하고
없으면 추가하는 방식으로 동작한다.</p>
<h3 id="style-조작">style 조작</h3>
<p>JavaScript는 DOM을 통해 <strong>CSS 스타일도 변경</strong>할 수 있다.</p>
<pre><code class="language-javascript">element.style.color = &quot;red&quot;;</code></pre>
<p>예</p>
<pre><code class="language-javascript">const title = document.querySelector(&quot;h1&quot;);

title.style.color = &quot;red&quot;;</code></pre>
<p>그러면 <code>&lt;h1&gt;</code>의 글자 색이 빨간색으로 바뀐다.</p>
<p>CSS 속성은 JavaScript에서
<strong>camelCase 형태</strong>로 작성한다.</p>
<p>예</p>
<ul>
<li>background-color → backgroundColor</li>
<li>font-size → fontSize</li>
</ul>
<h3 id="data-속성">data 속성</h3>
<p>HTML에서는 <strong>사용자 정의 속성</strong>을 만들 수 있다.</p>
<p>이때 사용하는 것이 <code>data-*</code> 속성이다.</p>
<p>예</p>
<pre><code class="language-html">&lt;div data-id=&quot;10&quot;&gt;&lt;/div&gt;</code></pre>
<p>JavaScript에서는 <code>dataset</code>을 통해 접근할 수 있다.</p>
<pre><code class="language-javascript">element.dataset.id;</code></pre>
<hr>
<h2 id="7-이벤트와-이벤트-객체">7. 이벤트와 이벤트 객체</h2>
<h3 id="이벤트">이벤트</h3>
<p>웹 페이지에서는 다양한 <strong>사용자 행동</strong>이 발생한다.</p>
<p>예를 들어 다음과 같은 행동들이 있다.</p>
<ul>
<li>버튼 클릭</li>
<li>키보드 입력</li>
<li>마우스 이동</li>
<li>폼 제출</li>
</ul>
<p>이처럼 <strong>사용자의 행동이나 브라우저에서 발생하는 동작</strong>을
<strong>이벤트(Event)</strong>라고 한다.</p>
<p>JavaScript는 이러한 이벤트를 감지하고
<strong>특정 동작을 실행하도록 만들 수 있다.</strong></p>
<h3 id="이벤트-핸들러">이벤트 핸들러</h3>
<p>이벤트가 발생했을 때 실행되는 함수를
<strong>이벤트 핸들러(Event Handler)</strong>라고 한다.</p>
<p>예를 들어 버튼을 클릭했을 때
특정 코드가 실행되도록 만들 수 있다.</p>
<pre><code class="language-html">&lt;button id=&quot;btn&quot;&gt;클릭&lt;/button&gt;</code></pre>
<pre><code class="language-javascript">const button = document.querySelector(&quot;#btn&quot;);

button.addEventListener(&quot;click&quot;, function () {
  console.log(&quot;버튼 클릭됨&quot;);
});</code></pre>
<p>이 코드의 구조는 다음과 같다.</p>
<pre><code class="language-text">요소.addEventListener(이벤트, 실행할 함수)</code></pre>
<p>즉 위 코드는 <strong>&quot;버튼이 클릭되면 함수가 실행된다&quot;</strong> 는 의미가 된다.</p>
<h3 id="addeventlistener">addEventListener</h3>
<p><code>addEventListener</code>는 <strong>특정 이벤트가 발생했을 때 실행할 함수를 등록</strong>한다.</p>
<pre><code class="language-javascript">element.addEventListener(&quot;click&quot;, handler);</code></pre>
<p>예</p>
<pre><code class="language-javascript">button.addEventListener(&quot;click&quot;, () =&gt; {
  console.log(&quot;클릭됨&quot;);
});</code></pre>
<p>이 메서드는 여러 개의 이벤트를 <strong>동시에 등록</strong>할 수 있다는 특징이 있다.</p>
<h3 id="이벤트-객체">이벤트 객체</h3>
<p>이벤트가 발생하면
브라우저는 이벤트에 대한 정보를 담은 <strong>객체</strong>를 전달한다.</p>
<p>이를 <strong>이벤트 객체(Event Object)</strong>라고 한다.</p>
<pre><code class="language-javascript">button.addEventListener(&quot;click&quot;, function (event) {
  console.log(event);
});</code></pre>
<p>이 객체에는 <strong>이벤트가 발생한 상황에 대한 다양한 정보</strong>가 들어 있다.</p>
<p>예를 들어</p>
<ul>
<li>어떤 요소에서 이벤트가 발생했는지</li>
<li>마우스 좌표</li>
<li>눌린 키</li>
<li>이벤트 타입</li>
</ul>
<p>같은 정보들이다.</p>
<p>대표적으로 자주 사용하는 속성은 다음과 같다.</p>
<ul>
<li><code>event.target</code></li>
<li><code>event.type</code></li>
</ul>
<h3 id="preventdefault">preventDefault</h3>
<p>일부 HTML 요소는 기본 동작을 가지고 있다.</p>
<p>예를 들어</p>
<ul>
<li><code>&lt;a&gt;</code> → 링크 이동</li>
<li><code>&lt;form&gt;</code> → 페이지 새로고침</li>
</ul>
<p>이러한 기본 동작을 막고 싶을 때
<code>preventDefault()</code>를 사용한다.</p>
<pre><code class="language-javascript">link.addEventListener(&quot;click&quot;, function (event) {
  event.preventDefault();
});</code></pre>
<p>이 코드는 링크를 클릭해도
<strong>페이지 이동이 발생하지 않도록 만든다.</strong></p>
<hr>
<h2 id="8-핵심-정리">8. 핵심 정리</h2>
<p>지금까지 살펴본 내용을 정리하면
JavaScript가 웹 페이지를 제어하는 방식은 다음 흐름으로 이해할 수 있다.</p>
<pre><code class="language-text">DOM 선택 → DOM 탐색 → DOM 조작 → 이벤트 처리</code></pre>
<p>각 단계의 역할은 다음과 같다.</p>
<h3 id="dom-선택">DOM 선택</h3>
<p>JavaScript는 DOM을 조작하기 전에
<strong>먼저 어떤 요소를 다룰지 선택</strong>해야 한다.</p>
<p>대표적인 메서드</p>
<ul>
<li><code>getElementById</code></li>
<li><code>querySelector</code></li>
<li><code>querySelectorAll</code></li>
</ul>
<h3 id="dom-탐색">DOM 탐색</h3>
<p>DOM은 <strong>트리 구조</strong>이기 때문에
요소 간의 관계를 이용해 이동할 수 있다.</p>
<p>대표적인 탐색 속성</p>
<ul>
<li><code>parentElement</code></li>
<li><code>children</code></li>
<li><code>firstElementChild</code></li>
<li><code>lastElementChild</code></li>
<li><code>nextElementSibling</code></li>
<li><code>previousElementSibling</code></li>
</ul>
<h3 id="dom-조작">DOM 조작</h3>
<p>선택한 요소의 <strong>내용이나 구조</strong>를 변경할 수 있다.</p>
<p>대표적인 방법</p>
<ul>
<li><code>textContent</code></li>
<li><code>innerHTML</code></li>
<li><code>innerText</code></li>
<li><code>createElement</code></li>
<li><code>appendChild</code></li>
<li><code>remove</code></li>
</ul>
<h3 id="html-속성과-스타일-조작">HTML 속성과 스타일 조작</h3>
<p>DOM을 통해 <strong>HTML 속성과 CSS 스타일도 수정</strong>할 수 있다.</p>
<p>대표적인 방법</p>
<ul>
<li><code>getAttribute</code></li>
<li><code>setAttribute</code></li>
<li><code>classList</code></li>
<li><code>style</code></li>
<li><code>dataset</code></li>
</ul>
<h3 id="이벤트-처리-1">이벤트 처리</h3>
<p>JavaScript는 <strong>사용자의 행동에 반응</strong>하도록 만들 수 있다.</p>
<p>대표적인 개념</p>
<ul>
<li>이벤트(Event)</li>
<li>이벤트 핸들러(Event Handler)</li>
<li><code>addEventListener</code></li>
<li>이벤트 객체</li>
<li><code>preventDefault</code></li>
</ul>
<p>결국 JavaScript가 하는 일은
<strong>DOM을 조작해 웹 페이지를 동적으로 변화시키는 것</strong>이다.</p>
<p>이 구조를 이해하면
웹 페이지에서 발생하는 대부분의 인터랙션을 구현할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript this의 동작 원리 정리]]></title>
            <link>https://velog.io/@hogu__giriboy/JavaScript-this%EC%9D%98-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@hogu__giriboy/JavaScript-this%EC%9D%98-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Fri, 06 Mar 2026 01:04:09 GMT</pubDate>
            <description><![CDATA[<h2 id="1-this란-무엇인가">1. this란 무엇인가</h2>
<p><code>this</code>는 <strong>함수가 실행될 때 결정되는 객체 참조값</strong>이다.
즉, 함수 내부에서 <strong>&quot;지금 이 함수를 실행한 주체&quot;</strong>를 가리킨다.</p>
<p><code>this</code>는 <strong>함수가 만들어질 때 결정되지 않는다</strong>
<strong>함수가 호출되는 방식</strong>에 따라 결정된다
즉 <code>this</code>는 <strong>함수의 위치가 아니라 호출 방식(call-site)에 의해 결정</strong>된다.</p>
<h3 id="기본-예제">기본 예제</h3>
<pre><code class="language-javascript">const person = {
  name: &quot;홍길동&quot;,
  greet() {
    console.log(this.name);
  },
};

person.greet();</code></pre>
<p>실행 결과는 <code>홍길동</code>이다.
<code>greet()</code>를 <strong>person이 호출</strong>했기에
<code>this</code>는 <code>person</code>이 된다.</p>
<hr>
<h2 id="2-this가-결정되는-주요-규칙">2. this가 결정되는 주요 규칙</h2>
<p>JavaScript에서 <code>this</code>는 크게 <strong>네 가지 상황</strong>으로 이해하는 것이 일반적이다.</p>
<h3 id="일반-함수-호출">일반 함수 호출</h3>
<pre><code class="language-javascript">function test() {
  console.log(this);
}

test();</code></pre>
<p>출력 결과로는 브라우저 환경 <code>window</code>가 나온다.</p>
<p>객체 없이 <strong>그냥 함수로 호출</strong>했을 경우,
전역 객체가 <code>this</code>가 된다.</p>
<h3 id="객체의-메서드로-호출">객체의 메서드로 호출</h3>
<pre><code class="language-javascript">const user = {
  name: &quot;철수&quot;,
  sayName() {
    console.log(this.name);
  },
};

user.sayName();</code></pre>
<p>결과는 <code>철수</code>가 된다.
<code>user.sayName()</code> 형태로, <strong>점 앞의 객체(user)</strong>가 <code>this</code>가 된다.</p>
<h3 id="생성자-함수-new">생성자 함수 (new)</h3>
<pre><code class="language-javascript">function Person(name) {
  this.name = name;
}

const p = new Person(&quot;홍길동&quot;);

console.log(p.name);</code></pre>
<p><code>new</code>로 호출하면 새로운 객체가 생성 되고,
<code>this</code>가 그 객체를 가리켜, 객체가 반환된다.</p>
<h4 id="명시적-바인딩-call--apply--bind">명시적 바인딩 (call / apply / bind)</h4>
<pre><code class="language-javascript">function greet() {
  console.log(this.name);
}

const user = { name: &quot;영희&quot; };

greet.call(user);</code></pre>
<p>결과는 <code>영희</code>가 나온다.
<code>call</code>/<code>apply</code>/<code>bind</code>로 <strong><code>this</code>를 직접 지정할 수 있기 때문이다.</strong></p>
<hr>
<h2 id="3-this가-헷갈리는-대표-상황-콜백">3. this가 헷갈리는 대표 상황 (콜백)</h2>
<pre><code class="language-javascript">const person = {
  name: &quot;홍길동&quot;,
  greet() {
    setTimeout(function () {
      console.log(this.name);
    }, 1000);
  },
};

person.greet();</code></pre>
<p>결과는 <code>undefined</code>로,
<code>setTimeout</code> 안의 함수는 <strong>일반 함수 호출</strong>이기 때문이다.
즉 <code>this</code>는 전역 객체를 가리키게 된다.</p>
<hr>
<h2 id="4-arrow-function과-this">4. Arrow Function과 this</h2>
<p>Arrow Function은 <strong>자신만의 this를 가지지 않는다.</strong></p>
<p>대신 <strong>외부 스코프의 this를 그대로 사용한다.</strong></p>
<pre><code class="language-javascript">const person = {
  name: &quot;홍길동&quot;,
  greet() {
    setTimeout(() =&gt; {
      console.log(this.name);
    }, 1000);
  },
};

person.greet();</code></pre>
<p>결과는 <code>홍길동</code>,
바로 전 예제를 Arrow Function으로 바꿨을 뿐이다.
Arrow Function이 <code>greet()</code>의 <code>this</code>를 그대로 사용하기 때문이다.</p>
<hr>
<h2 id="5-핵심-정리">5. 핵심 정리</h2>
<ul>
<li><code>this</code>는 함수 내부에서 <strong>현재 실행 주체를 가리키는 참조값</strong>이다.</li>
<li><code>this</code>는 <strong>함수가 정의된 위치가 아니라 호출 방식에 의해 결정된다.</strong></li>
<li><code>객체.메서드()</code> 형태로 호출되면 <strong>점 앞의 객체가 this가 된다.</strong></li>
<li>일반 함수 호출에서는 <code>this</code>가 <strong>전역 객체(window)</strong>가 된다.</li>
<li><strong>Arrow Function은 자체적인 this를 만들지 않고 외부 this를 그대로 사용한다.</strong></li>
</ul>
<p>한 가지 더 짚어서
많이들 착각하는 포인트로,
많은 사람들이 <code>this</code>는 함수가 속한 객체라고 생각하는데,
실제로 <code>this</code>는 함수를 <strong>호출한</strong> 객체다.</p>
]]></description>
        </item>
    </channel>
</rss>