<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>ldh-dodo.log</title>
        <link>https://velog.io/</link>
        <description>꾸준함을 목표로 합니다.</description>
        <lastBuildDate>Fri, 27 Feb 2026 01:22:03 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>ldh-dodo.log</title>
            <url>https://velog.velcdn.com/images/ldh-dodo/profile/58b3eb43-836e-4132-9d38-078d81075bd9/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. ldh-dodo.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/ldh-dodo" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[네부캠] 온라인 모각코 서비스 개발기]]></title>
            <link>https://velog.io/@ldh-dodo/%EB%84%A4%EB%B6%80%EC%BA%A0-%EC%98%A8%EB%9D%BC%EC%9D%B8-%EB%AA%A8%EA%B0%81%EC%BD%94-%EC%84%9C%EB%B9%84%EC%8A%A4-%EA%B0%9C%EB%B0%9C%EA%B8%B0</link>
            <guid>https://velog.io/@ldh-dodo/%EB%84%A4%EB%B6%80%EC%BA%A0-%EC%98%A8%EB%9D%BC%EC%9D%B8-%EB%AA%A8%EA%B0%81%EC%BD%94-%EC%84%9C%EB%B9%84%EC%8A%A4-%EA%B0%9C%EB%B0%9C%EA%B8%B0</guid>
            <pubDate>Fri, 27 Feb 2026 01:22:03 GMT</pubDate>
            <description><![CDATA[<p><a href="https://commitplease.com/">온라인 모각코 서비스 : 잔디 심고 갈래?</a></p>
<h2 id="온라인-모각코-서비스를-만들고-싶어졌다">온라인 모각코 서비스를 만들고 싶어졌다</h2>
<p>네부캠 멤버십을 진행하면서 프로젝트 주제를 정해야 했다.
팀원 네 명의 공통된 생각은 하나였다.</p>
<blockquote>
<p><strong>네부캠이 끝나도 우리가 계속 사용할 서비스</strong>를 만들자.</p>
</blockquote>
<p>우리는 회의를 할 때 Figjam을 활용했고, 서로의 말에 엄청나게 적극적으로 리액션을 하며 회의를 하자는 규칙을 세웠다. </p>
<p align="center" style="color:#808080; font-size:16px">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/6748d205-4e58-4334-96fb-cb7ee411b81e/image.png" width ="100%"/>

<p>아래는 적극적인 회의의 흔적이다</p>
<table>
  <tr>
    <td>
      <img src="https://velog.velcdn.com/images/ldh-dodo/post/0ecf3898-b211-4991-ad71-c1d542c1a33b/image.png" width="100%" />
    </td>
    <td>
      <img src="https://velog.velcdn.com/images/ldh-dodo/post/948b34a5-fdb8-401b-b68c-35e825ea5822/image.png" width="100%" />
    </td>
  </tr>
</table>


<p>회의를 3주 동안 매일 회의만 8~9시간 하니까, 갈수록 서로 예민해지는 경향이 있었다.</p>
<p style="text-align:center;">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/dfd89335-7342-4ee0-944c-4f0bc9b99930/image.png" width="40%" />
</p>

<p>특히, 자신이 낸 아이디어가 reject 당할 때, 그 이유가 합리적이어도 가슴이 아픈 건 어쩔 수 없는 것 같다.</p>
<p>네 명 모두가 말한
<strong>‘네부캠이 끝나도 우리가 계속 사용할 서비스’</strong>에 도달하는 건 생각보다 쉽지 않았다.</p>
<p>회의는 크리스마스 연휴 전까지, 큰 소득 없이 이어졌다.</p>
<p>다행히 크리스마스 연휴가 겹치면서 머리를 식힐 시간이 생겼고,
연휴가 지난 뒤 한 주 동안 서로의 생각을 천천히 맞춰가며
팀 분위기도 다시 정상화할 수 있었다.</p>
<p align="center" style="color:#808080; font-size:16px">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/8fbf9844-9c5f-4c5b-8bf0-d8511f625198/image.png" width ="20%"
       style="padding: 0;margin:0; ">
</p>




<p>그렇게 3주의 고난 끝에, 드디어 서로가 만족할 만한 프로젝트 주제가 나왔다.
정말 다 같이 기뻐했고, 그 이후의 기획은 놀라울 정도로 빠르게 진행됐다.</p>
<p style="text-align:center;">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/87dab717-229d-430d-9718-effcd5aee610/image.png" width="50%" />
</p>

<p>네이버 부스트캠프 과정을 거치며 밤을 정말 많이 샜다.
그때마다 Slack 한편에 켜져 있던 동료들의 ‘초록색 상태 표시줄’을 보며
묘한 위안과 힘을 얻곤 했다.</p>
<p><img src="https://velog.velcdn.com/images/ldh-dodo/post/ca7b4f84-6fac-482f-a22b-840b845c6eb8/image.png" alt=""></p>
<p>“나만 이 시간에 깨어 있는 게 아니구나.”
그 <strong>파란불</strong> 하나가 이상하게 큰 힘이 됐다.</p>
<p>그래서 생각했다.</p>
<p>‘개발자의 성취를 더 직관적으로 보여주고,
같은 공간에서 함께 몰입하고 있다는 감각을 전해줄 수 있는 서비스가 있으면 좋지 않을까?’</p>
<p>그걸 우리가 직접 만들어보기로 했다.</p>
<p>그렇게 탄생한 서비스가
<strong>“잔디 심고 갈래? (Commit Please)”</strong>다.</p>
<p>우리는 단순히 접속하는 것보다, 개인과 공동의 성취를 확인할 수 있으면 좋겠다고 생각했다.</p>
<p><img src="https://velog.velcdn.com/images/ldh-dodo/post/d8e11fdf-fc71-43b4-88b7-9b2124258acc/image.png" alt=""></p>
<p>그래서 포인트를 모아 펫을 뽑을 수도,</p>
<p><img src="https://velog.velcdn.com/images/ldh-dodo/post/8056306e-7493-47d9-a3fc-86a4ffabf383/image.png" alt=""></p>
<p>자신의 개별 Task를 Todo 리스트로 관리할 수도,</p>
<p><img src="https://velog.velcdn.com/images/ldh-dodo/post/9e8adc89-42e5-4499-b028-63d207b47cba/image.png" alt=""></p>
<p>자신의 날짜별 활동을 추적할 수도,</p>
<p><img src="https://velog.velcdn.com/images/ldh-dodo/post/39268bf9-3b8d-4806-ab9c-958a97871444/image.png" alt=""></p>
<p>자신의 활동으로 맵을 변화시킬 수도 있게 만들어보았다.</p>
<p>전세계 개발자들과도 함께하고 싶은 마음이 있어, 국제화를 지원하여 외국 커뮤니티에도 홍보하고 있다. 
인도, 유럽 등에서도 가끔 와주신다 ㅎㅎ</p>
<p><img src="https://velog.velcdn.com/images/ldh-dodo/post/8d1cc698-950a-498b-aad5-72ba37b8cbe2/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/ldh-dodo/post/aefb57fa-9c6b-4b55-b14b-5918d69221f9/image.png" alt=""></p>
<p>아래는 감사하게도 서비스에 실제 접속해주신 사용자 분들의 모습이다.</p>
<p><img src="https://velog.velcdn.com/images/ldh-dodo/post/3212d995-c2b8-43f6-8a0f-9b8ccece234c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/ldh-dodo/post/ced9e7ed-226d-410c-b85a-d83c2091ac7c/image.png" alt=""></p>
<p><a href="https://commitplease.com/">잔디 심고 갈래? (Commit Please)</a>에 접속해서 함께 서로의 원동력이 되어보는 건 어떨까?</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[비동기 처리 방식]]></title>
            <link>https://velog.io/@ldh-dodo/%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC-%EB%B0%A9%EC%8B%9D</link>
            <guid>https://velog.io/@ldh-dodo/%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC-%EB%B0%A9%EC%8B%9D</guid>
            <pubDate>Mon, 25 Aug 2025 13:58:51 GMT</pubDate>
            <description><![CDATA[<h2 id="비동기-처리-방식"><strong>비동기 처리 방식</strong></h2>
<p><code>비동기 처리 방식</code> 이란 어떤 작업이 끝날 때까지 기다리지 않고, 다음 작업을 먼저 수행하는 방식을 말한다.</p>
<h1 id="웹-개발에서의-비동기-처리">웹 개발에서의 비동기 처리</h1>
<p>웹 개발에서 비동기 처리는 주로 <code>서버와 통신하는 작업</code>에서 사용된다. </p>
<p>이는 서버에 데이터를 요청하고 응답을 받는 데는 시간이 걸리기 때문이다.</p>
<p>만약 동기 방식으로 처리한다면, 서버에서 응답이 올 때까지 웹페이지가 멈춰버리게 될 것이다. </p>
<p>이 때문에 사용자는 아무것도 할 수 없는 상태가 된다.</p>
<p>하지만 비동기 방식을 사용하면, 서버에 데이터를 요청해놓고 웹페이지는 멈추지 않고 계속해서 사용자 입력을 받거나 다른 작업을 처리할 수 있다. </p>
<p>서버에서 응답이 오면, 그 때 받은 데이터를 이용해 화면을 업데이트하는 식으로 작동한다.</p>
<h1 id="js-비동기-처리-방식의-진화와-비교">JS 비동기 처리 방식의 진화와 비교</h1>
<p>세 가지 주요 방식이 존재한다.</p>
<ol>
<li>Callback (초기 ~ 2015년)</li>
<li>Promise (2015년 ES6)</li>
<li>Async / Await (2017년 ES8)</li>
</ol>
<h2 id="1-callback">1. Callback</h2>
<p>가장 오래된 방식이며, 다른 함수의 인자로 전달되어 특정 작업이 완료된 후 호출되는 함수이다.</p>
<h3 id="사용-예시">사용 예시</h3>
<pre><code class="language-jsx">// 전형적인 콜백 패턴
function fetchUser(id, callback) {
    setTimeout(() =&gt; {
        const user = { id, name: &#39;홍길동&#39; };
        callback(null, user);
    }, 1000);
}

// 사용법
fetchUser(123, (error, user) =&gt; {
    if (error) {
        console.error(&#39;에러 발생:&#39;, error);
        return;
    }
    console.log(&#39;사용자:&#39;, user);
});</code></pre>
<h3 id="단점">단점</h3>
<p><code>콜백 지옥</code> 이 발생할 수 있다.</p>
<p><code>콜백 지옥</code> 이란 비동기 작업이 여러 개 중첩될 때, 콜백 함수가 계속해서 깊어지는 현상을 말한다.</p>
<p>코드가 마치 피라미드처럼 안쪽으로 들여쓰기되어 가독성이 매우 떨어지고, 유지보수가 어려워지는 문제를 일으킨다.</p>
<p>예를 들어, 세 개의 비동기 작업을 순서대로 실행해야 한다고 가정해보자.</p>
<ol>
<li>사용자 정보를 가져온다.</li>
<li>가져온 사용자 정보로 게시물 목록을 가져온다.</li>
<li>가져온 게시물 목록으로 댓글을 가져온다.</li>
</ol>
<p>이러한 로직을 콜백 함수로 구현하면 다음과 같이 코드가 깊어진다.</p>
<pre><code class="language-jsx">// 첫 번째 비동기 함수: 사용자 정보 가져오기
getUser(userId, function(user) {
  // 두 번째 비동기 함수: 게시물 목록 가져오기
  getPosts(user.id, function(posts) {
    // 세 번째 비동기 함수: 댓글 가져오기
    getComments(posts[0].id, function(comments) {
      console.log(comments);
      // 작업이 더 많아지면 들여쓰기는 끝없이 깊어진다
    });
  });
});</code></pre>
<p>이러한 문제를 해결하기 위해 <code>Promise</code> 와 <code>async / await</code> 가 등장하게 된다.</p>
<h2 id="2-promise">2. Promise</h2>
<p>Promise는 기존의 콜백 함수 방식이 가진 &#39;콜백 지옥&#39; 문제를 해결하기 위해 도입되었다.</p>
<p><code>Promise</code>는 비동기 작업의 최종 완료 또는 실패를 나타내는 객체이다.</p>
<p>즉, 미래에 어떤 결과를 반환하거나 오류가 발생할 것을 약속하는 객체라고 생각하면 된다.</p>
<h3 id="promise의-세-가지-상태"><strong>Promise의 세 가지 상태</strong></h3>
<p>Promise는 비동기 작업의 진행 상황에 따라 다음 세 가지 상태 중 하나에 속합니다.</p>
<ol>
<li><code>대기(pending)</code><strong>:</strong> 비동기 작업이 아직 완료되지 않은 초기 상태</li>
<li><code>이행(fulfilled)</code><strong>:</strong> 비동기 작업이 성공적으로 완료된 상태 (resolve 호출됨)</li>
<li><code>거부(rejected)</code><strong>:</strong> 비동기 작업이 실패한 상태 (reject 호출됨)</li>
</ol>
<p>한 번 <code>fulfilled</code> 또는 <code>rejected</code> 상태가 되면, 다시 <code>pending</code> 상태로 돌아갈 수 없다.</p>
<h3 id="사용-예시-1">사용 예시</h3>
<pre><code class="language-jsx">// Promise 생성
const myPromise = new Promise((resolve, reject) =&gt; {
  // 비동기 작업 (예: 2초 후 완료)
  setTimeout(() =&gt; {
    const isSuccess = true;
    if (isSuccess) {
      resolve(&quot;작업 성공! 🎉&quot;); // 성공 시 resolve 호출
    } else {
      reject(&quot;작업 실패! 😭&quot;); // 실패 시 reject 호출
    }
  }, 2000);
});

// Promise 사용
myPromise
  .then((result) =&gt; {
    // 성공적으로 이행되면 실행
    console.log(result); // &quot;작업 성공! 🎉&quot; 출력
  })
  .catch((error) =&gt; {
    // 거부되면 실행
    console.error(error); // &quot;작업 실패! 😭&quot; 출력
  })
  .finally(() =&gt; {
    // 성공/실패 여부와 관계없이 항상 실행
    console.log(&quot;작업이 끝났습니다.&quot;);
  });</code></pre>
<h3 id="장점">장점</h3>
<ul>
<li><strong>가독성 향상:</strong> <code>then()</code>과 <code>catch()</code> 메서드를 체인처럼 연결하여 비동기 코드를 순차적으로 읽을 수 있게 해준다.</li>
<li><strong>쉬운 오류 처리:</strong> <code>catch()</code> 메서드를 통해 비동기 작업 도중 발생하는 모든 오류를 한 곳에서 처리할 수 있어, 에러 핸들링이 간결해진다.</li>
</ul>
<h3 id="단점-1">단점</h3>
<p><code>Promise</code>는 콜백 지옥 문제를 해결했지만, 여전히 몇 가지 문제점이 있다.</p>
<ul>
<li><strong>복잡한 체이닝</strong><ul>
<li>여러 개의 Promise를 순차적으로 처리해야 할 때, <code>.then()</code> 메서드를 계속해서 연결하는 <strong>체이닝(Chaining)</strong> 방식은 코드를 읽기 어렵게 만들 수 있다.</li>
<li>특히 체인이 길어지면 <code>중간 에러 처리용 catch</code>가 많이 생겨서 가독성이 급격하게 저하될 수 있다.</li>
</ul>
</li>
<li><strong>불필요한 중첩</strong><ul>
<li>모든 <code>.then()</code> 블록 안에 새로운 코드를 작성해야 하므로, 여전히 어느 정도의 들여쓰기가 발생합니다. 콜백 지옥만큼 심하지는 않지만, 코드가 깊어질수록 가독성이 떨어질 수 있다.</li>
</ul>
</li>
</ul>
<p><code>Callback</code> 에서의 시나리오로  예시 코드를 살펴보자.</p>
<ol>
<li>사용자 정보를 가져온다.</li>
<li>가져온 사용자 정보로 게시물 목록을 가져온다.</li>
<li>가져온 게시물 목록으로 댓글을 가져온다.</li>
</ol>
<pre><code class="language-jsx">// 가상의 API 호출 함수 (Promise 반환)
function getUser(userId) {
console.log(&#39;1. 사용자 정보 가져오는 중...&#39;);
return new Promise(resolve =&gt; setTimeout(() =&gt; resolve({ id: userId, name: &#39;Alice&#39;, posts: [101, 102] }), 1000));
}

function getPosts(postIds) {
console.log(&#39;2. 게시물 목록 가져오는 중...&#39;);
return new Promise(resolve =&gt; setTimeout(() =&gt; resolve([{ id: 101, title: &#39;첫 번째 글&#39; }, { id: 102, title: &#39;두 번째 글&#39; }]), 1000));
}

function getComments(postId) {
console.log(&#39;3. 댓글 목록 가져오는 중...&#39;);
return new Promise(resolve =&gt; setTimeout(() =&gt; resolve([&#39;댓글 1&#39;, &#39;댓글 2&#39;]), 1000));
}

// Promise 체이닝 시작
getUser(&#39;user123&#39;)
.then(user =&gt; {
// 첫 번째 작업 결과(user)를 다음 작업에 전달
return getPosts(user.posts);
})
.then(posts =&gt; {
// 두 번째 작업 결과(posts)를 다음 작업에 전달
return getComments(posts[0].id);
})
.then(comments =&gt; {
// 최종 결과 출력
console.log(&#39;✅ 최종 댓글:&#39;, comments);
})
.catch(error =&gt; {
console.error(&#39;❌ 오류 발생:&#39;, error);
});</code></pre>
<p>중첩 구조에서 가독성이 저하될 것이라는게 명확해 보인다.</p>
<h2 id="3-async--await">3. async / await</h2>
<p><code>async/await</code>는 이러한 Promise의 단점을 해결하고 비동기 코드를 동기 코드처럼 읽고 쓸 수 있도록 만든 문법이다.</p>
<h3 id="사용-예시-2">사용 예시</h3>
<pre><code class="language-jsx">// 가상의 API 호출 함수 (Promise 반환)
function getUser(userId) {
  console.log(&#39;1. 사용자 정보 가져오는 중...&#39;);
  return new Promise(resolve =&gt; setTimeout(() =&gt; resolve({ id: userId, name: &#39;Alice&#39;, posts: [101, 102] }), 1000));
}

function getPosts(postIds) {
  console.log(&#39;2. 게시물 목록 가져오는 중...&#39;);
  return new Promise(resolve =&gt; setTimeout(() =&gt; resolve([{ id: 101, title: &#39;첫 번째 글&#39; }, { id: 102, title: &#39;두 번째 글&#39; }]), 1000));
}

function getComments(postId) {
  console.log(&#39;3. 댓글 목록 가져오는 중...&#39;);
  return new Promise(resolve =&gt; setTimeout(() =&gt; resolve([&#39;댓글 1&#39;, &#39;댓글 2&#39;]), 1000));
}

// async 함수 선언
async function fetchData() {
  try {
    // 1. await 키워드로 Promise가 완료될 때까지 기다림
    const user = await getUser(&#39;user123&#39;);

    // 2. await 키워드로 다음 Promise가 완료될 때까지 기다림
    const posts = await getPosts(user.posts);

    // 3. await 키워드로 마지막 Promise가 완료될 때까지 기다림
    const comments = await getComments(posts[0].id);

    // 4. 모든 작업이 끝난 후 최종 결과 출력
    console.log(&#39;✅ 최종 댓글:&#39;, comments);

  } catch (error) {
    // try-catch로 모든 에러를 한 곳에서 처리
    console.error(&#39;❌ 오류 발생:&#39;, error);
  }
}

// 함수 호출
fetchData();
</code></pre>
<h3 id="장점-1">장점</h3>
<ul>
<li>우수한 가독성<ul>
<li>비동기 코드가 동기 코드처럼 위에서 아래로 흐르는 직선적 구조처럼 보인다. (코드 읽는 순서와 실행 순서가 그대로 일치함)</li>
<li><code>Promise</code> 의 경우, 체인이 길어질 수록 가독성이 좋지 않다는 단점이 있었고, 이를 보완</li>
</ul>
</li>
</ul>
<h3 id="단점-2">단점</h3>
<ol>
<li><p><strong>동시성(Concurrency) 처리 주의 필요</strong></p>
<p>  <code>await</code>는 <code>Promise</code>가 완료될 때까지 코드가 일시 정지한다는 특징 때문에, 여러 비동기 작업을 동시에 병렬적으로 처리하는 데 불리하다.</p>
<p> 예를 들어, 서로 의존성이 없는 3개의 API를 호출하는 상황이라고 가정했을 때, <code>await</code> 를 다음과 같이 사용했다고 가정해보자.</p>
<pre><code class="language-jsx"> async function sequentialFetch() {
   const result1 = await fetch(&#39;api/data1&#39;); // 1초 소요
   const result2 = await fetch(&#39;api/data2&#39;); // 1초 소요
   const result3 = await fetch(&#39;api/data3&#39;); // 1초 소요
   // 총 3초 소요
 }</code></pre>
<p> 총 3초의 시간이 소요된다.</p>
<p> 해당 문제를 해결하기 위해, <code>Promise.all()</code> 을 사용하면 모든 <code>fetch</code> 가 한 번에 실행되도록 해서, <code>1초</code> 의 시간이 소요되게 할 수 있다.</p>
<pre><code class="language-jsx"> async function parallelFetch() {
   // Promise.all()을 사용해 모든 fetch가 동시에 실행되도록 함
   const [result1, result2, result3] = await Promise.all([
     fetch(&#39;api/data1&#39;),
     fetch(&#39;api/data2&#39;),
     fetch(&#39;api/data3&#39;)
   ]);
   // 총 1초 소요
 }</code></pre>
<p> 단, <code>Promise.all()</code>은 서로 의존성이 없는 여러 개의 비동기 작업을 동시에(병렬로) 실행할 때 사용하는 메서드이고, 의존성이 없는 관계에 적합하다.</p>
<p> 만약, A의 결과가 B에 필요한 경우, B는 A가 끝날 때까지 기다려야만 시작할 수 있다면, 이런 관계를 <code>의존 관계</code>라고 부른다. </p>
<p> 이 경우에는 병렬 처리가 불가능하며, <code>await</code>를 사용한 순차적 처리가 더 자연스럽고 적합하다.</p>
<p> 작업 간의 의존성이 있다면 <code>async / await</code> 를 통해 코드의 가독성을 확보하는 것이 좋고,</p>
<p> 작업 간의 의존성이 없다면 <code>Promise.all()</code> 을 사용해 모든 작업을 동시에 실행하는 것이 훨씬 빠르고 효율적이다.</p>
</li>
<li><p><strong>에러 처리 범위 제한</strong></p>
<p> <code>try-catch</code>는 해당 블록 내의 에러만 처리할 수 있어,</p>
<p> 비동기 작업이 여러 함수에 분산되어 있을 때 에러 처리가 복잡해질 수 있다.</p>
<pre><code class="language-jsx">  // 에러 처리가 복잡해지는 경우
   async function complexOperation() {
     try {
       const step1 = await operation1(); // 여기서 에러 발생 가능
     } catch (error) {
       // operation1의 에러만 처리
     }

     try {
       const step2 = await operation2(); // 여기서도 에러 발생 가능
     } catch (error) {
       // operation2의 에러만 처리
     }
   }</code></pre>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Map vs HashMap]]></title>
            <link>https://velog.io/@ldh-dodo/Map-vs-HashMap</link>
            <guid>https://velog.io/@ldh-dodo/Map-vs-HashMap</guid>
            <pubDate>Wed, 23 Jul 2025 06:57:50 GMT</pubDate>
            <description><![CDATA[<p>JS에서 기본적인 <code>HashMap</code>을 구현하고자 했다.</p>
<h1 id="map">Map</h1>
<ul>
<li>키-값 쌍을 저장할 수 있는 내장 객체이다.</li>
<li>삽입 순서를 보장한다.</li>
</ul>
<h2 id="주요-메서드-및-속성">주요 메서드 및 속성</h2>
<ul>
<li><code>set(key, value)</code> :  구현해야하는 <code>put()</code> 과 동일</li>
<li><code>get(key)</code> : 구현해야하는 <code>get()</code> 과 동일</li>
<li><code>has(key)</code>  :  구현해야하는 <code>containsKey()</code> 와 동일</li>
<li><code>delete()</code> :  구현해야하는 <code>remove()</code> 와 동일</li>
<li><code>clear()</code> : 구현해야하는 <code>clear()</code> 와 동일</li>
<li><code>size</code> : 구현해야하는 <code>size()</code> 와 동일</li>
</ul>
<h1 id="map-vs-hashmap">Map vs HashMap</h1>
<p>JS 에서는 <code>HashMap</code>이 없기 때문에(Map이 곧 HashMap), Kotlin에서의 둘의 차이를 알아보자.</p>
<table>
<thead>
<tr>
<th>구분</th>
<th>Map</th>
<th>HashMap</th>
</tr>
</thead>
<tbody><tr>
<td>내부 구조</td>
<td>없음</td>
<td>해시 테이블</td>
</tr>
<tr>
<td>저장/조회 방법</td>
<td>구현하기 나름</td>
<td>해시 함수 기반</td>
</tr>
</tbody></table>
<p><code>Map</code> 은 인터페이스이고 따라서 내부 구현이 되어있지 않다. <code>HashMap</code>은 이 인터페이스를 기반으로  <code>해시 함수</code> 와 <code>해시 테이블</code> 을 사용해 내부 구현을 한 <code>구현체</code> 인 것이다.</p>
<p>그래서 사실 본질적인 차이 <code>Map은 인터페이스이고 HashMap은 구현체이다</code> 라는 것이다.</p>
<h2 id="hashmap-동작-순서">HashMap 동작 순서</h2>
<ol>
<li><code>hashCode()</code> 메서드를 호출해 정수형 해시값을 얻는다.</li>
<li>해당 해시값을 바탕으로 내부 해시 함수가 추가 연산(비트 마스킹, 재해싱) 등을 수행한다.</li>
<li>2번 동작을 통해 해시 테이블 내 저장할 위치(버킷 인덱스) 를 결정한다.</li>
</ol>
<h2 id="해시함수">해시함수</h2>
<p>먼저, <code>해시 함수</code> 란 <code>키 -&gt; 고정 길이의 숫자(해시값)</code> 으로 변환하는 함수이다.</p>
<p>결국, <code>키를 해시 테이블의 인덱스로 빠르고 균등하게 변환</code> 하는 것이 목적이다.</p>
<h3 id="좋은-해시-함수의-조건">좋은 해시 함수의 조건</h3>
<p>효율적이고 효과적인 해시 함수는 다음과 같은 조건을 만족해야 한다.</p>
<table>
<thead>
<tr>
<th><strong>특징</strong></th>
<th><strong>설명</strong></th>
</tr>
</thead>
<tbody><tr>
<td>충돌 최소화</td>
<td>서로 다른 입력이 같은 해시값을 갖는 경우(충돌)을 최소화해야 함</td>
</tr>
<tr>
<td>빠른 계산 속도</td>
<td>해시값을 빠르게 계산할 수 있어야한다.</td>
</tr>
<tr>
<td>균등 분포</td>
<td>키들의 해시값이 해시 테이블의 각 버킷에 고르게 분포되어야 한다.</td>
</tr>
<tr>
<td>결과 예측 불가능</td>
<td>입력값에 아주 작은 변화가 있으면 해시값도 크게 달라져야 한다. (보안 중요시)</td>
</tr>
</tbody></table>
<p>해시값이 모든 버킷에 고르게 분포되어야 각 버킷이 적절히 사용되어 충돌이 적고, 조회 및 삽입이 빠르다고 한다.</p>
<h2 id="해시맵-구조상-버킷-사이즈">해시맵 구조상 버킷 사이즈</h2>
<p>해시맵은 내부에 여러 개의 저장 공간을 가진 <code>배열</code> 을 가지고 있다.</p>
<p>그리고, 이 배열의 각 칸을 <code>버킷</code> 이라고 부른다.</p>
<p>예를 들어, <code>hash = [ [], [] ]</code>  은 2개의 빈 배열(버킷) 이 있고, 이 배열의 길이가 <code>버킷 사이즈</code> 를 의미한다.</p>
<h1 id="구현-함수">구현 함수</h1>
<ul>
<li><code>clear()</code> : 전체 맵을 초기화한다.</li>
<li><code>containsKey(String key)</code> : 해당 키가 존재하는지 여부에 따라 <code>boolean</code> 결과를 리턴한다.</li>
<li><code>get(String)</code>  : 해당 키와 매치되는 값을 찾아 리턴한다.</li>
<li><code>isEmpty()</code> : 비어있는 맵인지 Bool 결과를 리턴한다.</li>
<li><code>keys()</code>   : 전체 키 목록을 [String] 배열로 리턴한다.</li>
<li><code>put(String key, String value)</code> 키-값을 추가한다.</li>
<li><code>remove(String key)</code> : 해당 키에 있는 값을 삭제한다.</li>
<li><code>size()</code> : 전체 아이템 개수를 리턴한다.</li>
</ul>
<h2 id="구현-전-고민">구현 전 고민</h2>
<ul>
<li>버킷 개수가 많을 수록, 충돌 확률이 감소할 텐데, 그렇다면 적절한 버킷 개수를 어떻게 구성할 것인가?<ul>
<li>초반에 적당한 버킷 개수로 시작하고, 재해싱을 통해 버킷 수를 늘리고 기존에 저장된 키들을 다시 해시해서 재배치해야 할 것같다.</li>
</ul>
</li>
<li>초반에 적합한 버킷 개수는 몇일까?<ul>
<li>Java, Kotlin의 초기 버킷 개수는 16개라고 한다. 기본적으로 버킷 개수는 2의 거듭제곱을 선호한다고 하는데,<code>비트 연산</code> 을 통해 성능을 비약적으로 개선할 수 있기 때문이라고 한다.</li>
</ul>
</li>
<li>재해싱을 하기 위한 트리거 조건은 몇으로 설정할까?<ul>
<li>보통 <code>loadFactor(부하율) = 엔트리 수 / 버킷 수</code>  가 <code>0.75</code> 를 초과하면, 다음 엔트리를 추가할 때 재해싱 트리거를 발생시킨다고 한다. 이것을 채택하자.</li>
<li>재해싱시 배열 크기는 2배로 증가시킬려고 한다.</li>
</ul>
</li>
<li>기존 Map은 Map.get(key) 를 통해서, 값을 찾았다. 그런데 만약 <code>hash = [ [1, 2], [4] ]</code> 처럼 구조가 생겼다면, 어떻게 키 값과 비교하면서 값을 도출할 수 있는가?<ul>
<li><code>hash [ [ [key, value], [key value] ] , [ ] ... ]</code> 와 같이, key 값도 같이 저장해서 순회할 수 있도록 해보자.</li>
</ul>
</li>
<li>버킷의 개수가 충분하다고 해도, 특정 버킷에 값이 쏠릴 수 있을 것이다. 어떻게하면 버킷에 균등하게 값을 삽입할 수 있을 것인가?<ul>
<li>좋은 해시 함수 설계가 필요.</li>
<li>입력값이 조금만 달라도 완전히 다른 해시값이 나와야 함.</li>
<li>패턴 있는 값들이 들어와도 특정 영역에 편중되지 않아야 함.</li>
</ul>
</li>
<li>삭제가 많아서, 빈 버킷이 많이 생겼을 때도 재해싱이 필요할까?<ul>
<li>Java에서는 삭제 후 자동 축소는 하지 않는다고 한다. 전부 삭제시에는 <code>clear</code> 을 사용하고, 애초에 엄청난 양의 메모리가 남을 만큼 삭제를 하지 않기 때문인 이유도 있다고 한다.</li>
</ul>
</li>
<li>버킷의 크기가 2의 승수로 늘어나다보면 엄청나게 커질텐데, 최대 크기를 몇으로 제한해야할까?<ul>
<li>Java에서는 1 &lt;&lt; 30 으로 제한한다. (약 10억 7천만)</li>
</ul>
</li>
<li>JS의 Map에서 <code>size</code>는 메서드가 아니라 속성이지만, O(1) 시간에 접근가능하다. 어떻게 이게 가능할까?<ul>
<li><code>size</code> 를 클래스 멤버로 관리하여, <code>put</code>  을 할 때마다 1씩 늘려주면 된다.</li>
</ul>
</li>
</ul>
<h3 id="구현-설정-값">구현 설정 값</h3>
<p>JS의 <code>Map</code>  객체는 내부 구현 세부사항을 명확히 규정하지 않았기 때문에, <code>Java</code> 나 <code>Kotlin</code> 을 참고해서 작성했다.</p>
<ul>
<li>버킷 초기 값 : 16</li>
<li>재해싱 트리거 : <code>loadFactor(부하율) = 엔트리 수 / 버킷 수</code>  &gt; 0.75 초과시, 다음 엔트리 추가할 때.</li>
<li>재해싱시 배열 크기 증가분 : 2배</li>
<li>버킷 내부 구조 : <code>[key value]</code></li>
<li>내부 요소 삭제시 재해싱 여부: X</li>
<li>버킷 최대 크기 : 1 &lt;&lt; 30</li>
</ul>
<h3 id="메서드를-구현할-때-고려했던-사항들">메서드를 구현할 때 고려했던 사항들</h3>
<h3 id="hashcodestring-key">hashCode(String key)</h3>
<p>해시 값을 반환하는 함수이다.</p>
<p>자바에서의 <code>hashCode</code> 의 내부 구조를 살펴보니, 해시 계산에 소수를 쓴다는 것을 확인했다.</p>
<p>해시 계산에 소수를 쓰면 <code>충돌 확률이 줄고, 분포가 균등해진다</code> 는 이유라고 한다.</p>
<p>또한, Java의 창시자 중 한 명이 실험을 통해 <code>31</code>이 가장 적당하다고 판단했다고 한다.</p>
<p>그래서 나도 <code>31</code> 을 해시 계산에 사용했다.</p>
<p>그리고, Java의 <code>int</code> 는 32비트 정수인데, <code>hashCode()</code>  도 <code>int</code> 값을 반환하기 때문에, JS 에서의 64비트를 <code>32비트 int화</code>  할 필요가 있어서 <code>| 0</code>  연산도 추가했다가, 음수 값이 나오길래 <code>&gt;&gt;&gt;</code> 로 대체했다.</p>
<h3 id="getindexstring-key">getIndex(String key)</h3>
<p><code>hashCode</code> 를 통해 가져온 해시 값을 버킷 총 개수로 나눈 나머지를 통해 인덱스를 구하는 함수이다.</p>
<p>hash 값은 <code>&gt;&gt;&gt;</code> 로 인해 반드시 양수이고, <code>%</code> 연산만 잘 사용하면 되므로 구현이 어렵지 않았다.</p>
<h3 id="isneedrehashing">isNeedReHashing()</h3>
<p><code>loadFactor(부하율) = 엔트리 수 / 버킷 수</code>  </p>
<p><code>loadFactor</code> &gt; 0.75 라면, 리해싱이 필요하다는 의미로, <code>boolean</code> 을 반환하도록 했다.</p>
<h3 id="putstring-key-string-value">put(String key, String value)</h3>
<p>기존 key가 있다면 대체를하고 기존 key에 해당하는 버킷 마지막에 삽입하면 문제없이 구현이 가능할거라 생각했다.</p>
<p>JS에서 제공하는 <code>Map</code> 의 <code>Set</code> 메서드는, 반환 값으로 <code>this</code> 를 전달하여 메서드 체이닝을 지원하도록 하기 때문에, 나도 이와 같이 <code>this</code> 를 반환하도록 했다.</p>
<h3 id="removestring-key">remove(String key)</h3>
<p>JS에서 제공하는 <code>Map</code> 의 <code>delete</code> 메서드는 반환 값으로 <code>boolean</code> 을 제공하기 때문에, 여기서도 이 형식을 따랐다.</p>
<h3 id="clear">clear()</h3>
<p><code>clear</code> 을 구현하면서 버킷 크기를 초기로 돌릴까, 아니면 리해싱 이후의 크기를 유지할까 고민이 많았다. </p>
<p>JS나 Java에서는 리해싱  이후의 크기를 유지하고, <code>clear</code> 한 뒤 다시 많은 데이터를 넣을 가능성이 있기에 돌리지 않는 것이 기본 세팅이라고 한다.</p>
<h3 id="메서드-명세">메서드 명세</h3>
<table>
<thead>
<tr>
<th><strong>메서드 이름</strong></th>
<th>반환 값</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>#hashCode(String key)</td>
<td>해시값</td>
<td>Key를 바탕으로 해시값을 생성해 반환한다.</td>
</tr>
<tr>
<td>#getIndex(String key)</td>
<td>버킷 인덱스 값</td>
<td>해시 값을 바탕으로 한 버킷 인덱스 값을 반환한다.</td>
</tr>
<tr>
<td>#isNeedReHashing()</td>
<td>boolean</td>
<td>리해싱이 필요한지 판별해준다.</td>
</tr>
<tr>
<td>remove(String key)</td>
<td>boolean</td>
<td>key에 해당하는 요소를 삭제하고, 삭제 되었다면 <code>true</code> 를, 아니라면 <code>false</code>를 반환한다.</td>
</tr>
<tr>
<td>isEmpty()</td>
<td>boolean</td>
<td>맵이 비어있다면 <code>true</code> 를, 아니라면 <code>false</code> 를 반환한다.</td>
</tr>
<tr>
<td>put(String key, String value)</td>
<td>this</td>
<td>키-값을 추가한다. 메서드 체이닝을 지원하기 위해 <code>this</code> 를 반환한다.</td>
</tr>
<tr>
<td>clear</td>
<td>undefined</td>
<td>모든 버킷을 초기화한다. 리해싱 이후라면 리해싱 이후 크기로 초기화한다.</td>
</tr>
<tr>
<td>containsKey(String key)</td>
<td>boolean</td>
<td>해당 키가 존재하면 <code>true</code> 를, 아니라면 <code>false</code>  를 반환한다.</td>
</tr>
<tr>
<td>keys</td>
<td>전체 키 값 또는 undefined</td>
<td>전체 키 목록을 반환한다. 키에 해당하는 값이 없을 경우, undefined를 반환한다.</td>
</tr>
<tr>
<td>get(String key)</td>
<td>키와 매치되는 값</td>
<td>키와 매치되는 값을 반환한</td>
</tr>
<tr>
<td>size()</td>
<td>전체 아이템 개수</td>
<td>전체 아이템 개수를 리턴한다.</td>
</tr>
<tr>
<td>rehash()</td>
<td>undefined</td>
<td>버킷 사이즈를 두 배로 늘리고, 기존 요소들도 재할당한다.</td>
</tr>
<tr>
<td>#validateInitialValue(initialValue)</td>
<td>undefined</td>
<td>초기 값을 검증하고, 문제가 있다면 에러를 발생시킨다.</td>
</tr>
</tbody></table>
<h1 id="구현-코드">구현 코드</h1>
<pre><code class="language-js">class HashMap {
  #INITIAL_CAPACITY = 16;
  #MAX_BUCKET_SIZE = 1 &lt;&lt; 30;
  #MAX_LOADFACTOR = 0.75;
  #RESIZE_FACTOR = 2;
  #INPUT_ERROR_MESSAGE = &quot;[ERROR] 초기 값 형태는 [[&#39;key&#39;, &#39;value&#39;], [&#39;key&#39;, &#39;value&#39;]] 와 같은 형태여야 합니다.&quot;;

  #size;
  #bucketSize;
  #buckets;

  constructor(initialValue = []) {
    this.#bucketSize = this.#INITIAL_CAPACITY;
    this.#buckets = [];
    this.#size = 0;

    for (let i = 0; i &lt; this.#bucketSize; i++) {
      this.#buckets[i] = [];
    }

    this.#validateInitialValue(initialValue);

    for (const [key, value] of initialValue) {
      this.put(key, value);
    }
  }

  /**
   * 전체 맵을 초기화한다.
   */
  clear() {
    this.#buckets = [];
    for (let i = 0; i &lt; this.#bucketSize; i++) {
      this.#buckets[i] = [];
    }

    this.#size = 0;
  }

  /**
   * 해당 키가 존재하는지 여부에 따라 boolean 결과를 리턴한다.
   * @param {string} key
   */
  containsKey(key) {
    return this.get(key) !== undefined;
  }

  /**
   * 해당 키와 매치되는 값을 찾아 리턴한다.
   * @param {string} key
   */
  get(key) {
    const idx = this.#getIndex(key);
    const bucket = this.#buckets[idx];

    for (const pair of bucket) {
      if (pair[0] === key) return pair[1];
    }

    return undefined;
  }

  /**
   * 비어있는 맵인지 Bool 결과를 리턴한다.
   */
  isEmpty() {
    return this.size() === 0;
  }

  /**
   * 전체 키 목록을 [String] 배열로 리턴한다.
   */
  keys() {
    const keys = [];

    for (const bucket of this.#buckets) {
      for (const pair of bucket) {
        keys.push(pair[0]);
      }
    }

    return keys;
  }

  /**
   * 키-값을 추가한다.
   * @param {string} key
   * @param {string} value
   */
  put(key, value) {
    if (this.#isNeedReHashing()) {
      this.#rehash();
    }

    const idx = this.#getIndex(key);
    const bucket = this.#buckets[idx];

    for (const pair of bucket) {
      if (pair[0] === key) {
        pair[1] = value;
        return this;
      }
    }

    bucket.push([key, value]);
    this.#size++;

    return this;
  }

  /**
   * 해당 키에 있는 값을 삭제한다.
   * @param {string} key
   */
  remove(key) {
    const idx = this.#getIndex(key);
    const bucket = this.#buckets[idx];

    for (let i = 0; i &lt; bucket.length; i++) {
      if (bucket[i][0] === key) {
        bucket.splice(i, 1);
        this.#size--;
        return true;
      }
    }

    return false;
  }

  /**
   * 전체 아이템 개수를 리턴한다.
   */
  size() {
    return this.#size;
  }

  /**
   * 배열 크기를 2배로 증가하고 기존 요소를 재할당한다.
   */
  #rehash() {
    const oldBuckets = this.#buckets;
    const nextBucketSize = this.#RESIZE_FACTOR * this.#bucketSize;

    if (nextBucketSize &gt; this.#MAX_BUCKET_SIZE) return;
    this.#bucketSize = nextBucketSize;
    this.clear();

    for (const oldBucket of oldBuckets) {
      for (const [key, value] of oldBucket) {
        this.put(key, value);
      }
    }
  }

  /**
   * 해시 값을 도출한다.
   * @param {string} key
   */
  #hashCode(key) {
    let hash = 0;

    for (let i = 0; i &lt; key.length; i++) {
      hash = (31 * hash + key.charCodeAt(i)) &gt;&gt;&gt; 0;
    }

    return hash;
  }

  /**
   * 해시 값에 따라 버킷 인덱스를 반환한다.
   * @param {string} key
   * @returns
   */
  #getIndex(key) {
    const hash = this.#hashCode(key);
    return hash % this.#bucketSize;
  }

  #isNeedReHashing() {
    return (this.size() + 1) / this.#bucketSize &gt; this.#MAX_LOADFACTOR;
  }

  #validateInitialValue(initialValue) {
    if (initialValue.length &gt; 0) {
      if (typeof initialValue !== &quot;object&quot;) {
        throw new Error(this.#INPUT_ERROR_MESSAGE);
      }

      if (!initialValue.hasOwnProperty(&quot;length&quot;)) {
        throw new Error(this.#INPUT_ERROR_MESSAGE);
      }

      for (const init of initialValue) {
        if (init.length !== 2 || typeof init[0] !== &quot;string&quot; || typeof init[1] !== &quot;string&quot;)
          throw new Error(this.#INPUT_ERROR_MESSAGE);
      }
    }
  }

  printBucketSize() {
    console.log(this.#bucketSize);
  }
}

try {
  const hashMap = new HashMap();
  const hashMap2 = new HashMap([[&quot;홍길동&quot;, &quot;24&quot;]]);

  hashMap.printBucketSize(); // bucketSize : 16

  hashMap.put(&quot;1&quot;, &quot;val1&quot;);
  hashMap.put(&quot;2&quot;, &quot;val1&quot;);
  hashMap.put(&quot;3&quot;, &quot;val1&quot;);
  hashMap.put(&quot;4&quot;, &quot;val1&quot;);
  hashMap.put(&quot;5&quot;, &quot;val1&quot;);
  hashMap.put(&quot;6&quot;, &quot;val1&quot;);

  console.log(hashMap.keys()); // [1, 2, 3, 4, 5, 6]

  hashMap.remove(&quot;1&quot;);
  hashMap.remove(&quot;2&quot;);
  hashMap.remove(&quot;3&quot;);

  console.log(hashMap.keys()); // [4, 5, 6]

  hashMap.put(&quot;7&quot;, &quot;val1&quot;);
  hashMap.put(&quot;8&quot;, &quot;val1&quot;);
  hashMap.put(&quot;9&quot;, &quot;val1&quot;);
  hashMap.put(&quot;99&quot;, &quot;val1&quot;);
  hashMap.put(&quot;11&quot;, &quot;val1&quot;);
  hashMap.put(&quot;22&quot;, &quot;val1&quot;);
  hashMap.put(&quot;33&quot;, &quot;val1&quot;);
  hashMap.put(&quot;44&quot;, &quot;val1&quot;);
  hashMap.put(&quot;55&quot;, &quot;val1&quot;);
  hashMap.put(&quot;66&quot;, &quot;val1&quot;);
  hashMap.put(&quot;77&quot;, &quot;val1&quot;);
  hashMap.put(&quot;88&quot;, &quot;val1&quot;);

  hashMap.printBucketSize(); // bucketSize : 32

  console.log(hashMap.isEmpty()); // false

  hashMap.clear();

  console.log(hashMap.isEmpty()); // true
} catch (err) {
  console.log(err);
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[함수형 프로그래밍]]></title>
            <link>https://velog.io/@ldh-dodo/%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</link>
            <guid>https://velog.io/@ldh-dodo/%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</guid>
            <pubDate>Wed, 23 Jul 2025 06:47:38 GMT</pubDate>
            <description><![CDATA[<h1 id="순수-함수pure-function">순수 함수(Pure Function)</h1>
<h2 id="순수-함수란">순수 함수란?</h2>
<p><code>순수 함수</code> 는 아래 두 가지 조건을 만족하는 함수다.</p>
<ol>
<li>항상 동일한 입력에 대해 동일한 출력을 반환</li>
<li>함수 외부 상태에 영향을 주지 않고, 외부 상태에 의존하지 않음</li>
</ol>
<p>각각의 조건을 예시와 함께 살펴보자</p>
<h2 id="1-항상-동일한-입력에-대해-동일한-출력을-반환">1. 항상 동일한 입력에 대해 동일한 출력을 반환</h2>
<p>입력값이 같다면 언제 호출하든 결과가 같음을 보장한다는 의미이다.
예를 들어, <code>f(2) === 4</code> 였다면, 몇번이고 실행해도 무조건 <code>4</code>가 나와야 한다.</p>
<pre><code class="language-js">function add(a, b) {
  return a + b;
}</code></pre>
<h2 id="2-함수-외부-상태에-영향을-주지-않고-외부-상태에-의존하지-않음">2. 함수 외부 상태에 영향을 주지 않고, 외부 상태에 의존하지 않음</h2>
<p>외부의 변수를 변경하지 않고 (즉, 부작용이 생길 우려가 없음), 외부 변수나 I/O(파일, 네트워크 등)에 의존하지 않는다는 의미이다.</p>
<p>아래 예시는 외부 변수 <code>counter</code>을 함수 내에서 변경하므로 순수 함수가 아니다.</p>
<pre><code class="language-js">let counter = 0;
function increase() {
  counter++;
  return counter;
}</code></pre>
<p>이것을 순수 함수로 변경하면 다음과 같아진다.</p>
<pre><code class="language-js">function increase(counter) {
  return counter + 1;
}</code></pre>
<p>다음처럼, <code>taxRate</code> 라는 외부 상태에 의존하여, 해당 외부 상태가 변경되면 결과도 바뀌는 함수도 순수 함수가 아니다.</p>
<pre><code class="language-js">function calculatePrice(price) {
  return price + price * taxRate;
}</code></pre>
<p><code>taxRate</code> 도 매개변수로 주어져야 순수 함수라고 할 수 있다.</p>
<pre><code class="language-js">function calculatePrice(price, taxRate) {
  return price + price * taxRate;
}</code></pre>
<h2 id="순수-함수의-장점">순수 함수의 장점</h2>
<ul>
<li>1번 조건에 의해 항상 결과가 같아서 예측 가능하다는 장점이 있고, 연쇄 효과로 테스트와 디버깅이 쉬움</li>
<li>다른 곳에서도 쉽게 재사용이 가능</li>
<li>부작용이 없음을 보장하기 때문에 안전하게 병렬 실행하거나 결과를 캐시할 수 있음</li>
</ul>
<h1 id="불변성immutability">불변성(Immutability)</h1>
<p><code>불변성</code> 이란 <code>값이 한번 정해지면 절대로 바뀌지 않는 성질</code> 을 의미한다.</p>
<h2 id="불변성을-지키지-않은-예시">불변성을 지키지 않은 예시</h2>
<pre><code class="language-js">let user = { name: &quot;도현&quot; };
user.name = &quot;길동&quot;;</code></pre>
<p>이 예시는 기존 객체의 속성을 직접적으로 변경했기 때문에, 불변성을 깨는 방식이라고 할 수 있다.</p>
<h2 id="불변성을-지킨-예시">불변성을 지킨 예시</h2>
<pre><code class="language-js">let user = { name: &quot;도현&quot; };
let newUser = { ...user, name: &quot;길동&quot; };
user = newUser;</code></pre>
<p>위 코드에서는 기존 user 객체는 변경되지 않고,
전개 연산자(...user)를 통해 복사된 새로운 객체에 name 값을 변경해 newUser를 생성한다.</p>
<p>즉, 기존 객체는 그대로 유지되며,
새로운 객체를 만들어 값을 바꾸는 방식이기 때문에 불변성을 지키는 예시라고 할 수 있다.</p>
<p>단, <code>user = newUser</code>는 변수 <code>user</code>가 새로운 객체를 가리키도록 <strong>참조가 바뀐 것</strong>일 뿐,
기존 객체 자체는 <strong>변경되지 않았다는 점</strong>에서 불변성을 유지한 것이다.</p>
<h2 id="불변성을-지키지-않으면-어떤-문제가-발생할-수-있는가">불변성을 지키지 않으면 어떤 문제가 발생할 수 있는가</h2>
<h3 id="1-예측-불가능한-상태-변화">1. 예측 불가능한 상태 변화</h3>
<p>만약, 하나의 객체를 다른 곳에서 공유하고 있다고 해보자.
어느 한 곳에서 객체를 직접 수정했다면, 다른 쪽에도 영향이 갈 것이다.</p>
<p>결국 코드가 점점 스파게티처럼 꼬일 수 있다.</p>
<pre><code class="language-js">const user = { name: &quot;도현&quot; };
const copied = user; // 다른 파일에서 사용중이라고 가정

copied.name = &quot;길동&quot;;
console.log(user.name); // &quot;도현&quot; 을 예상했으나 &quot;길동&quot; 출력</code></pre>
<h3 id="2-react에서의-렌더링-문제">2. React에서의 렌더링 문제</h3>
<p>React는 상태 변화가 일어났는지를 판단하고, 만약 그렇다면 리렌더링을 한다.</p>
<p>그런데, 기존 객체를 직접 수정하게 되면, 객체의 참조가 변경되지 않아 변경으로 간주하지 않고, 리렌더링도 발생하지 않는다.</p>
<p>따라서, 상태 기반 UI 프레임워크(예: React)에서는 상태의 불변성을 지켜야
변경 사항을 정확하게 감지하고, 올바르게 리렌더링할 수 있다.</p>
<p>이처럼 불변성과 순수 함수는 예측 가능하고 안정적인 코드를 작성하는 데에 중요한 역할을 한다.</p>
<h1 id="참조-투명성referential-transparency">참조 투명성(Referential Transparency)</h1>
<p>어떤 표현식을 그 결과 값으로 바꿔도 프로그램의 동작이 변하지 않는 특성을 의미한다.</p>
<pre><code class="language-js">function add(a, b) {
  return a + b;
}

const x = add(2, 3); // 항상 5 반환</code></pre>
<p>위 예시에서 add(2, 3)을 언제든지 5로 바꿔도 프로그램 동작에 전혀 영향을 주지 않는다.</p>
<p>이럴 때 우리는 해당 표현식이 <strong>참조 투명성(referential transparency)</strong>을 만족한다고 말한다.</p>
<p>그리고 함수가 순수 함수라면 자연스럽게 <code>참조 투명성</code>을 갖게 된다.</p>
<p>참조 투명성 덕분에 코드를 읽고 추론하기가 훨씬 쉬워진다.</p>
<h1 id="함수형-표현-functional-expression">함수형 표현 (Functional Expression)</h1>
<p><code>함수형 표현</code>은 <strong>함수를 값처럼 다루는 방식</strong>을 말한다.</p>
<p>즉, 함수를 <strong>변수에 할당</strong>하거나, <strong>다른 함수의 인수</strong>로 전달하거나, <strong>함수의 반환값</strong>으로 사용할 수 있다는 뜻이다.</p>
<h2 id="함수형-표현의-특징">함수형 표현의 특징</h2>
<h3 id="1-일급-객체-first-class">1. 일급 객체 (First-class)</h3>
<p>함수가 숫자, 문자열 등 다른 데이터 타입과 <strong>동등하게 취급</strong>된다는 의미다.</p>
<p>즉, <strong>변수에 저장하고, 인자로 전달하며, 반환값으로 사용할 수 있다</strong>는 특성이 있다.</p>
<h3 id="2-익명-함수-anonymous-function">2. 익명 함수 (Anonymous Function)</h3>
<p>이름이 없는 함수를 정의할 수 있다.<br>주로 <strong>즉시 실행</strong>되거나 <strong>다른 함수의 인수로 전달</strong>될 때 유용하다.</p>
<h3 id="3-클로저-closure">3. 클로저 (Closure)</h3>
<p>자바스크립트의 중요한 특성 중 하나다.</p>
<p>함수가 <strong>선언 당시의 스코프(환경)</strong>를 기억하여, 함수가 해당 스코프 밖에서 호출되어도 <strong>그 내부 변수에 접근할 수 있게</strong> 해준다.</p>
<h3 id="4-고차-함수-higher-order-function">4. 고차 함수 (Higher-order Function)</h3>
<p>고차 함수는 <strong>함수를 인자로 받거나, 함수를 반환하는 함수</strong>를 말한다.</p>
<pre><code class="language-js">function greet(name) {
  return `Hello, ${name}!`;
}

function sayHello(fn, name) {
  console.log(fn(name));
}

sayHello(greet, &quot;도현&quot;); // Hello, 도현!</code></pre>
<hr>
<h2 id="함수형-표현의-종류">함수형 표현의 종류</h2>
<h3 id="1-함수-선언문-function-declaration">1. 함수 선언문 (Function Declaration)</h3>
<pre><code class="language-js">function add(a, b) {
  return a + b;
}</code></pre>
<p>함수 이름을 사용해 <strong>독립적으로 선언</strong>하는 방식이다.</p>
<h3 id="2-함수-표현식-function-expression">2. 함수 표현식 (Function Expression)</h3>
<pre><code class="language-js">const add = function (a, b) {
  return a + b;
};</code></pre>
<p>함수를 <strong>변수에 할당</strong>하는 방식으로, <strong>익명 함수 또는 이름 있는 함수</strong> 모두 가능하다.</p>
<h3 id="3-화살표-함수-표현식-arrow-function-expression">3. 화살표 함수 표현식 (Arrow Function Expression)</h3>
<pre><code class="language-js">const add3 = (a, b) =&gt; a + b;</code></pre>
<p>화살표 함수는 <code>function</code> 키워드 대신 <code>=&gt;</code>를 사용하는 <strong>간결한 함수 표현 방식</strong>이다.</p>
<p><code>this</code> 바인딩 방식이 일반 함수와 다르며, 콜백 함수나 짧은 로직에서 자주 사용된다.</p>
<h2 id="함수-선언문-vs-함수-표현식-vs-화살표-함수">함수 선언문 vs 함수 표현식 vs 화살표 함수</h2>
<p>세 가지 표현 방식의 가장 큰 차이점은 <strong>호이스팅(Hoisting)</strong>이다.</p>
<h3 id="함수-선언문">함수 선언문</h3>
<ul>
<li>자바스크립트 엔진이 <strong>컴파일(해석) 단계</strong>에서 함수 전체를 <strong>메모리에 등록</strong>한다.</li>
<li>따라서 <strong>선언 이전에 호출해도 에러 없이 실행</strong>된다.</li>
</ul>
<pre><code class="language-js">console.log(foo()); // 3

function foo() {
  return 3;
}</code></pre>
<h3 id="함수-표현식--화살표-함수">함수 표현식 / 화살표 함수</h3>
<ul>
<li><code>const</code>, <code>let</code>으로 선언된 변수는 <strong>호이스팅되지만 초기화 전까지 접근할 수 없다.</strong></li>
<li>초기화 이전에 접근하면 <strong>ReferenceError</strong>가 발생한다.</li>
<li>이를 <strong>TDZ(Temporal Dead Zone)</strong>라고 부른다.</li>
</ul>
<pre><code class="language-js">console.log(func1()); // ReferenceError

const func1 = function () {
  return 3;
};

const func2 = () =&gt; 3;
console.log(func2()); // 3</code></pre>
<p>함수 표현식은 <code>함수를 값으로 만들어 변수에 할당하는 코드</code>이기 때문에<br><strong>런타임에 이르러서야 함수가 생성된다.</strong></p>
<h3 id="함수-표현-방식별-호이스팅-및-호출-가능-여부">함수 표현 방식별 호이스팅 및 호출 가능 여부</h3>
<table>
<thead>
<tr>
<th>표현 방식</th>
<th>호이스팅 영향</th>
<th>선언 전에 호출 가능 여부</th>
</tr>
</thead>
<tbody><tr>
<td>함수 선언문</td>
<td>함수 <strong>전체가 호이스팅</strong>됨</td>
<td>✅ 가능</td>
</tr>
<tr>
<td>함수 표현식 (<code>const</code>)</td>
<td>변수 <strong>선언만 호이스팅</strong>, 함수는 런타임에 할당</td>
<td>❌ 불가능 (ReferenceError 발생)</td>
</tr>
<tr>
<td>화살표 함수 (<code>const</code>)</td>
<td>변수 <strong>선언만 호이스팅</strong>, 함수는 런타임에 할당</td>
<td>❌ 불가능 (ReferenceError 발생)</td>
</tr>
</tbody></table>
<h2 id="함수형-표현의-예시">함수형 표현의 예시</h2>
<h3 id="1-변수에-함수-할당">1. 변수에 함수 할당</h3>
<pre><code class="language-js">const greet = function (name) {
  return `안녕하세요 ${name} 님!`;
};

console.log(greet(&quot;도현&quot;));</code></pre>
<p><code>greet</code> 변수에 익명 함수가 할당되었고, 이 함수를 <code>greet()</code> 형태로 호출할 수 있다.</p>
<hr>
<h3 id="2-다른-함수의-인수로-전달-콜백-함수">2. 다른 함수의 인수로 전달 (콜백 함수)</h3>
<pre><code class="language-js">function operate(a, b, callback) {
  return callback(a, b);
}

const add = function (x, y) {
  return x + y;
};

const multiply = function (x, y) {
  return x * y;
};

console.log(operate(5, 3, add)); // 8
console.log(operate(5, 3, multiply)); // 15</code></pre>
<p><code>operate</code> 함수는 세 번째 인자로 <strong>함수(callback)</strong>를 받는다.<br>이처럼 <strong>다른 함수에 인수로 전달되는 함수</strong>를 <strong>콜백 함수</strong>라고 한다.</p>
<hr>
<h3 id="3-함수의-반환값으로-사용">3. 함수의 반환값으로 사용</h3>
<pre><code class="language-js">function createMultiplier(factor) {
  return function (number) {
    return number * factor;
  };
}

const multiplyByTwo = createMultiplier(2);
const multiplyByTen = createMultiplier(10);

console.log(multiplyByTwo(5)); // 10
console.log(multiplyByTen(5)); // 50</code></pre>
<p><code>createMultiplier</code> 함수는 <strong>다른 함수를 반환</strong>한다.<br>즉, 고차 함수(Higher-order Function)이다.</p>
<hr>
<h3 id="4-즉시-실행-함수-표현-iife-immediately-invoked-function-expression">4. 즉시 실행 함수 표현 (IIFE: Immediately Invoked Function Expression)</h3>
<pre><code class="language-js">(function () {
  const message = &quot;즉시 실행되는 함수입니다&quot;;
  console.log(message);
})();</code></pre>
<p>이름 없는 함수를 정의하자마자 즉시 호출하는 패턴이다.<br>주로 <strong>스코프를 격리</strong>하거나 <strong>초기화 코드를 실행</strong>할 때 사용된다.</p>
<hr>
<h2 id="함수형-표현의-장점">함수형 표현의 장점</h2>
<ul>
<li><p><strong>모듈성과 재사용성 증가</strong><br>→ 함수를 독립적인 단위로 만들어 다양한 곳에서 재사용할 수 있다.</p>
</li>
<li><p><strong>높은 유연성</strong><br>→ 함수를 동적으로 생성하고 전달할 수 있어 코드의 유연성이 높다.</p>
</li>
<li><p><strong>코드의 간결성</strong><br>→ 특히 화살표 함수와 함께 사용될 때 더 간결한 코드 작성이 가능하다.</p>
</li>
<li><p><strong>테스트 용이성</strong><br>→ 순수 함수와 함께 사용될 경우, 예측 가능한 동작으로 테스트가 쉬워진다.</p>
</li>
</ul>
<h1 id="함수형-프로그래밍-functional-programming">함수형 프로그래밍 (Functional Programming)</h1>
<p><strong>함수형 프로그래밍</strong>은 프로그래밍을 함수의 조합으로 구성하는 <strong>선언형(Declarative) 프로그래밍 패러다임</strong>이다.<br>상태(state)와 변경(mutation)을 최소화하고, <strong>순수 함수와 고차 함수</strong>를 중심으로 코드를 구성한다.</p>
<hr>
<h2 id="함수형-프로그래밍의-핵심-철학">함수형 프로그래밍의 핵심 철학</h2>
<ul>
<li><strong>무엇을 할 것인가(What)</strong>를 설명<br>→ 명령형(imperative)은 &quot;어떻게 할 것인가(How)&quot;를 설명</li>
<li>데이터와 동작을 <strong>함수로 추상화</strong></li>
<li>부작용(side effects)을 줄이고 <strong>예측 가능한 코드</strong> 작성</li>
</ul>
<hr>
<h2 id="주요-개념">주요 개념</h2>
<h3 id="1-순수-함수-pure-function">1. 순수 함수 (Pure Function)</h3>
<h3 id="2-불변성-immutability">2. 불변성 (Immutability)</h3>
<h3 id="3-고차-함수-higher-order-function">3. 고차 함수 (Higher-order Function)</h3>
<h3 id="4-일급-객체로서의-함수">4. 일급 객체로서의 함수</h3>
<h3 id="5-함수-합성-function-composition">5. 함수 합성 (Function Composition)</h3>
<p>작은 함수들을 조합해 <strong>더 복잡한 기능을 만드는 것</strong>.</p>
<pre><code class="language-js">const add2 = (x) =&gt; x + 2;
const mul3 = (x) =&gt; x * 3;

const compose = (f, g) =&gt; (x) =&gt; f(g(x));

const addThenMul = compose(mul3, add2);

console.log(addThenMul(4)); // (4 + 2) * 3 = 18</code></pre>
<hr>
<h3 id="6-커링-currying">6. 커링 (Currying)</h3>
<p><strong>하나의 인자만 받는 함수</strong>를 연속적으로 호출해 최종 결과를 만드는 기법.</p>
<pre><code class="language-js">function multiply(a) {
  return function (b) {
    return a * b;
  };
}

const double = multiply(2);
console.log(double(5)); // 10</code></pre>
<h2 id="함수형-프로그래밍의-장점">함수형 프로그래밍의 장점</h2>
<ul>
<li><strong>코드 예측 가능성 증가</strong> (순수 함수 덕분)</li>
<li><strong>테스트와 디버깅이 쉬움</strong></li>
<li><strong>재사용성과 조합성 향상</strong></li>
<li>상태 관리가 쉬워 <strong>버그 발생률 감소</strong></li>
<li>병렬 처리, 비동기 처리에 유리</li>
</ul>
<hr>
<h2 id="주의할-점--단점">주의할 점 / 단점</h2>
<ul>
<li>너무 과도하게 함수만 사용하면 가독성이 떨어질 수 있음</li>
<li>성능 면에서 부작용을 허용하는 방식보다 느릴 수 있음 (ex. 불변성 유지로 인한 복사 비용)</li>
</ul>
<hr>
<h2 id="자바스크립트와-함수형-프로그래밍">자바스크립트와 함수형 프로그래밍</h2>
<p>자바스크립트는 <strong>함수형 프로그래밍을 지원하는 멀티 패러다임 언어</strong>다.<br>다음과 같은 내장 함수로 함수형 스타일 코드를 쉽게 작성할 수 있다:</p>
<ul>
<li><code>map</code>, <code>filter</code>, <code>reduce</code></li>
<li><code>forEach</code>, <code>find</code>, <code>some</code>, <code>every</code></li>
<li>화살표 함수 <code>()=&gt;{}</code> (간결한 표현)</li>
<li><code>Object.freeze()</code> (불변 객체 생성)</li>
</ul>
<pre><code class="language-js">const numbers = [1, 2, 3, 4];

const doubled = numbers.map((n) =&gt; n * 2); // [2, 4, 6, 8]
const evens = numbers.filter((n) =&gt; n % 2 === 0); // [2, 4]
const sum = numbers.reduce((acc, cur) =&gt; acc + cur, 0); // 10</code></pre>
<hr>
<h2 id="정리">정리</h2>
<table>
<thead>
<tr>
<th>개념</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>순수 함수</td>
<td>동일한 입력 → 동일한 출력, 부작용 없음</td>
</tr>
<tr>
<td>불변성</td>
<td>상태 변경 없이 새로운 값 반환</td>
</tr>
<tr>
<td>고차 함수</td>
<td>함수를 인자/반환값으로 사용</td>
</tr>
<tr>
<td>함수 합성</td>
<td>여러 함수를 연결해 하나의 함수처럼 사용</td>
</tr>
<tr>
<td>커링</td>
<td>다중 인자를 나눠서 하나씩 받는 함수 구조화</td>
</tr>
</tbody></table>
<p>함수형 프로그래밍은 <strong>복잡한 상태 관리와 부작용을 줄여</strong> 더 <strong>안정적이고 유지보수하기 좋은 코드</strong>를 만드는 데 매우 유용하다고 할 수 있겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로세스 메모리 구조 모델]]></title>
            <link>https://velog.io/@ldh-dodo/%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B5%AC%EC%A1%B0-%EB%AA%A8%EB%8D%B8</link>
            <guid>https://velog.io/@ldh-dodo/%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B5%AC%EC%A1%B0-%EB%AA%A8%EB%8D%B8</guid>
            <pubDate>Thu, 17 Jul 2025 06:35:10 GMT</pubDate>
            <description><![CDATA[<h1 id="프로세스-메모리-구조-모델">프로세스 메모리 구조 모델</h1>
<img src = "https://s3.ap-northeast-2.amazonaws.com/lucas-image.codesquad.kr/16403278811501%2AfwkyPI8Gmzd0Q_XAGM5_eA.png"/>

<h2 id="스택-영역">스택 영역</h2>
<p>스택 영역은 함수 호출시 사용되는 메모리 공간으로, 함수 호출과 관련된 <code>지역변수</code> 와 <code>매개변수</code> <code>리턴 주소</code>
<code>함수 호출 컨텍스트</code> 가 저장된다.</p>
<p>스택은 프로세스 시작 시 운영체제가 고정된 크기로 한 번 할당한다. 따라서, 스택 크기는 고정되어 있고, 런타임에 변경할 수없
다.</p>
<p>운영체제가 프로세스 시작 시 정해진 크기(예: 1MB, 8MB 등)를 할당하고, 너무 많은 재귀 호출이나 큰 지역 변수 배열을 선언하
게 되면 <code>Stack Overflow</code> 가 발생하게 된다.</p>
<h2 id="힙-영역">힙 영역</h2>
<p>힙 영역은 프로그래머가 명시적으로 동적 메모리를 할당하는 공간으로, 주로 <code>new</code>, <code>malloc</code> 등과 같은 명령어를 통해 런타임중
에 메모리를 할당하고 해제한다.</p>
<p>이 영역은 크기가 고정되어 있지 않고, 프로세스가 사용할 수 있는 가용 메모리 공간 내에서 유동적으로 확장된다.</p>
<p><code>C / C++</code> 에서의 힙 메모리는 힙 메모리는 개발자가 직접 할당하고 해제해야 하며, 메모리 해제를 누락할 경우 메모리 누수
(Memory Leak)가 발생할 수 있다.</p>
<p>반면, <code>가비지 컬렉터(GC)</code>가 있는 <code>Java</code>, <code>JavaScript</code>, <code>Python</code> 등 은 개발자가 직접 메모리를 해제하지 않아도, 사용하지않
는 객체를 자동으로 감지하여 메모리를 해제해준다.</p>
<p>힙은 스택과 반대 방향(낮은 주소 → 높은 주소)으로 메모리를 확장하며, 스택과 힙이 서로 침범하지 않도록 주의해야 한다.</p>
<p>동적배열, 객체, 콜백 등과 같이 크기가 유동적이고 생명 주기가 함수 호출보다 긴 데이터들을 저장하는 데 적합하다.</p>
<h2 id="data-영역bss-gvar">Data 영역(BSS, GVAR)</h2>
<p>프로그램이 실행될 때 생성되고 프로그램이 종료되면 시스템에 반환되며, 전역변수, 정적변수, 배열, 구조체 등이 저장된다.</p>
<p><code>BSS 영역(Block Started by Symbol)</code>은 초기화되지 않은 전역 변수와 정적 변수가 저장되는 공간이다.. 메모리 시작 시 0으로초
기화된 상태로 할당된다.</p>
<p><code>GVAR(Global Variable) 영역</code>은 보통 전역 변수를 의미하는 용어로, 초기화된 전역 변수들이 저장되는 데이터(Data) 영역과 초
기화되지 않은 전역 변수들이 저장되는 BSS 영역 둘 다를 포함하는 더 넓은 개념이다.</p>
<p>결론적으로 <code>GVAR 영역</code> = 데이터 영역(초기화된 전역 변수) + <code>BSS 영역</code> 이고, <code>GVAR 영역</code> 안에 <code>BSS</code> 영역이 포함되어 있는
것이다.</p>
<p>두 영역 모두 실행 시점에 RAM에 할당되어 프로그램이 동작하는 동안 사용된다.</p>
<h2 id="text-영역">TEXT 영역</h2>
<p>프로그램이 실행될 때 생성되고, 프로그램이 종료되면 시스템에 반환되는 메모리 영역으로, 실행할 <strong>코드(명령어)</strong>가저장되는
공간이다.</p>
<p>TEXT 영역에는 컴파일된 기계어 코드가 위치하며, 보통 읽기 전용으로 설정되어 코드가 실행 중에 변경되지 않는다. 이 영역은함
수, 루프, 조건문 등 프로그램 로직을 수행하는 명령어들이 포함되어 있다.</p>
<p>TEXT 영역은 실행 파일에서 고정된 크기로 할당되며, 프로세스가 시작될 때 메모리에 적재된다.</p>
<p>스택 영역과 하는 일이 비슷해보이지만 다르다.</p>
<p>TEXT 영역은 프로그램이 <code>무엇을 할 것인가</code>에 대한 명령어(코드)를 저장하는 영역이라면,</p>
<p>STACK 영역은 <code>현재 무엇을 하고 있는가</code>에 관한 정보를 저장하는 영역이다.</p>
<p>JVM(Java Virtual Machine) 은 실행 코드를 직접 메모리에 기계어 형태로 저장하지 않고, 바이트코드 형태로 관리하며, 별도의
<strong>코드 영역(Code Area)</strong>에서 실행을 관리합니다.</p>
<p>Node.js, 브라우저(JavaScript 엔진) 등 자바스크립트 런타임 역시 전통적인 TEXT 영역 대신, 함수 객체와 스크립트 코드 등을위
한 별도의 코드 영역을 사용합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리눅스와 유닉스]]></title>
            <link>https://velog.io/@ldh-dodo/%EB%A6%AC%EB%88%85%EC%8A%A4%EC%99%80-%EC%9C%A0%EB%8B%89%EC%8A%A4</link>
            <guid>https://velog.io/@ldh-dodo/%EB%A6%AC%EB%88%85%EC%8A%A4%EC%99%80-%EC%9C%A0%EB%8B%89%EC%8A%A4</guid>
            <pubDate>Tue, 15 Jul 2025 04:20:18 GMT</pubDate>
            <description><![CDATA[<p>네이버 부스트캠프를 하느라 블로그 작성을 못하고 있는데, 부스트캠프를 하며 학습한 내용도 올리려고 한다.</p>
<h1 id="리눅스는-뭐고-유닉스는-뭔데">리눅스는 뭐고 유닉스는 뭔데?</h1>
<p>일단, 리눅스와 유닉스 둘 다 OS인 것은 알고 있다. 수도없이 들어본 <code>리눅스</code> 와 <code>유닉스</code> 지만, 둘의 차이점을 모르기에 알아
보고 정리하기로 했다.</p>
<h3 id="유닉스">유닉스</h3>
<p>유닉스는 <code>1969년</code> <code>AT&amp;T 벨 연구소</code> 에서 개발된 운영체제이다.</p>
<h4 id="유닉스-그래서-왜-만들어졌는데">유닉스.. 그래서, 왜 만들어졌는데?</h4>
<p>MIT, GE, Bell Labs가 함께 만든 <code>MULTICS</code> 라는 프로젝트가 있었다고 한다. 이 프로젝트는 다중 사용자(멀티 유저), 다중 작업(멀티태스킹)이 가능한 운영체제를 개발하는 것이 목표였다.</p>
<p>하지만, 구현이 복잡하고 매우 느린 탓에, 개발 시간이 지연되었고 결국 개발 비용이 너무 늘어났다.</p>
<p>그래서 <code>AT&amp;T</code>는 <code>MULTICS</code> 프로젝트에서 <code>1969년</code>에 탈퇴하게 되었고, 이 문제를 해결할만한 OS를 만들고자 하였다.</p>
<p>그래서 나온것이 <code>MULTICS</code>의 반대 개념인 <code>UNICS(Uniplexed Information and Computing System)</code> 이다. 이후 철자가 <code>UNIX</code> 로 바뀌었다.</p>
<p><code>MULTICS</code> 가 복잡한 다중 사용자 환경(멀티 유저, 엄격한 보안 권한 분리) 을 목표로 했다면, <code>UNIX</code>는 상대적으로 단순한 <code>멀티유저 자원</code>으로 시작했다.</p>
<p><code>멀티 유저 자원</code> 이라 함은 여러 사용자가 컴퓨터 시스템을 동시에 사용할 때, CPU, 메모리와 같은 자원을 나눠 쓰게 되는데 이런 자원을 관리하는 모든 시스템과 기법을 일컫는다.</p>
<p><code>MULTICS</code> 가 사용자마자 세밀한 권한을 부여하고, 접근을 제어한다면, <br> <code>UNIX</code> 는 사용자 계정과 비밀번호로 간단히 구분하는 방식을 채택했다고 보면된다.</p>
<p><code>AT&amp;T</code> 에서 개발한 초기 <code>UNIX</code> 는 어셈블리어로 작성되었는데, <br> <code>1972년</code> <code>Dennis Ritchie</code> 가 C언어를 개발한 이후,
<code>1973년</code> 유닉스가 C언어로 재작성되었다.</p>
<p>C언어로 재작성된 <code>UNIX</code>는 이식성이 높고 유지보수하기 쉬운 운영체제가 되었다.</p>
<p>유닉스의 철학은 <code>모든 것은 파일이다</code>, <code>작고 단순한 프로그램을 조합하라</code> 이며, 이후 <code>LINUX</code>, <code>BSD</code> <code>macOS</code> 에도 이어졌다고 한다.</p>
<h3 id="리눅스">리눅스</h3>
<p>리눅스는 1991년 <code>리누스 토르발스(Linus Torvalds)</code>가 개발한 운영체제 커널이다.</p>
<h4 id="리눅스-그래서-왜-만들어졌는데">리눅스.. 그래서, 왜 만들어졌는데?</h4>
<p>리눅스는 당시 상용 운영체제들이 비싸고, 폐쇄적이며, 제한적인 기능만 제공하는 문제를 해결하고자 만들어졌다. 
이는 당시 <code>UNIX</code>도 가지고 있던 문제점이었다.</p>
<p>특히, <code>MINIX</code>라는 교육용 유닉스 계열 운영체제에 영감을 받아, 더 자유롭고 확장 가능한 운영체제를 목표로 했다.</p>
<p><code>리누스 토르발스</code>는 개인 프로젝트로 시작했으나, 이후 전 세계 개발자들이 참여하는 오픈소스 프로젝트로 발전하였다.</p>
<p>리눅스 커널은 <code>유닉스(UNIX)</code>의 철학과 설계를 계승하면서도, 자유롭게 수정·배포할 수 있는 <code>GPL(General Public License)</code> 하에
공개되었다.</p>
<h4 id="리눅스의-주요-특징">리눅스의 주요 특징</h4>
<ul>
<li>오픈 소스(Open Source) : 누구나 코드를 보고, 수정하고, 배포할 수 있음</li>
<li>유닉스 철학 계승 </li>
<li>모듈화된 커널 구조
필요에 따라 기능을 추가하거나 제거할 수 있는 모듈 시스템 지원</li>
<li>다중 사용자 및 다중 작업 지원
멀티태스킹, 멀티유저 기능이 강력함</li>
<li>광범위한 하드웨어 지원
PC부터 서버, 임베디드 시스템까지 다양한 하드웨어에서 동작</li>
<li>풍부한 배포판과 커뮤니티
우분투, 페도라, 데비안, 레드햇 등 다양한 배포판이 존재</li>
<li>서버 운영체제, 클라우드 인프라, 모바일(안드로이드), IoT 등 다양한 분야에서 활용되며, 세계 IT 환경에 지대한 영향을 미쳤다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액트 상태와 지연 초기화]]></title>
            <link>https://velog.io/@ldh-dodo/%EC%83%81%ED%83%9C%EC%99%80-%EC%A7%80%EC%97%B0-%EC%B4%88%EA%B8%B0%ED%99%94</link>
            <guid>https://velog.io/@ldh-dodo/%EC%83%81%ED%83%9C%EC%99%80-%EC%A7%80%EC%97%B0-%EC%B4%88%EA%B8%B0%ED%99%94</guid>
            <pubDate>Mon, 09 Jun 2025 07:28:27 GMT</pubDate>
            <description><![CDATA[<p><code>지연 초기화(lazy initialization)</code> 란 초기 값을 상태 초기값으로 바로 계산하지 않고, 초기값을 반환하는 함수를 <code>useState</code>에 전달해서 React가 상태를 필요로 할 때만 초기값을 계산하도록 하는 방법이다.</p>
<p>다음 두 코드를 살펴보자.</p>
<pre><code class="language-javascript">export default function App() {
  const [count, setCount] = useState(0);

  return (
    &lt;div&gt;
      {count}
      &lt;button onClick={() =&gt; setCount((c) =&gt; c + 1)}&gt;Increment Count&lt;/button&gt;
    &lt;/div&gt;
  );
}</code></pre>
<pre><code class="language-js">const init = () =&gt; 0;

export default function App() {
  const [count, setCount] = useState(init);

  return (
    &lt;div&gt;
      {count}
      &lt;button onClick={() =&gt; setCount((c) =&gt; c + 1)}&gt;Increment Count&lt;/button&gt;
    &lt;/div&gt;
  );
}</code></pre>
<p>첫 번째 코드는 상태 초기 값을 바로 0으로 전달한다.
두 번째 코드는 <code>init</code> 이라는 함수를 전달하여 상태를 초기화한다.</p>
<p>지금처럼 0을 반환하는 경우 성능 차이가 없지만, 만약 <code>init</code> 함수 내에 무거운 계산이 포함되어 있다면?</p>
<p>그럴 때 <code>지연 초기화</code> 를사용하면 무거운 초기값 계산을 꼭 필요한 시점까지 미룰 수 있어 초기 화면 렌더링 성능 향상에 도움이 된다.</p>
<p><code>useReducer</code> 는 세 번째 매개변수로 지연 초기화에 필요한 함수를 받는데, 다음과 같다.</p>
<pre><code class="language-js">const [state, dispatch] = useReducer(reducer, 0, init);</code></pre>
<p><code>init()</code> 처럼 함수를 호출한 값을 전달하는 것이 아니라, 함수 자체를 전달해야 한다는걸 꼭 기억하자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[유틸리티 타입]]></title>
            <link>https://velog.io/@ldh-dodo/%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0-%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@ldh-dodo/%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0-%ED%83%80%EC%9E%85</guid>
            <pubDate>Wed, 28 May 2025 06:54:32 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>본 포스팅은 <a href="https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8/dashboard">한 입 크기로 잘라먹는 타입스크립트</a> 강의를 참고하여 작성했습니다. 문제가 될 소지가 있다면 댓글로 알려주세요. 바로 수정하겠습니다.</p>
</blockquote>
<h1 id="유틸리티-타입">유틸리티 타입</h1>
<p><code>유틸리티 타입</code> 이란, 제네릭, 맵드 타입, 조건부 타입 등의 타입 조작 기능을 이용해 자주 사용되는 타입변환 패턴을 미리 정의해 놓은 타입을 말한다.</p>
<p>자주 유틸리티 타입을 살펴보고, 구현 코드도 이해해보자.</p>
<h2 id="조건부-타입-기반">조건부 타입 기반</h2>
<h3 id="excludet-u">Exclude&lt;T, U&gt;</h3>
<p>T에서 U를 제거하는 타입이다.</p>
<h4 id="사용-예시">사용 예시</h4>
<pre><code class="language-typescript">type A = Exclude&lt;string | boolean, boolean&gt;;
// A : string</code></pre>
<p><code>string | boolean</code> 타입에서 <code>boolean</code> 을 제거하므로, A의 타입은 <code>string</code>이 된다.</p>
<h4 id="구현-코드">구현 코드</h4>
<pre><code class="language-typescript">type Exclude&lt;T, U&gt; = T extends U ? never : T;</code></pre>
<p>T가 U를 확장하는 타입이라면 <code>never</code> 을, 아니라면 <code>T</code> 타입을 반환하도록 해서, U 타입만 제거해줄 수 있다.</p>
<h3 id="extracttu">Extract&lt;T,U&gt;</h3>
<p>T에서 U를 추출하는 타입이다.</p>
<h4 id="사용-예시-1">사용 예시</h4>
<pre><code class="language-typescript">type B = Extract&lt;string | boolean, boolean&gt;;
// B : boolean</code></pre>
<p><code>string | boolean</code> 타입에서 <code>boolean</code> 을 추출하므로, B의 타입은 <code>boolean</code>이 된다.</p>
<h4 id="구현-코드-1">구현 코드</h4>
<pre><code class="language-typescript">type Extract&lt;T, U&gt; = T extends U ? T : never;</code></pre>
<p>T가 U를 확장하는 타입이라면 <code>T</code> 를, 아니라면 <code>never</code> 타입을 반환하도록 해서, T 타입만 추출해줄 수 있다.</p>
<h3 id="returntypet">ReturnType&lt;T&gt;</h3>
<p>함수의 반환값 타입을 추출하는 타입이다.</p>
<h4 id="사용-예시-2">사용 예시</h4>
<pre><code class="language-typescript">function funcC() {
  return &quot;hello&quot;;
}

type ReturnA = ReturnType&lt;typeof funcC&gt;;
// ReturnA : string</code></pre>
<p><code>funcC</code> 의 반환 값이 <code>string</code> 이고, 따라서 <code>ReturnA</code> 의 타입은 <code>string</code>이 된다.</p>
<h4 id="구현-코드-2">구현 코드</h4>
<pre><code class="language-typescript">type ReturnType&lt;
  T extends (...args: any) =&gt; any
&gt; = T extends (...args: any) =&gt; infer R
  ? R
  : never;</code></pre>
<p><code>T extends (...args: any) =&gt; any</code> 은 <code>T</code> 가 <code>어떤 함수 타입</code> 이어야 한다는 제약 조건을 명시한다.</p>
<p><code>T extends (...args: any) =&gt; infer R
  ? R
  : never;</code>  은 
  <code>T</code>가 함수 타입이라면 반환 타입을 <code>R</code>로 추론하고, 그렇지 않으면 <code>never</code>를 반환한다는 의미이다.</p>
<h2 id="맵드-타입-기반">맵드 타입 기반</h2>
<p>아래 <code>Post</code> 타입을 기반으로 이해해보자.</p>
<pre><code class="language-typescript">interface Post {
  title: string;
  tags: string[];
  content: string;
  thumbnailURL?: string;
}</code></pre>
<h3 id="partialt">Partial&lt;T&gt;</h3>
<p>특정 객체 타입의 모든 프로퍼티를 선택적 프로퍼티로 바꿔주는 타입이다.</p>
<h4 id="사용-예시-3">사용 예시</h4>
<pre><code class="language-typescript">const draft: Partial&lt;Post&gt; = {
  title: &quot;제목은 나중에&quot;,
  content: &quot;초안&quot;,
};</code></pre>
<p><code>Post</code> 의 <code>tags</code>는 필수 프로퍼티였지만, 선택적 프로퍼티로 바뀌었기 때문에 생략해도 무방한 모습이다.</p>
<h4 id="구현-코드-3">구현 코드</h4>
<pre><code class="language-typescript">type Partial&lt;T&gt; = {
  [key in keyof T]?: T[key];
}</code></pre>
<p>맵드 타입과 <code>?</code> 키워드를 이용해 구현한다.</p>
<h3 id="requiredt">Required&lt;T&gt;</h3>
<p>특정 객체 타입의 모든 프로퍼티를 필수 프로퍼티로 바꿔주는 타입이다.</p>
<h4 id="사용-예시-4">사용 예시</h4>
<pre><code class="language-typescript">const withThumbnailPost: Required&lt;Post&gt; = {
  title: &quot;아무 post&quot;,
  tags: [&quot;ts&quot;],
  content: &quot;&quot;,
  thumbnailURL: &quot;https..&quot;,
};</code></pre>
<p><code>thumbnailURL</code> 은 선택적 프로퍼티였지만, 해당 유틸리티 타입을 사용해 필수 프로퍼티가 되었다.</p>
<h4 id="구현-코드-4">구현 코드</h4>
<pre><code class="language-typescript">type Required&lt;T&gt; = {
  [key in keyof T]-?: T[key];
};</code></pre>
<p>선택적 프로퍼티를 나타내는<code>?</code> 을 <code>제거한다(-)</code> 라는 의미로, <code>-?</code> 를 사용해 구현한다.</p>
<h3 id="readonlyt">Readonly&lt;T&gt;</h3>
<p>특정 객체 타입의 모든 프로퍼티를 읽기 전용 프로퍼티로 만들어주는 타입이다.</p>
<h4 id="사용-예시-5">사용 예시</h4>
<pre><code class="language-typescript">const readonlyPost: Readonly&lt;Post&gt; = {
  title: &quot;보호된 게시글&quot;,
  tags: [],
  content: &quot;&quot;,
};

readonlyPost.content = &quot;&quot;; // error!!</code></pre>
<p>모든 속성이 읽기 전용으로 지정되어, 수정을 하려고 하면 에러가 발생한다.</p>
<h4 id="구현-코드-5">구현 코드</h4>
<pre><code class="language-typescript">type Readonly&lt;T&gt; = {
  readonly [key in keyof T]: T[key];
};</code></pre>
<p>맵드 타입과 <code>readonly</code> 키워드를 이용해 구현한다.</p>
<h3 id="pickt-k">Pick&lt;T, K&gt;</h3>
<p>객체 타입 <code>T</code>로부터 지정한 프로퍼티 <code>K</code> 만 추출하는 타입이다.</p>
<h4 id="사용-예시-6">사용 예시</h4>
<pre><code class="language-typescript">const legacyPost: Pick&lt;Post, &quot;title&quot; | &quot;content&quot;&gt; = {
  title: &quot;엣날 글&quot;,
  content: &quot;옛날 컨텐츠&quot;,
};</code></pre>
<p><code>Post</code> 타입에서 <code>title</code> 과 <code>content</code> 타입만 추출한 모습이다.</p>
<h4 id="구현-코드-6">구현 코드</h4>
<pre><code class="language-typescript">type Pick&lt;T, K extends keyof T&gt; = {
  [key in K]: T[key];
};</code></pre>
<p><code>K extends keyof T</code> : <code>K</code>는 반드시 <code>T</code>의 키의 일부여야 한다는 제약조건</p>
<h3 id="omitt-k">Omit&lt;T, K&gt;</h3>
<p>객체 타입 <code>T</code> 에서 지정한 프로퍼티 <code>K</code>를 제외하는 타입이다.</p>
<h4 id="사용-예시-7">사용 예시</h4>
<pre><code class="language-typescript">const noTitlePost: Omit&lt;Post, &quot;title&quot;&gt; = {
  content: &quot;&quot;,
  tags: [],
  thumbnailURL: &quot;&quot;,
};</code></pre>
<p><code>Post</code> 타입에서 <code>title</code> 속성만 제외한 모습이다.</p>
<h4 id="구현-코드-7">구현 코드</h4>
<pre><code class="language-typescript">type Omit&lt;T, K extends keyof T&gt; = Pick&lt;T, Exclude&lt;keyof T, K&gt;&gt;;</code></pre>
<p><code>K extends keyof T</code> : <code>K</code>는 반드시 <code>T</code>의 키의 일부여야 한다는 제약조건
<code>Exclude&lt;keyof T, K&gt;</code> : <code>T</code>의 키 중에서 <code>K</code>를 제외한 키만 추출
<code>Pick&lt;T, Exclude&lt;keyof T, K&gt;&gt;</code> : <code>T</code> 에서 <code>K</code>를 제외한 키들만 골라 새로운 타입으로 구성</p>
<h3 id="recordk-v">Record&lt;K, V&gt;</h3>
<p>동일한 패턴을 갖는 객체 타입을 정의하는 타입이다.</p>
<h4 id="사용-예시-8">사용 예시</h4>
<pre><code class="language-typescript">type Thumbnail = Record&lt;&quot;large&quot; | &quot;medium&quot; | &quot;small&quot;, { url: string }&gt;;
// type ThumbnailLegacy = {
//   large: {
//     url: string;
//   };
//   medium: {
//     url: string;
//   };
//   small: {
//     url: string;
//   };
// };</code></pre>
<h4 id="구현-코드-8">구현 코드</h4>
<pre><code class="language-typescript">type Record&lt;K extends keyof any, V&gt; = {
  [key in K]: V;
};</code></pre>
<p><code>K extends keyof any</code> : K가 어떤 객체의 키 타입임을 명시한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입 조작]]></title>
            <link>https://velog.io/@ldh-dodo/%ED%83%80%EC%9E%85-%EC%A1%B0%EC%9E%91</link>
            <guid>https://velog.io/@ldh-dodo/%ED%83%80%EC%9E%85-%EC%A1%B0%EC%9E%91</guid>
            <pubDate>Wed, 28 May 2025 05:30:34 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>본 포스팅은 <a href="https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8/dashboard">한 입 크기로 잘라먹는 타입스크립트</a> 강의를 참고하여 작성했습니다. 문제가 될 소지가 있다면 댓글로 알려주세요. 바로 수정하겠습니다.
</blockquote>
<p>타입을 조작하는 도구에는 <code>제네릭</code> <code>인덱스드 액세스 타입</code> <code>keyof 연산자</code> <code>Mapped(맵드) 타입</code> <code>템플릿 리터럴 타입</code> <code>조건부 타입</code> 이 있다.</p>
<p><code>타입을 조작</code> 한다는 것은 기존 타입을 기반으로 새로운 타입을 만들거나, 타입에 대한 어떤 조건이나 연산을 적용해 타입의 형태를 바꾸는 것을 말한다.</p>
<p>하나씩 살펴보자.</p>
<h1 id="타입-조작-방법">타입 조작 방법</h1>
<h2 id="제네릭">제네릭</h2>
<p>함수, 클래스, 인터페이스 등을 선언할 때, 타입을 고정하지 않고, 파라미터처럼 받아서 사용할 수 있게 하는 문법이다.
제네릭이 타입 조작 방법에 해당하지 않는다고 생각할 수도 있는데, 문맥에 따라 타입이 동적으로 결정되기 때문에 타입 조작에 해당한다.</p>
<h3 id="제네릭-클래스-예시">제네릭 클래스 예시</h3>
<pre><code class="language-typescript">class List&lt;T&gt; {
  constructor(private list: T[]) {}

  push(data: T) {
    this.list.push(data);
  }

  pop() {
    return this.list.pop();
  }

  print() {
    console.log(this.list);
  }
}

const numberList = new List([1, 2, 3]);
numberList.pop();
numberList.push(4);
numberList.print();

numberList.push(&#39;2&#39;); // error!!

const stringList = new List([&quot;1&quot;, &quot;2&quot;, &quot;3&quot;]);
stringList.pop();</code></pre>
<h2 id="인덱스드-액세스-타입">인덱스드 액세스 타입</h2>
<p>객체, 배열, 튜플 타입에서 특정 프로퍼티 혹은 요소의 타입을 추출하는 타입이다.
복합하고 큰 타입에서, 필요한 작은 타입으로 추출하여 쓸 수 있다.</p>
<p>예를 들어, 다음과 같은 코드가 있다고 해보자.</p>
<pre><code class="language-typescript">interface Post {
  title: string;
  content: string;
  author: {
    id: number;
    name: string;
  };
}

const post: Post = {
  title: &quot;게시글 제목&quot;,
  content: &quot;게시글 본문&quot;,
  author: {
    id: 1,
    name: &quot;홍길동&quot;,
  },
};

function printAuthorInfo(author: { id: number; name: string }) {
  console.log(`${author.name} - ${author.id}`);
}</code></pre>
<p><code>printAuthorInfo</code> 함수에서 매개변수 <code>author</code> 에 대한 타입을 <code>{id : number; name: string}</code> 의 형태로 정의했다.
그런데 만약, <code>author</code> 의 내부 구조가 바뀐다면, 일일이 수정해줘야 하는 상황이 발생한다.</p>
<p>이런 상황에서, <code>인덱스드 액세스 타입</code>을 통해 해결할 수 있다.</p>
<pre><code class="language-typescript">function printAuthorInfo(author: Post[&#39;author&#39;]) {
  console.log(`${author.name} - ${author.id}`);
}</code></pre>
<p><code>Post[&#39;author&#39;]</code> 과 같이 타입을 지정해주면, <code>Post</code> 라는 타입의 <code>author</code> 속성의 타입이 추출된다.
그래서 이후 <code>author</code> 구조가 변경되어도, 알아서 타입이 추출되기 때문에 따로 수정해줄 필요가 없게된다.</p>
<p>여기서 주의할 점은,<code>Post[&#39;author&#39;]에서</code> <code>&#39;author&#39;</code> 은 리터럴 타입이어야 하며, 변수 값은 사용할 수 없다는 것이다. 
즉, 아래와 같이 코드를 작성하면 오류가 발생한다.</p>
<pre><code class="language-typescript">const author = &#39;author&#39;
type Author = Post[author]; // error!!</code></pre>
<p>인덱스 부분에는 반드시 리터럴 타입이 와야 하며, 일반 변수를 사용하면 오류가 발생하니 주의하자.</p>
<h2 id="keyof-연산자">keyof 연산자</h2>
<p>프로퍼티의 키들을 모두 스트링 리터럴 유니온 타입으로 추출하는 연산자이다.</p>
<p>아래 코드를 보자.</p>
<pre><code class="language-typescript">interface Person {
  name: string;
  age: number;
}

const person: Person = {
  name: &quot;홍길동&quot;,
  age: 26,
};

function getPropertyKey(person: Person, key: &quot;name&quot; | &quot;age&quot;) {
   return person[key];
}

getPropertyKey(person, &quot;name&quot;); // 홍길동</code></pre>
<p><code>getPropertyKey()</code> 함수는 <code>Person</code> 타입 객체와 해당 객체의 키중 하나를 받아, 해당 키에 해당하는 값을 반환하는 함수이다.</p>
<p>만약, <code>Person</code> 타입에 키가 많이 추가 되었다면? 혹은, 기존 타입이 수정되어야 한다면?
매개변수에 일일이 추가해주거나 수정해줘야 하는 번거로움이 생길 것이다.</p>
<p>이럴 때, <code>keyof</code> 연산자를 사용하면 해결할 수 있다.</p>
<pre><code class="language-typescript">function getPropertyKey(person: Person, key: keyof Person) {
   return person[key];
}</code></pre>
<p>두 번째 함수는 <code>keyof</code> 연산자를 사용해 타입 변경에 유연하게 대응할 수 있다.</p>
<h2 id="mapped맵드-타입">Mapped(맵드) 타입</h2>
<p>기존의 객체 타입으로부터 새로운 객체 타입을 만드는 타입이다.</p>
<p>다음 코드를 보자.</p>
<pre><code class="language-typescript">interface User {
  id: number;
  name: string;
  age: number;
}

interface PartialUser {
  id?: number;
  name?: string;
  age?: number;
}

// 한명의 유저 정보를 업데이트
function updateUser(user: PartialUser) {
  // update
}

updateUser({
  // id :1,
  // name: :&#39;이도현&#39;
  age: 25,
});
</code></pre>
<p>코드를 살펴보면, <code>User</code> 타입이 존재하고, 해당 타입의 객체를 <code>update</code> 할 수 있는 함수가 존재한다. 
그런데, <code>update</code>는 부분적으로 이루어질 수 있기에, 기존 <code>User</code> 의 속성이 전부 <code>선택적 프로퍼티</code> 인 <code>PartialUser</code>을 새로 정의했다.</p>
<p><code>User</code> 타입의 길이가 길어질 수록, 두 배로 코드가 늘어나기에 비효율적으로 보인다.
이를 <code>Mapped 타입</code> 을 통해 해결할 수 있다.</p>
<pre><code class="language-typescript">type PartialUser = {
  [key in &quot;id&quot; | &quot;name&quot; | &quot;age&quot;]?: User[key];
}</code></pre>
<p>코드가 생소할 수 있는데 풀이하면 다음과 같다.
<code>[key in &quot;id&quot; | &quot;name&quot; | &quot;age&quot;]?</code> : key가 <code>&#39;id&#39;</code>, <code>&#39;name&#39;</code>, <code>&#39;age&#39;</code> 일 수 있고, 선택적 프로퍼티임.
<code>User[key]</code> : 해당 key에 대응하는 속성의 타입을 의미한다. 
예를 들어 <code>key</code> 가 &#39;id&#39; 라면, <code>User[&#39;id&#39;]</code> 인 <code>number</code>가 된다.</p>
<p>즉, <code>key</code> 에 어떤 값이 들어갈 수 있는지를 표현하고, 그것을 <code>User[key]</code> 를 통해 <code>value</code>를 정의한 것이다.</p>
<p>앞서서 <code>&quot;id&quot; | &quot;name&quot; | &quot;age&quot;</code> 와 같은 스트링 리터럴 유니온 타입은 <code>keyof</code> 를 통해 대체 가능하다고 했다. 만약, 모든 <code>key</code> 에 대해서 새로운 타입을 재정의할 것이라면 아래와 같이 코드를 작성하면 더욱 간단하게 표현 가능하다.</p>
<pre><code class="language-typescript">type PartialUser = {
     [key in keyof User]?: User[key]; 
}</code></pre>
<h2 id="템플릿-리터럴-타입">템플릿 리터럴 타입</h2>
<p>스트링 리터럴 타입을 기반으로, 정해진 패턴의 문자열만 포함하는 타입이다.</p>
<pre><code class="language-typescript">type Color = &quot;red&quot; | &quot;black&quot; | &quot;green&quot;;
type Animal = &quot;dog&quot; | &quot;cat&quot; | &quot;chicken&quot;;

// type ColoredAnimal = &#39;red-dog&#39; | &#39;red-cat&#39; | ...</code></pre>
<p>두 타입이 있고, 두 타입을 합친 패턴의 새로운 타입인 <code>ColoredAnimal</code> 을 만들고자 하는 상황이라고 가정하자.</p>
<p>그럼 아래와 같이 작성 가능하다.</p>
<pre><code class="language-typescript">type ColoredAnimal = `${Color}-${Animal}`;
// -&gt; type ColoredAnimal = &quot;red-dog&quot; | &quot;red-cat&quot; | &quot;red-chicken&quot; | &quot;black-dog&quot; | &quot;black-cat&quot; | &quot;black-chicken&quot; | &quot;green-dog&quot; | &quot;green-cat&quot; | &quot;green-chicken&quot;</code></pre>
<h2 id="조건부-타입">조건부 타입</h2>
<p><code>조건부 타입</code> 은 조건에 따라 타입을 다르게 지정할 수 있는 문법이다.</p>
<pre><code class="language-typescript">type A = number extends string ? string : number;</code></pre>
<p>해당 예제는 <code>number</code> 타입이 <code>string</code> 타입을 확장한다면 A의 타입을 <code>string</code> 으로, 그렇지 않다면 <code>number</code> 타입으로 설정하라는 의미이다.</p>
<p>제네릭과 함께 사용한다면, 더 유용하게 사용할 수 있다.</p>
<h3 id="제네릭과-조건부-타입">제네릭과 조건부 타입</h3>
<p>만약, 매개변수로 <code>text</code> 를 입력받고, 해당 값이 문자열이면 공백을 제거하여 반환하는 함수를 만든다고 가정하자.</p>
<pre><code class="language-typescript">function removeSpaces&lt;T&gt;(text: T): T extends string ? string : undefined {
  if(typeof text === &quot;string&quot;) {
    return text.replaceAll(&quot; &quot;, &quot;&quot;);
  } else {
    return undefined;
  }
}</code></pre>
<p>이 코드는 제네릭을 통해, <code>T</code>가 <code>string</code> 타입이라면 반환 타입도 <code>string</code> 으로, 그렇지 않다면 <code>undefined</code>로 지정하려고 했다.
하지만, 함수 구현부에서는 조건부 타입의 결과를 컴파일 타임에 알 수 없기 때문에 타입 오류가 발생한다.</p>
<p>각 반환문에서 <code>as any</code> 타입 단언을 통해 해결할 수도 있겠지만, 타입 안전성을 해칠 우려가 있다.</p>
<p>이럴 때 <code>오버로드 시그니처</code> 를 사용하면, 구현부에서도 조건부 타입의 결과를 추론할 수 있게 할 수 있어 유용하다.</p>
<pre><code class="language-typescript">function removeSpaces&lt;T&gt;(text: T): T extends string ? string : undefined;
function removeSpaces(text: any) {
  if (typeof text === &quot;string&quot;) {
    return text.replaceAll(&quot; &quot;, &quot;&quot;);
  } else {
    return undefined;
  }
}</code></pre>
<h3 id="분산적인-조건부-타입">분산적인 조건부 타입</h3>
<p>조건부 타입을 유니온 타입과 함께 사용했을 때, 각 유니온 멤버에 분산적으로 적용되게 하는 문법이다.</p>
<pre><code class="language-typescript">type StringNumberSwitch&lt;T&gt; = T extends number ? string : number;

let c: StringNumberSwitch&lt;number | string&gt;; // c : number | string;</code></pre>
<p>얼핏보면 <code>number | string</code> 타입이 <code>number</code> 타입을 extends 하지 않기 때문에, c의 타입은 <code>number</code> 타입이라고 생각할 수 있다.</p>
<p>조건부 타입을 유니온 타입과 함께 사용하면 유니온 타입 그 자체로 타입 변수에 할당되지 않고, 각각 분리되어 할당되게 된다.</p>
<p>다시 말해 아래처럼 동작하는 것이다.</p>
<pre><code class="language-typescript">let c : StringNumberSwitch&lt;number&gt; | StringNumberSwitch&lt;string&gt;</code></pre>
<p>다른 예시를 하나 더 살펴보자.</p>
<pre><code class="language-typescript">let d: StringNumberSwitch&lt;boolean | number | string&gt;;</code></pre>
<p>해당 예시를 뜯어서 살펴보면 다음과 같다.</p>
<p><strong>1단계</strong></p>
<pre><code class="language-typescript">StringNumberSwitch&lt;boolean&gt; |
StringNumberSwitch&lt;number&gt;  |
StringNumberSwitch&lt;string&gt; </code></pre>
<p><strong>2단계</strong></p>
<pre><code class="language-typescript">number |
string |
number</code></pre>
<p><strong>결과</strong></p>
<pre><code class="language-typescript">number | string
// never 타입은 공집합 타입이기 때문에 유니온 타입에서 사라짐</code></pre>
<h3 id="조건부-타입과-infer">조건부 타입과 infer</h3>
<p><code>infer</code> 는 <code>inference(추론하다)</code> 의 약어로, 조건부 타입 내에서 타입을 임시 변수로 추론할 때 사용합니다.</p>
<p>만약, 특정 함수에서 반환하는 타입을 추론해야 하는 상황이 있다고 가정해보자.</p>
<pre><code class="language-typescript">type FuncA = () =&gt; string;
type FuncB = () =&gt; number;

type ReturnType&lt;T&gt; = T extends () =&gt; infer R ? R : never;

type C = ReturnType&lt;FuncA&gt;; // C : string
type D = ReturnType&lt;FuncB&gt;; // D : number</code></pre>
<p><code>infer R</code>은 조건이 참일 경우, 함수 반환 타입을 R로 추론해 사용할 수 있도록 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입 좁히기]]></title>
            <link>https://velog.io/@ldh-dodo/%ED%83%80%EC%9E%85-%EC%A2%81%ED%9E%88%EA%B8%B0</link>
            <guid>https://velog.io/@ldh-dodo/%ED%83%80%EC%9E%85-%EC%A2%81%ED%9E%88%EA%B8%B0</guid>
            <pubDate>Wed, 21 May 2025 14:16:19 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>본 포스팅은 <a href="https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8/dashboard">한 입 크기로 잘라먹는 타입스크립트</a> 강의를 참고하여 작성했습니다. 문제가 될 소지가 있다면 댓글로 알려주세요. 바로 수정하겠습니다.
</blockquote>
<h1 id="타입-좁히기">타입 좁히기</h1>
<p><code>타입 좁히기</code> 란 조건문 등의 분기 로직을 통해 복수의 타입이 가능한 값을 구체적인 단일 타입으로 좁히는 것을 말한다.</p>
<h2 id="언제-필요한가">언제 필요한가?</h2>
<p>함수의 매개변수가 <code>union</code> 타입인데, 분기별로 매개변수의 메서드를 사용해야 한다거나, 매개변수의 속성 값을 사용할 때와 같은 상황에 필요하다.</p>
<pre><code class="language-typescript">function func(value: number | string) {
  value.toFixed(); // error!!
}</code></pre>
<p>위와 같은 상황에서, value가 어떤 타입인지 정확히 좁혀지지 않았기 때문에, <code>number</code> 타입 메서드인 <code>toFixed()</code> 를 사용하면 에러가 나는 것이다.</p>
<h2 id="어떻게-사용하나">어떻게 사용하나?</h2>
<p>타입스크립트가 변수의 타입을 좁히도록 도와주는 도구를 <code>타입 가드</code> 라고 한다.
이 <code>타입 가드</code> 를 적절히 사용하여 타입을 좁힐 수 있다.</p>
<h3 id="typeof">typeof</h3>
<p><code>typeof</code> 를 통해 해당 타입이 어떤 타입인지 알아낼 수 있다.</p>
<pre><code class="language-typescript">function func(value: number | string) {
    if(typeof value === &quot;number&quot;) {
      console.log(value.toFixed());
      // value : number
    } else if(typeof value === &quot;string&quot;) {
      console.log(value.toUpperCase());
      // value : string
    }
}</code></pre>
<p>이렇게 <code>if</code> 문과 <code>typeof</code> 를 이용해 분기를 나눈다면, 해당 블록 내에서 각각 좁혀진 타입으로 인식하게 된다.
이렇게 되면 각 타입의 메서드를 사용해도 문제가 없다.</p>
<p>본문과는 관계 없는 이야기지만, 타입스크립트에서는 <code>typeof</code>를 타입 정의에 사용하면 기존과 다른 동작을 수행한다.</p>
<pre><code class="language-typescript">const person = {
  name: &quot;홍길동&quot;,
  age: 26,
};

type Person = typeof Person;

// 다음처럼 타입이 추출된다.
// type Person = {
//    name: string;
//    age: number;
// }</code></pre>
<p>이렇게 사용하면, <code>person</code> 객체의 타입을 뽑아내서 새로운 타입으로 정의할 수 있다.</p>
<h3 id="instanceof">instanceof</h3>
<p><code>instanceof</code> 는 왼쪽 값이 오른쪽 클래스(생성자 함수)의 인스턴스인지 확인하고 맞다면 <code>true</code>를, 아니라면 <code>false</code>를 반환한다.</p>
<pre><code class="language-typescript">type Person = {
  name: string;
  age: number;
};

function func(value: Date | null | Person) {
    if(value instanceof Date) {
      // value : Date
    } //else if(value instanceof Person) {
      // error!!
    //}
}</code></pre>
<p>여기서 주의할 점은, 사용자 정의 타입인 Person은 클래스가 아닌 타입이기 때문에, 주석 처리된 부분처럼 사용할 수 없다는 점이다.
<code>Date</code> 는 JS 내장 클래스이기 때문에 가능하다.</p>
<p>그렇다면, 사용자 정의 타입은 어떤 식으로 확인할 수 있을까?</p>
<h3 id="in">in</h3>
<p><code>in</code> 키워드는 지정한 속성의 이름이 객체 자체 또는 그 객체의 프로토타입 체인에 존재하면 <code>true</code>를, 그렇지 않다면 <code>false</code>를 반환하게 한다.</p>
<pre><code class="language-typescript">type Person = {
  name: string;
  age: number;
};

function func(value: number | Person) {
  if (typeof value === &quot;number&quot;) {
    value.toFixed();
  } 
  else if (&quot;age&quot; in value) {
    // value : Person
  }
}</code></pre>
<p><code>age</code> 속성이 <code>Person</code> 타입에 존재하기 때문에, 타입이 <code>Person</code> 으로 좁혀진다.</p>
<p>하지만, 실제로 코드를 처음 본 상황이라면 저 분기의 <code>value</code>의 타입이 <code>Person</code> 임을 단번에 인지하기란 쉽지 않다.</p>
<p><code>in</code>을 사용하면 직관성이 떨어지는데, 이를 해결할 방법으로 <code>서로소 유니온 타입</code> 과 <code>사용자 정의 타입 가드</code> 가 해결책이 될 수 있다.</p>
<h3 id="서로소-유니온-타입">서로소 유니온 타입</h3>
<p><code>서로소 유니온 타입</code> 이란, 교집합이 없는 타입들로만 만든 유니온 타입을 말한다.</p>
<p>앞서 살펴본 <code>in</code> 키워드를 통해 타입 좁히기를 진행한 예시를 보자.</p>
<pre><code class="language-typescript">type Admin = {
  name: string;
  kickCount: number;
};
type Member = {
  name: string;
  point: number;
};
type Guest = {
  name: string;
  visitCount: number;
};

type User = Admin | Member | Guest;

function login(user: User) {
  if (&quot;kickCount&quot; in user) {
    // user : Admin
  } else if (&quot;point&quot; in user) {
    // user : Member
  } else if (&quot;visitCount&quot; in user) {
    // user : Guest
  }
}</code></pre>
<p><code>login</code> 기능을 <code>Admin</code> <code>Member</code> <code>Guest</code> 타입에 따라 설정할 수 있도록 만들어두었다.
하지만, 직관성이 떨어져 <code>user</code> 의 타입을 추론하는데 시간이 걸린다.</p>
<p>다음은 서로소 유니온 타입을 사용한 예시이다.</p>
<pre><code class="language-typescript">type Admin = {
  tag: &quot;ADMIN&quot;;
  name: string;
  kickCount: number;
};

type Member = {
  tag: &quot;MEMBER&quot;;
  name: string;
  point: number;
};

type Guest = {
  tag: &quot;GUEST&quot;;
  name: string;
  visitCount: number;
};

type User = Admin | Member | Guest;

function login(user: User) {
  switch (user.tag) {
    case &quot;ADMIN&quot;: {
      // user : Admin
    }
    case &quot;MEMBER&quot;: {
      // user : Member
    }
    case &quot;GUEST&quot;: {
      // user : Guest
    }
  }
}</code></pre>
<p><code>tag</code> 라는 속성이 각 타입에 추가되었는데, 추가된 <code>tag</code>의 타입은 <code>&quot;ADMIN&quot;</code> <code>&quot;MEMBER&quot;</code> <code>&quot;GUEST&quot;</code> 로, 문자열 리터럴 타입이다.</p>
<p>세 타입에 각기 다른 타입이 추가되어, <code>User</code> 는 서로소 유니온 타입이 되었고 이를 이용해 <code>swtich</code> 문으로 직관성을 향상시키면서 타입 좁히기를 할 수 있게 되었다.</p>
<p>그런데 만약, 서로소 유니온 타입을 만들 수 없는 상황에서 타입 좁히기를 해야한다면, <code>사용자 정의 타입 가드</code> 를 사용할 수 있다.</p>
<h3 id="사용자-정의-타입-가드">사용자 정의 타입 가드</h3>
<pre><code class="language-typescript">type Dog =  {
  name: string;
  isBark: boolean;
}

type Cat = {
  name: string;
  isScratch: boolean;
}

type Animal = Dog | Cat;

function isDog(animal: Animal) : animal is Dog { // 함수가 참이면, 매개변수가 해당 타입임을 명시한다.
  return (animal as Dog).isBark !== undefined;
}

function warning(animal: Animal) {
  if(isDog(animal)) {
    // animal : Dog
  }
}</code></pre>
<p><code>isDog</code> 함수는 <code>Animal</code> 타입의 매개변수를 인자로 받는다.
<code>animal is Dog</code> 부분은, 함수가 <code>true</code>를 반환하는 경우, <code>animal</code>이 <code>Dog</code> 타입임을 타입스크립트에게 알려주는 <code>사용자 정의 타입 가드 시그니처</code> 이다.
<code>return</code> 문은 특정 객체에만 존재하는 프로퍼티를 검사하여, <code>true</code> 또는 <code>false</code> 값을 반환하도록 한다.
즉, 타입 단언을 통한 <code>return</code> 문은, 특정 타입에만 존재하는 프로퍼티 값을 확인하여 <code>true</code> 혹은 <code>false</code> 를 반환하는 역할을 하고,
<code>is</code> 키워드는 해당 값이 <code>true</code> 라면 해당 변수가 <code>Dog</code> 임을 명시해준다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹 접근성 개선 방법]]></title>
            <link>https://velog.io/@ldh-dodo/%EC%9B%B9-%EC%A0%91%EA%B7%BC%EC%84%B1-%EA%B0%9C%EC%84%A0-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@ldh-dodo/%EC%9B%B9-%EC%A0%91%EA%B7%BC%EC%84%B1-%EA%B0%9C%EC%84%A0-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Sun, 04 May 2025 07:34:54 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>프로젝트를 리팩토링하려고 하는데, 현재 스크린 리더만으로 서비스를 이용하는데 어려움이 있어, 이에 대한 내용부터 개선을 진행하려고 한다.
웹 접근성에 대한 개념과, 웹 접근성이 좋아야하는 이유, 개선 방법들을 알아보고 프로젝트에 적용해볼 것이다.</p>
</blockquote>
<h1 id="웹-접근성accessibility-a11y">웹 접근성(Accessibility, a11y)</h1>
<p><code>웹 접근성</code> 이란 장애가 있는 사람들도 웹을 인지하고, 이해하고, 탐색하고, 상호작용할 수 있으며, 웹에 기여할 수 있도록 보장하는 것이다.</p>
<p><code>WCAG(Web Content Accessibility Guidelines)</code> 는 이 웹 접근성을 달성하기 위해 <code>4가지 핵심 원칙(P.O.U.R)</code> 을 제시한다.</p>
<p>그렇다면 우리는 왜 웹 접근성을 고려해야할까?</p>
<h2 id="웹-접근성이-중요한-이유">웹 접근성이 중요한 이유</h2>
<h3 id="모두를-위한-사용성">모두를 위한 사용성</h3>
<p>웹의 창시자 <code>팀 버너스 리</code> 는 이렇게 말했다.</p>
<blockquote>
<p>웹은 모두를 위한 것입니다. 누구든, 어떤 기기든, 어떤 능력이든 간에 접근할 수 있어야 합니다.
— Tim Berners-Lee</p>
</blockquote>
<p>애초에 웹은 모든 사람이 자유롭고 평등하게 정보에 접근할 수 있도록 설계되었다.
모든 이들이 동일한 수준으로 웹을 사용할 수는 없겠지만, 처음 설계된 목적에 맞게, 그 이상에 최대한 가까워지기 위해 우리는 웹 접근성을 고려해야 할 것이다.</p>
<h3 id="검색-엔진-최적화seo-개선-및-ux-향상">검색 엔진 최적화(SEO) 개선 및 UX 향상</h3>
<p>웹 접근성을 고려하는 것은 검색 엔진 최적화(SEO) 측면에서도 실질적인 이점을 가져다준다.</p>
<p>접근성을 고려하기 위해 적절한 시맨틱 태그를 활용하면, 이는 검색 엔진 크롤러가 페이지의 콘텐츠를 더 잘 이해할 수 있도록 돕는다.</p>
<p>뿐만 아니라, SEO를 개선하기 위한 명확한 콘텐츠 구조, 키보드 내비게이션, 텍스트 대체 설명 등은 장애 유무와 관계없이 모든 사용자에게 더 나은 경험을 제공하므로, 사용자 경험(UX)도 자연스럽게 향상된다.</p>
<p>결국, 이는 사람들의 유입과 더 많은 콘텐츠의 노출로 이어지며, 서비스의 접근성이 향상되어 비즈니스적으로 긍정적인 효과를 낳는다.</p>
<h2 id="4가지-핵심-원칙pour">4가지 핵심 원칙(P.O.U.R)</h2>
<p><code>WCAG</code> 는 4가지 핵심 원칙으로 <code>Perceivable(지각 가능)</code> <code>Operable(운용 가능)</code> <code>Understandable(이해 가능)</code> <code>Robust(견고함)</code> 을 언급한다.</p>
<p>이 중 하나라도 충족되지 않으면, 장애가 있는 사용자는 웹을 사용할 수 없다고 판단한다.</p>
<h3 id="지각-가능perceivable">지각 가능(Perceivable)</h3>
<blockquote>
<p>정보와 사용자 인터페이스 구성요소는 사용자가 지각할 수 있는 방식으로 제공되어야 합니다.</p>
</blockquote>
<p>이 말은, 모든 사용자가 웹에서 제공되는 정보를 어떤 방식으로든 인식할 수 있어야 한다는 뜻이다. 
이를 위해 다음과 같은 방식을 고려할 수 있다고 한다.</p>
<ul>
<li>만약 사용자가 시각적으로 읽지 못한다면, 대체 텍스트나 스크린 리더와 같은 보조 기술을 통해 정보를 전달할 수 있어야 함.</li>
<li>만약 소리가 포함되는 컨텐츠가 있다면, 자막을 제공하거나 소리가 없는 대체 방법으로 정보를 제공해야 함.</li>
<li>정보를 제공하는 방법이 하나의 감각에 의존해서는 안됨. 시각, 청각, 촉각 등 여러 가지 감각을 통해 정보를 인식할 수 있어야 함. 예를 들어, 소리가 나는 버튼은 시각적 표시나 진동 등의 다른 방식으로도 사용자가 그 버튼을 인식할 수 있어야 함.</li>
</ul>
<h3 id="운용-가능operable">운용 가능(Operable)</h3>
<blockquote>
<p>사용자 인터페이스 구성요소와 내비게이션은 운용 가능해야 합니다.</p>
</blockquote>
<p>이 말은, 웹에서의 모든 상호작용이 사용자가 실행할 수 있는 방식으로 제공되어야 함을 의미한다.
즉, 사용자가 실제로 상호작용할 수 없는 요소나 인터페이스는 없어야한다.</p>
<ul>
<li>웹사이트나 애플리케이션의 내비게이션 및 상호작용 요소는 반드시 키보드로 조작할 수 있어야 함</li>
<li>사용자가 특정 작업을 완료할 수 있는 충분한 시간을 제공해야 함.</li>
<li>사용자가 인터페이스와 상호작용할 때, 인터페이스의 동작은 예상한 대로 작동해야 함.</li>
</ul>
<h3 id="이해-가능understandable">이해 가능(Understandable)</h3>
<blockquote>
<p>정보와 사용자 인터페이스의 작동은 이해 가능해야 합니다.</p>
</blockquote>
<p>이는 애플리케이션에서 제공되는 정보와 인터페이스가 사용자가 이해할 수 있는 방식으로 제공되어야 한다는 의미이다.</p>
<ul>
<li>명확한 언어를 사용해야 함. (복합적이거나 전문적인 용어는 최대한 지양하고, <strong>일상적</strong>인 언어를 사용해야 함.</li>
<li>인터페이스가 예측 가능해야 함. (사용자가 특정 기능과 상호작용할 때, 그 행동이 어떤 결과를 초래할지 미리알 수 있어야 함.)</li>
</ul>
<h3 id="견고함robust">견고함(Robust)</h3>
<blockquote>
<p>콘텐츠는 보조 기술을 포함한 다양한 사용자 에이전트에 의해 신뢰성 있게 해석될 수 있을 만큼 견고해야 합니다.</p>
</blockquote>
<p>웹 콘텐츠가 다양한 기술 환경에서 잘 동작할 수 있도록 설계되어야 한다는 의미이다.
즉, 기술의 발전이나 새로운 장치나 브라우저가 등장하더라도 웹 콘텐츠는 항상 접근 가능하고, 사용자에게 문제없이 제공되어야 한다는 원칙이다.</p>
<ul>
<li>웹 표준을 준수해야함. (웹 콘텐츠가 다양한 기술환경에서 제대로 작동하기 위해서는, <code>HTML</code> <code>CSS</code> <code>JavaScript</code> 와 같은 웹 기술이 <code>W3C</code> 의 표준을 따르도록 작성되어야 함.)</li>
<li>웹 콘텐츠는 보조 기술과도 호환되어야 함. (웹 콘텐츠가 스크린 리더, 음성 인식 소프트웨어, 키보드 내비게이션과 같은 보조기술에서 잘 동작해야 함.)</li>
</ul>
<h1 id="웹-접근성-개선-방법">웹 접근성 개선 방법</h1>
<p>웹 접근성이 중요한 이유, 4가지 핵심원칙에 대해 알아봤으니, 이제 실제로 어떻게 웹 접근성을 개선할 수 있는지에 대해 알아보자.</p>
<h2 id="1-시멘틱-태그-사용">1. 시멘틱 태그 사용</h2>
<p><code>시멘틱 태그</code> 란 HTML에서 의미를 담고 있는 태그를 말한다. 비시맨틱한 역할을 하는 <code>div</code> 나 <code>span</code> 사용을 지양하고, <code>header</code> <code>main</code> 과 같은 시맨틱 태그를 사용해야한다.</p>
<p>시멘틱 태그를 사용하면 모든 사용자가 페이지의 구조를 명확하게 이해할 수 있으며,
콘텐츠를 구분하기 쉬워지고 혼란을 줄일 수 있다.</p>
<p>예를 들어, strong 태그는 스크린 리더에서 해당 문장을 더 강하게 읽어주며, 검색 엔진도 이 문장을 중요한 정보로 인식한다.</p>
<p>결국 시멘틱 태그는 보조 기술이 페이지의 구조와 핵심 내용을 더 쉽게 파악하게 도와주고, 접근성과 사용자 경험을 모두 향상시킨다.</p>
<h2 id="2-대체-텍스트-및-텍스트-콘텐츠-사용">2. 대체 텍스트 및 텍스트 콘텐츠 사용</h2>
<p>이미지는 시각적 정보이므로 시각장애인은 내용을 인식할 수 없다.
따라서, <code>alt</code> 속성으로 대체 텍스트를 제공해야 한다.</p>
<pre><code class="language-html">&lt;img src=&quot;coke.jpg&quot; alt=&quot;코카콜라 사진&quot;/&gt;</code></pre>
<p>하지만, 의미 없는 배경이나 아이콘 이미지의 경우 <code>alt</code> 를 빈 값으로 둬서 스크린 리더가 무시하도록 하는게 좋다.</p>
<p>만약에 비디오 콘텐츠가 있다면, 청각장애인을 위해 자막(track)이나 전체 스크립트를 함께 제공해야 한다.</p>
<h2 id="3-키보드-접근성-향상">3. 키보드 접근성 향상</h2>
<h3 id="키보드만으로-조작-가능해야-함">키보드만으로 조작 가능해야 함</h3>
<p>마우스를 사용할 수 없는 사용자도 있기 때문에, 페이지 내의 모든 상호작용은 <code>Tab</code> <code>Enter</code> <code>Space</code> 키로만 조작이 가능해야 한다.</p>
<h3 id="tabindex-적절히-사용하기">tabindex 적절히 사용하기</h3>
<p><code>Tab</code> 키를 통해 HTML 구조를 따라 논리적인 순서로 이동할 수 있도록 마크업을 구성해야한다.</p>
<p><code>tabindex</code> 속성은 수동으로 탭 순서를 지정할 수 있지만, 과도한 사용은 지양해야 한다.</p>
<h4 id="과도하게-사용하면-안되는-이유">과도하게 사용하면 안되는 이유</h4>
<p>실제로 HTML 요소가 <strong>시멘틱 태그</strong>로 잘 구성되어 있다면, <code>tabindex</code> 를 자주 사용할 필요 없이 자연스러운 탭 순서가 자동으로 결정된다.
과도한 <code>tabindex</code> 은 오히려 비논리적으로 구성되어 사용자에게 혼란을 줄 수 있고, 보조 기술을 사용하는 사용자에게도 문제가 된다.</p>
<p>또한, <code>tabindex</code> 를 사용하는 기존 요소에 수정이 필요하거나, 다른 요소가 추가될 때마다 <code>tabindex</code> 값을 계속 수정해야 할 필요성이 생기기에, 유지보수에도 악영향을 초래한다.</p>
<h4 id="tabindex-속성">tabindex 속성</h4>
<h5 id="tabindex--0">tabindex = &quot;0&quot;</h5>
<p><code>tabindex = &quot;0&quot;</code> 을 사용하면, <code>div</code> 나 <code>span</code> 과 같은 기본적으로 포커스를 받을 수 없는 요소를 탭 순서에 포함시킬 수 있다.
일반적으로 포커스를 받을 수 없는 요소들이 반드시 탭 순서에 포함되어야 할 때, 사용하면 유용하겠다.</p>
<h5 id="tabindex---1">tabindex = &quot;-1&quot;</h5>
<p><code>tabindex = &quot;-1&quot;</code> 을 사용하면, 해당 요소가 탭 순서에서 제외되며, 해당 요소에 포커스가 이동하지 않는다.
해당 속성을 사용하면 모달 창이 띄워졌을 때, 배경은 탭 순서에서 제외되어 사용자는 모달 내부 요소만 탐색할 수 있게된다. 이는<code>포커스 트랩</code> 을 구현하는 데 유용하다.</p>
<h2 id="4-적절한-색상-대비">4. 적절한 색상 대비</h2>
<p>텍스트와 배경 간의 적절한 대비가 모든 사용자가 환경에 구애받지 않고 웹을 쉽게 읽을 수 있도록 돕는다.</p>
<p><code>WCAG</code> 에 따르면, 일반 텍스트는 배경과의 대비가 최소 <code>4.5:1</code> 이상이어야 한다.
이는 텍스트가 배경과 충분히 구분되어 가독성을 높이고, 시각적 편안함을 제공하는 최소 수치라고 한다.</p>
<p>그리고, 색상만을 중요한 정보를 전달하지 않는 것도 중요하다고 한다.
버튼이나 경고 메시지에서 색상만으로 상태를 표현하지 말고, 아이콘이나 텍스트 등 여러 방법을 통해 함께 제공하는 것이 권장된다.</p>
<h2 id="5-적절한-폰트-크기">5. 적절한 폰트 크기</h2>
<p>적절한 폰트 크기는 사용자가 웹을 쉽게 읽게 돕는다.
글자가 너무 작으면 여러 환경에서 읽기 어려울 수 있기 때문에, 최소 크기 기준을 정하는 것이 좋다.</p>
<p><code>px</code> 단위로 폰트를 설정하면, 웹 페이지의 텍스트 크기가 고정되기 때문에 <code>rem</code> 단위를 사용해 폰트 크기가 상대적으로 조절 가능하게 하는 것이 좋다.
본문 텍스트는 최소 <code>1rem(16px)</code> 이상이 권장되고, 이는 모바일에서도 읽기 쉬운 크기를 보장한다.</p>
<h2 id="6-반응형-디자인">6. 반응형 디자인</h2>
<p>반응형 디자인은 <code>다양한 화면 크기와 디바이스 환경에 맞게 웹 페이지를 최적화하는 디자인 방식</code> 이다.</p>
<p>화면이 큰 디바이스와, 화면이 작은 디바이스는 아무래도 표현할 수 있는 콘텐츠의 양이 다르다. 이를 고려하여 웹사이트를 설계하는 것이 중요하다.
이를 통해, 사용자가 어떤 장치를 사용하더라도 콘텐츠가 잘 표시되고, 정보가 쉽게 탐색될 수 있도록 도울 수 있다.</p>
<p>이는 웹 접근성 향상에도 중요한 역할을 한다.</p>
<h2 id="7-wai-aria-속성-사용">7. WAI-ARIA 속성 사용</h2>
<p><code>WAI-ARIA (Web Accessibility Initiative - Accessible Rich Internet Applications)</code> 은 시각적, 청각적 장애를 가진 사용자가 웹을 원활하게 사용할 수 있도록 도와주는 기술이다.
스크린 리더와 같은 보조 기술이 웹 콘텐츠를 더 정확하게 이해하고 사용자에게 제공하는 걸 돕는다.</p>
<p>자주 사용되고 중요한 속성들을 살펴보자!</p>
<h3 id="aria-labelledby">aria-labelledby</h3>
<p><code>aria-labelledby</code> 는 어떤 요소의 이름 또는 설명을 다른 HTML 요소의 콘텐츠로부터 가져오고 싶을 때 사용한다.</p>
<pre><code class="language-html">&lt;h2 id=&quot;formTitle&quot;&gt;회원 가입&lt;/h2&gt;
&lt;form aria-labelledby=&quot;formTitle&quot;&gt;
&lt;/form&gt; </code></pre>
<p>이렇게 사용하면 form 요소가 자신을 설명할 이름을 <code>formTitle</code> 이라는 id를 가진 요소의 콘텐츠로부터 가져온다는 의미이다.</p>
<p>따라서 스크린 리더는 이 <code>form</code> 을 사용자에게 <code>회원 가입</code> 이라고 설명한다.</p>
<h3 id="aria-label">aria-label</h3>
<p><code>aria-label</code> 은 웹 요소에 대한 텍스트 레이블을 명시적으로 지정할 때 사용되는 속성이다. 
화면에 보이는 텍스트가 없거나, 텍스트가 명확하지 않은 경우에 유용하다.</p>
<p><code>aria-labelledby</code> 은 이미 페이지에 존재하는 다른 요소(제목, 설명 텍스트 등)의 id를 참조하여 해당 콘텐츠를 레이블로 사용하고,
<code>aria-label</code> 은 특정 요소에 대한 텍스트 레이블을 <code>직접 제공</code> 한다는 차이가 있다.</p>
<pre><code class="language-html">&lt;button aria-label=&quot;검색&quot;&gt;🔍&lt;/button&gt;</code></pre>
<p>이렇게 시각적 텍스트가 없지만 보조 기술이 해당 요소를 올바르게 설명할 수 있도록 한다.</p>
<h3 id="aria-describedby">aria-describedby</h3>
<p><code>aria-describedby</code> 는 어떤 요소를 설명하는 다른 요소를 지정할 때 사용한다.</p>
<p>스크린 리더는 해당 속성으로 지정된 요소의 콘텐츠를, 기본 라벨 다음에<code>추가 설명</code> 으로 읽어준다.</p>
<pre><code class="language-html">&lt;label for=&quot;email&quot;&gt;이메일&lt;/label&gt;
&lt;input id=&quot;email&quot; type=&quot;email&quot; aria-describedby=&quot;emailHelp&quot;/&gt;
&lt;div id=&quot;emailHelp&quot;&gt;이메일은 example@domain.com 형식이어야 합니다.&lt;/div&gt;</code></pre>
<p>이렇게 사용하면 <code>input</code> 요소의 추가 설명을 <code>emailHelp</code> 라는 id를 가진 요소의 콘텐츠로부터 가져와 사용자에게 안내한다.</p>
<h3 id="aria-live">aria-live</h3>
<p><code>aria-live</code> 속성은 동적 콘텐츠가 업데이트 될 때 스크린 리더에게 해당 내용을 자동으로 읽도록 지시한다.
즉, 새로 생긴 콘텐츠나 변경된 콘텐츠를 자동으로 알리기 위해 사용된다.</p>
<p><code>off</code> <code>plite</code> <code>assertive</code> 의 세 가지 주요 값이 있다.</p>
<h4 id="aria-liveoff">aria-live=&quot;off&quot;</h4>
<p><code>aria-live=&quot;off&quot;</code>는 콘텐츠 업데이트를 스크린 리더가 읽지 않도록 설정한다.
<code>aria-live</code> 속성을 사용하지 않는 것과 동일한 효과를 가진다.</p>
<p>의도적으로 해당 영역의 변경 사항을 스크린 리더에 알리지 말라는 의도를 나타내고 싶을 때 사용한다.</p>
<h4 id="aria-livepolite">aria-live=&quot;polite&quot;</h4>
<p><code>aria-live=&quot;polite&quot;</code>는 콘텐츠가 업데이트 되면 스크린 리더가 현재 읽고 있는 내용을 마친 후 새로운 정보를 읽기 시작한다.
즉, <code>덜 긴급한 알림</code>에 적합하다.</p>
<pre><code class="language-html">&lt;div aria-live=&quot;polite&quot;&gt;
  &lt;p&gt;현재 읽고 있는 내용을 읽고 이 메시지를 읽습니다.&lt;/p&gt;
&lt;/div&gt;</code></pre>
<h4 id="aria-liveassertive">aria-live=&quot;assertive&quot;</h4>
<p><code>aria-live=&quot;assertive&quot;</code>는 콘텐츠가 업데이트 되면 현재 스크린 리더가 읽고 있는 내용을 중단하고 즉시 알리도록 합니다.
즉, <code>긴급하고 중요한 알림</code>에 적합하다.</p>
<pre><code class="language-html">&lt;div aria-live=&quot;assertive&quot;&gt;
  &lt;p&gt;현재 읽고 있는 내용을 중단하고 이 메시지를 읽습니다.&lt;/p&gt;
&lt;/div&gt;</code></pre>
<h3 id="role">role</h3>
<p>HTML 요소가 어떤 역할을 하는지를 보조 기술에 명시적으로 알려준다.</p>
<p>role에 들어갈 수 있는 값이 많지만, 몇 가지만 살펴보자.</p>
<h4 id="rolealert">role=&quot;alert&quot;</h4>
<p><code>role=&quot;alert&quot;</code> 는 긴급한 정보를 즉시 사용자에게 알리는 역할을 한다.
스크린 리더는 즉시 읽고, 사용자에게 중요한 알림을 빠르게 전달한다.</p>
<p>페이지 내에서 발생한 오류나 중요한 경고를 알리는데 유용하다.</p>
<pre><code class="language-html">&lt;div role=&quot;alert&quot;&gt;
  &lt;p&gt; 저장에 실패했습니다. 다시 시도하세요.&lt;/p&gt;
&lt;/div&gt;</code></pre>
<h4 id="roledialog">role=&quot;dialog&quot;</h4>
<p><code>role=&quot;dialog&quot;</code> 은 웹 페이지의 특정 요소가 대화 상자(dialog) 임을 명시하는 역할을 한다.
주로 모달 창이나 팝업 창과 같은 사용자 인터페이스 요소에 사용된다.</p>
<p>이 속성을 통해 사람들에게 해당 요소가 중요한 대화 상자임을 알리기 위해 사용된다.</p>
<pre><code class="language-html">&lt;div role=&quot;dialog&quot; aria-labelledby=&quot;dialogTitle&quot; aria-hidden=&quot;true&quot;&gt;
  &lt;h2 id=&quot;dialogTitle&quot;&gt;삭제 확인&lt;/h2&gt;
  &lt;p&gt;이 항목을 삭제하시겠습니까?&lt;/p&gt;
  &lt;button&gt;삭제&lt;/button&gt;
  &lt;button&gt;취소&lt;/button&gt;
&lt;/div&gt;</code></pre>
<p>해당 예시는 사용자가 삭제 확인을 위한 대화 상자와 상호작용하는 상황이다.
이 대화 상자는 스크린 리더에 의해 대화 상자로 인식된다.</p>
<p>만약, <code>role=&quot;dialog&quot;</code>가 없다면 해당 요소는 그냥 일반적인 <code>div</code>로 처리될 것이다.</p>
<h4 id="rolealertdialog">role=&quot;alertdialog&quot;</h4>
<p><code>role=&quot;alertdialog&quot;</code> 는 <code>role=&quot;alert</code>와 <code>role=&quot;dialog&quot;</code> 를 합친 것으로, 긴급한 알림이나 즉각적으로 사용자 반응을 요구하는 대화 상자에 사용된다.
즉, 중요한 알림을 전달하지만, 대화 상자의 역할을 할 때 사용한다.</p>
<p>예를 들어, 승인/취소를 요구하는 메시지가 있을 때가 있다.</p>
<pre><code class="language-html">&lt;div role=&quot;alertdialog&quot; aria-labelledby=&quot;alertTitle&quot; aria-describedby=&quot;alertMessage&quot; aria-live=&quot;assertive&quot;&gt;
  &lt;h2 id=&quot;alertTitle&quot;&gt;경고!&lt;/h2&gt;
  &lt;p id=&quot;alertDesc&quot;&gt;이 작업을 수행하면 복구할 수 없습니다. 계속하시겠습니까?&lt;/p&gt;
  &lt;button&gt;확인&lt;/button&gt;
  &lt;button&gt;취소&lt;/button&gt;
&lt;/div&gt;</code></pre>
<p>하지만, <code>role=&quot;dialog</code> 와 <code>role=&quot;dialog</code> 와 달리 포커스를 강제로 가져온다.</p>
<h3 id="aria-hidden">aria-hidden</h3>
<p><code>aria-hidden</code>의 값을 <code>true</code>로 설정하면 해당 요소와 그 자식 요소들을 스크린 리더 등 보조 기술에서 감추도록 지시한다.
시각적으로는 보여도, 접근성 트리에는 포함되지 않는다.</p>
<p>시각적으로는 중요한 정보이지만, 보조 기술에게는 무의미하거나 혼란을 주는 정보거나, 이미지 배경 등을 숨길 때 사용한다.</p>
<pre><code class="language-html">&lt;div aria-hidden=&quot;true&quot;&gt;로딩 중...&lt;/div&gt;</code></pre>
<h3 id="aria-checked">aria-checked</h3>
<p><code>aria-checked</code>는 선택 상태를 나타내는 요소(체크박스, 토글 버튼)에서, 현재 <code>체크 여부</code> 를 보조 기술에 전달하기 위해 사용하는 속성이다.</p>
<p>값이 <code>true</code> 면 체크되어 있음을,
값이 <code>false</code> 면 체크되어 있지 않음을,
값이 <code>mixed</code> 면 부분적으로 체크됨(일부만 선택)을 의미한다.</p>
<p>보통 UI의 상태에 따라 해당 설정을 동적으로 변경하면서 사용하며, 시각적인 변화와 스크린 리더가 인식하는 상태를 일치시키는 것이 중요하다.</p>
<h3 id="aria-pressed">aria-pressed</h3>
<p><code>aria-pressed</code> 는 주로 토글 버튼 또는 상태를 변경하는 버튼에서 사용된다.</p>
<p>값이 <code>true</code> 면 버튼이 눌린 상태를,
값이 <code>false</code> 면 버튼이 눌리지 않는 상태를,
값이 <code>mixed</code> 면 부분 눌림 상태(토글 스위치의 중간 상태)를 의미한다.</p>
<h3 id="aria-required">aria-required</h3>
<p><code>aria-required</code> 는 입력 필드가 필수 입력 항목임을 보조 기술에 알려주는 속성이다.</p>
<p>값이 <code>true</code> 면 해당 입력 필드가 <code>필수 항목</code> 임을,
값이 <code>false</code> 면 해당 입력 필드가 <code>필수가 아님</code> 을 의미한다.</p>
<pre><code class="language-html">&lt;label for=&quot;email&quot;&gt;이메일&lt;/label&gt;
&lt;input id=&quot;email&quot; type=&quot;email&quot; aria-required=&quot;true&quot;&gt;            </code></pre>
<p>이렇게 하면 스크린 리더는 이 입력 필드를 필수 항목으로 읽어준다.
보통 UI의 상태에 따라 해당 설정을 동적으로 변경하면서 사용하며, 시각적인 변화와 스크린 리더가 인식하는 상태를 일치시키는 것이 중요하다.</p>
<h3 id="aria-disabled">aria-disabled</h3>
<p><code>aria-disabled</code> 는 해당 요소가 비활성화 상태임을 보조 기술에 알리는 속성이다.</p>
<p>값이 <code>true</code> 면 해당 요소는 비활성화 상태임을,
값이 <code>false</code> 면 해당 요소는 활성화 상태임을 알린다.</p>
<p>시각적 상태를 명시하는 것이므로, 해당 요소 동작을 실제로 비활성화 하는 것은 아니다. 따라서, 시각적인 변화와 스크린 리더가 인식하는 상태를 일치시키는 것이 중요하다.</p>
<h3 id="aria-selected">aria-selected</h3>
<p><code>aria-selected</code> 는 목록이나 항목 중에서 선택된 항목을 보조 기술에 알려주는 속성이다.</p>
<p>값이 <code>true</code> 면 항목이 선택됨을,
값이 <code>false</code> 면 항목이 선택되지 않음을 의미한다.</p>
<h3 id="aria-modal">aria-modal</h3>
<p><code>aria-modal</code> 은 모달 대화 상자의 접근성을 향상시키는데 사용되는 속성이다.</p>
<p>값이 <code>true</code> 면, 현재 모달 창이 활성화 되어 다른 콘텐츠와 상호작용이 가능함을,
값이 <code>false</code> 면, 현재 모달 창이 닫히거나 모달이 아닌 상태임을 나타낸다.</p>
<h3 id="aria-expanded">aria-expanded</h3>
<p><code>aria-expanded</code> 는 접을 수 있거나 펼칠 수 있는 UI 요소에서, 현재 상태가 <code>펼쳐져 있는지</code> <code>접혀 있는지</code> 를 보조 기술에 알려주는 속성이다.</p>
<p>값이 <code>true</code> 라면 요소가 펼쳐져 있음을,
값이 <code>false</code> 라면 요소가 접혀 있음을 의미힌다.</p>
<p>드롭다운이나 내비게이션 메뉴 토글 등에 사용하면 유용하다.</p>
<p>보통 UI의 상태에 따라 해당 설정을 동적으로 변경하면서 사용하며, 시각적인 변화와 스크린 리더가 인식하는 상태를 일치시키는 것이 중요하다.</p>
<h3 id="aria-controls">aria-controls</h3>
<p><code>aria-controls=&quot;targetID&quot;</code> 는 해당 요소가 <code>targetID</code> 를 가진 요소를 제어한다는 의미이다.
보통 <code>aria-expanded</code> 와 함께 사용한다.</p>
<pre><code class="language-html">&lt;button aria-controls=&quot;dropdownMenu&quot; aria-expanded=&quot;false&quot;&gt;메뉴 열기&lt;/button&gt;
&lt;ul id=&quot;dropdownMenu&quot; hidden&gt;
  &lt;li&gt;항목 1&lt;/li&gt;
  &lt;li&gt;항목 2&lt;/li&gt;
&lt;/ul&gt;</code></pre>
<p><code>dropdownMenu</code> 요소를 <code>button</code> 이 제어한다는 의미이다.
보조 기술이 이 속성을 참고해 연관성을 파악할 수 있도록 도와준다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트 계층도 이해하기]]></title>
            <link>https://velog.io/@ldh-dodo/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B3%84%EC%B8%B5%EB%8F%84-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@ldh-dodo/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B3%84%EC%B8%B5%EB%8F%84-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 29 Apr 2025 05:55:50 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>본 포스팅은 <a href="https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8/dashboard">한 입 크기로 잘라먹는 타입스크립트</a> 강의를 참고하여 작성했습니다.사용된 모든 이미지는 해당 강의에서 가져온 것입니다. 문제가 될 소지가 있다면 댓글로 알려주세요. 바로 수정하겠습니다.
</blockquote>
<p>타입들은 부모와 자식 간의 관계를 가지며, 모든 타입들의 관계를 늘어놓으면 결국 타입 계층도가 완성된다.
타입 스크립트의 타입 계층도는 아래와 같다.</p>
 <div align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/65c53100-872a-4702-834a-0d93abff007f/image.png" width = "1000px"/>
</div>

<p>타입 계층도를 이해하게 되면, <code>타입 호환성</code> 에 대해 이해할 수 있는데, <code>타입 호환성</code> 이란 어떤 타입을 다른 타입으로 취급해도 괜찮은지 판단하는 것을 말한다.</p>
<div align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/94179a81-bc15-4e4d-9f23-a41d552fbd3d/image.png" width = "700px"/>
</div>

<p>위 사진으로 이해해보자면 <code>number</code> 타입을 <code>number literal</code> 타입으로 취급하는 것은 안되지만, 반대는 가능하다.
모든 정사각형은 직사각형으로 취급해도 괜찮지만, 반대는 안되는 관계가 이와같다.</p>
<p>이는 <code>number</code> 타입이 <code>number literal</code> 타입의 슈퍼타입(부모타입) 이기 때문에 가능하다.</p>
<pre><code class="language-typescript">let num1 : number = 10;
let num2 : 10 = 10;

num1 = num2; // OK

------------------------

let num1 : number = 10;
let num2 : 10 = 10;

num2 = num1; // error!!</code></pre>
<p>이렇게 서브 타입의 값을 슈퍼 타입으로 취급하는 <code>업 캐스팅</code> 은 모든 상황에 가능하다. 
반면에, 슈퍼 타입의 값을 서브 타입으로 취급하는 <code>다운 캐스팅</code>은 대부분의 상황에 불가능하다.</p>
<p>특수한 타입과 함께 타입 계층도를 코드 예시와 함께 이해해보자.</p>
<h1 id="코드와-함께-살펴보기">코드와 함께 살펴보기</h1>
<h2 id="unknown">unknown</h2>
<p>계층도를 보면, <code>unknown</code> 타입은 가장 최상단에 위치한다.
즉, <code>모든 타입의 슈퍼타입</code> 이다. </p>
<p>따라서, 모든 타입의 변수들을 <code>unknown</code> 타입으로 취급할 수 있다.(업 캐스팅)
하지만, 다운캐스팅은 불가능하다.</p>
<pre><code class="language-typescript">  let a: unknown = 1;
  let b: unknown = &quot;hello&quot;;
  let c: unknown = true;
  let d: unknown = null;
  let e: unknown = undefined; // OK. upcasting 


  let unknownVar : unknown;
  let num: number = unknownVar // error!! downcasting</code></pre>
<h2 id="never">never</h2>
<p><code>never</code> 타입은 타입 계층도 최하단에 위치한다.
즉, <code>모든 타입의 서브타입</code> 이고, 모든 집합의 부분집합이다. 이는 곧 공집합을 의미한다.</p>
<pre><code class="language-typescript">  function neverFunc(): never {
    while (true) {}
  }

  let num: number = neverFunc();
  let str: string = neverFunc();
  let bool: boolean = neverFunc(); // OK. upcasting

  let never1: never = 10; // error!! downcasting</code></pre>
<p>코드를 보다보면 혼동이 올 수 있지만, 결국 <code>업 캐스팅 은 모든 상황에 가능하고,</code>다운 캐스팅<code>은 대부분의 상황에 불가능하다</code> 라는 기준만 잡는다면, 해당 상황을 이해할 수 있다.</p>
<h2 id="void">void</h2>
<p><code>void</code> 타입은 <code>undefined</code>의 슈퍼타입이고, 따라서 업캐스팅이 가능하다.</p>
<pre><code class="language-typescript">function voidFunc(): void {
  console.log(&quot;hi&quot;);
  return undefined;
}

let voidVar: void = undefined;</code></pre>
<p>당연하게도 <code>void</code> 타입의 변수에 <code>undefined</code>를 할당할 수 있고, 이와 같은 맥락으로 <code>void</code> 타입의 함수가 <code>undefined</code> 를 반환 하도록 할 수 있다.</p>
<h2 id="any">any</h2>
<p>타입 계층도를 자세히 살펴보면, <code>any</code> 타입 머리 위에 <code>치트키</code> 라고 써있는 걸 볼 수 있다.
강의를 진행해주신 <code>이정환</code> 님이 이해하기 쉽도록 적어두신 용어이다.
이는 <code>any</code> 타입이 타입 계층도를 완벽하게 무시한다는 의미이다.</p>
<p><code>any</code> 타입은 모든 타입의 <code>슈퍼 타입</code> 이면서, <code>never</code> 을 제외한 모든 타입의 <code>서브 타입</code>  이다.</p>
<p>타입 계층도로만 보면, <code>unknown</code> 타입의 변수를 <code>any</code> 타입의 변수에 넣는 것이 불가능해보이지만(다운 캐스팅), <code>any</code> 타입은 타입 계층도를 무시하기 때문에, 아래와 같이 다운 캐스팅이 가능하다.</p>
<pre><code class="language-typescript">let anyVar : any ;
let unknownVar : unknown;

anyVar = unknownVar;</code></pre>
<p>타입 계층도를 볼 때 혼란이 올 수 있겠지만, <code>any</code> 타입에 한해서는 <code>never</code> 을 제외한 모든 관계가 무시된다고 생각하면 된다.
그래서 더더욱 위험한 타입이니, 사용을 지양하는게 좋겠다.</p>
<h1 id="객체-타입의-호환성">객체 타입의 호환성</h1>
<p>다운 캐스팅과 업 캐스팅 개념을 가지고, 객체 타입 간의 호환성은 어떻게 되는지 알아보자.
객체 타입들도, 기본 타입처럼 <code>슈퍼 타입</code> 과 <code>서브 타입</code> 관계를 가진다.</p>
<p>아래 코드를 보자.</p>
<pre><code class="language-typescript">type Animal = {
  name: string;
  color: string;
};

type Dog = {
  name: string;
  color: string;
  breed: string;
};

let animal: Animal = {
  name: &quot;기린&quot;,
  color: &quot;yellow&quot;,
};

let dog: Dog = {
  name: &quot;돌돌이&quot;,
  color: &quot;brown&quot;,
  breed: &quot;진도&quot;,
};</code></pre>
<p><code>Animal</code> 타입과 <code>Dog</code> 타입을 보면, <code>Dog</code> 타입의 속성이 더 많기 때문에, <code>슈퍼 타입(부모 타입)</code> 이라고 헷갈릴 수도 있다.
하지만, 객체 타입 관계에서는 속성이 더 적은 쪽이 <code>슈퍼 타입</code> 이다.</p>
<p>이는 <code>정사각형</code> 과 <code>직사각형</code> 의 조건을 놓고 보면 이해하기 쉽다.
<code>정사각형의 조건</code> : <code>직사각형의 조건</code> + <code>네 변의 길이가 모두 같다</code></p>
<p><code>정사각형</code> 은 <code>직사각형</code> 보다 더 많은 조건을 만족해야 하므로, 더 구체적인 형태이다.
따라서 <code>정사각형</code>은 <code>직사각형</code>의 자식이다.</p>
<p>이해할 때는 이런 비유로 이해하면 좋고, 속성이 더 적은쪽(조건이 더 까다로움)이 <code>슈퍼 타입</code> 이라는걸 기억해두자.</p>
<p>큰 나무 가지(슈퍼 타입)에서, 프로퍼티가 추가되면서 곁가지들(서브 타입)이 뻗어나오는 모습을 상상하자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입 별칭과 인덱스 시그니처]]></title>
            <link>https://velog.io/@ldh-dodo/%ED%83%80%EC%9E%85-%EB%B3%84%EC%B9%AD%EA%B3%BC-%EC%9D%B8%EB%8D%B1%EC%8A%A4-%EC%8B%9C%EA%B7%B8%EB%8B%88%EC%B2%98</link>
            <guid>https://velog.io/@ldh-dodo/%ED%83%80%EC%9E%85-%EB%B3%84%EC%B9%AD%EA%B3%BC-%EC%9D%B8%EB%8D%B1%EC%8A%A4-%EC%8B%9C%EA%B7%B8%EB%8B%88%EC%B2%98</guid>
            <pubDate>Mon, 28 Apr 2025 01:55:22 GMT</pubDate>
            <description><![CDATA[<p>타입스크립트에서 타입을 더 효율적이고 유연하게 다룰 수 있도록 도와주는<code>타입 별칭(type alias)</code> 와 <code>인덱스 시그니처(index signature)</code> 에 대해 정리해보려고 한다.</p>
<h1 id="타입-별칭">타입 별칭</h1>
<p>타입을 마치 변수처럼 선언해서 사용할 수 있도록 해준다.</p>
<p>만약 아래와 같은 코드가 있다고 해보자.</p>
<pre><code class="language-typescript">let user1: {
  id: number,
  name: string,
  nickname: string,
  age : number,
} = {
  id: 1,
  name: &quot;leedo&quot;,
  nickname: &quot;lee&quot;,
  age : 26,
};

let user2 ...
let user3 ...</code></pre>
<p>타입이 중복될 뿐만 아니라, 속성이 추가되면 일일이 추가해야할 것이다.
이러한 번거로움을 <code>타입 별칭</code> 으로 해결할 수 있다.</p>
<pre><code class="language-typescript">type User = {
    id: number;
    name: string;
    nickname: string;
    age: number;
 }

let user1 : User = {
  id: number,
  name: string,
  nickname: string,
  age : number,
}

let user2 : User = ...
let user3 : User = ...</code></pre>
<p>가독성이 좋아졌을 뿐만 아니라, 중복되는 코드도 압도적으로 줄어들 것이다.
또, <code>User</code> 타입을 따로 빼서 모듈화 하는 것도 가능하겠다.</p>
<p>주의해야할 점은, 타입 별칭은 변수처럼 작동하기 때문에, 같은 스코프 내에서 중복되면 안된다는 점이다.</p>
<h1 id="인덱스-시그니처">인덱스 시그니처</h1>
<p><code>인덱스 시그니처(index signature)</code> 는 객체의 키와 값의 타입을 동적으로 정의할 수 있게 해준다.</p>
<p>다음 코드를 살펴보자.</p>
<pre><code class="language-typescript">type CountryCodes = {
  Korea : string,
  UnitedStates : string,
  UnitedKingdom : string,
}

let countryCodes : CountryCodes = {
  Korea : &quot;ko&quot;,
  UnitedStates : &quot;us&quot;,
  UnitedKingdom : &quot;uk&quot;,
}</code></pre>
<p>지금은 국가 코드를 세 개만 적어놨지만, 만약 그 수가 늘어나면, 늘어나는 만큼 타입에 추가해줘야 할 것이다.
이러한 번거로움을 <code>인덱스 시그니처</code>로 해결할 수 있다.</p>
<pre><code class="language-typescript">type CountryCodes {
    [key: string] : string;
}

let countryCodes : CountryCodes = {
  Korea : &quot;ko&quot;,
  UnitedStates : &quot;us&quot;,
  UnitedKingdom : &quot;uk&quot;,
}</code></pre>
<p>위와 같이 사용하면 <code>key</code> 는 <code>string</code> 타입으로, <code>value</code> 는 <code>string</code> 타입으로 정의해서 사용할 수 있다.
이렇듯 <code>인덱스 시그니처</code> 는 키와 값의 패턴이 일정할 때 사용하면 편리하다.</p>
<p>하지만, 인덱스 시그니처는 규칙을 위반하지만 않으면 상관이 없기 때문에, 다음과 같은 상황을 주의해야 한다.</p>
<pre><code class="language-typescript">    let contryCodes : CountryCodes = {}; // OK</code></pre>
<p>규칙을 위반할 프로퍼티가 없기 때문에, 타입스크립트에서 에러로 간주하지 않는다.</p>
<p>만약, 반드시 존재해야 할 프로퍼티가 있다면, 아래와 같이 따로 지정해줄 수 있다.</p>
<pre><code class="language-typescript">type CountryCodes = {
  [key: string]: string;
  Korea: string;
};


let contryCodes : CountryCodes = {}; // error !!

-------------------

let contryCodes : CountryCodes = { // OK
  Korea: &#39;ko&#39;,
};</code></pre>
<p>추가 프로퍼티로 지정해준 프로퍼티가 없다면, 에러를 발생시킨다.</p>
<p>하지만, 아래와 같이 추가 프로퍼티와 기존에 명시된 타입이 일치하지 않는다면, 에러를 발생시키니 주의해야 한다.</p>
<pre><code class="language-typescript">type CountryCodes = {
  [key: string]: string;
  Korea: number; // error !!
};</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트 타입 정리]]></title>
            <link>https://velog.io/@ldh-dodo/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%83%80%EC%9E%85-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@ldh-dodo/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%83%80%EC%9E%85-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Mon, 28 Apr 2025 01:31:47 GMT</pubDate>
            <description><![CDATA[<p>타입스크립트를 다시 공부하면서, 타입부터 정리해보려고 한다.</p>
<h2 id="원시-타입primitive-type">원시 타입(Primitive Type)</h2>
<p>원시 타입은, <code>하나의 값만 저장</code> 하는 타입으로, <code>number</code> <code>string</code> <code>boolean</code> <code>null</code> <code>undefined</code> 가 있다.</p>
<h3 id="number">number</h3>
<p>모든 숫자 값을 나타내는 타입이다.
<code>Infinity</code> <code>-Infinity</code> <code>NaN</code> 도 number 타입에 포함된다.</p>
<pre><code class="language-typescript">let num1 : number = Infinity;
let num2 : number = -Infinity;
let num3 : number = NaN;

// 다른 타입에서만 사용할 수 있는 메서드는 사용하면 에러가 난다.

num1.toUpperCase() // error!</code></pre>
<h3 id="string">string</h3>
<p>문자열을 의미하는 타입이다.
당연하게도, 백틱을 이용한 템플릿 리터럴도 포함한다.</p>
<pre><code class="language-typescript">let str1: string = `hi ${num1}`;</code></pre>
<h3 id="boolean">boolean</h3>
<p><code>true</code>와 <code>false</code> 만 저장할 수 있다.</p>
<h3 id="null">null</h3>
<p><code>null</code> 값 이외에는 담을 수 없다.</p>
<h3 id="undefined">undefined</h3>
<p><code>undefined</code> 값 이외에는 담을 수 없다.</p>
<h2 id="리터럴-타입">리터럴 타입</h2>
<p><code>리터럴 타입</code> 이란, <code>고정된 값</code> 만을 허용하는 타입이다.</p>
<pre><code class="language-typescript">let num: 10 = 10;  // 10만 허용할 것임.
num = 20;  // error!!

let strA: &#39;hello&#39; = &#39;hello&#39;;
strA = &#39;hi&#39;; // error!!</code></pre>
<p>이게 대체 어디에 쓰이겠냐 싶겠지만, 이후에 복합적인 타입을 만들 때 유용하니까 알아두자.</p>
<h2 id="배열-타입">배열 타입</h2>
<p>같은 타입의 여러 값을 순서대로 저장하는 자료형이다.</p>
<pre><code class="language-typescript">let strArr: string[] = [&quot;hi&quot;, &quot;there&quot;];
let boolArr: Array&lt;boolean&gt; = [true, true, true]; // 제네릭으로 나타낼 수 있다.

let multiArr1: (string | number)[] = [1, &quot;hello&quot;]; 
let multiArr2: Array&lt;string | boolean&gt; = [true, &quot;hello&quot;]; // 멀티 타입도 가능

let doubleArr: number[][] = [[1, 2],[3, 4]]; // 다차원도 가능</code></pre>
<h2 id="튜플-타입">튜플 타입</h2>
<p>길이와 타입이 고정된 배열이다.
자바스크립트에는 없고, 타입스크립트에서만 특별히 제공된다.</p>
<pre><code class="language-typescript">let tup1: [boolean, boolean] = [true, false]; // 선언

tup1 = [1, false] // error!! boolean 타입만 가능
tup1 = [false, false, false] // error!! 길이 초과</code></pre>
<p>튜플은 타입스크립트에서만 특별히 제공된다고 했는데, 실제로 JS로 컴파일되어 변환될 때는 결국 배열로 컴파일된다.
그렇기 때문에, 실제 배열처럼 <code>push()</code> 나 <code>pop()</code> 메서드를 사용할 수 있다.
두 메서드는 배열의 길이에 영향을 주게 되는데, 그렇다면 에러가 날까?</p>
<pre><code class="language-typescript">tup1.push(false); // not error</code></pre>
<p>놀랍게도, 타입스크립트가 감지하지 못한다.
따라서, 튜플 타입을 사용할 때는 <code>push()</code> 나 <code>pop()</code> 을 각별히 주의해서 사용해야 할 필요가 있겠다.
물론, 두 메서드를 사용할 때 내부 타입은 제대로 검사해주고, <code>길이</code> 가 달라지는걸 캐치하지 못할 뿐이다.</p>
<h3 id="튜플-예시">튜플 예시</h3>
<p>튜플은 어디에 사용할 수 있을까?</p>
<p>예를 들어, 유저 정보와 나이를 배열로 관리한다고 해보자.</p>
<pre><code class="language-typescript">const users = [
  [&#39;Alice&#39;, 21],
  [&#39;Bob&#39;, 22],
];

// 유저 정보를 추가할 것임.
users.push(22, &#39;leedo&#39;); // error!! 인덱스 위치 불일치</code></pre>
<p>위와 같이, 인덱스의 순서와 위치가 헷갈릴 수 있는 상황을 막아준다. </p>
<h2 id="객체-타입">객체 타입</h2>
<p>객체를 나타내는 <code>object</code> 타입과, <code>객체 리터럴</code> 타입이 존재한다.</p>
<h3 id="object">Object</h3>
<pre><code class="language-typescript">let user: object = {
  id: 1,
  name: &quot;leedo&quot;,
};

console.log(user.id) // error!!;</code></pre>
<p>언뜻 보면 이 코드는 에러가 나지 않을 것 같지만, <code>object</code> 타입은 단순히 <code>객체</code> 라는 정보만을 주기 때문에, 타입스크립트가 <code>user</code> 안에 어떤 속성이 있는지 감지하지 못하고, 에러가 발생한다.</p>
<p>따라서, 아래와 같이 객체 리터럴 타입을 사용해야 한다.</p>
<h3 id="객체-리터럴-타입">객체 리터럴 타입</h3>
<p>구체적인 객체의 구조와 속성을 정의하는 타입이다.
객체가 가져야 할 <code>속성</code> 과 <code>속성의 타입</code> 을 지정하는 방법이다.</p>
<pre><code class="language-typescript">let user: {
  readonly id: number;
  name: string;
  option?: string;
} = {
  id: 1,
  name: &quot;leedo&quot;,
};

console.log(user.id) // leedo

user.id = &#39;길동이&#39;; // error!! readonly</code></pre>
<p>선택적 프로퍼티(Optional property)를 사용해, 있어도 되고, 없어도 되는 속성으로 지정해줄 수도 있다.</p>
<p><code>readonly</code> 키워드를 사용해, 읽기 전용 속성으로 만들어줄 수도 있다.</p>
<h2 id="enum-타입">enum 타입</h2>
<p><code>enum(Enumeration : 열거형)</code> 타입은 관련된 상수 값들을 하나로 묶어서, 이름을 부여해 사용할 수 있게 해주는 타입이다.</p>
<p>만약, 특정 <code>User</code>가 존재하고, <code>Role</code> 이라는 속성 값을 가진다고 해보자.</p>
<pre><code class="language-typescript">const user1 = {
 role: 0, // ADMIN
}

const user2 = {
 role: 1, // USER 
}

const user1 = {
 role: 2, // GUEST 
}</code></pre>
<p>이렇게 0은 어드민을, 1은 일반 유저를, 2는 게스트를 나타내기 위해 사용했다고 했다고 가정하자.
과연 몇 개월, 몇 년이 지난 후에 저 코드를 봤을 때, 저 숫자들이 무엇을 의미하는지 알 수 있을까?
이런 헷갈릴 수 있는 상황을 enum을 통해 방지할 수 있다.</p>
<pre><code class="language-typescript">enum Role {
  ADMIN = 0,
  USER = 1,
  GUEST = 2,
}

const user1 = {
 role: Role.ADMIN,
}

const user2 = {
 role: Role.USER,
}

const user1 = {
 role: Role.GUEST,
}</code></pre>
<p>이런식으로 사용할 수 있다.
주석 없이도 이해할 수 있는 코드가 좋은 코드라고 배웠고, 이렇게하면 주석 없이도 해당 코드를 이해하는데 어려움이 들지 않을 것이다.</p>
<h2 id="any-타입과-unknown-타입">any 타입과 unknown 타입</h2>
<p><code>any</code> 타입과 <code>Unknown</code> 타입은 변수에 저장할 값이 확실하지 않을 때 활용할 수 있는 타입이다.
비슷해보이지만, 두 타입은 차이가 있다.
<code>any</code>는 어떤 타입이던 상관 없음을, <code>unknown</code>은 어떤 타입인지 모름을 의미한다는 개념만 가지고 코드를 살펴보자.</p>
<h3 id="변수-할당">변수 할당</h3>
<pre><code class="language-typescript">let anyVar: any = 10;
anyVar = &quot;hi&quot;;</code></pre>
<pre><code class="language-typescript">let unknownVar: unknown = 10;
unknownVar = &quot;hi&quot;;</code></pre>
<p>두 타입 모두 저런 식으로 타입을 왔다갔다 하면서 사용할 수 있다.</p>
<h3 id="메서드-사용">메서드 사용</h3>
<pre><code class="language-typescript">let anyVar: any = 10;

anyVar.toUpperCase();
anyVar.toFixed();</code></pre>
<pre><code class="language-typescript">let unknownVar: unknown = 10;

unknownVar.toUpperCase(); // error!!
unknownVar.toFixed(); // error!!</code></pre>
<p><code>any</code> 타입은 어떤 타입이든 상관없기에, 타입 검사를 하지 않고, 따라서 어떤 메서드를 사용하던 간에 에러를 발생시키지 않는 모습이다.
<code>unknown</code> 타입은 어떤 타입인지 모르기에, 특정 메서드 사용을 허용하지 않는다.</p>
<h3 id="다른-변수에-할당">다른 변수에 할당</h3>
<pre><code class="language-typescript">let anyVar: any = 10;
let num : number = 20;
num = anyVar;</code></pre>
<pre><code class="language-typescript">let unknownVar: unknown = 10;
let num : number = 20;
num = unknownVar; // error!!</code></pre>
<p>위와 같은 맥락으로, <code>unknown</code> 에는 뭐가 들어있을지 모르기 때문에 다른 변수에 할당하는 것을 금지한다.</p>
<h3 id="그렇다면-unknown은-어떻게-사용해야-할까">그렇다면, unknown은 어떻게 사용해야 할까?</h3>
<p>저렇게 아무 동작도 안된다면, <code>unknown</code> 타입은 어떻게 사용해야 할까?
나중에 글을 적겠지만, <code>타입 좁히기</code> 를 통해 가능하다.</p>
<pre><code class="language-typescript">if(typeof unknownVar === &quot;string&quot;) {
  unknownVar.toUpperCase(); // not error
}</code></pre>
<p>이렇게 <code>unknown</code> 타입의 변수가 <code>string</code> 타입임을 명시해줘야 비로소 <code>string</code> 타입의 메서드를 사용할 수 있게 되는 것이다.</p>
<h2 id="void-타입과-never-타입">void 타입과 never 타입</h2>
<p>아무것도 없음을 나타내기 위해 사용할 수 있는 타입으로 <code>void</code>와 <code>never</code>가 있다.</p>
<h3 id="void">void</h3>
<p><code>void</code>는 단어 그 자체로, <code>존재하지 않음</code> <code>공허</code> 와 같은 의미를 지닌다.
따라서, 아무것도 없음을 나타낸다고 생각하면 된다.</p>
<pre><code class="language-typescript">function func1() : void {
  console.log(&#39;아무것도 없어요!!&#39;):
}</code></pre>
<p>이렇게 어떠한 return 값도 반환하지 않는 함수에서 주로 사용한다.</p>
<p>기존에 배운 <code>undefined</code> 와 <code>null</code>을 아무것도 없음을 나타내는데 사용하면 될 것 같지만, 두 타입은 함수의 반환 타입으로 지정하는 순간, 실제로 <code>undefined</code>와 <code>null</code>을 반환해줘야 하기 때문에 문제가 된다.</p>
<p>물론, <code>void</code> 타입은 변수에 타입으로 지정할 수 있다.</p>
<pre><code class="language-typescript">let var1 : void;

var1 = 1; // error!!
var1 = &quot;hi&quot; // error!!
var1 = undefined // OK
var1 = null // error!! 하지만, strictNullChecks&quot;: false 라면 OK</code></pre>
<p>하지만, <code>undefined</code> 를 제외하고는 어떤 값을 할당해도 에러가 난다.
<code>tsconfig.json</code>에서 <code>&quot;compilerOptions</code> 의 옵션인 <code>&quot;strictNullChecks&quot;: false</code> 즉, 엄격한 null 검사 옵션을 꺼주게 되면, null 까지도 할당 가능하다.</p>
<h3 id="never">never</h3>
<p><code>never</code> 타입은 <code>불가능한</code> <code>존재하지 않는</code> 이라는 의미를 갖는 타입이다.</p>
<p>정상적으로 종료가 되지 않기에, 예외를 던지거나, 무한 루프를 돌아서 종료에 도달할 수 없어 절대 반환이 불가능한 상황에 사용한다.</p>
<pre><code class="language-typescript">function func2(): never {
  while(true) {}
}

funciton func3(): never {
  throw new Error();
}</code></pre>
<p>물론, <code>never</code> 타입도 변수의 타입으로 지정할 수 있다.</p>
<pre><code class="language-typescript">let neverVar: never;

neverVar = 1; // error!!
neverVar = &#39;hi&#39;; // error!!
neverVar = undefined; // error!!
neverVar = null; // error!! strictNullChecks&quot;: false 여도 error.
</code></pre>
<p>그 어떤 값도 <code>never</code> 타입의 변수에 저장할 수 없다.
기존에 <code>void</code> 타입 변수에는 <code>strictNullChecks&quot;: false</code> 옵션을 꺼주면 <code>null</code> 을 할당할 수 있었는데, <code>never</code> 타입은 허용하지 않는다.</p>
<h3 id="console-검사">console 검사</h3>
<p>그렇다면, 두 변수를 <code>console.log()</code>로 찍게되면 어떻게 될까? 라는 궁금증이 들어서 해보게 되었다.</p>
<p>기존 JS에서는 변수를 선언만 한다면, <code>undefined</code> 가 할당되고, 출력해도 동일하다.</p>
<p>따라서 <code>undefined</code>를 할당할 수 있는 <code>void</code> 타입은 <code>undefined</code> 가 출력될 것으로 예상하지만, <code>never</code> 타입은 어떻게 나올지가 궁금했다.</p>
<pre><code class="language-typescript">let voidVar : void;

console.log(voidVar); // undefined</code></pre>
<p>내 예상대로,<code>void</code> 타입은 <code>undefined</code> 를 출력한다.</p>
<pre><code class="language-typescript">let neverVar : void;

console.log(neverVar); // error. &quot;변수가 할당되기 전에 사용되었습니다.&quot; </code></pre>
<p><code>never</code> 타입의 변수를 출력할 때는, <code>변수가 할당되기 전에 사용되었습니다.</code> 라는 에러 문구를 띄워준다!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS에서의 OOP]]></title>
            <link>https://velog.io/@ldh-dodo/JS%EC%97%90%EC%84%9C%EC%9D%98-OOP</link>
            <guid>https://velog.io/@ldh-dodo/JS%EC%97%90%EC%84%9C%EC%9D%98-OOP</guid>
            <pubDate>Tue, 22 Apr 2025 15:50:18 GMT</pubDate>
            <description><![CDATA[<figure>
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/305f0bc4-51d8-4be8-b6a7-348b8080ffde/image.png" alt="OOP의 네 가지 특성">
  <figcaption style="font-size: 12px; color: gray;">출처: https://blog.algomaster.io/p/basic-oop-concepts-explained-with-code</figcaption>
</figure>


<p><code>OOP(Object Oriented Programming)</code> 이란 무엇이고, JS에서 어떻게 객체지향을 구현할 수 있을까?</p>
<h2 id="객체란">객체란?</h2>
<h3 id="객체란-1">객체란?</h3>
<p>일단, <code>객체</code>에 대해 알아보자.
<code>객체</code> 란 속성(데이터)과 기능(동작)을 가진 독립적인 단위를 말한다.
예를 들어, <code>강아지</code> 라는 동물을 객체라고 했을 때, <code>이름</code> 이라는 <code>속성</code>을 가지고, <code>짖기</code> 라는 <code>동작</code> 을 한다는 걸 알 수 있다.</p>
<p>다시 말해서, 데이터와 그 데이터를 조작하는 기능까지 갖춘 구조체를 <code>객체</code> 라고 한다.</p>
<h3 id="객체-지향이란">객체 지향이란?</h3>
<p><code>객체 지향</code> 이란, 프로그램 전체를 이런 객체들의 상호작용으로 구성하는 방식이다.
현실 세계처럼 여러 객체들이 소통하고 상호작용하여 문제를 해결하는 구조이다.
객체 지향은 <code>누가 무엇을 할 것인가</code> 에 초점을 맞추기 때문에, 각 객체가 자신의 역할과 책임을 가지고, 서로 메시지를 주고 받으면서 동작한다.</p>
<h3 id="절차-지향이란">절차 지향이란?</h3>
<p>객체 지향의 대조적인 개념인 <code>절차 지향</code> 이란 프로그램을 <code>순서대로 실행되는 절차</code> 중심으로 구성하는 방식이다.
절차 지향은 <code>누가 무엇을 할 것인가</code> 가 아닌, <code>어떻게 할 것인가</code> 에 초점을 맞춘다.</p>
<p>아래는 객체 지향과 절차 지향의 차이를 간단한 예시로 나타낸 코드이다.</p>
<pre><code class="language-javascript">// 절차 지향
catBark(); 
dogBark();
wolfBark();

-------------
// 객체 지향
cat.bark();  
dog.bark(); 
wolf.bark();</code></pre>
<h4 id="그럼-절차-지향은-나쁜가">그럼 절차 지향은 나쁜가?</h4>
<p>당연하게도, 절차 지향만의 장점이 존재한다. 절차 지향은 단순하고 직관적인 흐름과 성능 최적화에 유리하기 때문에, 하드웨어와 밀접한 작업, 임베디드 시스템, 시스템 프로그래밍과 같은 분야에서 주로 사용된다. 절차 지향은 특히 성능이 중요한 환경에서 자주 사용된다. 결국 메모리 관리나 최적화가 중요한 상황에서는 절차 지향이 매우 효과적이다.</p>
<p>다시 말해, 절차 지향은 단순하고 성능 중심의 문제를 해결하는 데 적합하고, 객체 지향은 유지보수성, 확장성, 재사용성이 중요한 대규모 시스템에 적합한 것이다. 사용되는 분야와 목적이 다를 뿐이다.</p>
<h2 id="왜-oop를-써야하는가">왜 OOP를 써야하는가?</h2>
<p>그렇다면, 왜 OOP를 써야할까?</p>
<h3 id="코드로-이해해보자">코드로 이해해보자</h3>
<p>객체 지향에서는 <code>상속</code> 이라는 개념이 존재한다.
<code>상속</code>은 <code>공통된 기능은 재사용</code>하고, <code>새로운 기능은 확장</code>할 수 있게끔 하는 개념으로, 코드의 중복을 줄이고, 기존 코드는 절대 수정할 필요가 없이 새로운 객체를 추가할 수 있다.</p>
<pre><code class="language-javascript">// 절차지향

catBark() {
 console.log(&#39;고양이가 소리를 낸다. 야옹 야옹!&#39;); 
}

dogBark() {
  console.log(&#39;강아지가 소리를 낸다. 멍멍!&#39;);
}

wolfBark() {
  console.log(&#39;나는 늑대야. 아우우~~&#39;);
}

wolfBite() {
    console.log(&#39;나는 무니까 조심해&#39;);
}
--------------------------------------------------
// 객체 지향

class Animal {
  constructor(name){
    this.name = name;
  }

  bark(val) {
    console.log(`${this.name}가 소리를 낸다. ${val}`);
  }
}

class Cat extends Animal {
}

class Dog extends Animal {
}

class Wolf extends Animal {
     bark() {
      console.log(`나는 ${this.name}야. 아우우~~`);
    }

      bite() {
      console.log(&#39;나는 무니까 조심해&#39;);
    }
}
const dog = new Dog(&#39;강아지&#39;);
const cat = new Cat(&#39;고양이&#39;);
const wolf = new Wolf(&#39;늑대&#39;);

dog.bark(&#39;멍멍!&#39;);
cat.bark(&#39;야옹 야옹!&#39;);
wolf.bark();
wolf.bite();
</code></pre>
<h4 id="재사용성">재사용성</h4>
<p>객체 지향에서는 Cat Dog Wolf 클래스는 똑같이 bark() 라는 메서드를 가지고 있지만, 중복되는 부분은 재사용하고 있다.
반면에, 절차 지향에서는 같은 내용이지만 catBark() dogBark() wolfBark() 와 같이 다른 이름의 함수로 정의를 하고 있는 것을 알 수 있다.</p>
<p>이렇듯, 객체 지향을 통해 새로운 클래스를 추가할 때, 기존 코드를 변경하지 않고 새로운 클래스를 재사용하여 간편하게 추가할 수 있다.</p>
<h4 id="유지보수성">유지보수성</h4>
<p>만약, <code>catBark()</code> <code>dogBark()</code> 의 메서드에서, <code>~가 소리를 낸다 -&gt; ~는 소리를 낸다</code> 의 형식으로 바꿔야 하는 상황을 생각해보자.
그리고, 이 메서드는 1억개가 있다고 가정하자.</p>
<p>절차 지향으로 구현된 이 메서드들을 바꾸기 위해서는, 1억 번의 수정 작업이 필요할 것이다.
이 번거로운 과정 속에서, 오타가 발생할 수도 있고, 빠뜨린 메서드가 있을 수 있다.</p>
<p>하지만, 객체 지향으로 구현된 이 메서드들을 바꾸기 위해서는, 부모 클래스인 <code>Animal</code> 만 단 한번 수정하면 된다.</p>
<p>극단적인 예시이긴 하지만, 이렇게 비교하니 얼마나 객체 지향이 유지보수성 측면에서 뛰어난지 와닿는다.</p>
<h4 id="확장성">확장성</h4>
<p>만약 새로운 메서드를 추가하는 상황이라고 가정해보자.
<code>run()</code> 이라는 메서드를 추가하려면, 절차 지향에서도 <code>catRun()</code> <code>dogRun()</code> 과 같이 추가할 수 있다.</p>
<p>하지만, 객체 지향에서는 상속과 오버라이드를 통해, 새로운 메서드를 추가하는 방식이 규칙적이고 일관성 있는 확장을 가능하게 만들어, 코드의 가독성과 유지보수성을 크게 향상시킨다.</p>
<h2 id="객체-지향의-특성">객체 지향의 특성</h2>
<p>객체 지향의 특성은 다음과 같다.</p>
<ol>
<li>캡슐화(Encapsulation)</li>
<li>상속(Inheritance)</li>
<li>다형성(Polymorphism)</li>
<li>추상화(Abstraction)</li>
</ol>
<h3 id="1-캡슐화encapsulation">1. 캡슐화(Encapsulation)</h3>
<p>캡슐화는 객체의 데이터를 보호하고, 외부에서 직접 접근하지 못하게 한다.</p>
<h4 id="예시-코드">예시 코드</h4>
<pre><code class="language-javascript">class People {
  #age;

  constructor(age) {
    this.setAge(age);
  }

  setAge(age) {
    this.#age = age;
  }

  getAge(age) {
    return this.#age;
  }
}</code></pre>
<h4 id="캡슐화의-장점">캡슐화의 장점</h4>
<p>위의 코드를 보면, <code>setAge()</code> 를 통해 나이를 설정할 수 있고, <code>getAge()</code> 를 통해 나이 정보를 얻을 수 있다.</p>
<p>이렇게 데이터를 보호하고, 외부에서 직접 접근하지 못하게 하면, 어떤 이점이 있을까?</p>
<h5 id="데이터-무결성-보호">데이터 무결성 보호</h5>
<p><code>데이터의 무결성</code> 이란 <code>데이터가 정확하고 일관되며 신뢰할 수 있는 상태로 유지되는 것</code> 을 의미한다.
<code>setAge()</code> 함수를 살짝 변형해보겠다.</p>
<pre><code class="language-javascript">...
setAge(age) {
     if(typeof(age) !== &#39;number&#39; || age &lt; 0) throw new Error(&quot;나이는 0보다 작을 수 없습니다&quot;);
      this.#age = age;
}</code></pre>
<p>만약, 이런식으로 유효성 검사를 추가한다면 나이 값에 0보다 작은 값이 들어가는 것을 방지해줄 수 있다.</p>
<h4 id="캡슐화의-단점">캡슐화의 단점</h4>
<p>모든 속성에 대해 getter와 setter을 작성하다보면, 당연하게도 코드의 길이가 비약적으로 늘어날 것이다.
또한, 테스트 코드에서 내부 상태를 확인하고 싶을 때, 직접 접근이 제한되어 우회하여 접근하는 것도 번거로울 수 있다.</p>
<h3 id="2-상속inheritance">2. 상속(Inheritance)</h3>
<p><code>상속</code> 이란 부모 클래스의 속성과 메서드를 자식 클래스가 물려받아 재사용하는 특성이다.
위에서 살펴봤으니 넘어가겠다.</p>
<h3 id="3-다형성polymorphism">3. 다형성(Polymorphism)</h3>
<p>다형성은 같은 이름의 메서드가 서로 다른 동작을 수행할 수 있는 특성이다.
원래라면 다형성은 <code>오버라이딩</code> 과 <code>오버로딩</code> 을 통해 구현된다.
<code>오버라이딩</code> 은 상속 받은 메서드를 재정의하는 방식이고,
<code>오버로딩</code> 은 같은 메서드 이름으로 다른 매개변수를 받는 방식이다.</p>
<p>하지만 JS에서는 <code>오버로딩</code> 을 지원하지 않는다.
다른 언어에서는 인수의 개수에 따라 여러 메서드를 정의할 수 있지만, JS에서는 여러 메서드를 정의하면, 가장 나중에 정의한 메서드에 덮어 씌워지기 때문이다.</p>
<p>물론, 비슷하게 구현하는 방법은 존재한다.</p>
<p>다형성의 장점도 위에서 살펴보았으니 넘어가겠다.</p>
<h3 id="4-추상화abstraction">4. 추상화(Abstraction)</h3>
<p><code>추상화</code> 란 복잡한 구현을 숨기고, 중요한 부분만을 드러내어 단순화하는 특성이다.
사용자는 클래스 내 메서드를 통해, 내부 로직을 알 필요 없이 동작을 수행할 수 있다.</p>
<p>혹은 아래와 같이 추상 클래스를 구현하도록 할 수도 있다.</p>
<pre><code class="language-javascript">class Car { // 추상 클래스
  startEngine() {
    throw new Error(&quot;startEngine() 메서드는 서브 클래스에서 구현해야 합니다.&quot;);
  }

  stopEngine() {
    throw new Error(&quot;stopEngine() 메서드는 서브 클래스에서 구현해야 합니다.&quot;);
  }
}

class ElectricCar extends Car {
  startEngine() {
    console.log(&quot;전기차 엔진을 시작합니다.&quot;);
  }

  stopEngine() {
    console.log(&quot;전기차 엔진을 멈춥니다.&quot;);
  }
}</code></pre>
<h2 id="oop-in-js">OOP in JS</h2>
<p>JS는 원래 프로토타입 기반(prototype-based) 언어로 설계되었다.
즉, 클래스 개념이 존재하지 않았고, 객체 간의 상속은 프로토타입을 이용하여 구현되었다.
ES6 부터 클래스 개념이 도입되었고, 이를 통해 OOP를 더 직관적으로 구현할 수 있게 된 것이다.</p>
<h3 id="상속">상속</h3>
<p>상속은 프로토타입을 통해 아래와 같이 구현될 수 있다.</p>
<pre><code class="language-javascript">function Animal(name) {
  this.name = name;

  this.bark = function() {
    console.log(`${this.name}는 짖는다`);
  }
}

function Dog(name) {
  Animal.apply(this, [name]); // 생성자 및 프로퍼티 상속
}

Dog.prototype = Object.create(Animal.prototype); // 메서드 상속
Dog.prototype.constructor = Dog; // 위 설정으로 인해, 생성자가 Animal. 따라서 생성자를 Dog로 변경해줘야 함 .
</code></pre>
<h3 id="캡슐화">캡슐화</h3>
<p>위에서 설명했듯이, <code>나이</code> 라는 속성에, 잘못된 값이 들어가지 않도록 유효성 검사를 해줄 수 있다.</p>
<pre><code class="language-javascript">var person = {
  age : 20,
};

person.age = -20;

// 위와 같은 상황을 방지해야 함!!

var person = {
  age : 20,
  setAge : function(newAge) {
    if(typeof(newAge) !== &quot;number&quot; || newAge &lt; 0) {
      throw new Age(&quot;Invalid Age&quot;);
    } else {
      this.age = newAge;
    }
  },
  getAge: function() {
    return this.age;
  }
};
</code></pre>
<p>다형성과 추상화는 사실상 상속과 캡슐화의 변형으로 만들어지는 것이기 때문에, 코드는 생략하도록 하겠다.</p>
<h1 id="js에서의-oop">JS에서의 OOP</h1>
<p><code>ES2015</code> 이전에는 클래스 개념이 존재하지 않았다.
그렇다면 어떻게 객체를 정의하고 인스턴스를 만들었는가?</p>
<p>가장 처음에는 <code>객체 리터럴 방식</code> 이 존재했다.</p>
<h2 id="object-literal-pattern객체-리터럴">Object Literal Pattern(객체 리터럴)</h2>
<pre><code class="language-js">const healthObj = {
  name: &quot;달리기&quot;,
  lastTime: &quot;PM10:12&quot;,
  showHealth() {
    console.log(
      this.name + &quot;님, 오늘은 &quot; + this.lastTime + &quot;에 운동을 하셨네요&quot;
    );
  },
};

healthObj.showHealth();</code></pre>
<p>여러 인스턴스를 생성할 필요가 없고, 단일 인스턴스(1회용 객체)로 사용하기 좋다.</p>
<p>하지만 만약 여러 개의 비슷한 객체를 만들어야 한다면,
객체 리터럴을 반복 선언할 경우 메서드가 각 객체에 중복 정의되어 메모리 비효율과 코드 관리의 어려움이 발생할 수 있다.
이때부터는 생성자 함수, 프로토타입 패턴, 혹은 ES6 클래스 같은 인스턴스 생성 방식이 필요하다.</p>
<h2 id="es6-class-patternes2015-이후-도입">ES6 Class Pattern(ES2015 이후 도입)</h2>
<pre><code class="language-js">class Health {
  constructor(name, healthTime) {
    this.name = name;
    this.healthTime = healthTime;
  }

  showHealth() {
    console.log(
      this.name + &quot;님, 오늘은 &quot; + this.healthTime + &quot;에 운동을 하셨네요&quot;
    );
  }
}

const ho = new Health(&quot;crong&quot;, &quot;12:12&quot;);
ho.showHealth();</code></pre>
<p>내부적으로는 여전히 <code>prototype</code> 기반으로 작동하고, 가독성이 좋아 클래스 상속, 추상화 등 OOP 개념을 사용하기 편리하다는 장점이 있다.</p>
<h2 id="constructor-function-pattern">Constructor Function Pattern</h2>
<pre><code class="language-js">function Health(name, healthTime) {
  this.name = name;
  this.healthTime = healthTime;
  this.showHealth = function () {
    console.log(
      this.name + &quot;님, 오늘은 &quot; + this.healthTime + &quot;에 운동을 하셨네요&quot;
    );
  };
}

const ho = new Health(&quot;crong&quot;, &quot;12:12&quot;);
ho.showHealth();</code></pre>
<p><code>function</code>을 생성자로 사용하는 방식이다.</p>
<p><code>new</code> 키워드로 호출하면 <code>this</code>가 새로운 인스턴스를 가리키기 때문에 가능한 방식이다.</p>
<p>하지만 모든 인스턴스가 <code>showHealth</code> 메서드를 개별적으로 갖기 때문에 메모리 비효율적이다.</p>
<pre><code class="language-js">const ho1 = new Health(&quot;crong&quot;, &quot;12:12&quot;);
const ho2 = new Health(&quot;babo&quot;, &quot;10:00&quot;);

console.log(ho1.showHealth === ho2.showHealth); // false</code></pre>
<p><code>ho1.showHealth</code> 와 <code>ho2.showHealth</code> 는 다른 함수 객체이다.</p>
<p>즉, 인스턴스를 100개 만들면 <code>showHealth</code> 함수도 100개가 생긴다는 의미이다.</p>
<p><code>prototype</code> 기반일 경우, 인스턴스를 1억개 만들어도 <code>showHealth</code> 함수는 1개이기 때문에, 메모리 효율성에서 차이가 난다.</p>
<h2 id="prototype-pattern">Prototype Pattern</h2>
<p>공통 메서드는 <code>prototype</code>에 보관하여 메모리를 절약하는 방식이다.</p>
<pre><code class="language-js">function Health(name, healthTime) {
  this.name = name;
  this.healthTime = healthTime;
}

Health.prototype.showHealth = function () {
  console.log(
    this.name + &quot;님, 오늘은 &quot; + this.healthTime + &quot;에 운동을 하셨네요&quot;
  );
};

const ho = new Health(&quot;crong&quot;, &quot;12:12&quot;);
ho.showHealth();</code></pre>
<p>모든 인스턴스가 <code>prototype</code> 의 메서드를 공유하기 때문에, 메모리를 절약할 수 있다.</p>
<h2 id="class-pattern-vs-prototype-pattern">Class Pattern vs Prototype Pattern</h2>
<p>JavaScript의 ES6 클래스 패턴과 기존 프로토타입 패턴은 내부적으로 동일한 프로토타입 기반 상속을 사용한다.</p>
<p>하지만 문법과 사용성 측면에서 차이가 있다.</p>
<ul>
<li><p><strong>프로토타입 패턴</strong>은 생성자 함수와 prototype 객체를 직접 다루는 방식으로,<br>복잡할 수 있지만 동작 원리를 명확히 이해하기에 좋다.</p>
</li>
<li><p><strong>클래스 패턴</strong>은 문법적 설탕(Syntactic Sugar)으로,<br>코드가 간결하고 가독성이 좋아 유지보수와 확장성이 뛰어나다.</p>
</li>
</ul>
<p>두 패턴 모두 공통 메서드는 프로토타입에 한 번만 존재하여,<br>수많은 인스턴스를 생성해도 메모리 효율이 뛰어나다는 장점이 있다.</p>
<h3 id="문법적-설탕syntatic-sugar">문법적 설탕(Syntatic Sugar)</h3>
<p><code>문법적 설탕</code> 이란 기존에 가능했던 기능을 더 보기 좋고, 쓰기 쉽게 만들어주는 문법적 편의 기능 이다.</p>
<p>즉, 똑같은 일을 하지만, 코드를 더 간결하고 직관적으로 쓸 수 있게 <code>맛있게</code> 만들어 놓은 문법이다.</p>
<p>그래서 <code>설탕</code> 이라는 표현이 들어갔다고 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Blink와 Rendering Process]]></title>
            <link>https://velog.io/@ldh-dodo/Blink%EC%99%80-Rendering-Process</link>
            <guid>https://velog.io/@ldh-dodo/Blink%EC%99%80-Rendering-Process</guid>
            <pubDate>Tue, 22 Apr 2025 08:55:03 GMT</pubDate>
            <description><![CDATA[<h1 id="렌더링-엔진">렌더링 엔진</h1>
<p><code>렌더링 엔진(Rendering Engine)</code> 은 HTML, CSS, JS 등 웹 문서를 해석해서 사용자에게 시각적으로 보여주는 기능을 담당하는 소프트웨어이다.</p>
<p>대표적인 렌더링 엔진으로는 <code>Blink</code> <code>Webkit</code> <code>Gecko</code> 정도가 있다.</p>
<h2 id="blink">Blink</h2>
<p>Chrome은 원래 Webkit을 사용했지만, 현재는 Blink를 사용중이다.
Blink는 Webkit에서 파생되어 Google에 의해 만들어졌다.</p>
<p>기존의 Webkit은 <code>모놀리식(Monolithic) 구조</code>로, 렌더링 엔진의 내부 기능들이 하나의 프로세스 내에서 밀접하게 연결되어 작동하는 구조였다.
하지만, Chrome은 각각의 탭들을 독립된 프로세스로 실행하려는 지향했고, 이를 구현하기 위해 Webkit 내에서 크롬 전용 포크를 만들어서 관리했지만, 점점 관리가 복잡해졌다고 한다.</p>
<p>이러한 구조적 및 개발 방향성 차이로 인해, 구글은 Chrome에 적합한 독립된 렌더링 엔진을 개발하고자 했고, 이게 바로 Blink이다.</p>
<h2 id="rendering-process">Rendering Process</h2>
<p><code>렌더링 프로세스</code> 란, 사용자가 웹사이트에 접근했을 때, HTML/CSS/JS를 받아 화면에 실제로 보이게 하기까지의 과정을 의미한다.</p>
<p>HTML/CSS/JS를 수신했다고 가정하고, 아래와 같은 순서로 렌더링 프로세스가 진행된다.</p>
<ol>
<li>HTML 파싱 -&gt; DOM 트리 생성</li>
<li>CSS 파싱 -&gt; CSSOM 트리 생성</li>
<li>DOM + CSSOM -&gt; 렌더 트리 생성</li>
<li>Layout</li>
<li>Paint</li>
<li>Composite</li>
<li>화면 출력</li>
</ol>
<h3 id="1-html-파싱">1. HTML 파싱</h3>
<div align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/ed4da7f0-3e21-4644-8b4a-e23e9cce01d1/image.png"/>
</div>
브라우저는 HTML 문서를 파싱하면서 `DOM(Document Object Model)` 이라는 트리를 만든다.

<h3 id="2-css-파싱">2. CSS 파싱</h3>
<div align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/3fe8c10f-9df8-44e2-961d-2508cab428f8/image.png"/>
</div>

<p>CSS 파일이나 <code>&lt;style&gt;</code> 태그 안의 스타일은 CSS 파서가 해석해서 <code>CSSOM(CSS Object Model)</code> 트리를 만든다.
<code>어떤 노드에 어떤 스타일이 적용될지</code> 에 대한 정보를 가진다.</p>
<h3 id="3-렌더-트리-생성">3. 렌더 트리 생성</h3>
<div align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/07a3c7c5-57cb-479b-93a2-ac33cea57236/image.png"/>
</div>
1번과 2번 과정의 결과물인 `DOM` 과 `CSSOM`을 결합해서 `렌더 트리(Render Tree)`를 만든다.
렌더 트리는 `실제로 화면에 표시되는 요소들` 만 포함한다.
즉, `display: none`과 같은 요소는 포함시키지 않는다.

<h3 id="4-layout혹은-reflow">4. Layout(혹은 Reflow)</h3>
<p>렌더 트리를 기반으로 각 요소의 정확한 위치(x, y), 크기(width, height)를 계산하는 단계다.
폰트, 이미지 크기, 뷰포트 크기 등이 영향을 미친다.
레이아웃 계산은 문서의 크기나 요소가 바뀔 때마다 전체 페이지 혹은 일부 요소들의 레이아웃을 다시 계산하는 작업이 발생할 수도 있다.</p>
<h3 id="5-paint">5. Paint</h3>
<p>Layout에서 계산된 위치 정보와 크기를 바탕으로 브라우저가 화면을<code>실제 픽셀 단위로 그리는 작업</code> 을 한다.
이 과정에서 CPU가 많이 사용된다.</p>
<h3 id="6-composite">6. Composite</h3>
<p>웹 페이지는 여러 <code>레이어</code> 로 나뉘는데, 해당 레이어들을 GPU에 전달해서 화면에 조합하는 과정이다.
애니메이션, 3D, transform, opacity(투명도) 등은 각각 별도의 레이어로 만들어져서 성능 향상을 도모한다.</p>
<h3 id="7-화면-출력">7. 화면 출력</h3>
<p>최종적으로 GPU가 각 레이어를 조합해 하나의 완성된 프레임을 화면에 표시한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[컴퓨터 네트워크 - 네트워크 심화]]></title>
            <link>https://velog.io/@ldh-dodo/%EC%BB%B4%ED%93%A8%ED%84%B0-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%8B%AC%ED%99%94</link>
            <guid>https://velog.io/@ldh-dodo/%EC%BB%B4%ED%93%A8%ED%84%B0-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%8B%AC%ED%99%94</guid>
            <pubDate>Wed, 02 Apr 2025 07:07:41 GMT</pubDate>
            <description><![CDATA[<h1 id="안정성을-위한-기술">안정성을 위한 기술</h1>
<p>사용자가 폭증하면 서버가 불안정해지고 부하가 가증될 것이다.
이럴 때 어떤 기술을 사용해서 해결할까?</p>
<p><code>네트워크가 안정적이다</code> 라고 말할 때의 <code>안정성</code>은 <code>특정 기능을 언제든 균일한 성능으로 수행할 수 있는 특성</code> 으로 정의될 수 있다.
그렇다면 안정성은 어떻게 수치화할 수 있을까?
안정성의 정도를 나타내는 용어로 <code>가용성</code> 과 <code>고가용성</code> 이라는 용어가 있다.</p>
<h2 id="가용성">가용성</h2>
<p><code>가용성</code> 이란 컴퓨터 시스템이 특정 기능을 실제로 수행할 수 있는 시간의 비율이다.
다시 말해, 전체 사용 시간 중에서 정상적인 사용 시간을 의미한다.
이를 수식으로 나타내면 다음과 같다.</p>
<p>$가용성 = \frac{업타임}{다운 타임}$</p>
<p><code>업타임</code>이란 정상적인 사용시간을, <code>다운 타임</code>이란 정상적인 사용이 불가능한 시간을 의미한다.
해당 수식의 결괏값이 크다는 것은, 전체 사용 시간 중에서 대부분을 사용 가능하다는 의미이며, 이를 <code>가용성이 높다</code> 라고 말한다.
가용성이 높은 성질을 <code>고가용성(HA; High Availability)</code></p>
<p>일반적으로 안정적이라고 평가받는 시스템은 <code>99.999%</code> 이상을 목표로 한다.
<code>9가 다섯개</code> 라는 의미에서 <code>파이브 나인스</code> 라고도 한다.</p>
<p>가용성을 높이려면 다운타임을 낮춰야 한다.
다운타임의 발생 원인을 모두 찾아 원천적으로 차단하기는 실질적으로 어려우므로, 문제가 발생하덜도 계속 가능할 수 있도록 설계하는 것이 중요하다.
문제가 발생하덜도 기능할 수 있는 능력을 <code>결함 감내</code> 라고 부른다.</p>
<h2 id="이중화">이중화</h2>
<p><code>이중화</code> 란 결함을 감냏여 가용성을 높이기 위한 가장 기본적이고 대표적인 방법으로, 쉽게 말해서 <code>예비(백업)을 마련하는 방법</code> 이다.</p>
<p>이중화할 수 있는 대상은 다양하다.
<code>서버 컴퓨터</code> <code>네트워크 인터페이스(NIC)</code> <code>스위치</code> 와 같은 물리적 장비 뿐만 아니라, <code>데이터베이스</code> <code>웹 서버 프로그램</code> 등도 이중화할 수  있는 대상이다.
이중화할 수 있는 대상들은 대부분 <code>문제가 발생할 경우 시스템 전체가 중단될 수 있는 대상</code> 이라는 공통점이 있다.
<code>문제가 발생할 경우 시스템 전체가 중단될 수 있는 대상</code> 을 가리키는 용어가 있는데, 
이것이 바로 <code>단일 장애점(SPOF; Single Point Of Failure)</code> 이다.</p>
<p>예를 들어, 다음 그림에서 라우터가 고장나면 제공받는 서비스 전체가 동작하지 않기 때문에 SPOF는 라우터이다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/f7bc71b7-0d84-4df7-b02d-bcf539e01b8a/image.png" width="600px"/>
</p>

<p>가용성을 높이기 위해서는 SPOF를 이중화하는 것이 좋다.</p>
<h3 id="이중화-구성-방식">이중화 구성 방식</h3>
<p>이중화 구성에는 크게 두 가지 방식이 있다. 
<code>액티브/스탠바이(active-standby)</code> 와 <code>액티브/액티브(active-active)</code> 이다.</p>
<p><code>액티브</code> 란 가동 상태를 의미하며 <code>스탠바이</code> 는 액티브의 백업으로서 대기하는 상태를 의미한다.</p>
<h4 id="액티브스탠바이">액티브/스탠바이</h4>
<p>한 시스템은 가동하고, 다른 시스템은 백업 용도로 대기 상태(스탠바이)로 두는 이중화 구성 방식이다.
시스템에 문제가 발생할 경우 스탠바이 시스템이 자동으로 액티브 시스템을 대신하여 동작한다.
이렇게 자동전환 되는 기능을 <code>페일오버(failover)</code> 라고 한다.</p>
<p>하지만, 두 장비가 동시에 가동되는 것은 아니기에 하나의 장비를 사용할 때에 비해 성능상의 큰 변화를 기대하기는 어렵다.</p>
<h4 id="액티브액티브">액티브/액티브</h4>
<p>두 시스템 모두를 가동 상태로 두는 구성 방식이다.
이렇게 구성하면 부하를 분산시킬 수 있어 성능상의 이점을 가질 수 있다.
다만, 한 시스템에 문제가 발생하면 순간적으로 다른 시스템에 부하가 급증할 수 있으며, 이로 인해 추가적인 문제가 발생할 수 있다.</p>
<h2 id="다중화">다중화</h2>
<p>이중화가 <code>무언가를 이중으로 두는 기술</code> 이라면, <code>무언가를 여러 개 두는 기술</code> 인 다중화도 있다.
다중화하면 이중화된 구성에 비해 더욱 안정적인 운영이 가능하다.</p>
<p>이중화/다중화의 사례로 윈도우에서 사용되는 <code>티밍</code> 과 리눅스에서 사용되는 <code>본딩</code>이 있다.
두 기술은 여러개의 네트워크 인터페이스(NIC)를 이중화/다중화하여 마치 더 뛰어나고 안정적인 성능을 보유한 하나의 인터페이스처럼 보이게 하는 기술이다.
예를 들어, 1Gbps 속도를 지원하는 인터페이스 세 개를 티밍하여 하나의 3Gbps 인터페이스를 사용하는 것과 같은 효과를 얻을 수 있다.</p>
<h2 id="로드-밸런싱">로드 밸런싱</h2>
<p>고가용성을 요구하는 호스트는 일반적으로 서버이므로, 서버 중심으로 얘기해보자.
서버를 다중화했더라도 특정 서버에만 트래픽이 몰릴 경우, 가용성이 떨어질 수 있다.
<code>트래픽</code>의 사전적 정의는 <code>주어진 시점에 네트워크를 경유한 데이터의 양</code> 인데, 일반적으로 트래픽 측정은 노드에서 이루어지므로 <code>주어진 시점에 특정 노드를 경유한 패킷의 양</code> 이라고 이해하면 되겠다.</p>
<p>트래픽의 고른 분배를 위해 사용되는 기술이 <code>로드 밸런싱(load balancing)</code> 이다.
로드 밸런싱은 <code>로드 밸런서</code> 에 의해 수행된다.
로드 밸런서는 <code>L4 스위치</code> <code>L7 스위치</code> 로 수행할 수도 있지만, 로드 밸런싱 기능을 제공하는 소프트웨어를 설치하면 일반 호스트로도 로드 밸런서를 사용할 수 있다.</p>
<p>로드 밸런서는 다음 그림처럼 이중화나 다중화된 서버와 클라이언트 사이에 존재한다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/b164860a-1188-4aee-882b-c2068b1eeeb6/image.png" width="600px"/>
</p>


<p>클라이언트는 로드 밸런서에 요청을 보내고, 로드 밸런서는 해당 요청을 각 서버에 균형있게 분배한다.</p>
<p>다중화된 서버 환경에서 어떤 서버에 문제가 생긴다면 다른 서버들이 이를 빠르게 감지할 수 있어야 할텐데, 현재 상태를 주기적으로 체크하는 검사를 <code>헬스 체크</code> 라고 한다.
이 헬스 체크는 주로 로드 밸런서에 의해 수행된다.</p>
<h3 id="로드-밸런싱-알고리즘">로드 밸런싱 알고리즘</h3>
<p>로드 밸런서가 요청을 전달할 수 있는 서버가 여러 개 있을 경우, 어떤 서버에 요청을 전달해야 할까?
부하가 균등하게 분산되도록 부하 대상을 선택하는 방법을 <code>로드 밸런싱 알고리즘</code> 이라고 한다.</p>
<p>대표적인 로드 밸런싱 알고리즘에는 단순히 서버를 돌아가며 부하를 전달하는 <code>라운드 로빈 알고리즘(round robin)</code> 과 연결이 적은 서버부터 우선적으로 부하를 전달하는 <code>최소 연결 알고리즘(least connection)</code> 이 있다.</p>
<p>두 알고리즘은 <code>가중치</code> 를 부여해서 알고리즘에 따라 동작하되 가중치가 높은 서버가 더 많이 선택되어 더 많은 부하를 받도록 동작할 수도 있는데, 이를 각각 <code>가중치 라운드 로빈 알고리즘</code> 과
<code>가중치 최소 연결 알고리즘</code> 이라 한다.</p>
<p>라운드 로빈 알고리즘이 아래 그림과 같이 단순히 서버를 돌아가며 부하를 전달한다면,
가중치 라운드 로빈 알고리즘은 두 번째 그림과 같이 가중치가 다섯 배인 <code>서버 1</code>에 다섯 배 많은 부하를 전달한다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/aed152e0-6868-4b4e-b745-dcd139c97866/image.png" width="600px"/>
</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/dbf6a0dd-9a4e-4878-b39c-f837736cb469/image.png" width="600px"/>


<h2 id="암호와-인증서">암호와 인증서</h2>
<p>멀리 떨어진 컴퓨터와 민감한 메시지를 주고 받기 위해서는 암호화가 필요하다.</p>
<p><code>암호화</code> 란 원문 데이터를 알아볼 수 없는 형태로 변경하는 것을,
<code>복호화</code> 란 암호화된 데이터를 원문 데이터로 되돌리는 것을 의미한다.</p>
<h3 id="대칭-키-암호화-방식과-공개-키-암호화-방식">대칭 키 암호화 방식과 공개 키 암호화 방식</h3>
<p><code>키</code>와 <code>원문 데이터</code> 에 수학적 연산 과정을 거치면 <code>암호문</code> 이 생성된다.
이 수학적 연산 과정을 <code>암호화 알고리즘</code> 이라 한다.</p>
<p>주고 받는 데이터를 암호화하고 복호화하는 방법에는 대칭 키 암호화와 비대칭 키(공개 키) 암호화 방식이 있다.</p>
<h4 id="대칭-키-암호화-방식">대칭 키 암호화 방식</h4>
<p>다음 그림과 같이 암호화와 복호화에 동일한 키를 사용하는 방식이다.</p>
</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/79804555-0c66-46f1-9580-8458ac101863/image.png" width="600px"/>
</p>


<p>다만, 이 방식은 상대방에게 안전하게 키를 전달하기가 어렵다는 문제가 있다.
암호화 키와 복호화 키가 동일하기 때문에, 키가 유출된다면 큰 문제가 발생할 것이다.</p>
<p><code>키를 안전하게 상대방과 공유하는 방법</code> 은 사실상 <code>메시지를 안전하게 상대방과 공유하는 방법</code> 과 다를 바가 없으므로, 그 방법으로 메시지를 주고받으면 되지, 굳이 암호화를 할 필요가 없을 것이다.</p>
<p>그래서 등장한 것이 <code>공개 키 암호화(비대칭키 암호화)</code> 방식이다.</p>
<h4 id="공개-키-암호화비대칭키-암호화-방식">공개 키 암호화(비대칭키 암호화) 방식</h4>
<p>암호화를 위한 키와 복호화를 위한 키가 다른 방식이다.
이 한 쌍의 키를 각각 <code>공개 키</code> 와 <code>개인 키</code> 라 부른다.
암호화를 위해 사용된 <code>공개 키</code> 는 이름처럼 누구에게나 공개해도 무방하고, 암호화만을 위해 사용되었기 때문에 원문 메시지를 유추할 수 있지도 않다.</p>
<p>만약, A가 B에게 <code>안녕, 나는 A야</code> 라는 문자열을 안전하게 전송하고자 한다면, 어떤 일이 일어나는지 확인해보자.</p>
<ol>
<li>A가 B의 공개 키를 요청하고, B는 A에게 공개 키를 전달한다. (공개 키는 그냥 전송해도 된다.)</li>
<li>A는 전달받은 공개 키로 메시지를 암호화하고, B에게 전송한다. (제 3자가 보아도 암호문을 이해할 수 없다.)</li>
<li>개인 키를 가지고 있는 B가 암호를 복호화하여 <code>안녕 나는 A야</code> 라는 문자열을 확인할 수 있다.</li>
</ol>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/d0372825-8472-4d4f-bd76-ed4b96f3ea5d/image.png" width="600px"/>
</p>

<p>공개 키 암호화 방식은 대칭 키 암호화 방식에 비해 암호화 및 복호화에 시간과 부하가 상대적으로 많이 들지만, 키를 안전하게 공유할 수 있다는 장점이 있다.
하지만, 대칭 키 암호화는 암호화 및 복호화를 빠르게 수행할 수 있기 때문에, 이 두 방식을 결합하여 사용하는 방식이 많다.</p>
<h4 id="대칭-키-암호화-방식--공개-키-암호화-방식">대칭 키 암호화 방식 + 공개 키 암호화 방식</h4>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/f507aab5-04bf-4ccb-8c1b-557c4bc9913a/image.png" width="600px"/>
</p>

<p>공개 키 암호화 방식에서 복호하여 메시지 원문을 얻었다면, 이 방식에서는 대칭 키를 복호화한다고 생각하면 편하다.</p>
<p>대칭 키(세션 키)를 안전하게 공유하는 것이 문제 였기 때문에, 이 대칭 키를 <code>공개 키 암호화 방식</code> 으로 암호화 한 뒤, 전달 받는 쪽에서는 <code>개인키</code>로 복호화 하여 대칭 키를 얻고, 그 <code>대칭 키</code>로 메시지 본문을 얻는 방식이다.
이러한 방식으로 활용되는 <code>대칭 키</code>를 <code>세션 키</code> 라고한다.</p>
<h4 id="인증서와-디지털-서명">인증서와 디지털 서명</h4>
<h5 id="인증서">인증서</h5>
<p><code>인증서</code> 라는 용어는 일반적으로 <code>공개 키 인증서</code> 를 일컫는다.
<code>공개 키 인증서</code> 란 공개 키와 공개 키의 유효성을 입증하기 위한 전자 문서이다.</p>
<p>내 컴퓨터가 웹 서버와 공개 키 암호화 방식으로 통신한다고 가정했을 때, 나는 웹 서버로부터 공개 키를 전달받게 될 것이다.
그런데, <code>과연 이 공개키를 신뢰할 수 있는가?</code></p>
<p>이에 대한 신뢰성을 위해 웹 서버는 아래와 같이 여러 내용을 포함한 인증서를 전송한다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/5da773bb-bd0b-422a-8e7d-e759412b1eaf/image.png" width="600px"/>
</p>


<p>이러한 인증서는 <code>인증 기관(CA; Certification Authority)</code> 라는 제 3의 기관에서 발급한다.
<code>인증 기관</code> 은 인증서의 발급, 검증, 저장과 같은 역할을 수행할 수 있는 공인 기관이다.</p>
<p>인증서에는 <code>이 공개 키 인증서는 진짜야. 내가 보증할게</code> 라는 내용을 담은 <code>서명(signature)</code>이 있고, 이 값을 바탕으로 인증서를 검증할 수 있다.</p>
<p>서명 값은 <code>인증서 내용에 대한 해시 값(fingerprint)</code> 을 <code>CA 의 개인 키로 암호화하는 방식</code> 으로 만들어진다.
CA는 이렇게 얻어낸 정보를 서명 값으로 삼아 클라이언트에게 인증서와 함께 전달한다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/5f548026-e224-4a2c-8196-ab64e58e1c0b/image.png" width="600px"/>
</p>

<h5 id="디지털-서명">디지털 서명</h5>
<p><code>디지털 서명</code> 이란 개인 키로 암호화 된 메시지를 공개 키로 복호화함으로써 신원을 증명하는 절차를 말한다.</p>
<p><code>CA가 보낸 서명</code> 은 <code>해시 값을 개인키로 암호화 한 것</code> 이라고 했다.
그럼 우린 이 서명을 복호화하여 해시 값을 얻고, 우리가 직접 인증서 데이터에 대한 해시 값을 구한 뒤, 비교할 것이다.
값이 일치한다면 인증서는 확실히 CA의 개인 키로 만들어졌음을 보장한다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/8c1dcbd8-43df-4136-a7c1-b801cf985227/image.png" width="600px"/>
</p>

<h3 id="https-ssl과-tls">HTTPS: SSL과 TLS</h3>
<p>지금까지 배운 대칭 키 암호화와 공개 키 암호화 방식, 공개 키 인증서를 기반으로 동작하는 프로토콜로 <code>SSL(Secure Socket Layer)</code> 과 <code>TLS(Transport Layer Security)</code> 가 있다.
SSL과 TLS는 인증과 암호화를 수행하는 프로토콜이며, TLS는 SSL을 계승한 프로토콜이다.</p>
<p>SSL/TLS를 사용하는 대표적인 프로토콜은 <code>HTTPS(HTTP over TLS)</code> 이다.
<code>HTTPS</code> 는 HTTP 메시지의 안전한 송수신을 위해 개발된 프로토콜이다.</p>
<p>오늘날 주로 사용되는 TLS 1.3을 기반으로 HTTPS가 어떻게 동작하는지 간략하게 알아보자.
HTTPS 메시지는 크게 다음과 같은 단계를 거쳐 송수신한다.</p>
<ol>
<li>TCP 쓰리 웨이 핸드셰이크 (4장 학습)</li>
<li>TLS 핸드셰이크</li>
<li>암호화된 메시지 송수신 </li>
</ol>
<h4 id="1-tcp-쓰리-웨이-핸드셰이크">1. TCP 쓰리 웨이 핸드셰이크</h4>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/57600300-cfc6-4f38-832c-346e245dc400/image.png" width="600px"/>
</p>

<p>1번은 이전에 학습했다.
TCP 연결을 수립하기 위해 두 호스트가 각각 SYN, SYN+ACK, ACK 플래그가 설정된 TCP 세그먼트를 주고받는 것이 쓰리 웨이 핸드셰이크였다.</p>
<h4 id="2-tls-핸드셰이크">2. TLS 핸드셰이크</h4>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/a01f5fcd-78fa-47de-ba7f-3d356a5a2d9c/image.png" width="600px"/>
</p>

<p>꽤 복잡하지만, TLS는 암호학에 대한 기반 지식을 요하기 때문에 처음부터 모든 메시지와 의미를 외울 필요는 없다고 한다.
TLS 핸드셰이크의 핵심은 두 가지이다.
<code>1. 암호화 통신을 위한 키를 교환한다</code> <code>2. 인증서 송수신과 검증이 이루어진다.</code></p>
<p>위 그림의 메시지를 살펴보자.</p>
<h5 id="client-hello">Client Hello</h5>
<p>가장 먼저 클라이언트는 <code>ClientHello</code> 메시지를 보낸다.
해당 메시지는 암호화된 통신을 위해 서로 맞춰 봐야 할 정보들을 제시하는 메시지이다.
<code>지원되는 TLS 버전</code> <code>사용 가능한 암호화 방식과 해시 함수</code> <code>키를 만들기 위해 사용할 클라이언트의 난수</code> 등이 포함되어 있다.
이 때, <code>사용 가능한 암호화 방식과 해시 함수</code>를 담은 정보를 <code>암호 스위트(cipher suite)</code> 라고 한다.
다음 그림처럼 생겼다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/0ef7a25b-98b1-42eb-b43f-a13271f5b1a6/image.png" width="600px"/>
</p>

<h5 id="server-hello">Server Hello</h5>
<p>서버는 <code>Client Hello</code> 에 대한 메시지 응답으로 <code>ServerHello</code> 메시지를 전송한다.
<code>Server Hello</code> 메시지는 제시된 정보들을 선택하는 메시지이다.
따라서 이 메시지에는 <code>선택된 TLS 버전</code> <code>암호 스위트 등의 정보</code> <code>키를 만들기 위해 사용할 서버의 난수</code> 등이 포함되어 있다.</p>
<p><code>Client Hello</code> 와 <code>Server Hello</code> 메시지를 주고 받으면 암호화된 통신을 위해 사전 협의해야 할 정보들이 결정된다.
이렇게 결정된 정보를 토대로 서버와 클라이언트는 암호화에 사용할 키를 만들 수 있다.
이것이 TLS 핸드셰이크에서의 키 교환이다.</p>
<p>이 단계 이후부터 클라이언트와 서버는 키로 암호화된 암호문을 주고받을 수 있게 된다.</p>
<h5 id="certificate--certificateverify">Certificate , CertificateVerify</h5>
<p>서버는 <code>인증서</code> 를 의미하는 Certificate 메시지와 <code>검증을 위한 디지털 서명</code> 을 의미하는 CertificateVerify 메시지를 전송한다.</p>
<p>클라이언트는 이 메시지를 토대로 서버의 공개 키를 검증하게 된다.</p>
<h5 id="finished">Finished</h5>
<p>이어서 서버와 클라이언트는 TLS 핸드셰이크의 마지막을 의미하는 Finished 메시지를 주고 받는다.</p>
<p>이 후, TLS 핸드셰이크를 얻어낸 키를 기반으로 암호화된 데이터를 주고받는다.
TLS 1.3 에서는 Finished 메시지와 함께 암호화된 메시지를 전송할 수 있다.</p>
<blockquote>
<p><strong>HTTPS에 대해서 설명해주세요.</strong>
HTTPS는 HTTP와 달리 SSL/TLS 프로토콜을 통해 데이터를 암호화하여 전송하기 때문에 보안적인 측면에서 우수한 장점이 있습니다.</p>
</blockquote>
<blockquote>
<p><strong>SSL/TLS이 뭔가요?</strong>
SSL은 컴퓨터 네트워크에서 통신 보안을 제공하는 프로토콜입니다.
SSL은 핸드셰이크 과정에서 인증 기관, 인증서, 공개키 암호화, 대칭키 암호화를 사용하여 클라이언트와 서버 간의 암호화된 통신을 가능하게 합니다.
TLS는 SSL이 표준화되면서 변경된 이름입니다.</p>
</blockquote>
<blockquote>
<p><strong>대칭키 암호화 방식에 대해 설명해주세요.</strong>
대칭키 암호화는 하나의 동일한 키를 사용하여 암호화와 복호화를 수행하는 방식입니다. 암호화 및 복호화 속도가 빠르다는 장점이 있지만, 안전하게 키를 공유하기 어렵다는 단점이 있습니다.</p>
</blockquote>
<blockquote>
<p><strong>비대칭키(공개키) 암호화 방식에 대해서 설명해주세요.</strong>
비대칭키 암호화는 서로 다른 키를 사용하여 암호화와 복호화를 수행하는 방식입니다.
암호화에 사용하는 공개키와 복호화에 사용하는 개인키 한 쌍으로 이루어져 있으며, 공개키는 누구나 알 수 있지만 개인키는 비밀로 유지됩니다.
이 방식은 키를 안전하게 공유할 수 있다는 장점이 있지만, 대칭키 암호화보다 연산 속도가 느리다는 단점이 있습니다.</p>
</blockquote>
<blockquote>
<p><strong>전자 서명에 대해서 설명해주세요.</strong>
전자 서명은 비대칭키 암호화 방식을 사용하여, 개인키로 데이터를 암호화하고 공개키로 이를 검증하는 기술입니다.
전자 서명이 공개키로 검증 가능하다는 점을 이용해, 특정 개인이 서명했음을 신뢰할 수 있습니다.</p>
</blockquote>
<blockquote>
<p><strong>HTTPS 암호화 과정에 대해 설명해주세요. (SSL Handshake의 동작 과정을 설명해 주세요.)</strong>
SSL HandShake는 크게 4단계로 이루어집니다.
첫 번째로, 클라이언트는 <code>ClientHello</code> 메시지를 보냅니다.
이 메시지에는 지원하는 TLS 버전, 암호 스위트, 키 생성을 위한 클라이언트 난수 등이 포함되어 있습니다.
두 번째로 서버는 <code>Client Hello</code> 에 대한 메시지 응답으로 <code>ServerHello</code> 메시지를 전송합니다.
이 메시지에는 <code>선택된 TLS 버전</code> <code>암호 스위트</code> <code>키 생성을 위한 서버의 난수</code> 등이 포함되어 있습니다.
세 번째로, 클라이언트는 서버의 공개키로 세션 키를 암호화하여 서버에 보내면, 서버는 자신의 개인 키로 복호화하여 세션 키를 얻게 됩니다. 이후 클라이언트와 서버는 이 세션 키를 이용해 대칭키 암호화 방식으로 데이터를 주고 받습니다.
마지막으로, 클라이언트와 서버는 대칭키 암호화를 사용해 본격적인 HTTPS 통신을 시작합니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[컴퓨터 네트워크 - 응용계층]]></title>
            <link>https://velog.io/@ldh-dodo/%EC%BB%B4%ED%93%A8%ED%84%B0-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%9D%91%EC%9A%A9%EA%B3%84%EC%B8%B5</link>
            <guid>https://velog.io/@ldh-dodo/%EC%BB%B4%ED%93%A8%ED%84%B0-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%9D%91%EC%9A%A9%EA%B3%84%EC%B8%B5</guid>
            <pubDate>Wed, 26 Mar 2025 07:37:46 GMT</pubDate>
            <description><![CDATA[<p>서버와 클라이언트는 다음과 같은 정보를 식별할 수 있어야 한다.</p>
<ol>
<li>클라이언트가 메시지를 주고받고자 하는 대상을 식별해야 함</li>
<li>송수신하고자 하는 정보를 식별해야 함</li>
</ol>
<p>1번은 도메인 네임을 통해,
2번은 URL과 URN을 통해 식별할 수 있다.</p>
<p>각각에 대해 먼저 살펴보자.</p>
<h1 id="dnsdomain-name-system">DNS(Domain Name System)</h1>
<p><code>DNS</code> 란 계층적이고 분산된 도메인 네임에 대한 관리체계이자 이를 관리하는 애플리케이션 계층 프로토콜이다.</p>
<h2 id="도메인-네임과-네임-서버">도메인 네임과 네임 서버</h2>
<p>네트워크 상의 어떤 호스트를 특정하기 위해 IP 주소를 사용한다고 했는데, 오로지 IP만 사용하기에는 번거로울 수 있다.
이는 호스트의 IP 주소는 언제든지 바뀔 수 있고, 기억하기 어렵다는데서 기인한다.</p>
<p>그래서 사용자는 일반적으로 상대 호스트를 특정하기 위해 <code>도메인 네임</code> 을 많이 사용한다.
예를 들어, <code>www.example.com</code> 과 같은 문자열이 도메인 네임이다.</p>
<p>도메인 네임과 IP 주소는 묶어서 관리하는데, 이를 관리하는 서버를 <code>네임 서버(혹은 DNS 서버)</code> 라고 부른다.
네임 서버(DNS 서버)는 호스트의 도메인 네임과 IP 주소를 모아 관리하는 공용 전화번호부와 같은 역할을 한다.</p>
<p>도메인 네임을 네임 서버에 질의하면, 해당 도메인 네임에 대한 IP 주소를 알아낼 수 있다.
물론, 반대도 가능하다.</p>
<h3 id="도메인-네임을-쓰는-이유">도메인 네임을 쓰는 이유</h3>
<ol>
<li>도메인 네임은 IP에 비해 기억하기 쉽다.</li>
<li>IP 주소가 바뀌더라도, 바뀐 IP 주소에 도메인 네임을 대응하면 되어 IP 주소만으로 호스트를 특정하는 것보다 간편하다.</li>
</ol>
<h3 id="도메인-네임-관리">도메인 네임 관리</h3>
<p>도메인 네임은 점(.)을 기준으로 <code>계층적으로</code> 분류된다.
최상단에 <code>루트 도메인</code>이 있고, 그 다음 단계인<code>최상위 도메인</code>이 있으며, 계속 그 다음 단계의 도메인이 존재한다.
다음 그림을 보자.</p>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/61c0a52d-1a65-4b54-b02c-be773d3bf4aa/image.png" width="600px">
</p>

<p>흔히 최상위 도메인을 도메인 네임의 마지막 부분이라고 생각하기 쉬운데, 사실 루트 도메인도 도메인 네임의 일부이다.
다만 일반적으로 루트 도메인을 생략해서 표기해서, 우리가 흔히 볼 수 있는 <code>www.example.com</code> 과 같은 주소의 마지막에는 점(.)이 생략되어 있다고 생각하면 된다.</p>
<p><code>www.example.com</code> 에서 <code>www</code> 는 3단계 도메인이고, 도메인의 단계는 더 늘어날 수 있지만, 일반적으로는 3~5 단계 정도이다.
그리고 위 주소 처럼 도메인 네임을 모두 포함하는 도메인 네임을 <code>전체 주소 도메인 네임(FQDN; Fully-Qualified Domain Name)</code> 이라고 한다.</p>
<h3 id="계층적-네임-서버">계층적 네임 서버</h3>
<p>DNS가 계층적이고 분산된 도메인 네임에 대한 관리체계라고 했는데, 계층적인 도메인 네임을 효율적으로 관리하기 위해 네임 서버 또한 계층적인 형태를 이룬다.</p>
<p>IP 주소를 모르는 상태에서 도메인 네임에 대응되는 IP 주소를 알아내는 과정을 <code>리졸빙(resolving)</code> 한다라고 하고, 이 과정에서 아래와 같은 다양한 네임 서버들이 사용된다.</p>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/6b6c47ed-23b7-4388-98ae-18e2935094ad/image.png" width="600px">
</p>

<p><code>로컬 네임 서버</code>, <code>루트 네임 서버</code>, <code>TLD(최상위 도메인) 네임 서버</code>, <code>책임 네임 서버</code>에 대해 알아보자.</p>
<h4 id="로컬-네임-서버local-name-server">로컬 네임 서버(local name server)</h4>
<p>클라이언트와 맞닿아 있는 네임 서버로, 클라이언트가 도메인 네임을 통해 IP 주소를 알아내고자 할 때 가장 먼저 찾게되는 네임 서버이다.
로컬 네임 서버의 주소는 <code>1. ISP에서 할당해주거나</code> <code>2. 공개 DNS 서버를 이용하거나</code> 로 결정된다.
일반적으로는 ISP에서 할당해 주는 경우가 많지만, 구글의 <code>8.8.8.8</code> <code>8.8.4.4</code> 와 클라우드 플레어의 <code>1.1.1.1</code> 와 같은 공개 DNS 서버를 이용할 수도 있다.</p>
<p>만약 로컬 네임서버가 대응되는 IP 주소를 알고 있다면, 클라이언트에게 IP 주소를 알려준다.
만약 로컬 네임서버가 대응되는 IP 주소를 모를 경우에는 <code>루트 네임 서버</code> 에게 해당 도메인 네임을 질의하게 된다.</p>
<h4 id="루트-네임-서버root-name-server">루트 네임 서버(root name server)</h4>
<p>루트 네임 서버는 루트 도메인을 관장하는 네임 서버로, 질의에 대해 TLD 네임 서버의 IP 주소를 반환하는 역할을 한다.
단순히 다음 네임 서버의 위치(IP)를 반환하는 역할만 수행한다.</p>
<h4 id="tld최상위-도메인-네임-서버">TLD(최상위 도메인) 네임 서버</h4>
<p>TLD를 관리하는 네임 서버이다. TLD 네임 서버는 다음 그림과 같이 질의에 대해 TLD 하위 도메인 네임을 관리하는 네임 서버 주소를 반환한다.
단순히 다음 네임 서버의 위치(IP)를 반환하는 역할만 수행한다.</p>
<h4 id="책임-네임-서버authoritative-name-server">책임 네임 서버(authoritative name server)</h4>
<p>자신이 관리하는 도메인 영역의 질의에 대해서는 다른 서버에게 떠넘기지 않고 곧바로 답할 수 있는 네임 서버이다.
일반적으로 로컬 네임 서버는 책임 네임 서버로부터 원하는 IP 주소를 얻어낸다.</p>
<p>결국 아래처럼 네임 서버들은 계층적인 구조를 띠고 있다.</p>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/d1c0bbd7-96c2-41e1-8861-8ba45b1edb22/image.png" width="600px">
</p>

<h3 id="재귀적-질의와-반복적-질의">재귀적 질의와 반복적 질의</h3>
<h4 id="재귀적-질의recursive-query">재귀적 질의(recursive query)</h4>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/6382479d-1151-4242-b30d-f1bd4b464da3/image.png" width="600px">
</p>

<p><code>재귀적 질의</code> 는 클라이언트가 로컬 네임 서버에게 도메인 네임을 질의하면, 로컬 네임 서버는 루트 네임 서버에게, 루트 네임 서버는 TLD 네임 서버에게, TLD 네임 서버가 다음 단계에 질의하는 과정을 반복하며 최종 응답을 역순으로 전달받는, 즉 재귀적으로 질의가 이루어지는 방식이다.</p>
<h4 id="반복적-질의iterative-query">반복적 질의(iterative query)</h4>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/04492003-fb2e-4902-9000-ea7f7ce802f0/image.png" width="600px">
</p>

<p><code>반복적 질의</code> 는 로컬 네임 서버의 역할이 두드러진다.
<code>반복적 질의</code>란 클라이언트가 로컬 네임 서버에게 IP 주소를 알고 싶은 도메인 네임을 질의하면, 로컬 네임 서버는 루트 도메인에게 질의해서 다음으로 질의할 네임 서버의 주소를 응답 받고, 이렇게 다음으로 질의할 네임 서버의 주소를 응답받는 과정을 반복하다가 최종 응답 결과를 클라이언트에게 알려주는 방식이다.</p>
<h4 id="dns-캐시">DNS 캐시</h4>
<p>위의 리졸빙 방식은 여러 단계를 거쳐야 해서 시간이 오래 걸리고 네트워크 상의 메시지 수가 지나치게 늘어날 수 있다는 단점이 있다.
이를 보완하기 위해, 네임 서버들이 기존에 응답받은 결과를 임시로 저장했다가 추후 같은 질의에 이를 활용할 수 있는데, 이를 <code>DNS 캐시</code> 라고 한다.
임시 저장된 값은 TTL(Time To Live) 라는 값이 만료되면 사라진다.</p>
<h1 id="자원을-식별하는-uri">자원을 식별하는 URI</h1>
<p>송수신하고자 하는 정보를 식별하기 위한 방식인 URI와, URI를 식별 정보 기준으로 분류한 개념인 URL, URN에 대해 살펴볼 것이다.</p>
<p>이 개념들을 이해하기 전, <code>자원(resource)</code> 에 대한 이해가 필요하다.</p>
<p><code>자원</code> 이란 네트워크상의 메시지를 통해 주고받는 대상을 의미한다.
이는 HTML 파일이 될 수도 있고, 이미지나 동영상, 혹은 텍스트 파일이 될 수도 있다.</p>
<p>네트워크 상에서 자원을 주고받으려면 자원을 식별할 수 있어야 한다.
자원을 식별할 수 있는 정보를 <code>URI(Uniform Resource Identifier)</code> 라고 부른다.</p>
<p><code>위치</code>를 이용해 자원을 식별하는 방식을 <code>URL(Uniform Resource Locator)</code> 이라 하고,
<code>이름</code>을 이용해 자원을 식별하는 방식을 <code>URN(Uniform Resource Name)</code> 이라 한다.</p>
<h2 id="urluniform-resource-locator">URL(Uniform Resource Locator)</h2>
<p>오늘날 더 많이 사용되는 방식은 URL 이다.
일반적인 URL 형식은 다음과 같다.</p>
<p>각각의 부분에 대해 살펴보자.</p>
<h3 id="1-scheme">1. scheme</h3>
<p><code>scheme</code> 은 자원에 접근하는 방법을 의미한다. 일반적으로 사용할 프로토콜이 명시되는데, HTTP를 사용하여 자원에 접근할 때는 <code>http://</code> 를 사용하고, HTTPS를 사용하여 자원에 접근할 때는 <code>https://</code> 을 사용한다.</p>
<h3 id="2-authority">2. authority</h3>
<p>authority 에는 <code>호스트를 특정할 수 있는 정보</code>, 이를 테면 IP 주소 혹은 도메인 네임이 명시된다. 콜론 뒤에 포트 번호를 덧붙일 수도 있다.</p>
<h3 id="3-path">3. path</h3>
<p><code>자원이 위치한 경로</code>가 명시된다. 자원의 위치는 슬래시(/)를 기준으로 표현되고, 최상위 경로 또한 슬래시로 표현된다.</p>
<p>만약, 최상위 경로(/) 아래, home(/home) 아래, images(/home/images) 아래에 a.png 이라는 자원이 있다고 하면</p>
<p>a.png의 path는 <code>/home/images/a.png</code> 이 된다.</p>
<h3 id="4-query">4. query</h3>
<p>URL 구문만으로 자원의 위치를 식별할 수 있는 경우도 있지만, <code>더 많은 정보</code> 가 필요할 때 사용할 수 있는 것이 <code>쿼리 문자열(혹은 쿼리 파라미터)</code> 이다.
쿼리 문자열은 물음표(?)로 시작되는 &lt;키=값&gt;의 형태로 앰퍼샌드(&amp;)를 사용하여 여러 쿼리 문자열을 연결할 수 있다.
아래와 같이 말이다.</p>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/9c2dbe9d-233c-487e-bac9-808ce96433a6/image.png" width="600px">
</p>

<h3 id="fragment">fragment</h3>
<p>fragment는 <code>자원의 한 조각을 가리키기 위한 정보</code> 이다. 흔히 HTML 파일과 같은 자원에서 특정 부분을 가리키기 위해 사용된다.
만약 <code>#section-1.1.2</code> 라는 fragment가 있다면, HTML 파일의 특정부분으로 이동하여 보일 것이고, 
fragment가 없다면, HTML의 첫 부분이 보일 것이다.</p>
<h2 id="urnuniform-resource-name">URN(Uniform Resource Name)</h2>
<p>만약, 자원의 위치가 변한다면 URL은 유효해지지 않을 수 있다.
이것은 URL의 고질적인 문제 중 하나인데, URN은 이를 보완할 수 있다.</p>
<p><code>URN</code>은 자원에 고유한 이름을 붙이는 이름 기반 식별자다. 따라서, 자원의 위치와 무관하게 자원을 식별할 수 있다는 장점이 있다.</p>
<p>다음 URN은 ISBN이 <code>0451450523</code>인 도서를 나타내는 URN 예시이다.</p>
<p><code>urn:isbn:0451450523</code></p>
<p>이와 같은 URN을 이용하면 위치나 프로토콜과 무관하게 자원을 식별할 수 있다.
다만 URN은 아직 URL 만큼 널리 채택된 방식은 아니기에 자원을 식별할 URI로는 URL이 더 많이 사용된다.</p>
<h1 id="httphypertext-transfer-protocol">HTTP(Hypertext Transfer Protocol)</h1>
<p>HTTP는 응용
HTTP에는 중요한 네 가지 특성이 있다.</p>
<ol>
<li>요청과 응답을 기반으로 동작</li>
<li>미디어 독립적</li>
<li>상태를 유지하지 않음</li>
<li>지속 연결을 지원</li>
</ol>
<p>이 특성들에 대해 살펴보자.</p>
<h2 id="http의-특성">HTTP의 특성</h2>
<h3 id="요청-응답-기반-프로토콜">요청-응답 기반 프로토콜</h3>
<p>HTTP는 <code>클라이언트-서버 구조 기반의 요청-응답 프로토콜</code> 이다.
HTTP는 클라이언트와 서버가 서로 <code>HTTP 요청 메시지</code>와 <code>HTTP 응답 메시지</code>를 주고받는 구조로 동작한다.
같은 HTTP 메시지일지라도, <code>HTTP 요청 메시지</code>와 <code>HTTP 응답 메시지</code>는 형태가 다르다.</p>
<h3 id="미디어-독립적-프로토콜">미디어 독립적 프로토콜</h3>
<p>그렇다면 HTTP로는 어떤 자원을 주고받을 수 있을까?
HTTP는 자원의 특성을 제한하지 않는다.
다시 말해서, HTTP는 주고받을 <code>자원의 특성과 무관하게 그저 자원을 주고받을 수단(인터페이스)</code> 역할을 수행한다.</p>
<p>HTTP에서 메시지로 주고받는 자원의 종류를 <code>미디어 타입</code> 혹은 <code>MIME 타입(Multipurpose Internet Mail Extensions Type)</code> 이라고 부른다.</p>
<p>미디어 타입은 기본적으로 슬래시를 기준으로하는 <code>타입/서브타입</code> 형식으로 구성된다.
<code>타입</code> 은 데이터의 유형을 나타내고, <code>서브타입</code> 은 주어진 타입에 대한 세부 유형을 나타낸다.
<code>text/html</code> <code>text/plain</code> <code>image/png</code> 가 그 예시다.</p>
<p>또한 미디어 탕비에는 부가적인 설명을 위해 선택적으로 매개변수가 포함될 수도 있다.
매개변수는 <code>타입/서브타입;매개변수=값</code> 의 형식으로 표현된다.</p>
<p>예를 들어 <code>type/html;charset=UTF-8</code> 은 미디어 타입이 HTML 문서 타입이며, HTML 문서 내에서 사용된 문자는 모두 UTF-8로 인코딩되었음을 의미한다.</p>
<h3 id="스테이트리스-프로토콜">스테이트리스 프로토콜</h3>
<p>HTTP는 상태를 유지하지 않는 <code>스테이트리스(stateless)</code> 프로토콜이다.
이는 서버가 HTTP 요청을 보낸 클라이언트와 관련된 상태를 기억하지 않는다는 의미이다.
이러한 특성 때문에 클라이언트의 모든 HTTP 요청은 기본적으로 독립적인 요청으로 간주된다.</p>
<p>만약 HTTP가 상태를 유지하는 프로토콜이었다면 어땠을까?
서버가 모든 클라이언트의 상태 정보를 유지해야 하기 때문에, 서버에 부담이 갈 것이다.
또한, 서버가 여러 대로 구성될 경우, 모든 서버가 모든 클라이언트의 상태 정보를 공유해야 하는데 이는 매우 번거롭고 어려운 작업이다.
그리고 클라이언트는 자신의 상태를 기억하는 특정 서버하고만 상호 작용할 수 있게 되어, 특정 클라이언트가 특정 서버에 종속될 수 있다.</p>
<p>HTTP가 처음 만들어졌을 때부터 오늘날까지 이어지는 중요한 설계 목표는 <code>확장성(scalability)</code> 과 <code>견고성(robustness)</code> 이다.
스테이트리스한 특성은 언제든 쉽게 서버를 추가할 수 있게 해주어 높은 확장성을, 서버 중 하나에 문제가 생겨도 쉽게 다른 서버로 대체가 가능하다는 점 때문에 높은 견고성을 보장할 수 있다.</p>
<h3 id="지속-연결-프로토콜">지속 연결 프로토콜</h3>
<p>기본적으로 HTTP는 TCP 상에서 동작하는데, HTTP는 비연결형 프로토콜이지만, TCP는 연결형 프로토콜이다.
따라서 초기의 HTTP 버전은 쓰리 웨이 핸드셰이크를 통해 TCP 연결을 수립한 후, 요청에 대한 응답을 받으면 연결을 종료하는 방식으로 동작했다.
만약, 이후 추가적인 요청-응답이 필요하다면 다시 TCP 연결을 수립해야했다.
이러한 방식을 <code>비지속 연결</code> 이라고 한다.</p>
<p>하지만, 최근 대중적으로 사용되는 HTTP 버전(HTTP 1.1 이상)은 <code>지속 연결</code> 이라는 기술을 제공한다. 이는 <code>킵 얼라이브(keep-alive)</code> 라고도 부른다.
이는 하나의 TCP 연결상에서 여러 개의 요청-응답을 주고받을 수 있는 기술이다.
아래 그림을 통해 비지속 연결과 지속 연결의 차이점을 볼 수 있다.</p>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/d2e2f25a-de53-4f8a-a76d-bf638db0db3a/image.png" width="600px">
</p>

<h2 id="http-버전">HTTP 버전</h2>
<p><a href="https://developer.mozilla.org/ko/docs/Web/HTTP/Guides/Evolution_of_HTTP">https://developer.mozilla.org/ko/docs/Web/HTTP/Guides/Evolution_of_HTTP</a></p>
<h2 id="http-메시지-구조">HTTP 메시지 구조</h2>
<p>대중적으로 사용되는 HTTP 1.1 버전의 메시지를 위주로 학습하겠다.
HTTP 메시지의 구조는 크게 다음과 같다.</p>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/cce426aa-4f4b-43a2-967c-b01306e3b3df/image.png" width="600px">
</p>

<p>HTTP 메시지는 <code>시작 라인</code> <code>필드 라인</code> <code>메시지 본문</code> 으로 이루어져 있다.
<code>필드 라인</code> 과 <code>메시지 본문</code> 사이에는 빈 줄바꿈이 있다.</p>
<h3 id="시작-라인">시작 라인</h3>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/d845711b-8c2c-4de8-9911-8fe1c7564e74/image.png" width="600px">
</p>

<p>HTTP 메시지가 HTTP 요청 메시지일 경우, <code>시작 라인</code> 은 <code>요청 라인</code>이 된다.
HTTP 메시지가 HTTP 응답 메시지일 경우, <code>시작 라인</code> 은 <code>상태 라인</code>이 된다.</p>
<h4 id="요청-라인request-line">요청 라인(request-line)</h4>
<p>요청 라인의 형식은 다음과 같다.</p>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/2255bde0-5a2d-4ce1-ae7d-4aa452452be4/image.png" width="600px">
</p>

<h5 id="메서드method">메서드(method)</h5>
<p><code>메서드</code> 란 클라이언트가 서버의 자원(요청 대상)에 대해 수행할 작업의 종류를 나타낸다.
대표적으로 <code>GET</code> <code>POST</code> <code>PUT</code> <code>DELETE</code> 등이 있다.</p>
<h5 id="요청-대상request-target">요청 대상(request-target)</h5>
<p><code>요청 대상</code>은 HTTP 요청을 보낼 서버의 자원을 의미한다.
보통 이곳에는 (쿼리가 포함된) URL의 경로가 명시된다.
<code>http://www.example.com/hello?q=world</code> 로 요청을 보낼 경우, 요청 대상은 <code>/hello?q=word</code>가 된다.</p>
<h5 id="http-버전http-version">HTTP 버전(HTTP-version)</h5>
<p>이름 그대로 사용된 HTTP 버전을 의미한다. 
<code>HTTP/&lt;버전&gt;</code> 과 같이 표기되며, HTTP 버전 1.1은 <code>HTTP/1.1</code> 로 표기된다.</p>
<h4 id="상태-라인status-line">상태 라인(status-line)</h4>
<p>상태 라인의 형식은 다음과 같다.</p>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/c3263d4f-5e16-4bdb-8798-e465f37ab32a/image.png" width="600px">
</p>

<h5 id="상태-코드status-code">상태 코드(status code)</h5>
<p>요청에 대한 결과를 나타내는 세 자리 정수이다.
클라이언트는 상태 코드를 통해 요청이 어떻게 처리되었는지 판단할 수 있다.
예를 들어, <code>200</code> 은 <code>요청이 성공적으로 받아들여지고 수행되었음</code> 을, <code>404</code> 는 <code>요청한 자원이 존재하지 않음</code> 을 의미한다.</p>
<p><code>HTTP/1.1 200 OK</code> <code>HTTP/1.1 404 Not Found</code> 와 같이 표현된다.</p>
<h3 id="필드-라인">필드 라인</h3>
<p>필드 라인에는 0개 이상의 <code>HTTP 헤더</code>가 명시된다. 그래서 이를 <code>헤더 라인</code> 이라고도 부른다.
필드 라인에 명시되는 각 HTTP 헤더는 콜론(:)을 기준으로 <code>헤더 이름</code> 고 하나 이상의 <code>헤더 값</code> 으로 구성 된다.</p>
<p>다음 그림에서 첫 줄을 제외한 부분이 HTTP 헤더이다.</p>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/87a6741c-3327-4c3c-b4ce-4ce78fa96b12/image.png" width="600px">
</p>

<p>이 부분은 다룰 내용이 꽤 있으니, 아래서 따로 정리하겠다.</p>
<h3 id="메시지-본문message-body">메시지 본문(message-body)</h3>
<p>HTTP 요청 혹은 응답 메시지에서 본문이 필요할 경우 이는 <code>메시지 본문</code> 에 명시된다.
메시지 본문은 존재하지 않을 수도 있고, 다음과 같이 다양한 콘텐츠 타입이 사용될 수도 있다.</p>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/a9853ad6-f11c-4303-bca5-0291b1569b66/image.png" width="600px">
</p>

<h2 id="http-메서드">HTTP 메서드</h2>
<p>HTTP 요청 메시지에서 사용될 수 있는 다양한 메서드를 학습해보자.</p>
<p>다음과 같은 종류가 있다.</p>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/eb4e1e02-1c2e-447e-8bdd-9c693193558b/image.png" width="600px">
</p>

<h3 id="get---가져다-주세요">GET - 가져다 주세요</h3>
<p>GET 메서드는 특정 자원을 조회할 때 사용된다.
가장 흔히 사용되는 메서드 중 하나이며, 웹 브라우저에서도 빈번히 사용된다.
GET 메서드에 요청 메시지 본문을 포함시키는 것은 바람직하지 않으며, 쿼리 문자열이 사용되는 경우가 많다.</p>
<h3 id="head---헤더만-가져다-주세요">HEAD - 헤더만 가져다 주세요</h3>
<p>HEAD 메서드는 사실상 GET 메서드와 동일한 역할을 한다.
유일한 차이점은 응답 메시지에 메시지 본문이 포함되지 않는다는 것이다.
즉, 서버는 요청에 대한 응답으로 응답 메시지의 헤더만을 반환한다.</p>
<h3 id="post---처리해-주세요">POST - 처리해 주세요</h3>
<p>POST 메서드는 서버로 하여금 특정 작업을 처리하도록 요청하는 메서드이다.
처리할 대상은 흔히 메시지 본문으로 명시된다.
만약 성공적으로 POST 요청이 처리되어 새로운 자원이 생성되면, 서버는 응답 메시지의 Location 헤더를 통해 새로 생성된 자원의 위치를 클라이언트에게 알려줄 수 있다.</p>
<h3 id="put---덮어써-주세요">PUT - 덮어써 주세요</h3>
<p>PUT 메서드는 쉽게 말해서 &#39;덮어쓰기&#39; 를 요청하는 메서드이다.
요청 자원이 없다면 메시지 본문으로 새롭게 생성하거나,
이미 자원이 존재한다면 메시지 본문으로 자원을 완전히 대체하는 메서드이다.</p>
<h3 id="patch---일부-수정해주세요">PATCH - 일부 수정해주세요</h3>
<p>PATCH 메서드는 PUT 메서드와 비교하며 이해하는 것이 좋다.
PUT 메서드가 덮어쓰기, 완전한 대체에 가깝다면,
PATCH 메서드는 부분적 수정이다.</p>
<p>만약, 기존 데이터와 요청 데이터가 일치하는 정보가 있다면, 그 부분은 그대로 유지하고 일치하지 않는 부분만 덮어쓴다고 생각하면 된다.</p>
<h3 id="delete---삭제해-주세요">DELETE - 삭제해 주세요</h3>
<p>DELETE 메서드는 특정 자원을 삭제하고 싶을 때 사용하는 메서드다.</p>
<h2 id="http-상태-코드">HTTP 상태 코드</h2>
<p>상태 코드는 요청에 대한 결과를 나타내는 세 자리 정수이다.
상태 코드는 굉장히 다양하지만, 백의 자리 수를 기준으로 유형을 구분할 수 있다.</p>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/b5ff78c4-b5a8-44ba-9585-83a94aa44ac7/image.png" width="600px">
</p>

<p>자주 사용되는 200번대 상태 코드부터 500 번대 상태 코드까지 자세히 알아보자.</p>
<h3 id="200번대--성공-상태-코드">200번대 : 성공 상태 코드</h3>
<p>200번대 상태 코드는 <code>요청이 성공했음</code> 을 의미한다.
주로 사용되는 상태 코드는 <code>200(OK)</code> <code>201(Created)</code> <code>202(Accepted)</code>, <code>204(No Content)</code> 이다.</p>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/e07cae10-1244-4206-b318-ec0535fd6b40/image.png" width="600px">
</p>

<h4 id="201created">201(Created)</h4>
<p>POST 요청을 통해 서버에 새로운 자원을 성공한 경우, 상태 코드 201로 요청이 성공했으며 새로운 자원이 만들어졌음을 알린다.</p>
<h4 id="202accepted">202(Accepted)</h4>
<p>요청을 잘 받았으나, 아직 요청한 작업을 끝내지 않았음을 의미한다.
작업 시간이 긴 대용량 파일 업로드 작업이나 배치 작업과 같이 요청 결과를 곧바로 응답하기 어려운 상황에 사용된다.</p>
<h4 id="204no-content">204(No Content)</h4>
<p>요청 메시지에 대해 성공적으로 작업을 완료했더라도 마땅히 메시지 본문으로 표기할 것이 없을 경우 상태코드 204로 응답할 수 있다.
200은 응답 본문이 있지만 204는 그렇지 않음을 기억하자.</p>
<h3 id="300번대--리다이렉션-코드">300번대 : 리다이렉션 코드</h3>
<p><code>리다이렉트</code> 란 클라이언트가 요청한 자원이 다른 곳에 있을 때, 클라이언트의 요청을 다른 곳으로 이동시키는 것을 의미한다.</p>
<p>클라이언트가 요청한 자원이 다른 URL에 있을 경우, 서버는 응답 메시지의 Location 헤더를 통해 요청한 자원이 위치한 URL을 안내해줄 수 있다.
이를 수신한 클라이언트는 Location 헤더에 명시된 URL로 즉시 재요청을 보내어 새로운 URL에 대한 응답을 받게 된다.</p>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/72561654-890c-4376-a16e-e737a1e45625/image.png" width="600px">
</p>


<p>리다이렉션의 유형은 크게 <code>영구적인 리다이렉션</code> 과 <code>일시적인 리다이렉션</code> 으로 구분된다.</p>
<h4 id="영구적인-리다이렉션permanent-redirection">영구적인 리다이렉션(permanent redirection)</h4>
<p><code>영구적인 리다이렉션(permanent redirection)</code> 은 자원이 완전히 새로운 곳으로 이동하여 경로가 영구적으로 재지정되는 것을 의미한다.
서버가 도메인을 이전하는 등 웹 사이트의 큰 개편이 있을 때 이런 영구적인 리다이렉션을 접할 수 있다.</p>
<p>영구적인 리다이렉션과 관련한 상태 코드로는 301과 308이 있다.</p>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/973fe689-3f9d-460c-9ffe-b9fe2059e2a3/image.png" width="600px">
</p>

<p>상태 코드 301과 308의 차이점은 <code>클라이언트의 재요청 메서드 변경 여부</code> 에 있다.</p>
<p>클라이언트가 처음에 POST 메서드로 요청을 보낸 뒤, Location 헤더에 명시된 경로로 재요청을 보내야 한다는 상황에서, 두 번째 요청 메서드는 다음 그림처럼 GET 요청으로 바뀔 수도 있다.</p>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/72f7b7fc-0f0e-4fa0-907d-35b1fa73d077/image.png" width="600px">
</p>


<p>만약 클라이언트가 첫 번째 요청으로 POST 메서드를 보내고 상태 코드 308을 받았다면, 
두 번째 요청에서도 POST 메서드를 유지하게 된다.</p>
<h4 id="일시적인-리다이렉션temporary-redirection">일시적인 리다이렉션(temporary redirection)</h4>
<p>자원의 위치가 임시로 변경되었거나 임시로 사용할 URL이 필요한 경우에 주로 사용된다.</p>
<p>일시적인 리다이렉션과 관련한 상태 코드는 302, 303, 307이 있다.</p>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/09bb7c15-ed0e-48b1-8b17-0c75bd431ce3/image.png" width="600px">
</p>

<p>302와 307은 앞에서 살펴본 것과 똑같이 동작한다.</p>
<p><code>303(See Other)</code> 은, 첫 번째 요청의 메서드가 무엇이든지 간에, 두 번째 요청 메서드를 GET으로 하도록 강제한다.</p>
<h3 id="400번대-클라이언트-상태-에러-코드">400번대: 클라이언트 상태 에러 코드</h3>
<p>400 번대 상태 코드는 <code>클라이언트에 의한 에러가 있음</code> 을 알려주는 상태 코드다.
서버가 처리할 수 없는 형태로 요청을 보냈거나, 존재하지 않는 자원에 대해 요청을 보내는 경우가 이에 해당한다.</p>
<p>400번대 상태 코드의 대표 유형은 다음과 같다.</p>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/a1a09d41-276d-485f-b62e-dd3ff4ccd05a/image.png" width="600px">
</p>


<h4 id="400bad-request">400(Bad Request)</h4>
<p>클라이언트의 요청이 잘못되었음을 알려주는 상태 코드다.
클라이언트의 요청 메시지의 내용이나 형식 자체에 문제가 있어, 서버가 요청 메시지를 올바르게 처리할 수 없는 경우가 이에 해당한다.</p>
<h4 id="401unauthorized">401(Unauthorized)</h4>
<p>웹상에서 정보를 검색할 때, 모든 자원에 접근이 가능한 것은 아니다.
특정 자원에 접근하기 위해 <code>인증</code>이 필요할 때가 있는데, 요청에 대한 <code>인증</code>이 필요할 경우 서버는 401 상태 코드를 응답할 수 있다.
서버는 반드시 <code>WWW-Authenticate</code> 라는 헤더를 통해 인증 방법을 알려주어야 하는데, 이는 아래서 살펴보겠다.</p>
<h4 id="403forbidden">403(Forbidden)</h4>
<p>클라이언트의 권한이 충분하지 않다면 상태코드 403을 응답한다.
즉, <code>자원에 접근할 권한이 없음</code> 을 의미한다.</p>
<h4 id="404not-found">404(Not Found)</h4>
<p>접근하고자 하는 자원이 존재하지 않음을 알리는 상태 코드이다.
존재하더라도 공개하지 않는 자원에 대해 404를 응답하는 경우가 있다.</p>
<h4 id="405method-not-allowed">405(Method Not Allowed)</h4>
<p>일부 메서드는 구현되어 있지 않을 수 있는데, 구현되지 않은 메서드로 요청을 보낸다면 405를 통해 해당 메서드의 미지원을 알린다.</p>
<h3 id="500번대-서버-에러-상태-코드">500번대: 서버 에러 상태 코드</h3>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/7439704e-5a4c-44c1-be6c-31f7ba55c78f/image.png" width="600px"/>
</p>

<p>500번대 상태 코드 원인은 서버다. 
클라이언트가 올바르게 요청을 보냈을 지라도 발생할 수 있는 서버 에러에 대한 상태 코드이다.</p>
<h4 id="500internal-server-error">500(Internal Server Error)</h4>
<p><code>서버의 예기치 못한 상황으로 인해 요청을 처리할 수 없음</code> 이라는 의미이다.
다소 포괄적이기 때문에, 서버 내 에러를 통칭하기도 한다.</p>
<h4 id="502bad-gateway">502(Bad Gateway)</h4>
<p>클라이언트와 서버 사이에 위치한 중간 서버의 통신 오류를 나타내는 상태 코드다.
클라이언트와 서버 사이에 위치한 중간 서버가 유효하지 않거나 잘못된 응답을 받는다면 502를 응답한다.</p>
<h4 id="503service-unavailable">503(Service Unavailable)</h4>
<p><code>현재 서비스를 일시적으로 이용할 수 없음</code> 을 의미한다.
서버가 과부하 상태에 있거나 일시적인 점검 상태일 때 볼 수 있다.</p>
<h2 id="http-헤더와-http-기반-기술">HTTP 헤더와 HTTP 기반 기술</h2>
<p>HTTP 메시지의 두 번째 줄인 <code>필드 라인</code>에 대해 배워보자.</p>
<h3 id="http-헤더">HTTP 헤더</h3>
<p>HTTP 헤더는 <code>필드 이름(헤더 이름):필드 값(헤더 값)</code> 의 형식으로 이루어져 있다.</p>
<p>HTTP 요청시 자주 사용되는 헤더, HTTP 응답 시 주로 사용되는 헤더, HTTP 요청과 응답 모두에서 자주 활용되는 헤더 순으로 살펴보자.</p>
<h4 id="요청-시-활용되는-http-헤더">요청 시 활용되는 HTTP 헤더</h4>
<p><code>Host</code> <code>User-Agent</code> <code>Referer</code> <code>Authorization</code> 에 대해 알아보자.</p>
<h5 id="host">Host</h5>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/cbc73b06-27f6-473b-a7d8-b26ac86272ec/image.png" width="600px">
</p>

<p>Host는 요청을 보낼 대상 호스트를 나타내는 헤더이다.
주로 <code>도메인 네임</code> 으로 명시되며, 포트 번호가 포함될 수 있다.</p>
<h5 id="user-agent">User-Agent</h5>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/f198a7da-17f5-4844-9e0f-10f9f5ff82ff/image.png" width="600px">
</p>

<p><code>유저 에이전트</code> 란 웹 브라우저와 같이 HTTP 요청을 시작하는 <code>클라이언트 측의 프로그램</code>을 의미한다.
서버 입장에서는 이 헤더를 통해 클라이언트의 접속 환경을 유추할 수 있다.</p>
<h5 id="referer">Referer</h5>
<p>클라이언트가 요청을 보낼 때 머무르고 있던 URL이 명시된다.
영문법적으로는 Referrer이 맞지만, 초기 개발 당시 오타로 인해 Referer 이라는 표기가 오늘날까지 사용되고 있다고 한다.
클라이언트의 유입 경로를 확인하는데 용이하다.</p>
<pre><code>GET /page2.html HTTP/1.1
Host: www.example.com
Referer: https://www.google.com/search?q=example</code></pre><p>위 예시에서 서버는 사용자가 구글에서 검색 후 들어왔다는 것을 알 수 있다.</p>
<h5 id="authorization">Authorization</h5>
<p>클라이언트의 인증 정보를 담는 헤더이다.
다음 처럼 <code>인증 타입(type)</code> 과 <code>인증을 위한 정보(credentials)</code> 가 차례로 명시된다.</p>
<p><code>Authorization : &lt;type&gt; &lt;credentials&gt;</code></p>
<p>인증 타입 종류는 다양하지만, 가장 기본적인 HTTP 인증 타입은 Basic이라는 타입이다.
Basic 타입 인증은 <code>username:password</code> 와 같이 사용자 아이디와 비밀번호를 콜론을 이용해 합친 뒤, 이를 Base64 인코딩한 값을 인증 정보로 삼는 방식이다.</p>
<pre><code>Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=</code></pre><p>위 예시에서
<code>Basic = 인증 타입</code> 이고, 
<code>dXNlcm5hbWU6cGFzc3dvcmQ=</code> 은 username:password를 Base64 인코딩한 값이다.</p>
<h4 id="응답-시-활용되는-http-헤더">응답 시 활용되는 HTTP 헤더</h4>
<p>HTTP 응답 시 활용되는 대표적인 헤더인 <code>Server</code> <code>Allow</code> <code>Retry-After</code> <code>Location</code> <code>WWW-Authenticate</code> 헤더에 대해 알아보자.</p>
<h5 id="server">Server</h5>
<p>Server 헤더는 요청을 처리하는 서버 측의 소프트 웨어와 관련된 정보를 명시한다.
예를 들어 다음 예시 헤더는 <code>Unix 운영체제에서 동작하는 아파치 HTTP 서버</code> 를 의미한다.</p>
<p><code>Server : Apache/2.4.1 (Unix)</code></p>
<h5 id="allow">Allow</h5>
<p>Allow 헤더는 클라이언트에게 허용된 HTTP 메서드 목록을 알려주기 위해 사용된다.
앞선 절에서 배웠던 405(Method Not Allowed) 상태 코드를 응답하는 메시지에서 Allow 헤더가 함께 사용된다.
다음 예시를 살펴보면 이해가 쉽다.</p>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/d260e5b4-bc9f-4e0a-9ed4-efd57661705d/image.png" width="600px">
</p>

<p>서버에서 <code>GET</code> 요청을 받았는데, <code>POST</code> 와 <code>OPTIONS</code> 메서드만 사용가능하다고 클라이언트에게 알려주는 사진이다.</p>
<h5 id="retry-after">Retry-After</h5>
<p>앞선 절에서 배웠던 503(Service Unavailable) 상태 코드는 <code>현재는 요청을 처리할 수 없으나 추후 가능할 수도 있음</code> 을 의미했다. 
이 응답과 함께 사용될 수 있는 헤더가 <code>Retry-After</code> 헤더이다.
다음 예시는 각각 <code>2024년 8월 23일 금요일 09시 이후에 사용 가능하다</code> 라는 사실
<code>120 초 이후에 사용 가능하다</code> 라는 사실을 나타내는 헤더이다.</p>
<p>각각 다른 예시이고, 동시에 여러 헤더를 사용한 예시가 아님에 유의하자. </p>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/e99dc2e1-6475-4632-a872-50e877511751/image.png" width="600px">
</p>

<h5 id="location">Location</h5>
<p>Location은 이전 절에 언급했던 대로, 클라이언트에게 자원의 위치를 알려주기 위해 사용되는 헤더이다.
주로 리다이렉션이 발생했거나 새로운 자원이 생성되었을 때 사용된다.</p>
<pre><code>HTTP/1.1 301 Moved Permanently
Location: https://www.example.com/new-page</code></pre><p>위 예시를 살펴보자.
<code>301(Moved Permanently)</code> 를 통해 재요청 메서드가 변경될 수 있는 영구적인 리다이렉션을 명시하고 있다.</p>
<p>이 때, 브라우저는 <code>Location</code> 에 명시된 주소로 자동으로 이동하고 새로운 자원을 받아오게 된다.</p>
<p>그러니까 실제 사용자 경험 관점에서 대부분의 경우 리다이렉션이 발생한 줄 모른다고 한다.(알아서 <code>Location</code> 주소의 자원을 받아오므로)</p>
<pre><code>HTTP/1.1 201 Created
Location: https://www.example.com/resources/123</code></pre><p>다음처럼 새 리소스를 생성하고, 해당 리소스가 어느 URL로 접근 가능한지에 대해 명시할 때도 사용할 수 있다.</p>
<h5 id="www-authenticate">WWW-Authenticate</h5>
<p>상태코드 401(Unauthorized)는 요청한 자원에 대한 유효한 인증이 없을 때 응답하는 코드다.
상태코드 401과 함께 사용되는 헤더가 <code>WWW-Authenticate</code> 이다.
이 헤더는 자원에 접근하기 위한 인증 방식을 설명하는 헤더이다.
이를테면 다음과같이 Basic 인증을 요구할 수 있다.</p>
<p><code>WWW-Authenticate: Basic</code></p>
<p>다만 실제로는 이보다 조금 더 많은 정보를 알려주는 경우가 많다.
예를 들어 다음과 같이 <code>보안 영역(realm)</code>을 함께 알려주건, 인증에 사용될 문자집합도 알려줄 수 있다.</p>
<p><code>WWW-Authenticate: Basic realm=&quot;Access to engineering site&quot;, charset=&quot;UTF-8&quot;</code></p>
<p><code>realm</code> 이란 보안이 적용될 영역을 의미한다. 같은 서버가 제공하는 자원일지라도, <code>Engineering site</code> 라는 영역에 속한 자원에 접근 가능한 사용자는 <code>Financial site</code> 라는 영역에 속한 자원에 접근이 불가할 수 있다.</p>
<h4 id="요청과-응답-모두에서-활용되는-http-헤더">요청과 응답 모두에서 활용되는 HTTP 헤더</h4>
<h5 id="date">Date</h5>
<p>메시지가 생성된 날짜와 시각에 관련된 정보를 담은 헤더이다.
<code>Date: Tue, 15 Nov 1994 08:12:31 GMT</code></p>
<h5 id="connection">Connection</h5>
<p>클라이언트의 요청과 응답 간의 연결 방식을 설정하는 헤더이다.
<code>Connection: keep-alive</code> 헤더를 통해 상대방에게 지속 연결을 희망함을 알릴 수도,
<code>Connection: close</code> 헤더를 통해 서버나 클라이언트가 연결을 종료하고 싶음을 알릴 수도 있다.</p>
<p>만약 <code>요청1-&gt;요청2-&gt;요청3</code> 이 연달아 발생하는 상황이라고 해보자.</p>
<p>그럼 <code>요청1</code> 과 <code>요청2</code> 에는 <code>keep-alive</code> 를 명시해서 TCP 연결을 재사용해 핸드셰이크 비용을 줄일 수 있음이 명확하다.</p>
<p>그렇다면 <code>요청3</code> 은 <code>close</code> 를 해야할까 <code>keep-alive</code> 를 해야할까?</p>
<p><code>요청3</code> 이 끝난 이후, 사용자의 행동을 알 수 있다면 명확해지겠지만, 사용자가 어떤 행동을 취할지 모르는 상황이라고 가정했을 때, 결정하기가 쉽지 않을 것이다.</p>
<p>그래서 <code>keep-alive</code> 를 사용하되, <code>timeout</code> 을 짧게 설정해서 잠시동안 TCP 연결을 재사용할 수 있는 환경을 만들어주기도 한다고 한다.</p>
<h5 id="content-length">Content-length</h5>
<p>본문의 바이트 단위 크기(길이) 를 나타낸다.
<code>Content-length: 100</code></p>
<p>일반적으로 자동으로 본문 길이를 계산해서 보내주기 때문에 따로 명시할 필요는 없다.
서버가 요청 본문의 끝을 정확히 알 수 있도록 해서 내부적으로 서버가 언제 데이터 수신을 멈춰야 할지 알 수 있다고 한다.</p>
<p>그런데, 스트리밍 서비스나 압축된 데이터처럼 데이터가 동적으로 생성되거나 크기가 너무 클 경우에는 크기를 미리 알 수 없을 수 있다고 한다.</p>
<p>이럴 때는 <code>Transfer-Encoding: chunked</code> 를 사용해 데이터를 <code>청크</code> 단위로 나누어 보내고, 서버는 각 청크의 크기를 표시하되 마지막 청크를 보낼 때 <code>0</code> 으로 표시하여 데이터 전송이 끝났음을 알려준다고 한다.</p>
<h5 id="content-type-content-language-content-encoding">Content-Type, Content-Language, Content-Encoding</h5>
<p>이 헤더들은 전송하려는 메시지 본문의 표현 방식을 설정하는 헤더이다.
이런 점에서 이 헤더들은 <code>표현 헤더</code> 의 일종이라고도 부른다.</p>
<p><code>Content-Type</code> 헤더는 메시지 본문에서 사용된 미디어 타입을 담고 있다.
예를 들어 다음 헤더는 메시지 본문이 <code>HTML 문서 형식</code>이며, 문자 인코딩으로 <code>UTF-8</code>을 사용한다는 정보를 알려준다.
<code>Content-Type: text/html; charset=UTF-8</code></p>
<p><code>Content-Language</code> 헤더는 메시지 본문에 사용된 자연어를 명시한다.
해당 헤더 값은 언어 태그로 명시되며, 언어 태그는 하이픈(-)으로 구분된 다음과 같은 구조를 따른다.</p>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/5fdde03c-b0ce-4e13-9e89-be547d163ca7/image.png" width="600px">
</p>

<p>일반적으로는 <code>첫 번째 서브 태그(en, ko)</code>나 <code>두 번째 서브 태그(en-US, ko-KR)</code>까지만 사용된다.</p>
<p>주로 사용되는 언어 코드와 국가 코드는 다음과 같다.</p>
<p align="center" style="display:flex">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/c30c92b0-40ac-402f-ae81-bba9ba52ae08/image.png" width="300px">
      <img src="https://velog.velcdn.com/images/ldh-dodo/post/13eee6e2-e4ec-4b56-8014-065eaa0941ea/image.png" width="300px">
</p>


<p><code>Content-Encoding</code> 헤더에는 메시지 본문을 압축하거나 변환한 방식이 명시된다.
HTTP를 통해 송수신되는 데이터는 전송 속도를 개선하기 위해 종종 압축이나 변환이 되고는 하는데, 이때 사용된 방식이 명시된다.
명시될 수 있는 대표적인 값은 <code>gzip</code> <code>compress</code> <code>deflate</code> <code>br</code> 등이 있다.</p>
<h3 id="캐시">캐시</h3>
<p><code>캐시</code>란 불필요한 대역폭 낭비와 응답 지연을 방지하기 위해 정보의 사본을 임시로 저장하는 기술이다.
사본을 임시로 저장해두면 동일한 요청에 대해 캐시된 데이터를 활용할 수 있기에 불필요한 대역폭 낭비를 줄이고 빠르게 데이터에 접근할 수 있다.</p>
<p>만약, 클라이언트가 이미지를 조회하기 위해 서버를 향해 GET 요청을 보내고, 같은 이미지를 두 번 세 번 요청하는 상황에 캐시를 이용하면 좋다.</p>
<p><code>웹 브라우저</code>에 저장된 캐시를 <code>개인 전용 캐시(private cache)</code>,
<code>클라이언트와 서버 사이</code>에 저장된 캐시를 <code>공용 캐시(public cache)</code> 라고 부른다.
개인 전용 캐시에 초점을 맞춰 살펴볼 것이다.</p>
<p>캐시가 사본을 저장한다고 했는데, 그렇다면 캐시된 데이터가 최신 상태를 유지하고 있는지를 어떻게 알 수 있을까?</p>
<p>최신 원본 데이터와 얼마나 유사한지를 <code>캐시 신선도(cache freshness)</code>라고 표현하기도 하는데, 이 캐시 신선도를 유지하는 가장 기본적인 방법은 <code>캐시된 데이터에 유효 기간을 설정하는 방법</code> 이다.
캐시 데이터에 유효 기간을 설정하고, 기간이 만료되었다면 원본 데이터를 다시 요청하는 방식으로 캐시 신선도를 유지할 수 있다.</p>
<p>캐시할 데이터에 유효 기간을 부여하는 방법으로 응답 메시지의 <code>Expires 헤더(날짜)</code> 와 <code>Cache-Control 헤더의 Max-Age</code> 값 (초)을 사용할 수 있다.</p>
<p>아래 두 그림을 보면 이해가 쉽다.</p>
<p align="center" style="display:flex">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/7c72e044-c976-40f4-81bc-db4973145351/image.png" width="300px">
      <img src="https://velog.velcdn.com/images/ldh-dodo/post/fc580cea-da76-4422-ae9b-213ef8937247/image.png" width="300px">
</p>

<p align="center">

</p>

<p>캐시된 자원이 만료되었더라도, 캐시된 자원이 여전히 최신 정보라면 클라이언트는 굳이 서버로부터 같은 자원을 응답받을 필요가 없다.
따라서 캐시의 유효 기간이 만료되었다면 클라이언트는 캐시된 자원이 여전히 신선한지를 재검사해야 할 필요가 있다.
이 재검사 방법에는 <code>날짜를 기반으로 서버에게 물어보는 방법</code> 과 <code>엔티티 태그를 기반으로 서버에게 물어보는 방법</code> 이 있다.</p>
<h3 id="날짜-기반-재검사-방법">날짜 기반 재검사 방법</h3>
<p>클라이언트는 <code>If-Modified-Since</code> 헤더를 통해 서버에게 <code>특정 시점 이후로 원본 데이터에 변경</code> 이 있는지 물어볼 수 있다.
이 시점 이후로 원본에 변경이 있었다면, 그때만 새 자원으로 응답하도록 서버에게 요청하는 헤더이다.</p>
<p>다음 예시를 통해 이해해보자.
해당 요청 메시지는 <code>2024년 8월 23일 금요일 09:00:00 이후에 www.example.com/index.html 의 자원이 변경되었니? 변경이 되었을 경우에만 새 자원으로 응답해 줘</code> 라는 요청 메시지와 같다.</p>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/a57089d0-9d9e-4f06-b19a-5e80d9c75650/image.png" width="600px">
</p>

<p>자, 이제 서버 입장에서 해당 헤더를 받았다고 가정하고 생각해보자.
서버의 자원은 크게 셋 중 하나의 상황을 따르게 된다.</p>
<ol>
<li>요청받은 자원이 변경되었음</li>
<li>요청받은 자원이 변경되지 않았음</li>
<li>요청받은 자원이 삭제되었음</li>
</ol>
<h4 id="요청받은-자원이-변경된-상황">요청받은 자원이 변경된 상황</h4>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/a76578e1-9973-40fc-915b-4cd9e39cc3ca/image.png" width="600px">
</p>

<p>서버는 상태 코드 200(OK)와 함께 새로운 자원을 반환한다.</p>
<h4 id="요청받은-자원이-변경되지-않은-상황">요청받은 자원이 변경되지 않은 상황</h4>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/a6f2f220-e904-4cc2-8496-49e7bc127f27/image.png" width="600px">
</p>


<p>서버는 상태 코드 304(Not Modified)를 통해 클라이언트에게 자원이 변경되지 않았음을 알린다.
서버는 자원의 변경 여부 뿐만 아니라, <code>마지막 변경 시점</code>도 알려줄 수 있는데, 이를 위한 헤더로 <code>Last Modified</code> 가 있다. 
이 헤더는 특정 자원이 마지막으로 수정된 시점을 나타낸다.</p>
<h4 id="요청-받은-자원이-삭제된-상황">요청 받은 자원이 삭제된 상황</h4>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/f162c37d-7f2f-4cc7-ad42-54ba5de8ce90/image.png" width="600px">
</p>

<p>서버는 상태코드 404(Not Found)를 통해 요청한 자원이 존재하지 않음을 알린다.</p>
<h3 id="엔티티-태그-기반-재검사-방법">엔티티 태그 기반 재검사 방법</h3>
<p><code>엔티티 태그(Etag)</code>는 <code>자원의 버전</code> 을 식별하기 위한 정보이다.
여기서 <code>버전</code> 이란 <code>유의미한 변경 사항</code> 을 의미한다.
즉, 자원이 변경될 때마다 자원의 버전을 식별하는 Etag 값이 변경된다.</p>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/d1e23cff-3baa-400a-8731-90e55c3d9e32/image.png" width="600px">
</p>



<p>클라이언트는 캐시 신선도를 검사하기 위해 <code>이 Etag 값과 일치하는 값이 있니?</code> 와 같이 물어볼 수 있다.
이를 위해 사용하는 헤더가 바로 <code>If-None-Match</code> 이다.
예를 들어, 다음의 요청 메시지는 <code>혹시 Etag 값이 abc인 www.example.com/index.html 이라는 자원이 있니? 해당 자원이 변경되었다면(Etag 값이 변경되었다면) 그때만 새 자원으로 응답해줘</code> 라는 요청 메시지와 같다.</p>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/3b2efb95-f84f-4647-b6bf-d9fd7ac07260/image.png" width="600px">
</p>


<p>이 때도 서버의 자원은 다음과 같은 상황 중 하나를 따른다.</p>
<ol>
<li>요청받은 자원이 변경되었음</li>
<li>요청받은 자원이 변경되지 않았음</li>
<li>요청받은 자원이 삭제되었음</li>
</ol>
<p>이에 대한 동작은 날짜 기반 재검사와 동일하다.</p>
<h3 id="쿠키">쿠키</h3>
<p>HTTP는 기본적으로 상태를 유지하지 않는 스테이트리스 프로토콜이다.
만약 그렇다면, 다음과 같은 기능은 어떻게 구현되는 것일까?</p>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/39d42312-0ebe-4183-988b-6f9458196548/image.png" width="600px">
</p>

<p>위 기능은 클라이언트의 상태를 알고 있어야만 구현할 수 있는 기능일텐데 말이다.</p>
<p>HTTP 쿠키(이하 쿠키)를 통해 이러한 기능을 구현할 수 있다.
<code>쿠키(cookie)</code>란 서버에서 생성되어 클라이언트 측에 저장되는 데이터로, 상태를 유지하지 않는 HTTP의 특성을 보완하기 위한 수단이다.
서버가 클라이언트의 상태를 알 수 있게끔 하는 특별한 데이터이다.</p>
<p>쿠키는 개발자도구 -&gt; 애플리케이션 -&gt; 저장 용량 -&gt; 쿠키 에서 확인할 수 있다.</p>
<p>쿠키를 이루는 정보는 기본적으로 <code>&lt;이름, 값&gt;</code> 쌍의 형태를 띠고 있고, 추가로 <code>적용 범위</code>와 <code>만료 기간</code> 등 다양한 속성을 가질 수 있다.</p>
<p>서버는 쿠키를 생성하여 클라이언트에게 전송하고, 클라이언트는 전달받은 쿠키를 저장해 두었다가 추후 동일한 서버에 보내는 요청 메시지에 쿠키를 포함하여 전송한다.
서버는 쿠키 정보를 참고해 두 개의 요청이 같은 클라이언트에서 왔는지, 로그인 상태를 유지하고 있는지 등을 알 수 있다.</p>
<p>서버가 생성하는 쿠키는 응답 메시지의 <code>Set-Cookie</code> 헤더를,
클라이언트가 서버로부터 전달받은 쿠키를 활용할 때는 요청 메시지의 <code>Cookie</code> 헤더를 통해 전달된다.</p>
<h4 id="응답-메시지">응답 메시지</h4>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/c0e19d92-af2f-4481-8b49-eb96804fd67e/image.png" width="600px">
</p>

<p>응답 메시지의 Set-Cookie 헤더를 통해 쿠키의 이름, 값과 더불어 세미콜론(;) 으로 구분되는 속성들을 전달할 수 있다.</p>
<p>쿠키 관련 정보로 <code>이름</code> <code>값</code> 뿐만 아니라, <code>도메인</code> 과 <code>경로</code>가 있다.</p>
<h5 id="domain">domain</h5>
<p><code>www.naver.com</code> 에서 받은 쿠키를 전혀 다른 웹사이트인 <code>www.google.com</code> 에 전송하면 안되기에, 쿠키는 사용 가능한 도메인이 정해져 있다.
이는 응답 메시지 속 <code>Set-Cookie</code> 헤더의 <code>domain</code> 속성으로 정해진다.</p>
<h5 id="path">path</h5>
<p>또한 같은 도메인이라도 경로별로 쿠키를 구분하여 사용하고 싶을 때가 있다.
특정 도메인의 특정 하위 경로에서만 사용하고자 하는 쿠키가 있을 수 있기에, 이 때는 <code>Set-Cookie</code> 헤더의 <code>path</code> 속성으로 지정할 수 있다.
그러면, path로 지정된 경로와 그 앞부분이 일치하는 경로(하위 경로) 에서 해당 쿠키 정보를 활용할 수 있게 된다.</p>
<h5 id="expires--max-age">Expires / Max-Age</h5>
<p><code>Expires</code> 속성에는 <code>[요일, DD-MM-YY HH:MM:SS GMT]</code> 형식으로 표기되는 쿠키 만료 시점이,
<code>Max-Age</code> 속성에는 초 단위 유효 기간이 전달 될 수 있다.
해당 유효기간이 지나면, 해당 쿠키는 삭제되어 전달되지 않는다.</p>
<h4 id="요청-메시지">요청 메시지</h4>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/3b72706b-1a74-4793-ac95-1f429154bdf4/image.png" width="600px">
</p>

<p>요청 메시지의 Cookie 헤더 값은 서버에 전달할 쿠키의 이름과 값을 나타내는 헤더이다.
여러 개의 쿠키 값을 전달해야 한다면, 세미 콜론을 이용한다.</p>
<h4 id="쿠키의-한계">쿠키의 한계</h4>
<p>쿠키의 대표적인 한계는 바로 <code>보안</code> 이다.
쿠키에 개인 정보를 비롯해 보안에 민감한 정보를 담아 송수신하고 저장하는 것은 바람직하지 못하다.
쿠키 정보가 쉽게 노출되거나 조작될 수 있기 때문이다.</p>
<p>이를 보완하기 위한 속성으로 <code>Secure</code> 와 <code>HttpOnly</code> 라는 속성이 있다.</p>
<h5 id="secure">Secure</h5>
<p>나중에 학습하겠지만, 간단하게 알아보겠다.
HTTPS 프로토콜이 사용되는 경우에만 쿠키를 전송하도록 하는 속성이다.
HTTPS 프로토콜은 HTTP를 더 안전한 방식으로 전송할 수 있는 프로토콜이다.</p>
<h5 id="httponly">HttpOnly</h5>
<p>HTTP 송수신을 통해서만 쿠키를 이용하도록 제한하는 속성이다.
쿠키는 JS를 통해서도 접근이 가능한데, 악의적 의도를 가진 해커는 (정상적인 HTTP 송수신을 통해서가 아닌) JS로 쿠키를 중간에 가로채거나 위변조할 수 있다.
HttpOnly는 이런 상황을 방지하기 위해 자바스크립트에서 쿠키에 접근하지 못하도록 하는 속성이다.</p>
<h3 id="콘텐츠-협상과-표현">콘텐츠 협상과 표현</h3>
<p>한국에서 접속하거나 한국어 계정으로 특정 URL에 접속하면 한국어로 된 웹 페이지를 볼 수 있고,
해외로 나가서 웹 페이지를 접속하면 해당 국가의 웹 페이지를 볼 수 있는 상황을 경험해 본 적 있을 것이다.</p>
<p>분명 같은 자원을 요청했는데, 어떻게 다른 결과를 얻는걸까?
이는 HTTP의 <code>콘텐츠 협상(content negotiation)</code>을 통해 이루어진다.
<code>콘텐츠 협상</code> 이라나 같은 URI에 대해 가장 적합한 <code>자원의 형태</code>를 제공하는 메커니즘을 의미한다.
이 때, <code>송수신 가능한 자원의 형태</code> 를 자원의 <code>표현(representation)</code> 이라고 한다.</p>
<p>클라이언트가 선호하는 표현을 반영하고자 콘텐츠 관련 협상 HTTP 헤더들이 사용된다.</p>
<p>선호하는 미디어 타입을 나타내기 위한 <code>Accept</code>,
선호하는 언어를 나타내기 위한<code>Accept-Language</code>,
선호하는 문자 인코딩과 압축 방식을 나타내기 위한 <code>Accept-Charset</code> <code>Accept-Encoding</code> 이 있다.</p>
<p>클라이언트가 선호하는 언어가 한국어일 경우, <code>Accept-Language: ko</code> 를 헤더에 추가하여 서버에 요청하면, 서버는 클라이언트가 선호하는 언어를 인식하여 한국어로 표현된 자원을 보내주게 되는 것이다.</p>
<h4 id="콘텐츠-협상의-우선-순위">콘텐츠 협상의 우선 순위</h4>
<p>콘텐츠 협상에서 중요한 점은 선호도에 <code>우선 순위</code> 를 반영할 수 있다는 점이다. 
예를 들어, <code>한국어를 가장 선호하지만, 영어도 받을 용의가 있다</code> 와 같이 표현이 가능하다.</p>
<p>이러한 우선 순위는 콘텐츠 협상 관련 헤더의 <code>q</code> 값으로 표현된다.
<code>q</code>는 <code>Quality Value</code> 의 약자로, 특정 표현을 얼마나 선호하는지를 나타낸다.
생략 되었을 경우에는 1을 의미하고, 범위는 0부터 1까지이며, 값이 클수록 우선순위가 높다.</p>
<p>다음 예시를 보면, 한국어, 영어 순으로 선호하고, HTML, XML, 일반 텍스트순으로 선호한다는 것을 알 수 있다.</p>
<p align="center">
    <img src="https://velog.velcdn.com/images/ldh-dodo/post/d5a89e35-5057-4635-9067-ffccabc4443f/image.png" width="600px">
</p>

<blockquote>
<p><strong>DNS가 뭔가요?</strong>
DNS는 도메인 네임을 IP 주소로 변환해주는 애플리케이션 계층 프로토콜이자, 계층적이고 분산된 도메인 네임에 대한 관리체계입니다.</p>
</blockquote>
<blockquote>
<p><strong>DNS 작동 방식에 대해 설명해주세요.</strong>
사용자가 웹 브라우저에 도메인 이름을 입력하면, 브라우저는 먼저 로컬 캐시에서 해당 도메인의 IP 주소를 확인하려 시도합니다. 
만약 로컬 캐시에 없으면, 브라우저는 DNS 서버에 요청을 보냅니다. DNS 서버는 해당 도메인 이름에 대한 IP 주소를 찾아서 브라우저에 반환합니다. 
이 과정에서 DNS 서버는 루트 네임 서버, TLD 네임 서버, 책임 네임 서버로 나뉜 여러 계층을 거쳐 IP 주소를 찾을 수 있습니다.
이후 브라우저는 받은 IP 주소를 사용하여 실제 웹 서버에 HTTP 요청을 보냅니다. 
이로써 웹 페이지가 로드됩니다.</p>
</blockquote>
<blockquote>
<p><strong>DNS 질의 종류에 대해 설명해주세요.</strong>
DNS 질의에는 재귀적 질의와 반복적 질의가 있습니다.
재귀적 질의는 클라이언트가 로컬 네임서버에게 도메인 네임을 질의하면, 로컬 네임 서버는 루트 네임 서버에게, 루트 네임 서버는 TLD 네임 서버에게, TLD 네임 서버는 다음 단계에 질의하는 과정을 반복하며, 최종 응답을 역순으로 전달받는, 즉 재귀적으로 질의가 이루어지는 방식입니다.
반복적 질의는 클라이언트가 로컬 네임서버에게 도메인 네임을 질의하면, 로컬 네임 서버는 루트 도메인에게 질의해서 다음으로 질의할 네임 서버의 주소를 응답 받고, 이렇게 다음으로 질의할 네임 서버의 주소를 응답받는 과정을 반복하다가 최종 응답 결과를 클라이언트에게 알려주는 방식입니다.</p>
</blockquote>
<blockquote>
<p><strong>DNS 서버에게 IP 주소를 요청할 때, 왜 UDP를 사용하나요?</strong>
DNS는 신뢰성보다 속도가 더 중요한 서비스이기 때문에 연결 설정 비용이 없는 UDP를 사용합니다.
따라서 연결 지향적인 TCP보다 빠르고 효율적인 UDP를 사용하는 것이 유리합니다. </p>
</blockquote>
<blockquote>
<p><strong>DNS 레코드가 무엇인가요?</strong>
DNS 서버는 데이터베이스 서버의 한 유형이며, 클라이언트로부터 질의를 받았을 때 그에 맞는 데이터를 응답해야 합니다. 데이터베이스의 한 항목(row)을 DNS 서버에서는 리소스 레코드라고 부릅니다.
예를들어 A 레코드는 도메인 이름을 IPv4 주소로 매핑하는 데 사용됩니다.</p>
</blockquote>
<blockquote>
<p><strong>HTTP 프로토콜에 대해서 설명해주세요.</strong>
HTTP는 웹에서 클라이언트와 서버 간에 정보를 주고받기 위한 프로토콜입니다. 
클라이언트는 HTTP 메서드를 사용해 요청을 보내고, 서버는 HTTP 상태 코드로 응답합니다. 
HTTP는 비연결성 프로토콜로, 요청과 응답 간에 상태를 유지하지 않으며, GET, POST, PUT, DELETE 등의 메서드를 사용하여 데이터를 처리합니다.</p>
</blockquote>
<blockquote>
<p><strong>HTTP의 요청/응답 모델에 대해 설명해주세요.</strong>
HTTP의 요청/응답 모델은 클라이언트가 서버에 요청을 보내면, 서버가 이에 대한 응답을 반환하는 방식입니다. 
클라이언트는 GET, POST 등의 HTTP 메서드와 함께 요청을 보내고, 서버는 요청에 대한 처리 결과를 HTTP 상태 코드와 함께 응답합니다. 
이 모델은 비연결성이므로, 요청과 응답 간에 상태를 유지하지 않습니다.</p>
</blockquote>
<blockquote>
<p><strong>HTTP 메서드중 GET과 POST의 차이점에 대해 설명해주세요.</strong>
GET 메서드는 클라이언트가 서버에게 데이터를 요청할 때 사용합니다. 이때 요청은 URL에 포함된 쿼리 파라미터를 통해 데이터를 전달합니다. 서버는 요청에 대한 응답으로 데이터를 클라이언트에게 전달합니다.
POST 메서드는 클라이언트가 서버에 데이터를 보낼 때 사용합니다. 주로 폼 데이터를 전송할 때 사용하며, 서버는 데이터를 받아 처리하고, 보통 저장하거나 처리한 결과를 응답합니다.</p>
</blockquote>
<blockquote>
<p><strong>HTTP 메서드중 PUT과 PATCH의 차이점에 대해 설명해주세요.</strong>
PUT 메서드는 리소스를 덮어쓰거나 새로 생성할 때 사용합니다. 주로 요청 본문에 포함된 데이터로 해당 리소스를 덮어쓰는 방식으로 작동합니다. 만약 리소스가 없으면 새로 생성하기도 합니다.
PATCH 메서드는 리소스의 일부만 수정할 때 사용합니다. 
전체 리소스를 덮어쓰지 않고, 요청 본문에 포함된 일부 데이터만 수정합니다.</p>
</blockquote>
<blockquote>
<p><strong>HTTP 상태 코드가 뭔가요? 알고 있는 상태 코드 몇가지 설명해주세요</strong>
HTTP 상태 코드는 서버가 클라이언트의 요청을 처리한 후 그 결과를 나타내는 코드입니다.
상태 코드는 3자리 숫자로 이루어져 있으며, 첫 번째 숫자는 응답의 범주를 나타냅니다.
예를 들어, 상태 코드 200은 요청이 성공적으로 처리되었음을 의미하고, 상태 코드 404는 요청한 자원을 찾을 수 없음을 의미하고, 상태코드 500은 서버의 예기치 못한 상황으로 요청을 처리할 수 없음을 의미합니다.</p>
</blockquote>
<blockquote>
<p><strong>HTTP 헤더가 뭘까요? 알고 있는 헤더 몇 가지 설명해주세요.</strong>
HTTP 헤더는 클라이언트와 서버 간에 전송되는 추가적인 메타데이터입니다.
예를 들어, Content-Type 헤더는 요청이나 응답 본문의 데이터 형식을 명시합니다.
Authorization 헤더는 클라이언트의 인증 정보를 포함합니다.
User-Agent 헤더는 클라이언트의 브라우저나 운영체제에 대한 정보를 포함합니다.</p>
</blockquote>
<blockquote>
<p><strong>HTTP의 무상태성(Stateless)에 대해서 설명해주세요.</strong>
HTTP의 무상태성이란, 서버가 HTTP 요청을 보낸 클라이언트와 관련된 상태를 기억하지 않는다는 의미입니다.
즉, 각 요청은 기본적으로 독립적인 요청으로 간주됩니다.
무상태성 덕분에 언제든 쉽게 서버를 추가할 수 있어 확장성이 높아지고 서버 장애시 쉽게 다른 서버로 대체가 가능해 견고성이 높아집니다.</p>
</blockquote>
<blockquote>
<p><strong>HTTP Keep-Alive에 대해서 설명해주세요.</strong>
HTTP Keep-Alive는 HTTP 요청과 응답 간에 TCP 연결을 유지하는 기능입니다.
이를 통해 여러 요청과 응답을 하나의 연결로 처리할 수 있어, 매번 새로운 연결을 설정할 필요가 없고, 그로 인해 웹 응답 속도가 빨라집니다.</p>
</blockquote>
<blockquote>
<p><strong>HTTP 파이프라이닝에 대해서 설명해주세요.</strong>
HTTP 파이프라이닝은 요청에 대한 응답을 기다리지 않고 여러 개의 요청을 한 번에 보내는 기법입니다.
응답을 기다리지 않기 때문에 서버와 클라이언트 간의 대기 시간을 줄여서 성능을 향상시킬 수 있습니다.</p>
</blockquote>
<blockquote>
<p><strong>HTTP/1.1, HTTP/2, HTTP/3 각각의 특징에 대해 설명해주세요.</strong>
HTTP/1.1은 전통적인 HTTP 프로토콜로, 각 요청마다 독립적인 TCP 연결을 사용합니다.
이로 인해 여러 요청을 처리할 때 대기 시간이 발생할 수 있습니다.
HTTP/2는 성능 향상을 위해 여러 요청을 하나의 연결로 처리할 수 있는 다중화 기능을 제공합니다.
이로 인해 여러 요청을 병렬로 처리할 수 있어 대기 시간이 줄어듭니다.
HTTP/3는 QUIC(Quick UDP Internet Connections) 프로토콜을 기반으로 한 HTTP 버전으로, TCP 대신 UDP를 사용합니다.
이로 인해 연결 설정 시간이 단축되고, 패킷 손실에 대한 회복력이 향상되어 더 빠르고 안정적인 통신을 제공합니다.</p>
</blockquote>
<blockquote>
<p><strong>쿠키와 세션에 대해서 설명해주세요.</strong>
쿠키는 HTTP 서버가 클라이언트를 식별할 수 있도록 하기 위해서 사용됩니다.
클라이언트가 웹 서버에 처음 접속하면, 서버는 쿠키를 생성해 클라이언트에게 전달합니다.
이후 클라이언트는 서버에 요청을 보낼 때 쿠키를 함께 전송하며, 서버는 이를 통해 클라이언트를 식별합니다.
쿠키는 클라이언트 측에 저장되기 때문에, 유출될 위험이 있을 수 있습니다.
그리고 세션은 쿠키와 유사하지만 서버 측에서 클라이언트의 정보를 저장한다는 점에서 차이가 있습니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[컴퓨터 네트워크 - 전송 계층]]></title>
            <link>https://velog.io/@ldh-dodo/%EC%BB%B4%ED%93%A8%ED%84%B0-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%A0%84%EC%86%A1-%EA%B3%84%EC%B8%B5</link>
            <guid>https://velog.io/@ldh-dodo/%EC%BB%B4%ED%93%A8%ED%84%B0-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%A0%84%EC%86%A1-%EA%B3%84%EC%B8%B5</guid>
            <pubDate>Wed, 19 Mar 2025 08:49:24 GMT</pubDate>
            <description><![CDATA[<h1 id="ip의-한계">IP의 한계</h1>
<p>저번에 학습한 네트워크 계층의 IP는 한계점이 있다.
그것은 바로 <code>신뢰할 수 없는 통신</code> 과 <code>비연결형 통신</code>을 한다는 점이다.</p>
<p><code>신뢰할 수 없는 통신</code> 은 IP 프로토콜이 패킷이 수신지까지 제대로 전송되었다는 보장을 하지않는 특징이다.
패킷의 데이터가 손상되거나 중복된 패킷이 전송되어도 확인하지 않고, 재전송하지 않으며, 순서대로 패킷이 도착할 것이라는 보장도 해주지 않는다.
이러한 전송 특성을 다른 말로 <code>최선형 전달</code> 이라고도 한다.
이는 <code>최선을 다해 보겠지만, 전송 결과에 대해서는 어떠한 보장도 하지 않습니다</code> 를 의미한다.</p>
<p><code>비연결형 통신</code>은 송수신 호스트 간에 사전 연결 수립 작업을 거치지 않는 특징을 의미한다.
그저 수신지를 향해 패킷을 내보내기만 할 뿐이다.</p>
<p>그렇지만 이런 특징은 모든 패킷이 제대로 전송되었는지 일일이 확인하고, 호스트 간의 연결을 수립하는 작업이 없기 때문에 패킷의 빠른 송수신이 가능하다.</p>
<p>금융 서비스처럼 반드시 신뢰성 있는 전송을 보장해야 하는 경우가 있는 반면, 동영상 스트리밍 서비스나 실시간 영상 통화처럼 빠른 전송이 우선시 되는 경우도 있다.</p>
<p>이처럼 신뢰성 있는 전송이 모든 경우에 필요하지는 않다.</p>
<p>아무튼, 이러한 두 특징을 보완할 수 있는 연결형 통신을 지원하는 대표적인 프로토콜로 <code>TCP</code>가 있는데, 아래서 차차 살펴볼 것이다.</p>
<h2 id="icmpinternet-control-message-protocol">ICMP(Internet Control Message Protocol)</h2>
<p>IP의 신뢰할 수 없는 전송 특성과 비연결형 전송 특성을 보완하기 위한 네트워크 계층의 프로토콜로 ICMP가 있다.
ICMP는 <code>IP 패킷의 전송 과정에 대한 피드백 메시지를 얻기 위해</code> 사용하는 프로토콜이다.
유의해야할 점은, <code>ICMP가 IP의 신뢰성을 보장하지 않는다는 것</code>이다.
데이터 전송을 보장해주는게 아닌, 네트워크 문제 진단 및 오류 보고를 위한 프로토콜인 것이다.</p>
<h3 id="icmp-메시지-종류">ICMP 메시지 종류</h3>
<p>ICMP 메시지 종류로는 크게 두 가지가 있다.</p>
<ol>
<li>오류 메시지</li>
</ol>
<ul>
<li>패킷 전송 중 발생한 문제를 알림 (예: 목적지 도달 불가, 시간 초과 등)</li>
</ul>
<ol start="2">
<li>진단 메시지</li>
</ol>
<ul>
<li>네트워크 상태를 확인하고 문제를 분석 (예: ping 명령어)</li>
</ul>
<h1 id="응용-계층과의-연결-다리-포트">응용 계층과의 연결 다리, 포트</h1>
<p>만약, 네트워크 외부에서 내가 전송받으려는 사진 파일을 구성하는 패킷들이 라우팅되어 컴퓨터로 도착했다고 가정해보자. 
그리고 나는 컴퓨터로 웹 브라우저, 게임, 메신저 프로그램을 실행하고 있다.</p>
<p>패킷이 컴퓨터에 도달했으니까, 전송이 끝난 것일까?
아니다. 이 패킷들은 실행 중인 특정 애플리케이션 프로세스까지 전달되어야 한다.
즉, 패킷의 최종 수신 대상은 특정 애플리케이션 프로세스라는 것이다.</p>
<p>패킷이 실행 중인 특정 애플리케이션 까지 전달되려면, 패킷에 <code>특정 애플리케이션을 식별할 수 있는 정보</code> 가 포함되어야 한다. 이러한 정보를 <code>포트</code> 라고 한다.</p>
<h2 id="포트의-번호">포트의 번호</h2>
<p>전송 계층에서는 <code>포트 번호</code> 를 통해 특정 애플리케이션을 식별한다.
정확히는 수신지 포트와 송신지 포트를 통해 송수신지 호스트의 애플리케이션을 식별한다.</p>
<p>포트 번호는 16비트로 표현 가능하고, 번호의 범위에 따라 세 종류로 나뉜다.
각각 <code>잘 알려진 포트</code>, <code>등록된 포트</code>, <code>동적 포트</code> 이다.</p>
<p align="center"><img src="https://velog.velcdn.com/images/ldh-dodo/post/269aee3d-e856-4185-823d-bf8e30361c66/image.png" width="600px"/></p>

<h3 id="잘-알려진-포트well-known-port">잘 알려진 포트(well known port)</h3>
<ul>
<li><p>0번 ~ 1023 번 포트</p>
</li>
<li><p>시스템 포트라고도 불림</p>
</li>
<li><p>범용적으로 사용되는 애플리케이션 프르토콜이 일반적으로 사용하는 포트 번호를 의미</p>
<p align="center"><img src="https://velog.velcdn.com/images/ldh-dodo/post/2a20ec95-b111-4e5e-9dd6-59a80bb2e4a1/image.png" width="600px"/></p>

</li>
</ul>
<h3 id="등록된-포트registered-port">등록된 포트(registered port)</h3>
<ul>
<li>1024 ~ 49151 번 포트</li>
<li>잘 알려진 포트에 비해 덜 범용적이지만, 흔히 사용되는 애플리케이션 프로토콜에 할당하기 위해 사용됨</li>
</ul>
<h3 id="동적-포트dynamic-port">동적 포트(dynamic port)</h3>
<ul>
<li>49152 ~ 65535 번 포트</li>
<li><code>사설 포트</code>, <code>임시 포트</code> 라고도 불림</li>
</ul>
<p>보통, <code>서버</code> 는 <code>잘 알려진 포트</code> 혹은 <code>등록된 포트</code> 로 동작하는 경우가 많고,
<code>클라이언트</code> 는 <code>동적 포트</code> 번호 중 임의의 번호가 할당되는 경우가 많다.</p>
<p>IP 주소와 포트 번호에 대한 정보가 함께 주어지면, <code>특정 호스트에서 실행 중인 특정 애플리케이션 프로세스</code> 를 식별할 수 있는데, 그래서 포트 번호는 <code>IP 주소:포트 번호</code> 형식으로 IP와 함께 표기되는 경우가 많다.
<code>192.168.0.15:8000</code> 처럼 말이다.</p>
<h1 id="전송-계층-역할-정리">전송 계층 역할 정리</h1>
<p>전송 계층은 신뢰할 수 있는 연결형 통신이 가능한 프로토콜을 제공하기에, 네트워크 계층의 한계를 보완할 수 있고, 포트를 통해 응용 계층의 애플리케이션을 식별함으로써 응용 계층과의 연결 다리 역할을 수행한다.</p>
<h1 id="포트-기반-nat">포트 기반 NAT</h1>
<p>NAT이란 IP 주소를 변호나하는 기술이며 사설 IP 주소와 공인 IP 주소를 변환하는데 사용된다고 했다. 이러한 변환을 위해 주로 사용되는 것이 <code>NAT 변환 테이블</code> 이다.</p>
<h2 id="nat-변환-테이블">NAT 변환 테이블</h2>
<p>NAT 변환 테이블은 다음 그림처럼 변환의 대상이 되는 IP 주소 쌍이 명시되어 있다.</p>
<p align="center"><img src="https://velog.velcdn.com/images/ldh-dodo/post/d1cf1eb7-f2df-4ee6-9e47-716105e67c14/image.png" width="600px"/></p>

<p>그런데, 이런 방식에는 한 가지 문제점이 있다.
테이블을 자세히 살펴보면 사설 IP 주소 하나 당 공인 IP 주소가 대응되는데, 이는 결국 사설 IP 주소의 수만큼 공인 IP 주소가 필요하다는 말이다.</p>
<p>이런 이유로 오늘날 대중적으로 활용되는 NAT은 다수의 사설 IP 주소를 그보다 적은 공인 IP 주소로 변환하는 방식을 채택한다.
여기서 바로 포트가 활용된다.</p>
<h3 id="naptnetwork-address-port-translation">NAPT(Network Address Port Translation)</h3>
<p>NAPT는 포트를 활용해 하나의 공인 IP 주소를 여러 사설 IP 주소가 공유할 수 있도록 하는 NAT의 일종이다.
NAPT는 다음 그림처럼, NAT 테이블에 변환할 IP 주소 쌍과 더불어 포트 번호도 함께 기록하고 변환한다.</p>
<p align="center"><img src="https://velog.velcdn.com/images/ldh-dodo/post/f6652f72-c703-4569-97ac-9d192ebc01ca/image.png" width="600px"/></p>

<p>같은 공인 IP 주소로 변환되더라도 포트 번호가 다르기 때문에 내부 IP 주소를 구분할 수 있게 되는 것이다.
이 방식 덕분에, 사설 IP 주소와 공인 IP 주소를 N : 1로 관리할 수 있다.</p>
<h1 id="포트-포워딩">포트 포워딩</h1>
<p>NAT 변환 테이블이 백지 상태일 때, 외부에서 내부 네트워크로의 연결을 시도한다고 가정하자.
하지만 NAT은 외부에서 오는 &quot;새로운 연결&quot;을 기본적으로 차단하기 때문에, 그냥은 통신이 불가능하다.</p>
<p>그러나 <strong>포트 포워딩(port forwarding)</strong>을 사용하면 가능하다!</p>
<p>포트 포워딩이란, 네트워크 내 특정 장치(호스트)에 미리 IP 주소와 포트 번호를 할당하고,
외부에서 해당 &quot;IP주소:포트번호&quot;로 들어오는 트래픽을 해당 장치로 전달하는 기능이다.
쉽게 말해, <code>외부에서 오는 트래픽을 내가 원하는 내부 장치로 안내하는 길잡이</code> 역할을 한다.</p>
<p>즉, 특정 외부 포트로 들어오는 요청이 있으면, 미리 지정한 내부 IP와 내부 포트로 트래픽을 유도하는 방식이다.
이를 통해 내부 네트워크 정보(IP 주소 등)는 외부에 노출되지 않은 채로 통신이 가능해진다.</p>
<h1 id="tcp와-udp">TCP와 UDP</h1>
<p>네트워크 계층에서 가장 중요한 프로토콜이 IP라면, 전송 계층에서 가장 중요한 프로토콜은 <code>TCP</code>와 <code>UDP</code> 이다.
<code>TCP(Transmission Control Protocol</code> 는 신뢰할 수 있는 통신을 위한 <code>연결형 프로토콜</code>이고,
<code>UDP(User Datagram Protocol)</code> 는 TCP보다 신뢰성은 떨어지지만 비교적 빠른 통신이 가능한 <code>비연결형 프로토콜</code>이다.</p>
<h2 id="tcp-통신-단계와-세그먼트-구조">TCP 통신 단계와 세그먼트 구조</h2>
<p>TCP 통신을 세 단계로 나누면 다음과 같다.</p>
<ol>
<li>연결 수립</li>
<li>데이터 송수신</li>
<li>연결 종료</li>
</ol>
<h3 id="tcp-세그먼트-구조">TCP 세그먼트 구조</h3>
<h4 id="mssmaximum-segment-size">MSS(Maximum Segment Size)</h4>
<p>MTU는 헤더의 크기까지 포함했던 반면, MSS는 TCP 헤더 크기는 제외한다.</p>
<h4 id="tcp-세그먼트-헤더-구조">TCP 세그먼트 헤더 구조</h4>
<p align="center"><img src="https://velog.velcdn.com/images/ldh-dodo/post/315b40f7-1758-49b0-b5d1-48bf9daa0ed4/image.png" width="600px"/></p>

<p>이 중에서, TCP의 기본 동작을 이해하기 위한 기본적인 필드를 살펴보자.</p>
<h5 id="송신지-포트와-수신지-포트">송신지 포트와 수신지 포트</h5>
<ul>
<li>이름 그대로 송신지 또는 수신지 애플리케이션을 식별하는 포트 번호가 명시됨</li>
</ul>
<h5 id="순서-번호sequence-number">순서 번호(sequence number)</h5>
<ul>
<li>순서 번호가 명시됨</li>
<li><code>순서 번호</code> 란 송수신되는 세그먼트의 올바른 순서를 보장하기 위해 세그먼트 데이터의 첫 바이트에 부여되는 번호</li>
</ul>
<h5 id="확인-응답-번호acknowledgment-number">확인 응답 번호(acknowledgment number)</h5>
<ul>
<li>상대 호스트가 보낸 세그먼트에 대한 응답으로, <code>다음으로 수신하기를 기대하는 순서 번호</code>가 명시됨</li>
</ul>
<h5 id="제어-비트control-bits">제어 비트(control bits)</h5>
<ul>
<li>현재 세그먼트에 대한 부가 정보를 나타냄</li>
<li><code>플래그 비트</code> 라고도 부름</li>
</ul>
<h5 id="윈도우window">윈도우(window)</h5>
<ul>
<li>수신 윈도우의 크기가 명시됨</li>
<li><code>수신 윈도우</code> 란 한 번에 수신하고자 하는 데이터의 양을 나타냄</li>
</ul>
<h4 id="제어-비트">제어 비트</h4>
<p>제어 비트는 기본적으로 8비트로 구성된다. TCP의 기본 동작을 논할 때 가장 자주 언급되는 세 개의 제어비트는 아래와 같다.</p>
<ul>
<li>ACK : 세그먼트의 승인을 나타내기 위한 비트</li>
<li>SYN : 연결을 수립하기 위한 비트</li>
<li>FIN : 연결을 종료하기 위한 비트</li>
</ul>
<p>해당 비트가 1로 설정되어 있다는걸 편의상 <code>SYN 세그먼트</code> 와 같이 지칭하겠다</p>
<h4 id="순서-번호와-확인-응답-번호">순서 번호와 확인 응답 번호</h4>
<p>TCP의 신뢰성을 보장하기 위한 필드로, 한 쌍으로 묶어서 기억하는게 편하다.
<code>순서 번호</code> 가 세그먼트의 올바른 송수신 순서를 보장하기 위한 번호로, 세그먼트 데이터의 첫 바이트에 부여되는 번호라고 했는데 이것이 무슨 의미인지 알아보자.</p>
<p>만약, 내가 응용 계층을 전송해야 하는 데이터가 1900바이트 크기의 데이터고, 이는 MSS 단위로 전송될 수 있다. MSS가 500 바이트라고 가정했을 때, 다음 그림처럼 네 개의 세그먼트로 쪼갤 수 있을 것이다. </p>
<p align="center"><img src="https://velog.velcdn.com/images/ldh-dodo/post/5c2c89c0-48de-4909-a296-e5f52ece6283/image.png" width="600px"/></p>

<p>위 사진과 같이, SYN 플래그가 1로 설정된 첫 번째 세그먼트의 경우, <code>무작위의 순서 번호</code> 가 지정이 되는데, 이를 <code>초기 순서 번호(ISN; Initial Sequence Number)</code> 라고 한다.
그 이후의 순서 번호는 <code>초기 순서 번호 + 송신한 바이트 수 (떨어진 바이트 수)</code> 가 된다.</p>
<p><code>SYN</code> 플래그가 1이라는 것은 해당 패킷이 <code>연결 수립용</code> 이라는 것을 의미한다.</p>
<p>예를 들어, 세그먼트 B는 500바이트 이므로 순서번호가 <code>100 + 500 = 600</code> 이 되고,
세그먼트 C는 <code>600 + 500 = 1100</code> 이 된다.</p>
<p><code>확인 응답 번호</code> 는 순서 번호에 대한 응답이다.
<code>다음으로 제가 받을 순서 번호는 이것입니다</code> 를 의미하고,<code>수신한 순서 번호 + 1(ACK + 1)</code> 로 설정된다.</p>
<p>다음 그림처럼 호스트 순서 번호가 8000인 세그먼트를 잘 수신한 뒤, 8001 번 세그먼트를 받기를 원한다면, 확인 응답 번호로 8001를 명시한 세그먼트를 전송한다.</p>
<p align="center"><img src="https://velog.velcdn.com/images/ldh-dodo/post/724ddefc-494c-4553-856e-59264d98d9f8/image.png" width="600px"/></p>

<h3 id="tcp-연결-수립과-종료">TCP 연결 수립과 종료</h3>
<h4 id="tcp-연결-수립--쓰리-웨이-핸드셰이크three---way-handshake">TCP 연결 수립 : 쓰리 웨이 핸드셰이크(three - way handshake)</h4>
<p><code>쓰리 웨이 핸드 셰이크</code> 란 세 단계로 이루어진 TCP의 연결 수립 과정을 의미한다.</p>
<p>아래 사진처럼 세 단계를 거친 후, 본격적인 송수신이 시작된다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/db0a5115-44c7-443d-84ee-20cb5f509895/image.png" width="600px"/>
</p>

<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/e7852b96-34df-476e-ba61-205ad58826ed/image.png" width="600px"/>
</p>

<p>사진에 보이는 <code>액티브 오픈</code> 이란, 처음 연결을 시작하는 호스트의 연결 수립 과정을 말하며 여기서는 <code>클라이언트가 TCP 연결을 시작하는 과정</code> 이다.
<code>패시브 오픈</code> 이란 <code>서버가 연결 요청을 기다리는 과정</code> 이다.</p>
<p>서로의 초기 시퀀스 번호를 알아야 이후 데이터 전송 시 순서와 무결성을 보장할 수 있기 때문에, 서로를 소개하는 과정이라고 생각하면 되겠다.</p>
<h4 id="tcp-연결-종료--포-웨이-핸드셰이크four---way-handshake">TCP 연결 종료 : 포 웨이 핸드셰이크(four - way handshake)</h4>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/85277590-ee4f-449c-a674-cdab5c463fa3/image.png" width="600px"/>
</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/57c2e7bf-fc4e-45c9-9fb9-6d0960127391/image.png" width="600px"/>
</p>

<p>사진처럼 송수신 호스트가 각자 한 번씩 FIN과 ACK를 주고받으며 이루어진다.</p>
<p><code>액티브 클로즈</code> 는 먼저 연결을 종료하려는 호스트에 의해 수행되는 과정 (주로 클라이언트) 이고,
<code>패시브 클로즈</code> 는 연결 종료 요청을 받아들이는 호스트에 의해 수행 (주로 서버) 되는 과정이다.</p>
<h3 id="tcp-상태">TCP 상태</h3>
<p>TCP는 연결형 통신과 신뢰할 수 있는 통신을 위해 다양한 <code>상태</code> 를 유지한다.
<code>상태</code> 란 현재 어떤 통신 과정에 있는지를 나타내는 정보이다. 
이러한 이유 때문에<code>스테이트풀(stateful)</code> 프로토콜의 일종이라고도 한다.</p>
<p>주요 상태는 아래와 같다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/9818c38b-d3e9-4ac2-a18f-de4bafc8bca6/image.png" width="600px"/>
</p>

<p>1번 : 연결이 수립되지 않은 상태
2번 : 연결 수립 과정에서 주로 볼 수 있는 상태
3번 : 연결 종료 과정에서 주로 볼 수 있는 상태</p>
<p>로 생각하면 쉽다.</p>
<h4 id="연결이-수립되지-않은-상태">연결이 수립되지 않은 상태</h4>
<ul>
<li><code>CLOSED</code> : <code>아무런 연결이 없는 상태</code></li>
<li><code>LISTEN</code> : <code>일종의 연결 대기 상태</code>. 
패시브 오픈 호스트가 LISTEN 상태를 유지하고, 액티브 오픈 호스트가 SYN 세그먼트를 보내면 쓰리 웨이 핸드 셰이크가 시작됨.</li>
</ul>
<h4 id="연결-수립-상태">연결 수립 상태</h4>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/13798e27-5485-4943-a9be-3ed9fb2299db/image.png" width="600px"/>
</p>

<ul>
<li><code>SYN-SENT</code> : 액티브 오픈 호스트가 SYN 세그먼트를 보낸 뒤, <code>SYN + ACK 세그먼트를 기다리는 상태</code>
즉, 연결 요청을 보낸 뒤, 대기하는 상태</li>
<li><code>SYN-RECEIVED</code> : 패시브 오픈 호스트가 <code>SYN + ACK 세그먼트를 보낸 뒤 그에 대한 ACK 세그먼트를 기다리는 상태</code></li>
<li><code>ESTABLISHED</code> : <code>연결이 확립되었음을 나타내는 상태</code> -&gt; 데이터 송수신 가능 상태</li>
</ul>
<h3 id="연결-종료-상태">연결 종료 상태</h3>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/3cdff5ed-5040-4a81-95e5-b0c2baf71efc/image.png" width="600px"/>
</p>

<ul>
<li><code>FIN-WAIT-1</code> : 액티브 클로즈 호스트가 FIN 세그먼트를 보낸 직후의 상태</li>
<li><code>CLOSED-WAIT</code> : FIN 세그먼트를 받은 패시브 클로즈 호스트가 응답으로 ACK 세그먼트를 보낸 후 대기하는 상태</li>
<li><code>FIN-WAIT-2</code> : FIN-WAIT-1 상태에서 ACK 세그먼트를 받은 뒤, 상대 호스트의 FIN 세그먼트를 기다리는 상태</li>
<li><code>LAST-ACK</code> : CLOSE-WAIT 상태에서 FIN 세그먼트를 전송한 뒤 이에 대한 ACK 세그먼트를 기다리는 상태</li>
<li><code>TIME-WAIT</code> : 액티브 클로즈 호스트가 FIN 세그먼트를 수신한 뒤, 이에 대한 ACK 세그먼트를 전송한 뒤의 상태.
패시브 클로즈 호스트는 ACK 세그먼트를 수신하면 CLOSED 상태로 전이하지만,
TIME-WAIT 상태에 접어든 액티브 클로즈 호스트는 일정 시간을 기다린 뒤 CLOSED 상태로 전이함.</li>
<li><blockquote>
<p>세그먼트가 올바르게 전송되지 않았을 때 일정 시간 대기하지 않고 연결을 종료할 경우, 상대 호스트는 마지막 ACK 세그먼트를 전송받을 수 없기 때문에 대기 시간이 필요.</p>
</blockquote>
</li>
<li><code>CLOSING</code> : 동시에 연결을 종료하려 할 때 전이되는 상태. 
서로가 FIN 세그먼트를 보내고 받은 뒤 그에 대한 ACK 세그먼트를 보냈지만 자신의 FIN 세그먼트에 대한 ACK 세그먼트를 받지 못했을 때 접어드는 상태. (아래 사진 참고)</li>
</ul>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/f4b79745-680b-45d5-b48f-15efd005dd58/image.png" width="600px"/>
</p>

<h3 id="udp-데이터그램-구조">UDP 데이터그램 구조</h3>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/3bc9529b-2d43-45c9-8546-d65e8e8c8244/image.png" width="600px"/>
</p>

<p><code>UDP</code> 는 TCP와 달리 비연결형 통신을 수행하는 신뢰할 수 없는 프로토콜이다.
따라서 <code>연결 수립 및 해제</code> <code>재전송을 통한 오류제어</code> <code>혼잡 제어</code> <code>흐름 제어</code> 를 수행하지 않는다.
TCP처럼 상태를 유지하지도 않기 때문에 <code>스테이트리스(stateless)</code> 프로토콜의 일종이라고도 한다.
적은 오버헤드로 패킷을 빠르게 처리 가능하기 때문에, 실시간성이 강조되는 상황에서 TCP 보다 많이 쓰인다.</p>
<h4 id="송신지-포트와-수신지-포트-1">송신지 포트와 수신지 포트</h4>
<ul>
<li>송수신지의 포트 번호</li>
</ul>
<h4 id="길이">길이</h4>
<ul>
<li>헤더를 포함한 UDP 데이터그램의 바이트</li>
</ul>
<h4 id="체크섬">체크섬</h4>
<ul>
<li>데이터 전송 과정에서 오류가 발생했는지 검사하기 위한 필드</li>
<li>데이터그램이 훼손되었는지를 나타내는 정보</li>
<li><code>수신지까지 잘 도착했는지</code> 를 나타내는 신뢰성/비신뢰성과 관련이 없음</li>
</ul>
<h3 id="udp-송수신-과정">UDP 송수신 과정</h3>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/18818dae-44b5-42b7-8200-ab5a14052c9c/image.png" width="600px"/>
</p>

<p>TCP가 하나씩 확실히 전달한다면, UDP는 사진처럼 <code>빠르게 마구 던지는</code> 것과 같다.
그 과정에서 패킷이 손실되거나 순서가 바뀔 수 있다.</p>
<h1 id="tcp의-오류-흐름-혼잡-제어">TCP의 오류, 흐름, 혼잡 제어</h1>
<p><code>오류 제어</code>, <code>흐름 제어</code>, <code>혼잡 제어</code> 는 모두 TCP의 신뢰성을 보장하기 위한 기능이다.
TCP는 재전송을 기반으로 다양한 <code>오류</code>를 <code>제어</code>하고, <code>흐름 제어</code>를 통해 처리할 수 있을 만큼의 데이터만을 주고받으며, <code>혼잡 제어</code>를 통해 네트워크가 혼잡한 정도에 따라 전송량을 조절한다.</p>
<h2 id="오류-제어error-control">오류 제어(error control)</h2>
<p>오류를 제어하기 위해 TCP는 잘못된 세그먼트를 재전송하는 방법을 사용한다.
그렇다면, 송신 호스트가 세그먼트 전송 과정에 문제가 있음을 인지해야 할텐데, <code>체크섬</code> 은 세그먼트의 훼손 여부만 나타낼 뿐이다. 
이 체크섬 값이 잘못되었다면 수신 호스트는 해당 패킷을 폐기할 뿐, 송신 호스트가 이를 알아차릴 수 없다.</p>
<p>따라서, TCP가 신뢰성을 보장하려면
<code>1. 송신 호스트가 송신한 세그먼트에 문제가 있음</code> 을 인지해야 하고, 
<code>2. 오류를 감지하면, 해당 세그먼트를 재전송</code> 할 수 있어야 한다.</p>
<p>그렇다면, TCP가 어떤 상황에서 송신한 세그먼트에 문제가 있음을 감지하는가를 알아야 한다.
이 상황에는 크게 두 가지가 있다.
하나는 중복된 ACK 세그먼트를 수신했을 때이고, 하나는 타임아웃이 발생했을 때이다.</p>
<h3 id="재전송-기법">재전송 기법</h3>
<h4 id="1-중복된-ack-세그먼트를-수신했을-때">1. 중복된 ACK 세그먼트를 수신했을 때</h4>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/4f4f2872-ce8d-4139-996b-edec73194939/image.png" width="600px"/>
</p>

<p>수신 호스트 측이 받은 세그먼트의 순서 번호 중에서 일부가 누락되었다면, 특정 세그먼트를 보내달라는 요청을 반복해서 전송할 것이다. 이 상황이 이에 해당한다.
이 때 빠른 재전송이 발생한다.</p>
<h5 id="빠른-재전송fast-retransmit">빠른 재전송(fast retransmit)</h5>
<p><code>빠른 재전송</code> 은 재전송 타이머가 만료되기 전이라도 세 번의 중복 ACK 세그먼트가 수신되었다면 해당 세그먼트를 곧바로 재전송하는 기능이다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/50fb9175-962d-4218-9e50-e12aea5f0369/image.png" width="600px"/>
</p>

<p>세그먼트의 일부가 유실되어, 수신 호스트가 동일한 ACK 세그먼트를 전송해 송신 호스트가 세 번의 동일한 ACK 세그먼트를 수신하면, 곧바로 재전송을 하기에 타이머가 끝날 때까지 기다리는 시간을 줄일 수 있다.</p>
<h4 id="2-타임아웃이-발생했을-때">2. 타임아웃이 발생했을 때</h4>
<p>TCP는 타임아웃이 발생하면 문제가 생겼음을 인지한다.
TCP 세그먼트를 송신하는 호스트는 모두 <code>재전송 타이머</code> 라는 값을 유지한다.
호스트가 세그먼트를 전송할 때마다 재전송 타이머를 시작하게 되는데, 이 타이머의 카운트다운이 끝난 상황을 <code>타임 아웃</code> 이라고 한다.
타임아웃이 발생할 까지 ACK 세그먼트를 받지 못하면, <code>세그먼트가 상대 호스트에게 정상적으로 도착하지 않았다고 간주</code> 하여 세그먼트를 재전송한다.</p>
<h3 id="arq--재전송-기법">ARQ : 재전송 기법</h3>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/dfa1e3e6-68ab-4941-906c-1a16777dc165/image.png" width="600px"/>
</p>

<p>수신 호스트의 답변(ACK)과 타임아웃 발생을 토대로 문제를 진단하고, 문제가 생긴 메시지를 재전송함으로써 신뢰성을 확보하는 방식을 <code>ARQ (Automatic Repeat Request)</code>라고 한다.
ARQ의 종류는 다양한데, 가장 대표적인 세 가지 방식인 <code>Stop-and-Wait ARQ</code>와 <code>GO-Back-N ARQ</code> 그리고 <code>Selective Repeat ARQ</code> 에 대해 알아보겠다.</p>
<p>참고로, 전송 계층의 TCP는 ARQ를 사용하는 대표적인 프로토콜이지만, ARQ 자체는 전송 계층만의 기술은 아니다.</p>
<h4 id="stop-and-wait-arq">Stop-and-Wait ARQ</h4>
<p><code>Stop-and-Wait ARQ</code> 는 제대로 전달했음을 확인하기 전까지는 새로운 메시지를 보내지 않는 방식이다.
단순하지만 높은 신뢰성을 자랑한다.
하지만, 송신 호스트 입장에서 확인 응답을 받기 전까지는 다음 전송을 할 수 있어도 하지 못하기 때문에 <code>네트워크 이용 효율이 낮다</code> 는 단점이 있다. 이는 결국 성능의 저하로 이어진다.
따라서 오늘날의 인터넷 환경의 TCP에서는 특별한 경우가 아닌 이상 이 방식을 사용하지 않는다.</p>
<p>위 방식을 해결하려면, 아래 그림처럼 각 세그먼트에 대한 ACK 세그먼트가 도착하기 전이더라도 여러 세그먼트를 보낼 수 있어야 한다.
이렇게 연속해서 메시지를 전송할 수 있는 기술을 <code>파이프라이닝(pipelining)</code> 이라고 한다.
오늘날의 TCP는 파이프라이닝 기술이 사용되는 나머지 두 방식을 기반으로 동작한다.</p>
<h4 id="go-back-n-arq">Go-Back-N ARQ</h4>
<p><code>Go-Back-N ARQ</code> 는 여러 세그먼트를 전송하고, 도중에 잘못 전송된 세그먼트가 발생할 경우 해당 세그먼트부터 전부 다시 전송하는 방식이다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/8be6f92a-2959-4282-b299-57f3c5236955/image.png" width="600px"/>
</p>

<p>위 사진처럼, n+2번 세그먼트를 전송했지만 손실이 났을 경우, 이에 대한 ACK 세그먼트를 받지 못지 못했기에 타임아웃이 발생한다.
이 때, 송신 호스트는 잘못된 송신이 있음을 인지하고, ACK 세그먼트를 수신받지 못한 n+2 번 세그먼트부터 다시 전송하게 된다.</p>
<p>여기서 순서 번호 n번에 대한 ACK 세그먼트는 &#39;n번까지의&#39; 확인 응답이라고 할 수 있으므로, Go-Back-N ARQ의 ACK 세그먼트를 <code>누적 확인 응답</code> 이라고 한다.</p>
<h4 id="selective-repeat-arq">Selective Repeat ARQ</h4>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/959555c5-aed4-46f1-b5d0-3fcf56b94d5b/image.png" width="600px"/>
</p> 

<p><code>Selective Repeat ARQ</code>는 이름 그대로 선택적으로 재전송하는 방법이다.
<code>Go-Back-N ARQ</code> 송신 과정은 모든 세그먼트를 다시 재전송해야한다는 단점이 있지만, 이 방식은 수신 호스트 측에서 제대로 전송받은 각각의 패킷들에 대해 ACK 세그먼트를 전송하는 방식이다.
이 때문에 해당 방식의 ACK 세그먼트를 <code>개별 확인 응답</code> 이라고 부른다.</p>
<h2 id="흐름-제어flow-control">흐름 제어(flow control)</h2>
<p>호스특 한 번에 받아서 처리할 수 있는 세그먼트의 양에는 한계가 있기 때문에, 파이프라이닝 기반의 ARQ 재전송 기법이 동작하려면 반드시 흐름 제어를 고려해야 한다.</p>
<p>만약 수신 호스트가 한 번에 n개의 바이트를 받아서 처리할 수 있다면, 송신 호스트는 이 점을 인지하여 n개 바이트를 넘지 않는 선에서 송신해야한다. 
더 많은 양을 전송할 경우 일부 세그먼트가 처리되지 못할 수도 있기 때문이다.</p>
<p>TCP의 흐름제어란 이런 문제 상황을 방지하고자 송신 호스트가 수신 호스트의 처리 속도를 고려하며 송수신 속도를 균일하게 유지하는 것을 의미한다.</p>
<p>참고로, <code>Stop-and-Wait ARQ</code>를 사용하면 확인 응답이 오기 전까지는 추가적인 세그먼트를 전송하지 않기에 별도의 흐름 제어가 필요하지 않다.</p>
<p>파이프라이닝이 연속해서 세그먼트를 전송할 수 있는 기술이긴 하지만, 무작정 무한한 데이터를 연속해서 보낼 수는 없기에 <code>슬라이딩 윈도우</code> 라는 방식이 필요하다.</p>
<h3 id="슬라이딩-윈도우sliding-window">슬라이딩 윈도우(sliding window)</h3>
<p>슬라이딩 윈도우를 이해하기 전, 먼저 <code>윈도우(window)</code> 에 대한 이해가 필요하다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/e162eaff-980f-41f7-9b48-c98ce4305c52/image.png" width="600px"/>
</p>

<p><code>윈도우</code> 란 송신 호스트가 파이프라이닝할 수 있는 최대량을 의미한다.
윈도우의 크기만큼 확인 응답을 받지 않고도 한 번에 전송가능하다는 의미이다.</p>
<p>윈도우의 크기가 크면 한 번에 전송할 수 있는 데이터가 많을 것이고,
윈도우의 크기가 작으면 한 번에 전송할 수 있는 데이터가 적을 것이다.
윈도우 크기에서 벗어난 숫자에 해당하는 세그먼트는 전송할 수 없다.</p>
<p>첫 번째, 두 번째, 세 번째, 네 번째 세그먼트를 전송했고, 수신 호스트로부터 첫 번째 세그먼트에 대한 ACK를 받았다고 가정해보자. 그렇다면 윈도우는 아래처럼 오른쪽으로 한 칸 이동하게 된다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/c985c406-1014-451d-ac48-3ffd1a61db50/image.png" width="600px"/>
</p>


<p>윈도우가 점차 오른쪽으로 미끄러지듯 움직이는데, 이러한 흐름 제어를 <code>슬라이딩 윈도우</code> 라고 부른다.</p>
<p>송신 호스트만 윈도우를 고려하는 것은 아니고, 수신 호스트도 윈도우를 고려한다.
사실, 송신측 윈도우(송신 윈도우) 는 수신 호스트가 알려주는 수신 측 윈도우(수신 윈도우)를 토대로 알 수 있는 정보이다.</p>
<p>TCP 세그먼트 구조에서, 윈도우 필드에 명시되는 값이 바로 이 <code>수신 윈도우</code> 의 크기다.</p>
<h2 id="혼잡-제어congestion-control">혼잡 제어(congestion control)</h2>
<p><code>혼잡</code> 이란 많은 트래픽으로 인해 패킷의 처리 속도가 늦어지거나 유실될 우려가 있는 네트워크 상황을 의미한다.
<code>혼잡 제어</code> 란 이와 같은 혼잡을 제어하기 위한 기능이다.
혼잡 제어를 수행하는 송신 호스트는 네트워크 혼잡도를 판단하고 혼잡한 정도에 맞춰 유동적으로 전송량을 조절하며 전송한다.</p>
<p><code>혼잡 윈도우</code> 는 혼잡 없이 전송할 수 있을 법한 데이터 양을 의미하는데, 혼잡 윈도우가 작다면 네트워크가 혼잡한 상황이기에 한 번에 전송할 수 있는 세그먼트 수가 작음을 의미한다.</p>
<p>이 <code>혼잡 윈도우</code> 크기가 어느 정도가 적당한지는 혼잡 제어 알고리즘을 통해 결정할 수 있다.</p>
<h3 id="혼잡-제어-알고리즘">혼잡 제어 알고리즘</h3>
<h4 id="rttround-trip-time">RTT(Round Trip Time)</h4>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/979ca3be-d686-4876-8029-41d16a74ecac/image.png" width="600px"/>
</p>

<ul>
<li>메시지를 전송한 뒤 그에 대한 답변을 받는데까지 걸리는 시간</li>
</ul>
<h4 id="aimdaddtive-increasemultiplicative-decrease">AIMD(Addtive Increase/Multiplicative Decrease)</h4>
<p>의미를 직역하면 <code>합으로 증가, 곱으로 감소</code> 라는 의미이다.
혼잡이 감지되지 않는다면 혼잡 윈도우를 <code>RTT</code>마다 1씩 선형적으로 증가시키고,
혼잡이 감지되면 혼잡 윈도우를 절반으로 떨어뜨리는 동작을 반복하는 알고리즘이다.</p>
<p>AIMD 알고리즘은 혼잡을 제어할 수 있는 가장 기본적인 아이디어이지만, 이것만으로 혼잡 제어가 가능하지는 않다. 이를 조금 더 정교하게 만들 혼잡 제어 알고리즘은 다음과 같다.</p>
<p><strong>1. 느린 시작(slow start)</strong>
<strong>2. 혼잡 회피(congestion avoidance)</strong>
<strong>3. 빠른 회복(fast recovery)</strong></p>
<h4 id="느린-시작slow-start-알고리즘">느린 시작(slow start) 알고리즘</h4>
<p>혼잡 윈도우를 1부터 시작해 수신된 ACK 세그먼트 하나당 1씩 증가시키는 방버이다.
결과적으로 다음 그림처럼 2배씩 지수적으로 증가하게 된다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/e01781ac-7120-441a-b624-f8b20c43208d/image.png" width="600px"/>
</p>

<p>AIMD 방식은 처음 연결이 수립된 뒤, 혼잡 윈도우 크기가 증가되는 속도가 느리기 때문에, 이 알고리즘을 활용하면 초기 전송 속도를 어느 정도 빠르게 확보할 수 있다는 장점이 있다.</p>
<p>하지만, 혼잡 윈도우를 언제까지나 지수적으로 증가시킬 수는 없다. 그렇게되면 언젠가 혼잡 상황을 마주할 확률이 높아지기 때문이다.
그렇다면 언제까지 증가해야 할까?</p>
<p>느린 시작 알고리즘을 사용할 때 함께 사용하는 값으로 <code>느린 시작 임계치(slow start threshold)</code> 라는 값이 정해져 있다.
<code>혼잡 윈도우 값이 계속 증가하다가 느린 임계치 이상</code> 이 되거나 <code>타임 아웃이 발생</code> 하거나, <code>세 번의 중복된 ACK 세그먼트가 발생</code> 하여 혼잡이 감지되면 다음 세 가지 방법 중 하나를 선택하게 된다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/f3dd3db5-4855-48f3-b5b2-b0f777576642/image.png" width="600px"/>
</p>

<h4 id="혼잡-회피congestion-avoidance-알고리즘">혼잡 회피(congestion avoidance) 알고리즘</h4>
<p><code>혼잡 회피</code> 알고리즘은 RTT 마다 혼잡 윈도우를 1MSS(Maximum Segment Size)씩 증가시키는 알고리즘이다.
혼잡 윈도우를 지수적으로 증가시키는 느린 시작과는 달리, 혼잡 윈도우 크기를 선형적으로 증가시키는 것을 볼 수 있다.</p>
<p>느린 시작 임계치를 넘어선 시점부터는 혼잡이 발생할 우려가 있으니, 조심해서 혼잡 윈도우를 증가시키는 방식이다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/9e5da501-db59-4ee0-aa6f-016e55951f28/image.png" width="600px"/>
</p>

<p>혼잡 회피 도중에도 혼잡이 발생하면 위 세 가지 상황을 나타낸 표의 절차대로 수행한다.</p>
<h4 id="빠른-회복fast-recovery-알고리즘">빠른 회복(fast recovery) 알고리즘</h4>
<p>세 번의 중복된 ACK 세그먼트를 수신하면 빠른 재전송과 더불어 빠른 회복 알고리즘이 수행된다.
<code>빠른 회복</code> 알고리즘은 세 번의 중복 ACK 세그먼트를 수신했을 때, 느린 시작은 건너뛰고 혼잡 회피를 수행하는 알고리즘으로, 빠르게 전송률을 회복하기 위한 알고리즘이다.</p>
<p>다만, 빠른 회복 도중이라도 타임아웃이 발생하면 혼잡 윈도우 크기는 1로, 느린 시작 임계치는 혼잡이 감지된 시점의 절반으로 떨어뜨린 후 다시 느린 시작을 수행한다.</p>
<p>지금까지 학습한 혼잡 제어 알고리즘을 그림으로 정리하면 다음과 같다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/26b588a2-d2b7-4826-a66f-cde04000f753/image.png" width="600px"/>
</p>


<blockquote>
<p><strong>TCP에 대해 설명해주세요.</strong>
TCP는 신뢰성이 보장되는 연결형 프로토콜입니다.
3-way handshake로 연결을 설정하고, 4-way handshake로 해제합니다.
시퀀스 넘버와 ACK 넘버를 활용해 패킷이 순서대로 도착하도록 보장하며, 오류 제어와 흐름 제어를 통해 패킷 손실 시 재전송하여 신뢰성을 유지합니다.
또한, 혼잡 제어를 통해 네트워크 상태에 따라 전송 속도를 조절하여 효율적인 데이터 전송을 보장합니다.</p>
</blockquote>
<blockquote>
<p><strong>3 way handshake에 대해 설명해주세요.</strong>
3 way handshake는 세 단계로 이루어진 TCP의 연결 수립 과정을 의미합니다.
먼저, 클라이언트가 서버에게 연결 요청을 보내면서 SYN(Synchronizaion) 패킷을 전송합니다.
그러면 서버는 이 요청을 받고, 응답으로 SYN 패킷과 ACK(Acknowledgement) 패킷을 전송합니다.
마지막으로, 클라이언트가 이를 확인했다는 의미로 ACK 패킷을 보내면 연결이 완료됩니다.</p>
</blockquote>
<blockquote>
<p><strong>4 way handshake에 대해 설명해주세요.</strong>
4 way handshake는 네 단계로 이루어진 TCP의 연결 해제 과정을 의미합니다.
먼저, 클라이언트가 서버에게 연결을 해제하겠다는 의미의 FIN(Finish) 패킷을 전송합니다.
그러면 서버는 이 요청을 받고, 응답으로 ACK 패킷을 전송합니다.
그리고 서버도 연결을 종료하기 위해 클라이언트에게 FIN 패킷을 전송합니다.
마지막으로, 클라이언트가 이를 확인했다는 의미로 ACK 패킷을 보내면 연결이 완전히 종료됩니다.</p>
</blockquote>
<blockquote>
<p><strong>TCP 빠른 재전송에 대해서 설명해주세요.</strong>
빠른 재전송이란 패킷 손실을 감지하고 재전송 타이머 만료를 기다리지 않고 즉시 재전송하는 TCP 메커니즘입니다.
만약, 세 번의 중복 ACK 세그먼트가 수신되었다면 패킷이 손실되었다고 간주하고 해당 세그먼트를 곧바로 재전송합니다.</p>
</blockquote>
<blockquote>
<p><strong>Flow control에 대해 설명해주세요.</strong>
흐름 제어(Flow Control)는 송신자가 수신자의 처리 속도를 초과하지 않도록 데이터 전송을 조절하는 메커니즘입니다.
TCP에서 대표적인 흐름 제어 방식으로는 슬라이딩 윈도우(Sliding Window) 가 있으며, 수신자는 자신의 버퍼 상태에 따라 윈도우 크기를 송신자에게 알려줍니다. 
송신자는 이 정보를 기반으로 데이터 전송 속도를 조절하여 수신 버퍼 오버플로우를 방지하고, 안정적인 데이터 흐름을 유지합니다.</p>
</blockquote>
<blockquote>
<p><strong>Congestion control에 대해 설명해주세요.</strong>
혼잡 제어(Congestion Control)는 네트워크에서 트래픽이 과도하게 증가하여 패킷 손실이나 지연이 발생하는 것을 방지하는 메커니즘입니다.
TCP에서는 AIMD(Additive Increase, Multiplicative Decrease), 느린 시작, 혼잡 회피, 빠른 회복 알고리즘 등의 기법을 활용하여 네트워크 상태에 따라 전송 속도를 동적으로 조절합니다. 
이를 통해 네트워크 자원을 효율적으로 사용하고, 패킷 손실을 최소화하며 안정적인 데이터 전송을 보장합니다.</p>
</blockquote>
<blockquote>
<p><strong>전송후 대기 프로토콜이 뭘까요?</strong>
전송후 대기(Stop-and-Wait) 프로토콜은 송신자가 하나의 패킷을 전송한 후, 수신자로부터 응답(ACK)을 받을 때까지 대기하는 방식의 흐름 제어 프로토콜입니다.
단순하고 높은 신뢰성을 보장하지만, 응답이 오기 전까지는 전송이 중단되므로 네트워크 이용 효율이 낮다는 단점이 있습니다.</p>
</blockquote>
<blockquote>
<p><strong>파이프라이닝 프로토콜이 뭘까요?</strong>
파이프라이닝(Pipelining) 프로토콜은 여러 개의 패킷을 연속으로 전송하여 전송 효율을 높이는 프로토콜입니다. 
이는 전송후 대기(Stop-and-Wait) 프로토콜의 비효율성을 보완하기 위해 사용되며, 송신자는 수신자의 응답(ACK)을 기다리지 않고 일정한 윈도우 크기만큼 연속적으로 패킷을 전송할 수 있습니다.
대표적인 파이프라이닝 프로토콜로는 오류가 발생하면 해당 오류 패킷 이후의 모든 패킷을 다시 전송하는 Go-Back-N 과 오류가 발생한 패킷만 재전송하는 Selective Repeat이 있습니다.</p>
</blockquote>
<blockquote>
<p><strong>UDP에 대해 설명해주세요.</strong>
UDP는 TCP와 달리 비연결형 통신을 수행하는 신뢰할 수 없는 프로토콜로, 데이터를 빠르게 전송하는 데 초점을 맞춘 프로토콜입니다.
실시간 스트리밍과 온라인 게임같은 데이터의 정확성보다 빠른 전송이 중요한 경우 사용됩니다.</p>
</blockquote>
<blockquote>
<p><strong>UDP의 장단점을 설명해 주세요.</strong>
UDP는 비연결형 프로토콜로, 빠른 전송이 가능하다는 장점이 있습니다.
또한, 헤더 크기가 작아 오버헤드가 적고, 연결 설정 과정 없이 바로 데이터를 전송할 수 있어 지연 시간이 짧다는 장점도 있습니다.
하지만, 흐름 제어 및 혼잡 제어가 없어서 패킷 손실 및 순서 오류가 발생하더라도 재전송하지 않기에, 신뢰할 수 없다는 단점이 있습니다.</p>
</blockquote>
<blockquote>
<p><strong>UDP 체크섬에 대해 설명해주세요.</strong>
UDP 체크섬은 데이터그램이 전송 중 오류가 발생했는지 검사하기 위한 필드입니다.
송신자가 데이터의 체크섬 값을 계산하여 UDP 헤더에 포함하면, 수신자는 이를 다시 계산하여 일치 여부를 확인합니다.
만약 값이 다르면 데이터가 손상된 것으로 판단하고 폐기합니다.
다만, 체크섬은 오류 감지만 할 뿐, 재전송을 수행하지 않기 때문에 UDP의 신뢰성과는 무관합니다.</p>
</blockquote>
<blockquote>
<p><strong>ICMP가 뭘까요?</strong>
ICMP는 IP 패킷의 전송 과정에 대한 피드백 메시지를 얻기 위해 사용하는 프로토콜이다.
IP 프로토콜과 함께 동작하며, 데이터 전송 중 발생하는 문제를 감지하고 알리는 역할을 합니다.
대표적인 ICMP 메시지로는 ping 요청 및 응답(Echo Request/Reply), TTL 초과(Time Exceeded) 메시지가 있습니다.
ping 명령어를 통해 ICMP 패킷을 송신하여 네트워크를 진단할 수 있습니다.
네트워크 연결 상태를 확인하는 데 활용되지만, 신뢰성 있는 전송을 보장하지는 않음에 유의해야 합니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[컴퓨터 네트워크 - 네트워크 계층]]></title>
            <link>https://velog.io/@ldh-dodo/%EC%BB%B4%ED%93%A8%ED%84%B0-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EA%B3%84%EC%B8%B5</link>
            <guid>https://velog.io/@ldh-dodo/%EC%BB%B4%ED%93%A8%ED%84%B0-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EA%B3%84%EC%B8%B5</guid>
            <pubDate>Fri, 14 Mar 2025 05:36:52 GMT</pubDate>
            <description><![CDATA[<h1 id="데이터-링크-계층의-한계">데이터 링크 계층의 한계</h1>
<p>이전에 배운 물리 계층과 데이터 링크 계층만으로는 LAN을 넘어서 통신하는데 어려움이 있다.</p>
<p>그 이유로는 <strong>첫째</strong>, 서로 다른 LAN에 속한 호스트가 통신할 때, 수 많은 네트워크 장비를 거치며 다양한 경로를 통해 이동해야 하기 때문이다. 
패킷이 이동할 최적의 경로를 결정하는 것을 <code>라우팅</code> 이라고 하며, 물리 계층과 데이터 링크 계층의 장비로는 라우팅을 수행할 수 없지만, 네트워크 계층의 장비로는 가능하다.</p>
<p><strong>둘째</strong>, MAC 주소만으로는 모든 네트워크에 속한 호스트의 위치를 특정하기 어렵기 때문이다.
모든 호스트가 모든 네트워크에 속한 모든 호스트의 MAC 주소를 서로 알고 있기는 사실상 어렵다. 
택배를 예시로, MAC 주소가 수신인 역할을 한다면 수신지 역할을 하는 정보가 필요한데, 이 정보가 바로 네트워크 계층의 <code>IP</code> 이다.
IP 주소는 <code>논리 주소</code> 라고도 부르는데, MAC 주소가 일반적으로 NIC마다 할당되는 고정된 주소라면 IP 주소는 호스트에 직접 할당이 가능하기 때문이다. 
<code>DHCP(Dynamic Host Configuration Protocol)</code> 이라는 특정 프로토콜을 통해 자동으로 IP 주소를 자동으로 할당받거나 사용자가 직접 할당할 수도 있고, 한 호스트가 여러 IP 주소를 가질 수도 있다.</p>
<h1 id="인터넷-프로토콜">인터넷 프로토콜</h1>
<p>네트워크 계층의 가장 핵심적인 프로토콜 하나를 꼽자면 단연 <code>인터넷 프로토콜(IP; Internet Protocol)</code> 이다.
IP에는 <code>IPv4</code> 와 <code>IPv6</code> 버전 두 가지가 존재한다.</p>
<h2 id="ip의-주요-기능">IP의 주요 기능</h2>
<p>IP의 기능은 다양하지만 대표적으로 크게 두 가지가 있다.
<code>IP주소 지정</code> 과 <code>IP 단편화</code> 가 이에 해당한다.</p>
<h3 id="ip주소-지정">IP주소 지정</h3>
<ul>
<li>IP 주소를 바탕으로 송수신 대상을 지정하는 것을 의미<h3 id="ip-단편화">IP 단편화</h3>
</li>
<li>전송하고자 하는 패킷의 크기 &gt; <code>MTU</code>(최대 전송 단위; Maximum Transmission Unit) 일 경우, MTU 크기 이하의 복수의 패킷으로 나누는 것을 의미</li>
<li><code>MTU</code> : 한 번에 전송 가능한 IP 패킷의 최대 크기.
일반적으로 1500바이트이고, MTU 크기 이하로 나누어진 패킷은 수신지에 도착하면 다시 재조합됨</li>
</ul>
<h3 id="ipv4">IPv4</h3>
<h3 id="ip-주소의-형태">IP 주소의 형태</h3>
<ul>
<li>4바이트(32비트) 표현 가능</li>
<li>192.168.1.1 처럼 표현됨</li>
<li><code>옥텟</code> 으로 표현됨</li>
<li><code>옥텟</code> : 점으로 구분된 8비트(0 ~ 255 범위의 10진수)를 의미함</li>
<li><code>192</code> <code>168</code> <code>1</code> <code>1</code> 은 전부 옥텟</li>
</ul>
<h3 id="ipv4-패킷">IPv4 패킷</h3>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/f80cc77c-3218-4481-ac04-39aff7a21ef2/image.png" width="600px"/>
</p>

<ul>
<li>프레임의 페이로드로 데이터 필드에 명시됨</li>
</ul>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/bb83a5c1-2fcb-4a1a-8411-f3f74dce116d/image.png" width="600px"/>
</p>

<p>이 중에서, 특히 중요한 <code>식별자</code> <code>플래그</code> <code>단편화 오프셋</code> <code>TTL</code> <code>프로토콜</code> <code>송신지 IP 주소</code> <code>수신지 IP 주소</code> 에 대해 살펴보자.</p>
<h4 id="식별자">식별자</h4>
<ul>
<li>패킷에 할당된 번호</li>
<li>IPv4 패킷이 여러 조각으로 쪼개졌다면, 어떤 메시지에서부터 쪼개졌는지 인식하기 위해 사용</li>
</ul>
<h4 id="플래그">플래그</h4>
<ul>
<li>총 세 개의 비트로 구성된 필드</li>
<li><code>첫 번째 비트</code> : 항상 0으로 예약된 비트. 사용 X</li>
<li><code>두 번째 비트</code> : <code>DF(Don&#39;t Fragment)</code> 라는 이름을 가짐. 0이라면 IP단편화가 가능하고, 1이라면 IP 단편화를 수행하지 말라는 의미</li>
<li><code>세 번째 비트</code> : <code>MF(More Fragment)</code> 라는 이름을 가짐. 0이라면 이 패킷이 마지막 패킷임을 의미하고, 1이라면 쪼개진 패킷이 더 있음을 의미함.</li>
</ul>
<h4 id="단편화-오프셋">단편화 오프셋</h4>
<ul>
<li>단편화되기 전에 패킷의 초기 데이터에서 몇 번째로 떨어진 패킷인지를 나타냄</li>
<li>단편화되어 전송되는 패킷들이 수신지에 순서대로 도착하지 않을 수도 있기 때문에, 자신이 몇 번째 데이터에 속하는지 알아야하기 때문에 사용</li>
</ul>
<h4 id="ttltime-to-live">TTL(Time to Live)</h4>
<ul>
<li>패킷의 수명을 의미</li>
<li><code>홉</code> : 패킷이 호스트 또는 라우터에 한 번 전달되는 것</li>
<li>TTL은 홉 마다 1씩 감소하고, 0으로 떨어진 패킷은 폐기함</li>
<li>무의미한 패킷이 네트워크 상에 지속적으로 남아있는 것을 방지</li>
</ul>
<h4 id="프로토콜">프로토콜</h4>
<ul>
<li>상위 계층의 프로토콜이 무엇인지를 나타냄</li>
<li>예시로 TCP는 6번, UDP는 17번</li>
</ul>
<h4 id="송신지-ip-주소와-수신지-ip-주소">송신지 IP 주소와 수신지 IP 주소</h4>
<ul>
<li>송수신지의 IPv4 주소를 나타냄</li>
</ul>
<h3 id="ipv6">IPv6</h3>
<ul>
<li>16바이트(128비트)로 표현 가능</li>
<li><code>2001:0230:abcd:ffff:0000:0000:ffff:1111</code> 처럼 표현됨.</li>
<li>IPv4 주소는 약 43억개로, 주소의 총량이 쉽게 고갈될 수 있기 때문에 등장함</li>
</ul>
<h4 id="ipv6-패킷">IPv6 패킷</h4>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/edb21520-bfc4-4d4c-a602-d429bbdd0c1d/image.png" width="600px"/>
</p>

<ul>
<li>기본 헤더가 IPv4에 비해 간소화 된 형태</li>
</ul>
<p>이 중에서 <code>다음 헤더</code> <code>홉 제한</code> <code>송신지 IP 주소</code> <code>수신지 IP 주소</code>에 대해 살펴보자.</p>
<h4 id="다음-헤더next-header">다음 헤더(next header)</h4>
<ul>
<li><code>상위 계층의 프로토콜</code> 혹은 <code>확장 헤더</code>를 가리킴</li>
<li><code>확장 헤더</code> : 추가적인 헤더 정보가 필요할 경우에 추가 헤더를 가질 수 있는데, 이를 의미
기본 헤더와 페이로드 데이터 사이에 위치함<p align="center">
<img src="https://velog.velcdn.com/images/ldh-dodo/post/f6e80998-545f-402c-af5f-808c297db64f/image.png" width="600px"/>
</p>

</li>
</ul>
<h4 id="홉-제한hot-limit">홉 제한(hot limit)</h4>
<ul>
<li>IPv4의 TTL 필드와 비슷하게 <code>패킷의 수명</code>을 나타냄</li>
</ul>
<h4 id="송신지-ip주소와-수신지-ip-주소">송신지 IP주소와 수신지 IP 주소</h4>
<ul>
<li>IPv6 주소 지정을 가능하게 해줌</li>
</ul>
<h5 id="ipv6의-단편화">IPv6의 단편화</h5>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/553fe8bc-4caa-4ff2-a38c-52cbc862f378/image.png" width="600px"/>
</p>

<ul>
<li>IPv6는 단편화 관련 필드가 없어서, 확장 헤더에 포함된 <code>단편화 확장 헤더</code> 를 통해 단편화가 이루어짐(위 사진 참고)</li>
<li><code>단편화 오프셋</code> : IPv4의 단편화 오프셋과 같은 역할</li>
<li><code>M 플래그</code> : IPv4의 MF 플래그와 같은 역할</li>
<li><code>식별자</code> : IPv4의 식별자 필드와 같은 역할</li>
</ul>
<h3 id="arpaddress-resolution-protocol">ARP(Address Resolution Protocol)</h3>
<ul>
<li>IP 주소를 통해 MAC 주소를 알아내는 프로토콜</li>
<li>동일 네트워크 내에 있는 송수신 대상의 IP 주소를 통해 MAC 주소를 알아낼 수 있음</li>
</ul>
<h4 id="arp의-동작-과정">ARP의 동작 과정</h4>
<ol>
<li>ARP 요청</li>
<li>ARP 응답</li>
<li>ARP 테이블 갱신</li>
</ol>
<h5 id="1-arp-요청">1. ARP 요청</h5>
<p>A가 B에게 패킷을 보내고 싶다고 가정하자.
A는 네트워크 내의 모든 호스트에게 브로드캐스트 메시지를 보낸다.
이 메시지는 <code>ARP 요청</code> 이라는 ARP 패킷이다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/906af28e-0c4d-498a-bf6a-3c49258e754e/image.png" width="600px"/>
</p>


<h5 id="2-arp-응답">2. ARP 응답</h5>
<p>네트워크 내의 모든 호스트가 ARP 요청 메시지를 수신하지만, B를 제외한 나머지 호스트는 자신의 IP 주소가 아니므로 이를 무시한다.
그리고 B는 자신의 MAC 주소를 담은 메시지를 A에게 전송한다.
이 유니캐스트 메시지는 <code>ARP 응답</code>이라는 ARP 패킷이다.
B의 MAC 주소가 포함된 메시지를 수신한 A는 B의 MAC 주소를 알게 된다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/1f86cff2-f98c-4af1-b5fa-d50977867815/image.png" width="600px"/>




<h5 id="3-arp-테이블-갱신">3. ARP 테이블 갱신</h5>
<p>ARP를 활용할 수 있는 모든 호스트는 <code>ARP 테이블</code> 이라는 정보를 유지하는데, 이는 IP 주소와 그에 맞는 MAC 주소 테이블을 대응하는 표이다.
ARP 테이블은 일정 시간이 지나면 삭제되고, 임의로 삭제할 수도 있다.
앞으로 A는 B와 통신할 때 굳이 브로드캐스트로 ARP 요청을 보낼 필요가 없어진다.</p>
<p align="center">
  <img src="https://velog.velcdn.com/images/ldh-dodo/post/1ab12a25-4086-4802-842d-6e3b56b8d5f1/image.png" width="600px"/>
</p>

<h4 id="ip-단편화를-피하는-방법">IP 단편화를 피하는 방법</h4>
<p>데이터가 여러 패킷으로 쪼개지면 전송해야할 패킷의 헤더가 늘어나 불필요한 트래픽 증가와 대역폭 낭비로 이어진다.
또한, 쪼개진 IP 패킷을 합치는 과정에서 발생하는 부하도 성능 저하를 야기함
이와 같은 이유로 IP 단편화는 되도록 하지 않는 것이 좋다.</p>
<p>IP 단편화를 피하기 위해서는 <code>IP 단편화 없이 주고 받을 수 있는 최대 크기</code> 만큼만 전송해야 한다. 이 크기를 <code>경로 MTU</code> 라고 한다.
경로 MTU를 구하고 해당 크기만큼만 송수신하여 IP 단편화를 회피하는 기술을 <code>경로 MTU 발견</code> 이라고 한다.</p>
<p>만약, 어떤 호스트로부터 처리 가능한 MTU 크기를 넘어선 IP 패킷을 전달 받았고, DF 플래그가 1로 설정(<code>단편화를 수행하지 말라</code>) 어 있다고 가정해보자.
이 때는 IP 패킷을 전달한 호스트에게 <code>DF 플래그가 설정되어 있는데, 이걸 단편화 없이 처리할 수 없습니다</code> 라는 특정 오류 메시지를 전달한다.</p>
<p>이걸 이용해 이 <code>오류 메시지를 받지 않을 때까지 전달하는 데이터 크기를 점차 줄여나가면서</code>, 서로의 경로 MTU를 알아갈 수 있다.</p>
<h2 id="ip-주소">IP 주소</h2>
<p>하나의 IP 주소는 <code>네트워크 주소</code> 와 <code>호스트 주소로</code> 이루어진다.
<code>네트워크 주소</code> 는 호스트가 속한 <code>특정 네트워크를 식별하는 역할</code> 을 하며
<code>호스트 주소</code> 는 네트워크 내에서 <code>특정 호스트를 식별하는 역할</code> 을 한다.</p>
<p>IP 주소에서 네트워크 주수와 호스트 주소를 구분하는 범위는 유동적일 수 있다.
만약, 호스트 주소 공간을 크게 할당했는데,  가정 내에서만 사용한다면 낭비되는 IP 주소가 많을 것이고,
호스트 주소 공간을 작게 할당했는데, 대규모 인원이 사용하는 회사에서 사용한다면 IP주소가 부족할 것이다.
이런 고민을 해결하기 위해 생겨난 개념이 바로 IP 주소의 <code>클래스</code> 이다.</p>
<h3 id="클래스풀-주소-체계classful-addressing">클래스풀 주소 체계(Classful addressing)</h3>
<p><code>클래스</code> 는 네트워크 크기에 따라 IP 주소를 분류하는 기준이다.
클래스를 이용하면 필요한 호스트 IP 개수에 따라 네트워크 크기를 가변적으로 조정해 네트워크 주소와 호스트 주소를 구획할 수 있게된다.
클래스를 기반으로 IP 주소를 관리하는 주소 체계를 클래스풀 주소 체계라고 한다.</p>
<p>클래스의 종류는 A, B, C, D, E 가 있고, D는 멀티캐스트를 위한 클래스, E는 특수한 목적을 위한 클래스로, 예약된 클래스이다.
따라서 네트워크 크기를 나누는데 실질적으로 사용되는 클래스는 A, B, C 이다.</p>
<p align="center">
 <img src="https://velog.velcdn.com/images/ldh-dodo/post/23827df9-feb5-4cbb-85a9-783893d236cf/image.png" width="600px"/>
</p>

<p>호스트 주소가 전부 0인 IP 주소는 해당 네트워크 주소를 의미하는 <code>네트워크 주소</code>, 
호스트 주소가 전부 1인 IP 주소는 <code>브로드캐스트를 위한 주소</code> 로, 실제 호스트에게 할당 가능한 주소는 이론상의 주소보다 2개가 적음을 인지하자.</p>
<h4 id="a-클래스">A 클래스</h4>
<p align="center">
 <img src="https://velog.velcdn.com/images/ldh-dodo/post/76e5f036-2654-4b13-bafb-6303e719e577/image.png" width="600px"/>
</p>

<ul>
<li>비트 &#39;0&#39; 으로 시작함</li>
<li>네트워크 주소는 1옥텟, 호스트 주소는 3옥텟으로 구성</li>
<li>이론상 $$2^7$$ 개의 네트워크, $$2²⁴$$ 개의 호스트 주소를 가짐</li>
<li>고정된 비트(&#39;0&#39;)를 제외하고 나머지 7자리가 네트워크 주소를 구성한다고 이해하면 됨</li>
</ul>
<h4 id="b-클래스">B 클래스</h4>
<p align="center">
 <img src="https://velog.velcdn.com/images/ldh-dodo/post/4493fdcc-9b34-42bb-8210-046f932aad72/image.png" width="600px"/>
</p>

<ul>
<li>비트 &#39;10&#39; 으로 시작함</li>
<li>네트워크 주소는 2옥텟, 호스트 주소는 2옥텟으로 구성</li>
<li>이론상 $$2¹⁴$$ 개의 네트워크, $$2¹⁶$$ 개의 호스트 주소를 가짐</li>
<li>고정된 비트(&#39;10&#39;)를 제외하고 나머지 14자리가 네트워크 주소를 구성한다고 이해하면 됨</li>
</ul>
<h4 id="c-클래스">C 클래스</h4>
<p align="center">
 <img src="https://velog.velcdn.com/images/ldh-dodo/post/22d94a47-c554-4fd9-89a0-f180fd9740c2/image.png" width="600px"/>
</p>

<ul>
<li>비트 &#39;110&#39; 으로 시작함</li>
<li>네트워크 주소는 3옥텟, 호스트 주소는 1옥텟으로 구성</li>
<li>이론상 $$2²¹$$ 개의 네트워크, $$2^8$$ 개의 호스트 주소를 가짐</li>
<li>고정된 비트(&#39;110&#39;)를 제외하고 나머지 21자리가 네트워크 주소를 구성한다고 이해하면 됨</li>
</ul>
<h3 id="클래스리스-주소-체계classless-addressing">클래스리스 주소 체계(Classless addressing)</h3>
<p>클래스풀 주소 체계를 이용하면 할당 가능한 호스트의 주소 공간을 유동적으로 관리할 수 있지만, 결국 고정된 크기기 때문에, IP 주소의 낭비가 생기는 상황이 존재한다.
만약, 300명의 직원이 사용할 컴퓨터를 동일 네트워크로 구성할 때, C 클래스는 호스트에게 할당할 수 있는 IP 주소가 254개 뿐이기 때문에, B 클래스를 이용해야 하고, 이는 결국 IP 주소의 낭비로 이어진다.</p>
<p>그래서 클래스풀 주소 체계보다 더 유동적이고, 정교하게 네트워크를 구획할 수 있는 <code>클래스리스 주소 체계</code>가 등장했다.
이름 그대로, 클래스에 구애받지 않고 네트워크의 영역을 나누어 호스트에게 IP 주소를 할당하는 방식이다.</p>
<h3 id="서브넷-마스크">서브넷 마스크</h3>
<p>클래스풀 주소 체계는 클래스를 통해 네트워크 주소와 호스트 주소를 구분했다.
그렇다면 클래스리스 주소 체계는 뭘로 두 주소를 구분할까?
그것은 바로 <code>서브넷 마스크</code> 이다.
서브넷 마스크는 IP 주소에서 네트워크 부분을 1, 호스트 부분은 0으로 표기한 <code>비트열</code> 이다.
서브넷 마스크를 이용해 클래스를 원하는 크기로 더 잘게 쪼개어 사용하는 것을 <code>서브네팅</code> 이라고 한다.</p>
<p><code>서브넷 마스크와 IP 주소를 AND 연산하는 방식</code> 을 통해 네트워크 주소와 호스트 주소를 구분짓는다.</p>
<p>만약, 서브넷 마스크가 <code>255.255.255.0</code> 이라면, 서브넷 마스크의 0이 8개라는 의미고, 호스트 주소는 8비트로 표현이 가능하다는 의미이다.
네트워크 주소를 구한 뒤, 그 주소를 기점으로 $$2^8$$ 개의 호스트를 할당할 수 있다.</p>
<p>아래 예시를 보자.</p>
<p align="center" style="display:flex">
 <img src="https://velog.velcdn.com/images/ldh-dodo/post/4f06ff81-9b6e-4913-8711-fc34afdb82a9/image.png" width="400px"/>
 <img src="https://velog.velcdn.com/images/ldh-dodo/post/ae4e4f45-62ca-4c78-b368-615b7e0b4344/image.png" width="400px"/>
</p>

<ol>
<li>IP 주소와 서브넷 마스크를 AND 연산해서 <code>네트워크 주소</code> 를 구할 수 있다. 
(192.1658.219.0)</li>
<li>서브넷 마스크에서 0이 8개이므로, 호스트 주소를 8비트로 표현이 가능하고, 이를 통해 실제로 할당 가능한 호스트 IP 주소의 범위를 구할 수 있다.
(네트워크 주소, 브로트 캐스트를 제외하면 192.168.219.1 ~ 192.168.219.254)</li>
</ol>
<h4 id="cidr-표기법">CIDR 표기법</h4>
<p>서브넷 마스크를 표기하는 방법은 두 가지가 있다.</p>
<ul>
<li>255.255.255.0 과 같이 10진수로 직접 표기 (앞의 예시가 이에 해당)</li>
<li><code>IP 주소/서브넷 마스크 상의 1의 개수</code> 형식 으로 표기</li>
</ul>
<p>두 번째 방식을 <code>CIDR 표기법(Classless Inter-Domain Routing notation)</code> 이라고 부르고, 자주 활용된다.</p>
<p><code>IP 주소 192.168.219.103 과 서브넷 마스크 255.255.255.0</code>
-&gt; <code>192.168.219.103/24</code> (CIDR 표기 방식)</p>
<h2 id="공인-ip-주소와-사설-ip-주소">공인 IP 주소와 사설 IP 주소</h2>
<p>IP는 고유한 IP 주소를 의미하는 <code>공인 IP 주소</code> 와 고유하지 않은 IP 주소인 <code>사설 IP 주소</code> 로 나뉠 수 있다.</p>
<h3 id="공인-ip-주소public-ip-address">공인 IP 주소(public IP address)</h3>
<ul>
<li>전 세계에서 고유한 IP 주소</li>
<li>ISP나 공인 IP 주소 할당 기관을 통해 할당받을 수 있음</li>
</ul>
<h3 id="사설-ip-주소private-ip-address">사설 IP 주소(private IP address)</h3>
<p><code>사설 IP 주소</code>란 사설 네트워크에서 사용하기 위한 IP 주소이다.
<code>사설 네트워크</code>란 인터넷, 외부 네트워크에 공개되지 않은 네트워크를 의미한다.
실제로 우리가 사용하는 네트워크 기기의 IP 주소를 별도로 신청해서 할당받지는 않았을 텐데, 그 이유가 바로 LAN 내의 호스트는 사설 IP 주소를 사용하기 때문이다.</p>
<p>IP 주소 공간 중에서 사설 IP 주소로 사용하도록 특별히 예약된 IP 주소 공간이 있고, 이는 다음과 같다.</p>
<ul>
<li>10.0.0.0/8 (10.0.0.0 - 10.255.255.255)</li>
<li>172.16.0.0/12 (172.16.0.0 - 172.31.255.255)</li>
<li>192.168.0.0/16 (192.168.0.0 - 192.168.255.255)</li>
</ul>
<p>사설 IP 주소의 할당 주체는 일반적으로 <code>라우터</code> 이고, 할당받은 사설 IP 주소는 해당 호스트가 속한 사설 네트워크에서만 유효하므로, 다른 네트워크 상의 사설 IP 주소와 중복될 수 있음을 인지하자.</p>
<p>사설 IP가 사설 네트워크에서만 유효하다면, 사설 IP 주소를 사용하는 호스트가 외부 네트워크와 통신하려면 어떻게 해야할까? 
<code>NAT</code> 이라는 기술을 사용하면 된다.</p>
<h3 id="natnetwork-address-translation">NAT(Network Address Translation)</h3>
<p><code>NAT</code> 이란, 네트워크 내부에서 사용되는 사설 IP 주소와 네트워크 외부에서 사용되는 공인 IP 주소를 변환하는데 사용된다.</p>
<p>대부분의 라우터와 공유기는 NAT 기능을 내장하고 있기 때문에, 사설 IP 주소가 공유기를 거쳐 공인 IP로 변경되고, 외부 네트워크로 전송되거나 반대로 외부 네트워크로부터 받은 패킷 속 공인 IP 주소가 공유기를 거쳐 사설 IP 주소로 변경되어 사설 네트워크 속 호스트에 이르게 된다.</p>
<p>NAT의 정확한 동작 방식은 다음 장에서 살펴보겠다.</p>
<h2 id="정적-ip-주소와-동적-ip-주소">정적 IP 주소와 동적 IP 주소</h2>
<p>호스트에 IP 주소를 할당하는 방식은 <code>정적 할당</code> 과 <code>동적 할당</code> 이 있다.
<code>정적 할당</code> 은 수작업을 통해 이루어지고, <code>동적 할당</code> 은 DHCP라는 프로토콜을 통해 이루어진다.</p>
<h3 id="정적-할당">정적 할당</h3>
<p>호스트에 직접 수작업으로 IP 주소를 부여하는 방식이다.
이렇게 할당된 주소를 <code>정적 IP 주소</code> 라고 부른다.</p>
<h3 id="동적-할당">동적 할당</h3>
<p>IP 주소를 정적으로만 할당하다 보면, 의도치 않게 잘못된 IP 주소를 입력할 수도 있고, 중복된 IP 주소를 입력할 수도 있다.</p>
<p>이럴 때 사용 가능한 방식이 바로 <code>동적 할당</code> 이다.
IP 동적 할당 사용되는 대표적인 프로토콜이 바로 <code>DHCP(Dynamic Host Configuration Protocol</code> 이다. 
DHCP는 응용 계층에 속하지만, 네트워크 계층의 개념을 이해하기 위해 살펴보자.</p>
<h4 id="dhcp">DHCP</h4>
<p>DHCP를 통한 IP 주소 할당은 <code>IP 주소를 할당받고자 하는 호스트(클라이언트)</code>와 <code>해당 호스트에게 IP 주소를 제공하는 DHCP 서버</code> 간에 메시지를 주고받음으로써 이루어진다.</p>
<p>DHCP 서버는 클라이언트에게 할당 가능한 IP 주소 목록을 관리하고, 클라이언트가 요청할 때 IP 주소를 할당한다.</p>
<p>DCHP로 할당받은 IP 주소는 사용할 기간(임대 기간)이 정해져 있는데, 일반적으로 수 시간에서 수일로 설정한다.
윈도우 사용자는 <code>ipconfig /all</code> 명령어를 통해 <code>임대 시작 날짜</code> 와 <code>임대 만료 날짜</code> 를 확인할 수 있다.</p>
<h4 id="dhcp-메시지-전달-과정">DHCP 메시지 전달 과정</h4>
<p>IP 주소를 할당 받는 과정에서 클라이언트와 DHCP 서버 간에 주고받는 메시지의 종류는 크게 네가지가 있다.</p>
<p><strong>1. DHCP Discover</strong>
<strong>2. DHCP Offer</strong>
<strong>3. DHCP Request</strong>
<strong>4. DHCP Acknowledgment(이하 DHCP ACK)</strong></p>
<p align="center">
 <img src="https://velog.velcdn.com/images/ldh-dodo/post/45324b92-1dba-4617-89f0-2439caf79849/image.png" width="600px"/>
</p>


<h5 id="1-dhcp-discover클라이언트---dhcp-서버">1. DHCP Discover(클라이언트 -&gt; DHCP 서버)</h5>
<ul>
<li>Discover : 발견하다 </li>
<li>클라이언트가 브로드캐스트 전송을 통해 <code>DHCP Discover</code> 메시지를 전송해 DHCP 서버를 찾는 과정</li>
<li>전송시점에 클라이언트는 IP 주소를 할당받지 못했으므로, 송신지 IP 주소는 <code>0.0.0.0</code></li>
<li>브로드 캐스트 전송이므로 목적지 IP 주소는 <code>255.255.255.255</code></li>
</ul>
<h5 id="2-dhcp-offerdhcp-서버---클라이언트">2. DHCP Offer(DHCP 서버 -&gt; 클라이언트)</h5>
<ul>
<li>Offer : 제안하다</li>
<li>DHCP 서버가 <code>DHCP Discover</code> 메시지를 받은 뒤, 클라이언트에게 <code>DHCP Offer</code> 메시지를 보냄</li>
<li><code>DHCP Offer</code> 메시지는 클라이언트에게 할당해 줄 IP 주소를 제안하는 메시지로, 서브넷 마스크, 임대 기간 등의 정보가 포함됨</li>
</ul>
<h5 id="3-dhcp-request클라이언트---dhcp-서버">3. DHCP Request(클라이언트 -&gt; DHCP 서버)</h5>
<ul>
<li><code>DHCP Offer</code> 메시지에 대한 응답. 브로드 캐스트로 전송됨</li>
<li><code>DHCP Offer 메시지 잘 받았고, 네가 전송해준 IP 주소를 써도 되지?</code> 라고 묻는 재확인 절차</li>
</ul>
<h5 id="3-dhcp-ackdhcp-서버---클라이언트">3. DHCP ACK(DHCP 서버 -&gt; 클라이언트)</h5>
<ul>
<li>DHCP 서버가 클라이언트에게 DHCP ACK 메시지를 보내 최종 승인</li>
<li>클라이언트가 <code>DHCP ACK</code> 메시지를 받으면 네트워크 설정을 완료한 후, 임대 기간 동안 IP 주소를 사용</li>
<li>임대 기간이 끝나기 전에 임대 기간을 연장할 수 있는데, 이를 <code>임대 갱신</code> 이라 함.  </li>
<li>IP 주소의 임대 기간이 끝나기 전에 기본적으로 두 차례 자동 수행되고, 임대 갱신 과정이 모두 실패하면 IP 주소가 DHCP 서버로 반납됨.<ul>
<li><code>1차 갱신(T1)</code> : 임대 기간의 50% 시점에 갱신 요청    </li>
<li><code>2차 갱신(T2)</code> : 임대 기간의 87.5% 시점에 다시 갱신 요청</li>
</ul>
</li>
</ul>
<h2 id="라우팅">라우팅</h2>
<p>라우터의 핵심 기능은 <code>패킷이 이동할 최적의 경로를 설정한 뒤 해당 경로로 패킷을 이동시키는 것</code> 이다.
이를 <code>라우팅</code> 이라 한다.</p>
<p><code>라우팅 테이블</code> 이 만들어지는 방법과 프로토콜에 따라 라우팅을 분류하면 다음 그림과 같이 표현할 수 있다.</p>
<p align="center" style="display:flex">
 <img src="https://velog.velcdn.com/images/ldh-dodo/post/cc584a82-4cfc-4524-ab49-c79a4becb39b/image.png" width="600px"/>
</p>

<h3 id="라우터">라우터</h3>
<p>멀리 떨어진 호스트 간의 통신 과정에서 패킷은 서로에게 도달하기 까지 여러 라우터를 거쳐서 다양한 경로로 이동할 수 있다.
이처럼 라우팅 도중 패킷이 호스트와 라우터 간에, 혹은 라우터와 라우터 간에 이동하는 하나의 과정을 <code>홉</code> 이라고 부른다.
즉, 패킷은 <code>여러 홉을 거쳐</code> 라우팅될 수 있다.</p>
<p align="center" style="display:flex">
 <img src="https://velog.velcdn.com/images/ldh-dodo/post/47d0ddb0-52af-4900-89e4-554b389b437f/image.png" width="600px"/>
</p>

<p>그렇다면 라우터는 패킷을 전달할 다음 홉이 어디인지 어떻게 알까?
그것은 <code>라우팅 테이블</code> 덕분이다.</p>
<h3 id="라우팅-테이블">라우팅 테이블</h3>
<p><code>라우팅 테이블</code> 은 특정 수신지까지 도달하기 위한 정보를 명시한 일종의 표와 같은 정보다.
라우터는 라우팅 테이블을 참고하여 수신지까지의 도달 경로를 판단한다.
라우팅 테이블에 포함된 정보는 라우팅 방식에 따라, 호스트의 환경에 따라 달라지지만, 공통적인 정보이자 핵심적인 정보로는 <code>수신지 IP 주소와 서브넷 마스크</code>, <code>다음 홉</code>, <code>네트워크 인터페이스</code> <code>메트릭</code> 이 있다.</p>
<h4 id="수신지-ip-주소와-서브넷-마스크">수신지 IP 주소와 서브넷 마스크</h4>
<ul>
<li>최정적으로 패킷을 전달할 대상을 의미함</li>
</ul>
<h4 id="다음-홉next-hop">다음 홉(next hop)</h4>
<ul>
<li>최종 수신지까지 가기 위해 다음으로 거쳐야 할 호스트의 IP 주소나 인터페이스를 의미</li>
<li><code>게이트웨이</code> 라고 명시되기도 함</li>
</ul>
<h4 id="네트워크-인터페이스">네트워크 인터페이스</h4>
<ul>
<li>패킷을 내보낼 통로</li>
</ul>
<h4 id="메트릭metric">메트릭(metric)</h4>
<ul>
<li>해당 경로로 이동하는 데에 드는 비용을 의미</li>
<li>라우터는 메트릭이 낮은 경로를 선호함</li>
</ul>
<h3 id="디폴트-라우트">디폴트 라우트</h3>
<p>패킷 내의 수신지 IP 주소가 라우팅 테이블에 있는 수신지 IP 주소, 서브넷 마스크 항목과 완벽하게 합치되는 경우가 있지만, 그렇지 않은 경우가 있다.
즉, 라우팅 테이블에 없는 경로로 패킷을 전송해야 할 때가 있는데, 이 경우에는 <code>기본적으로 패킷을 내보낼 경로</code> 를 설정하여 해당 경로로 패킷을 내보낸다.
이를 <code>디폴트 라우트</code> 라 한다.</p>
<p align="center" style="display:flex">
 <img src="https://velog.velcdn.com/images/ldh-dodo/post/35792606-2939-4bb1-860a-59eb149cdf37/image.png" width="600px"/>
</p>

<h3 id="정적-라우팅과-동적-라우팅">정적 라우팅과 동적 라우팅</h3>
<p>라우팅 테이블이 만들어지는 방식은 크게 두 가지로, <code>정적 라우팅</code> 과 <code>동적 라우팅</code> 이 있다.</p>
<h4 id="정적-라우팅">정적 라우팅</h4>
<ul>
<li>사용자가 수동으로 직접 채워 넣은 라우팅 테이블의 항목을 토대로 라우팅되는 방식</li>
<li>네트워크의 규모가 커지고 관리해야 할 라우터가 늘어날 경우, 이 방식에는 한계가 있음</li>
<li>입력 실수가 발생할 가능성이 존재</li>
<li>해당 경로 상에 문제가 발생했을 경우, 경로를 우회하지 못하고 문제가 발생한 경로로 패킷을 전송할 수 밖에 없다는 한계점이 존재</li>
</ul>
<h4 id="동적-라우팅">동적 라우팅</h4>
<ul>
<li>정적 라우팅의 단점을 보완할 수 있는 라우팅 방식</li>
<li>자동으로 라우팅 테이블 항목을 만들고, 이를 이용하여 라우팅 하는 방식</li>
<li>라우팅 테이블 항목이 수시로 변할 수 있음</li>
<li>네트워크 경로상에 문제가 발생했을 때, 이를 우회할 수 있게 경로를 자동으로 갱신</li>
<li><code>(동적) 라우팅 프로토콜</code> 을 사용해 자동으로 라우팅 테이블 항목을 생성</li>
</ul>
<h4 id="asautonomous-system">AS(Autonomous System)</h4>
<ul>
<li>동일한 라우팅 정책으로 운영되는 라우터들의 집단 네트워크</li>
<li>한 회사나 단체에서 관리하는 라우터 집단이 그 예시</li>
<li>한 AS 내에는 다수의 라우터가 있고, 라우터들은 AS 내부에서 통신하거나 AS 외부와 통신할 수 있음</li>
<li>AS 경계에서 AS 내외로 통신을 주고 받는 라우터를 <code>AS 경계 라우터(ASBR; Autonomous System Boundary Router)</code> 라고 함</li>
</ul>
<p align="center" style="display:flex">
 <img src="https://velog.velcdn.com/images/ldh-dodo/post/1ce6f75a-c7ef-4dbe-8c0b-791c770ced64/image.png" width="600px"/>
</p>

<h3 id="동적-라우팅-프로토콜">(동적) 라우팅 프로토콜</h3>
<p>모든 라우터는 특정 수신지까지 도달하기 위한 최적의 경로를 찾아 라우팅 테이블에 추가하려 노력한다. 이를 위해 라우터끼리 서로 자신의 정보를 교환하게 되는데, 이 과정에서 사용되는 프로토콜이 바로 <code>(동적) 라우팅 프로토콜</code> 이다.
<code>라우팅 프로토콜</code> 이란 라우터끼리 정보를 교환하며 패킷이 이동할 최적의 경로를 찾기 위한 프로토콜 이다.</p>
<p>라우팅 프로토콜은 크게 AS 내부에서 수행되느냐, AS 외부에서 수행되느냐에 따라 종류를 나눌 수 있다.
AS 내부에서 수행되는 라우팅 프로토콜을 <code>IGP (Interior Gateway Protocol)</code> 이라 한다.
AS 외부에서 수행되는 라우팅 프로토콜은 <code>EGP (Exterior Gateway Protocol)</code> 이라 한다.</p>
<p>대표적인 IGP로는 RIP와 OSPF가 있고, 대표적인 EGP로는 BGP가 있다.</p>
<p align="center" style="display:flex">
 <img src="https://velog.velcdn.com/images/ldh-dodo/post/b9c77e2e-3837-4dd9-9834-3ee076b88055/image.png" width="600px"/>
</p>

<h4 id="igp-rip와-ospf">IGP: RIP와 OSPF</h4>
<h5 id="riprouting-information-protocol">RIP(Routing Information Protocol)</h5>
<p align="center" style="display:flex">
 <img src="https://velog.velcdn.com/images/ldh-dodo/post/125a39cb-b8f6-4ce7-a9a2-700c78cba5e5/image.png" width="600px"/>
</p>

<p>RIP는 최적의 경로를 선정하는 과정에서 <code>거리 벡터</code> 기반의 라우팅 프로토콜이다. 이는 이름 그대로 거리를 기반으로 최적의 경로를 찾는 라우팅 프로토콜을 의미한다. 이 때의 <code>거리</code> 는 <code>홉의 수 (패킷이 경유한 라우터의 수)</code> 를 의미한다.</p>
<p>RIP는 인접한 라우터끼리 경로 정보를 주기적으로 교환하며 라우팅 테이블을 갱신하고, 이를 통해 라우터는 특정 수신지에 도달하기까지의 홉 수를 알 수 있다.
그리고 <code>홉 수가 가장 적은 경로</code> 를 최적의 경로라고 판단한다.
따라서, 홉 수가 적을수록 라우팅 테이블상의 메트릭 값도 작아진다.</p>
<h5 id="ospfopen-shortest-path-first">OSPF(Open Shortest Path First)</h5>
<p>OSPF는 <code>링크 상태</code> 라우팅 프로토콜이다. </p>
<p>OSPF는 노드와 간선의 이루어진 링크 정보를 비롯한 현재 네트워크의 상태를 그래프의 형태로 <code>링크 상태 데이터베이스</code> 에 저장한다.</p>
<p>링크 상태 데이터베이스에는 라우터들의 연결 관계, 연결 비용 등 현재 네트워크의 상태를 그래프로 표현하기 위한 데이터가 저장되어 있다.</p>
<p>라우터는 링크 상태 데이터베이스를 기반으로 현재 네트워크 구성을 마치 지도처럼 그린 뒤 최적의 경로를 선택한다.</p>
<p>OSPF에서는 최적의 경로를 결정하기 위해 <code>대역폭</code> 을 기반을 메트릭을 계산한다.
대역폭이 높은 링크일 수록 메트릭이 낮은 경로로 인식한다.</p>
<p>라우터 간에 경로 정보를 주기적으로 교환하며 라우팅 테이블을 갱신하는 RIP와 달리, OSPF는 네트워크 구성이 변경되었을 때 라우팅 테이블이 갱신된다.</p>
<p>그런데 만약, 네트워크 구성이 변경될 때마다 라우팅 테이블이 변경된다면, 네트워크의 규모가 매우 커졌을 때 링크 상태 데이터베이스에 모든 정보를 저장하기 어려울 것이다.</p>
<p>이에 OSPF에서는 AS를 <code>에어리어</code> 라는 단위로 나누고, 구분된 에어리어 내에서만 링크 상태를 공유한다.</p>
<p>에어리어에는 다음 그림 처럼 번호가 부여되어 있으며, 에어리어 경계에 있는 <code>ABR (Area Border Router)</code> 이라는 라우터가 에어리어의 연결을 담당한다.</p>
<p align="center" style="display:flex">
 <img src="https://velog.velcdn.com/images/ldh-dodo/post/a858e9ca-bcc5-44d9-af7a-42d2b5cf7f74/image.png" width="600px"/>
</p>

<h3 id="egp--bgp">EGP : BGP</h3>
<p>대표적인 EGP로는 <code>BGP(Border Gateway Protocol)</code>이 있다.</p>
<p>AS 간의 통신에서 사용되는 대표적인 프로토콜이지만, 사실 BGP로 AS 내 라우터 간 통신도 가능하다.</p>
<p>AS 간 통신을 위한 BGP는 eBGP, AS 내의 통신을 위한 BGP는 iBGP 라고 정의한다.</p>
<h4 id="bgp">BGP</h4>
<p>AS 간에 정보를 주고받기 위해서는 AS 내에서 두 개 이상의 eBGP를 사용하는 라우터가 서로 간에 연결되어야 한다.</p>
<p>이 연결은 BGP 라우터 간에 BGP 메시지를 주고받음으로써 이루어지는데, BGP 메시지를 주고받을 수 있도록 연결된 BGP 라우터를 <code>피어</code> 라고 정의한다.</p>
<p>즉, 다른 AS와 BGP 연결을 유지하기 위해서 BGP 라우터끼리 연결되어 <code>피어</code>가 되어야 하고, 이렇게 피어 관계가 되도록 연결하는 과정을 <code>피어링</code> 이라 한다.</p>
<p>BGP는 RIP와 OSPF에 비해 최적의 경로를 결정하는 과정이 복잡하고, 일정하지 않은 경우가 많다.
경로 결정 과정에서 수신지 주소 뿐만 아니라, <code>속성</code> 과 <code>정책</code> 이 고려되기 때문이다.</p>
<h5 id="1-as-path-속성">1. AS-PATH 속성</h5>
<p>메시지가 수신지에 이르는 과정에서 통과하는 AS의 목록을 의미한다.
만약, AS1 -&gt; AS2로 곧장 간다면 PATH는 <code>AS2</code>, 
AS1 -&gt; AS3 -&gt; AS2 로 간다면 PATH는 <code>AS3 AS2</code> 가 된다.</p>
<p>이를 통해 BGP의 특징을 알 수 있다.</p>
<ul>
<li>BGP는 AS 간 라우팅을 할 때, 거치게 될 <code>라우터</code> 의 수가 아닌 <code>AS</code> 의 수를 고려한다.<ul>
<li>따라서, AS-PATH 길이가 더 짧은 경로라 할지라도, 거치게 될 라우터의 홉 수가 더 많을 수 있음</li>
</ul>
</li>
<li>둘 째, BGP는 <code>거리</code>가 아닌 <code>경로</code>를 고려한다.<ul>
<li>메시지가 같은 경로를 무한히 반복하며 이동하는 순환을 방지하기 위해 경로를 고려함 만약, 자신의 AS가 AS-PATH에 포함되어 있을 경우, <code>순환</code> 으로 간주해 해당 메시지를 폐기함.</li>
</ul>
</li>
</ul>
<h5 id="2-next_hop-속성">2. NEXT_HOP 속성</h5>
<p>다음으로 거칠 라우터의 IP 주소를 나타낸다.
AS1 -&gt; AS3 -&gt; AS2에 도달하는 경로의 NEXT-HOP은 AS1과 연결된 라우터 의 IP 주소이다.</p>
<h5 id="3-local_pref-속성">3. LOCAL_PREF 속성</h5>
<p>LOCAL PREFerence의 약자이다.
이는 AS 외부 경로에 있어 AS 내부에서 어떤 경로를 선호할지에 대한 척도를 나타내는 속성이다.
AS-PATH나 NEXT-HOP 속성보다 우선시 되며, 이 값이 클수록 우선으로 선택된다.
<code>정책적인 이유로 이 경로를 우선시 하겠다</code>, <code>설령 이 경로가 다른 경로에 비해 비교적 비효율적일지라도 이 경로를 우선시하겠다</code> 라는 의미와도 같다.</p>
<blockquote>
<p><strong>IPV4와 IPV6는 어떤 차이점이 있을까요?</strong>
IPv4에서는 주소를 4바이트로 표현하고, IPv6에서는 주소를 16바이트로 표현합니다.
IPv4에서 제공되는 주소 개수의 한계 때문에 IPv6가 등장했습니다. 
IPv6는 보편화 단계에 있으며 IPv4와 혼용해서 사용하기 위해 터널링 기법을 사용합니다.</p>
</blockquote>
<blockquote>
<p><strong>서브넷과 서브넷 마스크에 대해 설명해주세요.</strong>
서브넷은 하나의 큰 네트워크를 더 작은 네트워크로 나눈 부분을 의미합니다.
서브넷 마스크는 IP 주소상에서 네트워크 부분을 1, 호스트 부분은 0으로 표기한 비트열입니다.</p>
</blockquote>
<blockquote>
<p><strong>라우팅이 뭘까요?</strong>
라우팅이란 패킷이 이동할 최적의 경로를 설정한 뒤 해당 경로로 패킷을 이동시키는 것입니다.</p>
</blockquote>
<blockquote>
<p><strong>라우터에 대해서 설명해주세요.</strong>
라우터는 네트워크 계층에서 동작하는 네트워크 장비로, 수신한 데이터 패킷의 목적지까지의 최적 경로를 판단하여 데이터를 전송합니다. 이를 위해 라우터는 라우팅 테이블을 참고하여 도달 경로를 결정합니다. 여러 라우터를 거쳐 데이터가 전달되기 때문에, 서로 멀리 떨어져 있는 호스트 간에도 원활하게 통신할 수 있습니다.</p>
</blockquote>
<blockquote>
<p><strong>Public IP와 Private IP 차이는 뭘까요?</strong>
공인 IP 주소는 전 세계에서 고유한 IP 주소로, ISP나 공인 IP 주소 할당 기관을 통해 할당받을 수 있습니다.
사설 IP 주소란 사설 네트워크에서 사용하기 위한 IP 주소로, 고유하지 않을 수 있으며, 특정 범위 내에서 자유롭게 할당 가능합니다.</p>
</blockquote>
<blockquote>
<p><strong>라우팅 프로토콜에 대해서 설명해주세요.</strong>
라우팅 프로토콜이란 라우터끼리 정보를 교환하며 패킷이 이동할 최적의 경로를 찾기 위한 프로토콜 입니다.
AS 내부에서 수행되는 IGP와 AS 외부에서 수행되는 EGP로 나눌 수 있습니다.
IGP의 RIP는 거리 벡터 라우팅 프로토콜로, 경로의 &quot;거리&quot;를 기준으로 최적의 경로를 찾습니다. 거리 벡터 프로토콜은 각 라우터가 인접 라우터와의 거리를 교환하여 경로를 계산합니다.
IGP의 OSPF는 링크 상태 라우팅 프로토콜로, 각 라우터가 네트워크의 전체 링크 상태 정보를 교환하여 최적의 경로를 계산합니다. OSPF는 경로의 &quot;비용&quot;을 기준으로 경로를 선택합니다.
EGP의 BGP는 AS 간 경로 교환을 담당하는 프로토콜로, AS-PATH, NEXT-HOP, LOCAL-PREF 등 다양한 속성을 고려하여 최적의 경로를 찾습니다.</p>
</blockquote>
<blockquote>
<p>*<em>IP는 어떻게 할당될까요? *</em>
IP를 할당하는 방식에는 정적할당과 동적할당이 있습니다.
정적 할당은 호스트에 직접 수작업으로 IP 주소를 부여하는 방식입니다.
동적 할당은 DHCP와 같은 프로토콜을 통해 자동으로 호스트에 IP주소를 할당해주는 방식입니다.</p>
</blockquote>
<blockquote>
<p><strong>NAT가 뭘까요?</strong>.
NAT은 네트워크 내부에서 사용되는 사설 IP 주소와 네트워크 외부에서 사용되는 공인 IP 주소를 변환하는데 사용되는 기술입니다.
사설 IP 주소가 공유기를 거쳐 공인 IP로 변경되고 외부 네트워크로 전송되거나 반대로 외부 네트워크로부터 받은 패킷 속 공인 IP 주소가 공유기를 거쳐 사설 IP 주소로 변경되어 사설 네트워크 속 호스트에 이르게 됩니다.</p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>