<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>blight_k.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Tue, 15 Sep 2026 13:05:37 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>blight_k.log</title>
            <url>https://velog.velcdn.com/images/blight_k/profile/8bd3ee43-b49a-4c29-b497-36a2690c2122/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. blight_k.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/blight_k" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[대규모 트래픽으로 인한 서버 과부하 해결 방법 #2]]></title>
            <link>https://velog.io/@blight_k/%EB%8C%80%EA%B7%9C%EB%AA%A8-%ED%8A%B8%EB%9E%98%ED%94%BD%EC%9C%BC%EB%A1%9C-%EC%9D%B8%ED%95%9C-%EC%84%9C%EB%B2%84-%EA%B3%BC%EB%B6%80%ED%95%98-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-2</link>
            <guid>https://velog.io/@blight_k/%EB%8C%80%EA%B7%9C%EB%AA%A8-%ED%8A%B8%EB%9E%98%ED%94%BD%EC%9C%BC%EB%A1%9C-%EC%9D%B8%ED%95%9C-%EC%84%9C%EB%B2%84-%EA%B3%BC%EB%B6%80%ED%95%98-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-2</guid>
            <pubDate>Tue, 15 Sep 2026 13:05:37 GMT</pubDate>
            <description><![CDATA[<h1 id="서킷-브레이커">서킷 브레이커</h1>
<p>서킷 브레이커는 서비스 장애를 빠르게 감지하고, 장애가 다른 서비스로 연쇄적으로 전파되는 것을 막는 장애 대응 패턴이다.</p>
<h2 id="서킷-브레이커가-필요한-이유">서킷 브레이커가 필요한 이유</h2>
<p>여러 서비스가 연결된 시스템에서는 하나의 요청을 처리하기 위해 여러 서비스를 차례대로 호출하는 경우가 많다.</p>
<p>예를 들어 주문 서비스가 재고 서비스와 배송 서비스, 알림 서비스를 순서대로 호출한다고 가정한다. 이때 재고 서비스에 장애가 발생하면 주문 서비스는 응답이 올 때까지 계속 기다리게 된다.</p>
<p>주문 서비스가 사용할 수 있는 작업 스레드가 100개이고, 그중 98개가 재고 서비스의 응답을 기다리고 있다면 새로운 요청을 처리할 수 있는 스레드는 2개만 남게 된다. 재고 서비스의 장애가 길어질수록 대기 중인 요청이 쌓이고, 정상 상태인 배송 서비스와 알림 서비스도 호출되지 못하게 된다.</p>
<p>이처럼 한 서비스의 장애가 주변 서비스로 연쇄적으로 전파되는 현상을 계단식 실패 또는 연쇄 장애라고 한다.</p>
<p>서킷 브레이커는 장애가 발생한 서비스에 대한 요청을 일정 시간 차단함으로써 이러한 문제를 방지한다. 요청을 계속 보내고 응답을 기다리는 대신 즉시 오류를 반환하여 스레드와 연결 같은 시스템 자원을 보호한다.</p>
<h2 id="서킷-브레이커의-상태">서킷 브레이커의 상태</h2>
<p>서킷 브레이커는 <code>Closed</code>, <code>Open</code>, <code>Half-Open</code>의 세 가지 상태를 가진다.</p>
<h3 id="closed-상태">Closed 상태</h3>
<p>Closed는 외부 서비스가 정상적으로 동작하고 있다고 판단하는 상태다. 모든 요청을 외부 서비스로 전달하면서 성공과 실패 결과를 기록한다.</p>
<p>일정한 요청 수가 쌓였을 때 실패율이 설정된 임계치를 넘으면 장애가 발생한 것으로 판단하고 Open 상태로 전환한다.</p>
<h3 id="open-상태">Open 상태</h3>
<p>Open은 외부 서비스에 장애가 있다고 판단하여 요청을 차단하는 상태다. 이 상태에서는 실제 서비스를 호출하지 않고 즉시 오류나 미리 준비된 대체 응답을 반환한다.</p>
<p>즉시 응답을 반환하기 때문에 불필요한 대기 요청이 쌓이지 않으며, 장애가 발생한 서비스도 복구할 시간을 확보할 수 있다.</p>
<p>Open 상태는 설정된 대기 시간이 지나면 Half-Open 상태로 전환된다.</p>
<h3 id="half-open-상태">Half-Open 상태</h3>
<p>Half-Open은 장애가 복구되었는지 확인하는 상태다. 제한된 수의 요청만 외부 서비스로 전달하여 정상 동작 여부를 확인한다.</p>
<p>테스트 요청이 성공하면 서비스가 복구된 것으로 판단하여 Closed 상태로 돌아간다. 테스트 요청이 다시 실패하면 장애가 계속되고 있다고 판단하여 Open 상태로 돌아간다.</p>
<h2 id="동작-흐름">동작 흐름</h2>
<p>서킷 브레이커는 다음과 같은 흐름으로 동작한다.</p>
<ol>
<li>정상 상태에서는 요청을 외부 서비스로 전달하고 처리 결과를 기록한다.</li>
<li>일정 기간의 실패율이 임계치를 넘으면 외부 서비스 호출을 차단한다.</li>
<li>차단된 상태에서는 실제 호출 없이 즉시 오류나 대체 응답을 반환한다.</li>
<li>설정된 시간이 지나면 일부 요청을 보내 서비스의 복구 여부를 확인한다.</li>
<li>요청이 성공하면 정상 상태로 돌아가고, 실패하면 다시 호출을 차단한다.</li>
</ol>
<h2 id="설정-시-고려할-점">설정 시 고려할 점</h2>
<p>실패율 임계치를 지나치게 낮게 설정하면 일시적인 오류에도 요청이 쉽게 차단될 수 있다. 반대로 임계치를 너무 높게 설정하면 장애 감지가 늦어져 시스템 자원이 이미 소진될 수 있다.</p>
<p>실패율을 계산하기 위한 최소 요청 수도 함께 설정해야 한다. 요청이 한두 번 실패했다는 이유만으로 장애라고 판단하면 실제 서비스 상태를 정확하게 반영하기 어렵기 때문이다.</p>
<p>서킷 브레이커만 적용한다고 모든 장애를 막을 수 있는 것은 아니다. 응답 제한 시간을 설정하는 타임아웃과 재시도 횟수 제한, 대체 응답을 제공하는 폴백 등을 함께 적용해야 안정적인 장애 대응이 가능하다.</p>
<h2 id="정리">정리</h2>
<p>서킷 브레이커의 핵심 목적은 장애가 발생한 서비스를 계속 호출하지 않도록 차단하는 데 있다. 이를 통해 대기 요청으로 인한 자원 고갈을 막고, 하나의 장애가 전체 시스템으로 확산되는 것을 줄일 수 있다.</p>
<p>또한 장애가 발생한 서비스에는 복구 시간을 제공하고, 호출하는 서비스에는 빠른 실패 처리를 제공하여 전체 시스템의 안정성을 높인다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[대규모 트래픽으로 인한 서버 과부하 해결 방법 #1]]></title>
            <link>https://velog.io/@blight_k/%EB%8C%80%EA%B7%9C%EB%AA%A8-%ED%8A%B8%EB%9E%98%ED%94%BD%EC%9C%BC%EB%A1%9C-%EC%9D%B8%ED%95%9C-%EC%84%9C%EB%B2%84-%EA%B3%BC%EB%B6%80%ED%95%98-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-1</link>
            <guid>https://velog.io/@blight_k/%EB%8C%80%EA%B7%9C%EB%AA%A8-%ED%8A%B8%EB%9E%98%ED%94%BD%EC%9C%BC%EB%A1%9C-%EC%9D%B8%ED%95%9C-%EC%84%9C%EB%B2%84-%EA%B3%BC%EB%B6%80%ED%95%98-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-1</guid>
            <pubDate>Tue, 08 Sep 2026 12:59:44 GMT</pubDate>
            <description><![CDATA[<p>서버 과부하는 서버가 처리할 수 있는 양보다 많은 요청이 들어와 CPU, 메모리, 네트워크, 디스크 I/O, 스레드, DB 커넥션 등의 자원이 한계에 도달한 상태다.</p>
<p>과부하가 발생하면 응답이 늦어지고 타임아웃과 5xx 오류가 증가한다. 심한 경우 서버가 새로운 요청을 받지 못하거나 프로세스가 종료된다.</p>
<p>예를 들어 평소 초당 100건을 처리하던 서비스에 이벤트 문자 발송 직후 초당 3,000건의 요청이 들어오면 애플리케이션 서버뿐 아니라 DB 커넥션 풀과 외부 API까지 함께 포화될 수 있다.</p>
<h2 id="모니터링으로-원인을-확인한다">모니터링으로 원인을 확인한다</h2>
<p>과부하를 해결하려면 먼저 어떤 자원이 부족한지 알아야 한다. CPU 사용률이 높다고 해서 항상 CPU가 원인인 것은 아니다. 느린 쿼리로 요청이 쌓이면서 CPU 사용률까지 높아졌을 수도 있다.</p>
<p>서버에서는 다음 항목을 주로 확인한다.</p>
<ul>
<li>CPU와 메모리 사용률</li>
<li>디스크 I/O와 남은 용량</li>
<li>네트워크 송수신량</li>
<li>초당 요청 수와 응답 시간</li>
<li>4xx와 5xx 오류 수</li>
<li>실행 중인 스레드와 이벤트 루프 지연</li>
<li>DB 커넥션 수와 느린 쿼리</li>
<li>외부 API의 응답 시간</li>
</ul>
<p>AWS를 사용하면 CloudWatch로 지표를 수집하고 임계치를 넘었을 때 알림을 보낼 수 있다. 자체 서버에서는 Netdata, Prometheus, Grafana 같은 도구를 사용할 수 있다.</p>
<p>운영자가 대시보드를 계속 보고 있을 필요는 없다. CPU 사용률, 오류율, 응답 시간 등에 임계치를 설정하고 Slack이나 문자로 알림을 받도록 구성한다.</p>
<p>CPU가 80%를 넘었을 때만 알림을 보내면 이미 장애가 시작된 뒤일 수 있다. 다음처럼 여러 단계를 두는 편이 좋다.</p>
<pre><code class="language-text">CPU 70%가 5분 지속 → 경고
CPU 85%가 3분 지속 → 긴급 알림
5xx 오류율 5% 초과 → 즉시 긴급 알림
응답 시간 p95 2초 초과 → 성능 저하 알림</code></pre>
<p>어떤 페이지에 트래픽이 몰렸는지 확인하려면 서버 자원 지표만으로는 부족하다. 웹 서버 접근 로그나 APM을 함께 수집해야 한다.</p>
<pre><code class="language-text">/api/attendance        초당 1,500건
/api/member/search     초당 200건
/api/report/download   초당 20건</code></pre>
<p>이런 정보를 확인해야 출석 API를 확장할지, 검색 쿼리를 개선할지, 보고서 생성을 비동기로 바꿀지 판단할 수 있다.</p>
<p>Cloudflare 같은 CDN 사업자의 상태 페이지는 CDN 서비스 자체의 장애 여부를 확인하는 용도다. 우리 서비스에서 어떤 페이지에 요청이 몰렸는지는 별도의 로그와 모니터링으로 확인해야 한다.</p>
<h2 id="오토스케일링으로-처리-용량을-늘린다">오토스케일링으로 처리 용량을 늘린다</h2>
<p>AWS EC2 Auto Scaling은 트래픽이나 자원 사용량에 따라 서버 인스턴스 수를 자동으로 조절한다.</p>
<pre><code class="language-text">서버 2대 운영
→ CPU 사용률 상승
→ CloudWatch 지표 감지
→ 서버 4대로 확장
→ 트래픽 감소
→ 다시 서버 2대로 축소</code></pre>
<p>오토스케일링은 기존 서버의 CPU나 메모리를 즉시 늘리는 기능이라기보다, 일반적으로 서버 인스턴스를 추가하거나 제거하는 수평 확장 방식이다. CloudWatch 지표가 설정한 목표에서 벗어나면 스케일링 작업이 실행된다. <a href="https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-scaling-target-tracking.html">AWS Auto Scaling 정책</a></p>
<p>새 인스턴스를 생성하고 애플리케이션을 실행한 뒤 정상 상태가 되기까지는 시간이 필요하다. 갑자기 트래픽이 몰리면 서버가 준비되기 전에 장애가 발생할 수 있다.</p>
<p>예정된 행사나 문자 발송처럼 트래픽 증가 시점을 알고 있다면 미리 서버 수를 늘리는 예약 스케일링을 사용할 수 있다. 평소에도 최소 여유 용량을 확보해두는 것이 좋다.</p>
<h2 id="로드밸런서로-요청을-분산한다">로드밸런서로 요청을 분산한다</h2>
<p>서버를 여러 대 실행해도 요청을 한 서버에만 보내면 확장 효과가 없다. 로드밸런서는 들어오는 요청을 여러 서버에 분산한다.</p>
<pre><code class="language-text">사용자 요청
      ↓
로드밸런서
  ├─ 서버 A
  ├─ 서버 B
  └─ 서버 C</code></pre>
<p>로드밸런서는 서버의 상태를 주기적으로 검사한다. 서버 B가 응답하지 않으면 정상인 서버 A와 C로 요청을 전달한다.</p>
<p>오토스케일링과 로드밸런서는 보통 함께 사용한다. 오토스케일링이 서버를 추가하면 로드밸런서가 새 서버에도 요청을 보내고, 서버가 제거되면 해당 서버로의 요청을 중단한다. <a href="https://docs.aws.amazon.com/autoscaling/ec2/userguide/autoscaling-load-balancer.html">AWS의 로드밸런서와 Auto Scaling 연동</a></p>
<p>AWS의 관리형 로드밸런서는 서비스 자체가 트래픽에 맞춰 확장된다. 일반적으로 사용자가 로드밸런서에 EC2 Auto Scaling을 직접 설정하지는 않는다. 직접 Nginx나 HAProxy를 운영한다면 로드밸런서도 여러 대 구성하고 장애 전환 구조를 마련해야 한다.</p>
<h2 id="불필요한-요청은-서버까지-보내지-않는다">불필요한 요청은 서버까지 보내지 않는다</h2>
<p>서버 수를 늘리는 것만으로 모든 과부하를 해결할 수는 없다. 같은 요청이 반복된다면 캐시와 CDN을 활용해야 한다.</p>
<p>이미지, JavaScript, CSS 같은 정적 파일은 CDN에서 제공할 수 있다. 자주 조회하지만 변경이 적은 데이터는 Redis나 애플리케이션 캐시에 저장할 수 있다.</p>
<p>예를 들어 교회 공지 목록을 사용자 1만 명이 조회한다고 해보자. 요청마다 DB를 조회하면 동일한 쿼리가 1만 번 실행된다. 공지 목록을 30초 동안 캐시하면 DB 조회 횟수를 크게 줄일 수 있다.</p>
<p>다만 사용자별 권한이나 교회별 데이터가 다른 경우에는 캐시 키를 정확하게 분리해야 한다.</p>
<h2 id="처리할-수-없는-요청은-조절한다">처리할 수 없는 요청은 조절한다</h2>
<p>서버가 감당할 수 있는 양보다 많은 요청을 계속 받으면 전체 서비스가 함께 느려진다. 이때는 일부 요청을 제한하거나 나중에 처리해야 한다.</p>
<p>로그인 시도, 검색, 문자 발송에는 Rate Limiting을 적용할 수 있다. 사용자나 IP별 요청 횟수를 제한해 한 사용자가 서버 자원을 독점하지 못하도록 한다.</p>
<p>문자 발송, 통계 집계, 보고서 생성처럼 즉시 끝날 필요가 없는 작업은 큐에 넣고 백그라운드에서 처리할 수 있다.</p>
<pre><code class="language-text">사용자 요청
→ 작업 접수
→ 큐에 저장
→ 작업 서버가 순서대로 처리
→ 완료 결과 제공</code></pre>
<p>장애가 심할 때는 부가 기능을 잠시 중단하고 핵심 기능만 제공하는 방법도 있다. 추천 목록이나 상세 통계를 끄더라도 로그인과 출석 등록은 유지하는 식이다.</p>
<h2 id="예측하지-못한-장애에-대응한다">예측하지 못한 장애에 대응한다</h2>
<p>블랙스완은 발생 가능성을 미리 예측하기 어렵지만, 발생하면 큰 영향을 미치는 사건을 뜻한다.</p>
<p>Google의 Black Swan 프로토콜은 일반적인 트래픽 과부하 해결 기법이라기보다, 광범위하고 긴급한 보안 취약점에 대응하기 위한 사고 대응 절차에 가깝다. Google은 Shellshock 취약점이 공개됐을 때 이 절차를 가동했다.</p>
<p>당시 대응 과정은 다음과 같았다.</p>
<ul>
<li>영향받는 시스템과 각 시스템의 위험 수준을 파악한다.</li>
<li>영향을 받을 수 있는 내부 팀에 상황을 전달한다.</li>
<li>취약한 시스템을 가능한 한 빠르게 업데이트한다.</li>
<li>복구 계획과 대응 상황을 파트너와 고객에게 알린다.</li>
</ul>
<p><a href="https://google.github.io/building-secure-and-reliable-systems/raw/ch07.html">Google의 Black Swan 대응 사례</a></p>
<p>대규모 트래픽 장애에서도 이 원칙을 적용할 수 있다. 먼저 영향을 받은 기능과 사용자 범위를 확인하고, 담당자를 정해 대응을 조율한다. 트래픽 제한이나 기능 축소로 피해 확산을 막고, 복구 상황을 내부와 고객에게 전달한다.</p>
<p>서비스가 복구된 뒤에는 시간대별 지표와 로그를 바탕으로 원인을 분석한다. 어떤 경보가 늦었는지, 어느 자원이 먼저 포화됐는지, 확장이 왜 따라가지 못했는지를 확인하고 재발 방지 작업을 정한다.</p>
<p>서버 과부하는 서버 수만 늘린다고 해결되지 않는다. 모니터링으로 병목을 찾고, 로드밸런서와 오토스케일링으로 용량을 확보하며, 캐시·큐·요청 제한으로 서버가 처리해야 할 양을 줄여야 한다. 예측하지 못한 장애가 발생했을 때 움직일 수 있는 대응 절차도 함께 준비해야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[무선 LAN은 어떻게 데이터를 전달할까]]></title>
            <link>https://velog.io/@blight_k/%EB%AC%B4%EC%84%A0-LAN%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EC%A0%84%EB%8B%AC%ED%95%A0%EA%B9%8C</link>
            <guid>https://velog.io/@blight_k/%EB%AC%B4%EC%84%A0-LAN%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EC%A0%84%EB%8B%AC%ED%95%A0%EA%B9%8C</guid>
            <pubDate>Tue, 08 Sep 2026 12:45:00 GMT</pubDate>
            <description><![CDATA[<p>무선 LAN은 케이블 대신 전파를 이용해 일정한 범위 안에 네트워크를 구축하는 기술이다. 흔히 사용하는 와이파이가 대표적인 무선 LAN 기술이다.</p>
<p>스마트폰이나 노트북은 데이터를 무선 신호로 변환해 AP에 전달한다. AP는 받은 데이터를 유선 네트워크나 다른 무선 장치로 전달한다.</p>
<pre><code class="language-text">스마트폰 → 전파 → AP → 유선 네트워크 → 인터넷</code></pre>
<p>와이파이는 주로 2.4GHz와 5GHz 주파수 대역을 사용한다. 최근의 Wi-Fi 6E와 Wi-Fi 7은 6GHz 대역도 사용할 수 있다.</p>
<h2 id="24ghz-대역">2.4GHz 대역</h2>
<p>2.4GHz는 비교적 먼 거리까지 신호가 도달하며 벽이나 문 같은 장애물을 통과하는 능력도 좋은 편이다.</p>
<p>공유기가 거실에 있고 노트북을 벽으로 분리된 방에서 사용한다면 2.4GHz가 더 안정적으로 연결될 수 있다.</p>
<p>또한 오래된 스마트폰, 노트북, 프린터, IoT 기기까지 지원하는 경우가 많아 호환성이 좋다.</p>
<p>단점은 사용자가 많다는 점이다. 와이파이뿐 아니라 블루투스, 무선 키보드, 전자레인지 등 여러 장치가 비슷한 주파수 대역을 사용한다. 주변에 무선 장치가 많으면 간섭이 생겨 속도가 느려질 수 있다.</p>
<h2 id="5ghz-대역">5GHz 대역</h2>
<p>5GHz는 2.4GHz보다 사용할 수 있는 채널이 많고 더 넓은 채널 폭을 구성할 수 있다. 주변 간섭도 비교적 적어서 가까운 거리에서는 더 빠른 통신에 유리하다.</p>
<p>같은 방에서 노트북으로 대용량 파일을 내려받거나 고화질 영상을 시청할 때는 5GHz가 적합하다.</p>
<p>다만 주파수가 높을수록 벽과 같은 장애물을 통과하면서 신호가 더 크게 약해지는 경향이 있다. 공유기와 장치 사이에 벽이 여러 개 있거나 거리가 멀면 연결 속도가 급격히 떨어지거나 통신이 끊길 수 있다.</p>
<p>5GHz라는 이유만으로 항상 빠른 것은 아니다. 실제 속도는 공유기와의 거리, 장애물, 채널 혼잡도, 채널 폭, 와이파이 규격, 장치 성능에 따라 달라진다.</p>
<h2 id="어떤-주파수를-선택해야-할까">어떤 주파수를 선택해야 할까</h2>
<p>공유기와 가까운 곳에서 빠른 속도가 필요하다면 5GHz가 유리하다. 공유기에서 멀리 떨어져 있거나 중간에 벽이 많다면 2.4GHz가 더 안정적일 수 있다.</p>
<pre><code class="language-text">가까운 거리와 빠른 속도 → 5GHz
먼 거리와 여러 장애물 → 2.4GHz
오래된 기기와 IoT 기기 → 2.4GHz</code></pre>
<p>요즘 공유기는 2.4GHz와 5GHz를 동시에 제공하는 경우가 많다. 하나의 와이파이 이름으로 두 대역을 제공하고, 공유기가 장치의 위치와 신호 상태에 따라 적절한 대역을 선택하기도 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[와이파이는 어떻게 전송 순서를 정할까 
]]></title>
            <link>https://velog.io/@blight_k/%EC%99%80%EC%9D%B4%ED%8C%8C%EC%9D%B4%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%A0%84%EC%86%A1-%EC%88%9C%EC%84%9C%EB%A5%BC-%EC%A0%95%ED%95%A0%EA%B9%8C</link>
            <guid>https://velog.io/@blight_k/%EC%99%80%EC%9D%B4%ED%8C%8C%EC%9D%B4%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%A0%84%EC%86%A1-%EC%88%9C%EC%84%9C%EB%A5%BC-%EC%A0%95%ED%95%A0%EA%B9%8C</guid>
            <pubDate>Sun, 06 Sep 2026 12:00:48 GMT</pubDate>
            <description><![CDATA[<h2 id="반이중-통신과-csmaca">반이중 통신과 CSMA/CA</h2>
<p>반이중 통신은 양쪽 장치가 데이터를 주고받을 수 있지만, 동시에 송신과 수신을 할 수는 없는 방식이다. 무전기로 대화할 때 한 사람이 말하면 다른 사람은 기다려야 하는 것과 같다.</p>
<p>전이중 통신은 양쪽에서 동시에 데이터를 보내고 받을 수 있는 방식이다. 전화로 상대방의 말을 들으면서 나도 말할 수 있는 것과 같다. 다만 전이중 통신이라고 해서 반드시 회선이 하나 더 필요한 것은 아니다. 송수신 경로를 분리하거나, 같은 매체에서 신호를 구분하는 기술로 구현할 수도 있다.</p>
<p>일반적인 와이파이 통신은 같은 무선 채널에서 반이중 방식으로 동작한다. 여러 장치가 채널을 공유하므로, 데이터를 보내기 전에 다른 장치가 사용 중인지 확인해야 한다.</p>
<p>CSMA/CA로 충돌 가능성을 줄인다</p>
<p>CSMA/CA는 채널의 사용 상태를 확인하고, 전송 시점을 분산해 충돌 가능성을 줄이는 방식이다. 여기서 CA는 Collision Avoidance, 즉 충돌 회피를 뜻한다.</p>
<p>회의에서 여러 사람이 동시에 말을 시작하면 서로의 말을 알아듣기 어렵다. 다른 사람이 말하는지 듣고, 말이 끝난 뒤 잠시 기다렸다가 발언하면 겹칠 가능성이 줄어든다. CSMA/CA도 비슷한 방식으로 동작한다.</p>
<p>아래는 기본적인 경쟁 기반 전송에서 특정 수신자에게 데이터 프레임을 보내는 과정이다.</p>
<ol>
<li>현재 채널이 사용 중인지 확인한다</li>
</ol>
<p>장치는 자신이 통신할 채널을 감지한다. 다른 장치가 사용 중이면 전송을 미루고 기다린다.</p>
<p>사용 중인 채널을 발견할 때마다 다른 빈 채널을 찾아 이동하는 것은 아니다. 같은 AP에 연결된 장치들은 해당 연결에서 사용하는 채널의 전송 기회를 놓고 경쟁한다.</p>
<ol start="2">
<li>IFS만큼 기다린다</li>
</ol>
<p>채널이 비었다고 바로 전송하지는 않는다. 일정 시간 동안 계속 비어 있는지 확인한다. 이때 사용하는 프레임 간 대기 간격을 IFS(Interframe Space)라고 한다.</p>
<p>IFS는 도로의 안전거리와 비슷하지만, 프레임의 역할에 따라 먼저 전송할 기회를 주는 기능도 있다.</p>
<p>기본 DCF 방식에서 새로운 데이터 전송을 시작할 때는 DIFS를 사용한다. 수신 확인인 ACK를 보낼 때는 그보다 짧은 SIFS를 사용한다. 덕분에 다른 장치가 새로운 전송을 시작하기 전에 ACK를 먼저 보낼 수 있다. <a href="https://www.gta.ufrj.br/ftp/gta/TechReports/CCD06.pdf">CSMA/CA 동작 설명 논문</a></p>
<ol start="3">
<li>무작위 백오프로 전송 시점을 나눈다</li>
</ol>
<p>모든 장치가 같은 시간만큼 기다린 뒤 동시에 전송하면 다시 충돌할 수 있다. 이를 줄이기 위해 무작위로 추가 대기 시간을 정한다. 이 과정을 백오프라고 한다.</p>
<p>장치는 0부터 CW(Contention Window)까지의 정수 중 하나를 선택한다. 선택한 값은 기다릴 슬롯 수가 된다.</p>
<p>예를 들어 CW가 15이고 노트북이 3, 스마트폰이 7을 선택했다고 가정한다. 채널이 계속 비어 있다면 노트북이 먼저 전송한다. 스마트폰은 3개 슬롯을 기다렸으므로 남은 값은 4가 된다.</p>
<p>이때 스마트폰은 카운트다운을 멈춘다. 노트북의 전송과 ACK 교환이 끝나고 채널이 정해진 시간 동안 다시 비어 있으면, 남은 4개 슬롯부터 이어서 기다린다. 매번 새로운 난수를 뽑는 것은 아니다. <a href="https://www.gta.ufrj.br/ftp/gta/TechReports/CCD06.pdf">백오프 카운터 동작</a></p>
<ol start="4">
<li>ACK로 전송 결과를 확인한다</li>
</ol>
<p>수신 장치가 데이터 프레임을 정상적으로 받으면 ACK 프레임을 보낸다. 여기서 ACK는 TCP의 ACK 세그먼트와 별개인 무선 링크의 확인 응답이다.</p>
<p>송신 장치가 정해진 시간 안에 ACK를 받으면 해당 전송을 성공으로 처리한다. ACK를 받지 못하면 재전송을 시도한다.</p>
<p>다만 ACK가 없다고 해서 데이터가 반드시 도착하지 않았다는 뜻은 아니다. 데이터는 도착했지만 돌아오는 ACK가 손실되었을 수도 있다. 송신 장치에서는 성공을 확인할 수 없으므로 재전송하는 것이다.</p>
<p>실패할수록 대기 범위를 넓힌다</p>
<p>재전송에서도 여러 장치가 비슷한 시점에 전송하면 다시 충돌할 수 있다. 따라서 실패하면 무작위 대기 값을 선택하는 범위를 넓힌다.</p>
<p>일반적인 이진 지수 백오프에서는 CW를 다음과 같이 늘리되, 정해진 최댓값을 넘지 않도록 한다.</p>
<p>CW의 다음 값 = 2 × (현재 CW + 1) − 1</p>
<p>예를 들어 초기 CW가 15인 경우, 실패에 따라 31, 63처럼 커진다. 성공하면 초기 범위로 돌아간다. 범위가 커진다는 것은 평균 대기 시간이 길어진다는 뜻이다. 새로 뽑는 값이 무작위이므로 매번 이전보다 오래 기다리는 것은 아니다.</p>
<p>따라서 재시도 횟수 k를 0부터 세면서 대기 범위를 단순히 0부터 2^k−1까지라고 설명하면 초기 경쟁 윈도가 빠진다. 실제 동작에는 초기 CW와 최대 CW가 있으며, 재전송 횟수에도 제한이 있다. ACK를 받았을 때가 아니라 받지 못해 재시도할 때 대기 범위를 늘린다. <a href="https://www.gta.ufrj.br/ftp/gta/TechReports/CCD06.pdf">경쟁 윈도 증가와 초기화</a></p>
<p>와이파이와 근거리 무선 통신</p>
<p>와이파이는 IEEE 802.11 기반의 대표적인 무선 LAN 기술이다. 노트북이나 스마트폰을 AP에 연결해 같은 네트워크 안에서 데이터를 주고받도록 한다. 인터넷 연결이 없어도 와이파이로 내부 네트워크를 구성할 수 있다.</p>
<p>블루투스와 지그비도 가까운 거리에서 무선으로 통신하지만, 일반적으로 무선 개인 영역 네트워크인 WPAN 계열로 구분한다. 따라서 와이파이를 대표적인 무선 LAN 기술이라고 설명하는 것은 맞다. 다만 근거리 무선 통신 전체를 와이파이라고 부를 수는 없다.</p>
<p>노트북을 사내 네트워크에 연결하는 데는 와이파이를, 무선 이어폰을 휴대폰에 연결하는 데는 블루투스를, 저전력 센서의 측정값을 전달하는 데는 지그비를 사용하는 식이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[물리계층 데이터 전달]]></title>
            <link>https://velog.io/@blight_k/%EB%AC%BC%EB%A6%AC%EA%B3%84%EC%B8%B5-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EB%8B%AC</link>
            <guid>https://velog.io/@blight_k/%EB%AC%BC%EB%A6%AC%EA%B3%84%EC%B8%B5-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EB%8B%AC</guid>
            <pubDate>Fri, 04 Sep 2026 15:59:29 GMT</pubDate>
            <description><![CDATA[<h1 id="물리-계층에서-데이터는-어떻게-전달될까">물리 계층에서 데이터는 어떻게 전달될까</h1>
<p>네트워크의 물리 계층은 데이터를 전기 신호, 빛, 전파로 변환해 실제 전송 매체로 전달하는 계층이다. 컴퓨터가 처리하는 데이터는 0과 1이지만, 케이블 내부에서는 전압의 변화로, 광케이블에서는 빛의 변화로 표현된다.</p>
<h2 id="nic와-mac-주소">NIC와 MAC 주소</h2>
<p>NIC(Network Interface Card)는 컴퓨터를 네트워크에 연결하는 장치다. 흔히 랜카드라고 부른다.</p>
<p>NIC에는 네트워크 장치를 식별하기 위한 MAC 주소가 할당된다. 다만 MAC 주소를 이용해 이더넷 프레임을 처리하는 기능은 물리 계층이 아니라 데이터 링크 계층에 해당한다. 따라서 NIC는 물리 계층과 데이터 링크 계층에 걸쳐 동작한다고 볼 수 있다.</p>
<p>예를 들어 컴퓨터가 데이터를 전송하면 NIC는 이더넷 프레임을 전기 신호로 변환해 랜선으로 내보낸다. 반대로 전기 신호를 받으면 이를 다시 데이터로 변환한다.</p>
<h2 id="리피터와-ap">리피터와 AP</h2>
<p>리피터는 전달 과정에서 약해진 신호를 복원하거나 증폭해 더 먼 곳까지 전달하는 장치다.</p>
<p>와이파이 확장기도 리피터와 비슷한 역할을 한다. 다만 와이파이가 전혀 잡히지 않는 장소에 설치하면 원래 신호를 제대로 받을 수 없다. 공유기의 신호가 어느 정도 도달하는 위치와 음영 지역 사이에 설치해야 한다.</p>
<p>AP(Access Point)는 유선 네트워크와 무선 네트워크를 연결하는 장치다. 단순히 패킷을 복사하는 장치라기보다 무선 단말이 유선 네트워크에 접속할 수 있도록 중계하는 브리지에 가깝다.</p>
<p>가정에서 사용하는 공유기는 보통 다음 기능을 하나의 장치에 포함한다.</p>
<ul>
<li>라우터</li>
<li>스위치</li>
<li>AP</li>
<li>DHCP 서버</li>
<li>NAT</li>
</ul>
<p>별도의 AP에 유선 랜을 연결하면 기존 유선 네트워크를 와이파이로 사용할 수도 있다. 회사나 학교 천장에 설치된 무선 장비가 대표적인 AP다.</p>
<h2 id="전이중-통신과-반이중-통신">전이중 통신과 반이중 통신</h2>
<p>현재의 유선 이더넷은 대부분 전이중 통신을 사용한다. 전이중 통신은 양쪽 장치가 동시에 데이터를 보내고 받을 수 있는 방식이다.</p>
<p>전화 통화를 생각하면 이해하기 쉽다. 두 사람이 동시에 말하고 들을 수 있으므로 전이중 통신에 해당한다.</p>
<p>기가비트 이더넷에서는 여러 가닥의 구리선을 이용해 송신과 수신을 동시에 처리한다. 각 장치는 일반적으로 스위치와 직접 연결되므로 다른 장치와 회선을 공유하지 않는다. 이 때문에 충돌을 검사할 필요도 없다.</p>
<p>반이중 통신은 한쪽이 데이터를 보내는 동안 다른 쪽은 기다려야 하는 방식이다. 무전기가 대표적인 예다. 한 사람이 말하는 동안 다른 사람은 들을 수만 있다.</p>
<p>과거에는 여러 장치가 하나의 회선을 공유하는 허브 환경에서 CSMA/CD 방식을 사용했다. 장치는 데이터를 보내기 전에 회선이 사용 중인지 확인하고, 전송 중 충돌이 발생하면 잠시 기다렸다가 다시 전송했다.</p>
<p>현재의 스위치 기반 전이중 이더넷에서는 충돌이 발생하지 않으므로 CSMA/CD를 사용하지 않는다.</p>
<h2 id="ieee-8023과-이더넷">IEEE 802.3과 이더넷</h2>
<p>IEEE 802.3은 유선 이더넷의 표준이다. 이 표준에는 다음과 같은 내용이 정의되어 있다.</p>
<ul>
<li>이더넷 프레임의 구조</li>
<li>데이터 전송 속도</li>
<li>사용할 수 있는 케이블과 최대 거리</li>
<li>신호를 전송하는 방법</li>
<li>매체 접근 방식</li>
</ul>
<p>예를 들어 1000BASE-T는 일반적인 랜선을 이용하는 1Gbps 이더넷 규격이다. <code>1000</code>은 전송 속도, <code>BASE</code>는 베이스밴드 방식, <code>T</code>는 트위스티드 페어 케이블을 의미한다.</p>
<h2 id="트위스티드-페어-케이블">트위스티드 페어 케이블</h2>
<p>일반적인 랜선 내부에는 두 가닥의 구리선이 한 쌍으로 꼬여 있다. 이를 트위스티드 페어 케이블이라고 한다. 선을 꼬는 이유는 외부 전자기파로 인한 신호 간섭을 줄이기 위해서다.</p>
<p>트위스티드 페어 케이블은 차폐 여부에 따라 구분할 수 있다.</p>
<ul>
<li>UTP는 별도의 차폐 처리가 없는 케이블이다.</li>
<li>STP는 금속 재질로 차폐 처리된 케이블이다.</li>
</ul>
<p>가정과 사무실에서는 가격이 저렴하고 설치가 쉬운 UTP 케이블을 주로 사용한다. 공장처럼 전자기 간섭이 심한 환경에서는 STP 케이블이 유리하다.</p>
<p>랜선 끝에서 흔히 볼 수 있는 커넥터는 일반적으로 RJ45라고 부른다. 정확한 형태는 8개의 접점을 가진 8P8C 커넥터지만, 실무에서는 대부분 RJ45라는 이름을 사용한다.</p>
<h2 id="광섬유-케이블">광섬유 케이블</h2>
<p>광섬유 케이블은 전기 신호 대신 빛을 이용해 데이터를 전송한다. 내부의 코어와 클래딩은 굴절률이 서로 다르다. 이 차이로 인해 빛이 광섬유 내부에서 전반사하며 먼 거리까지 전달된다.</p>
<p>광섬유는 구리선보다 먼 거리까지 빠르게 데이터를 전송할 수 있고 전자기 간섭의 영향도 거의 받지 않는다. 데이터센터나 통신사 네트워크에서 10Gbps, 100Gbps 이상의 연결에 주로 사용한다.</p>
<p>광섬유는 크게 싱글모드와 멀티모드로 구분한다. 싱글모드는 장거리 통신에 적합하고, 멀티모드는 비교적 짧은 거리의 건물 내부나 데이터센터 연결에 많이 사용한다.</p>
<p>결국 물리 계층의 역할은 데이터를 사람이 볼 수 없는 신호로 바꾸어 실제 매체로 전달하는 것이다. 그 위에서 이더넷과 MAC 주소가 장치를 구분하고, 스위치와 AP가 데이터가 이동할 경로를 이어준다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTTPS는 어떻게 통신을 암호화할까]]></title>
            <link>https://velog.io/@blight_k/HTTPS%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%86%B5%EC%8B%A0%EC%9D%84-%EC%95%94%ED%98%B8%ED%99%94%ED%95%A0%EA%B9%8C</link>
            <guid>https://velog.io/@blight_k/HTTPS%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%86%B5%EC%8B%A0%EC%9D%84-%EC%95%94%ED%98%B8%ED%99%94%ED%95%A0%EA%B9%8C</guid>
            <pubDate>Thu, 03 Sep 2026 10:02:57 GMT</pubDate>
            <description><![CDATA[<p>웹사이트에 접속하다 보면 주소 앞에 <code>https://</code>가 붙어 있는 것을 볼 수 있다.</p>
<p>HTTPS를 사용한다는 것은 브라우저와 서버 사이에서 주고받는 데이터를 TLS를 이용해 보호한다는 의미다.</p>
<p>예를 들어 내가 서버에 다음과 같은 데이터를 보낸다고 해보자.</p>
<pre><code class="language-text">password=hello1234</code></pre>
<p>이 데이터가 평문 그대로 네트워크를 지나간다면 중간에서 패킷을 가로챈 사람이 내용을 그대로 확인할 수 있다.</p>
<p>HTTPS에서는 이런 데이터를 암호화해서 전송한다.</p>
<pre><code class="language-text">password=hello1234

↓ 암호화

8a71c09f3e4d....</code></pre>
<p>공격자가 패킷을 가로채더라도 암호문만 보이기 때문에 원래 내용을 쉽게 알아낼 수 없다.</p>
<p>그런데 여기서 한 가지 궁금한 점이 생긴다.</p>
<p><strong>브라우저와 서버는 어떤 키로 데이터를 암호화하는 것일까?</strong></p>
<p>이걸 이해하려면 먼저 대칭 암호화와 비대칭 암호화의 차이를 알아야 한다.</p>
<h2 id="대칭-암호화">대칭 암호화</h2>
<p>대칭 암호화는 암호화와 복호화에 같은 키를 사용하는 방식이다.</p>
<p>예를 들어 <code>orange</code>라는 비밀키가 있다고 생각해보자.</p>
<pre><code class="language-text">Hello

↓ orange라는 키로 암호화

암호문

↓ orange라는 키로 복호화

Hello</code></pre>
<p>암호화할 때 사용한 키와 복호화할 때 사용하는 키가 동일하기 때문에 대칭 암호화라고 부른다.</p>
<p>대표적인 알고리즘으로 AES가 있다.</p>
<p>과거에는 DES도 사용되었지만 현재는 보안상의 이유로 거의 사용하지 않는다.</p>
<p>대칭 암호화의 가장 큰 장점은 빠르다는 것이다.</p>
<p>HTTPS처럼 많은 데이터를 계속 주고받아야 하는 환경에서는 실제 데이터 암호화에 대칭 암호화를 사용한다.</p>
<h2 id="aes는-어떻게-데이터를-암호화할까">AES는 어떻게 데이터를 암호화할까</h2>
<p>AES는 현재 가장 널리 사용되는 대칭키 암호화 알고리즘 중 하나다.</p>
<p>예를 들어 AES-128은 128비트 키를 사용하며 총 10개의 라운드를 거쳐 데이터를 변환한다.</p>
<p>AES를 단순히 &quot;문자를 뒤섞는 알고리즘&quot;이라고 생각하기 쉽지만 실제로는 조금 더 체계적이다.</p>
<p>각 라운드에서는 대략 다음과 같은 연산이 수행된다.</p>
<pre><code class="language-text">SubBytes
ShiftRows
MixColumns
AddRoundKey</code></pre>
<p>각 바이트 값을 다른 값으로 치환하고, 위치를 이동시키고, 서로 섞은 뒤 마지막으로 암호화 키에서 만들어진 라운드 키를 XOR 연산으로 적용한다.</p>
<p>이런 연산을 여러 번 반복하면서 원래 데이터와 전혀 다른 형태의 암호문을 만든다.</p>
<p>AES-128은 10라운드, AES-192는 12라운드, AES-256은 14라운드를 사용한다.</p>
<p>HTTPS에서도 AES-128-GCM 같은 형태가 많이 사용된다.</p>
<p>중요한 것은 AES가 매우 빠르다는 점이다.</p>
<p>그래서 실제 HTTP 요청이나 응답 데이터를 암호화할 때는 이런 대칭 암호화 알고리즘을 사용한다.</p>
<p>그런데 문제가 하나 있다.</p>
<p>브라우저와 서버가 AES를 사용하려면 둘 다 같은 비밀키를 가지고 있어야 한다.</p>
<p>그렇다면 처음 만난 브라우저와 서버는 이 비밀키를 어떻게 안전하게 만들어낼까?</p>
<p>여기서 비대칭 암호 방식과 키 교환 알고리즘이 등장한다.</p>
<h2 id="비대칭-암호화">비대칭 암호화</h2>
<p>비대칭 암호 방식에서는 서로 다른 두 개의 키를 사용한다.</p>
<pre><code class="language-text">공개키
개인키</code></pre>
<p>이름 그대로 공개키는 다른 사람에게 공개해도 되지만 개인키는 소유자만 가지고 있어야 한다.</p>
<p>대표적인 알고리즘으로 RSA가 있다.</p>
<p>RSA에서는 공개키로 암호화한 데이터를 대응되는 개인키로 복호화할 수 있다.</p>
<pre><code class="language-text">데이터

↓ 서버 공개키

암호문

↓ 서버 개인키

원래 데이터</code></pre>
<p>과거 TLS에서는 이 성질을 이용해 세션키를 전달하기도 했다.</p>
<p>하지만 현재 TLS에서는 RSA를 이런 식의 키 교환 용도로 사용하지 않는다.</p>
<p>그 대신 Diffie-Hellman 계열의 키 교환 방식이 사용된다.</p>
<h2 id="diffie-hellman-키-교환">Diffie-Hellman 키 교환</h2>
<p>Diffie-Hellman의 재미있는 점은 서로 비밀키를 직접 전달하지 않고도 같은 비밀값을 만들 수 있다는 것이다.</p>
<p>두 사람이 있다고 해보자.</p>
<pre><code class="language-text">Client
Server</code></pre>
<p>둘은 먼저 공개된 값 <code>g</code>와 <code>p</code>를 사용한다.</p>
<pre><code class="language-text">g = 5
p = 23</code></pre>
<p>이 값들은 공격자가 알아도 상관없다.</p>
<p>Client는 자신만 알고 있는 비밀값 <code>a</code>를 하나 선택한다.</p>
<pre><code class="language-text">a = 6</code></pre>
<p>그리고 다음 값을 계산한다.</p>
<pre><code class="language-text">A = g^a mod p

A = 5^6 mod 23
A = 8</code></pre>
<p>Server 역시 자신만의 비밀값 <code>b</code>를 선택한다.</p>
<pre><code class="language-text">b = 15</code></pre>
<p>그리고 같은 방식으로 계산한다.</p>
<pre><code class="language-text">B = g^b mod p

B = 5^15 mod 23
B = 19</code></pre>
<p>이제 Client와 Server는 <code>A</code>와 <code>B</code>를 서로 교환한다.</p>
<pre><code class="language-text">Client → 8 → Server
Client ← 19 ← Server</code></pre>
<p>중간에서 누군가 패킷을 보고 있다고 해도 <code>g</code>, <code>p</code>, <code>A</code>, <code>B</code>는 모두 볼 수 있다.</p>
<p>하지만 각자가 가지고 있는 <code>a</code>와 <code>b</code>는 공개되지 않는다.</p>
<p>Client는 Server가 보내준 B를 이용해 다음 값을 계산한다.</p>
<pre><code class="language-text">B^a mod p

19^6 mod 23

= 2</code></pre>
<p>Server도 Client가 보내준 A를 이용한다.</p>
<pre><code class="language-text">A^b mod p

8^15 mod 23

= 2</code></pre>
<p>둘 다 결과가 같다.</p>
<p>그 이유는 결국 두 계산 모두 다음 값이 되기 때문이다.</p>
<pre><code class="language-text">g^(ab) mod p</code></pre>
<p>결과적으로 Client와 Server는 비밀값을 네트워크로 직접 전송하지 않고도 같은 공유 비밀을 만들어낼 수 있다.</p>
<p>실제 TLS에서는 이 값을 그대로 AES 키로 사용하는 것이 아니라, 이 공유 비밀과 핸드셰이크 과정의 여러 값을 KDF에 넣어 여러 개의 암호화 키를 만들어낸다.</p>
<p>흔히 이 키들을 세션키라고 부른다.</p>
<h2 id="그런데-공개된-값으로-비밀값을-알아낼-수-있지-않을까">그런데 공개된 값으로 비밀값을 알아낼 수 있지 않을까</h2>
<p>공격자가 다음 값들을 알고 있다고 생각해보자.</p>
<pre><code class="language-text">g = 5
p = 23
A = 8</code></pre>
<p>그러면 결국 다음 식에서 <code>a</code>를 알아내면 된다.</p>
<pre><code class="language-text">5^a mod 23 = 8</code></pre>
<p>숫자가 이렇게 작다면 1부터 하나씩 넣어보는 브루트포스로 금방 알아낼 수 있다.</p>
<p>하지만 실제 암호화에서는 비교할 수 없을 정도로 큰 숫자를 사용한다.</p>
<p>이때 <code>g^a mod p</code>를 계산하는 것은 쉽지만 결과만 가지고 다시 <code>a</code>를 구하는 것은 매우 어렵다.</p>
<p>이를 이산 로그 문제라고 한다.</p>
<p>Diffie-Hellman은 이 문제의 계산 난이도를 이용한다.</p>
<h2 id="ecdhe는-무엇인가">ECDHE는 무엇인가</h2>
<p>현대 TLS에서는 일반적인 Diffie-Hellman보다 ECDHE를 많이 사용한다.</p>
<p>ECDHE는 다음의 약자다.</p>
<pre><code class="language-text">Elliptic Curve Diffie-Hellman Ephemeral</code></pre>
<p>타원곡선 위에서 Diffie-Hellman과 비슷한 방식으로 공유 비밀을 만든다.</p>
<p>기본적인 아이디어는 동일하다.</p>
<pre><code class="language-text">Client 개인값
        ↓
Client 공개값 ──────────────┐
                            │
                            ↓
                       공유 비밀
                            ↑
                            │
Server 공개값 ──────────────┘
        ↑
Server 개인값</code></pre>
<p>다만 일반적인 정수 모듈러 연산 대신 타원곡선상의 수학 문제를 이용한다.</p>
<p>이를 타원곡선 이산 로그 문제라고 한다.</p>
<p>같은 수준의 보안을 제공하면서 상대적으로 작은 키를 사용할 수 있기 때문에 현대 암호 시스템에서 널리 사용된다.</p>
<p>마지막에 붙어 있는 <code>E</code>, 즉 Ephemeral도 중요하다.</p>
<p>연결할 때마다 임시 키를 새로 만들어 사용한다는 의미다.</p>
<p>덕분에 나중에 서버의 장기 개인키가 유출되더라도 과거에 캡처해둔 TLS 통신까지 한꺼번에 복호화되는 것을 막을 수 있다.</p>
<p>이 특성을 Forward Secrecy라고 한다.</p>
<h2 id="예전-tls의-rsa-방식과-무엇이-다른가">예전 TLS의 RSA 방식과 무엇이 다른가</h2>
<p>과거에는 RSA를 이용한 TLS 키 교환도 사용되었다.</p>
<p>구조를 단순하게 표현하면 다음과 같다.</p>
<pre><code class="language-text">Server
  ↓
RSA 공개키 전달
  ↓
Client

Client
  ↓
세션키 생성
  ↓
서버 RSA 공개키로 암호화
  ↓
Server

Server
  ↓
RSA 개인키로 복호화
  ↓
세션키 획득</code></pre>
<p>여기서는 Client가 만든 비밀값 자체가 네트워크를 통해 전달된다.</p>
<p>물론 RSA 공개키로 암호화되어 있기 때문에 바로 읽을 수 있는 것은 아니다.</p>
<p>하지만 공격자가 과거 TLS 패킷을 모두 저장해두었다가 나중에 서버 RSA 개인키를 탈취한다면 과거에 전송된 세션키를 복호화할 가능성이 생긴다.</p>
<p>ECDHE는 구조가 다르다.</p>
<pre><code class="language-text">Client                          Server

개인값 a                         개인값 b
   │                               │
   ↓                               ↓
공개값 A                         공개값 B

       A ────────────────────→
       ←──────────────────── B

Client에서 공유 비밀 계산      Server에서 공유 비밀 계산</code></pre>
<p>실제 공유 비밀 자체가 네트워크를 통해 이동하지 않는다.</p>
<p>Client와 Server가 각자 계산해서 같은 값을 만들어낸다.</p>
<p>이 때문에 현대 TLS에서 ECDHE가 중요하다.</p>
<p>TLS 1.3에서는 과거의 RSA key exchange 방식이 제거되었으며, 일반적인 HTTPS 연결에서는 ECDHE를 이용한 키 교환을 흔히 볼 수 있다.</p>
<h2 id="tls-핸드셰이크">TLS 핸드셰이크</h2>
<p>이제 이 내용을 실제 HTTPS 연결에 대입해볼 수 있다.</p>
<p>브라우저에서 HTTPS 서버에 접속하면 HTTP 데이터를 보내기 전에 먼저 TLS 핸드셰이크가 진행된다.</p>
<p>TLS 1.3을 단순화하면 다음과 같은 흐름이다.</p>
<pre><code class="language-text">Client                              Server

ClientHello
 ─────────────────────────────────→

                              ServerHello
                              EncryptedExtensions
                              Certificate
                              CertificateVerify
                              Finished
 ←─────────────────────────────────

Finished
 ─────────────────────────────────→

========== 암호화된 HTTP 통신 ==========</code></pre>
<p>먼저 Client가 <code>ClientHello</code>를 전송한다.</p>
<p>여기에는 TLS 협상에 필요한 여러 정보가 들어간다.</p>
<p>예를 들면 다음과 같다.</p>
<pre><code class="language-text">지원하는 TLS 버전
지원하는 Cipher Suite
Client Random
지원하는 암호 그룹
ECDHE Key Share</code></pre>
<p>Server는 ClientHello를 확인하고 자신이 사용할 TLS 설정을 결정한다.</p>
<p>그리고 <code>ServerHello</code>를 전송한다.</p>
<pre><code class="language-text">선택된 TLS 버전
선택된 Cipher Suite
Server Random
Server ECDHE Key Share</code></pre>
<p>Client와 Server는 서로 전달한 ECDHE 공개값과 자신이 가지고 있는 개인값을 이용해 같은 공유 비밀을 계산한다.</p>
<p>그 공유 비밀을 기반으로 TLS의 키 파생 함수를 사용해 실제 통신에 필요한 여러 키를 만든다.</p>
<p>그 이후부터는 핸드셰이크 메시지의 상당 부분도 암호화된다.</p>
<h2 id="서버-인증서는-왜-필요한가">서버 인증서는 왜 필요한가</h2>
<p>여기까지 보면 한 가지 문제가 남는다.</p>
<p>ECDHE를 사용하면 Client와 Server가 공유 비밀을 안전하게 만들 수 있다.</p>
<p>하지만 Client 입장에서 지금 통신하고 있는 서버가 정말 내가 접속하려던 서버인지 어떻게 확인할까?</p>
<p>공격자가 중간에서 자신을 서버라고 속일 수도 있다.</p>
<p>그래서 TLS에서는 인증서를 사용한다.</p>
<p>Server는 핸드셰이크 과정에서 자신의 인증서를 Client에게 전달한다.</p>
<pre><code class="language-text">Certificate</code></pre>
<p>브라우저는 인증서를 확인한다.</p>
<p>대표적으로 다음과 같은 내용을 검증한다.</p>
<pre><code class="language-text">신뢰할 수 있는 인증기관에서 발급했는가

현재 접속한 도메인과 인증서의 도메인이 일치하는가

인증서가 유효기간 안에 있는가

인증서 체인이 정상적인가</code></pre>
<p>그리고 서버는 <code>CertificateVerify</code> 메시지를 통해 인증서에 대응되는 개인키를 실제로 가지고 있다는 사실도 증명한다.</p>
<p>결국 TLS에서 인증서는 단순히 데이터를 암호화하기 위한 것이 아니라 상대방의 신원을 확인하는 데 중요한 역할을 한다.</p>
<h2 id="해시-함수">해시 함수</h2>
<p>TLS를 공부하다 보면 SHA-256 같은 이름도 자주 등장한다.</p>
<p>해시 함수는 임의 길이의 데이터를 고정 길이 값으로 변환한다.</p>
<p>예를 들어 다음 데이터가 있다고 해보자.</p>
<pre><code class="language-text">Hello TLS</code></pre>
<p>SHA-256을 적용하면 항상 256비트의 해시 결과가 나온다.</p>
<pre><code class="language-text">Hello TLS
     ↓
SHA-256
     ↓
고정 길이의 해시값</code></pre>
<p>입력 데이터가 조금만 달라져도 결과는 크게 달라진다.</p>
<pre><code class="language-text">Hello TLS
Hello TLS!</code></pre>
<p>두 데이터의 해시는 전혀 다른 값이 나온다.</p>
<p>그리고 일반적인 암호학적 해시 함수는 해시값으로부터 원래 데이터를 되돌리는 것이 현실적으로 매우 어렵게 설계되어 있다.</p>
<p>TLS에서는 이런 해시 함수가 여러 곳에서 사용된다.</p>
<p>예를 들어 핸드셰이크 메시지들의 내용을 해싱해서 서로 같은 핸드셰이크를 보고 있는지 검증하거나, 키를 파생하거나, Finished 메시지를 계산하는 데 사용된다.</p>
<p>전자서명 과정에서도 해시가 중요한 역할을 한다.</p>
<h2 id="cipher-suite는-무엇인가">Cipher Suite는 무엇인가</h2>
<p>TLS 패킷을 Wireshark로 보면 다음과 같은 이름을 볼 수 있다.</p>
<pre><code class="language-text">TLS_AES_128_GCM_SHA256</code></pre>
<p>이것을 Cipher Suite, 우리말로 암호 제품군이라고 부른다.</p>
<p>TLS 1.3의 Cipher Suite는 크게 다음과 같이 읽을 수 있다.</p>
<pre><code class="language-text">TLS_AES_128_GCM_SHA256
    └──────┬──────┘ └──┬──┘
           │            │
       데이터 암호화     해시 함수</code></pre>
<p><code>AES_128_GCM</code>은 실제 데이터를 암호화하는 방법을 의미한다.</p>
<p>AES는 암호화 알고리즘이고, GCM은 AES를 안전하게 사용하는 방식 중 하나다.</p>
<p>GCM은 단순히 데이터를 암호화하는 것뿐만 아니라 데이터가 중간에서 변조되지 않았는지도 확인할 수 있다.</p>
<p>이런 방식을 AEAD라고 한다.</p>
<pre><code class="language-text">Authenticated Encryption with Associated Data</code></pre>
<p><code>SHA256</code>은 TLS 핸드셰이크와 키 파생 등에 사용하는 해시 알고리즘을 나타낸다.</p>
<p>여기서 주의할 점이 하나 있다.</p>
<p>TLS 1.2까지는 Cipher Suite 이름 안에 RSA나 ECDHE 같은 키 교환 방식까지 포함되는 경우가 많았다.</p>
<p>예를 들면 다음과 같다.</p>
<pre><code class="language-text">TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256</code></pre>
<p>하지만 TLS 1.3에서는 Cipher Suite와 키 교환 및 전자서명 알고리즘을 분리했다.</p>
<p>그래서</p>
<pre><code class="language-text">TLS_AES_128_GCM_SHA256</code></pre>
<p>만 보고 ECDHE인지 RSA인지 판단하는 구조가 아니다.</p>
<p>이 부분이 TLS 1.2와 TLS 1.3을 공부할 때 가장 헷갈리기 쉬운 부분 중 하나다.</p>
<h2 id="결국-https에서-무엇이-사용되는가">결국 HTTPS에서 무엇이 사용되는가</h2>
<p>HTTPS에서는 하나의 암호 알고리즘만 사용하는 것이 아니다.</p>
<p>각 기술이 서로 다른 역할을 맡고 있다.</p>
<pre><code class="language-text">인증서
    ↓
서버가 진짜인지 확인

ECDHE
    ↓
공유 비밀 생성

HKDF
    ↓
통신에 사용할 여러 키 생성

AES-GCM
    ↓
실제 HTTP 데이터 암호화

SHA-256
    ↓
핸드셰이크 해시 및 키 파생 등에 사용</code></pre>
<p>왜 이렇게 복잡하게 구성되어 있을까.</p>
<p>각 기술마다 잘하는 것이 다르기 때문이다.</p>
<p>비대칭 암호와 공개키 기반 기술은 상대방을 인증하거나 안전하게 키를 합의하는 데 유용하지만 많은 데이터를 암호화하기에는 상대적으로 느리다.</p>
<p>AES 같은 대칭 암호는 매우 빠르지만 Client와 Server가 같은 키를 가지고 있어야 한다.</p>
<p>그래서 TLS는 여러 암호 기술을 조합한다.</p>
<pre><code class="language-text">처음 연결할 때

인증서 + 공개키 기술 + ECDHE
        ↓
안전하게 공유 비밀 생성

이후 실제 데이터 통신

AES 같은 대칭 암호
        ↓
빠르게 암호화</code></pre>
<p>이것이 HTTPS 통신의 핵심 구조다.</p>
<h2 id="wireshark로-직접-확인해보기">Wireshark로 직접 확인해보기</h2>
<p>TLS는 이론만 공부하는 것보다 실제 패킷을 보면 훨씬 이해하기 쉽다.</p>
<p>Wireshark로 HTTPS 연결을 캡처하면 다음과 같은 메시지를 확인할 수 있다.</p>
<pre><code class="language-text">Client Hello
Server Hello
Application Data</code></pre>
<p>Client Hello를 열어보면 브라우저가 지원하는 TLS 버전과 Cipher Suite, Key Share 같은 값을 확인할 수 있다.</p>
<p>Server Hello에서는 서버가 실제로 선택한 TLS 버전과 Cipher Suite를 볼 수 있다.</p>
<p>TLS 1.3에서는 ServerHello 이후의 많은 핸드셰이크 메시지가 이미 암호화되기 때문에 예전 TLS 버전처럼 모든 내용을 그대로 볼 수 있는 것은 아니다.</p>
<p>그래도 Wireshark를 통해</p>
<pre><code class="language-text">브라우저가 무엇을 제안했는지
서버가 무엇을 선택했는지
어떤 Cipher Suite가 사용되었는지
어떤 Key Share가 전달되었는지</code></pre>
<p>정도는 직접 확인할 수 있다.</p>
<p>HTTPS를 단순히 &quot;웹 통신을 암호화한다&quot; 정도로만 알고 있을 때는 복잡해 보이지만 구조를 나눠보면 생각보다 명확하다.</p>
<p>서버가 진짜인지 인증하고, 서로 직접 전달하지 않고 공유 비밀을 만들고, 그 비밀에서 세션에 사용할 키를 만든 뒤, 빠른 대칭 암호를 사용해 실제 데이터를 주고받는다.</p>
<p>결국 HTTPS의 핵심은 하나의 강력한 암호 알고리즘을 사용하는 것이 아니다.</p>
<p><strong>서로 다른 역할을 가진 여러 암호 기술을 적절하게 조합하는 것</strong>에 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[로그인을 구현하는 방법 ]]></title>
            <link>https://velog.io/@blight_k/%EB%A1%9C%EA%B7%B8%EC%9D%B8%EC%9D%84-%EA%B5%AC%ED%98%84%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@blight_k/%EB%A1%9C%EA%B7%B8%EC%9D%B8%EC%9D%84-%EA%B5%AC%ED%98%84%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Mon, 31 Aug 2026 13:56:43 GMT</pubDate>
            <description><![CDATA[<p>로그인을 공부할 때 가장 먼저 헷갈렸던 것은 세션과 쿠키의 역할이었다. 둘은 함께 사용되는 경우가 많지만 같은 것은 아니다.</p>
<h2 id="http는-사용자를-기억하지-않는다">HTTP는 사용자를 기억하지 않는다</h2>
<p>HTTP는 기본적으로 상태가 없는 프로토콜이다. 이전 요청을 보낸 사용자와 다음 요청을 보낸 사용자가 같은 사람인지 HTTP 자체만으로는 알 수 없다.</p>
<pre><code class="language-text">첫 번째 요청: 로그인했어요.
두 번째 요청: 상품 목록을 보여주세요.</code></pre>
<p>두 요청이 같은 브라우저에서 전송됐더라도 서버가 이를 연결할 별도의 장치가 필요하다. 이때 사용하는 방법 중 하나가 세션이다.</p>
<p>세션은 서버와 클라이언트의 네트워크 연결이 계속 유지되는 상태가 아니다. 서로 독립적인 HTTP 요청들을 같은 사용자에게서 온 요청으로 묶어 주는 논리적인 상태에 가깝다.</p>
<h2 id="세션-id란">세션 ID란?</h2>
<p>세션 ID는 특정 세션을 구분하기 위해 서버가 생성한 임의의 문자열이다.</p>
<pre><code class="language-text">세션 ID: q8fN2mK4xP...</code></pre>
<p>브라우저에는 보통 세션 ID만 쿠키로 저장한다. 사용자 ID나 권한 같은 실제 로그인 정보는 서버의 메모리, 데이터베이스 또는 Redis 같은 별도의 세션 저장소에서 관리한다.</p>
<pre><code class="language-text">브라우저
sessionId=q8fN2mK4xP...

서버의 세션 저장소
q8fN2mK4xP... → userId: 15, role: USER, expiresAt: ...</code></pre>
<p>따라서 세션 ID만 보고 사용자 정보를 직접 유추할 수 없어야 하며, 충분히 길고 예측하기 어려운 값으로 생성해야 한다.</p>
<h2 id="세션-기반-로그인-과정">세션 기반 로그인 과정</h2>
<p>사용자가 아이디와 비밀번호를 입력하면 다음 순서로 로그인이 진행된다.</p>
<pre><code class="language-text">1. 브라우저가 POST /login 요청을 보낸다.
2. 서버가 아이디와 비밀번호를 확인한다.
3. 인증에 성공하면 새로운 세션 ID를 생성한다.
4. 서버는 세션 ID와 사용자 정보를 세션 저장소에 연결한다.
5. Set-Cookie 응답 헤더로 세션 ID를 브라우저에 전달한다.
6. 브라우저는 이후 요청마다 세션 쿠키를 자동으로 전송한다.
7. 서버는 세션 저장소를 조회해 로그인 상태와 사용자를 확인한다.</code></pre>
<p>서버의 응답은 다음과 같은 형태다.</p>
<pre><code class="language-http">Set-Cookie: sessionId=q8fN2mK4xP; HttpOnly; Secure; SameSite=Lax; Path=/</code></pre>
<p>이후 브라우저는 조건에 맞는 요청마다 쿠키를 자동으로 포함한다.</p>
<pre><code class="language-http">Cookie: sessionId=q8fN2mK4xP</code></pre>
<p>서버는 전달받은 세션 ID로 세션 저장소를 조회한다.</p>
<pre><code class="language-text">세션이 존재하고 만료되지 않음
→ 로그인 사용자로 처리

세션이 없거나 만료됨
→ 로그인하지 않은 사용자로 처리</code></pre>
<p>이 과정 덕분에 사용자는 페이지를 이동하거나 API를 여러 번 호출해도 로그인 상태를 유지할 수 있다.</p>
<h2 id="세션-기반-인증의-장점">세션 기반 인증의 장점</h2>
<p>세션 기반 인증에서는 실제 사용자 정보와 권한을 서버가 관리한다. 사용자를 강제로 로그아웃시키거나 권한을 변경해야 할 때 서버의 세션을 삭제하거나 수정하면 바로 반영할 수 있다.</p>
<p>브라우저에는 의미 없는 세션 ID만 저장하므로 사용자 정보를 쿠키에 직접 넣는 방식보다 관리하기도 쉽다.</p>
<h2 id="세션-기반-인증의-단점">세션 기반 인증의 단점</h2>
<p>세션 방식은 서버가 로그인 상태를 저장해야 한다.</p>
<p>사용자가 많아지면 세션 데이터도 함께 증가한다. 세션을 애플리케이션 서버의 메모리에만 저장하면 서버 재시작 시 데이터가 사라질 수 있고, 서버를 여러 대 사용할 때 다른 서버에서는 해당 세션을 찾지 못하는 문제도 생긴다.</p>
<p>그래서 규모가 커지면 Redis나 데이터베이스처럼 여러 서버가 함께 접근할 수 있는 세션 저장소를 사용한다. 다만 이 경우 요청마다 저장소를 조회해야 하므로 네트워크 통신과 읽기·쓰기 비용이 추가된다.</p>
<p>세션 객체를 JSON으로 변환해 MySQL의 <code>VARCHAR</code>나 <code>TEXT</code> 등에 저장하는 구현이라면 직렬화와 역직렬화 비용도 발생한다.</p>
<pre><code class="language-text">세션 객체
→ JSON 문자열로 직렬화
→ 데이터베이스 저장
→ 데이터베이스 조회
→ 다시 객체로 역직렬화</code></pre>
<p>세션에는 꼭 필요한 정보만 저장하고, 만료된 세션을 주기적으로 삭제하는 것이 좋다.</p>
<h2 id="세션-쿠키의-보안-설정">세션 쿠키의 보안 설정</h2>
<p>세션 ID가 탈취되면 공격자가 해당 사용자로 로그인할 수 있으므로 일반 쿠키보다 더 주의해서 관리해야 한다.</p>
<ul>
<li>로그인 성공 시 기존 세션 ID를 그대로 사용하지 않고 새로 발급한다.</li>
<li>JavaScript에서 읽을 필요가 없다면 <code>HttpOnly</code>를 설정한다.</li>
<li>운영 환경에서는 HTTPS와 <code>Secure</code>를 사용한다.</li>
<li><code>SameSite=Lax</code> 또는 <code>Strict</code>를 적용해 불필요한 교차 사이트 전송을 제한한다.</li>
<li>유휴 시간과 최대 유지 시간을 정하고 서버에서도 만료를 검사한다.</li>
<li>로그아웃하면 브라우저 쿠키뿐 아니라 서버의 세션도 삭제한다.</li>
<li>비밀번호나 개인정보를 세션 ID 자체에 포함하지 않는다.</li>
</ul>
<p>특히 로그인 성공 시 세션 ID를 새로 발급하는 과정은 세션 고정 공격을 막기 위해 중요하다.</p>
<h2 id="정리">정리</h2>
<p>세션 기반 인증은 서버가 로그인 상태를 직접 관리하는 방식이다.</p>
<pre><code class="language-text">브라우저는 세션 ID를 보관하고
서버는 세션 ID에 연결된 사용자 정보를 보관한다.</code></pre>
<p>브라우저가 요청마다 세션 쿠키를 보내면 서버가 이를 조회해 사용자를 확인한다. 구현 방식이 직관적이고 서버에서 세션을 통제하기 쉽지만, 사용자가 많아질수록 저장 공간과 세션 저장소 조회 비용이 증가한다.</p>
<p>참고 자료: <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Overview">MDN HTTP 개요</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/Security/Authentication/Session_management">MDN 세션 관리</a>, <a href="https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html">OWASP 세션 관리 가이드</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[쿠키 보안 설정과 LocalStorage·SessionStorage 차이]]></title>
            <link>https://velog.io/@blight_k/%EC%BF%A0%ED%82%A4-%EB%B3%B4%EC%95%88-%EC%84%A4%EC%A0%95%EA%B3%BC-LocalStorageSessionStorage-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@blight_k/%EC%BF%A0%ED%82%A4-%EB%B3%B4%EC%95%88-%EC%84%A4%EC%A0%95%EA%B3%BC-LocalStorageSessionStorage-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Mon, 31 Aug 2026 12:23:41 GMT</pubDate>
            <description><![CDATA[<p>Node.js로 간단한 HTTP 서버를 만들면서 응답 헤더에 쿠키를 설정해 봤다.</p>
<pre><code class="language-javascript">const http = require(&quot;http&quot;);

const hostname = &quot;127.0.0.1&quot;;
const port = 3000;

const server = http.createServer((req, res) =&gt; {
  res.setHeader(&quot;Content-Type&quot;, &quot;text/plain; charset=utf-8&quot;);

  res.setHeader(&quot;Set-Cookie&quot;, [
    &quot;kundol=amumu; HttpOnly&quot;,
    &quot;loltier=master; Secure&quot;
  ]);

  res.end(&quot;응답!\n&quot;);
});

server.listen(port, hostname, () =&gt; {
  console.log(`Server running at http://${hostname}:${port}/`);
});</code></pre>
<p>브라우저에 쿠키를 저장할 때는 <code>Set-Cookie</code> 응답 헤더를 사용한다. 뒤에 붙는 <code>HttpOnly</code>, <code>Secure</code> 같은 속성은 쿠키가 사용되는 범위를 제한한다.</p>
<h2 id="httponly와-secure의-차이">HttpOnly와 Secure의 차이</h2>
<p><code>HttpOnly</code>가 설정된 쿠키는 브라우저의 <code>document.cookie</code>로 읽을 수 없다. XSS 공격으로 세션 쿠키가 탈취될 가능성을 줄이는 데 도움이 된다.</p>
<pre><code class="language-http">Set-Cookie: sessionId=abc123; HttpOnly</code></pre>
<p>다만 JavaScript에서 값을 직접 읽을 수 없을 뿐, <code>fetch()</code>처럼 JavaScript가 실행한 HTTP 요청에는 조건이 맞으면 쿠키가 자동으로 포함된다.</p>
<p><code>Secure</code>는 쿠키를 HTTPS 연결에서만 전송하도록 제한하는 속성이다.</p>
<pre><code class="language-http">Set-Cookie: sessionId=abc123; Secure</code></pre>
<p><code>Secure</code>만 붙였다고 JavaScript 접근까지 막아지는 것은 아니다. 세션 쿠키라면 일반적으로 두 속성을 함께 사용한다.</p>
<pre><code class="language-http">Set-Cookie: sessionId=abc123; HttpOnly; Secure</code></pre>
<p>로컬 개발 환경에서는 브라우저가 <code>localhost</code>나 루프백 주소를 예외적으로 취급하기도 하지만 브라우저별 동작 차이가 있을 수 있다. 운영 환경에서는 HTTPS 사용을 전제로 하는 것이 안전하다.</p>
<h2 id="세션-쿠키를-설정할-때-확인할-것">세션 쿠키를 설정할 때 확인할 것</h2>
<p>세션 ID에는 이메일, 회원번호처럼 사용자를 유추할 수 있는 정보를 그대로 넣지 않는다. 충분히 길고 예측하기 어려운 임의의 값을 만들고, 실제 사용자 정보는 서버에서 관리하는 편이 안전하다.</p>
<p>또한 다음과 같은 속성을 함께 검토해야 한다.</p>
<pre><code class="language-http">Set-Cookie: sessionId=random-token; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=1800</code></pre>
<ul>
<li><code>HttpOnly</code>: JavaScript에서 쿠키를 읽지 못하게 한다.</li>
<li><code>Secure</code>: HTTPS 요청에서만 쿠키를 전송한다.</li>
<li><code>SameSite</code>: 다른 사이트에서 시작된 요청에 쿠키가 전달되는 범위를 제한한다.</li>
<li><code>Path</code>: 쿠키가 전송될 URL 범위를 지정한다.</li>
<li><code>Max-Age</code>: 브라우저에 쿠키를 보관할 시간을 지정한다.</li>
</ul>
<p>쿠키의 만료 시간과 서버의 세션 만료 시간은 별개다. 브라우저에서 쿠키가 삭제되더라도 서버 세션이 계속 유효할 수 있으므로, 서버에서도 유휴 시간과 최대 유지 시간을 관리해야 한다.</p>
<h2 id="cookie-localstorage-sessionstorage-비교">Cookie, LocalStorage, SessionStorage 비교</h2>
<p>세 가지 모두 브라우저에 상태를 저장할 수 있지만 사용 목적과 동작 방식이 다르다.</p>
<table>
<thead>
<tr>
<th>구분</th>
<th>Cookie</th>
<th>LocalStorage</th>
<th>SessionStorage</th>
</tr>
</thead>
<tbody><tr>
<td>일반적인 용량</td>
<td>쿠키 하나당 약 4KB</td>
<td>브라우저에 따라 약 5~10MB</td>
<td>브라우저에 따라 약 5MB</td>
</tr>
<tr>
<td>유지 기간</td>
<td><code>Expires</code>, <code>Max-Age</code> 또는 세션 종료 시점</td>
<td>직접 삭제하기 전까지 유지</td>
<td>해당 탭이나 창이 닫힐 때까지</td>
</tr>
<tr>
<td>접근 범위</td>
<td>도메인과 경로 설정에 따라 결정</td>
<td>같은 출처의 탭과 창에서 공유</td>
<td>같은 출처이면서 같은 탭에서 사용</td>
</tr>
<tr>
<td>JavaScript 접근</td>
<td><code>HttpOnly</code>가 없으면 가능</td>
<td>가능</td>
<td>가능</td>
</tr>
<tr>
<td>서버 자동 전송</td>
<td>조건에 맞는 요청마다 전송</td>
<td>전송되지 않음</td>
<td>전송되지 않음</td>
</tr>
<tr>
<td>주요 용도</td>
<td>세션과 인증 상태</td>
<td>오래 유지할 사용자 설정</td>
<td>탭 단위의 임시 상태</td>
</tr>
</tbody></table>
<p>쿠키와 Web Storage는 엄밀히 말하면 HTTP 캐시와는 다르다. 저장된 값을 애플리케이션에서 잘 활용하면 중복 요청을 줄일 수 있지만, 자동으로 서버 부하를 줄여주는 것은 아니다. 특히 쿠키는 관련 요청마다 서버로 함께 전송되므로 너무 많은 데이터를 담으면 오히려 요청 크기가 커진다.</p>
<p>LocalStorage에는 테마나 언어 같은 오래 유지할 설정을, SessionStorage에는 작성 중인 폼이나 탭별 진행 상태 같은 임시 데이터를 저장하기 좋다. 인증 토큰처럼 민감한 정보는 XSS 위험을 고려해야 하므로 무조건 LocalStorage에 넣기보다 서버 세션과 <code>HttpOnly</code> 쿠키 사용을 우선 검토하는 편이 좋다.</p>
<h2 id="쿠키-허용-알림창은-항상-필요할까">쿠키 허용 알림창은 항상 필요할까?</h2>
<p>쿠키를 사용한다는 이유만으로 모든 서비스에 동일한 형태의 허용 팝업이 무조건 필요한 것은 아니다. 로그인 유지에 꼭 필요한 쿠키인지, 분석이나 맞춤형 광고에 사용하는 쿠키인지에 따라 요구사항이 달라질 수 있다.</p>
<p>개인정보나 행태정보를 수집한다면 수집 목적, 항목, 보유 기간, 거부 방법 등을 개인정보 처리방침에 명확히 안내해야 한다. 별도의 동의가 필요한 쿠키라면 동의를 받기 전에 비필수 쿠키를 저장하지 않고, 이용자가 거부하거나 나중에 철회할 수 있도록 구성해야 한다.</p>
<p>실제 서비스를 출시할 때는 서비스 대상 국가와 쿠키 사용 목적을 기준으로 개인정보보호위원회와 KISA의 최신 지침을 확인하는 것이 좋다.</p>
<p>참고 자료: <a href="https://datatracker.ietf.org/doc/html/rfc6265">RFC 6265</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie">MDN Set-Cookie 문서</a>, <a href="https://www.pipc.go.kr/np/cop/bbs/selectBoardArticle.do?bbsId=BS074&amp;mCode=C020010000&amp;nttId=9888">개인정보보호위원회 온라인 행태정보 정책</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[유니퀘스트, 멀티캐스트, 브로드캐스트]]></title>
            <link>https://velog.io/@blight_k/%EC%9C%A0%EB%8B%88%ED%80%98%EC%8A%A4%ED%8A%B8-%EB%A9%80%ED%8B%B0%EC%BA%90%EC%8A%A4%ED%8A%B8-%EB%B8%8C%EB%A1%9C%EB%93%9C%EC%BA%90%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@blight_k/%EC%9C%A0%EB%8B%88%ED%80%98%EC%8A%A4%ED%8A%B8-%EB%A9%80%ED%8B%B0%EC%BA%90%EC%8A%A4%ED%8A%B8-%EB%B8%8C%EB%A1%9C%EB%93%9C%EC%BA%90%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Sat, 30 Aug 2025 11:57:11 GMT</pubDate>
            <description><![CDATA[<p>개발을 하다 보면 네트워크 통신 방식을 접하게 된다. 데이터를 주고받는 방식에는 여러 가지가 있는데, 가장 대표적인 세 가지 방식인 유니캐스트, 멀티캐스트, 브로드캐스트에 대해 알아보고자 한다. 각 방식이 어떻게 다르며, 어떤 상황에서 사용되는지 예시를 통해 쉽게 이해해 보자.</p>
<h2 id="1-유니캐스트-unicast--1-대-1-우편배달">1. 유니캐스트 (Unicast) : 1 대 1 우편배달</h2>
<blockquote>
<p><strong>유니캐스트(Unicast)</strong>는 단일 송신자와 단일 수신자 간의 1:1 통신을 의미한다. 네트워크에서 가장 흔하게 사용되는 통신 방식이다.</p>
</blockquote>
<p>컴퓨터 A가 컴퓨터 B에게만 데이터를 보내는 상황을 생각하면 된다. 다른 컴퓨터들은 이 통신에 관여하지 않는다.</p>
<ul>
<li><strong>특징</strong>: 특정 대상과 정확하게 데이터를 주고받아야 할 때 사용한다. 데이터 처리가 간단하고, 대부분의 통신이 이 방식으로 이루어진다.</li>
<li><strong>현실 예시</strong>: 특정 주소로 보내는 &#39;편지&#39;나 &#39;택배&#39;와 같다. 보내는 사람과 받는 사람이 명확하게 정해져 있다.</li>
<li><strong>기술 예시</strong>:<ul>
<li><strong>HTTP 요청</strong>: 웹 브라우저에서 특정 웹사이트(예: <code>velog.io</code>)에 접속할 때, 내 컴퓨터는 velog 서버와 1:1로 유니캐스트 통신을 시작한다. 서버는 요청을 보낸 &#39;나에게만&#39; 웹페이지 데이터를 보내준다.</li>
<li><strong>SSH 접속</strong>: 원격 서버에 접속할 때 내 컴퓨터와 서버는 1:1 암호화 통신을 한다.</li>
</ul>
</li>
</ul>
<pre><code></code></pre><h2 id="2-멀티캐스트-multicast--관심사-기반-단체-채팅방-💬">2. 멀티캐스트 (Multicast) : 관심사 기반 단체 채팅방 💬</h2>
<blockquote>
<p><strong>멀티캐스트(Multicast)</strong>는 특정 그룹(하나 이상의 노드)에게만 데이터를 한 번에 전송하는 1:N 통신 방식이다.</p>
</blockquote>
<p>송신자는 데이터를 딱 한 번만 보내지만, 네트워크 장비(라우터 등)가 이 데이터를 복제하여 &#39;이 정보를 받기로 약속한&#39; 여러 명의 수신자에게 전달해 준다.</p>
<ul>
<li><strong>특징</strong>: 동일한 데이터를 여러 대상에게 보내야 할 때, 유니캐스트를 여러 번 하는 것보다 네트워크 효율성이 훨씬 좋다. 데이터를 수신하고 싶은 노드만 그룹에 가입(<code>join</code>)하여 데이터를 받는다.</li>
<li><strong>현실 예시</strong>: &#39;구독자에게만 발송되는 뉴스레터&#39;나 &#39;특정 멤버만 있는 카카오톡 단체 채팅방&#39;과 같다. 관심 있는 사람들만 그룹에 참여해 소식을 받는다.</li>
<li><strong>기술 예시</strong>:<ul>
<li><strong>IPTV (실시간 방송)</strong>: 방송국에서 특정 채널(예: tvN)의 방송 데이터를 멀티캐스트로 한 번만 송신한다. 시청자 중 tvN 채널을 켜는 사람(해당 멀티캐스트 그룹에 가입)들만 그 방송 데이터를 수신하여 TV를 볼 수 있다.</li>
<li><strong>온라인 증권 시세 정보</strong>: 실시간으로 변하는 주식 시세 정보를 수많은 클라이언트에게 전달할 때 사용된다.</li>
</ul>
</li>
</ul>
<pre><code></code></pre><h2 id="3-브로드캐스트-broadcast--전체-대상-교내-방송-📢">3. 브로드캐스트 (Broadcast) : 전체 대상 교내 방송 📢</h2>
<blockquote>
<p><strong>브로드캐스트(Broadcast)</strong>는 같은 네트워크 대역에 있는 모든 노드에게 데이터를 전송하는 1:All 통신 방식이다.</p>
</blockquote>
<p>수신을 원하든 원하지 않든, 같은 네트워크에 연결된 모두에게 메시지를 뿌린다.</p>
<ul>
<li><strong>특징</strong>: 메시지를 받을 대상을 특정할 수 없거나, 네트워크상의 모든 노드에 알려야 할 정보가 있을 때 사용한다. 하지만 네트워크에 부하를 많이 줄 수 있어 제한적인 상황에서만 사용된다.</li>
<li><strong>현실 예시</strong>: &#39;학교 전체에 울려 퍼지는 교내 방송&#39;이나 &#39;아파트 단지 전체에 안내하는 관리사무소 방송&#39;과 같다. 듣기 싫어도 일단 들린다.</li>
<li><strong>기술 예시</strong>:<ul>
<li><strong>DHCP (Dynamic Host Configuration Protocol)</strong>: 컴퓨터가 네트워크에 처음 연결될 때, IP 주소를 할당받기 위해 &quot;IP 주소 좀 할당해 줄 DHCP 서버 어디 있나요?&quot;라고 브로드캐스트를 통해 네트워크상의 모든 노드에게 요청을 보낸다.</li>
</ul>
</li>
</ul>
<pre><code></code></pre><h2 id="📝-한눈에-비교하기">📝 한눈에 비교하기</h2>
<table>
<thead>
<tr>
<th align="left">구분</th>
<th align="left">대상</th>
<th align="left">특징</th>
<th align="left">대표 예시</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>유니캐스트</strong></td>
<td align="left">특정 1개 노드 (1:1)</td>
<td align="left">가장 일반적인 통신 방식</td>
<td align="left">웹 서핑(HTTP)</td>
</tr>
<tr>
<td align="left"><strong>멀티캐스트</strong></td>
<td align="left">지정된 그룹의 모든 노드 (1:N)</td>
<td align="left">그룹에 가입한 노드만 수신, 효율적</td>
<td align="left">IPTV, 온라인 증권</td>
</tr>
<tr>
<td align="left"><strong>브로드캐스트</strong></td>
<td align="left">같은 네트워크의 모든 노드 (1:All)</td>
<td align="left">수신 의사와 상관없이 모두에게 전달, 부하 높음</td>
<td align="left">ARP, DHCP</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[네트워크 토폴로지의 필요성]]></title>
            <link>https://velog.io/@blight_k/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%ED%86%A0%ED%8F%B4%EB%A1%9C%EC%A7%80%EC%9D%98-%ED%95%84%EC%9A%94%EC%84%B1</link>
            <guid>https://velog.io/@blight_k/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%ED%86%A0%ED%8F%B4%EB%A1%9C%EC%A7%80%EC%9D%98-%ED%95%84%EC%9A%94%EC%84%B1</guid>
            <pubDate>Thu, 28 Aug 2025 14:11:38 GMT</pubDate>
            <description><![CDATA[<p>토폴로지는 네트워크의 <strong>성능과 안정성을 결정하는 핵심 요소</strong>이며, 특히 <strong>병목 현상(Bottleneck)</strong>을 이해하고 해결하는 중요한 척도가 된다</p>
<hr>
<h2 id="병목-현상이란">병목 현상이란?</h2>
<p><strong>병목 현상</strong>이란 말 그대로 병의 목처럼 좁아지는 구간 때문에 전체 흐름이 느려지는 것을 의미한다. 네트워크에서는 <strong>과도한 트래픽으로 인해 데이터의 흐름이 제한되는 상황</strong>을 가리킨다. 사용자가 몰려 웹사이트 접속이 느려지거나 서버가 다운되는 것이 대표적인 예다.</p>
<hr>
<h2 id="서버가-다운되면-어떻게-해야-할까">서버가 다운되면 어떻게 해야 할까?</h2>
<p>만약 갑자기 늘어난 트래픽 때문에 서비스가 마비되었다면 어떻게 대처해야 할까? 우리는 두 가지 관점에서 해결책을 생각해 볼 수 있다.</p>
<h3 id="1-자원의-양을-늘린다-scale-up">1. 자원의 양을 늘린다 (Scale-up)</h3>
<p>가장 먼저 시도하는 방법은 서버 자체의 성능을 높이는 것이다. <strong>메모리(RAM)나 CPU 같은 하드웨어 자원을 증설</strong>하여 서버가 더 많은 트래픽을 처리할 수 있도록 하는 것이다. 이는 가장 직관적인 해결책이다.</p>
<h3 id="2-토폴로지-관점에서-회선을-늘린다-scale-out">2. 토폴로지 관점에서 회선을 늘린다 (Scale-out)</h3>
<p>하지만 서버 자원을 무한정 늘릴 수는 없다. 이때 네트워크 토폴로지에 대한 이해가 빛을 발한다. <strong>토폴로지 관점에서 보면, 단순히 회선을 추가하여 데이터가 지나갈 수 있는 길을 넓혀주는 것</strong>만으로도 병목 현상을 크게 개선할 수 있다.</p>
<p>예를 들어, 모든 데이터가 하나의 중앙 허브를 거치는 스타형 토폴로지라면 허브가 병목 지점이 되기 쉽다. 이때 여러 경로로 트래픽을 분산시킬 수 있는 메시 토폴로지를 일부 도입하거나, 중요한 서버에 이중 회선을 연결하는 등의 구조적 변경을 통해 트래픽을 분산시키고 안정성을 확보할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[네트워크(3)]]></title>
            <link>https://velog.io/@blight_k/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC3</link>
            <guid>https://velog.io/@blight_k/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC3</guid>
            <pubDate>Thu, 28 Aug 2025 14:04:10 GMT</pubDate>
            <description><![CDATA[<h1 id="링-토폴로지와-메시-토폴로지-간단히-알아보기">링 토폴로지와 메시 토폴로지 간단히 알아보기</h1>
<p>네트워크 토폴로지(Network Topology)는 컴퓨터 네트워크의 요소들(링크, 노드 등)을 물리적으로 연결해 놓은 방식, 즉 네트워크의 구조를 의미한다. 오늘은 여러 토폴로지 중 <strong>링형(Ring Topology)</strong>과 <strong>메시형(Mesh Topology)</strong>에 대해 알아보려 한다.</p>
<hr>
<h2 id="링-토폴로지-ring-topology">링 토폴로지 (Ring Topology)</h2>
<p>링 토폴로지는 이름 그대로 각 노드가 양옆의 두 노드와 연결되어 전체적으로 <strong>하나의 고리(Ring) 형태</strong>를 이루는 네트워크 구조이다.</p>
<p>데이터는 링을 따라 한 방향으로 흐르며, 각 노드는 데이터를 수신하고 목적지가 자신이 아니면 옆 노드로 재전송하는 역할을 한다.</p>
<p>특히 링 토폴로지에서는 <strong>토큰 링(Token Ring)</strong> 프로토콜을 사용하는 경우가 많다.</p>
<ul>
<li><strong>토큰 링 프로토콜</strong>: 네트워크 상에 <strong>단 하나의 토큰(Token)</strong>이 존재한다. 이 토큰을 소유한 노드만이 데이터를 전송할 권한을 갖게 되어 충돌을 방지한다.</li>
</ul>
<h3 id="장점과-단점">장점과 단점</h3>
<p>링 토폴로지의 가장 큰 장점 중 하나는 <strong>장애 발생 시 어떤 노드에 문제가 생겼는지 쉽게 파악</strong>할 수 있다는 점이다.</p>
<p>하지만 단점도 명확하다. 이론적으로는 노드의 추가와 삭제가 간단해 보이지만, 실제로는 노드를 추가하거나 삭제하기 위해 <strong>연결을 끊는 순간 전체 네트워크가 일시적으로 중단</strong>된다. 또한, 데이터가 목적지에 도달하기까지 여러 노드를 거쳐야 하므로 <strong>전송 지연</strong>이 발생할 수 있다.</p>
<h3 id="이중-링-dual-ring">이중 링 (Dual Ring)</h3>
<p>이러한 단점을 보완하기 위해 <strong>이중 링(Dual Ring)</strong> 방식을 사용하기도 한다. 두 개의 링을 사용하여 하나는 주 경로로, 다른 하나는 백업 경로로 활용한다. 이를 통해 한쪽 링에 문제가 생겨도 다른 링을 통해 통신을 유지할 수 있어 안정성을 높인다.</p>
<hr>
<h2 id="🕸️-메시-토폴로지-mesh-topology">🕸️ 메시 토폴로지 (Mesh Topology)</h2>
<p>메시 토폴로지는 여러 노드들이 <strong>그물망(Mesh)처럼 서로 연결</strong>된 구조를 가진다. 모든 노드가 다른 모든 노드와 직접 연결될 필요는 없으며, 연결 방식에 따라 두 가지로 나뉜다.</p>
<ul>
<li><strong>풀 메시 (Full Mesh)</strong>: 네트워크의 모든 노드가 서로 <strong>1:1로 직접 연결</strong>된 구조.</li>
<li><strong>부분 메시 (Partial Mesh)</strong>: 일부 중요한 노드들만 여러 노드와 연결되고, 나머지 노드는 일부와만 연결된 구조.</li>
</ul>
<h3 id="장점과-단점-1">장점과 단점</h3>
<p>메시 토폴로지는 여러 개의 연결 회선을 가지므로 <strong>특정 회선에 장애가 발생해도 다른 경로를 통해 통신을 유지</strong>할 수 있어 매우 안정적이다. 또한, 트래픽이 한쪽으로 몰릴 경우 <strong>다른 경로로 트래픽을 분산</strong>시켜 효율적인 데이터 전송이 가능하다.</p>
<p>그러나 가장 큰 단점은 <strong>비용</strong>이다. 노드 간에 수많은 회선이 필요하므로 구축 및 유지보수 비용이 크게 증가한다. 특히 풀 메시 구조는 노드 수가 늘어날수록 필요한 연결 수가 기하급수적으로 늘어난다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[네트워크(2)]]></title>
            <link>https://velog.io/@blight_k/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC2</link>
            <guid>https://velog.io/@blight_k/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC2</guid>
            <pubDate>Wed, 27 Aug 2025 12:04:58 GMT</pubDate>
            <description><![CDATA[<h1 id="네트워크-토폴로지-학습-기록">네트워크 토폴로지 학습 기록</h1>
<p>네트워크 토폴로지(Network Topology)는 노드와 링크가 어떤 구조로 연결되어 있는지를 나타내는 방식이다. 이번에는 대표적인 토폴로지인 버스, 스타, 트리 토폴로지에 대해 알아본다.</p>
<hr>
<h2 id="버스-토폴로지-bus-topology">버스 토폴로지 (Bus Topology)</h2>
<p>버스 토폴로지는 <strong>중앙 회선 하나(백본, Backbone)에 여러 개의 노드가 연결된 구조</strong>를 가진다. 모든 노드는 동일한 하나의 전송 회선에 연결되어 통신한다.</p>
<h3 id="장점">장점</h3>
<ul>
<li><strong>설치 비용 절감</strong>: 회선 하나만 설치하면 되므로 비용이 적게 든다.</li>
<li><strong>유지보수 용이성</strong>: 구조가 단순하여 유지보수가 비교적 쉽다.</li>
<li><strong>노드 장애의 영향 제한</strong>: 특정 노드 하나에 장애가 발생해도 전체 네트워크에 영향을 주지 않는다.</li>
</ul>
<h3 id="단점">단점</h3>
<ul>
<li><strong>중앙 회선 장애의 파급력</strong>: 만약 중앙 회선(백본)이 손상되면 전체 네트워크가 마비된다.</li>
<li><strong>보안 취약성</strong>: 모든 데이터가 단일 회선을 통해 전송되므로, 회선을 도청하면 전체 노드의 통신 내용이 노출될 위험이 있다.</li>
</ul>
<hr>
<h2 id="스타-토폴로지-star-topology">스타 토폴로지 (Star Topology)</h2>
<p>스타 토폴로지는 <strong>하나의 중앙 노드를 중심으로 여러 개의 말단 노드가 별 모양처럼 독립적으로 연결</strong>된 구조이다. 모든 통신은 반드시 중앙 노드를 거쳐서 이루어진다.</p>
<h3 id="장점-1">장점</h3>
<ul>
<li><strong>중앙 집중적 보안 관리</strong>: 중앙 노드에 방화벽과 같은 보안 기능을 집중시켜 효율적인 관리가 가능하다. 공격자는 반드시 중앙 노드를 거쳐야 하므로 보안상 이점이 있다.</li>
<li><strong>노드 장애의 영향 제한</strong>: 하나의 말단 노드에 장애가 발생해도 다른 노드와의 통신에는 영향을 주지 않는다.</li>
</ul>
<h3 id="단점-1">단점</h3>
<ul>
<li><strong>중앙 노드 의존성</strong>: 중앙 노드에 장애가 발생하면 모든 노드 간의 통신이 중단되어 전체 네트워크가 마비된다.</li>
<li><strong>설치 비용</strong>: 모든 노드를 중앙 노드에 개별적으로 연결해야 하므로 케이블링 비용이 많이 들 수 있다.</li>
</ul>
<hr>
<h2 id="트리-토폴로지-tree-topology">트리 토폴로지 (Tree Topology)</h2>
<p>트리 토폴로지는 <strong>계층적인 구조를 가지는 네트워크</strong>로, 버스 토폴로지와 스타 토폴로지가 결합된 형태라고 볼 수 있다. 최상위 루트 노드를 중심으로 하위 노드들이 나무가지처럼 뻗어 나가는 구조를 가진다.</p>
<h3 id="장점-2">장점</h3>
<ul>
<li><strong>확장 용이성</strong>: 계층 구조의 가장 아래에 있는 리프(leaf) 노드를 추가하기 쉬워 네트워크 확장이 편리하다.</li>
<li><strong>장애 영향 범위 제한</strong>: 리프 노드에 장애가 발생해도 해당 노드와 그 하위 노드에만 영향이 있을 뿐, 전체 네트워크에는 큰 영향을 주지 않는다.</li>
</ul>
<h3 id="단점-2">단점</h3>
<ul>
<li><strong>루트 노드 의존성</strong>: 최상위 루트 노드에 장애가 발생하면 그 아래에 연결된 모든 하위 노드의 통신이 불가능해진다.</li>
<li><strong>관리의 복잡성</strong>: 계층이 복잡해질수록 네트워크 관리가 어려워지고, 루트 노드에 가까울수록 트래픽이 집중될 수 있다. 또한, 루트 노드를 변경하거나 삭제하는 작업은 매우 어렵다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[네트워크(1)]]></title>
            <link>https://velog.io/@blight_k/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC1</link>
            <guid>https://velog.io/@blight_k/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC1</guid>
            <pubDate>Wed, 27 Aug 2025 11:53:47 GMT</pubDate>
            <description><![CDATA[<h2 id="네트워크-기초-세상을-연결하는-보이지-않는-끈">네트워크 기초: 세상을 연결하는 보이지 않는 끈</h2>
<p>오늘은 네트워크의 가장 기본적인 개념인 노드, 링크, 트래픽, 처리량, 대역폭, RTT에 대해 학습한 내용을 정리해본다.</p>
<h3 id="네트워크란-무엇일까-노드와-링크">네트워크란 무엇일까? 노드와 링크</h3>
<p><strong>네트워크</strong>란 간단히 말해 <strong>노드(Node)</strong>와 <strong>링크(Link)</strong>가 서로 연결되어 리소스를 공유하는 집합을 의미한다.</p>
<ul>
<li><strong>노드</strong>: 네트워크를 구성하는 각 지점을 말한다. 우리가 사용하는 컴퓨터, 스마트폰뿐만 아니라 서버, 라우터, 스위치 같은 네트워크 장비 모두가 노드에 해당한다.</li>
<li><strong>링크</strong>: 노드와 노드를 연결하는 매개체를 뜻한다. 눈에 보이는 랜선 같은 <strong>유선</strong> 연결과 와이파이(Wi-Fi)나 LTE 같은 <strong>무선</strong> 연결이 모두 링크에 속한다. 우리가 와이파이를 통해 인터넷에 접속했다면, 스마트폰(노드)과 공유기(노드)가 무선이라는 링크로 연결된 것이다.</li>
</ul>
<h3 id="데이터의-흐름-트래픽과-처리량">데이터의 흐름: 트래픽과 처리량</h3>
<p>네트워크를 도로망에 비유한다면, 그 위를 달리는 자동차들은 데이터라고 할 수 있다.</p>
<ul>
<li><p><strong>트래픽(Traffic)</strong>: 특정 시간 동안 링크를 통해 흐르는 <strong>데이터의 양</strong>을 말한다. 예를 들어, 우리가 유튜브 영상을 시청하거나 클라우드에서 파일을 내려받을 때 발생하는 데이터의 총량이 바로 트래픽이다. 트래픽이 많아진다는 것은 그만큼 많은 데이터가 네트워크를 통해 오고 간다는 의미이며, 보통 <strong>bps(bits per second)</strong> 단위를 사용한다.</p>
</li>
<li><p><strong>처리량(Throughput)</strong>: 링크를 통해 <strong>성공적으로 전달된 데이터의 양</strong>을 의미한다. 트래픽이 도로에 진입한 모든 자동차의 수라면, 처리량은 막힘없이 목적지에 도착한 자동차의 수에 비유할 수 있다. 아무리 트래픽이 많아도 네트워크가 이를 감당하지 못하면 처리량은 낮아진다. 예를 들어, <strong>동시 접속자가 너무 많아지면</strong> 서버가 모든 요청을 원활하게 처리하지 못해 <strong>처리량이 낮아지는 현상</strong>이 발생한다. 단위는 트래픽과 동일하게 bps를 사용한다.</p>
</li>
</ul>
<h3 id="네트워크의-성능-대역폭과-rtt">네트워크의 성능: 대역폭과 RTT</h3>
<ul>
<li><p><strong>대역폭(Bandwidth)</strong>: 주어진 시간 동안 네트워크 링크를 통해 전송될 수 있는 <strong>데이터의 최대량</strong>을 의미한다. 고속도로의 차선에 비유하면 이해하기 쉽다. 2차선 도로보다 4차선 도로가 더 많은 차를 동시에 수용할 수 있듯이, 대역폭이 넓을수록 한 번에 더 많은 데이터를 전송할 수 있다.</p>
<p>  예를 들어, <strong>100Mbps의 대역폭</strong>을 가진 서버가 있다고 가정해보자. 한 명의 사용자가 평균적으로 <strong>100kbps</strong>의 데이터를 사용한다면, 이 서버는 이론적으로 몇 명의 동시 접속자를 감당할 수 있을까?</p>
<blockquote>
<p>$100 \text{ Mbps} = 100,000 \text{ kbps}$</p>
<p>$100,000 \text{ kbps} / 100 \text{ kbps} = 1000$</p>
</blockquote>
<p>  계산상으로는 약 <strong>1,000명</strong>의 동시 접속자를 지원할 수 있다.</p>
</li>
<li><p><strong>RTT(Round Trip Time)</strong>: <strong>왕복 지연 시간</strong>을 의미하며, 특정 데이터 패킷이 출발지(노드)에서 목적지(노드)까지 갔다가 다시 출발지로 돌아오는 데 걸리는 총 시간을 나타낸다. RTT가 짧을수록 네트워크의 응답 속도가 빠르다는 것을 의미한다. 온라인 게임을 할 때 &#39;핑(Ping)&#39;이 높다고 하는 것이 바로 이 RTT가 긴 상황을 말한다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[의존성 주입과 전략패턴의 차이]]></title>
            <link>https://velog.io/@blight_k/%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85%EA%B3%BC-%EC%A0%84%EB%9E%B5%ED%8C%A8%ED%84%B4%EC%9D%98-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@blight_k/%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85%EA%B3%BC-%EC%A0%84%EB%9E%B5%ED%8C%A8%ED%84%B4%EC%9D%98-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Wed, 02 Jul 2025 01:26:51 GMT</pubDate>
            <description><![CDATA[<h2 id="전략-패턴과-의존성-주입의-차이와-공통점">전략 패턴과 의존성 주입의 차이와 공통점</h2>
<p>개발을 하다 보면, “무언가를 쉽게 교체하고 싶다”라는 욕구가 자주 생긴다. 이럴 때 많이 언급되는 개념이 바로 <strong>전략 패턴</strong>과 <strong>의존성 주입(DI)</strong> 이다.</p>
<p>둘 다 쉽게 교체가 가능하다는 점에서 비슷하게 느껴질 수 있다. 하지만 본질적으로 목적과 사용 방식, 그리고 적용되는 맥락이 다르다.</p>
<hr>
<h3 id="전략-패턴이란">전략 패턴이란?</h3>
<p>전략 패턴은 행위(Behavior)에 기반한 디자인 패턴이다.
어떤 컨텍스트(Context) 안에서 알고리즘(혹은 행위)을 런타임에 쉽게 교체할 수 있도록 도와준다.
즉, 인터페이스를 통해 여러 전략(알고리즘, 행위 등)을 정의하고, 실제 사용하는 쪽에서는 구체적인 전략이 아니라 추상화된 전략 인터페이스에만 의존하게 된다.
필요할 때 전략을 갈아끼우듯이 교체할 수 있다.</p>
<p>예를 들어, 결제 방식을 신용카드, 카카오페이, 네이버페이 등으로 자유롭게 바꿔서 사용할 수 있다.</p>
<hr>
<h3 id="의존성-주입di이란">의존성 주입(DI)이란?</h3>
<p>의존성 주입은 어떤 객체가 사용할 의존성을 직접 생성하지 않고, 외부에서 주입받는 방식이다.
쉽게 말해, 클래스 안에서 직접 <code>new</code>로 생성하지 않고, 누군가(프레임워크, 팩토리, 외부 코드 등)가 대신 만들어서 넣어주는 구조다.</p>
<p>이 역시 “무언가를 쉽게 교체한다”는 점에서 전략 패턴과 비슷해 보인다.
의존성을 바꿔치기하거나, 테스트용 목 객체로 교체할 때도 유용하다.</p>
<hr>
<h3 id="공통점과-차이점">공통점과 차이점</h3>
<p>둘 다 “교체 가능성”이라는 공통점이 있다.
하지만 목적과 역할이 다르다.</p>
<ul>
<li><strong>전략 패턴</strong>은 “행위(알고리즘)” 자체를 추상화해서 런타임에 쉽게 교체하는 데 초점을 둔다.</li>
<li><strong>의존성 주입</strong>은 “외부 의존성(객체)”을 직접 만들지 않고 주입받음으로써, 결합도를 낮추고 유연하게 교체하는 데 초점을 둔다.</li>
</ul>
<p>전략 패턴은 GoF(Design Patterns)에서 정의된 명확한 패턴 중 하나다.
의존성 주입은 디자인 패턴이라기보다는 설계 원칙 또는 구조에 가깝다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[스프링부트 필터와 인터셉터의 차이]]></title>
            <link>https://velog.io/@blight_k/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%ED%95%84%ED%84%B0%EC%99%80-%EC%9D%B8%ED%84%B0%EC%85%89%ED%84%B0%EC%9D%98-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@blight_k/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%ED%95%84%ED%84%B0%EC%99%80-%EC%9D%B8%ED%84%B0%EC%85%89%ED%84%B0%EC%9D%98-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Fri, 30 May 2025 01:32:12 GMT</pubDate>
            <description><![CDATA[<p>Spring Boot 애플리케이션을 개발하다 보면, HTTP 요청과 응답을 중간에 가로채어 처리해야 하는 경우가 자주 발생한다. 이때 사용하는 대표적인 방법이 바로 <strong>Filter</strong>와 <strong>Interceptor</strong>이다. 두 기능 모두 비슷한 역할을 하지만, 동작하는 시점과 활용 목적이 다르기 때문에 그 차이를 명확히 이해하고 적절한 상황에 적용하는 것이 중요하다.</p>
<hr>
<h2 id="1-filter란">1. Filter란?</h2>
<p><strong>Filter</strong>는 서블릿 API의 일부로, Spring의 <code>DispatcherServlet</code>에 도달하기 <strong>이전</strong> 단계에서 모든 요청을 처리한다. 즉, Spring Framework의 처리 흐름에 진입하기 전에 동작하게 된다.</p>
<h3 id="filter의-주요-활용-예시">Filter의 주요 활용 예시</h3>
<ul>
<li><strong>로깅</strong>: 요청 및 응답 정보를 로그로 남겨 디버깅 및 모니터링에 활용한다.</li>
<li><strong>GZIP 압축</strong>: 응답 데이터를 압축하여 전송 속도를 개선한다.</li>
<li><strong>문자 인코딩 처리</strong>: UTF-8 등 통일된 인코딩 설정을 적용한다.</li>
<li><strong>보안 검사</strong>: 방화벽 설정, 속도 제한, 접근 제어 등의 전처리를 수행한다.</li>
<li><strong>요청/응답 수정</strong>: 예를 들어 데이터 래핑, 캐싱 처리, XSS 필터링을 수행한다.</li>
</ul>
<h3 id="filter-구현-예시">Filter 구현 예시</h3>
<pre><code class="language-java">@Component
public class LoggingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        System.out.println(&quot;Request received: &quot; + request.getRemoteAddr());
        chain.doFilter(request, response); // 다음 필터 또는 서블릿으로 전달
        System.out.println(&quot;Response sent.&quot;);
    }
}</code></pre>
<ul>
<li>모든 요청에 적용되며, 정적 리소스(CSS, JS, 이미지 등)도 포함된다.</li>
<li>서블릿 컨테이너 수준에서 동작하기 때문에 <strong>Spring Bean이나 Controller에 직접 접근할 수 없다</strong>.</li>
</ul>
<hr>
<h2 id="2-interceptor란">2. Interceptor란?</h2>
<p><strong>Interceptor</strong>는 Spring MVC의 구성 요소로, Spring 내부의 DispatcherServlet이 요청을 Controller로 전달하기 전후에 작동한다.</p>
<h3 id="interceptor의-주요-활용-예시">Interceptor의 주요 활용 예시</h3>
<ul>
<li><strong>인증 및 권한 검사</strong>: 로그인 상태 확인, 권한 검증 등의 로직을 수행한다.</li>
<li><strong>실행 시간 측정</strong>: 컨트롤러 처리 시간 등의 퍼포먼스 모니터링에 활용한다.</li>
<li><strong>Model/View 수정</strong>: 응답 전 공통 데이터를 삽입하거나 뷰 모델을 수정한다.</li>
<li><strong>공통 속성 추가</strong>: 사용자 정보 등 공통 데이터를 모든 응답에 포함시킨다.</li>
</ul>
<h3 id="interceptor-구현-예시">Interceptor 구현 예시</h3>
<pre><code class="language-java">@Component
public class AuthInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        System.out.println(&quot;Checking authentication...&quot;);
        return true; // 컨트롤러로 요청 계속 전달
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response,
                           Object handler, ModelAndView modelAndView) {
        System.out.println(&quot;Controller executed.&quot;);
    }
}</code></pre>
<ul>
<li><strong>Spring MVC Controller에만 적용되며</strong>, 정적 리소스에는 적용되지 않는다.</li>
<li>Spring ApplicationContext 내부에서 작동하므로 <strong>Spring Bean을 주입(@Autowired)받아 사용할 수 있다</strong>.</li>
</ul>
<hr>
<h2 id="3-filter-vs-interceptor-주요-차이점">3. Filter vs Interceptor 주요 차이점</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>Filter</th>
<th>Interceptor</th>
</tr>
</thead>
<tbody><tr>
<td>실행 시점</td>
<td>DispatcherServlet 이전</td>
<td>Controller 실행 전/후</td>
</tr>
<tr>
<td>적용 범위</td>
<td>모든 요청 (정적 리소스 포함)</td>
<td>Spring MVC Controller 요청</td>
</tr>
<tr>
<td>Spring Bean 접근</td>
<td>❌ 불가능</td>
<td>✅ 가능</td>
</tr>
<tr>
<td>요청 타입</td>
<td>전체 HTTP 요청</td>
<td>Controller 요청만</td>
</tr>
<tr>
<td>요청/응답 수정</td>
<td>헤더, 인코딩, 압축 등 가능</td>
<td>Model, View 수정 가능 (응답 바디 직접 수정은 어려움)</td>
</tr>
<tr>
<td>주요 용도</td>
<td>보안 필터, 로깅, 인코딩</td>
<td>인증, 권한 검사, 컨트롤러 후처리</td>
</tr>
</tbody></table>
<hr>
<h2 id="4-언제-무엇을-써야-할까">4. 언제 무엇을 써야 할까?</h2>
<h3 id="filter를-사용해야-할-때">Filter를 사용해야 할 때</h3>
<ul>
<li>모든 요청(정적 리소스 포함)에 대해 <strong>로깅</strong>, <strong>보안 검사</strong>, <strong>인코딩 처리</strong> 등을 적용하고자 할 때</li>
<li><strong>응답 압축(GZIP)</strong> 등의 기능이 필요한 경우</li>
<li>Spring Bean을 사용하지 않고 순수 서블릿 수준에서 처리할 경우</li>
</ul>
<h3 id="interceptor를-사용해야-할-때">Interceptor를 사용해야 할 때</h3>
<ul>
<li>컨트롤러 접근 전에 <strong>인증/인가 검사</strong>가 필요한 경우</li>
<li>컨트롤러 실행 시간을 측정하거나 로그를 남기고 싶은 경우</li>
<li>응답 전 공통 데이터(Model)를 삽입하고 싶은 경우</li>
<li>**Spring Bean(Service, Repository 등)**을 활용한 비즈니스 로직이 필요한 경우</li>
</ul>
<hr>
<h2 id="5-요약">5. 요약</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>Filter</th>
<th>Interceptor</th>
</tr>
</thead>
<tbody><tr>
<td>레벨</td>
<td>서블릿 레벨</td>
<td>Spring MVC 레벨</td>
</tr>
<tr>
<td>적용 대상</td>
<td>전체 HTTP 요청</td>
<td>Spring Controller 요청</td>
</tr>
<tr>
<td>Spring Bean 접근</td>
<td>❌ 불가능</td>
<td>✅ 가능</td>
</tr>
<tr>
<td>대표 용도</td>
<td>인코딩, 압축, 보안</td>
<td>인증, 로깅, 모델 처리</td>
</tr>
</tbody></table>
<h3 id="💡-선택-기준-요약">💡 선택 기준 요약</h3>
<ul>
<li><strong>HTTP 요청 자체를 조작해야 한다면 → Filter</strong></li>
<li><strong>Controller 로직 전후로 로직을 삽입하고 싶다면 → Interceptor</strong></li>
</ul>
<hr>
<h2 id="마무리">마무리</h2>
<p>Spring Boot에서 HTTP 흐름을 제어해야 할 때 Filter와 Interceptor는 매우 유용한 도구이다. 기능적으로 겹치는 부분도 있지만, <strong>실행 시점과 활용 목적이 분명히 다르기 때문에 상황에 따라 적절하게 선택하여 사용해야 한다.</strong></p>
<p>실무에서는 이 둘을 <strong>조합하여 사용하는 경우도 많으므로</strong>, 명확한 이해를 바탕으로 설계하는 것이 중요하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[컨텍스트란 ?]]></title>
            <link>https://velog.io/@blight_k/%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8%EB%9E%80</link>
            <guid>https://velog.io/@blight_k/%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8%EB%9E%80</guid>
            <pubDate>Fri, 16 May 2025 14:19:59 GMT</pubDate>
            <description><![CDATA[<h1 id="컨텍스트context란-무엇인가-쉽게-이해해보자">컨텍스트(Context)란 무엇인가? 쉽게 이해해보자</h1>
<p>개발 공부를 하다 보면 자주 마주치는 단어 중 하나가 **&quot;컨텍스트(context)&quot;**입니다. 어렵게 느껴지지만, 우리 일상 속에서 이미 자주 경험하고 있는 개념입니다.</p>
<h2 id="카페에서-커피-주문할-때의-컨텍스트">카페에서 커피 주문할 때의 컨텍스트</h2>
<p>여러분이 카페에 들어가서 &quot;아이스 아메리카노 하나 주세요&quot;라고 말한다고 해보겠습니다. 이때 점원은 여러분의 상태나 주변 정보를 바탕으로 행동합니다. 예를 들어:</p>
<ul>
<li>나(고객)가 누구인지</li>
<li>혼자인지 여럿인지</li>
<li>머그잔인지 테이크아웃인지</li>
</ul>
<p>이 모든 정보들이 대화의 <strong>컨텍스트</strong>입니다. 즉, 단순히 말로 표현하지 않았더라도 상황(context) 속에서 유추할 수 있는 정보들 입니다.</p>
<h2 id="개발에서의-컨텍스트란">개발에서의 컨텍스트란?</h2>
<p>소프트웨어에서의 컨텍스트도 이와 비슷합니다. 어떤 작업이나 상태를 처리하기 위해 <strong>필요한 최소한의 정보</strong>를 뜻합니다. 예를 들어:</p>
<ul>
<li>로그인한 사용자의 정보</li>
<li>현재 선택된 언어</li>
<li>테마(다크 모드인지 라이트 모드인지)</li>
<li>현재 위치한 페이지나 경로</li>
</ul>
<p>이런 정보들을 <strong>컨텍스트</strong>라고 부르고, 이 정보들을 기준으로 UI나 로직이 다르게 동작할 수 있습니다.</p>
<h2 id="os에서의-컨텍스트-컨텍스트-스위칭">OS에서의 컨텍스트: 컨텍스트 스위칭</h2>
<p>운영체제에서는 여러 프로세스가 CPU를 번갈아 사용할 수 있도록 하는데, 이때 각각의 작업 상태(레지스터, 메모리 위치 등)를 저장하고 불러오는 과정을 **컨텍스트 스위칭(context switching)**이라고 합니다.</p>
<h2 id="http에서도-컨텍스트">HTTP에서도 컨텍스트?</h2>
<p>HTTP 통신을 할 때도 컨텍스트가 존재합니다. 대표적인 예가 <strong>HTTP Header</strong>입니다. 예를 들어:</p>
<pre><code class="language-http">Authorization: Bearer eyJhbGciOi...
Accept-Language: ko-KR</code></pre>
<p>이런 헤더는 요청을 보낼 때 클라이언트의 상태나 조건을 서버에 전달하는 <strong>컨텍스트 정보</strong>입니다.</p>
<h2 id="react에서의-context-api">React에서의 Context API</h2>
<p>React에서는 <strong>Context API</strong>를 통해 컴포넌트 트리 전체에 데이터를 쉽게 전달할 수 있습니다. props drilling 없이도 여러 컴포넌트에서 공유해야 하는 전역적인 상태(예: 테마, 로그인 정보)를 관리할 수 있습니다.</p>
<pre><code class="language-tsx">const ThemeContext = createContext(&quot;light&quot;);

function App() {
  return (
    &lt;ThemeContext.Provider value=&quot;dark&quot;&gt;
      &lt;Toolbar /&gt;
    &lt;/ThemeContext.Provider&gt;
  );
}</code></pre>
<p>여기서 <code>ThemeContext</code>는 현재 UI의 상태에 따라 다르게 렌더링되도록 돕는 <strong>컨텍스트 저장소</strong> 역할을 합니다.</p>
<hr>
<h2 id="마무리-컨텍스트는-상황-정보다">마무리: 컨텍스트는 &quot;상황 정보&quot;다</h2>
<p>정리하자면, <strong>컨텍스트는 어떤 작업을 하기 위해 필요한 주변 정보</strong>입니다. 꼭 IT 용어가 아니라도, 우리는 일상에서 컨텍스트를 바탕으로 대화하고, 판단하고, 행동하고 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Flux 패턴이란 ?]]></title>
            <link>https://velog.io/@blight_k/Flux-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@blight_k/Flux-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Sun, 11 May 2025 09:32:02 GMT</pubDate>
            <description><![CDATA[<h1 id="flux-패턴이란-복잡한-상태-관리를-단방향으로-해결한다">Flux 패턴이란? 복잡한 상태 관리를 단방향으로 해결한다</h1>
<p>웹 애플리케이션이 복잡해지면 상태(state) 관리가 점점 어려워진다. 특히 MVC 패턴에서는 <code>Model</code>과 <code>View</code> 사이의 관계가 많아질수록 <strong>데이터 흐름이 꼬이고</strong>, <strong>상태 추적이 어려워지는 문제</strong>에 직면하게 된다.</p>
<p>이러한 문제를 해결하기 위해 <strong>Facebook</strong>에서 제안한 아키텍처가 바로 <strong>Flux 패턴</strong>이다.</p>
<hr>
<h2 id="flux-패턴이란">Flux 패턴이란?</h2>
<p>Flux는 <strong>단방향 데이터 흐름</strong>을 기반으로 상태를 예측 가능하게 관리하는 아키텍처 패턴이다. MVC처럼 컴포넌트가 서로 참조하는 구조가 아니라, 명확한 흐름을 가진 파이프라인 구조를 지닌다.</p>
<p>Flux의 핵심 구성은 아래와 같다:</p>
<pre><code>Action → Dispatcher → Store → View</code></pre><hr>
<h2 id="flux의-구조">Flux의 구조</h2>
<h3 id="1-action">1. Action</h3>
<p><code>Action</code>은 사용자의 이벤트(예: 버튼 클릭)나 API 응답처럼 애플리케이션에서 발생하는 <strong>모든 의도를 명시적으로 표현</strong>한다.</p>
<pre><code class="language-ts">// action.ts
export const markAsRead = (messageId: number) =&gt; ({
  type: &#39;MARK_AS_READ&#39;,
  payload: { id: messageId },
});</code></pre>
<h3 id="2-dispatcher">2. Dispatcher</h3>
<p><code>Dispatcher</code>는 모든 액션을 <strong>중앙 집중식으로 처리</strong>하는 허브 역할을 한다. 어떤 <code>Action</code>이 발생했을 때, 어떤 <code>Store</code>에게 전달할지 결정한다.</p>
<pre><code class="language-ts">// dispatcher.ts
import { Dispatcher } from &#39;flux&#39;;

const dispatcher = new Dispatcher();
export default dispatcher;</code></pre>
<h3 id="3-store">3. Store</h3>
<p><code>Store</code>는 <strong>상태(state)를 관리하는 계층</strong>이다. <code>Action</code>을 받아 내부 상태를 갱신한 뒤, View에 알려준다.</p>
<pre><code class="language-ts">// messageStore.ts
import { EventEmitter } from &#39;events&#39;;
import dispatcher from &#39;./dispatcher&#39;;

class MessageStore extends EventEmitter {
  private messages: { id: number; read: boolean }[] = [];

  getMessages() {
    return this.messages;
  }

  handleActions(action: any) {
    switch (action.type) {
      case &#39;MARK_AS_READ&#39;:
        this.messages = this.messages.map((msg) =&gt;
          msg.id === action.payload.id ? { ...msg, read: true } : msg
        );
        this.emit(&#39;change&#39;);
        break;
    }
  }
}

const messageStore = new MessageStore();
dispatcher.register(messageStore.handleActions.bind(messageStore));
export default messageStore;</code></pre>
<h3 id="4-view">4. View</h3>
<p><code>View</code>는 <code>Store</code>로부터 상태를 받아 화면을 렌더링한다. 단방향이기 때문에 View → Store로 직접 접근하지 않고, 오직 Action을 통해 상태를 변경한다.</p>
<pre><code class="language-tsx">// MessageList.tsx (React 기준)
import { useEffect, useState } from &#39;react&#39;;
import messageStore from &#39;./messageStore&#39;;
import { markAsRead } from &#39;./action&#39;;
import dispatcher from &#39;./dispatcher&#39;;

export function MessageList() {
  const [messages, setMessages] = useState(messageStore.getMessages());

  useEffect(() =&gt; {
    const onChange = () =&gt; setMessages(messageStore.getMessages());
    messageStore.on(&#39;change&#39;, onChange);
    return () =&gt; messageStore.removeListener(&#39;change&#39;, onChange);
  }, []);

  const handleClick = (id: number) =&gt; {
    dispatcher.dispatch(markAsRead(id));
  };

  return (
    &lt;ul&gt;
      {messages.map((msg) =&gt; (
        &lt;li key={msg.id} onClick={() =&gt; handleClick(msg.id)}&gt;
          {msg.read ? &lt;s&gt;읽음&lt;/s&gt; : &#39;읽지 않음&#39;}
        &lt;/li&gt;
      ))}
    &lt;/ul&gt;
  );
}</code></pre>
<hr>
<h2 id="왜-mvc가-아닌-flux인가">왜 MVC가 아닌 Flux인가?</h2>
<p>MVC는 규모가 작을 때는 효율적이지만, 애플리케이션이 커질수록 <strong>Model과 View 간의 의존성과 데이터 흐름이 복잡해지는 경향</strong>이 있다. 특히 &quot;읽음 / 읽지 않음&quot; 같은 상태 변경이 여러 컴포넌트에 영향을 줄 때, Model 간 동기화가 어렵고 <strong>버그 추적도 복잡해진다.</strong></p>
<p>반면 Flux는 모든 데이터 흐름을 <strong>Action → Dispatcher → Store → View</strong> 순서로 고정하기 때문에 다음과 같은 장점이 생긴다.</p>
<hr>
<h2 id="flux-패턴의-장점">Flux 패턴의 장점</h2>
<ul>
<li><strong>단방향 데이터 흐름</strong>: 어디에서 상태가 변경됐는지 추적하기 쉬움</li>
<li><strong>상태 일관성 향상</strong>: 하나의 Store에서만 관리하므로 동기화 문제 해결</li>
<li><strong>디버깅과 테스팅이 쉬움</strong>: 액션 기반으로 테스트 가능</li>
<li><strong>유지보수 용이</strong>: 컴포넌트 간 직접 참조가 없어 결합도가 낮음</li>
</ul>
<hr>
<h2 id="redux는-flux-패턴의-진화-버전이다">Redux는 Flux 패턴의 진화 버전이다</h2>
<p>많은 React 개발자들에게 친숙한 <strong>Redux</strong>는 사실 Flux 패턴을 따르는 라이브러리다.
Redux는 Flux의 복잡한 Dispatcher를 없애고 <code>Reducer</code>와 <code>Store</code>만으로 상태 관리를 단순화했다.</p>
<pre><code class="language-ts">// Redux의 간단한 reducer 예시
function messageReducer(state = [], action) {
  switch (action.type) {
    case &#39;MARK_AS_READ&#39;:
      return state.map((msg) =&gt;
        msg.id === action.payload.id ? { ...msg, read: true } : msg
      );
    default:
      return state;
  }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring MVC 패턴 적용 사례]]></title>
            <link>https://velog.io/@blight_k/Spring-MVC-%ED%8C%A8%ED%84%B4-%EC%A0%81%EC%9A%A9-%EC%82%AC%EB%A1%80</link>
            <guid>https://velog.io/@blight_k/Spring-MVC-%ED%8C%A8%ED%84%B4-%EC%A0%81%EC%9A%A9-%EC%82%AC%EB%A1%80</guid>
            <pubDate>Sun, 11 May 2025 09:22:53 GMT</pubDate>
            <description><![CDATA[<h1 id="spring-web-mvc-dispatcherservlet의-요청-처리-흐름-정리">Spring Web MVC: DispatcherServlet의 요청 처리 흐름 정리</h1>
<p>스프링 프레임워크는 다양한 모듈로 구성된 아키텍처를 기반으로 동작한다. 그중 <strong>웹 애플리케이션을 구축할 때 핵심 역할</strong>을 담당하는 것이 <code>spring-web</code> 모듈이며, 이 모듈에서 가장 중심이 되는 컴포넌트는 바로 <code>DispatcherServlet</code>이다.</p>
<p>이번 글에서는 <strong>Spring Web MVC에서 DispatcherServlet이 어떻게 요청을 처리하는지</strong> MVC 패턴을 중심으로 하나씩 살펴보도록 한다.</p>
<hr>
<h2 id="🧭-dispatcherservlet이란">🧭 DispatcherServlet이란?</h2>
<p><code>DispatcherServlet</code>은 프론트 컨트롤러(Front Controller) 패턴을 구현한 서블릿으로, <strong>모든 HTTP 요청의 진입점</strong>이 된다. 웹 애플리케이션에서 클라이언트의 요청을 받아 적절한 컨트롤러로 위임하고, 최종적으로 View를 만들어 사용자에게 응답을 반환하는 역할을 한다.</p>
<p>Spring Boot 프로젝트를 만들면 기본적으로 이 DispatcherServlet이 자동으로 등록되어 있다.</p>
<hr>
<h2 id="요청-처리-흐름-spring-mvc-아키텍처">요청 처리 흐름: Spring MVC 아키텍처</h2>
<p>Spring Web은 전통적인 <strong>MVC(Model-View-Controller)</strong> 패턴을 따른다. 이를 기반으로 <code>DispatcherServlet</code>의 요청 처리 흐름을 살펴보면 다음과 같다.</p>
<pre><code>Client -&gt; DispatcherServlet -&gt; HandlerMapping -&gt; Controller 
       -&gt; Business Logic / Service -&gt; Model -&gt; ViewResolver -&gt; View</code></pre><h3 id="1-클라이언트-요청-수신">1. 클라이언트 요청 수신</h3>
<p>사용자의 브라우저에서 <code>GET /hello</code> 와 같은 요청이 들어오면 이 요청은 <code>DispatcherServlet</code>이 가장 먼저 받는다.</p>
<pre><code class="language-txt">GET /hello HTTP/1.1</code></pre>
<h3 id="2-handlermapping을-통해-컨트롤러-찾기">2. HandlerMapping을 통해 컨트롤러 찾기</h3>
<p><code>DispatcherServlet</code>은 요청을 처리할 적절한 컨트롤러를 찾기 위해 <strong>HandlerMapping</strong>에게 질의한다.
Spring에서는 <code>@RequestMapping</code>, <code>@GetMapping</code>, <code>@PostMapping</code> 등의 어노테이션을 기준으로 어떤 컨트롤러 메서드가 이 요청을 처리할 수 있는지 결정한다.</p>
<pre><code class="language-java">@RestController
public class HelloController {

    @GetMapping(&quot;/hello&quot;)
    public String sayHello() {
        return &quot;hello&quot;;
    }
}</code></pre>
<p>이 경우 <code>/hello</code> 요청은 <code>sayHello()</code> 메서드에 매핑된다.</p>
<h3 id="3-컨트롤러에서-비즈니스-로직-처리">3. 컨트롤러에서 비즈니스 로직 처리</h3>
<p>컨트롤러는 필요하다면 서비스 레이어나 데이터베이스에 접근해서 비즈니스 로직을 처리한다.</p>
<pre><code class="language-java">@Service
public class HelloService {
    public String getMessage() {
        return &quot;Hello from Service&quot;;
    }
}

@RestController
@RequiredArgsConstructor
public class HelloController {

    private final HelloService helloService;

    @GetMapping(&quot;/hello&quot;)
    public String sayHello() {
        return helloService.getMessage();
    }
}</code></pre>
<h3 id="4-모델model에-데이터-저장">4. 모델(Model)에 데이터 저장</h3>
<p>컨트롤러에서 데이터를 준비하면, 이를 <code>Model</code> 객체에 담아 View로 전달할 수 있다.
(만약 <code>@RestController</code>가 아닌 <code>@Controller</code>를 사용하는 경우, 템플릿 엔진을 통한 View 렌더링이 가능하다.)</p>
<pre><code class="language-java">@Controller
public class GreetingController {

    @GetMapping(&quot;/greet&quot;)
    public String greet(Model model) {
        model.addAttribute(&quot;name&quot;, &quot;Spring&quot;);
        return &quot;greeting&quot;; // View 이름
    }
}</code></pre>
<h3 id="5-viewresolver를-통해-view-결정">5. ViewResolver를 통해 View 결정</h3>
<p>컨트롤러가 반환한 문자열(&quot;greeting&quot;)은 <strong>ViewResolver</strong>에 의해 실제 뷰 파일로 매핑된다.
예를 들어, <code>Thymeleaf</code>를 사용하고 있다면 <code>resources/templates/greeting.html</code> 파일을 찾아 렌더링한다.</p>
<pre><code class="language-html">&lt;!-- resources/templates/greeting.html --&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;h1&gt;Hello, [[${name}]]!&lt;/h1&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<h3 id="6-view-렌더링-및-사용자-응답">6. View 렌더링 및 사용자 응답</h3>
<p>최종적으로 View가 렌더링되어 사용자에게 HTML 응답으로 전송된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ReactNode와 ReactElement의 차이]]></title>
            <link>https://velog.io/@blight_k/ReactNode%EC%99%80-ReactElement%EC%9D%98-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@blight_k/ReactNode%EC%99%80-ReactElement%EC%9D%98-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Fri, 09 May 2025 04:50:24 GMT</pubDate>
            <description><![CDATA[<h2 id="타입의-범위">타입의 범위</h2>
<p>세 가지 타입은 아래와 같이 포괄 관계를 가진다:</p>
<pre><code>ReactNode &gt; ReactElement ≒ JSX.Element</code></pre><ul>
<li><code>ReactNode</code>: 가장 포괄적인 타입. 문자열, 숫자, boolean, <code>null</code>, <code>undefined</code>, <code>ReactElement</code>, <code>ReactFragment</code>, <code>ReactPortal</code> 등 모두 포함된다.</li>
<li><code>ReactElement</code>: 컴포넌트의 반환값으로 가장 많이 사용되는 타입. JSX의 결과물이기도 하다.</li>
<li><code>JSX.Element</code>: <code>ReactElement&lt;any, any&gt;</code>의 타입 alias로 거의 같은 개념이다.</li>
</ul>
<hr>
<h2 id="reactelement란">ReactElement란?</h2>
<p><code>ReactElement</code>는 컴포넌트가 반환하는 객체의 형태를 타입으로 정의한 것이다.</p>
<pre><code class="language-ts">interface ReactElement&lt;P = any, T extends string | JSXElementConstructor&lt;any&gt; = string | JSXElementConstructor&lt;any&gt;&gt; {
  type: T;
  props: P;
  key: Key | null;
}</code></pre>
<p>즉, JSX 문법을 사용하면 내부적으로 <code>React.createElement()</code> 함수가 호출되어 <code>ReactElement</code> 객체를 생성한다.</p>
<p>예를 들어 다음 JSX 코드를:</p>
<pre><code class="language-tsx">&lt;CustomButton disabled&gt;버튼&lt;/CustomButton&gt;</code></pre>
<p>바벨은 아래처럼 트랜스파일링 한다:</p>
<pre><code class="language-js">React.createElement(CustomButton, { disabled: true }, &quot;버튼&quot;)</code></pre>
<hr>
<h2 id="jsxelement란">JSX.Element란?</h2>
<p><code>JSX.Element</code>는 전역 <code>JSX</code> 네임스페이스에 정의되어 있으며, <code>React.ReactElement&lt;any, any&gt;</code>와 같다.</p>
<pre><code class="language-ts">declare global {
  namespace JSX {
    interface Element extends React.ReactElement&lt;any, any&gt; {}
  }
}</code></pre>
<p>따라서 함수형 컴포넌트에서 반환하는 타입으로 <code>JSX.Element</code> 또는 <code>ReactElement</code>를 사용할 수 있다. 사실상 이 둘은 동일하다고 봐도 무방하다.</p>
<pre><code class="language-tsx">function MyComponent(): JSX.Element {
  return &lt;div&gt;Hello&lt;/div&gt;;
}</code></pre>
<hr>
<h2 id="reactnode란">ReactNode란?</h2>
<p><code>ReactNode</code>는 JSX에서 표현할 수 있는 모든 값을 포함하는 타입이다. 대표적으로 다음과 같은 타입들이 포함된다:</p>
<ul>
<li><code>string</code>, <code>number</code></li>
<li><code>boolean</code> (렌더링되지 않음)</li>
<li><code>null</code>, <code>undefined</code></li>
<li><code>ReactElement</code></li>
<li><code>ReactFragment</code> (여러 요소를 묶는 용도)</li>
<li><code>ReactPortal</code> (DOM 외부로 렌더링)</li>
</ul>
<p>예를 들어 <code>children</code> 타입에 가장 흔하게 사용되는 타입이다:</p>
<pre><code class="language-ts">type PropsWithChildren&lt;P&gt; = P &amp; { children?: ReactNode };</code></pre>
<pre><code class="language-tsx">function Layout({ children }: { children: ReactNode }) {
  return &lt;div&gt;{children}&lt;/div&gt;;
}</code></pre>
<hr>
<h2 id="언제-어떤-타입을-써야-할까">언제 어떤 타입을 써야 할까?</h2>
<table>
<thead>
<tr>
<th>상황</th>
<th>타입 추천</th>
<th>이유</th>
</tr>
</thead>
<tbody><tr>
<td>함수형 컴포넌트 반환값</td>
<td><code>ReactElement</code> or <code>JSX.Element</code></td>
<td>대부분 JSX를 반환하므로</td>
</tr>
<tr>
<td>children prop</td>
<td><code>ReactNode</code></td>
<td>텍스트, 태그, 배열 등 모든 표현 가능</td>
</tr>
<tr>
<td>UI를 동적으로 조립할 때</td>
<td><code>ReactNode</code></td>
<td>배열, 조건부 렌더링 등을 지원해야 함</td>
</tr>
<tr>
<td>특정 엘리먼트를 명확히 감쌀 때</td>
<td><code>ReactElement</code></td>
<td><code>React.cloneElement()</code> 등과 같이 엘리먼트 자체를 다룰 때</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[MVC / MVP / MVVM 패턴이란 ?]]></title>
            <link>https://velog.io/@blight_k/MVC-MVP-MVVM-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@blight_k/MVC-MVP-MVVM-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Sat, 03 May 2025 08:05:24 GMT</pubDate>
            <description><![CDATA[<h2 id="mvc-패턴model---view---controller">MVC 패턴(Model - View - Controller)</h2>
<p>MVC 패턴은 애플리케이션을 <strong>모델(Model)</strong>, <strong>뷰(View)</strong>, <strong>컨트롤러(Controller)</strong>의 세 가지 역할로 나누어 구성하는 디자인 패턴입니다.</p>
<h3 id="●-모델model">● 모델(Model)</h3>
<p>모델은 애플리케이션의 핵심 데이터와 그 데이터를 처리하는 로직을 담당합니다. 데이터베이스, 상수, 변수 등이 이에 해당하며, 컨트롤러를 통해 데이터를 생성, 수정, 삭제합니다. 뷰나 컨트롤러에 의존하지 않고 독립적으로 존재합니다.</p>
<h3 id="●-뷰view">● 뷰(View)</h3>
<p>뷰는 사용자 인터페이스(UI)를 구성합니다. 예를 들어 입력 박스, 체크박스, 버튼과 같이 사용자가 직접 눈으로 보거나 상호작용하는 요소들이 뷰에 해당합니다. 뷰는 데이터를 직접 처리하지 않고, 데이터를 모델로부터 전달받아 화면에 표시하는 역할을 합니다.</p>
<h3 id="●-컨트롤러controller">● 컨트롤러(Controller)</h3>
<p>컨트롤러는 뷰와 모델 사이를 중재하는 역할을 합니다. 사용자의 입력 이벤트(클릭, 입력 등)를 처리하고, 적절한 모델을 갱신하거나 뷰를 업데이트합니다. 메인 로직을 처리하는 중심 축이라고 볼 수 있습니다.</p>
<hr>
<h3 id="mvc-패턴의-장점">MVC 패턴의 장점</h3>
<ol>
<li>역할이 명확히 분리되어 있어 유지보수 및 개발 효율이 높습니다.</li>
<li>뷰와 모델이 분리되어 있어 재사용성과 테스트가 용이합니다.</li>
<li>규모가 큰 애플리케이션에 적합하며 확장성이 좋습니다.</li>
</ol>
<h3 id="mvc-패턴의-단점">MVC 패턴의 단점</h3>
<ul>
<li>애플리케이션이 복잡해질수록 <strong>모델과 뷰 사이의 간접적인 의존 관계</strong>가 생기고, 구조가 뒤얽힐 수 있습니다. 특히 뷰가 모델을 직접 참조하거나 변경하게 되면 의존성이 증가하게 됩니다.</li>
</ul>
<h3 id="대표적인-프레임워크">대표적인 프레임워크</h3>
<ul>
<li>Java: <strong>Spring Framework</strong></li>
<li>Python: <strong>Django</strong></li>
<li>JavaScript: <strong>Express.js</strong></li>
</ul>
<hr>
<h2 id="mvp-패턴model---view---presenter">MVP 패턴(Model - View - Presenter)</h2>
<p>MVP 패턴은 MVC 패턴에서 컨트롤러가 **프레젠터(Presenter)**로 대체된 구조입니다. 가장 큰 특징은 **뷰(View)**와 **프레젠터(Presenter)**가 <strong>1:1 관계</strong>를 가진다는 점입니다.</p>
<h3 id="●-presenter">● Presenter</h3>
<p>Presenter는 사용자 인터페이스 로직을 처리하며, 뷰와 모델 간의 완전한 분리를 가능하게 합니다. 뷰는 가능한 한 단순하게 유지되며, 모든 로직은 프레젠터가 담당합니다. 뷰는 프레젠터에 대한 인터페이스만 알고 있어 테스트가 용이합니다.</p>
<h3 id="mvp-패턴의-특징">MVP 패턴의 특징</h3>
<ul>
<li>뷰와 프레젠터가 강한 결합을 가지기 때문에 뷰당 프레젠터가 하나씩 필요합니다.</li>
<li>테스트 코드 작성이 수월하며, 복잡한 UI 로직을 분리하기에 좋습니다.</li>
</ul>
<hr>
<h2 id="mvvm-패턴model---view---viewmodel">MVVM 패턴(Model - View - ViewModel)</h2>
<p>MVVM 패턴은 MVC 패턴에서 컨트롤러가 <strong>뷰모델(ViewModel)</strong>로 대체된 구조입니다. <strong>뷰(View)</strong>와 <strong>뷰모델(ViewModel)</strong> 간에는 <strong>양방향 데이터 바인딩</strong>이 존재합니다. 이 구조는 특히 UI와 데이터 상태가 밀접하게 연동되는 애플리케이션에 적합합니다.</p>
<h3 id="viewmodel">ViewModel</h3>
<p>ViewModel은 뷰의 상태와 동작을 관리하며, 뷰와 모델 간의 중간 매개체 역할을 합니다. 뷰모델은 커맨드(Command)와 바인딩(Binding) 개념을 통해 뷰와 동기화됩니다.</p>
<ul>
<li><strong>데이터 바인딩</strong>: 모델의 데이터가 변경되면 자동으로 뷰에 반영되고, 뷰의 입력도 모델에 자동으로 전달됩니다.</li>
<li><strong>커맨드</strong>: 버튼 클릭 등 여러 UI 요소에 대한 이벤트 처리를 하나의 액션 단위로 묶는 기법입니다.</li>
</ul>
<h3 id="mvvm-패턴의-장점">MVVM 패턴의 장점</h3>
<ul>
<li>코드의 <strong>분리도</strong>가 높고, 뷰에 대한 테스트가 매우 용이합니다.</li>
<li>데이터 바인딩을 통해 코드량이 줄고 생산성이 향상됩니다.</li>
</ul>
<h3 id="mvvm-패턴의-단점">MVVM 패턴의 단점</h3>
<ul>
<li>데이터 바인딩을 제대로 이해하지 않으면 디버깅이 어려울 수 있습니다.</li>
<li>단순한 프로젝트에 적용할 경우 오히려 복잡도가 증가할 수 있습니다.</li>
</ul>
<h3 id="대표적인-프레임워크-1">대표적인 프레임워크</h3>
<ul>
<li>JavaScript: <strong>Vue.js</strong>, <strong>Angular</strong></li>
<li>.NET: <strong>WPF</strong>, <strong>Xamarin</strong></li>
<li>Kotlin: <strong>Jetpack Compose</strong>, <strong>Android ViewModel</strong></li>
</ul>
<hr>
<table>
<thead>
<tr>
<th>패턴</th>
<th>핵심 구조</th>
<th>특징</th>
<th>대표 프레임워크</th>
</tr>
</thead>
<tbody><tr>
<td>MVC</td>
<td>Model - View - Controller</td>
<td>역할 분리, 대규모에 적합</td>
<td>Spring, Django</td>
</tr>
<tr>
<td>MVP</td>
<td>Model - View - Presenter</td>
<td>테스트 용이, 강한 1:1 관계</td>
<td>Android MVP</td>
</tr>
<tr>
<td>MVVM</td>
<td>Model - View - ViewModel</td>
<td>양방향 바인딩, UI 중심 앱에 적합</td>
<td>Vue, WPF, Jetpack Compose</td>
</tr>
</tbody></table>
<p>각 패턴은 목적과 사용하는 플랫폼에 따라 다르게 적용됩니다. 프로젝트의 규모, 테스트 요구사항, UI 복잡성 등을 고려하여 적절한 아키텍처 패턴을 선택하는 것이 중요합니다.</p>
]]></description>
        </item>
    </channel>
</rss>