<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>발자취</title>
    <link>https://usingsystem.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sun, 21 Jun 2026 16:32:04 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>usingsystem</managingEditor>
    <image>
      <title>발자취</title>
      <url>https://tistory1.daumcdn.net/tistory/5514864/attach/97f134c571764f6e9a7658fd5ac08774</url>
      <link>https://usingsystem.tistory.com</link>
    </image>
    <item>
      <title>[AI] 딥러닝 개념정리</title>
      <link>https://usingsystem.tistory.com/578</link>
      <description>&lt;h2 data-end=&quot;189&quot; data-start=&quot;158&quot; data-ke-size=&quot;size26&quot;&gt;1. 신경망(Neural Network)과 딥러닝&lt;/h2&gt;
&lt;p data-end=&quot;411&quot; data-start=&quot;191&quot; data-ke-size=&quot;size16&quot;&gt;신경망은 중요한 feature를 사람이 직접 설계하지 않고, &lt;b&gt;데이터로부터 스스로 특징을 학습하고 가중치(weight)를 부여하는 모델&lt;/b&gt;이다.&lt;br /&gt;여러 층(layer)에 걸친 내부 파라미터를 학습하여 복잡한 패턴을 표현할 수 있으며, &lt;b&gt;raw data를 거의 그대로 입력으로 사용 가능&lt;/b&gt;하다는 장점이 있다.&lt;br /&gt;이러한 특성 때문에 컴퓨터 비전, 자연어 처리 등 다양한 분야에서 활용된다.&lt;/p&gt;
&lt;p data-end=&quot;487&quot; data-start=&quot;413&quot; data-ke-size=&quot;size16&quot;&gt;신경망은 지도학습(supervised learning)과 비지도학습(unsupervised learning) 모두에 사용될 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;621&quot; data-start=&quot;489&quot; data-ke-size=&quot;size16&quot;&gt;신경망은 기본적으로 입력층(input layer), 은닉층(hidden layer), 출력층(output layer)으로 구성된다.&lt;br /&gt;은닉층이 없는 경우 단층 퍼셉트론(perceptron) 또는 선형 모델과 유사한 형태가 된다.&lt;/p&gt;
&lt;p data-end=&quot;621&quot; data-start=&quot;489&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;657&quot; data-start=&quot;628&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;뉴런(Perceptron)과 활성화 함수&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;785&quot; data-start=&quot;659&quot; data-ke-size=&quot;size16&quot;&gt;신경망에서 하나의 뉴런(neuron)은 퍼셉트론(perceptron)이라고도 불리며, 하나의 함수로 볼 수 있다.&lt;br /&gt;뉴런 내부에는 &lt;b&gt;pre-activation&lt;/b&gt;과 &lt;b&gt;activation(활성화 함수)&lt;/b&gt; 단계가 존재한다.&lt;/p&gt;
&lt;p data-end=&quot;621&quot; data-start=&quot;489&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;z=w1x1+w2x2+bz = w_1 x_1 + w_2 x_2 + b&lt;/span&gt;&lt;span aria-hidden=&quot;true&quot;&gt;&lt;span&gt;&lt;span&gt;z&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;w&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;w&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;b&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;887&quot; data-start=&quot;820&quot; data-ke-size=&quot;size16&quot;&gt;위 식이 &lt;b&gt;pre-activation 값&lt;/b&gt;이며, 여기에 활성화 함수를 적용하면 다음 레이어로 전달되는 출력값이 된다.&lt;/p&gt;
&lt;p data-end=&quot;887&quot; data-start=&quot;820&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;887&quot; data-start=&quot;820&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;만약 히든레이어없이 입력층과 출력층만 존재하고 출력층에 Linear Regression을 한다면 전통적인 머신러닝의 Linear Regression과 다를게 없다.&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;activation Functions 종류&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sigmoid - output에서 많이 사용됨 범위 (0~1)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tanh - 시계열 데이터 처리에서 많이 사용됨 (-1 ~ 1)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;relu -&amp;nbsp; 현대 deep learning은 거의 relu사용 범위 ( 0~x), 기울기가 항상 1&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;activation(출력) 종류&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Softmax Activation Function - 출력값의 다중 class 분류를 위해 출력값에 대해 정규화 (확률 분포 출력)&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;1) Neural Network 훈련의 핵심 방법&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot; data-start=&quot;711&quot; data-end=&quot;755&quot;&gt;
&lt;p data-start=&quot;713&quot; data-end=&quot;755&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;손실함수 &amp;rarr; 역전파로 gradient 계산 &amp;rarr; 경사하강법으로 업데이트&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot; data-start=&quot;757&quot; data-end=&quot;797&quot;&gt;이 세 가지는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;훈련 과정에서 모두 필수적으로 연결되어 동작&lt;/b&gt;합니다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;(1) 손실함수&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;경사하강법과 오차 역전파가 동작하려면 손실함수가 필요.&lt;span&gt;&amp;nbsp;&lt;/span&gt;모델의 예측과 정답 사이의 차이를 수치로 나타냅니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;비용함수(cost function), 목적함수(object function) 등으로 불리며 경사하강법이 가능하도록&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;미분 가능한 함수&lt;/b&gt;를 정의&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;손실함수는 &lt;b&gt;모델 전체가 아니라 출력값에만 적용&lt;/b&gt;된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Input&amp;nbsp;&amp;rarr;&amp;nbsp;Hidden&amp;nbsp;Layers&amp;nbsp;&amp;rarr;&amp;nbsp;Output&amp;nbsp;Layer&amp;nbsp;&amp;rarr;&amp;nbsp;Loss&amp;nbsp;Function&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;딥러닝을 사용하여 문제를 해결 하기 위한 대표적 손실함수(loss) 구하는 3가지 ( 대부분 이 3가지를 사용 )&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;Linear Regression (선형회귀 문제에서) - MSE(Mean Squared Error) 실수 값을 예측하는 회귀 문제에서 사용된다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Binary Classification (이진분류 or Logistic Regression 문제에서) - Binary-Cross-Entropy &lt;b&gt;Sigmoid 출력&lt;/b&gt;과 함께 사용되며, 두 클래스 간 확률 예측에 사용된다. &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Multi-Class Classification (다중분류 문제에서) - 항상 &lt;b&gt;활성화함수(activation)은 softmax로 사용해야하며 loss함수는 Categorical-Cross-Entropy&lt;/b&gt; &lt;b&gt;출력층에&amp;nbsp;&lt;/b&gt;함께 사용된다, &lt;b&gt;이 때 softmax를 사용하기 위해서는 one hot encoding을 필수적으로 해줘야한다.&lt;/b&gt; 여러 클래스 확률 분포를 학습한다. so&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;(2) Backpropagation (오차 역전파)&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;손실 함수를 기준으로, 각 가중치가 얼마나 손실에 기여했는지 미분 해서 기울기(&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;gradient&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&amp;nbsp;를 구합니다.즉&lt;span&gt;&amp;nbsp;&lt;/span&gt;손실함수를 최소화 하는 방향으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;신경망 전체의 parameter(w혹은 b)가 어느 방향으로 얼마나 바뀌어야 하는지(gradient)를 계산하는것 (경사하강법에 사용할 방향을 구하는 부분임.)&lt;/b&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;(3) Gradient Descent (경사하강법)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미분에 의한 함수 기울기를 구해 최소값을 찾아가는 방법으로 오차 역전파로 계산된 gradient(기울기 방)를 사용해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;가중치를 실제로 업데이트&lt;/b&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목적 - 실제값과 예측값의 차이를 최소화 하는 parameter(w혹은 b) 발견&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방법 - 손실함수를 정의하여 손실함수의 값이 0으로 수렴하도록 parameter (w혹은 b) 조절&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;new w = old w - (learning rate) * (gradient)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㄴ learning rate - 이동속도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㄴ gradient - 미분 기울기 방향 cost function, 음의기울기면 +, 양의 기울기면 -&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style13&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 26.5116%;&quot;&gt;개념&lt;/td&gt;
&lt;td style=&quot;width: 40.814%;&quot;&gt;역할&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 26.5116%;&quot;&gt;&lt;b&gt;손실 함수&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.814%;&quot;&gt;예측이 얼마나 틀렸는지 평가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 26.5116%;&quot;&gt;&lt;b&gt;역전파(backprop)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.814%;&quot;&gt;모든 파라미터의 gradient 계산&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 26.5116%;&quot;&gt;&lt;b&gt;경사하강법(optimizer)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.814%;&quot;&gt;gradient를 이용해 실제 파라미터 업데이트&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;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;2) 경사하강법안에 Global Minimum, Learning Rate, Optimizer&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경사하강법은 손실 함수의 global minimum을 찾기 위해, learning rate를 사용하여 optimizer 방식으로 파라미터를 업데이트하는 과정이다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot; data-start=&quot;335&quot; data-end=&quot;369&quot;&gt;(1) Global Minimum: 손실 함수의 최종 목표( 경사하강법의 목적지)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot; data-start=&quot;371&quot; data-end=&quot;490&quot;&gt;Global Minimum은 손실 함수가 가질 수 있는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;전역 최소값&lt;/b&gt;을 의미한다. 손실 함수는 모델이 예측한 값과 실제 값의 차이를 수치로 표현한 것이며, 이 값이 작을수록 모델의 성능이 좋다고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot; data-start=&quot;492&quot; data-end=&quot;544&quot;&gt;손실 함수의 값을 지형으로 비유하면, 다양한 언덕과 골짜기가 있는 복잡한 지형이 된다. 이때:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-start=&quot;546&quot; data-end=&quot;629&quot;&gt;
&lt;li data-start=&quot;546&quot; data-end=&quot;587&quot;&gt;&lt;b&gt;Local Minimum&lt;/b&gt;은 특정 영역에서 가장 낮은 지점이며&lt;/li&gt;
&lt;li data-start=&quot;588&quot; data-end=&quot;629&quot;&gt;&lt;b&gt;Global Minimum&lt;/b&gt;은 전체 지형에서 가장 낮은 지점이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot; data-start=&quot;631&quot; data-end=&quot;802&quot;&gt;딥러닝 모델의 학습 목표는 이론적으로는 Global Minimum에 도달하는 것이지만, 실제로는 파라미터 수가 매우 많고 지형이 복잡하기 때문에 Global Minimum을 정확히 찾는 것은 거의 불가능하다. 대신, 일반화 성능이 좋은 적절한 Local Minimum에 도달하는 것이 현실적인 목표가 된다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot; data-start=&quot;809&quot; data-end=&quot;842&quot;&gt;(2) Learning Rate: 파라미터 업데이트 크기 (경사하강법의 한 걸음 크기)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot; data-start=&quot;844&quot; data-end=&quot;962&quot;&gt;Learning Rate는 한 번의 학습 단계에서 파라미터를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;얼마나 크게 업데이트할지&lt;/b&gt;를 결정하는 하이퍼파라미터이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot; data-start=&quot;1052&quot; data-end=&quot;1204&quot;&gt;Learning Rate가 너무 크면 손실 함수의 최소점을 지나쳐 발산하거나 진동할 수 있고, 너무 작으면 학습 속도가 매우 느려지거나 최적점에 도달하지 못할 수 있다. 따라서 Learning Rate는 딥러닝 모델 학습에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;가장 중요한 하이퍼파라미터&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;중 하나로 간주된다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot; data-start=&quot;1211&quot; data-end=&quot;1247&quot;&gt;(3) Optimizer: 파라미터를 어떻게 업데이트할 것인가 (경사하강법의 내려가는 방법)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot; data-start=&quot;1249&quot; data-end=&quot;1328&quot;&gt;Optimizer는 손실 함수에 대한 기울기(gradient)를 이용하여&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;파라미터를 어떤 방식으로 업데이트할지&lt;/b&gt;를 결정하는 알고리즘이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot; data-start=&quot;1330&quot; data-end=&quot;1401&quot;&gt;가장 기본적인 Optimizer는 Gradient Descent이며, 실제 딥러닝에서는 다음과 같은 알고리즘들이 널리 사용된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-start=&quot;1403&quot; data-end=&quot;1692&quot;&gt;
&lt;li data-start=&quot;1403&quot; data-end=&quot;1507&quot;&gt;&lt;b&gt;SGD (Stochastic Gradient Descent)&lt;/b&gt;: 미니배치 단위로 파라미터를 업데이트하며, 노이즈를 통해 Local Minimum에서 탈출하는 데 도움이 된다.&lt;/li&gt;
&lt;li data-start=&quot;1508&quot; data-end=&quot;1563&quot;&gt;&lt;b&gt;Momentum&lt;/b&gt;: 이전 기울기 정보를 누적하여 진동을 줄이고 빠르게 수렴하도록 한다.&lt;/li&gt;
&lt;li data-start=&quot;1564&quot; data-end=&quot;1611&quot;&gt;&lt;b&gt;RMSProp&lt;/b&gt;: 파라미터별로 학습률을 조절하여 안정적인 학습을 돕는다.&lt;/li&gt;
&lt;li data-start=&quot;1612&quot; data-end=&quot;1692&quot;&gt;&lt;b&gt;Adam&lt;/b&gt;: Momentum과 RMSProp의 장점을 결합한 알고리즘으로,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;현재 가장 많이 사용되는 기본 Optimizer로 간주된다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot; data-start=&quot;1694&quot; data-end=&quot;1764&quot;&gt;Optimizer는 Learning Rate를 활용하여 파라미터 업데이트를 수행하며, 학습 속도와 안정성에 큰 영향을 미친다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot; data-start=&quot;1771&quot; data-end=&quot;1788&quot;&gt;(4) 세 개념의 관계 정리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot; data-start=&quot;1790&quot; data-end=&quot;1827&quot;&gt;딥러닝 학습 과정에서 이 세 개념은 다음과 같은 흐름으로 연결된다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot; data-start=&quot;1829&quot; data-end=&quot;1994&quot;&gt;
&lt;li data-start=&quot;1829&quot; data-end=&quot;1873&quot;&gt;손실 함수가 정의되고 Global Minimum이 이론적 목표가 된다.&lt;/li&gt;
&lt;li data-start=&quot;1874&quot; data-end=&quot;1925&quot;&gt;Backpropagation을 통해 각 파라미터에 대한 gradient가 계산된다.&lt;/li&gt;
&lt;li data-start=&quot;1926&quot; data-end=&quot;1994&quot;&gt;Optimizer가 Learning Rate를 사용하여 파라미터를 업데이트하고, 손실 함수의 최소값을 향해 이동한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot; data-start=&quot;1996&quot; data-end=&quot;2080&quot;&gt;즉,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Global Minimum은 목표&lt;/b&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Learning Rate는 이동 크기&lt;/b&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Optimizer는 이동 전략&lt;/b&gt;이라고 볼 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot; data-start=&quot;1996&quot; data-end=&quot;2080&quot;&gt;2. Hyper-parameter와 과적합 방지 기법&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) epoch&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 DataSet의 neural network를 통해 한번 처리(학습)된 것으로 하나의 epoch는 한번에 처리하기 큰 사이즈 이므로 여러 개의 batch로 나누어 처리한다. 훈련을 위해서는 여러 번 epoch를 반복해야한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) Dropout regularization&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dropout은&amp;nbsp;학습&amp;nbsp;과정에서&amp;nbsp;뉴런을&amp;nbsp;확률적으로&amp;nbsp;비활성화(drop)하는&amp;nbsp;정규화&amp;nbsp;기법으로,&amp;nbsp;특정&amp;nbsp;뉴런이나&amp;nbsp;feature에&amp;nbsp;대한&amp;nbsp;과도한&amp;nbsp;의존을&amp;nbsp;방지하여&amp;nbsp;과적합(overfitting)을&amp;nbsp;줄이기&amp;nbsp;위해&amp;nbsp;사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dropout은&amp;nbsp;신경망을&amp;nbsp;랜덤한&amp;nbsp;서브네트워크들의&amp;nbsp;앙상블로&amp;nbsp;학습시키는&amp;nbsp;효과를&amp;nbsp;갖는다.&lt;/p&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. CNN(Convolutional neural network) - 합성곱 신경망&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;CNN은 특별한 Layers를 가지고 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) Convolutionnal Lyaer(합성곱층)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Image 정보의 공간적 지역 특성 보존&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Kernel(Filter)을 이용한 이미지 특성 추출 (filter란 스스로학습하는 parameter이다.) kenel은 경사하강법과 오차역전파로 나온 학습된 값이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;410&quot; data-origin-height=&quot;151&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uZnMq/dJMcabJ4qZE/DN1gFeWK9YMciMj1c8gXqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uZnMq/dJMcabJ4qZE/DN1gFeWK9YMciMj1c8gXqK/img.png&quot; data-alt=&quot;출처 : https://wikidocs.net/64066&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uZnMq/dJMcabJ4qZE/DN1gFeWK9YMciMj1c8gXqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuZnMq%2FdJMcabJ4qZE%2FDN1gFeWK9YMciMj1c8gXqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;410&quot; height=&quot;151&quot; data-origin-width=&quot;410&quot; data-origin-height=&quot;151&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : https://wikidocs.net/64066&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;input(사진픽샐) -&amp;gt; image patch * kernel =&amp;nbsp; ouput&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 나오는 output은 같은 값이 나온 거는 같은 패턴이라는 인식을 할 수 있다.&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;- padding : 합성곱층을 하면 할 수록 입력과 커널의 곱으로 출력층이 작아진다. 만약 중요한 정보가 있으면 유실이 날 수 있다. 이런 현상을 막기위해 주위에 x, y축에 의미 없는 0 값을 둘러주는 것이 padding이다.&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가지 특성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Locality&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- kernel size&amp;nbsp; 만큼의 작은 구역(patch)의 인접한 pixel 들에 대한 correlation관계를 비선형 필터를 적용하여 추출한다 이러한 특성을 적용하여 필터를 여러개 적용하면다양한 local특징을 추출 가능하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;image 전체가 아니라 filter area에만 연결&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. parameter sharing&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- input 상의 모든 patch들이 동일한 kernel을 적용하여 next layer의 output을 출력한다. 이럴 경우 parameter의 수를 획기적으로 줄일 수 있다. 즉 동일한&amp;nbsp;커널(필터)을&amp;nbsp;이미지&amp;nbsp;전체에&amp;nbsp;반복&amp;nbsp;적용하는&amp;nbsp;매개변수&amp;nbsp;공유&amp;nbsp;덕분에,&amp;nbsp;각&amp;nbsp;연결마다&amp;nbsp;가중치를&amp;nbsp;갖는&amp;nbsp;밀집층보다&amp;nbsp;매개변수가&amp;nbsp;훨씬&amp;nbsp;적어&amp;nbsp;효율적입니다.&lt;/p&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) Pooling Layer(풀링층)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Image data의 정보 손실 없는 압축 -&amp;gt; 계산량 및 메모리 사용량 축소, 파라미터의 수 감소(과적합 방지)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계산은 하지만 pooling의 뉴런은 가중치가없음. 오로지 사이즈만 줄이는게 목적으로 풀링 최대값을 갖고오는 풀링방법과 평균 값을 가져오는 평균값을 추출하는 연산이 있다. max풀링 방법이 정확도가 더 정확하다.&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;&amp;nbsp;스트라이드가&amp;nbsp;커지면&amp;nbsp;이동&amp;nbsp;횟수가&amp;nbsp;줄어들어&amp;nbsp;결과&amp;nbsp;이미지의&amp;nbsp;크기가&amp;nbsp;더&amp;nbsp;작아집니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;352&quot; data-origin-height=&quot;127&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCcdmV/dJMcaiblHTP/HNhHhInNETT5EdKwMUXWE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCcdmV/dJMcaiblHTP/HNhHhInNETT5EdKwMUXWE0/img.png&quot; data-alt=&quot;출처 : https://wikidocs.net/62306&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCcdmV/dJMcaiblHTP/HNhHhInNETT5EdKwMUXWE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCcdmV%2FdJMcaiblHTP%2FHNhHhInNETT5EdKwMUXWE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;352&quot; height=&quot;127&quot; data-origin-width=&quot;352&quot; data-origin-height=&quot;127&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : https://wikidocs.net/62306&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) CNN의 기초가 되는 LeNet-5&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LeNet-5는 1998년Yann LeCun이 제안한 초기 CNN(Convolutional Neural Network) 모델로 주로 손글씨 숫자 인식(우편번호 인식 등)에 사용되었고, 현대 딥러닝 CNN의 시초라고 볼 수 있다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;nbsp;Layer&lt;/td&gt;
&lt;td&gt;타입&lt;/td&gt;
&lt;td&gt;뉴런&lt;/td&gt;
&lt;td&gt;커널&lt;/td&gt;
&lt;td&gt;Stride&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;tr&gt;
&lt;td&gt;Input&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;td&gt;32&amp;times;32&amp;times;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;흑백 이미지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C1&lt;/td&gt;
&lt;td&gt;Conv2d&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;5&amp;times;5&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;28&amp;times;28&amp;times;6&lt;/td&gt;
&lt;td&gt;156&lt;/td&gt;
&lt;td&gt;Tanh&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S2&lt;/td&gt;
&lt;td&gt;max Pool&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;2&amp;times;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;14&amp;times;14&amp;times;6&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;학습가능 scale, bias&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C3&lt;/td&gt;
&lt;td&gt;Conv2d&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;5&amp;times;5&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;10&amp;times;10&amp;times;16&lt;/td&gt;
&lt;td&gt;1,516&lt;/td&gt;
&lt;td&gt;부분 연결&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S4&lt;/td&gt;
&lt;td&gt;max Pool&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;2&amp;times;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;5&amp;times;5&amp;times;16&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;학습가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C5&lt;/td&gt;
&lt;td&gt;dense&lt;/td&gt;
&lt;td&gt;120&lt;/td&gt;
&lt;td&gt;5&amp;times;5&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&amp;times;1&amp;times;120&lt;/td&gt;
&lt;td&gt;48,120&lt;/td&gt;
&lt;td&gt;Fully Connected Layer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;F6&lt;/td&gt;
&lt;td&gt;dense&lt;/td&gt;
&lt;td&gt;84&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;84&lt;/td&gt;
&lt;td&gt;10,164&lt;/td&gt;
&lt;td&gt;Fully Connected Layer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output&lt;/td&gt;
&lt;td&gt;softmax&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;850&lt;/td&gt;
&lt;td&gt;Softmax&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1639&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cPqSCW/dJMcabpOAJq/3MVPgAW1H144mgflkcKdV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cPqSCW/dJMcabpOAJq/3MVPgAW1H144mgflkcKdV1/img.png&quot; data-alt=&quot;출처 : https://d2l.ai/_images/lenet.svg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cPqSCW/dJMcabpOAJq/3MVPgAW1H144mgflkcKdV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcPqSCW%2FdJMcabpOAJq%2F3MVPgAW1H144mgflkcKdV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1639&quot; height=&quot;450&quot; data-origin-width=&quot;1639&quot; data-origin-height=&quot;450&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : https://d2l.ai/_images/lenet.svg&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4) Transfer Learning (전이학습)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Transfer&amp;nbsp;Learning(전이학습)은&amp;nbsp;이미&amp;nbsp;대규모&amp;nbsp;데이터로&amp;nbsp;사전&amp;nbsp;학습(pretrained)&amp;nbsp;된&amp;nbsp;모델의&amp;nbsp;가중치(weight)를&amp;nbsp;가져와 &lt;br /&gt;새로운&amp;nbsp;문제에&amp;nbsp;재사용하는&amp;nbsp;기법이다.&amp;nbsp;특히&amp;nbsp;CNN&amp;nbsp;기반&amp;nbsp;이미지&amp;nbsp;모델에서&amp;nbsp;매우&amp;nbsp;널리&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;CNN 구조는 크게 두 부분으로 나뉜다:&lt;br /&gt;1) Convolutional Layers&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특징(feature) 추출 역할, 일반적인 이미지 특징 학습, 재사용 가능 &lt;br /&gt;&lt;br /&gt;2) Fully Connected Layers &lt;br /&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;따라서 전이학습에서는 이렇게&amp;nbsp;구성한다 &lt;br /&gt;Pretrained&amp;nbsp;Conv&amp;nbsp;Layers&amp;nbsp;&amp;nbsp;&amp;rarr;&amp;nbsp;그대로&amp;nbsp;사용 &lt;br /&gt;Custom&amp;nbsp;Dense&amp;nbsp;Layers&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;rarr;&amp;nbsp;새로&amp;nbsp;추가 &lt;br /&gt;&lt;br /&gt;전이학습 방식 2가지&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;&amp;nbsp;1. Feature Extraction 방식&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1222&quot; data-start=&quot;1147&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1182&quot; data-start=&quot;1147&quot;&gt;기존 Conv Layer를 &lt;b&gt;freeze (동결)&lt;/b&gt; 시킴&lt;/li&gt;
&lt;li data-end=&quot;1197&quot; data-start=&quot;1183&quot;&gt;가중치 업데이트 안 함&lt;/li&gt;
&lt;li data-end=&quot;1222&quot; data-start=&quot;1198&quot;&gt;새로 추가한 Dense Layer만 학습&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1227&quot; data-start=&quot;1224&quot; data-ke-size=&quot;size16&quot;&gt;장점 : 학습 빠름, 작은 데이터셋에서 안정적&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Fine-Tuning 방식&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1319&quot; data-start=&quot;1283&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1305&quot; data-start=&quot;1283&quot;&gt;많은 데이터가 있을 때, 일부 Conv Layer도 함께 학습, 가중치 일부 업데이트&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1324&quot; data-start=&quot;1321&quot; data-ke-size=&quot;size16&quot;&gt;장점 : 더 높은 성능 가능, 새로운 도메인에 적응 가능&lt;/p&gt;
&lt;p data-end=&quot;1324&quot; data-start=&quot;1321&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;1324&quot; data-start=&quot;1321&quot; data-ke-size=&quot;size26&quot;&gt;4. RNN(Recurrent Neural Network)&lt;/h2&gt;
&lt;p data-end=&quot;1324&quot; data-start=&quot;1321&quot; data-ke-size=&quot;size16&quot;&gt;시퀀스 데이터(시계열 데이터)에 특화 되어있음.&lt;/p&gt;
&lt;p data-end=&quot;1324&quot; data-start=&quot;1321&quot; data-ke-size=&quot;size16&quot;&gt;기억 능력을 갖고 있다. 시퀀스데이터는 순서대로 무언가 들어오는 데이터를 기억하고 있다가 어떤 결과가 나오는지 표현해야 하기 때문(새로운 입력이 들어올 때 마다 네트워크는 자신의 기억을 조금씩 수정) 입력을 모두 처리하고 난 후 네트워크에게 남겨진 기억은 시퀀스 전체를 요약함&lt;/p&gt;
&lt;p data-end=&quot;1324&quot; data-start=&quot;1321&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1324&quot; data-start=&quot;1321&quot; data-ke-size=&quot;size16&quot;&gt;사용하는 곳&lt;/p&gt;
&lt;p data-end=&quot;1324&quot; data-start=&quot;1321&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;532&quot; data-start=&quot;525&quot;&gt;주가 예측&lt;/li&gt;
&lt;li data-end=&quot;544&quot; data-start=&quot;533&quot;&gt;센서 데이터 분석&lt;/li&gt;
&lt;li data-end=&quot;554&quot; data-start=&quot;545&quot;&gt;IoT 데이터&lt;/li&gt;
&lt;li data-end=&quot;566&quot; data-start=&quot;555&quot;&gt;서버 트래픽 예측&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1324&quot; data-start=&quot;1321&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Rnn을 순서대로 펼쳐 놓으면 weight를 공유하는 매우 deep한 neural network가 된다.&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;1324&quot; data-start=&quot;1321&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;연결된 backpropagation 을 사용한다.(BPTT라고함)&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;1324&quot; data-start=&quot;1321&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1324&quot; data-start=&quot;1321&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;output은 다중분류일 경우 softmax, 연속된 수일 경우 linear regression을 사용하지만 다중분류를 더 잘 맞춘다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;1324&quot; data-start=&quot;1321&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1324&quot; data-start=&quot;1321&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LSTN(Long Short Term Memory) 가장 많이 사용되는 RNN모델중 하나.&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;1324&quot; data-start=&quot;1321&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장기기억을 계속 유지하는 long term memory가 존재한다.&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-end=&quot;1324&quot; data-start=&quot;1321&quot; data-ke-size=&quot;size26&quot;&gt;5. Autoencoder&lt;/h2&gt;
&lt;p data-end=&quot;164&quot; data-start=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;오토인코더(Autoencoder)는 &lt;b&gt;입력 데이터를 그대로 출력으로 복원하도록 학습하는 신경망이다.&lt;/b&gt; 겉으로 보기에는 단순히 &amp;ldquo;입력을 복사하는 모델&amp;rdquo;처럼 보일 수 있지만, 실제 목적은 복사가 아니다. &lt;b&gt;핵심은 데이터를 압축하는 과정에서 그 데이터의 본질적인 구조를 스스로 학습하도록 만드는 데 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;364&quot; data-start=&quot;166&quot; data-ke-size=&quot;size16&quot;&gt;일반적인 신경망은 입력을 받아 정답 라벨을 예측한다. 예를 들어 이미지를 보고 숫자를 분류하거나, 값을 예측하는 식이다. 그러나 오토인코더는 정답 라벨이 필요하지 않다. 입력 자체가 곧 정답이 된다. 모델은 입력을 받아 내부적으로 압축한 뒤, 다시 원래 형태로 복원하도록 학습된다. 이 과정에서 입력과 출력의 차이를 최소화하는 방향으로 가중치를 조정한다.&lt;/p&gt;
&lt;p data-end=&quot;616&quot; data-start=&quot;366&quot; data-ke-size=&quot;size16&quot;&gt;오토인코더의 구조는 크게 세 부분으로 나뉜다. 첫 번째는 인코더(Encoder)이다. 인코더는 입력 데이터를 점점 작은 차원으로 줄여 나간다. 두 번째는 병목 구간(Bottleneck)이다. 이 부분은 차원이 가장 작으며, 입력 데이터의 핵심 정보만 담고 있는 잠재 표현(latent representation)이 된다. 세 번째는 디코더(Decoder)이다. 디코더는 압축된 정보를 다시 원래 차원으로 확장하여 입력과 유사한 데이터를 만들어낸다.&lt;/p&gt;
&lt;p data-end=&quot;818&quot; data-start=&quot;618&quot; data-ke-size=&quot;size16&quot;&gt;중요한 점은 병목 구간의 존재이다. 만약 중간 차원을 줄이지 않는다면 모델은 단순히 입력을 그대로 복사하는 방법을 배워버릴 수 있다. 하지만 차원을 강제로 줄이면, 모델은 제한된 공간 안에 데이터를 표현해야 한다. 이 과정에서 불필요한 정보나 노이즈는 제거되고, 데이터의 구조적 특징만 남게 된다. 즉, 오토인코더는 압축을 통해 중요한 패턴을 학습하게 된다.&lt;/p&gt;
&lt;p data-end=&quot;1001&quot; data-start=&quot;820&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 손글씨 이미지 데이터가 있다고 하자. 각 이미지는 수백 개의 픽셀 값으로 이루어져 있지만, 실제로 숫자를 구분하는 데 중요한 정보는 선의 모양, 굵기, 위치와 같은 특징들이다. 오토인코더는 이러한 핵심 요소를 작은 차원의 벡터 안에 담아 표현하는 방법을 학습한다. 그리고 그 표현을 이용해 다시 이미지를 복원한다.&lt;/p&gt;
&lt;p data-end=&quot;1192&quot; data-start=&quot;1003&quot; 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-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;1345&quot; data-start=&quot;1194&quot; data-ke-size=&quot;size16&quot;&gt;결국 오토인코더는 단순한 복원 모델이 아니다. 입력 데이터를 압축하고 복원하는 과정을 통해 데이터가 가진 본질적인 구조를 이해하도록 학습하는 모델이다. 라벨 없이도 데이터의 특징을 스스로 학습할 수 있다는 점에서, 딥러닝에서 중요한 비지도 학습 기법 중 하나로 여겨진다.&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;1345&quot; data-start=&quot;1194&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1158&quot; data-start=&quot;1152&quot; data-ke-size=&quot;size16&quot;&gt;오토인코더는 데이터의 구조를 학습하는 모델이다. (분류 모델 아님, 예측 모델 아님 라벨 필요 없음)&lt;/p&gt;
&lt;p data-end=&quot;1255&quot; data-start=&quot;1218&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;비지도 학습 (Unsupervised Learning)&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;1255&quot; data-start=&quot;1218&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1283&quot; data-start=&quot;1262&quot; 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 data-end=&quot;1295&quot; data-start=&quot;1285&quot;&gt;차원 축소 : PCA보다 강력 (비선형 가능)&lt;/li&gt;
&lt;li data-end=&quot;1295&quot; data-start=&quot;1285&quot;&gt;노이즈 제거 : Denoising Autoencoder&lt;/li&gt;
&lt;li data-end=&quot;1295&quot; data-start=&quot;1285&quot;&gt;이상 탐지 : 정상 데이터만 학습 &amp;rarr; 이상 데이터는 복원 못함 &amp;rarr; 오차 큼&lt;/li&gt;
&lt;li data-end=&quot;1295&quot; data-start=&quot;1285&quot;&gt;특징 추출 : Encoder 부분만 떼서 feature extractor로 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. GAN(Generative adversarial network)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;computer가 이미지, 인간의 목소리, 악기소리 등을 실제와 같이 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위조를 담당하는 generator(생성자)와 위조를 판별하는 Discriminator(판별자)의 두개의 Deep Neural network으로 구성된다.&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI</category>
      <author>usingsystem</author>
      <guid isPermaLink="true">https://usingsystem.tistory.com/578</guid>
      <comments>https://usingsystem.tistory.com/578#entry578comment</comments>
      <pubDate>Tue, 3 Mar 2026 22:29:23 +0900</pubDate>
    </item>
    <item>
      <title>[AI] Apache Spark 개념정리</title>
      <link>https://usingsystem.tistory.com/575</link>
      <description>&lt;h2 data-end=&quot;97&quot; data-start=&quot;72&quot; data-ke-size=&quot;size26&quot;&gt;1. Apache Spark 개념 및 특징 정리&lt;/h2&gt;
&lt;p data-end=&quot;234&quot; data-start=&quot;99&quot; data-ke-size=&quot;size16&quot;&gt;Apache Spark는 &lt;b&gt;대규모 데이터 분석과 분산 처리&lt;/b&gt;를 위해 만들어진 오픈소스 프레임워크입니다.&lt;br /&gt;빅데이터 환경에서 빠른 속도, 다양한 데이터 처리 기능, 그리고 확장성을 제공합니다.&lt;/p&gt;
&lt;p data-end=&quot;246&quot; data-start=&quot;241&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;246&quot; data-start=&quot;241&quot; data-ke-size=&quot;size16&quot;&gt;특징&lt;/p&gt;
&lt;p data-end=&quot;277&quot; data-start=&quot;248&quot; data-ke-size=&quot;size16&quot;&gt;1. High Performance (고성능)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;606&quot; data-start=&quot;278&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;481&quot; data-start=&quot;278&quot;&gt;&lt;b&gt;In-Memory 연산&lt;/b&gt;을 통해 기존 Hadoop MapReduce보다 훨씬 빠른 속도를 제공합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;481&quot; data-start=&quot;344&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;413&quot; data-start=&quot;344&quot;&gt;MapReduce는 디스크 기반으로 데이터를 저장하고 네트워크를 통해 데이터를 주고받는 과정이 많아 속도가 느립니다.&lt;/li&gt;
&lt;li data-end=&quot;481&quot; data-start=&quot;416&quot;&gt;Spark는 데이터를 &lt;b&gt;메모리에 올려서 연산&lt;/b&gt;을 수행하고, 꼭 필요할 때만 디스크나 네트워크를 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;522&quot; data-start=&quot;482&quot;&gt;덕분에 &lt;b&gt;최대 100배 이상 빠른 성능&lt;/b&gt;을 보여주기도 합니다.&lt;/li&gt;
&lt;li data-end=&quot;606&quot; data-start=&quot;523&quot;&gt;머신러닝용 라이브러리(MLlib), 그래프 연산 라이브러리(GraphX) 등을 지원하여 데이터 분석을 더 쉽고 효율적으로 진행할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;637&quot; data-start=&quot;613&quot; data-ke-size=&quot;size16&quot;&gt;2. Scalability (확장성)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;907&quot; data-start=&quot;638&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;684&quot; data-start=&quot;638&quot;&gt;Spark는 &lt;b&gt;수평 확장(Scale-Out)&lt;/b&gt; 구조로 설계되어 있습니다.&lt;/li&gt;
&lt;li data-end=&quot;739&quot; data-start=&quot;685&quot;&gt;동일하거나 유사한 사양의 서버(노드)를 여러 대 묶어 하나의 &lt;b&gt;클러스터&lt;/b&gt;를 구성합니다.&lt;/li&gt;
&lt;li data-end=&quot;796&quot; data-start=&quot;740&quot;&gt;이를 통해 &lt;b&gt;페타바이트(PB)~제타바이트(ZB)급의 대규모 데이터&lt;/b&gt;도 처리할 수 있습니다.&lt;/li&gt;
&lt;li data-end=&quot;907&quot; data-start=&quot;797&quot;&gt;Spark는 다양한 클러스터 매니저 위에서 동작할 수 있습니다:
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;907&quot; data-start=&quot;839&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;865&quot; data-start=&quot;839&quot;&gt;&lt;b&gt;Apache Hadoop YARN&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;888&quot; data-start=&quot;868&quot;&gt;&lt;b&gt;Apache Mesos&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;907&quot; data-start=&quot;891&quot;&gt;&lt;b&gt;Kubernetes&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;939&quot; data-start=&quot;914&quot; data-ke-size=&quot;size16&quot;&gt;3. Structured Data 지원&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1118&quot; data-start=&quot;940&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1020&quot; data-start=&quot;940&quot;&gt;Spark는 정형 데이터뿐만 아니라 &lt;b&gt;비정형(Unstructured) 데이터&lt;/b&gt;를 구조화(Structured)하여 분석할 수 있습니다.&lt;/li&gt;
&lt;li data-end=&quot;1118&quot; data-start=&quot;1021&quot;&gt;SQL처럼 다루기 쉽게 만들어주는 &lt;b&gt;Spark SQL&lt;/b&gt;을 제공하며, 다양한 소스(CSV, JSON, Parquet, Hive 등)에서 데이터를 가져올 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1166&quot; data-start=&quot;1125&quot; data-ke-size=&quot;size16&quot;&gt;4. Real-Time Data Processing (실시간 처리)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1304&quot; data-start=&quot;1167&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1221&quot; data-start=&quot;1167&quot;&gt;&lt;b&gt;Spark Streaming&lt;/b&gt; 라이브러리를 사용하면 실시간 데이터 처리도 가능합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1304&quot; data-start=&quot;1240&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1255&quot; data-start=&quot;1240&quot;&gt;실시간 로그 모니터링&lt;/li&gt;
&lt;li data-end=&quot;1275&quot; data-start=&quot;1258&quot;&gt;IoT 센서 데이터 분석&lt;/li&gt;
&lt;li data-end=&quot;1304&quot; data-start=&quot;1278&quot;&gt;클릭 스트림(Clickstream) 분석&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;1324&quot; data-start=&quot;1311&quot; data-ke-size=&quot;size26&quot;&gt;2. Spark 아키텍처&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1402&quot; data-start=&quot;1326&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1402&quot; data-start=&quot;1326&quot;&gt;Spark 클러스터는 크게 &lt;b&gt;마스터 노드(Master Node)&lt;/b&gt; 와 &lt;b&gt;워커 노드(Worker Node)&lt;/b&gt; 로 구성됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;1690&quot; data-start=&quot;1404&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;1534&quot; data-start=&quot;1404&quot;&gt;&lt;b&gt;Master Node&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1534&quot; data-start=&quot;1428&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1465&quot; data-start=&quot;1428&quot;&gt;드라이버 프로그램(Driver Program)을 실행합니다. 마스터&amp;nbsp;노드는&amp;nbsp;스파크&amp;nbsp;컨텍스트와&amp;nbsp;드라이버&amp;nbsp;프로그램을&amp;nbsp;실행하며,&amp;nbsp;클러스터의&amp;nbsp;자원을&amp;nbsp;관리하고&amp;nbsp;작업&amp;nbsp;실행을&amp;nbsp;조율하는&amp;nbsp;역할을&amp;nbsp;합니다.&lt;/li&gt;
&lt;li data-end=&quot;1534&quot; data-start=&quot;1469&quot;&gt;SparkContext가 존재하며, 클러스터 매니저(YARN, Mesos 등)에게 작업(Job)을 전달합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1690&quot; data-start=&quot;1536&quot;&gt;&lt;b&gt;Worker Node&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1690&quot; data-start=&quot;1560&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1590&quot; data-start=&quot;1560&quot;&gt;여러 개의 &lt;b&gt;Executor&lt;/b&gt;가 실행됩니다.&lt;/li&gt;
&lt;li data-end=&quot;1650&quot; data-start=&quot;1594&quot;&gt;Executor는 실제 연산을 담당하며, 그 안에서 &lt;b&gt;Task&lt;/b&gt; 단위로 나누어 실행합니다.&lt;/li&gt;
&lt;li data-end=&quot;1690&quot; data-start=&quot;1654&quot;&gt;필요에 따라 캐시(Cache)를 활용하여 성능을 높입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1845&quot; data-start=&quot;1692&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1845&quot; data-start=&quot;1692&quot;&gt;전체 작업 흐름:
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;1845&quot; data-start=&quot;1708&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;1755&quot; data-start=&quot;1708&quot;&gt;사용자가 Job을 제출 &amp;rarr; SparkContext가 클러스터 매니저에게 전달&lt;/li&gt;
&lt;li data-end=&quot;1787&quot; data-start=&quot;1758&quot;&gt;클러스터 매니저가 워커 노드에 Job을 배분&lt;/li&gt;
&lt;li data-end=&quot;1822&quot; data-start=&quot;1790&quot;&gt;각 워커 노드에서 Executor가 Task 실행&lt;/li&gt;
&lt;li data-end=&quot;1845&quot; data-start=&quot;1825&quot;&gt;결과를 모아 사용자에게 반환&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;1890&quot; data-start=&quot;1852&quot; data-ke-size=&quot;size26&quot;&gt;3. RDD (Resilient Distributed Dataset)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RDD는 대규모 데이터를 RAM에 올려서 클러스터 여러 노드에 분산 저장하고, 메모리 기반으로 빠르게 처리하며, 장애가 발생해도 연산 기록(lineage)을 이용해 복구할 수 있는 &lt;b&gt;Spark의 기본이되는 핵심 데이터 단위입니다. &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;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;2395&quot; data-start=&quot;1941&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;2034&quot; data-start=&quot;1941&quot;&gt;&lt;b&gt;Immutable(불변성)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2034&quot; data-start=&quot;1970&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1998&quot; data-start=&quot;1970&quot;&gt;한 번 생성된 RDD는 변경할 수 없습니다.&lt;/li&gt;
&lt;li data-end=&quot;2034&quot; data-start=&quot;2004&quot;&gt;데이터를 수정하려면 새로운 RDD가 생성됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2140&quot; data-start=&quot;2037&quot;&gt;&lt;b&gt;Fault-Tolerance(내결함성)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2140&quot; data-start=&quot;2073&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2140&quot; data-start=&quot;2073&quot;&gt;데이터가 손실되거나 손상(Corrupt)되면 자동으로 다른 노드에 있는 복제본(Replica)을 통해 복구합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2233&quot; data-start=&quot;2143&quot;&gt;&lt;b&gt;Parallel Processing(병렬 처리)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2233&quot; data-start=&quot;2184&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2233&quot; data-start=&quot;2184&quot;&gt;데이터는 클러스터의 여러 노드에 분산되어 저장&amp;middot;처리되므로 병렬 연산이 가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2395&quot; data-start=&quot;2236&quot;&gt;&lt;b&gt;Lazy Evaluation(지연 연산)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2395&quot; data-start=&quot;2273&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2341&quot; data-start=&quot;2273&quot;&gt;&lt;b&gt;지연&amp;nbsp;연산은&amp;nbsp;변환&amp;nbsp;작업을&amp;nbsp;즉시&amp;nbsp;실행하지&amp;nbsp;않고&amp;nbsp;기록해&amp;nbsp;두었다가,&amp;nbsp;collect(), save() 같은 액션(Action)이 호출되기 전까지는 실제 연산이 수행되지 않습니다.&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;2395&quot; data-start=&quot;2347&quot;&gt;이를 통해 Spark는 최적화된 실행 계획을 세워 효율적으로 연산을 수행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1764148490303&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#가상환경일 경우 패스등록
import os
import sys
os.environ[&quot;PYSPARK_PYTHON&quot;] = sys.executable&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1763983304110&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 예전 방식
import pyspark
sc = pyspark.SparkContext('local[*]') # Spark 실행 엔진을 로컬 CPU 코어로 켠 것, 메모리 생성

rdd = sc.parallelize(range(1000)) # 0~999까지 숫자를 메모리(RAM)에 분산 저장한 RDD 생성
rdd.takeSample(False, 5)&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1763983474802&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 최신방식
from pyspark.sql import SparkSession

spark = SparkSession.builder.getOrCreate() # PySpark의 통합 엔진 DataFrame, SQL, Hive, RDD 작업 모두 여기서 시작
sc = spark.sparkContext # RDD 작업을 할 때 필요

rdd = sc.parallelize(range(1000))
rdd.takeSample(False, 5)&lt;/code&gt;&lt;/pre&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. Key Value Pair 명령&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`reduceByKey`는&amp;nbsp;동일&amp;nbsp;키의&amp;nbsp;값들을&amp;nbsp;합산과&amp;nbsp;같은&amp;nbsp;연산으로&amp;nbsp;하나로&amp;nbsp;줄이는&amp;nbsp;반면,&amp;nbsp;`groupByKey`는&amp;nbsp;단순히&amp;nbsp;동일&amp;nbsp;키의&amp;nbsp;값들을&amp;nbsp;하나의&amp;nbsp;목록으로&amp;nbsp;묶습니다.&amp;nbsp;`reduceByKey`가&amp;nbsp;보통&amp;nbsp;더&amp;nbsp;효율적일&amp;nbsp;수&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;`filter`&amp;nbsp;변환은&amp;nbsp;제공된&amp;nbsp;조건(predicate)&amp;nbsp;함수를&amp;nbsp;각&amp;nbsp;요소에&amp;nbsp;적용하여&amp;nbsp;결과가&amp;nbsp;참인&amp;nbsp;요소들만&amp;nbsp;포함하는&amp;nbsp;새로운&amp;nbsp;RDD를&amp;nbsp;반환합니다.&amp;nbsp;데이터&amp;nbsp;정제&amp;nbsp;등에&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;`countByValue` 액션은 RDD의 모든 고유 요소와 해당 요소의 발생 횟수를 계산하여 (값, 개수) 쌍의 Dictionarry형태로 편리하게 반환해 줍니다. 워드 카운트 같은 작업에 적합해요.&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;`collect`&amp;nbsp;액션은&amp;nbsp;분산된&amp;nbsp;RDD의&amp;nbsp;모든&amp;nbsp;데이터를&amp;nbsp;드라이버&amp;nbsp;프로그램으로&amp;nbsp;수집하여&amp;nbsp;로컬&amp;nbsp;메모리에&amp;nbsp;파이썬&amp;nbsp;리스트&amp;nbsp;형태로&amp;nbsp;반환합니다.&amp;nbsp;RDD&amp;nbsp;크기가&amp;nbsp;클&amp;nbsp;때는&amp;nbsp;주의해야&amp;nbsp;합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1764155916857&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import pyspark

sc = pyspark.SparkContext.getOrCreate()
# Key / Value RDD

# creating Key / Value RDD
total_by_brand = rdd.map(lambda brand: (brand, 1))

# # reduceByKey(): Merge the values for each key using an associative and commutative reduce function.
from operator import add
rdd = sc.parallelize([(&quot;a&quot;, 1), (&quot;b&quot;, 1), (&quot;a&quot;, 1)])
sorted(rdd.reduceByKey(add).collect())
[('a', 2), ('b', 1)]


# groupByKey(): Group the values for each key in the RDD into a single sequence. Hash-partitions the resulting RDD with numPartitions partitions.
rdd = sc.parallelize([(&quot;a&quot;, 1), (&quot;b&quot;, 1), (&quot;a&quot;, 1)])
sorted(rdd.groupByKey().mapValues(f=len).collect())
[('a', 2), ('b', 1)]
sorted(rdd.groupByKey().mapValues(list).collect())
[('a', [1, 1]), ('b', [1])]


# sortByKey(): Sorts this RDD, which is assumed to consist of (key, value) pairs.
tmp = [('a', 1), ('b', 2), ('1', 3), ('d', 4), ('2', 5)]
sc.parallelize(tmp).sortByKey().first()
('1', 3)


# keys(), values(): Create a RDD of keys or just values
rdd = sc.parallelize([(&quot;a&quot;, 1), (&quot;b&quot;, 1), (&quot;a&quot;, 1)])
rdd.keys()
['a', 'b', 'a']

# join, rightOuterJoin, leftOuterJoin, cogroup, subtractByKey
x = sc.parallelize([(&quot;a&quot;, 1), (&quot;b&quot;, 4)])
y = sc.parallelize([(&quot;a&quot;, 2), (&quot;a&quot;, 3)])
sorted(x.join(y).collect())
[('a', (1, 2)), ('a', (1, 3))]


# Efficiency is the key for performance!!!
# if you only need values, use mapValues() or flatMapValues()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`countByValue`&amp;nbsp;액션은&amp;nbsp;RDD의&amp;nbsp;모든&amp;nbsp;고유&amp;nbsp;요소와&amp;nbsp;해당&amp;nbsp;요소의&amp;nbsp;발생&amp;nbsp;횟수를&amp;nbsp;계산하여&amp;nbsp;(값,&amp;nbsp;개수)&amp;nbsp;쌍의&amp;nbsp;사전&amp;nbsp;형태로&amp;nbsp;편리하게&amp;nbsp;반환해&amp;nbsp;줍니다.&amp;nbsp;워드&amp;nbsp;카운트&amp;nbsp;같은&amp;nbsp;작업에&amp;nbsp;적합해요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;spark는 yeild와 궁합이 좋다.&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;588&quot; data-start=&quot;543&quot;&gt;flatMap은 &lt;b&gt;각 원소가 여러 개의 결과&lt;/b&gt;를 만들 수 있도록 설계됨&lt;/li&gt;
&lt;li data-end=&quot;631&quot; data-start=&quot;589&quot;&gt;함수에서 yield를 쓰면 이터레이터(iterator)를 반환&lt;/li&gt;
&lt;li data-end=&quot;682&quot; data-start=&quot;632&quot;&gt;flatMap은 이 이터레이터를 받아 &lt;b&gt;결과를 펼쳐(flatten)&lt;/b&gt; RDD에 넣음&lt;/li&gt;
&lt;li data-end=&quot;720&quot; data-start=&quot;683&quot;&gt;장점: 메모리를 많이 쓰지 않고 &lt;b&gt;대용량 데이터 처리 가능&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1764158505476&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def get_data(line, header):
    if line != header:
        col = line.split(',')
        city = col[6].strip(&quot;\&quot;&quot;)
        avg_temp_fahr = col[4]
        yield (city, avg_temp_fahr)
		return [(city, avg_temp_fahr)]  # 이렇게도 가능하지만 느림
        
parsed_line = lines.flatMap(lambda line: get_data(line, header))&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;map vs flatMap&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`flatMap`은 각 입력 항목에서 0개 이상의 여러 출력을 생성하고 그 결과를 단일 목록으로 평면화합니다. 반면 `map`은 항상 각 입력 항목에 대해 정확히 하나의 출력 항목만 생성한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;989&quot; data-start=&quot;920&quot;&gt;&lt;b&gt;map&lt;/b&gt;: 각 원소를 &lt;b&gt;단순 변환&lt;/b&gt;할 때
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;989&quot; data-start=&quot;954&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;989&quot; data-start=&quot;954&quot;&gt;예: 모든 숫자에 10 더하기, 문자열 소문자로 변환 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1067&quot; data-start=&quot;990&quot;&gt;&lt;b&gt;flatMap&lt;/b&gt;: 각 원소를 &lt;b&gt;여러 개로 나누거나 토큰화&lt;/b&gt;할 때
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1067&quot; data-start=&quot;1037&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1067&quot; data-start=&quot;1037&quot;&gt;예: 문장 &amp;rarr; 단어, CSV 행 &amp;rarr; 여러 값 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;352&quot; data-start=&quot;114&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;함수&lt;/td&gt;
&lt;td&gt;입력 RDD 원소&lt;/td&gt;
&lt;td&gt;출력 RDD 원소&lt;/td&gt;
&lt;td&gt;특징&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;270&quot; data-start=&quot;208&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;219&quot; data-start=&quot;208&quot;&gt;map&lt;/td&gt;
&lt;td data-end=&quot;234&quot; data-start=&quot;219&quot; data-col-size=&quot;sm&quot;&gt;1개&lt;/td&gt;
&lt;td data-end=&quot;249&quot; data-start=&quot;234&quot; data-col-size=&quot;sm&quot;&gt;1개&lt;/td&gt;
&lt;td data-end=&quot;270&quot; data-start=&quot;249&quot; data-col-size=&quot;sm&quot;&gt;각 원소를 &lt;b&gt;1:1&lt;/b&gt;로 변환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;352&quot; data-start=&quot;271&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;283&quot; data-start=&quot;271&quot;&gt;flatMap&lt;/td&gt;
&lt;td data-end=&quot;298&quot; data-start=&quot;283&quot; data-col-size=&quot;sm&quot;&gt;1개&lt;/td&gt;
&lt;td data-end=&quot;311&quot; data-start=&quot;298&quot; data-col-size=&quot;sm&quot;&gt;0개 이상&lt;/td&gt;
&lt;td data-end=&quot;352&quot; data-start=&quot;311&quot; data-col-size=&quot;sm&quot;&gt;각 원소를 &lt;b&gt;0~n개&lt;/b&gt;로 변환 후 &lt;b&gt;평탄화(flatten)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1764158852168&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;rdd = sc.parallelize([&quot;hello world&quot;, &quot;hi spark&quot;])

rdd_map = rdd.map(lambda x: x.split(&quot; &quot;))
print(rdd_map.collect())

출력 : [['hello', 'world'], ['hi', 'spark']]

rdd_flat = rdd.flatMap(lambda x: x.split(&quot; &quot;))
print(rdd_flat.collect())

출력 : ['hello', 'world', 'hi', 'spark']&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 데이타프레임(Dataframe)과 SparkSQL&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spark를 제대로 쓰려면 &lt;b&gt;RDD &amp;rarr; DataFrame &amp;rarr; SparkSQL&lt;/b&gt; 흐름을 이해해야 합니다.&lt;/p&gt;
&lt;h3 data-end=&quot;117&quot; data-start=&quot;88&quot; data-ke-size=&quot;size23&quot;&gt;1) 데이터프레임(DataFrame) 이란?&lt;/h3&gt;
&lt;p data-end=&quot;176&quot; data-start=&quot;119&quot; data-ke-size=&quot;size16&quot;&gt;Spark의 DataFrame은 &lt;b&gt;RDD(기본 데이터 구조)위에서 동작하는 더 고수준의 구조화된 데이터 모델&lt;/b&gt;로 더 빠르고 구조적 이다. 형태는 Pandas DataFrame과 비슷하지만 &lt;b&gt;엄청 큰 데이터(수십 GB~TB)를 클러스터에서 병렬 처리할 수 있다는 점&lt;/b&gt;이 다릅니다.&lt;/p&gt;
&lt;h4 data-end=&quot;285&quot; data-start=&quot;267&quot; data-ke-size=&quot;size20&quot;&gt;DataFrame의 특징&lt;/h4&gt;
&lt;p data-end=&quot;314&quot; data-start=&quot;287&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) 스키마(Schema)를 가진다 ( structure)&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;403&quot; data-start=&quot;337&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;name&lt;/td&gt;
&lt;td&gt;age&lt;/td&gt;
&lt;td&gt;city&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;403&quot; data-start=&quot;381&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;388&quot; data-start=&quot;381&quot;&gt;Bob&lt;/td&gt;
&lt;td data-end=&quot;394&quot; data-start=&quot;388&quot; data-col-size=&quot;sm&quot;&gt;20&lt;/td&gt;
&lt;td data-end=&quot;403&quot; data-start=&quot;394&quot; data-col-size=&quot;sm&quot;&gt;Seoul&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-end=&quot;461&quot; data-start=&quot;405&quot; data-ke-size=&quot;size16&quot;&gt;RDD는 구조를 모르지만, DataFrame은 구조를 알기 때문에 Spark가 최적화할 수 있음.&lt;/p&gt;
&lt;p data-end=&quot;461&quot; data-start=&quot;405&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;502&quot; data-start=&quot;468&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Catalyst 옵티마이저로 자동 최적화됨(카탈리스트 최적화 도구)&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;566&quot; data-start=&quot;503&quot; data-ke-size=&quot;size16&quot;&gt;Spark SQL 엔진이 DataFrame 연산을 분석해서 가장 빠른 방식으로 실행 계획을 자동으로 만들어줌.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;613&quot; data-start=&quot;572&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;584&quot; data-start=&quot;572&quot;&gt;쓸데없는 연산 제거&lt;/li&gt;
&lt;li data-end=&quot;596&quot; data-start=&quot;585&quot;&gt;병렬 실행 최적화&lt;/li&gt;
&lt;li data-end=&quot;613&quot; data-start=&quot;597&quot;&gt;디스크/메모리 접근 최소화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;633&quot; data-start=&quot;615&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;rarr; RDD보다 훨씬 빠르다&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;633&quot; data-start=&quot;615&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;664&quot; data-start=&quot;640&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) SQL처럼 다룰 수 있다&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;696&quot; data-start=&quot;665&quot; data-ke-size=&quot;size16&quot;&gt;DataFrame은 SQL 테이블처럼 사용할 수 있어서 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;df.select(&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&quot;age&quot;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;).where(df.age &amp;gt; &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;20&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;) &lt;/span&gt;이런 식으로 직관적인 표 형태의 연산이 가능.&lt;/p&gt;
&lt;h3 data-end=&quot;810&quot; data-start=&quot;789&quot; data-ke-size=&quot;size23&quot;&gt;2) Spark SQL 이란?&lt;/h3&gt;
&lt;p data-end=&quot;855&quot; data-start=&quot;812&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;DataFrame을 SQL 문법으로 처리할 수 있게 해주는 인터페이로 &lt;/b&gt;Spark SQL 엔진이 돌아가면서 DataFrame과 SQL 사이를 연결해줌.&lt;/p&gt;
&lt;p data-end=&quot;956&quot; data-start=&quot;929&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) SQL 문법 그대로 사용 가능&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;1010&quot; data-start=&quot;957&quot; data-ke-size=&quot;size16&quot;&gt;데이터를 분석하는 사람들이 SQL을 잘 알기 때문에 복잡한 분석을 바로 SQL로 작성 가능.&lt;/p&gt;
&lt;p data-end=&quot;1030&quot; data-start=&quot;1012&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) 표현력이 쉬움&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;1059&quot; data-start=&quot;1031&quot; data-ke-size=&quot;size16&quot;&gt;코드로 하면 길어지는 작업도 SQL 한 줄이면 끝.&lt;/p&gt;
&lt;p data-end=&quot;1078&quot; data-start=&quot;1061&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) 자동 최적화&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;1107&quot; data-start=&quot;1079&quot; data-ke-size=&quot;size16&quot;&gt;SQL도 Catalyst 엔진이 알아서 최적화해줌.&lt;/p&gt;
&lt;p data-end=&quot;1107&quot; data-start=&quot;1079&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1107&quot; data-start=&quot;1079&quot; data-ke-size=&quot;size16&quot;&gt;DataFrame &amp;harr; SparkSQL 예시( SparkSession)&lt;/p&gt;
&lt;pre id=&quot;code_1764160391726&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from pyspark.sql import SparkSession

# SparkContext + SQL + Hive + DataFrame + Catalog 모두 포함한 통합 엔트리포인트
spark = SparkSession.builder.appName(&quot;SparkSQL&quot;).getOrCreate()  

# DataFrame 생성
lines = spark.sparkContext.textFile(&quot;file:///&quot;)
#data = spark.read.option(&quot;header&quot;, &quot;true&quot;)\ csv 읽을 때 사용
            .option(&quot;inferSchema&quot;, &quot;true&quot;)\ 데이터를 추론해서 타입맞춰줌
            .csv(csv_file_path)
#data.printSchema()

file_data = lines.map(parse_line)

# SQL로 사용하기 위해 등록
schema_income = spark.createDataFrame(data=file_data).cache()# ram 메모리에 프레임생성고 ㅏ올림
schema_income.createOrReplaceTempView(&quot;file_data&quot;)# sql을 사용하기 위해 테이블이름 등록

# SQL 사용 가능
result = spark.sql(&quot;&quot;&quot;
    SELECT city, AVG(temperature)
    FROM table
    GROUP BY city
&quot;&quot;&quot;)

result.show()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1541&quot; data-start=&quot;1500&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;RDD vs DataFrame vs SparkSQL 차이 (정리)&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;1778&quot; data-start=&quot;1543&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&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;tr data-end=&quot;1639&quot; data-start=&quot;1595&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1605&quot; data-start=&quot;1595&quot;&gt;&lt;b&gt;RDD&lt;/b&gt;&lt;/td&gt;
&lt;td data-end=&quot;1620&quot; data-start=&quot;1605&quot; data-col-size=&quot;sm&quot;&gt;구조 없는 분산 데이터&lt;/td&gt;
&lt;td data-end=&quot;1626&quot; data-start=&quot;1620&quot; data-col-size=&quot;sm&quot;&gt;유연함&lt;/td&gt;
&lt;td data-end=&quot;1639&quot; data-start=&quot;1626&quot; data-col-size=&quot;sm&quot;&gt;느림, 최적화 X&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1707&quot; data-start=&quot;1640&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1656&quot; data-start=&quot;1640&quot;&gt;&lt;b&gt;DataFrame&lt;/b&gt;&lt;/td&gt;
&lt;td data-end=&quot;1673&quot; data-start=&quot;1656&quot; data-col-size=&quot;sm&quot;&gt;스키마 + 구조화된 데이터&lt;/td&gt;
&lt;td data-end=&quot;1692&quot; data-start=&quot;1673&quot; data-col-size=&quot;sm&quot;&gt;Catalyst 최적화로 빠름&lt;/td&gt;
&lt;td data-end=&quot;1707&quot; data-start=&quot;1692&quot; data-col-size=&quot;sm&quot;&gt;복잡한 로직은 제한적&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1778&quot; data-start=&quot;1708&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1723&quot; data-start=&quot;1708&quot;&gt;&lt;b&gt;SparkSQL&lt;/b&gt;&lt;/td&gt;
&lt;td data-end=&quot;1747&quot; data-start=&quot;1723&quot; data-col-size=&quot;sm&quot;&gt;SQL 문법으로 DataFrame 조작&lt;/td&gt;
&lt;td data-end=&quot;1763&quot; data-start=&quot;1747&quot; data-col-size=&quot;sm&quot;&gt;SQL만 알면 분석 가능&lt;/td&gt;
&lt;td data-end=&quot;1778&quot; data-start=&quot;1763&quot; data-col-size=&quot;sm&quot;&gt;SQL에 익숙해야 함&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. Dataframe StructType&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dataframe structType이란 만약 csv파일을 읽었을 경우 헤더가 있다면&lt;b&gt; inferSchema=True&lt;/b&gt;를 해서 사용하면 될 것 이다. 하지만&amp;nbsp; 헤더가 없을 경우엔 &lt;b&gt;StructType&lt;/b&gt;을 사용해 직접 헤더를 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;헤더가 있다면&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;206&quot; data-start=&quot;100&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;136&quot; data-start=&quot;100&quot;&gt;DataFrame의 컬럼 구조를 직접 정의할 때 사용하는 타입&lt;/li&gt;
&lt;li data-end=&quot;165&quot; data-start=&quot;137&quot;&gt;각 컬럼은 &lt;b&gt;StructField&lt;/b&gt;로 구성됨&lt;/li&gt;
&lt;li data-end=&quot;206&quot; data-start=&quot;166&quot;&gt;컬럼의 이름, 데이터 타입, NULL 허용 여부 등을 설정할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;261&quot; data-start=&quot;208&quot; data-ke-size=&quot;size16&quot;&gt;즉, &lt;b&gt;&quot;이 DataFrame은 어떤 컬럼을 어떤 타입으로 가지고 있다&quot;&lt;/b&gt; 를 정의하는 것.&lt;/p&gt;
&lt;pre id=&quot;code_1764163063917&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 헤더가 없는 csv일경우 직접 structType 구현
table_schema = t.StructType([
    t.StructField(&quot;xxx1&quot;, t.StringType(), True),
    t.StructField(&quot;xxx2&quot;, t.FloatType(), True),
    t.StructField(&quot;xxx3&quot;, t.StringType(), True)])

csv_file_path = &quot;file:/data.csv&quot;
df = spark.read.schema(table_schema).csv(csv_file_path)

df.printSchema()

# 헤더 있는 csv는 inferSchema true로 사용
data = spark.read.option(&quot;header&quot;, &quot;true&quot;)\ csv 읽을 때 사용
            .option(&quot;inferSchema&quot;, &quot;true&quot;)\ 데이터를 추론해서 타입맞춰줌
            .csv(csv_file_path)
            
data.printSchema()&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. BrodCast와 BroadCast 조인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BrodCast란 모든 워커(worker)에게 동일한 데이터를 &lt;b&gt;네트워크 오버헤드 없이 빠르게 전달하기 위한 캐시 메커니즘으로 Spark에서 작은 DataFrame을 클러스터 전체의 &lt;b&gt;모든 &lt;/b&gt;Executor 메모리에 복사해서 배포하는 기능.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;422&quot; data-start=&quot;357&quot; data-ke-size=&quot;size16&quot;&gt;Spark는 액션을 실행할 때 워커 노드들이 데이터를 받을 때 &lt;b&gt;네트워크를 통해 계속 전송&lt;/b&gt;해야 할 수 있음.&lt;/p&gt;
&lt;p data-end=&quot;445&quot; data-start=&quot;424&quot; data-ke-size=&quot;size16&quot;&gt;하지만 broadcast 변수를 쓰면 드라이버(driver)가 데이터를&lt;b&gt; 한 번만 보냄&lt;/b&gt; -&amp;gt;&amp;nbsp;워커들이 로컬 메모리에 저장한 뒤 -&amp;gt; 모든 task에서 재사용 즉, 네트워크 비용 대폭 절감,&amp;nbsp;속도 엄청 빨라짐&lt;/p&gt;
&lt;p data-end=&quot;445&quot; data-start=&quot;424&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;445&quot; data-start=&quot;424&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;BroadCast 조인&lt;/b&gt;이란 작은 테이블을 전체 워커 노드에 브로드캐스트하여, 큰 테이블과 조인을 빠르게 수행하는 방식으로 일반 조인은 큰 테이블끼리 &lt;b&gt;shuffle(데이터 섞기로 데이터를 다른 파티션으로 재배치하는 과정 )을 해서 노드 사이에 엄청난 네트워크 비용이 발생함.&lt;/b&gt; 하지만 작은 테이블을 broadcast하면&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;320&quot; data-start=&quot;297&quot;&gt;작은 테이블은 모든 노드에 미리 복제됨&lt;/li&gt;
&lt;li data-end=&quot;353&quot; data-start=&quot;321&quot;&gt;&lt;b&gt;큰 테이블은 shuffle 없이 &quot;로컬&quot;에서 바로 매칭&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;369&quot; data-start=&quot;354&quot;&gt;네트워크 비용 거의 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;b&gt;대규모 데이터 조인을 매우 빠르게 처리하는 최적화&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spark는 기본적으로 작은 테이블이면 자동으로 broadcast한다. &lt;b&gt;작은 테이블만&lt;/b&gt; broadcast해야 한다. Python UDF 내부에서 broadcast.value를 사용하면 워커가 Python 프로세스를 두 번 넘나들기 때문에 조금 느릴 수 있음.&lt;/p&gt;
&lt;pre id=&quot;code_1764592944516&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;meta = {&quot;A&quot;: 1, &quot;B&quot;: 2}

occupation_dict = spark.sparkContext.broadcast(meta)

def get_name(occupation_id: str) -&amp;gt; str:
    return occupation_dict.value[occupation_id]

occupation_lookup_udf = f.udf(get_occupation_name) # 함수를 넣을 수 있게

occupation_with_name = interviewer_count.withColumn(&quot;occupation_name&quot;, occupation_lookup_udf(f.col(&quot;occupation_id&quot;)))

occupation_with_name.show(10)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;445&quot; data-start=&quot;424&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;781&quot; data-start=&quot;741&quot;&gt;broadcast_meta.value &amp;rarr; 실제로 담겨 있는 데이터&lt;/li&gt;
&lt;li data-end=&quot;810&quot; data-start=&quot;782&quot;&gt;워커에서 접근해도 네트워크로 다시 전송되지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. coalesce와 repartition&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) coalesce&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;coalesce는 빠르게 파티션 수를 줄이는 최적화 함수로 파티션을 줄일 때 사용한다. 즉 기존 &lt;b&gt;파티션 (데이터를 여러 조각으로 나눠서 여러 워커 노드가 병렬로 처리할 수 있게 만든 단위)&lt;/b&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;&lt;b&gt; shuffle(데이터를 다른 파티션으로 재배치하는 과정)&lt;/b&gt;이 발생하지 않아 매우 빠르다.&lt;/p&gt;
&lt;pre id=&quot;code_1764594371571&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;df.coalesce(1)&lt;/code&gt;&lt;/pre&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) repartition&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;repartition는 반대로 파티션을 늘리는 용도로 사용하지만&lt;b&gt; shuffle 발생한다.&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;repartitionByRange는 특정 키값으로 파티션을 늘리는걸로&amp;nbsp; repartition, coalesce와 다르게 DataFrame에서만 가능&lt;/p&gt;
&lt;pre id=&quot;code_1764594407945&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;df.repartition(10)#repartition
df.repartition(200, &quot;city&quot;, &quot;job&quot;) #repartitionByRange&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-end=&quot;282&quot; data-start=&quot;249&quot; data-ke-size=&quot;size16&quot;&gt;1) 작업 병렬성을 늘리고 싶을 때 (파티션 &amp;uarr;)&lt;/p&gt;
&lt;p data-end=&quot;351&quot; data-start=&quot;283&quot; data-ke-size=&quot;size16&quot;&gt;처음 로딩된 데이터가 파티션이 너무 적으면 CPU 코어가 놀게 됨&lt;br /&gt;&amp;rarr; repartition(n)을 통해 병렬 처리 향상&lt;/p&gt;
&lt;p data-end=&quot;396&quot; data-start=&quot;353&quot; data-ke-size=&quot;size16&quot;&gt;2) 조인/집계 성능 향상을 위해 파티션을 균등하게 맞추고 싶을 때&lt;/p&gt;
&lt;p data-end=&quot;456&quot; data-start=&quot;397&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데이터 스큐( 데이터가 균등하게 분배되지 않아 특정 Task가 병목이 되는 문제. )&lt;/b&gt;가 있을 때 사용&lt;br /&gt;예) 특정 키만 몰려 있을 때 명시적으로 repartition(&quot;key&quot;)&lt;/p&gt;
&lt;p data-end=&quot;501&quot; data-start=&quot;458&quot; data-ke-size=&quot;size16&quot;&gt;3) 대규모 저장(write) 작업 전에 필요한 파티션 개수 맞추기&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;593&quot; data-start=&quot;502&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;543&quot; data-start=&quot;502&quot;&gt;파일 출력을 1개의 파일로 만들고 싶을 때 &amp;rarr; coalesce(1)&lt;/li&gt;
&lt;li data-end=&quot;593&quot; data-start=&quot;544&quot;&gt;HDFS에 적당한 파일 개수로 저장하고 싶을 때 &amp;rarr; repartition(200)&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 193px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 44px;&quot;&gt;
&lt;td style=&quot;height: 44px; width: 24.6512%;&quot;&gt;함수&lt;/td&gt;
&lt;td style=&quot;height: 44px; width: 8.83721%;&quot;&gt;파티션&lt;/td&gt;
&lt;td style=&quot;height: 44px; width: 11.1628%;&quot;&gt;셔플&lt;/td&gt;
&lt;td style=&quot;height: 44px; width: 26.2791%;&quot;&gt;테이터 재분배 방식&lt;/td&gt;
&lt;td style=&quot;height: 44px; width: 29.0698%;&quot;&gt;사용 목적&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 44px;&quot;&gt;
&lt;td style=&quot;height: 44px; width: 24.6512%;&quot;&gt;&lt;b&gt;repartition(n)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 44px; width: 8.83721%;&quot;&gt;증가&lt;/td&gt;
&lt;td style=&quot;height: 44px; width: 11.1628%;&quot;&gt;&lt;b&gt;발생&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 44px; width: 26.2791%;&quot;&gt;해시 기반 균등 재배치&lt;/td&gt;
&lt;td style=&quot;height: 44px; width: 29.0698%;&quot;&gt;병렬성 확보, 조인/집계 정확한 분배&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 44px;&quot;&gt;
&lt;td style=&quot;height: 44px; width: 24.6512%;&quot;&gt;&lt;b&gt;repartitionByRange(n, cols)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 44px; width: 8.83721%;&quot;&gt;증가&lt;/td&gt;
&lt;td style=&quot;height: 44px; width: 11.1628%;&quot;&gt;&lt;b&gt;발생&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 44px; width: 26.2791%;&quot;&gt;&lt;b&gt;Range 정렬 기반 파티션 분배&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 44px; width: 29.0698%;&quot;&gt;정렬 기반 작업 전 최적화, 스키마 정렬&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 44px;&quot;&gt;
&lt;td style=&quot;height: 44px; width: 24.6512%;&quot;&gt;&lt;b&gt;coalesce(n)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 44px; width: 8.83721%;&quot;&gt;줄임&lt;/td&gt;
&lt;td style=&quot;height: 44px; width: 11.1628%;&quot;&gt;&lt;b&gt;없음&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 44px; width: 26.2791%;&quot;&gt;기존 파티션 일부만 합침&lt;/td&gt;
&lt;td style=&quot;height: 44px; width: 29.0698%;&quot;&gt;빠른 파티션 감소, 최소 비용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. 빈 데이터와 시간 포멧 다루는 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;na.drop 키워드&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;how : any - 데이터가 하나라도 비어있으면 삭제, all - row에 모든 데이터가 비어있으면 삭제&lt;/li&gt;
&lt;li&gt;threash : 하나의 row에 데이터 빈칸의 숫자를 지정해 지정한 숫자만큼 비어있으면 삭제&lt;/li&gt;
&lt;li&gt;subset : 특정 컬럼을 지정해 해당 컬럼이 비어있으면 삭제&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1764594742600&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;df.na.drop(how=&quot;any&quot;).show() 
df.na.drop(thresh=2).show()
df.na.drop(subset=[&quot;salary&quot;]).show()&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;na.fill 키워드&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;null인 데이터를 채우기위해&lt;/p&gt;
&lt;pre id=&quot;code_1764595062103&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# # fill string
df.na.fill(&quot;engineer&quot;).show()

# # fill integer
df.na.fill(0).show()

# # fill the subset
df.na.fill(&quot;NA&quot;, subset=[&quot;occupation&quot;]).show()

# # fill the mean value
mean_value = df.select(f.mean(df['salary'])).collect()
df.na.fill(mean_value[0][0], subset=[&quot;salary&quot;]).show()&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;date&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1764595186653&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# # show year
df.select(f.year('date')).show()

# # show month
df.select(f.month('date')).show()

# # show day
df.select(f.dayofmonth('date').alias('day')).show()
df.select(f.dayofyear('date').alias('day')).show()&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333;&quot;&gt;10. YARN &lt;span style=&quot;text-align: start;&quot;&gt;(Yet Another Resource Negotiator)&lt;/span&gt; 이란?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-end=&quot;202&quot; data-start=&quot;120&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하둡의 클러스터 자원을 관리하고, 애플리케이션 실행을 스케줄링해주는 시스템&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;268&quot; data-start=&quot;209&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;228&quot; data-start=&quot;209&quot;&gt;누가 CPU를 얼마나 쓰는지&lt;/li&gt;
&lt;li data-end=&quot;247&quot; data-start=&quot;229&quot;&gt;메모리를 얼마만큼 배분할지&lt;/li&gt;
&lt;li data-end=&quot;268&quot; data-start=&quot;248&quot;&gt;어떤 노드에서 작업을 실행할지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;288&quot; data-start=&quot;270&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;모두 YARN이 관리&lt;/b&gt;한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11. Dataframe의 API 카테고리&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) Transformations&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스파크 특성상 데이터를 수정할 수 없다. 이 때 Transformations는 RDD/DataFrame을 입력 &amp;rarr; 새로운 RDD/DataFrame을 만드는 작업으로 &lt;b&gt;즉시 실행되지 않고&lt;/b&gt;, DAG(작업 계획)에만 기록되며 Action을 만나면 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DAG는 만들어두면 Spark가 뒤에서 최적화(scheduler)&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 176px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;span style=&quot;color: #ffffff; text-align: start;&quot;&gt; Transformation&amp;nbsp; &lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;span style=&quot;color: #ffffff; text-align: start;&quot;&gt; 설명&amp;nbsp; &lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;span style=&quot;color: #ffffff; text-align: start;&quot;&gt; Dependency&amp;nbsp; &lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;이유&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;map&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;각 요소를 1:1로 변환&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;Narrow&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;파티션 내에서만 처리됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;flatMap&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;요소 하나 &amp;rarr; 여러 요소&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;Narrow&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;파티션 내에서만 처리됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;filter&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;조건에 맞는 요소만 통과&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;Narrow&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;파티션 내부만 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;mapPartitions&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;파티션 단위 변환&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;Narrow&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;부모 파티션 1개에만 의존&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;sample&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;샘플링&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;Narrow&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;shuffle 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;coalesce(n, shuffle = false)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;파티션 수 줄임&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;Narrow&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;데이터 이동 거의 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt; union &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #333333; text-align: start;&quot;&gt;두 RDD 합침&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt; Narrow &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #333333; text-align: start;&quot;&gt;shuffle 없음 (그냥 두 파티션 나열)&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table style=&quot;letter-spacing: 0px; border-collapse: collapse; width: 100%; height: 233px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 35px;&quot;&gt;
&lt;td style=&quot;width: 21.1628%; height: 35px;&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt; &lt;span style=&quot;background-color: #6ed3d8; text-align: start;&quot;&gt;Transformation&lt;/span&gt;&lt;span style=&quot;background-color: #6ed3d8; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt; &lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.9767%; height: 35px;&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt; &lt;span style=&quot;background-color: #6ed3d8; text-align: start;&quot;&gt;설명&lt;/span&gt;&lt;span style=&quot;background-color: #6ed3d8; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt; &lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 12.5582%; height: 35px;&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt; &lt;span style=&quot;background-color: #6ed3d8; text-align: start;&quot;&gt;Dependency&lt;/span&gt;&lt;span style=&quot;background-color: #6ed3d8; text-align: start;&quot;&gt;&amp;nbsp;&lt;/span&gt; &lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.3024%; height: 35px;&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt; 이유 &lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;width: 21.1628%; height: 22px;&quot;&gt;&lt;b&gt;distinct&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.9767%; height: 22px;&quot;&gt;중복 제거&lt;/td&gt;
&lt;td style=&quot;width: 12.5582%; height: 22px;&quot;&gt;&lt;b&gt;Wide&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.3024%; height: 22px;&quot;&gt;같은 값을 모으기 위해 전체 파티션 재배치(shuffle) 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;width: 21.1628%; height: 22px;&quot;&gt;&lt;b&gt;repartition(n)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.9767%; height: 22px;&quot;&gt;파티션 수 재조정&lt;/td&gt;
&lt;td style=&quot;width: 12.5582%; height: 22px;&quot;&gt;&lt;b&gt;Wide&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.3024%; height: 22px;&quot;&gt;모든 데이터를 섞어서 파티션 재분배&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;width: 21.1628%; height: 22px;&quot;&gt;&lt;b&gt;sortBy / orderBy&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.9767%; height: 22px;&quot;&gt;전체 정렬&lt;/td&gt;
&lt;td style=&quot;width: 12.5582%; height: 22px;&quot;&gt;&lt;b&gt;Wide&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.3024%; height: 22px;&quot;&gt;global ordering 위해 shuffle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;width: 21.1628%; height: 22px;&quot;&gt;&lt;b&gt;groupByKey&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.9767%; height: 22px;&quot;&gt;key 기준 그룹화&lt;/td&gt;
&lt;td style=&quot;width: 12.5582%; height: 22px;&quot;&gt;&lt;b&gt;Wide&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.3024%; height: 22px;&quot;&gt;모든 동일 key를 한 파티션으로 모아야 함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;width: 21.1628%; height: 22px;&quot;&gt;&lt;b&gt;reduceByKey&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.9767%; height: 22px;&quot;&gt;key 기준 reduce&lt;/td&gt;
&lt;td style=&quot;width: 12.5582%; height: 22px;&quot;&gt;&lt;b&gt;Wide&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.3024%; height: 22px;&quot;&gt;shuffle 발생하지만 groupByKey보다 효율적&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;width: 21.1628%; height: 22px;&quot;&gt;&lt;b&gt;join&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.9767%; height: 22px;&quot;&gt;RDD/DataFrame join&lt;/td&gt;
&lt;td style=&quot;width: 12.5582%; height: 22px;&quot;&gt;&lt;b&gt;Wide&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.3024%; height: 22px;&quot;&gt;key 기준 서로 섞어야 함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;width: 21.1628%; height: 22px;&quot;&gt;&lt;b&gt;cogroup&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.9767%; height: 22px;&quot;&gt;여러 RDD key-group&lt;/td&gt;
&lt;td style=&quot;width: 12.5582%; height: 22px;&quot;&gt;&lt;b&gt;Wide&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.3024%; height: 22px;&quot;&gt;shuffle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;width: 21.1628%; height: 22px;&quot;&gt;&lt;b&gt;distinct&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 21.9767%; height: 22px;&quot;&gt;중복 제거&lt;/td&gt;
&lt;td style=&quot;width: 12.5582%; height: 22px;&quot;&gt;&lt;b&gt;Wide&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.3024%; height: 22px;&quot;&gt;같은 데이터를 모아야 하기 때문에 shuffle&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) Narrow Dependency(좁은 의존성) 과 Wide Dependency(넓은 의존성)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dependency란 RDD/DataFrame Transformation이 이전 Transformation의 어떤 파티션에 의존하는지를 나타내는 개념.&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;Narrow Dependency는 자식 RDD의 하나의 파티션이 부모 RDD의 소수(일반적으로 하나)의 파티션만 참조하는 경우로 파티션 이동이 없고 네트워크 통신이 없다. 즉 Executor가 자기 파티션만 보면되고 Shuffle이 없어 빠르다.&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;Wide Dependency는 자식 RDD의 하나의 파티션이 부모 RDD의 여러 파티션에 의존하는 경우로 여러 부모 파티션에서 데이터를 가져와야하고 네트워크 shuffle이 발생해서 비용이 큰 작업이므로 느리다.&lt;/p&gt;
&lt;pre id=&quot;code_1764851219376&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;textFile &amp;rarr; flatMap &amp;rarr; map &amp;rarr; reduceByKey &amp;rarr; sortByKey

flatMap		Narrow	한 파티션이 그대로 변환
map		Narrow	동일
reduceByKey	Wide	같은 key를 하나로 모아야 함 (shuffle)
sortByKey	Wide	전 파티션에서 key 정보를 모아야 함&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) Actions&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Transformations로 정의한 DAG를 실제로 실행시키는 명령으로 즉, &lt;b&gt;결과를 반환하거나 저장할 때 실행된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spark의 모든 동작은 Transformation으로 DAG를 만들고 Action에서 한 번에 실행된다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;1112&quot; data-start=&quot;835&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt; Action &lt;/b&gt;&lt;/td&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;906&quot; data-start=&quot;869&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;885&quot; data-start=&quot;869&quot;&gt;&lt;b&gt;collect()&lt;/b&gt;&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;906&quot; data-start=&quot;885&quot;&gt;전체 데이터를 드라이버로 가져옴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;936&quot; data-start=&quot;907&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;920&quot; data-start=&quot;907&quot;&gt;&lt;b&gt;show()&lt;/b&gt;&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;936&quot; data-start=&quot;920&quot;&gt;DataFrame 출력&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;963&quot; data-start=&quot;937&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;951&quot; data-start=&quot;937&quot;&gt;&lt;b&gt;take(n)&lt;/b&gt;&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;963&quot; data-start=&quot;951&quot;&gt;n개만 가져오기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;987&quot; data-start=&quot;964&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;978&quot; data-start=&quot;964&quot;&gt;&lt;b&gt;count()&lt;/b&gt;&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;987&quot; data-start=&quot;978&quot;&gt;개수 세기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1010&quot; data-start=&quot;988&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1002&quot; data-start=&quot;988&quot;&gt;&lt;b&gt;first()&lt;/b&gt;&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1010&quot; data-start=&quot;1002&quot;&gt;첫 요소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1043&quot; data-start=&quot;1011&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1026&quot; data-start=&quot;1011&quot;&gt;&lt;b&gt;reduce()&lt;/b&gt;&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1043&quot; data-start=&quot;1026&quot;&gt;모든 요소를 하나로 합침&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1077&quot; data-start=&quot;1044&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1067&quot; data-start=&quot;1044&quot;&gt;&lt;b&gt;saveAsTextFile()&lt;/b&gt;&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1077&quot; data-start=&quot;1067&quot;&gt;파일로 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1112&quot; data-start=&quot;1078&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1094&quot; data-start=&quot;1078&quot;&gt;&lt;b&gt;foreach()&lt;/b&gt;&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1112&quot; data-start=&quot;1094&quot;&gt;각 요소에 대해 함수 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1764850737096&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;rdd = sc.textFile(&quot;file.txt&quot;)

# Transformation: DAG에만 기록됨
words = rdd.flatMap(lambda line: line.split(&quot; &quot;))
pairs = words.map(lambda w: (w, 1))
counts = pairs.reduceByKey(lambda a, b: a + b)

# Action: 실제 실행
print(counts.collect())&lt;/code&gt;&lt;/pre&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;1053&quot; data-start=&quot;858&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;액션&lt;/td&gt;
&lt;td&gt;메모리&lt;/td&gt;
&lt;td&gt;사용 위치 설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;968&quot; data-start=&quot;915&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;931&quot; data-start=&quot;915&quot;&gt;&lt;b&gt;collect()&lt;/b&gt;&lt;/td&gt;
&lt;td data-end=&quot;942&quot; data-start=&quot;931&quot; data-col-size=&quot;sm&quot;&gt;드라이버 메모리&lt;/td&gt;
&lt;td data-end=&quot;968&quot; data-start=&quot;942&quot; data-col-size=&quot;sm&quot;&gt;모든 데이터를 드라이버로 가져옴 &amp;rarr; 위험&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1053&quot; data-start=&quot;969&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1001&quot; data-start=&quot;969&quot;&gt;&lt;b&gt;count(), reduce(), show()&lt;/b&gt;&lt;/td&gt;
&lt;td data-end=&quot;1016&quot; data-start=&quot;1001&quot; data-col-size=&quot;sm&quot;&gt;Executor 메모리&lt;/td&gt;
&lt;td data-end=&quot;1053&quot; data-start=&quot;1016&quot; data-col-size=&quot;sm&quot;&gt;계산은 Executor 메모리에서 수행 후 결과만 드라이버로&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;12. Logical Plan (논리적 실행 계획)과 Physical Plan (물리적 실행 계획)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Catalyst Optimizer가 SQL/DataFrame 연산을 처리할 때 거치는 핵심 개념으로 &lt;b&gt;왜 Spark가 자동 최적화되고&lt;/b&gt;, &lt;b&gt;어떻게 실행 계획이 만들어지는지&lt;/b&gt; 이해할 수 있어 &lt;b&gt;튜닝을&lt;/b&gt; 할 때 필요하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;1) Logicl Plan&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 작성한 쿼리/코드가 &lt;b&gt;무엇을 해야 하는지&lt;/b&gt;를 표현한 &amp;ldquo;논리적&amp;rdquo; 계획으로 아직 실행 방식(executor, shuffle)은 고려하지 않는다. 즉, &lt;b&gt;사용자 쿼리를 더 빠르게 실행할 수 있도록 논리 최적화&lt;/b&gt;를 수행한 상태이다. Spark는 아래와 같이&amp;nbsp; Logical Plan을 3단계로 구분한다.&lt;/p&gt;
&lt;p data-end=&quot;695&quot; data-start=&quot;653&quot; data-ke-size=&quot;size16&quot;&gt;① &lt;b&gt;Unresolved Logical Plan (미해결 계획)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;759&quot; data-start=&quot;696&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;719&quot; data-start=&quot;696&quot;&gt;테이블/컬럼이 존재하는지 아직 모름&lt;/li&gt;
&lt;li data-end=&quot;759&quot; data-start=&quot;720&quot;&gt;단지 문자열 기반으로 &quot;filter&quot;, &quot;select&quot;만 적혀 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;798&quot; data-start=&quot;761&quot; data-ke-size=&quot;size16&quot;&gt;② &lt;b&gt;Resolved Logical Plan (해결됨)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;863&quot; data-start=&quot;799&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;821&quot; data-start=&quot;799&quot;&gt;컬럼이 실제 존재하는지 검증 완료&lt;/li&gt;
&lt;li data-end=&quot;834&quot; data-start=&quot;822&quot;&gt;스키마 분석 끝&lt;/li&gt;
&lt;li data-end=&quot;863&quot; data-start=&quot;835&quot;&gt;함수(DataFrame API)도 실체로 매핑됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;904&quot; data-start=&quot;865&quot; data-ke-size=&quot;size16&quot;&gt;③ &lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Catalyst&lt;span&gt; &lt;/span&gt;&lt;/span&gt;Optimized Logical Plan (최적화됨)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1094&quot; data-start=&quot;940&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;973&quot; data-start=&quot;940&quot;&gt;predicate pushdown (필터 아래로 내리기)&lt;/li&gt;
&lt;li data-end=&quot;1008&quot; data-start=&quot;974&quot;&gt;projection pruning (필요 없는 컬럼 제거)&lt;/li&gt;
&lt;li data-end=&quot;1039&quot; data-start=&quot;1009&quot;&gt;constant folding (상수는 미리 계산)&lt;/li&gt;
&lt;li data-end=&quot;1071&quot; data-start=&quot;1040&quot;&gt;join reordering (join 순서 최적화)&lt;/li&gt;
&lt;li data-end=&quot;1094&quot; data-start=&quot;1072&quot;&gt;filter merge (필터 병합)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) Physical Plan&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Logical Plan을 실제 &lt;b&gt;실행하는 방법&lt;/b&gt;으로 바꾼 것 으로 실제 Spark 클러스터에서 어떻게 연산할지, 어떤 방식으로 수행할지를 포함한다.&lt;/p&gt;
&lt;p data-end=&quot;1944&quot; data-start=&quot;1878&quot; data-ke-size=&quot;size16&quot;&gt;Spark는 여러 개의 physical plan을 생성한 뒤그중 &lt;b&gt;가장 비용(cost)이 낮은 것&lt;/b&gt;을 선택함.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2074&quot; data-start=&quot;1946&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1980&quot; data-start=&quot;1946&quot;&gt;HashAggregate vs SortAggregate&lt;/li&gt;
&lt;li data-end=&quot;2021&quot; data-start=&quot;1981&quot;&gt;ShuffleHashJoin vs BroadcastHashJoin&lt;/li&gt;
&lt;li data-end=&quot;2051&quot; data-start=&quot;2022&quot;&gt;Whole Stage Codegen 적용 여부&lt;/li&gt;
&lt;li data-end=&quot;2074&quot; data-start=&quot;2052&quot;&gt;Exchange(Shuffle) 전략&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1764853275596&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;df.explain()		# Physical Plan 요약
df.explain(True)	# Parsed / Analyzed / Optimized Logical Plan + Physical Plan (전체)
df.explain(&quot;formatted&quot;)	# 사람이 읽기 편한 트리 형태의 출력
df.explain(&quot;cost&quot;)	# Cost-based optimizer 비용 정보 포함 (Spark 3+)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;13. Spark Memory Allocation&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spark Job을 동작시키는데 포퍼먼스에 중요한 부분을 담당하고 있다. &lt;b&gt;DriverMemory와 ExecutorMemory로 구성되어 있다.&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) Executor Memory&lt;/h3&gt;
&lt;h4 data-end=&quot;1060&quot; data-start=&quot;1034&quot; data-ke-size=&quot;size20&quot;&gt;(1) Execution Memory(Handle Computations)&lt;/h4&gt;
&lt;p data-end=&quot;1118&quot; data-start=&quot;1061&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Shuffle, Join, Sort, Aggregate&lt;/b&gt; 같은 연산의 중간 결과를 저장하는 공간. 즉 계산용 메모리로 실시간 연산을 위한 메모리 이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1118&quot; data-start=&quot;1061&quot; data-ke-size=&quot;size16&quot;&gt;부족할 경우 성능 저하일어 날 수 있음. Storage memory보다 우선권 있으며 storage가 점유한 메모리를 빌려올 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;1118&quot; data-start=&quot;1061&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2855&quot; data-start=&quot;2829&quot;&gt;&lt;b&gt;캐싱(Storage Memory)&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;2884&quot; data-start=&quot;2856&quot;&gt;&lt;b&gt;연산(Execution Memory)&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;2903&quot; data-start=&quot;2885&quot;&gt;&lt;b&gt;Shuffle 작업&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;2926&quot; data-start=&quot;2904&quot;&gt;&lt;b&gt;RDD/DataFrame 저장&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-end=&quot;1261&quot; data-start=&quot;1237&quot; data-ke-size=&quot;size20&quot;&gt;(2) Storage Memory(Caching)&lt;/h4&gt;
&lt;p data-end=&quot;1339&quot; data-start=&quot;1262&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;RDD/DataFrame Cache 메모리 &lt;/b&gt;(사용자가 df.cache() 또는 RDD .persist() 했을 때 저장)&amp;nbsp; 즉 캐시된 데이터를 보관하는 공간&lt;/p&gt;
&lt;h4 data-end=&quot;850&quot; data-start=&quot;827&quot; data-ke-size=&quot;size20&quot;&gt;(3) User Memory&lt;/h4&gt;
&lt;p data-end=&quot;850&quot; data-start=&quot;827&quot; data-ke-size=&quot;size16&quot;&gt;UDF / 사용자 코드 객체 저장&lt;/p&gt;
&lt;h3 data-end=&quot;1610&quot; data-start=&quot;1565&quot; data-ke-size=&quot;size23&quot;&gt;2) Driver Memory&lt;/h3&gt;
&lt;p data-end=&quot;1610&quot; data-start=&quot;1565&quot; data-ke-size=&quot;size16&quot;&gt;Spark의 두뇌(플랜 생성/스케줄링/collect 데이터 수신)와 같은 역할을 수행한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1610&quot; data-start=&quot;1565&quot;&gt;SparkSession / SparkContext 유지&lt;/li&gt;
&lt;li data-end=&quot;1610&quot; data-start=&quot;1565&quot;&gt;Logical Plan / Physical Plan 생성&lt;/li&gt;
&lt;li data-end=&quot;1610&quot; data-start=&quot;1565&quot;&gt;Job / Stage / Task 스케줄링&lt;/li&gt;
&lt;li data-end=&quot;1610&quot; data-start=&quot;1565&quot;&gt;collect(), show() 시 데이터가 Driver로 모임&lt;/li&gt;
&lt;li data-end=&quot;1610&quot; data-start=&quot;1565&quot;&gt;메타데이터 관리 (catelog정보, broadcast 변수관리, shuffle파일 위치 정보)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1764854084763&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;+------------------------------------------------------------+     +-----------------------------------------------------------+
|                        Driver Memory                       |     |                      Executor Memory                      |
|                   (spark.driver.memory)                    |     |                   (spark.executor.memory)                  |
|                                                            |     |                                                           |
|  +------------------------------------------------------+  |     |  +--------------------+   +-----------------------------+ |
|  |  SparkSession / SparkContext                         |  |     |  | Execution Memory    |   | Storage Memory            | |
|  |  Logical Plan / Physical Plan 생성                   |  |     |  | (연산 중간 결과)     |   | (캐시/RDD/Persist)        | |
|  |  DAG 스케줄링(Job / Stage / Task)                   |  |     |  +--------------------+   +-----------------------------+ |
|  |  collect/show 결과 저장                              |  |     |                                                           |
|  |  메타데이터 관리                                      |  |     |   +-----------------------------+                        |
|  |                                                      |  |     |   |        User Memory          |                        |
|  +------------------------------------------------------+  |     |   |   (UDF 객체, 기타 JVM 객체) |                        |
|                                                            |     |   +-----------------------------+                        |
+------------------------------------------------------------+     +-----------------------------------------------------------+
                                                                   |    Memory Overhead (Off-Heap)                           |
                                                                   |    (DirectBuffer, PySpark Worker, Tungsten)              |
                                                                   |    spark.executor.memoryOverhead                         |
                                                                   +-----------------------------------------------------------+&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;14. AQE(Adaptive Query Execution)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Spark SQL이 실행 도중에 실제 런타임 통계를 보고 쿼리 플랜을 자동으로 최적화하는 기능으로 &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;Spark은 원래 쿼리 실행 전에 &lt;b&gt;Logical Plan &amp;rarr; Physical Plan&lt;/b&gt; 을 만든 후, 그 Physical Plan을 그대로 실행했음. 하지만 조인 전략이 잘못 선택이 된다거나 파티션이 너무 작거나 너무 클 수 있다. 이런 경우 &lt;b&gt;초기 플랜이 비효율적&lt;/b&gt;이 되어 성능이 팍 떨어지게 된다. 이 때 AQE는 실행 도중에 &lt;b&gt;&amp;rdquo;어? 이 파티션 너무 큰데?&amp;rdquo; &amp;ldquo;이 조인 작은데 브로드캐스트 가능한데?&amp;rdquo;&lt;/b&gt; 하고 감지함 &amp;rarr; 즉시 플랜 수정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1289&quot; data-start=&quot;1216&quot; data-ke-size=&quot;size16&quot;&gt;AQE는 &lt;b&gt;Physical Plan 단계에서 발생하는 실행 중 최적화&lt;/b&gt;하기 때문에 Logical Plan이 아닌 Physical Plan이다.&lt;br /&gt;즉,&amp;nbsp; &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;실행 중에 &lt;/span&gt;&lt;b&gt;Physical Plan을 재작성함(Re-Optimize)&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;size18&quot;&gt;AQE가 하는 핵심 최적화 3가지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1)동적 조인 전략 변경 (Dynamic Join Rewriting)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 중에 조인 대상 데이터 크기를 보고 &lt;b&gt;Broadcast Join이 가능하면 자동으로 변경함&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 큰 효과. 특히 sort-merge join같은 경우 엄청난 비요이듬. 이런걸 자동으로&amp;nbsp; Broadcast join으로 변경&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) 파티션 자동 병합 / 분할 (Coalescing Shuffle Partitions)&lt;/p&gt;
&lt;p data-end=&quot;958&quot; data-start=&quot;864&quot; data-ke-size=&quot;size16&quot;&gt;AQE는 &lt;b&gt;실제 셔플된 파티션 크기를 보고 자동으로 &lt;/b&gt;너무 작은 파티션들 &amp;rarr; &lt;b&gt;합치고 &lt;/b&gt;너무 큰 파티션 &amp;rarr; &lt;b&gt;쪼갬&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;1023&quot; data-start=&quot;1003&quot; data-ke-size=&quot;size16&quot;&gt;즉, 파티션 크기를 균일하게 맞춰줌.&lt;/p&gt;
&lt;p data-end=&quot;1023&quot; data-start=&quot;1003&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) &lt;b&gt;데이터 스큐 처리 (Skew Join Optimization)&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;1112&quot; data-start=&quot;1075&quot; data-ke-size=&quot;size16&quot;&gt;특정 키에 너무 많은 데이터가 몰린 &lt;b&gt;스큐( 데이터가 균등하게 분배되지 않아 특정 Task가 병목이 되는 문제. )&lt;/b&gt;상황을 AQE가 감지해서 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;스큐 키만 작은 청크로 나눠 처리, &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;나머지는 정상 처리 &lt;/span&gt;&lt;b&gt;조인 스캔이 막혀서 느려지는 것을 방지함.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1765283479591&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# spark3.x 대는 자동으로 켜져있음
spark.conf.set(&quot;spark.sql.adaptive.enabled&quot;, &quot;true&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;15. DPP (Dynamic Partition Pruning)&lt;/h2&gt;
&lt;p data-end=&quot;97&quot; data-start=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실행 중에 불필요한 파티션을 자동으로 건너뛰도록(Skip) 하는 Spark SQL 최적화 기능이다. &lt;/b&gt;즉, 필요한 파티션만 읽고, 나머지는 읽지도 않는 런타임 파티션 필터링 기능&lt;/p&gt;
&lt;p data-end=&quot;97&quot; data-start=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;97&quot; data-start=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;DPP는 파티션 키 필터가 쿼리 실행 시점에 결정될 때 유용하다. 필요한 파티션만 읽어 I/O와 네트워크 부하를 줄여 성능을 높인다.&lt;/p&gt;
&lt;h2 data-end=&quot;97&quot; data-start=&quot;0&quot; data-ke-size=&quot;size26&quot;&gt;16. Spark Cache(재사용)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spark의 &lt;b&gt;Cache&lt;/b&gt;는 자주 사용되는 DataFrame이나 RDD를 &lt;b&gt;메모리(또는 디스크)에 저장해서 &lt;/b&gt;나중에 동일한 데이터를 다시 사용할 때 &lt;b&gt;빠르게 읽어오는 기능이다. &lt;/b&gt;즉 비싼 연산(Shuffle, Scan, Join 등)을 매번 다시 계산하지 않고 결과를 저장해두는 메모리 최적화 기능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐싱은 중간 계산 결과를 메모리에 저장하여 재사용하고 불필요한 재계산이나 I/O를 줄여 성능을 높여요. 데이터 분배는 Repartition, 느린 태스크 복제는 Speculative Execution, 변수 공유는 Accumulator에 해당한다.&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;Cache는 메모리를 사용하므로 사용이 끝나면 꼭 해제해야 함.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1577&quot; data-start=&quot;1555&quot; data-ke-size=&quot;size16&quot;&gt;Cache 꼭 써야 하는 경우&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1712&quot; data-start=&quot;1578&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1607&quot; data-start=&quot;1578&quot;&gt;동일한 DataFrame을 여러 번 사용할 때&lt;/li&gt;
&lt;li data-end=&quot;1641&quot; data-start=&quot;1608&quot;&gt;복잡한 ETL 파이프라인에서 중간 결과를 재활용할 때&lt;/li&gt;
&lt;li data-end=&quot;1676&quot; data-start=&quot;1642&quot;&gt;반복 계산(ML 알고리즘, iterative jobs)&lt;/li&gt;
&lt;li data-end=&quot;1712&quot; data-start=&quot;1677&quot;&gt;Shuffle 이후 결과를 여러 Action에서 재사용할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1732&quot; data-start=&quot;1714&quot; data-ke-size=&quot;size16&quot;&gt;사용하면 안 되는 경우&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1850&quot; data-start=&quot;1733&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1766&quot; data-start=&quot;1733&quot;&gt;DataFrame이 너무 커서 메모리에 못 들어갈 때&lt;/li&gt;
&lt;li data-end=&quot;1784&quot; data-start=&quot;1767&quot;&gt;한 번만 사용하는 데이터&lt;/li&gt;
&lt;li data-end=&quot;1806&quot; data-start=&quot;1785&quot;&gt;IO가 비싸지 않은 작은 데이터&lt;/li&gt;
&lt;li data-end=&quot;1850&quot; data-start=&quot;1807&quot;&gt;필터로 작은 Subset만 쓸 때 (필요한 부분만 캐시하는 게 더 효율적)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1765284745698&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;df.cache()
df.count()   # Action &amp;rarr; 캐시가 실제로 저장되는 시점

df.unpersist() # 해제&lt;/code&gt;&lt;/pre&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 110px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Level&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;MEMORY_ONLY&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;기본 Cache. 메모리에만 저장, 부족하면 재계산&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;MEMORY_AND_DISK&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;메모리 부족하면 디스크에도 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;MEMORY_ONLY_SER&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;메모리를 더 절약하기 위해 Serialized 형태&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;DISK_ONLY&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;디스크에만 저장 (I/O 느림)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;17. sql hint(강제 주입)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQL 쿼리 안에 힌트를 넣어서 &lt;b&gt;조인 전략, 파티션 방식, Broadcast 여부, Shuffle 동작&lt;/b&gt; 등을 강제로 제어할 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리소스등을 더 잘 활용가능함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQL Hint는 사용자가 쿼리 옵티마이저에게 물리적 실행 계획(예: 조인 전략, 파티션 수)에 대한 힌트를 제공하여 성능을 개선하는 방법으로 결과 데이터 변경과는 무관하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/*+ BROADCAST(dim) */ 이런식으로 사용함.&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;자주사용하는 hint&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2336&quot; data-start=&quot;2294&quot;&gt;BROADCAST(dim) &amp;mdash; 작은 테이블 broadcast&lt;/li&gt;
&lt;li data-end=&quot;2379&quot; data-start=&quot;2337&quot;&gt;REPARTITION(200) &amp;mdash; write 전 파티션 조정&lt;/li&gt;
&lt;li data-end=&quot;2425&quot; data-start=&quot;2380&quot;&gt;REPARTITION(50, user_id) &amp;mdash; 조인/집계 최적화&lt;/li&gt;
&lt;li data-end=&quot;2469&quot; data-start=&quot;2426&quot;&gt;MERGE 또는 SHUFFLE_HASH &amp;mdash; 조인 전략 강제&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;table style=&quot;border-collapse: collapse; width: 100%; height: 98px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;hint&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;BROADCAST / MAPJOIN&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;작은 테이블을 Broadcast Join으로 강제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;MERGE / SHUFFLE_HASH / SHUFFLE_REPLICATE_NL&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;조인 전략 직접 지정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;COALESCE / REPARTITION / REPARTITION_BY_RANGE&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;실행 시 파티션 수 조절&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;height: 10px;&quot;&gt;JOIN HINTS&lt;/td&gt;
&lt;td style=&quot;height: 10px;&quot;&gt;build side 선택 등 고급 제어&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;&lt;b&gt;예시&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1765286095621&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT /*+ BROADCAST(dim) */
       f.user_id, dim.level
FROM fact f
JOIN dim ON f.id = dim.id;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1765286213764&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;df.hint(&quot;broadcast&quot;)
df.hint(&quot;repartition&quot;, 200)
df.hint(&quot;repartition_by_range&quot;, 50, &quot;age&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;18. Accumulator(디버깅)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spark에서 작업(Task)들이 실행되면서 발생하는 값을 &lt;b&gt;안전하게 누적하기 위해 만든 변수로 디버깅 할때 활용 하면 좋다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;556&quot; data-start=&quot;526&quot; data-ke-size=&quot;size16&quot;&gt;즉 분산 Task들이 생성하는 로그성 통계 모으기에 유용함 ( 예) &lt;b&gt;에러 로우가 몇 개였는지&lt;/b&gt; count, null 데이터 개수, 필터링된 행 개수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;, 어떤 조건에 맞지 않는 값 누적 )&lt;/p&gt;
&lt;pre id=&quot;code_1765286491227&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from pyspark.sql import SparkSession

spark = SparkSession.builder.getOrCreate()
sc = spark.sparkContext

# accumulator 생성
acc = sc.accumulator(0)

rdd = sc.parallelize([1, 2, 3, 4, 5])

def process(x):
    if x % 2 == 0:
        acc.add(1)
    return x

rdd.map(process).collect()

print(&quot;짝수 개수:&quot;, acc.value)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-end=&quot;676&quot; data-start=&quot;630&quot; data-ke-size=&quot;size26&quot;&gt;19. Speculative execution&lt;/h2&gt;
&lt;p data-end=&quot;676&quot; data-start=&quot;630&quot; data-ke-size=&quot;size16&quot;&gt;Speculative Execution(추측 실행)은 Spark에서 &lt;b&gt;느리게 실행되는(Task straggler) 작업을 빠르게 하기 위해 뒤에서 같은 Task를 하나 더 실행시키는 최적화 기능이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;676&quot; data-start=&quot;630&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;433&quot; data-start=&quot;382&quot; data-ke-size=&quot;size16&quot;&gt;Spark는 클러스터 전체에서 많은 Executor&amp;middot;노드에 작업을 분배함.&lt;br /&gt;그중 하나가&amp;hellip;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;520&quot; data-start=&quot;435&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;447&quot; data-start=&quot;435&quot;&gt;머신 성능 저하&lt;/li&gt;
&lt;li data-end=&quot;457&quot; data-start=&quot;448&quot;&gt;GC 과다&lt;/li&gt;
&lt;li data-end=&quot;476&quot; data-start=&quot;458&quot;&gt;네트워크 문제가 있는 노드&lt;/li&gt;
&lt;li data-end=&quot;491&quot; data-start=&quot;477&quot;&gt;디스크 I/O 병목&lt;/li&gt;
&lt;li data-end=&quot;508&quot; data-start=&quot;492&quot;&gt;데이터 스큐(skew)&lt;/li&gt;
&lt;li data-end=&quot;520&quot; data-start=&quot;509&quot;&gt;하드웨어 이상&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;559&quot; data-start=&quot;522&quot; data-ke-size=&quot;size16&quot;&gt;&amp;hellip;등의 이유로 &lt;b&gt;특정 Task가 유독 느리게&lt;/b&gt; 실행될 수 있음.&lt;/p&gt;
&lt;p data-end=&quot;617&quot; data-start=&quot;561&quot; data-ke-size=&quot;size16&quot;&gt;그 &lt;b&gt;한 개의 느린 Task&lt;/b&gt; 때문에 전체 Stage가 대기 &amp;rarr; 결국 전체 Job 전체가 느려짐.&lt;/p&gt;
&lt;p data-end=&quot;691&quot; data-start=&quot;619&quot; data-ke-size=&quot;size16&quot;&gt;이걸 해결하려고 같은 Task를 하나 더 실행해서 &lt;b&gt;누가 먼저 끝나나 경쟁시키는 게 Speculative Execution&lt;/b&gt;.&lt;/p&gt;
&lt;pre id=&quot;code_1765286794097&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spark.speculation=true # 설정 옆에꺼 써도됨 spark.conf.set(&quot;spark.speculation&quot;,true)

#성능 옵션
spark.speculation.quantile=0.75   # 하위 25%가 느리면 speculative 실행
spark.speculation.multiplier=1.5  # 평균보다 1.5배 느리면 대상
spark.speculation.interval=100ms  # 검사 간격&lt;/code&gt;&lt;/pre&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot; data-start=&quot;630&quot; data-end=&quot;676&quot;&gt;20. Job Scheduling&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드라이버(Driver)가 &lt;b&gt;여러 Job을 어떤 순서로, 어떤 리소스로, 어떻게 실행할지 관리하는 메커니즘&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spark Job Scheduling은 여러 Job을 FIFO 또는 FAIR 방식으로 Executor 리소스를 나눠 실행하는 방식이며, TaskScheduler가 Executor에 Task를 배정해 전체 실행 흐름을 관리하는 시스템이다.&lt;/p&gt;
&lt;p data-end=&quot;225&quot; data-start=&quot;201&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;225&quot; data-start=&quot;201&quot; data-ke-size=&quot;size16&quot;&gt;Job Scheduling 핵심 구조&lt;/p&gt;
&lt;p data-end=&quot;263&quot; data-start=&quot;227&quot; data-ke-size=&quot;size16&quot;&gt;Spark 드라이버 내부에서 스케줄링은 다음 구성 요소가 참여해:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;457&quot; data-start=&quot;265&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;339&quot; data-start=&quot;265&quot;&gt;&lt;b&gt;Job - &lt;/b&gt;하나의 Action(count(), collect(), save 등)이 실행될 때 생성됨.&lt;/li&gt;
&lt;li data-end=&quot;396&quot; data-start=&quot;341&quot;&gt;&lt;b&gt;Stage - &lt;/b&gt;Job은 Shuffle 경계를 기준으로 여러 Stage로 나뉨.&lt;/li&gt;
&lt;li data-end=&quot;457&quot; data-start=&quot;398&quot;&gt;&lt;b&gt;Task - &lt;/b&gt;Stage는 여러 Task로 분리되어 Executor에서 병렬로 실행됨.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-end=&quot;572&quot; data-start=&quot;531&quot; data-ke-size=&quot;size23&quot;&gt;1) FIFO(First-In First-Out) &amp;mdash; 기본 스케줄러&lt;/h3&gt;
&lt;p data-end=&quot;587&quot; data-start=&quot;574&quot; data-ke-size=&quot;size16&quot;&gt;Spark 기본 모드&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;670&quot; data-start=&quot;588&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;608&quot; data-start=&quot;588&quot;&gt;먼저 제출된 Job이 먼저 실행됨&lt;/li&gt;
&lt;li data-end=&quot;670&quot; data-start=&quot;609&quot;&gt;후순위 Job은 앞 Job이 끝날 때까지 기다림&lt;br /&gt;(단, Executor가 남아 있으면 병렬 수행 가능)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;723&quot; data-start=&quot;672&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장점&lt;/b&gt;: 단순, 예측 가능&lt;br /&gt;&lt;b&gt;단점&lt;/b&gt;: 긴 작업이 있으면 뒤 Job이 오래 기다림&lt;/p&gt;
&lt;h3 data-end=&quot;776&quot; data-start=&quot;730&quot; data-ke-size=&quot;size23&quot;&gt;2) FAIR Scheduler &amp;mdash; 공정 스케줄링 (협업 환경에서 많이 씀)&lt;/h3&gt;
&lt;p data-end=&quot;804&quot; data-start=&quot;778&quot; data-ke-size=&quot;size16&quot;&gt;여러 사용자가 동시에 Job 실행할 때 사용됨.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;912&quot; data-start=&quot;806&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;838&quot; data-start=&quot;806&quot;&gt;Job들끼리 &lt;b&gt;Pool&lt;/b&gt;(리소스 그룹)로 나누고&lt;/li&gt;
&lt;li data-end=&quot;883&quot; data-start=&quot;839&quot;&gt;각 Pool에 &lt;b&gt;weight&lt;/b&gt; 또는 &lt;b&gt;minShare&lt;/b&gt; 값을 설정&lt;/li&gt;
&lt;li data-end=&quot;912&quot; data-start=&quot;884&quot;&gt;여러 Job을 &lt;b&gt;동시에 공정하게 병렬 실행&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;996&quot; data-start=&quot;914&quot; data-ke-size=&quot;size16&quot;&gt;개발자 A job 3개, 개발자 B job 1개 실행&lt;br /&gt;&amp;rarr; Pool weight에 따라 CPU/Executor를 나눠서 공정하게 실행됨&lt;/p&gt;
&lt;pre id=&quot;code_1765287459955&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#spark-defaults.conf
spark.scheduler.mode=FAIR&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;21. Spark Streming&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Spark Streaming&lt;/b&gt;은 Apache Spark에서 &lt;b&gt;실시간(스트리밍) 데이터 처리&lt;/b&gt;를 위한 기능으로 요즘은 주로 &lt;b&gt;Spark Structured Streaming&lt;/b&gt;을 의미한다고 보면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;325&quot; data-start=&quot;297&quot;&gt;&lt;b&gt;DataFrame / Dataset 기반&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;383&quot; data-start=&quot;326&quot;&gt;SQL, Catalyst Optimizer, AQE 등 &lt;b&gt;Spark SQL 생태계 그대로 사용&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;417&quot; data-start=&quot;384&quot;&gt;Structured&amp;nbsp;Streaming은&amp;nbsp;무한히&amp;nbsp;들어오는&amp;nbsp;데이터를&amp;nbsp;마치&amp;nbsp;배치&amp;nbsp;DataFrame처럼&amp;nbsp;처리하는&amp;nbsp;엔진&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-end=&quot;2653&quot; data-start=&quot;2640&quot; data-ke-size=&quot;size20&quot;&gt;장애 발생 시&lt;/h4&gt;
&lt;p data-end=&quot;2669&quot; data-start=&quot;2654&quot; data-ke-size=&quot;size16&quot;&gt;Executor 장애 - Task 재실행,&amp;nbsp;state 복구&lt;/p&gt;
&lt;p data-end=&quot;2706&quot; data-start=&quot;2693&quot; data-ke-size=&quot;size16&quot;&gt;Driver 장애 - &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;checkpoint 기반 &lt;/span&gt;&lt;b&gt;query 재시작, &lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;offset / state 복원&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;438&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwGynm/dJMcadAvOkU/8iUsSfH1wYWOkDEle98G3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwGynm/dJMcadAvOkU/8iUsSfH1wYWOkDEle98G3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwGynm/dJMcadAvOkU/8iUsSfH1wYWOkDEle98G3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwGynm%2FdJMcadAvOkU%2F8iUsSfH1wYWOkDEle98G3k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;589&quot; height=&quot;220&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;438&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1765799788921&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from pyspark.sql.functions import window

df = spark.readStream \
    .format(&quot;json&quot;) \
    .schema(&quot;eventTime TIMESTAMP, userId STRING&quot;) \
    .load(&quot;/logs/events&quot;)

result = df \
    .withWatermark(&quot;eventTime&quot;, &quot;10 minutes&quot;) \
    .groupBy(
        window(&quot;eventTime&quot;, &quot;10 minutes&quot;),
        &quot;userId&quot;
    ) \
    .count()

query = result.writeStream \
    .outputMode(&quot;append&quot;) \
    .format(&quot;console&quot;) \
    .start()

query.awaitTermination()&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) ouputmode&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;append mode는 결과가 더 이상 바뀌지 않는 시점이 명확할 때 &lt;b&gt;새로들어온 데이터만&lt;/b&gt;.(데이터 중복 x)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;update mode는 실시간 집계 중간 결과가 필요할 때 사용한다.(현재까지 집계 상황을 보고 싶을 때)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;complete는 테스트나 디버깅 용도로 전체 상태를 한눈에 보고 싶을 때 사용한다.(직관적이지만 리소스 비용이 비쌈)&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 88px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;mode&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;append&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;확정된 결과만&lt;/b&gt; 새로 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;update&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;변경된 결과만&lt;/b&gt; 갱신&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;complete&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;전체 결과를 매번 다시 출력&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) Window &amp;amp; Watermark&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Trigger &amp;rarr; 언제 실행할지 (시간 / 조건)&lt;/li&gt;
&lt;li&gt;Window &amp;rarr; 어떻게 묶을지 (시간 구간)&lt;/li&gt;
&lt;li&gt;Watermark&amp;nbsp;&amp;rarr;&amp;nbsp;언제&amp;nbsp;버릴지&amp;nbsp;(지연&amp;nbsp;허용&amp;nbsp;기준)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Window는&amp;nbsp;시간&amp;nbsp;구간으로&amp;nbsp;데이터를&amp;nbsp;묶어서&amp;nbsp;집계하는&amp;nbsp;방법&lt;/b&gt;&amp;nbsp;ex)&amp;nbsp;10분&amp;nbsp;단위로&amp;nbsp;잘라서&amp;nbsp;집계&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;window를 사용할 때 지연이 되면 지연된 데이터 상태를 계속 들고 있어야 해서 oom이 뜰 확률이 높음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;window의 종류는 tumbling와 sliding 두개가 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;tumbing은 시간이 겹치지않고 sliding은 overlab시간을 부여한다. 즉 Tumbling Window는 시간을 중복되지 않는, 고정된 크기의 윈도우로 나누고 Sliding Window는 중복이 발생하는 윈도우 방식이다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1765798414926&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from pyspark.sql.functions import window

window_duration = &quot;10 minutes&quot; # tumbing 의미
sliding_duration = &quot;5 minutes&quot; # sliding 의미

df.groupBy(
    window(df.eventTime, window_duration, sliding_duration)
).count()&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;Watermark는 Structured Streaming 전용으로 이 시간보다 늦게 오는 데이터는 안 받겠다 는 선언&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1765798423684&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;df.withWatermark(&quot;eventTime&quot;, &quot;10 minutes&quot;)&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;window와 wtermark를 함께 쓰면 메모리 관리에 용의 함. &lt;b&gt;&amp;ldquo;Watermark&amp;nbsp;없는&amp;nbsp;stateful&amp;nbsp;streaming은&amp;nbsp;위험하다&amp;rdquo;&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 data-end=&quot;1556&quot; data-start=&quot;1541&quot;&gt;Window 상태를 유지&lt;/li&gt;
&lt;li data-end=&quot;1603&quot; data-start=&quot;1557&quot;&gt;watermark 시점 지나면
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1603&quot; data-start=&quot;1578&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1585&quot; data-start=&quot;1578&quot;&gt;결과 확정&lt;/li&gt;
&lt;li data-end=&quot;1603&quot; data-start=&quot;1588&quot;&gt;state 정리 (GC)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1765798851561&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;df \
  .withWatermark(&quot;eventTime&quot;, &quot;10 minutes&quot;) \
  .groupBy(
      window(&quot;eventTime&quot;, &quot;10 minutes&quot;)
  ).count()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) Streaming Fault Tolerance란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스트리밍 처리 중 장애가 발생해도, 데이터 유실&amp;middot;중복 없이 계속 처리하도록 보장하는 메커니즘Spark Structured Streaming은 장애를 전제로 설계돼 있다. 각 micro-batch를 &lt;b&gt;완전히 처리했을 때만 커밋&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;체크포인트는 &lt;span style=&quot;background-color: #f8f9fa; color: #495057; text-align: start;&quot;&gt;내결함성의 핵심 목표는 시스템 장애 발생 시 데이터 레코드를 누락하거나 중복 없이 정확히 한 번만 처리하는 End-to-End Exactly Once Semantic을 보장하는 것이다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4) Stateless와 stateful&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에서 stateless와 stateful은 session이 유지되냐 안되냐의 뜻 으로 통한다. ex) stateless = rest api, stateful = socket&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;spark에서의 statelsess와 stateful은 연산이 이전 배치(또는 이전 레코드)의 결과를 &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;statelsess&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 data-end=&quot;291&quot; data-start=&quot;281&quot;&gt;상태 저장 없음&lt;/li&gt;
&lt;li data-end=&quot;305&quot; data-start=&quot;292&quot;&gt;재시작 시 부담 적음&lt;/li&gt;
&lt;li data-end=&quot;318&quot; data-start=&quot;306&quot;&gt;성능, 확장성 좋음&lt;/li&gt;
&lt;li data-end=&quot;335&quot; data-start=&quot;319&quot;&gt;Checkpoint 불필요&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;select, filter, map, flatMap, expolde, append, update ...&lt;/p&gt;
&lt;pre id=&quot;code_1765888305530&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;df.select(&quot;col&quot;)
df.filter($&quot;age&quot; &amp;gt; 30)
df.map(...)
df.withColumn(&quot;x&quot;, $&quot;a&quot; + 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;반면 &lt;b&gt;statefull&lt;/b&gt;은&amp;nbsp;&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 data-end=&quot;714&quot; data-start=&quot;697&quot;&gt;상태를 메모리/디스크에 유지&lt;/li&gt;
&lt;li data-end=&quot;734&quot; data-start=&quot;715&quot;&gt;&lt;b&gt;Checkpoint 필수&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;757&quot; data-start=&quot;735&quot;&gt;상태 크기 관리 중요 (OOM 위험)&lt;/li&gt;
&lt;li data-end=&quot;770&quot; data-start=&quot;758&quot;&gt;데이터 스큐에 민감&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;groupBy, count, sum, avg, window, mapGroupsWithState, complete ...&lt;/p&gt;
&lt;pre id=&quot;code_1765888424679&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;streamDF
  .withWatermark(&quot;eventTime&quot;, &quot;10 minutes&quot;)
  .groupBy(
    window($&quot;eventTime&quot;, &quot;5 minutes&quot;),
    $&quot;key&quot;
  )
  .count()&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5) streaming join&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;streaming join에는 2가지가 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) streaming dataframe -&amp;gt; static dataframe ( Stream&amp;ndash;Batch )&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;스트리밍&amp;nbsp;Outer&amp;nbsp;Join은&amp;nbsp;매칭되지&amp;nbsp;않은&amp;nbsp;데이터도&amp;nbsp;결과를&amp;nbsp;내보내야&amp;nbsp;하므로,&amp;nbsp;무한정&amp;nbsp;기다리지&amp;nbsp;않도록&amp;nbsp;Watermark와&amp;nbsp;이벤트&amp;nbsp;시간&amp;nbsp;제약이&amp;nbsp;반드시&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;설비 이벤트 + 설비 마스터, 센서 로그 + 기준 정보 등에 사용 가능&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;288&quot; data-start=&quot;253&quot;&gt;&lt;b&gt;스트리밍 데이터에 대해 &amp;ldquo;고정된 기준 테이블&amp;rdquo;을 조회&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;327&quot; data-start=&quot;289&quot;&gt;Static DF는 &lt;b&gt;스냅샷처럼 메모리에 올라간 참조 데이터&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;415&quot; data-start=&quot;397&quot;&gt;&lt;b&gt;거의 Stateless&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;474&quot; data-start=&quot;443&quot;&gt;Streaming 쪽의 이전 상태를 기억할 필요 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1765890731218&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val userInfo = spark.read.parquet(&quot;/users&quot;)  // Static

val events = spark.readStream
  .format(&quot;kafka&quot;)
  .load()

events
  .join(userInfo, &quot;userId&quot;)
  .select(&quot;userId&quot;, &quot;eventType&quot;, &quot;userName&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;2) streaming dataframe -&amp;gt; streaming dataframe ( &lt;/span&gt;Stream-Stream )&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 상관 분석에 사용이 자주됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 스트림에서 온 데이터를 조인하려면 한쪽 또는 양쪽 데이터를 상태 저장소에 임시로 저장하고 기다려야 합니다. 이는 상태를 관리하는 Stateful 변환이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1098&quot; data-start=&quot;1072&quot;&gt;&lt;b&gt;두 스트림이 시간 축을 기준으로 매칭&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1128&quot; data-start=&quot;1099&quot;&gt;양쪽 모두 계속 들어옴 &amp;rarr; &lt;b&gt;상태 유지 필수&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1223&quot; data-start=&quot;1206&quot;&gt;&lt;b&gt;강한 Stateful&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1247&quot; data-start=&quot;1224&quot;&gt;&lt;b&gt;양쪽 스트림의 과거 데이터 보관&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1275&quot; data-start=&quot;1248&quot;&gt;Watermark 없으면 상태 무한 증가&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1765890805731&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val impressions = spark.readStream
  .withWatermark(&quot;eventTime&quot;, &quot;10 minutes&quot;)

val clicks = spark.readStream
  .withWatermark(&quot;eventTime&quot;, &quot;10 minutes&quot;)

impressions.join(
  clicks,
  expr(&quot;&quot;&quot;
    impressions.adId = clicks.adId AND
    impressions.eventTime BETWEEN
      clicks.eventTime - interval 5 minutes AND
      clicks.eventTime + interval 5 minutes
  &quot;&quot;&quot;)
)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;22. Spark&amp;nbsp;MLlib&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spark &lt;b&gt;MLlib&lt;/b&gt;는 Apache Spark에서 제공하는 &lt;b&gt;분산 머신러닝 라이브러리로 &lt;/b&gt;RDD / DataFrame 기반으로 대규모 데이터를 분산 처리하며 머신러닝 알고리즘을 제공하는 라이브러리이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) 알고리즘&lt;/h3&gt;
&lt;p data-end=&quot;1538&quot; data-start=&quot;1512&quot; data-ke-size=&quot;size16&quot;&gt;분류 (Classification)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1631&quot; data-start=&quot;1539&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1560&quot; data-start=&quot;1539&quot;&gt;Logistic Regression, Decision Tree, Random Forest, Gradient-Boosted Trees, Naive Bayes&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1655&quot; data-start=&quot;1633&quot; data-ke-size=&quot;size16&quot;&gt;회귀 (Regression)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1759&quot; data-start=&quot;1656&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1675&quot; data-start=&quot;1656&quot;&gt;Linear Regression, Generalized Linear Regression, Decision Tree Regressor, Random Forest Regressor&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1783&quot; data-start=&quot;1761&quot; data-ke-size=&quot;size16&quot;&gt;군집 (Clustering)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1812&quot; data-start=&quot;1784&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1793&quot; data-start=&quot;1784&quot;&gt;K-Means, Gaussian Mixture&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1840&quot; data-start=&quot;1814&quot; data-ke-size=&quot;size16&quot;&gt;추천 (Recommendation)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1872&quot; data-start=&quot;1841&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1872&quot; data-start=&quot;1841&quot;&gt;ALS (Collaborative Filtering)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1886&quot; data-start=&quot;1874&quot; data-ke-size=&quot;size16&quot;&gt;차원 축소&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1898&quot; data-start=&quot;1887&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1892&quot; data-start=&quot;1887&quot;&gt;PCA, SVD&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1765973712318&quot; class=&quot;ini&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;lr = LogisticRegression()
model = lr.fit(train_df) # 모델생성

result = model.transform(test_df) # 데이터 변환&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) Feature Engineering&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;기능&lt;/td&gt;
&lt;td&gt;종류&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;문자열 인코딩&lt;/td&gt;
&lt;td&gt;StringIndexer, OneHotEncoder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;텍스트&lt;/td&gt;
&lt;td&gt;Tokenizer, NGram, TF-IDF&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;스케일링&lt;/td&gt;
&lt;td&gt;StandardScaler, MinMaxScaler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;벡터화&lt;/td&gt;
&lt;td&gt;VectorAssembler&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) Pipeline&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전처리 + 학습 + 예측을 하나의 DAG로 관리&lt;/p&gt;
&lt;pre id=&quot;code_1765973507659&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from pyspark.ml import Pipeline

pipeline = Pipeline(stages=[
    tokenizer,
    hashingTF,
    lr
])

model = pipeline.fit(train_df)
pred = model.transform(test_df)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI</category>
      <author>usingsystem</author>
      <guid isPermaLink="true">https://usingsystem.tistory.com/575</guid>
      <comments>https://usingsystem.tistory.com/575#entry575comment</comments>
      <pubDate>Mon, 15 Dec 2025 21:31:46 +0900</pubDate>
    </item>
    <item>
      <title>[AI] 전통적인 ML 개념정리</title>
      <link>https://usingsystem.tistory.com/577</link>
      <description>&lt;h2 data-end=&quot;182&quot; data-start=&quot;163&quot; 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;머신러닝 역사 &amp;amp; 기본 개념&lt;/li&gt;
&lt;li&gt;머신러닝의 종류&lt;/li&gt;
&lt;li&gt;머신러닝에 주로 사용되는 파이썬 라이브러리&lt;/li&gt;
&lt;li&gt;Scaling(스케일링)과&amp;nbsp;정규화&lt;/li&gt;
&lt;li&gt;스케일링&amp;nbsp;종류&lt;/li&gt;
&lt;li&gt;Feature Engineering&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;머신러닝&amp;nbsp;Model&amp;nbsp;개발&amp;nbsp;플로우&lt;/li&gt;
&lt;li&gt;전통적인&amp;nbsp;지도학습&amp;nbsp;알고리즘&lt;/li&gt;
&lt;li&gt;평가&amp;nbsp;지표&lt;/li&gt;
&lt;li&gt;용어&amp;nbsp;정리&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-end=&quot;182&quot; data-start=&quot;163&quot; data-ke-size=&quot;size26&quot;&gt;1. 머신러닝 역사 &amp;amp; 기본 개념&lt;/h2&gt;
&lt;p data-end=&quot;222&quot; data-start=&quot;184&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;전통적 프로그래밍 (Traditional Programming)&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;271&quot; data-start=&quot;223&quot; data-ke-size=&quot;size16&quot;&gt;전통적인 프로그래밍 방식은 사람이 &lt;b&gt;규칙(rule)&lt;/b&gt; 을 직접 만들어주는 rule base 방식이다. 이 방식은 데이터가 많아질수록 규칙을 모두 사람이 정의하기 어려워지는 한계가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;370&quot; data-start=&quot;273&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;297&quot; data-start=&quot;273&quot;&gt;예: if-else 규칙 기반 시스템&lt;/li&gt;
&lt;li data-end=&quot;370&quot; data-start=&quot;298&quot;&gt;특징: &lt;b&gt;규칙은 알지만, 답은 모르는 방식 &lt;/b&gt;&amp;rarr; 사람이 &quot;조건&quot;을 정의해야 하고, 컴퓨터는 그 규칙을 실행만 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;451&quot; data-start=&quot;425&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;머신러닝 (Machine Learning)&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;479&quot; data-start=&quot;452&quot; data-ke-size=&quot;size16&quot;&gt;머신러닝은 전통적인 프로그래밍과 정반대 개념이다. 답을 알고 있어 여러가지 규칙을 만들어 낼 수 있다. 즉, 데이터 속에서 패턴을 스스로 학습해 규칙을 만들어내는 방식이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;542&quot; data-start=&quot;481&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;507&quot; data-start=&quot;481&quot;&gt;&lt;b&gt;답(정답 데이터)은 알고 있지만&lt;/b&gt;,&lt;/li&gt;
&lt;li data-end=&quot;542&quot; data-start=&quot;508&quot;&gt;그 답을 만드는 &lt;b&gt;규칙(패턴)&lt;/b&gt; 은 컴퓨터가 도출한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;601&quot; data-start=&quot;589&quot; data-ke-size=&quot;size26&quot;&gt;2. 머신러닝의 종류&lt;/h2&gt;
&lt;h3 data-end=&quot;635&quot; data-start=&quot;603&quot; data-ke-size=&quot;size23&quot;&gt;1) 지도학습 (Supervised Learning)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-start=&quot;673&quot; data-end=&quot;767&quot;&gt;
&lt;li data-end=&quot;660&quot; data-start=&quot;636&quot;&gt;정답(label)이 있는 데이터를 학습으로 &lt;b&gt;패턴 인식 알고리즘&lt;/b&gt;이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;767&quot; data-start=&quot;673&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;721&quot; data-start=&quot;673&quot;&gt;&lt;b&gt;분류(Classification)&lt;/b&gt;: 개/고양이, 스팸/정상 &amp;rarr; 범주 예측&lt;/li&gt;
&lt;li data-end=&quot;767&quot; data-start=&quot;724&quot;&gt;&lt;b&gt;회귀(Regression)&lt;/b&gt;: 주가 예측, 집값 예측 &amp;rarr; 연속값 예측&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딥러닝(Deep Learning)도 지도학습 기반으로 많이 활용되며 정답이 있는 &lt;b&gt;데이터로 패턴을 익혀, 새 데이터의 정답을 예측&lt;/b&gt;하는 모델을 만든다.&lt;/p&gt;
&lt;h3 data-end=&quot;904&quot; data-start=&quot;869&quot; data-ke-size=&quot;size23&quot;&gt;2) 비지도학습 (Unsupervised Learning)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1016&quot; data-start=&quot;905&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;928&quot; data-start=&quot;905&quot;&gt;정답(label)이 없는 데이터를 학습으로 &lt;b&gt;패턴 인식 알고리즘&lt;/b&gt;이다.&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1016&quot; data-start=&quot;939&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;981&quot; data-start=&quot;939&quot;&gt;&lt;b&gt;군집화(Clustering)&lt;/b&gt;: 비슷한 데이터끼리 자동으로 묶기&lt;/li&gt;
&lt;li data-end=&quot;1016&quot; data-start=&quot;984&quot;&gt;&lt;b&gt;차원 축소(Dimension Reduction)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1054&quot; data-start=&quot;1018&quot; data-ke-size=&quot;size16&quot;&gt;비지도학습은 &amp;ldquo;이 묶음이 어떤 의미인지&amp;rdquo;는 &lt;b&gt;사람이 해석&lt;/b&gt;해야 한다.&lt;/p&gt;
&lt;h3 data-end=&quot;1096&quot; data-start=&quot;1061&quot; data-ke-size=&quot;size23&quot;&gt;3) 강화학습 (Reinforcement Learning)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1196&quot; data-start=&quot;1097&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1123&quot; data-start=&quot;1097&quot;&gt;스스로 시행착오를 반복해 최적의 행동을 학습 &lt;b&gt;신경망(neural)알고리즘&lt;/b&gt;에 속한다&lt;/li&gt;
&lt;li data-end=&quot;1173&quot; data-start=&quot;1124&quot;&gt;분류도 회귀도 아니고 &lt;b&gt;의사결정(Decision Making)&lt;/b&gt; 을 학습하는 방식&lt;/li&gt;
&lt;li data-end=&quot;1255&quot; data-start=&quot;1202&quot;&gt;데이터가 미리 정해져 있는 것이 아니라, &lt;b&gt;행동 &amp;rarr; 보상 &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;style2&quot; /&gt;
&lt;h2 data-end=&quot;1339&quot; data-start=&quot;1312&quot; 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;scikit-learn (sklearn) - 전통적인 머신러닝 도구로 전처리, 지도/비지도 학습 알고리즘, 모델 평가&lt;/li&gt;
&lt;li&gt;TesorFlow - Google 개발,&amp;nbsp;딥러닝(Deep Learning) 프레임워크&lt;/li&gt;
&lt;li&gt;Keras - TensorFlow 상위 API,&amp;nbsp;코드가 간단하고 직관적이어서 입문자 친화적&lt;/li&gt;
&lt;li&gt;PyTorch - Meta(Facebook) 개발,&amp;nbsp; 연구자 및 딥러닝 커뮤니티에서 가장 많이 사용되는 프레임워크&lt;/li&gt;
&lt;li&gt;Matplotlib - 파이썬 기반 데이터 시각화 라이브러리, 그림구조 : &lt;b&gt;Figure &amp;gt; Axes &amp;gt; Axis&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;style2&quot; /&gt;
&lt;h2 data-end=&quot;1740&quot; data-start=&quot;1718&quot; data-ke-size=&quot;size26&quot;&gt;4. Scaling(스케일링)과 정규화&lt;/h2&gt;
&lt;p data-end=&quot;1780&quot; data-start=&quot;1742&quot; data-ke-size=&quot;size16&quot;&gt;스케일링은 &lt;b&gt;각 Feature의 단위와 범위를 맞추는 과정&lt;/b&gt;이다. 스케일이 서로 다른 상태에서는 머신러닝 모델이 제대로 학습할 수 없기 때문이다.&lt;/p&gt;
&lt;h3 data-end=&quot;1816&quot; data-start=&quot;1791&quot; data-ke-size=&quot;size23&quot;&gt;1) 큰 값이 모델을 지배함( 특징값의 크기가 다르면 큰 값이 모델을 지배함)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1861&quot; data-start=&quot;1820&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1830&quot; data-start=&quot;1820&quot;&gt;예) 키: 170 | 몸무게: 70 | 연봉: 50,000,000&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1896&quot; data-start=&quot;1863&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;연봉&amp;rdquo; 값이 너무 커서 모델이 연봉만 중요하다고 착각한다.&lt;/p&gt;
&lt;h3 data-end=&quot;1937&quot; data-start=&quot;1898&quot; data-ke-size=&quot;size23&quot;&gt;2) 거리 기반 모델(KNN, K-means)에 영향&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1764063426165&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;distance = sqrt((x1 - y1)^2 + (x2 - y2)^2)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Feature 값의 단위가 다르면 특정 Feature가 거리를 거의 전부 결정해버립니다. &amp;rarr; 스케일이 큰 축으로만 거리 계산됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;단위가 큰 Feature가 전체 거리를 좌우함 &amp;rarr; 잘못된 분류 가능.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-end=&quot;2085&quot; data-start=&quot;2039&quot; data-ke-size=&quot;size23&quot;&gt;3) 경사하강법(Gradient Descent)의 최적화 수렴 속도 개선&lt;/h3&gt;
&lt;p data-end=&quot;2160&quot; data-start=&quot;2086&quot; data-ke-size=&quot;size16&quot;&gt;딥러닝이나 선형 모델은 경사하강법을 쓰는데 스케일이 다르면 손실함수의 모양이 길쭉한 타원이 되어 최적점으로 수렴하는 데 오랜 시간이 걸닌다.&lt;br /&gt;스케일링하면 &amp;rarr; 매끄러운 곡면 &amp;rarr; 빠르게 수렴&lt;/p&gt;
&lt;h3 data-end=&quot;2201&quot; data-start=&quot;2162&quot; data-ke-size=&quot;size23&quot;&gt;4) 모든 Feature를 동일 기준으로 해석하게 함&lt;/h3&gt;
&lt;p data-end=&quot;2220&quot; data-start=&quot;2202&quot; data-ke-size=&quot;size16&quot;&gt;모든 Feature를 동일한 기준(0~1 또는 평균 0, 분산 1)으로 맞추면 모델이 Feature 간 중요도를 제대로 비교할 수 있고 학습 과정이 안정적이고 불필요한 큰 값 영향 제거 즉, 학습 효율과 안정성, 성능 모두 좋아짐. 스케일링은 &amp;ldquo;특징값의 크기&amp;middot;단위 차이&amp;rdquo;를 없서 모델이 공정하게 학습하도록 만드는 기술.&lt;/p&gt;
&lt;p data-end=&quot;2220&quot; data-start=&quot;2202&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2220&quot; data-start=&quot;2202&quot; data-ke-size=&quot;size16&quot;&gt;특정 feature 의 value 가 다른 feature 들 보다 훨씬 크면, 그 값이 목적함수를 지배하게 되므로 정확한 학습이 되지 않음&lt;/p&gt;
&lt;h2 data-end=&quot;2236&quot; data-start=&quot;2227&quot; data-ke-size=&quot;size26&quot;&gt;5. 스케일링 종류&lt;/h2&gt;
&lt;h3 data-end=&quot;2266&quot; data-start=&quot;2238&quot; data-ke-size=&quot;size23&quot;&gt;1) Simple Feature Scaling&lt;/h3&gt;
&lt;h3 data-end=&quot;2349&quot; data-start=&quot;2308&quot; data-ke-size=&quot;size23&quot;&gt;2) Min&amp;ndash;Max Scaling (정규화 Normalization)&lt;/h3&gt;
&lt;p data-end=&quot;2366&quot; data-start=&quot;2350&quot; data-ke-size=&quot;size16&quot;&gt;최솟값 0, 최댓값 1로 맞춤&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2488&quot; data-start=&quot;2432&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2449&quot; data-start=&quot;2432&quot;&gt;장점 : 값이 0~1 범위로 깔끔해짐&lt;/li&gt;
&lt;li data-end=&quot;2488&quot; data-start=&quot;2450&quot;&gt;단점 : &lt;b&gt;이상치(outlier)가 있으면 전체 스케일 왜곡됨&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;2524&quot; data-start=&quot;2490&quot; data-ke-size=&quot;size23&quot;&gt;3) Standard Scaling (Z-score)&lt;/h3&gt;
&lt;p data-end=&quot;2537&quot; data-start=&quot;2525&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;가장 많이 쓰이는 방식&lt;/b&gt;으로 평균과 표준편차를 이용하여 scaling&amp;nbsp; 하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span aria-hidden=&quot;true&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt; $$&amp;nbsp;X_{new}&amp;nbsp;=&amp;nbsp;\frac{X_{old}&amp;nbsp;-&amp;nbsp;\mu}{\sigma}$$​&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;2724&quot; data-start=&quot;2590&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;2686&quot; data-start=&quot;2590&quot;&gt;데이터가 평균 0, 분산 1의 형태로 변환&lt;br /&gt;&amp;rarr; 대부분의 머신러닝 알고리즘(특히 선형모델, SVM, 로지스틱 회귀, KNN, PCA 등)은 데이터가 정규분포에 가깝다고 가정하기 때문에 StandardScaler가 가장 잘 맞습니다.&lt;/li&gt;
&lt;li data-end=&quot;2724&quot; data-start=&quot;2688&quot;&gt;이상치(outlier)의 영향이 상대적으로 적음&lt;br /&gt;Min-Max Scaling은 이상치가 하나만 있어도 전체 스케일이 망가짐. 하지만 Standard Scaling은 평균과 표준편차 기반이라 상대적으로 안정적입니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1764066335476&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
# 평균/표준편차 계산
X_train = sc.fit_transform(X_train)
# 평균/표준편차 계산 안해줌. 테스트데이터나 새 데이터는 평균 표준편차 계산을 하면안됨.
X_test  = sc.transform(X_test)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot; data-start=&quot;2731&quot; data-end=&quot;2750&quot;&gt;6. Feature Engineering (특성 or 컬럼)&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;머신러닝에서 말하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;feature(특성)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;은 데이터셋에서의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;컬럼(column)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;을 의미합니다.하지만 &quot;그냥 컬럼&quot;이 아니라&lt;span&gt;&amp;nbsp;&lt;/span&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;target과의 높은 관련성이 있어야함. ex. 무작위 숫자, 중복되는 컬럼, 주민등로번호 등은 좋은 feature가 될 수 없다.&lt;/li&gt;
&lt;li&gt;&lt;span&gt;prediction(예측) 시점에 알수 있음 ex. sales data는 익월에 집계&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;numeric 타입이어야한다. 문자열은 머신러닝이 이해하지 못 함.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;충분한 데이터 수를 가져야한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;missing values 처리(.fillna)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;편향 처리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Data Normalization -&amp;gt; feature scaling&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Binning ( 연속된 숫자의 범위 지정 ) ex) 가격에 범위를 지정해서 비싼지, 중간인지, 싼지를 구분해주면 좋&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span&gt;categorical 변수의 수치화(머신러닝은 numeric 타입만 가능하기 때문) ex. 남자 :0, 여자: 1&lt;/span&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;span&gt;ordinal category (순서/크기가 있는 feature) ex. L&amp;gt;M&amp;gt;S -&amp;gt; 3,2,1&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span&gt;nominal category (순서/크기가 없는 feature) ex. color의 숫자 표시, 남자 여자&lt;/span&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;span&gt;one-hot encoding 사용하면됨. 0,1 로 변경하는 방법&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1764216804155&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#one-hot-encoding
pd.get_dummies(df_titanic)&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-end=&quot;2750&quot; data-start=&quot;2731&quot; data-ke-size=&quot;size26&quot;&gt;7. 머신러닝 Model 개발 플로우&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제&amp;nbsp;정의&amp;nbsp;-&amp;gt;&amp;nbsp;data&amp;nbsp;준비&amp;nbsp;-&amp;gt;&amp;nbsp;model&amp;nbsp;선택&amp;nbsp;-&amp;gt;&amp;nbsp;model&amp;nbsp;작성&amp;nbsp;-&amp;gt;&amp;nbsp;model&amp;nbsp;평가&amp;nbsp;-&amp;gt;&amp;nbsp;model&amp;nbsp;개선&amp;nbsp;-&amp;gt;&amp;nbsp;결과&amp;nbsp;보고&lt;/p&gt;
&lt;pre id=&quot;code_1764063459591&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import libraries - sklearn, numpy, pandas, matplotlib, etc
&amp;darr;
data load - csv, sklearn.datasets, etc
&amp;darr;
data 내용 파악 - shape, statistics(특성), visualize(시각화)
&amp;darr;
train/ test dataset 분할 : sklearn, manual 
&amp;darr;
feature scaling 정규화
&amp;darr;
model train : fit() fit은 scikit-learn에서 사용되는 전통적 ml 훈련 메소드
&amp;darr;
모델평가 및 시각화&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-end=&quot;2989&quot; data-start=&quot;2973&quot; data-ke-size=&quot;size26&quot;&gt;8. 전통적인 지도학습 알고리즘&lt;/h2&gt;
&lt;h3 data-end=&quot;3006&quot; data-start=&quot;2996&quot; data-ke-size=&quot;size23&quot;&gt;1) 회귀 모델&lt;/h3&gt;
&lt;h4 data-end=&quot;3053&quot; data-start=&quot;3008&quot; data-ke-size=&quot;size20&quot;&gt;(1) 단변수 선형회귀(Univariate Linear Regression)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp;한개의&amp;nbsp;변수로&amp;nbsp;결과&amp;nbsp;예측&amp;nbsp;(ex.&amp;nbsp;혈압으로&amp;nbsp;당뇨병&amp;nbsp;여부&amp;nbsp;예측) &lt;br /&gt;-&amp;nbsp;x,y가&amp;nbsp;주어지고&amp;nbsp;w,b&amp;nbsp;가&amp;nbsp;미지수&amp;nbsp;선형회귀에서&amp;nbsp;X는&amp;nbsp;입력(독립변수,&amp;nbsp;feature)&amp;nbsp;y는&amp;nbsp;출력(종속변수,&amp;nbsp;정답)&amp;nbsp;역할을&amp;nbsp;합니다. &lt;br /&gt;-&amp;nbsp;w,b&amp;nbsp;를&amp;nbsp;infer(추정)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;y=wx+by = w x + b&lt;/span&gt;&lt;span aria-hidden=&quot;true&quot;&gt;&lt;span&gt;&lt;span&gt;y&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;w&lt;/span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;b&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3110&quot; data-start=&quot;3073&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3083&quot; data-start=&quot;3073&quot;&gt;w: 기울기 &amp;nbsp;&amp;rarr;&amp;nbsp;x가&amp;nbsp;1&amp;nbsp;증가할&amp;nbsp;때&amp;nbsp;y가&amp;nbsp;얼마나&amp;nbsp;증가하는지&lt;/li&gt;
&lt;li data-end=&quot;3110&quot; data-start=&quot;3084&quot;&gt;b: 절편 &amp;nbsp;&amp;rarr; 그래프가 y축을 만나는 지점&amp;nbsp;&lt;br /&gt;w,b 둘 다 학습해야 하는 파라미터&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;3131&quot; data-start=&quot;3112&quot; data-ke-size=&quot;size16&quot;&gt;평가 지표: &lt;b&gt;R&amp;sup2; Score(결정계수)&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1764066495939&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from sklearn.metrics import r2_score
r2_score(diabetes_y_test, y_pred)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;3178&quot; data-start=&quot;3138&quot; data-ke-size=&quot;size20&quot;&gt;(2) 다변수 선형회귀(Multivariate Regression)&lt;/h4&gt;
&lt;p data-end=&quot;3197&quot; data-start=&quot;3179&quot; data-ke-size=&quot;size16&quot;&gt;변수가 여러 개일 뿐 단변수 선형회귀와 개념은 동일&lt;/p&gt;
&lt;h3 data-end=&quot;3214&quot; data-start=&quot;3204&quot; data-ke-size=&quot;size23&quot;&gt;2) 분류 모델&lt;/h3&gt;
&lt;h4 data-end=&quot;3248&quot; data-start=&quot;3216&quot; data-ke-size=&quot;size20&quot;&gt;(1) KNN (K-Nearest Neighbors)&lt;/h4&gt;
&lt;p data-end=&quot;3248&quot; data-start=&quot;3216&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-size: 16px; letter-spacing: 0px;&quot;&gt;-&amp;nbsp;다른&amp;nbsp;observation(관측치,&amp;nbsp;x&amp;nbsp;data)과의&amp;nbsp;유사성에&amp;nbsp;따른&amp;nbsp;분류&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;3248&quot; data-start=&quot;3216&quot; data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp;서로&amp;nbsp;가까이&amp;nbsp;있는&amp;nbsp;data&amp;nbsp;들을&amp;nbsp;이웃(neighbor)이라고&amp;nbsp;부름 &lt;br /&gt;-&amp;nbsp;가까이&amp;nbsp;있는&amp;nbsp;이웃의&amp;nbsp;label들&amp;nbsp;중&amp;nbsp;가장&amp;nbsp;많은&amp;nbsp;것을&amp;nbsp;unknown&amp;nbsp;case의&amp;nbsp;predication으로&amp;nbsp;응답한다. &lt;br /&gt;-&amp;nbsp;장단점&amp;nbsp;:&amp;nbsp;간단하지만&amp;nbsp;dataset이&amp;nbsp;커지면&amp;nbsp;상당히&amp;nbsp;느려진다.&amp;nbsp;다차원&amp;nbsp;공간에서의&amp;nbsp;unknown&amp;nbsp;포인트와&amp;nbsp;새로들어온&amp;nbsp;데이터&amp;nbsp;거리를&amp;nbsp;다&amp;nbsp;계산해야&amp;nbsp;하기&amp;nbsp;때문.&lt;/p&gt;
&lt;pre id=&quot;code_1764126541486&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from sklearn.neighbors import KNeighborsClassifier
clf = KNeighborsClassifier(n_neighbors=15, weights='uniform')
clf.fit(X_train, y_train)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-end=&quot;3369&quot; data-start=&quot;3342&quot; data-ke-size=&quot;size20&quot;&gt;(2) Decision Tree (결정트리)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3462&quot; data-start=&quot;3370&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3398&quot; data-start=&quot;3370&quot;&gt;규칙을 &lt;b&gt;if-else로&lt;/b&gt; 자동 생성해주는 모델&lt;/li&gt;
&lt;li data-end=&quot;3429&quot; data-start=&quot;3399&quot;&gt;장점: 해석 용이(white-box model) 즉&amp;nbsp;설명이&amp;nbsp;필요한&amp;nbsp;분야에&amp;nbsp;주로&amp;nbsp;쓰임(EX.은행),&amp;nbsp;data&amp;nbsp;preprocessing이&amp;nbsp;필요없이&amp;nbsp;데이터를&amp;nbsp;그대로&amp;nbsp;사용가능&lt;/li&gt;
&lt;li data-end=&quot;3462&quot; data-start=&quot;3430&quot;&gt;단점: &lt;b&gt;과적합(overfitting) 위험 높아&lt;/b&gt; 훈련데이터는 잘 맞지만 검증데이터에서는 정답률 낮은 현상이 자주 발생한다. 훈련&amp;nbsp;데이터의&amp;nbsp;작은&amp;nbsp;변화에도&amp;nbsp;매우&amp;nbsp;민감하다.&lt;b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp;추후&amp;nbsp;&lt;b&gt;앙상블&amp;nbsp;모델과&amp;nbsp;밀접한&amp;nbsp;연관&lt;/b&gt;이&amp;nbsp;있음. &lt;br /&gt;-&amp;nbsp;모든&amp;nbsp;가능한&amp;nbsp;결정&amp;nbsp;경로(decision&amp;nbsp;path)를&amp;nbsp;tree&amp;nbsp;형태로&amp;nbsp;구성한다.&amp;nbsp; &lt;br /&gt;-&amp;nbsp;각&amp;nbsp;node는&amp;nbsp;test를&amp;nbsp;의미하고&amp;nbsp;branch는&amp;nbsp;test의&amp;nbsp;결과에&amp;nbsp;해당한다. &lt;br /&gt;- left node는 classification(분류)에 해당한다.&lt;br /&gt;&lt;br /&gt;-&amp;nbsp;알고리즘&amp;nbsp;종류 &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - ID3 기본적 알고리즘 정보이득을 이용한 트리 구성 &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - CART, C4.5, C5.0, CHAID, MARS &lt;br /&gt;&lt;br /&gt;-&amp;nbsp;Decision&amp;nbsp;Tree에서는&amp;nbsp;엔트로피가&amp;nbsp;높은&amp;nbsp;상태에서&amp;nbsp;낮은&amp;nbsp;상태가&amp;nbsp;되도록&amp;nbsp;데이터를&amp;nbsp;특정&amp;nbsp;조건을&amp;nbsp;찾아&amp;nbsp;나무&amp;nbsp;모양으로&amp;nbsp;구분해&amp;nbsp;간다.&lt;/p&gt;
&lt;pre id=&quot;code_1764066729903&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from sklearn import tree
clf = tree.DecisionTreeClassifier(max_depth=2, criterion='entropy') # max_depth를 잘 조절해야 정답률 차이가남. max_depth를 너무 크게잡으면 새로운 데이터를 못 맞출 확률 높음.
clf.fit(X_train, y_train)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-end=&quot;3564&quot; data-start=&quot;3529&quot; data-ke-size=&quot;size20&quot;&gt;(3) Logistic Regression(로지스틱 회귀)&lt;/h4&gt;
&lt;p data-end=&quot;3564&quot; data-start=&quot;3529&quot; data-ke-size=&quot;size16&quot;&gt;- &lt;b&gt;회귀모델중 유일하게 분류&lt;/b&gt;&amp;nbsp;모델로 선형회귀를 분류 문제에 적용할 수 있지 않을까? 라는 의문으로 부터 시작됨. 선형회귀 그래프의 y값의 일정 부분이 넘으면 0으로 이하면 1로 분류 하는 문제이다. (ex. 0.5이상이면 암으로 분류, 이하면 종양으로 분류) 하지만 이런 형태는 애매한 구역 발생. 그래서 Sigmoid 함수가 나옴.&amp;nbsp;&amp;nbsp;&lt;br /&gt;- &lt;b&gt;Sigmoid 함수&lt;/b&gt;는 0과 1을 출력하는 이진분류 모델이며 미분가능한 성질을 갖는다.&lt;/p&gt;
&lt;pre id=&quot;code_1764126462861&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from sklearn.linear_model import LogisticRegression
lr_classifier = LogisticRegression(solver='lbfgs', random_state=0)
lr_classifier.fit(X_train, y_train)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3) 회귀 분류 앙상블 학습 알고리즘(Ensemble Learning)&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;) 해서 더 좋은 결과를 얻습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다수의 약한 학습기 (weak learner)를 조합하여 더 높은 성능 추출을 목표로 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;228&quot; data-start=&quot;195&quot; data-ke-size=&quot;size18&quot;&gt;분류(Classification)와 회귀(Regression)에서의 앙상블 예시&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;분류 - 여러 모델의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;다수결 투표&lt;/b&gt;로 클래스를 결정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;회귀 - 여러 모델의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;평균값&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;또는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;가중평균&lt;/b&gt;을 사용해 예측값을 계산합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;387&quot; data-start=&quot;229&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;259&quot; data-start=&quot;229&quot;&gt;&lt;b&gt;Random Forest Classifier &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;(분류)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; or Regressor (회귀) (많이 사용됨)&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;294&quot; data-start=&quot;260&quot;&gt;&lt;b&gt;Gradient Boosting Classifier &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;(분류)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; or Regressor (회귀) &amp;nbsp;(많이 사용됨) &lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;338&quot; data-start=&quot;295&quot;&gt;XGBoost / LightGBM / CatBoost (분류 모드 or 회귀 모드)&lt;/li&gt;
&lt;li data-end=&quot;362&quot; data-start=&quot;339&quot;&gt;Voting Classifier(분류) or Regressor(회귀)&lt;/li&gt;
&lt;li data-end=&quot;387&quot; data-start=&quot;363&quot;&gt;Bagging Classifier &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;(분류)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; or Regressor (회귀)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-end=&quot;656&quot; data-start=&quot;610&quot; data-ke-size=&quot;size20&quot;&gt;(1) Bagging (Bootstrap Aggregating)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bagging은 같은 알고리즘의 모델을 &lt;b&gt;여러 개 만들고&lt;/b&gt;, 각 모델이 &lt;b&gt;조금씩 다른 무작위 데이터 샘플(모든 attribute 사용)&lt;/b&gt;로 학습하도록 한 뒤예측을 &lt;b&gt;평균(회귀)&lt;/b&gt; 또는 &lt;b&gt;투표(분류)&lt;/b&gt; 로 합치는 기법입니다. 즉, &lt;b&gt;&amp;ldquo;데이터 다양하게 뽑아서 여러 모델을 독립적으로 훈련 &amp;rarr; 최종 결합&amp;rdquo;&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;448&quot; data-start=&quot;349&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;393&quot; data-start=&quot;349&quot;&gt;원본 데이터에서 &lt;b&gt;중복을 허용하며(bootstrap) 랜덤 샘플링&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;423&quot; data-start=&quot;394&quot;&gt;여러 개의 모델을 &lt;b&gt;병렬로 독립적 학습&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;448&quot; data-start=&quot;424&quot;&gt;예측 결과를 &lt;b&gt;평균/투표로 합치기&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;536&quot; data-start=&quot;460&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;492&quot; data-start=&quot;460&quot;&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 data-end=&quot;492&quot; data-start=&quot;460&quot;&gt;&lt;b&gt;분산(Variance) 감소&lt;/b&gt; &amp;rarr; 과적합 방지&lt;/li&gt;
&lt;li data-end=&quot;510&quot; data-start=&quot;493&quot;&gt;모델이 안정적이고 일관됨&lt;/li&gt;
&lt;li data-end=&quot;536&quot; data-start=&quot;511&quot;&gt;&lt;b&gt;병렬(Parallel)&lt;/b&gt; 학습 가능 &amp;rarr; 빠름&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;726&quot; data-start=&quot;622&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;573&quot; data-start=&quot;548&quot;&gt;단점
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;573&quot; data-start=&quot;548&quot;&gt;편향(Bias) 감소 효과는 크지 않음&lt;/li&gt;
&lt;li data-end=&quot;607&quot; data-start=&quot;574&quot;&gt;단순한 데이터 구조에서 Boosting처럼 고성능은 아님&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;※ Random Forest (Bagging + Decision Tree) 모델&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;667&quot; data-start=&quot;622&quot;&gt;단순 bagging 알고리즘을 강화한 기법 으로 &lt;b&gt;대표 모델에 속함&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;667&quot; data-start=&quot;622&quot;&gt;&lt;b&gt;bagging은 랜덤하게 추출한 데이터의 모든 attribute를 모두 학습시킨다.&lt;/b&gt; 모든 attribute를 가지고 Tree를 만들 경우 매우 강한 attribute가 모든 tree에 항상 포함된다.&lt;b&gt; 이를 막기 위한 방법으로 무작위로 추출한 데이터의 서로 다른 특성으로 Tree를 만드는 기법(ex. 30개중 10개만 random selection) 즉, bagging을 보완한 알고리즘&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;1212&quot; data-start=&quot;973&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;구분&lt;/td&gt;
&lt;td&gt;Bagging&lt;/td&gt;
&lt;td&gt;Random forest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1084&quot; data-start=&quot;1041&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1051&quot; data-start=&quot;1041&quot;&gt;데이터 샘플링&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1067&quot; data-start=&quot;1051&quot;&gt;O (bootstrap)&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1084&quot; data-start=&quot;1067&quot;&gt;O (bootstrap)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1136&quot; data-start=&quot;1085&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1103&quot; data-start=&quot;1085&quot;&gt;특성(feature) 샘플링&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1110&quot; data-start=&quot;1103&quot;&gt;없음&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1136&quot; data-start=&quot;1110&quot;&gt;있음 (feature bagging)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1165&quot; data-start=&quot;1137&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1148&quot; data-start=&quot;1137&quot;&gt;트리 간 다양성&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1156&quot; data-start=&quot;1148&quot;&gt;보통 낮음&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1165&quot; data-start=&quot;1156&quot;&gt;훨씬 높음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1189&quot; data-start=&quot;1166&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1171&quot; data-start=&quot;1166&quot;&gt;성능&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1175&quot; data-start=&quot;1171&quot;&gt;△&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1189&quot; data-start=&quot;1175&quot;&gt;더 좋음(보통)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1212&quot; data-start=&quot;1190&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1199&quot; data-start=&quot;1190&quot;&gt;과적합 억제&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1204&quot; data-start=&quot;1199&quot;&gt;보통&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1212&quot; data-start=&quot;1204&quot;&gt;더 강함&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1764208634496&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from sklearn.ensemble import RandomForestClassifier

# Training set 에 대해 Random Forest Classifier model 을 fitting
rf = RandomForestClassifier(n_estimators=10, criterion='entropy', random_state=0)
rf.fit(X_train, y_train)

y_pred = rf.predict(X_test)

print(y_pred)
print(&quot;Test set true counts = &quot;, sum(y_test))
print(&quot;predicted true counts = &quot;, sum(y_pred))
print(&quot;accuracy = {:.2f}&quot;.format(sum(y_pred == y_test) / len(y_test)))

# making confusion matrix
print(&quot;confution matrix\n&quot;, 
      confusion_matrix(y_test, y_pred, labels=[1, 0]))
print(&quot;f1 score\n&quot;, f1_score(y_test, y_pred, labels=[1, 0]))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1802&quot; data-origin-height=&quot;525&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dc3xVk/dJMcacByqKO/Viq6UnegE2h2MQYGCP3pt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dc3xVk/dJMcacByqKO/Viq6UnegE2h2MQYGCP3pt0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dc3xVk/dJMcacByqKO/Viq6UnegE2h2MQYGCP3pt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdc3xVk%2FdJMcacByqKO%2FViq6UnegE2h2MQYGCP3pt0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1802&quot; height=&quot;525&quot; data-origin-width=&quot;1802&quot; data-origin-height=&quot;525&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-start=&quot;610&quot; data-end=&quot;656&quot;&gt;(2)&lt;span&gt; Boosting&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-end=&quot;886&quot; data-start=&quot;803&quot; data-ke-size=&quot;size16&quot;&gt;Boosting은 약한 모델(weak learners)을 여러 개 &lt;b&gt;순차적으로(이전 모델의 오류를 보완하면서)&lt;/b&gt; 학습시키는 기법입니다.즉, &lt;b&gt;&amp;ldquo;앞 모델이 못한 부분을 뒤 모델이 점점 보완해 나가는 것&amp;rdquo;&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;886&quot; data-start=&quot;803&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;b&gt;※&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt; Gradient Boosting (XGBRegressor)모델&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 data-start=&quot;622&quot; data-end=&quot;667&quot;&gt;&lt;b&gt;대표 모델에 속함&lt;/b&gt;&lt;/li&gt;
&lt;li data-start=&quot;622&quot; data-end=&quot;667&quot;&gt;앞 모델이 만든 &lt;b&gt;오차(잔차, gradient)를 다음 모델이 학습해서 보완&lt;/b&gt;하는 방식&lt;/li&gt;
&lt;li data-start=&quot;622&quot; data-end=&quot;667&quot;&gt;&quot;&lt;b&gt;경사하강법(gradient descent)&lt;/b&gt;&quot;의 개념을 &lt;b&gt;트리 모델에 적용&lt;/b&gt;한 것입니다.&lt;/li&gt;
&lt;li data-end=&quot;440&quot; data-start=&quot;417&quot;&gt;동작 방식&lt;br /&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;440&quot; data-start=&quot;417&quot;&gt;첫 번째 모델 예측 &amp;rarr; 오차 계산&lt;/li&gt;
&lt;li data-end=&quot;440&quot; data-start=&quot;417&quot;&gt;&lt;b&gt;오차(=잔차)&lt;/b&gt;&amp;nbsp;를 다음 트리가 학습&lt;/li&gt;
&lt;li data-end=&quot;440&quot; data-start=&quot;417&quot;&gt;계속해서&amp;nbsp;&lt;b&gt;잔차를 줄이는 방향&lt;/b&gt;으로 트리를 추가&lt;/li&gt;
&lt;li data-end=&quot;440&quot; data-start=&quot;417&quot;&gt;모든 트리의 예측을 더해 최종 모델을 완성&lt;/li&gt;
&lt;/ol&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;순차적(병렬x)&lt;/b&gt;으로 오류를 줄여 성능이 매우 높다 -&amp;gt; 복잡한 패턴도 차근차근 학습 가능&lt;/li&gt;
&lt;li&gt;트리라서 데이터 스케일링 필요 없음 - &amp;gt; &lt;b&gt;정규화/스케일링을 안 해도 됨, 이상치(outlier)에 상대적으로 강함&lt;/b&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;과적합(Overfitting) 위험이 Random Forest보다 큼 -&amp;gt; learning_rate, depth 튜닝 필요&lt;/li&gt;
&lt;li&gt;순차 학습이라 병렬화 어렵다. -&amp;gt; XGBoost 등장 이유&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1764208733322&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from sklearn.ensemble import GradientBoostingClassifier
#- min_samples_split : node 분리에 필요한 최소 sample 수 =&amp;gt; overfitting 방지  
#- max_depth : tree 깊이 조절 =&amp;gt; overfitting 방지
#- learning_rate : 각 tree 의 기여도 조정, n_estimators 와 trade-off 
#- n_estimators : number of sequential trees
gb = GradientBoostingClassifier(learning_rate=0.1, n_estimators=500, max_depth=5)
gb.fit(X_train, y_train)

y_pred = gb.predict(X_test)

print(y_pred)
print(&quot;Test set true counts = &quot;, sum(y_test))
print(&quot;predicted true counts = &quot;, sum(y_pred))
print(&quot;accuracy = {:.2f}&quot;.format(
            sum(y_pred == y_test) / len(y_test)))
            
# making confusion matrix
print(&quot;confution matrix\n&quot;, 
      confusion_matrix(y_test, y_pred, labels=[1, 0]))
print(&quot;f1 score\n&quot;, f1_score(y_test, y_pred, labels=[1, 0]))

# xgbregressor
from xgboost import XGBRegressor

model_ts = XGBRegressor(
    n_estimators=500, learning_rate=0.05, max_depth=6, subsample=0.8, colsample_bytree=0.8)
model_ts.fit(centeral_X_train, centeral_Y_train)&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot; data-start=&quot;2973&quot; data-end=&quot;2989&quot;&gt;9. 전통적인 비지도학습 알고리즘&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비지도 학습은 개발자도 정답을 모르며 데이터만 가지고 있다. AI에게 데이터를 전달해 패턴을 찾게 시키는 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;clustering(군집화)이란 비슷한 object들끼리 모으는 것 으로 label data가 없는 것이 classification(분류)모델과 차이 라고 할 수 있다.&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 고객의 취향에 맞는 책, 동영상 추천&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 뉴스 자동 분류 및 추천&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 유전자 분석&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 신용카드 정사 및 비정상 유형 찾기&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;1) Clustering(군집화) 알고리즘&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;군집화는 비지도 학습의 한 종류로, 레이블 없이 데이터 자체의 내재된 패턴을 찾아 유사한 데이터끼리 묶는 것입니다. 예측이나 분류와는 다르.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) K-Means Clustering&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다차원에서 샘플간 거리를 계산하는 방식으로 K-Means는 미리 정한 K개의 중심을 초기화하고, 각 데이터를 가장 가까운 중심으로 할당 후, 중심을 업데이트하며 반복하는 방식으로 군집을 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그룹핑을 직접정하기 때문에&lt;b&gt; 아웃라이어(Outlier) 디텍션에 약하다. &lt;/b&gt;그래서 &lt;b&gt;그룹핑을 지정하는 k의 개수를 잘 정하는 것이 중요하다.&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;Random 하게 k개의 centroid(중심점)를 정한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;각 centroid로 부터 각 data point 까지의 거리를 계산한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;각 data point 를 가장 가까운 centroid에 할당하여 cluster를 생성한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;k &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; centroid 의 위치를 다시 계산한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;centroid가 더 이상 움직이지 않을 때까지 2~4단계를 반복한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1764646198267&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs

centroidLocation = [[3,2], [1,-1],[-1,2]]

X, _ = make_blobs(n_samples=1500, centers=centroidLocation)

plt.scatter(X[:,0], X[:,1], marker='.')

k_means = KMeans(n_clusters=3)
k_means.fit(X)

from matplotlib.colors import ListedColormap

colors_bold = ListedColormap(['#FF0000', '#00FF00', '#0000FF'])
colors_light = ListedColormap(['#FFAAAA', '#AAFFAA', '#AAAAFF'])

plt.figure(figsize=(8,6))

for i in range(nclusters):
    members = k_means.labels_ == i
    plt.plot(X[members, 0], X[members, 1], '.', 
             color=colors_light(i), markersize=10, label=i)
    plt.plot(centers[i, 0], centers[i, 1], 'o', 
             color=colors_bold(i), markeredgecolor='k', markersize=20)

plt.title(&quot;KMeans&quot;)
plt.legend()&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(2) DBSCAN (Density-based Clustering)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DBSCAN은&amp;nbsp;밀도&amp;nbsp;기준에&amp;nbsp;따라&amp;nbsp;핵심&amp;nbsp;포인트,&amp;nbsp;경계&amp;nbsp;포인트,&amp;nbsp;이상치를&amp;nbsp;구분하며,&amp;nbsp;K-Means와&amp;nbsp;달리&amp;nbsp;군집&amp;nbsp;수를&amp;nbsp;미리&amp;nbsp;정하지&amp;nbsp;않아도&amp;nbsp;되고&amp;nbsp;비구형&amp;nbsp;군집에&amp;nbsp;강점이&amp;nbsp;있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;K-Means의 경우 임의로 cluster 지정하므로 same cluster 내의 data point 들이 실제로는 유사하지 않을 수 있다. 이런 부분을 보완하기 위해 DBSCAN은 밀도가 높은 지역과 낮은 지역을 서로 분리한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Outlier의 영향을 적게 받고, cluster 숫자를 미리 정해주지 않아도 되는 것이 장점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;즉 포인트들의 집합간 밀도&lt;/b&gt;를 파악 해서 군집화 해서 불규칙한 그룹핑도 가능하기 떄문에&amp;nbsp;&lt;b&gt;밀도에서 벗어난 아웃라이어(Outlier) 디텍션에 강력하다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1764646417144&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from sklearn.cluster import DBSCAN

epsilon = 0.3 # 반경
minimumSamples = 7
db = DBSCAN(eps=epsilon, min_samples=minimumSamples).fit(X)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;(3) Hierarchical Clustering(dendrogram)&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;생물학에서 많이 쓰이는 알고리즘.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(4) 차원 축소 기법 - PCA (Principal Component Analysis) 주성분 분석&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차원이 증가함에 따라 vector 공간내의 spcae도 증가하는데 데이터의 양이 적으면 빈공간이 많이 발생하여 에측의 정확도가 떨어진다. 이런 경우 유사한 성격의 feature( 키, 신장 등)는 하나의 새로운 feature로 성분을 합칠 수 있다.&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;/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;PCA를 사용하여 해결하고자 하는 주요 문제는 고차원 데이터 공간의 희소성(차원의 저주)를 하결하고자 한다. 차원의 저주는 특성(Feature) 수가 많아질수록 데이터 공간이 기하급수적으로 커져 데이터가 희소해지는 현상으로 PCA는 차원을 줄여 이 문제를 완화한다.&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;PCA는&amp;nbsp;데이터의&amp;nbsp;분산을 가장 잘 설명하는 새로운 축(주성분)을 찾아 데이터를 투영함으로써(이때 분산을 최대한 보존하려함), 원본 데이터의 정보 손실을 최소화하며 차원을 줄인다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1764734776065&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Apply kernel PCA
from sklearn.decomposition import PCA

pca = PCA(n_components=2) # 2 개 component 로 차원(열) 축소

X_train_pca = pca.fit_transform(X_train)
X_test_pca = pca.transform(X_test)

print(pca.components_.shape)
print(&quot;첫번째 주성분(고유벡터) :&quot;)
print(pca.components_[0])
print(&quot;두번째 주성분(고유벡터) :&quot;)
print(pca.components_[1])
print()
print('설명된 분산(고유값)의 비율: {}, 두 성분의 합: {:.2f}'.format(pca.explained_variance_ratio_,sum(pca.explained_variance_ratio_)))
      
# pca를 사용한 축소된 차원 데이터 사용해서 로지스특 돌려보기
clf = LogisticRegression(solver='lbfgs', random_state=0)
clf.fit(X_train_pca, y_train)&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-end=&quot;3578&quot; data-start=&quot;3571&quot; data-ke-size=&quot;size26&quot;&gt;10. 평가 지표&lt;/h2&gt;
&lt;h3 data-end=&quot;3594&quot; data-start=&quot;3585&quot; data-ke-size=&quot;size23&quot;&gt;1) 지도학습 회귀 모델 성능 평가&lt;/h3&gt;
&lt;h4 data-end=&quot;3627&quot; data-start=&quot;3596&quot; data-ke-size=&quot;size20&quot;&gt;(1) MSE (Mean Squared Error)&lt;/h4&gt;
&lt;p data-end=&quot;3639&quot; data-start=&quot;3628&quot; data-ke-size=&quot;size16&quot;&gt;MSE는&amp;nbsp;&lt;b&gt;&amp;ldquo;회귀(regression)&amp;rdquo;에서&amp;nbsp;사용하는&amp;nbsp;대표적인&amp;nbsp;손실&amp;nbsp;함수(loss&amp;nbsp;function)&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;3639&quot; data-start=&quot;3628&quot; data-ke-size=&quot;size16&quot;&gt;오차(예측값과 실제값의 차이)를 제곱하여 평균방법 으로 값이 작을수록 모델이 잘 맞추고 있는 것&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3670&quot; data-start=&quot;3641&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3660&quot; data-start=&quot;3641&quot;&gt;큰 오차에 &lt;b&gt;더 큰 패널티&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;3670&quot; data-start=&quot;3661&quot;&gt;이상치에 민감&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-end=&quot;3704&quot; data-start=&quot;3672&quot; data-ke-size=&quot;size20&quot;&gt;(2) MAE (Mean Absolute Error)&lt;/h4&gt;
&lt;p data-end=&quot;3717&quot; data-start=&quot;3705&quot; data-ke-size=&quot;size16&quot;&gt;오차의 절대값 평균, 이상치에 덜 민감&lt;/p&gt;
&lt;p data-end=&quot;3747&quot; data-start=&quot;3731&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;MSE vs MAE&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3802&quot; data-start=&quot;3748&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3776&quot; data-start=&quot;3748&quot;&gt;MSE: 정규분포 + &lt;b&gt;큰 오차 잡고 싶을 때&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상치가 거의 없는 데이터, 큰 오차를 더 강하게 잡고 싶을 때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선형회귀처럼 전통적인 회귀 모델, 미분하기 쉬워서 학습이 안정적 &lt;br /&gt;&amp;rarr;&amp;nbsp;수학적으로&amp;nbsp;예쁘고&amp;nbsp;최적화에&amp;nbsp;유리&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3802&quot; data-start=&quot;3748&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3802&quot; data-start=&quot;3777&quot;&gt;MAE: &lt;b&gt;이상치 많은 실제 데이터에서 유리&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상치(outlier)가 있을 가능성이 높음, Robust한(튼튼한)&amp;nbsp;모델이&amp;nbsp;필요할&amp;nbsp;때 &lt;br /&gt;에러의 크기를 있는 그대로 반영하고 싶을 때, 오차에&amp;nbsp;선형적&amp;nbsp;패널티를&amp;nbsp;주고&amp;nbsp;싶을&amp;nbsp;때 &lt;br /&gt;&amp;rarr;&amp;nbsp;실제&amp;nbsp;현장&amp;nbsp;데이터에서&amp;nbsp;MAE가&amp;nbsp;더&amp;nbsp;유리한&amp;nbsp;경우&amp;nbsp;많음&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;최적화(Gradient Descent) 관점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MSE &lt;br /&gt;미분 가능하고 부드러워서 최적화가 쉽다. Gradient가&amp;nbsp;큰&amp;nbsp;오차에서&amp;nbsp;커짐&amp;nbsp;&amp;rarr;&amp;nbsp;&lt;b&gt;학습 빠름&lt;/b&gt;&lt;br /&gt;MAE &lt;br /&gt;|x|은 x=0에서 미분 안 됨. 최적화가&amp;nbsp;상대적으로&amp;nbsp;까다롭고&amp;nbsp;&lt;b&gt;느릴&amp;nbsp;수&amp;nbsp;있음&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 176px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;항목&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;MSE (Mean Squared Error)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;MAE (Mean Absolute Error)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;수식 의미&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;제곱 오차의 평균&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;절대 오차의 평균&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;큰 오차 영향&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;아주 크게 반영됨 (벌칙 큼)&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;반영되지만 선형적으로 증가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;이상치(Outlier) 영향&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;매우 민감함&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;비교적 둔감함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;최적화 특성&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;미분 가능하여 학습이 쉽고 안정적&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;기울기 0 구간 존재로 불안정할 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;사용 상황&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;정규분포&amp;middot;이상치 적은 데이터&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;이상치 많고 현실 데이터 분포에 적합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;패널티 형태&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;오차 증가 시 패널티가 &lt;b&gt;급격히 증가 (제곱)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;오차 증가에 따라 &lt;b&gt;선형 증가&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;&lt;b&gt;오차 곡선 형태&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;U자 형태 (parabolic)&lt;/td&gt;
&lt;td style=&quot;height: 22px;&quot;&gt;절대값 형태 (V자 형태)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-end=&quot;3819&quot; data-start=&quot;3804&quot; data-ke-size=&quot;size23&quot;&gt;(3) R&amp;sup2; Score(결정계수)&lt;/h3&gt;
&lt;p data-end=&quot;3879&quot; data-start=&quot;3820&quot; data-ke-size=&quot;size16&quot;&gt;설명력 지표 (0~1)로 모델이&amp;nbsp;데이터를&amp;nbsp;얼마나&amp;nbsp;잘&amp;nbsp;설명하는지(설명력)를&amp;nbsp;0~1&amp;nbsp;사이에서&amp;nbsp;표현한&amp;nbsp;값&amp;nbsp;&lt;br /&gt;모델이&amp;nbsp;y를&amp;nbsp;얼마나&amp;nbsp;잘&amp;nbsp;설명하는지&amp;nbsp;평가하는&amp;nbsp;지표1에&amp;nbsp;가까울수록&amp;nbsp;좋고,&amp;nbsp;0이면&amp;nbsp;평균&amp;nbsp;수준,&amp;nbsp;음수면&amp;nbsp;매우&amp;nbsp;나쁨&lt;br /&gt;&lt;b&gt;회귀(regression)&amp;nbsp;모델의&amp;nbsp;&amp;ldquo;성능&amp;nbsp;점수&amp;rdquo;라고&amp;nbsp;보면&amp;nbsp;됩니다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1764067176054&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;R2 = 1&amp;minus;(예측값에 대한 분산의 합/분산의 합​)&lt;/code&gt;&lt;/pre&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;R&amp;sup2; 값&lt;/td&gt;
&lt;td&gt;&amp;nbsp;의미&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;1.0&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;완벽한 예측&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;0.0&lt;/b&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;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1764067192112&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from sklearn.metrics import r2_score
r2 = r2_score(y_true, y_pred)
print(&quot;R2:&quot;, r2)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-end=&quot;3896&quot; data-start=&quot;3886&quot; data-ke-size=&quot;size23&quot;&gt;2) 지도학습 분류모델 성능 평가&lt;/h3&gt;
&lt;h4 data-end=&quot;3918&quot; data-start=&quot;3898&quot; data-ke-size=&quot;size20&quot;&gt;Confusion Matrix(혼돈행렬)을&amp;nbsp;이용한&amp;nbsp;평가&amp;nbsp;&lt;/h4&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;4059&quot; data-start=&quot;3920&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;실제 예측&lt;/td&gt;
&lt;td&gt;Positive&lt;/td&gt;
&lt;td&gt;Negative&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;4023&quot; data-start=&quot;3988&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4000&quot; data-start=&quot;3988&quot;&gt;Positive&lt;/td&gt;
&lt;td data-end=&quot;4011&quot; data-start=&quot;4000&quot; data-col-size=&quot;sm&quot;&gt;TP&lt;/td&gt;
&lt;td data-end=&quot;4023&quot; data-start=&quot;4011&quot; data-col-size=&quot;sm&quot;&gt;FN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;4059&quot; data-start=&quot;4024&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4036&quot; data-start=&quot;4024&quot;&gt;Negative&lt;/td&gt;
&lt;td data-end=&quot;4047&quot; data-start=&quot;4036&quot; data-col-size=&quot;sm&quot;&gt;FP&lt;/td&gt;
&lt;td data-end=&quot;4059&quot; data-start=&quot;4047&quot; data-col-size=&quot;sm&quot;&gt;TN&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;4073&quot; data-start=&quot;4061&quot;&gt;classification(분류) 성능의 정확성 측정&lt;/li&gt;
&lt;li data-end=&quot;4073&quot; data-start=&quot;4061&quot;&gt;TP(True Positive) : 1을 1로 제대로 분류&lt;/li&gt;
&lt;li data-end=&quot;4073&quot; data-start=&quot;4061&quot;&gt;FP(False Positive) : 0을 1로 잘못 분류&lt;/li&gt;
&lt;li data-end=&quot;4073&quot; data-start=&quot;4061&quot;&gt;FN(False Negative) : 1을 0로 잘못 분류&lt;/li&gt;
&lt;li data-end=&quot;4073&quot; data-start=&quot;4061&quot;&gt;TN(True Positive) : 0을 0으로 제대로 분류&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1764128444893&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from sklearn.metrics import confusion_matrix
cm  = confusion_matrix(y_test, y_pred, labels=[1, 0])

print(&quot;confution matrix\n&quot;, cm)

plt.figure(figsize=(5,4))

ax = sns.heatmap(cm, annot=True, fmt='d', xticklabels=[1, 0], yticklabels=[1, 0])
ax.set_ylabel('Predicted')
ax.set_title('Confusion Matirx\nGround Truth')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;4073&quot; data-start=&quot;4061&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Classification rate(정확도) : (TP+TN) / (TP+TN+FP+FN)&amp;nbsp;&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 data-end=&quot;4073&quot; data-start=&quot;4061&quot;&gt;단순히 정확도(Accuracy) 계산(전체 데이터 중 제대로 분류된 데이터 비율) &lt;span aria-hidden=&quot;true&quot;&gt;​&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;4236&quot; data-start=&quot;4217&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Precision(정밀성) : TP / (TP+FP) &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 data-end=&quot;4236&quot; data-start=&quot;4217&quot;&gt;Model이 sample을 True로 분류했을 때 얼마나 자주 맞췄는가 (1에 가까울 수록 좋음)&lt;/li&gt;
&lt;li data-end=&quot;4236&quot; data-start=&quot;4217&quot;&gt;더&amp;nbsp;정확한&amp;nbsp;데이터만&amp;nbsp;사용하고&amp;nbsp;싶을&amp;nbsp;때&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Recall(민감도, 재현율) : TP / (TP+FN)&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;전체 Positive 데이타 중에서 Positive로 분류한 비율 (1에 가까울 수록 좋음)&lt;/li&gt;
&lt;li&gt;Positive한 부분을 놓치기 싫을 때 사용. 잘못 분류된게 많아도 다 알고 싶을 때 사용한다. (정확도는 떨어짐)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1764120818636&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from sklearn.metrics import accuracy_score, precision_score, recall_score

# predict는 예측된 클래스 번호 array([0., 0., ...])
y_pred = 모델.predict(X_test)

print(&quot;Test set의 실제 true 갯수 = &quot;, sum(y_test))
print(&quot;모델이 예측한 true 갯수 = &quot;, sum(y_pred))
print(&quot;accuracy = {:.2f}&quot;.format(accuracy_score(y_test, y_pred)))
print(&quot;precision = {:.2f}&quot;.format(precision_score(y_test, y_pred)))
print(&quot;recall = {:.2f}&quot;.format(recall_score(y_test, y_pred)))
---
# predict_proba는 각 클래스의 확률 [0.85, 0.15]
y_pred_proba = 모델.predict_proba(X_test)
print(y_pred_proba)
---
y_pred_proba_1 = y_pred_proba[:, 1]
threshold = 0.4
y_pred_1 = y_pred_proba_1 &amp;gt; threshold
print(&quot;threshold가 {}일 때 1 로 분류된 갯수: &quot;.format(threshold), sum(y_pred_1))
print(&quot;precision = {:.2f}&quot;.format(precision_score(y_test, y_pred_1)))
print(&quot;recall = {:.2f}&quot;.format(recall_score(y_test, y_pred_1)))
print(&quot;f1 score = &quot;, f1_score(y_test, y_pred_1))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;4272&quot; data-start=&quot;4238&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;4272&quot; data-start=&quot;4238&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;precision과 recall은 반비례를 갖는다.&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 data-end=&quot;4272&quot; data-start=&quot;4238&quot;&gt;&lt;b&gt;Confidence(정확도) 수준을 올리고 싶으면 Precision을 높이고 Recall을 낮춰 Threshold를 조정한다.&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;4272&quot; data-start=&quot;4238&quot;&gt;&lt;b&gt;너무 많은 Case를 놓치고 싶지 않은 경우 ReCall을 높이고 Precision을 낮춘다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;4363&quot; data-start=&quot;4351&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;F1-score(조화평균)&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 data-end=&quot;4395&quot; data-start=&quot;4364&quot;&gt;전체적 성능 측정에 활용&lt;br /&gt;F1&amp;nbsp;점수는&amp;nbsp;정밀도와&amp;nbsp;재현율의&amp;nbsp;조화&amp;nbsp;평균로&amp;nbsp;계산되며,&amp;nbsp;두&amp;nbsp;값이&amp;nbsp;모두&amp;nbsp;높을&amp;nbsp;때&amp;nbsp;높은&amp;nbsp;값을&amp;nbsp;가집니다.&amp;nbsp;불균형&amp;nbsp;데이터&amp;nbsp;세트에서&amp;nbsp;모델&amp;nbsp;성능을&amp;nbsp;평가할&amp;nbsp;때&amp;nbsp;특히&amp;nbsp;유용하게&amp;nbsp;사용돼요.&lt;/li&gt;
&lt;li data-end=&quot;4395&quot; data-start=&quot;4364&quot;&gt;F1-Score&amp;nbsp;=&amp;nbsp;2&amp;nbsp;*&amp;nbsp;(Precision&amp;nbsp;*&amp;nbsp;Recall)&amp;nbsp;/&amp;nbsp;(Precision&amp;nbsp;+&amp;nbsp;Recall) &lt;br /&gt;0일경우 precision과 recall둘다 좋지 않음, 1일경우 둘 다 좋음.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;4395&quot; data-start=&quot;4364&quot; data-ke-size=&quot;size16&quot;&gt;정밀도&amp;middot;재현율의 조화평균&lt;br /&gt;&lt;b&gt;불균형 데이터에서 특히 중요&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1764126844736&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from sklearn.metrics import f1_score
f1_score(y_test, y_pred_1)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;4416&quot; data-start=&quot;4397&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ROC Curve (수신자&amp;nbsp;조작&amp;nbsp;특성&amp;nbsp;곡선&amp;nbsp;Receiver&amp;nbsp;operating&amp;nbsp;characteristic&amp;nbsp;Curve)&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;FPR = FP / (FP + TN)&lt;/li&gt;
&lt;li&gt;선 아래 면적(AUC)이 클수록 좋다&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1764120915404&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from sklearn.metrics import roc_curve, roc_auc_score

y_probas = lr_classifier.predict_proba(X_test)
y_scores = y_probas[:,1]

fpr, tpr, _ = roc_curve(y_test, y_scores)
auc = roc_auc_score(y_test, y_scores)

plt.plot(fpr, tpr, label=&quot;auc=&quot;+ &quot;{:.2f}&quot;.format(auc))
plt.legend(loc=4)
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC Curve')&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-end=&quot;4451&quot; data-start=&quot;4444&quot; data-ke-size=&quot;size26&quot;&gt;12. 용어 정리&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1)&amp;nbsp;회귀(Regression)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연속 값을 예측하는 문제 유형으로 출력값이 연속적인 수치일 때 이를 예측하는 모델을 통틀어 회귀 라고 부릅니다. &lt;br /&gt;X를 설명 변수로 하여 Y(연속 값)를 예측하는 통계 기법 전반을 지칭. &lt;br /&gt;ex) 기온 예측, 집 값 예측,&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;4481&quot; data-start=&quot;4453&quot; data-ke-size=&quot;size20&quot;&gt;2) 표준편차 (Standard Deviation)&lt;/h4&gt;
&lt;p data-end=&quot;4514&quot; data-start=&quot;4482&quot; data-ke-size=&quot;size16&quot;&gt;표준 편차는 데이터가 평균을 중심으로 얼마나 흩어져 있는지 나타내는 통계값이다.&lt;/p&gt;
&lt;h4 data-end=&quot;4535&quot; data-start=&quot;4516&quot; data-ke-size=&quot;size20&quot;&gt;3) 내적 행렬(Dot Product)&lt;/h4&gt;
&lt;p data-end=&quot;4594&quot; data-start=&quot;4536&quot; data-ke-size=&quot;size16&quot;&gt;왼쪽 행렬의 행 &amp;times; 오른쪽 행렬의 열을 곱해 더하는 연산&lt;br /&gt;벡터의 유사도, 신경망 계산 등에 널리 쓰임&lt;/p&gt;
&lt;p data-end=&quot;4594&quot; data-start=&quot;4536&quot; data-ke-size=&quot;size16&quot;&gt;이&amp;nbsp;결과는&amp;nbsp;각&amp;nbsp;원소별&amp;nbsp;내적의&amp;nbsp;합이&amp;nbsp;되는&amp;nbsp;스칼라&amp;nbsp;값이&amp;nbsp;됩니다.&amp;nbsp;이는&amp;nbsp;벡터의&amp;nbsp;내적을&amp;nbsp;행렬&amp;nbsp;곱셈으로&amp;nbsp;확장한&amp;nbsp;것이라고&amp;nbsp;볼&amp;nbsp;수&amp;nbsp;있습니다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;4614&quot; data-start=&quot;4596&quot; data-ke-size=&quot;size20&quot;&gt;4) 사분위수 (Quartile)&lt;/h4&gt;
&lt;p data-end=&quot;4662&quot; data-start=&quot;4615&quot; data-ke-size=&quot;size16&quot;&gt;데이터를 크기 순으로 나열한 뒤, 전체를 4등분하는 경계값을 말합니다.&lt;br /&gt;가장 많이 사용되는 세 가지 사분위수로는 제1사분위수(Q1), 제2사분위수(Q2), 그리고 제3사분위수(Q3)가 있습니다.&lt;/p&gt;
&lt;p data-end=&quot;4662&quot; data-start=&quot;4615&quot; data-ke-size=&quot;size16&quot;&gt;25%(1사분위 수), 50%(2사분위 수), 75%(3사분위 수)&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5) Loss function(손실 함수)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서 cost(비용 함수)보다 많이 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의&amp;nbsp;데이터에&amp;nbsp;대해&amp;nbsp;모델이&amp;nbsp;얼마나&amp;nbsp;틀렸는지&amp;nbsp;측정하는&amp;nbsp;함수&amp;nbsp;(개별&amp;nbsp;데이터&amp;nbsp;오차)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반드시 미분 가능 해야함. (대표적 모델 선형회귀, 이진분류, 다중분류)&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;6) Cost function(비용 함수)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체&amp;nbsp;데이터에&amp;nbsp;대한&amp;nbsp;손실(Loss)의&amp;nbsp;평균&amp;nbsp;또는&amp;nbsp;합&amp;nbsp;오류&amp;nbsp;(전체&amp;nbsp;데이터의&amp;nbsp;평균&amp;nbsp;오차)&amp;nbsp;가설이&amp;nbsp;얼마나&amp;nbsp;틀렸는지&amp;nbsp;측정&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;7) Objective function (목적&amp;nbsp;함수)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최적화&amp;nbsp;대상이&amp;nbsp;되는&amp;nbsp;함수(cost&amp;nbsp;포함&amp;nbsp;더&amp;nbsp;큰&amp;nbsp;개념)&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;8) 엔트로피(Entropy)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 데이터 집합의 혼잡도(데이터가 분류되지 않고 얼마나 뒤죽박죽이냐). 즉, 우리가 가지고 있지 않은 정보의 양을 의미 &lt;br /&gt;혼잡도에 따라 엔트로피가 높으면 혼잡도 높음, 엔트로피가 낮으면 혼잡도가 낮음&amp;nbsp; &lt;br /&gt;(0~1)로 표현하며 혼잡도는 0으로 갈 수록 낮아짐.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;9) 데이터 세트 분할&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;과적합(Overfitting)&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;Traing Data에 비해 Test data의 Error율이 높게 나타나는 경우를 과적합 이라고 한다.&lt;/li&gt;
&lt;li&gt;&amp;nbsp;과적합된 모델을 High Variance Model이라고함.&lt;/li&gt;
&lt;li&gt;&amp;nbsp;학습 Data와 실제 Data 분포의 차이에 의한 error가 발생할 수 있다. &lt;b&gt;이를 줄이기 위해 충분한 복잡도를 갖는 모델의 DataSet의 크기를 늘려야한다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;과소적합(UnOverfitting)&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;과소적합된 모델을 High Bias Model이라고함.&lt;/li&gt;
&lt;li&gt;Approximation&amp;nbsp;Model(근사치로&amp;nbsp;만든&amp;nbsp;함수)과&amp;nbsp;true&amp;nbsp;function(실제&amp;nbsp;데이터)&amp;nbsp;의&amp;nbsp;차이에&amp;nbsp;의한&amp;nbsp;error가&amp;nbsp;발생한다.&amp;nbsp;**이를&amp;nbsp;줄이기&amp;nbsp;위해&amp;nbsp;데이터가&amp;nbsp;충분히&amp;nbsp;있는&amp;nbsp;상태에서&amp;nbsp;모델의&amp;nbsp;Complexity(복잡도)를&amp;nbsp;올린다.**&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;현실에서의&amp;nbsp;데이터&amp;nbsp;무한&amp;nbsp;수집과&amp;nbsp;true&amp;nbsp;function도&amp;nbsp;알&amp;nbsp;수&amp;nbsp;없을&amp;nbsp;때&amp;nbsp;간접적&amp;nbsp;방법&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Traing&amp;nbsp;set과&amp;nbsp;Testing&amp;nbsp;set은&amp;nbsp;섞이면&amp;nbsp;안되고&amp;nbsp;동일한&amp;nbsp;분포를&amp;nbsp;유지해야&amp;nbsp;한다.(검증할&amp;nbsp;데이터를&amp;nbsp;Traing&amp;nbsp;set에&amp;nbsp;넣을&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;Cross Validation(교차검증)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Traing set이 부족하여 testSet으로 나누기 불 충분할경우 사용하는 방법.&lt;/li&gt;
&lt;li&gt;traing&amp;nbsp;set을&amp;nbsp;여러개의&amp;nbsp;sub-set(ex.&amp;nbsp;하나의sub-set에&amp;nbsp;5개의&amp;nbsp;데이터로&amp;nbsp;나눴다면&amp;nbsp;4개는&amp;nbsp;train,&amp;nbsp;1개는&amp;nbsp;test)으로&amp;nbsp;나누고&amp;nbsp;각&amp;nbsp;모델을&amp;nbsp;이&amp;nbsp;sub-set의&amp;nbsp;조합으로&amp;nbsp;훈련시키고&amp;nbsp;나머지&amp;nbsp;부분으로&amp;nbsp;검증하는&amp;nbsp;방법이다.&lt;/li&gt;
&lt;li&gt;Precision / Recall / F1-Score&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;dataset split - 3split&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터&amp;nbsp;set을&amp;nbsp;3개로&amp;nbsp;나누어&amp;nbsp;학습시키는&amp;nbsp;방법이&amp;nbsp;가장&amp;nbsp;좋다고&amp;nbsp;한다. &lt;br /&gt;-&amp;nbsp;training&amp;nbsp;:&amp;nbsp;학습용 &lt;br /&gt;-&amp;nbsp;validation&amp;nbsp;:&amp;nbsp;hyper&amp;nbsp;parameter&amp;nbsp;tuning&amp;nbsp;용(ex.&amp;nbsp;cross&amp;nbsp;validation&amp;nbsp;등&amp;nbsp;사용) &lt;br /&gt;-&amp;nbsp;test&amp;nbsp;:&amp;nbsp;test용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;10) 경사하강법&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오차(loss)가 최소가 되는 지점을 찾기 위해, 기울기(gradient)가 내려가는 방향으로 파라미터를 조금씩 업데이트하는 최적화 방법&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;11) 아웃라이어(Outlier)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아웃라이어(Outlier)는&amp;nbsp;데이터&amp;nbsp;전체의&amp;nbsp;패턴에서&amp;nbsp;벗어난&amp;nbsp;극단적인&amp;nbsp;값을&amp;nbsp;의미하며,&amp;nbsp;통계적으로는&amp;nbsp;평균이나&amp;nbsp;중앙값에서&amp;nbsp;크게&amp;nbsp;떨어진&amp;nbsp;값으로&amp;nbsp;정의됩니다.&amp;nbsp;아웃라이어는&amp;nbsp;데이터&amp;nbsp;분석&amp;nbsp;결과를&amp;nbsp;왜곡할&amp;nbsp;수&amp;nbsp;있어,&amp;nbsp;이를&amp;nbsp;탐지하고&amp;nbsp;처리하는&amp;nbsp;것이&amp;nbsp;중요합니다.&amp;nbsp;대표적인&amp;nbsp;탐지&amp;nbsp;방법으로는&amp;nbsp;Z-score와&amp;nbsp;IQR(사분위수&amp;nbsp;범위)을&amp;nbsp;이용한&amp;nbsp;통계적&amp;nbsp;방법과,&amp;nbsp;밀도&amp;nbsp;기반&amp;nbsp;알고리즘인&amp;nbsp;DBScan,&amp;nbsp;트리&amp;nbsp;계열&amp;nbsp;알고리즘인&amp;nbsp;Isolation&amp;nbsp;Forest&amp;nbsp;등이&amp;nbsp;있습니다&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;12) 차원의 저주&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차원의 저주는 특성(Feature) 수가 많아질수록 데이터 공간이 기하급수적으로 커져 데이터가 희소해지는 현상으로 PCA를 사용하여 차원을 줄여 이 문제를 완화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;13) gradient (기울기)&lt;/h4&gt;
&lt;p data-end=&quot;563&quot; data-start=&quot;530&quot; data-ke-size=&quot;size16&quot;&gt;손실 함수가 가중치가 바뀔 때 얼마나 변하는지 알려주는 값 으로 파라미터를 어느 방향으로 얼마나 바꿔야 loss가 줄어드는지 알려주는 미분값이다. 즉 모델이 데이터를 보고 어디가 틀렸고 어떤 방향으로 고쳐야 하는지 수치로 표현한 신호이다. &lt;br /&gt;&lt;br /&gt;Gradient가&amp;nbsp;너무&amp;nbsp;크면&amp;nbsp;학습&amp;nbsp;불안정&amp;nbsp;하며&amp;nbsp;0과&amp;nbsp;가까울&amp;nbsp;수록&amp;nbsp;학습&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI</category>
      <author>usingsystem</author>
      <guid isPermaLink="true">https://usingsystem.tistory.com/577</guid>
      <comments>https://usingsystem.tistory.com/577#entry577comment</comments>
      <pubDate>Thu, 27 Nov 2025 16:01:06 +0900</pubDate>
    </item>
    <item>
      <title>[C#서버] Akka.net과 Cluster Part.5 적용해보자!</title>
      <link>https://usingsystem.tistory.com/557</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;그동안 공부했던 Akka.net과 IOCP 등 여러 기술을 조합하여 Messenger 서버를 개발해 보고 있습니다.&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;a href=&quot;https://github.com/tkddls3319/Akka.net-IOCP-Web-MessengerServer&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/tkddls3319/Akka.net-IOCP-Web-MessengerServer&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1737446144026&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - tkddls3319/Akka.net-IOCP-Web-MessengerServer: Akka.net과 IOCP를 결합한 채팅 서버 ( Cluster )&quot; data-og-description=&quot;Akka.net과 IOCP를 결합한 채팅 서버 ( Cluster ). Contribute to tkddls3319/Akka.net-IOCP-Web-MessengerServer development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/tkddls3319/Akka.net-IOCP-Web-MessengerServer&quot; data-og-url=&quot;https://github.com/tkddls3319/Akka.net-IOCP-Web-MessengerServer&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/0WrH9/hyX4x6bqFp/LaMbKkrpS4eF3DVRHUdfF1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/Dhqj4/hyX4mcuduK/QyetYSmj42fJRjfXTXJ1x1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/tkddls3319/Akka.net-IOCP-Web-MessengerServer&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/tkddls3319/Akka.net-IOCP-Web-MessengerServer&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/0WrH9/hyX4x6bqFp/LaMbKkrpS4eF3DVRHUdfF1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/Dhqj4/hyX4mcuduK/QyetYSmj42fJRjfXTXJ1x1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;GitHub - tkddls3319/Akka.net-IOCP-Web-MessengerServer: Akka.net과 IOCP를 결합한 채팅 서버 ( Cluster )&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Akka.net과 IOCP를 결합한 채팅 서버 ( Cluster ). Contribute to tkddls3319/Akka.net-IOCP-Web-MessengerServer development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.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;</description>
      <category>VisualStudio/C#서버</category>
      <author>usingsystem</author>
      <guid isPermaLink="true">https://usingsystem.tistory.com/557</guid>
      <comments>https://usingsystem.tistory.com/557#entry557comment</comments>
      <pubDate>Thu, 27 Nov 2025 16:00:41 +0900</pubDate>
    </item>
    <item>
      <title>[C#서버] Akka.net과 Cluster Part.4</title>
      <link>https://usingsystem.tistory.com/549</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Akka.NET 클러스터란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Akka.NET 클러스터는 &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;/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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Akka.NET 클러스터는 서버를 필요에 따라 쉽게 추가하거나 제거할 수 있습니다. 이를 통해 시스템은 수평으로 확장 가능하며, 클라이언트 수나 데이터 처리량이 증가해도 효율적으로 대응할 수 있습니다.&lt;/li&gt;
&lt;li&gt;각 서버는 클러스터 내에서 특정 역할을 맡습니다. 예를 들어, MMORPG 같은 게임에서는 &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;/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;/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;Akka.NET 클러스터는 중앙 관리 서버 없이도 동작할 수 있도록 설계되었습니다. 클러스터 리더(Cluster Leader)**는 각 서버 간의 상태를 관리하고, 노드 추가/제거 및 장애 감지 등을 자율적으로 수행합니다.&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;클러스터 내에서 각 서버가 연결될 수 있도록, 시드 노드(Seed Node)를 통해 초기 진입점을 설정합니다. 시드 노드는 클러스터 내의 모든 노드가 알고 있어야 하며, 이를 통해 클러스터에 동적으로 참여할 수 있습니다.&lt;/li&gt;
&lt;li&gt;서버 간의 연결 정보는 정적으로 구성 파일에 설정하거나, &lt;b&gt;서비스 디스커버리 도구&lt;/b&gt;(예: Consul, Kubernetes)를 통해 자동으로 관리할 수 있습니다.&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;클라이언트는 여러 서버와 동시에 연결하여, 다양한 기능을 동시에 이용할 수 있도록 합니다. 예를 들어, MMORPG에서는 클라이언트가 이동, 전투, 채팅 기능을 동시에 사용하기 위해 이동 서버, 전투 서버, 채팅 서버와 각각 연결됩니다.&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;클러스터는 Akka.NET의 &lt;b&gt;분산 데이터(Distributed Data)&lt;/b&gt; 기능을 사용하여 여러 서버에 데이터 저장을 분산하고 복제할 수 있습니다. 이를 통해 데이터 손실 위험을 줄이고, 높은 일관성을 유지할 수 있습니다.&lt;/li&gt;
&lt;li&gt;각 서버가 특정 기능을 맡아 처리하더라도 데이터가 필요할 때 분산 데이터 기능을 통해 접근하여 일관된 데이터를 제공받을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용 사례: MMORPG와 같은 대규모 시스템&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MMORPG에서는 각 서버가 다음과 같은 역할을 수행하며 협력합니다:&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;li&gt;&lt;b&gt;인벤토리 서버&lt;/b&gt;: 플레이어의 아이템과 인벤토리를 관리.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;클라이언트를 클러스터에 직접 연결해도 될 까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트를 Akka.NET 클러스터의 노드로 연결하는 것은 가능하지만, 일반적으로는 클러스터의 내부 노드로 추가하는 대신, 외부에서 API나 TCP/IP 소켓을 통해 통신하는 방식이 더 효율적입니다. 클라이언트를 클러스터 노드로 연결할 경우, 노드 수가 많아지면서 과부하가 발생할 수 있고, 네트워크 대역폭이 많이 소모될 수 있습니다. 또한, 클라이언트의 네트워크 연결이 불안정하거나 자주 끊길 경우, 클러스터의 안정성에 영향을 줄 수 있으며, 보안 정책을 철저히 관리해야 합니다. 따라서 클라이언트가 클러스터의 직접적인 노드로 참여하는 것보다는 클러스터 외부에서 필요한 데이터를 요청하거나 수신하는 방식이 안정적이고 관리가 용이합니다. 클라이언트를 클러스터에 연결할 때는 부하 관리, 연결 상태 유지, 보안 등을 주의 깊게 고려해야 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Akka.NET 클러스터의 용어&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;노드(Node)&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;시드 노드(Seed Node)&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;클러스터 리더(Cluster Leader)&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;Gossip Protocol&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;b&gt;장애 복구&lt;/b&gt; 및 상태 일관성 유지에 중요한 역할을 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;분산 데이터(Distributed Data)&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클러스터 내에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;데이터를 분산 저장 및 복제&lt;/b&gt;할 수 있도록 하는 기능입니다. 이를 통해 여러 노드가 동시에 일관된 데이터를 공유할 수 있으며, 장애가 발생해도 데이터 손실을 방지할 수 있습니다.&lt;/li&gt;
&lt;li&gt;분산 데이터는 데이터 일관성을 유지하면서도 높은 가용성을 보장합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;클러스터 분산 라우터(Cluster Aware Router)&lt;/b&gt;:&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;클러스터의 각 노드에 걸쳐 &lt;b&gt;메시지를 자동으로 분산&lt;/b&gt;시킬 수 있는 라우터입니다. 클러스터 내에서 자원을 최적화하여 부하를 고르게 분산시킵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;라우터는 클러스터의 상태를 인식하여, 장애가 발생한 노드로의 라우팅을 피하고, 정상 노드에 메시지를 전달합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;코디네이티드 셧다운(Coordinated Shutdown)&lt;/b&gt;:클러스터의 노드를 종료할 때, 모든 노드가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;안전하게 종료&lt;/b&gt;될 수 있도록 절차를 관리합니다. 이를 통해 데이터 손실이나 불완전한 종료를 방지하고, 모든 연결과 작업이 안전하게 마무리되도록 돕습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;역할(Role)&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;:클러스터 내 각 노드에 할당되는&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;역할&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;로, 특정 노드가 특정 작업을 수행할 수 있도록 지정합니다. 예를 들어, 노드에 &quot;이동&quot;, &quot;전투&quot;, &quot;채팅&quot; 등의 역할을 지정하여, 클러스터 내 기능을 분리하고 역할별로 작업을 분배할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;파트리셔닝(Partitioning)&lt;/b&gt;:클러스터 내에서 데이터를 여러 **구역(partition)**으로 나누어 관리하는 방식입니다. 이를 통해 클러스터는 데이터를 보다 효율적으로 분산하고, 특정 파트리션에 장애가 발생해도 전체 시스템이 영향을 덜 받도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;쿼럼(Quorum)&lt;/b&gt;:분산된 환경에서 특정 작업을 처리하기 위해 필요한 최소한의 &lt;b&gt;노드 수&lt;/b&gt;를 의미합니다. 쿼럼을 통해 분산 데이터에 대한 작업을 안정적으로 수행할 수 있습니다. 예를 들어, 데이터 복제나 특정 작업의 동의가 필요한 경우 쿼럼을 만족하는 노드들이 참여해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;피어(Peer)&lt;/b&gt;:클러스터 내의 &lt;b&gt;다른 노드들&lt;/b&gt;을 지칭하는 용어로, 각 노드는 피어로서 클러스터의 상태 정보 및 데이터를 공유하고, 필요한 경우 상호 간에 작업을 협력합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;리밸런싱(Rebalancing)&lt;/b&gt;:클러스터 내에서 &lt;b&gt;부하를 균등하게 분배&lt;/b&gt;하기 위해 데이터를 다른 노드로 이동시키는 작업입니다. 클러스터 상태에 따라 부하가 과중한 노드에서 상대적으로 가벼운 노드로 작업을 이동시켜, 자원을 최적화합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Gossip Protocol 프로토콜&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Akka.NET 클러스터의 Gossip 프로토콜은 클러스터 내 모든 노드가 주기적으로 상태 정보를 교환하여 클러스터 상태를 동기화하고, 장애를 감지하며 복구를 지원하는 핵심 통신 방법입니다. Gossip 프로토콜은 비동기적이고 확산적인 방식으로 작동하여, 특정 노드가 다른 노드 몇 개와 상태 정보를 주기적으로 교환하면, 그 정보가 다시 다른 노드로 퍼지면서 클러스터 전체에 전파됩니다. 이렇게 정보를 주기적으로 공유함으로써, 클러스터의 모든 노드는 시간이 지나면서 동일한 상태 정보를 갖게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gossip 프로토콜은 장애 감지와 자율적인 복구에 중요한 역할을 하며, 특정 노드가 응답하지 않거나 상태 업데이트가 없는 경우 장애로 간주하여 클러스터 전체에 이를 알립니다. 이를 통해 클러스터는 자율적으로 장애 노드를 감지하고, 이를 복구하거나 대체할 수 있도록 조정합니다. Gossip 프로토콜은 확장성이 뛰어나 대규모 클러스터에서도 효율적으로 작동하며, 장애가 발생해도 클러스터의 일관성과 안정성을 유지할 수 있게 해줍니다. Akka.NET 클러스터에서 Gossip 프로토콜은 클러스터가 안정적으로 운영될 수 있도록 상태를 지속적으로 관리하고, 클러스터 내에서 리더 선출, 노드 추가 및 제거 등의 조정 작업에도 도움을 줍니다.&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;&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;&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;참조&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://petabridge.com/cluster/lesson1.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://petabridge.com/cluster/lesson1.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1736292392296&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;Lesson 1 - Working with Akka.NET and Akka.Cluster&quot; data-og-description=&quot;Lesson 1 - Working with Akka.NET and Akka.Cluster&quot; data-og-host=&quot;petabridge.com&quot; data-og-source-url=&quot;https://petabridge.com/cluster/lesson1.html&quot; data-og-url=&quot;https://petabridge.com/cluster/lesson1.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bcB0w3/hyXWtKlFMh/HFtt0kGb3bFO5igzKNIMZK/img.png?width=669&amp;amp;height=648&amp;amp;face=0_0_669_648,https://scrap.kakaocdn.net/dn/xJSwl/hyXWxsoCX7/G4llNqxnsAW7Y8ViF709kk/img.png?width=768&amp;amp;height=484&amp;amp;face=0_0_768_484,https://scrap.kakaocdn.net/dn/vzDQy/hyXWrZ2Rc8/j8WTnEHxxpxSNByIn9wuN1/img.png?width=505&amp;amp;height=532&amp;amp;face=0_0_505_532&quot;&gt;&lt;a href=&quot;https://petabridge.com/cluster/lesson1.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://petabridge.com/cluster/lesson1.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bcB0w3/hyXWtKlFMh/HFtt0kGb3bFO5igzKNIMZK/img.png?width=669&amp;amp;height=648&amp;amp;face=0_0_669_648,https://scrap.kakaocdn.net/dn/xJSwl/hyXWxsoCX7/G4llNqxnsAW7Y8ViF709kk/img.png?width=768&amp;amp;height=484&amp;amp;face=0_0_768_484,https://scrap.kakaocdn.net/dn/vzDQy/hyXWrZ2Rc8/j8WTnEHxxpxSNByIn9wuN1/img.png?width=505&amp;amp;height=532&amp;amp;face=0_0_505_532');&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;Lesson 1 - Working with Akka.NET and Akka.Cluster&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Lesson 1 - Working with Akka.NET and Akka.Cluster&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;petabridge.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;a href=&quot;https://getakka.net/articles/clustering/cluster-overview.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://getakka.net/articles/clustering/cluster-overview.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1728366255894&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Akka.Cluster Overview | Akka.NET Documentation&quot; data-og-description=&quot;Akka.Cluster Overview What Is a &amp;quot;Cluster&amp;quot;? A cluster represents a fault-tolerant, elastic, decentralized peer-to-peer network of Akka.NET applications with no single point of failure or bottleneck. Akka.Cluster is the module that gives you the ability to c&quot; data-og-host=&quot;getakka.net&quot; data-og-source-url=&quot;https://getakka.net/articles/clustering/cluster-overview.html&quot; data-og-url=&quot;https://getakka.net/articles/clustering/cluster-overview.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/by1FRG/hyXecomunY/PLykFUfO7P81OgrKkFP2xk/img.png?width=676&amp;amp;height=548&amp;amp;face=0_0_676_548,https://scrap.kakaocdn.net/dn/RGj9l/hyXebXiyhR/bSAas2z8pBtp6ckrQ5NMZ1/img.png?width=592&amp;amp;height=548&amp;amp;face=0_0_592_548,https://scrap.kakaocdn.net/dn/oEeQI/hyXec9JBvA/Qy4rbs62gIBCqkRgtpSay0/img.png?width=549&amp;amp;height=554&amp;amp;face=0_0_549_554&quot;&gt;&lt;a href=&quot;https://getakka.net/articles/clustering/cluster-overview.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://getakka.net/articles/clustering/cluster-overview.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/by1FRG/hyXecomunY/PLykFUfO7P81OgrKkFP2xk/img.png?width=676&amp;amp;height=548&amp;amp;face=0_0_676_548,https://scrap.kakaocdn.net/dn/RGj9l/hyXebXiyhR/bSAas2z8pBtp6ckrQ5NMZ1/img.png?width=592&amp;amp;height=548&amp;amp;face=0_0_592_548,https://scrap.kakaocdn.net/dn/oEeQI/hyXec9JBvA/Qy4rbs62gIBCqkRgtpSay0/img.png?width=549&amp;amp;height=554&amp;amp;face=0_0_549_554');&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;Akka.Cluster Overview | Akka.NET Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Akka.Cluster Overview What Is a &quot;Cluster&quot;? A cluster represents a fault-tolerant, elastic, decentralized peer-to-peer network of Akka.NET applications with no single point of failure or bottleneck. Akka.Cluster is the module that gives you the ability to c&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;getakka.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://afsdzvcx123.tistory.com/entry/AkkaCluster-%EB%9E%80&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://afsdzvcx123.tistory.com/entry/AkkaCluster-%EB%9E%80&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1728371472954&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;Akka.Cluster 란?&quot; data-og-description=&quot;참조 https://getakka.net/articles/clustering/cluster-overview.html Cluster 란? 클러스터는 단일 실패 지점이나 병목현상이 없는 Akka.NET 애플리케이션의 내결함성, 탄력적, 분산형, peer to peer 네트워크를 나타냅니&quot; data-og-host=&quot;afsdzvcx123.tistory.com&quot; data-og-source-url=&quot;https://afsdzvcx123.tistory.com/entry/AkkaCluster-%EB%9E%80&quot; data-og-url=&quot;https://afsdzvcx123.tistory.com/entry/AkkaCluster-%EB%9E%80&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bv0KMs/hyXeiB8XSs/ZdlGAakHtVXKXWwkJ8gTo1/img.png?width=560&amp;amp;height=315&amp;amp;face=0_0_560_315,https://scrap.kakaocdn.net/dn/pEBrd/hyXd5is5fT/9VEURf97hJlOdyoq67B6dk/img.png?width=560&amp;amp;height=315&amp;amp;face=0_0_560_315,https://scrap.kakaocdn.net/dn/9Uq6J/hyXef6t4WE/Xe1ghVp2hmfTU0xL0AHUK1/img.png?width=682&amp;amp;height=585&amp;amp;face=0_0_682_585&quot;&gt;&lt;a href=&quot;https://afsdzvcx123.tistory.com/entry/AkkaCluster-%EB%9E%80&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://afsdzvcx123.tistory.com/entry/AkkaCluster-%EB%9E%80&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bv0KMs/hyXeiB8XSs/ZdlGAakHtVXKXWwkJ8gTo1/img.png?width=560&amp;amp;height=315&amp;amp;face=0_0_560_315,https://scrap.kakaocdn.net/dn/pEBrd/hyXd5is5fT/9VEURf97hJlOdyoq67B6dk/img.png?width=560&amp;amp;height=315&amp;amp;face=0_0_560_315,https://scrap.kakaocdn.net/dn/9Uq6J/hyXef6t4WE/Xe1ghVp2hmfTU0xL0AHUK1/img.png?width=682&amp;amp;height=585&amp;amp;face=0_0_682_585');&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;Akka.Cluster 란?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;참조 https://getakka.net/articles/clustering/cluster-overview.html Cluster 란? 클러스터는 단일 실패 지점이나 병목현상이 없는 Akka.NET 애플리케이션의 내결함성, 탄력적, 분산형, peer to peer 네트워크를 나타냅니&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;afsdzvcx123.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.slideshare.net/slideshow/akkanet-ndc2016/61378890#2&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.slideshare.net/slideshow/akkanet-ndc2016/61378890#2&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1728372059260&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Akka.NET 으로 만드는 온라인 게임 서버 (NDC2016)&quot; data-og-description=&quot;Akka.NET 으로 만드는 온라인 게임 서버 (NDC2016) - Download as a PDF or view online for free&quot; data-og-host=&quot;www.slideshare.net&quot; data-og-source-url=&quot;https://www.slideshare.net/slideshow/akkanet-ndc2016/61378890#2&quot; data-og-url=&quot;https://www.slideshare.net/slideshow/akkanet-ndc2016/61378890&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/d6Sauy/hyXd6VZLKD/gxWc60WnkFXTv6d9LQh8z1/img.jpg?width=640&amp;amp;height=360&amp;amp;face=0_0_640_360,https://scrap.kakaocdn.net/dn/b8UknA/hyXeeT3kqV/Mj1Ez47iJCX13btY89uXfk/img.jpg?width=640&amp;amp;height=360&amp;amp;face=0_0_640_360&quot;&gt;&lt;a href=&quot;https://www.slideshare.net/slideshow/akkanet-ndc2016/61378890#2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.slideshare.net/slideshow/akkanet-ndc2016/61378890#2&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/d6Sauy/hyXd6VZLKD/gxWc60WnkFXTv6d9LQh8z1/img.jpg?width=640&amp;amp;height=360&amp;amp;face=0_0_640_360,https://scrap.kakaocdn.net/dn/b8UknA/hyXeeT3kqV/Mj1Ez47iJCX13btY89uXfk/img.jpg?width=640&amp;amp;height=360&amp;amp;face=0_0_640_360');&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;Akka.NET 으로 만드는 온라인 게임 서버 (NDC2016)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Akka.NET 으로 만드는 온라인 게임 서버 (NDC2016) - Download as a PDF or view online for free&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.slideshare.net&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;</description>
      <category>VisualStudio/C#서버</category>
      <author>usingsystem</author>
      <guid isPermaLink="true">https://usingsystem.tistory.com/549</guid>
      <comments>https://usingsystem.tistory.com/549#entry549comment</comments>
      <pubDate>Thu, 27 Nov 2025 15:59:59 +0900</pubDate>
    </item>
    <item>
      <title>[C#서버] Akka.net과 Actor모델 Part.3</title>
      <link>https://usingsystem.tistory.com/548</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&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;Router&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Pool Router&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ActorSelect와 Route 비교&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;HOCON을 사용한 Router 설정&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비동기 actor간 메시지 전달 PipeTo와 ReceiveAsync&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비동기 호출 단순화 akka.Interfaced&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;액터 메세지 수신 교착 상태 방지와 ReceiveTimeout&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. 분산 메세지 전달 Router&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터는 다른 actor 그룹으로 메시지를 전달하는 메시징 허브 역할을 하는 특별한 종류의 actor 입니다. 라우터의 목적은 실제 작업을 수행할 배우들(즉, 라우티)을 통해 작업(메시지 스트림)을 분배하고 균형을 맞추는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터는 actor이지만 기존 actor와 다르게 한 번에 여러 메시지를 처리할 수 있습니다. 라우터의 목적은 메시지를 처리하는 것이 아니라, 다른 배우에게 전달하는 것입이기 때문에 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;풀 라우터 (Pool Router)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작업자 배우(라우티)를 생성하고 관리합니다.&lt;/li&gt;
&lt;li&gt;사용자가 라우터에 인스턴스 수를 제공하면 라우터가 작업자 배우를 자동으로 생성합니다.&lt;b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그룹 라우터 (Group Router)&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;라우터 생성 시 라우티의 ActorPaths를 지정하여 사용합니다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;라우터 구성 방식&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;절차적 구성 : 코드에서 직접 라우터를 구성하는 방법입니다.&lt;/li&gt;
&lt;li&gt;HOCON 구성 : HOCON을 사용하여 라우터 구성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;라우팅 전략에는 2가지가 존재한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 특정 상황 메세지&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 특정 요구사항이나 상황에 따라 동작하며, 예를 들어 모든 라우티에게 동일한 작업을 요구할 때 유용합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Broadcast 이 라우팅 전략에서는 라우터가 수신하는 모든 메시지를 모든 라우티에 전달합니다.&lt;/li&gt;
&lt;li&gt;Random 랜덤 라우팅 전략에서는 라우터가 메시지를 받을 때마다 무작위로 선택된 라우티에 메시지를 전달합니다.&lt;/li&gt;
&lt;li&gt;ConsistentHash 라우팅 전략에서는 라우터가 일관된 해싱을 사용하여 메시지의 데이터를 기반으로 라우티를 선택합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 부하 분산 라우팅&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 업을 라우티 간에 고르게 분산시켜 시스템의 효율성을 높입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RoundRobin: 라우티를 순환적으로 선택하여 메시지를 전송합니다. 부하가 비교적 균일하고, 모든 작업자가 비슷한 성능을 가질 때 유용합니다.&lt;/li&gt;
&lt;li&gt;SmallestMailbox (풀 라우터 전용) : 각 라우티의 메일박스에 있는 메시지 수를 기준으로 라우티를 선택합니다. 가장 적은 메시지를 보유한 라우티에 새로운 메시지를 전달합니다. 각 라우티의 메시지 큐가 과도하게 쌓이지 않도록 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;TailChopping: 이 전략은 메시지를 첫 번째 라우티에 전달한 후, 일정 시간 대기하여 응답을 기다립니다. 만약 첫 번째 라우티가 응답하지 않으면, 다음 라우티로 메시지를 전달합니다. 응답 시간이 중요한 시스템에서 사용됩니다. 예를 들어, 빠른 데이터 조회가 필요한 경우에 유용합니다.&lt;/li&gt;
&lt;li&gt;ResizableRouter (풀 라우터 전용) : 이 전략은 풀 라우터가 자동으로 라우티의 수를 조정할 수 있게 해줍니다. 시스템의 부하에 따라 라우티의 수를 늘리거나 줄이는 방식입니다. 사용자 수가 변동이 큰 서비스(예: 온라인 게임, 대규모 이벤트)에서 효과적입니다.&lt;/li&gt;
&lt;li&gt;ScatterGatherFirstCompleted: 모든 라우티에 메시지를 전송하고, 가장 먼저 응답한 라우티의 응답만을 받아서 원래 발신자에게 전달합니다. 규모 데이터 처리나, 신속한 결정이 필요한 상황에서 유용합니다. 예를 들어, 실시간 서비스에서 여러 소스에서 정보를 수집할 때 사용할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&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;Broadcast 메시지: 비 방송 라우터에게 전달하여 모든 라우티에 메시지를 전달하도록 만듭니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1727155659047&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;router.Tell(new Broadcast(&quot;ㅎㅇㅎㅇ&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;GetRoutees 메시지: 라우터의 라우티 목록을 반환합니다. 주로 디버깅에 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1727155671449&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;router.Tell(new GetRoutees());&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;PoisonPill 메시지: 수신한 배우를 즉시 종료합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1727155679152&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;router.Tell(PoisonPill.Instance);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;ActorSelect와 Route비교&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 99.1861%; height: 198px;&quot; border=&quot;1&quot; width=&quot;207&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 25px;&quot;&gt;
&lt;td style=&quot;height: 25px; width: 15.9942%;&quot; width=&quot;69&quot; height=&quot;45&quot;&gt;특성&lt;/td&gt;
&lt;td style=&quot;height: 25px; width: 46.5905%;&quot; width=&quot;69&quot;&gt;ACTORSELECT&lt;/td&gt;
&lt;td style=&quot;height: 25px; width: 59.181%;&quot; width=&quot;69&quot;&gt;ROUTE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 38px;&quot;&gt;
&lt;td style=&quot;height: 38px; width: 15.9942%;&quot; width=&quot;69&quot; height=&quot;113&quot;&gt;정의&lt;/td&gt;
&lt;td style=&quot;height: 38px; width: 46.5905%;&quot; width=&quot;69&quot;&gt;특정 패턴에 맞는 모든 배우를 선택하는 방법&lt;/td&gt;
&lt;td style=&quot;height: 38px; width: 59.181%;&quot; width=&quot;69&quot;&gt;메시지를 특정 라우티에 전달하는 방법&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 36px;&quot;&gt;
&lt;td style=&quot;height: 36px; width: 15.9942%;&quot; width=&quot;69&quot; height=&quot;136&quot;&gt;사용 목적&lt;/td&gt;
&lt;td style=&quot;height: 36px; width: 46.5905%;&quot; width=&quot;69&quot;&gt;여러 배우 중에서 특정 조건에 맞는 배우를 찾기 위해&lt;/td&gt;
&lt;td style=&quot;height: 36px; width: 59.181%;&quot; width=&quot;69&quot;&gt;메시지를 여러 라우티 간에 효율적으로 분산하기 위해&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 26px;&quot;&gt;
&lt;td style=&quot;height: 26px; width: 15.9942%;&quot; width=&quot;69&quot; height=&quot;136&quot;&gt;구현 방식&lt;/td&gt;
&lt;td style=&quot;height: 26px; width: 46.5905%;&quot; width=&quot;69&quot;&gt;ActorPaths 또는 ActorSelection을 사용하여 선택&lt;/td&gt;
&lt;td style=&quot;height: 26px; width: 59.181%;&quot; width=&quot;69&quot;&gt;라우터가 지정된 라우티에게 메시지를 전달&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 34px;&quot;&gt;
&lt;td style=&quot;height: 34px; width: 15.9942%;&quot; width=&quot;69&quot; height=&quot;136&quot;&gt;성능&lt;/td&gt;
&lt;td style=&quot;height: 34px; width: 46.5905%;&quot; width=&quot;69&quot;&gt;전체 배우 목록을 확인해야 하므로 상대적으로 느림&lt;/td&gt;
&lt;td style=&quot;height: 34px; width: 59.181%;&quot; width=&quot;69&quot;&gt;라우터를 통해 직접 메시지를 전달하여 빠름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 29px;&quot;&gt;
&lt;td style=&quot;height: 29px; width: 15.9942%;&quot; width=&quot;69&quot; height=&quot;136&quot;&gt;유지 관리&lt;/td&gt;
&lt;td style=&quot;height: 29px; width: 46.5905%;&quot; width=&quot;69&quot;&gt;동적으로 변경되는 배우의 상태를 반영하기 어려움&lt;/td&gt;
&lt;td style=&quot;height: 29px; width: 59.181%;&quot; width=&quot;69&quot;&gt;라우터가 상태를 관리하여 동적인 부하 분산 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;height: 10px; width: 15.9942%;&quot; width=&quot;69&quot; height=&quot;136&quot;&gt;응답 방식&lt;/td&gt;
&lt;td style=&quot;height: 10px; width: 46.5905%;&quot; width=&quot;69&quot;&gt;응답이 개별 배우에서 오며, 라우터를 우회함&lt;/td&gt;
&lt;td style=&quot;height: 10px; width: 59.181%;&quot; width=&quot;69&quot;&gt;응답이 라우터를 통해 돌아오며, 라우터가 중재함&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Group Router 사용예제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;액터를 직접적으로 생성하지 않는다. 그렇기 때문에 자동으로 액터를 생성하고 감독가능하며 동적으로 크기를 조정하고 자식 액터의 에러가 발생해도 시스템의 방식이 일관되게 유지되는 등 다양한 장점이 있는 &lt;b&gt;pool router를 주로사용한다.&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 84px;&quot; border=&quot;1&quot; width=&quot;207&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 38px;&quot;&gt;
&lt;td style=&quot;height: 38px;&quot; width=&quot;69&quot; height=&quot;136&quot;&gt;특성&lt;/td&gt;
&lt;td style=&quot;height: 38px;&quot; width=&quot;69&quot;&gt;BroadcastGroup&lt;/td&gt;
&lt;td style=&quot;height: 38px;&quot; width=&quot;69&quot;&gt;GroupRouter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot; height=&quot;136&quot;&gt;목적&lt;/td&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot;&gt;모든 라우티에 메시지를 전달&lt;/td&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot;&gt;미리 정의된 라우티 중 하나에게 메시지를 전달&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot; height=&quot;136&quot;&gt;메시지 전달 방식&lt;/td&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot;&gt;모든 라우티에 동일한 메시지를 전달&lt;/td&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot;&gt;선택된 하나의 라우티에게만 메시지를 전달&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 26px;&quot;&gt;
&lt;td style=&quot;height: 26px;&quot; width=&quot;69&quot; height=&quot;136&quot;&gt;사용 사례&lt;/td&gt;
&lt;td style=&quot;height: 26px;&quot; width=&quot;69&quot;&gt;상태 변경 알림, 긴급 알림&lt;/td&gt;
&lt;td style=&quot;height: 26px;&quot; width=&quot;69&quot;&gt;데이터베이스 쿼리 요청, 부하 분산&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;BroadcastGroup 예시&lt;/p&gt;
&lt;pre id=&quot;code_1727156162751&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using Akka.Actor;
using Akka.Routing;
using System.Collections.Generic;

public class Worker : ReceiveActor
{
    public Worker()
    {
        // 메시지를 수신할 때 처리할 로직
        Receive&amp;lt;string&amp;gt;(msg =&amp;gt; HandleMessage(msg));
    }

    private void HandleMessage(string msg)
    {
        // 수신한 메시지를 출력
        Console.WriteLine($&quot;Worker {Self.Path.Name} received: {msg}&quot;);
    }
}

class Program
{
    static void Main(string[] args)
    {
        var system = ActorSystem.Create(&quot;MyActorSystem&quot;);

        // 여러 작업자 생성
        var workers = new List&amp;lt;IActorRef&amp;gt;
        {
            system.ActorOf(Props.Create&amp;lt;Worker&amp;gt;(), &quot;worker1&quot;),
            system.ActorOf(Props.Create&amp;lt;Worker&amp;gt;(), &quot;worker2&quot;),
            system.ActorOf(Props.Create&amp;lt;Worker&amp;gt;(), &quot;worker3&quot;)
        };

        // BroadcastGroup 생성
        var broadcastGroup = system.ActorOf(Props.Empty.WithRouter(new BroadcastGroup(workers)));

        // 모든 작업자에게 메시지 전송
        broadcastGroup.Tell(&quot;Hello, everyone!&quot;);

        // 시스템 종료
        Console.ReadLine();
        system.Terminate();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Pool Router 사용예제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;액터를 여러 개의 인스턴스로 생성하고, 이러한 인스턴스들 사이에 메시지를 분산시키는 데 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;풀 라우터를 설정할 때 생성할 액터 인스턴스수를 지정하는 매개변수로 NrOfInstances가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;풀 라우터와 NrOfInstances는 함께 사용되어 Akka.NET에서 효율적인 액터 관리를 가능하게 합니다. 풀 라우터는 메시지를 처리할 액터 인스턴스를 관리하고, NrOfInstances는 이 인스턴스의 수를 정의하여 시스템의 부하 분산과 성능을 최적화합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1727237734784&quot; class=&quot;haxe&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;var router = Context.ActorOf(Props.Empty.WithRouter(
    new RoundRobinPool(5) // NrOfInstances 5개의 액터 인스턴스를 생성하여 라우팅
));&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;p data-ke-size=&quot;size16&quot;&gt;풀 라우터는 자신의 경로(routee)로 사용되는 액터들을 직접 생성하여 자식 액터로 관리합니다. 이 의미는 풀 라우터가 이러한 자식 액터들을 자동으로 감독(Supervise)하고 상태를 감시(DeathWatch)한다는 것입니다. 기본적으로 발생하는 오류는 부모 액터로 전파되어, 풀 라우터가 재시작되고 자식 액터들도 함께 재시작됩니다. 이는 시스템의 안정성을 높이고, 오류 발생 시 일관된 처리를 가능하게 합니다. 필요에 따라 감독 전략을 설정하여 이러한 동작을 커스터마이즈할 수 있습니다.&lt;/p&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;: 자식 액터(routee)에서 오류가 발생하면, 이 에러는 풀 라우터의 부모 액터로 전달됩니다.&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;size18&quot;&gt;&lt;b&gt;풀라우팅 사용법&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. Smallest Mailbox Router&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SmallestMailbox 라우터는 각 액터의 메일박스(즉, 메시지 큐)에 있는 메시지 수를 기반으로 라우팅합니다. 이 라우터는 현재 대기 중인 &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;pre id=&quot;code_1727237205315&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var router = Context.ActorOf(Props.Empty.WithRouter(
    new SmallestMailboxPool(5) // 5개의 액터 인스턴스를 생성하고, 큐가 가장 작은 액터에 메시지를 전송
));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2. Resizable Router&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Resizable Router는 &lt;b&gt;필요에 따라 액터의 수를 동적으로 늘리거나 줄일 수 있는 라우터입니다.&lt;/b&gt; 시스템의 부하 변화에 유연하게 대응할 수 있도록 설계되어 있습니다. 시스템의 부하 변화에 유연하게 대응할 수 있도록 설계되어 있으며, 액터의 수를 동적으로 조정하여 리소스를 효율적으로 관리할 수 있습니다.&amp;nbsp;&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;pre id=&quot;code_1727237218726&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var router = Context.ActorOf(Props.Empty.WithRouter(
    new ResizablePool(5) // 초기 5개의 액터 인스턴스를 생성하고, 필요에 따라 수를 조정
));&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. 풀 라우터와 그룹 라우터의 차이점 &lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;풀 라우터는 라우트를 생성하고 관리하여 자신이 생성한 액터 인스턴스의 부모가 되며, 이를 통해 생명주기 관리와 상태 모니터링이 가능합니다. 그룹 라우터는 외부에서 주어진 경로를 기반으로 하여 신뢰성이 낮고 고정된 라우트를 관리합니다. 이러한 차이로 인해 &lt;b&gt;풀 라우터는 일반적으로 더 안전하고 효율적인 선택이 됩니다. &lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 97px;&quot; border=&quot;1&quot; width=&quot;207&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 15px;&quot;&gt;
&lt;td style=&quot;height: 15px; width: 17.2093%;&quot; width=&quot;69&quot; height=&quot;91&quot;&gt;특징&lt;/td&gt;
&lt;td style=&quot;height: 15px; width: 40.4651%;&quot; width=&quot;69&quot;&gt;풀 라우터 (Pool Router)&lt;/td&gt;
&lt;td style=&quot;height: 15px; width: 42.093%;&quot; width=&quot;69&quot;&gt;그룹 라우터 (Group Router)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 52px;&quot;&gt;
&lt;td style=&quot;height: 52px; width: 17.2093%;&quot; width=&quot;69&quot; height=&quot;227&quot;&gt;라우트 관리&lt;/td&gt;
&lt;td style=&quot;height: 52px; width: 40.4651%;&quot; width=&quot;69&quot;&gt;풀 라우터는 자신의 라우트(액터 인스턴스)를 생성하고 관리합니다. 라우트의 부모가 됩니다.&lt;/td&gt;
&lt;td style=&quot;height: 52px; width: 42.093%;&quot; width=&quot;69&quot;&gt;외부에서 주어진 ActorPaths를 기반으로 라우트를 관리합니다. 라우트의 부모가 아닙니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;height: 10px; width: 17.2093%;&quot; width=&quot;69&quot; height=&quot;159&quot;&gt;신뢰성&lt;/td&gt;
&lt;td style=&quot;height: 10px; width: 40.4651%;&quot; width=&quot;69&quot;&gt;액터 인스턴스를 직접 관리하여 더 안전하고 신뢰할 수 있습니다.&lt;/td&gt;
&lt;td style=&quot;height: 10px; width: 42.093%;&quot; width=&quot;69&quot;&gt;라우트의 상태를 알지 못해 신뢰성이 떨어집니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;height: 10px; width: 17.2093%;&quot; width=&quot;69&quot; height=&quot;204&quot;&gt;라우트 수동 조정&lt;/td&gt;
&lt;td style=&quot;height: 10px; width: 40.4651%;&quot; width=&quot;69&quot;&gt;라우트 풀의 크기를 동적으로 조정할 수 있습니다. 액터를 추가하거나 제거할 수 있습니다.&lt;/td&gt;
&lt;td style=&quot;height: 10px; width: 42.093%;&quot; width=&quot;69&quot;&gt;설정된 이후 라우트의 수가 고정되며 조정할 수 없습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;height: 10px; width: 17.2093%;&quot; width=&quot;69&quot; height=&quot;181&quot;&gt;라우트 이름 지정&lt;/td&gt;
&lt;td style=&quot;height: 10px; width: 40.4651%;&quot; width=&quot;69&quot;&gt;라우트의 이름을 제어할 수 없으며, 라우터를 통해서만 통신할 수 있습니다.&lt;/td&gt;
&lt;td style=&quot;height: 10px; width: 42.093%;&quot; width=&quot;69&quot;&gt;액터의 경로를 알고 있어 직접적으로 특정 액터와 통신할 수 있습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;5. Hocon을 사용한 Routers설정&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HOCON(Human-Optimized Config Object Notation)은 Akka.NET의 다양한 설정을 쉽게 관리할 수 있는 구성 형식입니다. 라우터를 설정할 때 HOCON을 사용하면 코드와 구성의 분리를 통해 가독성을 높일 수 있습니다. HOCON을 사용하면 원격 배포와 액터의 행동을 코드 수정 없이 구성 변경만으로 조정할 수 있어, 유연성과 유지보수성을 크게 향상시킵니다.&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;그룹라우터인 broadcastPool을 사용할 때 nr-of-instances가 무시되지만 설정이 무엇인지 명확히 할 수 있는 유용한 주석이된다. 또한 풀라우터가 있을 경우 &lt;b&gt;코드에서 변경없이 사용할 수있다. 기준을 만드는 느낌.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1727249349445&quot; class=&quot;nix&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;akka {
	actor{
	  deployment{
	   /myRouter{
	      router = broadcast-pool
	      nr-of-instances = 3
	    }
	  }
   }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;액터를 생성할 때 WithRouter 메서드를 사용해야 라우터가 설정됩니다. 만약 구성 파일에서 정의한 라우터를 사용하고 싶다면 FromConfig 클래스를 활용할 수 있습니다. 이 클래스는 Akka.NET에게 해당 액터에 대한 라우터 사양을 구성에서 찾도록 지시합니다. 예를 들어, FromConfig.Instance를 사용하면 구성에서 정의한 라우터를 그대로 사용할 수 있습니다. 이를 통해 코드 수정 없이도 구성 파일만으로 액터의 동작을 쉽게 변경할 수 있습니다. &lt;b&gt;즉 액터 라우터 설정의 재사용성&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1727249373250&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;akka.actor.deployment {
/router1 {
    router = round-robin-pool
        resizer {
            enabled = on
            lower-bound = 2
            upper-bound = 3
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Hocon을 사용한 설정 사용방법 &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FromConfig.Instance는 Akka.NET에서 라우터를 정의할 때 HOCON 설정에서 라우터 정의를 가져오는 데 사용되는 특수한 객체입니다. 이를 사용하면 코드에서 라우터를 명시적으로 정의하지 않고도 HOCON 파일에 설정된 대로 라우터를 인스턴스화할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1727249727791&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;akka {
  actor {
    deployment {
      /router1 {
        router = consistent-hashing-pool
        nr-of-instances = 3
        virtual-nodes-factor = 17
      }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1727249734711&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var router1 = MyActorSystem.ActorOf(Props.Create(() =&amp;gt; new FooActor()).WithRouter(FromConfig.Instance), &quot;router1&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;6. 비동기 actor간 메시지 전달 ( PipeTo와 ReceiveAsync )&lt;/b&gt;&lt;/h2&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;1. ReceiveAsync&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동기 작업을 현재 액터에서 처리하면서 송신자에게 결과를 직접 전달할 때 유용하지만, 액터 메일박스의 차단으로 인해 상태 관리가 어려울 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1727399082229&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public MyActor()
{
    ReceiveAsync&amp;lt;SomeMessage&amp;gt;(async some =&amp;gt; {
        // 비동기 작업을 안전하게 사용할 수 있습니다.
        await SomeAsyncIO(some.Data);
        Sender.Tell(new EverythingIsAllOK());
    });
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2. PipeTo&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PipeTo는 Akka.NET에서 &lt;b&gt;메시지를 한 액터에서 다른 액터로 전달할 때 사용하는 메서드입니다.&lt;/b&gt; 비동기 작업의 결과를 다른 액터로 안전하게 전달하고, 액터 간의 메시지 흐름을 명확하게 유지하는 데 유리합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1727239794861&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class WorkerActor : ReceiveActor
{
    public WorkerActor()
    {
        Receive&amp;lt;string&amp;gt;(message =&amp;gt;
        {
            // 비동기 작업 수행
            var resultTask = PerformWorkAsync(message);
            // 결과를 다른 액터에게 전달
            resultTask.PipeTo(Sender);
        });
    }

    private async Task&amp;lt;string&amp;gt; PerformWorkAsync(string input)
    {
        await Task.Delay(1000); // 1초 대기
        return $&quot;Processed: {input}&quot;;
    }
}

// 결과를 처리할 액터
public class ResultActor : ReceiveActor
{
    public ResultActor()
    {
        Receive&amp;lt;string&amp;gt;(result =&amp;gt;
        {
            Console.WriteLine(result);
        });
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3. akka.interfaced&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터페이스를 통한 비동기 호출을 &lt;b&gt;단순화하고, 타입 안전성을 제공&lt;/b&gt;하는 데 초점이 있습니다. &lt;b&gt;액터 &lt;span style=&quot;color: #ee2323;&quot;&gt;외부&lt;/span&gt;에서 액터의 메서드를 비동기적으로 호출&lt;/b&gt;하고 그 응답을 기다릴 때 유용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Akka.Interfaced의 메서드는 기본적으로 &lt;b&gt;Task나 Task&amp;lt;T&amp;gt;로 반환&lt;/b&gt;되어 비동기 처리를 수행합니다. 호출하는 측에서는 await 키워드를 통해 액터의 응답을 기다리거나, 비동기 호출을 이어갈 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1728373004255&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface ICalculator : IInterfacedActor
{
    Task&amp;lt;int&amp;gt; Add(int a, int b);
}
public class CalculatorActor : InterfacedActor, ICalculator
{
    public Task&amp;lt;int&amp;gt; Add(int a, int b)
    {
        return Task.FromResult(a + b);
    }
}

//액터호출
var actorRef = actorSystem.ActorOf&amp;lt;CalculatorActor&amp;gt;();
var calculator = actorRef.Cast&amp;lt;ICalculator&amp;gt;();

// 비동기적으로 호출
int sum = await calculator.Add(5, 3);
Console.WriteLine($&quot;Sum: {sum}&quot;);  // 출력: Sum: 8&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;PipeTo vs ReceiveAsync&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;PipeTo 패턴이 액터 내부에서 비동기 작업을 수행하는 데 더 선호됩니다.&lt;/b&gt; 그 이유는 PipeTo 패턴은 무엇이 발생하고 있는지를 명확하게 나타내기 때문에, 코드의 가독성이 높아집니다. 이는 코드 유지 보수 시 도움이 됩니다. ReceiveAsync에서 await를 사용하면 액터의 메일박스가 계속해서 차단되며, 이는 액터가 &quot;한 번에 하나의 메시지&quot;를 처리한다는 보장을 준수하는 데 도움이 됩니다. 즉, PipeTo를 사용하면 액터의 상태를 안전하게 유지할 수 있습니다.&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;즉 ReceiveAsync보다 PipeTo가 더 선호되는 이유는 비동기 작업의 흐름을 더 명확하게 하고, 액터의 스레드 안전성을 보다 잘 보장하기 때문입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 129px;&quot; border=&quot;1&quot; width=&quot;207&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 45px;&quot;&gt;
&lt;td style=&quot;height: 45px; width: 20.814%;&quot; width=&quot;69&quot; height=&quot;45&quot;&gt;특징&lt;/td&gt;
&lt;td style=&quot;height: 45px; width: 38.8372%;&quot; width=&quot;69&quot;&gt;PipeTo&lt;/td&gt;
&lt;td style=&quot;height: 45px; width: 40.1163%;&quot; width=&quot;69&quot;&gt;ReceiveAsync&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 24px;&quot;&gt;
&lt;td style=&quot;height: 24px; width: 20.814%;&quot; width=&quot;69&quot; height=&quot;159&quot;&gt;사용 목적&lt;/td&gt;
&lt;td style=&quot;height: 24px; width: 38.8372%;&quot; width=&quot;69&quot;&gt;비동기 작업의 결과를 다른 액터에 전달할 때 사용&lt;/td&gt;
&lt;td style=&quot;height: 24px; width: 40.1163%;&quot; width=&quot;69&quot;&gt;비동기 메시지 처리 및 송신자에게 결과를 직접 전달할 때 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;height: 10px; width: 20.814%;&quot; width=&quot;69&quot; height=&quot;204&quot;&gt;가독성&lt;/td&gt;
&lt;td style=&quot;height: 10px; width: 38.8372%;&quot; width=&quot;69&quot;&gt;명시적이며 코드의 흐름을 쉽게 이해할 수 있음&lt;/td&gt;
&lt;td style=&quot;height: 10px; width: 40.1163%;&quot; width=&quot;69&quot;&gt;상대적으로 덜 명시적일 수 있으며, 복잡한 흐름에서는 이해하기 어려울 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;height: 10px; width: 20.814%;&quot; width=&quot;69&quot; height=&quot;227&quot;&gt;액터 메일박스 관리&lt;/td&gt;
&lt;td style=&quot;height: 10px; width: 38.8372%;&quot; width=&quot;69&quot;&gt;액터의 메일박스가 차단되지 않으며, 메시지 처리를 안전하게 보장&lt;/td&gt;
&lt;td style=&quot;height: 10px; width: 40.1163%;&quot; width=&quot;69&quot;&gt;await로 인해 메일박스가 차단될 수 있으며, &quot;한 번에 하나의 메시지&quot; 보장을 유지함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;height: 10px; width: 20.814%;&quot; width=&quot;69&quot; height=&quot;136&quot;&gt;비동기 작업 결과 처리&lt;/td&gt;
&lt;td style=&quot;height: 10px; width: 38.8372%;&quot; width=&quot;69&quot;&gt;다른 액터로 결과를 전달&lt;/td&gt;
&lt;td style=&quot;height: 10px; width: 40.1163%;&quot; width=&quot;69&quot;&gt;현재 액터에서 결과를 처리하고 송신자에게 직접 전달&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;height: 10px; width: 20.814%;&quot; width=&quot;69&quot; height=&quot;181&quot;&gt;스레드 전환&lt;/td&gt;
&lt;td style=&quot;height: 10px; width: 38.8372%;&quot; width=&quot;69&quot;&gt;비동기 작업의 결과를 다른 스레드에서 처리할 수 있음&lt;/td&gt;
&lt;td style=&quot;height: 10px; width: 40.1163%;&quot; width=&quot;69&quot;&gt;비동기 작업이 같은 스레드에서 처리되므로 상태를 쉽게 유지할 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;height: 10px; width: 20.814%;&quot; width=&quot;69&quot; height=&quot;204&quot;&gt;예외 처리&lt;/td&gt;
&lt;td style=&quot;height: 10px; width: 38.8372%;&quot; width=&quot;69&quot;&gt;비동기 작업의 결과가 다른 액터로 전달되기 때문에 예외 처리가 분리될 수 있음&lt;/td&gt;
&lt;td style=&quot;height: 10px; width: 40.1163%;&quot; width=&quot;69&quot;&gt;비동기 작업 중 발생하는 예외는 현재 액터에서 처리해야 함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;height: 10px; width: 20.814%;&quot; width=&quot;69&quot; height=&quot;113&quot;&gt;유지 관리&lt;/td&gt;
&lt;td style=&quot;height: 10px; width: 38.8372%;&quot; width=&quot;69&quot;&gt;결과를 다른 액터로 전달하기 때문에 더 유연함&lt;/td&gt;
&lt;td style=&quot;height: 10px; width: 40.1163%;&quot; width=&quot;69&quot;&gt;송신자에게 직접 결과를 전달하므로 덜 유연함&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;PipeTo vs Akka.Interfaced&lt;/h3&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;Akka.Interfaced&lt;/b&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;PipeTo&lt;/b&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;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 135px;&quot; border=&quot;1&quot; width=&quot;207&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 45px;&quot;&gt;
&lt;td style=&quot;height: 45px;&quot; width=&quot;69&quot; height=&quot;45&quot;&gt;특징&lt;/td&gt;
&lt;td style=&quot;height: 45px;&quot; width=&quot;69&quot;&gt;Akka.Interfaced&lt;/td&gt;
&lt;td style=&quot;height: 45px;&quot; width=&quot;69&quot;&gt;PipeTo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot; height=&quot;136&quot;&gt;주요 목적&lt;/td&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot;&gt;인터페이스 기반으로 액터와의 상호작용을 단순화&lt;/td&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot;&gt;비동기 작업의 결과를 액터에게 직접 전달하여 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot; height=&quot;181&quot;&gt;사용 사례&lt;/td&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot;&gt;외부에서 액터의 메서드를 비동기 호출하고 응답을 기다릴 때 사용&lt;/td&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot;&gt;액터 내부에서 비동기 작업의 결과를 메시지로 수신하여 처리할 때 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot; height=&quot;147&quot;&gt;비동기 처리 방식&lt;/td&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot;&gt;Task 또는 Task&amp;lt;T&amp;gt; 반환을 통해 비동기 응답을 직접 대기&lt;/td&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot;&gt;작업 결과를 PipeTo(Self) 또는 PipeTo(다른 액터)로 전달&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot; height=&quot;204&quot;&gt;코드 표현 방식&lt;/td&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot;&gt;await를 통해 일반 메서드 호출처럼 비동기 응답 대기&lt;/td&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot;&gt;액터가 메시지 수신자로서 결과를 받고, 비동기 작업 중에도 다른 메시지 처리 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot; rowspan=&quot;2&quot; width=&quot;69&quot; height=&quot;227&quot;&gt;주요 장점&lt;/td&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot;&gt;- 타입 안전성을 제공&lt;/td&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot;&gt;- 액터가 비동기 작업 결과를 자연스럽게 수신 및 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot; height=&quot;91&quot;&gt;- 가독성 높은 메서드 호출 방식&lt;/td&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot;&gt;- 병렬 처리 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot; height=&quot;204&quot;&gt;적합한 상황&lt;/td&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot;&gt;액터의 비동기 메서드를 외부에서 호출하며 타입 검사 및 간결한 코드가 필요할 때&lt;/td&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot;&gt;액터가 비동기 작업의 결과를 기다리지 않고 다른 작업을 병행하며 결과를 처리할 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot; height=&quot;113&quot;&gt;타입 안전성&lt;/td&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot;&gt;인터페이스를 통해 타입 안전성이 높음&lt;/td&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot;&gt;일반 메시지 전달 방식으로, 타입 검사는 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 10px;&quot;&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot; height=&quot;159&quot;&gt;응답 처리 위치&lt;/td&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot;&gt;외부 호출자 (외부에서 액터의 응답을 기다림)&lt;/td&gt;
&lt;td style=&quot;height: 10px;&quot; width=&quot;69&quot;&gt;액터 자신 또는 다른 액터 (결과를 수신하여 메시지로 처리함)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;7. 교착 상태 방지와 ReceiveTimeout&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ReceiveTimeout은 액터가 메시지를 수신하지 않을 때 자동으로 타임아웃을 설정할 수 있는 기능입니다. 이 기능을 사용하면 액터가 일정 시간 동안 메시지를 받지 않으면 자동으로 타임아웃 이벤트를 발생시켜 특정 작업을 수행할 수 있습니다.&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;span&gt;&amp;nbsp;&lt;/span&gt;ReceiveTimeout메시지보다 먼저 actor 사서함에 도착할 수 있습니다. 이 경우 다른 메시지가 해당 메시지보다 먼저 처리되어&lt;span&gt;&amp;nbsp;&lt;/span&gt;ReceiveTimeout무효화됩니다.&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;: 액터가 Context.SetReceiveTimeout(TimeSpan)을 호출하여 타임아웃을 설정합니다. 이 경우, 액터는 지정된 시간 동안 메시지를 수신하지 않을 경우 ReceiveTimeout 메시지를 수신하게 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1727654228626&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ReceiveTimeout을 5초로 설정
Context.SetReceiveTimeout(TimeSpan.FromSeconds(5));
// TimeOut 취소
Context.SetReceiveTimeout(null);
//5초 동안 메세지를 받지 못 한다면 자기자신에게 ReceiveTimeout 메세지
Receive&amp;lt;ReceiveTimeout&amp;gt;(timeout =&amp;gt;
{
});&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;&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;&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;&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;&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;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참조&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/petabridge/akka-bootcamp/blob/master/src/Unit-3/README.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/petabridge/akka-bootcamp/blob/master/src/Unit-3/README.md&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1727142807064&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;akka-bootcamp/src/Unit-3/README.md at master &amp;middot; petabridge/akka-bootcamp&quot; data-og-description=&quot;Self-paced training course to learn Akka.NET fundamentals from scratch - petabridge/akka-bootcamp&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/petabridge/akka-bootcamp/blob/master/src/Unit-3/README.md&quot; data-og-url=&quot;https://github.com/petabridge/akka-bootcamp/blob/master/src/Unit-3/README.md&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bLRYjX/hyW6zjPC0f/mSKiUrdx1Kgir6Ug7oxzz1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/owfpE/hyW6CAQvsM/80QyjRBDImhfDmyznlnRn1/img.png?width=819&amp;amp;height=447&amp;amp;face=0_0_819_447&quot;&gt;&lt;a href=&quot;https://github.com/petabridge/akka-bootcamp/blob/master/src/Unit-3/README.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/petabridge/akka-bootcamp/blob/master/src/Unit-3/README.md&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bLRYjX/hyW6zjPC0f/mSKiUrdx1Kgir6Ug7oxzz1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/owfpE/hyW6CAQvsM/80QyjRBDImhfDmyznlnRn1/img.png?width=819&amp;amp;height=447&amp;amp;face=0_0_819_447');&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;akka-bootcamp/src/Unit-3/README.md at master &amp;middot; petabridge/akka-bootcamp&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Self-paced training course to learn Akka.NET fundamentals from scratch - petabridge/akka-bootcamp&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.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;</description>
      <category>VisualStudio/C#서버</category>
      <author>usingsystem</author>
      <guid isPermaLink="true">https://usingsystem.tistory.com/548</guid>
      <comments>https://usingsystem.tistory.com/548#entry548comment</comments>
      <pubDate>Thu, 27 Nov 2025 15:59:54 +0900</pubDate>
    </item>
    <item>
      <title>[C#서버] Akka.net과 Actor모델 Part.2</title>
      <link>https://usingsystem.tistory.com/547</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;주요내용&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;액터 메시지 처리 스레드 관리 Dispatcher&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Akka.Net 애플리케이션 설정 HOCON(Human-Optimized&amp;nbsp;Config&amp;nbsp;Object&amp;nbsp;Notation)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ReceiveActor&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;메세지 예약 Scheduler&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;퍼블리시 구독 (pub-sub) 패턴&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;런타임 액터 동작 전환 BecomeStacked와 UnbecomStacked&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;액터 동작 전환과 메세지 임시저장 Stash&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. Dispatcher ( 액터 메시지 처리 스레드 관리 )&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;액터를 사용할 때, 메시지가 액터에 도달하는 과정은 매우 중요합니다. 여기서 핵심 역할을 하는 것이 바로 &lt;b&gt;Dispatcher&lt;/b&gt;입니다. Dispatcher는 액터의 메일박스에서 메시지를 꺼내어 액터가 실제로 작업을 수행하는 OnReceive() 메서드로 전달하는 중개자입니다. 쉽게 말해, 액터와 스레드 사이의 연결 고리라고 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 Dispatcher는 액터가 메시지를 처리하는 데 필요한 스레드를 관리합니다. 여러 액터가 동시에 메시지를 수신하고 처리할 수 있도록 도와줍니다. 예를 들어, 사용자가 UI에서 여러 작업을 동시에 수행하는 경우, Dispatcher는 각각의 액터가 독립적으로 메시지를 처리할 수 있게 합니다. 이를 통해 사용자 경험이 매끄럽고 반응성이 좋아지게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dispatcher의 종류는 다음과 같습니다:&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;ThreadPoolDispatcher&lt;/b&gt;: 기본 형태로, CLR의 스레드 풀을 기반으로 합니다. 최대한 많은 액터가 동시에 실행될 수 있도록 하여, 높은 성능을 제공합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SynchronizedDispatcher&lt;/b&gt;: UI와 관련된 작업을 수행할 때 유용합니다. UI 스레드에서 작업을 할 수 있도록 메시지를 스케줄링하여, UI 요소를 안전하게 업데이트할 수 있게 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SingleThreadDispatcher&lt;/b&gt;: 여러 액터가 하나의 스레드에서 실행됩니다. 특정 상황에서 유용하게 사용될 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Dispatcher 설정 ( 2가지 존재 )&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;App.config 또는 HOCON&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Dispatcher의 기본 설정은 일반적으로 App.config 파일이나 HOCON 구성 파일을 통해 정의됩니다. 예를 들어, akka.actor.synchronized-dispatcher를 설정하면 기본 Dispatcher로 사용할 수 있도록 정의할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;WithDispatcher 사용&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 액터가 자신의 Dispatcher를 오버라이드하고 싶을 때 WithDispatcher 메서드를 사용하여 코드에서 직접 설정할 수 있습니다. 이 방법은 특정 액터가 별도의 Dispatcher를 사용해야 할 때 유용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1727070142257&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Program.ChartActors.ActorOf(
                Props.Create(() =&amp;gt; new ButtonToggleActor(_coordinatorActor, btnCpu, CounterType.Cpu, false))
                    .WithDispatcher(&quot;akka.actor.synchronized-dispatcher&quot;));&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. HOCON&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;HOCON&lt;/b&gt;(Human-Optimized Config Object Notation)은 Akka.NET 애플리케이션의 설정을 정의하는 데 사용되는 강력한 구성 형식입니다. HOCON은 사람이 읽기 쉽고 쓰기 쉽게 설계되어 복잡한 설정을 명확하게 표현할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HOCON의 가장 큰 장점 중 하나는 가독성입니다. XML 같은 복잡한 형식에 비해 HOCON은 직관적이고 이해하기 쉽습니다. 주석을 추가할 수 있어 각 설정에 대한 설명을 달기 쉽고, 주석은 # 기호로 시작합니다. 이렇게 하면 코드에 대한 해설이나 추가 정보를 제공할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HOCON은 유연한 구문을 지원합니다. 예를 들어, 문자열을 따옴표 없이 쓸 수 있고 여러 줄에 걸쳐 작성할 수 있습니다. 이러한 유연성 덕분에 설정을 쉽게 수정하고 관리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강력한 타입 지원도 HOCON의 특징입니다. HOCON에서 반환되는 값은 강력한 타입으로, 예를 들어 정수나 문자열 등을 쉽게 가져올 수 있습니다. 이를 통해 코드에서 설정 값을 안전하게 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1727065305671&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
&amp;lt;configuration&amp;gt;
    &amp;lt;!-- 구성 섹션 정의 --&amp;gt;
    &amp;lt;configSections&amp;gt;
        &amp;lt;!-- 'akka'라는 이름의 섹션을 정의하고, Akka의 HOCON 설정을 처리할 수 있도록 지정 --&amp;gt;
        &amp;lt;section name=&quot;akka&quot; type=&quot;Akka.Configuration.Hocon.AkkaConfigurationSection, Akka&quot; /&amp;gt;
    &amp;lt;/configSections&amp;gt;

    &amp;lt;!-- Akka.NET 관련 설정 --&amp;gt;
    &amp;lt;akka&amp;gt;
        &amp;lt;hocon&amp;gt;
            &amp;lt;![CDATA[
            akka {
                actor {
                    deployment {
                        # ChartingActor를 구성하기 위한 설정
                        /charting {
                            # ChartingActor가 UI 스레드에서 실행되도록 동기화된 Dispatcher 사용
                            dispatcher = akka.actor.synchronized-dispatcher 
                        }
                    }
                }
            }
            ]]&amp;gt;
        &amp;lt;/hocon&amp;gt;
    &amp;lt;/akka&amp;gt;
&amp;lt;/configuration&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Dispatcher와의 관계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HOCON은 Akka.NET의 다양한 구성 요소를 설정하는 데 사용되며, 특히 액터의 &lt;b&gt;Dispatcher&lt;/b&gt;를 구성하는 데 중요한 역할을 합니다. Dispatcher는 액터의 메일박스에서 메시지를 처리하는 방식과 스레드를 관리하는 요소입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HOCON을 사용하면 Dispatcher의 종류나 설정을 손쉽게 조정할 수 있습니다. 예를 들어, HOCON 설정 파일에서 액터의 Dispatcher를 SynchronizedDispatcher로 지정하여 UI 스레드에서 실행되도록 할 수 있습니다. 이렇게 하면 UI와의 상호작용을 매끄럽게 할 수 있고, 스레드 간의 동기화 문제를 피할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. ReceiveActor ( UntypedActor보다 더 간결하고 명확한 패턴 )&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ReceiveActor는 UntypedActor를 기반으로 하여, 더 복잡한 패턴 매칭 및 메시지 처리를 쉽게 할 수 있도록 도와줍니다. ReceiveActor는 OnReceive() 메서드를 가지지 않습니다. 대신, Receive 메시지 핸들러를 ReceiveActor 생성자 내에서 직접 연결해야 합니다. 또는 생성자에서 호출되는 메서드 내에서도 가능합니다.&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;Receive&amp;lt;T&amp;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;Receive&amp;lt;T&amp;gt;(Action&amp;lt;T&amp;gt; handler)&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메시지가 T 타입일 때만 핸들러를 실행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Receive&amp;lt;T&amp;gt;(Predicate&amp;lt;T&amp;gt; pred, Action&amp;lt;T&amp;gt; handler)&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메시지가 T 타입이고, 조건 함수가 true를 반환할 때만 핸들러를 실행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Receive&amp;lt;T&amp;gt;(Action&amp;lt;T&amp;gt; handler, Predicate&amp;lt;T&amp;gt; pred)&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;Receive(Type type, Action&amp;lt;object&amp;gt; handler)&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;Receive(Type type, Action&amp;lt;object&amp;gt; handler, Predicate&amp;lt;object&amp;gt; pred)&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;ReceiveAny()&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 객체 인스턴스를 수용하는 핸들러입니다. 이전의 더 구체적인 Receive() 핸들러로 처리되지 않은 메시지를 처리하는 데 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1727065418057&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class StringActor : ReceiveActor
{
    public StringActor()
    {
        // 이제 예상대로 작동합니다.
        Receive&amp;lt;string&amp;gt;(s =&amp;gt; s.StartsWith(&quot;AkkaDotNetSuccess&quot;), s =&amp;gt;
        {
            // 문자열 처리
        });

        Receive&amp;lt;string&amp;gt;(s =&amp;gt; s.StartsWith(&quot;AkkaDotNet&quot;), s =&amp;gt;
        {
            // 문자열 처리
        });
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000; font-size: 1.62em; letter-spacing: -1px;&quot;&gt;4. Scheduler ( 메시지를 나중에 보내기 위한 스케줄러 사용하기 )&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Akka.NET의 &lt;b&gt;ActorSystem.Scheduler&lt;/b&gt;는 액터에 미래에 메시지를 보내도록 예약할 수 있는 단일 인스턴스입니다. 스케줄러는 한 번만 전송되는 메시지와 반복적으로 전송되는 메시지 모두를 처리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스케줄러 사용 방법&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;한 번만 전송되는 메시지&lt;/b&gt;: ScheduleTellOnce 메서드를 사용하여 특정 시간 후에 메시지를 전송할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1727066314157&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;system.Scheduler.ScheduleTellOnce(TimeSpan.FromMinutes(30), someActor, someMessage, ActorRefs.Nobody);&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;: ScheduleTellRepeatedly 메서드를 사용하여 정해진 간격으로 메시지를 반복적으로 전송할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1727066322953&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;system.Scheduler.ScheduleTellRepeatedly(TimeSpan.FromMinutes(30), TimeSpan.FromMinutes(30), someActor, someMessage, ActorRefs.Nobody);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;메시지 취소c&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예약된 메시지를 취소할 필요가 있을 경우, ICancelable을 사용하여 쉽게 취소할 수 있습니다. 메시지를 예약할 때 취소 지원을 추가하면, 나중에 Cancel() 메서드를 호출하여 해당 메시지를 취소할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;정확성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스케줄러의 정확성은 일반적으로 충분하지만, 높은 부하가 걸리면 예상보다 조금 늦게 실행될 수 있습니다. 15밀리초 이하의 정밀도가 필요한 경우에는 적합하지 않습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1727070468944&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    Context.System.Scheduler.ScheduleTellRepeatedly(TimeSpan.FromMilliseconds(250), TimeSpan.FromMilliseconds(250), Self,
        new GatherMetrics(), Self, _cancelPublishing);
}&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;size18&quot;&gt;&lt;b&gt; 퍼블리시-구독(pub-sub) 패턴 &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;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;div&gt;
&lt;pre id=&quot;code_1727066737116&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Subscribe { public IActorRef Subscriber; }
public class Unsubscribe { public IActorRef Subscriber; }
public class PublishMessage { public string Content; }​&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;퍼블리셔 액터 구현&lt;/b&gt;: 퍼블리셔 액터는 구독자의 목록을 관리하고, 메시지를 발행하는 기능을 포함합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1727066751350&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class PubActor : ReceiveActor
{
    private HashSet&amp;lt;IActorRef&amp;gt; _subscribers;

    public PubActor()
    {
        _subscribers = new HashSet&amp;lt;IActorRef&amp;gt;();

        Receive&amp;lt;Subscribe&amp;gt;(sub =&amp;gt;
        {
            _subscribers.Add(sub.Subscriber);
        });

        Receive&amp;lt;Unsubscribe&amp;gt;(unsub =&amp;gt;
        {
            _subscribers.Remove(unsub.Subscriber);
        });

        Receive&amp;lt;PublishMessage&amp;gt;(message =&amp;gt;
        {
            foreach (var subscriber in _subscribers)
            {
                subscriber.Tell(message); // 구독자에게 메시지 전달
            }
        });
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;구독자 액터 구현&lt;/b&gt;: 구독자는 관심 있는 메시지를 처리합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1727066768407&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class SubActor : ReceiveActor
{
    public SubActor()
    {
        Receive&amp;lt;PublishMessage&amp;gt;(message =&amp;gt;
        {
            Console.WriteLine($&quot;Received message: {message.Content}&quot;);
        });
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퍼블리셔와 구독자를 연결하고 메시지를 발행하는 예시입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1727066787685&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var system = ActorSystem.Create(&quot;MySystem&quot;);

var pubActor = system.ActorOf&amp;lt;PubActor&amp;gt;(&quot;pubActor&quot;);
var subActor1 = system.ActorOf&amp;lt;SubActor&amp;gt;(&quot;subActor1&quot;);
var subActor2 = system.ActorOf&amp;lt;SubActor&amp;gt;(&quot;subActor2&quot;);

// 구독 등록
pubActor.Tell(new Subscribe { Subscriber = subActor1 });
pubActor.Tell(new Subscribe { Subscriber = subActor2 });

// 메시지 발행
pubActor.Tell(new PublishMessage { Content = &quot;Hello, Subscribers!&quot; });&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;5.런타임에 액터 동작 전환&lt;span&gt;&amp;nbsp;&lt;/span&gt;BecomeStacked와 UnbecomeStacked&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스위치 가능한 행동은 액터 모델의 핵심 특성 중 하나로, 액터가 처리하는 메시지에 따라 그 행동을 동적으로 변경할 수 있는 기능입니다. 이 기능은 유한 상태 기계(Finite State Machines) 구현이나 다른 메시지에 따라 액터의 메시지 처리 방식을 변경하는 데 매우 유용합니다. &lt;b&gt;만약 행동 전환을 했다면 이전에 등록한 recive 행동은 모두 무효화된다.&lt;/b&gt;&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;/b&gt;: 액터가 새로운 동작으로 전환할 때 BecomeStacked를 호출하여 현재 동작을 스택에 추가하고 새로운 동작으로 변경합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;이전 동작 복원&lt;/b&gt;: UnbecomeStacked를 호출하면 스택에서 마지막 동작을 제거하고 이전 동작으로 돌아갑니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ReceiveActor에서의 행동 전환&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;Become&lt;/b&gt;:현재의 수신 메서드를 새로운 메서드로 교체합니다. 이전 행동은 스택에 남지 않으며, 새로운 행동만 활성화됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;BecomeStacked&lt;/b&gt;:새로운 행동을 스택에 추가하고, 이전 행동을 유지합니다. 여러 개의 행동을 쌓아 두고 필요할 때 쉽게 전환할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;UnbecomeStacked&lt;/b&gt;:스택에서 마지막 행동을 제거하고 이전 행동으로 복원합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1727075409943&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class MyReceiveActor : ReceiveActor
{
    public MyReceiveActor()
    {
        // 초기 행동 설정
        Become(Initial);
    }

    private void Initial()
    {
        Receive&amp;lt;Start&amp;gt;(msg =&amp;gt;
        {
            // 행동 전환
            Become(Processing);
        });
    }

    private void Processing()
    {
        Receive&amp;lt;Complete&amp;gt;(msg =&amp;gt;
        {
            // 작업 완료 처리
            UnbecomeStacked(); // 이전 행동으로 복원
        });

        Receive&amp;lt;OtherMessage&amp;gt;(msg =&amp;gt;
        {
            // 다른 메시지 처리
        });
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;UntypedActor에서&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Context.Become(Receive rec)- 현재 수신 루프를 새로운 동작으로 교체합니다. 기존의 행동 스택은 제거됩니다.&lt;/li&gt;
&lt;li&gt;Context.BecomeStacked(Receive rec)- 새로운 동작을 스택에 추가하면서 이전 동작을 유지합니다. 여러 개의 행동을 쌓을 수 있습니다.&lt;/li&gt;
&lt;li&gt;Context.UnbecomeStacked()- 스택에서 마지막 동작을 제거하고 이전 동작으로 복원합니다. 이 메서드는 BecomeStacked와 함께 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1727075373085&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class MyUntypedActor : UntypedActor
{
    protected override void OnReceive(object message)
    {
        if (message is Start)
        {
            // 행동 전환
            Context.Become(Processing);
        }
    }
    private void Processing(object message)
    {
        if (message is Complete)
        {
            // 작업 완료 처리
            Context.UnbecomeStacked(); // 이전 행동으로 복원
        }
        else
        {
            // 다른 메시지 처리
        }
    }
}&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;6. 메세지 임시 저장 Stash&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;앞서 알아본 런타임에 액터&lt;/b&gt; &lt;b&gt;행동 전환&lt;/b&gt;(behavior transition)은 액터가 다양한 상태를 관리하는 데 중요한 역할을 합니다. 액터가 상태를 전환할 때, 이전 상태에서 수신된 메시지를 처리할 수 없는 상황이 발생할 수 있습니다. 예를 들어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;온라인 쇼핑몰에서 주문을 처리하는 OrderActor라는 actor가 있다고 가정해봅시다. 이 actor는 두 가지 주요 상태를 가집니다:&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;/ol&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;OrderActor가 대기 상태일 때, 새로운 주문 메시지(PlaceOrder)를 받으면 이 메시지를 즉시 처리할 수 있습니다. 주문 처리 로직을 실행하고, 고객에게 주문 완료 메시지를 보내는 방식으로 작동합니다.&lt;/p&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;그러나 OrderActor가 현재 주문을 처리 중인 경우, 새로운 주문 메시지를 받았을 때는 이를 처리할 수 없습니다. 이때 stash 기능을 사용하여 처리할 수 없는 메시지를 임시로 저장하게 됩니다. 즉, actor는 새로운 주문 메시지를 받았지만, 처리할 수 없는 상황이기 때문에 이 메시지를 stash에 저장합니다.&lt;/p&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;주문 처리가 완료되면 ProcessingComplete라는 메시지를 수신하게 되며, 이때 stash에 저장된 메시지들을 다시 꺼내서 처리할 수 있는 상태로 전환됩니다. 이제 OrderActor는 대기 중이던 새로운 주문 메시지를 처리할 수 있게 됩니다.&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;/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;: Stash를 사용하면 액터가 특정 조건을 충족할 때까지 메시지를 대기시킬 수 있으므로, 복잡한 비즈니스 로직을 보다 유연하게 처리할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;행동 전환과 함께 사용&lt;/b&gt;: Stash는 행동 전환과 함께 사용되며, 특정 행동에서 다른 행동으로 전환할 때, 처리할 수 없는 메시지를 임시로 저장할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Stash 메서드&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;액터&lt;span&gt;&amp;nbsp;&lt;/span&gt;OnReceive나&lt;span&gt;&amp;nbsp;&lt;/span&gt;Receive&amp;lt;T&amp;gt;핸들러 내부에서 호출하여&lt;span&gt;&amp;nbsp;&lt;/span&gt;Stash.Stash()현재 메시지를 맨 위에 놓을 수 있습니다&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;Stash()&lt;/b&gt;: 현재 메시지를 스택에 저장합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt; Unstash(): &lt;/b&gt;저장된 메시지를 다시 처리할 수 있도록 꺼내는 기능입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;UnstashAll()&lt;/b&gt;: 스택에 저장된 모든 메시지를 다시 처리할 수 있도록 꺼냅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Stash 유형 정의 인터페이스&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1. IWithBoundedStash&lt;/p&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;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2. IWithUnboundedStash&lt;/p&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;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1727139816073&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 주문 메시지 클래스
public class PlaceOrder
{
    public string OrderId { get; }
    public PlaceOrder(string orderId)
    {
        OrderId = orderId;
    }
}

// 처리 완료 메시지 클래스
public class ProcessingComplete { }

// 주문 처리 Actor
public class OrderActor : ReceiveActor, IWithUnboundedStash
{
    // IStash 프로퍼티를 통해 stash 사용
    public IStash Stash { get; private set; }

    public OrderActor()
    {
        // 초기 상태에서 주문 메시지를 받는 경우
        Receive&amp;lt;PlaceOrder&amp;gt;(order =&amp;gt;
        {
            // 주문 처리 로직
            Console.WriteLine($&quot;주문 {order.OrderId}을 처리합니다.&quot;);
            // 상태를 처리 중 상태로 변경
            Become(Processing);
        });
    }

    // 처리 중 상태
    private void Processing()
    {
        // 새로운 주문 메시지를 받으면 stash에 저장
        Receive&amp;lt;PlaceOrder&amp;gt;(order =&amp;gt;
        {
            Console.WriteLine($&quot;처리 중이므로 주문 {order.OrderId}을 stash합니다.&quot;);
            Stash.Stash(); // 메시지를 stash에 저장
        });

        // 주문 처리가 완료되었음을 알리는 메시지 수신
        Receive&amp;lt;ProcessingComplete&amp;gt;(complete =&amp;gt;
        {
            Console.WriteLine(&quot;주문 처리가 완료되었습니다. stash에서 메시지를 처리합니다.&quot;);
            Stash.UnstashAll(); // stash에서 보관된 메시지를 처리할 수 있는 상태로 전환
        });

        // stash에서 꺼내온 메시지를 처리
        Receive&amp;lt;PlaceOrder&amp;gt;(order =&amp;gt;
        {
            // 보관된 주문 처리 로직
            Console.WriteLine($&quot;stash에서 주문 {order.OrderId}을 처리합니다.&quot;);
        });
    }
}&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;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/petabridge/akka-bootcamp/blob/master/src/Unit-2/README.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/petabridge/akka-bootcamp/blob/master/src/Unit-2/README.md&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1726184237441&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;akka-bootcamp/src/Unit-2/README.md at master &amp;middot; petabridge/akka-bootcamp&quot; data-og-description=&quot;Self-paced training course to learn Akka.NET fundamentals from scratch - petabridge/akka-bootcamp&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/petabridge/akka-bootcamp/blob/master/src/Unit-2/README.md&quot; data-og-url=&quot;https://github.com/petabridge/akka-bootcamp/blob/master/src/Unit-2/README.md&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bGiX9f/hyW2UHKYvs/pHdswBUXt4ABikhXlPNnZk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/petabridge/akka-bootcamp/blob/master/src/Unit-2/README.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/petabridge/akka-bootcamp/blob/master/src/Unit-2/README.md&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bGiX9f/hyW2UHKYvs/pHdswBUXt4ABikhXlPNnZk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;akka-bootcamp/src/Unit-2/README.md at master &amp;middot; petabridge/akka-bootcamp&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Self-paced training course to learn Akka.NET fundamentals from scratch - petabridge/akka-bootcamp&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>VisualStudio/C#서버</category>
      <author>usingsystem</author>
      <guid isPermaLink="true">https://usingsystem.tistory.com/547</guid>
      <comments>https://usingsystem.tistory.com/547#entry547comment</comments>
      <pubDate>Thu, 27 Nov 2025 15:59:49 +0900</pubDate>
    </item>
    <item>
      <title>[C#서버] Akka.net과 Actor모델 Part.1</title>
      <link>https://usingsystem.tistory.com/545</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&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;Akka.net 이란?&lt;/li&gt;
&lt;li&gt;액터관리와 &lt;b&gt;ActorSystem&amp;nbsp;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;액터간 메세지 전달 &lt;b&gt;Tell&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;액터참조와 &lt;b&gt;IActorRef&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;액터생성과 &lt;b&gt;Props&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;액터간 감독과 예외처리 &lt;b&gt;supervision&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;주소로 액터 찾기&lt;b&gt; ActorSelection&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ActorSelection과 IActorRef 차이점&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;액터 라이프사이&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Akka.net이란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.NET 플랫폼에서 사용할 수 있는 오픈 소스 프레임워크로, Actor 모델을 구현하여 병렬성과 분산 시스템을 쉽게 구축할 수 있도록 도와줍니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Actor모델 이란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Actor 모델은 큰 프로젝트에서 여러 작업을 동시에 처리하거나, 많은 사용자가 동시에 접속하는 시스템을 만들 때 매우 유용한 패턴입니다. 이 모델을 이해하기 위해 먼저, 우리가 흔히 사용하는 객체 지향 프로그래밍(OOP)과 비교해볼게요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 지향 프로그래밍에서는 메서드를 호출하고, 객체의 상태를 변경하는 것이 기본입니다. 그러나 동시성 문제를 처리하려면 여러 스레드가 공유하는 메모리와 이를 관리하는 락(lock)이라는 장치를 사용해야 합니다. 하지만 락은 복잡할 뿐만 아니라, 잘못 사용하면 시스템이 멈추거나 성능이 떨어지는 문제가 발생할 수 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 Actor 모델이 등장합니다. Actor 모델은 이러한 문제를 해결하기 위해 개발된 패턴으로, 핵심 개념은 '모든 것이 독립적으로 동작하는 작은 단위로 나누어져 있고, 이들이 메시지를 통해 서로 소통한다'는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. Actor의 독립성과 메시지 기반 동작:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Actor는 스스로 동작하는 작은 단위입니다. 각 Actor는 자신만의 상태와 행동을 가지고 있으며, 다른 Actor와 직접적인 상호작용 없이 '메시지'라는 형태로 소통합니다. 예를 들어, Actor가 어떤 작업을 하고 싶다면, 직접 다른 Actor의 메서드를 호출하는 대신, '이거 해줘'라는 메시지를 보냅니다. 이 방식은 서로의 상태를 직접 변경하지 않고, 요청을 보낸 후 처리 결과를 기다리거나 다른 일을 처리할 수 있게 해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 병렬성과 비동기 처리:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Actor 모델의 또 다른 중요한 개념은 비동기적 동작입니다. 비동기란 작업을 요청한 후 그 작업이 완료될 때까지 기다리는 대신, 다른 일을 계속할 수 있다는 것을 의미합니다. Actor 모델에서는 각 Actor가 메시지를 비동기적으로 받아서 처리하기 때문에, 여러 작업이 병렬로 진행될 수 있습니다. 이때 각 Actor는 독립적인 스레드로 동작하거나 스레드 풀이라는 것을 이용해 효율적으로 관리됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 상태와 동기화:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Actor는 자신만의 상태를 가지고 있고, 이 상태는 해당 Actor 자신만 변경할 수 있습니다. 상태를 공유하지 않고 메시지로만 소통하기 때문에, 공유 메모리 문제나 복잡한 동기화(락 사용 등)를 신경 쓸 필요가 없어요. 예를 들어, 게임에서 각 캐릭터가 독립적으로 움직이고 행동하는 것을 Actor라고 생각할 수 있습니다. 이 캐릭터들은 서로의 상태를 직접 변경할 수 없고, 메시지를 보내 어떤 행동을 유도할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 에러 처리와 계층 구조:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Actor 모델에서는 에러를 처리하는 방식도 체계적입니다. Actor는 부모-자식 관계로 계층 구조를 형성하고, 부모 Actor는 자식 Actor의 상태를 감시하고 관리할 수 있습니다. 자식 Actor에서 문제가 발생하면, 부모가 이를 감지하고 적절한 조치를 취할 수 있죠. 예를 들어, 자식을 재시작하거나, 중지할 수 있습니다. 이를 통해 시스템이 안정적으로 작동하도록 돕습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 구조 덕분에 Actor 모델은 복잡한 동시성 문제를 해결할 수 있고, 시스템의 성능과 안정성을 높이는 데 큰 도움이 됩니다. 병렬 작업이 많거나, 시스템의 확장이 중요한 경우에 특히 유용합니다. Actor 모델은 마치 각기 다른 악기를 연주하는 오케스트라 단원들이 서로 간섭하지 않고 지휘자의 지시에 따라 연주하는 것과 비슷하다고 생각하면 이해가 쉬울 것입니다. 각 Actor가 자신의 역할을 하고, 지시에 따라 움직이며, 메시지라는 악보를 통해 조화롭게 동작하는 거죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 Actor 모델을 통해 복잡한 동시성 문제를 풀어나갈 수 있답니다!&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; 객체 지향 패턴에서 동기적 메서드 호출이 이루어진다면, Actor 모델에서는 메서드 호출이 비동기로 변경됩니다. 이를 통해 병렬성과 비동기 처리를 자연스럽게 구현할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Thread 기반 동작:&lt;/b&gt; Actor는 독립적인 실행 단위로, 각 Actor는 독립된 스레드 기반 객체입니다. 하지만 반드시 모든 Actor가 하나의 스레드에 할당되는 것은 아니며, 스레드 풀을 통해 관리되어 더 많은 Actor를 효율적으로 실행할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;메시지 기반 동기화:&lt;/b&gt; Actor 모델에서는 상태 공유와 직접적인 동기화 대신 메시지 전달을 통해 동기화 문제를 처리합니다. 이는 각 Actor가 메시지를 수신하고 처리하는 방식으로 내부 상태를 관리하도록 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Actor 모델이 해결하려는 문제&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; OOP에서 메서드를 호출하여 내부 객체의 상태를 변경할 때, 공유 메모리에 접근하는 여러 스레드가 존재할 수 있습니다. 이로 인해 데이터 레이스, 동기화 문제, 복잡한 락 관리가 필요하게 됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;락의 문제점:&lt;/b&gt; 락(lock)을 사용하면 다음과 같은 문제가 발생합니다:
&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; 락과 관련된 스레드의 일시 정지와 복원은 운영체제(OS) 차원에서 높은 비용이 듭니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Deadlock:&lt;/b&gt; 잘못된 락 관리로 인해 시스템이 교착 상태에 빠질 위험이 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;위임된 작업의 에러 처리:&lt;/b&gt; 기존의 병렬 처리 방식인 Task-delegate concurrency에서는 작업을 위임할 때 발생하는 에러를 작업을 넘겨준 쪽에서 받기 어렵습니다. Actor 모델은 이러한 문제를 언어 수준에서 해결하고자 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Actor 모델의 주요 구조와 동작&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;MailBox(메시지 큐):&lt;/b&gt; 각 Actor는 자신만의 메시지 큐를 가지고 있습니다. 이 큐는 비동기적으로 메시지를 수신하고 저장합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;메시지:&lt;/b&gt; Actor는 메시지를 통해 서로 통신합니다. 특정 메서드를 직접 호출하는 대신, 특정 Actor에게 메시지를 전달하여 행동을 유도합니다. 모든 POCO( Plain Old CLR Object )는 메세지가 될 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Behavior:&lt;/b&gt; Actor는 수신한 메시지에 따라 특정 행동을 결정하고 실행합니다. 예를 들어:
&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;새로운 자식 Actor를 생성하거나 기존 자식을 제거할 수 있습니다.&lt;/li&gt;
&lt;li&gt;다른 Actor에게 메시지를 보낼 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;State:&lt;/b&gt; Actor의 상태는 Actor 자신만 변경할 수 있습니다. 상태는 init, ready, closed 등의 단계로 나타낼 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;동기화 대신 메시지 전달:&lt;/b&gt; Actor 모델에서는 락이나 블로킹 대신 메시지 전달 방식을 사용하여 동기화를 처리합니다. 이는 특정 Actor에게 메시지를 보내는 것으로, 직접적으로 스레드를 실행하는 것을 의미하지 않습니다.&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; Actor 간의 호출 관계를 통해 계층 구조(Hierarchy)가 형성됩니다. 예를 들어, 부모 Actor는 자식 Actor의 상태를 감시할 수 있으며, 자식 Actor에서 오류가 발생하면 이를 감지하고 대응할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;에러 전파와 관리:&lt;/b&gt; 부모 Actor는 자식 Actor의 에러를 관리할 수 있으며, 이를 통해 에러의 전파를 제어할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. 액터 관리 ( ActorSystem )&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ActorSystem은 하나의 프로세스 내에서 동작하는 여러 액터들을 조직화하고, 메시지 전달, 액터 생명주기 관리, 스케줄링 등을 담당합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기능&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;액터 생성 및 관리:&lt;/b&gt; ActorSystem은 액터의 부모 역할을 하며, 액터 트리 구조를 유지 관리합니다.&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;
&lt;p data-ke-size=&quot;size16&quot;&gt;ActorSystem은 하나의 애플리케이션에서 여러 개 생성할 수 있지만, 일반적으로 한 개만 사용하는 것이 일반적입니다. 이를 통해 애플리케이션 내의 모든 액터가 같은 컨텍스트 내에서 동작하게 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1726019893474&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class HelloActor : UntypedActor
{
    protected override void OnReceive(object message)
    {
        if (message is string msg)
        {
            Console.WriteLine($&quot;Received: {msg}&quot;);
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        // ActorSystem 생성
        ActorSystem MyActorSystem = ActorSystem.Create(&quot;MyActorSystem&quot;);

        // UntypedActor 생성
        // IActorRef는 Akka.NET에서 액터를 참조하기 위한 인터페이스입니다.
        // 액터 시스템에서 생성된 액터는 직접 참조되는 것이 아니라, IActorRef를 통해 참조됩니다.
        IActorRef helloActor = system.ActorOf(Props.Create(() =&amp;gt; new HelloActor()), &quot;helloActor&quot;);

        // 메시지 전송
        helloActor.Tell(&quot;Hello, Akka.NET!&quot;);

        // 시스템 종료
        Console.ReadLine();
        system.Terminate().Wait();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. 메시지 정의 및 처리 ( Tell )&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Tell()&lt;/b&gt;을 사용하여 Actor에게 메시지를 전달할 수 있다. UntypedActor를 상속받아 Actor를 만들경우 Actor가 처리 방법을 모르는 메시지를 전달받는 다면 무시하거나 무시한 메세지를 &lt;b&gt;Unhandled()&lt;/b&gt;를 통해 처리할 수 있다. ReceiveActor를 사용하면 Unhandled 로깅이 자동으로 수행된다.&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;메세지를 전달받은 Actor는 Sender를 통해 다른 Actor에게 답장을 하거나 메세지를 전달 할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1726024833063&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// MyActor 클래스는 UntypedActor를 상속하여 메시지를 처리하는 액터입니다.
public class MyActor : UntypedActor
{
    // 메시지를 수신할 때 호출되는 메서드
    protected override void OnReceive(object message)
    {
        // 메시지가 Messages.InputError 타입일 경우 처리
        if (message is Messages.InputError msg)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine(msg.Reason); // 에러 메시지 출력
        }
        else
        {
            // 처리할 수 없는 메시지는 Unhandled() 메서드를 호출하여 무시
            Unhandled(message);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;5. IActorRef와 Props&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;액터 참조 핸들러 IActorRef ( &lt;a href=&quot;http://api.getakka.net/docs/stable/html/56C46846.htm&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;http://api.getakka.net/docs/stable/html/56C46846.htm&lt;/a&gt; )&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;IActorRef는 액터에 대한 참조 또는 핸들 역할을 하며, ActorSystem을 통해 액터에게 메시지를 보낼 수 있도록 지원합니다. 액터와 직접 소통하지 않고, IActorRef에 메시지를 보내면 ActorSystem이 이를 전달합니다. IActorRef는 액터가 로컬이든 원격이든 관계없이 메시지를 보낼 수 있는 참조를 제공하며, 해당 액터가 과거에 존재했음을 보장합니다. 액터가 종료될 수 있기 때문에, 종료 알림을 받으려면 IActorContext.Watch를 사용하여 감시할 수 있고, 종료 시 Terminated 메시지를 받을 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Actor간 직접적인 통신을 하지 않는다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접적으로 하는 것이 아니라 ActorSystem을 통해서 이루어집니다.&amp;nbsp;&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; ActorSystem은 모든 메시지를 Envelope로 감싸서 전달하며, 이 Envelope에는 메시지에 대한 메타데이터가 포함되어 있습니다. 액터는 이 메타데이터를 활용해 메시지를 보다 효과적으로 처리할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;위치 투명성:&lt;/b&gt; 액터가 어느 프로세스나 머신에 있는지 신경 쓰지 않아도 됩니다. ActorSystem은 액터의 위치를 자동으로 관리하며, 이를 통해 원격 액터를 지원할 수 있습니다. 이렇게 하면 여러 머신에서 분산 처리하여 시스템의 확장성을 높일 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메시지가 액터에게 전달되었는지 확인하는 것 역시 ActorSystem이 관리하는 부분입니다. Akka.NET은 메시지 전달을 보장하는 다양한 메커니즘을 제공하므로, 이를 직접 관리할 필요는 없습니다. 현재로서는 메시지 전달이 ActorSystem의 역할이라는 점을 신뢰하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Actor의 계층 구조&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;액터는 계층을 형성합니다. 즉, 본질적으로 자신에게 직접 보고하는 &quot;최상위&quot; 액터가 있고&lt;span&gt;&amp;nbsp;&lt;/span&gt;ActorSystem다른 액터에게 보고하는 &quot;자식&quot; 액터가 있습니다. 자식 액터를 만드는 이유는 시스템의 복잡성을 관리하고, 역할과 책임을 분리하여 더 구조적이고 유연한 액터 시스템을 설계하기 위함입니다. 자식 액터는 부모 액터의 일부 작업을 분담하여 처리하거나, 특정 기능을 전담하도록 설계됩니다. 이를 통해 부모 액터는 더 간단한 역할을 유지하며, 자식 액터는 자신만의 상태와 로직을 관리할 수 있습니다. 또한, 자식 액터는 부모 액터가 실패하거나 재시작할 때 함께 관리될 수 있어, 내결함성과 안정성을 향상시킵니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;928&quot; data-origin-height=&quot;733&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vjMYl/btsJzfAnElk/BZqkKssFybuOs2j06cy6V0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vjMYl/btsJzfAnElk/BZqkKssFybuOs2j06cy6V0/img.png&quot; data-alt=&quot;계층에 따른 경로가 변경된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vjMYl/btsJzfAnElk/BZqkKssFybuOs2j06cy6V0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvjMYl%2FbtsJzfAnElk%2FBZqkKssFybuOs2j06cy6V0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;425&quot; height=&quot;336&quot; data-origin-width=&quot;928&quot; data-origin-height=&quot;733&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;계층에 따른 경로가 변경된다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1726032279403&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MyActorClass : UntypedActor{
    // PreStart는 액터가 시작될 때 호출되는 초기화 메서드이다. 보통 부모 자식 관계를 설정함.
	protected override void PreStart(){
		IActorRef myFirstChildActor = Context.ActorOf(Props.Create(() =&amp;gt;
        new MyChildActorClass()), &quot;myFirstChildActor&quot;);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;IActorRef와 context&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Context는 액터의 생명 주기, 자식 액터 관리, 메시지 전달, 로깅, 스케줄링 등의 작업을 지원합니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;Self&lt;/b&gt;: 현재 액터 자신에 대한 참조.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Sender&lt;/b&gt;: 현재 메시지를 보낸 액터에 대한 참조.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Parent&lt;/b&gt;: 부모 액터에 대한 참조.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Children&lt;/b&gt;: 자식 액터들에 대한 참조 목록.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ActorOf&lt;/b&gt;: 새로운 자식 액터를 생성.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Become/Unbecome&lt;/b&gt;: 행동을 변경하거나 복원.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1726035216443&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Self.Tell(&quot;Hello, Self!&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;액터 설정 Props&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;액터를 생성하기 위한 설정 정보를 담는 객체입니다. Props는 액터의 타입, 생성 방식, 필요한 인자 등을 정의하며, 액터 시스템에서 새로운 액터 인스턴스를 만들 때 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;new를 통한 인스턴스 객체생성 금지.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1726032686672&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Props props = Props.Create(() =&amp;gt; new MyActor(..), &quot;...&quot;);//람다형식

Props props2 = Props.Create&amp;lt;MyActor&amp;gt;();&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;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;6. 액터 계층과 감독&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자식 액터 계층 (Child Actor Hierarchy)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;액터는 부모-자식 관계를 통해 계층적으로 구성됩니다. 각 액터는 다른 액터를 생성할 수 있으며, 생성된 액터는 생성한 액터의 자식이 됩니다.&amp;nbsp;&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;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;감독 (Supervision)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;감독은 액터 계층의 부모-자식 관계에서 발생하는 오류를 관리하는 메커니즘입니다. 감독 모델은 부모 액터가 자식 액터의 실패를 감지하고, 해당 실패를 처리하는 방식을 정의합니다. 이러한 전략을 통해, 부모 액터는 자식 액터의 오류에 대응할 수 있으며, 전체 시스템이 하나의 실패로 인해 무너지는 것을 방지할 수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;각 액터는 부모 액터가 감독하며, 오류가 발생할 경우 이를 복구하는 데 도움을 줍니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;계층 구조에서 부모 액터는 자식 액터의 오류를 관리합니다. 자식 액터에 예외가 발생하면 부모가 오류 메시지를 받고, 적절한 지시를 내립니다(재시작, 중지 등) 감독의&lt;span&gt;&amp;nbsp;&lt;/span&gt;기본 전략은 &quot;One-For-One&quot;으로, 특정 자식에만 적용됩니다. &quot;All-For-One&quot; 전략은 해당 자식과 모든 형제 액터에 적용됩니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;SupervisorStrategy를 통한 감독&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부모 액터가 자식 액터의 실패(예외 발생 등)를 감지하고, 그에 따라 자식 액터를 어떻게 처리할지를 결정하는 정보를 제공합니다. OneForOneStrategy( One-For-One )또는 AllForOneStrategy( All-For-One ) 같은 전략을 설정할 수 있습니다:&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;OneForOneStrategy&lt;/b&gt;: 실패한 자식 액터에만 적용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;AllForOneStrategy&lt;/b&gt;: 한 자식의 실패가 다른 자식들에게도 영향을 미치도록 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SupervisorStrategy에서 사용되는 지시문&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;Resume&lt;/b&gt;: 실패한 자식 액터를 그대로 계속 실행합니다. (예외를 무시하고 상태를 유지)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Restart&lt;/b&gt;: 자식 액터를 재시작합니다. (상태 초기화)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Stop&lt;/b&gt;: 자식 액터를 중지합니다. (액터의 생명주기 종료)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Escalate&lt;/b&gt;: 실패를 상위 부모에게 전달하여 처리하도록 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1726106288766&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    public class TailCoordinatorActor : UntypedActor
    {
        protected override void OnReceive(object message)
        {
        }
        // 여기서 기본 SupervisorStrategy를 재정의하고 있습니다
        // 기본 전략은 Restart 지시문이 있는 One-For-One 전략입니다
        protected override SupervisorStrategy SupervisorStrategy()
        {
            return new OneForOneStrategy(
                10, // 최대 재시도 횟수
                TimeSpan.FromSeconds(30), // 시간 범위
                x =&amp;gt;
                {
                    // ArithmeticException은 애플리케이션에 치명적이지 않다고 간주할 수 있습니다
                    // 따라서 오류를 무시하고 계속 진행합니다.
                    if (x is ArithmeticException) return Directive.Resume;

                    // 복구할 수 없는 오류인 경우, 실패한 액터를 중지합니다
                    else if (x is NotSupportedException) return Directive.Stop;

                    // 그 외의 모든 경우에는, 실패한 액터를 재시작합니다
                    else return Directive.Restart;
                });
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;7. 주소로 액터 찾기 ActorSelection&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ActorSelection은 Akka.NET에서 특정 액터를 직접 참조하지 않고, 경로(path)를 통해 액터를 선택하고 메시지를 전송할 수 있는 기능입니다. 이를 통해 액터 시스템 내에서 액터를 유연하게 접근하고, 동적으로 메시지를 전달할 수 있습니다. ActorSelection은 액터의 위치나 정확한 참조를 알 필요 없이 경로를 통해 액터와 통신할 수 있게 해주기 때문에, 분산 시스템이나 액터 구조가 동적으로 변화하는 환경에서 매우 유용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ActorSelection은 실제 ActorRef를 사용하는 것보다 약간의 성능 비용이 추가됩니다. 따라서 참조를 명확히 알고 있는 경우, 직접 ActorRef를 사용하는 것이 더 효율적일 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ActorSelection이 생겨나게 된 이유&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;: 액터 시스템에서는 액터가 동적으로 생성, 삭제될 수 있습니다. 특정 액터에 접근하려면 ActorRef가 필요하지만, 모든 액터의 ActorRef를 항상 가지고 있는 것은 어렵습니다. 특히, 분산된 환경에서 특정 액터의 ActorRef를 유지하는 것은 비현실적입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;유연한 메시지 전송&lt;/b&gt;: 액터 시스템에서 모든 액터의 위치를 명확히 알고 있지 않더라도, 특정 경로를 따라 액터에게 메시지를 전송할 수 있어야 합니다. ActorSelection은 이러한 요구를 충족하기 위해 설계되었습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;위치 투명성&lt;/b&gt;: 액터 시스템의 중요한 개념 중 하나는 위치 투명성입니다. 즉, 액터가 어디에 있든지 상관없이 동일한 방식으로 접근하고 통신할 수 있어야 합니다. ActorSelection은 경로 기반의 접근 방식을 통해 이 위치 투명성을 지원합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ActorSelection 사용 이유&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;: 액터의 정확한 참조(ActorRef)를 알 필요 없이 경로를 통해 액터를 선택할 수 있습니다. 예를 들어, 자식 액터들이 동적으로 생성되거나 삭제되는 경우, 경로를 통해 유연하게 접근할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;분산 시스템 지원&lt;/b&gt;: 분산된 액터 시스템에서 네트워크를 넘어 원격 액터에게 메시지를 보낼 때 ActorSelection이 유용합니다. 경로를 통해 원격 액터의 위치를 추상화할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;유연성&lt;/b&gt;: 액터 구조가 고정되지 않고 동적으로 변화할 때, ActorSelection은 액터의 경로를 통해 메시지를 보내기 때문에, 구조가 변화하더라도 액터 참조를 지속적으로 관리하지 않아도 됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;브로드캐스트&lt;/b&gt;: 특정 경로를 따라 여러 액터가 존재할 경우, ActorSelection을 사용하여 한 번에 여러 액터에게 메시지를 전송할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ActorSelection과 IActorRef의 차이점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;IActorRef&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;액터에 대한 고유 참조입니다.&lt;/li&gt;
&lt;li&gt;액터를 생성할 때 반환되며, 이를 통해 해당 액터와 직접 통신할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ActorSelection&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 경로에 있는 하나 이상의 액터를 선택하는 메커니즘입니다.&lt;/li&gt;
&lt;li&gt;경로를 통해 액터를 동적으로 탐색하여 접근합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1726119850466&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 부모 액터 아래에 있는 모든 자식 액터를 선택하는 ActorSelection을 생성합니다.
var childSelection = Context.ActorSelection(&quot;/user/parent/*&quot;);

// 새로운 ChildActor를 생성하고, 그에 대한 IActorRef를 얻습니다.
IActorRef childRef = Context.ActorOf(Props.Create(() =&amp;gt; new ChildActor()), &quot;child1&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1726118718681&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using Akka.Actor;

public class ParentActor : UntypedActor
{
    protected override void OnReceive(object message)
    {
        if (message is string msg &amp;amp;&amp;amp; msg == &quot;start&quot;)
        {
            // 자식 액터를 생성하고, 특정 경로에 액터를 배치
            Context.ActorOf(Props.Create(() =&amp;gt; new ChildActor()), &quot;child1&quot;);

            // ActorSelection을 사용하여 경로를 통해 자식 액터에 접근
            var childSelection = Context.ActorSelection(&quot;child1&quot;);

            // ActorSelection을 통해 자식 액터에 메시지 전송
            childSelection.Tell(&quot;hello from parent&quot;);
        }
    }
}

public class ChildActor : UntypedActor
{
    protected override void OnReceive(object message)
    {
        if (message is string msg)
        {
            Console.WriteLine($&quot;ChildActor received message: {msg}&quot;);
        }
    }
}&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;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;8. 액터 라이프사이클&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;Akka.NET의 액터 라이프 사이클은 5단계로 구성됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;액터 라이프 사이클 5단계&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Starting (시작 중)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 단계는 액터가 ActorSystem에 의해 초기화되는 상태입니다. 액터가 생성되고 준비되며, 메시지를 받을 준비를 하는 단계입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Receiving (메시지 수신 중)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 단계에서는 액터가 메시지를 받을 수 있는 상태가 됩니다. 액터의 우편함(Mailbox)에 쌓인 메시지들이 차례로 OnReceive 메서드로 전달되어 처리됩니다. 이 단계에서 액터는 주로 메시지 처리 작업을 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. Stopping (중지 중)&lt;/p&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;종료 중인 경우&lt;/b&gt;: 액터가 완전히 종료되는 경우, 우편함에 남아 있는 모든 메시지는 ActorSystem의 DeadLetters 우편함으로 보내집니다. DeadLetters는 더 이상 전달할 수 없는 메시지를 저장하는 곳으로, 보통 액터가 이미 종료된 경우에 발생합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. Terminated (종료됨)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;액터가 완전히 종료된 상태입니다. 이 상태에서는 액터가 더 이상 존재하지 않으며, 해당 액터의 IActorRef로 메시지를 보내면 모두 DeadLetters로 전달됩니다. 액터는 재시작될 수 없으며, 만약 동일한 위치에 새로운 액터가 생성된다면, 새로운 IActorRef를 가지지만, 동일한 ActorPath를 가질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. Restarting (재시작 중)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 단계는 액터가 재시작될 때를 의미하며, 다시 Starting 상태로 돌아갑니다. 액터는 오류나 예외 발생 시 재시작될 수 있으며, 이때 기존 상태를 초기화하고 새롭게 시작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;액터 생명주기 재정의 메소드&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;605&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m0b5j/btsJz7gXptq/cFbtM4ciIvCywSCo9Ivtxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m0b5j/btsJz7gXptq/cFbtM4ciIvCywSCo9Ivtxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m0b5j/btsJz7gXptq/cFbtM4ciIvCywSCo9Ivtxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm0b5j%2FbtsJz7gXptq%2FcFbtM4ciIvCywSCo9Ivtxk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;431&quot; height=&quot;364&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;605&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;div&gt;
&lt;div data-message-id=&quot;07f22188-ff97-477e-8c17-d61571fe633d&quot; data-message-author-role=&quot;assistant&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;PreStart - &lt;/b&gt;PreStart는 액터가 메시지를 받기 전에 실행되는 초기화 로직을 넣을 수 있는 메서드입니다. 이곳에 액터의 초기 상태를 설정하거나 필요한 초기화 작업을 수행할 수 있습니다. 액터가 재시작될 때도 호출됩니다.&amp;nbsp;가장 많이 사용되는 후크 메서드입니다. 액터의 초기 상태 설정과 초기화 로직 실행에 사용됩니다. 새로운 액터가 시작될 때마다 필요한 준비 작업을 수행할 수 있습니다.
&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;/li&gt;
&lt;li&gt;&lt;b&gt;PreRestart - &lt;/b&gt;액터가 실패하여(예: 처리되지 않은 예외 발생) 부모 액터에 의해 재시작될 때, PreRestart 메서드가 호출됩니다. 이 메서드에서는 액터가 재시작되기 전에 필요한 정리 작업을 수행하거나, 현재 처리 중인 메시지를 저장해 두어 나중에 다시 처리할 수 있도록 할 수 있습니다. 세 번째로 많이 사용되며, 재시작 전 작업이 필요할 때 사용됩니다. 예를 들어, 메시지를 임시 저장하거나 재처리를 위해 준비하는 작업을 할 수 있습니다. 액터의 작업에 따라 사용 빈도와 방법이 달라질 수 있습니다.
&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;/li&gt;
&lt;li&gt;&lt;b&gt;PostStop - &lt;/b&gt;PostStop은 액터가 종료된 후 호출됩니다. 액터가 더 이상 메시지를 받지 않을 때, 정리 작업을 수행하기에 적합한 곳입니다. 액터가 종료될 때 파일 핸들이나 다른 시스템 자원을 해제해야 할 때 주로 사용됩니다. 이 메서드는 PreRestart 중에도 호출될 수 있지만, 필요에 따라 PreRestart에서 base.PreRestart를 호출하지 않아 이 동작을 피할 수 있습니다. 두 번째로 많이 사용되는 후크 메서드입니다. 액터가 종료될 때, 시스템 자원을 해제하거나 정리 작업을 수행하는 데 사용됩니다. 예를 들어, 파일 시스템 핸들 해제, 네트워크 연결 종료 등을 처리할 수 있습니다.
&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;/li&gt;
&lt;li&gt;&lt;b&gt;PostRestart - &lt;/b&gt;PostRestart는 액터가 재시작된 후 호출되며, PreRestart와 PreStart 사이에 호출됩니다. 이 메서드는 재시작의 원인이 된 오류를 추가로 분석하거나, 보고 작업을 수행하는 데 적합합니다. Akka.NET에서 기본적으로 제공하는 오류 처리 외에 추가적인 진단을 수행할 수 있는 기회입니다.
&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;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;감독과의 관계 (Supervision)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;액터가 예기치 않게 충돌하거나 예외를 던지면, 액터의 감독자(supervisor)는 자동으로 액터의 생명주기를 처음부터 다시 시작하게 만듭니다. 이 과정에서 액터의 메일박스에 남아 있는 메시지들은 유지되며, 새로 시작된 액터가 다시 처리할 수 있게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때, 부모의 감독 지시(SupervisionDirective)에 따라 액터의 행동이 결정됩니다. 부모는 자식 액터에게 종료, 재시작, 오류 무시 후 계속 작업 등의 지시를 내릴 수 있습니다. 기본 설정은 재시작이며, 이를 통해 문제가 되는 상태를 초기화하고 액터를 깨끗하게 새로 시작할 수 있게 됩니다. 재시작은 비용이 적게 들기 때문에 Akka.NET에서는 이러한 방식이 기본으로 사용됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1726182745944&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using Akka.Actor;
using System;

public class MyActor : UntypedActor
{
    // PreStart: 액터가 시작될 때 호출됩니다.
    protected override void PreStart()
    {
        base.PreStart();
        Console.WriteLine(&quot;MyActor is starting.&quot;);
        // 초기화 작업 예: 데이터베이스 연결 설정 등
    }
    // PreRestart: 액터가 재시작되기 전에 호출됩니다.
    protected override void PreRestart(Exception reason, object message)
    {
        base.PreRestart(reason, message);
        Console.WriteLine($&quot;MyActor is restarting due to: {reason.Message}&quot;);
        // 재시작 전 작업 예: 현재 상태 저장, 메시지 임시 저장 등
    }
    // PostStop: 액터가 종료된 후 호출됩니다.
    protected override void PostStop()
    {
        base.PostStop();
        Console.WriteLine(&quot;MyActor has stopped.&quot;);
        // 종료 후 자원 해제 예: 파일 핸들 닫기, 네트워크 연결 종료 등
    }
    // PostRestart: 액터가 재시작된 후 호출됩니다.
    protected override void PostRestart(Exception reason)
    {
        base.PostRestart(reason);
        Console.WriteLine(&quot;MyActor has restarted.&quot;);
        Console.WriteLine($&quot;Reason for restart: {reason.Message}&quot;);
        // 재시작 후 추가 작업 예: 상태 초기화, 추가 로깅 등
    }
    // 메시지를 처리하는 메서드
    protected override void OnReceive(object message)
    {
        // 메시지 처리 로직을 여기에 작성합니다.
        Console.WriteLine($&quot;Received message: {message}&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;a href=&quot;https://petabridge.com/blog/when-should-I-use-actor-selection/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://petabridge.com/blog/when-should-I-use-actor-selection/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1726118601586&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;When Should I Use Actor Selection?&quot; data-og-description=&quot;When Should I Use Actor Selection?&quot; data-og-host=&quot;petabridge.com&quot; data-og-source-url=&quot;https://petabridge.com/blog/when-should-I-use-actor-selection/&quot; data-og-url=&quot;https://petabridge.com/blog/when-should-I-use-actor-selection/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/o5xi3/hyWZk2isd7/LfJj23j97hOMxwW9XDKsFk/img.png?width=749&amp;amp;height=339&amp;amp;face=0_0_749_339&quot;&gt;&lt;a href=&quot;https://petabridge.com/blog/when-should-I-use-actor-selection/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://petabridge.com/blog/when-should-I-use-actor-selection/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/o5xi3/hyWZk2isd7/LfJj23j97hOMxwW9XDKsFk/img.png?width=749&amp;amp;height=339&amp;amp;face=0_0_749_339');&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;When Should I Use Actor Selection?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;When Should I Use Actor Selection?&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;petabridge.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;a href=&quot;https://github.com/petabridge/akka-bootcamp&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/petabridge/akka-bootcamp&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1726018322846&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - petabridge/akka-bootcamp: Self-paced training course to learn Akka.NET fundamentals from scratch&quot; data-og-description=&quot;Self-paced training course to learn Akka.NET fundamentals from scratch - petabridge/akka-bootcamp&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/petabridge/akka-bootcamp&quot; data-og-url=&quot;https://github.com/petabridge/akka-bootcamp&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b58IgM/hyW24J5Cxx/0eSldw8A4qBirRyBrangZ0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/petabridge/akka-bootcamp&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/petabridge/akka-bootcamp&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b58IgM/hyW24J5Cxx/0eSldw8A4qBirRyBrangZ0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;GitHub - petabridge/akka-bootcamp: Self-paced training course to learn Akka.NET fundamentals from scratch&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Self-paced training course to learn Akka.NET fundamentals from scratch - petabridge/akka-bootcamp&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/akkadotnet/akka.net?tab=readme-ov-file&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/akkadotnet/akka.net?tab=readme-ov-file&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1725953669133&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - akkadotnet/akka.net: Canonical actor model implementation for .NET with local + distributed actors in C# and F#.&quot; data-og-description=&quot;Canonical actor model implementation for .NET with local + distributed actors in C# and F#. - akkadotnet/akka.net&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/akkadotnet/akka.net?tab=readme-ov-file&quot; data-og-url=&quot;https://github.com/akkadotnet/akka.net&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/T20Ed/hyW24wsM0D/JrTGLKq57hQU9TtaAmPQCk/img.png?width=1600&amp;amp;height=1024&amp;amp;face=0_0_1600_1024&quot;&gt;&lt;a href=&quot;https://github.com/akkadotnet/akka.net?tab=readme-ov-file&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/akkadotnet/akka.net?tab=readme-ov-file&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/T20Ed/hyW24wsM0D/JrTGLKq57hQU9TtaAmPQCk/img.png?width=1600&amp;amp;height=1024&amp;amp;face=0_0_1600_1024');&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;GitHub - akkadotnet/akka.net: Canonical actor model implementation for .NET with local + distributed actors in C# and F#.&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Canonical actor model implementation for .NET with local + distributed actors in C# and F#. - akkadotnet/akka.net&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=BzTAdSxtrq0&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=BzTAdSxtrq0&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=BzTAdSxtrq0&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/OnJ8p/hyW21T1WHF/wIzH1egp8OpOpgvBKcrVXK/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=1116_592_1164_644&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;[.NET Conf 2021 x Seoul] AKKA.NET - 닷넷 코어 API와 AKKA의 만남&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/BzTAdSxtrq0&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&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;</description>
      <category>VisualStudio/C#서버</category>
      <author>usingsystem</author>
      <guid isPermaLink="true">https://usingsystem.tistory.com/545</guid>
      <comments>https://usingsystem.tistory.com/545#entry545comment</comments>
      <pubDate>Thu, 27 Nov 2025 15:59:43 +0900</pubDate>
    </item>
    <item>
      <title>[AI]AI 서버의 메모리 구조와 LLM 메모리 동작 원리 (양자화 등)</title>
      <link>https://usingsystem.tistory.com/576</link>
      <description>&lt;p data-end=&quot;427&quot; data-start=&quot;277&quot; data-ke-size=&quot;size16&quot;&gt;AI와 LLM을 공부하면서 &amp;ldquo;GPU 메모리와 시스템 메모리는 각각 어떤 역할을 할까?&amp;rdquo;라는 의문이 들었다.&lt;br /&gt;처음엔 단순히 처음엔 단순히 &amp;ldquo;GPU가 빠르니까 무조건 좋겠지&amp;rdquo;라고 생각했는데,&lt;br /&gt;LLM 모델을 직접 다뤄보면서 이게 그렇게 간단한 게 아니라는 걸 알게 됐다.&lt;br /&gt;공부하면 할수록 두 메모리의 역할이 확실히 다르다는 걸 알게 됐다.&lt;/p&gt;
&lt;p data-end=&quot;516&quot; data-start=&quot;429&quot; data-ke-size=&quot;size16&quot;&gt;이 글은 내가 공부하면서 직접 정리한 내용들을 중심으로,&lt;br /&gt;&lt;b&gt;AI 서버의 메모리 구조와 LLM이 실제로 어떻게 메모리를 사용하는지&lt;/b&gt; 정리한 것이다.&lt;/p&gt;
&lt;p data-end=&quot;516&quot; data-start=&quot;429&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;561&quot; data-start=&quot;523&quot; data-ke-size=&quot;size26&quot;&gt;1. GPU 메모리(VRAM)와 시스템 메모리(RAM)의 차이&lt;/h2&gt;
&lt;p data-end=&quot;589&quot; data-start=&quot;575&quot; data-ke-size=&quot;size16&quot;&gt;일단 간단하게 말하면,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;668&quot; data-start=&quot;590&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;628&quot; data-start=&quot;590&quot;&gt;&lt;b&gt;VRAM(GPU 메모리)&lt;/b&gt; &amp;rarr; 계산이 직접 일어나는 공간&lt;/li&gt;
&lt;li data-end=&quot;668&quot; data-start=&quot;629&quot;&gt;&lt;b&gt;RAM(시스템 메모리)&lt;/b&gt; &amp;rarr; 계산을 준비하고 관리하는 공간&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;685&quot; data-start=&quot;670&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 구분할 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;804&quot; data-start=&quot;687&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 이미지를 분류하는 AI 모델이 있다고 하자.&lt;br /&gt;데이터를 GPU로 바로 던지는 게 아니라,&lt;br /&gt;CPU에서 먼저 데이터를 읽고 가공해서 GPU로 넘긴다.&lt;br /&gt;이 중간 단계에서 RAM이 역할을 한다.&lt;/p&gt;
&lt;p data-end=&quot;881&quot; data-start=&quot;806&quot; data-ke-size=&quot;size16&quot;&gt;RAM은 데이터를 GPU가 쓸 수 있게 &amp;lsquo;정리&amp;rsquo;하고 &amp;lsquo;보관&amp;rsquo;하는 곳이고,&lt;br /&gt;VRAM은 그걸 받아서 &amp;lsquo;실제로 연산&amp;rsquo;을 하는 곳이다.&lt;/p&gt;
&lt;p data-end=&quot;887&quot; data-start=&quot;883&quot; data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;blockquote data-end=&quot;955&quot; data-start=&quot;888&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;955&quot; data-start=&quot;890&quot; data-ke-size=&quot;size16&quot;&gt;RAM은 일꾼에게 도구를 정리해 주는 조수 같은 역할,&lt;br /&gt;GPU는 직접 망치질을 하는 기술자 같은 역할이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-end=&quot;898&quot; data-start=&quot;866&quot; data-ke-size=&quot;size26&quot;&gt;2. 학습(Training) 과정에서 메모리의 역할&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p data-end=&quot;1059&quot; data-start=&quot;998&quot; data-ke-size=&quot;size16&quot;&gt;모델 학습은 GPU 위에서 돌아가지만,&lt;br /&gt;데이터는 보통 한 번에 다 못 올린다.&lt;br /&gt;그래서 다음처럼 흐른다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;[디스크]&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &amp;rarr; &lt;/span&gt;&lt;span&gt;&lt;span&gt;[RAM]&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &amp;rarr; &lt;/span&gt;&lt;span&gt;&lt;span&gt;[VRAM]&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1172&quot; data-start=&quot;1093&quot; data-ke-size=&quot;size16&quot;&gt;하드디스크(또는 SSD)에서 데이터를 읽어 RAM에 잠시 저장해두고,&lt;br /&gt;RAM에서 GPU VRAM으로 일부씩 옮겨가면서 학습을 진행한다.&lt;/p&gt;
&lt;p data-end=&quot;1263&quot; data-start=&quot;1174&quot; data-ke-size=&quot;size16&quot;&gt;이 과정을 &lt;b&gt;배치(batch)&lt;/b&gt; 단위로 반복한다.&lt;br /&gt;RAM이 넉넉하면 데이터를 미리 캐싱해둘 수 있어서&lt;br /&gt;GPU가 쉴 틈 없이 계속 연산할 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;1325&quot; data-start=&quot;1265&quot; data-ke-size=&quot;size16&quot;&gt;RAM이 부족하면 디스크에서 직접 데이터를 읽게 되는데,&lt;br /&gt;그럼 속도가 느려지고 학습 효율이 떨어진다.&lt;/p&gt;
&lt;blockquote data-end=&quot;1365&quot; data-start=&quot;1327&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;1365&quot; data-start=&quot;1329&quot; data-ke-size=&quot;size16&quot;&gt;그래서 GPU만큼이나 RAM 용량도 학습 성능에 영향을 준다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-end=&quot;1209&quot; data-start=&quot;1176&quot; data-ke-size=&quot;size26&quot;&gt;3. 추론 과정에서의 메모리 흐름&lt;/h2&gt;
&lt;p data-end=&quot;1494&quot; data-start=&quot;1409&quot; data-ke-size=&quot;size16&quot;&gt;학습이 끝나고 추론을 할 때도 비슷한 구조로 움직인다.&lt;br /&gt;모델이 저장되어 있는 경로에서 데이터를 읽어 GPU로 전달하는데,&lt;br /&gt;그 과정이 이렇게 된다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;[Disk]&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &amp;rarr; &lt;/span&gt;&lt;span&gt;&lt;span&gt;[RAM]&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &amp;rarr; &lt;/span&gt;&lt;span&gt;&lt;span&gt;[VRAM]&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &amp;rarr; (결과 계산) &amp;rarr; &lt;/span&gt;&lt;span&gt;&lt;span&gt;[RAM]&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1672&quot; data-start=&quot;1547&quot; data-ke-size=&quot;size16&quot;&gt;즉, 모델이 처음 로드될 때는 RAM에 올라갔다가&lt;br /&gt;그 다음 GPU로 전달되어 실제 연산이 이루어진다.&lt;br /&gt;GPU가 결과를 계산하면 다시 CPU로 결과가 돌아오고,&lt;br /&gt;RAM에서 후처리를 거쳐 사용자에게 응답을 보낸다.&lt;/p&gt;
&lt;p data-end=&quot;1757&quot; data-start=&quot;1674&quot; data-ke-size=&quot;size16&quot;&gt;이 과정에서 RAM이 적으면&lt;br /&gt;모델을 불러오는 속도나 응답이 느려질 수 있다.&lt;br /&gt;특히 LLM 모델처럼 용량이 큰 모델일수록 이 차이가 커진다.&lt;/p&gt;
&lt;h2 data-end=&quot;1569&quot; data-start=&quot;1538&quot; data-ke-size=&quot;size26&quot;&gt;4. 모델을 올릴 때 메모리가 점점 늘어나는 이유&lt;/h2&gt;
&lt;p data-end=&quot;1905&quot; data-start=&quot;1804&quot; data-ke-size=&quot;size16&quot;&gt;처음 모델을 GPU에 올리면&lt;br /&gt;VRAM에는 기본적으로 &amp;ldquo;모델 파라미터(가중치)&amp;rdquo;가 올라간다.&lt;br /&gt;예를 들어 모델 크기가 12GB라면&lt;br /&gt;기본적으로 VRAM 12GB를 차지한다.&lt;/p&gt;
&lt;p data-end=&quot;2007&quot; data-start=&quot;1907&quot; data-ke-size=&quot;size16&quot;&gt;그런데 막상 추론을 해보면 12GB 이상을 차지한다.&lt;br /&gt;그 이유는 모델이 계산을 하면서&lt;br /&gt;입력 텐서, 중간 연산 결과, 출력 텐서 등이&lt;br /&gt;추가로 VRAM을 쓰기 때문이다.&lt;/p&gt;
&lt;p data-end=&quot;2013&quot; data-start=&quot;2009&quot; data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;blockquote data-end=&quot;2054&quot; data-start=&quot;2014&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;2054&quot; data-start=&quot;2016&quot; data-ke-size=&quot;size16&quot;&gt;VRAM 사용량 = 모델 파라미터 + (입력 + 중간 계산 + 출력)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;2142&quot; data-start=&quot;2056&quot; data-ke-size=&quot;size16&quot;&gt;이 중간 계산값들은 추론이 끝나면 사라지지만,&lt;br /&gt;요청이 많거나 동시에 여러 요청을 처리할 땐&lt;br /&gt;이 임시 버퍼가 겹쳐져서 VRAM이 급격히 늘어난다.&lt;/p&gt;
&lt;h2 data-end=&quot;1954&quot; data-start=&quot;1927&quot; data-ke-size=&quot;size26&quot;&gt;5. 직관적으로 이해하기 list 예시&lt;/h2&gt;
&lt;p data-end=&quot;2010&quot; data-start=&quot;1956&quot; data-ke-size=&quot;size16&quot;&gt;이걸 조금 더 쉽게 설명하자면,&lt;br /&gt;마치 list[int] 타입 변수를 만드는 것과 비슷하다.&lt;/p&gt;
&lt;p data-end=&quot;2074&quot; data-start=&quot;2012&quot; data-ke-size=&quot;size16&quot;&gt;처음엔 int 하나라서 4byte만 차지하지만,&lt;br /&gt;리스트에 데이터를 추가할수록 4byte씩 계속 늘어난다.&lt;/p&gt;
&lt;p data-end=&quot;2168&quot; data-start=&quot;2076&quot; data-ke-size=&quot;size16&quot;&gt;GPU도 이와 비슷하게,&lt;br /&gt;모델을 VRAM에 올려두고 추론을 계속 수행하면&lt;br /&gt;임시 텐서(입력, 출력, 중간값 등)가 늘어나면서 VRAM 사용량이 점점 커진다.&lt;/p&gt;
&lt;p data-end=&quot;2234&quot; data-start=&quot;2170&quot; data-ke-size=&quot;size16&quot;&gt;물론 실제 GPU는 이렇게 단순히 선형적으로 증가하진 않지만,&lt;br /&gt;개념적으로는 이렇게 생각하면 훨씬 이해하기 쉽다.&lt;/p&gt;
&lt;h2 data-end=&quot;2302&quot; data-start=&quot;2241&quot; data-ke-size=&quot;size26&quot;&gt;6. max_length, batch_size, torch_dtype가 메모리에 미치는 영향&lt;/h2&gt;
&lt;p data-end=&quot;2408&quot; data-start=&quot;2304&quot; data-ke-size=&quot;size16&quot;&gt;VLLM을 사용하거나 모델을 로드할 때&lt;br /&gt;max_length, batch_size, torch_dtype(양자화 옵션) 값을 조정하면&lt;br /&gt;메모리 사용량이 눈에 띄게 달라진다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2563&quot; data-start=&quot;2410&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2460&quot; data-start=&quot;2410&quot;&gt;max_length : 한 번에 처리할 최대 토큰 수 &amp;rarr; 커질수록 VRAM 폭증&lt;/li&gt;
&lt;li data-end=&quot;2513&quot; data-start=&quot;2461&quot;&gt;batch_size : 동시에 처리할 입력 샘플 수 &amp;rarr; 커질수록 VRAM 크게 증가&lt;/li&gt;
&lt;li data-end=&quot;2563&quot; data-start=&quot;2514&quot;&gt;torch_dtype : 모델 가중치 정밀도 (fp32, fp16, int8 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2691&quot; data-start=&quot;2565&quot; data-ke-size=&quot;size16&quot;&gt;GPU는 모델을 올릴 때 단순히 &lt;b&gt;가중치(weight)&lt;/b&gt; 만 올리는 게 아니라,&lt;br /&gt;추론 중에 사용할 &lt;b&gt;버퍼 공간(buffer)&lt;/b&gt; 도 미리 확보한다.&lt;br /&gt;그래서 이런 설정값들이 VRAM 사용량에 직접적인 영향을 미친다.&lt;/p&gt;
&lt;h2 data-end=&quot;2741&quot; data-start=&quot;2698&quot; data-ke-size=&quot;size26&quot;&gt;7. Billion 단위 모델과 양자화(Quantization)의 이해&lt;/h2&gt;
&lt;p data-end=&quot;2852&quot; data-start=&quot;2743&quot; data-ke-size=&quot;size16&quot;&gt;모델에는 3B, 8B, 16B 같은 단위가 붙는다.&lt;br /&gt;여기서 &lt;b&gt;B(Billion)&lt;/b&gt; 은 &amp;ldquo;가중치의 개수&amp;rdquo;를 뜻한다.&lt;br /&gt;즉, 3B 모델은 약 30억 개의 weight를 가지고 있다는 의미다.&lt;/p&gt;
&lt;p data-end=&quot;2958&quot; data-start=&quot;2854&quot; data-ke-size=&quot;size16&quot;&gt;이제 본격적으로 &lt;b&gt;양자화(Quantization)&lt;/b&gt; 를 살펴보자.&lt;br /&gt;양자화는 단순한 압축이 아니라 &lt;b&gt;정밀도(precision)를 낮춰 계산량과 메모리 사용량을 줄이는 기술&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-end=&quot;2973&quot; data-start=&quot;2960&quot; data-ke-size=&quot;size16&quot;&gt;딥러닝 모델은 사실상&lt;/p&gt;
&lt;blockquote data-end=&quot;3022&quot; data-start=&quot;2974&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;3022&quot; data-start=&quot;2976&quot; data-ke-size=&quot;size16&quot;&gt;입력 &amp;times; 가중치(weight) = 출력&lt;br /&gt;으로 작동하는 거대한 수치 계산기다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;3075&quot; data-start=&quot;3024&quot; data-ke-size=&quot;size16&quot;&gt;그래서 가중치를 다루는 비트 수를 줄이면&lt;br /&gt;모델 크기와 메모리 사용량이 함께 줄어든다.&lt;/p&gt;
&lt;p data-end=&quot;3214&quot; data-start=&quot;3077&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어,&lt;br /&gt;fp32 가중치는 4byte이고,&lt;br /&gt;3B 모델이라면 4byte &amp;times; 3,000,000,000 = 12GB를 차지한다.&lt;br /&gt;하지만 GPU VRAM이 10GB밖에 없다면 로드가 불가능하다.&lt;br /&gt;이럴 때 양자화를 사용하면 된다.&lt;/p&gt;
&lt;p data-end=&quot;3296&quot; data-start=&quot;3216&quot; data-ke-size=&quot;size16&quot;&gt;fp16으로 바꾸면 2byte가 되어 크기가 절반으로 줄고,&lt;br /&gt;int8이면 1byte, int4면 0.5byte로 더 작아진다.&lt;/p&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;3534&quot; data-start=&quot;3298&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr&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;tr&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3411&quot; data-start=&quot;3375&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3382&quot; data-start=&quot;3375&quot;&gt;FP32&lt;/td&gt;
&lt;td data-end=&quot;3390&quot; data-start=&quot;3382&quot; data-col-size=&quot;sm&quot;&gt;4byte&lt;/td&gt;
&lt;td data-end=&quot;3395&quot; data-start=&quot;3390&quot; data-col-size=&quot;sm&quot;&gt;1x&lt;/td&gt;
&lt;td data-end=&quot;3411&quot; data-start=&quot;3395&quot; data-col-size=&quot;sm&quot;&gt;가장 정확하지만 무거움&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3450&quot; data-start=&quot;3412&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3419&quot; data-start=&quot;3412&quot;&gt;FP16&lt;/td&gt;
&lt;td data-end=&quot;3427&quot; data-start=&quot;3419&quot; data-col-size=&quot;sm&quot;&gt;2byte&lt;/td&gt;
&lt;td data-end=&quot;3435&quot; data-start=&quot;3427&quot; data-col-size=&quot;sm&quot;&gt;2x 감소&lt;/td&gt;
&lt;td data-end=&quot;3450&quot; data-start=&quot;3435&quot; data-col-size=&quot;sm&quot;&gt;학습용으로 많이 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3489&quot; data-start=&quot;3451&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3458&quot; data-start=&quot;3451&quot;&gt;INT8&lt;/td&gt;
&lt;td data-end=&quot;3466&quot; data-start=&quot;3458&quot; data-col-size=&quot;sm&quot;&gt;1byte&lt;/td&gt;
&lt;td data-end=&quot;3474&quot; data-start=&quot;3466&quot; data-col-size=&quot;sm&quot;&gt;4x 감소&lt;/td&gt;
&lt;td data-end=&quot;3489&quot; data-start=&quot;3474&quot; data-col-size=&quot;sm&quot;&gt;추론용으로 자주 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3534&quot; data-start=&quot;3490&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3497&quot; data-start=&quot;3490&quot;&gt;INT4&lt;/td&gt;
&lt;td data-end=&quot;3507&quot; data-start=&quot;3497&quot; data-col-size=&quot;sm&quot;&gt;0.5byte&lt;/td&gt;
&lt;td data-end=&quot;3515&quot; data-start=&quot;3507&quot; data-col-size=&quot;sm&quot;&gt;8x 감소&lt;/td&gt;
&lt;td data-end=&quot;3534&quot; data-start=&quot;3515&quot; data-col-size=&quot;sm&quot;&gt;LLM 서빙용, 극단적 압축&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h2 data-end=&quot;3571&quot; data-start=&quot;3541&quot; data-ke-size=&quot;size26&quot;&gt;8. 정밀도가 줄어드는데 정확도는 괜찮을까?&lt;/h2&gt;
&lt;p data-end=&quot;3629&quot; data-start=&quot;3573&quot; data-ke-size=&quot;size16&quot;&gt;처음엔 &amp;ldquo;숫자를 줄이면 정확도가 떨어지는 거 아닌가?&amp;rdquo; 싶었는데,&lt;br /&gt;실제로는 거의 차이가 없다고 한다.&lt;/p&gt;
&lt;p data-end=&quot;3734&quot; data-start=&quot;3631&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 0.8723151과 0.87231의 차이는&lt;br /&gt;추론 결과에 거의 영향을 주지 않는다.&lt;br /&gt;그래서 fp16, int8로 줄여도 결과값은 거의 동일하게 나온다.&lt;/p&gt;
&lt;h2 data-end=&quot;3779&quot; data-start=&quot;3741&quot; data-ke-size=&quot;size26&quot;&gt;9. 가중치는 -1 ~ 1인데, 어떻게 양자화시 int로 동작하지?&lt;/h2&gt;
&lt;p data-end=&quot;3826&quot; data-start=&quot;3781&quot; data-ke-size=&quot;size16&quot;&gt;이 부분도 궁금해서 찾아봤는데,&lt;br /&gt;정답은 &lt;b&gt;스케일(scale)&lt;/b&gt; 이었다.&lt;/p&gt;
&lt;p data-end=&quot;3904&quot; data-start=&quot;3828&quot; data-ke-size=&quot;size16&quot;&gt;가중치를 일정한 스케일로 변환해서,&lt;br /&gt;실제로는 0~255 같은 정수를 쓰지만&lt;br /&gt;이를 실수 범위로 다시 환산해 근사값으로 계산한다.&lt;/p&gt;
&lt;p data-end=&quot;3926&quot; data-start=&quot;3906&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 아래처럼 계산된다고 한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&lt;span&gt;scale = (float_max - float_min) / (int8_max - int8_min) = (1.0 - (-1.0)) / (127 - (-128)) = 2.0 / 255 &amp;asymp; 0.007843 &lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4101&quot; data-start=&quot;4064&quot; data-ke-size=&quot;size16&quot;&gt;이런 방식으로 int 값도 실수처럼 근사 동작하게 되는 것이다.&lt;/p&gt;
&lt;p data-end=&quot;4116&quot; data-start=&quot;4103&quot; data-ke-size=&quot;size16&quot;&gt;결국 양자화의 본질은&lt;/p&gt;
&lt;blockquote data-end=&quot;4176&quot; data-start=&quot;4117&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;4176&quot; data-start=&quot;4119&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;모델의 수를 적은 비트로 근사 표현해서 VRAM과 계산량을 줄이는 기술&amp;rdquo;&lt;br /&gt;이라고 정리할 수 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-end=&quot;4181&quot; data-start=&quot;4178&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;4215&quot; data-start=&quot;4183&quot; data-ke-size=&quot;size26&quot;&gt;10. 근데 왜 같은 7B 모델인데 크기가 다르지?&lt;/h2&gt;
&lt;p data-end=&quot;4311&quot; data-start=&quot;4217&quot; data-ke-size=&quot;size16&quot;&gt;허깅페이스 같은 곳에서 모델을 보면&lt;br /&gt;7B 모델인데 어떤 건 13GB, 어떤 건 20GB다.&lt;/p&gt;
&lt;p data-end=&quot;4311&quot; data-start=&quot;4217&quot; data-ke-size=&quot;size16&quot;&gt;앞에서 B과 모델 크기는 7B에 flaut32일 경우 28GB에 해당할 것 이다.&amp;nbsp;&lt;br /&gt;이건 단순히 파라미터 수가 같다고 해서 크기도 같지 않기 때문이다.&lt;/p&gt;
&lt;p data-end=&quot;4328&quot; data-start=&quot;4313&quot; data-ke-size=&quot;size16&quot;&gt;이유는 크게 두 가지다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;4457&quot; data-start=&quot;4330&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;4388&quot; data-start=&quot;4330&quot;&gt;&lt;b&gt;저장 포맷의 차이&lt;/b&gt; &amp;mdash; .safetensors, .gguf 등 저장 방식이 다르다.&lt;/li&gt;
&lt;li data-end=&quot;4457&quot; data-start=&quot;4389&quot;&gt;&lt;b&gt;구조적 요소의 차이&lt;/b&gt; &amp;mdash; 레이어 개수, hidden size, embedding 구조 등이 모델마다 다르다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-end=&quot;4482&quot; data-start=&quot;4459&quot; data-ke-size=&quot;size16&quot;&gt;또한 모델 파일에는 weight 외에도&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;4618&quot; data-start=&quot;4483&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;4495&quot; data-start=&quot;4483&quot;&gt;옵티마이저 상태&lt;/li&gt;
&lt;li data-end=&quot;4530&quot; data-start=&quot;4496&quot;&gt;학습 통계 (running mean, variance)&lt;/li&gt;
&lt;li data-end=&quot;4543&quot; data-start=&quot;4531&quot;&gt;토크나이저 정보&lt;/li&gt;
&lt;li data-end=&quot;4618&quot; data-start=&quot;4544&quot;&gt;메타데이터(config, dtype, version 등)&lt;br /&gt;이런 정보들이 함께 포함되기 때문에 실제 크기는 달라질 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI</category>
      <author>usingsystem</author>
      <guid isPermaLink="true">https://usingsystem.tistory.com/576</guid>
      <comments>https://usingsystem.tistory.com/576#entry576comment</comments>
      <pubDate>Thu, 6 Nov 2025 20:16:57 +0900</pubDate>
    </item>
    <item>
      <title>[NetWork] 기업에서 VPN과 ACL를 사용하는 이유</title>
      <link>https://usingsystem.tistory.com/574</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;VPN(Virtual Private Network)란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VPN은 공용 네트워크(예: 인터넷)를 통해 사설 네트워크에 안전하게 접속할 수 있도록 하는 암호화된 터널로 인터넷을 통해도 내부망처럼 동작가능합니다 이런 동작이 가능한 이유는 IP마스킹을&amp;nbsp;통해&amp;nbsp;사용자&amp;nbsp;IP를&amp;nbsp;숨기고&amp;nbsp;사내망&amp;nbsp;IP로&amp;nbsp;대체&amp;nbsp;하기&amp;nbsp;때문입니다. &lt;br /&gt;즉&amp;nbsp;VPN은&amp;nbsp;인증된&amp;nbsp;사용자만&amp;nbsp;원격&amp;nbsp;접속을&amp;nbsp;가능하게&amp;nbsp;하는&amp;nbsp;고급&amp;nbsp;암호화&amp;nbsp;및&amp;nbsp;인증&amp;nbsp;기법입니다. &lt;br /&gt;&lt;br /&gt;기업에서&amp;nbsp;VPN을&amp;nbsp;사용하는&amp;nbsp;이유는&amp;nbsp;보통&amp;nbsp;원격&amp;nbsp;근무&amp;nbsp;지원,&amp;nbsp;보안&amp;nbsp;통신,&amp;nbsp;내부망&amp;nbsp;리소스&amp;nbsp;접근&amp;nbsp;제어,&amp;nbsp;지사&amp;nbsp;간&amp;nbsp;통신&amp;nbsp;보안&amp;nbsp;확보를&amp;nbsp;위해&amp;nbsp;사용됩니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&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;/b&gt; &lt;/b&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;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;내부망 리소스 접근 제어&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;VPN을 통해서만 접근 가능한 시스템 구성 가능&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;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ACL이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크&amp;nbsp;트래픽의&amp;nbsp;접근을&amp;nbsp;제어하기&amp;nbsp;위한&amp;nbsp;목적으로&amp;nbsp;방화벽&amp;nbsp;등에서&amp;nbsp;특정&amp;nbsp;IP주소&amp;nbsp;포트&amp;nbsp;등에&amp;nbsp;대해&amp;nbsp;접근을&amp;nbsp;허용&amp;nbsp;또는&amp;nbsp;차단하는&amp;nbsp;규칙을&amp;nbsp;설정할&amp;nbsp;수&amp;nbsp;있습니다. &lt;br /&gt;즉&amp;nbsp;라우터나&amp;nbsp;방화벽등을&amp;nbsp;설정해서&amp;nbsp;접근을&amp;nbsp;제어합니다. &lt;br /&gt;&lt;br /&gt;기업에서&amp;nbsp;ACL을&amp;nbsp;사용하는&amp;nbsp;이유는&amp;nbsp;보안&amp;nbsp;강화,&amp;nbsp;서비스&amp;nbsp;분리,&amp;nbsp;악성&amp;nbsp;트래픽&amp;nbsp;대응,&amp;nbsp;네트워크&amp;nbsp;성능&amp;nbsp;최적화가&amp;nbsp;있습니다. &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%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;사용 목적&lt;/b&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;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;서비스 분리&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;각 부서별로 서버나 서비스 접근을 제한 (예: 인사팀만 인사DB 접근 허용)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;DDoS/악성 트래픽 대응&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;악성 IP나 특정 포트 트래픽을 즉시 차단 가능&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;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;VPN&amp;nbsp;+&amp;nbsp;ACL을&amp;nbsp;사용한다면?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은&amp;nbsp;기업은&amp;nbsp;ACL&amp;nbsp;+&amp;nbsp;VPN을&amp;nbsp;조합하여&amp;nbsp;이중&amp;nbsp;보안&amp;nbsp;체계를&amp;nbsp;운영합니다. &lt;br /&gt;&lt;br /&gt;VPN을&amp;nbsp;통해&amp;nbsp;먼저&amp;nbsp;인증&amp;nbsp;&amp;rarr;&amp;nbsp;내부&amp;nbsp;네트워크로&amp;nbsp;접근&amp;nbsp;허용 &lt;br /&gt;ACL을&amp;nbsp;통해&amp;nbsp;접근&amp;nbsp;권한&amp;nbsp;세분화&amp;nbsp;&amp;rarr;&amp;nbsp;특정&amp;nbsp;리소스만&amp;nbsp;접근&amp;nbsp;가능 &lt;br /&gt;&lt;br /&gt;EX) &lt;br /&gt;1.&amp;nbsp;외부망으로&amp;nbsp;VPN&amp;nbsp;접속&amp;nbsp;-&amp;gt;&amp;nbsp;ACL권한에&amp;nbsp;따라&amp;nbsp;인사팀만&amp;nbsp;인사&amp;nbsp;DB&amp;nbsp;접근&amp;nbsp;허용 &lt;br /&gt;2.&amp;nbsp;외부망으로&amp;nbsp;VPN&amp;nbsp;접속&amp;nbsp;-&amp;gt;&amp;nbsp;ACL권한에&amp;nbsp;따라&amp;nbsp;특정&amp;nbsp;IP나&amp;nbsp;Port만&amp;nbsp;접근할&amp;nbsp;수&amp;nbsp;있게&amp;nbsp;허용&lt;/p&gt;</description>
      <category>IT지식/NetWork</category>
      <author>usingsystem</author>
      <guid isPermaLink="true">https://usingsystem.tistory.com/574</guid>
      <comments>https://usingsystem.tistory.com/574#entry574comment</comments>
      <pubDate>Tue, 5 Aug 2025 16:33:24 +0900</pubDate>
    </item>
  </channel>
</rss>