<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Developer_TaeHui</title>
    <link>https://tae-hui.tistory.com/</link>
    <description> 창업, 사업, 자기개발, 운동, Web, App, Java, python, 이슈, 개발자, JavaScript, amazon, cloud server, 취업, 스펙, Android Studio, Spring, React, Node.js, 구독하면 댓글 남겨주세요.</description>
    <language>ko</language>
    <pubDate>Wed, 6 May 2026 15:34:39 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>TaeHuiLee</managingEditor>
    <image>
      <title>Developer_TaeHui</title>
      <url>https://tistory1.daumcdn.net/tistory/4865888/attach/69ed5fe94f0b4febabb6e438fc6ef64a</url>
      <link>https://tae-hui.tistory.com</link>
    </image>
    <item>
      <title>Kafka란 무엇인가?</title>
      <link>https://tae-hui.tistory.com/entry/Kafka%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;code&gt;Apache Kafka&lt;/code&gt;&lt;/b&gt;는 &lt;b&gt;&lt;code&gt;분산 메시징 플랫폼&lt;/code&gt;&lt;/b&gt;으로, 데이터를 &lt;b&gt;&lt;code&gt;생산자(Producer)&lt;/code&gt;&lt;/b&gt;에서 &lt;b&gt;&lt;code&gt;소비자(Consumer)&lt;/code&gt;&lt;/b&gt;로 빠르고 안정적으로 전달하기 위해 설계된 시스템입니다. 처음 LinkedIn에서 개발되었으며, 현재는 오픈소스로 전 세계적으로 사용되고 있습니다. Kafka는 단순한 &lt;b&gt;&lt;code&gt;메시징 큐&lt;/code&gt;&lt;/b&gt; 이상의 기능을 제공하며, &lt;b&gt;&lt;code&gt;대규모 실시간 데이터 스트리밍&lt;/code&gt;&lt;/b&gt; 및 &lt;b&gt;&lt;code&gt;분산 처리&lt;/code&gt;&lt;/b&gt;를 가능하게 합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.   &lt;code&gt;Kafka 개요&lt;/code&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kafka는 &lt;b&gt;&lt;code&gt;Publish-Subscribe 모델&lt;/code&gt;&lt;/b&gt; 기반으로 동작하며, 다양한 데이터 소스에서 발생하는 데이터를 처리하는 데 사용됩니다. 이를 통해 &lt;b&gt;&lt;code&gt;실시간 데이터 스트림&lt;/code&gt;&lt;/b&gt;을 관리하거나, 데이터를 저장소로 전송하는 &lt;b&gt;&lt;code&gt;데이터 파이프라인&lt;/code&gt;&lt;/b&gt; 역할을 수행합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Kafka의 주요 구성 요소&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;  Producer (생산자):&lt;/b&gt; 데이터를 생성하고 Kafka의 특정 &lt;b&gt;&lt;code&gt;토픽(Topic)&lt;/code&gt;&lt;/b&gt;으로 전송합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;  Broker (브로커):&lt;/b&gt; 데이터를 저장하고 관리하며, 여러 브로커가 &lt;b&gt;&lt;code&gt;클러스터&lt;/code&gt;&lt;/b&gt;를 형성하여 데이터를 &lt;b&gt;&lt;code&gt;분산 처리&lt;/code&gt;&lt;/b&gt;합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;  Consumer (소비자):&lt;/b&gt; 데이터를 읽고 처리하는 애플리케이션입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;  Topic (토픽):&lt;/b&gt; 데이터를 분류하기 위한 논리적인 단위입니다. 각 토픽은 여러 개의 &lt;b&gt;&lt;code&gt;Partition(파티션)&lt;/code&gt;&lt;/b&gt;으로 나뉩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;  Partition (파티션):&lt;/b&gt; 데이터를 물리적으로 나누어 저장하는 단위입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;  ZooKeeper/Kafka Raft:&lt;/b&gt; 클러스터 상태와 노드 메타데이터를 관리합니다. (최신 Kafka 버전에서는 &lt;b&gt;&lt;code&gt;Raft&lt;/code&gt;&lt;/b&gt;로 &lt;b&gt;&lt;code&gt;ZooKeeper&lt;/code&gt;&lt;/b&gt;를 대체)&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Kafka의 핵심 개념&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;br /&gt;Kafka는 &lt;b&gt;&lt;code&gt;분산 시스템&lt;/code&gt;&lt;/b&gt;으로 설계되어 높은 가용성과 확장성을 제공합니다. 데이터를 여러 노드에 분산 저장하여 성능을 최적화하고 장애에도 대비합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;  내구성(Durability):&lt;/b&gt;&lt;br /&gt;Kafka는 데이터를 &lt;b&gt;&lt;code&gt;디스크에 저장&lt;/code&gt;&lt;/b&gt;하며, 데이터를 복제하여 안정성을 유지합니다. 데이터 손실을 최소화하고 필요할 경우 재처리를 지원합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;  오프셋(Offset):&lt;/b&gt;&lt;br /&gt;Kafka는 데이터를 순차적으로 처리하기 위해 &lt;b&gt;&lt;code&gt;오프셋&lt;/code&gt;&lt;/b&gt; 개념을 사용합니다. 소비자는 오프셋을 기준으로 데이터를 읽으며, 원하는 시점으로 돌아가 데이터를 다시 처리할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.   &lt;code&gt;Kafka 데이터 처리 방식&lt;/code&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kafka의 데이터 처리 방식은 &lt;b&gt;&lt;code&gt;생산자 &amp;rarr; 브로커 &amp;rarr; 소비자&lt;/code&gt;&lt;/b&gt;의 흐름으로 이루어집니다. 각 단계에서 특정한 기능과 특징이 적용됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1️⃣   데이터 생성: &lt;code&gt;Producer&lt;/code&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;code&gt;생산자&lt;/code&gt;&lt;/b&gt;는 데이터를 생성한 후 Kafka의 특정 &lt;b&gt;&lt;code&gt;토픽&lt;/code&gt;&lt;/b&gt;으로 데이터를 보냅니다.&lt;/li&gt;
&lt;li&gt;데이터를 보낼 때, &lt;b&gt;&lt;code&gt;Round-Robin 방식&lt;/code&gt;&lt;/b&gt; 또는 &lt;b&gt;&lt;code&gt;Key 기반 해싱&lt;/code&gt;&lt;/b&gt;으로 특정 &lt;b&gt;&lt;code&gt;Partition&lt;/code&gt;&lt;/b&gt;에 데이터가 저장됩니다.&lt;/li&gt;
&lt;li&gt;Kafka는 프로듀서가 보낸 데이터를 &lt;b&gt;&lt;code&gt;배치 처리&lt;/code&gt;&lt;/b&gt;하여 네트워크 효율성을 높입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2️⃣   데이터 저장: &lt;code&gt;Broker&lt;/code&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Kafka의 브로커는 데이터를 &lt;b&gt;&lt;code&gt;Partition&lt;/code&gt;&lt;/b&gt; 단위로 저장합니다.&lt;/li&gt;
&lt;li&gt;각 Partition은 여러 브로커에 &lt;b&gt;&lt;code&gt;복제&lt;/code&gt;&lt;/b&gt;되어 안정성을 높입니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예: 복제 계수(Replication Factor)가 3인 경우, 동일한 데이터가 3개의 브로커에 저장됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Kafka는 데이터를 디스크에 저장하지만, 데이터 쓰기 및 읽기가 매우 빠릅니다. 이는 &lt;b&gt;&lt;code&gt;순차적 I/O&lt;/code&gt;&lt;/b&gt;를 활용하기 때문입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3️⃣   데이터 소비: &lt;code&gt;Consumer&lt;/code&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;code&gt;소비자&lt;/code&gt;&lt;/b&gt;는 Kafka에서 데이터를 읽어오는 역할을 합니다.&lt;/li&gt;
&lt;li&gt;소비자는 각 &lt;b&gt;&lt;code&gt;Partition&lt;/code&gt;&lt;/b&gt;에서 데이터를 &lt;b&gt;&lt;code&gt;오프셋 기반&lt;/code&gt;&lt;/b&gt;으로 읽습니다. 이렇게 하면 데이터를 &lt;b&gt;&lt;code&gt;실시간&lt;/code&gt;&lt;/b&gt;으로 처리하거나, 과거 데이터를 다시 읽는 &lt;b&gt;&lt;code&gt;재처리&lt;/code&gt;&lt;/b&gt;가 가능합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;소비자 그룹 (Consumer Group):&lt;/code&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;각 소비자는 Partition을 독점적으로 처리하며, 부하가 분산됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Kafka 데이터 처리의 특징&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;Kafka는 데이터를 실시간으로 처리하면서도, 데이터의 배치 처리 및 재처리를 지원합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;  Pull 기반 처리:&lt;/b&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;code&gt;요청(Pull)&lt;/code&gt;&lt;/b&gt; 하여 가져오는 방식입니다. 이는 RabbitMQ 같은 &lt;b&gt;&lt;code&gt;Push 기반&lt;/code&gt;&lt;/b&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;Kafka는 데이터를 &lt;b&gt;&lt;code&gt;로그 파일&lt;/code&gt;&lt;/b&gt; 형태로 저장하며, 설정에 따라 특정 기간 동안 데이터를 유지합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.   &lt;code&gt;Kafka의 성능&lt;/code&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kafka는 대규모 데이터를 빠르고 안정적으로 처리할 수 있도록 설계되었습니다. 아래는 Kafka 성능의 주요 특징입니다:&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1️⃣ ⚡ &lt;code&gt;처리량&lt;/code&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Kafka는 &lt;b&gt;&lt;code&gt;초당 수백만 건의 메시지&lt;/code&gt;&lt;/b&gt;를 처리할 수 있습니다.&lt;/li&gt;
&lt;li&gt;데이터 처리량은 &lt;b&gt;&lt;code&gt;파티션 수&lt;/code&gt;&lt;/b&gt;와 &lt;b&gt;&lt;code&gt;브로커 수&lt;/code&gt;&lt;/b&gt;에 따라 확장 가능하며, 클러스터의 크기를 조정하여 성능을 높일 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2️⃣ ⏱ &lt;code&gt;낮은 지연 시간&lt;/code&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Kafka는 &lt;b&gt;&lt;code&gt;밀리초(ms)&lt;/code&gt;&lt;/b&gt; 단위의 지연 시간으로 데이터를 처리합니다.&lt;/li&gt;
&lt;li&gt;실시간 데이터 처리에 적합하며, 실시간 분석, IoT 애플리케이션, 로그 처리에 널리 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3️⃣   &lt;code&gt;확장성&lt;/code&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Kafka는 새로운 브로커를 추가함으로써 &lt;b&gt;&lt;code&gt;클러스터 크기&lt;/code&gt;&lt;/b&gt;를 동적으로 확장할 수 있습니다.&lt;/li&gt;
&lt;li&gt;각 브로커가 독립적으로 동작하므로, 장애가 발생해도 전체 시스템에 영향을 주지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4️⃣   &lt;code&gt;내구성&lt;/code&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Kafka는 데이터를 &lt;b&gt;&lt;code&gt;디스크에 저장&lt;/code&gt;&lt;/b&gt;하며, 데이터 손실을 방지하기 위해 &lt;b&gt;&lt;code&gt;복제&lt;/code&gt;&lt;/b&gt;합니다.&lt;/li&gt;
&lt;li&gt;장애 복구 시 복제된 데이터를 기반으로 복원이 가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5️⃣   &lt;code&gt;안정성&lt;/code&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Kafka는 분산 환경에서 노드 간 통신을 효율적으로 관리하여 &lt;b&gt;&lt;code&gt;안정적인 메시징&lt;/code&gt;&lt;/b&gt;을 제공합니다.&lt;/li&gt;
&lt;li&gt;클러스터를 통해 데이터가 특정 노드에만 의존하지 않도록 설계되어 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. ⚖️ &lt;code&gt;RabbitMQ와의 차이점&lt;/code&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kafka와 RabbitMQ는 모두 메시징 시스템이지만, 설계 철학과 사용 사례에서 큰 차이가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특징&lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;Kafka&lt;/code&gt;&lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;RabbitMQ&lt;/code&gt;&lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;아키텍처&lt;/code&gt;분산 시스템, 브로커 기반중앙 집중형 메시징 큐&lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;메시징 모델&lt;/code&gt;Publish-Subscribe메시지 큐 (Queue)&lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;데이터 전달 방식&lt;/code&gt;Pull 기반Push 기반&lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;데이터 저장&lt;/code&gt;디스크 기반 (로그 보관 가능)메모리 우선 (빠른 처리)&lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;복잡성&lt;/code&gt;클러스터 설정 및 운영이 복잡상대적으로 간단한 설정&lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;실시간성&lt;/code&gt;밀리초 수준 (약간의 지연)매우 낮은 지연 시간 (실시간 처리)&lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;확장성&lt;/code&gt;브로커와 Partition 추가로 높은 확장성확장성은 제한적&lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;사용 사례&lt;/code&gt;대규모 데이터 처리, 로그 분석실시간 알림, 요청-응답 구조&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  RabbitMQ가 더 적합한 경우&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;code&gt;실시간 처리&lt;/code&gt;&lt;/b&gt;가 중요한 경우 (예: 알림 서비스, 요청-응답)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;소규모 메시징&lt;/code&gt;&lt;/b&gt; 환경에서 단순하고 빠른 설정이 필요한 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Kafka가 더 적합한 경우&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;code&gt;대규모 데이터 스트리밍&lt;/code&gt;&lt;/b&gt;이 필요한 경우 (예: 로그 수집, 데이터 파이프라인)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;높은 내구성&lt;/code&gt;&lt;/b&gt;과 데이터 보존이 중요한 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.   Kafka를 활용한 시스템 설계(결제 시스템)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;/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;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;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;b&gt;재처리(재시도)&lt;/b&gt; 및 &lt;b&gt;데드 레터 큐(Dead Letter Queue)&lt;/b&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ Kafka와 Spring Boot로 설계한 시스템 구조&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;1. 사용자 &amp;rarr; 2. 주문 서비스(Order Service) &amp;rarr; 3. Kafka (`order-created` 이벤트)
    &amp;darr;                                  ↘
4. 재고 서비스(Inventory Service)        6. 결제 서비스(Payment Service)
    &amp;darr;                                  ↘
5. Kafka (`inventory-confirmed` or       7. Kafka (`payment-success` or
   `inventory-failed` 이벤트)               `payment-failure` 이벤트)
    &amp;darr;                                  ↘
8. 알림 서비스(Notification Service) &amp;larr; 9. Kafka&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  단계별 구현 및 실패 처리 전략&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1️⃣ 주문 생성 (Order Create)&lt;/h4&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;: 사용자의 요청을 받아 주문 정보를 저장하고 Kafka에 &lt;code&gt;order-created&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;Kafka로 메시지 발행 실패 시 &lt;b&gt;재시도&lt;/b&gt; 로직 추가.&lt;/li&gt;
&lt;li&gt;DB 트랜잭션과 Kafka 발행 간 &lt;b&gt;트랜잭션 동기화&lt;/b&gt;(e.g., Kafka Transactional API) 구현.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@RestController
@RequestMapping(&quot;/orders&quot;)
public class OrderController {

    @Autowired
    private KafkaTemplate&amp;lt;String, Order&amp;gt; kafkaTemplate;

    @PostMapping
    public ResponseEntity&amp;lt;String&amp;gt; createOrder(@RequestBody Order order) {
        try {
            // 주문 저장 (DB)
            orderService.saveOrder(order);

            // Kafka에 이벤트 발행
            kafkaTemplate.send(&quot;order-created&quot;, order);
            return ResponseEntity.ok(&quot;Order Created Successfully!&quot;);
        } catch (Exception e) {
            // 예외 처리: Kafka 발행 실패 시 로깅 및 재시도
            log.error(&quot;Failed to create order: &quot;, e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(&quot;Failed to create order&quot;);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2️⃣ 재고 확인 (Inventory Check)&lt;/h4&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;code&gt;order-created&lt;/code&gt; 이벤트를 구독하고 재고를 확인합니다. 결과에 따라 &lt;code&gt;inventory-confirmed&lt;/code&gt; 또는 &lt;code&gt;inventory-failed&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;b&gt;재고 확인 실패&lt;/b&gt;: Kafka 재시도를 설정하거나, 실패 메시지를 &lt;b&gt;데드 레터 큐(Dead Letter Queue)&lt;/b&gt;로 전송.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;재시도&lt;/b&gt;: 특정 횟수(예: 3회) 재시도 후 실패하면 알림 서비스로 전달.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;axapta&quot;&gt;&lt;code&gt;@KafkaListener(topics = &quot;order-created&quot;, groupId = &quot;inventory-service&quot;)
public void checkInventory(Order order) {
    try {
        boolean inventoryAvailable = inventoryService.checkInventory(order.getItems());
        if (inventoryAvailable) {
            kafkaTemplate.send(&quot;inventory-confirmed&quot;, order);
        } else {
            kafkaTemplate.send(&quot;inventory-failed&quot;, order);
        }
    } catch (Exception e) {
        // 실패 처리 로직
        log.error(&quot;Inventory check failed for order: &quot; + order.getOrderId(), e);

        // 데드 레터 큐로 전송
        kafkaTemplate.send(&quot;inventory-dlq&quot;, order);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3️⃣ 결제 처리 (Payment Process)&lt;/h4&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;code&gt;inventory-confirmed&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;b&gt;결제 실패&lt;/b&gt;: Kafka 이벤트로 &lt;code&gt;payment-failure&lt;/code&gt; 발행 후 알림 서비스에서 처리.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;결제 시스템 장애&lt;/b&gt;: Kafka 재시도 설정으로 복구 시도.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;axapta&quot;&gt;&lt;code&gt;@KafkaListener(topics = &quot;inventory-confirmed&quot;, groupId = &quot;payment-service&quot;)
public void processPayment(Order order) {
    try {
        boolean paymentSuccess = paymentService.process(order);
        if (paymentSuccess) {
            kafkaTemplate.send(&quot;payment-success&quot;, order);
        } else {
            kafkaTemplate.send(&quot;payment-failure&quot;, order);
        }
    } catch (Exception e) {
        // 예외 발생 시 재시도 또는 데드 레터 큐 처리
        log.error(&quot;Payment processing failed for order: &quot; + order.getOrderId(), e);
        kafkaTemplate.send(&quot;payment-dlq&quot;, order);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4️⃣ 사용자 알림 (Notification)&lt;/h4&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;code&gt;payment-success&lt;/code&gt; 또는 &lt;code&gt;payment-failure&lt;/code&gt; 이벤트를 구독하여 사용자에게 알림(SMS, Email 등)을 보냅니다.&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;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@KafkaListener(topics = {&quot;payment-success&quot;, &quot;payment-failure&quot;}, groupId = &quot;notification-service&quot;)
public void sendNotification(Order order) {
    try {
        if (&quot;payment-success&quot;.equals(order.getStatus())) {
            notificationService.sendSuccessNotification(order);
        } else {
            notificationService.sendFailureNotification(order);
        }
    } catch (Exception e) {
        // 알림 실패 처리
        log.error(&quot;Notification failed for order: &quot; + order.getOrderId(), e);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;Kafka 재시도&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Spring Kafka의 &lt;code&gt;RetryTemplate&lt;/code&gt;을 사용하여 메시지 재처리.&lt;/li&gt;
&lt;li&gt;설정 예시:&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1737137328033&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  kafka:
    # Kafka 브로커 주소 설정 (클러스터의 여러 브로커를 쉼표로 구분하여 추가 가능)
    producer:
      bootstrap-servers: localhost:9092

      # 메시지 전송 보장 수준 설정
      # all: 모든 복제본(ISR)에 저장 완료 후 응답 (최고의 신뢰성)
      # 1: 리더 브로커만 저장 후 응답 (성능 향상, 일부 유실 가능성)
      # 0: 브로커 응답을 기다리지 않음 (유실 가능성 높음, 매우 빠름)
      acks: all 

      # 전송 실패 시 재시도 횟수 (일시적 네트워크 장애 등 대응)
      retries: 5

      # Producer가 메시지를 일정 크기(16KB)까지 모아서 전송하여 성능 최적화
      batch-size: 16384

      # 메시지를 전송하기 전 대기하는 시간 (단위: ms, 대기 후 배치 전송)
      linger.ms: 5

      # 메시지 버퍼 크기 (Producer가 메시지를 전송하기 전까지 보관할 메모리 크기)
      buffer-memory: 33554432

      # 메시지 중복 방지를 위해 동시에 처리할 수 있는 최대 요청 수
      max-in-flight-requests-per-connection: 5

      # Producer에서 메시지를 직렬화할 때 사용할 Serializer (String 기반)
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.apache.kafka.common.serialization.StringSerializer

    consumer:
      # 자동 커밋을 비활성화하여, 수동으로 메시지를 확인 후 커밋 (데이터 유실 방지)
      enable-auto-commit: false

      # Consumer가 새 컨슈머 그룹으로 시작할 때 어디서부터 데이터를 읽을지 지정
      # earliest: 가장 오래된 메시지부터 소비
      # latest: 가장 최근의 메시지부터 소비
      auto-offset-reset: earliest

      # 한 번에 가져올 최대 메시지 수 (너무 크면 메모리 부족 위험)
      max-poll-records: 100

      # 최소 가져올 데이터 크기 (50KB 이상의 데이터가 쌓일 때 가져오도록 설정)
      fetch-min-size: 50000

      # 데이터를 기다리는 최대 시간 (단위: ms, 적절한 값 조정 필요)
      fetch-max-wait: 500

      # 컨슈머가 브로커에게 보내는 하트비트 간격 (단위: ms)
      heartbeat-interval: 3000

      # 컨슈머가 일정 시간 내에 하트비트를 보내지 않으면 그룹에서 제거 (단위: ms)
      session-timeout-ms: 10000

      # Consumer에서 메시지를 역직렬화할 때 사용할 Deserializer (String 기반)
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.apache.kafka.common.serialization.StringDeserializer

    listener:
      # 컨슈머가 메시지를 배치 단위로 수신하여 처리 (성능 최적화)
      type: batch

      # 특정 토픽이 존재하지 않을 때 예외를 발생시키지 않도록 설정
      missing-topics-fatal: false

      # 메시지 처리 실패 시 최대 5번까지 재시도 (오류 복구 로직을 위해 설정)
      retry:
        max-attempts: 5

      # 재시도 간 간격 설정 (1초부터 시작, 실패할 때마다 증가)
      backoff:
        initial-interval: 1000  # 첫 번째 재시도 간격 (1초)
        multiplier: 2.0         # 다음 재시도까지의 시간 배수 증가 (1초 &amp;rarr; 2초 &amp;rarr; 4초)
        max-interval: 10000     # 최대 재시도 간격 (10초)

      # 메시지 처리가 완료된 후 수동으로 오프셋을 커밋하도록 설정 (acknowledgment 필요)
      ack-mode: manual_immediate

      # 메시지 처리 실패 시 Dead Letter Queue(DLQ)에 저장 (재처리 및 분석)
      error-handler:
        dead-letter-publish: true&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;데드 레터 큐(Dead Letter Queue)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Listener처리 실패 메시지를 별도의 Kafka 토픽으로 이동.&lt;/li&gt;
&lt;li&gt;실패한 데이터를 분석 및 복구하는 데 사용.&lt;/li&gt;
&lt;li&gt;Spring Kafka에서 &lt;b&gt;데드 레터 큐&lt;/b&gt;를 활성화하려면 &lt;code&gt;error-handling&lt;/code&gt; 설정을 추가합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;3&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;Kafka 트랜잭션 API를 사용하여 데이터베이스 작업과 Kafka 이벤트 발행을 원자적으로 처리.&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;Kafka와 Spring Boot의 &lt;b&gt;Actuator&lt;/b&gt;를 활용한 실시간 모니터링.&lt;/li&gt;
&lt;li&gt;예외 발생 시 &lt;b&gt;로깅 및 알림&lt;/b&gt;을 통해 장애 대응.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Outbox 패턴&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Outbox&lt;/code&gt; 패턴은 Kafka와 RDBMS를 함께 사용하는 환경에서 데이터 일관성과 신뢰성을 보장&lt;/li&gt;
&lt;li&gt;Kafka 발행 실패와 같은 상황에서 메시지 유실을 방지하며 안정적인 시스템을 설계&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;오류 처리 핵심&lt;/b&gt;: 재시도, 데드 레터 큐, 트랜잭션 관리, 모니터링을 통해 시스템 신뢰성을 극대화합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&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;code&gt;Kafka&lt;/code&gt;의 동작 방식:&lt;/b&gt;&lt;br /&gt;&lt;code&gt;Kafka&lt;/code&gt;는 &lt;b&gt;&lt;code&gt;Producer&lt;/code&gt;&lt;/b&gt;, &lt;b&gt;&lt;code&gt;Broker&lt;/code&gt;&lt;/b&gt;, &lt;b&gt;&lt;code&gt;Consumer&lt;/code&gt;&lt;/b&gt;, &lt;b&gt;&lt;code&gt;Topic&lt;/code&gt;&lt;/b&gt;, &lt;b&gt;&lt;code&gt;Partition&lt;/code&gt;&lt;/b&gt; 등으로 구성된 &lt;b&gt;&lt;code&gt;분산 메시징 시스템&lt;/code&gt;&lt;/b&gt;으로, 데이터를 생성, 저장, 소비하는 과정에서 높은 확장성과 안정성을 제공합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;Kafka의 장단점&lt;/code&gt;&lt;/b&gt;:&lt;br /&gt;Kafka는 대규모 데이터 스트리밍과 내구성이 강점이지만, 설정 및 운영이 복잡하며 실시간 처리가 필요한 경우 적합하지 않을 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;RabbitMQ와의 차이점&lt;/code&gt;&lt;/b&gt;:&lt;br /&gt;&lt;code&gt;RabbitMQ&lt;/code&gt;는 단순하고 빠른 메시징에 적합하며, &lt;code&gt;Kafka&lt;/code&gt;는 대규모 데이터 처리 및 스트리밍에 특화된 플랫폼입니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>IT 지식</category>
      <category>Apache Kafka</category>
      <category>broker</category>
      <category>Consumer</category>
      <category>kafk</category>
      <category>MQ</category>
      <category>Producer</category>
      <category>rabbitmq</category>
      <category>메세지큐</category>
      <category>분산 메시징 플랫폼</category>
      <category>카프카</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/297</guid>
      <comments>https://tae-hui.tistory.com/entry/Kafka%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80#entry297comment</comments>
      <pubDate>Sat, 18 Jan 2025 03:05:31 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] Spring Boot 대규모 트래픽 처리 #Redis #Scheduler</title>
      <link>https://tae-hui.tistory.com/entry/JAVA-Spring-Boot-%EB%8C%80%EA%B7%9C%EB%AA%A8-%ED%8A%B8%EB%9E%98%ED%94%BD-%EC%B2%98%EB%A6%AC-Redis-Scheduler</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.  ️ Spring Boot 대규모 트래픽 처리 방안: Redis, Scheduler&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대규모 트래픽 상황에서 데이터를 효율적으로 처리하기 위한 방법중 하나로 &lt;code&gt;Redisd&lt;/code&gt;와 &lt;code&gt;Scheduler&lt;/code&gt;를 활용한 개발 방법을 정리해 보려 합니다.&lt;br /&gt;&lt;code&gt;Redis&lt;/code&gt;에 데이터를 임시 저장하고, 일정 시간 간격으로 DB에 적재하는 방식을 구현합니다. &lt;code&gt;Redis&lt;/code&gt;는 빠른 쓰기 작업에 유리하며, 데이터 배치를 통해 DB의 부담을 줄일 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.   로직과 코드 구현&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;데이터를 Redis의 리스트(List) 구조에 저장합니다.&lt;/li&gt;
&lt;li&gt;가장 최근 키에 데이터를 추가하되, 리스트가 3000개 이상일 경우 새로운 키를 생성합니다.&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;Redis에서 가장 오래된 키부터 데이터를 가져옵니다.&lt;/li&gt;
&lt;li&gt;데이터를 DB에 적재하고, 처리된 키는 삭제합니다.&lt;/li&gt;
&lt;li&gt;데이터가 3000개 미만이면 추가 데이터를 가져와 배치를 완성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3️⃣ 구현 코드&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. Redis에 데이터 저장 서비스&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;@Service
public class RedisDataService {

    private final RedisTemplate&amp;lt;String, String&amp;gt; redisTemplate;

    public RedisDataService(RedisTemplate&amp;lt;String, String&amp;gt; redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public void saveData(String data) {
        // 1. 가장 최근 Key 찾기
        String recentKey = findMostRecentKey();

        // 2. Key의 데이터 개수 확인
        Long size = redisTemplate.opsForList().size(recentKey);

        if (size != null &amp;amp;&amp;amp; size &amp;lt; 3000) {
            // 기존 Key에 데이터 저장
            redisTemplate.opsForList().leftPush(recentKey, data);
        } else {
            // 새로운 Key 생성
            String newKey = generateNewKey();
            redisTemplate.opsForList().leftPush(newKey, data);
        }
    }

    private String findMostRecentKey() {
        // Redis Key 조회 후 가장 최근 Key 반환
        Set&amp;lt;String&amp;gt; keys = redisTemplate.keys(&quot;data_*&quot;);
        return keys.stream().max(String::compareTo).orElse(generateNewKey());
    }

    private String generateNewKey() {
        // 새로운 Key 생성: 현재 시간 + UUID
        return &quot;data_&quot; + LocalDateTime.now().format(DateTimeFormatter.ofPattern(&quot;yyyyMMdd_HHmmss&quot;)) + &quot;_&quot; + UUID.randomUUID();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. Redis 데이터를 DB로 옮기는 스케줄러&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;@Service
public class RedisToDbScheduler {

    private final RedisTemplate&amp;lt;String, String&amp;gt; redisTemplate;
    private final DataRepository dataRepository;

    public RedisToDbScheduler(RedisTemplate&amp;lt;String, String&amp;gt; redisTemplate, DataRepository dataRepository) {
        this.redisTemplate = redisTemplate;
        this.dataRepository = dataRepository;
    }

    @Scheduled(fixedRate = 5000) // 5초마다 실행
    public void processRedisKeys() {
        Set&amp;lt;String&amp;gt; keys = redisTemplate.keys(&quot;data_*&quot;);

        if (keys != null &amp;amp;&amp;amp; !keys.isEmpty()) {
            // 가장 오래된 Key 정렬 후 처리
            List&amp;lt;String&amp;gt; sortedKeys = keys.stream().sorted().toList();

            int totalProcessed = 0;
            List&amp;lt;String&amp;gt; batchData = new ArrayList&amp;lt;&amp;gt;();

            for (String key : sortedKeys) {
                // 현재 Key의 데이터 가져오기
                List&amp;lt;String&amp;gt; dataList = redisTemplate.opsForList().range(key, 0, -1);

                if (dataList != null &amp;amp;&amp;amp; !dataList.isEmpty()) {
                    batchData.addAll(dataList);
                    totalProcessed += dataList.size();

                    // Key 삭제
                    redisTemplate.delete(key);

                    // 3000개 이상 채워지면 DB에 INSERT 후 종료
                    if (totalProcessed &amp;gt;= 3000) {
                        break;
                    }
                }
            }

            // 배치 INSERT
            if (!batchData.isEmpty()) {
                List&amp;lt;DataEntity&amp;gt; entities = batchData.stream()
                        .map(data -&amp;gt; new DataEntity(data))
                        .collect(Collectors.toList());
                dataRepository.saveAll(entities);
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&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;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;부하 분산:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Redis&lt;/code&gt;를 완충 버퍼로 사용해 트래픽 몰림 방지.&lt;/li&gt;
&lt;li&gt;DB는 3000개 단위로 배치 처리하여 성능 최적화.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;데이터 유실 방지:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Redis&lt;/code&gt;에 데이터를 저장 후 처리하므로 실시간 데이터 유실 가능성 낮음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;확장성:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Redis Cluster&lt;/code&gt;와 함께 사용하면 높은 트래픽도 안정적으로 처리 가능.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;code&gt;Key&lt;/code&gt; 관리 복잡성:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Key 개수가 많아지면 스케줄러 처리 비용 증가.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;스케줄 주기 조정 필요:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;트래픽 상황에 따라 5초 주기가 너무 짧거나 길 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Redis&lt;/code&gt; 부하:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;과도한 트래픽으로 인한 Redis 부하가 발생할 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Redis&lt;/code&gt; 설정 복잡성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Redis의 원자성과 격리성을 보장하기 위해 추가 설정이 필요할 수 도 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;code&gt;Kafka&lt;/code&gt; 사용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다음 글에서는 Kafka 메시지 큐를 통해 사용해 보다 효율적으로 처리하는 방법을 정리해 보도록 하겠습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.   정리&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Redis 데이터 저장 로직&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;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;Redis에서 오래된 데이터를 배치로 가져와 DB에 저장.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Kafka 도입으로 개선&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;</description>
      <category>Java</category>
      <category>cache</category>
      <category>db 부하</category>
      <category>Kafka</category>
      <category>Redis</category>
      <category>redis cache</category>
      <category>Scheduled</category>
      <category>spring 트래픽</category>
      <category>과부하</category>
      <category>대규모</category>
      <category>트래픽</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/296</guid>
      <comments>https://tae-hui.tistory.com/entry/JAVA-Spring-Boot-%EB%8C%80%EA%B7%9C%EB%AA%A8-%ED%8A%B8%EB%9E%98%ED%94%BD-%EC%B2%98%EB%A6%AC-Redis-Scheduler#entry296comment</comments>
      <pubDate>Tue, 14 Jan 2025 23:47:46 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] Spring 테스트 : Mock VS MockBean</title>
      <link>https://tae-hui.tistory.com/entry/JAVA-Spring-%ED%85%8C%EC%8A%A4%ED%8A%B8-Mock-VS-MockBean</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 글에서 Spring에서의 단위테스트와 통합테스트에 대해 간략하게 정리해 보았습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://tae-hui.tistory.com/entry/JAVASpring%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1-%EB%B0%A9%EB%B2%95-%EB%8B%A8%EC%9C%84-%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%86%B5%ED%95%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8&quot;&gt;[JAVA] Spring을 이용한 테스트 코드 작성 방법 (단위 테스트, 통합 테스트)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1736765690905&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[JAVA] Spring을 이용한 테스트 코드 작성 방법 (단위 테스트, 통합 테스트)&quot; data-og-description=&quot;소프트웨어 개발에서 테스트 코드는 단순히 오류를 찾는 도구를 넘어, CI/CD(Continuous Integration/Continuous Deployment) 파이프라인과 개발 생산성 향상에 핵심적인 역할을 합니다.이 글에서는 JPA를 사용&quot; data-og-host=&quot;tae-hui.tistory.com&quot; data-og-source-url=&quot;https://tae-hui.tistory.com/entry/JAVASpring%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1-%EB%B0%A9%EB%B2%95-%EB%8B%A8%EC%9C%84-%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%86%B5%ED%95%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8&quot; data-og-url=&quot;https://tae-hui.tistory.com/entry/JAVASpring%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1-%EB%B0%A9%EB%B2%95-%EB%8B%A8%EC%9C%84-%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%86%B5%ED%95%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/OWf0b/hyX0z4botj/MnBhdbQnc8s4x5INmTbBek/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/by0gyb/hyX0tCTM0P/N2Z07Npp76KadyzEylpah1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://tae-hui.tistory.com/entry/JAVASpring%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1-%EB%B0%A9%EB%B2%95-%EB%8B%A8%EC%9C%84-%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%86%B5%ED%95%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://tae-hui.tistory.com/entry/JAVASpring%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1-%EB%B0%A9%EB%B2%95-%EB%8B%A8%EC%9C%84-%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%86%B5%ED%95%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/OWf0b/hyX0z4botj/MnBhdbQnc8s4x5INmTbBek/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/by0gyb/hyX0tCTM0P/N2Z07Npp76KadyzEylpah1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[JAVA] Spring을 이용한 테스트 코드 작성 방법 (단위 테스트, 통합 테스트)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;소프트웨어 개발에서 테스트 코드는 단순히 오류를 찾는 도구를 넘어, CI/CD(Continuous Integration/Continuous Deployment) 파이프라인과 개발 생산성 향상에 핵심적인 역할을 합니다.이 글에서는 JPA를 사용&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;tae-hui.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 해당 내용에 대해 조금 더 깊에 알아보려고 합니다. 특히, &lt;b&gt;Mock 객체와 MockBean의 차이점&lt;/b&gt;, 통합테스트에서의 &lt;code&gt;Application Context 초기화&lt;/code&gt;, 그리고 &lt;b&gt;성능 최적화 방법&lt;/b&gt;을 자세히 정리했습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.   Spring 테스트의 기본: &lt;code&gt;@SpringBootTest&lt;/code&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;code&gt;@SpringBootTest&lt;/code&gt;란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;@SpringBootTest&lt;/code&gt;는 &lt;b&gt;Spring Boot 애플리케이션 전체를 초기화&lt;/b&gt;하며, &lt;b&gt;통합 테스트&lt;/b&gt;를 위한 환경을 제공합니다. 이 애노테이션을 선언하면 &lt;b&gt;애플리케이션 컨텍스트(Application Context)&lt;/b&gt;가 생성되고, 컨텍스트에 등록된 &lt;code&gt;@Service&lt;/code&gt;, &lt;code&gt;@Repository&lt;/code&gt;, &lt;code&gt;@Component&lt;/code&gt; 같은 빈들을 사용할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ Application Context 초기화의 특징&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;Application Context는 등록된 빈만 사용 가능&lt;/b&gt;: &lt;code&gt;@SpringBootTest&lt;/code&gt;는 애플리케이션 전체 컨텍스트를 초기화하기 때문에, 테스트 대상 클래스에서 &lt;b&gt;등록되지 않은 빈은 사용할 수 없습니다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;모든 의존성이 반드시 빈이어야 함&lt;/b&gt;: 테스트 클래스에서 의존성을 주입받으려면, 해당 의존성이 반드시 Application Context에 등록되어 있어야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;장점&lt;/h4&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;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;단점&lt;/h4&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;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용 예시&lt;/h3&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@SpringBootTest
public class ExampleServiceTest {

    @Autowired
    private ExampleService exampleService;

    @Test
    public void testServiceLogic() {
        String result = exampleService.doSomething();
        assertEquals(&quot;ExpectedValue&quot;, 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;b&gt;Tip&lt;/b&gt;: &lt;code&gt;@SpringBootTest&lt;/code&gt;는 애플리케이션 전체를 테스트할 때 유용합니다. 하지만 모든 테스트에서 사용하면 속도 문제가 생길 수 있으니 필요한 경우에만 해야합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.  ️ &lt;code&gt;@MockBean&lt;/code&gt;: Mock 객체를 Application Context에 등록하기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;code&gt;@MockBean&lt;/code&gt;의 역할&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;@MockBean&lt;/code&gt;은 Spring의 &lt;b&gt;Mock 객체&lt;/b&gt;를 Application Context에 등록하는 방법입니다. 실제 빈 대신 &lt;b&gt;Mock 객체를 주입&lt;/b&gt;하여 테스트에 활용할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;주요 특징&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Mock 객체를 사용하면서도, &lt;b&gt;Spring 컨텍스트와 연동&lt;/b&gt;된 테스트를 수행할 수 있습니다.&lt;/li&gt;
&lt;li&gt;Mock 객체의 동작을 정의하여 원하는 시나리오를 검증할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용 예시&lt;/h3&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@SpringBootTest
public class ExampleServiceTest {

    @MockBean
    private ExampleRepository exampleRepository;

    @Autowired
    private ExampleService exampleService;

    @Test
    public void testMockRepository() {
        // Mock 동작 정의
        when(exampleRepository.getData()).thenReturn(&quot;MockData&quot;);

        String result = exampleService.processData();
        assertEquals(&quot;Processed MockData&quot;, result);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;장점&lt;/h4&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;: Mock 객체를 주입하더라도 실제 컨텍스트를 초기화하여 전반적인 시스템을 테스트할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;테스트 대상의 의존성 대체&lt;/b&gt;: 특정 클래스의 테스트에서만 Mock 객체를 사용하고, 나머지는 실제 빈으로 동작합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;단점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;@SpringBootTest&lt;/code&gt;와 함께 사용되므로 테스트 속도가 느릴 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.   &lt;code&gt;@Mock&lt;/code&gt;과 &lt;code&gt;@InjectMocks&lt;/code&gt;: Mockito 기반의 단위 테스트&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;code&gt;@Mock&lt;/code&gt;과 &lt;code&gt;@InjectMocks&lt;/code&gt;란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;@Mock&lt;/code&gt;은 Mock 객체를 생성하고, &lt;code&gt;@InjectMocks&lt;/code&gt;는 해당 Mock 객체를 주입하여 테스트 대상을 초기화합니다. Spring 컨텍스트를 전혀 사용하지 않고 순수하게 &lt;b&gt;Mockito 프레임워크&lt;/b&gt;만으로 테스트를 수행합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용 예시&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@RunWith(MockitoJUnitRunner.class)
public class ExampleServiceTest {

    @Mock
    private ExampleRepository exampleRepository;

    @InjectMocks
    private ExampleService exampleService;

    @Test
    public void testUsingMockito() {
        // Mock 동작 정의
        when(exampleRepository.getData()).thenReturn(&quot;MockData&quot;);

        String result = exampleService.processData();
        assertEquals(&quot;Processed MockData&quot;, result);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;장점&lt;/h4&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;: Spring 컨텍스트를 초기화하지 않아 매우 빠릅니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;간단한 구조&lt;/b&gt;: 단위 테스트 작성이 용이하며, 특정 메서드나 클래스의 동작을 독립적으로 검증할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;단점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Spring 컨텍스트를 활용하지 않기 때문에, 빈의 의존성이나 설정 검증은 불가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.   &lt;code&gt;@SpringBootTest&lt;/code&gt; vs Mockito 테스트: 언제 사용할까?&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;code&gt;@SpringBootTest&lt;/code&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;: 여러 계층(Service, Repository 등)을 함께 테스트해야 할 때.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;빈 의존성 확인&lt;/b&gt;: 의존성 주입 및 설정이 올바른지 확인해야 할 때.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Spring Boot 애플리케이션 전체 동작 검증&lt;/b&gt;: 실제 환경과 유사한 테스트 환경이 필요할 때.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Mockito 기반 테스트를 사용하는 상황&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;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.   성능 최적화: &lt;code&gt;@SpringBootTest&lt;/code&gt;를 사용할 때 주의할 점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;@SpringBootTest&lt;/code&gt;를 사용하면 Application Context의 초기화가 일어나기 때문에 속도가 느릴 수 있습니다. 하지만 Spring Boot는 &lt;b&gt;컨텍스트 캐싱&lt;/b&gt;을 활용하여 동일한 컨텍스트를 재사용하므로, 설정이 변경되지 않는 한 성능 문제가 심각하지 않습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;/li&gt;
&lt;li&gt;다른 프로파일 사용&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;@ActiveProfiles&lt;/code&gt;로 지정된 프로파일이 다르면 별도의 Context를 초기화합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;3&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;다른 설정 클래스&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;@SpringBootTest(classes = ...)&lt;/code&gt;로 지정된 설정이 다르면 매번 초기화합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;4&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;@DirtiesContext&lt;/code&gt; 사용&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 테스트가 Context를 더럽혔다고 표시하면, 이후 테스트는 새로운 Context를 초기화합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;WebEnvironment 설정&lt;/b&gt;: 테스트 환경의 WebEnvironment를 &lt;code&gt;NONE&lt;/code&gt;으로 설정하면 불필요한 서버 초기화를 방지할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@SpringBootTest(webEnvironment = WebEnvironment.NONE) public class ExampleServiceTest { // Test logic here }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;테스트 분리&lt;/b&gt;: 통합 테스트(&lt;code&gt;@SpringBootTest&lt;/code&gt;)와 단위 테스트(Mockito 기반)를 명확히 분리하여 필요 이상으로 컨텍스트를 초기화하지 않도록 합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6.   정리: Mock VS MockBean 비교표&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: auto;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; text-align: center; font-weight: bold;&quot;&gt;항목&lt;/td&gt;
&lt;td style=&quot;height: 21px; text-align: center; font-weight: bold;&quot;&gt;&lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;@MockBean&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px; text-align: center; font-weight: bold;&quot;&gt;&lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;@Mock&lt;/code&gt; + &lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;@InjectMocks&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&lt;b&gt;Spring Context 초기화 여부&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;초기화함&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;초기화하지 않음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&lt;b&gt;테스트 속도&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;상대적으로 느림&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;매우 빠름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&lt;b&gt;빈 의존성 테스트&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;가능&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;불가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&lt;b&gt;사용 용도&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;통합 테스트&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;단위 테스트&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 요약&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&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;code&gt;@SpringBootTest&lt;/code&gt;&lt;/b&gt;: 애플리케이션 전체 컨텍스트를 초기화하기 때문에, 테스트 대상 클래스에서 등록되지 않은 빈은 사용할 수 없다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;@MockBean&lt;/code&gt;&lt;/b&gt;: 통합 테스트에서 유용하며, Application Context와 연동된 Mock 객체를 생성합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;@Mock&lt;/code&gt;&lt;/b&gt;: 빠른 단위 테스트에 적합하며, Spring과 독립적으로 동작합니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java</category>
      <category>application context</category>
      <category>Mock</category>
      <category>MockBean</category>
      <category>spring boot</category>
      <category>spring boot mock</category>
      <category>spring text</category>
      <category>spring unit text</category>
      <category>SpringBootTest</category>
      <category>test</category>
      <category>스프링 테스트</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/295</guid>
      <comments>https://tae-hui.tistory.com/entry/JAVA-Spring-%ED%85%8C%EC%8A%A4%ED%8A%B8-Mock-VS-MockBean#entry295comment</comments>
      <pubDate>Mon, 13 Jan 2025 19:49:19 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] Spring을 이용한 테스트 코드 작성 방법 (단위 테스트, 통합 테스트)</title>
      <link>https://tae-hui.tistory.com/entry/JAVASpring%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1-%EB%B0%A9%EB%B2%95-%EB%8B%A8%EC%9C%84-%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%86%B5%ED%95%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어 개발에서 &lt;b&gt;테스트 코드&lt;/b&gt;는 단순히 오류를 찾는 도구를 넘어, CI/CD(Continuous Integration/Continuous Deployment) 파이프라인과 개발 생산성 향상에 핵심적인 역할을 합니다.&lt;br /&gt;이 글에서는 JPA를 사용한 DB 연결 후 테스트를 작성하는 방법을 단계별로 알아보겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.   단위 테스트와 통합 테스트의 차이&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 단위 테스트(Unit Test)&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;: JUnit, Mockito 등.&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;code&gt;@ExtendWith&lt;/code&gt;, &lt;code&gt;@Mock&lt;/code&gt;, &lt;code&gt;@InjectMocks&lt;/code&gt; 등을 활용하여 &lt;b&gt;외부 의존성을 모킹(Mock)&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;테스트 환경에서 빠르게 검증.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 통합 테스트(Integration Test)&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;: 여러 구성 요소(Controller, Service, Repository 등)가 올바르게 협력하는지 검증.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;도구&lt;/b&gt;: Spring Boot Test, H2 Database.&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;code&gt;@SpringBootTest&lt;/code&gt;를 활용하여 &lt;b&gt;애플리케이션 컨텍스트를 로드&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;실제 데이터베이스와 연결하거나 테스트용 메모리 데이터베이스 사용.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.   준비 사항&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1 의존성 추가 (Maven 기준)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트를 위한 주요 라이브러리를 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의존성을 &lt;code&gt;pom.xml&lt;/code&gt; 파일에 추가합니다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;dependencies&amp;gt;
    &amp;lt;!-- Spring Boot Starter Test --&amp;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-test&amp;lt;/artifactId&amp;gt;
        &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
    &amp;lt;/dependency&amp;gt;

    &amp;lt;!-- Spring Data JPA --&amp;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-jpa&amp;lt;/artifactId&amp;gt;
    &amp;lt;/dependency&amp;gt;

    &amp;lt;!-- H2 Database (테스트용) --&amp;gt;
    &amp;lt;dependency&amp;gt;
        &amp;lt;groupId&amp;gt;com.h2database&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;h2&amp;lt;/artifactId&amp;gt;
        &amp;lt;scope&amp;gt;runtime&amp;lt;/scope&amp;gt;
    &amp;lt;/dependency&amp;gt;
&amp;lt;/dependencies&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2 테스트 환경 설정 (&lt;code&gt;application.yml&lt;/code&gt;)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 전용 데이터베이스를 설정합니다. 여기서는 &lt;code&gt;H2&lt;/code&gt;를 사용합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;application-test.yml&lt;/code&gt;&lt;/h4&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;spring:
  datasource:
    url: jdbc:h2:mem:testdb
    driver-class-name: org.h2.Driver
    username: sa
    password: 
  jpa:
    hibernate:
      ddl-auto: create-drop
    show-sql: true&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.   단위 테스트 작성하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단위 테스트에서는 데이터베이스 대신 Mock을 활용합니다. 예제로 간단한 &lt;b&gt;&lt;code&gt;UserService&lt;/code&gt;&lt;/b&gt;의 단위 테스트를 작성해 볼게요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 User 엔티티 및 리포지토리&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private String email;

    // Getters and Setters
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;@Repository
public interface UserRepository extends JpaRepository&amp;lt;User, Long&amp;gt; {}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2 UserService&lt;/h3&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;@Service
public class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User createUser(String name, String email) {
        User user = new User();
        user.setName(name);
        user.setEmail(email);
        return userRepository.save(user);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.3 UserService 단위 테스트 작성&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@ExtendWith(MockitoExtension.class)
public class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private UserService userService;

    @Test
    void testCreateUser() {
        // Given
        String name = &quot;John&quot;;
        String email = &quot;john@example.com&quot;;
        User user = new User();
        user.setName(name);
        user.setEmail(email);

        when(userRepository.save(any(User.class))).thenReturn(user);

        // When
        User createdUser = userService.createUser(name, email);

        // Then
        assertEquals(name, createdUser.getName());
        assertEquals(email, createdUser.getEmail());
        verify(userRepository, times(1)).save(any(User.class));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;✅ &lt;code&gt;@ExtendWith(MockitoExtension.class)&lt;/code&gt;&lt;/h4&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JUnit 5(=JUnit Jupiter)와 &lt;b&gt;Mockito&lt;/b&gt;를 통합해 사용할 때 필요한 어노테이션입니다.&lt;/li&gt;
&lt;li&gt;Mockito에서 제공하는 Mock 객체를 테스트 클래스에 주입할 수 있도록 설정합니다.&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;단위 테스트에서 Mockito를 사용하여 테스트 대상을 모킹(Mocking)할 때 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;✅ &lt;code&gt;@Mock&lt;/code&gt;&lt;/h4&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Mockito에서 제공하는 어노테이션으로, &lt;b&gt;가짜 객체(Mock)&lt;/b&gt;를 생성합니다.&lt;/li&gt;
&lt;li&gt;실제 객체 대신 Mock 객체를 사용하여 외부 의존성을 제거하고 코드의 특정 부분만 테스트할 수 있습니다.&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;리포지토리, API 호출 등 실제 객체와 상호작용하지 않고 원하는 동작을 시뮬레이션할 때.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;✅ &lt;code&gt;@InjectMocks&lt;/code&gt;&lt;/h4&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Mockito에서 제공하는 어노테이션으로, &lt;b&gt;테스트 대상 클래스에 Mock 객체를 주입&lt;/b&gt;합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@Mock&lt;/code&gt;으로 생성된 Mock 객체를 테스트 대상 클래스의 생성자나 필드에 자동으로 주입합니다.&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;테스트 대상 클래스에 의존성 주입(DI)이 필요한 경우.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.   통합 테스트 작성하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;통합 테스트에서는 실제 데이터베이스와 연결된 환경에서 테스트를 실행합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.1 통합 테스트 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;@SpringBootTest&lt;/code&gt;와 &lt;code&gt;@Transactional&lt;/code&gt; 어노테이션을 활용해 통합 테스트를 작성합니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@SpringBootTest
@Transactional
 @ActiveProfiles(&quot;test&quot;) // test 프로파일 활성화
public class UserIntegrationTest {

    @Autowired
    private UserRepository userRepository;

    @Test
    void testSaveAndFindUser() {
        // Given
        User user = new User();
        user.setName(&quot;Alice&quot;);
        user.setEmail(&quot;alice@example.com&quot;);

        // When
        userRepository.save(user);
        Optional&amp;lt;User&amp;gt; foundUser = userRepository.findById(user.getId());

        // Then
        assertTrue(foundUser.isPresent());
        assertEquals(&quot;Alice&quot;, foundUser.get().getName());
        assertEquals(&quot;alice@example.com&quot;, foundUser.get().getEmail());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;✅ &lt;code&gt;@SpringBootTest&lt;/code&gt;&lt;/h4&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Spring Boot 통합 테스트&lt;/b&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;여러 Bean 간의 상호작용 및 애플리케이션 전반적인 통합 테스트를 실행할 때.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;✅ &lt;code&gt;@Transactional&lt;/code&gt;&lt;/h4&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;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;/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;데이터베이스를 사용하는 통합 테스트에서 DB 상태를 초기화해야 할 때.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ &lt;code&gt;@ActiveProfiles&lt;/code&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 Spring Profile을 활성화하도록 설정합니다.&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;code&gt;application-test.yml&lt;/code&gt;과 같은 테스트 전용 설정 파일을 적용하고 싶을 때.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.   정리: 단위 테스트와 통합 테스트의 핵심 비교&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: auto;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; text-align: center; font-weight: bold;&quot;&gt;특징&lt;/td&gt;
&lt;td style=&quot;height: 21px; text-align: center; font-weight: bold;&quot;&gt;단위 테스트&lt;/td&gt;
&lt;td style=&quot;height: 21px; text-align: center; font-weight: bold;&quot;&gt;통합 테스트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center; font-weight: bold;&quot;&gt;목적&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;특정 단위의 독립적 검증&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;시스템의 통합된 흐름 검증&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center; font-weight: bold;&quot;&gt;외부 의존성&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;Mock 객체 사용&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;실제 데이터베이스 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center; font-weight: bold;&quot;&gt;실행 속도&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;빠름&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;상대적으로 느림&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center; font-weight: bold;&quot;&gt;어노테이션&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;@ExtendWith(MockitoExtension.class)&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;@SpringBootTest&lt;/code&gt;, &lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;@Transactional&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;단위 테스트&lt;/b&gt;는 빠르게 개발 주기를 반복할 때 유용하며, &lt;b&gt;통합 테스트&lt;/b&gt;는 실제 환경과의 상호작용을 검증하는 데 필수적입니다.&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>Java</category>
      <category>InjectMocks</category>
      <category>Integration</category>
      <category>Java</category>
      <category>Mock</category>
      <category>Spring</category>
      <category>spring boot</category>
      <category>SpringBootTest</category>
      <category>test</category>
      <category>단위테스트</category>
      <category>통합테스트</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/294</guid>
      <comments>https://tae-hui.tistory.com/entry/JAVASpring%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1-%EB%B0%A9%EB%B2%95-%EB%8B%A8%EC%9C%84-%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%86%B5%ED%95%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8#entry294comment</comments>
      <pubDate>Sat, 11 Jan 2025 08:00:02 +0900</pubDate>
    </item>
    <item>
      <title>[GIT] Git 히스토리 이메일 및 이름 수정</title>
      <link>https://tae-hui.tistory.com/entry/GIT-Git-%ED%9E%88%EC%8A%A4%ED%86%A0%EB%A6%AC-%EC%9D%B4%EB%A9%94%EC%9D%BC-%EB%B0%8F-%EC%9D%B4%EB%A6%84-%EC%88%98%EC%A0%95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Git&lt;/code&gt;을 사용하다 히스토리에 기록된 &lt;code&gt;이메일&lt;/code&gt;과 &lt;code&gt;이름 정보&lt;/code&gt;를 수정할 일이 생겨 이참에 수정 방법에 대해 포스팅해보려고합니다&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  주의: 작업 환경 준비&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기존 프로젝트에 직접 작업하지 마세요!&lt;/b&gt;&lt;br /&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;/li&gt;
&lt;li&gt;원격 저장소를 새 폴더에 클론합니다:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git clone &amp;lt;원격 저장소 URL&amp;gt; &amp;lt;새 폴더 이름&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;새로 클론한 저장소에서 아래의 모든 작업을 진행하세요.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. &lt;code&gt;git filter-repo&lt;/code&gt; 설치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;git filter-repo&lt;/code&gt;는 Git의 기본 명령어에 포함되지 않으므로 별도로 설치해야 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  1.1. Python 설치 여부 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Python이 설치되어 있는지 확인합니다:&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;python --version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는:&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;python3 --version&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;b&gt;팁:&lt;/b&gt; Python이 설치되지 않았다면 &lt;a href=&quot;https://www.python.org/downloads/&quot;&gt;Python 공식 웹사이트&lt;/a&gt;에서 설치하세요. 설치 시 &lt;b&gt;pip&lt;/b&gt;(Python 패키지 관리자)도 함께 설치되었는지 확인하세요.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ 1.2. pip를 통해 &lt;code&gt;git filter-repo&lt;/code&gt; 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python과 pip가 준비되었다면, 아래 명령어로 &lt;code&gt;git-filter-repo&lt;/code&gt;를 설치합니다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;pip install git-filter-repo&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 1.3. 설치 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치가 완료되었는지 아래 명령어로 확인합니다:&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;git filter-repo --version&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 수정 대상 데이터 확인하기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  2.1. 이메일 및 이름 데이터 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;히스토리에서 이메일과 이름 정보를 확인합니다.&lt;br /&gt;다음 명령어를 실행하여 필요한 데이터를 확인하세요:&lt;/p&gt;
&lt;pre class=&quot;perl&quot;&gt;&lt;code&gt;git log --all --format=&quot;%h - author: %an &amp;lt;%ae&amp;gt; / committer: %cn &amp;lt;%ce&amp;gt;&quot;&lt;/code&gt;&lt;/pre&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;/ul&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;abc123 - author: John Doe &amp;lt;johndoe@example.com&amp;gt; / committer: Jane Smith &amp;lt;janesmith@example.com&amp;gt;&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;b&gt;팁:&lt;/b&gt; 결과에서 수정하고자 하는 이메일과 이름을 찾아 &lt;b&gt;emails_to_update&lt;/b&gt; 리스트에 추가하세요.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Git 히스토리 이메일 및 이름 수정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;git filter-repo&lt;/code&gt;를 사용하여 이메일과 이름을 수정합니다.&lt;br /&gt;아래는 &lt;b&gt;Test용 데이터&lt;/b&gt;로 작성된 스크립트입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  3.1. &lt;code&gt;git filter-repo&lt;/code&gt; 실행&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드를 사용해 이메일과 이름을 수정하세요:&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;git filter-repo --force --commit-callback &quot;
# 수정 대상 이메일 및 이름
old_emails = ['oldemail@example.com', 'testuser@example.com']  # 기존 이메일
new_email = 'newemail@example.com'  # 변경할 이메일

old_names = ['Old Name', 'Test User']  # 기존 이름
new_name = 'New User'  # 변경할 이름

# 바이트 문자열을 일반 문자열로 변환
def to_str(byte_str):
    return byte_str.decode('utf-8') if isinstance(byte_str, bytes) else byte_str

# 일반 문자열을 바이트 문자열로 변환
def to_bytes(string):
    return string.encode('utf-8') if isinstance(string, str) else string

with open('filter-repo-debug.log', 'a') as log_file:
    # 이메일 및 이름 가져오기
    author_email = to_str(commit.author_email)
    committer_email = to_str(commit.committer_email)
    author_name = to_str(commit.author_name)
    committer_name = to_str(commit.committer_name)

    # 로그 기록
    log_file.write(f'Processing commit: {author_email}, {committer_email}, {author_name}, {committer_name}\\n')

    # 이메일 수정
    if author_email in old_emails:
        log_file.write(f'Updating author_email: {author_email} -&amp;gt; {new_email}\\n')
        commit.author_email = to_bytes(new_email)

    if committer_email in old_emails:
        log_file.write(f'Updating committer_email: {committer_email} -&amp;gt; {new_email}\\n')
        commit.committer_email = to_bytes(new_email)

    # 이름 수정
    if author_name in old_names:
        log_file.write(f'Updating author_name: {author_name} -&amp;gt; {new_name}\\n')
        commit.author_name = to_bytes(new_name)

    if committer_name in old_names:
        log_file.write(f'Updating committer_name: {committer_name} -&amp;gt; {new_name}\\n')
        commit.committer_name = to_bytes(new_name)
&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  3.2. 결과 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정된 이메일과 이름 정보를 확인합니다:&lt;/p&gt;
&lt;pre class=&quot;perl&quot;&gt;&lt;code&gt;git log --all --format=&quot;%h - author: %an &amp;lt;%ae&amp;gt; / committer: %cn &amp;lt;%ce&amp;gt;&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 원격 저장소 다시 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;git filter-repo&lt;/code&gt;를 사용한 경우 원격 저장소(remote)와 로컬 저장소의 커밋 히스토리가 달라집니다.&lt;br /&gt;따라서 원격 저장소를 다시 설정하거나 강제로 푸시해야 합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.1. 기존 원격 저장소 백업&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 원격 저장소를 백업하거나 이름을 변경해 데이터 유실을 방지합니다.&lt;/p&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;git remote rename origin origin-backup&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;b&gt;팁:&lt;/b&gt; 원격 저장소를 백업하지 않아도 괜찮다면 이 단계는 생략 가능합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.2. 원격 저장소 재설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정된 로컬 저장소를 새로운 원격 저장소에 연결합니다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;git remote add origin &amp;lt;원격 저장소 URL&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.3. 강제 푸시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정된 히스토리를 원격 저장소에 강제로 푸시합니다.&lt;/p&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;git push origin main --force&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;b&gt;팁:&lt;/b&gt; &lt;code&gt;--force&lt;/code&gt; 또는 &lt;code&gt;--force-with-lease&lt;/code&gt; 옵션을 사용하세요. 강제 푸시는 기존 히스토리를 덮어쓰기 때문에 협업 중인 경우 팀원들에게 반드시 사전 안내를 해야 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.4. 최종 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원격 저장소에 수정된 내용이 정상적으로 반영되었는지 확인합니다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;git log origin/main --oneline&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 캐시와 불필요한 데이터 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Git 저장소의 캐시와 임시 데이터를 정리합니다.&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now --aggressive&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 팀원에게 안내하기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6.1. 팀원의 로컬 저장소 초기화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀원들은 기존 로컬 저장소가 새로운 히스토리와 충돌하지 않도록 초기화 후 다시 클론해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;rm -rf &amp;lt;로컬 저장소 폴더&amp;gt;
git clone &amp;lt;원격 저장소 URL&amp;gt;&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;b&gt;중요:&lt;/b&gt; 반드시 새로운 저장소를 기반으로 작업해야 합니다.&lt;br /&gt;기존 원격 저장소를 대체했다면 팀원들에게 이를 공지하세요.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✨ 정리&lt;/h2&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; Python 설치 여부 확인 &amp;rarr; pip로 &lt;code&gt;git filter-repo&lt;/code&gt; 설치.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터 확인:&lt;/b&gt; &lt;code&gt;git log&lt;/code&gt;를 사용해 이메일과 이름 정보를 확인.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;수정 작업:&lt;/b&gt; &lt;code&gt;git filter-repo&lt;/code&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;li&gt;&lt;b&gt;팀원 안내:&lt;/b&gt; 새로 생성된 원격 저장소에 클론하도록 안내.&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>Git</category>
      <category>GIT</category>
      <category>git commit user update</category>
      <category>git commit user 수정</category>
      <category>git email 수정</category>
      <category>git history update</category>
      <category>git push email 수정</category>
      <category>git update</category>
      <category>git 수정</category>
      <category>git 이메일 수정</category>
      <category>github</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/293</guid>
      <comments>https://tae-hui.tistory.com/entry/GIT-Git-%ED%9E%88%EC%8A%A4%ED%86%A0%EB%A6%AC-%EC%9D%B4%EB%A9%94%EC%9D%BC-%EB%B0%8F-%EC%9D%B4%EB%A6%84-%EC%88%98%EC%A0%95#entry293comment</comments>
      <pubDate>Wed, 1 Jan 2025 08:00:18 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] ArrayList vs LinkedList 완벽 정리</title>
      <link>https://tae-hui.tistory.com/entry/JAVA-ArrayList-vs-LinkedList-%EC%99%84%EB%B2%BD-%EC%A0%95%EB%A6%AC</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. ArrayList와 LinkedList의 성능 차이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ArrayList와 LinkedList는 중간 삽입/삭제의 시간 복잡도가 모두 &lt;b&gt;O(N)&lt;/b&gt;으로 동일합니다. 하지만 실제로는 &lt;b&gt;ArrayList가 더 빠른 경우가 많습니다.&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;ArrayList&lt;/b&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;특정 위치를 찾는 데 O(1)로 빠르며, 삽입/삭제 시 데이터 이동이 발생하지만, &lt;b&gt;캐시 최적화 효과&lt;/b&gt;로 인해 성능이 더 좋습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LinkedList&lt;/b&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; 기반 자료구조로, 삽입/삭제는 연결만 변경하면 되지만 특정 위치를 탐색하는 데 O(N)이 걸립니다.&lt;/li&gt;
&lt;li&gt;메모리가 비연속적이어서 캐시 효율이 떨어지고, 실제로 더 느릴 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;결론&lt;/b&gt;: 이론적으로 LinkedList가 유리할 것 같지만, 캐시 친화적인 ArrayList가 더 빠른 경우가 많습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 삽입/삭제가 빈번할 때 ArrayList가 더 빠른 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;삽입/삭제가 많다면 LinkedList가 더 적합하다&quot;는 말을 들어본 적 있으시죠? 이론적으로는 맞지만, 현실에서는 &lt;b&gt;ArrayList가 더 나은 경우&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;ArrayList의 경우&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;b&gt;CPU 캐시 효율&lt;/b&gt;이 좋아 성능이 더 빠를 때가 많습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LinkedList의 경우&lt;/b&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;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;정리&lt;/b&gt;: 삽입/삭제 작업만을 보더라도, ArrayList의 캐시 효율로 인해 실제로 더 나은 성능을 보이는 경우가 많습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 앞뒤 삽입/삭제도 ArrayList가 더 빠를까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ArrayList와 LinkedList 모두 &lt;b&gt;O(1)&lt;/b&gt;로 수행됩니다.&lt;/li&gt;
&lt;li&gt;ArrayList는 배열 끝에 포인터를 변경하면 끝이고, LinkedList도 마지막 노드에 연결하면 끝나기 때문입니다.&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;b&gt;ArrayList&lt;/b&gt;: 첫 번째 위치에 삽입/삭제 시 모든 요소를 &lt;b&gt;한 칸씩 이동&lt;/b&gt;해야 하므로 &lt;b&gt;O(N)&lt;/b&gt;입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LinkedList&lt;/b&gt;: 첫 번째 노드의 포인터만 변경하면 되므로 &lt;b&gt;O(1)&lt;/b&gt;입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;결론&lt;/b&gt;: &lt;b&gt;앞쪽 삽입/삭제는 LinkedList가 더 빠르지만&lt;/b&gt;, 뒤쪽 삽입/삭제는 &lt;b&gt;둘 다 O(1)&lt;/b&gt;으로 비슷한 성능을 보입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 기본 용량&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;ArrayList&lt;/b&gt;: 초기 용량(기본 10) 설정 후, 용량 초과 시 기존 용량의 &lt;b&gt;1.5배&lt;/b&gt;로 배열을 확장합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LinkedList&lt;/b&gt;: 별도의 초기 용량 개념이 없으며, 필요한 경우에 노드를 동적으로 생성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. LinkedList와 ArrayList의 구조적 차이&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✨ ArrayList&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;CPU 캐시 효율&lt;/b&gt;이 높습니다.&lt;/li&gt;
&lt;li&gt;삽입/삭제 시 데이터 이동이 필요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✨ LinkedList&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;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 왜 LinkedList가 캐시 친화적이지 않은가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LinkedList는 각 노드가 &lt;b&gt;비연속적인 메모리&lt;/b&gt;에 저장됩니다.&lt;br /&gt;이 때문에 &lt;b&gt;캐시 지역성&lt;/b&gt;(Cache Locality)이 떨어지게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ArrayList는 배열로 연속적으로 데이터를 저장하므로 캐시 친화적입니다. 하지만, LinkedList는 포인터를 따라가며 노드를 탐색해야 하므로 &lt;b&gt;캐시 효율이 낮아지고 속도가 느려질 수 있습니다.&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 언제 LinkedList를 선택해야 하나?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LinkedList를 선택하는 경우는 아래와 같습니다.&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;b&gt;정확한 메모리 관리&lt;/b&gt;가 필요한 경우 (각 노드가 독립적으로 관리되므로 필요 없는 노드를 빠르게 제거할 수 있음)&lt;/li&gt;
&lt;li&gt;메모리 크기 확장이 자주 일어나는 상황 (ArrayList는 배열 크기 확장이 발생할 때 성능 저하가 생길 수 있음)&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 대부분의 경우 ArrayList가 더 적합합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✨ 핵심 요약&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;ArrayList&lt;/b&gt;는 배열 기반으로 캐시 효율이 좋아 대부분의 경우 더 빠릅니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LinkedList&lt;/b&gt;는 삽입/삭제가 많고 탐색이 거의 없는 경우에 적합합니다.&lt;/li&gt;
&lt;li&gt;데이터의 &lt;b&gt;접근 패턴&lt;/b&gt;과 &lt;b&gt;삽입/삭제 빈도&lt;/b&gt;를 고려해 적절히 선택하세요!&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>Java</category>
      <category>array</category>
      <category>ArrayList</category>
      <category>Java</category>
      <category>java collection</category>
      <category>Java List</category>
      <category>LinkedList</category>
      <category>list</category>
      <category>list 구현체</category>
      <category>리스트</category>
      <category>자바 배열</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/292</guid>
      <comments>https://tae-hui.tistory.com/entry/JAVA-ArrayList-vs-LinkedList-%EC%99%84%EB%B2%BD-%EC%A0%95%EB%A6%AC#entry292comment</comments>
      <pubDate>Tue, 31 Dec 2024 08:00:07 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] PriorityQueue 설명 및 사용법</title>
      <link>https://tae-hui.tistory.com/entry/JAVA-PriorityQueue-%EC%84%A4%EB%AA%85-%EB%B0%8F-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;PriorityQueue&lt;/code&gt;는 자바에서 제공하는 &lt;b&gt;우선순위 큐(Priority Queue)&lt;/b&gt; 구현체로, &lt;b&gt;요소를 우선순위에 따라 자동으로 정렬&lt;/b&gt;하여 관리하는 자료구조입니다. 내부적으로 &lt;b&gt;힙(Heap)&lt;/b&gt; 자료구조를 기반으로 하며, &lt;b&gt;최소 힙(Min-Heap)&lt;/b&gt;을 기본으로 사용합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.   &lt;code&gt;PriorityQueue&lt;/code&gt;란?&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.1 주요 특징&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;br /&gt;(기본적으로 &lt;b&gt;오름차순&lt;/b&gt;으로 정렬되며, 사용자 정의 정렬 순서를 지정할 수 있습니다.)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;FIFO가 아닌 우선순위 기반 처리&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반적인 큐(FIFO)와 다르게, 요소는 &lt;b&gt;우선순위가 높은 순서&lt;/b&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;li&gt;내부적으로 &lt;b&gt;힙(Heap) 자료구조&lt;/b&gt;를 사용하여 정렬 및 삽입/삭제 작업을 효율적으로 수행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.   &lt;code&gt;PriorityQueue&lt;/code&gt; 주요 메서드&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 141px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; text-align: center; font-weight: bold;&quot;&gt;메서드&lt;/td&gt;
&lt;td style=&quot;height: 21px; text-align: center; font-weight: bold;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;add(E e)&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;큐에 요소 삽입 (성공 시 &lt;code&gt;true&lt;/code&gt; 반환, 실패 시 예외 발생)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;offer(E e)&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;큐에 요소 삽입 (성공 시 &lt;code&gt;true&lt;/code&gt; 반환, 실패 시 &lt;code&gt;false&lt;/code&gt; 반환)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;poll()&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;우선순위가 가장 높은 요소를 제거하고 반환 (큐가 비어 있으면 &lt;code&gt;null&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;remove()&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;우선순위가 가장 높은 요소를 제거하고 반환 (큐가 비어 있으면 예외)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;peek()&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;우선순위가 가장 높은 요소를 조회 (큐가 비어 있으면 &lt;code&gt;null&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px; text-align: center;&quot;&gt;&lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;element()&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;우선순위가 가장 높은 요소를 조회 (큐가 비어 있으면 예외)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.   &lt;code&gt;PriorityQueue&lt;/code&gt; 기본 사용법&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 기본 우선순위 (오름차순)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;PriorityQueue&lt;/code&gt;는 기본적으로 요소를 &lt;b&gt;오름차순&lt;/b&gt;으로 정렬합니다. 즉, 숫자가 작은 값이 가장 높은 우선순위를 가집니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;코드 예제&lt;/h4&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;import java.util.PriorityQueue;

public class PriorityQueueExample {
    public static void main(String[] args) {
        PriorityQueue&amp;lt;Integer&amp;gt; queue = new PriorityQueue&amp;lt;&amp;gt;();

        // 요소 삽입
        queue.add(30);
        queue.add(10);
        queue.add(20);

        // 우선순위가 가장 높은 요소부터 제거
        System.out.println(queue.poll()); // 출력: 10
        System.out.println(queue.poll()); // 출력: 20
        System.out.println(queue.poll()); // 출력: 30
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;결과&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요소가 &lt;b&gt;10, 20, 30&lt;/b&gt; 순서로 정렬되어 반환됩니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2 사용자 정의 정렬&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선순위를 직접 정의하려면 &lt;b&gt;&lt;code&gt;Comparator&lt;/code&gt;&lt;/b&gt;를 제공해야 합니다. 예를 들어, 내림차순 또는 사용자 정의 객체를 기준으로 정렬할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;내림차순 정렬&lt;/h4&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;import java.util.PriorityQueue;
import java.util.Comparator;

public class PriorityQueueDescendingExample {
    public static void main(String[] args) {
        // 내림차순 정렬
        PriorityQueue&amp;lt;Integer&amp;gt; queue = new PriorityQueue&amp;lt;&amp;gt;(Comparator.reverseOrder());

        // 요소 삽입
        queue.add(30);
        queue.add(10);
        queue.add(20);

        // 출력
        while (!queue.isEmpty()) {
            System.out.println(queue.poll()); // 30, 20, 10
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;사용자 정의 객체 정렬&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 학생 객체(Student)를 점수(grade)에 따라 정렬한다고 가정해 봅시다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;import java.util.PriorityQueue;
import java.util.Comparator;

class Student {
    String name;
    int grade;

    public Student(String name, int grade) {
        this.name = name;
        this.grade = grade;
    }

    @Override
    public String toString() {
        return name + &quot; (&quot; + grade + &quot;)&quot;;
    }
}

public class PriorityQueueCustomObjectExample {
    public static void main(String[] args) {
        // 점수(grade) 오름차순 정렬
        PriorityQueue&amp;lt;Student&amp;gt; queue = new PriorityQueue&amp;lt;&amp;gt;(Comparator.comparingInt(s -&amp;gt; s.grade));

        // 학생 데이터 삽입
        queue.add(new Student(&quot;Alice&quot;, 85));
        queue.add(new Student(&quot;Bob&quot;, 92));
        queue.add(new Student(&quot;Charlie&quot;, 78));

        // 출력
        while (!queue.isEmpty()) {
            System.out.println(queue.poll()); // Charlie (78), Alice (85), Bob (92)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;내림차순으로 사용자 정의 객체 정렬&lt;/h4&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;import java.util.PriorityQueue;
import java.util.Comparator;

public class PriorityQueueDescendingCustomObjectExample {
    public static void main(String[] args) {
        // 점수(grade) 내림차순 정렬
        PriorityQueue&amp;lt;Student&amp;gt; queue = new PriorityQueue&amp;lt;&amp;gt;((s1, s2) -&amp;gt; s2.grade - s1.grade);

        // 학생 데이터 삽입
        queue.add(new Student(&quot;Alice&quot;, 85));
        queue.add(new Student(&quot;Bob&quot;, 92));
        queue.add(new Student(&quot;Charlie&quot;, 78));

        // 출력
        while (!queue.isEmpty()) {
            System.out.println(queue.poll()); // Bob (92), Alice (85), Charlie (78)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.  ️ 내부 동작 원리&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.1 힙(Heap) 자료구조&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;PriorityQueue&lt;/code&gt;는 내부적으로 &lt;b&gt;힙(Heap)&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;최소 힙(Min-Heap)&lt;/b&gt;으로, 루트 노드가 가장 작은 값을 가집니다.&lt;/li&gt;
&lt;li&gt;삽입(&lt;code&gt;add&lt;/code&gt;)과 삭제(&lt;code&gt;poll&lt;/code&gt;) 시, 힙의 성질을 유지하기 위해 &lt;b&gt;O(log n)&lt;/b&gt; 시간 복잡도를 가집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.   제한 사항 및 주의점&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.1 요소의 순서 보장&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;PriorityQueue&lt;/code&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;5.2 중간 요소 접근&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;code&gt;peek()&lt;/code&gt; 또는 &lt;code&gt;poll()&lt;/code&gt;로 접근 가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.3 정렬 방식&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;code&gt;Comparator&lt;/code&gt;로 변경할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. ⚡ &lt;code&gt;PriorityQueue&lt;/code&gt;와 다른 큐 구현체 비교&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 16%; text-align: center; font-weight: bold;&quot;&gt;구현체&lt;/td&gt;
&lt;td style=&quot;width: 16%; text-align: center; font-weight: bold;&quot;&gt;내부 구조&lt;/td&gt;
&lt;td style=&quot;width: 16%; text-align: center; font-weight: bold;&quot;&gt;삽입/삭제 성능&lt;/td&gt;
&lt;td style=&quot;width: 12%; text-align: center; font-weight: bold;&quot;&gt;요소 정렬&lt;/td&gt;
&lt;td style=&quot;width: 12%; text-align: center; font-weight: bold;&quot;&gt;FIFO&lt;/td&gt;
&lt;td style=&quot;width: 12%; text-align: center; font-weight: bold;&quot;&gt;LIFO&lt;/td&gt;
&lt;td style=&quot;width: 16%; text-align: center; font-weight: bold;&quot;&gt;우선순위 기반&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;PriorityQueue&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;힙&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;O(log n)&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;✅&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;❌&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;❌&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;ArrayDeque&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;배열&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;O(1)&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;❌&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;✅&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;✅&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;LinkedList&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;연결 리스트&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;O(1)&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;❌&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;✅&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;✅&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7.   요약 및 활용 사례&lt;/h2&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;code&gt;PriorityQueue&lt;/code&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;code&gt;Comparator&lt;/code&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;li&gt;데이터 스트림 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;code&gt;PriorityQueue&lt;/code&gt;는 우선순위가 중요한 모든 상황에서 효율적으로 활용할 수 있는 자료구조입니다.&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>Java</category>
      <category>Java</category>
      <category>java queue</category>
      <category>PriorityQueue</category>
      <category>Que</category>
      <category>Queue</category>
      <category>queue sort</category>
      <category>자바 큐</category>
      <category>자바 큐 정렬</category>
      <category>큐</category>
      <category>큐 정렬</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/291</guid>
      <comments>https://tae-hui.tistory.com/entry/JAVA-PriorityQueue-%EC%84%A4%EB%AA%85-%EB%B0%8F-%EC%82%AC%EC%9A%A9%EB%B2%95#entry291comment</comments>
      <pubDate>Mon, 30 Dec 2024 08:00:56 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] Queue와 Deque 무엇이 좋을까?</title>
      <link>https://tae-hui.tistory.com/entry/JAVA-Queue%EC%99%80-Deque-%EB%AC%B4%EC%97%87%EC%9D%B4-%EC%A2%8B%EC%9D%84%EA%B9%8C</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;자바에서 &lt;code&gt;Queue&lt;/code&gt; 인터페이스는 기본적인 단방향 큐(FIFO)를 정의하는데 사용됩니다. 하지만, 실제로 &lt;b&gt;&lt;code&gt;Queue&lt;/code&gt; 대신 &lt;code&gt;Deque&lt;/code&gt;를 사용하여 동일한 동작을 구현해도 성능에 문제가 없습니다.&lt;/b&gt; &lt;code&gt;Deque&lt;/code&gt;는 큐와 스택 동작을 모두 지원하므로, &lt;code&gt;Queue&lt;/code&gt;의 모든 기능을 충분히 대체할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.   &lt;code&gt;Queue&lt;/code&gt;와 &lt;code&gt;Deque&lt;/code&gt;의 차이&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.1 &lt;code&gt;Queue&lt;/code&gt;의 특징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단방향 삽입/삭제만 가능하며, FIFO(First In, First Out) 동작을 따릅니다.&lt;/li&gt;
&lt;li&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;code&gt;add(E e)&lt;/code&gt;, &lt;code&gt;offer(E e)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;삭제&lt;/b&gt;: &lt;code&gt;poll()&lt;/code&gt;, &lt;code&gt;remove()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;조회&lt;/b&gt;: &lt;code&gt;peek()&lt;/code&gt;, &lt;code&gt;element()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.2 &lt;code&gt;Deque&lt;/code&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;b&gt;&lt;code&gt;Deque&lt;/code&gt;를 단방향 큐로 사용하면, &lt;code&gt;Queue&lt;/code&gt;의 모든 기능을 대체할 수 있습니다.&lt;/b&gt;&lt;/li&gt;
&lt;li&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;code&gt;addLast(E e)&lt;/code&gt; (뒤쪽 삽입, &lt;code&gt;Queue&lt;/code&gt;의 &lt;code&gt;offer&lt;/code&gt;와 동일)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;삭제&lt;/b&gt;: &lt;code&gt;pollFirst()&lt;/code&gt; (앞쪽 삭제, &lt;code&gt;Queue&lt;/code&gt;의 &lt;code&gt;poll&lt;/code&gt;과 동일)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;조회&lt;/b&gt;: &lt;code&gt;peekFirst()&lt;/code&gt; (앞쪽 조회, &lt;code&gt;Queue&lt;/code&gt;의 &lt;code&gt;peek&lt;/code&gt;과 동일)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.   &lt;code&gt;Deque&lt;/code&gt;를 사용하여 &lt;code&gt;Queue&lt;/code&gt; 동작 구현&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 예제&lt;/h3&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;import java.util.ArrayDeque;
import java.util.Deque;

public class DequeAsQueueExample {
    public static void main(String[] args) {
        Deque&amp;lt;Integer&amp;gt; deque = new ArrayDeque&amp;lt;&amp;gt;();

        // 큐의 삽입 동작
        deque.addLast(10); // Queue의 offer()와 동일
        deque.addLast(20);
        deque.addLast(30);

        // 큐의 삭제 동작
        System.out.println(deque.pollFirst()); // 출력: 10 (Queue의 poll()과 동일)
        System.out.println(deque.peekFirst()); // 출력: 20 (Queue의 peek()과 동일)

        // 남은 요소 출력
        System.out.println(deque); // 출력: [20, 30]
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.   &lt;code&gt;Deque&lt;/code&gt;로 구현할 때 성능 분석&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Deque&lt;/code&gt;를 &lt;code&gt;Queue&lt;/code&gt;처럼 사용할 때의 성능은 &lt;b&gt;&lt;code&gt;Queue&lt;/code&gt; 구현체와 동일하거나 더 효율적&lt;/b&gt;입니다. 특히, &lt;b&gt;&lt;code&gt;ArrayDeque&lt;/code&gt;&lt;/b&gt;와 &lt;b&gt;&lt;code&gt;LinkedList&lt;/code&gt;&lt;/b&gt;를 사용할 경우의 성능을 비교해 보면 다음과 같은 특징이 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 &lt;code&gt;ArrayDeque&lt;/code&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;O(1)&lt;/b&gt;: 양 끝에서의 삽입/삭제가 빠릅니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ArrayDeque&lt;/code&gt;는 내부적으로 배열을 사용하므로 &lt;b&gt;캐시 친화적&lt;/b&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;큐의 크기가 가득 차면 내부 배열을 2배로 늘리는 동작이 발생하지만, 이는 삽입/삭제의 평균 성능에 큰 영향을 미치지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2 &lt;code&gt;LinkedList&lt;/code&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;O(1)&lt;/b&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;code&gt;ArrayDeque&lt;/code&gt;보다 메모리를 더 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.   왜 &lt;code&gt;Deque&lt;/code&gt;를 사용하는 것이 괜찮은가?&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.1 &lt;code&gt;Queue&lt;/code&gt; 대신 &lt;code&gt;Deque&lt;/code&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;code&gt;Deque&lt;/code&gt;는 양방향 삽입/삭제 기능을 지원하므로, 더 유연하게 동작할 수 있습니다.&lt;/li&gt;
&lt;li&gt;하지만 단방향 동작만 구현해도 충분히 &lt;code&gt;Queue&lt;/code&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;code&gt;ArrayDeque&lt;/code&gt;나 &lt;code&gt;LinkedList&lt;/code&gt;를 사용하여 &lt;code&gt;Deque&lt;/code&gt;를 구현하면, &lt;code&gt;Queue&lt;/code&gt;의 주요 구현체(&lt;code&gt;LinkedList&lt;/code&gt;와 &lt;code&gt;ArrayDeque&lt;/code&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;code&gt;ArrayDeque&lt;/code&gt;는 배열 기반이기 때문에 메모리 접근 속도가 빠르고, 대부분의 상황에서 &lt;code&gt;LinkedList&lt;/code&gt;보다 효율적입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. ⚡ &lt;code&gt;Deque&lt;/code&gt;와 &lt;code&gt;Queue&lt;/code&gt; 구현체 비교&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.3023%; text-align: center; font-weight: bold;&quot;&gt;구현체&lt;/td&gt;
&lt;td style=&quot;width: 11.6977%; text-align: center; font-weight: bold;&quot;&gt;동작 방식&lt;/td&gt;
&lt;td style=&quot;width: 16%; text-align: center; font-weight: bold;&quot;&gt;삽입/삭제 성능&lt;/td&gt;
&lt;td style=&quot;width: 16%; text-align: center; font-weight: bold;&quot;&gt;중간 접근 성능&lt;/td&gt;
&lt;td style=&quot;width: 16%; text-align: center; font-weight: bold;&quot;&gt;메모리 사용&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; font-weight: bold;&quot;&gt;특징&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 20.3023%;&quot;&gt;&lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;Queue&lt;/code&gt; + &lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;LinkedList&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 11.6977%;&quot;&gt;연결 리스트&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 16%;&quot;&gt;O(1)&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 16%;&quot;&gt;O(n)&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 16%;&quot;&gt;높음&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 20%;&quot;&gt;동적 크기, 삽입/삭제 효율적&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 20.3023%;&quot;&gt;&lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;Queue&lt;/code&gt; + &lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;ArrayDeque&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 11.6977%;&quot;&gt;배열&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 16%;&quot;&gt;O(1)&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 16%;&quot;&gt;O(n)&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 16%;&quot;&gt;낮음&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 20%;&quot;&gt;빠른 삽입/삭제, 캐시 친화적&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 20.3023%;&quot;&gt;&lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;Deque&lt;/code&gt; + &lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;ArrayDeque&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 11.6977%;&quot;&gt;배열&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 16%;&quot;&gt;O(1)&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 16%;&quot;&gt;O(n)&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 16%;&quot;&gt;낮음&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 20%;&quot;&gt;큐와 스택 모두 가능, 양방향 삽입/삭제 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 20.3023%;&quot;&gt;&lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;Deque&lt;/code&gt; + &lt;code style=&quot;background-color: #e6f5ff; color: #333333;&quot;&gt;LinkedList&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 11.6977%;&quot;&gt;연결 리스트&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 16%;&quot;&gt;O(1)&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 16%;&quot;&gt;O(n)&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 16%;&quot;&gt;높음&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 20%;&quot;&gt;큐와 스택 모두 가능, 유연한 크기 확장 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6.   요약&lt;/h2&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;code&gt;Deque&lt;/code&gt;로 &lt;code&gt;Queue&lt;/code&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;code&gt;Deque&lt;/code&gt;를 사용하면 단방향 큐(&lt;code&gt;Queue&lt;/code&gt;)의 동작을 그대로 구현할 수 있습니다.&lt;/li&gt;
&lt;li&gt;예: &lt;code&gt;addLast&lt;/code&gt; &amp;rarr; &lt;code&gt;offer&lt;/code&gt;, &lt;code&gt;pollFirst&lt;/code&gt; &amp;rarr; &lt;code&gt;poll&lt;/code&gt;, &lt;code&gt;peekFirst&lt;/code&gt; &amp;rarr; &lt;code&gt;peek&lt;/code&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;code&gt;Deque&lt;/code&gt;를 사용하여 큐를 구현할 경우 성능은 동일하거나 더 효율적입니다.&lt;/li&gt;
&lt;li&gt;특히 &lt;code&gt;ArrayDeque&lt;/code&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;code&gt;ArrayDeque&lt;/code&gt;를 사용하세요.&lt;/li&gt;
&lt;li&gt;양방향 삽입/삭제가 필요하거나 더 유연한 사용이 필요하면 &lt;code&gt;Deque&lt;/code&gt; 인터페이스로 구현하세요.&lt;/li&gt;
&lt;li&gt;정렬이 필요하다면 Queue 인터페이스를 이용한 PriorityQueue 구현체를 사용해야합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  결론적으로, &lt;code&gt;Deque&lt;/code&gt;는 &lt;code&gt;Queue&lt;/code&gt;를 대체할 수 있는 충분히 강력하고 성능이 좋습니다.&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>Java</category>
      <category>arraydequeue</category>
      <category>deque</category>
      <category>Java</category>
      <category>LinkedList</category>
      <category>Queue</category>
      <category>데크</category>
      <category>덱</category>
      <category>자바</category>
      <category>자바 큐</category>
      <category>큐</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/290</guid>
      <comments>https://tae-hui.tistory.com/entry/JAVA-Queue%EC%99%80-Deque-%EB%AC%B4%EC%97%87%EC%9D%B4-%EC%A2%8B%EC%9D%84%EA%B9%8C#entry290comment</comments>
      <pubDate>Fri, 27 Dec 2024 08:00:52 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] @Transactional 알아보기 Part.3 #격리(Isolation)</title>
      <link>https://tae-hui.tistory.com/entry/JAVA-Transactional-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-Part3-%EA%B2%A9%EB%A6%ACIsolation</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  트랜잭션 격리 수준(Isolation Level)&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  격리 수준의 정의&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;격리 수준은 동시에 실행되는 트랜잭션 간의 &lt;b&gt;데이터 충돌 방지 및 무결성 보장&lt;/b&gt;을 위해 설정하는 규칙입니다. 각 수준은 트랜잭션 성능과 일관성 사이에서 &lt;b&gt;트레이드오프&lt;/b&gt;를 제공합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;격리 수준은 ANSI SQL 표준에 정의되어 있으며, Spring에서는 이를 데이터베이스에 전달하여 적용합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;격리 수준 설정 방법&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring에서 &lt;code&gt;@Transactional&lt;/code&gt; 어노테이션에 &lt;code&gt;isolation&lt;/code&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;aspectj&quot;&gt;&lt;code&gt;@Transactional(isolation = Isolation.READ_COMMITTED)
public void processData() {
    // 트랜잭션 동작
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  격리 수준 값과 특징&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 Spring과 ANSI SQL에서 제공하는 주요 격리 수준과 각 문제를 방지하는 방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 91px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 20px; text-align: center;&quot;&gt;&lt;u&gt;&lt;b&gt; 격리 수준 &lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 20px; text-align: center;&quot;&gt;&lt;u&gt;&lt;b&gt; 설명 &lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 20px; text-align: center;&quot;&gt;&lt;u&gt;&lt;b&gt; 방지되는 문제 &lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 20px; text-align: center;&quot;&gt;&lt;u&gt;&lt;b&gt; 발생 가능한 문제 &lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 20px;&quot;&gt;&lt;code style=&quot;color: #333333; text-align: start;&quot;&gt;READ_UNCOMMITTED&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 20px;&quot;&gt;커밋되지 않은 데이터 읽기 가능&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 20px;&quot;&gt;없음&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 20px;&quot;&gt;Dirty Read 발생&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;&lt;code style=&quot;color: #333333; text-align: start;&quot;&gt;READ_COMMITTED&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;커밋된 데이터만 읽기 가능&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;Dirty Read 방지&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;Non-Repeatable Read 발생&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;&lt;code style=&quot;color: #333333; text-align: start;&quot;&gt;REPEATABLE_READ&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;동일 데이터 트랜잭션 동안 일관성 유지&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;Dirty Read, Non-Repeatable Read 방지&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;Phantom Read 발생&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;&lt;code style=&quot;color: #333333; text-align: start;&quot;&gt;SERIALIZABL&lt;/code&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;E&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;트랜잭션 순차 실행으로 완전 격리&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;Dirty Read, Non-Repeatable Read, Phantom Read 방지&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;성능 저하, 교착 상태 발생 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&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;b&gt;Dirty Read (더티 읽기)&lt;/b&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Dirty Read&lt;/b&gt;는 &lt;b&gt;커밋되지 않은 데이터를 읽는 문제&lt;/b&gt;로, 롤백된 데이터가 다른 트랜잭션에 영향을 줄 수 있습니다.&lt;/p&gt;
&lt;/blockquote&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;트랜잭션 A가 데이터를 수정했으나 커밋하지 않음.&lt;/li&gt;
&lt;li&gt;트랜잭션 B가 수정 중인 데이터를 읽음.&lt;/li&gt;
&lt;li&gt;트랜잭션 A가 롤백되면, 트랜잭션 B는 잘못된 데이터를 사용하게 됨.&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;code&gt;READ_COMMITTED&lt;/code&gt; 이상으로 설정.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2️⃣ &lt;b&gt;Non-Repeatable Read (불일치 읽기)&lt;/b&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Non-Repeatable Read&lt;/b&gt;는 &lt;b&gt;같은 데이터를 반복 조회할 때 값이 달라지는 문제&lt;/b&gt;입니다.&lt;/p&gt;
&lt;/blockquote&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;트랜잭션 A가 데이터를 읽음.&lt;/li&gt;
&lt;li&gt;트랜잭션 B가 데이터를 수정하고 커밋.&lt;/li&gt;
&lt;li&gt;트랜잭션 A가 같은 데이터를 다시 읽으면 값이 달라짐.&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;code&gt;REPEATABLE_READ&lt;/code&gt; 이상으로 설정.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3️⃣ &lt;b&gt;Phantom Read (팬텀 읽기)&lt;/b&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Phantom Read&lt;/b&gt;는 &lt;b&gt;조건에 맞는 데이터 집합이 트랜잭션 도중에 변경되는 문제&lt;/b&gt;입니다.&lt;/p&gt;
&lt;/blockquote&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;트랜잭션 A가 &lt;code&gt;WHERE&lt;/code&gt; 조건으로 데이터를 조회.&lt;/li&gt;
&lt;li&gt;트랜잭션 B가 새로운 데이터를 추가하거나 삭제하고 커밋.&lt;/li&gt;
&lt;li&gt;트랜잭션 A가 같은 조건으로 다시 조회하면 결과 집합이 달라짐.&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;code&gt;SERIALIZABLE&lt;/code&gt;로 설정.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  격리 수준 예제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1️⃣ &lt;b&gt;&lt;code&gt;READ_UNCOMMITTED&lt;/code&gt; 예제 (Dirty Read 허용)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void processUncommittedData() {
    // 커밋되지 않은 데이터 읽기 가능
}&lt;/code&gt;&lt;/pre&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;: Dirty Read 발생.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2️⃣ &lt;b&gt;&lt;code&gt;READ_COMMITTED&lt;/code&gt; 예제 (Dirty Read 방지)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;@Transactional(isolation = Isolation.READ_COMMITTED)
public void processCommittedData() {
    // 커밋된 데이터만 읽기
}&lt;/code&gt;&lt;/pre&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;: Non-Repeatable Read 발생.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3️⃣ &lt;b&gt;&lt;code&gt;REPEATABLE_READ&lt;/code&gt; 예제 (Non-Repeatable Read 방지)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;@Transactional(isolation = Isolation.REPEATABLE_READ)
public void processRepeatableData() {
    // 트랜잭션 동안 동일 데이터 일관성 유지
}&lt;/code&gt;&lt;/pre&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;: Phantom Read 발생 가능.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4️⃣ &lt;b&gt;&lt;code&gt;SERIALIZABLE&lt;/code&gt; 예제 (팬텀 읽기 방지)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;@Transactional(isolation = Isolation.SERIALIZABLE)
public void processSerializableData() {
    // 트랜잭션 순차 실행
}&lt;/code&gt;&lt;/pre&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;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  스냅샷과 MVCC (Multi-Version Concurrency Control)&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스냅샷이란?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스냅샷은 &lt;b&gt;트랜잭션이 시작된 시점의 데이터 상태&lt;/b&gt;를 캡처하여, 트랜잭션 동안 일관성을 유지합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;MVCC 동작 방식&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스냅샷 데이터는 데이터베이스 엔진의 &lt;b&gt;Undo Log&lt;/b&gt; 또는 메모리에 저장.&lt;/li&gt;
&lt;li&gt;각 트랜잭션은 &lt;b&gt;스냅샷을 기반으로 읽기 작업&lt;/b&gt;을 수행하므로 다른 트랜잭션의 변경에 영향을 받지 않음.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;스냅샷 저장 위치&lt;/b&gt;&lt;/h4&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;Undo Log&lt;/b&gt;에 저장.&lt;/li&gt;
&lt;li&gt;WAS(Application Server)로 올라가지 않음.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;code&gt;READ_UNCOMMITTED&lt;/code&gt; 또는 &lt;code&gt;READ_COMMITTED&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터 정확성이 중요한 경우&lt;/b&gt;: &lt;code&gt;REPEATABLE_READ&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;가장 높은 일관성이 필요한 경우&lt;/b&gt;: &lt;code&gt;SERIALIZABLE&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  요약&lt;/h2&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;b&gt;&lt;code&gt;READ_UNCOMMITTED&lt;/code&gt;&lt;/b&gt;: Dirty Read 허용. 성능 우선.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;READ_COMMITTED&lt;/code&gt;&lt;/b&gt;: Dirty Read 방지. 일반적인 설정.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;REPEATABLE_READ&lt;/code&gt;&lt;/b&gt;: Non-Repeatable Read 방지.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;SERIALIZABLE&lt;/code&gt;&lt;/b&gt;: 가장 높은 일관성 제공. 성능 저하 가능.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스냅샷과 MVCC&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;Undo Log 또는 메모리에서 관리.&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;b&gt;Dirty Read&lt;/b&gt;: &lt;code&gt;READ_COMMITTED&lt;/code&gt; 이상.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Non-Repeatable Read&lt;/b&gt;: &lt;code&gt;REPEATABLE_READ&lt;/code&gt; 이상.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Phantom Read&lt;/b&gt;: &lt;code&gt;SERIALIZABLE&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;</description>
      <category>Java</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/289</guid>
      <comments>https://tae-hui.tistory.com/entry/JAVA-Transactional-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-Part3-%EA%B2%A9%EB%A6%ACIsolation#entry289comment</comments>
      <pubDate>Thu, 26 Dec 2024 08:00:43 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] @Transactional 알아보기 Part.2 #전파(Propagation)</title>
      <link>https://tae-hui.tistory.com/entry/JAVA-Transactional-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-Part-%EC%A0%84%ED%8C%8CPropagation</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  트랜잭션 전파(Propagation)란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜잭션 전파(Propagation)는 트랜잭션의 &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;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;p data-ke-size=&quot;size16&quot;&gt;Spring에서 제공하는 &lt;b&gt;전파 속성&lt;/b&gt;은 &lt;code&gt;@Transactional&lt;/code&gt; 어노테이션의 &lt;code&gt;propagation&lt;/code&gt; 속성으로 설정할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  트랜잭션 전파와 격리 수준의 차이&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;전파(Propagation)&lt;/b&gt;&lt;/h4&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;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;격리 수준(Isolation Level)&lt;/b&gt;&lt;/h4&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;예: Dirty Read 방지, Repeatable Read 보장 등.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;간단히&lt;/b&gt;: 전파는 &lt;b&gt;트랜잭션의 관계&lt;/b&gt;, 격리 수준은 &lt;b&gt;데이터 무결성&lt;/b&gt;을 관리.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  트랜잭션 전파 속성의 종류와 동작 방식&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring은 7가지 전파 속성을 제공합니다. 각 속성의 동작 방식을 살펴봅니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 139px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 19.0698%; height: 20px;&quot;&gt;속성&lt;/td&gt;
&lt;td style=&quot;width: 80.9302%; height: 20px;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 19.0698%; height: 17px;&quot;&gt;&lt;code style=&quot;background-color: #e6f5ff; color: #333333; text-align: start;&quot;&gt;REQUIRED&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;width: 80.9302%; height: 17px;&quot;&gt;(기본값) 기존 트랜잭션에 참여. 없으면 새로 생성.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 19.0698%; height: 17px;&quot;&gt;&lt;code style=&quot;background-color: #e6f5ff; color: #333333; text-align: start;&quot;&gt;REQUIRES_NEW&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;width: 80.9302%; height: 17px;&quot;&gt;항상 새로운 트랜잭션 생성. 기존 트랜잭션은 일시 중단.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 19.0698%; height: 17px;&quot;&gt;&lt;code style=&quot;background-color: #e6f5ff; color: #333333; text-align: start;&quot;&gt;SUPPORTS&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;width: 80.9302%; height: 17px;&quot;&gt;트랜잭션이 있으면 참여, 없으면 트랜잭션 없이 실행.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 19.0698%; height: 17px;&quot;&gt;&lt;code style=&quot;background-color: #e6f5ff; color: #333333; text-align: start;&quot;&gt;NOT_SUPPORTED&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;width: 80.9302%; height: 17px;&quot;&gt;트랜잭션 없이 실행. 현재 트랜잭션이 있으면 일시 중단.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 19.0698%; height: 17px;&quot;&gt;&lt;code style=&quot;background-color: #e6f5ff; color: #333333; text-align: start;&quot;&gt;MANDATORY&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;width: 80.9302%; height: 17px;&quot;&gt;반드시 기존 트랜잭션에 참여. 없으면 예외 발생.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 19.0698%; height: 17px;&quot;&gt;&lt;code style=&quot;background-color: #e6f5ff; color: #333333; text-align: start;&quot;&gt;NEVER&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;width: 80.9302%; height: 17px;&quot;&gt;트랜잭션이 있으면 예외 발생. 트랜잭션 없이 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 19.0698%; height: 17px;&quot;&gt;&lt;code style=&quot;background-color: #e6f5ff; color: #333333; text-align: start;&quot;&gt;NESTED&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;width: 80.9302%; height: 17px;&quot;&gt;부모 트랜잭션 내부에서 독립적인 롤백이 가능한 트랜잭션 생성. (Savepoint 기반)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  주요 전파 속성의 상세 동작 방식과 예제&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1️⃣ &lt;b&gt;&lt;code&gt;REQUIRED&lt;/code&gt; 전파 속성&lt;/b&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;b&gt;새 트랜잭션 생성&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;기본값으로 설정되어 있으며, 가장 일반적으로 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;사용 예제: 주문 및 결제&lt;/b&gt;&lt;/h4&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;코드&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@Service
public class OrderService {
    @Transactional(propagation = Propagation.REQUIRED)
    public void placeOrder() {
        saveOrderDetails();  // 같은 트랜잭션
        processPayment();    // 같은 트랜잭션
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void saveOrderDetails() {
        // 주문 정보 저장
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void processPayment() {
        // 결제 처리
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;placeOrder&lt;/code&gt; 메서드에서 오류 발생 시, 모든 작업이 롤백됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;동일한 트랜잭션&lt;/b&gt;으로 처리됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2️⃣ &lt;b&gt;&lt;code&gt;REQUIRES_NEW&lt;/code&gt; 전파 속성&lt;/b&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;일시 중단&lt;/b&gt;됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;사용 예제: 주문 처리와 로그 저장&lt;/b&gt;&lt;/h4&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;코드&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@Service
public class OrderService {
    @Transactional(propagation = Propagation.REQUIRED)
    public void processOrder() {
        saveOrderDetails();  // 부모 트랜잭션
        saveAuditLog();      // 독립 트랜잭션
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void saveOrderDetails() {
        // 주문 데이터 저장
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveAuditLog() {
        // 로그 데이터 저장
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;saveAuditLog&lt;/code&gt;는 부모 트랜잭션과 &lt;b&gt;독립적으로 커밋/롤백&lt;/b&gt;됩니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;saveOrderDetails&lt;/code&gt;에서 예외가 발생해도, 로그는 저장됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3️⃣ &lt;b&gt;&lt;code&gt;SUPPORTS&lt;/code&gt; 전파 속성&lt;/b&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;b&gt;트랜잭션 없이 실행&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;사용 예제: 상품 조회&lt;/b&gt;&lt;/h4&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;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;코드&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;@Service
public class ProductService {
    @Transactional(propagation = Propagation.SUPPORTS)
    public List&amp;lt;Product&amp;gt; getAllProducts() {
        // 상품 조회
        return productRepository.findAll();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/h4&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;b&gt;트랜잭션 없이 실행&lt;/b&gt;됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4️⃣ &lt;b&gt;&lt;code&gt;NOT_SUPPORTED&lt;/code&gt; 전파 속성&lt;/b&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;b&gt;일시 중단&lt;/b&gt;됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;사용 예제: 파일 저장&lt;/b&gt;&lt;/h4&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;코드&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@Service
public class FileService {
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void saveFile(File file) {
        // 파일 저장 작업
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/h4&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5️⃣ &lt;b&gt;&lt;code&gt;MANDATORY&lt;/code&gt; 전파 속성&lt;/b&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;b&gt;예외 발생&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;사용 예제: 주문 상태 업데이트&lt;/b&gt;&lt;/h4&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;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;코드&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@Service
public class OrderService {
    @Transactional(propagation = Propagation.REQUIRED)
    public void processOrder() {
        updateOrderStatus();  // 기존 트랜잭션에 참여
    }

    @Transactional(propagation = Propagation.MANDATORY)
    public void updateOrderStatus() {
        // 주문 상태 업데이트
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;processOrder&lt;/code&gt;가 트랜잭션 없이 호출되면 &lt;b&gt;예외 발생&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;트랜잭션 내에서 호출될 때만 정상 실행.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6️⃣ &lt;b&gt;&lt;code&gt;NEVER&lt;/code&gt; 전파 속성&lt;/b&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;b&gt;트랜잭션 없이 실행&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;사용 예제: 캐싱 작업&lt;/b&gt;&lt;/h4&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;코드&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@Service
public class CacheService {
    @Transactional(propagation = Propagation.NEVER)
    public void cacheData(Object data) {
        // 캐싱 작업
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;트랜잭션 내에서 호출되면 &lt;b&gt;IllegalTransactionStateException&lt;/b&gt; 발생.&lt;/li&gt;
&lt;li&gt;트랜잭션 없이 호출될 때만 실행.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7️⃣ &lt;b&gt;&lt;code&gt;NESTED&lt;/code&gt; 전파 속성&lt;/b&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;하며, Savepoint를 생성해 트랜잭션의 일부 작업만 롤백 가능합니다.&lt;/li&gt;
&lt;li&gt;부모 트랜잭션은 유지되며, 자식 트랜잭션에서 발생한 오류는 Savepoint를 활용해 부분적으로 롤백됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  동작 원리&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;부모 트랜잭션이 시작되면, NESTED 전파를 가진 메서드가 호출될 때 Savepoint가 생성됩니다.&lt;/li&gt;
&lt;li&gt;자식 트랜잭션에서 오류가 발생하면 Savepoint까지만 롤백되며, 부모 트랜잭션은 영향을 받지 않습니다.&lt;/li&gt;
&lt;li&gt;부모 트랜잭션이 커밋되지 않으면 자식 트랜잭션도 커밋되지 않습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  사용 예제: 주문과 배송 처리&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;상황&lt;/b&gt;&lt;/h4&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;b&gt;배송 데이터만 롤백&lt;/b&gt;되고 주문 데이터는 유지됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;코드&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Service
public class OrderService {

    @Transactional(propagation = Propagation.REQUIRED)
    public void processOrder() {
        saveOrderDetails();     // 부모 트랜잭션
        processShipping();      // Savepoint 생성 (NESTED)
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void saveOrderDetails() {
        // 주문 데이터 저장 (부모 트랜잭션)
        System.out.println(&quot;Order details saved&quot;);
    }

    @Transactional(propagation = Propagation.NESTED)
    public void processShipping() {
        System.out.println(&quot;Processing shipping...&quot;);
        if (someConditionFails()) {
            throw new RuntimeException(&quot;Shipping failed!&quot;);
        }
        // 배송 데이터 저장
        System.out.println(&quot;Shipping details saved&quot;);
    }

    private boolean someConditionFails() {
        return true; // 테스트를 위해 항상 실패하도록 설정
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;code&gt;processOrder()&lt;/code&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;code&gt;Propagation.REQUIRED&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;saveOrderDetails()&lt;/code&gt; 실행 &amp;rarr; 주문 데이터 저장.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;processShipping()&lt;/code&gt; 호출&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;NESTED 전파에 따라 &lt;b&gt;Savepoint&lt;/b&gt;가 생성됩니다.&lt;/li&gt;
&lt;li&gt;배송 데이터 처리 중 예외 발생(&lt;code&gt;throw new RuntimeException()&lt;/code&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;code&gt;processShipping&lt;/code&gt;)이 Savepoint로 롤백됩니다.&lt;/li&gt;
&lt;li&gt;부모 트랜잭션(&lt;code&gt;processOrder&lt;/code&gt;)은 유지되며 커밋 가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;code&gt;saveOrderDetails()&lt;/code&gt;에서 주문 정보가 저장됩니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;processShipping()&lt;/code&gt;에서 예외가 발생해 배송 데이터 저장이 롤백됩니다.&lt;/li&gt;
&lt;li&gt;부모 트랜잭션은 성공적으로 커밋됩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  결과 해석&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/h4&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;&lt;b&gt;적용 사례&lt;/b&gt;&lt;/h4&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  전파 속성으로 인해 전체 작업이 롤백되는 상황과 해결 방법&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;문제&lt;/h4&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;/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;b&gt;&lt;code&gt;REQUIRES_NEW&lt;/code&gt;&lt;/b&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;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;@Transactional
public void parentTransaction() {
    childTransaction(); // 부모 트랜잭션 참여
    saveLog(); // 독립 트랜잭션
}

@Transactional(propagation = Propagation.REQUIRED)
public void childTransaction() {
    throw new RuntimeException(&quot;Rollback!&quot;);
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveLog() {
    // 독립적으로 로그 저장
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  독립적인 트랜잭션에서 예외 발생 시 처리 방법&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;문제&lt;/h4&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;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;해결 방법&lt;/h4&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;code&gt;REQUIRES_NEW&lt;/code&gt;&lt;/b&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;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveAuditLog() {
    try {
        // 로그 저장 작업
    } catch (Exception e) {
        // 예외 처리
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  전파 속성으로 인해 새로운 트랜잭션을 만들지 못하는 상황&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;문제&lt;/h4&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;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;해결 방법&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;AopContext 사용&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;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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  요약&lt;/h2&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;code&gt;REQUIRED&lt;/code&gt;, &lt;code&gt;REQUIRES_NEW&lt;/code&gt;, &lt;code&gt;SUPPORTS&lt;/code&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;code&gt;REQUIRED&lt;/code&gt;: 기본값. 트랜잭션 참여 또는 생성.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;REQUIRES_NEW&lt;/code&gt;: 새로운 독립 트랜잭션 생성.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NESTED&lt;/code&gt;: 부모 트랜잭션의 일부로 동작하며 Savepoint 생성.&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;code&gt;REQUIRES_NEW&lt;/code&gt;로 해결.&lt;/li&gt;
&lt;li&gt;내부 호출 문제는 &lt;b&gt;AopContext&lt;/b&gt;나 &lt;b&gt;빈 분리&lt;/b&gt;로 해결.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>Java</category>
      <category>@transcational</category>
      <category>aop</category>
      <category>aopcontext</category>
      <category>BEAN</category>
      <category>nested</category>
      <category>Propagation</category>
      <category>required</category>
      <category>REQUIRES_NEW</category>
      <category>savepoint</category>
      <category>supports</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/288</guid>
      <comments>https://tae-hui.tistory.com/entry/JAVA-Transactional-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-Part-%EC%A0%84%ED%8C%8CPropagation#entry288comment</comments>
      <pubDate>Tue, 24 Dec 2024 08:00:15 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] @Transactional 알아보기 Part.1  #프록시와 트랜잭션 동작 원리</title>
      <link>https://tae-hui.tistory.com/entry/JAVA-Transactional-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-Part1</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  &lt;code&gt;@Transactional&lt;/code&gt;이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring에서 제공하는 트랜잭션 관리 어노테이션으로, 데이터 작업의 &lt;b&gt;ACID&lt;/b&gt; 특성을 보장하며 트랜잭션 시작, 커밋, 롤백을 자동으로 처리합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  @Transactional의 동작 원리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;@Transactional&lt;/code&gt;은 &lt;b&gt;AOP(Aspect-Oriented Programming)&lt;/b&gt; 기반으로 동작하며, 프록시 객체가 &lt;b&gt;트랜잭션의 시작과 종료를 제어&lt;/b&gt;합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;프록시 동작 구조&lt;/b&gt;&lt;/h4&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;가 호출 &amp;rarr; &lt;b&gt;프록시 객체&lt;/b&gt;가 메서드를 가로챔.&lt;/li&gt;
&lt;li&gt;프록시 객체가 트랜잭션을 시작(Commit/Rollback 결정).&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실제 객체&lt;/b&gt;의 메서드 실행.&lt;/li&gt;
&lt;li&gt;메서드 종료 후 트랜잭션 종료.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  트랜잭션은 원래 하나인가? 독립적으로 동작한다는 의미는?&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;트랜잭션은 하나의 작업 단위&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;독립적이라는 의미&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;b&gt;전파 속성(Propagation)&lt;/b&gt;을 통해 다른 트랜잭션과의 관계를 정의.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  트랜잭션 생성 및 독립적 관리 방식&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Spring의 트랜잭션 생성 방식&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;전파 속성(Propagation)&lt;/b&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;독립적 관리 사례&lt;/b&gt;&lt;/h4&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;code&gt;REQUIRES_NEW&lt;/code&gt;&lt;/b&gt;를 사용해 부모 트랜잭션과 독립된 새 트랜잭션을 생성.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  클래스 내부 호출과 트랜잭션 동작&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  왜 클래스 내부에서 &lt;code&gt;@Transactional&lt;/code&gt; 메서드를 호출하면 새로운 트랜잭션이 생성되지 않는가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring의 &lt;code&gt;@Transactional&lt;/code&gt;은 &lt;b&gt;프록시 객체&lt;/b&gt;를 통해 트랜잭션을 관리합니다.&lt;br /&gt;그러나 클래스 내부에서 메서드를 호출하면 &lt;b&gt;프록시 객체를 거치지 않고 직접 호출&lt;/b&gt;하기 때문에 새로운 트랜잭션이 생성되지 않습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;문제 상황 코드&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;public class MyService {
    @Transactional
    public void outerMethod() {
        innerMethod(); // 프록시 객체를 거치지 않고 호출
    }

    @Transactional
    public void innerMethod() {
        // 트랜잭션 동작하지 않음
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;프록시를 거치지 않아 발생하는 문제&lt;/b&gt;&lt;/h4&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;code&gt;outerMethod()&lt;/code&gt;는 트랜잭션이 적용되지만, 내부 호출(&lt;code&gt;innerMethod()&lt;/code&gt;)은 적용되지 않음.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;내부 호출은 &lt;b&gt;프록시 객체&lt;/b&gt;를 우회하기 때문.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Spring AOP의 프록시 구조&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;프록시 객체란?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring의 AOP는 &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;b&gt;프록시 객체&lt;/b&gt;는 실제 메서드를 호출하기 전/후에 부가 기능(트랜잭션 관리)을 추가합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;프록시 호출 흐름&lt;/b&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[클라이언트] &amp;rarr; [프록시 객체] &amp;rarr; [실제 객체]&lt;/p&gt;
&lt;/blockquote&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;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;내부 호출 문제 발생 이유&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클래스 내부에서 호출 시 &lt;b&gt;[클라이언트] &amp;rarr; [프록시]&lt;/b&gt; 구조를 우회하여, 실제 객체의 메서드가 직접 호출됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&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;1️⃣ &lt;b&gt;&lt;code&gt;AopContext.currentProxy()&lt;/code&gt;를 사용&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring에서 제공하는 &lt;code&gt;AopContext.currentProxy()&lt;/code&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;java&quot;&gt;&lt;code&gt;public class MyService {
    @Transactional
    public void outerMethod() {
        ((MyService) AopContext.currentProxy()).innerMethod(); // 프록시를 통해 호출
    }

    @Transactional
    public void innerMethod() {
        // 트랜잭션 적용
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2️⃣ &lt;b&gt;내부 호출 대상 메서드를 별도 빈으로 분리&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;분리된 클래스 코드&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@Component
public class InnerService {
    @Transactional
    public void innerMethod() {
        // 새로운 트랜잭션 생성
    }
}

@Component
public class MyService {
    @Autowired
    private InnerService innerService;

    @Transactional
    public void outerMethod() {
        innerService.innerMethod(); // 프록시를 거쳐 호출
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  프록시 객체와 트랜잭션이 클래스 외부 호출에서만 동작하는 이유&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;프록시 객체 동작의 본질&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring은 &lt;b&gt;JDK 동적 프록시&lt;/b&gt; 또는 &lt;b&gt;CGLIB 프록시&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;JDK 동적 프록시&lt;/b&gt;: 인터페이스 기반.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CGLIB 프록시&lt;/b&gt;: 클래스 상속 기반.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;클래스 외부 호출이 필요한 이유&lt;/b&gt;&lt;/h4&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;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Spring 트랜잭션에서 내부 호출이 프록시를 거치지 않는다는 것은 무슨 의미인가?&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;내부 호출 동작 원리&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Spring은 AOP 프록시를 통해 트랜잭션을 제어.&lt;/li&gt;
&lt;li&gt;내부 호출은 &lt;b&gt;프록시 객체&lt;/b&gt;를 거치지 않고 실제 객체 메서드를 직접 호출.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;문제 해결 방법&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;AopContext 사용&lt;/b&gt;: 프록시 객체를 직접 참조.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;빈 분리&lt;/b&gt;: 호출 메서드를 별도 빈으로 분리.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  메서드 단위의 &lt;code&gt;@Transactional&lt;/code&gt;은 클래스 전체에 영향을 미치는가?&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;적용 범위&lt;/b&gt;&lt;/h4&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;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;주의&lt;/b&gt;&lt;/h4&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;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  요약&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&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;code&gt;@Transactional&lt;/code&gt;의 동작 원리&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AOP 기반 프록시 객체를 통해 트랜잭션을 제어.&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;code&gt;AopContext.currentProxy()&lt;/code&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;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;code&gt;@Transactional&lt;/code&gt;은 기본적으로 메서드 단위로 적용되며, 클래스 레벨에 선언 시 모든 메서드가 트랜잭션 대상.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>Java</category>
      <category>@Transactional</category>
      <category>@transactional의 동작 원리</category>
      <category>aop</category>
      <category>Spring Transaction</category>
      <category>string transactional</category>
      <category>transaction</category>
      <category>커밋</category>
      <category>트렌젝션</category>
      <category>프록시 객체</category>
      <category>프록시 패턴</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/287</guid>
      <comments>https://tae-hui.tistory.com/entry/JAVA-Transactional-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-Part1#entry287comment</comments>
      <pubDate>Mon, 23 Dec 2024 15:06:08 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] Spring 공통 모듈을 패키지화하고 GitHub Packages에 등록하는 방법</title>
      <link>https://tae-hui.tistory.com/entry/JAVA-Spring-%EA%B3%B5%ED%86%B5-%EB%AA%A8%EB%93%88%EC%9D%84-%ED%8C%A8%ED%82%A4%EC%A7%80%ED%99%94%ED%95%98%EA%B3%A0-GitHub-Packages%EC%97%90-%EB%93%B1%EB%A1%9D%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot 프로젝트를 진행하다 보면, 여러 프로젝트에서 재사용할 수 있는 &lt;b&gt;공통 모듈&lt;/b&gt;을 패키지화해서 관리하고 싶을 때가 많습니다. 이번 포스팅에서는 &lt;b&gt;공통 모듈을 Maven 패키지로 만들어 GitHub Packages에 등록&lt;/b&gt;하고, 이를 다른 프로젝트에서 사용하는 방법을 처음부터 끝까지 자세히 설명합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.   공통 모듈 프로젝트 생성&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1️⃣ 프로젝트 구조 만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot 공통 모듈 프로젝트를 생성합니다. &lt;b&gt;Maven&lt;/b&gt; 프로젝트로 설정하는 것이 중요합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Maven 프로젝트 생성 명령어:&lt;/h4&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;mvn archetype:generate -DgroupId=com.example -DartifactId=springboot-common-modules -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;디렉토리 구조:&lt;/h4&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;springboot-common-modules
├── src/main/java/com/example/common
│   └── StringUtils.java
├── src/test/java/com/example/common
├── pom.xml&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2️⃣ 공통 모듈 코드 작성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공통적으로 사용할 코드를 &lt;code&gt;src/main/java&lt;/code&gt; 폴더에 추가합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예: &lt;code&gt;StringUtils&lt;/code&gt; 클래스&lt;/h4&gt;
&lt;pre class=&quot;axapta&quot;&gt;&lt;code&gt;package com.example.common;

public class StringUtils {
    public static boolean isEmpty(String str) {
        return str == null || str.trim().isEmpty();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.   Maven &lt;code&gt;pom.xml&lt;/code&gt; 설정&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1️⃣ 기본 POM 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트의 &lt;code&gt;pom.xml&lt;/code&gt; 파일에 아래 내용을 추가합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;POM 파일 구조:&lt;/h4&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;project xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot;
         xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
         xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&quot;&amp;gt;
    &amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;/modelVersion&amp;gt;
    &amp;lt;groupId&amp;gt;com.example&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;springboot-common-modules&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.0.0&amp;lt;/version&amp;gt;
    &amp;lt;packaging&amp;gt;jar&amp;lt;/packaging&amp;gt;

    &amp;lt;name&amp;gt;Spring Boot Common Modules&amp;lt;/name&amp;gt;
    &amp;lt;description&amp;gt;Reusable Spring Boot components&amp;lt;/description&amp;gt;

    &amp;lt;dependencies&amp;gt;
        &amp;lt;!-- 필요 시 의존성 추가 --&amp;gt;
    &amp;lt;/dependencies&amp;gt;
&amp;lt;/project&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.   GitHub Packages에 배포하기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1️⃣ &lt;code&gt;distributionManagement&lt;/code&gt; 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub Packages로 배포하려면 &lt;code&gt;pom.xml&lt;/code&gt; 파일에 아래 내용을 추가해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;distributionManagement&amp;gt;
    &amp;lt;repository&amp;gt;
        &amp;lt;id&amp;gt;github&amp;lt;/id&amp;gt;
        &amp;lt;url&amp;gt;https://maven.pkg.github.com/&amp;lt;GitHub-사용자명&amp;gt;/&amp;lt;저장소-이름&amp;gt;&amp;lt;/url&amp;gt;
    &amp;lt;/repository&amp;gt;
&amp;lt;/distributionManagement&amp;gt;&lt;/code&gt;&lt;/pre&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;code&gt;id&lt;/code&gt;&lt;/b&gt;: &lt;code&gt;github&lt;/code&gt;로 설정.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;url&lt;/code&gt;&lt;/b&gt;: GitHub Packages URL을 &lt;code&gt;&amp;lt;GitHub 사용자명&amp;gt;&lt;/code&gt;과 &lt;code&gt;&amp;lt;저장소 이름&amp;gt;&lt;/code&gt;으로 대체하여 입력.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2️⃣ 인증 정보 추가 (&lt;code&gt;settings.xml&lt;/code&gt;)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub Packages에 업로드하려면 &lt;code&gt;~/.m2/settings.xml&lt;/code&gt; 파일에 인증 정보를 추가합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;settings.xml&lt;/code&gt; 내용:&lt;/h4&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;settings&amp;gt;
    &amp;lt;servers&amp;gt;
        &amp;lt;server&amp;gt;
            &amp;lt;id&amp;gt;github&amp;lt;/id&amp;gt;
            &amp;lt;username&amp;gt;GITHUB_USERNAME&amp;lt;/username&amp;gt;
            &amp;lt;password&amp;gt;GITHUB_PERSONAL_ACCESS_TOKEN&amp;lt;/password&amp;gt;
        &amp;lt;/server&amp;gt;
    &amp;lt;/servers&amp;gt;
&amp;lt;/settings&amp;gt;&lt;/code&gt;&lt;/pre&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;code&gt;id&lt;/code&gt;&lt;/b&gt;: &lt;code&gt;github&lt;/code&gt;로 설정 (POM의 &lt;code&gt;distributionManagement&lt;/code&gt;와 동일).&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;username&lt;/code&gt;&lt;/b&gt;: GitHub 사용자명.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;password&lt;/code&gt;&lt;/b&gt;: GitHub Personal Access Token.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3️⃣ GitHub Actions로 자동 배포 설정&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1️⃣ &lt;code&gt;.github/workflows/deploy.yml&lt;/code&gt; 작성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub Actions를 이용해 Push할 때 자동으로 배포되도록 설정합니다.&lt;/p&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;name: Deploy Maven Package

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout repository
      uses: actions/checkout@v3

    - name: Set up Java
      uses: actions/setup-java@v3
      with:
        distribution: 'temurin'
        java-version: '17'

    - name: Cache Maven dependencies
      uses: actions/cache@v3
      with:
        path: ~/.m2
        key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
        restore-keys: ${{ runner.os }}-maven

    - name: Deploy to GitHub Packages
      run: mvn deploy
      env:
        GITHUB_USERNAME: ${{ secrets.GITHUB_ACTOR }}
        GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.   다른 프로젝트에서 공통 모듈 사용하기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1️⃣ &lt;code&gt;pom.xml&lt;/code&gt;에 의존성 추가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub Packages에 업로드한 공통 모듈을 사용하는 프로젝트에서 아래 내용을 추가합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;GitHub Packages 저장소 정보 추가:&lt;/h4&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;repositories&amp;gt;
    &amp;lt;repository&amp;gt;
        &amp;lt;id&amp;gt;github&amp;lt;/id&amp;gt;
        &amp;lt;url&amp;gt;https://maven.pkg.github.com/&amp;lt;GitHub-사용자명&amp;gt;/&amp;lt;저장소-이름&amp;gt;&amp;lt;/url&amp;gt;
    &amp;lt;/repository&amp;gt;
&amp;lt;/repositories&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;공통 모듈 의존성 추가:&lt;/h4&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;dependencies&amp;gt;
    &amp;lt;dependency&amp;gt;
        &amp;lt;groupId&amp;gt;com.example&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;springboot-common-modules&amp;lt;/artifactId&amp;gt;
        &amp;lt;version&amp;gt;1.0.0&amp;lt;/version&amp;gt;
    &amp;lt;/dependency&amp;gt;
&amp;lt;/dependencies&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. ✅ 정리&lt;/h2&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;Maven 프로젝트로 생성 후 코드를 작성.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GitHub Packages 설정&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;pom.xml&lt;/code&gt;의 &lt;code&gt;distributionManagement&lt;/code&gt;와 &lt;code&gt;settings.xml&lt;/code&gt;에 인증 정보 추가.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GitHub Actions로 자동 배포&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Push 시 자동으로 GitHub Packages에 배포되도록 설정.&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;blockquote data-ke-style=&quot;style2&quot;&gt;구현 참조: &lt;a href=&quot;https://github.com/taehui8260/springboot-common-modules/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/taehui8260/springboot-common-modules/&lt;/a&gt;&lt;/blockquote&gt;</description>
      <category>Java</category>
      <category>Java</category>
      <category>java maven</category>
      <category>maven</category>
      <category>maven 등록</category>
      <category>Spring</category>
      <category>spring boot</category>
      <category>spring boot maven</category>
      <category>spring boot 공통모듈</category>
      <category>spring maven</category>
      <category>spring 공통 모듈</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/286</guid>
      <comments>https://tae-hui.tistory.com/entry/JAVA-Spring-%EA%B3%B5%ED%86%B5-%EB%AA%A8%EB%93%88%EC%9D%84-%ED%8C%A8%ED%82%A4%EC%A7%80%ED%99%94%ED%95%98%EA%B3%A0-GitHub-Packages%EC%97%90-%EB%93%B1%EB%A1%9D%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95#entry286comment</comments>
      <pubDate>Fri, 6 Dec 2024 18:31:44 +0900</pubDate>
    </item>
    <item>
      <title>[WEB] Nuxt.js와 Spring을 이용한 POI Excel 파일 업로드 및 처리</title>
      <link>https://tae-hui.tistory.com/entry/WEB-Nuxtjs%EC%99%80-Spring%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-POI-Excel-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C-%EB%B0%8F-%EC%B2%98%EB%A6%AC</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 &lt;b&gt;프론트엔드에서 업로드된 Excel 파일&lt;/b&gt;을 &lt;b&gt;Spring 백엔드에서 받아서 Apache POI&lt;/b&gt;로 처리하는 방법을 알아보겠습니다. 업로드된 파일을 어떻게 서버에서 다룰 수 있는지 &lt;b&gt;Spring MVC&lt;/b&gt;와 &lt;b&gt;Apache POI&lt;/b&gt;를 활용하여 구현합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전체 구현 개요&lt;/h2&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;Nuxt.js를 통해 파일을 업로드.&lt;/li&gt;
&lt;li&gt;서버로 HTTP POST 요청을 보냄.&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;Spring Controller에서 파일을 받음.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MultipartFile&lt;/code&gt; 객체로 업로드된 파일 처리.&lt;/li&gt;
&lt;li&gt;Apache POI로 Excel 데이터를 읽고 출력 또는 가공.&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;데이터베이스 저장 또는 JSON 응답 반환.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 프론트엔드: Nuxt.js로 파일 업로드 구현&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nuxt.js는 Vue.js 기반으로 작동하기 때문에 파일 업로드는 Vue의 기본 이벤트 처리 방식을 사용합니다. 아래는 Nuxt.js에서 파일 업로드를 구현하는 코드입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  업로드 UI 생성 (Nuxt 페이지 컴포넌트)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;pages/upload.vue&lt;/code&gt;에 아래 코드를 작성합니다.&lt;/p&gt;
&lt;pre class=&quot;django&quot;&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;h1&amp;gt;Excel 파일 업로드&amp;lt;/h1&amp;gt;
    &amp;lt;form @submit.prevent=&quot;handleFileUpload&quot;&amp;gt;
      &amp;lt;input type=&quot;file&quot; @change=&quot;onFileChange&quot; accept=&quot;.xlsx, .xls&quot; /&amp;gt;
      &amp;lt;button type=&quot;submit&quot;&amp;gt;업로드&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
    &amp;lt;div v-if=&quot;uploadResult&quot;&amp;gt;
      &amp;lt;h2&amp;gt;업로드 결과:&amp;lt;/h2&amp;gt;
      &amp;lt;pre&amp;gt;{{ uploadResult }}&amp;lt;/pre&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script&amp;gt;
export default {
  data() {
    return {
      file: null, // 선택한 파일
      uploadResult: null, // 서버에서 받은 응답 데이터
    };
  },
  methods: {
    // 파일 선택 핸들러
    onFileChange(event) {
      this.file = event.target.files[0];
    },
    // 파일 업로드 처리
    async handleFileUpload() {
      if (!this.file) {
        alert(&quot;파일을 선택해주세요!&quot;);
        return;
      }

      // FormData 생성
      const formData = new FormData();
      formData.append(&quot;file&quot;, this.file);

      try {
        const response = await this.$axios.post(&quot;/upload&quot;, formData, {
          headers: { &quot;Content-Type&quot;: &quot;multipart/form-data&quot; },
        });
        this.uploadResult = response.data;
      } catch (error) {
        console.error(&quot;업로드 실패:&quot;, error);
        alert(&quot;업로드 중 오류가 발생했습니다.&quot;);
      }
    },
  },
};
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Nuxt Axios 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nuxt.js에서 파일 업로드를 처리하기 위해 &lt;code&gt;@nuxtjs/axios&lt;/code&gt; 모듈을 사용합니다. &lt;code&gt;nuxt.config.js&lt;/code&gt; 파일에 Axios 설정을 추가합니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;export default {
  modules: [&quot;@nuxtjs/axios&quot;],
  axios: {
    baseURL: &quot;http://localhost:8080&quot;, // Spring 백엔드 URL
  },
};&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 백엔드: Spring 파일 업로드 처리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring에서 Nuxt.js로부터 전달받은 파일을 처리하는 방법입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Controller: 파일 업로드 처리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nuxt.js가 보낸 파일을 &lt;code&gt;@RequestParam&lt;/code&gt;으로 받습니다. &lt;code&gt;MultipartFile&lt;/code&gt; 객체를 사용하여 파일 내용을 처리합니다.&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

@RestController
public class ExcelController {

    @PostMapping(&quot;/upload&quot;)
    public List&amp;lt;List&amp;lt;String&amp;gt;&amp;gt; uploadExcel(@RequestParam(&quot;file&quot;) MultipartFile file) {
        List&amp;lt;List&amp;lt;String&amp;gt;&amp;gt; excelData = new ArrayList&amp;lt;&amp;gt;();

        try (InputStream inputStream = file.getInputStream();
             Workbook workbook = new XSSFWorkbook(inputStream)) {

            // 첫 번째 시트 읽기
            Sheet sheet = workbook.getSheetAt(0);
            for (Row row : sheet) {
                List&amp;lt;String&amp;gt; rowData = new ArrayList&amp;lt;&amp;gt;();
                for (Cell cell : row) {
                    switch (cell.getCellType()) {
                        case STRING:
                            rowData.add(cell.getStringCellValue());
                            break;
                        case NUMERIC:
                            rowData.add(String.valueOf(cell.getNumericCellValue()));
                            break;
                        case BOOLEAN:
                            rowData.add(String.valueOf(cell.getBooleanCellValue()));
                            break;
                        default:
                            rowData.add(&quot; &quot;);
                    }
                }
                excelData.add(rowData);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return excelData; // JSON으로 반환
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Spring 설정: CORS 지원&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nuxt.js와 Spring이 다른 포트를 사용하므로, CORS 설정이 필요합니다. 예를 들어, Spring에서 모든 도메인의 요청을 허용하려면 아래 코드를 추가합니다.&lt;/p&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig {

    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping(&quot;/**&quot;).allowedOrigins(&quot;http://localhost:3000&quot;); // Nuxt.js 주소
            }
        };
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 전체 데이터 흐름&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;프론트엔드 (Nuxt.js)&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 Excel 파일을 선택하고 업로드 요청을 보냄.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;axios&lt;/code&gt;를 통해 백엔드 &lt;code&gt;/upload&lt;/code&gt; 엔드포인트로 POST 요청.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;백엔드 (Spring)&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;업로드된 파일을 &lt;code&gt;MultipartFile&lt;/code&gt;로 받고, Apache POI로 데이터를 읽음.&lt;/li&gt;
&lt;li&gt;읽은 데이터를 JSON 형태로 Nuxt.js로 응답.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;프론트엔드 (Nuxt.js)&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 contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 완성된 코드 요약&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Nuxt.js &lt;code&gt;pages/upload.vue&lt;/code&gt;&lt;/h3&gt;
&lt;pre class=&quot;django&quot;&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;h1&amp;gt;Excel 파일 업로드&amp;lt;/h1&amp;gt;
    &amp;lt;form @submit.prevent=&quot;handleFileUpload&quot;&amp;gt;
      &amp;lt;input type=&quot;file&quot; @change=&quot;onFileChange&quot; accept=&quot;.xlsx, .xls&quot; /&amp;gt;
      &amp;lt;button type=&quot;submit&quot;&amp;gt;업로드&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
    &amp;lt;div v-if=&quot;uploadResult&quot;&amp;gt;
      &amp;lt;h2&amp;gt;업로드 결과:&amp;lt;/h2&amp;gt;
      &amp;lt;pre&amp;gt;{{ uploadResult }}&amp;lt;/pre&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script&amp;gt;
export default {
  data() {
    return {
      file: null,
      uploadResult: null,
    };
  },
  methods: {
    onFileChange(event) {
      this.file = event.target.files[0];
    },
    async handleFileUpload() {
      if (!this.file) {
        alert(&quot;파일을 선택해주세요!&quot;);
        return;
      }
      const formData = new FormData();
      formData.append(&quot;file&quot;, this.file);

      try {
        const response = await this.$axios.post(&quot;/upload&quot;, formData, {
          headers: { &quot;Content-Type&quot;: &quot;multipart/form-data&quot; },
        });
        this.uploadResult = response.data;
      } catch (error) {
        console.error(&quot;업로드 실패:&quot;, error);
        alert(&quot;업로드 중 오류가 발생했습니다.&quot;);
      }
    },
  },
};
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Spring &lt;code&gt;ExcelController.java&lt;/code&gt;&lt;/h3&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;@RestController
public class ExcelController {

    @PostMapping(&quot;/upload&quot;)
    public List&amp;lt;List&amp;lt;String&amp;gt;&amp;gt; uploadExcel(@RequestParam(&quot;file&quot;) MultipartFile file) {
        List&amp;lt;List&amp;lt;String&amp;gt;&amp;gt; excelData = new ArrayList&amp;lt;&amp;gt;();

        try (InputStream inputStream = file.getInputStream();
             Workbook workbook = new XSSFWorkbook(inputStream)) {

            Sheet sheet = workbook.getSheetAt(0);
            for (Row row : sheet) {
                List&amp;lt;String&amp;gt; rowData = new ArrayList&amp;lt;&amp;gt;();
                for (Cell cell : row) {
                    switch (cell.getCellType()) {
                        case STRING:
                            rowData.add(cell.getStringCellValue());
                            break;
                        case NUMERIC:
                            rowData.add(String.valueOf(cell.getNumericCellValue()));
                            break;
                        case BOOLEAN:
                            rowData.add(String.valueOf(cell.getBooleanCellValue()));
                            break;
                        default:
                            rowData.add(&quot; &quot;);
                    }
                }
                excelData.add(rowData);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return excelData;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 &lt;b&gt;Nuxt.js와 Spring&lt;/b&gt;을 연동하여 Excel 파일을 업로드하고 데이터를 읽는 과정을 설명했습니다. 이를 통해 &lt;b&gt;Nuxt.js가 백엔드로 파일을 전송&lt;/b&gt;하고, &lt;b&gt;Spring에서 데이터를 처리&lt;/b&gt;하여 &lt;b&gt;JSON으로 반환&lt;/b&gt;하는 방법을 알아보았습니다.&lt;/p&gt;</description>
      <category>WEB</category>
      <category>backend 엑셀 업로드</category>
      <category>Java</category>
      <category>Java 엑셀 업로드</category>
      <category>Nuxt</category>
      <category>poi</category>
      <category>poi 엑셀 업로드</category>
      <category>엑셀</category>
      <category>엑셀 업로드</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/285</guid>
      <comments>https://tae-hui.tistory.com/entry/WEB-Nuxtjs%EC%99%80-Spring%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-POI-Excel-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C-%EB%B0%8F-%EC%B2%98%EB%A6%AC#entry285comment</comments>
      <pubDate>Wed, 27 Nov 2024 15:58:03 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] Java로 Excel 파일 생성 및 관리: Apache POI 사용법</title>
      <link>https://tae-hui.tistory.com/entry/JAVA-Java%EB%A1%9C-Excel-%ED%8C%8C%EC%9D%BC-%EC%83%9D%EC%84%B1-%EB%B0%8F-%EA%B4%80%EB%A6%AC-Apache-POI-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 &lt;b&gt;Apache POI&lt;/b&gt; 라이브러리를 사용해 &lt;b&gt;Java로 Excel 파일을 생성&lt;/b&gt;하는 방법을 처음부터 끝까지 설명합니다. &lt;b&gt;스타일 적용&lt;/b&gt;, &lt;b&gt;데이터 작성&lt;/b&gt;, &lt;b&gt;컬럼 스타일링&lt;/b&gt; 등, 실용적인 예제를 통해 구현 방법을 단계별로 알아보겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Apache POI란?&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Apache POI&lt;/b&gt;는 Java 기반으로 MS Office 문서를 다룰 수 있는 강력한 라이브러리입니다. 특히, Excel 파일을 처리하는 데 가장 널리 쓰입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Excel 파일 생성 프로세스&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;br /&gt;Maven 프로젝트라면 &lt;code&gt;pom.xml&lt;/code&gt;에 아래 의존성 추가&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
   &amp;lt;groupId&amp;gt;org.apache.poi&amp;lt;/groupId&amp;gt;
   &amp;lt;artifactId&amp;gt;poi-ooxml&amp;lt;/artifactId&amp;gt;
   &amp;lt;version&amp;gt;5.2.3&amp;lt;/version&amp;gt; &amp;lt;!-- 최신 버전을 확인하세요 --&amp;gt;
&amp;lt;/dependency&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Apache POI 주요 클래스&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Workbook&lt;/code&gt;: Excel 파일을 나타냄. (HSSFWorkbook: &lt;code&gt;.xls&lt;/code&gt;, XSSFWorkbook: &lt;code&gt;.xlsx&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Sheet&lt;/code&gt;: 워크시트(탭)를 나타냄.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Row&lt;/code&gt;와 &lt;code&gt;Cell&lt;/code&gt;: 각 행과 셀.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 구현 방법: 단계별 설명&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  1단계: Workbook 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Excel 파일을 생성하려면 먼저 &lt;b&gt;Workbook&lt;/b&gt; 객체를 초기화해야 합니다. &lt;code&gt;.xlsx&lt;/code&gt; 파일은 &lt;code&gt;XSSFWorkbook&lt;/code&gt;을 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;Workbook workbook = new XSSFWorkbook();&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  2단계: Sheet 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;workbook.createSheet(&quot;Sheet 이름&quot;)&lt;/code&gt;을 호출해 워크시트를 추가합니다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;Sheet sheet = workbook.createSheet(&quot;첫 번째 시트&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  3단계: Row와 Cell 추가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Excel 파일에 데이터를 추가하려면 &lt;code&gt;Row&lt;/code&gt;와 &lt;code&gt;Cell&lt;/code&gt; 객체를 생성해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Row row = sheet.createRow(0); // 첫 번째 행 생성
Cell cell = row.createCell(0); // 첫 번째 셀 생성
cell.setCellValue(&quot;안녕하세요!&quot;); // 데이터 입력&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  4단계: 스타일 적용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;CellStyle&lt;/code&gt;을 사용해 셀의 스타일을 설정할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 스타일 초기화&lt;/h4&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setFontHeightInPoints((short) 14); // 글꼴 크기
font.setBold(true); // 굵게
style.setFont(font);
style.setAlignment(HorizontalAlignment.CENTER); // 가운데 정렬
style.setVerticalAlignment(VerticalAlignment.CENTER); // 세로 가운데 정렬&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 스타일 적용&lt;/h4&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;cell.setCellStyle(style);&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  5단계: 데이터와 스타일 반복 적용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 반복적으로 추가하고 스타일을 적용하려면 &lt;b&gt;for 루프&lt;/b&gt;를 활용합니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;for (int i = 0; i &amp;lt; 10; i++) {
    Row dataRow = sheet.createRow(i + 1); // 첫 번째 행 아래부터 데이터 추가
    for (int j = 0; j &amp;lt; 5; j++) {
        Cell dataCell = dataRow.createCell(j);
        dataCell.setCellValue(&quot;데이터 &quot; + (i + 1) + &quot;-&quot; + (j + 1));
        dataCell.setCellStyle(style);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  6단계: 컬럼 너비 조정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동으로 컬럼 크기를 설정하려면 다음 메소드를 사용합니다:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;for (int i = 0; i &amp;lt; 5; i++) {
    sheet.autoSizeColumn(i); // i번째 컬럼의 너비를 데이터에 맞게 자동 조정
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  7단계: Excel 파일 저장&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성한 Excel 파일을 실제로 저장하려면 &lt;b&gt;FileOutputStream&lt;/b&gt;을 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;try (FileOutputStream fileOut = new FileOutputStream(&quot;sample.xlsx&quot;)) {
    workbook.write(fileOut); // 워크북 내용을 파일로 출력
}
workbook.close(); // 리소스 해제&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 완성된 코드 예제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 위의 모든 단계를 포함한 전체 구현 코드입니다:&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.FileOutputStream;

public class ExcelExample {
    public static void main(String[] args) {
        try (Workbook workbook = new XSSFWorkbook()) {
            Sheet sheet = workbook.createSheet(&quot;첫 번째 시트&quot;);

            // 제목 행 추가
            Row row = sheet.createRow(0);
            Cell cell = row.createCell(0);
            cell.setCellValue(&quot;안녕하세요!&quot;);

            // 스타일 설정
            CellStyle style = workbook.createCellStyle();
            Font font = workbook.createFont();
            font.setFontHeightInPoints((short) 14);
            font.setBold(true);
            style.setFont(font);
            style.setAlignment(HorizontalAlignment.CENTER);
            style.setVerticalAlignment(VerticalAlignment.CENTER);
            cell.setCellStyle(style);

            // 데이터 추가
            for (int i = 0; i &amp;lt; 10; i++) {
                Row dataRow = sheet.createRow(i + 1);
                for (int j = 0; j &amp;lt; 5; j++) {
                    Cell dataCell = dataRow.createCell(j);
                    dataCell.setCellValue(&quot;데이터 &quot; + (i + 1) + &quot;-&quot; + (j + 1));
                    dataCell.setCellStyle(style);
                }
            }

            // 컬럼 크기 자동 조정
            for (int i = 0; i &amp;lt; 5; i++) {
                sheet.autoSizeColumn(i);
            }

            // 파일 저장
            try (FileOutputStream fileOut = new FileOutputStream(&quot;sample.xlsx&quot;)) {
                workbook.write(fileOut);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 Apache POI를 활용한 Excel 파일 생성 방법을 보았습니다.&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;Workbook 생성부터 데이터 추가, 스타일링, 파일 저장&lt;/b&gt;까지 전 과정&lt;/li&gt;
&lt;li&gt;실제 프로젝트에서 &lt;b&gt;다양한 Excel 템플릿 생성 및 데이터 관리&lt;/b&gt;에 활용&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java</category>
      <category>java excel</category>
      <category>java 엑셀</category>
      <category>java 엑셀 다운</category>
      <category>poi</category>
      <category>spring boot poi</category>
      <category>spring boot 엑셀 다운</category>
      <category>엑셀 다운</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <category>포이 라이브러리</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/284</guid>
      <comments>https://tae-hui.tistory.com/entry/JAVA-Java%EB%A1%9C-Excel-%ED%8C%8C%EC%9D%BC-%EC%83%9D%EC%84%B1-%EB%B0%8F-%EA%B4%80%EB%A6%AC-Apache-POI-%EC%82%AC%EC%9A%A9%EB%B2%95#entry284comment</comments>
      <pubDate>Tue, 26 Nov 2024 18:12:04 +0900</pubDate>
    </item>
    <item>
      <title>[Linux] 리눅스 환경에서 sudo 권한 실행 시 비밀번호를 묻지 않도록 설정하는 방법</title>
      <link>https://tae-hui.tistory.com/entry/Linux-%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-sudo-%EA%B6%8C%ED%95%9C-%EC%8B%A4%ED%96%89-%EC%8B%9C-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8%EB%A5%BC-%EB%AC%BB%EC%A7%80-%EC%95%8A%EB%8F%84%EB%A1%9D-%EC%84%A4%EC%A0%95%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ 리눅스 환경에서 &lt;code&gt;sudo&lt;/code&gt; 권한 실행 시 비밀번호를 묻지 않도록 설정하는 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스 시스템에서 &lt;code&gt;sudo&lt;/code&gt;를 사용할 때 매번 비밀번호를 입력해야 하는 건 보안적인 이유 때문입니다. 하지만 개발 환경에서는 이를 생략해야 할 경우가 종종 있습니다. 이번 포스팅에서는 &lt;b&gt;&lt;code&gt;sudo&lt;/code&gt; 비밀번호 없이 실행되도록 설정하는 방법&lt;/b&gt;과 &lt;b&gt;이 설정이 어떤 경우에 유용하며 어떻게 작동하는지&lt;/b&gt;를 알아보겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.  ️ 비밀번호를 묻지 않도록 설정하는 과정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스 시스템에서 특정 유저나 그룹이 &lt;code&gt;sudo&lt;/code&gt; 명령을 사용할 때 비밀번호 입력을 건너뛰도록 설정하려면 &lt;b&gt;&lt;code&gt;sudoers&lt;/code&gt; 파일&lt;/b&gt;을 수정해야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.1. &lt;code&gt;sudoers&lt;/code&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;code&gt;sudo visudo&lt;/code&gt; 명령을 실행합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;sudo visudo&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;code&gt;visudo&lt;/code&gt;는 &lt;code&gt;sudoers&lt;/code&gt; 파일을 편집하기 위해 사용되는 전용 명령입니다. 이 명령을 통해 편집하면 실수로 파일이 손상되는 것을 방지할 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;sudoers&lt;/code&gt; 파일을 편집 창에서 열게 되면 다음과 같은 내용이 보입니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;apache&quot;&gt;&lt;code&gt;# User privilege specification
root    ALL=(ALL:ALL) ALL&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.2. 유저 비밀번호 입력 생략 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 특정 유저의 &lt;code&gt;sudo&lt;/code&gt; 비밀번호를 묻지 않도록 설정합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;특정 유저에 대한 설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;&amp;lt;your_username&amp;gt;&lt;/code&gt; 대신 비밀번호를 생략하려는 유저명을 입력합니다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;&amp;lt;your_username&amp;gt; ALL=(ALL) NOPASSWD: ALL&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;특정 명령만 비밀번호 없이 허용&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 명령에 대해서만 비밀번호를 묻지 않도록 하려면&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;&amp;lt;your_username&amp;gt; ALL=(ALL) NOPASSWD: /path/to/command&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, &lt;code&gt;/sbin/reboot&lt;/code&gt; 명령만 비밀번호 없이 실행하도록 설정하려면&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;&amp;lt;your_username&amp;gt; ALL=(ALL) NOPASSWD: /sbin/reboot&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.3. 그룹 단위로 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 유저를 포함한 그룹에 대해 설정하려면 &lt;code&gt;sudoers&lt;/code&gt; 파일에 다음과 같은 내용을 추가합니다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;%&amp;lt;groupname&amp;gt; ALL=(ALL) NOPASSWD: ALL&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;&amp;lt;groupname&amp;gt;&lt;/code&gt;은 비밀번호를 생략할 그룹 이름입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.4. 변경사항 저장&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정을 완료했으면, &lt;b&gt;&lt;code&gt;:wq&lt;/code&gt;&lt;/b&gt; 를 입력해 저장하고 &lt;code&gt;sudoers&lt;/code&gt; 파일을 닫습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 파일 저장 중에 에러가 발생했다면, &lt;code&gt;visudo&lt;/code&gt; 명령이 문제를 알려줍니다. 이 경우 올바르게 수정한 뒤 다시 저장하세요.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.   이러한 설정이 유용한 경우&lt;/h2&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;개발 환경 자동화&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스크립트나 배치 작업에서 &lt;code&gt;sudo&lt;/code&gt; 명령을 반복적으로 사용할 때, 비밀번호 입력이 불필요한 경우.&lt;/li&gt;
&lt;li&gt;CI/CD 파이프라인에서 자동으로 작업을 처리하기 위해.&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;특정 유저 계정만 사용하고 보안 위험이 낮은 환경(예: VM)에서.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;IoT 및 임베디드 환경&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 이러한 설정은 보안에 민감한 환경에서는 절대 권장되지 않습니다. &lt;b&gt;누군가 계정에 접근한다면 비밀번호 없이 시스템을 제어할 수 있기 때문&lt;/b&gt;입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.   설정이 작동하는 원리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스의 &lt;code&gt;sudo&lt;/code&gt;는 &lt;b&gt;&lt;code&gt;/etc/sudoers&lt;/code&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;code&gt;ALL=(ALL)&lt;/code&gt;:&lt;br /&gt;유저가 모든 호스트에서 모든 명령을 실행할 수 있음을 의미합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NOPASSWD&lt;/code&gt;:&lt;br /&gt;비밀번호를 묻지 않고 명령을 실행할 수 있음을 의미합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 편집한 대로 설정하면, &lt;code&gt;sudo&lt;/code&gt;는 실행 시 비밀번호를 요청하지 않습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. ⚠️ 주의사항&lt;/h2&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;&lt;code&gt;sudo&lt;/code&gt;는 모든 명령 실행을 &lt;code&gt;/var/log/auth.log&lt;/code&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;code&gt;sudoers&lt;/code&gt; 파일을 잘못 수정하면 시스템에 문제가 발생할 수 있습니다. 꼭 &lt;code&gt;visudo&lt;/code&gt;를 사용해야합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.   요약&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;sudo visudo&lt;/code&gt;를 사용해 &lt;code&gt;sudoers&lt;/code&gt; 파일을 엽니다.&lt;/li&gt;
&lt;li&gt;특정 유저 또는 그룹에 대해 &lt;code&gt;NOPASSWD&lt;/code&gt; 옵션을 추가합니다.&lt;/li&gt;
&lt;li&gt;설정 저장 후 테스트합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;&amp;lt;your_username&amp;gt; ALL=(ALL) NOPASSWD: ALL&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 방법은 &lt;b&gt;개발 환경 자동화&lt;/b&gt;와 같은 상황에서 매우 유용하지만, &lt;b&gt;보안에 민감한 환경에서는 신중히 적용&lt;/b&gt;해야 합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;</description>
      <category>IT 지식</category>
      <category>linux  root login</category>
      <category>linux sudo</category>
      <category>linux 관리자 권한</category>
      <category>linux 사용법</category>
      <category>linux 자동 로그인</category>
      <category>root</category>
      <category>개발 환경 자동화</category>
      <category>관리자 우회 사용법</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/283</guid>
      <comments>https://tae-hui.tistory.com/entry/Linux-%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-sudo-%EA%B6%8C%ED%95%9C-%EC%8B%A4%ED%96%89-%EC%8B%9C-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8%EB%A5%BC-%EB%AC%BB%EC%A7%80-%EC%95%8A%EB%8F%84%EB%A1%9D-%EC%84%A4%EC%A0%95%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95#entry283comment</comments>
      <pubDate>Mon, 25 Nov 2024 18:48:24 +0900</pubDate>
    </item>
    <item>
      <title>[DB] NoSQL의 모든 것</title>
      <link>https://tae-hui.tistory.com/entry/DB-NoSQL%EC%9D%98-%EB%AA%A8%EB%93%A0-%EA%B2%83</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  1. NoSQL의 모든 것&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스(Database)는 애플리케이션 개발에서 중요한 요소입니다. 전통적으로 &lt;b&gt;SQL 기반의 관계형 데이터베이스(RDBMS)&lt;/b&gt;가 널리 사용되었지만, 최근에는 &lt;b&gt;NoSQL&lt;/b&gt;이 떠오르고 있습니다. 오늘은 &lt;b&gt;NoSQL&lt;/b&gt;에 대해 &lt;code&gt;기본 개념&lt;/code&gt;, &lt;code&gt;특징&lt;/code&gt;, &lt;code&gt;종류&lt;/code&gt;, &lt;code&gt;구현 방법&lt;/code&gt;을 깊이 있게 다뤄보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  2. NoSQL이란?&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 정의&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;NoSQL&lt;/b&gt;은 &quot;Not Only SQL&quot;의 약자로, 전통적인 SQL 데이터베이스와 다른 방식으로 데이터를 저장하고 관리하는 데이터베이스를 의미합니다. &lt;b&gt;비관계형 데이터베이스&lt;/b&gt;라고도 불리며, 대규모 데이터 처리와 유연한 설계를 지원합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ NoSQL의 RDB에 비해 장점&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;RDBMS는 테이블 스키마가 고정되어 있지만, NoSQL은 유연한 데이터 구조를 지원합니다. 이를 통해 &lt;b&gt;다양한 데이터 유형&lt;/b&gt;과 &lt;b&gt;비정형 데이터&lt;/b&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;RDBMS는 보통 고성능 서버(수직 확장)를 추가해야 하지만, NoSQL은 &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;/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;NoSQL은 데이터를 여러 서버에 분산하여 저장하고 처리합니다.&lt;/li&gt;
&lt;li&gt;예를 들어, 사용자 수가 폭발적으로 증가하는 애플리케이션에서 &lt;b&gt;데이터 읽기/쓰기 속도를 유지&lt;/b&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;NoSQL은 특정 쿼리 패턴에 최적화된 구조를 선택하여 RDB보다 빠른 읽기/쓰기 성능을 발휘합니다.&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;JSON, XML, 이미지, 동영상과 같은 비정형 데이터를 저장하고 관리하기에 적합합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  3. NoSQL의 주요 종류&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NoSQL은 사용 사례에 따라 여러 유형으로 나뉩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1️⃣ 키-값 저장소(Key-Value Store)&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;code&gt;Key&lt;/code&gt;와 &lt;code&gt;Value&lt;/code&gt; 쌍으로 저장.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장점&lt;/b&gt;: 단순한 구조로 &lt;b&gt;빠른 읽기/쓰기 성능&lt;/b&gt; 제공.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예시&lt;/b&gt;: Redis, DynamoDB.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2️⃣ 문서형(Document Store)&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;: 데이터를 JSON 또는 BSON 문서 형태로 저장.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장점&lt;/b&gt;: 유연한 데이터 모델과 계층적 구조 지원.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예시&lt;/b&gt;: MongoDB, CouchDB.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3️⃣ 그래프 데이터베이스(Graph Database)&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;: Neo4j, ArangoDB.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4️⃣ 열 지향 데이터베이스(Column Family Store)&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;: 데이터를 열(Column) 단위로 저장.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장점&lt;/b&gt;: 대규모 분석 작업에 적합.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예시&lt;/b&gt;: Apache Cassandra, HBase.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  4. NoSQL의 대규모 분산 처리&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 분산 처리의 원리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NoSQL은 &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;샤딩(Sharding)&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;복제(Replication)&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;li&gt;&lt;b&gt;CAP 이론&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;분산 시스템에서 &lt;b&gt;Consistency(일관성)&lt;/b&gt;, &lt;b&gt;Availability(가용성)&lt;/b&gt;, &lt;b&gt;Partition Tolerance(파티션 허용성)&lt;/b&gt; 중 두 가지를 우선시합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, MongoDB는 데이터가 여러 노드에 분산 저장되며, 서버 장애가 발생해도 데이터 복구가 가능합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  5. 가장 보편적으로 사용되는 NoSQL: MongoDB&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ MongoDB란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MongoDB는 &lt;b&gt;문서형 데이터베이스&lt;/b&gt;로, JSON과 유사한 &lt;b&gt;BSON&lt;/b&gt; 포맷으로 데이터를 저장합니다. &lt;b&gt;유연한 스키마&lt;/b&gt; 덕분에 개발 초기부터 복잡한 데이터 구조 설계 없이도 빠르게 시작할 수 있어요.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  6. MongoDB 설치 및 구현&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ MongoDB 설치&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.mongodb.com&quot;&gt;MongoDB 공식 웹사이트&lt;/a&gt;에서 설치 파일 다운로드.&lt;/li&gt;
&lt;li&gt;설치 후, &lt;code&gt;mongod&lt;/code&gt; 명령어로 서버 실행.&lt;/li&gt;
&lt;li&gt;MongoDB 클라이언트(예: MongoDB Compass)나 명령줄에서 데이터 작업 수행.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ MongoDB 기본 명령어&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MongoDB에서 가장 기본적인 데이터 작업은 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;# 데이터베이스 목록 보기
show dbs

# 데이터베이스 선택 (없으면 생성)
use my_database

# 컬렉션 생성 및 데이터 삽입
db.my_collection.insertOne({ name: &quot;Alice&quot;, age: 25, city: &quot;Seoul&quot; })

# 모든 데이터 조회
db.my_collection.find()

# 조건에 맞는 데이터 조회
db.my_collection.find({ age: { $gte: 20 } })

# 데이터 수정
db.my_collection.updateOne({ name: &quot;Alice&quot; }, { $set: { age: 26 } })

# 데이터 삭제
db.my_collection.deleteOne({ name: &quot;Alice&quot; })&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ MongoDB 사용 예제: Node.js로 CRUD 애플리케이션 만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 &lt;b&gt;Node.js&lt;/b&gt;와 &lt;b&gt;Mongoose&lt;/b&gt;를 이용해 MongoDB와 연결하고 CRUD 작업을 수행하는 코드 예제입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1️⃣ 프로젝트 초기화 및 의존성 설치&lt;/h4&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;mkdir mongodb-example
cd mongodb-example
npm init -y
npm install mongoose express body-parser&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2️⃣ MongoDB 연결 및 스키마 정의&lt;/h4&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;const mongoose = require('mongoose');
const express = require('express');
const bodyParser = require('body-parser');

const app = express();
app.use(bodyParser.json());

// MongoDB 연결
mongoose.connect('mongodb://localhost:27017/my_database', {
  useNewUrlParser: true,
  useUnifiedTopology: true,
});

// 스키마 정의
const UserSchema = new mongoose.Schema({
  name: String,
  age: Number,
  city: String,
});

const User = mongoose.model('User', UserSchema);

// 간단한 라우트
app.get('/', (req, res) =&amp;gt; {
  res.send('Hello MongoDB!');
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3️⃣ CRUD 라우트 구현&lt;/h4&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;app.post('/users', async (req, res) =&amp;gt; {
  const user = new User(req.body);
  await user.save();
  res.send(user);
});

app.get('/users', async (req, res) =&amp;gt; {
  const users = await User.find();
  res.send(users);
});

app.put('/users/:id', async (req, res) =&amp;gt; {
  const user = await User.findByIdAndUpdate(req.params.id, req.body, { new: true });
  res.send(user);
});

app.delete('/users/:id', async (req, res) =&amp;gt; {
  await User.findByIdAndDelete(req.params.id);
  res.send({ message: 'User deleted' });
});

// 서버 실행
app.listen(3000, () =&amp;gt; {
  console.log('Server is running on http://localhost:3000');
});&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  7. 정리: NoSQL의 강점과 활용 방법&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ NoSQL의 RDB 대비 주요 강점&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;빠른 읽기/쓰기 성능&lt;/b&gt;으로 실시간 애플리케이션에 적합.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;다양한 데이터 유형 지원&lt;/b&gt;(JSON, 이미지, 동영상 등).&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ MongoDB 활용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MongoDB는 웹 애플리케이션, IoT 플랫폼, 로그 관리 시스템 등 다양한 분야에서 사용됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;NoSQL&lt;/b&gt;은 대규모 데이터를 처리할 수 있는 강력한 데이터베이스입니다. 특히 &lt;b&gt;MongoDB&lt;/b&gt;는 유연하고 접근성이 좋아 초보 개발자도 쉽게 시작할 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>DB</category>
      <category>mariadb</category>
      <category>MongoDB</category>
      <category>nosql</category>
      <category>nosql 사용법</category>
      <category>nosql 장저</category>
      <category>nosql 장점</category>
      <category>RDB</category>
      <category>노에스큐엘</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/282</guid>
      <comments>https://tae-hui.tistory.com/entry/DB-NoSQL%EC%9D%98-%EB%AA%A8%EB%93%A0-%EA%B2%83#entry282comment</comments>
      <pubDate>Sun, 24 Nov 2024 22:42:49 +0900</pubDate>
    </item>
    <item>
      <title>MariaDB란 무엇인가</title>
      <link>https://tae-hui.tistory.com/entry/MariaDB%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.   MariaDB란 무엇인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;MariaDB&lt;/b&gt;는 원래 MySQL의 창시자인 &lt;b&gt;몬티 와이드니우스(Michael &quot;Monty&quot; Widenius)&lt;/b&gt;가 MySQL의 대체제로 개발한 오픈 소스 데이터베이스입니다. MySQL과 매우 유사한 인터페이스를 제공하며, 기존 MySQL을 사용하던 애플리케이션에서 손쉽게 MariaDB로 전환할 수 있도록 설계되었습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.1 MariaDB와 오라클의 관계&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;MySQL의 역사&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MySQL은 1995년에 처음 개발되었고, 빠르게 관계형 데이터베이스 시장에서 주요 플레이어로 자리 잡았습니다.&lt;/li&gt;
&lt;li&gt;2008년, MySQL은 &lt;b&gt;썬 마이크로시스템즈(Sun Microsystems)&lt;/b&gt;에 인수되었습니다.&lt;/li&gt;
&lt;li&gt;2010년, 썬 마이크로시스템즈가 &lt;b&gt;오라클(Oracle)&lt;/b&gt;에 인수되면서 MySQL 역시 오라클의 소유가 되었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MariaDB의 탄생&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MySQL의 창시자였던 몬티 와이드니우스는 오라클의 MySQL 운영 방식에 대한 우려를 표명하며, &lt;b&gt;MariaDB&lt;/b&gt;를 별도로 개발하기 시작했습니다.&lt;/li&gt;
&lt;li&gt;MariaDB는 MySQL의 코드베이스에서 분기된 &lt;b&gt;포크 프로젝트&lt;/b&gt;로 시작되었으며, MySQL의 100% 오픈 소스 철학을 계승합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.  ️ MariaDB의 핵심 구성 요소&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MariaDB는 여러 가지 모듈과 구성 요소로 이루어져 있으며, 데이터베이스 엔진부터 고급 기능까지 다양한 요소를 포함합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1 스토리지 엔진(Storage Engine)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MariaDB는 MySQL처럼 다양한 &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;b&gt;InnoDB&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MariaDB의 기본 스토리지 엔진.&lt;/li&gt;
&lt;li&gt;트랜잭션 지원, 외래 키, ACID 특성 제공.&lt;/li&gt;
&lt;li&gt;복잡한 애플리케이션과 대규모 트랜잭션 처리에 적합.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Aria&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MyISAM의 대체 엔진으로 설계됨.&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;b&gt;ColumnStore&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;MyRocks&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RocksDB 기반의 스토리지 엔진.&lt;/li&gt;
&lt;li&gt;쓰기 성능 최적화 및 효율적인 저장 공간 활용.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;TokuDB&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;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2 Replication(복제)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MariaDB는 기본적으로 데이터베이스 복제를 지원합니다. 이를 통해 데이터 가용성을 높이고 읽기 부하를 분산할 수 있습니다.&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;Master-Slave Replication&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;li&gt;&lt;b&gt;Master-Master Replication&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;li&gt;&lt;b&gt;Galera Cluster&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MariaDB에서 클러스터링을 구현하는 기술.&lt;/li&gt;
&lt;li&gt;트랜잭션이 모든 노드에 동기화되도록 보장.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.   MariaDB 설치 및 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MariaDB를 설치하고 기본적으로 사용할 수 있는 방법을 알아보겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 MariaDB 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MariaDB는 다양한 운영체제에서 사용할 수 있습니다. 여기서는 Ubuntu 기준으로 설명하겠습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1️⃣ Ubuntu에서 설치&lt;/h4&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;sudo apt update
sudo apt install mariadb-server&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2️⃣ MariaDB 서버 시작&lt;/h4&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;sudo systemctl start mariadb
sudo systemctl enable mariadb&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3️⃣ 설치 확인&lt;/h4&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;mariadb --version&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2 초기 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MariaDB 설치 후 보안 설정을 진행합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1️⃣ 보안 스크립트 실행&lt;/h4&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;sudo mysql_secure_installation&lt;/code&gt;&lt;/pre&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;h4 data-ke-size=&quot;size20&quot;&gt;2️⃣ MariaDB 접속&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MariaDB에 접속하려면 다음 명령어를 사용합니다:&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;sudo mariadb -u root -p&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.  ️ MariaDB 사용법&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.1 데이터베이스 생성 및 사용&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1️⃣ 데이터베이스 생성&lt;/h4&gt;
&lt;pre class=&quot;n1ql&quot;&gt;&lt;code&gt;CREATE DATABASE test_db;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2️⃣ 데이터베이스 확인&lt;/h4&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;SHOW DATABASES;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3️⃣ 데이터베이스 사용&lt;/h4&gt;
&lt;pre class=&quot;php&quot;&gt;&lt;code&gt;USE test_db;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.2 테이블 생성 및 데이터 삽입&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1️⃣ 테이블 생성&lt;/h4&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100),
    email VARCHAR(100)
);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2️⃣ 데이터 삽입&lt;/h4&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;INSERT INTO users (name, email) VALUES
('Alice', 'alice@example.com'),
('Bob', 'bob@example.com');&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3️⃣ 데이터 조회&lt;/h4&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;SELECT * FROM users;&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.  ️ RDB의 장점과 한계&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.1 RDB(Relational Database)의 장점&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;RDB는 엄격한 스키마와 제약 조건(Constraints)을 통해 데이터의 정확성과 일관성을 보장합니다.&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;트랜잭션의 ACID(원자성, 일관성, 격리성, 지속성) 특성을 보장하여 안정적인 데이터 관리를 제공합니다.&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;li&gt;&lt;b&gt;표준화된 쿼리 언어(SQL)&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;5.2 RDB의 한계&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;수평적 확장(Scale-Out)에 어려움이 있어, 데이터가 매우 방대해지면 성능이 저하될 수 있습니다.&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;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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;br /&gt;요약&lt;/b&gt;: RDB는 일관성, 무결성, 트랜잭션 중심의 시스템에 적합하고, NoSQL은 확장성과 비정형 데이터 처리에 적합합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 요약&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;MariaDB&lt;/b&gt;는 MySQL의 대안으로, 오픈 소스 철학을 유지하며 안정성과 성능을 제공합니다.&lt;/li&gt;
&lt;li&gt;RDB는 &lt;b&gt;데이터 무결성과 트랜잭션 관리&lt;/b&gt;에서 강력하지만, &lt;b&gt;확장성과 유연성&lt;/b&gt;에서 한계가 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;NoSQL&lt;/b&gt;은 비정형 데이터를 처리하고 확장성 있는 애플리케이션에서 뛰어난 성능을 발휘합니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>DB</category>
      <category>DB</category>
      <category>mariadb</category>
      <category>mariadb 사용법</category>
      <category>nosql</category>
      <category>RDB</category>
      <category>데이터베이스</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/281</guid>
      <comments>https://tae-hui.tistory.com/entry/MariaDB%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80#entry281comment</comments>
      <pubDate>Sat, 23 Nov 2024 23:34:54 +0900</pubDate>
    </item>
    <item>
      <title>[Docker]Docker를 이용해 MariaDB 설치하기</title>
      <link>https://tae-hui.tistory.com/entry/DockerDocker%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%B4-MariaDB-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 포스팅에서 다뤘던 [&lt;a href=&quot;https://tae-hui.tistory.com/entry/Docker-%EB%A6%AC%EB%88%85%EC%8A%A4%EC%97%90%EC%84%9C-Docker-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%8B%A4%ED%96%89-%EB%B0%A9%EB%B2%95&quot;&gt;리눅스에서 Docker 설치 및 실행 방법&lt;/a&gt;] 내용을 참고하시면, 이번 주제에 대한 배경 지식이나 기초 정보를 확인하실 수 있습니다!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  이 포스팅에서는 Docker를 이용해 MariaDB를 설치하고 사용하는 방법을 단계별로 설명하겠습니다. 최신 버전 MariaDB 사용 시 발생할 수 있는 오류를 방지하기 위해 &lt;code&gt;10.7.4&lt;/code&gt; 버전을 사용하는 방법을 중점적으로 다룹니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 원하는 MariaDB Image를 다운로드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker는 이미지 기반의 가상화 기술로, MariaDB 이미지를 사용해 빠르게 데이터베이스 환경을 구축할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 명령어&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;docker pull mariadb:10.7.4&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;code&gt;docker pull&lt;/code&gt;&lt;/b&gt;: Docker Hub에서 MariaDB 이미지를 다운로드.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;mariadb:10.7.4&lt;/code&gt;&lt;/b&gt;: MariaDB의 &lt;code&gt;10.7.4&lt;/code&gt; 버전을 명시적으로 지정합니다. 최신 버전이 불안정할 수 있으므로 권장됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Dockerfile 작성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerfile은 MariaDB 컨테이너 이미지를 사용자 정의하는 데 사용됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ Dockerfile 내용&lt;/h3&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;FROM mariadb:10.7.4

# 설정 파일 복사
#COPY my.cnf /etc/mysql/my.cnf
COPY my.cnf /etc/mysql/mariadb.conf.d/my.cnf

# 포트 노출
EXPOSE 3306&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;code&gt;FROM mariadb:10.7.4&lt;/code&gt;&lt;/b&gt;: 베이스 이미지를 설정. 여기서는 MariaDB의 특정 버전을 사용합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;COPY my.cnf /etc/mysql/my.cnf&lt;/code&gt;&lt;/b&gt;: MariaDB 설정 파일(&lt;code&gt;my.cnf&lt;/code&gt;)을 컨테이너 내부로 복사합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;EXPOSE 3306&lt;/code&gt;&lt;/b&gt;: 컨테이너의 3306 포트를 노출. MariaDB의 기본 포트입니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 설정 파일 작성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MariaDB의 동작 방식을 설정하는 &lt;code&gt;my.cnf&lt;/code&gt; 파일 작성 방법입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 예시 파일&lt;/h3&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;[mysqld]
# MariaDB 설정 예제
user=mysql
datadir=/var/lib/mysql
socket=/var/run/mysqld/mysqld.sock

# 로그 파일 위치
log-error=/var/log/mysql/error.log
general_log_file=/var/log/mysql/mysql.log
slow_query_log_file=/var/log/mysql/mysql-slow.log

# 기타 설정
symbolic-links=0&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;code&gt;[mysqld]&lt;/code&gt;&lt;/b&gt;: MariaDB의 데몬 설정을 정의합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;datadir&lt;/code&gt;&lt;/b&gt;: 데이터 파일이 저장될 경로.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;로그 파일 설정&lt;/b&gt;: MariaDB가 기록하는 로그 파일 위치를 지정합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;symbolic-links&lt;/code&gt;&lt;/b&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. Docker 컨테이너 실행&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MariaDB를 실행하는 Docker 명령어를 알아봅니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 명령어&lt;/h3&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;docker run --rm -d -p 3306:3306 --name mariadb \
-e MARIADB_USER=[user] \
-e MARIADB_PASSWORD=[password] \
-e MARIADB_ROOT_PASSWORD=[rootPassword] \
-e TZ=Asia/Seoul \
-v /appdata/mariadb/data:/var/lib/mysql \ 
-v /appdata/mariadb/config:/etc/mysql/mariadb.conf.d \
-v /appdata/mariadb/logs:/var/log/mysql \

mariadb:10.7.4&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;code&gt;-e&lt;/code&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;code&gt;MARIADB_USER&lt;/code&gt;: MariaDB 사용자 이름.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MARIADB_PASSWORD&lt;/code&gt;: 사용자의 비밀번호.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MARIADB_ROOT_PASSWORD&lt;/code&gt;: 관리자 계정(root)의 비밀번호.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TZ=Asia/Seoul&lt;/code&gt;: 타임존 설정.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;--rm&lt;/code&gt;&lt;/b&gt;: 컨테이너 종료 시 컨테이너를 삭제.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;-d&lt;/code&gt;&lt;/b&gt;: 백그라운드 실행.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;-p 3306:3306&lt;/code&gt;&lt;/b&gt;: 로컬 포트(3306)와 컨테이너 포트(3306)를 연결.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;-v&lt;/code&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;code&gt;/var/lib/mysql&lt;/code&gt;: 데이터 파일이 저장될 위치.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/mysql/&lt;/code&gt;: MariaDB 설정 파일 위치.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/var/log/mysql&lt;/code&gt;: MariaDB 로그 파일 위치.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 데이터 파일과 설정 파일의 권한 확인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MariaDB 컨테이너가 데이터를 올바르게 관리하기 위해서는 &lt;b&gt;데이터 디렉토리&lt;/b&gt; 및 &lt;b&gt;설정 파일 디렉토리&lt;/b&gt;의 권한이 정확히 설정되어 있어야 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 권한 확인 및 수정 명령어&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;sudo chown -R 1001:1001 /appdata/mariadb/data  &lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;code&gt;chown -R 1001:1001&lt;/code&gt;&lt;/b&gt;: &lt;code&gt;/appdata/mariadb/data&lt;/code&gt; 디렉토리의 소유자와 그룹을 컨테이너의 기본 MariaDB UID/GID(&lt;code&gt;1001&lt;/code&gt;)로 변경.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MariaDB 사용자 권한&lt;/b&gt;: 기본적으로 MariaDB 컨테이너는 UID/GID &lt;code&gt;1001&lt;/code&gt;로 실행되므로, 해당 사용자가 데이터를 읽고 쓸 수 있어야 합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⚠️ &lt;b&gt;주의&lt;/b&gt;: 잘못된 권한 설정은 MariaDB 컨테이너 실행 오류를 유발할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 기본 보안 설정 강화&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MariaDB의 보안을 강화하려면 다음 설정을 추가로 고려해야합니다.&lt;/p&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;MariaDB의 &lt;b&gt;root 계정&lt;/b&gt;은 강력한 권한을 가지고 있어 보안에 취약할 수 있습니다. 원격 접근을 제한하고 별도의 관리 계정을 생성해야합니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;CREATE USER 'admin'@'%' IDENTIFIED BY 'secure\_password';  
GRANT ALL PRIVILEGES ON _._ TO 'admin'@'%' WITH GRANT OPTION;  
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'root'@'%';  &lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 권한 최소화&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;GRANT SELECT, INSERT, UPDATE ON my\_database.\* TO 'my\_user'@'%';  &lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. &lt;code&gt;my.cnf&lt;/code&gt; 보안 설정 추가&lt;/h4&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;\[mysqld\]  
bind-address = 127.0.0.1 # 로컬 연결로 제한  
skip-symbolic-links # 심볼릭 링크 비활성화  &lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;code&gt;bind-address&lt;/code&gt;&lt;/b&gt;: MariaDB의 원격 접근을 제한하거나 특정 IP만 허용.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;skip-symbolic-links&lt;/code&gt;&lt;/b&gt;: 보안 문제를 방지하기 위해 심볼릭 링크 비활성화.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 컨테이너 내부에서 데이터베이스 생성 및 계정 추가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MariaDB 컨테이너에 접속해 데이터베이스를 생성하고 계정을 설정합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 컨테이너 접속&lt;/h3&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;docker exec -it mariadb bash
mysql -uroot -p
[rootPassword]&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 데이터베이스 생성&lt;/h3&gt;
&lt;pre class=&quot;n1ql&quot;&gt;&lt;code&gt;CREATE DATABASE my_database;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 사용자 계정 생성 및 권한 부여&lt;/h3&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;CREATE USER 'my_user'@'%' IDENTIFIED BY 'my_password';
GRANT ALL PRIVILEGES ON my_database.* TO 'my_user'@'%';
FLUSH PRIVILEGES;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;code&gt;docker exec -it mariadb bash&lt;/code&gt;&lt;/b&gt;: 실행 중인 컨테이너에 접속.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;mysql -uroot -p&lt;/code&gt;&lt;/b&gt;: root 사용자로 MariaDB에 로그인.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;CREATE DATABASE&lt;/code&gt;&lt;/b&gt;: 새로운 데이터베이스 생성.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;CREATE USER&lt;/code&gt;&lt;/b&gt;: MariaDB 사용자 계정 생성.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;GRANT ALL PRIVILEGES&lt;/code&gt;&lt;/b&gt;: 사용자가 특정 데이터베이스에 대해 모든 권한을 가질 수 있도록 설정.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;FLUSH PRIVILEGES&lt;/code&gt;&lt;/b&gt;: 권한 변경 사항을 즉시 적용.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h1&gt;  정리&lt;/h1&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Docker로 MariaDB 이미지(&lt;code&gt;10.7.4&lt;/code&gt;)를 다운로드.&lt;/li&gt;
&lt;li&gt;MariaDB 설정 파일(&lt;code&gt;my.cnf&lt;/code&gt;)을 작성하고 Dockerfile에 반영.&lt;/li&gt;
&lt;li&gt;데이터 디렉토리 및 설정 파일 권한 확인 (&lt;code&gt;chown&lt;/code&gt; 명령어 사용).&lt;/li&gt;
&lt;li&gt;보안을 강화하여 원격 접근을 제한하고 최소 권한을 부여.&lt;/li&gt;
&lt;li&gt;Docker 컨테이너 실행 및 내부에서 데이터베이스와 계정을 생성.&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>Docker</category>
      <category>DB</category>
      <category>docker</category>
      <category>docker db</category>
      <category>docker mariadb</category>
      <category>mariadb</category>
      <category>mariadb container</category>
      <category>mysql</category>
      <category>oracle</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/280</guid>
      <comments>https://tae-hui.tistory.com/entry/DockerDocker%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%B4-MariaDB-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0#entry280comment</comments>
      <pubDate>Fri, 22 Nov 2024 10:23:28 +0900</pubDate>
    </item>
    <item>
      <title>WSL에서 Docker 컨테이너 내부에서 외부 호출이 불가능한 문제 해결하기</title>
      <link>https://tae-hui.tistory.com/entry/WSL%EC%97%90%EC%84%9C-Docker-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EB%82%B4%EB%B6%80%EC%97%90%EC%84%9C-%EC%99%B8%EB%B6%80-%ED%98%B8%EC%B6%9C%EC%9D%B4-%EB%B6%88%EA%B0%80%EB%8A%A5%ED%95%9C-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;WSL(Windows Subsystem for Linux)을 사용할 때 Docker 컨테이너 내부에서 외부 네트워크에 접근할 수 없는 문제가 발생했습니다. 이 문제는 &lt;b&gt;WSL과 Docker의 네트워크 구조 및 설정&lt;/b&gt;에서 비롯된 것이며, DNS 문제와 네트워크 브릿지 충돌 등이 원인일 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제의 원인과 개념을 자세히 설명하고, 이를 해결하는 방법을 차근차근 소개하겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.   문제의 원인 분석&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.1 WSL 네트워크 환경의 특징&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WSL2는 Windows 내부에서 동작하는 &lt;b&gt;가상화된 Linux 환경&lt;/b&gt;으로, 기본적으로 Windows 호스트 네트워크를 통해 인터넷에 연결됩니다.&lt;br /&gt;WSL2와 Docker 간의 네트워크 설정이 서로 충돌하거나 호환되지 않으면 다음 문제가 발생할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Docker 컨테이너에서 외부 네트워크에 접근하지 못함.&lt;/li&gt;
&lt;li&gt;WSL과 Docker 사이의 네트워크가 단절됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.2 네트워크 브릿지 설정 문제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker는 기본적으로 &lt;b&gt;bridge 네트워크&lt;/b&gt;를 사용하여 컨테이너 간 통신 및 외부 네트워크 연결을 관리합니다.&lt;br /&gt;WSL2와 Docker가 사용하는 네트워크 인터페이스가 충돌하면 문제가 발생할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  네트워크 브릿지란?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 브릿지는 컨테이너와 호스트 간의 통신, 컨테이너 간 통신, 외부 네트워크 연결을 가능하게 해주는 가상 네트워크입니다.&lt;br /&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;Docker와 WSL 사이의 네트워크가 단절됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.3 DNS 설정 문제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 내부에서 &lt;b&gt;도메인 이름&lt;/b&gt;을 해석하려면 DNS 서버를 통해 도메인을 IP 주소로 변환해야 합니다.&lt;br /&gt;WSL2 환경에서 Docker는 다음과 같은 이유로 DNS 문제를 겪을 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;WSL이 자동 생성한 &lt;code&gt;resolv.conf&lt;/code&gt; 파일에 설정된 &lt;code&gt;nameserver&lt;/code&gt;가 WSL 내부 DNS를 참조하지만, 컨테이너는 이를 사용할 수 없음.&lt;/li&gt;
&lt;li&gt;Docker의 기본 DNS 설정이 WSL 네트워크와 충돌하거나, DNS 요청이 무한 순환되는 상황 발생.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.4 &lt;code&gt;resolv.conf&lt;/code&gt; 파일의 문제 (저는 이 문제였습니다.)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WSL2에서 &lt;code&gt;/etc/resolv.conf&lt;/code&gt; 파일은 다음과 같이 자동 생성됩니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;# This file was automatically generated by WSL. To stop automatic generation of this file, add the following entry to /etc/wsl.conf:
# [network]
# generateResolvConf = false
nameserver 172.17.176.1&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;172.17.176.1&lt;/b&gt;: WSL 내부에서 생성된 가상 네트워크 인터페이스 주소입니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 주소는 WSL 내부에서는 유효하지만, Docker 컨테이너에서는 접근 불가능할 수 있습니다.&lt;/li&gt;
&lt;li&gt;WSL과 Docker 간 &lt;b&gt;네트워크 네임스페이스가 분리&lt;/b&gt;되어 있기 때문입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.5 방화벽/네트워크 규칙 문제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows 방화벽이 WSL과 Docker 사이의 네트워크 요청을 차단할 수도 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들어, WSL과 Docker의 네트워크 트래픽이 차단되면 DNS 요청이 실패하거나 네트워크 연결이 끊어질 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.   해결 방법: 단계별로 문제 해결하기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1 WSL의 DNS 자동 생성 비활성화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WSL이 자동으로 생성하는 &lt;code&gt;/etc/resolv.conf&lt;/code&gt; 파일을 비활성화하고, 직접 DNS 설정을 추가합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;설정 방법:&lt;/h4&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;code&gt;/etc/wsl.conf&lt;/code&gt; 파일 수정:&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[network]
generateResolvConf = false&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;WSL 재시작:&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wsl --shutdown&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DNS 설정 파일 생성:&lt;/b&gt;아래 내용을 추가합니다:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nameserver 8.8.8.8
nameserver 8.8.4.4&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sudo nano /etc/resolv.conf&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;이유:&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Google Public DNS(&lt;code&gt;8.8.8.8&lt;/code&gt;)를 명시적으로 사용하여 DNS 요청이 실패하지 않도록 설정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2 Docker 데몬 설정 수정(저는 이 방법을 사용했습니다.)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker가 사용할 DNS 서버와 네트워크 주소 범위를 명시적으로 설정합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;설정 파일 경로:&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;/etc/docker/daemon.json&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;설정 내용:&lt;/h4&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;dns&quot;: [&quot;8.8.8.8&quot;, &quot;8.8.4.4&quot;],
  &quot;default-address-pools&quot;: [
    {
      &quot;base&quot;: &quot;192.168.1.0/24&quot;,
      &quot;size&quot;: 24
    }
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;각 항목 설명:&lt;/h4&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;code&gt;dns&lt;/code&gt;:&lt;/b&gt; 컨테이너가 사용할 DNS 서버를 명시.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;default-address-pools&lt;/code&gt;:&lt;/b&gt; Docker 네트워크의 IP 주소 범위를 명시하여 WSL 네트워크와의 충돌 방지.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Docker 서비스 재시작:&lt;/h4&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo service docker restart&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.3 네트워크 브릿지 확인 및 수정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker 네트워크 설정을 확인하고, 브릿지 네트워크를 조정합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Docker 네트워크 확인:&lt;/h4&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;docker network ls&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브릿지 네트워크(&lt;code&gt;bridge&lt;/code&gt;)가 활성화되어 있는지 확인합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;IP 충돌 방지:&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker의 IP 풀을 재설정했으므로 브릿지가 올바르게 작동하지 않을 경우 아래 명령어로 네트워크를 재설정합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;docker network prune&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.4 방화벽 규칙 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows 방화벽에서 WSL과 Docker 관련 트래픽이 허용되도록 설정합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.5 DNS 캐시 초기화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WSL에서 DNS 캐시를 초기화하여 이전의 잘못된 DNS 설정을 제거합니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;sudo resolvectl flush-caches&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. ✨ 정리&lt;/h2&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;WSL의 자동 생성 DNS 설정이 Docker 컨테이너에서 동작하지 않음.&lt;/li&gt;
&lt;li&gt;Docker와 WSL 네트워크 충돌로 DNS 요청 실패.&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;WSL의 &lt;code&gt;/etc/resolv.conf&lt;/code&gt; 파일을 비활성화하고, 명시적인 DNS 설정 추가.&lt;/li&gt;
&lt;li&gt;Docker 데몬 설정(&lt;code&gt;/etc/docker/daemon.json&lt;/code&gt;)에 DNS와 네트워크 주소 범위를 명시.&lt;/li&gt;
&lt;li&gt;Windows 방화벽 규칙 확인 및 수정.&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;Docker 컨테이너에서 외부 네트워크에 정상적으로 연결되는지 확인합니다:
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;docker run --rm alpine ping -c 3 google.com&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 과정을 차근차근 따라 하면, WSL 환경에서 Docker 컨테이너가 외부 네트워크에 접근하지 못하는 문제를 확실히 해결할 수 있습니다.  &lt;/p&gt;</description>
      <category>IT 지식</category>
      <category>docker</category>
      <category>docker dns</category>
      <category>docker network</category>
      <category>docker 외부 호출 오류</category>
      <category>WSL</category>
      <category>wsl docker</category>
      <category>wsl 외부 호출</category>
      <category>오블완</category>
      <category>컨테이너 내부 외부 호출</category>
      <category>티스토리챌린지</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/279</guid>
      <comments>https://tae-hui.tistory.com/entry/WSL%EC%97%90%EC%84%9C-Docker-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EB%82%B4%EB%B6%80%EC%97%90%EC%84%9C-%EC%99%B8%EB%B6%80-%ED%98%B8%EC%B6%9C%EC%9D%B4-%EB%B6%88%EA%B0%80%EB%8A%A5%ED%95%9C-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0#entry279comment</comments>
      <pubDate>Thu, 21 Nov 2024 19:35:37 +0900</pubDate>
    </item>
    <item>
      <title>[Docker] SSH 연결 문제와 Docker 설정: 원인부터 해결까지</title>
      <link>https://tae-hui.tistory.com/entry/Docker-SSH-%EC%97%B0%EA%B2%B0-%EB%AC%B8%EC%A0%9C%EC%99%80-Docker-%EC%84%A4%EC%A0%95-%EC%9B%90%EC%9D%B8%EB%B6%80%ED%84%B0-%ED%95%B4%EA%B2%B0%EA%B9%8C%EC%A7%80</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  SSH 연결 문제와 Docker 설정: 원인부터 해결까지&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker 사용 중 서버에 SSH로 접속하여 도커 명령어를 실행하려고 할 때 접속이 끊기는 문제가 발생했습니다. 이 문제는 Docker의 &lt;b&gt;IP 대역 충돌&lt;/b&gt;에서 비롯되었습니다. 앞으로 이러한 오류를 방지하기위해 이번 포스팅에서는 문제의 원인을 깊이 이해하고, 이를 해결하기 위한 방법인 &lt;b&gt;&lt;code&gt;daemon.json&lt;/code&gt; 설정 수정&lt;/b&gt;절차를 단계별로 알아보겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.   문제 상황&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제&lt;/b&gt;&lt;br /&gt;로컬 PC에서 서버에 SSH로 접속 후 Docker를 실행하면 접속이 끊기거나 오류가 발생.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;Docker의 기본 네트워크 IP 대역&lt;/b&gt;인 &lt;code&gt;172.17.0.0/16&lt;/code&gt;이 &lt;b&gt;로컬 네트워크의 IP 대역&lt;/b&gt;과 겹칠 때 충돌.&lt;/li&gt;
&lt;li&gt;충돌로 인해 Docker의 브리지 네트워크와 SSH 연결 간에 패킷이 잘못 라우팅되거나 차단됨.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.   원인 분석&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1 Docker 네트워크 기본값 이해하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker는 컨테이너 간 통신을 위해 &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;b&gt;브리지 네트워크 IP 대역:&lt;/b&gt; &lt;code&gt;172.17.0.0/16&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Docker는 브리지 네트워크를 통해 컨테이너와 호스트 간 통신을 중계.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2 브리지 네트워크란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브리지 네트워크는 &lt;b&gt;호스트 머신과 Docker 컨테이너 간 가상 네트워크&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;기본적으로 Docker는 브리지 네트워크를 통해 &lt;b&gt;컨테이너 간 통신&lt;/b&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;blockquote data-ke-style=&quot;style2&quot;&gt;예:&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;호스트 머신: 192.168.1.10&lt;/li&gt;
&lt;li&gt;Docker 컨테이너 네트워크: 172.17.0.0/16&lt;br /&gt;이처럼 서로 다른 대역을 가지므로 네트워크 충돌이 없어야 하지만, 로컬 네트워크 대역이 중복되면 문제가 발생합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.3 충돌의 영향&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;SSH 연결 차단:&lt;/b&gt; 네트워크 충돌로 인해 SSH 연결 패킷이 손실될 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Docker 작동 문제:&lt;/b&gt; 네트워크 충돌은 Docker 컨테이너 간 통신에도 문제를 유발합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.   해결 방법: &lt;code&gt;daemon.json&lt;/code&gt; 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제를 해결하려면 Docker의 &lt;b&gt;네트워크 IP 대역을 변경&lt;/b&gt;하여 충돌을 방지해야 합니다. 이를 위해 &lt;b&gt;&lt;code&gt;/etc/docker/daemon.json&lt;/code&gt;&lt;/b&gt; 파일을 수정합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 &lt;code&gt;daemon.json&lt;/code&gt; 기본 구조&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;daemon.json&lt;/code&gt;은 Docker 데몬의 설정 파일로, JSON 형식으로 작성됩니다. 이 파일을 통해 Docker의 기본 동작을 커스터마이징할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2 주요 설정 항목&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;&lt;code&gt;data-root&lt;/code&gt;:&lt;/b&gt; Docker 데이터 경로를 변경.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;bip&lt;/code&gt;:&lt;/b&gt; 브리지 네트워크 IP 대역 설정.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;default-address-pools&lt;/code&gt;:&lt;/b&gt; Docker Compose의 네트워크 대역 설정.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;insecure-registries&lt;/code&gt;:&lt;/b&gt; HTTP로 접근 가능한 프라이빗 레지스트리 설정.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;registry-mirrors&lt;/code&gt;:&lt;/b&gt; Docker Hub 미러 설정.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.   실습: &lt;code&gt;daemon.json&lt;/code&gt; 수정&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.1 설정 파일 열기&lt;/h3&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;sudo vi /etc/docker/daemon.json&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.2 수정 내용&lt;/h3&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
    // Docker Root 경로 설정
    &quot;data-root&quot;: &quot;/data/docker&quot;,

    // 기본 Docker 브리지 네트워크 설정 (충돌 방지용 IP 대역)
    &quot;bip&quot;: &quot;192.172.100.1/24&quot;,

    // Docker Compose의 기본 네트워크 대역 설정
    &quot;default-address-pools&quot;: [
        {
            &quot;base&quot;: &quot;192.172.100.1/16&quot;,
            &quot;size&quot;: 24
        }
    ],

    // Docker 레지스트리 HTTP 연결 허용
    &quot;insecure-registries&quot;: [&quot;10.20.100.1:5000&quot;],

    // Docker Hub 미러 설정
    &quot;registry-mirrors&quot;: [&quot;https://&amp;lt;my-docker-mirror-host&amp;gt;&quot;]
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명&lt;/p&gt;
&lt;/blockquote&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;code&gt;bip&lt;/code&gt;&lt;/b&gt;: Docker 브리지 네트워크의 기본 IP 대역을 변경.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;default-address-pools&lt;/code&gt;&lt;/b&gt;: Docker Compose에서 기본 네트워크 대역을 별도로 설정.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;insecure-registries&lt;/code&gt;&lt;/b&gt;: HTTP로 동작하는 프라이빗 레지스트리를 허용.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;registry-mirrors&lt;/code&gt;&lt;/b&gt;: Docker Hub 대안 미러 설정.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.   적용 및 확인&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.1 Docker 데몬 재시작&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정을 저장한 후 Docker 데몬을 재시작합니다:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;sudo systemctl restart docker&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.2 설정 적용 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경된 네트워크 대역을 확인하려면 다음 명령어를 실행:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;docker network inspect bridge&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력에서 &lt;code&gt;Subnet&lt;/code&gt;이 새로 설정한 대역으로 변경되었는지 확인합니다:&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
    &quot;Subnet&quot;: &quot;192.172.100.1/24&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. ⚠️ 주의 사항&lt;/h2&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; 일부 환경에서는 Docker 데몬 재시작만으로 충분하지만, 네트워크 충돌로 인해 시스템 재부팅이 필요할 수도 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;구성 파일 JSON 유효성 검사:&lt;/b&gt; JSON 파일 수정 시 유효한 형식인지 반드시 확인.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;기존 네트워크 삭제:&lt;/b&gt; 기존 컨테이너의 네트워크 설정은 변경되지 않을 수 있으므로 필요시 네트워크를 삭제 후 다시 생성:
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;docker network rm &amp;lt;network_name&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7.   요약 및 결론&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; Docker와 로컬 네트워크 간의 IP 대역 충돌로 SSH 연결이 차단됨.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;해결책:&lt;/b&gt; &lt;code&gt;daemon.json&lt;/code&gt; 파일에서 &lt;code&gt;bip&lt;/code&gt; 및 &lt;code&gt;default-address-pools&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;code&gt;sudo vi /etc/docker/daemon.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Docker 재시작: &lt;code&gt;sudo systemctl restart docker&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;네트워크 확인: &lt;code&gt;docker network inspect bridge&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 내용을 따라가며 설정을 적용하면 Docker 네트워크 충돌 문제를 깔끔하게 해결할 수 있습니다. 더 궁금한 점이 있으면 댓글로 남겨주세요!  &lt;/p&gt;</description>
      <category>Docker</category>
      <category>docker</category>
      <category>docker bip</category>
      <category>docker bip 충돌</category>
      <category>docker error</category>
      <category>docker ip 충돌</category>
      <category>docker ssh</category>
      <category>docker ssh 안됨</category>
      <category>docker 오류</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/278</guid>
      <comments>https://tae-hui.tistory.com/entry/Docker-SSH-%EC%97%B0%EA%B2%B0-%EB%AC%B8%EC%A0%9C%EC%99%80-Docker-%EC%84%A4%EC%A0%95-%EC%9B%90%EC%9D%B8%EB%B6%80%ED%84%B0-%ED%95%B4%EA%B2%B0%EA%B9%8C%EC%A7%80#entry278comment</comments>
      <pubDate>Wed, 20 Nov 2024 13:57:39 +0900</pubDate>
    </item>
    <item>
      <title>[Docker] 리눅스에서 Docker 설치 및 실행 방법</title>
      <link>https://tae-hui.tistory.com/entry/Docker-%EB%A6%AC%EB%88%85%EC%8A%A4%EC%97%90%EC%84%9C-Docker-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%8B%A4%ED%96%89-%EB%B0%A9%EB%B2%95</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  Docker는 컨테이너 기반의 가상화 기술로, 개발 환경을 손쉽게 구성하고 배포하는 데 널리 사용됩니다. 이번 포스팅에서는 &lt;b&gt;Ubuntu&lt;/b&gt;와 &lt;b&gt;CentOS&lt;/b&gt;를 중심으로 리눅스에서 Docker를 설치하고 실행하는 방법을 단계별로 알아보겠습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.   Ubuntu에서 Docker 설치 및 실행&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.1 Docker 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Docker를 설치하기 전에 패키지 목록을 업데이트합니다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;sudo apt update
sudo apt install -y docker.io&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.2 Docker 서비스 시작&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치가 완료되었다면 Docker 서비스를 시작합니다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;sudo systemctl start docker&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.3 부팅 시 Docker 자동 시작 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부팅 시 자동으로 Docker가 실행되도록 설정합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo systemctl enable docker&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.4 WSL 사용자의 경우 자동 실행 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;WSL&lt;/b&gt; 환경에서는 &lt;code&gt;systemctl&lt;/code&gt;을 사용할 수 없으므로 별도 스크립트를 만들어 자동 실행하도록 설정합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스크립트 생성:&lt;/h4&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;sudo vi /etc/init.d/docker_daemon_run.sh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트 내용:&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;#!/bin/bash
sudo dockerd &amp;amp;&amp;gt; /dev/null &amp;amp;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스크립트 실행 권한 부여:&lt;/h4&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;sudo chmod +x /etc/init.d/docker_daemon_run.sh&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;WSL 시작 시 실행되도록 설정:&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;~/.bashrc&lt;/code&gt; 파일의 끝에 다음 내용을 추가합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sudo /etc/init.d/docker_daemon_run.sh&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;visudo&lt;/code&gt;를 통해 스크립트 실행 시 비밀번호 입력을 생략하도록 설정합니다.visudo 편집기에서 다음 줄을 추가:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;사용자 이름&amp;gt; ALL=(ALL) NOPASSWD: /etc/init.d/docker_daemon_run.sh&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sudo visudo&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.5 현재 사용자를 Docker 그룹에 추가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker를 루트 권한 없이 사용하려면 현재 사용자를 Docker 그룹에 추가해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;sudo usermod -aG docker $USER&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;b&gt;주의:&lt;/b&gt; 변경 사항을 적용하려면 로그아웃 후 다시 로그인하거나 시스템을 재시작해야 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.   CentOS에서 Docker 설치 및 실행&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1 Docker 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CentOS에서는 &lt;code&gt;yum&lt;/code&gt; 패키지 관리자를 사용하여 Docker를 설치합니다.&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;sudo yum install -y docker&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2 Docker 서비스 시작&lt;/h3&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;sudo systemctl start docker&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.3 부팅 시 Docker 자동 시작 설정&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo systemctl enable docker&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.4 현재 사용자를 Docker 그룹에 추가&lt;/h3&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;sudo usermod -aG docker $USER&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.   기타 배포판에서 Docker 설치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스의 다른 배포판에서도 Docker 설치가 가능합니다. 아래는 공식 스크립트나 명령어를 사용하는 방법입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 공식 Docker 설치 스크립트 (Ubuntu, Debian 등)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 스크립트를 사용하면 Docker 설치가 간단합니다.&lt;/p&gt;
&lt;pre class=&quot;dsconfig&quot;&gt;&lt;code&gt;curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2 CentOS 및 RHEL 기반 시스템&lt;/h3&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install docker-ce docker-ce-cli containerd.io
sudo systemctl start docker
sudo systemctl enable docker
sudo usermod -aG docker $USER&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.3 Fedora&lt;/h3&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;sudo dnf install -y docker
sudo systemctl start docker
sudo systemctl enable docker
sudo usermod -aG docker $USER&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.   Docker 설치 후 확인 및 기본 설정&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.1 Docker 버전 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker가 정상적으로 설치되었는지 확인하려면 다음 명령어를 실행합니다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;docker --version&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.2 컨테이너 실행&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 컨테이너를 실행하여 테스트합니다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;docker run hello-world&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 명령어는 Docker가 제대로 작동하는지 확인하기 위해 &lt;code&gt;hello-world&lt;/code&gt; 이미지를 다운로드하고 실행합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.   macOS 및 Windows에서 Docker 설치&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.1 macOS&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;macOS에서는 &lt;a href=&quot;https://www.docker.com/products/docker-desktop/&quot;&gt;Docker Desktop&lt;/a&gt;을 공식 웹사이트에서 다운로드하는 방법이 일반적입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.2 Windows&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows에서도 Docker Desktop을 설치하는 것이 권장됩니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&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;Ubuntu, CentOS 등에서 Docker를 설치하는 방법은 패키지 관리자 또는 공식 설치 스크립트를 통해 간단히 수행할 수 있습니다.&lt;/li&gt;
&lt;li&gt;설치 후 반드시 Docker Daemon을 시작하고, 사용자를 Docker 그룹에 추가하여 권한을 설정해야합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker --version&lt;/code&gt; 명령어로 설치를 확인하고, &lt;code&gt;docker run hello-world&lt;/code&gt;로 실행 테스트를 진행할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Docker</category>
      <category>docker</category>
      <category>docker 권한</category>
      <category>docker 자동실행</category>
      <category>linux</category>
      <category>linux docker</category>
      <category>ubuntu</category>
      <category>ubuntu docker</category>
      <category>도커 설치</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/277</guid>
      <comments>https://tae-hui.tistory.com/entry/Docker-%EB%A6%AC%EB%88%85%EC%8A%A4%EC%97%90%EC%84%9C-Docker-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%8B%A4%ED%96%89-%EB%B0%A9%EB%B2%95#entry277comment</comments>
      <pubDate>Tue, 19 Nov 2024 17:16:40 +0900</pubDate>
    </item>
    <item>
      <title>[Docker] Dockerfile 작성법</title>
      <link>https://tae-hui.tistory.com/entry/Docker-Dockerfile-%EC%9E%91%EC%84%B1%EB%B2%95</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 포스팅에서 다뤘던 &lt;code&gt;[&lt;a style=&quot;background-color: #e6f5ff; color: #0070d1; text-align: start;&quot; href=&quot;https://tae-hui.tistory.com/entry/Docker-Docker%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80&quot;&gt;Docker란 무엇인가?&lt;/a&gt;]&lt;/code&gt; 내용을 참고하시면, 이번 주제에 대한 배경 지식이나 기초 정보를 확인하실 수 있습니다!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure id=&quot;og_1731932782862&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Docker] Docker란 무엇인가?&quot; data-og-description=&quot;  1. Docker란 무엇인가요?Docker는 애플리케이션을 컨테이너라는 독립된 환경에 격리시켜 실행할 수 있게 도와주는 오픈 소스 플랫폼입니다. 애플리케이션과 그에 필요한 모든 라이브러리, 종속&quot; data-og-host=&quot;tae-hui.tistory.com&quot; data-og-source-url=&quot;https://tae-hui.tistory.com/entry/Docker-Docker%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80&quot; data-og-url=&quot;https://tae-hui.tistory.com/entry/Docker-Docker%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/czGwj6/hyXzLxdIl8/I3oVwoB9fOKNqBG4XAXDX1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/uorJc/hyXzTaYjTY/tZd1rON0Vm1sfOsRenD4RK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://tae-hui.tistory.com/entry/Docker-Docker%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://tae-hui.tistory.com/entry/Docker-Docker%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/czGwj6/hyXzLxdIl8/I3oVwoB9fOKNqBG4XAXDX1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/uorJc/hyXzTaYjTY/tZd1rON0Vm1sfOsRenD4RK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[Docker] Docker란 무엇인가?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;  1. Docker란 무엇인가요?Docker는 애플리케이션을 컨테이너라는 독립된 환경에 격리시켜 실행할 수 있게 도와주는 오픈 소스 플랫폼입니다. 애플리케이션과 그에 필요한 모든 라이브러리, 종속&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;tae-hui.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.  ️ Dockerfile이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerfile은 컨테이너 이미지를 정의하는 &lt;b&gt;스크립트 파일&lt;/b&gt;로, 이미지 빌드에 필요한 설정과 명령을 담고 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  주요 특징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Dockerfile은 단계별로 &lt;b&gt;이미지를 정의&lt;/b&gt;합니다.&lt;/li&gt;
&lt;li&gt;빌드 시 각 단계가 &lt;b&gt;레이어(layer)&lt;/b&gt;로 저장됩니다.&lt;/li&gt;
&lt;li&gt;Docker는 레이어를 캐싱해 빌드 속도를 최적화합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.   Dockerfile 명령어: 기본부터 심화까지&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerfile은 다양한 명령어로 구성됩니다. &lt;b&gt;명령어와 역할&lt;/b&gt;을 하나씩 살펴보겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.1.  ️ &lt;code&gt;FROM&lt;/code&gt;: 베이스 이미지 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker 이미지는 항상 &lt;b&gt;베이스 이미지&lt;/b&gt;에서 시작합니다. 공식 이미지를 사용하는 것이 일반적입니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;# Python 3.9 슬림 버전 사용
FROM python:3.9-slim&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;b&gt;슬림(slim)&lt;/b&gt; 이미지는 경량화된 베이스 이미지로, 불필요한 라이브러리가 포함되지 않아 최적화에 유리합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.2.   &lt;code&gt;RUN&lt;/code&gt;: 명령 실행&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 빌드 과정에서 실행할 명령어를 정의합니다. 주로 &lt;b&gt;패키지 설치&lt;/b&gt;, &lt;b&gt;환경 구성&lt;/b&gt;에 사용됩니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# 패키지 업데이트 및 설치
RUN apt-get update &amp;amp;&amp;amp; apt-get install -y curl git&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt; &amp;zwj;  최적화 팁&lt;/h4&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;code&gt;RUN&lt;/code&gt; 명령을 하나로 합쳐 레이어를 줄이세요.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RUN apt-get update &amp;amp;&amp;amp; \ apt-get install -y curl git &amp;amp;&amp;amp; \ apt-get clean &amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/*&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;캐싱 활용&lt;/b&gt;: 빈번히 변경되는 파일(코드)보다 의존성 설치 명령을 먼저 작성하세요.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.3.   &lt;code&gt;COPY&lt;/code&gt;와 &lt;code&gt;ADD&lt;/code&gt;: 파일 복사&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;code&gt;COPY&lt;/code&gt;: 단순 파일 복사.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ADD&lt;/code&gt;: URL 다운로드, 압축 해제 기능 지원.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;# 현재 디렉토리의 모든 파일을 컨테이너의 /app 디렉토리로 복사
COPY . /app&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;b&gt;&lt;code&gt;COPY&lt;/code&gt; 추천&lt;/b&gt;: 대부분의 경우 &lt;code&gt;COPY&lt;/code&gt;가 더 효율적입니다. &lt;code&gt;ADD&lt;/code&gt;는 URL이나 압축 파일 처리 시에만 사용하세요.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.4.  ️ &lt;code&gt;WORKDIR&lt;/code&gt;: 작업 디렉토리 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령어 실행의 기본 경로를 설정합니다. 이후 명령(&lt;code&gt;RUN&lt;/code&gt;, &lt;code&gt;CMD&lt;/code&gt;, &lt;code&gt;COPY&lt;/code&gt;)은 이 디렉토리를 기준으로 실행됩니다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;WORKDIR /app&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.5.   &lt;code&gt;ENV&lt;/code&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;routeros&quot;&gt;&lt;code&gt;ENV APP_ENV=production
ENV DEBUG=false&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;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.6.   &lt;code&gt;EXPOSE&lt;/code&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;angelscript&quot;&gt;&lt;code&gt;EXPOSE 8080&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;b&gt;주의&lt;/b&gt;: &lt;code&gt;EXPOSE&lt;/code&gt;는 포트를 명시적으로 지정하는 역할만 합니다. 컨테이너 실행 시 실제로 열려면 &lt;code&gt;docker run -p&lt;/code&gt; 옵션이 필요합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.7.  ️ &lt;code&gt;CMD&lt;/code&gt;와 &lt;code&gt;ENTRYPOINT&lt;/code&gt;: 컨테이너 실행 명령 설정&lt;/h3&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;code&gt;CMD&lt;/code&gt;: 컨테이너 시작 시 기본 실행 명령.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ENTRYPOINT&lt;/code&gt;: 고정 실행 명령.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CMD 예제&lt;/h4&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;CMD [&quot;python&quot;, &quot;app.py&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ENTRYPOINT 예제&lt;/h4&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;ENTRYPOINT [&quot;python&quot;]
CMD [&quot;app.py&quot;]&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;b&gt;동시 사용&lt;/b&gt;: &lt;code&gt;ENTRYPOINT&lt;/code&gt;는 고정된 실행 파일, &lt;code&gt;CMD&lt;/code&gt;는 옵션으로 함께 사용할 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.8.   Multi-stage Build: 이미지 최적화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Multi-stage Build&lt;/b&gt;는 빌드와 런타임 환경을 분리해 이미지 크기를 최소화합니다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;# 1단계: 빌드
FROM node:16 AS builder
WORKDIR /app
COPY . .
RUN npm install &amp;amp;&amp;amp; npm run build

# 2단계: 런타임
FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html
EXPOSE 80
CMD [&quot;nginx&quot;, &quot;-g&quot;, &quot;daemon off;&quot;]&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;b&gt;이점&lt;/b&gt;: 빌드에 필요한 도구와 의존성을 최종 이미지에 포함하지 않아 더 작은 이미지를 생성합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.   종합 Dockerfile 예제&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Python Flask 프로젝트 예제&lt;/h3&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;# 베이스 이미지 설정
FROM python:3.9-slim

# 환경 변수 설정
ENV PYTHONUNBUFFERED=1 APP_ENV=production

# 작업 디렉토리 설정
WORKDIR /app

# 의존성 설치
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 애플리케이션 코드 복사
COPY . .

# 포트 공개
EXPOSE 5000

# 실행 명령 설정
CMD [&quot;python&quot;, &quot;app.py&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Node.js 프로젝트 예제&lt;/h3&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;# 1단계: 빌드
FROM node:16 AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
RUN npm run build

# 2단계: 런타임
FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html
EXPOSE 80
CMD [&quot;nginx&quot;, &quot;-g&quot;, &quot;daemon off;&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.   Dockerfile 작성 시 주의사항 및 체크리스트&lt;/h2&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;code&gt;slim&lt;/code&gt; 또는 &lt;code&gt;alpine&lt;/code&gt; 추천.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;불필요한 파일 제외&lt;/b&gt;: &lt;code&gt;.dockerignore&lt;/code&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;code&gt;RUN&lt;/code&gt; 명령을 한 줄로 합쳐 레이어 수 줄이기.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;환경 변수 활용&lt;/b&gt;: 설정값을 코드에서 분리.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Multi-stage Build 사용&lt;/b&gt;: 빌드와 런타임 환경 분리.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. ✨ 최종 요약&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Dockerfile 작성 프로세스&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;FROM&lt;/code&gt;: 베이스 이미지 선택.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WORKDIR&lt;/code&gt;: 작업 디렉토리 설정.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;COPY&lt;/code&gt; / &lt;code&gt;ADD&lt;/code&gt;: 파일 복사.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RUN&lt;/code&gt;: 의존성 설치 및 환경 구성.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ENV&lt;/code&gt;: 환경 변수 설정.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EXPOSE&lt;/code&gt;: 외부 통신 포트 정의.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CMD&lt;/code&gt; / &lt;code&gt;ENTRYPOINT&lt;/code&gt;: 컨테이너 실행 명령 설정.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실전 팁&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Multi-stage Build로 &lt;b&gt;이미지 크기 최소화&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;불필요한 레이어 줄이기.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.dockerignore&lt;/code&gt;로 빌드 최적화.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Docker</category>
      <category>docker</category>
      <category>docker build</category>
      <category>docker image making</category>
      <category>docker run</category>
      <category>Dockerfile</category>
      <category>도커</category>
      <category>도커 빌드 방법</category>
      <category>도커 이미지 만들기</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/276</guid>
      <comments>https://tae-hui.tistory.com/entry/Docker-Dockerfile-%EC%9E%91%EC%84%B1%EB%B2%95#entry276comment</comments>
      <pubDate>Mon, 18 Nov 2024 21:30:01 +0900</pubDate>
    </item>
    <item>
      <title>[Nginx] Nginx 설정과 활용법</title>
      <link>https://tae-hui.tistory.com/entry/Nginx-Nginx-%EC%84%A4%EC%A0%95%EA%B3%BC-%ED%99%9C%EC%9A%A9%EB%B2%95</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Nginx 설정과 활용법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nginx는 웹 서버, 리버스 프록시, 로드 밸런싱 등 다양한 용도로 활용할 수 있는 만능 도구라서, 제대로 이해하고 활용하면 큰 도움이 됩니다. 아래는 Nginx로 할 수 있는 주요 작업 10가지와 구체적인 설정 방법입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&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;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nginx를 웹 서버로 설정하여 정적 파일(HTML, CSS, JS)을 서빙합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설정 파일 (&lt;code&gt;nginx.conf&lt;/code&gt;)&lt;/h3&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;server {
    listen 80;
    server_name localhost;

    location / {
        root /usr/share/nginx/html;
        index index.html;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;code&gt;nginx.conf&lt;/code&gt; 파일을 생성합니다.&lt;/li&gt;
&lt;li&gt;도커 컨테이너 실행 시 설정 파일을 마운트합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;docker run -d -p 80:80 -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf nginx&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.   리버스 프록시 설정&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설정 내용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백엔드 서버(예: Node.js, Python 등)에 요청을 전달하는 리버스 프록시 설정입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설정 파일&lt;/h3&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;server {
    listen 80;

    location / {
        proxy_pass http://backend:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;code&gt;backend&lt;/code&gt;라는 이름으로 실행합니다.&lt;/li&gt;
&lt;li&gt;도커 네트워크를 공유하도록 설정합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;docker network create my-network
docker run --network my-network --name backend my-backend-image
docker run --network my-network -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf -p 80:80 nginx&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.   로드 밸런싱 (Round Robin)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설정 내용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nginx를 사용하여 여러 백엔드 서버에 로드 밸런싱을 적용합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설정 파일&lt;/h3&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;upstream backend_servers {
    server backend1:3000;
    server backend2:3000;
}

server {
    listen 80;

    location / {
        proxy_pass http://backend_servers;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실행&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;docker network create lb-network
docker run --network lb-network --name backend1 my-backend-image
docker run --network lb-network --name backend2 my-backend-image
docker run --network lb-network -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf -p 80:80 nginx&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.   로드 밸런싱 (Least Connections)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설정 내용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;least_conn&lt;/code&gt; 방식으로 트래픽을 처리하도록 설정합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설정 파일&lt;/h3&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;upstream backend_servers {
    least_conn;
    server backend1:3000;
    server backend2:3000;
}

server {
    listen 80;

    location / {
        proxy_pass http://backend_servers;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.   HTTPS 설정&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설정 내용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Let's Encrypt를 사용해 HTTPS를 적용합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설정 파일&lt;/h3&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate /etc/nginx/ssl/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/key.pem;

    location / {
        proxy_pass http://backend:3000;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SSL 인증서 마운트&lt;/h3&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;docker run -d -p 443:443 \
-v $(pwd)/nginx.conf:/etc/nginx/nginx.conf \
-v $(pwd)/ssl:/etc/nginx/ssl nginx&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&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;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서브도메인 별로 다른 백엔드로 요청을 전달합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설정 파일&lt;/h3&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;server {
    listen 80;
    server_name api.example.com;

    location / {
        proxy_pass http://api_backend:3000;
    }
}

server {
    listen 80;
    server_name www.example.com;

    location / {
        proxy_pass http://web_backend:3000;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7.   캐싱 설정&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설정 내용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 파일에 대한 캐싱을 설정합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설정 파일&lt;/h3&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;server {
    listen 80;

    location /static/ {
        root /usr/share/nginx/html;
        expires 30d;
        add_header Cache-Control &quot;public, must-revalidate&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8.  ️ 방화벽 역할 (IP 제한)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설정 내용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 IP 주소만 접근 가능하도록 설정합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설정 파일&lt;/h3&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;server {
    listen 80;

    location / {
        allow 192.168.1.1;
        deny all;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9.   Forward Proxy&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설정 내용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nginx를 클라이언트용 포워드 프록시로 사용합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설정 파일&lt;/h3&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;server {
    listen 8888;

    location / {
        proxy_pass $scheme://$http_host$request_uri;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10.   Custom Error Pages&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설정 내용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;404 에러 시 사용자 정의 에러 페이지를 반환합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설정 파일&lt;/h3&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;server {
    listen 80;

    error_page 404 /custom_404.html;

    location /custom_404.html {
        root /usr/share/nginx/html;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ 설정 확인 및 재시작&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설정 파일 확인&lt;/h3&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;docker exec -it &amp;lt;nginx-container-id&amp;gt; nginx -t&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설정 적용&lt;/h3&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;docker restart &amp;lt;nginx-container-id&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&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;Nginx의 주요 활용&lt;/b&gt;: 웹 서버, 리버스 프록시, 로드 밸런싱, HTTPS, 캐싱 등.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;도커와 함께 사용&lt;/b&gt;: &lt;code&gt;-v&lt;/code&gt; 옵션으로 설정 파일을 마운트하여 관리.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;로드 밸런싱&lt;/b&gt;: Round Robin, Least Connections 등 다양한 방식 지원.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;보안 및 최적화&lt;/b&gt;: IP 제한, 캐싱, HTTPS 설정.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Nginx</category>
      <category>https</category>
      <category>LB</category>
      <category>nginx</category>
      <category>nginx ssl</category>
      <category>round robin</category>
      <category>로드 밸런싱</category>
      <category>리버스 프록시</category>
      <category>오블완</category>
      <category>캐싱 설정</category>
      <category>티스토리챌린지</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/275</guid>
      <comments>https://tae-hui.tistory.com/entry/Nginx-Nginx-%EC%84%A4%EC%A0%95%EA%B3%BC-%ED%99%9C%EC%9A%A9%EB%B2%95#entry275comment</comments>
      <pubDate>Sun, 17 Nov 2024 10:00:41 +0900</pubDate>
    </item>
    <item>
      <title>[Jenkins] Jenkins Docker 컨테이너에서 Docker를 사용해 Git 소스 배포하기</title>
      <link>https://tae-hui.tistory.com/entry/Jenkins-Jenkins-Docker-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EC%97%90%EC%84%9C-Docker%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4-Git-%EC%86%8C%EC%8A%A4-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이전 포스팅에서 다뤘던 [ &lt;a href=&quot;https://tae-hui.tistory.com/entry/Jenkins-CICD-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0&quot;&gt;Jenkins CI/CD 환경 구축하기&lt;/a&gt; ] 내용을 참고하시면, 이번 주제에 대한 배경 지식이나 기초 정보를 확인하실 수 있습니다!&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1731752492261&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Jenkins] CI/CD 환경 구축하기&quot; data-og-description=&quot;  Jenkins로 CI/CD 환경 구축하기 - 기본 플러그인 소개부터 설치까지!개발이나 DevOps를 처음 시작한 분이라면 지속적 통합과 지속적 배포(Continuous Integration/Continuous Deployment, CI/CD) 자동화 도구로 Jen&quot; data-og-host=&quot;tae-hui.tistory.com&quot; data-og-source-url=&quot;https://tae-hui.tistory.com/entry/Jenkins-CICD-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0&quot; data-og-url=&quot;https://tae-hui.tistory.com/entry/Jenkins-CICD-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bz11i2/hyXwseoWen/frQGex714gudE9WT8lm4K1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cS0XTS/hyXwlTUgpB/W9NaduKEY2PMGBmXl4XFe0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://tae-hui.tistory.com/entry/Jenkins-CICD-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://tae-hui.tistory.com/entry/Jenkins-CICD-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bz11i2/hyXwseoWen/frQGex714gudE9WT8lm4K1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cS0XTS/hyXwlTUgpB/W9NaduKEY2PMGBmXl4XFe0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[Jenkins] CI/CD 환경 구축하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;  Jenkins로 CI/CD 환경 구축하기 - 기본 플러그인 소개부터 설치까지!개발이나 DevOps를 처음 시작한 분이라면 지속적 통합과 지속적 배포(Continuous Integration/Continuous Deployment, CI/CD) 자동화 도구로 Jen&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;tae-hui.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Jenkins Docker 컨테이너에서 Docker를 사용해 Git 소스 배포하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Jenkins&lt;/b&gt;를 &lt;b&gt;Docker&lt;/b&gt; 컨테이너로 실행하면서 내부에서 호스트의 &lt;b&gt;Docker&lt;/b&gt; 환경을 사용할 수 있게 설정하고, &lt;b&gt;GitHub&lt;/b&gt; 소스를 가져와 빌드 및 배포까지 진행하는 방법을 알아보겠습니다. 이 과정에서는 &lt;b&gt;Jenkins&lt;/b&gt;의 강력한 &lt;b&gt;CI/CD&lt;/b&gt; 기능과 &lt;b&gt;Docker&lt;/b&gt;를 활용한 배포 자동화를 결합합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  1. Jenkins 컨테이너 실행하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, Jenkins를 Docker 컨테이너로 실행하고, &lt;b&gt;호스트의 Docker 소켓&lt;/b&gt;을 컨테이너에 마운트하여 Docker 명령어를 사용할 수 있도록 설정해야 합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  Docker 명령어로 Jenkins 컨테이너 실행&lt;/h4&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;docker run --rm -d -p 28080:8080 -p 50000:50000 \
-v /appdata/jenkins/jenkins_home:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /appdata/deploy:/home/portal/deploy \
-e TZ=Asia/Seoul \
-e JENKINS_OPTS=&quot;--httpPort=8080&quot; \
-u root \
--name jenkins-container jenkins/jenkins:lts&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;주요 옵션 설명&lt;/h4&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;code&gt;-v /var/run/docker.sock:/var/run/docker.sock&lt;/code&gt;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;호스트 Docker 소켓을 마운트하여 컨테이너 내부에서 Docker 명령어를 실행할 수 있게 만듭니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;-u root&lt;/code&gt;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;root 사용자로 컨테이너를 실행합니다. Jenkins 컨테이너 내부에서 Docker 명령을 실행하려면 루트 권한이 필요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;-v /appdata/jenkins/jenkins_home:/var/jenkins_home&lt;/code&gt;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Jenkins 데이터를 호스트 디렉토리에 저장하여 데이터가 지속됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  2. Jenkins 초기 설정&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;Jenkins 설치 후 초기 설정&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브라우저에서 Jenkins에 접속합니다: &lt;code&gt;http://&amp;lt;호스트 IP&amp;gt;:28080&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;초기 관리자 비밀번호는 컨테이너 로그에서 확인할 수 있습니다.&amp;nbsp;&lt;/li&gt;
&lt;li class=&quot;axapta&quot;&gt;&lt;code&gt;docker logs jenkins-container&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Jenkins UI에서 플러그인을 설치하고, 관리 계정을 생성합니다.&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;Jenkins 대시보드 &amp;rarr; &lt;b&gt;Manage Jenkins&lt;/b&gt; &amp;rarr; &lt;b&gt;Manage Plugins&lt;/b&gt; &amp;rarr; &lt;b&gt;Available&lt;/b&gt; 탭에서 다음 플러그인을 설치하세요:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Git Plugin&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Docker Pipeline&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SSH Pipeline Steps&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;[&lt;a href=&quot;https://github.com/taehui8260/jenkins-plugins&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/taehui8260/jenkins-plugins&lt;/a&gt;]해당 Github Repository에서 유용한 필수 플러그인을 직접 다운 후 적용할 수 있습니다.&lt;/blockquote&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Credential 설정&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;GitHub Access Token&lt;/b&gt;: GitHub와 연동하기 위해 Personal Access Token을 생성하고 Jenkins에 추가합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Docker Hub 계정&lt;/b&gt;: Docker 이미지를 푸시하기 위해 Docker Hub 계정을 Jenkins에 등록합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SSH 계정&lt;/b&gt;: 원격 배포 서버에 접근하기 위해 SSH 정보를 등록합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  3. Jenkinsfile 작성하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Jenkinsfile을 작성하여, GitHub에서 코드를 가져오고 빌드 및 Docker 작업을 수행하는 파이프라인을 설정해 보겠습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Jenkinsfile 코드&lt;/h4&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;pipeline {
    agent any // Jenkins 노드에서 실행

    environment {
        GIT_REPO_URL = 'https://github.com/사용자명/레포지토리명.git'
        GIT_CREDENTIALS_ID = '깃허브-크리덴셜-ID'
        DOCKER_IMAGE = '사용자명/레포지토리명'
        DOCKER_CREDENTIALS_ID = '도커-크리덴셜-ID'
        CURRENT_DATE = sh(returnStdout: true, script: 'date +%Y%m%d%H%M').trim()
    }

    stages {
        stage('Clone Repository') {
            steps {
                git branch: 'main', credentialsId: &quot;${GIT_CREDENTIALS_ID}&quot;, url: &quot;${GIT_REPO_URL}&quot;
            }
        }

        stage('Build') {
            steps {
                script {
                    def MAVEN_HOME = tool 'MAVEN_3.9.4'
                    sh &quot;${MAVEN_HOME}/bin/mvn -f pom.xml clean package&quot;
                }
            }
        }

        stage('Docker Build') {
            steps {
                script {
                    sh &quot;docker build -t ${DOCKER_IMAGE}:${CURRENT_DATE} .&quot;
                }
            }
        }

        stage('Docker Push') {
            steps {
                script {
                    withCredentials([string(credentialsId: &quot;${DOCKER_CREDENTIALS_ID}&quot;, variable: 'DOCKER_PASSWORD')]) {
                        sh &quot;&quot;&quot;
                        echo \$DOCKER_PASSWORD | docker login -u ${DOCKER_IMAGE} --password-stdin
                        docker push ${DOCKER_IMAGE}:${CURRENT_DATE}
                        &quot;&quot;&quot;
                    }
                }
            }
        }

        stage('Deploy') {
            steps {
                script {
                    sh &quot;docker run -d --name my-app-container ${DOCKER_IMAGE}:${CURRENT_DATE}&quot;
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  4. 주요 단계 설명&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;GitHub에서 코드 가져오기&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;git&lt;/code&gt; 명령어를 사용하여 GitHub의 소스를 Jenkins 워크스페이스로 가져옵니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Maven 빌드&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;mvn clean package&lt;/code&gt; 명령어로 프로젝트를 빌드합니다. Maven 설정은 Jenkins Tools에서 관리합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Docker 빌드&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;docker build&lt;/code&gt; 명령어로 프로젝트의 Docker 이미지를 생성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Docker 푸시&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생성된 이미지를 Docker Hub로 푸시합니다. Credential을 안전하게 관리하기 위해 &lt;code&gt;withCredentials&lt;/code&gt;를 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Docker 배포&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Docker 컨테이너를 실행하여 배포합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  5. Jenkins Job 생성 및 실행&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Jenkins 대시보드에서 &lt;b&gt;New Item&lt;/b&gt; &amp;rarr; &lt;b&gt;Pipeline&lt;/b&gt; 선택.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Pipeline Script from SCM&lt;/b&gt; 설정:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SCM: Git&lt;/li&gt;
&lt;li&gt;Repository URL: &lt;code&gt;https://github.com/사용자명/레포지토리명.git&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Credentials: GitHub Access Token 선택.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Jenkins Job 실행 후, 단계별 로그를 확인하고 Docker Hub 및 컨테이너 상태를 점검합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  6. 추가 팁&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;Docker Volume 활용&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;li&gt;&lt;b&gt;Credential 보안 관리&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Jenkins의 &lt;b&gt;Credential Store&lt;/b&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;Jenkins Pipeline에 알림 설정(Slack, Email)을 추가하여 빌드 및 배포 상태를 실시간으로 확인할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  요약&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Jenkins Docker 컨테이너 설정&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;호스트의 Docker 소켓을 마운트하여 내부에서 Docker를 사용할 수 있도록 설정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GitHub와 Docker 연동&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Jenkinsfile을 통해 GitHub에서 코드를 가져오고, Docker 이미지를 빌드하여 Docker Hub에 푸시한 후 배포합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자동화된 CI/CD 파이프라인&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Jenkins를 활용해 빌드, 분석, Docker 작업, 배포까지 자동화된 워크플로우를 구현합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker와 Jenkins를 통합하여 손쉽게 CI/CD 환경을 구축할 수 있습니다!&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>IT 지식</category>
      <category>docker</category>
      <category>Jenkins</category>
      <category>jenkins docker</category>
      <category>jenkins pipeline</category>
      <category>jenkins 도커</category>
      <category>도커</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <category>파이프라인</category>
      <category>파이프라인 작성법</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/274</guid>
      <comments>https://tae-hui.tistory.com/entry/Jenkins-Jenkins-Docker-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EC%97%90%EC%84%9C-Docker%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4-Git-%EC%86%8C%EC%8A%A4-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0#entry274comment</comments>
      <pubDate>Sat, 16 Nov 2024 19:25:03 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] Java의 예외 계층 구조와 개념</title>
      <link>https://tae-hui.tistory.com/entry/JAVA</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 포스팅에서 다뤘던 [ &lt;a href=&quot;https://tae-hui.tistory.com/entry/JAVA-Java%EC%97%90%EC%84%9C-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC%EB%B0%A9%EB%B2%95&quot;&gt;[JAVA] Java에서 예외 처리방법&lt;/a&gt; ] 내용을 참고하시면, 이번 주제에 대한 배경 지식이나 기초 정보를 확인하실 수 있습니다!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1731637894417&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[JAVA] Java에서 예외 처리방법&quot; data-og-description=&quot;  1. Java에서 예외 처리방법자바에서 예외 처리는 프로그램의 오류를 안전하게 처리하고 앱의 안정성을 유지하는 중요한 개념입니다. 예외는 예상치 못한 상황에서 발생할 수 있는 오류들을 &quot; data-og-host=&quot;tae-hui.tistory.com&quot; data-og-source-url=&quot;https://tae-hui.tistory.com/entry/JAVA-Java%EC%97%90%EC%84%9C-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC%EB%B0%A9%EB%B2%95&quot; data-og-url=&quot;https://tae-hui.tistory.com/entry/JAVA-Java%EC%97%90%EC%84%9C-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC%EB%B0%A9%EB%B2%95&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c3UhOl/hyXzR4sTfD/Hc7uPKnRYWgBVHq67kWHQ0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cUYmVs/hyXwub0T2L/W1epT33Z72BfAqijg2e71K/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://tae-hui.tistory.com/entry/JAVA-Java%EC%97%90%EC%84%9C-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC%EB%B0%A9%EB%B2%95&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://tae-hui.tistory.com/entry/JAVA-Java%EC%97%90%EC%84%9C-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC%EB%B0%A9%EB%B2%95&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c3UhOl/hyXzR4sTfD/Hc7uPKnRYWgBVHq67kWHQ0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cUYmVs/hyXwub0T2L/W1epT33Z72BfAqijg2e71K/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[JAVA] Java에서 예외 처리방법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;  1. Java에서 예외 처리방법자바에서 예외 처리는 프로그램의 오류를 안전하게 처리하고 앱의 안정성을 유지하는 중요한 개념입니다. 예외는 예상치 못한 상황에서 발생할 수 있는 오류들을&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;tae-hui.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.   Java의 예외 계층 구조와 개념&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java에서는 프로그램 실행 중 발생할 수 있는 문제를 &lt;b&gt;체계적으로 관리&lt;/b&gt;하고 &lt;b&gt;프로그램의 안정성을 높이기 위해&lt;/b&gt; 예외 처리를 제공합니다. 모든 예외 상황은 &lt;code&gt;Throwable&lt;/code&gt;이라는 최상위 클래스를 중심으로 &lt;b&gt;Error&lt;/b&gt;와 &lt;b&gt;Exception&lt;/b&gt;으로 구분되며, 각 예외는 발생하는 시점과 성격에 따라 다르게 처리됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Throwable 계층 구조&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;code&gt;Throwable&lt;/code&gt;&lt;/b&gt;: 모든 예외 객체의 최상위 부모 클래스입니다. 예외 상황을 구분하는 기반이 되며, Error와 Exception의 공통 부모 역할을 합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;Error&lt;/code&gt;&lt;/b&gt;: 주로 JVM 레벨에서 발생하는 치명적 오류로, 개발자가 예측하거나 처리하기 매우 어려운 경우가 많습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;Exception&lt;/code&gt;&lt;/b&gt;: 애플리케이션 수준에서 발생하는 오류로, 적절한 처리를 통해 복구 가능성이 높은 오류입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; &lt;/b&gt;&amp;nbsp;JVM&amp;nbsp;실행&amp;nbsp;vs&amp;nbsp;애플리케이션&amp;nbsp;수준:&amp;nbsp;개념과&amp;nbsp;차이점&lt;b&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;JVM 실행(JVM Level)&lt;/b&gt;은 Java Virtual Machine이 Java 코드를 운영 체제 위에서 &lt;b&gt;실행하고 관리하는 수준&lt;/b&gt;을 의미합니다. 이 단계에서 발생하는 오류는 JVM 내부 자원 부족, 메모리 문제, 시스템 하드웨어 문제 등 시스템 전반에 영향을 미치는 심각한 오류가 포함됩니다. JVM은 이처럼 &lt;b&gt;시스템 리소스와 직접 관련된 문제&lt;/b&gt;를 다루기 때문에 일반적으로 복구가 불가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면, &lt;b&gt;애플리케이션 수준(Application Level)&lt;/b&gt;은 우리가 작성한 &lt;b&gt;애플리케이션 코드&lt;/b&gt;에서 발생하는 예외를 의미합니다. 여기에는 파일 입출력 문제, 데이터베이스 접근 오류, 잘못된 연산 등 &lt;b&gt;로직이나 외부 자원과의 상호작용에서 발생하는 문제&lt;/b&gt;가 포함됩니다. 이와 같은 문제는 예외 처리를 통해 &lt;b&gt;애플리케이션이 안정적으로 실행될 수 있도록 복구&lt;/b&gt;할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.   Error와 Exception: 발생 시점과 성격에 따른 차이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java의 예외는 크게 &lt;b&gt;JVM 실행 수준에서 발생하는 시스템 오류&lt;/b&gt;와 &lt;b&gt;애플리케이션 수준에서 발생하는 오류&lt;/b&gt;로 구분할 수 있습니다. 이 차이에 따라 Error와 Exception으로 구분됩니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) Error 클래스 - JVM 레벨에서 발생하는 시스템 오류&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Error&lt;/b&gt;는 대부분 JVM이 Java 코드를 실행하는 중 &lt;b&gt;메모리 부족, 무한 재귀 호출, 시스템 하드웨어 문제&lt;/b&gt; 등과 같은 치명적인 문제를 만났을 때 발생합니다. Error는 시스템 차원에서 발생하는 문제이기 때문에 &lt;b&gt;대부분 복구가 불가능&lt;/b&gt;하며, &lt;b&gt;try-catch 블록으로 처리하지 않는 것이 일반적&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;: 실행 중 JVM이 심각한 문제를 만났을 때&lt;/li&gt;
&lt;li&gt;&lt;b&gt;복구 가능성&lt;/b&gt;: 대부분 복구 불가능, 단 일부 간접적 처리 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주요 Error 예시&lt;/b&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;code&gt;OutOfMemoryError&lt;/code&gt;&lt;/b&gt;: JVM이 사용할 수 있는 메모리가 부족할 때 발생
&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;b&gt;처리 가능성&lt;/b&gt;: &lt;code&gt;try-catch&lt;/code&gt;로 잡을 수는 있으나, 메모리를 해제하거나 최적화하는 등 근본적인 해결이 필요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;StackOverflowError&lt;/code&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;b&gt;처리 가능성&lt;/b&gt;: 종료 조건을 설정하여 무한 재귀 호출을 방지하고, 코드 자체를 수정하여 예방할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;InternalError&lt;/code&gt;&lt;/b&gt;: JVM 내부에서 예기치 않은 문제가 발생할 때 발생
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예: JVM 실행 중 예상하지 못한 하드웨어나 운영 체제의 문제가 발생한 경우&lt;/li&gt;
&lt;li&gt;&lt;b&gt;처리 가능성&lt;/b&gt;: 발생 원인이 JVM 내부 문제이기 때문에 애플리케이션 수준에서는 해결이 어렵습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;AssertionError&lt;/code&gt;&lt;/b&gt;: 코드 검증을 위해 사용된 &lt;code&gt;assert&lt;/code&gt; 구문이 실패할 때 발생
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예: 특정 조건이 거짓으로 판명될 때 &lt;code&gt;assert&lt;/code&gt;로 예외 상황을 확인하고 개발자가 이를 검토할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;처리 가능성&lt;/b&gt;: 주로 디버깅 과정에서 사용되며, &lt;code&gt;assert&lt;/code&gt; 구문을 통해 테스트 중인 조건이 만족되지 않을 때 발생합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) Exception 클래스 - 애플리케이션 레벨에서 발생하는 오류&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Exception&lt;/b&gt;은 애플리케이션이 동작 중 발생할 수 있는 오류를 의미하며, 보통 &lt;b&gt;적절한 예외 처리를 통해 복구 가능&lt;/b&gt;합니다. Exception은 다시 &lt;b&gt;Checked Exception&lt;/b&gt;과 &lt;b&gt;Unchecked Exception (Runtime Exception)&lt;/b&gt;으로 나뉩니다. 각각의 예외는 &lt;b&gt;발생 시점과 상황에 따라 다르게 처리&lt;/b&gt;됩니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.   Checked Exception vs Unchecked Exception: 예외 발생 시점과 구체적인 예시&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) Checked Exception (컴파일 시점에서 발생)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Checked Exception&lt;/b&gt;은 &lt;b&gt;외부 자원과의 상호작용 중 발생할 가능성이 높은 예외&lt;/b&gt;로, 예외 처리가 강제됩니다. 컴파일러는 Checked Exception이 발생할 가능성이 있는 코드를 &lt;code&gt;try-catch&lt;/code&gt;로 감싸거나 &lt;code&gt;throws&lt;/code&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;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;주요 Checked Exception 예시&lt;/b&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;code&gt;IOException&lt;/code&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;li&gt;&lt;b&gt;&lt;code&gt;SQLException&lt;/code&gt;&lt;/b&gt;: 데이터베이스와 연결 중 발생하는 예외
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예: 데이터베이스 연결 오류, SQL 구문 오류 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;ClassNotFoundException&lt;/code&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;code&gt;Class.forName(&quot;ClassName&quot;)&lt;/code&gt;으로 클래스를 찾으려 했으나 클래스가 존재하지 않는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;InterruptedException&lt;/code&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;code&gt;Thread.sleep()&lt;/code&gt; 중에 다른 스레드가 인터럽트를 발생시키는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) Unchecked Exception (Runtime Exception, 실행 시점에서 발생)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Unchecked Exception&lt;/b&gt;, 즉 &lt;b&gt;RuntimeException&lt;/b&gt;은 &lt;b&gt;실행 중 발생하는 예외&lt;/b&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;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;주요 Unchecked Exception 예시&lt;/b&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;code&gt;NullPointerException&lt;/code&gt;&lt;/b&gt;: &lt;code&gt;null&lt;/code&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;li&gt;&lt;b&gt;&lt;code&gt;ArrayIndexOutOfBoundsException&lt;/code&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;li&gt;&lt;b&gt;&lt;code&gt;ArithmeticException&lt;/code&gt;&lt;/b&gt;: 잘못된 산술 연산을 시도할 때
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예: 0으로 나누기를 시도하는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;IllegalArgumentException&lt;/code&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;li&gt;&lt;b&gt;&lt;code&gt;ClassCastException&lt;/code&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;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) Unchecked Exception과 RuntimeException의 관계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java에서 &lt;b&gt;RuntimeException&lt;/b&gt;은 모든 Unchecked Exception의 상위 클래스입니다. &lt;code&gt;RuntimeException&lt;/code&gt;을 상속하는 예외 클래스들은 모두 Unchecked Exception에 해당하므로, &lt;b&gt;&lt;code&gt;RuntimeException&lt;/code&gt;으로 선언하면 모든 Unchecked Exception을 포괄&lt;/b&gt;할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  RuntimeException이 포괄하는 예외들&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;NullPointerException&lt;/b&gt;: null 참조가 발생할 때&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ArrayIndexOutOfBoundsException&lt;/b&gt;: 배열의 유효하지 않은 인덱스를 참조할 때&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ArithmeticException&lt;/b&gt;: 잘못된 산술 연산(예: 0으로 나누기)을 시도할 때&lt;/li&gt;
&lt;li&gt;&lt;b&gt;IllegalArgumentException&lt;/b&gt;: 메서드에 부적절한 인수가 전달될 때&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ClassCastException&lt;/b&gt;: 잘못된 타입으로 객체를 캐스팅할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 &lt;b&gt;모든 Unchecked Exception은 RuntimeException을 상속받아&lt;/b&gt; 이루어지기 때문에 &lt;code&gt;catch (RuntimeException e)&lt;/code&gt; 구문으로 Unchecked Exception을 포괄적으로 처리할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4) Checked Exception을 포괄하는 상위 클래스는 존재하지 않음&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Checked Exception&lt;/b&gt;은 컴파일러가 예외 처리를 강제하는 예외들이기 때문에, Checked Exception 전체를 포괄하는 상위 클래스는 없습니다. &lt;b&gt;Java는 Checked Exception을 반드시 구체적으로 명시하도록 설계&lt;/b&gt;했기 때문에, Checked Exception의 최상위 클래스를 만들지 않았습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  왜 Checked Exception은 포괄하는 클래스가 없을까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java는 &lt;b&gt;Checked Exception이 발생할 가능성이 높은 코드에서 예외를 명확하게 처리하도록 강제&lt;/b&gt;하는 언어입니다. 예를 들어, 파일 입출력, 데이터베이스 접근, 네트워크 연결 등에서 발생할 수 있는 예외들은 각각 &lt;code&gt;IOException&lt;/code&gt;, &lt;code&gt;SQLException&lt;/code&gt; 등으로 구체화되어 있고, 개발자는 특정 예외에 맞는 처리를 하도록 요구받습니다. 이를 통해 코드의 안정성과 예외 처리의 구체성을 높일 수 있죠.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 모든 Checked Exception을 한꺼번에 잡아 처리할 수 있는 상위 클래스는 없으며, 개발자가 &lt;b&gt;필요한 Checked Exception만 구체적으로 처리&lt;/b&gt;하도록 디자인되어 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5)   모든 예외를 포괄하고 싶을 때: Exception 사용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 예외 상황을 한 번에 포괄하고 싶을 때는 &lt;b&gt;Exception&lt;/b&gt; 클래스를 사용할 수 있습니다. &lt;b&gt;Exception&lt;/b&gt; 클래스는 &lt;code&gt;Throwable&lt;/code&gt; 하위의 모든 Checked Exception과 Unchecked Exception의 부모 클래스이므로, 모든 예외를 포괄적으로 처리할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;php&quot;&gt;&lt;code&gt;try {
    // 예외가 발생할 가능성이 있는 코드
} catch (Exception e) {
    // 모든 Checked 및 Unchecked 예외를 포괄하는 처리
}&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;b&gt;구체적인 예외 처리의 장점을 잃어버릴 수 있으므로 주의해서 사용&lt;/b&gt;해야 합니다. 상황에 맞는 예외를 구체적으로 처리하는 것이 보통 더 권장됩니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.   예외 처리 방법: try-catch-finally, throw, throws&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java에서는 예외를 효율적으로 관리하기 위해 다양한 예외 처리 구문을 제공합니다. 각 구문에 대해 살펴보겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  try-catch-finally 구문&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;code&gt;try-catch&lt;/code&gt;&lt;/b&gt; 구문은 예외가 발생할 가능성이 있는 코드를 &lt;code&gt;try&lt;/code&gt; 블록에 작성하고, 특정 예외가 발생했을 때 &lt;code&gt;catch&lt;/code&gt; 블록에서 이를 처리하는 기본 구문입니다. &lt;code&gt;finally&lt;/code&gt; 블록은 예외 발생 여부와 관계없이 항상 실행되며, 주로 리소스를 정리하는 데 사용됩니다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;try {
  // 예외 발생 가능성이 있는 코드
} catch (ExceptionType e) {
  // 예외 발생 시 처리 코드
} finally {
  // 예외 발생 여부와 관계없이 항상 실행됨
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  throw와 throws 키워드&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;code&gt;throw&lt;/code&gt;&lt;/b&gt;: 특정 예외를 강제로 발생시킬 때 사용합니다. 예를 들어, 잘못된 사용자 입력값이 들어왔을 때 &lt;code&gt;throw new IllegalArgumentException(&quot;Invalid input&quot;)&lt;/code&gt;과 같이 사용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;throws&lt;/code&gt;&lt;/b&gt;: 메서드 선언부에서 사용되며, 메서드가 처리하지 않고 호출한 쪽으로 예외를 전달할 때 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;public void someMethod() throws IOException {
  // 메서드 내에서 IOException 발생 가능성 있음
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.   예외 처리의 중요성: 프로그램 안정성 및 유지보수성 강화&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java의 예외 처리는 단순히 오류를 해결하기 위한 도구가 아니라, 프로그램이 예기치 않은 상황에서도 안정적으로 동작할 수 있도록 합니다. 예외 처리를 통해 외부 자원 문제, 사용자 입력 오류 등을 효과적으로 관리하여 &lt;b&gt;시스템 다운타임을 최소화&lt;/b&gt;할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Java 예외 처리 요약&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Error&lt;/b&gt;: 시스템 수준에서 발생하며, 보통 복구가 불가능합니다.&lt;br /&gt;&lt;b&gt;Checked Exception&lt;/b&gt;: 컴파일 시점에서 발생 가능성이 감지되며, 예외 처리가 강제됩니다.&lt;br /&gt;&lt;b&gt;Unchecked Exception (Runtime Exception)&lt;/b&gt;: 실행 시점에 발생하며, 개발자가 코드 자체를 수정해 해결할 수 있습니다.&lt;br /&gt;&lt;b&gt;try-catch-finally&lt;/b&gt; 구문을 사용해 예외를 효과적으로 관리하고 &lt;b&gt;throw&lt;/b&gt;와 &lt;b&gt;throws&lt;/b&gt;로 예외를 전달하거나 강제로 발생시킬 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 내용으로 Java 예외 처리의 개념, 구조, 종류와 처리 방법을 종합적으로 설명해드렸습니다.   각 예외 유형과 그에 따른 처리를 통해 더욱 안정적인 프로그램을 작성할 수 있습니다!&lt;/p&gt;</description>
      <category>Java</category>
      <category>checked exception</category>
      <category>error</category>
      <category>exception</category>
      <category>java exception</category>
      <category>runtime exception</category>
      <category>Throw</category>
      <category>try-catch</category>
      <category>unchecked exception</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/273</guid>
      <comments>https://tae-hui.tistory.com/entry/JAVA#entry273comment</comments>
      <pubDate>Fri, 15 Nov 2024 11:36:32 +0900</pubDate>
    </item>
    <item>
      <title>WSL(Windows Subsystem for Linux)에서 SSH 설정하기</title>
      <link>https://tae-hui.tistory.com/entry/WSLWindows-Subsystem-for-Linux%EC%97%90%EC%84%9C-SSH-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 포스팅에서 다뤘던 &lt;code&gt;[&lt;a style=&quot;background-color: #e6f5ff; color: #0070d1; text-align: start;&quot; href=&quot;https://tae-hui.tistory.com/entry/WSLWindows-Subsystem-for-Linux-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%84%A4%EC%A0%95&quot;&gt;WSL(Windows Subsystem for Linux) 설치 및 설정&lt;/a&gt;]&lt;/code&gt; 내용을 참고하시면, 이번 주제에 대한 배경 지식이나 기초 정보를 확인하실 수 있습니다!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure id=&quot;og_1731577399216&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;WSL(Windows Subsystem for Linux) 설치 및 설정&quot; data-og-description=&quot;1.   WSL(Windows Subsystem for Linux) 설치 및 설정: 우분투 기반 사용하기WSL은 윈도우에서 리눅스 환경을 간편하게 사용할 수 있도록 해줍니다. 여기서는 Ubuntu 배포판을 설치하는 과정과 고정 IP 설정 &quot; data-og-host=&quot;tae-hui.tistory.com&quot; data-og-source-url=&quot;https://tae-hui.tistory.com/entry/WSLWindows-Subsystem-for-Linux-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%84%A4%EC%A0%95&quot; data-og-url=&quot;https://tae-hui.tistory.com/entry/WSLWindows-Subsystem-for-Linux-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%84%A4%EC%A0%95&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bKnoms/hyXwp2KML6/Dph7J9cnZ0nXdOJpkkUTq0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/boDVRO/hyXzWkg7vf/UGXk4ytBxuRicrWkMKUTIk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://tae-hui.tistory.com/entry/WSLWindows-Subsystem-for-Linux-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%84%A4%EC%A0%95&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://tae-hui.tistory.com/entry/WSLWindows-Subsystem-for-Linux-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%84%A4%EC%A0%95&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bKnoms/hyXwp2KML6/Dph7J9cnZ0nXdOJpkkUTq0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/boDVRO/hyXzWkg7vf/UGXk4ytBxuRicrWkMKUTIk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;WSL(Windows Subsystem for Linux) 설치 및 설정&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;1.   WSL(Windows Subsystem for Linux) 설치 및 설정: 우분투 기반 사용하기WSL은 윈도우에서 리눅스 환경을 간편하게 사용할 수 있도록 해줍니다. 여기서는 Ubuntu 배포판을 설치하는 과정과 고정 IP 설정&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;tae-hui.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️&amp;nbsp; WSL에서 SSH 설정하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WSL에서 SSH를 사용하려면 SSH 서버를 활성화하고, SSH를 통해 외부에서 접속 가능하도록 설정해야 합니다. 아래 단계에 따라 설정하면 됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  1. SSH 설치 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WSL에서 SSH를 사용하려면 SSH 서버가 설치되어 있어야 합니다. Ubuntu 등 WSL 배포판을 사용하고 있다면, 다음 명령어로 SSH 설치 여부를 확인합니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;sudo apt update &amp;amp;&amp;amp; sudo apt install openssh-server -y&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  2. SSH 서버 포트 확인하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 SSH 서버는 22번 포트를 사용합니다. SSH 서버가 올바르게 설치된 후에는 &lt;code&gt;ss&lt;/code&gt; 명령어로 포트 상태를 확인할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;sudo ss -tuln | grep 22&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 명령어는 현재 열려 있는 포트를 확인하고, 22번 포트가 열려 있는지 확인해줍니다. 만약 이 명령어의 출력에 22번 포트가 표시된다면, SSH 서버가 올바르게 작동 중이라는 의미입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  3. SSH 서버 상태 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 SSH 서버가 실행 중인지 확인해야 합니다. 다음 명령어를 사용하면 SSH 서버의 현재 상태를 알 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo service ssh status&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상태가 &lt;b&gt;active(running)&lt;/b&gt;으로 표시된다면, SSH 서버가 정상적으로 실행 중인겁니다.&lt;/li&gt;
&lt;li&gt;만약 &lt;b&gt;inactive&lt;/b&gt;이나 &lt;b&gt;stopped&lt;/b&gt; 상태로 표시되면 SSH 서버를 시작해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;▶️ 4. SSH 서버 시작하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSH 서버가 비활성화 상태라면 다음 명령어로 시작할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo service ssh start&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 SSH 서버가 실행되었고, 외부에서 접근할 수 있는 상태가 되었습니다. 그러나, 기본 설정으로는 &lt;b&gt;비밀번호 인증&lt;/b&gt;이 비활성화되어 있을 수 있습니다. 이를 활성화해야 외부에서 비밀번호를 사용해 접속할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  5. 비밀번호 인증 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSH 서버에서 비밀번호 인증을 활성화하려면 SSH 설정 파일을 수정해야 합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;SSH 설정 파일을 엽니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;sudo vi /etc/ssh/sshd_config&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;파일을 열었다면, &lt;b&gt;&lt;code&gt;PasswordAuthentication&lt;/code&gt; 옵션&lt;/b&gt;을 찾고, 해당 라인을 수정합니다.&lt;/li&gt;
&lt;/ol&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;code&gt;PasswordAuthentication&lt;/code&gt; 값을 &lt;code&gt;yes&lt;/code&gt;로 설정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;PasswordAuthentication yes&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;3&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;설정을 변경했으면 파일을 저장하고 종료합니다. &lt;b&gt;vi 편집기&lt;/b&gt;에서 저장 후 종료하려면, &lt;code&gt;Esc&lt;/code&gt; 키를 누른 후 &lt;code&gt;:wq&lt;/code&gt;를 입력.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  6. SSH 서버 재시작&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정 변경 사항을 적용하려면 SSH 서버를 다시 시작해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo service ssh restart&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 SSH 서버가 비밀번호 인증을 허용하며, 외부에서 접속 가능해졌습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  7. WSL 실행 시 SSH 서버 자동 시작 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WSL이 시작될 때마다 SSH 서버를 자동으로 시작하려면 &lt;b&gt;&lt;code&gt;~/.bashrc&lt;/code&gt; 파일&lt;/b&gt;에 SSH 서버 시작 명령을 추가해야 합니다.&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;code&gt;~/.bashrc&lt;/code&gt; 파일&lt;/b&gt;을 엽니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;vi ~/.bashrc&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;파일 맨 아래에 다음 줄을 추가합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo service ssh start&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 명령어는 WSL이 실행될 때마다 SSH 서버를 자동으로 시작합니다.&lt;/li&gt;
&lt;li&gt;파일을 저장하고 나가려면 &lt;code&gt;Esc&lt;/code&gt;를 누르고 &lt;code&gt;:wq&lt;/code&gt;를 입력합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;3&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;추가 설정을 적용하려면 터미널을 새로 열거나 아래 명령어로 &lt;code&gt;.bashrc&lt;/code&gt; 파일을 다시 로드합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;source ~/.bashrc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 WSL을 실행할 때마다 자동으로 SSH 서버가 시작됩니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  8. SSH로 접속 테스트하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정을 마쳤다면, 이제 원격에서 접속을 테스트할 수 있습니다. SSH 클라이언트가 있는 다른 장치에서 다음 명령어를 사용하여 접속합니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;ssh username@server-ip&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령어에서 &lt;code&gt;username&lt;/code&gt;은 WSL 환경의 사용자 이름으로, &lt;code&gt;server-ip&lt;/code&gt;는 WSL이 설치된 장치의 IP 주소로 교체합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&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;로컬 네트워크 외부에서 SSH 접속을 시도할 경우, 라우터에서 포트 포워딩 설정이 필요할 수 있습니다.&lt;br /&gt;SSH 보안을 위해 추가적으로 방화벽 설정을 검토하세요.&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>IT 지식</category>
      <category>ssh</category>
      <category>ubuntu</category>
      <category>ubuntu wsl</category>
      <category>ubuntu 원격 접속</category>
      <category>WSL</category>
      <category>wsl ssh</category>
      <category>wsl 원격접속</category>
      <category>오블완</category>
      <category>원격접속</category>
      <category>티스토리챌린지</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/272</guid>
      <comments>https://tae-hui.tistory.com/entry/WSLWindows-Subsystem-for-Linux%EC%97%90%EC%84%9C-SSH-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0#entry272comment</comments>
      <pubDate>Thu, 14 Nov 2024 18:56:21 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] Java에서 예외 처리방법</title>
      <link>https://tae-hui.tistory.com/entry/JAVA-Java%EC%97%90%EC%84%9C-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC%EB%B0%A9%EB%B2%95</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  1. Java에서 예외 처리방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바에서 예외 처리는 프로그램의 오류를 안전하게 처리하고 앱의 안정성을 유지하는 중요한 개념입니다. 예외는 예상치 못한 상황에서 발생할 수 있는 오류들을 지칭하는데, 자바에서는 &lt;code&gt;try-catch-finally&lt;/code&gt; 구문, &lt;code&gt;throws&lt;/code&gt; 키워드, 그리고 &lt;b&gt;커스텀 예외&lt;/b&gt;를 통해 다양한 예외 상황을 처리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 포스트에서는 자바에서 예외를 처리하는 방법을 &lt;b&gt;세부적으로 설명&lt;/b&gt;하고, &lt;b&gt;예외 처리와 트랜잭션 처리의 관계&lt;/b&gt;를 알아본 뒤, &lt;b&gt;커스텀 예외&lt;/b&gt;를 만드는 방법까지 상세히 설명하겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; &amp;zwj;♂️ 2. 자바에서 예외 처리하는 주요 방법&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.1 &lt;code&gt;try-catch&lt;/code&gt; 구문 사용하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;try-catch&lt;/code&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;try&lt;/b&gt;: 예외가 발생할 가능성이 있는 코드를 감싸는 블록입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;catch&lt;/b&gt;: 특정 예외가 발생했을 때 실행되는 블록으로, 예외 종류에 따라 여러 개를 작성할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;finally&lt;/b&gt;: 예외 발생 여부와 관계없이 항상 실행되는 블록으로, 주로 자원을 해제할 때 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;try {
    // 예외가 발생할 가능성이 있는 코드
} catch (ExceptionType e) {
    // 예외 처리 코드
} finally {
    // 예외 발생 여부와 상관없이 실행되는 코드
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.2 &lt;code&gt;throws&lt;/code&gt; 키워드 사용하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메소드에서 특정 예외를 처리하지 않고 호출한 쪽에 넘길 때 사용합니다. 이를 &lt;b&gt;예외 전가&lt;/b&gt;라고 하며, 메소드 선언부에 &lt;code&gt;throws&lt;/code&gt; 키워드를 붙여 선언합니다.&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;public void exampleMethod() throws IOException {
    // 예외 발생 가능성이 있는 코드
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 호출자가 예외를 직접 처리하도록 책임을 넘기기 때문에 예외가 발생한 상황에서 유연하게 대응할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  3. 트랜잭션과 예외 처리의 관계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜잭션은 데이터베이스에서 하나의 작업 단위로, &lt;b&gt;모든 작업이 성공적으로 완료되거나, 하나라도 실패하면 전체를 롤백&lt;/b&gt;하는 특징이 있습니다. Java와 스프링에서는 &lt;code&gt;@Transactional&lt;/code&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;h4 data-ke-size=&quot;size20&quot;&gt;3.1 기본적인 트랜잭션 롤백 규칙&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Unchecked Exception&lt;/b&gt; (런타임 예외): &lt;code&gt;RuntimeException&lt;/code&gt;과 그 하위 예외들은 기본적으로 트랜잭션을 롤백합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Checked Exception&lt;/b&gt;: &lt;code&gt;Exception&lt;/code&gt;을 상속한 예외는 트랜잭션을 롤백하지 않으며, 필요 시 직접 설정이 필요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링에서는 이러한 규칙을 이해하고 @Transactional 애너테이션의 &lt;code&gt;rollbackFor&lt;/code&gt; 속성을 사용해 &lt;b&gt;롤백할 예외 유형을 지정&lt;/b&gt;할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;@Transactional(rollbackFor = Exception.class)
public void someMethod() throws Exception {
    // 트랜잭션 관리 코드
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; &amp;zwj;♂️ 4. try-catch를 사용할 때 트랜잭션이 롤백되지 않는 이유&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링의 트랜잭션 처리는 &lt;b&gt;프록시(Proxy) 패턴&lt;/b&gt;을 통해 이루어집니다. &lt;code&gt;@Transactional&lt;/code&gt;이 적용된 메소드에서 예외가 발생하면 프록시는 해당 예외를 감지해 트랜잭션을 롤백하게 됩니다. 하지만 &lt;code&gt;try-catch&lt;/code&gt; 구문으로 예외를 잡아내면, 스프링은 예외가 발생했는지 인지하지 못하고 트랜잭션을 &lt;b&gt;정상 종료(커밋)&lt;/b&gt; 시키게 됩니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시 코드&lt;/h4&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;@Transactional
public void processTransaction() {
    try {
        // 예외가 발생할 수 있는 코드
    } catch (Exception e) {
        // 예외를 잡아버려서 트랜잭션 롤백이 되지 않음
        System.out.println(&quot;Exception caught: &quot; + e.getMessage());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드에서는 예외가 발생하더라도 &lt;code&gt;catch&lt;/code&gt;에서 처리해버리면 트랜잭션이 자동으로 롤백되지 않습니다. 이를 해결하기 위해 &lt;b&gt;&lt;code&gt;catch&lt;/code&gt; 블록에서 &lt;code&gt;RuntimeException&lt;/code&gt;으로 다시 예외를 던지거나 &lt;code&gt;throws&lt;/code&gt;를 사용해 상위 메소드로 예외를 전파&lt;/b&gt;할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;@Transactional
public void processTransaction() {
    try {
        // 예외가 발생할 수 있는 코드
    } catch (Exception e) {
        System.out.println(&quot;Exception caught: &quot; + e.getMessage());
        throw new RuntimeException(e); // 예외를 다시 던져 트랜잭션 롤백 유도
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  5. 커스텀 예외 만들기와 글로벌 예외 처리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바에서는 사용자 정의 예외(커스텀 예외)를 만들어 특정 상황에서 발생하는 예외를 명확하게 구분할 수 있습니다. 커스텀 예외는 &lt;b&gt;Exception&lt;/b&gt; 클래스를 상속하거나, 더 넓은 범위에서 잡기 위해 &lt;b&gt;RuntimeException&lt;/b&gt;을 상속받아 만들 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5.1 커스텀 예외 만들기 예시&lt;/h4&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;public class CustomException extends RuntimeException {
    public CustomException(String message) {
        super(message);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 만든 커스텀 예외는 필요할 때마다 &lt;code&gt;throw&lt;/code&gt;로 발생시킬 수 있으며, 예외 메시지나 추가 정보를 전달해 디버깅에 도움이 됩니다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;public void riskyMethod() {
    if (/* 특정 조건 */) {
        throw new CustomException(&quot;커스텀 예외 발생: 특정 조건을 만족하지 않음&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5.2 커스텀 예외와 글로벌 예외 처리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링에서는 &lt;code&gt;@ControllerAdvice&lt;/code&gt;와 &lt;code&gt;@ExceptionHandler&lt;/code&gt;를 통해 &lt;b&gt;글로벌 예외 처리&lt;/b&gt;를 할 수 있어요. 글로벌 예외 처리를 사용하면 특정 예외가 발생했을 때 일관된 응답을 반환할 수 있고, 모든 컨트롤러에 공통적으로 적용됩니다.&lt;/p&gt;
&lt;h5&gt;예시 코드&lt;/h5&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(CustomException.class)
    public ResponseEntity&amp;lt;String&amp;gt; handleCustomException(CustomException ex) {
        return new ResponseEntity&amp;lt;&amp;gt;(ex.getMessage(), HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity&amp;lt;String&amp;gt; handleGeneralException(Exception ex) {
        return new ResponseEntity&amp;lt;&amp;gt;(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 &lt;code&gt;GlobalExceptionHandler&lt;/code&gt; 클래스는 커스텀 예외인 &lt;code&gt;CustomException&lt;/code&gt;이 발생하면 일관된 메시지와 상태 코드를 클라이언트에 반환하는 역할을 합니다. 이런 글로벌 예외 처리 덕분에 &lt;b&gt;코드의 일관성&lt;/b&gt;을 유지하고 &lt;b&gt;중복된 예외 처리 코드&lt;/b&gt;를 줄일 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5.3 커스텀 예외와 트랜잭션 롤백 설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커스텀 예외가 발생했을 때 트랜잭션이 롤백되길 원한다면, 커스텀 예외를 &lt;b&gt;RuntimeException&lt;/b&gt;으로 상속하면 기본적으로 롤백됩니다. 만약 그렇지 않다면, &lt;code&gt;@Transactional&lt;/code&gt; 애너테이션의 &lt;code&gt;rollbackFor&lt;/code&gt; 속성을 설정해 강제로 롤백할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;@Transactional(rollbackFor = CustomException.class)
public void someTransactionalMethod() throws CustomException {
    throw new CustomException(&quot;커스텀 예외 발생으로 인한 롤백&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  6. 예외 처리 흐름 요약&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;예외 발생&lt;/b&gt;: 예외가 발생하면 JVM은 이를 인식하고 예외 객체를 생성합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예외 처리 탐색&lt;/b&gt;: JVM은 &lt;code&gt;try-catch&lt;/code&gt; 블록을 찾고, 일치하는 &lt;code&gt;catch&lt;/code&gt; 블록이 있는지 확인합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예외 처리 수행&lt;/b&gt;: 해당 예외와 일치하는 &lt;code&gt;catch&lt;/code&gt; 블록이 있으면, 그 안의 코드가 실행되고 예외는 처리됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;finally 블록 실행&lt;/b&gt;: &lt;code&gt;finally&lt;/code&gt; 블록이 있으면 무조건 실행됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;트랜잭션 롤백 여부 판단&lt;/b&gt;: 예외가 &lt;code&gt;RuntimeException&lt;/code&gt; 또는 &lt;code&gt;Error&lt;/code&gt;이면 기본적으로 트랜잭션이 롤백됩니다. 그렇지 않으면 설정에 따라 다르게 동작합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;요약&lt;/b&gt;&lt;br /&gt;자바의 예외 처리는 주로 &lt;code&gt;try-catch&lt;/code&gt;, &lt;code&gt;throws&lt;/code&gt;, 그리고 커스텀 예외를 통해 관리합니다. 트랜잭션은 &lt;code&gt;RuntimeException&lt;/code&gt;에 의해 롤백되며, 커스텀 예외의 경우도 상속된 클래스에 따라 롤백 여부가 결정됩니다. 또한, &lt;b&gt;글로벌 예외 처리&lt;/b&gt;를 통해 일관된 예외 응답을 반환하고 코드 중복을 줄일 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>Java</category>
      <category>exception</category>
      <category>Java</category>
      <category>runtimeexeption</category>
      <category>throws</category>
      <category>transactional</category>
      <category>try-catch</category>
      <category>오블완</category>
      <category>자바</category>
      <category>자바 예외처리</category>
      <category>티스토리챌린지</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/270</guid>
      <comments>https://tae-hui.tistory.com/entry/JAVA-Java%EC%97%90%EC%84%9C-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC%EB%B0%A9%EB%B2%95#entry270comment</comments>
      <pubDate>Wed, 13 Nov 2024 21:37:44 +0900</pubDate>
    </item>
    <item>
      <title>[Kubernetes] 쿠버네티스(Kubernetes)란 무엇인가</title>
      <link>https://tae-hui.tistory.com/entry/Kubernetes-%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4Kubernetes%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ 1. 쿠버네티스(Kubernetes)란 무엇인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버네티스(Kubernetes)는 &lt;b&gt;컨테이너화된 애플리케이션을 자동으로 배포, 관리 및 확장&lt;/b&gt;하는 오픈 소스 플랫폼입니다. 쿠버네티스는 &lt;b&gt;Google에서 개발한 컨테이너 오케스트레이션 툴&lt;/b&gt;로 시작되었고, 현재는 CNCF(Cloud Native Computing Foundation)에서 관리하고 있습니다. 쿠버네티스는 특히 &lt;b&gt;대규모의 분산 시스템&lt;/b&gt;에서 뛰어난 확장성과 자동화 기능을 제공하는데, 이 덕분에 &lt;b&gt;클라우드 환경에서 애플리케이션을 쉽게 관리&lt;/b&gt;할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✨ 쿠버네티스의 핵심 개념&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버네티스를 이해하려면, 주요 개념 몇 가지를 알아야 합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  Pod&lt;/h4&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;각 Pod는 고유 IP를 가지며, 같은 Pod 안의 컨테이너들은 네트워크와 스토리지를 공유합니다.&lt;/li&gt;
&lt;li&gt;Pod는 하나 이상의 컨테이너를 포함할 수 있지만, 대부분의 경우 하나의 컨테이너만 포함합니다. Pod는 쿠버네티스에서 논리적 단위로, 실제로 컨테이너는 Docker와 같은 컨테이너 런타임에 의해 실행됩니다.&lt;/li&gt;
&lt;li&gt;Pod 내부의 컨테이너들은 네트워크 네임스페이스를 공유하기 때문에 같은 IP를 사용하여 서로 통신할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  Node&lt;/h4&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;각 Node는 여러 Pod를 포함할 수 있으며, Node가 여러 개 모여 클러스터를 이룹니다.&lt;/li&gt;
&lt;li&gt;Node에는 &lt;b&gt;kubelet&lt;/b&gt;이라는 에이전트가 실행되어 쿠버네티스의 API 서버와 통신하며, 해당 Node에서 Pod를 실행하고 관리합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  Cluster&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;여러 Node가 모여 구성하는 하나의 큰 시스템&lt;/b&gt;입니다. 쿠버네티스가 클러스터 전체를 관리하며, 애플리케이션의 확장, 자원 할당, 장애 복구 등을 담당합니다.&lt;/li&gt;
&lt;li&gt;클러스터는 여러 Node를 네트워크로 연결하여 구성되며, 각 Node는 물리적으로 분산되어 있을 수 있지만, 네트워크를 통해 하나의 클러스터로 관리됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  ReplicaSet&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 Pod의 &lt;b&gt;복제본 수를 지정&lt;/b&gt;하여 항상 원하는 개수를 유지하게 해주는 오브젝트입니다.&lt;/li&gt;
&lt;li&gt;예를 들어, Pod 인스턴스를 3개로 설정하면, 한 인스턴스가 죽더라도 자동으로 새 인스턴스를 생성해 3개를 유지합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  Deployment&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Deployment는 &lt;b&gt;애플리케이션의 버전 관리와 롤백&lt;/b&gt;을 지원해줍니다. 예를 들어 애플리케이션을 배포하다가 문제가 발생하면 손쉽게 이전 버전으로 되돌릴 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ 2. 쿠버네티스의 주요 기능과 특징&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  확장성 (Scalability)&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;Pod의 수를 늘려 쉽게 확장&lt;/b&gt;할 수 있습니다. 반대로 사용량이 적을 때는 자원을 절약할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  3. 도커(Docker)와 쿠버네티스의 관계&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Docker란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker는 &lt;b&gt;컨테이너화된 애플리케이션을 만드는 도구&lt;/b&gt;입니다. &lt;b&gt;개발 환경에서부터 배포 환경까지 일관된 환경을 제공&lt;/b&gt;해주는 도구로, 컨테이너 이미지라는 패키지를 통해 애플리케이션을 환경에 구애받지 않고 실행할 수 있게 해줍니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Docker와 Kubernetes의 관계&lt;/h3&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;b&gt;도커&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;컨테이너를 실행하는 역할&lt;/b&gt;을 한다면, 쿠버네티스는 &lt;b&gt;여러 개의 컨테이너를 하나의 시스템으로서 관리하는 역할&lt;/b&gt;을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버네티스는 도커 외에도 &lt;b&gt;containerd&lt;/b&gt;나 &lt;b&gt;CRI-O&lt;/b&gt;와 같은 다른 컨테이너 런타임을 사용할 수 있습니다. Pod는 쿠버네티스에서 논리적 단위로, Docker를 포함한 다양한 컨테이너 런타임에 의해 실행됩니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ 4. 쿠버네티스 기본 구성 요소와 실습 예제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버네티스를 이해하기 위해 실제 예제를 통해 살펴보겠습니다. 이번 예제에서는 간단한 &lt;b&gt;Nginx 서버를 배포&lt;/b&gt;하는 과정을 보여드리겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Step 1: 쿠버네티스 클러스터 생성&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;미니쿠베(Minikube)&lt;/b&gt;를 사용하여 로컬에서 쿠버네티스 클러스터를 생성할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;minikube start&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Step 2: Nginx Pod 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 &lt;code&gt;nginx-pod.yaml&lt;/code&gt; 파일을 만들어서 Nginx 컨테이너를 실행해보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
  - name: nginx
    image: nginx:latest
    ports:
    - containerPort: 80&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일을 만든 후 다음 명령어로 Pod를 생성합니다.&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;kubectl apply -f nginx-pod.yaml&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Step 3: Pod 상태 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pod가 잘 생성되었는지 확인하려면 &lt;code&gt;kubectl get pods&lt;/code&gt; 명령어를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;kubectl get pods&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Step 4: Deployment 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Deployment를 사용하여 Nginx 애플리케이션을 배포해 보겠습니다. &lt;code&gt;nginx-deployment.yaml&lt;/code&gt; 파일을 작성합니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작성한 파일을 배포합니다.&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;kubectl apply -f nginx-deployment.yaml&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Step 5: 서비스 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부에서 Nginx 서버에 접근할 수 있도록 &lt;b&gt;Service&lt;/b&gt;를 설정합니다. &lt;code&gt;nginx-service.yaml&lt;/code&gt; 파일을 작성합니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  type: NodePort
  selector:
    app: nginx
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 명령어로 서비스를 생성합니다.&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;kubectl apply -f nginx-service.yaml&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Step 6: 서비스 접근&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로, 생성한 서비스의 NodePort를 통해 Nginx 애플리케이션에 접근할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;minikube service nginx-service&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&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;/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;확장성&lt;/b&gt;: 대규모 애플리케이션 관리에 유리하여 유연한 확장이 가능합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버네티스는 여러 Node를 네트워크로 연결하여 관리합니다. 각 Node는 물리적으로 분산되어 있을 수 있지만, 네트워크를 통해 하나의 클러스터로 구성됩니다. &lt;b&gt;컨트롤 플레인&lt;/b&gt;은 클러스터 전체를 관리하며, &lt;b&gt;API 서버&lt;/b&gt;는 각 Node와 통신하여 Pod의 상태를 제어합니다. 각 Node의 &lt;b&gt;kubelet&lt;/b&gt;은 API 서버와 상호작용하며 Pod를 실행하고 상태를 모니터링합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버네티스는 &lt;b&gt;컨테이너화된 애플리케이션을 배포하고 관리하는 자동화된 플랫폼&lt;/b&gt;입니다. Docker와 함께 사용되며, &lt;b&gt;복잡한 환경에서의 효율적인 애플리케이션 관리&lt;/b&gt;를 가능하게 해줍니다. Pod, Node, Cluster 같은 기본 개념을 통해 확장성과 복원성을 갖춘 시스템을 구축할 수 있으며, &lt;code&gt;kubectl&lt;/code&gt; 명령어와 YAML 파일을 통해 다양한 설정을 할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;요약하자면&lt;/b&gt;: 쿠버네티스는 여러 컨테이너를 자동으로 관리하고 확장하는 플랫폼으로, 도커 컨테이너를 효율적으로 오케스트레이션하여 대규모 애플리케이션 운영을 지원합니다.&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>IT 지식</category>
      <category>docker</category>
      <category>k8s</category>
      <category>kube</category>
      <category>Kubernetes</category>
      <category>Node</category>
      <category>pod</category>
      <category>오블완</category>
      <category>쿠버네티스</category>
      <category>쿠베</category>
      <category>티스토리챌린지</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/269</guid>
      <comments>https://tae-hui.tistory.com/entry/Kubernetes-%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4Kubernetes%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80#entry269comment</comments>
      <pubDate>Tue, 12 Nov 2024 16:34:16 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] 프로젝트에서 PMD를 이용한 소스 품질 검사</title>
      <link>https://tae-hui.tistory.com/entry/JAVA-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90%EC%84%9C-PMD%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%86%8C%EC%8A%A4-%ED%92%88%EC%A7%88-%EA%B2%80%EC%82%AC</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ 프로젝트에서 PMD를 이용한 소스 품질 검사&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PMD는 Java와 같은 언어로 작성된 코드를 분석하여 &lt;b&gt;코드 스타일, 복잡성, 중복, 오류 가능성&lt;/b&gt; 등의 문제를 잡아주는 정적 코드 분석 도구입니다. 품질 좋은 코드를 유지하려면 특히 중요한데, PMD는 프로젝트 코드의 품질을 한층 더 높이는 데 큰 도움을 줍니다. 오늘은 PMD에 대해 알아보고, 설정 방법과 사용 예시를 자세히 설명하겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.   PMD란 무엇인가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;PMD&lt;/b&gt;(Programming Mistake Detector)는 오픈소스 정적 코드 분석 도구로, Java, JavaScript, XML 등을 포함한 여러 언어를 지원합니다. PMD는 코드에 대한 다양한 규칙(Rule)을 제공하여 &lt;b&gt;코드의 오류&lt;/b&gt;나 &lt;b&gt;중복 코드&lt;/b&gt; 등을 탐지합니다. 이를 통해 복잡성이나 코드 스타일 문제를 미리 발견하고, 유지 보수성을 높이는 데 큰 역할을 합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2.  ️ PMD의 주요 기능&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PMD는 아래와 같은 주요 기능을 제공합니다:&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;: 예를 들어, 클래스 이름이 CamelCase인지 확인하거나, 메소드가 너무 길지 않은지 검사합니다.&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;li&gt;&lt;b&gt;커스텀 규칙&lt;/b&gt; 작성 기능: 프로젝트 요구에 맞게 맞춤형 규칙을 만들 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.   PMD 설치하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PMD는 일반적으로 Gradle, Maven 등 빌드 도구에 플러그인으로 추가하여 사용합니다. 여기서는 &lt;b&gt;Gradle&lt;/b&gt;과 &lt;b&gt;Maven&lt;/b&gt; 설정 방법을 설명하겠습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Gradle에 PMD 추가하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 &lt;code&gt;build.gradle&lt;/code&gt; 파일에서 PMD 플러그인을 추가합니다.&lt;/p&gt;
&lt;pre class=&quot;gml&quot;&gt;&lt;code&gt;plugins {
    id 'java'
    id 'pmd'
}

pmd {
    toolVersion = '6.50.0'  // 원하는 버전 입력
    ignoreFailures = true   // 오류가 발생해도 빌드를 중단하지 않음
}

tasks.withType(Pmd) {
    reports {
        xml.required = true
        html.required = false
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PMD가 설치되면 &lt;code&gt;./gradlew pmdMain&lt;/code&gt; 명령어로 메인 소스에 대한 PMD 검사를 수행할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Maven에 PMD 추가하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;pom.xml&lt;/code&gt; 파일에 PMD 플러그인을 추가합니다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;build&amp;gt;
    &amp;lt;plugins&amp;gt;
        &amp;lt;plugin&amp;gt;
            &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;maven-pmd-plugin&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;3.15.0&amp;lt;/version&amp;gt; &amp;lt;!-- 원하는 버전 입력 --&amp;gt;
            &amp;lt;configuration&amp;gt;
                &amp;lt;linkXref&amp;gt;false&amp;lt;/linkXref&amp;gt;
                &amp;lt;printFailingErrors&amp;gt;true&amp;lt;/printFailingErrors&amp;gt;
            &amp;lt;/configuration&amp;gt;
            &amp;lt;executions&amp;gt;
                &amp;lt;execution&amp;gt;
                    &amp;lt;phase&amp;gt;verify&amp;lt;/phase&amp;gt;
                    &amp;lt;goals&amp;gt;
                        &amp;lt;goal&amp;gt;check&amp;lt;/goal&amp;gt;
                    &amp;lt;/goals&amp;gt;
                &amp;lt;/execution&amp;gt;
            &amp;lt;/executions&amp;gt;
        &amp;lt;/plugin&amp;gt;
    &amp;lt;/plugins&amp;gt;
&amp;lt;/build&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 &lt;code&gt;mvn pmd:check&lt;/code&gt; 명령어를 통해 PMD 검사를 실행할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.   PMD의 Rule 설정하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PMD는 &lt;code&gt;rulesets&lt;/code&gt;을 통해 코드 검사 규칙을 정의합니다. 기본 제공되는 rule을 사용할 수도 있고, 필요한 경우 직접 정의할 수도 있습니다. PMD가 제공하는 규칙에는 &lt;b&gt;Java Basic Rules, Code Style Rules, Design Rules&lt;/b&gt; 등이 있으며, 예를 들어 &lt;code&gt;rulesets/java/quickstart.xml&lt;/code&gt; 파일에서 규칙을 정의할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;ruleset name=&quot;My Custom Rules&quot; xmlns=&quot;http://pmd.sourceforge.net/ruleset/2.0.0&quot;&amp;gt;
    &amp;lt;description&amp;gt;Custom ruleset for my project.&amp;lt;/description&amp;gt;

    &amp;lt;!-- 기본 규칙 추가 --&amp;gt;
    &amp;lt;rule ref=&quot;category/java/bestpractices.xml&quot;/&amp;gt;
    &amp;lt;rule ref=&quot;category/java/codestyle.xml&quot;/&amp;gt;

    &amp;lt;!-- 복잡성 규칙 커스터마이징 --&amp;gt;
    &amp;lt;rule ref=&quot;category/java/design.xml/CyclomaticComplexity&quot;&amp;gt;
        &amp;lt;properties&amp;gt;
            &amp;lt;property name=&quot;max&quot; value=&quot;10&quot;/&amp;gt; &amp;lt;!-- 복잡도가 10 이상이면 경고 --&amp;gt;
        &amp;lt;/properties&amp;gt;
    &amp;lt;/rule&amp;gt;
&amp;lt;/ruleset&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;규칙 파일 적용하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 작성한 규칙 파일을 빌드 설정에서 적용할 수 있습니다. 예를 들어 Gradle에서는 다음과 같이 적용합니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;pmd {
    ruleSetFiles = files(&quot;config/pmd/ruleset.xml&quot;)  // 설정 파일 경로 입력
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.   PMD 검사 실행하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 설정한 PMD를 이제 실행하여 소스 품질을 검사해보겠습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Gradle에서 PMD 검사 실행하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 명령어로 PMD 검사를 수행할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;./gradlew pmdMain&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행하면 &lt;code&gt;build/reports/pmd/main.html&lt;/code&gt; 파일에 검사 결과가 저장됩니다. 결과 보고서를 통해 &lt;b&gt;어떤 파일의 어떤 부분에서 문제&lt;/b&gt;가 발생했는지 확인할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Maven에서 PMD 검사 실행하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Maven을 사용하는 경우 다음 명령어를 실행합니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;mvn pmd:check&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PMD가 코드 품질 검사를 수행하고, 보고서를 &lt;code&gt;target/site/pmd.html&lt;/code&gt; 파일로 생성합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6.   PMD로 코드 품질 검사 예시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 예제를 통해 PMD의 코드 품질 검사 기능을 확인해보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;public class SampleClass {
    public void sampleMethod() {
        int i = 0;
        while (true) { // PMD가 무한 루프 가능성 경고 발생
            i++;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 PMD는 &lt;code&gt;while (true)&lt;/code&gt;로 인해 &lt;b&gt;무한 루프 가능성&lt;/b&gt; 경고를 발생시킵니다. 이외에도 메서드가 너무 길거나 복잡도가 높은 경우, &lt;b&gt;중복 코드&lt;/b&gt;가 있는 경우 등 다양한 상황에서 경고를 줄 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7.   PMD 결과 개선하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PMD 검사 결과는 코드의 품질을 향상시키는 가이드라인 역할을 합니다. &lt;b&gt;무한 루프 방지, 복잡성 줄이기, 중복 코드 제거&lt;/b&gt; 등 PMD가 제시한 문제를 해결하며 코드의 유지 보수성을 크게 높일 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요약&lt;br /&gt;&lt;b&gt;PMD&lt;/b&gt;는 정적 코드 분석 도구로 Java 코드 품질 개선에 유용하다.&lt;br /&gt;&lt;b&gt;Gradle&lt;/b&gt;과 &lt;b&gt;Maven&lt;/b&gt;으로 손쉽게 설정 가능하다.&lt;br /&gt;&lt;b&gt;Custom Rules&lt;/b&gt;로 프로젝트에 맞는 규칙을 적용할 수 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PMD는 소스 품질을 높이는 데 큰 역할을 하므로, 프로젝트 초기부터 도입하는 것을 추천드립니다! Programming Mistake Detector&lt;/p&gt;</description>
      <category>Java</category>
      <category>gradle pmd</category>
      <category>java pmd</category>
      <category>maven pmd</category>
      <category>PMD</category>
      <category>programming mistake detector</category>
      <category>spring pmd</category>
      <category>오블완</category>
      <category>자바 소스 검토</category>
      <category>자바 소스 품질</category>
      <category>티스토리챌린지</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/268</guid>
      <comments>https://tae-hui.tistory.com/entry/JAVA-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90%EC%84%9C-PMD%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%86%8C%EC%8A%A4-%ED%92%88%EC%A7%88-%EA%B2%80%EC%82%AC#entry268comment</comments>
      <pubDate>Mon, 11 Nov 2024 15:45:23 +0900</pubDate>
    </item>
    <item>
      <title>[Docker] Docker란 무엇인가?</title>
      <link>https://tae-hui.tistory.com/entry/Docker-Docker%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  1. Docker란 무엇인가요?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker는 &lt;b&gt;애플리케이션을 컨테이너&lt;/b&gt;라는 독립된 환경에 격리시켜 실행할 수 있게 도와주는 오픈 소스 플랫폼입니다. 애플리케이션과 그에 필요한 모든 라이브러리, 종속성 등을 하나의 패키지로 묶어 &lt;b&gt;일관된 실행 환경&lt;/b&gt;을 제공하죠. Docker는 애플리케이션을 서로 격리된 상태로 실행하며, 애플리케이션이 여러 환경에서 동일하게 작동하도록 합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  2. Docker의 탄생 배경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker의 탄생 배경은 &lt;b&gt;복잡한 개발 환경의 일관성 유지&lt;/b&gt;라는 문제에서 시작되었습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;개발 환경과 배포 환경이 다르면 발생하는 문제를 해결하고자 Docker가 탄생했습니다.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거에는 애플리케이션 개발 및 테스트 시, 운영 환경과의 차이로 인해 문제가 발생하곤 했습니다. 개발자는 자신이 만든 애플리케이션이 운영 환경에서도 동일하게 동작하길 원했지만, 라이브러리 버전이나 OS 버전 등의 차이로 인해 예기치 못한 오류가 발생했습니다. &lt;b&gt;Docker는 이를 해결&lt;/b&gt;하고자 애플리케이션을 독립된 컨테이너로 관리하는 개념을 도입하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker가 만들어낸 변화는 코드의 &quot;한 번 작성, 어디서나 실행&quot;이라는 아이디어를 현실화하는 것이었습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;⚙️ 3. Docker의 특장점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker는 다양한 기능을 통해 개발자와 운영 팀에게 많은 이점을 제공합니다. 주요 특장점은 다음과 같습니다:&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ 3.1 환경의 일관성 유지&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker 컨테이너는 어디서든 동일한 환경을 제공합니다. 개발자가 자신의 로컬 환경에서 테스트한 애플리케이션을 그대로 운영 환경에 배포할 수 있어 환경 차이로 인한 문제를 방지합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  3.2 빠른 배포 및 확장성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker는 이미지를 생성해 다양한 환경에 빠르게 배포할 수 있도록 도와줍니다. 또한 컨테이너를 쉽게 확장할 수 있어 &lt;b&gt;높은 확장성&lt;/b&gt;을 제공합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  3.3 경량화된 가상화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너는 가상 머신(VM)과는 다르게 OS 커널을 공유하여 더 적은 리소스를 사용합니다. &lt;b&gt;가볍고 빠르게 실행&lt;/b&gt;할 수 있다는 장점이 있죠.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  3.4 효율적인 리소스 관리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker는 각 컨테이너가 호스트 시스템의 자원을 효율적으로 사용할 수 있도록 관리합니다. 이를 통해 서버 리소스를 절약하고, 컨테이너를 더 많이 운영할 수 있게 됩니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  4. Docker의 동작 원리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker의 핵심 개념은 &lt;b&gt;이미지와 컨테이너&lt;/b&gt;입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  4.1 이미지(Image)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지는 Docker 컨테이너의 템플릿이라고 생각할 수 있습니다. 애플리케이션 실행에 필요한 모든 파일, 환경 설정이 포함되어 있죠. 이미지를 통해 동일한 환경을 만들어낼 수 있으며, 여러 곳에서 동일한 컨테이너를 만들 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  4.2 컨테이너(Container)&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;Docker의 동작 원리는 간단히 말해 &lt;b&gt;이미지를 기반으로 컨테이너를 만들어 애플리케이션을 실행하는 것&lt;/b&gt;입니다. Docker는 컨테이너에 대한 격리된 네임스페이스를 생성하고, 필요한 자원을 할당하여 애플리케이션이 독립적으로 실행될 수 있도록 도와줍니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  5. 자주 쓰는 Docker 명령어 예시&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker를 효율적으로 사용하려면 자주 사용되는 명령어를 잘 알아두어야 합니다. 주요 명령어를 살펴보겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ 5.1 이미지 관련 명령어&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;code&gt;docker pull [이미지 이름]&lt;/code&gt;&lt;/b&gt;&lt;br /&gt;Docker Hub에서 특정 이미지를 다운로드합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;docker build -t [이미지 이름] .&lt;/code&gt;&lt;/b&gt;&lt;br /&gt;Dockerfile을 이용해 이미지를 빌드합니다. &lt;code&gt;-t&lt;/code&gt; 옵션을 통해 이미지에 이름을 지정할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;docker images&lt;/code&gt;&lt;/b&gt;&lt;br /&gt;로컬에 저장된 Docker 이미지 목록을 확인합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  5.2 컨테이너 관련 명령어&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;code&gt;docker run [옵션] [이미지 이름]&lt;/code&gt;&lt;/b&gt;&lt;br /&gt;이미지를 기반으로 컨테이너를 실행합니다. &lt;code&gt;-d&lt;/code&gt; 옵션으로 백그라운드 실행, &lt;code&gt;-p&lt;/code&gt; 옵션으로 포트를 매핑할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;docker ps&lt;/code&gt;&lt;/b&gt;&lt;br /&gt;현재 실행 중인 컨테이너 목록을 보여줍니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;docker stop [컨테이너 ID]&lt;/code&gt;&lt;/b&gt;&lt;br /&gt;특정 컨테이너를 중지합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;docker rm [컨테이너 ID]&lt;/code&gt;&lt;/b&gt;&lt;br /&gt;중지된 컨테이너를 삭제합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ 5.3 네트워크와 볼륨 관련 명령어&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;code&gt;docker network ls&lt;/code&gt;&lt;/b&gt;&lt;br /&gt;Docker 네트워크 목록을 확인합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;docker volume create [볼륨 이름]&lt;/code&gt;&lt;/b&gt;&lt;br /&gt;데이터 볼륨을 생성합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;docker volume ls&lt;/code&gt;&lt;/b&gt;&lt;br /&gt;생성된 볼륨 목록을 확인합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  6. 마무리 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker는 애플리케이션을 실행하는 독립된 환경을 제공하여 개발 환경과 배포 환경 간의 일관성을 보장해 주는 컨테이너 기술입니다. Docker를 사용하면 애플리케이션이 어디서나 동일하게 실행될 수 있으며, 배포가 간편하고 확장성이 뛰어납니다. 이미지와 컨테이너를 활용한 Docker의 원리를 이해하면 애플리케이션의 일관성을 유지하고 효율적으로 운영할 수 있답니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주요 Docker 명령어&lt;/b&gt;: &lt;code&gt;docker pull&lt;/code&gt;, &lt;code&gt;docker build&lt;/code&gt;, &lt;code&gt;docker run&lt;/code&gt;, &lt;code&gt;docker ps&lt;/code&gt;, &lt;code&gt;docker stop&lt;/code&gt; 등이 있으며, 각각 이미지를 다루고 컨테이너를 실행하거나 관리할 때 사용됩니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Docker의 개념과 특장점, 주요 명령어까지 알아봤습니다.   Docker로 애플리케이션을 쉽게 관리해보세요!&lt;/p&gt;</description>
      <category>Docker</category>
      <category>docker</category>
      <category>docker build</category>
      <category>docker container</category>
      <category>Docker Image</category>
      <category>docker pull</category>
      <category>도커 탄생</category>
      <category>오블완</category>
      <category>이미지</category>
      <category>컨테이너</category>
      <category>티스토리챌린지</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/267</guid>
      <comments>https://tae-hui.tistory.com/entry/Docker-Docker%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80#entry267comment</comments>
      <pubDate>Sun, 10 Nov 2024 22:27:36 +0900</pubDate>
    </item>
    <item>
      <title>[Jenkins] Jenkins에서 GitHub 및 GitLab 인증 연동하기</title>
      <link>https://tae-hui.tistory.com/entry/Jenkins-Jenkins%EC%97%90%EC%84%9C-GitHub-%EB%B0%8F-GitLab-%EC%9D%B8%EC%A6%9D-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Jenkins에서 GitHub 및 GitLab 인증 연동하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Jenkins를 사용해 GitHub이나 GitLab의 리포지토리와 통합하고 파이프라인을 자동화하려면 인증 설정이 필요합니다. 이번 글에서는 GitHub과 GitLab에 대한 인증 방법과 Jenkins 파이프라인 설정까지 설명하겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. GitHub 및 GitLab 인증 방식&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub과 GitLab에 접근하기 위해서는 Jenkins에서 &lt;b&gt;OAuth 토큰&lt;/b&gt;이나 &lt;b&gt;SSH 키&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;OAuth 토큰 방식&lt;/b&gt;: GitHub 또는 GitLab에서 생성한 개인 액세스 토큰을 사용합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SSH 키 방식&lt;/b&gt;: Jenkins 서버와 리포지토리 간에 SSH 키를 사용해 인증합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. GitHub 연동하기&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.1 GitHub Access Token 발급&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;GitHub에 로그인 후, &lt;b&gt;Settings &amp;gt; Developer settings &amp;gt; Personal access tokens&lt;/b&gt;로 이동합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Generate new token&lt;/b&gt;을 클릭하고, 필요한 권한을 선택해 토큰을 생성합니다. (보통 &lt;code&gt;repo&lt;/code&gt;, &lt;code&gt;admin:repo_hook&lt;/code&gt; 권한 필요)&lt;/li&gt;
&lt;li&gt;생성된 토큰을 복사해 두세요. 이 토큰은 한 번만 표시되므로 잃어버리면 재생성이 필요합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2 Jenkins에 GitHub Access Token 등록&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Jenkins 대시보드에서 &lt;b&gt;Manage Jenkins &amp;gt; Manage Credentials&lt;/b&gt;로 이동합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Global credentials&lt;/b&gt;에서 &lt;b&gt;Add Credentials&lt;/b&gt;를 클릭합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Kind&lt;/b&gt;는 &lt;code&gt;Secret text&lt;/code&gt;로 설정하고, &lt;b&gt;Secret&lt;/b&gt;에 아까 복사한 GitHub Access Token을 입력합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ID&lt;/b&gt;와 &lt;b&gt;Description&lt;/b&gt;을 지정해 나중에 구분하기 쉽게 설정합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. GitLab 연동하기&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.1 GitLab Access Token 발급&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;GitLab에서 로그인 후 &lt;b&gt;Settings &amp;gt; Access Tokens&lt;/b&gt;로 이동합니다.&lt;/li&gt;
&lt;li&gt;토큰 이름을 작성하고, 필요한 &lt;b&gt;Scope&lt;/b&gt;를 선택합니다 (&lt;code&gt;api&lt;/code&gt;, &lt;code&gt;read_repository&lt;/code&gt;, &lt;code&gt;write_repository&lt;/code&gt; 등).&lt;/li&gt;
&lt;li&gt;생성된 토큰을 복사해 둡니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.2 Jenkins에 GitLab Access Token 등록&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Jenkins의 &lt;b&gt;Manage Jenkins &amp;gt; Manage Credentials&lt;/b&gt;로 이동합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Global credentials&lt;/b&gt;에서 &lt;b&gt;Add Credentials&lt;/b&gt;를 선택합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Kind&lt;/b&gt;를 &lt;code&gt;Secret text&lt;/code&gt;로 설정하고, &lt;b&gt;Secret&lt;/b&gt;에 GitLab Access Token을 입력합니다.&lt;/li&gt;
&lt;li&gt;ID와 Description을 입력하여 나중에 관리하기 쉽게 합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. Jenkins 파이프라인 작성 예시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub 또는 GitLab의 저장소를 빌드하고 배포하는 Jenkins 파이프라인을 작성해보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;pipeline {
    agent any
    stages {
        stage('Checkout') {
            steps {
                // GitHub 또는 GitLab 리포지토리에서 코드 체크아웃
                git branch: 'main', 
                    url: 'https://github.com/username/repository.git', 
                    credentialsId: 'GitHub-Token-ID'
            }
        }
        stage('Build') {
            steps {
                echo 'Building...'
                // 빌드 명령어 (예: Gradle, Maven 등)
            }
        }
        stage('Deploy') {
            steps {
                echo 'Deploying...'
                // 배포 명령어 (예: Kubernetes, Docker 등)
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 &lt;code&gt;credentialsId&lt;/code&gt; 값은 Jenkins의 Credentials에 등록한 ID를 입력하면 됩니다. &lt;code&gt;branch&lt;/code&gt;와 &lt;code&gt;url&lt;/code&gt;은 본인의 리포지토리에 맞게 수정해주세요.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. 마무리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Jenkins에서 GitHub 및 GitLab 리포지토리를 연동하고 파이프라인을 설정하는 방법을 알아봤습니다. 인증 방식으로 &lt;b&gt;OAuth 토큰&lt;/b&gt;과 &lt;b&gt;SSH 키&lt;/b&gt;가 있으며, 각 저장소의 토큰을 Jenkins의 Credentials로 추가하고 파이프라인에서 &lt;code&gt;credentialsId&lt;/code&gt;를 사용해 참조하는 구조입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 방법을 통해 간단히 GitHub 및 GitLab 연동을 자동화할 수 있으며, 파이프라인에서 빌드와 배포 프로세스를 정의할 수 있습니다.&lt;/p&gt;</description>
      <category>IT 지식</category>
      <category>credentialsid</category>
      <category>GIT</category>
      <category>github</category>
      <category>GitLab</category>
      <category>Jenkins</category>
      <category>jenkins credentialsid</category>
      <category>오블완</category>
      <category>젠킨스 인증</category>
      <category>티스토리챌린지</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/266</guid>
      <comments>https://tae-hui.tistory.com/entry/Jenkins-Jenkins%EC%97%90%EC%84%9C-GitHub-%EB%B0%8F-GitLab-%EC%9D%B8%EC%A6%9D-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0#entry266comment</comments>
      <pubDate>Sat, 9 Nov 2024 09:42:01 +0900</pubDate>
    </item>
    <item>
      <title>[Jenkins] CI/CD 환경 구축하기</title>
      <link>https://tae-hui.tistory.com/entry/Jenkins-CICD-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Jenkins로 CI/CD 환경 구축하기 - 기본 플러그인 소개부터 설치까지!&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발이나 DevOps를 처음 시작한 분이라면 &lt;b&gt;지속적 통합과 지속적 배포(Continuous Integration/Continuous Deployment, CI/CD)&lt;/b&gt; 자동화 도구로 &lt;i&gt;Jenkins&lt;/i&gt;를 한 번쯤 들어봤을 겁니다. Jenkins는 오픈 소스 기반의 자동화 서버로, 다양한 빌드와 배포 작업을 자동화할 수 있게 도와줍니다. 그럼 Jenkins의 기본 개념과 함께, CI/CD 작업을 자동화하는 데 필수적인 플러그인 설치 방법과 그 사용법에 대해 자세히 알아보겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  1. Jenkins란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Jenkins는 주로 소프트웨어 프로젝트의 자동화된 빌드, 테스트, 배포 파이프라인을 관리하기 위해 개발된 오픈 소스 도구입니다. 개발 과정에서 반복되는 많은 작업을 자동화하여 생산성을 높이고, 팀이 더 빠르게 소프트웨어를 배포할 수 있게 해줍니다.&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;Jenkins의 주요 기능&lt;/b&gt;은 다양한 형태의 빌드, 테스트, 배포 작업을 조합해 하나의 파이프라인으로 묶는 것입니다. 이를 통해 코드 변경 사항이 배포까지 이어지는 과정을 &lt;i&gt;자동화&lt;/i&gt;할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;플러그인&lt;/b&gt;을 통해 기능을 무한히 확장할 수 있어, 거의 모든 빌드 도구 및 소스 코드 관리 도구(SCM)와 통합할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Jenkins의 탄생 배경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Jenkins는 2004년에 Hudson이라는 이름으로 시작되었습니다. 오라클이 Hudson 프로젝트를 인수하면서 라이센스 문제로 분리된 것이 바로 Jenkins입니다. 이후 Jenkins는 독립적인 오픈 소스 프로젝트로 빠르게 발전했고, 현재는 DevOps와 CI/CD의 표준 도구 중 하나로 자리 잡았습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ 2. Jenkins 설치하기&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Java 설치 필수 (주로 JDK 11 이상 권장)&lt;/li&gt;
&lt;li&gt;최소 메모리 256MB (권장 1GB 이상)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;설치 과정&lt;/b&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Java 설치 후 환경 변수 설정을 완료합니다.&lt;/li&gt;
&lt;li&gt;Jenkins 공식 사이트에서 설치 파일을 다운로드합니다.&lt;/li&gt;
&lt;li&gt;다운로드한 파일을 실행하여 설치합니다.&lt;/li&gt;
&lt;li&gt;설치 후 브라우저에서 &lt;code&gt;http://localhost:8080&lt;/code&gt;으로 접속하여 관리자 암호를 입력해 초기 설정을 마칩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  3. Jenkins에서 유용한 기본 플러그인들&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Jenkins는 다양한 작업을 자동화하고, 다양한 도구와 통합할 수 있도록 &lt;b&gt;플러그인&lt;/b&gt; 기반으로 동작합니다. 여기서 Jenkins의 주요 기능과 통합을 위한 필수 플러그인들을 살펴볼게요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 3.1 기본 플러그인 목록 및 기능 설명&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;Folders&lt;/b&gt;: 프로젝트를 폴더로 분류하여 관리할 수 있도록 해주는 플러그인으로, 여러 프로젝트를 관리할 때 유용합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;OWASP Markup Formatter&lt;/b&gt;: OWASP 표준을 기반으로 안전한 마크업을 지원하여 보안을 강화합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Build Timeout&lt;/b&gt;: 빌드 시간이 너무 오래 걸리면 자동으로 중단해 주어, 자원을 낭비하지 않도록 해줍니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Credentials Binding&lt;/b&gt;: 암호나 API 토큰 같은 자격 증명을 안전하게 관리할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Timestamper&lt;/b&gt;: 빌드 로그에 타임스탬프를 추가해 로그 분석을 용이하게 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Workspace Cleanup&lt;/b&gt;: 빌드 전후에 워크스페이스를 정리하여 불필요한 파일을 제거하고 깨끗한 환경에서 빌드를 진행합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Ant&lt;/b&gt;: Apache Ant 빌드 도구를 지원하는 플러그인입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Gradle&lt;/b&gt;: Gradle 빌드 도구를 지원하여 더욱 다양한 빌드 옵션을 제공합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Pipeline&lt;/b&gt;: Jenkins 파이프라인 스크립트를 통해 빌드 및 배포를 자동화하는 필수 플러그인입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GitHub Branch Source&lt;/b&gt;: GitHub의 특정 브랜치를 빌드 소스로 설정하여, 브랜치마다 빌드를 자동화할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Pipeline: GitHub Groovy Libraries&lt;/b&gt;: 파이프라인에서 GitHub의 Groovy 라이브러리를 사용할 수 있어 다양한 스크립트를 손쉽게 사용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Pipeline Graph View&lt;/b&gt;: 파이프라인 빌드를 그래프로 시각화해 보여줍니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Git&lt;/b&gt;: Git을 통한 소스 코드 관리를 위한 필수 플러그인입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SSH Build Agents&lt;/b&gt;: SSH를 사용하여 원격 빌드 에이전트와 통신할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Matrix Authorization Strategy&lt;/b&gt;: 권한을 매트릭스 형식으로 설정할 수 있어 사용자 권한 관리를 체계적으로 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;PAM Authentication&lt;/b&gt;: PAM을 사용해 시스템 계정 인증을 Jenkins에서 활용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LDAP&lt;/b&gt;: LDAP를 통한 사용자 인증을 지원하여 중앙집중적인 사용자 관리를 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Email Extension &amp;amp; Mailer&lt;/b&gt;: 빌드 성공, 실패 등에 따라 이메일로 알림을 받을 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Dark Theme&lt;/b&gt;: Jenkins 인터페이스에 다크 테마를 적용하여 눈의 피로를 줄일 수 있어요.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Slack Notification&lt;/b&gt;: Slack에 빌드 결과를 알림으로 보낼 수 있는 플러그인입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  4. 플러그인 설치 및 적용 방법&lt;/h2&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;code&gt;/plugins&lt;/code&gt; 디렉토리에서 다운로드한 후, Jenkins의 플러그인 디렉토리 &lt;code&gt;/var/jenkins_home/plugins&lt;/code&gt;에 복사합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Jenkins 재기동&lt;/b&gt;: 플러그인을 적용하려면 Jenkins를 재시작해야 합니다. 명령어를 사용해 간단히 재기동할 수 있습니다.&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;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;sudo systemctl restart jenkins&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;3&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;플러그인 적용 확인&lt;/b&gt;: 재기동 후, Jenkins 관리 화면에서 플러그인 설치 여부를 확인하고, 설정에 반영되었는지 확인합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  5. Jenkins의 SCM(소스 코드 관리)와의 통합&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Jenkins는 &lt;b&gt;Git, GitHub, GitLab 등 다양한 소스 코드 관리(SCM) 도구와 통합&lt;/b&gt;할 수 있습니다. 특히 &lt;code&gt;GitHub Branch Source&lt;/code&gt;와 &lt;code&gt;Git&lt;/code&gt; 플러그인은 소스 코드 변경 사항에 따라 자동으로 빌드 및 배포 파이프라인을 시작하는 데 유용합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  GitHub 연동 예시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Jenkins에서 GitHub와 연동하는 과정은 다음과 같습니다.&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;Jenkins에 GitHub 플러그인 설치&lt;/b&gt;: &lt;code&gt;GitHub Branch Source&lt;/code&gt;와 &lt;code&gt;Git&lt;/code&gt; 플러그인을 설치합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GitHub 웹훅(Webhook) 설정&lt;/b&gt;: GitHub 리포지토리 설정에서 웹훅을 추가하여 코드 변경 시 Jenkins에서 빌드가 자동으로 트리거되도록 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Credential 설정&lt;/b&gt;: GitHub에 인증할 자격 증명(Credential)을 Jenkins에 등록합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Pipeline 스크립트 작성&lt;/b&gt;: &lt;code&gt;Jenkinsfile&lt;/code&gt;을 작성하여 빌드와 배포 과정을 설정합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Jenkinsfile 예시&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;pipeline {
   agent any
   stages {
       stage('Build') {
           steps {
               echo 'Building...'
           }
       }
       stage('Test') {
           steps {
               echo 'Testing...'
           }
       }
       stage('Deploy') {
           steps {
               echo 'Deploying...'
           }
       }
   }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  구현 및 플러그인 참조 링크&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&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://github.com/taehui8260/web-auto-deploy&quot;&gt;https://github.com/taehui8260/web-auto-deploy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Plugin 참조: &lt;a href=&quot;https://github.com/taehui8260/jenkins-plugins&quot;&gt;https://github.com/taehui8260/jenkins-plugins&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  요약&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Jenkins는 다양한 빌드와 배포 작업을 자동화하여 DevOps 프로세스를 간소화하는 강력한 도구입니다. 플러그인을 통해 기능을 확장할 수 있어 &lt;b&gt;다양한 빌드 도구와 소스 코드 관리 시스템&lt;/b&gt;과 손쉽게 통합할 수 있습니다. 기본 플러그인 설치를 통해 Jenkins 환경을 안정적이고 효율적으로 구축하고, 자동화 파이프라인 설정을 통한 완벽한 CI/CD 환경을 만들어 보세요!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;위의 과정들을 따라가면서 자신만의 Jenkins 환경을 구축해보세요.  &lt;/h4&gt;</description>
      <category>IT 지식</category>
      <category>CD</category>
      <category>CI</category>
      <category>CI/CD</category>
      <category>docker</category>
      <category>Jenkins</category>
      <category>jenkins plugins</category>
      <category>배포 자동화</category>
      <category>오블완</category>
      <category>젠킨스</category>
      <category>티스토리챌린지</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/265</guid>
      <comments>https://tae-hui.tistory.com/entry/Jenkins-CICD-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0#entry265comment</comments>
      <pubDate>Fri, 8 Nov 2024 16:16:01 +0900</pubDate>
    </item>
    <item>
      <title>웹 접근성(Web Accessibility)이란</title>
      <link>https://tae-hui.tistory.com/entry/%EC%9B%B9-%EC%A0%91%EA%B7%BC%EC%84%B1Web-Accessibility%EC%9D%B4%EB%9E%80</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ 1. 웹 접근성(Web Accessibility)이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 접근성(Web Accessibility)은 장애 유무와 관계없이 모든 사용자가 웹 사이트나 애플리케이션에 접근하고 사용할 수 있도록 만드는 개념입니다. 웹 접근성은 시각, 청각, 인지 및 운동 장애를 가진 사용자를 포함해 나이와 언어에 관계없이 모든 사용자에게 공평한 웹 환경을 제공하는 것이 목표입니다. 웹 접근성은 특히 사회적 약자를 배려하는 측면에서 매우 중요하지만, 궁극적으로는 &lt;b&gt;모든 사용자&lt;/b&gt;가 더 쉽게, 빠르게 정보를 얻을 수 있도록 도와주는 역할을 하기도 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;SEO에 유리&lt;/b&gt;한 측면도 있습니다. 접근성을 높인 웹사이트는 구조적으로도 깔끔해지기 때문에 검색 엔진 최적화에 긍정적 영향을 미칩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ 2. 웹 접근성 표준과 원칙&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 접근성의 국제 표준으로 가장 많이 참고되는 것이 &lt;b&gt;W3C&lt;/b&gt;에서 정의한 &lt;b&gt;WCAG(Web Content Accessibility Guidelines)&lt;/b&gt;입니다. WCAG는 다양한 접근성 지침을 제시하며, 주요 목표를 4가지 원칙으로 정리하였습니다.&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;인식 가능성(Perceivable)&lt;/b&gt;: 모든 사용자에게 콘텐츠가 잘 보이거나 들려야 해요. 텍스트 대체 텍스트, 자막 제공 등이 해당.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;운용 가능성(Operable)&lt;/b&gt;: 사용자가 다양한 방법(키보드, 음성 등)으로 인터페이스를 조작.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;이해 가능성(Understandable)&lt;/b&gt;: 정보와 사용자 인터페이스가 이해하기 쉬워야함.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;견고성(Robust)&lt;/b&gt;: 콘텐츠가 다양한 환경(브라우저, 스크린 리더 등)에서도 문제없이 작동해야함.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  3. 웹 접근성 구현 요소&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  대체 텍스트(alt text) 제공&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 요소에 &lt;b&gt;alt 속성&lt;/b&gt;을 사용하여 시각 장애가 있는 사용자가 이미지의 의미를 알 수 있도록 설명을 제공합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;&amp;lt;img src=&quot;image.jpg&quot; alt=&quot;강아지가 공원에서 뛰어노는 모습&quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  색 대비 조정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;텍스트와 배경 간의 색 대비를 충분히 높여 &lt;b&gt;시각 장애&lt;/b&gt;나 &lt;b&gt;색각 이상&lt;/b&gt;을 가진 사용자도 내용을 쉽게 인식할 수 있게 합니다. WCAG는 &lt;b&gt;텍스트 대비를 4.5:1 이상&lt;/b&gt;으로 권장합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  키보드 접근성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 기능을 &lt;b&gt;키보드로 조작 가능&lt;/b&gt;하게 만들면, 마우스를 사용하기 어려운 사용자도 콘텐츠를 사용할 수 있습니다. 일반적으로 &lt;code&gt;Tab&lt;/code&gt; 키로 이동하고 &lt;code&gt;Enter&lt;/code&gt;나 &lt;code&gt;Space&lt;/code&gt; 키로 선택할 수 있게 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  ARIA(Accessible Rich Internet Applications) 속성 사용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ARIA 속성은 HTML에 추가하여 스크린 리더가 보다 정확하게 내용을 전달할 수 있도록 합니다. 예를 들어, 동적 콘텐츠가 많은 웹사이트에서 &lt;code&gt;role&lt;/code&gt;이나 &lt;code&gt;aria-label&lt;/code&gt; 같은 속성을 사용하여 콘텐츠의 의미와 상태를 전달합니다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;button aria-label=&quot;메뉴 열기&quot;&amp;gt; &amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  4. 웹 접근성 테스트 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 접근성 테스트는 다양한 도구와 기술을 활용해 수동 및 자동으로 진행할 수 있습니다. 다음은 몇 가지 주요한 테스트 방법입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;code&gt;Tab&lt;/code&gt;, &lt;code&gt;Enter&lt;/code&gt;, &lt;code&gt;Space&lt;/code&gt; 키를 사용해 콘텐츠를 이동하고 조작해볼 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스크린 리더 테스트&lt;/b&gt;: &lt;b&gt;스크린 리더&lt;/b&gt;(예: NVDA, VoiceOver)를 사용해 텍스트 대체 텍스트, 링크 설명, ARIA 속성 등이 올바르게 읽히는지 테스트합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;색상 대비 확인&lt;/b&gt;: WCAG 기준 대비율(4.5:1)을 참고해 텍스트와 배경 색상을 검토합니다. Chrome의 DevTools 또는 접근성 검사 도구에서 확인할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;WAVE&lt;/b&gt;: WAVE(Web Accessibility Evaluation Tool)는 웹 페이지의 접근성을 검사하고 문제를 시각적으로 표시해주는 도구입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Lighthouse&lt;/b&gt;: Google Chrome의 Lighthouse는 페이지 성능 및 접근성 테스트를 할 수 있습니다. DevTools에서 접근성 탭을 열어 Lighthouse의 접근성 보고서를 확인해볼 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;axe&lt;/b&gt;: axe는 Chrome과 Firefox에서 사용할 수 있는 접근성 확장 프로그램으로, 다양한 접근성 문제를 자동으로 검사합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  5. 웹 접근성 개선을 위한 팁&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;code&gt;label&lt;/code&gt;, &lt;code&gt;fieldset&lt;/code&gt;, &lt;code&gt;legend&lt;/code&gt; 등)를 올바르게 사용.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;탭 순서(Tab Order)&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 contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 접근성은 모든 사용자, 특히 장애가 있는 사용자에게도 공평한 웹 경험을 제공하기 위한 노력입니다. WCAG의 4대 원칙에 따라 다양한 요소를 구현하고, 수동 및 자동화 도구로 테스트해 접근성을 높일 수 있습니다. 특히 텍스트 대체, 색 대비, 키보드 조작 등을 신경 써야 하며, 접근성 검사 도구를 통해 문제를 발견하고 개선해 나가면 됩니다.&lt;/p&gt;</description>
      <category>IT 지식</category>
      <category>Accessibility</category>
      <category>AXE</category>
      <category>SEO</category>
      <category>web accessibility</category>
      <category>대체 텍스트</category>
      <category>오블완</category>
      <category>웹</category>
      <category>웹 접근성</category>
      <category>접근성</category>
      <category>티스토리챌린지</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/264</guid>
      <comments>https://tae-hui.tistory.com/entry/%EC%9B%B9-%EC%A0%91%EA%B7%BC%EC%84%B1Web-Accessibility%EC%9D%B4%EB%9E%80#entry264comment</comments>
      <pubDate>Thu, 7 Nov 2024 14:21:23 +0900</pubDate>
    </item>
    <item>
      <title>WSL(Windows Subsystem for Linux) 설치 및 설정</title>
      <link>https://tae-hui.tistory.com/entry/WSLWindows-Subsystem-for-Linux-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%84%A4%EC%A0%95</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.   WSL(Windows Subsystem for Linux) 설치 및 설정: 우분투 기반 사용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WSL은 윈도우에서 리눅스 환경을 간편하게 사용할 수 있도록 해줍니다. 여기서는 &lt;b&gt;Ubuntu&lt;/b&gt; 배포판을 설치하는 과정과 &lt;b&gt;고정 IP 설정&lt;/b&gt; 및 &lt;b&gt;Windows-Windows Subsystem 간 통신&lt;/b&gt; 방법을 다뤄보겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  1-1. WSL 설치 및 Ubuntu 세팅하기&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1️⃣ WSL 설치 활성화&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 WSL을 설치하고 활성화합니다. 아래 명령어를 &lt;b&gt;Windows PowerShell(관리자 권한)&lt;/b&gt; 에서 실행&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;wsl --install&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 명령은 최신 WSL과 함께 기본으로 &lt;b&gt;Ubuntu&lt;/b&gt; 배포판까지 설치합니다. 만약 직접 선택하고 싶다면, 아래처럼 특정 버전을 선택&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;wsl --install -d Ubuntu-20.04&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;b&gt;Tip&lt;/b&gt;: 설치 후 &lt;code&gt;wsl --set-default-version 2&lt;/code&gt; 명령을 실행해 WSL 2로 기본 설정하면 더 빠른 파일 시스템과 네트워크 속도를 얻을 수 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2️⃣ WSL 1과 WSL 2 전환 방법&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;WSL 1과 WSL 2의 차이&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 100px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;&lt;b&gt;항목&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;&lt;b&gt;WSL 1&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px;&quot;&gt;&lt;b&gt;WSL 2&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 16px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 16px;&quot;&gt;&lt;b&gt;커널&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 16px;&quot;&gt;리눅스 호환 계층&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 16px;&quot;&gt;진짜 리눅스 커널 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 16px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 16px;&quot;&gt;&lt;b&gt;성능&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 16px;&quot;&gt;파일 시스템 작업에 빠름&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 16px;&quot;&gt;전체 성능 개선, I/O 작업 최적화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 16px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 16px;&quot;&gt;&lt;b&gt;네트워크&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 16px;&quot;&gt;Windows와 IP 공유&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 16px;&quot;&gt;별도 가상 네트워크 IP 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 16px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 16px;&quot;&gt;&lt;b&gt;Docker 지원&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 16px;&quot;&gt;제한적 지원&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 16px;&quot;&gt;완벽한 Docker 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 16px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 16px;&quot;&gt;&lt;b&gt;가상화 필요&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 16px;&quot;&gt;필요 없음&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 16px;&quot;&gt;Hyper-V 가상화 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WSL 2를 기본 버전으로 설정&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;wsl --set-default-version 2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 배포판(예: Ubuntu)의 WSL 버전을 1에서 2로 전환&lt;/p&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;wsl --set-version [Ubuntu] 2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 명령어로 WSL에 설치된 배포나 목록을 보고 실행하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;wsl --list --verbose

wsl -d [name]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  1-2. WSL로 우분투 실행 및 초기 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치가 완료되면 &lt;b&gt;Ubuntu&lt;/b&gt; 앱을 실행합니다. 처음 실행 시 사용자 이름과 비밀번호 설정이 필요합니다. 이 설정이 완료되면, 아래 명령어를 입력해 패키지를 최신 상태로 유지&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo apt update &amp;amp;&amp;amp; sudo apt upgrade -y&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.   WSL에서 고정 IP 설정 및 접근 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 WSL의 IP 주소는 &lt;b&gt;매번 재시작될 때마다 바뀝니다.&lt;/b&gt;. 고정 IP로 접근하려면 몇 가지 설정이 필요합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  2-1. WSL 고정 IP 설정하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;고정 IP를 설정하려면&lt;/b&gt; WSL 네트워크 인터페이스를 조정하고, NAT(네트워크 주소 변환)를 설정해야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0.&lt;b&gt; net-tools&lt;/b&gt; 패키지 설치&lt;/p&gt;
&lt;pre id=&quot;code_1730180172487&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt update
sudo apt install net-tools&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 먼저, WSL2의 기본 네트워크 인터페이스를 확인합니다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;ifconfig&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터페이스 이름은 일반적으로 &lt;code&gt;eth0&lt;/code&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 이제 WSL 시작 시 고정 IP를 할당하도록 설정합니다. 아래 경로에 &lt;b&gt;스크립트 파일&lt;/b&gt;을 만들어 고정 IP를 부여할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;code&gt;/etc/init.d/fix_ip.sh&lt;/code&gt; 파일 생성하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 명령어로 고정 IP 설정 스크립트를 만듭니다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;sudo nano /etc/init.d/fix_ip.sh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트 내용:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;#!/bin/bash
ip addr add 192.168.50.2/24 dev eth0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;awk&quot;&gt;&lt;code&gt;sudo chmod +x /etc/init.d/fix_ip.sh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.&amp;nbsp; WSL 시작 시 이 스크립트가 실행되도록 설정합니다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;nano ~/.bashrc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bashrc 파일의 맨 끝에 다음 줄을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1730986583970&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo /etc/init.d/fix_ip.sh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;/b&gt;위 명령어는 sudo 권한이 필요하기 때문에, sudo 비밀번호 입력을 요구하지 않도록 visudo를 사용하여 다음 줄을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1730986646920&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo visudo&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일에 다음 줄을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1730986662478&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;사용자 이름&amp;gt; ALL=(ALL) NOPASSWD: /etc/init.d/fix_ip.sh&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 재접속 후 IP를 확인해보면 추가된걸 확인할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1730181305434&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ip add show&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  2-2. 윈도우에서 WSL에 접근하기 (고정 IP 사용)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. WSL 내부에서 &lt;b&gt;서버를 실행&lt;/b&gt;한 후 고정 IP로 접근할 수 있습니다. 예를 들어, WSL에서 간단한 웹 서버를 실행해봅시다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;sudo python3 -m http.server 80&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2.&lt;b&gt;Windows에서 브라우저&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;URL&lt;/b&gt;: &lt;code&gt;http://127.0.0.1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 설정한 고정 IP는(192.168.50.2) &lt;b&gt;WSL의 네트워크 인터페이스가 가상 네트워크에 연결&lt;/b&gt;되어 있기 때문에 외부 접근이 차단되어 있습니다. 따라서 &lt;span style=&quot;color: #666666; text-align: center;&quot;&gt;192.168.50.2로 접근을 원한다면 별도의 포트포워딩이 필요합니다. &lt;br /&gt;본 게시물에서는 필요성을 못느껴 따로 설명하지 않도록 하겠습니다.&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;만약 접근이 되지 않으면 WSL의 &lt;b&gt;방화벽 설정&lt;/b&gt;을 확인하거나 &lt;code&gt;Windows Defender Firewall&lt;/code&gt;에서 해당 IP와 포트를 허용해야 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.   WSL에서 Windows로의 IP 접근 설정하기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  3-1. WSL에서 Windows의 IP 접근&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WSL에서 윈도우로 IP로 접근하려면 &lt;b&gt;Windows의 IP 주소&lt;/b&gt;를 알아야 합니다. PowerShell에서 다음 명령어로 윈도우의 IP를 확인할 수 있습니다:&lt;/p&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;ipconfig&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력된 정보 중 &lt;code&gt;IPv4 주소&lt;/code&gt;를 확인합니다. 예를 들어 &lt;code&gt;192.168.0.1&lt;/code&gt;이라면, WSL에서 다음과 같이 핑 테스트를 해봅니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ping 192.168.0.1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 핑이 정상 작동하지 않는다면, 윈도우 방화벽에서 WSL의 접근을 허용해야 합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  3-2. Windows의 서버에 접근하기 (예: 웹 서버)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;윈도우에서 로컬 서버를 열었다면, WSL에서도 해당 서버에 접근이 가능합니다. 예를 들어 윈도우에서 Python HTTP 서버를 열어봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Windows 명령 프롬프트:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;python -m http.server 8080&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 WSL에서 해당 서버에 접속합니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;curl http://192.168.0.1:8080&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.   윈도우에서 WSL로의 IP 접근 허용하기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  4-1. 윈도우에서 WSL 접근을 위한 방화벽 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 &lt;b&gt;WSL의 서버에 접근이 불가능하다면&lt;/b&gt;, 윈도우 방화벽이 문제일 수 있습니다. 다음과 같이 설정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. &lt;b&gt;Windows Defender 방화벽&lt;/b&gt;을 엽니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2.&lt;b&gt; 고급 설정&lt;/b&gt; &amp;gt; &lt;b&gt;인바운드 규칙&lt;/b&gt;에서 새 규칙을 만듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. &lt;b&gt;포트&lt;/b&gt; 선택 후, &lt;b&gt;특정 포트&lt;/b&gt;(예: 80 또는 8080)를 입력합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 연결을 &lt;b&gt;허용&lt;/b&gt;으로 선택하고, 규칙에 이름을 지정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 &lt;b&gt;Windows에서 WSL 서버&lt;/b&gt;로 접근이 가능합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.   정리&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;WSL 설치&lt;/b&gt;: &lt;code&gt;wsl --install&lt;/code&gt;로 간편하게 설치하고 Ubuntu 환경 세팅.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;고정 IP 설정&lt;/b&gt;: WSL에서 고정 IP 할당 스크립트 작성.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Windows에서 WSL로 접근&lt;/b&gt;: WSL 내부 서버를 고정 IP로 운영하고, 브라우저에서 접근.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;WSL에서 Windows로 접근&lt;/b&gt;: &lt;code&gt;ipconfig&lt;/code&gt;로 윈도우 IP 확인 후 WSL에서 핑 테스트.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;방화벽 설정&lt;/b&gt;: 필요한 포트를 방화벽에서 허용해 원활한 통신 보장.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;</description>
      <category>IT 지식</category>
      <category>windows</category>
      <category>Windows Linux</category>
      <category>WSL</category>
      <category>WSL1</category>
      <category>WSL2</category>
      <category>리눅스</category>
      <category>우분투</category>
      <category>윈도우 리눅스</category>
      <category>윈도우 리눅스 사용법</category>
      <category>윈도우 우분투</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/263</guid>
      <comments>https://tae-hui.tistory.com/entry/WSLWindows-Subsystem-for-Linux-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%84%A4%EC%A0%95#entry263comment</comments>
      <pubDate>Tue, 29 Oct 2024 08:00:20 +0900</pubDate>
    </item>
    <item>
      <title>[Git] 한 컴퓨터에서 두 개의 Git 계정 사용하는 방법</title>
      <link>https://tae-hui.tistory.com/entry/Git-%ED%95%9C-%EC%BB%B4%ED%93%A8%ED%84%B0%EC%97%90%EC%84%9C-%EB%91%90-%EA%B0%9C%EC%9D%98-Git-%EA%B3%84%EC%A0%95-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  한 컴퓨터에서 두 개의 Git 계정 사용하는 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Git을 사용하는 개발자라면, 때로는 회사용 계정과 개인용 계정을 동시에 관리해야 할 때가 있습니다. &lt;b&gt;SSH 키&lt;/b&gt;와 &lt;b&gt;Git 설정&lt;/b&gt;을 활용하여 두 계정을 효율적으로 관리할 수 있는 방법을 단계별로 알아보겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  1. 문제 상황&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사와 개인 프로젝트를 모두 관리하려면, 두 개의 Git 계정을 한 컴퓨터에서 사용하는 방법이 필요합니다. 기본적으로 Git은 하나의 계정을 설정하도록 되어 있지만, 다양한 프로젝트에서 여러 계정을 사용해야 할 경우 문제가 발생할 수 있습니다. 이를 해결하기 위한 방법이 바로 &lt;b&gt;SSH 키&lt;/b&gt;를 사용한 계정 분리입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  2. 해결 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 두 계정을 동시에 사용할 수 있도록 설정하는 방법을 알아보겠습니다. 다음 단계로 &lt;b&gt;SSH 키 생성&lt;/b&gt;, &lt;b&gt;SSH 키 등록&lt;/b&gt;, &lt;b&gt;Git 설정&lt;/b&gt;을 통해 두 계정을 깔끔하게 관리할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ 2-1. SSH 키 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Git 계정에 대해 별도의 SSH 키를 생성합니다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;# user01 계정용 SSH 키 생성
ssh-keygen -t rsa -C &quot;user01@example.com&quot; -f ~/.ssh/id_rsa_user01

# user02 계정용 SSH 키 생성
ssh-keygen -t rsa -C &quot;user02@example.com&quot; -f ~/.ssh/id_rsa_user02&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  2-2. SSH 키를 GitHub에 등록&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub에 SSH 키를 등록해야 원격 리포지토리에 접근할 수 있습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;SSH 공개 키를 확인하고 복사합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;cat ~/.ssh/id_rsa_user01.pub&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;GitHub에 로그인하고 &lt;a href=&quot;https://github.com/settings/keys&quot;&gt;SSH and GPG keys 설정 페이지&lt;/a&gt;로 이동합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;New SSH key&lt;/b&gt;를 클릭하고, 위에서 복사한 공개 키를 붙여넣습니다.&lt;/li&gt;
&lt;li&gt;동일한 방식으로 &lt;code&gt;user02&lt;/code&gt; 계정용 공개 키도 등록합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;⚙️ 2-3. SSH config 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSH 키를 만들었다면, &lt;code&gt;~/.ssh/config&lt;/code&gt; 파일에 두 계정을 구분하는 설정을 추가합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# user01 계정 설정
Host github-user01
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_rsa_user01

# user02 계정 설정
Host github-user02
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_rsa_user02&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  2-4. SSH 에이전트에 SSH 키 추가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSH 에이전트에 키를 추가하여 두 계정을 활성화합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# SSH 에이전트 시작
eval &quot;$(ssh-agent -s)&quot;

# user01 계정용 SSH 키 추가
ssh-add ~/.ssh/id_rsa_user01

# user02 계정용 SSH 키 추가
ssh-add ~/.ssh/id_rsa_user02&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  3. 리포지토리 클론 및 푸시&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 계정 설정이 끝났다면, 이제 리포지토리를 각 계정에 맞게 클론하고, 그 계정으로 푸시하는 방법을 알아봅니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  3-1. 클론 받을 때&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 계정에 맞는 SSH 별칭을 사용하여 리포지토리를 클론합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;user01 계정으로 클론 받기:&lt;/h4&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;git clone git@github-user01:username/repository.git&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;user02 계정으로 클론 받기:&lt;/h4&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;git clone git@github-user02:username/repository.git&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  3-2. 푸시할 때&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클론받은 리포지토리에서 작업 후 &lt;code&gt;git push&lt;/code&gt; 명령어를 사용하면, 클론할 때 설정한 계정으로 푸시가 이루어집니다. 예를 들어, &lt;code&gt;user01&lt;/code&gt; 계정으로 클론받았다면 푸시도 &lt;code&gt;user01&lt;/code&gt; 계정으로 진행됩니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  푸시 계정을 변경하고 싶다면:&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 다른 계정으로 푸시하고 싶다면, 원격 URL을 변경하거나 사용자 정보를 설정할 수 있습니다.&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;원격 URL 변경&lt;/b&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;dsconfig&quot;&gt;&lt;code&gt;git remote set-url origin git@github-user02:username/repository.git&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Git 사용자 정보 설정&lt;/b&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;git config user.name &quot;user02&quot;
git config user.email &quot;user02@example.com&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  4. 결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 한 컴퓨터에서 두 개의 Git 계정을 사용하는 방법을 마스터했습니다. &lt;b&gt;SSH 키&lt;/b&gt;와 &lt;b&gt;SSH config&lt;/b&gt; 파일을 통해 계정을 깔끔하게 분리하고, 필요에 따라 계정을 전환하며 효율적으로 프로젝트를 관리할 수 있습니다.&lt;/p&gt;</description>
      <category>Git</category>
      <category>GIT</category>
      <category>git global config</category>
      <category>git 계정 변경</category>
      <category>git 설정</category>
      <category>github</category>
      <category>github 계정 두개</category>
      <category>깃</category>
      <category>깃 계정 변경</category>
      <category>깃허브</category>
      <category>깃허브 계정 전환</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/262</guid>
      <comments>https://tae-hui.tistory.com/entry/Git-%ED%95%9C-%EC%BB%B4%ED%93%A8%ED%84%B0%EC%97%90%EC%84%9C-%EB%91%90-%EA%B0%9C%EC%9D%98-Git-%EA%B3%84%EC%A0%95-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95#entry262comment</comments>
      <pubDate>Fri, 18 Oct 2024 08:00:03 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] Spring 돌아보기 Part.3 #JDBC #MyBatis #Multiple DataSources</title>
      <link>https://tae-hui.tistory.com/entry/JAVA-Spring-%EB%8F%8C%EC%95%84%EB%B3%B4%EA%B8%B0-Part3-JDBC-MyBatis</link>
      <description>&lt;h1&gt;  Spring Data Access (JDBC, Transaction)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring 프레임워크를 사용하여 애플리케이션을 개발할 때, 데이터베이스와의 연동은 필수적인 부분입니다. 이번 글에서는 &lt;b&gt;Spring의 JDBC&lt;/b&gt;를 사용한 데이터 접근과 함께 &lt;b&gt;MyBatis 통합&lt;/b&gt;, &lt;b&gt;트랜잭션 관리&lt;/b&gt; 방법, 그리고 &lt;b&gt;데이터베이스 연동 설정&lt;/b&gt;을 해보겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.   Spring JDBC Template&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✔️ Spring JDBC Template란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Spring JDBC Template&lt;/b&gt;은 JDBC(Java Database Connectivity) 사용 시 발생하는 보일러플레이트 코드를 대폭 줄여주기 위해 설계된 유틸리티 클래스입니다. JDBC를 사용할 때 반복적으로 사용되는 코드, 예를 들면 데이터베이스 커넥션을 열고 닫는 것, &lt;code&gt;PreparedStatement&lt;/code&gt;와 같은 SQL 구문을 작성하는 것, 예외 처리 등을 자동으로 처리해주어 &lt;b&gt;더 적은 코드로 깔끔하게 데이터베이스 작업&lt;/b&gt;을 수행할 수 있게 해줍니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  JDBC Template 사용 방법&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring에서 &lt;code&gt;JdbcTemplate&lt;/code&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;DataSource&lt;/b&gt; 설정을 통해 데이터베이스 커넥션을 구성&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JdbcTemplate&lt;/code&gt; 객체를 생성하여 데이터베이스와 상호작용&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예시 코드&lt;/h3&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
    return new JdbcTemplate(dataSource);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 보듯, &lt;code&gt;DataSource&lt;/code&gt;를 통해 데이터베이스 커넥션을 설정하고, 이를 &lt;code&gt;JdbcTemplate&lt;/code&gt;에 전달하여 사용할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  주요 메서드&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;queryForObject&lt;/code&gt;: 단일 결과를 반환할 때 사용&lt;/li&gt;
&lt;li&gt;&lt;code&gt;query&lt;/code&gt;: 여러 행을 반환할 때 사용&lt;/li&gt;
&lt;li&gt;&lt;code&gt;update&lt;/code&gt;: 삽입, 수정, 삭제를 수행할 때 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시로, &lt;code&gt;queryForObject&lt;/code&gt;를 이용해 단일 결과를 조회하는 코드를 보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;String sql = &quot;SELECT name FROM users WHERE id = ?&quot;;
String name = jdbcTemplate.queryForObject(sql, new Object[]{id}, String.class);&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.   MyBatis와의 통합 및 XML Mapper 사용&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✔️ MyBatis 소개&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;MyBatis&lt;/b&gt;는 SQL을 XML 파일에 정의하고, 자바 객체와 SQL 간의 매핑을 쉽게 해주는 프레임워크입니다. Spring에서 MyBatis를 사용하는 이유는 복잡한 SQL 쿼리와 객체 간의 매핑 작업을 더욱 직관적이고 효율적으로 처리할 수 있기 때문입니다. 특히, &lt;b&gt;SQL을 XML 파일로 분리&lt;/b&gt;하여 관리할 수 있는 것이 큰 장점입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  MyBatis 의존성 추가 (pom.xml)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyBatis를 사용하기 위해서는 다음과 같이 의존성을 추가합니다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.mybatis.spring.boot&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;mybatis-spring-boot-starter&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;2.3.0&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  MyBatis 설정 (application.yml)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyBatis와 Spring을 연동하려면 간단한 설정이 필요합니다. &lt;code&gt;application.yml&lt;/code&gt; 파일에서 데이터베이스 및 MyBatis 설정을 다음과 같이 할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydatabase
    username: myuser
    password: mypassword
    driver-class-name: com.mysql.cj.jdbc.Driver
  mybatis:
    config-location: classpath:mybatis-config.xml
    mapper-locations: classpath:mappers/*.xml&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 설정을 통해 MyBatis와 MySQL이 연동되며, 쿼리문을 XML 파일로 분리해 사용할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✔️ XML Mapper 사용 예시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Mapper 인터페이스&lt;/b&gt;를 만들고, SQL 쿼리를 XML 파일로 분리하여 사용합니다. 먼저, 인터페이스를 정의한 다음, 그와 매핑되는 XML 파일에서 SQL을 정의합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  Mapper 인터페이스&lt;/h4&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;public interface UserMapper {
    User findById(Long id);
    List&amp;lt;User&amp;gt; findAll();
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  XML 파일로 쿼리 정의 (&lt;code&gt;UserMapper.xml&lt;/code&gt;)&lt;/h4&gt;
&lt;pre class=&quot;dust&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&amp;gt;
&amp;lt;!DOCTYPE mapper
  PUBLIC &quot;-//mybatis.org//DTD Mapper 3.0//EN&quot;
  &quot;http://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;&amp;gt;

&amp;lt;mapper namespace=&quot;com.example.mapper.UserMapper&quot;&amp;gt;

    &amp;lt;!-- 단일 사용자 조회 --&amp;gt;
    &amp;lt;select id=&quot;findById&quot; parameterType=&quot;long&quot; resultType=&quot;com.example.model.User&quot;&amp;gt;
        SELECT * FROM users WHERE id = #{id}
    &amp;lt;/select&amp;gt;

    &amp;lt;!-- 모든 사용자 조회 --&amp;gt;
    &amp;lt;select id=&quot;findAll&quot; resultType=&quot;com.example.model.User&quot;&amp;gt;
        SELECT * FROM users
    &amp;lt;/select&amp;gt;

&amp;lt;/mapper&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XML 파일에서는 SQL 쿼리를 정의하고, &lt;code&gt;#{}&lt;/code&gt; 구문을 통해 동적으로 파라미터 값을 주입받습니다. 이를 통해 SQL 쿼리를 자바 코드에서 분리하여 XML 파일로 관리할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.   트랜잭션 관리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스 작업에서 트랜잭션 관리는 매우 중요한 부분입니다. 트랜잭션은 여러 작업을 하나의 단위로 묶어 &lt;b&gt;원자성&lt;/b&gt;을 보장하며, 작업 중 하나라도 실패하면 전체 작업을 롤백할 수 있도록 도와줍니다. Spring에서는 트랜잭션을 선언적 또는 프로그래매틱하게 관리할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✔️ 선언적 트랜잭션 관리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선언적 트랜잭션 관리&lt;/b&gt;는 가장 많이 사용하는 방식으로, 애너테이션이나 XML 설정을 통해 트랜잭션을 관리하는 방식입니다. 가장 많이 사용하는 애너테이션은 &lt;code&gt;@Transactional&lt;/code&gt;입니다. 메서드 또는 클래스 레벨에서 이 애너테이션을 붙이면, 해당 메서드 내의 모든 데이터베이스 작업이 트랜잭션으로 처리됩니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시&lt;/h4&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;@Transactional
public void transferMoney(Long fromAccountId, Long toAccountId, Double amount) {
    // 출금
    accountRepository.debit(fromAccountId, amount);
    // 입금
    accountRepository.credit(toAccountId, amount);
}&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;h3 data-ke-size=&quot;size23&quot;&gt;✔️ 프로그래매틱 트랜잭션 관리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프로그래매틱 트랜잭션 관리&lt;/b&gt;는 코드에서 직접 트랜잭션을 관리하는 방법입니다. &lt;code&gt;PlatformTransactionManager&lt;/code&gt;와 &lt;code&gt;TransactionTemplate&lt;/code&gt;을 사용하여 트랜잭션을 시작, 커밋 또는 롤백할 수 있습니다. 선언적 트랜잭션과 달리, 코드 내에서 트랜잭션의 범위를 명시적으로 정의할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시&lt;/h4&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;public void transferMoney(Long fromAccountId, Long toAccountId, Double amount) {
    DefaultTransactionDefinition def = new DefaultTransactionDefinition();
    TransactionStatus status = transactionManager.getTransaction(def);

    try {
        // 출금
        accountRepository.debit(fromAccountId, amount);
        // 입금
        accountRepository.credit(toAccountId, amount);

        transactionManager.commit(status);  // 트랜잭션 커밋
    } catch (Exception e) {
        transactionManager.rollback(status);  // 트랜잭션 롤백
        throw e;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. ⚙️ 데이터베이스 연동 설정 (DataSource 설정, HikariCP 등)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring에서 데이터베이스와의 연동을 위해서는 &lt;code&gt;DataSource&lt;/code&gt; 설정이 필요합니다. &lt;code&gt;DataSource&lt;/code&gt;는 데이터베이스 커넥션을 관리하며, Spring Boot에서는 기본적으로 &lt;b&gt;HikariCP&lt;/b&gt;가 기본 연결 풀(Connection Pool)로 제공됩니다. HikariCP는 성능이 뛰어나고 경량화된 커넥션 풀로 많은 애플리케이션에서 사용됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✔️ HikariCP 설정 예시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot에서는 간단하게 &lt;code&gt;application.yml&lt;/code&gt; 파일에서 HikariCP 설정을 할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: root
    password: password
    driver-class-name: com.mysql.cj.jdbc.Driver
    hikari:
      maximum-pool-size: 10
      minimum-idle: 5
      idle-timeout: 30000
      max-lifetime: 1800000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 설정을 통해 HikariCP를 이용한 데이터베이스 커넥션 풀링을 구성할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. ⚙️ 두 개의 데이터베이스 사용하기 (Multiple DataSources)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot에서는 두 개 이상의 데이터베이스를 사용하여 각각 독립적으로 트랜잭션을 관리할 수 있습니다. 여기서는 &lt;b&gt;두 개의 &lt;code&gt;DataSource&lt;/code&gt;&lt;/b&gt;를 설정하고, 각각의 데이터베이스에 대해 트랜잭션을 어떻게 처리할 수 있는지 설명하겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  두 개의 데이터베이스 설정 (application.yml)&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;spring:
  datasource:
    primary:
      url: jdbc:mysql://localhost:3306/primarydb
      username: primary_user
      password: primary_password
      driver-class-name: com.mysql.cj.jdbc.Driver
    secondary:
      url: jdbc:mysql://localhost:3306/secondarydb
      username: secondary_user
      password: secondary_password
      driver-class-name: com.mysql.cj.jdbc.Driver

mybatis:
  primary:
    mapper-locations: classpath:mappers/primary/*.xml
  secondary:
    mapper-locations: classpath:mappers/secondary/*.xml&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 설정은 각각 &lt;b&gt;Primary 데이터베이스&lt;/b&gt;와 &lt;b&gt;Secondary 데이터베이스&lt;/b&gt;에 대한 연결 정보를 제공합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✔️ Primary DataSource 설정&lt;/h3&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;@Configuration
@MapperScan(basePackages = &quot;com.example.mapper.primary&quot;, sqlSessionFactoryRef = &quot;primarySqlSessionFactory&quot;)
public class PrimaryDataSourceConfig {

    @Primary
    @Bean(name = &quot;primaryDataSource&quot;)
    @ConfigurationProperties(prefix = &quot;spring.datasource.primary&quot;)
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Primary
    @Bean(name = &quot;primarySqlSessionFactory&quot;)
    public SqlSessionFactory primarySqlSessionFactory(@Qualifier(&quot;primaryDataSource&quot;) DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        return sessionFactory.getObject();
    }

    @Primary
    @Bean(name = &quot;primarySqlSessionTemplate&quot;)
    public SqlSessionTemplate primarySqlSessionTemplate(@Qualifier(&quot;primarySqlSessionFactory&quot;) SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✔️ Secondary DataSource 설정&lt;/h3&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;@Configuration
@MapperScan(basePackages = &quot;com.example.mapper.secondary&quot;, sqlSessionFactoryRef = &quot;secondarySqlSessionFactory&quot;)
public class SecondaryDataSourceConfig {

    @Bean(name = &quot;secondaryDataSource&quot;)
    @ConfigurationProperties(prefix = &quot;spring.datasource.secondary&quot;)
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = &quot;secondarySqlSessionFactory&quot;)
    public SqlSessionFactory secondarySqlSessionFactory(@Qualifier(&quot;secondaryDataSource&quot;) DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        return sessionFactory.getObject();
    }

    @Bean(name = &quot;secondarySqlSessionTemplate&quot;)
    public SqlSessionTemplate secondarySqlSessionTemplate(@Qualifier(&quot;secondarySqlSessionFactory&quot;) SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&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;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 개의 데이터베이스를 사용하는 경우, 일반적으로 &lt;code&gt;@Transactional&lt;/code&gt; 애너테이션을 사용하면 &lt;b&gt;하나의 트랜잭션 매니저&lt;/b&gt;만 적용됩니다. 예를 들어 하나의 서비스에서 두 개의 데이터베이스를 동시에 사용하는 경우, 한 데이터베이스에서 에러가 발생해도 다른 데이터베이스의 변경 사항은 롤백되지 않을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 해결하기 위해 &lt;b&gt;ChainedTransactionManager&lt;/b&gt;를 이용하여 두 개의 트랜잭션을 &lt;b&gt;동기화&lt;/b&gt;할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✔️ ChainedTransactionManager 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ChainedTransactionManager&lt;/b&gt;는 여러 개의 트랜잭션 매니저를 묶어 하나의 트랜잭션처럼 동작하게 만드는 방법입니다. 이를 통해 두 개 이상의 데이터베이스에서 발생하는 작업을 하나의 트랜잭션으로 처리할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ChainedTransactionManager 설정 예시&lt;/h4&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@Configuration
public class ChainedTransactionManagerConfig {

    @Bean(name = &quot;chainedTransactionManager&quot;)
    public ChainedTransactionManager transactionManager(
            @Qualifier(&quot;primaryTransactionManager&quot;) PlatformTransactionManager primaryTransactionManager,
            @Qualifier(&quot;secondaryTransactionManager&quot;) PlatformTransactionManager secondaryTransactionManager) {
        return new ChainedTransactionManager(primaryTransactionManager, secondaryTransactionManager);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✔️ ChainedTransactionManager를 이용한 트랜잭션 적용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 &lt;b&gt;ChainedTransactionManager&lt;/b&gt;를 설정한 후, 서비스 레이어에서 두 개의 데이터베이스를 호출할 때 트랜잭션을 적용할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시 서비스 클래스&lt;/h4&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@Service
public class UserService {

    @Autowired
    private PrimaryUserMapper primaryUserMapper;

    @Autowired
    private SecondaryUserMapper secondaryUserMapper;

    @Autowired
    private ChainedTransactionManager chainedTransactionManager;

    @Transactional(transactionManager = &quot;chainedTransactionManager&quot;)
    public void createUserInBothDB(User user) {
        primaryUserMapper.insertUser(user);   // Primary DB에 사용자 추가
        secondaryUserMapper.insertUser(user); // Secondary DB에 사용자 추가
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서는 &lt;code&gt;ChainedTransactionManager&lt;/code&gt;를 통해 &lt;b&gt;두 데이터베이스의 트랜잭션을 묶어&lt;/b&gt; 처리하게 됩니다. 하나의 데이터베이스에서 에러가 발생하면 전체 트랜잭션이 &lt;b&gt;롤백&lt;/b&gt;됩니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  요약&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Spring JDBC Template&lt;/b&gt;은 복잡한 JDBC 작업을 간소화해주는 유틸리티로, 데이터베이스와의 상호작용을 쉽게 할 수 있게 도와줍니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MyBatis&lt;/b&gt;는 SQL과 자바 객체 간의 매핑을 지원하는 프레임워크로, SQL 쿼리를 &lt;b&gt;XML 파일로 분리&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;에서 HikariCP와 같은 커넥션 풀을 사용하여 성능 최적화를 도모할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;두 개의 데이터베이스&lt;/b&gt;를 사용하는 경우, 각각의 &lt;b&gt;DataSource&lt;/b&gt;와 &lt;b&gt;트랜잭션 매니저&lt;/b&gt;를 설정해야 하며, 트랜잭션을 묶어 관리하려면 &lt;b&gt;ChainedTransactionManager&lt;/b&gt;를 사용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ChainedTransactionManager&lt;/b&gt;를 사용하면 두 개 이상의 데이터베이스에서 하나의 트랜잭션처럼 작업을 처리할 수 있어, 데이터 일관성을 유지할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>Java</category>
      <category>DB</category>
      <category>Java</category>
      <category>java db</category>
      <category>multiple jdbc</category>
      <category>mybatis</category>
      <category>mysql</category>
      <category>spring boot db</category>
      <category>spring database connection</category>
      <category>Spring DB</category>
      <category>spring 다중 db</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/261</guid>
      <comments>https://tae-hui.tistory.com/entry/JAVA-Spring-%EB%8F%8C%EC%95%84%EB%B3%B4%EA%B8%B0-Part3-JDBC-MyBatis#entry261comment</comments>
      <pubDate>Thu, 10 Oct 2024 08:00:25 +0900</pubDate>
    </item>
    <item>
      <title>[DB] JOIN과 EXISTS의 차이점</title>
      <link>https://tae-hui.tistory.com/entry/DB-JOIN%EA%B3%BC-EXISTS%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  1. 데이터베이스에서 &lt;code&gt;JOIN&lt;/code&gt;과 &lt;code&gt;EXISTS&lt;/code&gt;의 차이점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스 쿼리를 작성할 때, 데이터를 가져오기 위해 &lt;code&gt;JOIN&lt;/code&gt;을 사용할지, 아니면 특정 조건이 존재하는지만 확인하기 위해 &lt;code&gt;EXISTS&lt;/code&gt;를 사용할지 고민하게 되는 경우가 있습니다. &lt;code&gt;JOIN&lt;/code&gt;과 &lt;code&gt;EXISTS&lt;/code&gt;의 &lt;b&gt;차이점&lt;/b&gt;과 &lt;b&gt;각각의 사용 목적&lt;/b&gt;에 대해 자세히 알아보겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  2. &lt;code&gt;JOIN&lt;/code&gt;: 데이터를 가져오는 도구&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;JOIN&lt;/code&gt;은 말 그대로 &lt;b&gt;두 개 이상의 테이블을 합쳐서&lt;/b&gt; 원하는 데이터를 가져오는 데 사용됩니다. 데이터를 결합하여 새로운 결과를 반환할 때 주로 사용됩니다. &lt;code&gt;JOIN&lt;/code&gt;의 종류로는 &lt;code&gt;INNER JOIN&lt;/code&gt;, &lt;code&gt;LEFT JOIN&lt;/code&gt;, &lt;code&gt;RIGHT JOIN&lt;/code&gt;, &lt;code&gt;FULL JOIN&lt;/code&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;INNER JOIN&lt;/b&gt;: 두 테이블에서 &lt;b&gt;공통된 값&lt;/b&gt;이 있는 행만 가져옵니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LEFT JOIN&lt;/b&gt;: 왼쪽 테이블의 모든 데이터를 가져오고, 오른쪽 테이블에서 일치하는 데이터가 없는 경우 &lt;code&gt;NULL&lt;/code&gt;을 반환합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;RIGHT JOIN&lt;/b&gt;: &lt;code&gt;LEFT JOIN&lt;/code&gt;과 반대로, 오른쪽 테이블의 모든 데이터를 가져오고, 왼쪽 테이블에서 일치하지 않는 데이터는 &lt;code&gt;NULL&lt;/code&gt;로 반환됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;FULL JOIN&lt;/b&gt;: 왼쪽과 오른쪽 테이블의 모든 데이터를 가져오며, 일치하지 않는 부분은 &lt;code&gt;NULL&lt;/code&gt;로 채웁니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt; &amp;zwj;  &lt;code&gt;JOIN&lt;/code&gt; 예시&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 &lt;code&gt;JOIN&lt;/code&gt;을 사용하는 간단한 예시입니다:&lt;/p&gt;
&lt;pre class=&quot;n1ql&quot;&gt;&lt;code&gt;SELECT 
    a.name, 
    b.order_date
FROM 
    customers a
INNER JOIN 
    orders b
ON 
    a.customer_id = b.customer_id;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 쿼리는 &lt;code&gt;customers&lt;/code&gt; 테이블과 &lt;code&gt;orders&lt;/code&gt; 테이블을 &lt;code&gt;INNER JOIN&lt;/code&gt;으로 연결하여, 고객의 이름과 그 고객의 주문 날짜를 함께 가져오는 방식입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  3. &lt;code&gt;EXISTS&lt;/code&gt;: 존재 여부를 판단하는 도구&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;EXISTS&lt;/code&gt;는 &lt;b&gt;데이터의 존재 여부를 확인&lt;/b&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;h4 data-ke-size=&quot;size20&quot;&gt; &amp;zwj;  &lt;code&gt;EXISTS&lt;/code&gt; 예시&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 &lt;code&gt;EXISTS&lt;/code&gt;를 사용하는 간단한 예시입니다:&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;SELECT 
    name
FROM 
    customers a
WHERE 
    EXISTS (
        SELECT 
            1 
        FROM 
            orders b 
        WHERE 
            a.customer_id = b.customer_id
    );&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 쿼리는 &lt;code&gt;customers&lt;/code&gt; 테이블에서 고객이 존재하는지 확인하고, 그 고객이 &lt;code&gt;orders&lt;/code&gt; 테이블에 주문 기록이 있는지 여부만 확인합니다. 서브쿼리에서 조건을 만족하는 데이터가 존재하면 &lt;code&gt;EXISTS&lt;/code&gt;는 &lt;code&gt;TRUE&lt;/code&gt;를 반환하여 고객의 이름을 가져옵니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  4. &lt;code&gt;JOIN&lt;/code&gt;과 &lt;code&gt;EXISTS&lt;/code&gt;의 차이점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;JOIN&lt;/code&gt;과 &lt;code&gt;EXISTS&lt;/code&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;code&gt;JOIN&lt;/code&gt;: 데이터를 &lt;b&gt;가져오는&lt;/b&gt; 것이 목적입니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EXISTS&lt;/code&gt;: 데이터를 가져오는 것이 아니라, 데이터의 &lt;b&gt;존재 여부를 확인&lt;/b&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;code&gt;JOIN&lt;/code&gt;: 테이블 간의 데이터를 결합하여 새로운 결과를 반환합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EXISTS&lt;/code&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;code&gt;JOIN&lt;/code&gt;: 필요한 데이터를 가져오는 데 사용되지만, 큰 테이블을 연결할 경우 성능에 영향을 미칠 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EXISTS&lt;/code&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;code&gt;JOIN&lt;/code&gt;: 실제로 두 테이블에서 데이터를 결합하여 원하는 값을 가져올 때 사용합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EXISTS&lt;/code&gt;: 특정 조건에 맞는 데이터가 &lt;b&gt;존재하는지만&lt;/b&gt; 판단할 때 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  5. 정리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;JOIN&lt;/code&gt;은 &lt;b&gt;두 개 이상의 테이블을 결합&lt;/b&gt;하여 데이터를 가져오는 데 사용됩니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EXISTS&lt;/code&gt;는 데이터를 결합하지 않고, &lt;b&gt;특정 조건이 존재하는지만&lt;/b&gt; 확인합니다.&lt;/li&gt;
&lt;li&gt;성능 면에서 &lt;code&gt;JOIN&lt;/code&gt;은 대량의 데이터를 처리할 때 성능 저하가 있을 수 있지만, &lt;code&gt;EXISTS&lt;/code&gt;는 존재 여부만 확인하기 때문에 상대적으로 더 가볍습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상황에 따라 &lt;code&gt;JOIN&lt;/code&gt;과 &lt;code&gt;EXISTS&lt;/code&gt;를 적절히 사용하는 것이 중요합니다. 데이터 결합이 필요한 경우에는 &lt;code&gt;JOIN&lt;/code&gt;, 단순히 조건을 확인하려는 경우에는 &lt;code&gt;EXISTS&lt;/code&gt;를 사용하면 됩니다.&lt;/p&gt;</description>
      <category>DB</category>
      <category>DB</category>
      <category>db 조건</category>
      <category>Exists</category>
      <category>exists 사용법</category>
      <category>inner join</category>
      <category>Join</category>
      <category>left join</category>
      <category>mariadb</category>
      <category>mysql</category>
      <category>right join</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/260</guid>
      <comments>https://tae-hui.tistory.com/entry/DB-JOIN%EA%B3%BC-EXISTS%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90#entry260comment</comments>
      <pubDate>Fri, 27 Sep 2024 08:00:09 +0900</pubDate>
    </item>
    <item>
      <title>[Vue] 여러 컨테이너 다루기: v-show, v-if, :is, keep-alive</title>
      <link>https://tae-hui.tistory.com/entry/Vue-%EC%97%AC%EB%9F%AC-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EB%8B%A4%EB%A3%A8%EA%B8%B0-v-show-v-if-is-keep-alive</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Vue에서 여러 컨테이너 다루기: &lt;code&gt;v-show&lt;/code&gt;, &lt;code&gt;v-if&lt;/code&gt;, &lt;code&gt;:is&lt;/code&gt;, &lt;code&gt;keep-alive&lt;/code&gt; 사용법과 차이점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vue로 개발하다 보면, 여러 &lt;b&gt;컨테이너(컴포넌트)&lt;/b&gt;를 조건에 따라 보이거나 숨기거나, 동적으로 전환하는 상황이 자주 발생합니다. 이를 효과적으로 처리하기 위한 주요 도구로는 &lt;b&gt;&lt;code&gt;v-show&lt;/code&gt;&lt;/b&gt;, &lt;b&gt;&lt;code&gt;v-if&lt;/code&gt;&lt;/b&gt;, &lt;b&gt;&lt;code&gt;:is&lt;/code&gt;&lt;/b&gt;, 그리고 &lt;b&gt;&lt;code&gt;keep-alive&lt;/code&gt;&lt;/b&gt;가 있습니다. 이번 포스팅에서는 이들을 사용하는 방법과 각 방법의 차이, &lt;b&gt;동적 컴포넌트 전환 시 서로 다른 &lt;code&gt;props&lt;/code&gt; 전달 방법&lt;/b&gt;과 &lt;b&gt;동적으로 이벤트 메서드를 처리하는 방법&lt;/b&gt;을 포함해 자세히 알아보겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  1. &lt;code&gt;v-if&lt;/code&gt;: 조건에 따라 컴포넌트를 추가/제거&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;code&gt;v-if&lt;/code&gt;&lt;/b&gt;는 조건이 &lt;code&gt;true&lt;/code&gt;일 때 컴포넌트를 DOM에 &lt;b&gt;추가&lt;/b&gt;하고, 조건이 &lt;code&gt;false&lt;/code&gt;일 때 컴포넌트를 DOM에서 &lt;b&gt;완전히 제거&lt;/b&gt;합니다. 다시 렌더링될 때는 &lt;b&gt;초기 상태&lt;/b&gt;로 돌아가기 때문에, 컴포넌트를 추가/제거할 때 &lt;b&gt;상태가 초기화&lt;/b&gt;되는 것이 필요한 경우 적합합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;code&gt;v-if&lt;/code&gt; 예시 코드&lt;/h4&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;button @click=&quot;showComponent = !showComponent&quot;&amp;gt;컴포넌트 보이기/숨기기&amp;lt;/button&amp;gt;
    &amp;lt;ChildComponent v-if=&quot;showComponent&quot; /&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script&amp;gt;
import ChildComponent from './ChildComponent.vue';

export default {
  data() {
    return {
      showComponent: true
    };
  }
}
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&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;DOM에서 완전히 제거&lt;/b&gt;함으로써 성능 최적화 가능.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단점&lt;/b&gt;: 컴포넌트를 제거하고 다시 렌더링할 때 &lt;b&gt;상태가 초기화&lt;/b&gt;됨.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용 시기&lt;/b&gt;: &lt;b&gt;상태가 초기화&lt;/b&gt;되어야 할 때, 예를 들어 &lt;b&gt;폼 데이터 리셋&lt;/b&gt;이나 &lt;b&gt;탭 전환 시 페이지 초기화&lt;/b&gt;가 필요할 때 적합합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  2. &lt;code&gt;v-show&lt;/code&gt;: 상태 유지한 채 컴포넌트 숨기기/보이기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;code&gt;v-show&lt;/code&gt;&lt;/b&gt;는 컴포넌트를 DOM에 &lt;b&gt;유지&lt;/b&gt;한 채로, 조건에 따라 &lt;b&gt;CSS&lt;/b&gt;를 사용해 보이거나 숨깁니다. 컴포넌트는 계속 DOM에 남아 있으므로, &lt;b&gt;상태가 유지&lt;/b&gt;됩니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;code&gt;v-show&lt;/code&gt; 예시 코드&lt;/h4&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;button @click=&quot;isVisible = !isVisible&quot;&amp;gt;컴포넌트 보이기/숨기기&amp;lt;/button&amp;gt;
    &amp;lt;ChildComponent v-show=&quot;isVisible&quot; /&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script&amp;gt;
import ChildComponent from './ChildComponent.vue';

export default {
  data() {
    return {
      isVisible: true
    };
  }
}
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&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;b&gt;단점&lt;/b&gt;: 컴포넌트가 DOM에 계속 남아 있기 때문에, &lt;b&gt;메모리 사용량&lt;/b&gt;이 증가할 수 있음.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용 시기&lt;/b&gt;: &lt;b&gt;상태를 유지&lt;/b&gt;한 채로 빠르게 전환할 필요가 있을 때, 예를 들어 &lt;b&gt;탭 전환&lt;/b&gt;에서 탭의 상태가 그대로 유지되어야 하는 경우 적합합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  3. &lt;code&gt;:is&lt;/code&gt;: 동적으로 컴포넌트 전환 및 &lt;code&gt;props&lt;/code&gt; 동적 전달&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;code&gt;:is&lt;/code&gt;&lt;/b&gt;는 여러 컴포넌트 중 하나를 &lt;b&gt;동적으로 선택&lt;/b&gt;해서 렌더링할 수 있게 해줍니다. 또한, 동적으로 전환되는 컴포넌트에 서로 다른 &lt;b&gt;&lt;code&gt;props&lt;/code&gt;&lt;/b&gt;를 전달할 수 있습니다. 이를 통해 상황에 맞게 &lt;b&gt;다른 데이터&lt;/b&gt;를 각 컴포넌트에 넘겨줄 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;code&gt;:is&lt;/code&gt;와 &lt;code&gt;props&lt;/code&gt; 동적 전달 예시 코드&lt;/h4&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;button @click=&quot;changeComponent('Component1', { propA: '데이터 A' })&quot;&amp;gt;컴포넌트 1&amp;lt;/button&amp;gt;
    &amp;lt;button @click=&quot;changeComponent('Component2', { propB: '데이터 B' })&quot;&amp;gt;컴포넌트 2&amp;lt;/button&amp;gt;

    &amp;lt;!-- 동적 컴포넌트 및 props 전달 --&amp;gt;
    &amp;lt;component :is=&quot;currentComponent&quot; v-bind=&quot;currentProps&quot; /&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script&amp;gt;
import Component1 from './Component1.vue';
import Component2 from './Component2.vue';

export default {
  data() {
    return {
      currentComponent: 'Component1',
      currentProps: {
        propA: '데이터 A'
      }
    };
  },
  methods: {
    // 컴포넌트와 props 동적 변경
    changeComponent(component, props) {
      this.currentComponent = component;
      this.currentProps = props;
    }
  },
  components: {
    Component1,
    Component2
  }
}
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&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;b&gt;각 컴포넌트에 다른 &lt;code&gt;props&lt;/code&gt;를 동적으로 전달&lt;/b&gt;할 수 있음.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단점&lt;/b&gt;: 상태가 유지되지 않고, 전환될 때 &lt;b&gt;새로운 인스턴스&lt;/b&gt;가 생성됨. 이를 방지하기 위해 &lt;b&gt;&lt;code&gt;keep-alive&lt;/code&gt;&lt;/b&gt;를 사용해야 함.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용 시기&lt;/b&gt;: 동적으로 &lt;b&gt;여러 컴포넌트를 전환&lt;/b&gt;해야 하고, 각 컴포넌트에 &lt;b&gt;다른 데이터(&lt;code&gt;props&lt;/code&gt;)&lt;/b&gt;를 전달할 때 적합합니다. 예를 들어 &lt;b&gt;탭 전환 시 각 탭에 서로 다른 데이터를 보여줄 때&lt;/b&gt; 유용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  4. &lt;code&gt;keep-alive&lt;/code&gt;: 상태를 유지한 채 컴포넌트 캐싱&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;code&gt;keep-alive&lt;/code&gt;&lt;/b&gt;는 &lt;b&gt;캐싱된 컴포넌트&lt;/b&gt;의 상태를 유지하도록 도와줍니다. 동적으로 컴포넌트를 전환할 때, 컴포넌트가 &lt;b&gt;초기화되지 않고&lt;/b&gt; &lt;b&gt;이전 상태를 유지&lt;/b&gt;해야 한다면 &lt;b&gt;&lt;code&gt;keep-alive&lt;/code&gt;&lt;/b&gt;를 사용하면 됩니다. 컴포넌트를 전환해도 메모리에 &lt;b&gt;캐싱된 상태&lt;/b&gt;로 유지되기 때문에 성능 최적화에도 도움이 됩니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;code&gt;keep-alive&lt;/code&gt;와 &lt;code&gt;props&lt;/code&gt; 동적 전달 예시 코드&lt;/h4&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;button @click=&quot;changeComponent('Component1', { propA: '데이터 A' })&quot;&amp;gt;컴포넌트 1&amp;lt;/button&amp;gt;
    &amp;lt;button @click=&quot;changeComponent('Component2', { propB: '데이터 B' })&quot;&amp;gt;컴포넌트 2&amp;lt;/button&amp;gt;

    &amp;lt;!-- keep-alive로 상태 유지 --&amp;gt;
    &amp;lt;keep-alive&amp;gt;
      &amp;lt;component :is=&quot;currentComponent&quot; v-bind=&quot;currentProps&quot; /&amp;gt;
    &amp;lt;/keep-alive&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script&amp;gt;
import Component1 from './Component1.vue';
import Component2 from './Component2.vue';

export default {
  data() {
    return {
      currentComponent: 'Component1',
      currentProps: {
        propA: '데이터 A'
      }
    };
  },
  methods: {
    changeComponent(component, props) {
      this.currentComponent = component;
      this.currentProps = props;
    }
  },
  components: {
    Component1,
    Component2
  }
}
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&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;b&gt;다른 &lt;code&gt;props&lt;/code&gt;&lt;/b&gt;를 넘길 수 있음.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단점&lt;/b&gt;: 컴포넌트를 메모리에 &lt;b&gt;캐싱&lt;/b&gt;하므로, 많은 컴포넌트를 캐싱할 경우 메모리 사용량 증가 가능.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용 시기&lt;/b&gt;: &lt;b&gt;상태 유지&lt;/b&gt;가 필요하면서도 동적 컴포넌트 전환이 필요한 상황에서 사용. 예를 들어, &lt;b&gt;동적 페이지 전환&lt;/b&gt; 시 각 페이지의 &lt;b&gt;입력 상태&lt;/b&gt;나 &lt;b&gt;폼 데이터&lt;/b&gt;를 그대로 유지하고 싶을 때 적합합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  5. 동적 컴포넌트 전환 시 &lt;code&gt;$emit&lt;/code&gt; 메서드도 동적으로 변경하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동적으로 컴포넌트를 전환할 때, &lt;b&gt;이벤트(&lt;code&gt;$emit&lt;/code&gt;)&lt;/b&gt;를 통해 &lt;b&gt;상위 컴포넌트에 데이터를 전달&lt;/b&gt;해야 할 때가 많습니다. 이때 &lt;b&gt;컴포넌트에 따라 이벤트 메서드도 동적으로 변경&lt;/b&gt;할 필요가 있습니다. 컴포넌트를 전환하면서 해당 컴포넌트에서 발생하는 이벤트를 &lt;b&gt;동적으로 처리&lt;/b&gt;할 수 있도록 설정할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  동적 컴포넌트에서 이벤트 처리 예시&lt;/h4&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;button @click=&quot;currentComponent = 'Component1'&quot;&amp;gt;컴포넌트 1&amp;lt;/button&amp;gt;
    &amp;lt;button @click=&quot;currentComponent = 'Component2'&quot;&amp;gt;컴포넌트 2&amp;lt;/button&amp;gt;

    &amp;lt;component :is=&quot;currentComponent&quot; @custom-event=&quot;handleEvent&quot; /&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script&amp;gt;
import Component1 from './Component1.vue';
import Component2 from './Component2.vue';

export default {
  data() {
    return {
      currentComponent: 'Component1'
    };
  },
  methods: {
    handleEvent(data) {
      // 이벤트에 따라 처리할 동작을 설정
      if (this.currentComponent === 'Component1') {
        console.log('Component1 이벤트:', data);
      } else if (this.currentComponent === 'Component2') {
        console.log('Component2 이벤트:', data);
      }
    }
  },
  components: {
    Component1,
    Component2
  }
}
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&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;b&gt;동일한 이벤트 리스너&lt;/b&gt;를 사용하되, &lt;b&gt;컴포넌트에 따라 다른 동작&lt;/b&gt;을 수행할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  6. v-if, v-show, :is, keep-alive 차이점 요약&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&amp;nbsp;&lt;/th&gt;
&lt;th&gt;&amp;nbsp;&lt;/th&gt;
&lt;th&gt;&amp;nbsp;&lt;/th&gt;
&lt;th&gt;&amp;nbsp;&lt;/th&gt;
&lt;th&gt;&amp;nbsp;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;v-if&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;v-show&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;:is&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;keep-alive&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;렌더링 동작&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;조건에 따라 컴포넌트를 DOM에서 제거&lt;/td&gt;
&lt;td&gt;컴포넌트를 DOM에 유지, CSS로 숨김&lt;/td&gt;
&lt;td&gt;동적으로 컴포넌트를 교체&lt;/td&gt;
&lt;td&gt;상태를 유지하면서 컴포넌트를 캐싱&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;상태 유지 여부&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;컴포넌트가 제거되면 상태 초기화&lt;/td&gt;
&lt;td&gt;상태가 유지됨&lt;/td&gt;
&lt;td&gt;상태가 유지되지 않음 (keep-alive 필요)&lt;/td&gt;
&lt;td&gt;상태 유지됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;DOM 제거 여부&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;컴포넌트가 DOM에서 완전히 제거됨&lt;/td&gt;
&lt;td&gt;컴포넌트는 DOM에 남아 있음&lt;/td&gt;
&lt;td&gt;컴포넌트가 동적으로 전환됨&lt;/td&gt;
&lt;td&gt;컴포넌트가 메모리에 남아 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;성능&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;자주 변경 시 성능 저하 가능성 있음&lt;/td&gt;
&lt;td&gt;성능에 유리&lt;/td&gt;
&lt;td&gt;성능에 유리, keep-alive로 상태 유지 필요&lt;/td&gt;
&lt;td&gt;성능 최적화, 메모리 사용량 증가 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;사용 목적&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;상태가 초기화되어야 할 때&lt;/td&gt;
&lt;td&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&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;code&gt;v-if&lt;/code&gt;&lt;/b&gt;: 컴포넌트를 조건에 따라 &lt;b&gt;추가/제거&lt;/b&gt;하며, &lt;b&gt;상태가 초기화&lt;/b&gt;됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;v-show&lt;/code&gt;&lt;/b&gt;: 상태를 유지한 채 &lt;b&gt;DOM에서 컴포넌트를 숨기거나 보이기&lt;/b&gt;만 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;:is&lt;/code&gt;&lt;/b&gt;: 여러 컴포넌트를 동적으로 &lt;b&gt;전환&lt;/b&gt;할 수 있으며, &lt;b&gt;다른 &lt;code&gt;props&lt;/code&gt;&lt;/b&gt;를 동적으로 전달할 수 있습니다. 상태가 초기화되며, &lt;b&gt;&lt;code&gt;keep-alive&lt;/code&gt;&lt;/b&gt;로 상태를 유지할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;keep-alive&lt;/code&gt;&lt;/b&gt;: &lt;b&gt;상태를 유지&lt;/b&gt;하면서 동적으로 컴포넌트를 전환할 때 사용합니다. 상태가 캐싱되며, 성능 최적화에 도움을 줍니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;동적 컴포넌트에서 &lt;code&gt;$emit&lt;/code&gt;&lt;/b&gt;을 통해 상위 컴포넌트로 이벤트를 전달할 때는 &lt;b&gt;동적으로 변경된 컴포넌트&lt;/b&gt;의 이벤트를 상위 컴포넌트에서 &lt;b&gt;동적으로 처리&lt;/b&gt;할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>WEB</category>
      <category>:is</category>
      <category>keep-alive</category>
      <category>v-if</category>
      <category>v-show</category>
      <category>VUE</category>
      <category>vue keep-alive</category>
      <category>vue 컴포넌트</category>
      <category>vue 컴포넌트 관리</category>
      <category>vue 컴포넌트 관리법</category>
      <category>vue 컴포넌트 히스토리 캐시</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/259</guid>
      <comments>https://tae-hui.tistory.com/entry/Vue-%EC%97%AC%EB%9F%AC-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EB%8B%A4%EB%A3%A8%EA%B8%B0-v-show-v-if-is-keep-alive#entry259comment</comments>
      <pubDate>Wed, 25 Sep 2024 08:00:45 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] Spring Boot Redis 캐시 사용법</title>
      <link>https://tae-hui.tistory.com/entry/JAVA-Spring-Boot-Redis-%EC%BA%90%EC%8B%9C-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.  ️ Redis란 무엇인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis는 &lt;b&gt;Remote Dictionary Server&lt;/b&gt;의 약자로, 데이터를 &lt;b&gt;메모리&lt;/b&gt;에 저장하여 빠르게 처리하는 &lt;b&gt;오픈 소스 인메모리 데이터베이스&lt;/b&gt;입니다. &lt;b&gt;Key-Value 구조&lt;/b&gt;로 데이터를 저장하고, 다양한 데이터 구조를 지원하는 것이 특징입니다. 일반적인 데이터베이스와 달리 데이터를 디스크가 아닌 메모리에 저장하기 때문에 읽기/쓰기 속도가 매우 빠릅니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Redis의 특징&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;: Redis는 모든 데이터를 메모리에 저장하고, 필요에 따라 데이터를 디스크에 백업할 수 있습니다. 이로 인해 데이터 처리 속도가 매우 빠르며, 특히 &lt;b&gt;실시간 데이터 처리&lt;/b&gt;에 유리합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;다양한 데이터 구조 지원&lt;/b&gt;: Redis는 단순한 Key-Value 저장소일 뿐만 아니라, 다양한 데이터 타입을 지원합니다. 그 예로 다음과 같은 타입이 있습니다:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;String&lt;/b&gt;: 가장 기본적인 형태로, 문자열 데이터를 저장합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;List&lt;/b&gt;: 연결 리스트 형태로, 순서가 있는 여러 값을 저장할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Set&lt;/b&gt;: 중복되지 않는 값들의 집합을 저장할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Hash&lt;/b&gt;: 필드와 값의 쌍으로 데이터를 저장할 수 있습니다. 흔히 자바의 Map과 비슷한 구조입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Sorted Set&lt;/b&gt;: 점수를 기반으로 정렬된 집합을 관리할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;지속성 옵션&lt;/b&gt;: Redis는 메모리에 저장된 데이터를 디스크에 백업할 수 있는 옵션을 제공합니다. 이를 통해 &lt;b&gt;RDB 스냅샷&lt;/b&gt;과 &lt;b&gt;AOF(Append-Only File)&lt;/b&gt;와 같은 방식으로 데이터를 영구적으로 저장하거나 복구할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스케일링과 확장성&lt;/b&gt;: Redis는 &lt;b&gt;클러스터링&lt;/b&gt;을 지원하여 여러 노드에 데이터를 분산시켜 저장하고 처리할 수 있습니다. 이를 통해 &lt;b&gt;수평 확장&lt;/b&gt;이 가능하며, 큰 데이터를 분산 저장할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Pub/Sub 기능&lt;/b&gt;: Redis는 &lt;b&gt;발행-구독(Pub/Sub)&lt;/b&gt; 패턴을 지원해, 메시지 브로커로도 사용할 수 있습니다. 실시간 채팅, 알림 서비스 같은 시스템에서 사용됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Atomic Operation&lt;/b&gt;: Redis는 모든 명령이 &lt;b&gt;원자적(atomic)&lt;/b&gt;으로 실행되기 때문에, 복수의 클라이언트가 동시에 동일한 데이터에 접근해도 안전하게 처리됩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Redis의 주요 용도&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;: Redis는 주로 캐시로 사용됩니다. 서버의 데이터를 Redis에 캐싱함으로써, 데이터베이스에 대한 접근을 줄이고 성능을 향상시킬 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;세션 저장소&lt;/b&gt;: 사용자의 세션을 Redis에 저장해 빠르게 접근하고, 여러 서버에서 공유할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;순위 시스템&lt;/b&gt;: Sorted Set을 사용하여 점수 기반의 순위를 관리할 수 있습니다. 예를 들어, 게임 리더보드나 인기 콘텐츠 순위를 만들 때 유용합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실시간 분석&lt;/b&gt;: 실시간 데이터를 빠르게 처리할 수 있는 특징 때문에, 로그 처리나 실시간 모니터링 시스템에서도 Redis를 많이 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.   Redis의 동작 원리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis는 데이터를 &lt;b&gt;메모리&lt;/b&gt;에 저장하기 때문에, 매우 빠른 읽기 및 쓰기 성능을 제공합니다. 모든 데이터는 메모리에 상주하며, 필요에 따라 &lt;b&gt;비동기적으로 디스크에 저장&lt;/b&gt;하여 데이터를 영구적으로 유지할 수 있습니다. Redis의 동작 원리는 다음과 같습니다:&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;Key-Value 데이터 저장&lt;/b&gt;: Redis는 Key-Value 방식으로 데이터를 저장하며, 이를 통해 빠르게 데이터를 찾고 처리할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비동기적 데이터 영속성&lt;/b&gt;: Redis는 메모리에 데이터를 저장하지만, &lt;b&gt;RDB(Redis Database Backup)&lt;/b&gt; 방식이나 &lt;b&gt;AOF(Append-Only File)&lt;/b&gt; 방식을 사용해 데이터를 디스크에 저장하여 영구성을 제공합니다. RDB 방식은 일정 주기로 데이터를 저장하고, AOF 방식은 명령어 단위로 로그를 기록해 복구 시 이 명령을 다시 실행합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;원자적 작업&lt;/b&gt;: Redis의 모든 명령은 &lt;b&gt;원자적&lt;/b&gt;으로 처리됩니다. 즉, 동시에 여러 클라이언트가 같은 데이터에 접근하더라도 데이터 손상이 없으며, 항상 일관된 상태를 유지합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Pub/Sub 동작 원리&lt;/b&gt;: Redis의 Pub/Sub 기능은 메시지를 발행자와 구독자가 직접적으로 연결해주는 방식입니다. 발행자는 특정 채널에 메시지를 보내고, 구독자는 해당 채널에 연결된 모든 메시지를 수신합니다. 실시간 알림이나 채팅 같은 시스템에 적합합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.   Spring에서 Redis 사용 방법 (Maven)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Redis의 동작 원리를 이해했다면, Spring에서 Redis를 어떻게 사용하는지 살펴보겠습니다. Spring에서는 &lt;b&gt;Spring Data Redis&lt;/b&gt;를 통해 Redis와 쉽게 연동할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Maven 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;pom.xml&lt;/b&gt; 파일에 아래와 같이 의존성을 추가합니다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&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;p data-ke-size=&quot;size16&quot;&gt;그리고, &lt;b&gt;application.yml&lt;/b&gt; 파일에 Redis 서버 정보를 설정합니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;spring:
  redis:
    host: localhost
    port: 6379&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Spring에서 Redis를 사용할 준비가 완료되었습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  캐시 어노테이션 사용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring은 Redis와의 캐시 통합을 돕기 위해 몇 가지 유용한 어노테이션을 제공합니다:&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;@Cacheable&lt;/b&gt;: 메서드 실행 전에 캐시를 먼저 확인하고, 데이터가 없으면 메서드를 실행해 결과를 캐시에 저장합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;@CachePut(value = &quot;users&quot;, key = &quot;#user.id&quot;)
public User updateUser(User user) {
    // 메서드가 실행되고, 그 결과가 캐시에 저장됨
    return userRepository.save(user);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;@CachePut&lt;/b&gt;: 메서드를 항상 실행하고 그 결과를 캐시에 업데이트합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;@CachePut(value = &quot;users&quot;, key = &quot;#user.id&quot;)
public User updateUser(User user) {
    // 메서드가 실행되고, 그 결과가 캐시에 저장됨
    return userRepository.save(user);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;@CacheEvict&lt;/b&gt;: 캐시에서 데이터를 삭제할 때 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@CacheEvict(value = &quot;users&quot;, key = &quot;#userId&quot;)
public void deleteUser(String userId) {
    // 캐시에서 해당 유저 데이터가 삭제됨
    userRepository.deleteById(userId);
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.   직렬화와 역직렬화 방식 (Java, Jackson)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis는 데이터를 저장할 때 직렬화 방식으로 데이터를 변환하여 메모리에 저장합니다. Spring에서 Redis와 데이터를 주고받을 때는 &lt;b&gt;Java 직렬화&lt;/b&gt; 또는 &lt;b&gt;JSON 직렬화(Jackson)&lt;/b&gt;를 주로 사용합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Java 직렬화&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Java 직렬화는 클래스가 &lt;b&gt;Serializable&lt;/b&gt; 인터페이스를 구현해야 합니다.&lt;/li&gt;
&lt;li&gt;Java 직렬화는 데이터가 클 수 있다는 단점이 있으며, 클래스 구조가 변경되면 이전에 직렬화된 데이터를 읽을 때 문제가 발생할 수 있습니다. 이때 &lt;b&gt;serialVersionUID&lt;/b&gt;를 설정해주면 클래스의 변경 여부를 체크해 호환성을 보장할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Jackson을 사용한 JSON 직렬화&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Jackson을 사용하면 데이터를 &lt;b&gt;JSON&lt;/b&gt; 형식으로 직렬화해 저장할 수 있으며, 다른 시스템과의 &lt;b&gt;호환성&lt;/b&gt;이 좋습니다.&lt;/li&gt;
&lt;li&gt;Jackson 직렬화에서는 &lt;b&gt;serialVersionUID&lt;/b&gt;가 필요하지 않으며, 필드 이름과 구조를 기반으로 직렬화 및 역직렬화가 진행됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Jackson 직렬화 설정 예시&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;public class RedisConfig {
    @Bean
    public RedisTemplate&amp;lt;String, Object&amp;gt; redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate&amp;lt;String, Object&amp;gt; template = new RedisTemplate&amp;lt;&amp;gt;();
        template.setConnectionFactory(connectionFactory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new Jackson2JsonRedisSerializer&amp;lt;&amp;gt;(Object.class));
        return template;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.  &amp;zwj;  Redis 사용 시 발생할 수 있는 오류와 처리 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis를 사용하다 보면 다양한 오류가 발생할 수 있습니다. 그중 자주 발생하는 오류를 살펴보고, 그 처리 방법을 제시합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. &lt;b&gt;직렬화/역직렬화 오류 (SerializationException)&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;발생 상황&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis에 데이터를 직렬화하여 저장하는 과정에서 &lt;b&gt;직렬화 포맷&lt;/b&gt;이 잘못되면 데이터를 읽을 때 문제가 발생할 수 있습니다. 이런 경우, &lt;b&gt;SerializationException&lt;/b&gt;과 같은 예외가 발생할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;처리 방법&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직렬화 오류는 &lt;b&gt;Redis 캐시 관련 메서드&lt;/b&gt;에서 발생할 수 있으며, 이를 &lt;b&gt;트라이-캐치 블록&lt;/b&gt;으로 감싸서 오류를 처리하고, 캐시에서 데이터를 읽지 못하면 DB에서 데이터를 불러오는 방식으로 처리할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;@Cacheable(value = &quot;users&quot;, key = &quot;#userId&quot;)
public User getUserFromCache(String userId) {
    try {
        // Redis에서 데이터를 가져옴
        return (User) redisTemplate.opsForValue().get(userId);
    } catch (SerializationException e) {
        // 직렬화 오류 처리
        logger.error(&quot;Serialization error when getting user from cache&quot;, e);
        // 캐시에서 데이터를 읽을 수 없을 경우, DB에서 조회
        return userRepository.findById(userId).orElse(null);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. &lt;b&gt;Connection 오류 (RedisConnectionFailureException)&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;발생 상황&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis 서버와의 네트워크 문제로 인해 발생하는 오류입니다. Redis 서버가 중지되거나, 네트워크 연결이 끊어진 경우 발생할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;처리 방법&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis 연결 문제는 &lt;b&gt;LettuceConnectionFactory&lt;/b&gt;와 같은 Redis 연결 설정에서 처리할 수 있습니다. &lt;b&gt;연결 상태를 주기적으로 확인&lt;/b&gt;하여 문제가 발생할 경우 다시 연결을 시도할 수 있도록 구성할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;@Bean
public LettuceConnectionFactory redisConnectionFactory() {
    LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory();
    connectionFactory.setValidateConnection(true);  // 연결 상태를 주기적으로 확인
    return connectionFactory;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. &lt;b&gt;모델 클래스와 캐시 데이터의 불일치 (ClassCastException)&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;발생 상황&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델 클래스의 필드가 변경되거나 삭제된 경우, Redis에 저장된 데이터와의 불일치로 인해 &lt;b&gt;ClassCastException&lt;/b&gt;이 발생할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;처리 방법&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델 클래스에 &lt;b&gt;@JsonIgnoreProperties(ignoreUnknown = true)&lt;/b&gt; 어노테이션을 사용하여, &lt;b&gt;알 수 없는 필드를 무시&lt;/b&gt;하고 역직렬화를 안전하게 수행할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;@JsonIgnoreProperties(ignoreUnknown = true)
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    private String id;
    private String name;
    private int age;

    // 기본 생성자, getter, setter
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;트라이-캐치와 메서드 실행&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring에서 Redis 캐시를 사용할 때 &lt;b&gt;@Cacheable&lt;/b&gt;이나 &lt;b&gt;@CachePut&lt;/b&gt;을 사용하는 경우, Redis에서 &lt;b&gt;오류가 발생하더라도 메서드 자체는&lt;/b&gt; 계속 실행됩니다. 캐시에서 데이터를 찾지 못하면 메서드의 비즈니스 로직이 실행되고, 그 결과가 캐시에 저장됩니다. 그러나 Redis 오류는 로그에 남게 되므로, 트라이-캐치 블록을 사용해 오류 처리를 통해 안정성을 높일 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;Redis는 빠른 성능의 인메모리 데이터베이스&lt;/b&gt;로, 다양한 데이터 구조와 캐시, 세션 관리, 실시간 데이터 처리에 적합합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Spring Data Redis&lt;/b&gt;를 통해 쉽게 Redis와 연동할 수 있으며, &lt;b&gt;@Cacheable, @CachePut, @CacheEvict&lt;/b&gt; 등의 어노테이션을 사용해 캐시 관리를 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Redis 오류&lt;/b&gt;는 직렬화/역직렬화 오류, 연결 문제, 타입 불일치 등의 문제가 있을 수 있으며, 이를 적절한 &lt;b&gt;예외 처리&lt;/b&gt;와 &lt;b&gt;어노테이션&lt;/b&gt;으로 관리해야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;트라이-캐치 블록&lt;/b&gt;을 통해 Redis 오류가 발생하더라도 서비스가 정상적으로 동작하도록 처리할 수 있으며, 캐시와 비즈니스 로직이 원활하게 연결됩니다.&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>Java</category>
      <category>cashe</category>
      <category>Java</category>
      <category>java redis</category>
      <category>Redis</category>
      <category>redis cashe</category>
      <category>Redis 사용법</category>
      <category>Spring</category>
      <category>spring boot</category>
      <category>Spring Boot Redis</category>
      <category>자바 레디스</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/258</guid>
      <comments>https://tae-hui.tistory.com/entry/JAVA-Spring-Boot-Redis-%EC%BA%90%EC%8B%9C-%EC%82%AC%EC%9A%A9%EB%B2%95#entry258comment</comments>
      <pubDate>Fri, 20 Sep 2024 08:00:49 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] Spring 돌아보기 Part.2 #AOP</title>
      <link>https://tae-hui.tistory.com/entry/JAVA-AOP-%EB%9E%80-Spring</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Aspect-Oriented Programming (AOP)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AOP는 &lt;b&gt;관심사 분리&lt;/b&gt;를 목표로 하는 프로그래밍 패러다임입니다. 특히 &lt;b&gt;로깅, 트랜잭션 관리, 보안&lt;/b&gt;과 같은 &lt;b&gt;횡단 관심사(Cross-cutting concerns)&lt;/b&gt;를 비즈니스 로직에서 분리할 수 있도록 해줍니다. 비즈니스 로직과는 상관없는 코드가 곳곳에 중복해서 나타나는 문제를 해결해주기 때문에 유지보수성을 크게 향상시킬 수 있습니다. 이를 통해 비즈니스 로직은 본연의 역할에만 집중할 수 있게 되는 거죠.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  AOP의 핵심 개념&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;횡단 관심사(Cross-Cutting Concern)&lt;/b&gt;: 애플리케이션 전반에 걸쳐 사용되는 공통된 기능들. 예를 들어, 로깅, 트랜잭션 관리, 보안 처리 같은 것들이 이에 해당됩니다. 이들은 각 모듈에서 필요로 하지만, 해당 모듈의 핵심 기능은 아닙니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Aspect&lt;/b&gt;: 횡단 관심사 로직을 모듈화한 것을 &lt;b&gt;Aspect&lt;/b&gt;라고 부릅니다. 예를 들어, 트랜잭션 관리, 로깅 등을 각각의 Aspect로 만들 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Advice&lt;/b&gt;: Aspect가 언제, 어떻게 적용될지 정의하는 동작입니다. &lt;b&gt;Before, After, Around&lt;/b&gt;와 같은 여러 타입이 존재합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Join Point&lt;/b&gt;: Aspect가 적용될 수 있는 &lt;b&gt;지점&lt;/b&gt;을 의미합니다. 예를 들어, 메서드 호출이나 예외 처리 등이 이에 해당합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Pointcut&lt;/b&gt;: &lt;b&gt;Join Point&lt;/b&gt; 중 실제로 &lt;b&gt;Advice&lt;/b&gt;가 실행되는 지점을 선택하는 것입니다. AOP에서는 특정 메서드나 클래스에만 Aspect가 적용되도록 Pointcut을 정의합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.   AOP 설정 및 적용 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AOP를 적용하려면, 기본적으로 Spring이 AOP를 인식하고 관리할 수 있도록 설정이 필요합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) AOP 의존성 추가 (Maven 예시)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 Spring 프로젝트에서는 AOP 의존성이 기본적으로 포함되어 있지만, 만약 그렇지 않다면 다음과 같이 의존성을 추가해줘야 합니다:&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&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-aop&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) AOP 기능 활성화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;@EnableAspectJAutoProxy&lt;/code&gt; 어노테이션을 통해 Spring의 AOP 기능을 활성화할 수 있습니다. &lt;b&gt;Spring Boot&lt;/b&gt; 프로젝트에서는 보통 &lt;b&gt;Application 클래스&lt;/b&gt;에 이 어노테이션을 추가합니다:&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@SpringBootApplication
@EnableAspectJAutoProxy  // AOP 기능 활성화
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) &lt;code&gt;@Aspect&lt;/code&gt;로 AOP 적용하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Aspect를 만들기 위해서는 &lt;code&gt;@Aspect&lt;/code&gt; 어노테이션을 사용해 클래스를 정의하고, 해당 클래스를 &lt;b&gt;Spring 컨텍스트에 등록&lt;/b&gt;해야 합니다. 일반적으로 &lt;code&gt;@Component&lt;/code&gt;나 &lt;code&gt;@Bean&lt;/code&gt;을 사용하여 Spring이 관리하는 Bean으로 등록합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;로깅을 위한 AOP 예시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 메서드 호출 전에 로그를 남기는 &lt;b&gt;Aspect&lt;/b&gt;를 정의한 예시입니다:&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@Aspect
@Component  // Spring Bean으로 등록
public class LoggingAspect {

    @Before(&quot;execution(* com.example.service.*.*(..))&quot;)
    public void logBefore(JoinPoint joinPoint) {
        System.out.println(&quot;메서드 실행 전: &quot; + joinPoint.getSignature().getName());
    }

    @After(&quot;execution(* com.example.service.*.*(..))&quot;)
    public void logAfter(JoinPoint joinPoint) {
        System.out.println(&quot;메서드 실행 후: &quot; + joinPoint.getSignature().getName());
    }

    @AfterThrowing(&quot;execution(* com.example.service.*.*(..))&quot;)
    public void rollbackTransaction() {
      // 트랜잭션 롤백 로직
      System.out.println(&quot;트랜잭션 롤백&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;code&gt;@Before&lt;/code&gt;&lt;/b&gt; 어노테이션을 통해 메서드 실행 &lt;b&gt;전&lt;/b&gt;에 로그를 출력하는 Advice를 적용했고,&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;@After&lt;/code&gt;&lt;/b&gt; 어노테이션으로 메서드가 끝난 후에도 로그를 출력하게 했습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;@AfterThrowing&lt;/code&gt;&lt;/b&gt; 어노테이션으로 예외처리 시 로그를 출력하게 했습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;execution(* com.example.service.*.*(..))&lt;/code&gt;는 &lt;b&gt;Pointcut 표현식&lt;/b&gt;으로, &lt;code&gt;com.example.service&lt;/code&gt; 패키지의 모든 클래스의 모든 메서드에 대해 AOP를 적용한다는 의미입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4) 트랜잭션을 위한 AOP 예시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜잭션 관리도 AOP로 처리할 수 있습니다. 하지만 일반적으로는 Spring이 제공하는 &lt;code&gt;@Transactional&lt;/code&gt; 어노테이션을 사용하는 것이 편리합니다.&lt;/p&gt;
&lt;pre class=&quot;axapta&quot;&gt;&lt;code&gt;@Service
public class OrderService {

    @Transactional
    public void placeOrder(Order order) {
        // 주문 상태 확인
        if (!order.isValid()) {
            throw new IllegalStateException(&quot;주문 상태가 유효하지 않습니다.&quot;);
        }

        // 주문 처리 로직
        orderRepository.save(order);

        // 기타 비즈니스 로직
        // 트랜잭션은 알아서 커밋됩니다.
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드에서 &lt;code&gt;@Transactional&lt;/code&gt; 어노테이션을 사용하면, Spring이 내부적으로 AOP를 이용해 트랜잭션을 관리합니다. &lt;b&gt;비즈니스 로직&lt;/b&gt;이 정상적으로 완료되면 트랜잭션이 &lt;b&gt;커밋&lt;/b&gt;되고, 예외가 발생하면 &lt;b&gt;롤백&lt;/b&gt;됩니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.   정리&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;AOP&lt;/b&gt;는 로깅, 트랜잭션 관리 같은 공통 기능을 비즈니스 로직과 분리하여 더 깔끔한 코드를 작성할 수 있게 해줍니다. 이를 통해 중복 코드를 제거하고, 유지보수가 쉬워집니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Spring AOP&lt;/b&gt;는 &lt;code&gt;@Aspect&lt;/code&gt;, &lt;code&gt;@Before&lt;/code&gt;, &lt;code&gt;@After&lt;/code&gt; 같은 어노테이션을 사용해 쉽게 적용할 수 있으며, AOP 기능을 활성화하려면 &lt;b&gt;&lt;code&gt;@EnableAspectJAutoProxy&lt;/code&gt;&lt;/b&gt; 어노테이션을 추가해줘야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;트랜잭션 관리&lt;/b&gt;는 보통 &lt;code&gt;@Transactional&lt;/code&gt; 어노테이션을 사용하여 Spring에서 자동으로 처리할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>Java</category>
      <category>@Aspect</category>
      <category>aop</category>
      <category>Aspect-Oriented Programming</category>
      <category>Java</category>
      <category>java aop</category>
      <category>Spring</category>
      <category>Spring AOP</category>
      <category>관점지향</category>
      <category>관점지향 프로그래밍</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/257</guid>
      <comments>https://tae-hui.tistory.com/entry/JAVA-AOP-%EB%9E%80-Spring#entry257comment</comments>
      <pubDate>Thu, 12 Sep 2024 08:00:28 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] Spring 돌아보기 Part.1 #IoC #DI #싱글톤</title>
      <link>https://tae-hui.tistory.com/entry/JAVA-Spring-%EB%8F%8C%EC%95%84%EB%B3%B4%EA%B8%B0-IoC-DI-%EC%8B%B1%EB%93%A4%ED%86%A4</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.   &lt;b&gt;Spring의 IoC와 DI, 그리고 그 동작 방식&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring에서 중요한 개념 중 하나는 &lt;b&gt;IoC(제어의 역전)&lt;/b&gt;과 &lt;b&gt;DI(의존성 주입)&lt;/b&gt;입니다. 이 두 가지는 결국 &lt;b&gt;객체의 생성과 관리&lt;/b&gt;를 누가 담당하는지에 관한 이야기입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래는 개발자가 프로그램 내에서 필요한 객체를 직접 생성하고, 의존 관계가 있으면 그 관계를 직접 설정해야 했습니다. 하지만 &lt;b&gt;Spring&lt;/b&gt;은 &lt;b&gt;IoC&lt;/b&gt;를 통해 이런 책임을 프레임워크가 맡습니다. 쉽게 말하면, 객체 생성과 의존성 관리의 제어권을 Spring이 가져가고, 개발자는 비즈니스 로직에만 집중할 수 있게 되는 것이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;IoC&lt;/b&gt; 개념을 구현하는 방법이 바로 &lt;b&gt;DI(Dependency Injection)&lt;/b&gt;입니다. &lt;b&gt;DI&lt;/b&gt;는 말 그대로 객체가 필요한 의존성을 외부에서 주입받는 방식입니다. 예를 들어, 서비스 클래스가 &lt;code&gt;Repository&lt;/code&gt;를 필요로 한다면, 개발자가 직접 &lt;code&gt;Repository&lt;/code&gt; 객체를 생성하지 않고, Spring이 이를 자동으로 주입해줍니다. 코드를 한번 보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@Component
public class UserService {

    private final UserRepository userRepository;

    // 생성자 주입 방식
    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void createUser(User user) {
        userRepository.save(user);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;UserService&lt;/code&gt;는 &lt;code&gt;UserRepository&lt;/code&gt;를 필요로 하고, 우리는 생성자 주입 방식으로 이를 받아들입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;@Autowired&lt;/code&gt; 어노테이션 덕분에 Spring이 &lt;code&gt;UserRepository&lt;/code&gt; 빈을 자동으로 찾아 주입해주는 것이죠. &lt;b&gt;이것이 바로 DI(의존성 주입)&lt;/b&gt;입니다. 이렇게 Spring이 객체 간의 의존성을 해결해주면, 개발자는 각 객체가 어떤 구체적인 클래스에 의존하는지 알 필요가 없고, 결과적으로 &lt;b&gt;객체 간의 결합도&lt;/b&gt;가 낮아지며 코드가 더 유연해집니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.   &lt;b&gt;의존성 주입의 효과와 적용 시점&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;DI&lt;/b&gt;는 객체 간 결합도를 낮추는 데 큰 역할을 합니다. 만약 &lt;code&gt;UserService&lt;/code&gt;가 &lt;code&gt;UserRepository&lt;/code&gt;를 직접 생성하는 코드라면 다음과 같이 작성될 겁니다.&lt;/p&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;public class UserService {

    private final UserRepository userRepository = new UserRepository();

    public void createUser(User user) {
        userRepository.save(user);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드에서는 &lt;code&gt;UserService&lt;/code&gt;가 &lt;code&gt;UserRepository&lt;/code&gt;의 구체적인 구현에 의존하고 있습니다. 만약 &lt;code&gt;UserRepository&lt;/code&gt;의 구현이 변경되거나 새로운 타입의 &lt;code&gt;Repository&lt;/code&gt;가 필요해진다면, &lt;code&gt;UserService&lt;/code&gt;도 함께 수정해야 합니다. 이렇게 &lt;b&gt;직접 객체를 생성&lt;/b&gt;하는 방식은 클래스 간 결합도를 높이고, 변경이 발생할 때 유연성이 떨어지게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 DI를 사용하면 클래스 내부에서 구체적인 구현을 몰라도 됩니다. Spring이 외부에서 의존성을 주입해주기 때문에, &lt;code&gt;UserService&lt;/code&gt;는 &lt;code&gt;UserRepository&lt;/code&gt;의 구체적인 동작을 몰라도 됩니다. 결과적으로 클래스는 인터페이스나 추상 클래스에만 의존하게 되어, 이후에 변경이 생기더라도 쉽게 처리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &lt;b&gt;Spring에서는 의존성 주입이 애플리케이션 실행 시 자동으로&lt;/b&gt; 이루어집니다. Spring이 애플리케이션을 구동하면서 필요한 빈들을 미리 생성하고, 주입을 완료하기 때문에 개발자가 직접 신경 쓰지 않아도 됩니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3.   &lt;b&gt;DI를 활용한 다양한 주입 방식&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring에서는 주입 방식으로 크게 &lt;b&gt;생성자 주입&lt;/b&gt;, &lt;b&gt;필드 주입&lt;/b&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;: 의존성을 객체가 생성될 때 주입받는 방식입니다. 생성자에 의존성을 명시하고, Spring이 해당 의존성을 주입해줍니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@Component
public class UserService {

    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;필드 주입&lt;/b&gt;: 클래스의 필드에 직접 &lt;code&gt;@Autowired&lt;/code&gt;를 붙여서 주입하는 방식입니다. 코드가 간단하지만, 테스트하기 어렵고 순환 참조 문제를 발견하기 어렵다는 단점이 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;@Component
public class UserService {

    @Autowired
    private UserRepository userRepository;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;3&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;세터 주입&lt;/b&gt;: 의존성을 세터 메서드를 통해 주입받는 방식입니다. 의존성이 필수가 아닐 때 유용하게 사용할 수 있지만, 생성자 주입만큼 많이 사용되진 않습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@Component
public class UserService {

    private UserRepository userRepository;

    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.   &lt;b&gt;DI에서 구체적인 객체 선택과 주입&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring에서 여러 구현체가 있을 때, 어떤 객체가 주입될지는 &lt;b&gt;Spring이 타입을 기준으로&lt;/b&gt; 결정합니다. 만약 여러 구현체가 같은 타입으로 존재한다면, Spring은 &lt;b&gt;&lt;code&gt;@Primary&lt;/code&gt;&lt;/b&gt;나 &lt;b&gt;&lt;code&gt;@Qualifier&lt;/code&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;b&gt;&lt;code&gt;@Primary&lt;/code&gt;&lt;/b&gt;: 여러 빈 중에서 기본적으로 주입할 빈을 지정하는 방법입니다. 여러 개의 빈이 있을 때 우선적으로 선택됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@Primary
@Component
public class PrimaryUserRepository implements UserRepository {
    // ...
}&lt;/code&gt;&lt;/pre&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;code&gt;@Qualifier&lt;/code&gt;&lt;/b&gt;: 특정한 이름의 빈을 명시적으로 주입하고 싶을 때 사용합니다. 이를 통해 여러 구현체 중 하나를 선택할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;@Component
public class UserService {

    private final UserRepository userRepository;

    @Autowired
    public UserService(@Qualifier(&quot;secondaryUserRepository&quot;) UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.   &lt;b&gt;싱글톤 스코프의 컨트롤러와 의존성 관리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring에서 빈의 기본 스코프는 &lt;b&gt;싱글톤&lt;/b&gt;입니다. 즉, 애플리케이션이 시작될 때 한 번만 생성되고 이후에 재사용됩니다. 하지만, 싱글톤 빈을 사용하더라도 각 요청마다 다른 데이터를 처리할 수 있습니다. 왜냐하면, &lt;b&gt;컨트롤러&lt;/b&gt; 같은 경우는 상태를 가지지 않고, 매 요청마다 새로운 파라미터나 데이터를 처리하기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 만약 요청마다 새로운 빈이 필요하다면, &lt;code&gt;@RequestScope&lt;/code&gt;를 사용해서 요청마다 다른 빈을 생성할 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@Component
@RequestScope
public class RequestScopedBean {
    // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면, 각 요청마다 새로운 빈이 생성되어 서로 다른 데이터를 처리할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;정리&lt;/b&gt;:&lt;br /&gt;Spring에서 &lt;b&gt;IoC&lt;/b&gt;와 &lt;b&gt;DI&lt;/b&gt;는 객체의 생성과 의존성 관리를 프레임워크가 대신 해주는 방식입니다. &lt;b&gt;의존성 주입&lt;/b&gt; 덕분에 코드 간의 결합도가 낮아지고, 유지보수성이 높아집니다. 주입 방식으로는 &lt;b&gt;생성자 주입&lt;/b&gt;이 가장 많이 사용되며, 여러 구현체가 있을 경우 &lt;code&gt;@Primary&lt;/code&gt;나 &lt;code&gt;@Qualifier&lt;/code&gt;로 주입할 객체를 선택할 수 있습니다. 또한, 싱글톤 스코프의 빈을 사용하면서도 각 요청마다 다른 데이터를 처리할 수 있는 다양한 방법이 제공됩니다.&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>Java</category>
      <category>di</category>
      <category>IOC</category>
      <category>ioc id</category>
      <category>Spring</category>
      <category>spring boot</category>
      <category>Spring DI</category>
      <category>Spring IOC</category>
      <category>spring 동작 방식</category>
      <category>Spring 싱글톤</category>
      <category>싱글톤</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/256</guid>
      <comments>https://tae-hui.tistory.com/entry/JAVA-Spring-%EB%8F%8C%EC%95%84%EB%B3%B4%EA%B8%B0-IoC-DI-%EC%8B%B1%EB%93%A4%ED%86%A4#entry256comment</comments>
      <pubDate>Tue, 10 Sep 2024 08:00:45 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] 객체 비교와 Integer 클래스</title>
      <link>https://tae-hui.tistory.com/entry/Java-%EA%B0%9D%EC%B2%B4-%EB%B9%84%EA%B5%90%EC%99%80-Integer-%ED%81%B4%EB%9E%98%EC%8A%A4</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt; ️ 객체 비교와 Integer 클래스&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Integer.parseInt vs Integer.valueOf&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java에서 &lt;code&gt;Integer.parseInt&lt;/code&gt;와 &lt;code&gt;Integer.valueOf&lt;/code&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;code&gt;Integer.parseInt(String s)&lt;/code&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;b&gt;반환 타입&lt;/b&gt;: &lt;code&gt;int&lt;/code&gt; (기본형)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;설명&lt;/b&gt;: 주어진 문자열 &lt;code&gt;s&lt;/code&gt;를 기본형 &lt;code&gt;int&lt;/code&gt;로 변환합니다. 이 메서드는 단순히 문자열을 파싱하여 정수로 반환할 뿐입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;특징&lt;/b&gt;: 이 메서드는 &lt;code&gt;int&lt;/code&gt; 타입을 반환하기 때문에, 객체가 아닌 기본형 값을 다루게 됩니다. 또한 null을 전달하면 예외가 발생합니다.&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;code&gt;Integer.parseInt(&quot;123&quot;)&lt;/code&gt;는 &lt;code&gt;int&lt;/code&gt; 타입의 &lt;code&gt;123&lt;/code&gt;을 반환합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;Integer.valueOf(String s)&lt;/code&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;b&gt;반환 타입&lt;/b&gt;: &lt;code&gt;Integer&lt;/code&gt; (객체형)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;설명&lt;/b&gt;: 주어진 문자열 &lt;code&gt;s&lt;/code&gt;를 &lt;code&gt;Integer&lt;/code&gt; 객체로 변환합니다. 내부적으로는 &lt;code&gt;Integer.parseInt&lt;/code&gt;를 호출하고, 그 결과를 &lt;code&gt;Integer&lt;/code&gt; 객체로 반환합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;특징&lt;/b&gt;: &lt;code&gt;Integer&lt;/code&gt; 객체를 반환하며, 자바의 Integer 캐싱 메커니즘에 따라 -128에서 127 사이의 값은 캐싱되어 동일한 참조를 가지게 됩니다. 따라서 메모리와 성능 측면에서 이점이 있습니다.&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;code&gt;Integer.valueOf(&quot;123&quot;)&lt;/code&gt;는 &lt;code&gt;Integer&lt;/code&gt; 객체를 반환합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;int num1 = Integer.parseInt(&quot;123&quot;);
Integer num2 = Integer.valueOf(&quot;123&quot;);&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;code&gt;Integer.parseInt&lt;/code&gt;는 기본형 &lt;code&gt;int&lt;/code&gt;를, &lt;code&gt;Integer.valueOf&lt;/code&gt;는 객체형 &lt;code&gt;Integer&lt;/code&gt;를 반환합니다. null 처리와 캐싱 메커니즘의 차이로 인해, 사용 목적에 따라 적합한 메서드를 선택하는 것이 중요합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;❓ == 연산자 vs equals 메서드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java에서 객체 비교를 할 때, &lt;code&gt;==&lt;/code&gt; 연산자와 &lt;code&gt;equals()&lt;/code&gt; 메서드의 차이는 매우 중요합니다. 특히 &lt;code&gt;Integer&lt;/code&gt;와 같은 객체형 데이터를 비교할 때, 이 차이를 이해하는 것이 필수적입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. == 연산자&lt;/b&gt;&lt;/h4&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;code&gt;==&lt;/code&gt; 연산자는 &lt;b&gt;참조(메모리 주소)&lt;/b&gt;를 비교합니다. 즉, 두 객체가 같은 메모리 주소를 가리키고 있을 때만 &lt;code&gt;true&lt;/code&gt;를 반환합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주의사항&lt;/b&gt;: 기본형 &lt;code&gt;int&lt;/code&gt;와 객체형 &lt;code&gt;Integer&lt;/code&gt; 간의 비교에서는 다르게 동작할 수 있습니다. 기본형 &lt;code&gt;int&lt;/code&gt;는 단순히 값만을 비교하지만, &lt;code&gt;Integer&lt;/code&gt; 객체 간 비교에서는 메모리 주소가 비교되므로, 같은 값을 가질지라도 &lt;code&gt;false&lt;/code&gt;를 반환할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. equals() 메서드&lt;/b&gt;&lt;/h4&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;code&gt;equals()&lt;/code&gt; 메서드는 &lt;b&gt;값&lt;/b&gt;을 비교합니다. &lt;code&gt;Integer&lt;/code&gt; 클래스의 &lt;code&gt;equals()&lt;/code&gt; 메서드는 내부적으로 두 객체가 같은 값을 가지는지를 비교하도록 오버라이드되어 있습니다. 즉, 같은 값을 가지는 두 &lt;code&gt;Integer&lt;/code&gt; 객체가 있다면, &lt;code&gt;equals()&lt;/code&gt;는 &lt;code&gt;true&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;code&gt;Integer a = 128; Integer b = 128;&lt;/code&gt; 이 경우, &lt;code&gt;a.equals(b)&lt;/code&gt;는 &lt;code&gt;true&lt;/code&gt;를 반환하지만, &lt;code&gt;a == b&lt;/code&gt;는 &lt;code&gt;false&lt;/code&gt;를 반환할 수 있습니다. 이는 &lt;code&gt;Integer&lt;/code&gt; 객체가 128 이상의 값일 때 캐싱되지 않기 때문에 서로 다른 메모리 주소를 가지기 때문입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Integer a = Integer.valueOf(128);
Integer b = Integer.valueOf(128);

System.out.println(a == b); // false: 서로 다른 객체를 참조
System.out.println(a.equals(b)); // true: 같은 값을 가짐

int c = 128;
System.out.println(a == c); // true (auto-unboxing)&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;code&gt;Integer&lt;/code&gt;와 &lt;code&gt;int&lt;/code&gt;를 비교할 때는 자동 언박싱으로 인해 값이 비교되므로, &lt;code&gt;==&lt;/code&gt; 연산자는 true를 반환할 수 있습니다. 하지만 &lt;code&gt;Integer&lt;/code&gt; 객체 간 비교에서는 &lt;code&gt;equals()&lt;/code&gt;를 사용하는 것이 안전합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Integer와 int의 비교&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Integer&lt;/code&gt;와 &lt;code&gt;int&lt;/code&gt;를 비교할 때는 &lt;b&gt;자동 언박싱&lt;/b&gt;(auto-unboxing)이라는 개념이 중요합니다. Java는 &lt;code&gt;Integer&lt;/code&gt; 객체를 &lt;code&gt;int&lt;/code&gt; 기본형으로 자동 변환하는 기능을 제공합니다. 따라서 다음과 같은 상황에서 &lt;code&gt;==&lt;/code&gt; 연산자가 &lt;code&gt;true&lt;/code&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Integer a = 128; int b = 128;&lt;/code&gt; 이 경우, &lt;code&gt;a == b&lt;/code&gt;는 &lt;code&gt;true&lt;/code&gt;를 반환합니다. 이는 Java가 &lt;code&gt;Integer&lt;/code&gt; 객체를 자동으로 &lt;code&gt;int&lt;/code&gt;로 변환하여 값 비교를 수행하기 때문입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;: Java는 -128에서 127 사이의 &lt;code&gt;Integer&lt;/code&gt; 값에 대해 캐싱을 제공합니다. 이 범위 내에서는 동일한 값을 가지는 &lt;code&gt;Integer&lt;/code&gt; 객체가 동일한 메모리 주소를 참조합니다. 따라서 이 범위 내에서는 &lt;code&gt;==&lt;/code&gt; 연산자도 &lt;code&gt;true&lt;/code&gt;를 반환할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Integer x = Integer.valueOf(127);
Integer y = Integer.valueOf(127);

System.out.println(x == y); // true: 같은 객체를 참조&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;code&gt;Integer&lt;/code&gt;와 &lt;code&gt;int&lt;/code&gt;를 비교할 때는 자동 언박싱과 캐싱 메커니즘을 이해하는 것이 중요합니다. 특히 &lt;code&gt;==&lt;/code&gt; 연산자와 &lt;code&gt;equals()&lt;/code&gt; 메서드를 적절히 사용하는 것이 핵심입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2.   Stream API에서의 정렬과 데이터 처리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java의 Stream API는 컬렉션 데이터를 효율적으로 처리하고, 정렬 작업을 간단하게 수행할 수 있도록 도와줍니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Stream에서의 정렬&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stream API에서 정렬을 하려면 &lt;code&gt;sorted()&lt;/code&gt; 메서드를 사용합니다. 기본적으로 &lt;code&gt;sorted()&lt;/code&gt; 메서드는 자연 순서(natural order)로 정렬합니다. 이때 &lt;code&gt;Integer&lt;/code&gt;와 같은 객체를 정렬할 때도 자연 순서대로, 즉 오름차순으로 정렬됩니다. 여기서 &lt;code&gt;int&lt;/code&gt;와 &lt;code&gt;Integer&lt;/code&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Integer&lt;/code&gt; 객체를 스트림에서 정렬할 때, &lt;code&gt;sorted()&lt;/code&gt; 메서드는 객체 간의 비교를 위해 &lt;code&gt;compareTo()&lt;/code&gt; 메서드를 사용합니다. 이때 &lt;code&gt;Integer&lt;/code&gt;는 내부적으로 &lt;code&gt;int&lt;/code&gt; 값으로 비교를 수행합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int&lt;/code&gt; 값은 기본형으로 단순히 값 자체를 비교하지만, &lt;code&gt;Integer&lt;/code&gt; 객체는 자동 언박싱되어 비교됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;List&amp;lt;Integer&amp;gt; numbers = Arrays.asList(3, 1, 4, 1, 5, 9);
List&amp;lt;Integer&amp;gt; sortedNumbers = numbers.stream()
                                     .sorted()
                                     .collect(Collectors.toList());

System.out.println(sortedNumbers); // [1, 1, 3, 4, 5, 9]&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;code&gt;sorted()&lt;/code&gt;는 &lt;code&gt;Integer&lt;/code&gt; 객체의 자연 순서에 따라 오름차순으로 정렬합니다. 이는 내부적으로 &lt;code&gt;int&lt;/code&gt; 값으로 자동 언박싱되어 비교가 이루어지기 때문입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  사용자 정의 정렬 방식 적용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 특정 조건에 따라 커스텀 정렬을 하고 싶다면, &lt;code&gt;Comparator&lt;/code&gt;를 사용할 수 있습니다. &lt;code&gt;sorted(Comparator&amp;lt;? super T&amp;gt; comparator)&lt;/code&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다음은 &lt;code&gt;Integer&lt;/code&gt;를 내림차순으로 정렬하는 예제입니다. 이때도 &lt;code&gt;Integer&lt;/code&gt; 객체는 &lt;code&gt;int&lt;/code&gt;로 자동 언박싱되어 비교가 이루어집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;List&amp;lt;Integer&amp;gt; numbers = Arrays.asList(3, 1, 4, 1, 5, 9);
List&amp;lt;Integer&amp;gt; sortedNumbersDesc = numbers.stream()
                                         .sorted(Comparator.reverseOrder())
                                         .collect(Collectors.toList());

System.out.println(sortedNumbersDesc); // [9, 5, 4, 3, 1, 1]&lt;/code&gt;&lt;/pre&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Integer&lt;/code&gt; 를 숫자를 정수로 변환한 후, 홀수는 앞에, 짝수는 뒤에 오도록 정렬 하는 예제입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;// 숫자를 정수로 변환한 후, 홀수는 앞에, 짝수는 뒤에 오도록 정렬
List&amp;lt;Integer&amp;gt; customSortedNumbers = numbersAsStrings.stream()
        .map(Integer::valueOf)
        .sorted((a, b) -&amp;gt; {
            // 홀수는 앞에, 짝수는 뒤에 오도록 정렬
            if (a % 2 != 0 &amp;amp;&amp;amp; b % 2 == 0) {
                return -1; // a가 앞에, b가 뒤에
            } else if (a % 2 == 0 &amp;amp;&amp;amp; b % 2 != 0) {
                return 1; // b가 앞에, a가 뒤에
            } else {
                return a.compareTo(b); // 같은 홀수끼리, 같은 짝수끼리는 오름차순 정렬
            }
        })
        .collect(Collectors.toList());&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;code&gt;Comparator.reverseOrder()&lt;/code&gt;를 사용해 내림차순으로 정렬할 수 있습니다. 이 과정에서도 &lt;code&gt;Integer&lt;/code&gt;는 &lt;code&gt;int&lt;/code&gt;로 언박싱되어 값 비교가 수행됩니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  메서드 참조와 Stream&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stream에서 메서드 참조(Method Reference)는 코드의 가독성을 높여줍니다. &lt;code&gt;Integer::parseInt&lt;/code&gt;와 같은 정적 메서드 참조를 활용하면, 보다 간결하고 직관적인 코드 작성을 할 수 있습니다. 여기서도 &lt;code&gt;Integer&lt;/code&gt;와 &lt;code&gt;int&lt;/code&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문자열 리스트를 정수 리스트로 변환할 때, &lt;code&gt;Integer::parseInt&lt;/code&gt;를 메서드 참조로 사용할 수 있습니다. 이 경우, &lt;code&gt;parseInt&lt;/code&gt;는 기본형 &lt;code&gt;int&lt;/code&gt;를 반환하기 때문에, 이후 연산에서도 기본형 &lt;code&gt;int&lt;/code&gt;로 처리됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;List&amp;lt;String&amp;gt; stringNumbers = Arrays.asList(&quot;1&quot;, &quot;2&quot;, &quot;3&quot;);
List&amp;lt;Integer&amp;gt; intNumbers = stringNumbers.stream()
                                        .map(Integer::parseInt)
                                        .collect(Collectors.toList());

System.out.println(intNumbers); // [1, 2, 3]&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;code&gt;int&lt;/code&gt;와 객체형 &lt;code&gt;Integer&lt;/code&gt; 간의 변환도 간편해집니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ 복잡한 데이터 구조의 처리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stream API는 복잡한 데이터 구조를 처리할 때도 강력한 도구입니다. 예를 들어, &lt;code&gt;Map&amp;lt;List&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt;, Object&amp;gt;&lt;/code&gt;와 같은 복잡한 구조에서 데이터를 추출하고 정렬하는 작업도 가능합니다. 이때도 &lt;code&gt;Integer&lt;/code&gt;와 &lt;code&gt;int&lt;/code&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약 리스트 내의 맵에서 특정 키의 값을 기준으로 정렬하려 한다면, 다음과 같이 구현할 수 있습니다. 이 경우에도 &lt;code&gt;Integer&lt;/code&gt;와 &lt;code&gt;int&lt;/code&gt; 간의 비교가 자동으로 이루어집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;List&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt; dataList = new ArrayList&amp;lt;&amp;gt;();
// 데이터 추가 작업 생략...

List&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt; sortedDataList = dataList.stream()
    .sorted((map1, map2) -&amp;gt; {
        Integer value1 = (Integer) map1.get(&quot;keyName&quot;);
        Integer value2 = (Integer) map2.get(&quot;keyName&quot;);
        return value1.compareTo(value2);
    })
    .collect(Collectors.toList());&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 Stream API를 사용하면 복잡한 데이터 구조도 간결하게 처리하고 정렬할 수 있습니다. 이 과정에서도 &lt;code&gt;Integer&lt;/code&gt; 객체는 자동으로 &lt;code&gt;int&lt;/code&gt;로 변환되어 값 비교가 수행됩니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;Stream의 &lt;code&gt;sorted()&lt;/code&gt;&lt;/b&gt;는 기본적으로 자연 순서로 정렬하며, &lt;code&gt;Comparator&lt;/code&gt;를 통해 사용자 정의 정렬을 적용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;메서드 참조&lt;/b&gt;를 활용하면 코드의 가독성과 간결성을 높일 수 있으며, 기본형 &lt;code&gt;int&lt;/code&gt;와 객체형 &lt;code&gt;Integer&lt;/code&gt; 간의 자동 변환이 중요합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;복잡한 데이터 구조&lt;/b&gt;에서도 Stream API는 효과적이며, &lt;code&gt;Integer&lt;/code&gt;와 &lt;code&gt;int&lt;/code&gt; 간의 차이를 이해하고 사용해야 합니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java</category>
      <category>comapre</category>
      <category>compareTo()</category>
      <category>iNT</category>
      <category>integer</category>
      <category>integer vs int</category>
      <category>자바</category>
      <category>자바 collection</category>
      <category>자바 숫자형</category>
      <category>자바 오토 언박싱</category>
      <category>자바 정수형</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/255</guid>
      <comments>https://tae-hui.tistory.com/entry/Java-%EA%B0%9D%EC%B2%B4-%EB%B9%84%EA%B5%90%EC%99%80-Integer-%ED%81%B4%EB%9E%98%EC%8A%A4#entry255comment</comments>
      <pubDate>Mon, 9 Sep 2024 08:00:27 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] #Deque VS Queue #ArrayDeque VS LinkedList #Map</title>
      <link>https://tae-hui.tistory.com/entry/Java-Deque-VS-Queue-ArrayDeque-VS-LinkedList-Map</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Java Collection?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java의 컬렉션 프레임워크는 데이터를 효율적으로 저장하고 조작할 수 있는 다양한 인터페이스와 클래스를 제공합니다. 이 포스트에서는 &lt;code&gt;Deque&lt;/code&gt;와 &lt;code&gt;Queue&lt;/code&gt;의 성능 차이, &lt;code&gt;ArrayDeque&lt;/code&gt;와 &lt;code&gt;LinkedList&lt;/code&gt;의 성능 비교, 그리고 &lt;code&gt;Map&lt;/code&gt;에서 키를 비교하는 방법에 대해 알아보겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Deque와 Queue의 성능 차이&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Deque&lt;/code&gt;(Double-Ended Queue)와 &lt;code&gt;Queue&lt;/code&gt;는 모두 Java에서 큐 자료구조를 구현한 인터페이스입니다. 하지만 이 둘은 기능적 차이뿐만 아니라, 성능 면에서도 약간의 차이가 있습니다.&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;Queue&lt;/b&gt;: 큐는 기본적으로 FIFO(First-In-First-Out) 방식으로 동작하는 자료구조입니다. 가장 많이 사용되는 구현체는 &lt;code&gt;LinkedList&lt;/code&gt;와 &lt;code&gt;PriorityQueue&lt;/code&gt;입니다. &lt;code&gt;LinkedList&lt;/code&gt;는 삽입과 삭제가 O(1) 시간이 소요되는 반면, &lt;code&gt;PriorityQueue&lt;/code&gt;는 요소를 정렬해야 하기 때문에 삽입과 삭제에 O(log n) 시간이 소요됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Deque&lt;/b&gt;: &lt;code&gt;Deque&lt;/code&gt;는 양방향으로 요소를 삽입하고 삭제할 수 있는 자료구조입니다. &lt;code&gt;ArrayDeque&lt;/code&gt;와 &lt;code&gt;LinkedList&lt;/code&gt;가 대표적인 구현체입니다. &lt;code&gt;Deque&lt;/code&gt;는 양쪽 끝에서의 삽입과 삭제를 O(1) 시간에 처리할 수 있어 &lt;code&gt;Queue&lt;/code&gt;보다 유연한 구조를 제공합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Deque와 Queue 성능 비교&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Deque&lt;/code&gt;와 &lt;code&gt;Queue&lt;/code&gt;의 성능 차이는 구현체에 따라 달라집니다. &lt;code&gt;Deque&lt;/code&gt; 인터페이스를 구현한 두 가지 대표적인 클래스는 &lt;code&gt;ArrayDeque&lt;/code&gt;와 &lt;code&gt;LinkedList&lt;/code&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;ArrayDeque&lt;/b&gt;: 배열 기반으로 구현되어 있으며, 중간에 요소를 추가하거나 삭제하는 경우 성능이 저하될 수 있습니다. 하지만 끝부분에서의 삽입과 삭제는 매우 빠릅니다(O(1)).&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LinkedList&lt;/b&gt;: 연결 리스트 기반으로 구현되어 있으며, 양쪽 끝에서의 삽입과 삭제가 O(1)로 빠릅니다. 그러나 메모리 오버헤드가 더 크고, 순차 접근 성능은 &lt;code&gt;ArrayDeque&lt;/code&gt;에 비해 떨어집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 &lt;code&gt;ArrayDeque&lt;/code&gt;와 &lt;code&gt;LinkedList&lt;/code&gt;의 성능을 비교하는 간단한 예제입니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Deque&amp;lt;Integer&amp;gt; arrayDeque = new ArrayDeque&amp;lt;&amp;gt;();
Deque&amp;lt;Integer&amp;gt; linkedList = new LinkedList&amp;lt;&amp;gt;();

// ArrayDeque 성능 테스트
long start = System.nanoTime();
for (int i = 0; i &amp;lt; 1000000; i++) {
    arrayDeque.addLast(i);
}
long end = System.nanoTime();
System.out.println(&quot;ArrayDeque 삽입 시간: &quot; + (end - start) + &quot; ns&quot;);

// LinkedList 성능 테스트
start = System.nanoTime();
for (int i = 0; i &amp;lt; 1000000; i++) {
    linkedList.addLast(i);
}
end = System.nanoTime();
System.out.println(&quot;LinkedList 삽입 시간: &quot; + (end - start) + &quot; ns&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 테스트에서는 &lt;code&gt;ArrayDeque&lt;/code&gt;가 끝부분에서의 삽입/삭제에서 더 나은 성능을 보여줍니다. 하지만 만약 중간 삽입/삭제가 빈번하다면 &lt;code&gt;LinkedList&lt;/code&gt;가 더 유리할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Map과 키 비교&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java의 &lt;code&gt;Map&lt;/code&gt; 인터페이스는 키와 값의 쌍으로 데이터를 저장하는 자료구조입니다. &lt;code&gt;Map&lt;/code&gt;에서 키를 비교하는 방법과 성능에 중요한 영향을 미치는 요소들을 살펴보겠습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Map에서 키가 정확히 일치하는지 확인하는 방법&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Map&lt;/code&gt;에서 키가 정확히 일치하는지 확인하는 방법은 &lt;code&gt;equals()&lt;/code&gt; 메서드를 사용하는 것입니다. &lt;code&gt;Map&lt;/code&gt;은 내부적으로 키를 비교할 때 &lt;code&gt;equals()&lt;/code&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;equals() 메서드&lt;/b&gt;: &lt;code&gt;equals()&lt;/code&gt; 메서드는 두 객체가 의미적으로 동일한지 비교합니다. 이 메서드를 오버라이드하여 객체 비교 로직을 커스터마이징할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시:&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;Map&amp;lt;String, String&amp;gt; map = new HashMap&amp;lt;&amp;gt;();
map.put(&quot;key1&quot;, &quot;value1&quot;);

boolean isEqual = map.containsKey(&quot;key1&quot;); // true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 &lt;code&gt;containsKey()&lt;/code&gt; 메서드는 내부적으로 &lt;code&gt;equals()&lt;/code&gt;를 사용하여 &lt;code&gt;key1&lt;/code&gt;이 존재하는지 확인합니다. &lt;code&gt;equals()&lt;/code&gt; 메서드가 정확히 구현되어 있다면, 정확한 키 비교가 가능합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Map의 키로 사용된 객체가 동일한지 확인하는 방법&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키로 사용된 객체가 동일한지 확인할 때는 &lt;code&gt;equals()&lt;/code&gt;와 &lt;code&gt;hashCode()&lt;/code&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;hashCode() 메서드&lt;/b&gt;: &lt;code&gt;hashCode()&lt;/code&gt;는 객체를 해시 테이블에 저장할 때 사용되는 정수 값을 반환합니다. 두 객체가 &lt;code&gt;equals()&lt;/code&gt; 메서드로 동일한지 판별될 경우, 동일한 &lt;code&gt;hashCode&lt;/code&gt; 값을 반환해야 합니다. 그렇지 않으면 해시 충돌이 발생해 성능이 저하될 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;equals()와 hashCode()의 관계&lt;/b&gt;: &lt;code&gt;Map&lt;/code&gt;은 내부적으로 키를 저장할 때 &lt;code&gt;hashCode()&lt;/code&gt;로 해시 값을 생성한 후, &lt;code&gt;equals()&lt;/code&gt;로 동일성을 확인합니다. 이 두 메서드를 올바르게 구현하는 것이 중요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시:&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;class Person {
    private String name;
    private int id;

    public Person(String name, int id) {
        this.name = name;
        this.id = id;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return id == person.id &amp;amp;&amp;amp; Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, id);
    }
}

Map&amp;lt;Person, String&amp;gt; personMap = new HashMap&amp;lt;&amp;gt;();
personMap.put(new Person(&quot;Alice&quot;, 1), &quot;Developer&quot;);

boolean exists = personMap.containsKey(new Person(&quot;Alice&quot;, 1)); // true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 &lt;code&gt;equals()&lt;/code&gt;와 &lt;code&gt;hashCode()&lt;/code&gt; 메서드를 적절하게 오버라이드하여 동일한 속성 값을 가진 &lt;code&gt;Person&lt;/code&gt; 객체들이 같은 키로 인식되도록 했습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;Deque와 Queue 성능 차이&lt;/b&gt;: &lt;code&gt;Deque&lt;/code&gt;는 양방향에서의 삽입과 삭제에 유리하며, &lt;code&gt;ArrayDeque&lt;/code&gt;는 메모리 효율성과 끝부분에서의 성능이 뛰어납니다. &lt;code&gt;LinkedList&lt;/code&gt;는 중간 삽입/삭제에서 더 좋은 성능을 보일 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Map과 키 비교&lt;/b&gt;: &lt;code&gt;Map&lt;/code&gt;에서 키 비교는 &lt;code&gt;equals()&lt;/code&gt;와 &lt;code&gt;hashCode()&lt;/code&gt; 메서드에 의해 결정됩니다. 올바르게 구현된 &lt;code&gt;equals()&lt;/code&gt;와 &lt;code&gt;hashCode()&lt;/code&gt;는 &lt;code&gt;Map&lt;/code&gt;에서 키로 사용된 객체의 동일성을 정확하게 판단하는 데 필수적입니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java</category>
      <category>ArrayList</category>
      <category>deque</category>
      <category>Java</category>
      <category>LinkedList</category>
      <category>map</category>
      <category>Queue</category>
      <category>자바</category>
      <category>자바 큐</category>
      <category>큐</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/254</guid>
      <comments>https://tae-hui.tistory.com/entry/Java-Deque-VS-Queue-ArrayDeque-VS-LinkedList-Map#entry254comment</comments>
      <pubDate>Fri, 6 Sep 2024 08:00:54 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] Java 메서드 참조</title>
      <link>https://tae-hui.tistory.com/entry/Java-Java-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%B0%B8%EC%A1%B0</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Java 메서드 참조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java에서 메서드 참조(Method Reference)는 람다 표현식을 간결하게 표현할 수 있는 방법입니다. 메서드 참조는 코드의 가독성을 높이고, 불필요한 중복을 줄이는 데 유용하죠. 이번 포스트에서는 메서드 참조의 유형, 메서드 참조의 동작 방식, 그리고 특정 객체의 메서드 참조 사용에 대해 &lt;b&gt;상세하게&lt;/b&gt; 설명드리겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;정적 메서드 참조&lt;/b&gt; (Static Method Reference)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인스턴스 메서드 참조&lt;/b&gt; (Instance Method Reference)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;특정 객체의 인스턴스 메서드 참조&lt;/b&gt; (Reference to an instance method of a particular object)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;생성자 참조&lt;/b&gt; (Constructor Reference)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 각 유형이 어떻게 동작하는지 자세히 알아보겠습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Integer::parseInt와 String::toUpperCase&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;code&gt;Integer::parseInt&lt;/code&gt;&lt;/b&gt;: 이 메서드 참조는 &lt;code&gt;Integer&lt;/code&gt; 클래스의 &lt;b&gt;정적 메서드&lt;/b&gt;인 &lt;code&gt;parseInt&lt;/code&gt;를 참조합니다. &lt;code&gt;parseInt&lt;/code&gt;는 문자열을 정수로 변환하는 작업을 수행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;  Function&amp;lt;String, Integer&amp;gt; parseFunction = Integer::parseInt;
  Integer result = parseFunction.apply(&quot;123&quot;);
  System.out.println(result); // 123&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;Integer::parseInt&lt;/code&gt;는 &lt;code&gt;Function&amp;lt;String, Integer&amp;gt;&lt;/code&gt; 인터페이스와 매핑됩니다. 이는 입력된 &lt;code&gt;String&lt;/code&gt;을 &lt;code&gt;Integer&lt;/code&gt;로 변환하는 기능을 합니다. &lt;code&gt;apply&lt;/code&gt; 메서드를 호출하면 &lt;code&gt;parseInt&lt;/code&gt; 메서드가 실행되어 문자열 &lt;code&gt;&quot;123&quot;&lt;/code&gt;이 정수 &lt;code&gt;123&lt;/code&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;code&gt;String::toUpperCase&lt;/code&gt;&lt;/b&gt;: 이 메서드 참조는 &lt;code&gt;String&lt;/code&gt; 클래스의 &lt;b&gt;인스턴스 메서드&lt;/b&gt;인 &lt;code&gt;toUpperCase&lt;/code&gt;를 참조합니다. 이 메서드는 문자열을 대문자로 변환합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;  Function&amp;lt;String, String&amp;gt; upperCaseFunction = String::toUpperCase;
  String result = upperCaseFunction.apply(&quot;hello&quot;);
  System.out.println(result); // HELLO&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;String::toUpperCase&lt;/code&gt;는 &lt;code&gt;Function&amp;lt;String, String&amp;gt;&lt;/code&gt;과 매핑되어, 입력된 문자열을 대문자로 변환합니다. 이 메서드 참조는 어떤 특정 인스턴스에 묶여 있지 않으며, 입력되는 문자열의 &lt;code&gt;toUpperCase&lt;/code&gt; 메서드를 호출하게 됩니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  정적 메서드 참조, 인스턴스 메서드 참조, 생성자 참조의 차이점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;정적 메서드 참조 (ClassName::methodName)&lt;/b&gt;: 클래스의 정적 메서드를 참조합니다. 이 참조는 클래스 자체에 대해 정적으로 정의된 메서드를 대상으로 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;Function&amp;lt;String, Integer&amp;gt; parseInt = Integer::parseInt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;Integer::parseInt&lt;/code&gt;는 &lt;code&gt;Integer&lt;/code&gt; 클래스에 정적으로 정의된 &lt;code&gt;parseInt&lt;/code&gt; 메서드를 참조합니다. 이 메서드는 &lt;code&gt;String&lt;/code&gt;을 받아서 &lt;code&gt;Integer&lt;/code&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;인스턴스 메서드 참조 (ClassName::methodName)&lt;/b&gt;: 특정 인스턴스에 의존하지 않고, 전달된 인스턴스의 메서드를 참조합니다. 이 참조는 인스턴스 메서드이지만, 클래스 이름으로 참조되며, 실제로는 메서드가 호출될 때 전달된 객체의 메서드를 실행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;  Function&amp;lt;String, String&amp;gt; upperCase = String::toUpperCase;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;String::toUpperCase&lt;/code&gt;는 전달된 &lt;code&gt;String&lt;/code&gt; 객체의 &lt;code&gt;toUpperCase&lt;/code&gt; 메서드를 호출합니다. 여기서 &lt;code&gt;upperCase&lt;/code&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;특정 객체의 인스턴스 메서드 참조 (instance::methodName)&lt;/b&gt;: 특정 객체의 메서드를 참조합니다. 이 유형의 메서드 참조는 미리 정의된 객체의 메서드를 대상으로 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;  String prefix = &quot;Hello, &quot;;
  Function&amp;lt;String, String&amp;gt; greeting = prefix::concat;
  String result = greeting.apply(&quot;World&quot;);
  System.out.println(result); // Hello, World&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;prefix::concat&lt;/code&gt;은 &lt;code&gt;prefix&lt;/code&gt;라는 특정 &lt;code&gt;String&lt;/code&gt; 객체의 &lt;code&gt;concat&lt;/code&gt; 메서드를 참조합니다. 이 메서드 참조는 &lt;code&gt;greeting.apply(&quot;World&quot;)&lt;/code&gt;가 호출될 때 &lt;code&gt;prefix.concat(&quot;World&quot;)&lt;/code&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;생성자 참조 (ClassName::new)&lt;/b&gt;: 클래스의 생성자를 참조하여 새로운 객체를 만듭니다. 이 참조는 기본 생성자 또는 매개변수가 있는 생성자와 연결될 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;  Supplier&amp;lt;List&amp;lt;String&amp;gt;&amp;gt; listSupplier = ArrayList::new;
  List&amp;lt;String&amp;gt; list = listSupplier.get();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ArrayList::new&lt;/code&gt;는 &lt;code&gt;ArrayList&lt;/code&gt;의 기본 생성자를 참조하며, &lt;code&gt;get()&lt;/code&gt; 메서드를 호출하면 새로운 &lt;code&gt;ArrayList&lt;/code&gt; 인스턴스가 생성됩니다. &lt;code&gt;Supplier&lt;/code&gt; 인터페이스와 함께 사용되어, &lt;code&gt;new ArrayList&amp;lt;&amp;gt;()&lt;/code&gt;와 동일한 효과를 제공합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  메서드 참조의 동작 방식&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드 참조는 람다 표현식의 간결한 대체물로 작동하며, Java 컴파일러는 이를 분석하여 적절한 메서드 또는 생성자를 호출합니다. 컴파일러가 어떻게 메서드 참조를 해석하고 작동하는지 이해해 봅시다.&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;code&gt;Integer::parseInt&lt;/code&gt;는 &lt;code&gt;Integer&lt;/code&gt; 클래스의 정적 메서드인 &lt;code&gt;parseInt&lt;/code&gt;로 매핑됩니다.이 경우 &lt;code&gt;parseInt&lt;/code&gt;는 특정 인스턴스 없이 호출될 수 있습니다. 컴파일러는 &lt;code&gt;Integer::parseInt&lt;/code&gt;가 &lt;code&gt;Function&amp;lt;String, Integer&amp;gt;&lt;/code&gt;과 같은 인터페이스에 맞는 메서드라는 점을 인식하고 이를 사용합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Function&amp;lt;String, Integer&amp;gt; parseInt = Integer::parseInt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인스턴스 메서드 참조&lt;/b&gt;: 클래스명만 지정된 경우, 메서드 참조는 전달된 객체의 인스턴스 메서드를 참조하는 것으로 해석됩니다. 이 경우, 컴파일러는 메서드 참조가 특정 객체의 인스턴스 메서드로 적용될 수 있는지를 판단합니다.컴파일러는 &lt;code&gt;String::toUpperCase&lt;/code&gt;가 &lt;code&gt;String&lt;/code&gt; 객체에서 호출 가능한 인스턴스 메서드라는 점을 인식합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Function&amp;lt;String, String&amp;gt; upperCase = String::toUpperCase;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;특정 객체의 인스턴스 메서드 참조&lt;/b&gt;: 특정 객체가 주어지고 그 객체의 메서드가 참조되는 경우, 해당 객체의 메서드가 참조됩니다. 이는 메서드 참조가 어떤 객체에 속한 메서드인지를 컴파일러가 직접 판단하게 됩니다.컴파일러는 &lt;code&gt;greeting::concat&lt;/code&gt;이 특정 &lt;code&gt;greeting&lt;/code&gt; 객체의 &lt;code&gt;concat&lt;/code&gt; 메서드를 참조한다는 것을 인식합니다. 이 참조는 &lt;code&gt;concatFunction.apply(&quot;World&quot;)&lt;/code&gt;가 호출될 때 &lt;code&gt;greeting.concat(&quot;World&quot;)&lt;/code&gt;를 수행하게 됩니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;String greeting = &quot;Hello, &quot;;
Function&amp;lt;String, String&amp;gt; concatFunction = greeting::concat;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;생성자 참조&lt;/b&gt;: 클래스명과 &lt;code&gt;new&lt;/code&gt; 키워드가 지정된 경우, 이를 생성자 참조로 인식합니다. 컴파일러는 생성자가 호출될 때 어떤 매개변수가 필요한지를 확인하고, 적절한 생성자를 선택합니다.여기서 &lt;code&gt;ArrayList::new&lt;/code&gt;는 &lt;code&gt;ArrayList&lt;/code&gt;의 기본 생성자를 참조하며, 컴파일러는 &lt;code&gt;Supplier&amp;lt;List&amp;lt;String&amp;gt;&amp;gt;&lt;/code&gt; 인터페이스에 맞게 이 참조를 사용합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Supplier&amp;lt;List&amp;lt;String&amp;gt;&amp;gt; listSupplier = ArrayList::new;&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  특정 객체의 메서드 참조&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 객체의 인스턴스 메서드를 Stream에서 메서드 참조로 사용할 수 있습니다. 이는 특정 객체를 대상으로 메서드를 호출해야 하는 경우 유용하게 사용됩니다. 이 경우 메서드 참조는 주로 람다 표현식의 대안으로 사용되며, 간결하고 가독성 높은 코드를 작성할 수 있게 해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시:&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;List&amp;lt;String&amp;gt; messages = Arrays.asList(&quot;Hello&quot;, &quot;World&quot;);
String prefix = &quot;Message: &quot;;
messages.stream()
        .map(prefix::concat)  // prefix 객체의 concat 메서드 참조
        .forEach(System.out::println);
// 출력: Message: Hello, Message: World&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예제에서는 &lt;code&gt;prefix::concat&lt;/code&gt;을 사용하여 Stream의 각 요소에 대해 &lt;code&gt;concat&lt;/code&gt; 메서드를 호출합니다. &lt;code&gt;prefix&lt;/code&gt; 객체에 대해 &lt;code&gt;concat&lt;/code&gt; 메서드가 호출되어, 각 메시지에 &quot;Message: &quot;라는 접두어가 추가됩니다. 이처럼 특정 객체의 메서드 참조는 Stream API와 결합하여 매우 강력한 기능을 발휘할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;: Java 컴파일러는 메서드 참조를 분석하여, 적절한 메서드나 생성자를 자동으로 선택하고 실행합니다. 이 과정에서 메서드 참조가 어떤 유형에 속하는지 판단하며, 적절한 함수형 인터페이스에 맞춰 실행합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;특정 객체의 메서드 참조&lt;/b&gt;: Stream에서 특정 객체의 메서드 참조를 사용하면, 각 요소에 대해 해당 객체의 메서드를 간편하게 호출할 수 있습니다. 이는 코드의 가독성을 높이고, 중복을 줄이는 데 도움이 됩니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java</category>
      <category>integer::parseint</category>
      <category>java stream</category>
      <category>java 메서드 참조</category>
      <category>java 스트림</category>
      <category>Stream</category>
      <category>메서드 참조 동작 방식</category>
      <category>메서드 참조 유형</category>
      <category>스트림</category>
      <category>자바</category>
      <category>특정 객체의 메서드 참조</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/253</guid>
      <comments>https://tae-hui.tistory.com/entry/Java-Java-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%B0%B8%EC%A1%B0#entry253comment</comments>
      <pubDate>Wed, 4 Sep 2024 08:00:16 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] Java Stream API 활용법</title>
      <link>https://tae-hui.tistory.com/entry/Java-Java-Stream-API-%ED%99%9C%EC%9A%A9%EB%B2%95</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  1. Java Stream API 활용법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java의 Stream API는 데이터 처리를 정말 편리하게 해주는 도구입니다. 데이터를 필터링, 매핑, 정렬, 수집하는 작업을 간단하게 할 수 있죠. 여기서 Stream을 어떻게 활용할 수 있는지, map과 mapToInt의 차이점, Collectors의 사용법, 그리고 복잡한 구조를 Stream으로 처리하는 방법에 대해 알아보겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Stream 활용법: 기본적인 사용 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stream은 컬렉션(예: List, Set, Map)이나 배열의 데이터를 순차적으로 처리할 수 있는 기능입니다. 이를 활용하면 &lt;b&gt;반복문 없이&lt;/b&gt; 간단하게 데이터를 조작할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;List&amp;lt;String&amp;gt; names = Arrays.asList(&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;);

// 스트림을 사용한 필터링과 매핑
List&amp;lt;String&amp;gt; filteredNames = names.stream()
                                  .filter(name -&amp;gt; name.startsWith(&quot;A&quot;))
                                  .map(String::toUpperCase)
                                  .collect(Collectors.toList());

System.out.println(filteredNames); // [ALICE]&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  map과 mapToInt의 차이점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;map&lt;/code&gt;과 &lt;code&gt;mapToInt&lt;/code&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;code&gt;map&lt;/code&gt;: 모든 타입의 Stream을 변환할 수 있습니다. 예를 들어, &lt;code&gt;Stream&amp;lt;String&amp;gt;&lt;/code&gt;을 &lt;code&gt;Stream&amp;lt;Integer&amp;gt;&lt;/code&gt;로 변환할 수 있죠.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mapToInt&lt;/code&gt;: &lt;b&gt;정수형 스트림(IntStream)&lt;/b&gt;으로 변환합니다. 그래서 주로 숫자 연산이 필요할 때 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;List&amp;lt;String&amp;gt; numbers = Arrays.asList(&quot;1&quot;, &quot;2&quot;, &quot;3&quot;);

// map 예시: String -&amp;gt; Integer 변환
List&amp;lt;Integer&amp;gt; intNumbers = numbers.stream()
                                  .map(Integer::parseInt)
                                  .collect(Collectors.toList());

// mapToInt 예시: String -&amp;gt; int 변환
int sum = numbers.stream()
                 .mapToInt(Integer::parseInt)
                 .sum();

System.out.println(sum); // 6&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Collectors와 Stream 관련&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stream의 결과를 원하는 형태로 수집하려면 Collectors를 사용합니다. 여기서는 &lt;code&gt;Collectors.toList()&lt;/code&gt;와 &lt;code&gt;Collectors.toCollection()&lt;/code&gt;의 차이점, 그리고 Stream을 사용해 List를 Deque나 Queue로 변환하는 방법을 살펴보겠습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Collectors.toList()와 Collectors.toCollection()의 차이점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Collectors.toList()&lt;/code&gt;: Stream의 결과를 List로 수집합니다. &lt;b&gt;기본적인&lt;/b&gt; 리스트로 변환할 때 사용하죠.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Collectors.toCollection()&lt;/code&gt;: Stream의 결과를 &lt;b&gt;원하는 컬렉션 타입&lt;/b&gt;으로 수집할 수 있습니다. 예를 들어, &lt;code&gt;Deque&lt;/code&gt;, &lt;code&gt;Queue&lt;/code&gt;, 또는 &lt;code&gt;PriorityQueue&lt;/code&gt; 같은 특정 컬렉션으로 변환할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Deque&amp;lt;String&amp;gt; deque = names.stream()
                           .collect(Collectors.toCollection(ArrayDeque::new));

PriorityQueue&amp;lt;String&amp;gt; priorityQueue = names.stream()
                                           .collect(Collectors.toCollection(PriorityQueue::new));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 Stream의 요소들을 &lt;code&gt;Deque&lt;/code&gt;나 &lt;code&gt;PriorityQueue&lt;/code&gt; 같은 컬렉션으로 쉽게 변환할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  중첩된 구조의 처리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stream을 사용하면 복잡한 구조도 효율적으로 처리할 수 있습니다. 예를 들어, &lt;code&gt;Map&amp;lt;List&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt;, Object&amp;gt;&lt;/code&gt; 같은 복잡한 구조에서도 특정 값을 가져올 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;Map&amp;lt;List&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt;, Object&amp;gt; complexMap = // 초기화된 복잡한 구조
// 특정 값을 가져오는 예시
Optional&amp;lt;Object&amp;gt; result = complexMap.entrySet().stream()
                                    .flatMap(entry -&amp;gt; entry.getKey().stream())
                                    .filter(innerMap -&amp;gt; &quot;특정 키&quot;.equals(innerMap.get(&quot;key&quot;)))
                                    .map(innerMap -&amp;gt; innerMap.get(&quot;value&quot;))
                                    .findFirst();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 중첩된 구조에서 원하는 값을 효율적으로 추출할 수 있게 해줍니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;Stream&lt;/b&gt;: 데이터를 필터링, 매핑, 수집하는데 유용하게 사용됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;map과 mapToInt&lt;/b&gt;: &lt;code&gt;map&lt;/code&gt;은 타입 변환, &lt;code&gt;mapToInt&lt;/code&gt;는 숫자 연산에 특화된 기능입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Collectors&lt;/b&gt;: Stream의 결과를 다양한 컬렉션 형태로 수집할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;복잡한 구조&lt;/b&gt;: Stream을 통해 복잡한 데이터 구조에서도 원하는 값을 쉽게 찾을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java</category>
      <category>Java</category>
      <category>java 8</category>
      <category>java map</category>
      <category>java stream</category>
      <category>Stream</category>
      <category>stream map</category>
      <category>stream.collect()</category>
      <category>스트림</category>
      <category>자바 Collectors</category>
      <category>자바 스트림</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/252</guid>
      <comments>https://tae-hui.tistory.com/entry/Java-Java-Stream-API-%ED%99%9C%EC%9A%A9%EB%B2%95#entry252comment</comments>
      <pubDate>Mon, 2 Sep 2024 08:00:03 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] JavaScript의 비동기 처리 이해하기</title>
      <link>https://tae-hui.tistory.com/entry/JavaScript-JavaScript%EC%9D%98-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  1. JavaScript의 비동기 처리 이해하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScript는 &lt;b&gt;싱글 스레드&lt;/b&gt;로 동작해요. 즉, 한 번에 하나의 작업만 처리할 수 있다는 뜻이죠. 그런데도 여러 비동기 작업을 동시에 처리하는 것처럼 보이는 이유는 JavaScript의 이벤트 루프(Event Loop) 덕분이에요. 자, 이 과정이 어떻게 이루어지는지 차근차근 설명해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  JavaScript는 싱글 스레드인데 비동기 작업을 어떻게 처리할까요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScript는 비동기 작업을 처리하기 위해 &lt;b&gt;이벤트 루프&lt;/b&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;code&gt;setTimeout&lt;/code&gt;, &lt;code&gt;fetch&lt;/code&gt;, &lt;code&gt;async/await&lt;/code&gt;)가 호출되면, 그 작업은 브라우저나 Node.js의 백그라운드에서 처리돼고, 이때 JavaScript는 다른 코드를 계속해서 실행할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;console.log(&quot;Start&quot;);

setTimeout(() =&amp;gt; {
  console.log(&quot;Inside setTimeout&quot;);
}, 2000);

console.log(&quot;End&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서는 &lt;code&gt;setTimeout&lt;/code&gt;이 호출되었지만, JavaScript는 이 작업을 백그라운드에서 처리하고 &lt;code&gt;console.log(&quot;End&quot;)&lt;/code&gt;를 먼저 실행한 후, 2초 후에 비동기 작업이 완료되면 &lt;code&gt;&quot;Inside setTimeout&quot;&lt;/code&gt;이 출력됩니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;태스크 큐(Task Queue)&lt;/b&gt;: 비동기 작업이 완료되면, 그 결과는 태스크 큐에 들어갑니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;setTimeout(() =&amp;gt; {
  console.log(&quot;First task&quot;);
}, 1000);

setTimeout(() =&amp;gt; {
  console.log(&quot;Second task&quot;);
}, 500);

console.log(&quot;Synchronous task&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예시에서는 &lt;code&gt;500ms&lt;/code&gt; 후에 &lt;code&gt;&quot;Second task&quot;&lt;/code&gt;가, &lt;code&gt;1000ms&lt;/code&gt; 후에 &lt;code&gt;&quot;First task&quot;&lt;/code&gt;가 출력되는데, 그 전에 &lt;code&gt;&quot;Synchronous task&quot;&lt;/code&gt;가 먼저 출력돼요. 이처럼 태스크 큐는 작업이 완료된 순서대로 큐에 저장됩니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;3&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;이벤트 루프(Event Loop)&lt;/b&gt;: 이벤트 루프는 콜 스택이 비어 있을 때, 태스크 큐에 있는 작업을 콜 스택으로 옮겨와 실행합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  비동기 함수 내부에서 &lt;code&gt;await&lt;/code&gt;를 사용하면 콜 스택과의 관계는 어떻게 되나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;await&lt;/code&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;await&lt;/code&gt;를 만나면 JavaScript는 해당 비동기 작업이 완료될 때까지 기다리지 않고, 함수의 실행을 일시 중지합니다.&lt;/li&gt;
&lt;li&gt;이 동안, 다른 코드가 실행될 수 있도록 콜 스택을 비워둡니다.&lt;/li&gt;
&lt;li&gt;비동기 작업이 완료되면, 그 작업은 태스크 큐에 들어가고, 이벤트 루프가 이 태스크를 콜 스택으로 옮겨 실행을 재개합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;async function fetchData() {
  console.log(&quot;Fetching data...&quot;);

  const response = await fetch(&quot;https://api.example.com/data&quot;);
  const data = await response.json();

  console.log(&quot;Data received:&quot;, data);
}

console.log(&quot;Before fetch&quot;);
fetchData();
console.log(&quot;After fetch&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드에서는 &lt;code&gt;fetchData&lt;/code&gt; 함수 내에서 &lt;code&gt;await&lt;/code&gt;가 호출될 때 JavaScript는 일시적으로 함수 실행을 중지하고, 나머지 코드 (&lt;code&gt;console.log(&quot;After fetch&quot;)&lt;/code&gt;)를 먼저 실행해요. 이후, 데이터가 받아지면 &lt;code&gt;&quot;Data received:&quot;&lt;/code&gt;와 함께 출력돼요.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  비동기 작업이 완료된 후 태스크 큐로 결과가 전달되는 과정은 어떻게 이루어지나요?&lt;/h3&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;/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;/ol&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;console.log(&quot;Start&quot;);

setTimeout(() =&amp;gt; {
  console.log(&quot;First task&quot;);
}, 1000);

setTimeout(() =&amp;gt; {
  console.log(&quot;Second task&quot;);
}, 500);

console.log(&quot;End&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드에서는 &lt;code&gt;&quot;Second task&quot;&lt;/code&gt;가 태스크 큐에 먼저 들어가고, 그 다음 &lt;code&gt;&quot;First task&quot;&lt;/code&gt;가 들어갑니다. 이벤트 루프는 콜 스택이 비었을 때 태스크 큐에서 &lt;code&gt;&quot;Second task&quot;&lt;/code&gt;를 먼저 가져와 실행합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  비동기 작업과 이벤트 루프의 역할&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 작업이 수행될 때, JavaScript는 해당 작업을 &lt;b&gt;브라우저의 백그라운드&lt;/b&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;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;: 비동기 작업이 완료된 후, 이벤트 루프가 이를 JavaScript 엔진으로 돌려줘서 후속 작업을 이어가게 합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;code&gt;mounted&lt;/code&gt; 훅에서 여러 비동기 작업이 순차적으로 실행되지 않는 이유는 무엇인가요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;mounted&lt;/code&gt; 훅에서 비동기 작업을 실행하면, 이 작업들은 병렬로 실행됩니다. 이는 각 비동기 작업이 &lt;b&gt;별도로 시작&lt;/b&gt;되기 때문입니다. 만약 순차적으로 실행하고 싶다면, &lt;code&gt;await&lt;/code&gt; 키워드를 사용하거나 &lt;code&gt;Promise&lt;/code&gt; 체이닝을 해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;async mounted() {
  await this.fetchData1();
  await this.fetchData2();
  await this.fetchData3();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드처럼 &lt;code&gt;await&lt;/code&gt;를 사용하면, 첫 번째 작업이 끝날 때까지 기다린 후 다음 작업이 실행됩니다. 하지만 &lt;code&gt;await&lt;/code&gt;를 사용하지 않으면 모든 작업이 동시에 시작됩니다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;mounted() {
  this.fetchData1();
  this.fetchData2();
  this.fetchData3();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우, &lt;code&gt;fetchData1&lt;/code&gt;, &lt;code&gt;fetchData2&lt;/code&gt;, &lt;code&gt;fetchData3&lt;/code&gt;는 동시에 호출되고 병렬로 실행됩니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  콜 스택, 태스크 큐, 마이크로태스크 큐 모두 하나의 스레드에서 처리가 가능한가요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맞아요! &lt;b&gt;콜 스택&lt;/b&gt;, &lt;b&gt;태스크 큐&lt;/b&gt;, 그리고 &lt;b&gt;마이크로태스크 큐&lt;/b&gt;는 모두 하나의 스레드에서 처리됩니다. JavaScript는 &lt;b&gt;싱글 스레드&lt;/b&gt; 언어이기 때문에, 이 모든 작업이 한 줄로 정리된 하나의 스레드에서 실행됩니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  태스크 큐와 마이크로태스크 큐도 결과를 저장하는 곳인가요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엄밀히 말하면, 태스크 큐와 마이크로태스크 큐는 &lt;b&gt;결과를 저장하는 곳&lt;/b&gt;이라기보다는, &lt;b&gt;실행 대기 중인 작업들을 보관하는 곳&lt;/b&gt;입니다. 비동기 작업이 완료되면 이 큐에 작업이 들어가고, 이벤트 루프가 이를 콜 스택으로 가져와 실행합니다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;Promise.resolve().then(() =&amp;gt; {
  console.log(&quot;Microtask 1&quot;);
});

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

Promise.resolve().then(() =&amp;gt; {
  console.log(&quot;Microtask 2&quot;);
});

console.log(&quot;Synchronous task&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예시에서 &lt;code&gt;&quot;Microtask 1&quot;&lt;/code&gt;과 &lt;code&gt;&quot;Microtask 2&quot;&lt;/code&gt;는 &lt;b&gt;마이크로태스크 큐&lt;/b&gt;에 들어가고, &lt;code&gt;&quot;Task&quot;&lt;/code&gt;는 &lt;b&gt;태스크 큐&lt;/b&gt;에 들어가요. 콜 스택이 비면 마이크로태스크가 먼저 실행되고, 그 다음 태스크가 실행합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  태스크 큐는 선입선출, 콜 스택은 후입선출 방식으로 동작하나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;선입선출(FIFO)&lt;/b&gt; 방식으로 동작해요. 먼저 들어온 작업이 먼저 실행됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;콜 스택&lt;/b&gt;은 &lt;b&gt;후입선출(LIFO)&lt;/b&gt; 방식으로 동작해요. 마지막에 들어온 작업이 먼저 실행됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function example() {
  function inner() {
    console.log(&quot;Inner function&quot;);
  }

  console.log(&quot;Before inner&quot;);
  inner();
  console.log(&quot;After inner&quot;);
}

example();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 &lt;code&gt;example()&lt;/code&gt;이 호출되면, &lt;code&gt;example&lt;/code&gt; 함수가 콜 스택에 쌓이고, 그 안에서 &lt;code&gt;inner()&lt;/code&gt;가 호출되면 &lt;code&gt;inner&lt;/code&gt; 함수가 콜 스택에 추가고, &lt;code&gt;inner&lt;/code&gt; 함수가 실행된 후 스택에서 빠지고, 그 다음 &lt;code&gt;example&lt;/code&gt; 함수가 완료됩니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;code&gt;mounted&lt;/code&gt;에서 여러 함수를 호출할 때, 함수들이 콜 스택에 한 번에 쌓이는 게 아니라 하나씩 처리 후 다시 쌓이나요? 함수 내에 함수가 있을 때만 스택에 넣나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;mounted&lt;/code&gt;에서 여러 함수를 호출하면, 각 함수 호출이 &lt;b&gt;순차적으로 콜 스택에 쌓이고&lt;/b&gt; 하나씩 처리됩. 함수 내에 또 다른 함수가 있다면, 그 함수가 콜 스택에 추가로 쌓이게 됩니다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function outer() {
  console.log(&quot;Outer function start&quot;);

  function inner() {
    console.log(&quot;Inner function&quot;);
  }

  inner();
  console.log(&quot;Outer function end&quot;);
}

outer();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예시에서 &lt;code&gt;outer()&lt;/code&gt;가 호출되면, &lt;code&gt;outer&lt;/code&gt; 함수가 콜 스택에 쌓이고, 그 안에서 &lt;code&gt;inner()&lt;/code&gt;가 호출되면 &lt;code&gt;inner&lt;/code&gt; 함수가 콜 스택에 쌓입니다. &lt;code&gt;inner&lt;/code&gt;가 먼저 실행되고, 완료되면 스택에서 빠지고, 그 다음 &lt;code&gt;outer&lt;/code&gt; 함수가 완료됩니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&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;JavaScript는 싱글 스레드지만, 이벤트 루프와 태스크 큐를 이용해 비동기 작업을 효율적으로 처리한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;await&lt;/code&gt;를 사용하면 콜 스택이 비동기 작업이 완료될 때까지 기다리지 않고 비워질 수 있다.&lt;/li&gt;
&lt;li&gt;비동기 작업이 완료되면 결과는 태스크 큐에 저장되고, 이벤트 루프가 이를 처리한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;이벤트 루프&lt;/b&gt;는 비동기 작업의 완료를 감지하고, 그 결과를 &lt;b&gt;콜 스택&lt;/b&gt;에 전달한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mounted&lt;/code&gt;에서 여러 비동기 작업을 순차적으로 실행하려면 &lt;code&gt;await&lt;/code&gt;를 사용해야 한다.&lt;/li&gt;
&lt;li&gt;콜 스택, 태스크 큐, 마이크로태스크 큐는 모두 하나의 스레드에서 처리된다.&lt;/li&gt;
&lt;li&gt;태스크 큐는 선입선출, 콜 스택은 후입선출 방식으로 동작한다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java Script</category>
      <category>ajax</category>
      <category>Async</category>
      <category>await</category>
      <category>axios</category>
      <category>Java</category>
      <category>javascript</category>
      <category>javascript  싱글 스레드</category>
      <category>javascript 비동기</category>
      <category>비동기</category>
      <category>스레드</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/251</guid>
      <comments>https://tae-hui.tistory.com/entry/JavaScript-JavaScript%EC%9D%98-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0#entry251comment</comments>
      <pubDate>Sun, 25 Aug 2024 14:53:47 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] Array에서 특정 데이터 개수 구하기 #reduce</title>
      <link>https://tae-hui.tistory.com/entry/Java-Script-Array%EC%97%90%EC%84%9C-%ED%8A%B9%EC%A0%95-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B0%9C%EC%88%98-%EA%B5%AC%ED%95%98%EA%B8%B0-reduce</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  JavaScript에서 카테고리별 개수 정렬하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네, JavaScript에서 객체를 생성한 후에 &lt;b&gt;카테고리별 개수를 정렬&lt;/b&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;b&gt;개수&lt;/b&gt;에 따라 정렬하는 방법입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  1. 카테고리별 개수 구하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 카테고리별로 개수를 집계해야 합니다. 다음은 예시 코드입니다:&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;const items = [
  { id: 1, category: 'fruit', name: 'apple' },
  { id: 2, category: 'vegetable', name: 'carrot' },
  { id: 3, category: 'fruit', name: 'banana' },
  { id: 4, category: 'fruit', name: 'orange' },
  { id: 5, category: 'vegetable', name: 'broccoli' },
  { id: 6, category: 'grain', name: 'rice' },
];

const categoryCounts = items.reduce((acc, item) =&amp;gt; {
  acc[item.category] = (acc[item.category] || 0) + 1;
  return acc;
}, {});

console.log('Category Counts:', categoryCounts);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 &lt;code&gt;items&lt;/code&gt; 배열에서 &lt;b&gt;카테고리별로 항목 개수를 계산&lt;/b&gt;합니다. &lt;code&gt;reduce&lt;/code&gt; 메서드를 사용해 &lt;code&gt;categoryCounts&lt;/code&gt; 객체를 만들었죠.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  2. 객체를 배열로 변환 후 정렬&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체는 기본적으로 순서를 가지지 않기 때문에 &lt;b&gt;정렬&lt;/b&gt;하려면 먼저 &lt;code&gt;Object.entries()&lt;/code&gt;를 사용해 &lt;b&gt;배열로 변환&lt;/b&gt;해야 합니다. 변환한 배열은 &lt;code&gt;sort()&lt;/code&gt; 메서드를 사용해 정렬할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.1 카테고리 이름 기준으로 정렬&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;javascript&quot;&gt;&lt;code&gt;const sortedByCategory = Object.entries(categoryCounts).sort((a, b) =&amp;gt; {
  return a[0].localeCompare(b[0]);
});

console.log('Sorted by Category Name:', sortedByCategory);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.2 개수 기준으로 정렬&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;javascript&quot;&gt;&lt;code&gt;const sortedByCount = Object.entries(categoryCounts).sort((a, b) =&amp;gt; {
  return b[1] - a[1];
});

console.log('Sorted by Count:', sortedByCount);&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  3. 정렬 결과 출력&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬 결과는 배열로 반환됩니다. 각 배열의 요소는 &lt;code&gt;[category, count]&lt;/code&gt; 형태로 이루어져 있죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 &lt;b&gt;정렬된 결과를 다시 객체 형태&lt;/b&gt;로 변환하고 싶다면, &lt;code&gt;Object.fromEntries()&lt;/code&gt;를 사용하면 됩니다:&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const sortedCategoryCounts = Object.fromEntries(sortedByCount);
console.log('Sorted Category Counts Object:', sortedCategoryCounts);&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  전체 코드 예제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 코드는 다음과 같이 구성됩니다:&lt;/p&gt;
&lt;pre class=&quot;qml&quot;&gt;&lt;code&gt;const items = [
  { id: 1, category: 'fruit', name: 'apple' },
  { id: 2, category: 'vegetable', name: 'carrot' },
  { id: 3, category: 'fruit', name: 'banana' },
  { id: 4, category: 'fruit', name: 'orange' },
  { id: 5, category: 'vegetable', name: 'broccoli' },
  { id: 6, category: 'grain', name: 'rice' },
];

const categoryCounts = items.reduce((acc, item) =&amp;gt; {
  acc[item.category] = (acc[item.category] || 0) + 1;
  return acc;
}, {});

const sortedByCategory = Object.entries(categoryCounts).sort((a, b) =&amp;gt; {
  return a[0].localeCompare(b[0]);
});

const sortedByCount = Object.entries(categoryCounts).sort((a, b) =&amp;gt; {
  return b[1] - a[1];
});

console.log('Sorted by Category Name:', sortedByCategory);
console.log('Sorted by Count:', sortedByCount);

// 정렬된 결과를 객체로 변환
const sortedCategoryCounts = Object.fromEntries(sortedByCount);
console.log('Sorted Category Counts Object:', sortedCategoryCounts);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;code&gt;Object.entries()&lt;/code&gt;를 사용해 &lt;b&gt;배열로 변환&lt;/b&gt;해야 한다.&lt;/li&gt;
&lt;li&gt;배열로 변환한 후 &lt;code&gt;sort()&lt;/code&gt; 메서드를 사용해 &lt;b&gt;카테고리 이름&lt;/b&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;</description>
      <category>Java Script</category>
      <category>array sort</category>
      <category>java script reduce</category>
      <category>object sort</category>
      <category>object to array</category>
      <category>object 정렬</category>
      <category>reduce 사용법</category>
      <category>자바 스크립트</category>
      <category>자바 스크립트 object</category>
      <category>자바스크립트 reduce</category>
      <category>정렬</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/250</guid>
      <comments>https://tae-hui.tistory.com/entry/Java-Script-Array%EC%97%90%EC%84%9C-%ED%8A%B9%EC%A0%95-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B0%9C%EC%88%98-%EA%B5%AC%ED%95%98%EA%B8%B0-reduce#entry250comment</comments>
      <pubDate>Mon, 12 Aug 2024 15:17:52 +0900</pubDate>
    </item>
    <item>
      <title>[DB] DB Table Key의 종류</title>
      <link>https://tae-hui.tistory.com/entry/DB-DB-Table-Key%EC%9D%98-%EC%A2%85%EB%A5%98</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  1. DB Table 생성할 때 Key의 종류&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스 테이블을 설계할 때, 키(key)는 매우 중요한 역할을 합니다. 각 키의 종류와 그 사용 이유, 그리고 예시 코드를 통해 쉽게 이해해보시죠.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  1.1 후보키 (Candidate Key)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;후보키는 테이블 내의 튜플을 고유하게 식별할 수 있는 속성(또는 속성들의 집합)을 의미합니다. 하나의 테이블에는 여러 개의 후보키가 존재할 수 있습니다. 후보키는 반드시 최소성을 만족해야 합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시:&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;학생 테이블에서 &lt;code&gt;학번&lt;/code&gt;과 &lt;code&gt;주민등록번호&lt;/code&gt;는 각각 학생을 고유하게 식별할 수 있는 후보키가 될 수 있습니다.&lt;/li&gt;
&lt;li&gt;또한, &lt;code&gt;이름&lt;/code&gt;과 &lt;code&gt;생년월일&lt;/code&gt;의 조합도 고유하게 식별할 수 있다면 후보키가 될 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;CREATE TABLE Student (
    student_id INT,
    name VARCHAR(50),
    ssn CHAR(13),
    birth_date DATE,
    PRIMARY KEY (student_id),
    UNIQUE (ssn),
    UNIQUE (name, birth_date)
);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  1.2 기본키 (Primary Key)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본키는 후보키 중에서 선택된 키로, 테이블 내의 각 행을 고유하게 식별합니다. 기본키는 null 값을 가질 수 없고, 중복될 수 없습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시:&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Student&lt;/code&gt; 테이블에서 &lt;code&gt;student_id&lt;/code&gt;가 기본키로 선택되었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;CREATE TABLE Student (
    student_id INT PRIMARY KEY,
    name VARCHAR(50),
    ssn CHAR(13) UNIQUE,
    birth_date DATE,
    UNIQUE (name, birth_date)
);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  1.3 참조키 (Foreign Key)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참조키는 다른 테이블의 기본키를 참조하는 키입니다. 두 테이블 간의 관계를 정의할 때 사용됩니다. 참조키는 참조하는 테이블에서 고유성을 가지지만, 참조하는 테이블에서는 반드시 유일성을 가질 필요는 없습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시:&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Enrollment&lt;/code&gt; 테이블에서 &lt;code&gt;student_id&lt;/code&gt;는 &lt;code&gt;Student&lt;/code&gt; 테이블의 &lt;code&gt;student_id&lt;/code&gt;를 참조합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;CREATE TABLE Enrollment (
    enrollment_id INT PRIMARY KEY,
    student_id INT,
    course_id INT,
    FOREIGN KEY (student_id) REFERENCES Student(student_id),
    FOREIGN KEY (course_id) REFERENCES Course(course_id)
);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  1.4 대체키 (Alternate Key)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대체키는 후보키 중 기본키로 선택되지 않은 나머지 키들을 의미합니다. 대체키도 테이블 내에서 고유성을 유지합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시:&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Student&lt;/code&gt; 테이블에서 &lt;code&gt;ssn&lt;/code&gt;이 대체키로 사용될 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;CREATE TABLE Student (
    student_id INT PRIMARY KEY,
    name VARCHAR(50),
    ssn CHAR(13) UNIQUE,
    birth_date DATE,
    UNIQUE (name, birth_date)
);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  1.5 슈퍼키 (Super Key)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;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;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시:&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학생 테이블 &lt;code&gt;Student&lt;/code&gt;에서 (student_id, name), (student_id, ssn), (ssn, name) 등은 모두 슈퍼키가 될 수 있습니다. 이 예시들은 각각 학생을 고유하게 식별할 수 있기 때문입니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;CREATE TABLE Student (
    student_id INT PRIMARY KEY,
    name VARCHAR(50),
    ssn CHAR(13) UNIQUE,
    birth_date DATE,
    UNIQUE (name, birth_date)
);&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  2. 예시 데이터를 이용한 상세 설명&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 테이블 구조를 기준으로 예시 데이터를 넣어보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;INSERT INTO Student (student_id, name, ssn, birth_date) VALUES (1, 'Alice', '900101-1234567', '1990-01-01');
INSERT INTO Student (student_id, name, ssn, birth_date) VALUES (2, 'Bob', '900202-2345678', '1990-02-02');&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Student&lt;/code&gt; 테이블에 두 명의 학생 데이터가 들어갔습니다.&lt;/li&gt;
&lt;li&gt;각 학생은 &lt;code&gt;student_id&lt;/code&gt;로 고유하게 식별됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;INSERT INTO Course (course_id, course_name) VALUES (1, 'Database Systems');
INSERT INTO Course (course_id, course_name) VALUES (2, 'Operating Systems');&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Course&lt;/code&gt; 테이블에 두 개의 과목이 추가되었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;n1ql&quot;&gt;&lt;code&gt;INSERT INTO Enrollment (enrollment_id, student_id, course_id) VALUES (1, 1, 1);
INSERT INTO Enrollment (enrollment_id, student_id, course_id) VALUES (2, 2, 2);&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Enrollment&lt;/code&gt; 테이블에 학생과 과목의 등록 정보가 추가되었습니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;student_id&lt;/code&gt;와 &lt;code&gt;course_id&lt;/code&gt;는 각각 &lt;code&gt;Student&lt;/code&gt; 테이블과 &lt;code&gt;Course&lt;/code&gt; 테이블을 참조하고 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  3. 슈퍼키와 후보키의 차이&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;슈퍼키 (Super Key)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;슈퍼키는 하나 이상의 속성(attribute)의 집합으로, 테이블 내의 모든 행을 고유하게 식별할 수 있습니다. 즉, 특정 속성 집합이 슈퍼키라면, 그 속성 집합을 이용해 각 행을 유일하게 구분할 수 있어야 합니다.&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;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;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시:&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학생 테이블 &lt;code&gt;Student&lt;/code&gt;에서 (student_id, name), (student_id, ssn), (ssn, name) 등은 모두 슈퍼키가 될 수 있습니다. 이 예시들은 각각 학생을 고유하게 식별할 수 있기 때문입니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;CREATE TABLE Student (
    student_id INT PRIMARY KEY,
    name VARCHAR(50),
    ssn CHAR(13) UNIQUE,
    birth_date DATE,
    UNIQUE (name, birth_date)
);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;후보키 (Candidate Key)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;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;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;예시:&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Student&lt;/code&gt; 테이블에서 student_id와 ssn은 각각 후보키가 될 수 있습니다. 이 두 속성은 각각 학생을 고유하게 식별할 수 있는 최소의 속성 집합이기 때문입니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;CREATE TABLE Student (
    student_id INT PRIMARY KEY,
    name VARCHAR(50),
    ssn CHAR(13) UNIQUE,
    birth_date DATE,
    UNIQUE (name, birth_date)
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, &lt;code&gt;name&lt;/code&gt;과 &lt;code&gt;birth_date&lt;/code&gt;의 조합도 유일성을 만족할 경우 후보키가 될 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;CREATE TABLE Student (
    student_id INT PRIMARY KEY,
    name VARCHAR(50),
    ssn CHAR(13) UNIQUE,
    birth_date DATE,
    UNIQUE (name, birth_date)
);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  4. 요약&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;후보키 (Candidate Key)&lt;/b&gt;: 튜플을 고유하게 식별할 수 있는 키.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;기본키 (Primary Key)&lt;/b&gt;: 후보키 중 하나를 선택하여 설정, null 값과 중복 불가.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;참조키 (Foreign Key)&lt;/b&gt;: 다른 테이블의 기본키를 참조하여 테이블 간의 관계 설정. 참조키 자체는 반드시 유일할 필요는 없습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;대체키 (Alternate Key)&lt;/b&gt;: 후보키 중 기본키로 선택되지 않은 나머지 키.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;슈퍼키 (Super Key)&lt;/b&gt;: 테이블 내 튜플을 고유하게 식별할 수 있는 모든 속성의 집합.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 키의 종류와 사용 방법을 잘 이해하셨나요? 데이터베이스 설계 시 이러한 키들을 적절히 활용하면&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DB</category>
      <category>alternate key</category>
      <category>candidate key</category>
      <category>Foreign Key</category>
      <category>Primary Key</category>
      <category>super key</category>
      <category>기본키</category>
      <category>대체키</category>
      <category>슈퍼키</category>
      <category>참조키</category>
      <category>후보키</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/249</guid>
      <comments>https://tae-hui.tistory.com/entry/DB-DB-Table-Key%EC%9D%98-%EC%A2%85%EB%A5%98#entry249comment</comments>
      <pubDate>Wed, 7 Aug 2024 08:00:26 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] 자바 버전별 차이점</title>
      <link>https://tae-hui.tistory.com/entry/Java%EC%9E%90%EB%B0%94-%EB%B2%84%EC%A0%84%EB%B3%84-%EC%B0%A8%EC%9D%B4%EC%A0%90</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1.   자바 버전별 차이점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바는 꾸준한 업데이트를 통해 많은 변화와 개선을 거쳐왔습니다. 버전별로 어떤 차이점이 있는지 알아보겠습니다. 주요 버전으로는 Java 8, Java 9, Java 10, Java 11, Java 12, Java 13, Java 14, Java 15, Java 16, Java 17이 있습니다. 여기서 각 버전별로 주요 특징과 차이점을 설명하겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Java 8&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;람다 표현식(Lambda Expressions)&lt;/li&gt;
&lt;li&gt;스트림 API (Stream API)&lt;/li&gt;
&lt;li&gt;기본 메소드 (Default Methods)&lt;/li&gt;
&lt;li&gt;java.time 패키지 (새로운 날짜와 시간 API)&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;새로운 날짜와 시간 API는 훨씬 직관적이고 사용하기 쉽습니다.&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;li&gt;&lt;b&gt;예시 코드&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Java8Example {
    public static void main(String[] args) {
        // 람다 표현식을 이용한 리스트 필터링
        List&amp;lt;String&amp;gt; names = Arrays.asList(&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;, &quot;David&quot;);
        List&amp;lt;String&amp;gt; filteredNames = names.stream()
                                          .filter(name -&amp;gt; name.startsWith(&quot;A&quot;))
                                          .collect(Collectors.toList());
        System.out.println(filteredNames);

        // 새로운 날짜와 시간 API 사용
        LocalDate today = LocalDate.now();
        LocalDate birthday = LocalDate.of(1990, Month.JANUARY, 1);
        Period p = Period.between(birthday, today);
        System.out.println(&quot;You are &quot; + p.getYears() + &quot; years old.&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Java 9&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모듈 시스템 (Module System)&lt;/li&gt;
&lt;li&gt;JShell (REPL)&lt;/li&gt;
&lt;li&gt;스트림 API 개선&lt;/li&gt;
&lt;li&gt;새로운 HTTP 클라이언트&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;JShell을 통해 빠른 테스트와 프로토타이핑이 가능합니다.&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;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;module com.example.myapp {
    requires java.base;
    requires java.sql;
}

// JShell 예시 (터미널에서 실행)
jshell&amp;gt; int sum(int a, int b) {
...&amp;gt;     return a + b;
...&amp;gt; }
jshell&amp;gt; sum(5, 3)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Java 10&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지역 변수 타입 추론 (Local-Variable Type Inference)&lt;/li&gt;
&lt;li&gt;G1 가비지 컬렉터 성능 개선&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;var 키워드를 사용하여 코드 가독성을 높일 수 있습니다.&lt;/li&gt;
&lt;li&gt;G1 가비지 컬렉터가 더욱 효율적으로 개선되었습니다.&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;var 키워드 사용 시 타입 추론에 주의가 필요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예시 코드&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;import java.util.List;

public class Java10Example {
    public static void main(String[] args) {
        var numbers = List.of(1, 2, 3, 4, 5);
        for (var number : numbers) {
            System.out.println(number);
        }

        // 타입 추론으로 인해 예상치 못한 결과가 발생할 수 있음
        var text = &quot;Hello, Java 10!&quot;;
        System.out.println(text);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Java 11&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Long-Term Support (LTS) 버전&lt;/li&gt;
&lt;li&gt;새로운 String 메소드 (lines, strip, repeat 등)&lt;/li&gt;
&lt;li&gt;HTTP 클라이언트 표준화&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;장기 지원(LTS)으로 안정적인 사용이 가능합니다.&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;일부 제거된 API에 대한 호환성 문제가 발생할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예시 코드&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public class Java11Example {
    public static void main(String[] args) {
        String str = &quot;  Hello World!  &quot;;
        System.out.println(str.strip()); // &quot;Hello World!&quot;
        System.out.println(&quot;Java&quot;.repeat(3)); // &quot;JavaJavaJava&quot;

        // HTTP 클라이언트 사용 예시
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(&quot;https://api.github.com&quot;))
                .build();
        client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
              .thenApply(HttpResponse::body)
              .thenAccept(System.out::println)
              .join();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Java 12 이후 버전&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;Java 12&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Switch 표현식 (미리보기 기능)&lt;/li&gt;
&lt;li&gt;새로운 JVM 메모리 관리 기능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예시 코드&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class Java12Example {
    public static void main(String[] args) {
        String day = &quot;TUESDAY&quot;;
        String result = switch (day) {
            case &quot;MONDAY&quot;, &quot;FRIDAY&quot;, &quot;SUNDAY&quot; -&amp;gt; &quot;Start of the week&quot;;
            case &quot;TUESDAY&quot; -&amp;gt; &quot;Midweek&quot;;
            default -&amp;gt; &quot;Weekday&quot;;
        };
        System.out.println(result); // &quot;Midweek&quot;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Java 13&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;새로운 재사용 가능한 컴파일러 API&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예시 코드&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;ceylon&quot;&gt;&lt;code&gt;public class Java13Example {
    public static void main(String[] args) {
        String textBlock = &quot;&quot;&quot;
                {
                    &quot;name&quot;: &quot;Alice&quot;,
                    &quot;age&quot;: 30
                }
                &quot;&quot;&quot;;
        System.out.println(textBlock);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Java 14&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Switch 표현식 표준화&lt;/li&gt;
&lt;li&gt;NullPointerException 메시지 개선&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예시 코드&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class Java14Example {
    public static void main(String[] args) {
        Object obj = null;
        try {
            obj.toString();
        } catch (NullPointerException e) {
            System.out.println(&quot;Caught exception: &quot; + e.getMessage());
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Java 15&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;ZGC 개선 및 새로운 숨겨진 클래스 기능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예시 코드&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;ceylon&quot;&gt;&lt;code&gt;public class Java15Example {
    public static void main(String[] args) {
        String html = &quot;&quot;&quot;
                &amp;lt;html&amp;gt;
                    &amp;lt;body&amp;gt;
                        &amp;lt;p&amp;gt;Hello, world&amp;lt;/p&amp;gt;
                    &amp;lt;/body&amp;gt;
                &amp;lt;/html&amp;gt;
                &quot;&quot;&quot;;
        System.out.println(html);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Java 16&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;패턴 매칭 for instanceof&lt;/li&gt;
&lt;li&gt;기록 클래스 (Record Classes)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예시 코드&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class Java16Example {
    public static void main(String[] args) {
        Object obj = &quot;Hello, Java 16!&quot;;
        if (obj instanceof String s) {
            System.out.println(s.toUpperCase());
        }

        record Person(String name, int age) {}
        Person person = new Person(&quot;Alice&quot;, 30);
        System.out.println(person.name() + &quot; is &quot; + person.age() + &quot; years old.&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Java 17&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;또 다른 Long-Term Support (LTS) 버전&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;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class Java17Example {
    public static void main(String[] args) {
        // Pattern matching for switch (preview)
        Object obj = 123;
        String result = switch (obj) {
            case Integer i -&amp;gt; &quot;Integer: &quot; + i;
            case String s -&amp;gt; &quot;String: &quot; + s;
            default -&amp;gt; &quot;Unknown type&quot;;
        };
        System.out.println(result); // &quot;Integer: 123&quot;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바는 각 버전마다 다양한 새로운 기능과 개선 사항이 도입되어 왔습니다. 주요 버전의 특징과 장단점을 이해하면 더 나은 선택을 할 수 있을 것입니다. 각 버전별 주요 변경 사항을 통해 필요에 맞는 자바 버전을 선택하고 활용하시기 바랍니다.&lt;/p&gt;</description>
      <category>Java</category>
      <category>Java</category>
      <category>java 11</category>
      <category>Java 17</category>
      <category>java version</category>
      <category>JDK 11</category>
      <category>JDK 17</category>
      <category>jdk 8</category>
      <category>jdk version</category>
      <category>자바 버전 별 차이점</category>
      <category>자바 버전 정리</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/248</guid>
      <comments>https://tae-hui.tistory.com/entry/Java%EC%9E%90%EB%B0%94-%EB%B2%84%EC%A0%84%EB%B3%84-%EC%B0%A8%EC%9D%B4%EC%A0%90#entry248comment</comments>
      <pubDate>Fri, 2 Aug 2024 00:39:16 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] JAVA로 REST API 구현하기</title>
      <link>https://tae-hui.tistory.com/entry/JAVA-JAVA%EB%A1%9C-REST-API-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  1. REST API란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST API는 &lt;b&gt;Representational State Transfer&lt;/b&gt;의 약자로, 웹에서 자원을 효율적으로 교환하기 위해 만들어진 아키텍처 스타일이에요. 간단히 말해, 웹 애플리케이션이 서버와 데이터를 주고받는 방식 중 하나죠. RESTful한 시스템은 자원(Resource), 자원의 상태(Representation), 이 자원을 조작하기 위한 표준 HTTP 메서드(GET, POST, PUT, DELETE)를 사용해요.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  2. HTTP 메서드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST API의 핵심은 HTTP 메서드를 사용해 자원과 상호작용하는 거예요. 주요 메서드에는 GET, POST, PUT, PATCH, DELETE 등이 있어요. 각 메서드는 특정 작업을 나타내요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ GET&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GET 메서드는 서버에서 자원을 가져올 때 사용해요. 보통 데이터를 조회(read)할 때 쓰죠.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;GET /users&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 요청은 모든 사용자의 정보를 가져오라는 의미예요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ POST&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;POST 메서드는 서버에 자원을 생성할 때 사용해요. 새로운 데이터를 추가(insert)할 때 쓰죠.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;POST /users
{
  &quot;name&quot;: &quot;John&quot;,
  &quot;email&quot;: &quot;john@example.com&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 요청은 새로운 사용자를 생성하라는 의미예요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ PUT&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PUT 메서드는 서버의 자원을 업데이트할 때 사용해요. 전체 자원을 교체하는 방식으로 이해하면 돼요.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;PUT /users/1
{
  &quot;name&quot;: &quot;John Doe&quot;,
  &quot;email&quot;: &quot;john.doe@example.com&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 요청은 ID가 1인 사용자의 정보를 전체적으로 업데이트하라는 의미예요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ PATCH&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PATCH 메서드는 서버의 자원을 부분적으로 업데이트할 때 사용해요. 일부 데이터만 수정할 때 쓰죠.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;PATCH /users/1
{
  &quot;email&quot;: &quot;new.email@example.com&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 요청은 ID가 1인 사용자의 이메일만 업데이트하라는 의미예요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ DELETE&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DELETE 메서드는 서버의 자원을 삭제할 때 사용해요.&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;DELETE /users/1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 요청은 ID가 1인 사용자를 삭제하라는 의미예요.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  3. JAVA로 REST API 사용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Java로 REST API를 어떻게 사용하는지 예시 코드를 통해 알아보아요. Spring Boot 프레임워크를 사용해서 간단한 RESTful 웹 서비스를 만들어볼게요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ Spring Boot 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Spring Boot 프로젝트를 생성하고, 필요한 의존성을 추가해요. &lt;code&gt;build.gradle&lt;/code&gt; 파일에 다음과 같이 설정할 수 있어요.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;plugins {
    id 'org.springframework.boot' version '2.5.4'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    runtimeOnly 'com.h2database:h2'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ 데이터 모델 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;User 데이터를 나타내는 간단한 모델 클래스를 생성해요.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;package com.example.demo;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String name;
    private String email;

    // getters and setters
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ 레포지토리 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JPA를 이용해 데이터베이스와 상호작용하는 레포지토리 인터페이스를 생성해요.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;package com.example.demo;

import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository&amp;lt;User, Integer&amp;gt; {
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ 컨트롤러 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 REST API의 엔드포인트를 정의하는 컨트롤러 클래스를 만들어볼게요.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping(&quot;/users&quot;)
public class UserController {

    @Autowired
    private UserRepository userRepository;

    @GetMapping
    public List&amp;lt;User&amp;gt; getAllUsers() {
        return userRepository.findAll();
    }

    @PostMapping
    public User createUser(@RequestBody User user) {
        return userRepository.save(user);
    }

    @PutMapping(&quot;/{id}&quot;)
    public User updateUser(@PathVariable int id, @RequestBody User user) {
        return userRepository.findById(id)
                .map(existingUser -&amp;gt; {
                    existingUser.setName(user.getName());
                    existingUser.setEmail(user.getEmail());
                    return userRepository.save(existingUser);
                })
                .orElseGet(() -&amp;gt; {
                    user.setId(id);
                    return userRepository.save(user);
                });
    }

    @PatchMapping(&quot;/{id}&quot;)
    public User partialUpdateUser(@PathVariable int id, @RequestBody User user) {
        return userRepository.findById(id)
                .map(existingUser -&amp;gt; {
                    if (user.getName() != null) {
                        existingUser.setName(user.getName());
                    }
                    if (user.getEmail() != null) {
                        existingUser.setEmail(user.getEmail());
                    }
                    return userRepository.save(existingUser);
                })
                .orElse(null);
    }

    @DeleteMapping(&quot;/{id}&quot;)
    public void deleteUser(@PathVariable int id) {
        userRepository.deleteById(id);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ 애플리케이션 실행&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot 애플리케이션을 실행하기 위해 메인 클래스를 설정해요.&lt;/p&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  4. JpaRepository에 대한 설명&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;UserRepository&lt;/code&gt; 인터페이스는 &lt;code&gt;JpaRepository&lt;/code&gt;를 확장해요. Spring Data JPA가 제공하는 &lt;code&gt;JpaRepository&lt;/code&gt;는 CRUD (Create, Read, Update, Delete) 작업을 위한 기본 메서드를 자동으로 구현해줘요. 주요 메서드들에는 다음과 같은 것들이 있어요:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;save(S entity)&lt;/code&gt;: 엔티티를 저장하거나 업데이트해요.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;findById(ID id)&lt;/code&gt;: 주어진 ID로 엔티티를 찾아요.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;findAll()&lt;/code&gt;: 모든 엔티티를 리스트로 반환해요.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;deleteById(ID id)&lt;/code&gt;: 주어진 ID로 엔티티를 삭제해요.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 메서드들은 SQL 쿼리를 직접 작성하지 않아도 데이터베이스와 상호작용할 수 있게 해줘요.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  5. JPA의 동작 원리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java Persistence API (JPA)는 자바 애플리케이션에서 관계형 데이터베이스를 사용하는 방식을 표준화한 인터페이스에요. JPA의 핵심 개념과 작동 방식은 다음과 같아요:&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ 엔티티(Entity)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스 테이블과 매핑되는 클래스에요. &lt;code&gt;@Entity&lt;/code&gt; 어노테이션을 사용해 정의해요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ 엔티티 매니저(Entity Manager)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엔티티의 생명주기를 관리해요. 엔티티를 데이터베이스에 삽입, 수정, 삭제, 조회하는 작업을 수행해요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ 영속성 컨텍스트(Persistence Context)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엔티티 매니저가 관리하는 엔티티 인스턴스의 집합이에요. 엔티티가 영속성 컨텍스트 내에 있으면 &quot;영속 상태&quot;라고 해요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ 트랜잭션(Transaction)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스 작업의 논리적 단위에요. 모든 작업은 트랜잭션 내에서 이루어져야 해요. 트랜잭션은 원자성을 보장해요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JPA는 이러한 개념을 바탕으로 동작하며, 개발자는 JPA를 통해 데이터베이스와의 상호작용을 쉽게 할 수 있어요.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  6. DeleteMapping의 동작 원리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;@DeleteMapping&lt;/code&gt; 어노테이션은 단순히 HTTP DELETE 요청을 처리하는 메서드라는 것을 명시해줄 뿐이에요. 실제 데이터베이스에서 삭제 작업을 수행하는 것은 &lt;code&gt;UserRepository&lt;/code&gt;의 &lt;code&gt;deleteById&lt;/code&gt; 메서드에 의해 이루어져요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;deleteById&lt;/code&gt; 메서드는 JPA 레포지토리 인터페이스의 기본 메서드 중 하나로, 엔티티의 ID를 인자로 받아 해당 엔티티를 데이터베이스에서 삭제해요. 이 메서드는 내부적으로 JPA가 제공하는 표준 SQL DELETE 쿼리를 생성하고 실행해요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, &lt;code&gt;@DeleteMapping&lt;/code&gt; 어노테이션을 사용하면 Spring Boot는 해당 엔드포인트에 대한 HTTP DELETE 요청을 받았을 때, 자동으로 연결된 메서드를 호출하고, 그 메서드에서 &lt;code&gt;deleteById&lt;/code&gt;를 통해 데이터베이스 삭제 작업을 수행하게 돼요.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  7. REST API의 장점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST API는 다음과 같은 장점들이 있어요:&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;b&gt;유연성&lt;/b&gt;: 다양한 데이터 형식을 지원해요 (JSON, XML 등).&lt;/li&gt;
&lt;li&gt;&lt;b&gt;가독성&lt;/b&gt;: URL과 HTTP 메서드를 사용해 직관적이에요.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;성능&lt;/b&gt;: HTTP의 캐싱 기능을 사용할 수 있어 성능이 좋아요.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST API는 현대 웹 개발에서 빼놓을 수 없는 중요한 기술이에요. GET, POST, PUT, PATCH, DELETE 같은 메서드를 잘 이해하고 활용하면, 효율적이고 확장 가능한 웹 서비스를 만들 수 있어요.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;</description>
      <category>Java</category>
      <category>API</category>
      <category>java api</category>
      <category>Java REST API</category>
      <category>rest api</category>
      <category>rest api 구현하는 방법</category>
      <category>rest api 란?</category>
      <category>rest api 종류</category>
      <category>자바 api 구현</category>
      <category>자바로 api 구현</category>
      <author>TaeHuiLee</author>
      <guid isPermaLink="true">https://tae-hui.tistory.com/247</guid>
      <comments>https://tae-hui.tistory.com/entry/JAVA-JAVA%EB%A1%9C-REST-API-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0#entry247comment</comments>
      <pubDate>Fri, 19 Jul 2024 08:00:59 +0900</pubDate>
    </item>
  </channel>
</rss>