<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>o_joon_.log</title>
        <link>https://velog.io/</link>
        <description>안녕하세요.</description>
        <lastBuildDate>Thu, 30 Jan 2025 06:56:46 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>o_joon_.log</title>
            <url>https://velog.velcdn.com/images/o_joon_/profile/a37de196-ba4f-4ad2-8521-bf220a8aac92/image.JPG</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. o_joon_.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/o_joon_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[pandas] 기본 문법 연습 - 1]]></title>
            <link>https://velog.io/@o_joon_/pandas-%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%95-%EC%97%B0%EC%8A%B5-1</link>
            <guid>https://velog.io/@o_joon_/pandas-%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%95-%EC%97%B0%EC%8A%B5-1</guid>
            <pubDate>Thu, 30 Jan 2025 06:56:46 GMT</pubDate>
            <description><![CDATA[<p>pandas에 대해 공부하면서, 기본 문법부터 알아두는게 필수이기 때문에 정리하는 중입니다.
pandas 10분 완성이라는 기초 가이드 페이지가 있어, 이를 보고 따라해보며 문법을 익히고 정리하는 포스팅입니다.</p>
<ul>
<li><a href="https://dataitgirls2.github.io/10minutes2pandas/">관련 링크</a></li>
</ul>
<p>내용이 생각보다 길어서, 2번 또는 3번에 나눠서 포스팅 할 예정입니다.
가이드에서 나온 설명에는 부족한 점이 있어서 추가 내용을 포함하여 정리중입니다.</p>
<p>Jupyter lab, VsCode를 통한 ipynb 파일 기준으로 작성했습니다.</p>
<hr>
<h3 id="목차">목차</h3>
<ol>
<li>Object Creation (객체 생성)</li>
<li>Viewing Data (데이터 확인하기)</li>
<li>Selection (선택)</li>
</ol>
<hr>
<p>먼저, 필요한 패키지를 설치해줍니다.
pandas, numpy, matplotlib를 설치하는데, import 전에 <code>pip install</code>을 통해 설치해주어야 합니다.
VSCode에서 터미널을 통해 설치해 줄 수 있습니다.</p>
<pre><code class="language-bash">$ pip install pandas
$ pip install numpy
$ pip install matplotlib</code></pre>
<p>설치가 완료되었으면 패키지를 불러옵니다.
일반적으로 각 패키지는 pd, np, plt라는 이름으로 불러옵니다.</p>
<pre><code class="language-python">import pandas as pd
import numpy as np
import matplotlib.pyplot as plt</code></pre>
<h2 id="1-object-creation-객체-생성">1. Object Creation (객체 생성)</h2>
<p>Pandas는 값을 가지고 있는 리스트를 통해 <code>Series</code>를 만들고, 정수로 만들어진 인덱스를 기본값으로 불러옵니다.</p>
<pre><code class="language-python">s = pd.Series([1, 3, 5, np.nan, 6, 8])</code></pre>
<pre><code class="language-python">s</code></pre>
<p><strong>Output:</strong></p>
<pre><code>0    1.0
1    3.0
2    5.0
3    NaN
4    6.0
5    8.0
dtype: float64</code></pre><ul>
<li><p><code>Series</code>: pandas에서 사용하는 <code>1차원 데이터 구조</code>입니다. 배열처럼 사용되며, 인덱스를 가집니다.
DataFrame의 한 열(column)이라고 보시면 되겠습니다.</p>
</li>
<li><p><code>np.nan</code>: <code>NaN</code> 값, 즉 값이 없는 결측 데이터입니다.</p>
</li>
<li><p><code>dtype</code>: <code>데이터 타입(data type)</code> 입니다. <code>int64</code>, <code>float64</code>, <code>bool</code>, <code>datetime64</code> 등이 있습니다.</p>
</li>
</ul>
<br>

<p><code>datatime</code> 인덱스와 레이블(Label)이 있는 column을 가지고 있는 numpy 배열을 전달하여 데이터프레임을 생성합니다. </p>
<pre><code class="language-python">dates = pd.date_range(&#39;20250123&#39;, periods=6)</code></pre>
<pre><code class="language-python">dates</code></pre>
<p><strong>Output:</strong></p>
<pre><code>DatetimeIndex([&#39;2025-01-23&#39;, &#39;2025-01-24&#39;, &#39;2025-01-25&#39;, &#39;2025-01-26&#39;,
               &#39;2025-01-27&#39;, &#39;2025-01-28&#39;],
              dtype=&#39;datetime64[ns]&#39;, freq=&#39;D&#39;)</code></pre><ul>
<li>periods를 설정하면, 입력한 날짜부터 n일차 까지의 날짜를 차례대로 생성합니다.</li>
</ul>
<br>


<pre><code class="language-python">df = pd.DataFrame(np.random.randn(6, 4), index=dates, columns=list(&#39;ABCD&#39;))</code></pre>
<pre><code class="language-python">df</code></pre>
<p><strong>Output:</strong></p>
<pre><code>                   A         B         C         D
2025-01-23  0.470623 -1.544299 -0.359713  0.341314
2025-01-24 -0.989620 -0.296540  0.083644 -1.485904
2025-01-25  0.602569 -0.716880  0.785357  0.572783
2025-01-26 -0.621514 -0.318548 -1.075782  2.061741
2025-01-27 -1.626353 -1.416525 -0.516248 -1.631344
2025-01-28 -0.817248  1.168349 -0.803450 -1.352421</code></pre><ul>
<li>각 row의 인덱스를 날짜 순서대로, column은 A, B, C, D로 하는 DataFrame을 생성합니다.</li>
<li><code>np.random.randn(row, column)</code>: 행(row)과 열(column)의 개수만큼 행렬을 생성하며, 각 칸마다 정규분포에서 난수를 생성합니다. 대부분 <code>-3에서 3사이의 값</code>입니다.</li>
</ul>
<br>


<p>Series와 같은 것으로 변환될 수 있는 객체들의 <code>dictionary</code>로 구성된 데이터프레임을 만듭니다.
각 Key는 column을, Value는 칸에 맞는 값이 할당됩니다. row는 기본 인덱스로 지정이되며, 따로 설정해주고 싶은 경우는 <code>pd.DataFrame.from_dict()</code> 등을 통해 가능하다고 하네요.</p>
<pre><code class="language-python">df2 = pd.DataFrame({
    &#39;A&#39;: 1.,
    &#39;B&#39;: pd.Timestamp(&#39;20250123&#39;),
    &#39;C&#39;: pd.Series(1, index=list(range(4)), dtype=&#39;float32&#39;),
    &#39;D&#39;: np.array([3] * 4, dtype=&#39;int32&#39;),
    &#39;E&#39;: pd.Categorical([&quot;test&quot;, &quot;traing&quot;, &quot;test&quot;, &quot;traint&quot;]),
    &#39;F&#39;: &#39;foo&#39;
})</code></pre>
<pre><code class="language-python">df2</code></pre>
<p><strong>Output:</strong></p>
<pre><code>     A          B    C  D       E    F
0  1.0 2025-01-23  1.0  3    test  foo
1  1.0 2025-01-23  1.0  3  traing  foo
2  1.0 2025-01-23  1.0  3    test  foo
3  1.0 2025-01-23  1.0  3  traint  foo</code></pre><ul>
<li><code>pd.Timestamp</code>: 날짜와 시간 정보를 포함하는 객체입니다.</li>
<li><code>pd.Categorical</code>: 메모리 최적화를 위해 유한한 범주의 값을 나타내는 객체입니다.
보통 문자열이나 정수로 표현하고, 데이터를 압축하기 때문에 메모리 사용량을 줄여 대규모 데이터에 유리하다고 합니다.</li>
</ul>
<br>

<p>DataFrame의 결과물의 column은 다양한 데이터 타입(dtpyes)으로 구성됩니다.</p>
<pre><code class="language-python">df2.dtypes</code></pre>
<p><strong>Output:</strong></p>
<pre><code>A          float64
B    datetime64[s]
C          float32
D            int32
E         category
F           object
dtype: object</code></pre><br>

<h2 id="2-viewing-data-데이터-확인하기">2. Viewing Data (데이터 확인하기)</h2>
<p>DataFrame의 가장 윗 줄과 마지막 줄을 확인하고 싶을 때에 사용하는 방법은 다음과 같습니다.</p>
<ul>
<li>괄호 안에는 숫자가 들어갈 수도 있고 안 들어갈 수도 있습니다.</li>
<li>숫자가 들어간다면, 윗 / 마지막 줄의 특정 줄을 불러올 수 있습니다.</li>
<li>숫자가 들어가지 않는다면, 기본값은 5로 처리됩니다.</li>
</ul>
<pre><code class="language-python">df.tail(3) # 끝에서 마지막 3줄
df.tail() # 끝에서 마지막 5줄</code></pre>
<p><strong>Output:</strong></p>
<pre><code>                   A         B         C         D
2025-01-24 -0.989620 -0.296540  0.083644 -1.485904
2025-01-25  0.602569 -0.716880  0.785357  0.572783
2025-01-26 -0.621514 -0.318548 -1.075782  2.061741
2025-01-27 -1.626353 -1.416525 -0.516248 -1.631344
2025-01-28 -0.817248  1.168349 -0.803450 -1.352421</code></pre><ul>
<li>해당 Output은 둘째 줄 코드인 <code>df.tail()</code>의 결과로, 뒤에서부터 5개의 row를 불러옵니다.</li>
</ul>
<br>

<pre><code class="language-python">df.head() # 처음에서 5줄</code></pre>
<p><strong>Output:</strong></p>
<pre><code>                   A         B         C         D
2025-01-23  0.470623 -1.544299 -0.359713  0.341314
2025-01-24 -0.989620 -0.296540  0.083644 -1.485904
2025-01-25  0.602569 -0.716880  0.785357  0.572783
2025-01-26 -0.621514 -0.318548 -1.075782  2.061741
2025-01-27 -1.626353 -1.416525 -0.516248 -1.631344</code></pre><ul>
<li>위에서부터 5개의 row를 불러옵니다.</li>
</ul>
<br>


<pre><code class="language-python">df.tail(3)</code></pre>
<p><strong>Output:</strong></p>
<pre><code>                   A         B         C         D
2025-01-26 -0.621514 -0.318548 -1.075782  2.061741
2025-01-27 -1.626353 -1.416525 -0.516248 -1.631344
2025-01-28 -0.817248  1.168349 -0.803450 -1.352421</code></pre><ul>
<li>뒤에서부터 3개의 row를 불러옵니다.</li>
</ul>
<br>


<p>인덱스 (index), 열 (column) 그리고 numpy 데이터에 대한 세부 정보를 봅니다.</p>
<pre><code class="language-python">df.index</code></pre>
<p><strong>Output:</strong></p>
<pre><code>DatetimeIndex([&#39;2025-01-23&#39;, &#39;2025-01-24&#39;, &#39;2025-01-25&#39;, &#39;2025-01-26&#39;,
               &#39;2025-01-27&#39;, &#39;2025-01-28&#39;],
              dtype=&#39;datetime64[ns]&#39;, freq=&#39;D&#39;)</code></pre><ul>
<li>index를 통해 각 row를 구분하는 인덱스의 배열을 불러올 수 있습니다.</li>
<li><code>freq</code>: <code>DatatimeIndex</code>가 있는 경우 출력되는 <code>시간 주기(frequency)</code> 입니다.<br>뒤에 붙는 문자열은 다음과 같습니다.<ul>
<li><code>D</code>:    일 단위 (Daily)</li>
<li><code>W</code>: 주 단위 (Weekly)</li>
<li><code>M</code>:    월 단위 (Month-End)</li>
<li><code>Q</code>:    분기 단위 (Quarter-End)</li>
<li><code>Y</code> or <code>A</code>:    연 단위 (Year-End)</li>
<li><code>H</code>:    시간 단위 (Hourly)</li>
<li><code>T</code>: or min    분 단위 (Minute)</li>
<li><code>S</code>:    초 단위 (Second)</li>
</ul>
</li>
</ul>
<br>

<pre><code class="language-python">df.columns</code></pre>
<p><strong>Output:</strong></p>
<pre><code>Index([&#39;A&#39;, &#39;B&#39;, &#39;C&#39;, &#39;D&#39;], dtype=&#39;object&#39;)</code></pre><ul>
<li>colums를 통해 column의 배열을 불러올 수 있습니다.</li>
</ul>
<br>

<pre><code class="language-python">df.values</code></pre>
<p><strong>Output:</strong></p>
<pre><code>array([[ 0.47062305, -1.54429928, -0.3597129 ,  0.34131384],
       [-0.98961962, -0.29653974,  0.08364368, -1.48590371],
       [ 0.60256886, -0.71687987,  0.78535709,  0.57278294],
       [-0.62151395, -0.31854779, -1.0757822 ,  2.06174073],
       [-1.62635282, -1.41652459, -0.51624819, -1.63134446],
       [-0.81724815,  1.16834876, -0.80344996, -1.35242074]])</code></pre><ul>
<li>values를 통해 DataFrame의 모든 값을 2차원 배열의 형태로 불러올 수 있습니다.</li>
</ul>
<br>


<p><code>describe()</code>는 데이터의 대략적인 통계적 정보 요약을 보여줍니다.</p>
<pre><code class="language-python">df.describe()</code></pre>
<p><strong>Output:</strong></p>
<pre><code>              A         B         C         D
count  6.000000  6.000000  6.000000  6.000000
mean  -0.496924 -0.520740 -0.314365 -0.248972
std    0.869657  0.983132  0.667768  1.484700
min   -1.626353 -1.544299 -1.075782 -1.631344
25%   -0.946527 -1.241613 -0.731650 -1.452533
50%   -0.719381 -0.517714 -0.437981 -0.505553
75%    0.197589 -0.302042 -0.027195  0.514916
max    0.602569  1.168349  0.785357  2.061741</code></pre><ul>
<li><code>count</code>: column의 유효값(결측값이 아닌 값)의 개수</li>
<li><code>mean</code>: column의 평균값</li>
<li><code>std</code>: column의 표준편차 (데이터의 분산 정도)</li>
<li><code>min</code>: column의 최솟값</li>
<li><code>25%</code>: column의 1사분위수 (데이터의 하위 25% 지점)</li>
<li><code>50%</code>: column의 중앙값 (데이터의 50% 지점, 2사분위수 또는 중위수)</li>
<li><code>75%</code>: column의 3사분위수 (데이터의 상위 25%를 제외한 하위 75% 지점)</li>
<li><code>max</code>: column의 최댓값</li>
</ul>
<br>


<p>데이터를 전치합니다.</p>
<pre><code class="language-python">df.T</code></pre>
<p><strong>Output:</strong></p>
<pre><code>   2025-01-23  2025-01-24  2025-01-25  2025-01-26  2025-01-27  2025-01-28
A    0.470623   -0.989620    0.602569   -0.621514   -1.626353   -0.817248
B   -1.544299   -0.296540   -0.716880   -0.318548   -1.416525    1.168349
C   -0.359713    0.083644    0.785357   -1.075782   -0.516248   -0.803450
D    0.341314   -1.485904    0.572783    2.061741   -1.631344   -1.352421</code></pre><ul>
<li>row와 column이 뒤집힌 것을 볼 수 있습니다.</li>
</ul>
<br>



<p><code>sort_index()</code>에서 axis = 0인 경우 행(row)을 기준으로 정렬하고, axis = 1인 경우 열(column)을 기준으로 정렬합니다.</p>
<pre><code class="language-python">df.sort_index(axis=1, ascending=False)</code></pre>
<p><strong>Output:</strong></p>
<pre><code>                   D         C         B         A
2025-01-23  0.341314 -0.359713 -1.544299  0.470623
2025-01-24 -1.485904  0.083644 -0.296540 -0.989620
2025-01-25  0.572783  0.785357 -0.716880  0.602569
2025-01-26  2.061741 -1.075782 -0.318548 -0.621514
2025-01-27 -1.631344 -0.516248 -1.416525 -1.626353
2025-01-28 -1.352421 -0.803450  1.168349 -0.817248</code></pre><ul>
<li><code>ascending</code>: 정렬 기준을 나타냅니다. <code>False는 내림차순</code>, <code>True는 오름차순</code>을 기준으로 정렬합니다.<br><code>default</code> 값은 <code>False</code> 입니다.</li>
<li>해당 결과는 column을 내림차순으로 정렬했으므로, 알파벳이 큰 D부터 작은 A까지 정렬되었습니다.</li>
</ul>
<br>

<p>값 별로 정렬합니다.</p>
<pre><code class="language-python">df.sort_values(by=&#39;B&#39;)</code></pre>
<p><strong>Output:</strong></p>
<pre><code>                   A         B         C         D
2025-01-23  0.470623 -1.544299 -0.359713  0.341314
2025-01-27 -1.626353 -1.416525 -0.516248 -1.631344
2025-01-25  0.602569 -0.716880  0.785357  0.572783
2025-01-26 -0.621514 -0.318548 -1.075782  2.061741
2025-01-24 -0.989620 -0.296540  0.083644 -1.485904
2025-01-28 -0.817248  1.168349 -0.803450 -1.352421</code></pre><ul>
<li><code>B</code>를 기준으로 각 row를 내림차순 합니다.</li>
</ul>
<br>

<h2 id="3-selection-선택">3. Selection (선택)</h2>
<p>Python 및 Numpy의 선택과 설정을 위한 표현들은 직관적이고 코드 작성을 위한 작업에 유용합니다.
<code>.at</code>, <code>.iat</code>, <code>.loc</code>, <code>.iloc</code>가 pandas에 최적화되어 있기 때문에 사용하는 것을 추천한다고 합니다.</p>
<h3 id="getting-데이터-얻기">Getting (데이터 얻기)</h3>
<p><code>df.A</code>와 동일한 Series를  생성하는 단일 column을 선택합니다.</p>
<pre><code class="language-python">df[&#39;A&#39;]</code></pre>
<p><strong>Output:</strong></p>
<pre><code>2025-01-23    0.470623
2025-01-24   -0.989620
2025-01-25    0.602569
2025-01-26   -0.621514
2025-01-27   -1.626353
2025-01-28   -0.817248
Freq: D, Name: A, dtype: float64</code></pre><ul>
<li>해당 결과의 데이터 구조는 <code>Series</code> 입니다.</li>
</ul>
<br>


<p>슬라이싱을 통해 row를 선택할 수 있습니다.</p>
<pre><code class="language-python">df[0:3]</code></pre>
<p><strong>Output:</strong></p>
<pre><code>                   A         B         C         D
2025-01-23  0.470623 -1.544299 -0.359713  0.341314
2025-01-24 -0.989620 -0.296540  0.083644 -1.485904
2025-01-25  0.602569 -0.716880  0.785357  0.572783</code></pre><ul>
<li>0번째 인덱스(&#39;20250123&#39;)부터 2번째 인덱스(&#39;20250125&#39;)까지의 row를 불러옵니다.</li>
</ul>
<br>

<pre><code class="language-python">df[&#39;20250123&#39;:&#39;20250125&#39;]</code></pre>
<p><strong>Output:</strong></p>
<pre><code>                   A         B         C         D
2025-01-23  0.470623 -1.544299 -0.359713  0.341314
2025-01-24 -0.989620 -0.296540  0.083644 -1.485904
2025-01-25  0.602569 -0.716880  0.785357  0.572783</code></pre><ul>
<li>직접 입력한 범위의 row를 불러옵니다. 숫자 인덱스와는 다르게 <code>뒤의 범위를 포함</code>합니다.</li>
</ul>
<br>


<h3 id="selection-by-label-label을-통한-선택">Selection by Label (Label을 통한 선택)</h3>
<p>Label을 사용하여 횡단면을 얻습니다.</p>
<pre><code class="language-python">df.loc[dates[0]]</code></pre>
<p><strong>Output:</strong></p>
<pre><code>A    0.470623
B   -1.544299
C   -0.359713
D    0.341314
Name: 2025-01-23 00:00:00, dtype: float64</code></pre><ul>
<li>결과의 데이터 구조는 <code>Series</code> 입니다.</li>
<li>0번째 인덱스(&#39;20250123&#39;)의 각 column에 대한 정보입니다.</li>
<li><code>loc</code>: <code>Label을 기준</code>으로 데이터를 불러옵니다.</li>
</ul>
<br>


<p>Label을 사용하여 여러 축의 데이터를 얻습니다.</p>
<pre><code class="language-python">df.loc[:, [&#39;A&#39;, &#39;B&#39;]]</code></pre>
<p><strong>Output:</strong></p>
<pre><code>                   A         B
2025-01-23  0.470623 -1.544299
2025-01-24 -0.989620 -0.296540
2025-01-25  0.602569 -0.716880
2025-01-26 -0.621514 -0.318548
2025-01-27 -1.626353 -1.416525
2025-01-28 -0.817248  1.168349</code></pre><ul>
<li>결과의 데이터 구조는 <code>DataFrame</code> 입니다.</li>
<li>loc에서는 <code>콤마(,)</code>를 기준으로 row, column을 나타내는데 <code>:</code>은 <code>모든 행</code>을, <code>[&#39;A&#39;, &#39;B&#39;]</code>는 <code>&#39;A&#39;와 &#39;B&#39; column</code>을 나타냅니다.</li>
<li>이렇게 row와 column의 범위를 모두 지정해주면 2차원 데이터 구조인 DataFrame을, 하나만 지정하면 1차원 데이터 구조인 Series 타입의 결과를 얻게됩니다.<br>(범위를 지정해야 2차원이고, 값 하나만 지정하면 1차원입니다. 아래와 비교하면 이해하기 편합니다.)</li>
</ul>
<br>


<p>양쪽 종단점을 포함한 Label 슬라이싱을 불러옵니다.</p>
<pre><code class="language-python">df.loc[&#39;20250123&#39;:&#39;20250125&#39;, [&#39;A&#39;,&#39;B&#39;]]</code></pre>
<p><strong>Output:</strong></p>
<pre><code>                   A         B
2025-01-23  0.470623 -1.544299
2025-01-24 -0.989620 -0.296540
2025-01-25  0.602569 -0.716880</code></pre><ul>
<li>row와 column를 지정한 만큼 불러옵니다.</li>
</ul>
<br>


<p>반환되는 객체의 차원을 줄입니다.</p>
<pre><code class="language-python">df.loc[&#39;20250123&#39;, [&#39;A&#39;,&#39;B&#39;]]</code></pre>
<p><strong>Output:</strong></p>
<pre><code>A    0.470623
B   -1.544299
Name: 2025-01-23 00:00:00, dtype: float64</code></pre><ul>
<li>하나의 row에 대한 결과를 불러오기 때문에, <code>Series</code> 타입입니다.</li>
</ul>
<br>

<p>스칼라 값을 얻습니다.</p>
<pre><code class="language-python">df.loc[dates[0], &#39;A&#39;]</code></pre>
<p><strong>Output:</strong></p>
<pre><code>0.47062305067298715</code></pre><ul>
<li><code>dates[0]</code>에 해당하는 인덱스(row)와 <code>A</code>에 해당하는 column이 교차하는 지점의 위치에 대한 스칼라 값입니다.<br>이 값을 통해 무엇을 하는지는 아직 잘 모르겠습니다.. 나중에 쓸 일이 있으면 추가하겠습니다!</li>
</ul>
<br>


<p>스칼라 값을 더 빠르게 구하는 방법입니다.</p>
<pre><code class="language-python">df.at[dates[0], &#39;A&#39;]</code></pre>
<p><strong>Output:</strong></p>
<pre><code>0.47062305067298715</code></pre><ul>
<li><code>at</code>: 단일 값인 <code>스칼라값에만 접근</code>합니다. <code>loc</code>는 <code>여러 개의 값을 선택</code>할 수 있지만 <code>at</code>은 <code>하나의 값만 선택</code>하기 때문에 <code>스칼라 값을 구하는 경우에 더 빠릅니다.</code></li>
</ul>
<br>


<h3 id="selection-by-position-위치로-선택하기">Selection by Position (위치로 선택하기)</h3>
<p>넘겨받은 정수의 위치(인덱스)를 기준으로 선택합니다.
loc가 인덱스의 특정 값을 통해 범위를 지정했다면, iloc는 인덱스 위치(배열의 0번째 인덱스와 같이)로 접근합니다.</p>
<pre><code class="language-python">df.iloc[3]</code></pre>
<p><strong>Output:</strong></p>
<pre><code>A   -0.621514
B   -0.318548
C   -1.075782
D    2.061741
Name: 2025-01-26 00:00:00, dtype: float64</code></pre><ul>
<li>인덱스의 3번째 값인 <code>20250126</code>에 대한 <code>Series</code> 데이터입니다.</li>
</ul>
<br>


<p>정수로 표기된 슬라이스들을 통해, Python/Numpy와 유사하게 작동합니다.</p>
<pre><code class="language-python">df.iloc[3:5, 0:2]</code></pre>
<p><strong>Output:</strong></p>
<pre><code>                   A         B
2025-01-26 -0.621514 -0.318548
2025-01-27 -1.626353 -1.416525</code></pre><ul>
<li>row의 3번째 인덱스부터 4번째 인덱스, column의 0번째 인덱스부터 1번째 인덱스까지의 <code>DataFrame</code> 데이터입니다.</li>
</ul>
<br>


<p>정수로 표기된 위치값의 리스트들을 통해, Python/Numpy의 스타일과 유사해집니다.</p>
<pre><code class="language-python">df.iloc[[1,2,4],[0,2]]</code></pre>
<p><strong>Output:</strong></p>
<pre><code>                   A         C
2025-01-24 -0.989620  0.083644
2025-01-25  0.602569  0.785357
2025-01-27 -1.626353 -0.516248</code></pre><ul>
<li>범위가 아닌 특정 row의 인덱스 위치를 리스트에 담아 불러올 수 있습니다.</li>
</ul>
<br>


<p>명시적으로 행을 나누고자 하는 경우입니다.</p>
<pre><code class="language-python">df.iloc[1:3,:]</code></pre>
<p><strong>Output:</strong></p>
<pre><code>                   A        B         C         D
2025-01-24 -0.989620 -0.29654  0.083644 -1.485904
2025-01-25  0.602569 -0.71688  0.785357  0.572783</code></pre><ul>
<li>row의 1번째 인덱스부터 2번째 인덱스, 모든 column의 <code>DataFrame</code> 데이터입니다.</li>
</ul>
<br>


<p>명시적으로 열을 나누고자 하는 경우입니다.</p>
<pre><code class="language-python">df.iloc[:,1:3]</code></pre>
<p><strong>Output:</strong></p>
<pre><code>                   B         C
2025-01-23 -1.544299 -0.359713
2025-01-24 -0.296540  0.083644
2025-01-25 -0.716880  0.785357
2025-01-26 -0.318548 -1.075782
2025-01-27 -1.416525 -0.516248
2025-01-28  1.168349 -0.803450</code></pre><ul>
<li>위와 동일하며, 범위만 다릅니다.</li>
</ul>
<br>


<p>명시적으로 (특정한) 값을 얻고자 하는 경우입니다.</p>
<pre><code class="language-python">df.iloc[1,1]</code></pre>
<p><strong>Output:</strong></p>
<pre><code>-0.29653974092309904</code></pre><ul>
<li><code>[20250124, B]</code>에 대한 스칼라 값입니다.</li>
</ul>
<br>

<p>스칼라 값을 빠르게 얻는 방법 입니다. (위와 동일)</p>
<pre><code class="language-python">df.iat[1,1]</code></pre>
<p><strong>Output:</strong></p>
<pre><code>-0.29653974092309904</code></pre><ul>
<li>loc, at에서의 차이와 같습니다.</li>
</ul>
<br>


<h3 id="boolean-indexing">Boolean Indexing</h3>
<p>데이터를 선택하기 위해 단일 열의 값을 사용합니다.</p>
<pre><code class="language-python">df[df.A &gt; 0]</code></pre>
<p><strong>Output:</strong></p>
<pre><code>                   A         B         C         D
2025-01-23  0.470623 -1.544299 -0.359713  0.341314
2025-01-25  0.602569 -0.716880  0.785357  0.572783</code></pre><ul>
<li><code>A</code>가 <code>0보다 큰 데이터만 필터링</code>한 데이터입니다.</li>
</ul>
<br>

<p>Boolean 조건을 충족하는 DataFrame에서 값을 선택합니다.</p>
<pre><code class="language-python">df[df &gt; 0]</code></pre>
<p><strong>Output:</strong></p>
<pre><code>                   A         B         C         D
2025-01-23  0.470623       NaN       NaN  0.341314
2025-01-24       NaN       NaN  0.083644       NaN
2025-01-25  0.602569       NaN  0.785357  0.572783
2025-01-26       NaN       NaN       NaN  2.061741
2025-01-27       NaN       NaN       NaN       NaN
2025-01-28       NaN  1.168349       NaN       NaN</code></pre><ul>
<li>모든 데이터 중, <code>0보다 큰 경우</code>에만 <code>값</code>을 표시하고, <code>아닌 경우</code>는 <code>NaN</code>을 표시합니다.</li>
</ul>
<br>

<p>필터링을 위한 메소드 <code>isin()</code>을 사용합니다.</p>
<pre><code class="language-python">df2 = df.copy()</code></pre>
<pre><code class="language-python">df2[&#39;E&#39;] = [&#39;one&#39;, &#39;one&#39;, &#39;two&#39;, &#39;three&#39;, &#39;four&#39;, &#39;three&#39;]</code></pre>
<pre><code class="language-python">df2</code></pre>
<p><strong>Output:</strong></p>
<pre><code>                   A         B         C         D      E
2025-01-23  0.470623 -1.544299 -0.359713  0.341314    one
2025-01-24 -0.989620 -0.296540  0.083644 -1.485904    one
2025-01-25  0.602569 -0.716880  0.785357  0.572783    two
2025-01-26 -0.621514 -0.318548 -1.075782  2.061741  three
2025-01-27 -1.626353 -1.416525 -0.516248 -1.631344   four
2025-01-28 -0.817248  1.168349 -0.803450 -1.352421  three</code></pre><ul>
<li>지정한 데이터가 들어있는 column <code>E</code>를 추가합니다.</li>
</ul>
<br>

<pre><code class="language-python">df2[df2[&#39;E&#39;].isin([&#39;two&#39;, &#39;four&#39;])]</code></pre>
<p><strong>Output:</strong></p>
<pre><code>                   A         B         C         D     E
2025-01-25  0.602569 -0.716880  0.785357  0.572783   two
2025-01-27 -1.626353 -1.416525 -0.516248 -1.631344  four</code></pre><ul>
<li><code>E</code> column 중, 값이 <code>two</code>와 <code>four</code>인 데이터의 row를 불러옵니다.</li>
</ul>
<br>


<h3 id="setting-설정">Setting (설정)</h3>
<p>새 column을 설정하면 데이터가 인덱스 별로 자동 정렬됩니다.</p>
<pre><code class="language-python">s1 = pd.Series([1, 2, 3, 4, 5, 6], index = pd.date_range(&#39;20250123&#39;, periods=6))</code></pre>
<pre><code class="language-python">s1</code></pre>
<p><strong>Output:</strong></p>
<pre><code>2025-01-23    1
2025-01-24    2
2025-01-25    3
2025-01-26    4
2025-01-27    5
2025-01-28    6
Freq: D, dtype: int64</code></pre><ul>
<li>새로운 데이터를 가진 column을 생성합니다.</li>
</ul>
<br>

<p>위에서 생성한 데이터를 <code>F</code>라는 column에 넣어 추가합니다.</p>
<pre><code class="language-python">df[&#39;F&#39;] = s1</code></pre>
<br>


<p>Label에 의해 값을 설정합니다.</p>
<pre><code class="language-python">df.at[dates[0], &#39;A&#39;] = 0</code></pre>
<ul>
<li><code>[20250123, A]</code>의 값을 0으로 바꿉니다.</li>
</ul>
<br>


<p>위치에 의해 값을 설정합니다.</p>
<pre><code class="language-python">df.iat[0, 1] = 0</code></pre>
<ul>
<li><code>[20250123, B]</code>의 값을 0으로 바꿉니다.</li>
</ul>
<br>


<p>Numpy 배열을 사용한 할당에 의해 값을 설정합니다.</p>
<pre><code class="language-python">df.loc[:, &#39;D&#39;] = np.array([5] * len(df))</code></pre>
<ul>
<li><code>D</code> column의 모든 값을 5로 바꿉니다.</li>
</ul>
<br>


<p>위 설정대로 작동한 결과입니다.</p>
<pre><code class="language-python">df</code></pre>
<p><strong>Output:</strong></p>
<pre><code>                   A         B         C    D  F
2025-01-23  0.000000  0.000000 -0.359713  5.0  1
2025-01-24 -0.989620 -0.296540  0.083644  5.0  2
2025-01-25  0.602569 -0.716880  0.785357  5.0  3
2025-01-26 -0.621514 -0.318548 -1.075782  5.0  4
2025-01-27 -1.626353 -1.416525 -0.516248  5.0  5
2025-01-28 -0.817248  1.168349 -0.803450  5.0  6</code></pre><ul>
<li>정상적으로 바뀌었습니다.</li>
</ul>
<br>

<p>where 연산을 설정한다.</p>
<pre><code class="language-python">df2 = df.copy()</code></pre>
<pre><code class="language-python">df2[df2 &gt; 0] = -df2</code></pre>
<ul>
<li>값이 0보다 큰 데이터를 음수로 바꿉니다.</li>
</ul>
<pre><code class="language-python">df2</code></pre>
<p><strong>Output:</strong></p>
<pre><code>                   A         B         C    D  F
2025-01-23  0.000000  0.000000 -0.359713 -5.0 -1
2025-01-24 -0.989620 -0.296540 -0.083644 -5.0 -2
2025-01-25 -0.602569 -0.716880 -0.785357 -5.0 -3
2025-01-26 -0.621514 -0.318548 -1.075782 -5.0 -4
2025-01-27 -1.626353 -1.416525 -0.516248 -5.0 -5
2025-01-28 -0.817248 -1.168349 -0.803450 -5.0 -6</code></pre><hr>
<p>이번 포스팅에서는 3번째 목차까지 다루었습니다. 다음 포스팅에서는 아마 6번째나 7번째 목차까지 다룰 것 같네요.
at, iat, loc, iloc가 헷갈렸었는데, 이번 공부를 통해 많이 이해하게 되었습니다!
페이지의 제목은 pandas 10분 다루기인데, 10시간으로 바꿔야하는게 아닌가 생각이되네요..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Linux] vim]]></title>
            <link>https://velog.io/@o_joon_/Linux-vim</link>
            <guid>https://velog.io/@o_joon_/Linux-vim</guid>
            <pubDate>Thu, 23 Jan 2025 00:21:29 GMT</pubDate>
            <description><![CDATA[<p>요즘 리눅스를 공부하면서 vim을 자주 만지는데, 역시 익숙하지가 않아서 너무 불편했습니다.
터미널에 vimtutor라는 명령어로 vim에 대해 공부할 수 있어서 가져왔습니다.
새로 공부할 때 마다 추가할 예정입니다!</p>
<hr>
<h1 id="vimtutor">Vimtutor</h1>
<h2 id="11-커서-움직이기">1.1) 커서 움직이기</h2>
<blockquote>
<p><code>h</code>, <code>j</code>, <code>k</code>, <code>l</code>를 통해 커서를 움직일 수 있다.</p>
</blockquote>
<ul>
<li><code>h</code>: 커서를 왼쪽으로 움직인다.</li>
<li><code>j</code>: 커서를 아래쪽으로 움직인다.</li>
<li><code>k</code>: 커서를 위쪽으로 움직인다.</li>
<li><code>l</code>: 커서를 오른쪽으로 움직인다.</li>
</ul>
<h2 id="12-빔을-시작하고-끝내기">1.2) 빔을 시작하고 끝내기</h2>
<blockquote>
<p><code>ESC</code> 키를 눌러 명령 모드로 빠져나올 수 있다.</p>
</blockquote>
<ul>
<li><code>q!</code> + <code>ENTER</code>: 바뀐 내용을 저장하지 않고 편집기를 빠져나간다.</li>
</ul>
<blockquote>
<p>쉘 프롬프트가 보인다면, 다시 vimtutor로 돌아오기 위해 다음과 같이 입력한다.</p>
</blockquote>
<ul>
<li><code>vimtutor</code> + <code>ENTER</code> or <code>vimtutor ko</code>(한국어버전) + <code>ENTER</code></li>
</ul>
<h2 id="13-텍스트-편집---지우기">1.3) 텍스트 편집 - 지우기</h2>
<blockquote>
<p>명령 모드에서 <code>x</code>를 누르면 커서가 위치한 곳의 글자를 지울 수 있다.</p>
</blockquote>
<h2 id="14-텍스트-편집---삽입">1.4) 텍스트 편집 - 삽입</h2>
<blockquote>
<p>명령 모드에서 <code>i</code>를 누르면 텍스트를 입력할 수 있다.</p>
</blockquote>
<p>텍스트를 지울 수는 없다.</p>
<h2 id="15-텍스트-편집---추가">1.5) 텍스트 편집 - 추가</h2>
<blockquote>
<p>명령 모드에서 <code>a</code>를 누르면 텍스트를 추가할 수 있다.</p>
</blockquote>
<p>텍스트를 지울 수 있다.</p>
<h2 id="16-파일-편집">1.6) 파일 편집</h2>
<blockquote>
<p>명령모드에서 <code>:wq</code>를 이용하여 파일을 저장하고 빠져나갈 수 있다.</p>
</blockquote>
<h2 id="1-요약">1 요약</h2>
<ol>
<li><code>h</code>, <code>j</code>, <code>k</code>, <code>l</code>: 커서를 움직인다.<ul>
<li><code>h</code>: 왼쪽</li>
<li><code>j</code>: 아래쪽</li>
<li><code>k</code>: 위쪽</li>
<li><code>l</code>: 오른쪽</li>
</ul>
</li>
<li><code>vim</code> + <code>FILENAME</code> + <code>ENTER</code>: 쉘 프롬프트에서 vim을 시작한다.</li>
<li><code>ESC</code> + <code>:q!</code> + <code>ENTER</code>: 수정한 내용을 무시한 채로 vim에서 빠져나간다.
<code>ESC</code> + <code>:wq</code> + <code>ENTER</code>: 수정한 내용을 저장한 후 vim에서 빠져나간다.</li>
<li><code>x</code>: 명령 모드에서 커서가 위치한 곳의 글자를 지운다.</li>
<li><code>i</code>: 명령 모드에서 커서 앞에 텍스트를 삽입한다.
<code>a</code>: 명령 모드에서 문장 뒤에 텍스트를 추가한다.</li>
</ol>
<hr>
<h2 id="21-삭제-명령">2.1) 삭제 명령</h2>
<blockquote>
<p>한 단어를 끝까지 지우려면 <code>dw</code>라고 치면 된다.</p>
</blockquote>
<ul>
<li><code>.</code> 또는 <code>공백</code>을 기준으로 지운다.</li>
</ul>
<h2 id="22-다른-삭제-명령">2.2) 다른 삭제 명령</h2>
<blockquote>
<p><code>d$</code>라고 치면 그 줄 끝까지 지워진다.</p>
</blockquote>
<h2 id="23-명령과-적용-대상에-대해">2.3) 명령과 적용 대상에 대해</h2>
<blockquote>
<p>삭제 명령 d의 형식은 다음과 같다.
<code>d 대상</code></p>
</blockquote>
<ul>
<li><code>d</code>: 지우는 명령이다.</li>
<li><code>대상</code>: 아래에 제시된 대상에 대해 명령을 수행한다.</li>
</ul>
<p>적용 가능한 대상의 종류</p>
<ul>
<li><code>w</code>: 커서에서 그 단어의 끝까지 지운다. (공백 포함)</li>
<li><code>e</code>: 커서에서 그 단어의 끝까지 지운다. (공백을 포함하지 않음)</li>
<li><code>$</code>: 커서에서 그 줄의 끝까지 지운다.</li>
</ul>
<h2 id="24-대상에-반복-적용하기">2.4) 대상에 반복 적용하기</h2>
<blockquote>
<p>대상 이전에 숫자를 넣어주면 그 만큼 반복된다.</p>
</blockquote>
<ul>
<li><code>숫자</code> + <code>w</code>: 커서를 숫자만큼 단어 뒤로 옮깁니다.</li>
<li><code>숫자</code> + <code>e</code>: 커서를 뒤로 숫자만큼 단어의 끝으로 옮깁니다.</li>
<li><code>0</code>: 문장의 시작부분으로 움직인다.</li>
</ul>
<h2 id="25-삭제에-반복-적용하기">2.5) 삭제에 반복 적용하기</h2>
<blockquote>
<p>명령과 숫자를 함께 사용하면 그만큼 반복 수행된다.
위에서 삭제 명령과 대상의 조합과 같이, 대상 이전에 횟수를 넣어 더 많이 삭제할 수 있다.
<code>d 횟수 대상</code></p>
</blockquote>
<ul>
<li><code>d</code> + <code>숫자</code> + <code>w</code>: 커서에서부터 숫자만큼 단어를 삭제한다.</li>
</ul>
<h2 id="26-줄-전체-조작하기">2.6) 줄 전체 조작하기</h2>
<blockquote>
<p><code>dd</code>를 입력하면 줄 전체를 지운다.
<code>횟수</code> + <code>dd</code> 로 여러 줄을 지울 수 있다.</p>
</blockquote>
<h2 id="27-취소-명령">2.7) 취소 명령</h2>
<blockquote>
<p><code>u</code>를 누르면 마지막 명령이 취소되며, <code>U</code>는 줄 전체를 수정한다.</p>
</blockquote>
<ul>
<li>U는 한 라인 내 모든 수정 사항이 되돌려진다.</li>
<li><code>CTRL</code> + <code>R</code>: 마지막 취소 작업을 다시 실행, 즉 이전에 되돌린 내용을 복원한다.
u로 취소한 변경 사항을 CTRL + R로 취소된 변경 사항을 되돌릴 수 있다.</li>
</ul>
<h2 id="2-요약">2 요약</h2>
<ol>
<li><p><code>dw</code>: 커서가 위치한 곳부터 단어의 끝까지 지운다.</p>
</li>
<li><p><code>d$</code>: 커서가 위치한 곳부터 줄 끝까지 지운다.</p>
</li>
<li><p><code>dd</code>: 줄 전체를 지운다.</p>
</li>
<li><p><code>숫자</code> + <code>w</code>: 횟수와 함께 대상을 반복시킨다.</p>
</li>
<li><p>명령 모드에서 내리는 명령의 형식은 다음과 같다.</p>
<p> <code>[횟수] 명령 대상</code> or <code>명령 [횟수] 대상</code></p>
<ul>
<li><code>횟수</code>: 그 명령을 몇 번 반복할 것인가</li>
<li><code>명령</code>: 어떤 명령을 내릴 것인가</li>
<li><code>대상</code>: 명령이 동작할 대상, 예를 들어 w (단어), $ (줄의 끝) 등</li>
</ul>
</li>
<li><p><code>0</code>: 커서를 문장 맨 앞으로 옮긴다.</p>
</li>
<li><p><code>u</code>: 이전 행동을 취소한다.
<code>U</code>: 한 줄에서 수정한 것을 모두 취소한다.
<code>CTRL-R</code>: 취소한 것을 다시 실행한다.</p>
</li>
</ol>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ: 13397] - Python / 파이썬 - 구간 나누기 2]]></title>
            <link>https://velog.io/@o_joon_/BOJ-13397-Python-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EA%B5%AC%EA%B0%84-%EB%82%98%EB%88%84%EA%B8%B0-2</link>
            <guid>https://velog.io/@o_joon_/BOJ-13397-Python-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EA%B5%AC%EA%B0%84-%EB%82%98%EB%88%84%EA%B8%B0-2</guid>
            <pubDate>Thu, 16 Jan 2025 08:15:37 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<ul>
<li><a href="https://www.acmicpc.net/problem/13397">https://www.acmicpc.net/problem/13397</a></li>
</ul>
<h2 id="시간-복잡도">시간 복잡도</h2>
<ul>
<li>두 수 차의 최댓값은 k, 배열의 크기가 n -&gt; $O(n log k)$</li>
<li>두 수 차의 최댓값은 9,999이고, 배열 크기의 최댓값은 5,000이므로 시간은 충분하다.</li>
</ul>
<h2 id="문제-접근법">문제 접근법</h2>
<blockquote>
<p>문제의 풀이가 너무 이해가 안가서 아주 자세하게 알아보았습니다.</p>
</blockquote>
<ul>
<li>이분탐색의 시작값(<code>start</code>)은 모든 수가 같다는 가정 하에 0이다.</li>
<li>이분탐색의 끝값(<code>end</code>)은 배열의 최댓값에서 최솟값을 뺀 값이다.<br>(구간의 차이가 가장 큰 값을 이분탐색의 끝값으로 시작한다.)</li>
<li>이분탐색을 통해 중간값(<code>mid</code>)를 구한다.</li>
<li>중간값은 구간의 점수의 최댓값-최솟값을 한 것 중 최댓값의 마지노선이다. </li>
<li>이 중간값이 최적의 값인지를 계산하는 함수(<code>is_valid()</code>)를 실행한다.<ul>
<li>해당 함수는 새로운 중간값(<code>mid</code>)으로 새로운 구간들을 구한다.</li>
<li>현재 중간값으로 구하는 구간들의 최소(<code>min_num</code>)와 최대(<code>max_num</code>)를 초기화한다.</li>
<li>수를 차례대로 탐색하면서 구간을 구하기 때문에, 구간의 개수(<code>count</code>)를 1로 초기화한다.</li>
<li>수를 차례대로 탐색한다.<ul>
<li>최댓값과 최솟값을 계속 비교하며 갱신한다.</li>
<li>현재 구간의 최댓값에서 최솟값을 뺐을 때, 그 값이 중간값보다 크면 새로운 구간을 만들어야 한다.</li>
<li>새로운 구간을 만들면 구간의 개수(<code>count</code>)가 증가하기 때문에 <code>count + 1</code>을 한다.</li>
<li>또한, 새로운 구간이 현재 수(<code>num</code>)부터 시작되기 때문에 최솟값(<code>min_num</code>)과 최댓값(<code>max_num</code>)을 현재 수로 초기화 한다.</li>
<li>구간의 개수가 m을 넘어가면 현재 중간값(<code>mid</code>)는 최적의 값이 아니기 때문에 <code>False</code>를 반환한다.</li>
<li>모든 수를 탐색한 후, 구간의 개수가 m 이하면 <code>True</code>를 반환한다.</li>
</ul>
</li>
</ul>
</li>
<li>현재 중간값(<code>mid</code>)이 최적의 값(<code>is_valid() == True</code>)인 경우, 현재 중간값을 결과값(<code>ans</code>)에 저장하고 끝값(<code>end</code>)을 <code>mid - 1</code>로 변경한다.<br>(현재 중간값으로 m개 이하의 수를 만드는 것이 가능해도, 그 이하에서 최적화된 수가 나올 수 있기 때문)</li>
<li>최적의 값이 아닌(<code>is_valid() == False</code>) 경우, 구간이 m개보다 많다는 뜻이기 때문에 시작값(<code>start</code>)을 <code>mid + 1</code>로 변경한다.</li>
</ul>
<ul>
<li><p>즉, <code>is_valid()</code>가 <code>True</code>라는 뜻은 현재의 중간값(<code>mid</code>)으로 m개 이하의 구간이 생기기 때문에, 중간값(<code>mid</code>)는 적정한 값이다.  </p>
</li>
<li><blockquote>
<p>이 중간값(<code>mid</code>) 중 최솟값을 구하는 것이 문제의 요구사항이다.  </p>
</blockquote>
</li>
<li><blockquote>
<p>중간값(<code>mid</code>)을 줄이고 다시 구간나누기 실행</p>
</blockquote>
</li>
<li><p><code>is_valid()</code>가 <code>Flase</code>라는 뜻은 현재의 중간값(<code>mid</code>)으로 m개 초과의 구간이 생기기 때문에, 중간값(<code>mid</code>)이 너무 작다는 뜻이다.  </p>
</li>
<li><blockquote>
<p>한 구간의 최댓값 - 최솟값의 마지노선이 작아 구간이 많이 생긴다.  </p>
</blockquote>
</li>
<li><blockquote>
<p>중간값(<code>mid</code>) 증가</p>
</blockquote>
</li>
</ul>
<h2 id="코드">코드</h2>
<pre><code class="language-python"># BOJ
# G4 - 13397(구간 나누기 2)

import sys
input = sys.stdin.readline    

n, m = map(int, input().split())
nums = list(map(int, input().split()))
start, end = 0, max(nums) - min(nums)
ans = 0

def is_valid(mid):
    min_num = max_num = nums[0]
    cnt = 1

    for num in nums:
        min_num = min(min_num, num)
        max_num = max(max_num, num)
        if max_num - min_num &gt; mid:
            cnt += 1
            min_num = max_num = num
            if cnt &gt; m:
                return False

    return True

while start &lt;= end:
    mid = (start + end) // 2

    if is_valid(mid):
        ans = mid
        end = mid - 1
    else:
        start = mid + 1

print(ans)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Python] pip & pdm]]></title>
            <link>https://velog.io/@o_joon_/Python-pip-pdm</link>
            <guid>https://velog.io/@o_joon_/Python-pip-pdm</guid>
            <pubDate>Tue, 14 Jan 2025 04:57:35 GMT</pubDate>
            <description><![CDATA[<p>파이썬 파일을 리눅스에서 실행하고, 패키지를 배포해 보는 실험을 하다 pip와 pdm이란 것에 대해 알게되었습니다. 둘의 차이가 궁금해서 구글링과 GPT의 힘을 빌려 정리해보는 포스팅입니다.</p>
<p>파이썬으로 작업을 진행할 때, 외부 패키지를 잘 사용하는 것이 중요합니다.
파이썬은 다양한 기능들을 지원하는 패키지들이 많고, 이를 통해 원하는 비즈니스 로직에 집중할 수 있습니다.
패키지 관리를 하는 방법엔 무엇이 있는지 알아보도록 하겠습니다.</p>
<hr>
<h1 id="pippython-package-installer">pip(Python Package Installer)</h1>
<blockquote>
<p>pip는 Python의 공식 패키지 관리 도구</p>
</blockquote>
<ul>
<li>Python 2.7.9 및 3.4 이상에서 기본적으로 제공</li>
<li><a href="https://pypi.org/">PyPI</a>에서 패키지를 설치하고 관리</li>
</ul>
<h3 id="주요-기능">주요 기능</h3>
<p><strong>패키지 설치</strong>
<code>pip install</code> 명령어를 사용하여 PyPI에서 패키지를 설치합니다.</p>
<p><strong>패키지 업데이트</strong>
<code>pip install —upgrade</code> 명령어를 사용하여 설치된 패키지를 최신 버전으로 업데이트합니다.</p>
<p><strong>패키지 제거</strong>
<code>pip uninstall</code> 명령어를 사용하여 설치된 패키지를 제거합니다.</p>
<p><strong>의존성 관리</strong>
<code>pip freeze</code> 명령어로 프로젝트에서 사용된 패키지들의 버전 정보를 <code>requirements.txt</code> 로 보내거나 해당 파일을 사용하여 의존성 설치를 합니다.</p>
<h3 id="사용법">사용법</h3>
<pre><code class="language-bash"># 패키지 설치
$ pip install &lt;PACKAGE_NAME&gt;

# 특정 버전의 패키지 설치
$ pip install &lt;PACKAGE_NAME&gt;==&lt;VERSION&gt;

# 설치된 패키지 목록 보기
$ pip list

# 의존성 목록을 requirements.txt로 저장
$ pip freeze &gt; requirements.txt

# requirements.txt에 있는 모든 패키지 설치
$ pip install -r requirements.txt</code></pre>
<h3 id="pip의-한계">pip의 한계</h3>
<p><strong>의존성 충돌 해결</strong></p>
<ul>
<li>pip는 종속성 버전 충돌을 해결하는데 어려움을 겪을 수 있습니다.</li>
<li>여러 패키지가 서로 다른 버전의 동일한 의존성을 요구하는 경우 충돌이 발생할 수 있습니다.</li>
</ul>
<p><strong>프로젝트 설정 관리 부족</strong></p>
<ul>
<li>pip는 프로젝트별 가상 환경 관리나 프로젝트 설정을 명확히 하지 않음
이는 다른 패키지 관리 도구들이 해결하려는 문제</li>
</ul>
<hr>
<h1 id="pdmpython-development-master">pdm(Python Development Master)</h1>
<blockquote>
<p>pdm은 pip의 기능을 확장하여 Python 패키지 관리 및 의존성 관리를 좀 더 효율적으로 해결하기위한 도구</p>
</blockquote>
<ul>
<li>pdm은 <code>PEP 518</code>을 준수. PEP는 Python의 코딩 규약, <a href="https://peps.python.org/pep-0518/">공식 문서</a></li>
<li><code>pyproject.toml</code>을 사용하여 프로젝트의 설정 및 의존성 관리를 통합적으로 처리</li>
</ul>
<h3 id="주요-기능-1">주요 기능</h3>
<p><strong>PEP 518 지원</strong>
<code>pyproject.toml</code>을 기반으로 의존성 및 설정 관리합니다.</p>
<p><strong>빠른 의존성 해결</strong>
pip보다 더 빠르고, 효율적으로 의존성을 해결을 제공합니다.</p>
<p><strong>의존성 그룹화</strong>
의존성을 <code>default, dev, optional</code> 그룹으로 나누어 관리합니다.
→ 개발 환경과 프로덕션 환경을 분리하는 데 유용합니다.</p>
<p><strong>가상 환경 관리</strong>
프로젝트에 맞는 가상 환경을 자동으로 생성하고 관리할 수 있습니다.</p>
<p><strong>단일 파일로 설정 관리</strong>
<code>pyproject.toml</code> 파일만 있으면 프로젝트 설정과 의존성 모두 관리 가능</p>
<h3 id="사용법-1">사용법</h3>
<pre><code class="language-bash"># pdm 설치
$ pip install pdm

# 프로젝트 초기화 (pyproject.toml 파일 생성)
$ pdm init

# 패키지 설치
# 여러 개의 패키지를 공백을 주고 입력하면 한 번에 여러 개의 패키지 추가 가능
$ pdm add &lt;PACKAGE_NAME&gt;

# 개발용 의존성 추가
$ pdm add --dev &lt;PACKAGE_NAME&gt;

# 패키지 제거
$ pdm remove &lt;PACKAGE_NAME&gt;

# 의존성 설치
$ pdm install

# 의존성 목록 보기
$ pdm list

# 프로젝트의 의존성 내보내기 (requirements.txt 형식으로 보내기)
pdm export -f requirements &gt; requirements.txt</code></pre>
<hr>
<h1 id="pip와-pdm의-차이점">pip와 pdm의 차이점</h1>
<table>
<thead>
<tr>
<th>기능</th>
<th>pip</th>
<th>pdm</th>
</tr>
</thead>
<tbody><tr>
<td>의존성 관리</td>
<td><code>requirements.txt</code> 파일을 사용하여 의존성 관리</td>
<td><code>pyproject.toml</code> 파일을 사용하여 의존성 및 설정 관리</td>
</tr>
<tr>
<td>의존성 해결</td>
<td>의존성 충돌을 해결하는 데 어려움이 있을 수 있음</td>
<td>더 효율적이고 빠른 의존성 해결</td>
</tr>
<tr>
<td>의존성 그룹화</td>
<td>의존성 그룹화 지원 X</td>
<td><code>default, dev, optional</code> 그룹으로 의존성 관리 가능</td>
</tr>
<tr>
<td>설정 파일</td>
<td><code>requirements.txt</code>와 <code>setup.py</code></td>
<td><code>pyproject.toml</code>만 사용</td>
</tr>
<tr>
<td>PEP 518 지원</td>
<td>지원 X</td>
<td>지원 O</td>
</tr>
<tr>
<td>가상 환경 관리</td>
<td>가상 환경을 수동으로 설정하고 관리</td>
<td>자동으로 가상 환경을 생성하고 관리</td>
</tr>
<tr>
<td>패키지 설치 방법</td>
<td>pip install <PACKAGE_NAME></td>
<td>pdm add <PACKAGE_NAME></td>
</tr>
<tr>
<td>패키지 내보내기</td>
<td>pip freeze &gt; requirements.txt</td>
<td>pdm export -f requirements &gt; requirements.txt</td>
</tr>
</tbody></table>
<h2 id="사용">사용</h2>
<h3 id="pip-사용">pip 사용</h3>
<ul>
<li>프로젝트가 간단하고, 복잡한 의존성 해결이나 패키지 관리가 필요하지 않거나, <code>pyproject.toml</code> 과 같은 새로운 시스템에 익숙하지 않은 경우 사용해도 충분합니다.</li>
</ul>
<h3 id="pdm-사용">pdm 사용</h3>
<ul>
<li><code>pyproject.toml</code> 기반의 의존성 관리와 가상 환경 관리가 필요하거나, 최신 표준을 따르려는 경우 사용하기 좋습니다.</li>
<li>빠르고 효율적인 의존성 해결, 다양한 의존성 그룹화 및 명확한 프로젝트 설정 관리를 원할 때 유용합니다.</li>
<li>대규모 프로젝트나 개발 환경과 프로덕션 환경을 분리하여 관리하고 싶은 경우 사용하기 좋습니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DB] Keys]]></title>
            <link>https://velog.io/@o_joon_/DB-Keys</link>
            <guid>https://velog.io/@o_joon_/DB-Keys</guid>
            <pubDate>Thu, 12 Dec 2024 08:59:03 GMT</pubDate>
            <description><![CDATA[<p>데이터베이스를 사용하려면 꼭 알아야 하는 Key(키)에 대한 내용 정리입니다.<br>해당 내용은 RDBMS를 기준으로 작성되었습니다.</p>
<h1 id="key">Key</h1>
<blockquote>
<p>키는 <code>테이블 내에서 데이터를 구별하고 식별할 수 있는 역할을 하는 속성</code>이다.</p>
</blockquote>
<ul>
<li>키는 데이터 무결성을 보장하고, 테이블 간의 관계를 정의하는 데 사용된다.</li>
<li>데이터베이스에서 키는 행 또는 데이터 항목을 고유하게 식별하는 데 중요한 역할을 한다.</li>
</ul>
<h2 id="키-설계에서-중요한-개념">키 설계에서 중요한 개념</h2>
<h3 id="유일성">유일성</h3>
<blockquote>
<p>유일성은 <code>데이터베이스에서 키가 각 행(레코드)를 고유하게 식별할 수 있도록 보장하는 속성</code>이다.</p>
</blockquote>
<ul>
<li>각 키 값은 <code>테이블 내에서 유일</code>해야 하며, <code>중복되지 않도록</code> 해야 한다.  </li>
<li>유일성이 보장되어야만 데이터 간에 혼동을 피할 수 있고, 정확한 조회가 가능하다.</li>
</ul>
<h3 id="최소성">최소성</h3>
<blockquote>
<p>최소성은 <code>키를 구성하는 컬럼(속성)들이 불필요한 요소를 포함하지 않도록 해야 한다는 속성</code>이다.</p>
</blockquote>
<ul>
<li>키를 구성하는 컬럼들 중 어떤 컬럼을 제외해도 여전히 유일성을 유지할 수 있으면, 해당 컬럼은 키에 포함될 필요가 없다.</li>
<li>e.g. 학생을 구별할 때, [학생 번호, 이름, 수강 과목]의 컬럼이 있으면 [학생 번호]만으로 학생을 구별할 수 있으므로 이는 최소성을 만족하고, 키로 사용하는 것이 좋다.</li>
<li>최소성은 <code>후보키의 중요한 속성</code>으로, 후보키는 가능한 적은 수의 컬럼으로 데이터를 고유하게 식별할 수 있어야 한다. -&gt; 키의 크기를 최소화해야 한다.</li>
</ul>
<h3 id="rdbms에서-키를-사용하는-이유">RDBMS에서 키를 사용하는 이유</h3>
<ul>
<li>데이터의 고유 식별 및 중복 방지</li>
<li>데이터 무결성 및 참조 무결성 보장</li>
<li>데이터 검색 성능 향상</li>
<li>테이블 간 관계 정의</li>
</ul>
<h3 id="키의-종류">키의 종류</h3>
<p><img src="https://velog.velcdn.com/images/o_joon_/post/f1de47a5-d4cd-47e3-9ea6-1b46929986a0/image.jpg" alt=""></p>
<ul>
<li>기본키(Primary Key)</li>
<li>후보키(Candidate Key)</li>
<li>대체키(Alternate Key)</li>
<li>외래키(Foreign Key)</li>
<li>복합키(Composite Key)</li>
<li>수퍼키(Super Key)</li>
</ul>
<h2 id="기본키primary-key">기본키(Primary Key)</h2>
<blockquote>
<p>기본키는 <code>테이블에서 각 행을 고유하게 식별하는 데 사용되는 키</code>이다.</p>
</blockquote>
<ul>
<li><code>유일성</code>과 <code>최소성</code>, <code>Not-NULL</code>을 보장한다.<ul>
<li>NULL값을 가질 수 없다.</li>
</ul>
</li>
<li>후보키 중에서 <code>메인으로 선정되는 키</code>이다.</li>
<li>행을 식별할 때 기준이 되는 <code>반드시 필요한 키</code>이다.</li>
</ul>
<h3 id="기본키를-위한-조건">기본키를 위한 조건</h3>
<ol>
<li>값의 변동이 잦지 않은 후보키여야 한다.</li>
<li>NULL값을 가질 수 있는 속성이 포함된 후보키가 아니어야 한다.</li>
<li>후보키 중 단순한 키여야 한다.</li>
<li>하나의 테이블에는 반드시 하나의 기본키만 존재한다.</li>
</ol>
<h3 id="예시">예시</h3>
<pre><code class="language-sql">CREATE TABLE Student (
    id INT PRIMARY KEY, -- 기본키
    name VARCHAR(100),
    email VARCHAR(100),
    age INT
);</code></pre>
<h2 id="후보키candidate-key">후보키(Candidate Key)</h2>
<blockquote>
<p>후보키는 테이블에서 <code>각 행을 고유하게 식별할 수 있는 여러 키 후보</code>이다.</p>
</blockquote>
<ul>
<li><code>유일성</code>과 <code>최소성</code>을 보장한다.</li>
<li>기본키는 후보키 중에서 선택되며, 선택되지 않은 후보키는 대체키이다.</li>
</ul>
<h3 id="예시-1">예시</h3>
<pre><code class="language-sql">CREATE TABLE Student (
    id INT PRIMARY KEY, -- 기본키
    name VARCHAR(100),
    email VARCHAR(100), -- 후보키(대체키)
    age INT
);</code></pre>
<h2 id="대체키alternate-key">대체키(Alternate Key)</h2>
<blockquote>
<p>대체키는 <code>기본키로 선택되지 않은 후보키</code>이다.</p>
</blockquote>
<ul>
<li><code>유일성</code>과 <code>최소성</code>을 보장한다.</li>
<li>예시는 위와 같다.</li>
</ul>
<h2 id="외래키foreign-key">외래키(Foreign Key)</h2>
<blockquote>
<p>외래키는 <code>다른 테이블의 기본키를 참조하여 두 테이블 간의 관계를 설정하는 키</code>이다.</p>
</blockquote>
<ul>
<li><code>최소성</code>을 보장한다.<br>(외래키가 참조하는 부모 테이블의 기본키 또는 고유키(UNIQUE)는 유일성을 보장한다.)</li>
<li>외래키는 부모 테이블의 기본키나 고유키를 참조하므로, 최소성이 이미 보장된다.</li>
<li>외래키를 설정하지 않아도 되지만, 데이터 무결성 때문에 설정하는 경우가 많다.</li>
<li>e.g. 아래의 예시에서 Student의 id를 변경시키는 경우, Student_Subject 테이블에 참조되어 있는 student_id가 함께 변경되어야 하기 때문이다.</li>
</ul>
<h3 id="예시-2">예시</h3>
<pre><code class="language-sql">CREATE TABLE Subject (
    id INT PRIMARY KEY,
    name VARCHAR(100)
);

CREATE TABLE Student (
    id INT PRIMARY KEY,
    name VARCHAR(100),
    email VARCHAR(100),
    age INT
);

CREATE Student_Subject (
    student_id INT,
    subject_id INT,
    FOREIGN KEY (student_id) REFERENCES Student(id), -- 외래키
    FOREIGN KEY (subject_id) REFERENCES Subject(id), -- 외래키
    PRIMARY KEY (student_id, subject_id)
);</code></pre>
<h2 id="복합키composite-key">복합키(Composite Key)</h2>
<blockquote>
<p>복합키는 <code>두 개 이상의 컬럼을 결합한 키</code>이다.</p>
</blockquote>
<ul>
<li><code>유일성</code>을 보장한다.</li>
<li>단일 컬럼으로는 데이터의 유일성을 보장할 수 없을 때, 컬럼들을 조합하여 사용한다.</li>
<li>e.g. 아래의 예시에서 order_id와 product_id 모두 있어야 상품을 구분할 수 있으므로, 복합키로 만들어 고유한 행을 식별할 수 있도록 한다.</li>
</ul>
<h3 id="예시-3">예시</h3>
<pre><code class="language-sql">CREATE TABLE OrderDetails (
    order_id INT,
    product_id INT,
    quanrity INT,
    PRIMARY KEY (order_id, product_id) -- 복합키
);</code></pre>
<h2 id="수퍼키super-key">수퍼키(Super Key)</h2>
<blockquote>
<p>수퍼키는 테이블에서 <code>한 개 이상의 컬럼을 포함하고, 그 조합이 각 행을 고유하게 식별할 수 있게 해주는 키</code>이다.</p>
</blockquote>
<ul>
<li><code>유일성</code>을 보장한다.</li>
<li>모든 후보키는 수퍼키이지만, 수퍼키는 불필요한 컬럼을 포함할 수 있다.<br>(최소성을 보장하지 않는다.)</li>
<li>아래의 예시에서 id와 이름의 조합은 수퍼키로 사용할 수 있지만, id만으로도 유일성을 만족하므로 수퍼키이지만 후보키로 사용될 수 없다.</li>
</ul>
<h3 id="예시-4">예시</h3>
<pre><code class="language-sql">CREATE TABLE Student (
    id INT PRIMARY KEY,
    name VARCHAR(100),
    age INT,
    UNIQUE (id, name) -- 수퍼키
);</code></pre>
<hr>
<h2 id="면접-대비-질문">면접 대비 질문</h2>
<p>Q 1)<br>기본키(Primary Key)와 복합 키(Composite Key)를 설명해보세요.</p>
<p>A 1)<br>기본키는 테이블에서 각 행을 고유하게 식별하는데 사용되는 키이며, 유일성과 최소성을 보장합니다.<br>테이블에는 기본키를 반드시 하나만 가지고 있어야 하며, NULL이거나 수정 및 업데이트가 불가능합니다.  </p>
<p>복합키는 두 개 이상의 컬럼을 결합한 키이며, 유일성을 보장합니다.<br>단일 컬럼으로 유일성을 보장할 수 없는 경우, 두 개 이상의 컬럼을 결합하여 사용합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Programmers] - Python / 파이썬 - 택배 배달과 수거하기]]></title>
            <link>https://velog.io/@o_joon_/Programmers-Python-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%ED%83%9D%EB%B0%B0-%EB%B0%B0%EB%8B%AC%EA%B3%BC-%EC%88%98%EA%B1%B0%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@o_joon_/Programmers-Python-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%ED%83%9D%EB%B0%B0-%EB%B0%B0%EB%8B%AC%EA%B3%BC-%EC%88%98%EA%B1%B0%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 09 Dec 2024 23:50:23 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<ul>
<li><a href="https://school.programmers.co.kr/learn/courses/30/lessons/150369">https://school.programmers.co.kr/learn/courses/30/lessons/150369</a></li>
</ul>
<h2 id="시간-복잡도">시간 복잡도</h2>
<ul>
<li>n만큼의 반복 * m만큼의 왕복 횟수 -&gt; $O(n \cdot m)$</li>
<li>n의 최댓값은 100,000이고, m의 최댓값은 50(원소의 최댓값이 50이고 cap의 최솟값이 1이기 때문에 최대 50번 반복)이기 때문에 충분하다.</li>
</ul>
<h2 id="문제-접근법">문제 접근법</h2>
<ul>
<li>최소 이동 거리를 위해서는 가장 먼 곳 부터 배달을 해야한다.</li>
<li>가장 먼 곳 부터 배달할 상자의 개수와 회수할 상자의 개수를 따로 계산한다.<br>(현재 위치에 배달할 상자나 회수할 상자가 남아 있는 경우, 한 번 더 왕복해야 하기 때문에 이를 위해서 둘을 따로 둔다.)</li>
<li>현재 위치의 이전 위치에 있는 집들은 어짜피 경로에 있기 때문에 고려하지 않고 뒤에서부터 탐색한다.</li>
</ul>
<ol>
<li>현재 배달할 상자의 개수와 수거할 상자의 개수를 저장할 변수를 초기화한다.</li>
<li>가장 먼 집부터 탐색을 시작한다.<br> 2-1. 현재 위치에 배달할 상자의 개수를 1번에서 초기화한 변수에 더한다.
 2-2. 현재 위치에서 회수할 상자의 개수를 1번에서 초기화한 변수에 더한다.
 2-3. 두 변수가 0 이하가 될 때 까지 최대 개수인 cap을 빼고, 왕복한 거리를 총 이동 거리에 더한다.<br> (한 번 왕복하면 cap만큼 상자를 배달하고 회수할 수 있기 때문에, 1 이상의 수가 남으면 한 번 더 왕복해야 하기 때문)</li>
<li>총 이동 거리를 출력한다.</li>
</ol>
<h2 id="코드">코드</h2>
<pre><code class="language-python"># Programmers
# Lv.2 - 택배 배달과 수거하기

def solution(cap, n, deliveries, pickups):
    ans = 0
    delivery = 0
    pickup = 0

    for i in range(n - 1, -1, -1):
        delivery += deliveries[i]
        pickup += pickups[i]

        while (delivery &gt; 0 or pickup &gt; 0):
            delivery -= cap
            pickup -= cap
            ans += (i + 1) * 2

    return ans</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Programmers] - Python / 파이썬 - 광물 캐기]]></title>
            <link>https://velog.io/@o_joon_/Programmers-Python-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EA%B4%91%EB%AC%BC-%EC%BA%90%EA%B8%B0</link>
            <guid>https://velog.io/@o_joon_/Programmers-Python-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EA%B4%91%EB%AC%BC-%EC%BA%90%EA%B8%B0</guid>
            <pubDate>Mon, 02 Dec 2024 09:12:02 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<ul>
<li><a href="https://school.programmers.co.kr/learn/courses/30/lessons/172927">https://school.programmers.co.kr/learn/courses/30/lessons/172927</a></li>
</ul>
<h2 id="시간-복잡도">시간 복잡도</h2>
<ul>
<li>dfs를 돌며 3번씩 k번 반복 -&gt; $O(3^k)$</li>
<li>k의 최댓값은 15이기 때문에 시간은 충분하다.</li>
<li>3^15 = 14,348,907</li>
</ul>
<h2 id="문제-접근법">문제 접근법</h2>
<ol>
<li>광물을 5개 단위로 배열에 담아 2차원 배열로 만든다.</li>
<li>백트래킹을 통해 모든 경우의 수를 계산한다.<br> 2-1. 모든 광물을 캤거나 곡괭이를 다 쓴 경우, 최솟값을 비교하여 변경한다.<br> 2-2. 현재 캐야 할 광물들을 캘 때 필요한 피로도를 각 곡괭이마다 계산한다.<br> 2-3. 2-2에서 계산한 피로도를 더한 후, 사용한 곡괭이의 수를 줄이고 캐야 할 광물의 인덱스를 늘려 백트래킹을 계속 한다.</li>
</ol>
<h2 id="코드">코드</h2>
<pre><code class="language-python"># Programmers
# Lv.2 - 광물 캐기

d = {&#39;diamond&#39;: [1, 5, 25], &#39;iron&#39;: [1, 1, 5], &#39;stone&#39;: [1, 1, 1]}
ans = 15 * 25 * 50

def calculate(pick, minerals):
    cnt = 0
    for mineral in minerals:
        cnt += d[mineral][pick]
    return cnt

def dfs(i, dia, iron, stone, minerals, total):
    if i == len(minerals) or (dia + iron + stone) == 0:
        global ans
        ans = min(ans, total)
        return

    if dia:
        dfs(i + 1, dia - 1, iron, stone, minerals, total + calculate(0, minerals[i]))
    if iron:
        dfs(i + 1, dia, iron - 1, stone, minerals, total + calculate(1, minerals[i]))
    if stone:
        dfs(i + 1, dia, iron, stone - 1, minerals, total + calculate(2, minerals[i]))

def solution(picks, minerals):
    minerals_sep = [minerals[i: i + 5] for i in range(0, len(minerals), 5)]
    dfs(0, picks[0], picks[1], picks[2], minerals_sep, 0)

    return ans</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Programmers] - Python / 파이썬 - 마법의 엘리베이터]]></title>
            <link>https://velog.io/@o_joon_/Programmers-Python-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%A7%88%EB%B2%95%EC%9D%98-%EC%97%98%EB%A6%AC%EB%B2%A0%EC%9D%B4%ED%84%B0</link>
            <guid>https://velog.io/@o_joon_/Programmers-Python-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%A7%88%EB%B2%95%EC%9D%98-%EC%97%98%EB%A6%AC%EB%B2%A0%EC%9D%B4%ED%84%B0</guid>
            <pubDate>Sat, 30 Nov 2024 06:51:51 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<ul>
<li><a href="https://school.programmers.co.kr/learn/courses/30/lessons/148653">https://school.programmers.co.kr/learn/courses/30/lessons/148653</a></li>
</ul>
<h2 id="시간-복잡도">시간 복잡도</h2>
<ul>
<li>n만큼의 반복 -&gt; $O(n)$</li>
</ul>
<h2 id="문제-접근법">문제 접근법</h2>
<ul>
<li>자릿수 대로 탐색하며 다음과 같은 조건을 확인해야 한다.<ul>
<li>현재 자릿수가 5인가? -&gt; -1을 5번 누르거나 +1을 5번 누르거나 횟수는 같지만, +1을 5번 누르면 다음 자릿수가 바뀌므로 확인해주어야 한다.</li>
</ul>
</li>
</ul>
<ol>
<li>층수를 각 자릿수마다 나누어 계산하기 위해 배열로 변환한다.</li>
<li>자릿수를 뒤에서부터 차례대로 탐색한다.
 2-1. 현재 자릿수가 5면서 다음 자릿수가 5미만이거나 현재 자릿수가 5 미만인 경우, 현재 자릿수를 총 버튼 수에 더한다.<br> 2-2. 아닌 경우, 10에서 현재 자릿수를 뺀 수를 총 버튼 수에 더하고, 다음 자릿수를 1 증가시킨다.</li>
<li>탐색 범위에서 제외된 첫 자릿수는 6 이상이면 10에서 빼고 1을 더하고 아닌 경우 현재 자릿수를 총 버튼 수에 더한다.</li>
</ol>
<h2 id="코드">코드</h2>
<pre><code class="language-python">def solution(storey):
    storey = list(map(int, str(storey)))
    ans = 0

    for i in range(len(storey) - 1, 0, -1):
        if (storey[i] == 5 and storey[i - 1] &lt; 5) or storey[i] &lt; 5:
            ans += storey[i]
        else:
            ans += 10 - storey[i]
            storey[i - 1] += 1
    ans += 10 - storey[0] + 1 if storey[0] &gt; 5 else storey[0]

    return ans</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Programmers] - Python / 파이썬 - 퍼즐 게임 챌린지]]></title>
            <link>https://velog.io/@o_joon_/Programmers-Python-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%ED%8D%BC%EC%A6%90-%EA%B2%8C%EC%9E%84-%EC%B1%8C%EB%A6%B0%EC%A7%80</link>
            <guid>https://velog.io/@o_joon_/Programmers-Python-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%ED%8D%BC%EC%A6%90-%EA%B2%8C%EC%9E%84-%EC%B1%8C%EB%A6%B0%EC%A7%80</guid>
            <pubDate>Thu, 28 Nov 2024 08:17:56 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<ul>
<li><a href="https://school.programmers.co.kr/learn/courses/30/lessons/340212">https://school.programmers.co.kr/learn/courses/30/lessons/340212</a></li>
</ul>
<h2 id="시간-복잡도">시간 복잡도</h2>
<ul>
<li>n만큼의 반복 + 이분탐색 범위(m) -&gt; $O(n \log m)$</li>
<li>n의 최대값은 300,000이고 m의 최대 범위는 100,000이기 때문에 시간은 충분하다.</li>
<li>$\log 100,000$ 의 값은 약 17이다.</li>
</ul>
<h2 id="문제-접근법">문제 접근법</h2>
<ul>
<li>이분탐색</li>
</ul>
<ol>
<li>diffs에서 가장 작은 값과 가장 큰 값을 각각 이분탐색의 시작, 끝 범위로 정한다.</li>
<li>이분탐색을 시작한다.<br> 2-1. 중간값을 구한다. -&gt; 현재 레벨<br> 2-2. 현재 레벨로 모든 퍼즐의 수행 시간을 나타낼 변수를 정의한다.<br> 2-3. 모든 퍼즐을 탐색한다.<br> ㅤㅤ2-3-1. 이전 퍼즐 시간은 현재 퍼즐의 인덱스가 0인 경우는 0, 나머지는 현재 인덱스 이전의 시간으로 정한다.<br> ㅤㅤ2-3-2. 문제의 조건에 맞게 현재 퍼즐과 현재 난이도에 맞는 수행 시간을 모두 더한다.<br> 2-4. 현재 모든 퍼즐의 수행 시간이 제한 시간보다 큰 경우, 현재 레벨을 정답으로 설정 후 이분탐색의 끝 범위를 중간값 - 1로 해준다.<br> ㅤㅤ-&gt; 최소 레벨을 구해야 하기 때문에, 제한 시간을 넘지 않으면 레벨을 점점 줄여나간다.<br> 2-5. 현재 모든 퍼즐의 수행 시간이 제한 시간보다 작은 경우, 이분탐색의 시작 범위를 중간값 + 1로 해준다.  </li>
<li>레벨을 출력한다. </li>
</ol>
<h2 id="코드">코드</h2>
<pre><code class="language-python"># Programmers
# Lv.2 - 퍼즐 게임 챌린지

def solution(diffs, times, limit):
    start, end = min(diffs), max(diffs)
    level = 0

    while start &lt;= end:
        mid = (start + end) // 2
        total = 0

        for i in range(len(diffs)):
            time_prev = times[i - 1] if i else 0
            total += (diffs[i] - mid) * (times[i] + time_prev) + times[i] if diffs[i] &gt; mid else times[i]

        if total &lt;= limit:
            level = mid
            end = mid - 1
        else:
            start = mid + 1

    return level</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ: 20922] - Python / 파이썬 - 겹치는 건 싫어]]></title>
            <link>https://velog.io/@o_joon_/BOJ-20922-Python-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EA%B2%B9%EC%B9%98%EB%8A%94-%EA%B1%B4-%EC%8B%AB%EC%96%B4</link>
            <guid>https://velog.io/@o_joon_/BOJ-20922-Python-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EA%B2%B9%EC%B9%98%EB%8A%94-%EA%B1%B4-%EC%8B%AB%EC%96%B4</guid>
            <pubDate>Wed, 27 Nov 2024 07:37:53 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<ul>
<li><a href="https://www.acmicpc.net/problem/20922">https://www.acmicpc.net/problem/20922</a></li>
</ul>
<h2 id="시간-복잡도">시간 복잡도</h2>
<ul>
<li>n만큼 순회(r) + 최대 n만큼 순회(l) -&gt; $O(n)$</li>
<li>n의 최대값이 200,000이므로 충분하다.</li>
</ul>
<h2 id="문제-접근법">문제 접근법</h2>
<ol>
<li>현재 범위의 수를 카운트 하기 위한 딕셔너리를 정의한다.</li>
<li>시작과 끝 숫자를 나타낼 두 변수를 정의한다.(초기엔 모두 0)</li>
<li>끝 숫자가 n이 될 때 까지 순회한다.<br> 3-1. 끝 숫자의 개수가 k개 미만인 경우, 끝 숫자의 카운트를 증가시키고 현재 끝 숫자를 1 늘려준 후 시작부터 끝의 범위를 구한다.
 3-2. 끝 숫자의 개수가 k개 이상인 경우, 시작 숫자의 카운트를 감소시키고 현재 시작 숫자를 1 늘려준다.</li>
<li>3-1에서 구했 던 범위 중 가장 큰 범위를 출력한다.</li>
</ol>
<h2 id="코드">코드</h2>
<pre><code class="language-python"># BOJ
# S1 - 20922(겹치는 건 싫어)

import sys
from collections import defaultdict
input = sys.stdin.readline

n, k = map(int, input().split())
nums = list(map(int, input().split()))
cnt = defaultdict(int)
l, r = 0, 0
ans = 0

while r &lt; n:
    if cnt[nums[r]] &lt; k:
        cnt[nums[r]] += 1
        r += 1
        ans = max(ans, r - l)
    else:
        cnt[nums[l]] -= 1
        l += 1

print(ans)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ: 2018] - Python / 파이썬 - 수들의 합]]></title>
            <link>https://velog.io/@o_joon_/BOJ-2018-Python-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%88%98%EB%93%A4%EC%9D%98-%ED%95%A9</link>
            <guid>https://velog.io/@o_joon_/BOJ-2018-Python-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%88%98%EB%93%A4%EC%9D%98-%ED%95%A9</guid>
            <pubDate>Sun, 24 Nov 2024 08:46:30 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<ul>
<li><a href="https://www.acmicpc.net/problem/2018">https://www.acmicpc.net/problem/2018</a></li>
</ul>
<h2 id="시간-복잡도">시간 복잡도</h2>
<ul>
<li>최대 n만큼의 반복 -&gt; $O(n)$</li>
</ul>
<h2 id="문제-접근법">문제 접근법</h2>
<ol>
<li>시작과 끝 숫자를 나타낼 두 변수를 정의한다.(초기엔 모두 0)</li>
<li>시작부터 끝까지의 합을 나타낼 변수를 정의한다.</li>
<li>끝 숫자가 n에 다다를 때 까지 다음과 같은 과정을 반복한다.<br> 3-1. 총 합이 n보다 작다면, 끝 숫자를 1 증가시키고 합에 더한다.<br> 3-2. 총 합이 n보다 크다면, 시작 숫자를 합에서 빼고 시작 숫자를 1 증가시킨다.<br> 3-3. 총 합이 n과 같다면, 카운트를 1 증가시키고 끝 숫자도 1 증가시킨다.</li>
</ol>
<h2 id="코드">코드</h2>
<pre><code class="language-python"># BOJ
# S5 - 2018(수들의 합 5)

import sys
input = sys.stdin.readline

n = int(input())
l, r = 0, 0
total = 0
ans = 0

while r &lt;= n:
    if total &lt; n:
        r += 1
        total += r
    elif total &gt; n:
        total -= l
        l += 1
    else:
        ans += 1
        r += 1
        total += r

print(ans)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CS] Database&DBMS]]></title>
            <link>https://velog.io/@o_joon_/DB-DataBase%EC%99%80-DBMS</link>
            <guid>https://velog.io/@o_joon_/DB-DataBase%EC%99%80-DBMS</guid>
            <pubDate>Wed, 20 Nov 2024 12:04:34 GMT</pubDate>
            <description><![CDATA[<p>CS 스터디를 하면서 정리한 내용을 포스팅 하고 있습니다!
<a href="https://github.com/People-who-Grow-in-a-Spiral/CS-Study">스터티 깃허브</a></p>
<p>이번 포스팅은 데이터베이스의 핵심 개념을 간단하게 정리한 글입니다.<br>해당 글은 DB, DBMS에 관한 개념과 특징만 다루고, 이와 함께 꼭 알아야 할 SQL, DB 자료구조(e.g. 인덱스) 등의 개념은 따로 작성 예정입니다.</p>
<hr>
<h1 id="database">DataBase</h1>
<blockquote>
<p>데이터베이스(DataBase, DB)란, 여러 사람에 의해 공유되어 사용될 목적으로 통합하여 관리되는 <code>데이터의 집합</code>이다.</p>
</blockquote>
<p>논리적으로 연관된 하나 이상의 데이터의 집합으로, 이를 고도로 구조화하여 검색 및 갱신의 효율을 높인다.<br>특정 다수의 이용자들에게 필요한 정보를 제공하거나, 특정 조직 내에서 필요로 하는 정보를 체계적으로 축적하고 제공한다.<br>데이터베이스는 데이터의 구조와 특성에 따라 매우 단순하거나 복잡할 수 있다.<br><a href="#dbms">DBMS</a>를 통해서 관리된다.</p>
<p><img src="https://velog.velcdn.com/images/o_joon_/post/569c64cd-f8c6-4452-9551-0b4394ea8228/image.png" alt=""></p>
<p>데이터(화물)은 데이터베이스(창고)에 저장되고, DBMS(관리자)는 화물을 창고에 적재하거나 창고에서 이용자로 보내주는 등의 역할을 한다.</p>
<p><img src="https://velog.velcdn.com/images/o_joon_/post/865648bf-5f4f-4e9f-82ce-8f12ab6d519c/image.png" alt=""></p>
<h2 id="db의-특징">DB의 특징</h2>
<ul>
<li><strong>동시 공유</strong>: 다수의 사용자가 동시에 같은 내용의 데이터를 이용할 수 있어야 한다.</li>
<li><strong>계속적인 변화</strong>: 새로운 데이터의 삽입, 삭제, 갱신으로 항상 최신의 데이터를 유지해야 한다.</li>
<li><strong>실시간 접근성</strong>: 데이터베이스는 사용자의 요청에 즉시 처리하고 응답해야 한다.</li>
<li><strong>내용에 의한 참조</strong>: 데이터베이스에 있는 데이터를 참조할 때, 사용자가 요구하는 데이터 내용으로 찾아야 한다.</li>
</ul>
<br>

<h1 id="dbms">DBMS</h1>
<blockquote>
<p>DBMS(DataBase Management System)란, 데이터베이스를 관리하고 운영하는 <code>소프트웨어</code>이다.</p>
</blockquote>
<p>데이터를 파일 단위로 직접 관리하면, 적은 수는 무리 없이 사용할 수 있다.<br>하지만, 데이터 수가 증가하여 관리해야 할 파일이 늘어나면 한계에 부딪힌다.<br>따라서, DBMS를 활용하여 데이터를 데이터베이스로 정리하여 <code>효율적</code>으로 데이터를 통제하고 관리할 수 있다.</p>
<p>DBMS는 데이터를 저장, 검색, 수정, 삭제하는 작업을 효율적으로 처리한다.</p>
<h2 id="dbms의-기능">DBMS의 기능</h2>
<ul>
<li><strong>중복 제어</strong>: 동일한 데이터가 여러 위치에 중복 저장되는 현상을 방지한다.</li>
<li><strong>접근 통제</strong>: 사용자마다 다양한 권한을 부여하고, 권한에 따라 데이터에 대한 접근을 제어한다.</li>
<li><strong>인터페이스 제공</strong>: 사용자에게 SQL 및 GUI, CLI 등 다양한 인터페이스를 제공한다.</li>
<li><strong>관계 표현</strong>: 서로 다른 데이터간의 다양한 관계를 표현할 수 있는 기능을 제공한다.</li>
<li><strong>무결성 제약 조건</strong>: 무결성에 관한 제약 조건을 정의/검사하는 기능을 제공한다.  </li>
<li><em>(데이터베이스는 반드시 무결성 제약 조건을 통과한 데이터만을 저장해야 한다.)*</em><br>(NoSQL의 경우, 제한적으로 중복 제어나 무결성이 무시되기도 한다.)</li>
</ul>
<h2 id="dbms의-종류">DBMS의 종류</h2>
<p>DBMS는 데이터를 관리하는 모든 시스템의 큰 틀이고, 유형에는 대표적으로 관계형 DBMS인 RDBMS, 비관계형 DBMS인 NoSQL이 있다.</p>
<h3 id="rdbms">RDBMS</h3>
<p>데이터를 <code>테이블 형식</code>으로 저장하고, 테이블 간 관계를 기반으로 데이터를 관리한다.<br>외래 키를 사용하여 테이블 간 join을 통해 여러 테이블의 데이터를 결합하여 가져올 수 있다.<br><code>ACID</code> 원칙을 준수한다.</p>
<blockquote>
<p>Atomicity(원자성): 트랜잭션은 모두 수행되거나 전혀 수행되지 않아야 한다.<br>Consistency(일관성): 트랜잭션이 완료되면 데이터베이스는 일관된 상태를 유지해야 한다.<br>Isolation(격리성): 동시에 실행되는 트랜잭션은 서로 간섭하지 않아야 한다.<br>Durability(지속성): 트랜잭션 완료 후 데이터는 영구적으로 저장되어야 한다.  </p>
</blockquote>
<p>MySQL, PostgreSQL, Oracle 등이 RDBMS에 해당한다.</p>
<h4 id="장점">장점</h4>
<ul>
<li>정해진 스키마에 따라 데이터를 저장하여 <code>명확한 데이터 구조</code>를 보장한다.   </li>
<li><em><code>데이터 정합성</code>*</em> -&gt; 데이터들의 값이 서로 일치하는 상태</li>
<li>데이터를 <code>중복 없이</code> 한 번만 저장한다.  </li>
<li><em><code>데이터 무결성</code>*</em> -&gt; 데이터가 처리되는 모든 과정에서 변경되거나 손상되지 않고 완전성, 정확성, 일관성을 유지하는 특성</li>
</ul>
<h4 id="단점">단점</h4>
<ul>
<li><code>시스템이 커질수록</code> join이 많아져 <code>쿼리가 복잡</code>해진다.</li>
<li>성능 향상을 위한 <code>Scale-up만 지원</code>한다.  </li>
<li><blockquote>
<p>Scale-out이 불가능하고, 비용이 기하급수적으로 커질 수 있다.</p>
</blockquote>
</li>
<li>스키마로 인해 <code>데이터가 유연하지 않다</code>.  </li>
<li><blockquote>
<p>스키마가 변경될 경우 번거로워진다.</p>
</blockquote>
</li>
</ul>
<h4 id="사용하는-경우">사용하는 경우</h4>
<ul>
<li><code>데이터 구조가 명확</code>하고 변경될 여지가 없으며, <code>스키마가 중요한 경우</code>에 사용한다.</li>
<li>중복된 데이터가 없기 때문에, <code>관계를 맺고 있는 데이터가 자주 변경</code>이 이루어지는 시스템에 적합하다.</li>
</ul>
<h3 id="nosql">NoSQL</h3>
<p>빅데이터의 등장으로 인해 데이터와 트래픽이 엄청나게 증가함에 따라 등장했다.<br>-&gt; RDBMS는 빅데이터를 처리하기 위해 장비 업그레이드(Scale-up)을 하고 비용이 많이 든다.<br>NoSQL은 이런 RDBMS의 단점을 극복하고 데이터의 분산 저장(Scale-out)을 목표로 한다.</p>
<p>데이터를 문서, 키-값, 그래프 등 <code>다양한 형식</code>으로 데이터를 관리한다.</p>
<h4 id="장점-1">장점</h4>
<ul>
<li>스키마가 없어 <code>유연하고 자유로운 데이터 구조</code>를 가지고 있다.  </li>
<li><blockquote>
<p>데이터 조정과 새로운 필드 추가가 자유롭다.</p>
</blockquote>
</li>
<li>성능 향상을 위해 <code>Scale-up/Scale-out이 모두 가능</code>하다.  </li>
<li><blockquote>
<p><code>데이터 분산</code>이 용이하고, RDBMS에 비해 <code>대용량의 데이터</code>를 저장할 수 있다.</p>
</blockquote>
</li>
</ul>
<h4 id="단점-1">단점</h4>
<ul>
<li>데이터 정합성을 보장하지 않기 때문에, <code>데이터 구조를 결정하기 어려울 수 있다</code>.</li>
<li><code>데이터의 중복이 발생 가능</code>하며, 중복된 데이터가 변경될 경우 <code>모든 컬렉션에서 수정</code>해야 한다.</li>
</ul>
<h4 id="사용하는-경우-1">사용하는 경우</h4>
<ul>
<li>정확한 <code>데이터 구조를 알 수 없고 데이터가 변경/확장될 수 있는 경우</code>에 사용한다.</li>
<li>데이터의 <code>수정이 많이 이루어지지 않는 시스템</code>에 적합하다.</li>
<li>데이터를 분산해서 저장(Scale-out)해야 할 만큼 <code>대용량의 데이터를 저장하는 경우</code>에 적합하다.</li>
</ul>
<h3 id="두-유형의-주요-차이">두 유형의 주요 차이</h3>
<table>
<thead>
<tr>
<th>구분</th>
<th>RDBMS</th>
<th>NoSQL</th>
</tr>
</thead>
<tbody><tr>
<td>데이터구조</td>
<td>관계형 테이블 형식</td>
<td>문서, 키-값, 그래프 등 다양한 형식</td>
</tr>
<tr>
<td>스키마</td>
<td>고정된 스키마</td>
<td>유연한 스키마</td>
</tr>
<tr>
<td>확장성</td>
<td>수직적 확장(Scale-up)</td>
<td>수직적 확장(Scale-up) 및 수평적 확장(Scale-out)</td>
</tr>
<tr>
<td>사용 사례</td>
<td>트랜잭션, 데이터 관계가 중요한 시스템</td>
<td>대규모 데이터, 비정형 데이터, 실시간 처리</td>
</tr>
</tbody></table>
<hr>
<h2 id="면접-대비-질문">면접 대비 질문</h2>
<p>Q 1)<br>DB의 특징은?  </p>
<p>A 1)<br>동계실내(동시 공유, 계속적인 변화, 실시간 접근성, 내용에 의한 참조)  </p>
<ul>
<li>동시 공유: 다수의 사용자가 같은 데이터를 동시에 이용할 수 있어야 한다.</li>
<li>계속적인 변화: 새로운 데이터의 삽입, 삭제, 갱신으로 항상 최신의 상태를 유지해야 한다.</li>
<li>실시간 접근성: 데이터베이스는 사용자의 요청에 즉시 처리하고 응답해야 한다.</li>
<li>내용에 의한 참조: 데이터베이스에 있는 데이터에 참조할 때, 사용자가 요구하는 데이터 내용으로 찾아야 한다.</li>
</ul>
<br>

<p>Q 2)<br>DBMS란?  </p>
<p>A 2)<br>데이터베이스를 관리하는 시스템으로, 사용자가 효율적으로 데이터베이스에 데이터를 저장, 검색, 수정, 삭제할 수 있도록 해준다.</p>
<br>

<p>Q 3)<br>RDBMS와 NoSQL의 차이점은?</p>
<p>A 3)<br>RDBMS는 고정된 스키마와 중복 없는 데이터로 정합성과 무결성을 보장하고, Scale-up만 지원한다.<br>NoSQL은 유연한 스키마와 중복 허용으로 확장성에 강하며, Scale-out 또한 지원한다.</p>
<br>

<p>Q 4)<br>트랜잭션의 ACID 원칙이란?</p>
<p>A 4)  </p>
<ul>
<li>Atomicity(원자성): 트랜잭션은 모두 수행되거나 전혀 수행되지 않아야 한다.  </li>
<li>Consistency(일관성): 트랜잭션이 완료되면 데이터베이스는 일관된 상태를 유지해야 한다.  </li>
<li>Isolation(격리성): 동시에 실행되는 트랜잭션은 서로 간섭하지 않아야 한다.  </li>
<li>Durability(지속성): 트랜잭션 완료 후 데이터는 영구적으로 저장되어야 한다.  </li>
</ul>
<hr>
<h2 id="참조">참조</h2>
<ul>
<li><a href="https://aws.amazon.com/ko/what-is/database/#:~:text=%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%8A%94%20%EC%A0%84%EC%9E%90%EC%A0%81%EC%9C%BC%EB%A1%9C%20%EC%A0%80%EC%9E%A5,%EB%B0%8F%20%ED%8E%B8%EC%A7%91%ED%95%A0%20%EC%88%98%20%EC%9E%88%EC%8A%B5%EB%8B%88%EB%8B%A4">https://aws.amazon.com/ko/what-is/database/#:~:text=%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%8A%94%20%EC%A0%84%EC%9E%90%EC%A0%81%EC%9C%BC%EB%A1%9C%20%EC%A0%80%EC%9E%A5,%EB%B0%8F%20%ED%8E%B8%EC%A7%91%ED%95%A0%20%EC%88%98%20%EC%9E%88%EC%8A%B5%EB%8B%88%EB%8B%A4</a>.</li>
<li><a href="https://namu.wiki/w/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4">https://namu.wiki/w/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4</a></li>
<li><a href="https://hongong.hanbit.co.kr/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-databasedb-dbms-sql%EC%9D%98-%EA%B0%9C%EB%85%90/">https://hongong.hanbit.co.kr/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-databasedb-dbms-sql%EC%9D%98-%EA%B0%9C%EB%85%90/</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ: 21921] - Python / 파이썬 - 블로그]]></title>
            <link>https://velog.io/@o_joon_/BOJ-21921-Python-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%B8%94%EB%A1%9C%EA%B7%B8</link>
            <guid>https://velog.io/@o_joon_/BOJ-21921-Python-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%B8%94%EB%A1%9C%EA%B7%B8</guid>
            <pubDate>Wed, 20 Nov 2024 07:19:54 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<ul>
<li><a href="https://www.acmicpc.net/problem/21921">https://www.acmicpc.net/problem/21921</a></li>
</ul>
<h2 id="시간-복잡도">시간 복잡도</h2>
<ul>
<li>최대 n만큼의 반복 -&gt; $O(n)$</li>
<li>n의 최대값이 250,000 이므로 충분하다.</li>
</ul>
<h2 id="문제-접근법">문제 접근법</h2>
<ol>
<li>입력 받은 방문자들의 총 합이 0이라면 바로 SAD를 출력하고 종료한다.</li>
<li>처음부터 x번째 까지 방문자의 합을 구한다.</li>
<li>딕셔너리를 정의하고 2번에서 구한 합을 key로 하여 value를 1로 설정한다. (value는 개수)</li>
<li>1번 인덱스부터 x명씩 윈도우 슬라이딩을 한다.<br> 4-1. 2번에서 구한 합으로부터 시작하여, 왼쪽에 있는 방문자를 빼고 오른쪽에 있는 방문자를 더한다.<br> 4-2. 4-1에서 계산한 값을 딕셔너리의 key로 하여 value를 1씩 추가한다.</li>
<li>딕셔너리에서 가장 큰 key를 구한다.(합이 가장 큰 경우)</li>
<li>해당 key와 value를 출력한다.</li>
</ol>
<h2 id="코드">코드</h2>
<pre><code class="language-python"># BOJ
# S3 - 21921(블로그)

import sys
from collections import defaultdict
input = sys.stdin.readline

n, x = map(int, input().split())
visitors = list(map(int, input().split()))

if sum(visitors) == 0:
    print(&quot;SAD&quot;)
else:
    total = sum(visitors[:x])
    results = defaultdict(int)
    results[total] += 1

    for i in range(1, n - x + 1):
        total = total - visitors[i - 1] + visitors[i + x - 1]
        results[total] += 1

    max_visitors = max(results)

    print(max_visitors)
    print(results[max_visitors])</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ: 20440] - Python / 파이썬 - 🎵니가 싫어 싫어 너무 싫어 싫어 오지 마 내게 찝쩍대지마🎵]]></title>
            <link>https://velog.io/@o_joon_/BOJ-20440-Python-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%8B%88%EA%B0%80-%EC%8B%AB%EC%96%B4-%EC%8B%AB%EC%96%B4-%EB%84%88%EB%AC%B4-%EC%8B%AB%EC%96%B4-%EC%8B%AB%EC%96%B4-%EC%98%A4%EC%A7%80-%EB%A7%88-%EB%82%B4%EA%B2%8C-%EC%B0%9D%EC%A9%8D%EB%8C%80%EC%A7%80%EB%A7%88</link>
            <guid>https://velog.io/@o_joon_/BOJ-20440-Python-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%8B%88%EA%B0%80-%EC%8B%AB%EC%96%B4-%EC%8B%AB%EC%96%B4-%EB%84%88%EB%AC%B4-%EC%8B%AB%EC%96%B4-%EC%8B%AB%EC%96%B4-%EC%98%A4%EC%A7%80-%EB%A7%88-%EB%82%B4%EA%B2%8C-%EC%B0%9D%EC%A9%8D%EB%8C%80%EC%A7%80%EB%A7%88</guid>
            <pubDate>Tue, 19 Nov 2024 07:24:00 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<ul>
<li><a href="https://www.acmicpc.net/problem/20440">https://www.acmicpc.net/problem/20440</a></li>
</ul>
<h2 id="시간-복잡도">시간 복잡도</h2>
<ul>
<li>n만큼의 반복 + 최대 n만큼의 탐색 + 최대 n크기로 정렬 -&gt; $O(n)$</li>
</ul>
<h2 id="문제-접근법">문제 접근법</h2>
<ul>
<li>접근 방법이 안떠올라서 구글링 -&gt; <a href="https://hi-guten-tag.tistory.com/354">참조1</a>, <a href="https://dalseoin.tistory.com/entry/%EB%B0%B1%EC%A4%8020440-%EB%8B%88%EA%B0%80-%EC%8B%AB%EC%96%B4-2">참조2</a></li>
</ul>
<ol>
<li>딕셔너리에 모기의 입퇴장 시간을 기록한다. -&gt; 입장 시간: +1, 퇴장 시간: -1</li>
<li>가장 많은 시간의 모기 수, 현재의 모기 수, 답이 될 입퇴장 시간을 초기화한다.</li>
<li>정확한 정답을 위한 flag를 초기화한다. -&gt; flag가 없는 경우, 퇴장 시간이 뒤로 밀릴 수 있다.<br>(최대치로 증가했다 감소하는 패턴이 반복되면, 퇴장시간이 마지막 패턴의 감소하는 시간이 될 수 있기 때문)</li>
<li>딕셔너리를정렬하여 가장 처음부터 탐색한다.<br> 4-1. 현재 모기 수에 현재 키에 해당하는 값(현재 시간의 모기 수)을 더한다.<br> 4-2. 현재 모기 수가 가장 많은 시간의 모기 수보다 크다면 갱신을 해준 후, 입장 시간을 현재 시간으로 설정하고 flag를 True로 체크해준다.<br> 4-3. 현재 모기 수가 가장 많은 시간의 모기 수보다 작으면서 flag가 True면, 퇴장 시간을 현재 시간으로 설정하고 flag를 False로 체크해준다.</li>
</ol>
<h4 id="정리">정리</h4>
<ul>
<li>모기의 정보가 들어있는 시간만을 차례대로 탐색하면서, 모기의 수가 최대가 되는 시간을 입장 시간으로 갱신하고 최대치에서 감소하는 시간을 퇴장 시간으로 갱신한다.   </li>
<li>같은 패턴이 여러 번 반복되어도 가장 빠른 시간대의 구간만을 출력해야 하기 때문에 flag가 필요하다.</li>
</ul>
<h2 id="코드">코드</h2>
<pre><code class="language-python"># BOJ
# G3 - 20440(🎵니가 싫어 싫어 너무 싫어 싫어 오지 마 내게 찝쩍대지마🎵 - 1)

import sys
from collections import defaultdict
input = sys.stdin.readline

n = int(input())
d = defaultdict(int)

for _ in range(n):
    s, e = map(int, input().split())
    d[s] += 1
    d[e] -= 1

max_mos, now_mos = 0, 0
tem, txm = 0, 0
flag = False

for i in sorted(d.keys()):
    now_mos += d[i]
    if now_mos &gt; max_mos:
        max_mos = now_mos
        tem = i
        flag = True
    elif now_mos &lt; max_mos and flag:
        txm = i
        flag = False

print(max_mos)
print(tem, txm)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ: 15724] - Python / 파이썬 - 이건 꼭 풀어야해!]]></title>
            <link>https://velog.io/@o_joon_/BOJ-15724-Python-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%9D%B4%EA%B1%B4-%EA%BC%AD-%ED%92%80%EC%96%B4%EC%95%BC%ED%95%B4</link>
            <guid>https://velog.io/@o_joon_/BOJ-15724-Python-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%9D%B4%EA%B1%B4-%EA%BC%AD-%ED%92%80%EC%96%B4%EC%95%BC%ED%95%B4</guid>
            <pubDate>Thu, 14 Nov 2024 07:58:31 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<ul>
<li><a href="https://www.acmicpc.net/problem/15724">https://www.acmicpc.net/problem/15724</a></li>
</ul>
<h2 id="시간-복잡도">시간 복잡도</h2>
<ul>
<li>n * m만큼 반복 + k만큼 반복 -&gt; $O(nm + k)$</li>
<li>n, m의 최대값은 1024, k는 10만이기 때문에 충분하다.</li>
</ul>
<h2 id="문제-접근법">문제 접근법</h2>
<ul>
<li>접근 방법이 안떠올라서 구글링 -&gt; <a href="https://yiyj1030.tistory.com/489">참조1</a>, <a href="https://my-coding-notes.tistory.com/237">참조2</a></li>
<li>푸는 방법 자체는 2차원 배열의 구간합이다.</li>
</ul>
<ol>
<li>2차원 배열의 누적합을 구하기 위해선, 다음과 같은 공식을 통해 각 열마다 누적합을 구해준다.
$dp[i][j] = area[i-1][j-1] + dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1]$</li>
<li>k만큼 반복하며 좌표 입력을 받는다.</li>
<li>2차원 배열의 구간합을 구하기 위해선, 다음과 같은 공식을 통해 원하는 구간의 합을 구해준다.
$dp[i][j] - dp[x-1][j] - dp[i][y-1] + dp[x-1][y-1]$</li>
</ol>
<h2 id="코드">코드</h2>
<pre><code class="language-python"># BOJ
# S1 - 15724(주지수)

import sys
input = sys.stdin.readline

n, m = map(int, input().split())
people = [list(map(int, input().split())) for _ in range(n)]
dp = [[0] * (m + 1) for _ in range(n + 1)]

for i in range(1, n + 1):
    for j in range(1, m + 1):
        dp[i][j] = people[i - 1][j - 1] + dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1]

for _ in range(int(input())):
    x1, y1, x2, y2 = map(int, input().split())
    print(dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1 - 1])</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BOJ: 2851] - Python / 파이썬 - 슈퍼마리오]]></title>
            <link>https://velog.io/@o_joon_/BOJ-2851-Python-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%8A%88%ED%8D%BC%EB%A7%88%EB%A6%AC%EC%98%A4</link>
            <guid>https://velog.io/@o_joon_/BOJ-2851-Python-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%8A%88%ED%8D%BC%EB%A7%88%EB%A6%AC%EC%98%A4</guid>
            <pubDate>Thu, 14 Nov 2024 07:56:10 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<ul>
<li><a href="https://www.acmicpc.net/problem/2851">https://www.acmicpc.net/problem/2851</a></li>
</ul>
<h2 id="시간-복잡도">시간 복잡도</h2>
<ul>
<li>최대 n만큼의 반복문을 탐색하기 때문에, $O(n)$.</li>
<li>n의 최대 값은 10이기 때문에, 제한시간 조건에 충분히 부합한다.</li>
</ul>
<h2 id="문제-접근법">문제 접근법</h2>
<ol>
<li>10개의 버섯을 차례대로 탐색한다.</li>
<li>탐색하면서 현재 버섯의 점수를 계속 더한다.</li>
<li>점수가 100일 경우, 즉시 반복문을 종료한다.</li>
<li>점수가 100보다 큰 경우, 처음부터 현재 점수까지 더한 값과 이전 점수까지만 더한 값을 비교한다.
4-1. 현재 점수까지 더한 값이 이전 점수까지만 더한 값보다 100과의 차이가 큰 경우, 이전 점수까지만 더한 값을 반환하고 출력한다.</li>
</ol>
<h2 id="코드">코드</h2>
<pre><code class="language-python"># BOJ
# B1 - 2851(슈퍼마리오)

def getABS(n):
    return abs(100 - n)

def solution(mushrooms) -&gt; int:
    ans = 0

    for mushroom in mushrooms:
        ans += mushroom

        if ans == 100:
            break
        if ans &gt; 100 and getABS(ans) &gt; getABS(ans - mushroom):
            ans -= mushroom
            break

    return ans

mushrooms = [int(input()) for _ in range(10)]
result = solution(mushrooms)

print(result)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Linux] 로컬에서 가상환경 서버 접속]]></title>
            <link>https://velog.io/@o_joon_/Linux-%EB%A1%9C%EC%BB%AC%EC%97%90%EC%84%9C-%EA%B0%80%EC%83%81%ED%99%98%EA%B2%BD-%EC%84%9C%EB%B2%84-%EC%A0%91%EC%86%8D</link>
            <guid>https://velog.io/@o_joon_/Linux-%EB%A1%9C%EC%BB%AC%EC%97%90%EC%84%9C-%EA%B0%80%EC%83%81%ED%99%98%EA%B2%BD-%EC%84%9C%EB%B2%84-%EC%A0%91%EC%86%8D</guid>
            <pubDate>Tue, 12 Nov 2024 13:27:13 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요! 직전에 Ubuntu를 가상환경(UTM)에 설치하는 포스팅을 했습니다.
이 포스팅은 로컬에서 이 가상환경 서버에 접속하는 과정 중에 어떤 문제들이 있었고, 어떻게 해결했는지에 대한 기록입니다.</p>
<p>서버 관련은 초짜라 로컬에서 계속 127.0.0.1, localhost 입력하면서 <strong>왜 안되지??</strong> 만 수도 없이 내뱉으며 삽질했습니다ㅠ</p>
<hr>
<h1 id="fastapi로-서버-열기">FastAPI로 서버 열기</h1>
<p><a href="https://fastapi.tiangolo.com/ko/">FastAPI 공식 문서</a></p>
<blockquote>
<p><strong>FastAPI란?</strong>
FastAPI는 현대적이고, 빠르며(고성능), 파이썬 표준 타입 힌트에 기초한 Python의 API를 빌드하기 위한 웹 프레임워크입니다. - 공식 문서 -</p>
</blockquote>
<p>FastAPI는 Django, Flask와 같은 파이썬 프레임워크입니다.
빠르고, 쉽고, API에 최적화 되어있기 때문에 급상승 중인 웹 애플리케이션 프레임워크라고 합니다.</p>
<h3 id="fastapi의-특징">FastAPI의 특징</h3>
<ul>
<li><h4 id="빠른-성능">빠른 성능</h4>
<p>FastAPI는 비동기 처리와 빠른 성능을 자랑합니다.
다른 Python 프레임워크에 비해 성능이 뛰어나며, Google의 gRPC나 Node.js와 같은 빠른 프레임워크와 비슷한 속도를 보여줍니다.</p>
</li>
<li><h4 id="자동화된-api-문서화">자동화된 API 문서화</h4>
<p>API를 자동으로 문서화해줍니다.
기본적으로 Swagger UI와 ReDoc을 제공하여 API를 쉽게 테스트하고 문서화할 수 있습니다.
OpenAPI 표준을 기반으로 한 문서화가 자동으로 생성됩니다.</p>
</li>
<li><h4 id="타이핑-지원">타이핑 지원</h4>
<p>타이핑 힌트를 통해 코드의 가독성과 안정성을 높이고, 자동으로 입력 값 검증을 할 수 있습니다.
예를 들어, 요청 파라미터나 바디의 데이터 타입을 명확하게 정의할 수 있습니다.</p>
</li>
<li><h4 id="비동기-프로그래밍-지원">비동기 프로그래밍 지원</h4>
<p>async/await 구문을 사용하여 비동기 요청을 처리할 수 있습니다. 
이를 통해 더 많은 동시 요청을 처리할 수 있게 되어 성능이 높아집니다.</p>
</li>
<li><h4 id="데이터-검증">데이터 검증</h4>
<p>Pydantic을 사용하여 요청받은 데이터를 자동으로 검증합니다. 
예를 들어, 클라이언트가 전송한 JSON 데이터를 Python 객체로 자동 변환하고, 타입 검사 및 유효성 검사도 수행됩니다.</p>
</li>
<li><h4 id="편리한-개발">편리한 개발</h4>
<p>코드 작성 시 바로 자동으로 문서화되고, 디버깅 시 오류 메시지와 타이핑 정보를 잘 제공합니다.
이로 인해 개발자가 오류를 빠르게 찾고 수정할 수 있습니다.</p>
</li>
</ul>
<p>이 외에도, 공식 문서가 아주 잘 되어있다는 장점 또한 있습니다.</p>
<p>이 포스팅은 해당 공식 문서에서 설명하는 과정 중 서버를 여는 과정까지만 진행했습니다.</p>
<h2 id="서버-세팅-과정">서버 세팅 과정</h2>
<h4 id="1-파이썬-설치">1. 파이썬 설치</h4>
<p>FastAPI가 파이썬 프레임워크이기 때문에, 파이썬을 먼저 리눅스에 설치해주어야 합니다.
한 줄이면 금방 끝납니다.</p>
<pre><code class="language-bash">sudo apt install python3</code></pre>
<h4 id="2-venv-설치">2. venv 설치</h4>
<p>venv는 Virtual Environment의 약자로, UTM과 같은 가상환경입니다.
venv를 통해 여러 개의 파이썬 패키지를 독립적으로 관리할 수 있는 것이죠.
즉, 로컬(맥) 위에 가상환경(리눅스 기반) 위에 가상환경(파이썬 패키지)처럼 실행되는 것입니다.
이것도 한 줄이면 금방 끝납니다.</p>
<pre><code class="language-bash">sudo apt install python3-venv</code></pre>
<h4 id="3-파이썬-가상환경-생성">3. 파이썬 가상환경 생성</h4>
<p>파이썬 가상환경을 만들어줍니다.
가장 뒤에 있는 문자가 가상환경 이름입니다.
해당 명령어를 통해 venv라는 이름의 파이썬 가상환경을 생성하는 것이죠.</p>
<pre><code class="language-bash">python3 -m venv venv</code></pre>
<p>이후 ls를 입력하면 venv 라는 폴더가 새로 생겨있습니다.
생성할 때 정한 이름으로 폴더가 생성됩니다.</p>
<h4 id="4-파이썬-가상환경-활성화">4. 파이썬 가상환경 활성화</h4>
<p>가장 앞의 venv는 생성한 가상환경 폴더입니다.
해당 명령어를 통해 활성화시켜줍니다.</p>
<pre><code class="language-bash">source ./venv/bin/activate</code></pre>
<p>활성화 시키면 다음 사진과 같이 입력 줄의 가장 앞에 가상환경의 이름이 붙습니다.
<img src="https://velog.velcdn.com/images/o_joon_/post/54684ef7-d414-4762-a159-8737e605f4e7/image.png" alt=""></p>
<p>비활성화를 위해선 단어 하나만 입력해주면 됩니다.</p>
<pre><code class="language-bash">deactivate</code></pre>
<h4 id="5-fastapi-설치">5. FastAPI 설치</h4>
<p>이제 파이썬 기반의 환경이므로, pip를 통해 나머지를 설치해주도록 합시다.
해당 과정 부터는 <a href="https://fastapi.tiangolo.com/ko/">FastAPI 공식 사이트</a>에서 그대로 따라하실 수 있습니다.</p>
<pre><code class="language-bash">pip install fastapi</code></pre>
<h4 id="6-uvicorn-설치">6. uvicorn 설치</h4>
<p>FastAPI에서 필수적으로 사용되는 uvicorn을 설치합니다.
uvicorn에 대한 내용은 <a href="https://devocean.sk.com/blog/techBoardDetail.do?ID=165922&amp;boardType=techBlog">해당 블로그</a>에서 확인하실 수 있습니다.</p>
<p>그냥 pip install uvicorn을 해도 되지만, 최소한의 파일로 작동할 수 있게 하기 위해 다음과 같이 합니다.</p>
<pre><code class="language-bash">pip install &quot;uvicorn[standard]&quot;</code></pre>
<h4 id="7-mainpy-생성">7. main.py 생성</h4>
<p>서버를 실행시키기 위한 파이썬 파일을 생성합니다.
해당 명령어를 통해 파일을 생성하시면 됩니다.</p>
<pre><code class="language-bash">vi main.py</code></pre>
<p>main.py에는 다음과 같은 내용을 입력하고 저장합니다.</p>
<pre><code class="language-python">from fastapi import FastAPI # fastapi 클래스를 불러옵니다. 

app = FastAPI() # FastAPI 클래스를 바탕으로 app이란 인스턴스를 만듭니다. 

@app.get(&quot;/&quot;) # GET 메소드로 가장 루트 url로 접속할 경우
async def root(): # root() 함수를 실행하고
    return {&quot;message&quot;: &quot;Hello World&quot;} # Hello World란 메시지를 반환합니다.</code></pre>
<h4 id="8-서버-실행">8. 서버 실행</h4>
<p>해당 명령어를 통해 서버를 실행합니다.</p>
<p>main: main.py 파일
app: main.py에서 생성한 FastAPI 객체
--reload: 코드가 변경된 후 서버 재시작(개발 환경에서만 사용)</p>
<pre><code class="language-bash">uvicorn main:app --reload</code></pre>
<p>해당 코드를 실행하면 다음과 같은 내용이 나옵니다.</p>
<p><img src="https://velog.velcdn.com/images/o_joon_/post/1a04f0fa-f695-4b8c-90f9-bf37df936c0d/image.png" alt=""></p>
<p>그럼 이제 주소창에 <code>127.0.0.1:8000</code>을 넣으면 <code>{&quot;message&quot; :&quot;Hello World&quot;}</code> 와 같은 내용이 출력되겠..</p>
<p><img src="https://velog.velcdn.com/images/o_joon_/post/f1225bd8-61cd-47cb-b574-75c6d88c4aab/image.png" alt=""></p>
<p>지 라는 생각을 하면 당연히 안됩니다!! (로컬-로컬, VM-VM(동일)은 일반적으로 됩니다.)
로컬호스트에 대한 개념을 몰라서 이거 해결하느라 몇시간 삽질한거였습니다..</p>
<p><img src="https://velog.velcdn.com/images/o_joon_/post/c513e6c1-aa4e-4ace-bc67-13fd7162f865/image.jpg" alt=""></p>
<p>(IP는 대충 예시로 다르다는 것을 표현하였습니다.)
로컬과 가상환경은 다른 IP 주소를 가지고 있기 때문에, localhost나 127.0.0.1로는 접속할 수 없습니다.</p>
<p>해당 과정을 해결하기 위해 <a href="https://velog.io/@chosj1526/UTM-ubuntu-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%B6%94%EA%B0%80-%EB%B0%8F-%EA%B3%A0%EC%A0%95-ip-%ED%95%A0%EB%8B%B9%ED%95%98%EA%B8%B0">해당 블로그</a>와 GPT의 힘을 빌려 해결하였습니다.</p>
<p>결론은 가상환경의 IP와 로컬의 IP가 다르기 때문에 로컬호스트로는 접속이 불가능한 것이었고,
이를 위해 가상환경에서 네트워크 세팅을 하여 외부에서도 접속이 가능하도록 만들어 주어야 했습니다.</p>
<p>해당 블로그의 해결과정과 조금 다른 점은 yaml 파일의 이름이었습니다.
저는 <code>50-cloud-init.yaml</code> 이라는 파일이었어요.
<code>ls /etc/netplan</code> 명령어를 통해 파일 이름을 확인하고 수정해야 합니다!</p>
<p>변경 사항을 모두 적용했다면 다음과 같은 명령어로 서버를 열어주셔야 합니다.
0.0.0.0을 주소창에 치는 것이 아닌, 설정해준 고정 IP를 입력하셔야 합니다!!
(이것도 모르고 0.0.0.0:8000 입력하면서 또 삽질 계속했습니다.)</p>
<pre><code class="language-bash">uvicorn main:app -reload --host 0.0.0.0 -- port 8000</code></pre>
<br>

<p>결과적으로 다음과 같이 실행되며 정상적으로 로컬에서 가상환경의 서버로 접속이 성공했습니다!</p>
<ul>
<li><p>로컬 웹페이지
<img src="https://velog.velcdn.com/images/o_joon_/post/3dab291b-ac72-4cc3-ae88-8a462ad3fd60/image.png" alt=""></p>
</li>
<li><p>가상환경 리눅스
<img src="https://velog.velcdn.com/images/o_joon_/post/309f7246-eead-4125-a49c-fe76f94a47d1/image.png" alt=""></p>
</li>
<li><p>생성된 API 문서(Swagger UI)
주소창 포트번호 뒤에 <code>/docs</code>를 추가하면 나오는 페이지입니다.
<img src="https://velog.velcdn.com/images/o_joon_/post/6766d85f-df39-4e0e-b33c-4cc6403f4c76/image.png" alt=""></p>
</li>
</ul>
<hr>
<h1 id="마치며">마치며</h1>
<p>정말 오랜만에 만져보는 리눅스였고, 서버 열기였습니다.
시간이 오래 지난 것도 있지만, 전공 수업을 열심히 듣고 기억했더라면 좀 더 빠르고 쉽게 하지 않았을까 싶네요..
과거를 반성하고 오늘의 삽질을 발판 삼아 열심히 공부해야겠습니다!!</p>
<hr>
<h1 id="참조">참조</h1>
<p><a href="https://typingdog.tistory.com/106">https://typingdog.tistory.com/106</a></p>
<p><a href="https://velog.io/@chosj1526/UTM-ubuntu-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%B6%94%EA%B0%80-%EB%B0%8F-%EA%B3%A0%EC%A0%95-ip-%ED%95%A0%EB%8B%B9%ED%95%98%EA%B8%B0">https://velog.io/@chosj1526/UTM-ubuntu-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%B6%94%EA%B0%80-%EB%B0%8F-%EA%B3%A0%EC%A0%95-ip-%ED%95%A0%EB%8B%B9%ED%95%98%EA%B8%B0</a></p>
<p><a href="https://blog.iamwhatiam.co.kr/215">https://blog.iamwhatiam.co.kr/215</a></p>
<p><a href="https://devocean.sk.com/blog/techBoardDetail.do?ID=165922&amp;boardType=techBlog">https://devocean.sk.com/blog/techBoardDetail.do?ID=165922&amp;boardType=techBlog</a></p>
<p><a href="https://www.hanbit.co.kr/channel/category/category_view.html?cms_code=CMS5997817104">https://www.hanbit.co.kr/channel/category/category_view.html?cms_code=CMS5997817104</a></p>
<p><a href="https://wikidocs.net/162345">https://wikidocs.net/162345</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Linux] Mac에  Ubuntu 설치]]></title>
            <link>https://velog.io/@o_joon_/Linux-Mac%EC%97%90-%EA%B0%80%EC%83%81%ED%99%98%EA%B2%BD-Ubuntu-Server-%EC%84%A4%EC%B9%98</link>
            <guid>https://velog.io/@o_joon_/Linux-Mac%EC%97%90-%EA%B0%80%EC%83%81%ED%99%98%EA%B2%BD-Ubuntu-Server-%EC%84%A4%EC%B9%98</guid>
            <pubDate>Tue, 12 Nov 2024 07:30:05 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요 오랜만입니다!
iOS 관련 공부와 포스팅만 하다가 공부해보고 싶은 새로운 것이 생겨서 시작해보았습니다.
데이터 관련 개발에 관심이 생겨서 시작했는데요, 리눅스 환경에서 파이썬을 통한 서버 개발 등을 시도해 보려고 하고 있습니다.
오늘은 그 첫 번째 과정으로, Ubuntu를 Mac에 설치해보았습니다.</p>
<br>

<p>들어가기에 앞서, 리눅스 환경에서 개발을 원하시는 경우 세팅하는 방법이 여러가지가 있습니다.</p>
<p>첫 번째는, <code>가상환경(UTM, VirtualBox 등)에 Ubuntu 직접 설치</code>가 있습니다.
제가 이 방법으로 설치하였습니다.</p>
<p>두 번째는, <code>브라우저에서 손쉽게 가상환경을 실행</code>하는 것입니다.
groomide의 대쉬보드, AWS의 Cloud9, GDP의 Cloud Engine 등과 같이 브라우저에서 리눅스를 이용하는 것입니다.
해당 방법은 구글링을 하면 금방 따라하실 수 있습니다.</p>
<hr>
<p>제 OS는 작성일 기준으로 다음과 같습니다.</p>
<p>기종: MacBook Pro 14
칩: Apple M3 Pro
메모리: 18GB
macOS: Sonoma 14.4.1</p>
<br>

<p>설치하려고 하는 Ubuntu는 Desktop용이 아닌 <code>Server</code>용입니다.
<code>GUI</code>(Graphic User Interface)가 아닌 <code>CLI</code>(Command Line Interface)로 리눅스를 사용해보려 합니다.
무슨 차이냐면, 터미널과 같이 아이콘 클릭이 아닌 커맨드 입력으로 모든 작업을 진행할 계획입니다.
(<em>관련 직종 현업자 분들이 이와 같은 과정을 추천해주셨습니다.</em>)</p>
<hr>
<p>가상환경(UTM) 및 Ubuntu 설치 과정은 <a href="https://solearn.tistory.com/275">해당 블로그</a> 를 보고 <code>3번 과정, Ubuntu Desktop의 전까지</code>만 따라했습니다.
CLI로 공부를 할 것이라면 이후부터 필요하지 않습니다!</p>
<p>여기서 <strong>주의해야 할 점</strong>은 다음과 같습니다.</p>
<p>애플 실리콘 기반이기 때문에, <code>Ubuntu Server for ARM</code> 을 다운받으셔야 합니다.
그렇지 않으면 가상 환경에서 다음 사진과 같은 에러가 발생하게 됩니다.
<a href="https://ubuntu.com/download/server/arm">다운 링크: Ubuntu Server for ARM</a></p>
<p><img src="https://velog.velcdn.com/images/o_joon_/post/bb39eba4-6e4e-43ca-8e0e-6f6db986025d/image.png" alt=""></p>
<p>설치 과정 블로그에선 UTM을 깃허브에서 설치하고 있지만, 사이트에서 직접 설치하셔도 됩니다. 
<a href="https://mac.getutm.app/">다운 링크: UTM</a></p>
<br>

<p><img src="https://velog.velcdn.com/images/o_joon_/post/43679e7b-37d9-4e76-a5a9-20aac2d9585a/image.png" alt=""></p>
<p>세팅이 끝나면 다음과 같이 맥의 터미널처럼 명령어들을 입력할 수 있습니다!</p>
<p>이 이후로는 리눅스 명령어 공부, 파이썬 설치 및 환경 세팅, 파이썬 서버 실행하기 등등을 해보려고 합니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift] Class vs Struct (2)]]></title>
            <link>https://velog.io/@o_joon_/Swift-Class-vs-Struct-2</link>
            <guid>https://velog.io/@o_joon_/Swift-Class-vs-Struct-2</guid>
            <pubDate>Wed, 21 Aug 2024 12:08:38 GMT</pubDate>
            <description><![CDATA[<p>이번 포스팅은 Class와 Struct에서 참조 카운트에 대해 알아보겠습니다.</p>
<p>지난 포스팅
<a href="https://velog.io/@o_joon_/Swift-Class-VS-Struct-1">Class vs Struct (1)</a></p>
<p>Class의 메모리 관리는 힙 영역에서 ARC에 의해 이루어지고, Struct의 메모리 관리는 스택 영역에서 LIFO 방식으로 알아서 된다고 알고 있습니다.</p>
<p>ARC에 대한 자세한 내용은 길기 때문에 ARC에 대한 포스팅을 따로 하고, 이번 포스팅에서는 참조 카운트 계산과 메모리 영역에서 어떻게 이루어지는지 간단하게 시각화하여 나타낼 예정입니다.
또한, 클래스 프로퍼티를 가지고 있는 클래스와 구조체를 비교하여 참조가 어떻게 이루어지는지도 알아볼 예정입니다.</p>
<hr>
<p>먼저, 지난 포스팅에서 설명했던 클래스는 참조 타입이고 구조체는 값 타입이라고 했었습니다.
이를 비교하기 위한 아주 기본적인(다들 알고 계신) 코드는 이렇습니다.</p>
<ul>
<li>주소값 출력 함수</li>
</ul>
<pre><code class="language-swift">func address(_ object: AnyObject) {
    print(Unmanaged.passUnretained(object).toOpaque())
}</code></pre>
<h1 id="값-타입-vs-참조-타입">값 타입 vs 참조 타입</h1>
<h2 id="class">Class</h2>
<ul>
<li>클래스 생성</li>
</ul>
<pre><code class="language-swift">class ExamplaryClass {
    var a: Int

    init(a: Int) {
        self.a = a
    }

    func updateA(_ a: Int) {
        self.a = a
    }
}

let exampleClass = ExamplaryClass(a: 10)
let reference = exampleClass
</code></pre>
<ul>
<li>참조 확인</li>
</ul>
<pre><code class="language-swift">print(exampleClass.a) // 10
print(reference.a) // 10</code></pre>
<p>두 값 모두 10이 출력됩니다.</p>
<pre><code class="language-swift">exampleClass.updateA(20)

print(exampleClass.a) // 20
print(reference.a) // 20</code></pre>
<p>exampleClass의 a를 20으로 업데이트하면, 두 값 모두 20이 출력됩니다.
reference는 exampleClass와 같은 클래스 인스턴스에 대한 참조를 가지고 있기 때문입니다.</p>
<pre><code class="language-swift">reference.updateA(30)

print(exampleClass.a) // 30
print(reference.a) // 30</code></pre>
<p>반대로 reference의 a를 30으로 업데이트해도 두 값 모두 30이 출력됩니다.</p>
<pre><code class="language-swift">address(exampleClass) // 0x0000600001801000
address(reference) // 0x0000600001801000</code></pre>
<p>둘의 참조된 주소값을 확인하면 동일하게 나옵니다.
이것이 참조 타입인 클래스의 특징입니다.</p>
<p>메모리에선 최종적으로 다음과 같이 할당되어 있습니다.
<img src="https://velog.velcdn.com/images/o_joon_/post/0bbf8b1b-8e47-4524-9754-55781dfbe6d8/image.jpg" alt=""></p>
<h2 id="struct">Struct</h2>
<ul>
<li>구조체 생성</li>
</ul>
<pre><code class="language-swift">struct ExamplaryStruct {
    var a: Int

    mutating func updateA(_ a: Int) {
        self.a = a
    }
}

var exampleStruct = ExamplaryStruct(a: 10)
var copy = exampleStruct</code></pre>
<p>클래스와 다른 점이 두 가지가 있습니다.
첫 번째는 updateA() 메서드의 앞에 붙은 <code>mutating</code> 키워드 입니다.
Swift에서 값 타입은 기본적으로 내부에서 인스턴스를 변경할 수 없기 때문에, mutating 키워드를 통해 변경할 수 있도록 해주어야 합니다.</p>
<p>두 번째는 <code>let</code>이 아닌 <code>var</code> 입니다.
클래스는 참조 타입이므로, let으로 선언해도 인스턴스의 내부 프로퍼티를 변경할 수 있습니다. (프로퍼티가 var인 경우)
let은 인스턴스에 대한 참조가 변하지 않는다는 표현일 뿐, 인스턴스의 프로퍼티는 수정할 수 있다는 것을 의미합니다.
반면에 구조체는 값 타입이므로, let으로 선언하면 인스턴스 뿐만 아니라 내부 프로퍼티도 모두 수정할 수 없습니다.</p>
<pre><code class="language-swift">exampleStruct.updateA(20)

print(exampleStruct.a) // 20
print(copy.a) // 10</code></pre>
<p>exampleStruct의 a값을 20으로 변경하면, exampleStruct의 a만 변경됩니다.
구조체는 클래스와 다르게 값 타입이므로, copy는 exampleStruct와 동일한 인스턴스를 가리키는 참조 값이 아닌 새로운 복사본이기 때문입니다.</p>
<pre><code class="language-swift">copy.updateA(30)

print(exampleStruct.a) // 20
print(copy.a) // 30</code></pre>
<p>copy의 a값을 변경하면, 위와 같은 이유로 copy의 a값만 변경됩니다.</p>
<pre><code class="language-swift">address(exampleStruct) // Error
address(copy) // Error</code></pre>
<p>구조체는 클래스와 다르게 값 타입이기 때문에, 특정 인스턴스를 가리키는 참조(포인터)가 없어 해당 코드를 실행할 수 없습니다.
그렇기 때문에 뒤에 나올 CFGetRetainCount() 또한 실행할 수 없습니다.</p>
<p>메모리에선 최종적으로 다음과 같이 할당되어 있습니다.
<img src="https://velog.velcdn.com/images/o_joon_/post/daf27411-2886-4d8a-b562-9758d71489b1/image.jpg" alt=""></p>
<h1 id="참조-카운트">참조 카운트</h1>
<p>ARC가 힙 영역에서 참조 카운트를 계산해서 카운트가 0이 되면 인스턴스를 메모리 상에서 해제합니다.
코드와 함께 참조 카운트가 어떻게 계산되는지 알아보도록 하겠습니다.</p>
<p>참조 카운트를 확인하기 위해서는 CFGetRetainCount() 라는 함수를 사용해주면 됩니다.</p>
<p><img src="https://velog.velcdn.com/images/o_joon_/post/ae95cb6f-e508-4c22-a3bb-ecdd5a2fefbc/image.png" alt=""></p>
<p>해당 함수에 대한 정의는 공식문서에서 다음과 같이 나와있습니다.</p>
<blockquote>
<p>Core Foundation 객체의 참조 카운트를 반환합니다.</p>
</blockquote>
<p>특정 객체의 현재 참조 카운트가 몇인지 나타내줍니다.
하지만, 클래스 하나를 생성하고 바로 참조 카운트를 해당 함수를 통해 출력하면 2가 나옵니다.
함수 호출 시, 일시적으로 1이 증가하여 출력되어서 그런 것 같습니다.</p>
<p><code>클래스가 아닌 구조체는 힙 영역에 존재하지 않기 때문에 사용할 수 없습니다.</code></p>
<ul>
<li>클래스 생성</li>
</ul>
<pre><code class="language-swift">class AClass {
    var bClass: BClass? = nil

    init() {
        print(&quot;AClass 메모리 할당&quot;)
    }

    deinit {
        print(&quot;AClass 메모리 해제&quot;)
    }
}

class BClass {
    var aClass: AClass? = nil

    init() {
        print(&quot;BClass 메모리 할당&quot;)
    }

    deinit {
        print(&quot;BClass 메모리 해제&quot;)
    }
}

var a: AClass? = AClass() // AClass 메모리 할당
var b: BClass? = BClass() // BClass 메모리 할당</code></pre>
<p>생성자를 통해 메모리 할당 시점을 출력하도록 했기 때문에, a와 b를 생성하면 각 클래스의 생성자가 호출됩니다.</p>
<ul>
<li>참조 카운트 확인</li>
</ul>
<pre><code class="language-swift">print(CFGetRetainCount(a)) // 2
print(CFGetRetainCount(b)) // 2</code></pre>
<p>위에서 설명했듯이, AClass는 a, BClass는 b가 각각 하나씩 참조하고 있어 참조 카운트가 1입니다.
CFGetRetainCount()를 통해 호출했기 때문에 2로 출력됩니다.</p>
<ul>
<li>메모리 주소 확인</li>
</ul>
<pre><code class="language-swift">address(a!) // 0x00006000023100c0
address(b!) // 0x00006000023106a0</code></pre>
<p>현재 시점의 메모리 할당을 시각적으로 표현하면 이런 식으로 되어있겠네요.
<img src="https://velog.velcdn.com/images/o_joon_/post/d68673a3-df2a-4bed-a195-e4f7a0b612f6/image.jpg" alt=""></p>
<p>그럼 각 클래스의 프로퍼티에 다음과 같이 서로의 클래스를 넣어주면 어떻게 될까요?</p>
<pre><code class="language-swift">a?.bClass = b
b?.aClass = a</code></pre>
<pre><code class="language-swift">print(CFGetRetainCount(a)) // 3
print(CFGetRetainCount(b)) // 3</code></pre>
<p>각각 3이 출력됩니다.
기존의 참조 카운트 값에서 서로를 힙 영역에서 다시 참조하기 때문에 1씩 증가합니다.</p>
<pre><code class="language-swift">address(a!.bClass!) // 0x00006000023106a0
address(b!.aClass!) // 0x00006000023100c0</code></pre>
<p>메모리 주소를 보면 서로를 참조하고 있는 것을 알 수 있습니다.</p>
<p>다음과 같이 말이죠.
<img src="https://velog.velcdn.com/images/o_joon_/post/4852edc0-b1c6-411a-b1a0-76edf3af61fd/image.jpg" alt=""></p>
<pre><code class="language-swift">a = nil
b = nil

print(CFGetRetainCount(a)) // Error
print(CFGetRetainCount(b)) // Error</code></pre>
<p>a와 b를 메모리 상에서 해제하였기 때문에 CFGetRetainCount()를 해주면 포인터가 존재하지 않아 에러가 발생합니다.</p>
<p>또한, 이 상태에서 a와 b를 nil로 하여 메모리 해제를 시켜주려 해도 완전히 해제되지 않습니다.
AClass의 bClass는 BClass를, BClass의 aClass는 AClass를 아직 참조하고 있기 때문입니다.</p>
<p>정상적으로 모두 메모리에서 해제가 되었다면 nil을 대입하였을 때, deinit에서 구현한 텍스트가 출력되어야 하지만 출력되지 않는 것을 보면 알 수 있죠.</p>
<p>메모리 상에선 다음과 같습니다. 아직 서로를 참조하고 있어 참조 카운트가 1씩 존재하기 때문에, 0이 되어야 메모리에서 완전히 해제해주는 ARC는 이들을 가만히 내비둡니다.</p>
<p><img src="https://velog.velcdn.com/images/o_joon_/post/521dca52-8eb3-4e71-9803-9145853df17f/image.jpg" alt=""></p>
<p>따라서, 메모리 상에서 완전히 이들을 해제해주고 싶다면 다음과 같은 과정을 거쳐야 합니다.</p>
<pre><code class="language-swift">a?.bClass = nil
b?.aClass = nil

print(CFGetRetainCount(a)) // 2
print(CFGetRetainCount(b)) // 2

a = nil // AClass 메모리 해제
b = nil // BClass 메모리 해제</code></pre>
<p>이를 강한 참조(Strong Reference)라고 합니다.
각 프로퍼티를 먼저 메모리에서 해제를 해 준 다음 자신의 메모리를 해제해 주어야 완전하게 참조 카운트가 0이 됩니다.</p>
<p>이를 피하기 위해 weak var 또는 unowned var를 통해 약한 참조를 해주어야 합니다만,
강한 참조, 약한 참조 등은 ARC에 대한 포스팅을 할 때 다시 다뤄볼 예정입니다.
이번 포스팅에서는 그냥 참조 카운트가 이런 식으로 이루어진다 라고 생각하면 될 것 같습니다.</p>
<h1 id="조금-더-복잡한-참조-카운트">조금 더 복잡한 참조 카운트</h1>
<p>그럼 조금 더 복잡한 구조로 예시를 확인해보겠습니다.</p>
<pre><code class="language-swift">class DummyClass { }

class ExamplaryClass {
    let dummyClass1 = DummyClass()
    let dummyClass2 = DummyClass()
    let dummyClass3 = DummyClass()
}

struct ExamplaryStruct {
    let dummyClass1 = DummyClass()
    let dummyClass2 = DummyClass()
    let dummyClass3 = DummyClass()
}</code></pre>
<p>다음과 같이 특정 클래스가 있고, 해당 클래스를 프로퍼티로 여러개 가진 클래스와 구조체가 있습니다.
여기서 여러 객체를 생성하고 각 참조 카운트와 주소를 확인해 보면 어떻게 될까요??</p>
<h3 id="클래스를-포함한-클래스">클래스를 포함한 클래스</h3>
<pre><code class="language-swift">let examplaryClass = ExamplaryClass()
let reference1 = examplaryClass
let reference2 = examplaryClass
let reference3 = examplaryClass</code></pre>
<p>ExamplaryClass를 참조하는 examplaryClass 하나를 생성하고 이를 같이 참조하는 3개의 참조를 생성했습니다.</p>
<pre><code class="language-swift">print(CFGetRetainCount(examplaryClass)) // 5
print(CFGetRetainCount(examplaryClass.dummyClass1)) // 2
print(CFGetRetainCount(examplaryClass.dummyClass2)) // 2
print(CFGetRetainCount(examplaryClass.dummyClass3)) // 2</code></pre>
<p>참조 카운트는 다음과 같습니다. 1씩 빼면 4, 1, 1, 1인 것이죠.</p>
<p>examplaryClass의 경우, ExamplaryClass 인스턴스를 생성할 때 1 증가. -&gt; 0 + 1 = 1
reference1, 2, 3에서 참조 할 때 각각 1씩 증가. -&gt; 1 + 3 = 4
이렇게 참조 카운트는 4가 됩니다.</p>
<p>각 dummyClass의 경우, ExamplaryClass 인스턴스를 생성할 때 이미 새로운 서로 다른 DummyClass 인스턴스를 3개 생성하기 때문에 각 참조 카운트는 1입니다.</p>
<p>그럼, 주소값을 확인 해볼까요?</p>
<pre><code class="language-swift">address(examplaryClass) // 0x0000600001568510
address(reference1) // 0x0000600001568510
address(reference2) // 0x0000600001568510
address(reference3) // 0x0000600001568510

address(examplaryClass.dummyClass1) // 0x00006000026cc040
address(reference2.dummyClass1) // 0x00006000026cc040

address(reference1.dummyClass2) // 0x00006000026cc050
address(reference3.dummyClass2) // 0x00006000026cc050</code></pre>
<p>examplaryClass, reference모두 같은 인스턴스를 참조하고 있죠.</p>
<p>dummyClass들에 대한 참조 또한 같은 더미 클래스라면 같은 주소값을 가지고 있습니다.
같은 인스턴스를 참조하고, 해당 인스턴스가 가진 더미 클래스들도 그 더미 클래스의 인스턴스를 참조하고 있기 때문입니다.
말로 설명하기 보다는 그림으로 이해하는게 더 빠를 것 같네요.</p>
<p>메모리 상에서는 이렇게 되어있습니다.
<img src="https://velog.velcdn.com/images/o_joon_/post/f56fbdf4-1ce5-4f72-8c0c-cd494e01c8cc/image.jpg" alt=""></p>
<h3 id="클래스를-포함한-구조체">클래스를 포함한 구조체</h3>
<pre><code class="language-swift">let examplaryStruct = ExamplaryStruct()
let copy1 = examplaryStruct
let copy2 = examplaryStruct
let copy3 = examplaryStruct</code></pre>
<p>ExamplaryStruct 값을 가진 examplaryStruct 하나를 생성하고 이의 복사본 3개를 생성했습니다.</p>
<pre><code class="language-swift">print(CFGetRetainCount(examplaryStruct)) // Error
print(CFGetRetainCount(examplaryStruct.dummyClass1)) // 5
print(CFGetRetainCount(examplaryStruct.dummyClass2)) // 5
print(CFGetRetainCount(examplaryStruct.dummyClass3)) // 5</code></pre>
<p>첫 번째 출력문은 실행할 수 없습니다. CFGetRetainCount()에 대한 정의를 설명할 때, 구조체는 사용할 수 없다고 써놓았죠?</p>
<p>여기서는 클래스와 확연하게 다른 점이 보입니다.
각 더미클래스의 참조 카운트가 4라는 점인데요. 왜 이렇게 되는지 알아보도록 합시다.</p>
<p>examplaryStruct를 생성할 때, dummyClass1, 2, 3을 가진 구조체를 하나 생성합니다.
더미 클래스들은 서로 다른 객체이기 때문에, 각각 다른 DummyClass에 대한 참조를 가지고 있습니다.
즉, ExamplaryStruct는 3개의 DummyClass 인스턴스에 대한 참조를 가진 구조체로써 스택 영역에 저장되는 것입니다.</p>
<p>copy 1, 2, 3은 구조체 이므로 값 타입이며, examplaryStruct의 복사본입니다.
스택 영역에 examplaryStruct와 동일한 구조체를 복사하여 저장됩니다.
이 구조체이 가지고 있는 더미 클래스들 또한 원본과 같은 참조를 가지고 있습니다.</p>
<p>따라서, examplaryStruct, copy1, 2, 3에 있는 각각의 더미 클래스들은 모두 3개의 더미클래스에 대한 참조인 것이죠. 그렇기 때문에 참조 카운트가 4인 것입니다.</p>
<p>메모리 상에는 다음과 같이 나타날 것입니다.(조금 눈아프지만 이렇게밖에 표현 못해서 죄송합니다..)
<img src="https://velog.velcdn.com/images/o_joon_/post/950279a8-2a58-4395-9ced-2deb8eaa36e4/image.jpg" alt=""></p>
<p>실제 개발에서는 이보다 더 복잡한 상황도 많을 것이라 예상됩니다.
따라서, 클래스 및 클로저 등을 사용하는 경우, 참조를 적절하게 활용하고 계산하여 메모리 누수를 최대한 방지해야겠죠?</p>
<br>

<h3 id="마치며">마치며</h3>
<p>이번 포스팅은 저도 처음 공부할 때, 참조 카운트 증가와 해제가 헷갈렸습니다.
특히, 구조체 속 클래스와 클래스 속 클래스에서 머리가 좀 아팠습니다.
하지만 직접 그림을 그려가며 공부해보니 이해가 잘 되더라구요.
다음에 앱을 개발하며 메모리 누수를 생각해야 할 때 그림을 그려가며 메모리 방지를 위해 노력해야겠다 생각했습니다 ㅎㅎ..</p>
<p>Class vs Struct에 대한 포스팅은 아직 끝나지 않았습니다..
3번째는 클래스와 구조체가 어떤 상황에서 조금 더 빠른지 몇 가지 실험을 할 예정입니다.</p>
<hr>
<h3 id="참조">참조</h3>
<p><a href="https://ios-development.tistory.com/1464">https://ios-development.tistory.com/1464</a>
<a href="https://stackoverflow.com/questions/24058906/printing-a-variable-memory-address-in-swift">https://stackoverflow.com/questions/24058906/printing-a-variable-memory-address-in-swift</a>
<a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/methods/">https://docs.swift.org/swift-book/documentation/the-swift-programming-language/methods/</a>
<a href="https://developer.apple.com/documentation/corefoundation/1521288-cfgetretaincount?language=objc">https://developer.apple.com/documentation/corefoundation/1521288-cfgetretaincount?language=objc</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift] Class vs Struct (1)]]></title>
            <link>https://velog.io/@o_joon_/Swift-Class-VS-Struct-1</link>
            <guid>https://velog.io/@o_joon_/Swift-Class-VS-Struct-1</guid>
            <pubDate>Tue, 20 Aug 2024 11:27:41 GMT</pubDate>
            <description><![CDATA[<p>오늘은 개발자라면 기본적으로 알아야 하며, 면접에서 단골 멘트인 Class와 Struct에 대한 것을 알아보도록 하겠습니다.
실제 코드들을 포함하여 여러가지 비교를 할 예정이기 때문에 나눠서 포스팅을 할건데, 첫 번째는 기본적인 개념과 공통점 및 차이점 등입니다.</p>
<hr>
<p>먼저, Class와 Struct는 모두 사용자 정의 데이터 타입을 정의하는 데 사용되는 구문이며, 상태 및 동작을 설명하는 데에 상요되는 데이터 멤버와 멤버 함수를 포함합니다.</p>
<p>개발자로써 Class와 Struct의 정확한 차이를 이해하고 두 가지 구문을 적절하게 활용하여 코드를 최적화 하는 것이 중요하기 때문에, 이에 대해 자세히 알아 볼 필요가 있습니다.</p>
<p>컴퓨터공학부를 전공하면서 이 두 구문의 차이를 생각했을 때, 단순하게 <code>Class는 참조 타입, Struct는 값 타입</code>이다. 라며 C++ 시간에 배웠던 것만 기억이 났습니다.</p>
<p>iOS를 공부하며 구글링을 통해 공부해 본 결과, <code>Class는 ARC를 통해 메모리를 관리하고 메모리의 힙 영역에 저장된다</code> 라는 것과 <code>Struct는 메모리의 스택 영역에 저장된다</code> 와 같은 새로운 사실들도 알게되었죠.</p>
<p>저는 보통 이런 것을 보면 어떤 식으로 이렇게 이루어지는 거지? 하며 원리와 활용 예시들을 보며 이해하는 편이기 때문에, 조금 더 자세하게 찾아보았습니다만 아직도 정확하게 모든 것을 이해하진 못해서 아쉬움이 남습니다.
하지만, 지금의 레벨에서는 모두 완벽하게 아는 것은 어렵다고 생각하기 때문에, 천천히 이론을 이해해 나가고 공부하며 성장해 나가려고 합니다.</p>
<p>그럼 먼저 Class와 Struct의 특징부터 알아보겠습니다.</p>
<h1 id="class">Class</h1>
<h4 id="참조-타입reference-type">참조 타입(Reference Type)</h4>
<ul>
<li>인스턴스는 메모리에서 하나의 객체를 참조하며, 여러 변수나 상수가 같은 객체를 참조할 수 있습니다.</li>
<li>인스턴스가 여러 참조에 의해 공유되므로, 한 참조에서 객체의 상태를 변경하면 다른 모든 참조에서도 그 변경이 반영됩니다.</li>
</ul>
<h4 id="상속inheritance">상속(Inheritance)</h4>
<ul>
<li>상속을 통해 다른 클래스로부터 상속받아 기능을 확장할 수 있습니다.</li>
</ul>
<h4 id="타입-캐스팅type-casting">타입 캐스팅(Type Casting)</h4>
<ul>
<li>인스턴스의 타입을 런타임에 캐스팅할 수 있습니다.</li>
<li>클래스 인스턴스의 타입을 확인하거나 부모 클래스의 인스턴스를 자식 클래스의 타입으로 캐스팅할 수 있습니다. (is, as)</li>
</ul>
<h4 id="소멸자deinit">소멸자(deinit)</h4>
<ul>
<li>deinit을 통해 클래스 인스턴스가 할당한 리소스를 해제할 수 있습니다.</li>
<li>해당 작업을 통해 메모리 할당 해제 시점을 확인할 수 있습니다.</li>
</ul>
<h4 id="메모리-영역memory-area">메모리 영역(Memory Area)</h4>
<ul>
<li>클래스 인스턴스는 일반적으로 힙 메모리에 저장됩니다.</li>
<li>인스턴스를 가리키는 참조는 스택 영역에 저장됩니다.</li>
<li>쉽게 보자면 다음과 같이 저장되는 것이죠.</li>
</ul>
<pre><code class="language-swift">class AClass { }
class BClass { }

var a1 = AClass()
var a2 = a1
var b1 = BClass()</code></pre>
<ul>
<li><img src="https://velog.velcdn.com/images/o_joon_/post/8f3ce655-458d-487d-9586-7038863ff887/image.jpg" alt=""></li>
</ul>
<h4 id="메모리-관리memory-management">메모리 관리(Memory Management)</h4>
<ul>
<li>ARC(Automatic Reference Counting)를 통해 메모리를 관리합니다. <em><strong>(매우 중요)</strong></em></li>
<li>참조 카운트가 0이 되면 메모리에서 객체가 해제됩니다.</li>
</ul>
<h1 id="struct">Struct</h1>
<h4 id="값-타입value-type">값 타입(Value Type)</h4>
<ul>
<li>인스턴스는 값을 직접 저장합니다.</li>
<li>구조체를 다른 변수에 할당하거나 함수를 전달할 때, 값이 복사됩니다.</li>
<li>복사된 구조체의 변경은 원본 구조체에 영향을 미치지 않습니다.</li>
</ul>
<h4 id="메모리-영역memory-area-1">메모리 영역(Memory Area)</h4>
<ul>
<li>구조체 인스턴스는 일반적으로 스택 메모리에 저장됩니다.</li>
<li>클래스의 메모리 영역에 있는 그림과 비교하자면, 힙 영역이 아닌 스택 영역에 있는 변수마다 각각 구조체가 들어있다고 생각하면 됩니다.</li>
</ul>
<br>

<p>Class와 Struct의 대표적인 특징들에 대해 알아봤습니다. 딱 봐도 Class가 Struct에 비해 뭐가 많죠??
그만큼 Class에 대해 설명할 것이 아주 많습니다. 그 것들을 여러 포스팅에 나눠서 설명할 예정이구요.</p>
<p>두 타입의 특징을 나열한 것이 곧 차이점과 같은데요, 그럼 공통점을 알아보겠습니다.</p>
<h1 id="class와-struct의-공통점">Class와 Struct의 공통점</h1>
<h4 id="속성properties">속성(Properties)</h4>
<ul>
<li>속성을 가질 수 있습니다.</li>
<li>이 속성들을 통해 데이터를 저장하고 나타냅니다.</li>
</ul>
<h4 id="메서드methods">메서드(Methods)</h4>
<ul>
<li>메서드를 정의할 수 있습니다.</li>
<li>이 메서드들을 통해 데이터 수정 등을 위한 기능을 제공합니다.</li>
</ul>
<h4 id="생성자initializers">생성자(Initializers)</h4>
<ul>
<li>생성자를 정의하여 초기 상태를 설정합니다.</li>
<li>생성자를 통해 인스턴스를 생성할 수 있습니다.</li>
</ul>
<ul>
<li>두 타입 모두 생성자를 정의하여 초기 설정을 할 수 있지만, 속성의 값이 옵셔널이 아니면서 특정한 값으로 정의되어있지 않은 경우, 클래스에선 생성자를 명시적으로 정의해주어야 합니다.
이유는 이렇습니다.<blockquote>
<p>구조체는 기본 생성자를 제공합니다.
값 타입이고 인스턴스가 메모리의 스택 영역에 저장됩니다.
간단한 데이터 컨테이너로 동작하기 때문에, 초기화 과정이 간단하게 설계되어 있습니다. <br>
클래스는 기본 생성자를 제공하지 않습니다.
참조 타입이고 인스턴스가 메모리의 힙 영역에 저장되고 상속을 지원합니다.
초기화 과정에서 상속받은 클래스의 초기화 과정도 고려해야 합니다.
따라서, 상속 관계에서 부모 클래스의 초기화를 보장해야 하므로 자동 생성자를 제공하기 어렵습니다.</p>
</blockquote>
</li>
</ul>
<h4 id="프로토콜-준수protocol-conformance">프로토콜 준수(Protocol Conformance)</h4>
<ul>
<li>프로토콜을 채택하고 준수할 수 있습니다.</li>
<li>준수한 프로토콜을 통해 특정 기능을 설정할 수 있습니다.</li>
</ul>
<h4 id="확장extension">확장(Extension)</h4>
<ul>
<li>확장을 통해 기능을 확장하여 정의할 수 있습니다.</li>
</ul>
<h4 id="서브스크립트subscripts">서브스크립트(Subscripts)</h4>
<ul>
<li>서브스크립트를 정의하여 인스턴스의 특정 값에 접근할 수 있습니다.</li>
<li>subscript(index: Int) -&gt; Any 이런 식으로 클래스나 구조체 안에 있는 배열에 접근하거나 특정 값을 반환하는 등의 작업을 할 수 있습니다.
하지만, Class와 Struct에 대한 공부와는 조금 거리가 있다고 판단하여 자세히 설명하지는 않겠습니다.
링크만 <a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/subscripts">여기</a> 걸어놓겠습니다.</li>
</ul>
<h1 id="class와-sturct의-사용">Class와 Sturct의 사용</h1>
<p>두 가지 타입의 특정을 비교하며 느낀 점은 이렇습니다. <code>그래서 언제 무엇을 사용해야 하는가?</code>
실제로 네이버 부캠 베이직 과정 중, 같은 기능을 구현하는 데에도 불구하고 팀원 한 분은 구조체를 사용하고 저는 클래스를 사용하여 구현하자는 얘기가 나왔었습니다.
저의 경우는 작은 수의 속성을 가진 데이터 모델은 보통 구조체로 구현하고 그 모델을 활용한 특정 기능을 구현하는 객체는 클래스로 구현해왔었고, 팀원 분은 모두 구조체로 구현해왔던 것 같았습니다.
둘 다 현재 상황에선 뭐가 이렇기 때문에 더 좋다 라는 명확한 설명을 할 수 없었던 터라 저는 구조체로 구현하는 것이 궁금하여 구조체로 구현하자고 했던 경험이 있네요.</p>
<p>공식 문서에서는 다음과 같이 설명하고 있습니다.</p>
<h4 id="기본적으로-구조체를-선택하세요">기본적으로 구조체를 선택하세요.</h4>
<ul>
<li>Swift에서는 구조체를 사용하여 일반적인 종류의 데이터를 표현하는 것이 좋습니다.</li>
<li>구조체는 저장 및 연산 프로퍼티, 메서드를 포함할 수 있으며, 프로토콜을 채택할 수 있습니다.</li>
<li>표준 라이브러리와 Foundation에서도 숫자, 문자열, 배열, 딕셔너리 등의 타입은 구조체를 사용합니다.</li>
<li>구조체는 값 타입이므로, 앱의 전체 상태를 고려하지 않아도 됩니다.</li>
</ul>
<h4 id="objective-c와-상호-운용성이-필요한-경우-클래스를-사용하세요">Objective-C와 상호 운용성이 필요한 경우 클래스를 사용하세요.</h4>
<ul>
<li>Objective-C API를 사용해 데이터를 처리하거나, Objective-C 프레임워크에서 정의된 기존 클래스 계층에 데이터 모델을 맞춰야 한다면 클래스와 상속을 통해 데이터 모델링을 해야 할 수도 있습니다.</li>
</ul>
<h4 id="식별-제어가-필요한-경우-클래스를-사용하세요">식별 제어가 필요한 경우 클래스를 사용하세요.</h4>
<p>뭐가 길게 써있지만, 제대로 이해를 하기 어려워 GPT 교수님을 이용해 쉽게 이해할 수 있도록 계속 질문하며 이해하고 정리하였습니다.</p>
<ul>
<li>클래스 인스턴스는 여러 변수에서 참조할 수 있기 때문에, 객체의 상태가 변경되면 해당 객체를 참조하는 모든 코드에서 변경된 상태를 확인할 수 있습니다.</li>
<li>식별 연산자를 통해 현재 접근하려는 객체가 완전히 동일한지 판단할 수 있고, 이를 통해 여러 군데에서 공유되는 인스턴스의 변경 사항이 예상치 못한 결과를 초래하는 것을 방지할 수 있습니다.</li>
</ul>
<h4 id="구조체와-프로토콜을-사용하여-상속과-동작-공유를-모델링하세요">구조체와 프로토콜을 사용하여 상속과 동작 공유를 모델링하세요.</h4>
<ul>
<li>구조체는 클래스와 달리 상속 기능을 제공하지 않지만, 구조체와 프로토콜 상속을 이용하여 상속을 기능을 구현할 수 있습니다.</li>
<li>클래스 상속을 통해 구축할 수 있는 상속 계층을 프로토콜 상속과 구조체를 사용해 모델링할 수 있습니다.</li>
<li>상속 관계를 처음부터 구축하는 경우, 프로토콜 상속을 우선적으로 사용하세요.
클래스 상속은 클래스 간에만 호환되지만, 프로토콜은 클래스, 구조체, 열거형 모두 상속이 가능하기 때문입니다.</li>
</ul>
<br>

<h3 id="마치며">마치며</h3>
<p>포스팅과 공부를 병행하며 여러 가지 자료를 찾아보고 있는데, 아직까지 실제 개발에서 언제 구조체를 사용하고 언제 클래스를 사용해야 하는 지에 대한 명확한 답을 내기가 어려운 것 같습니다.
현업에 종사하고 계시는 iOS 개발자 분들도 이 상황에 대한 의견이 많이 갈리는 것 같더라구요.
코드에 따라 구조체 안에 클래스가 들어가는 경우, 클래스 안에 구조체가 들어가는 경우 등 많은 복잡한 상황들이 존재하고 이를 memory leak을 최소화 하기 위해 어떻게 변경해야 하는 지 등등..
언젠간 저도 더 많은 것을 깨닫고 이를 실제 현업 종사자분들과 토론하며 새로운 지식을 쌓아가는 날이 있길 희망합니다.</p>
<p>Class와 Struct에 관한 내용 2편은 코드를 포함한 더 자세한 내용들을 다루려고 합니다.
Class에서 ARC가 참조 카운트를 어떻게 하는지, 두 타입이 어떤 상황에서 더 빠른지 등등이요.</p>
<hr>
<h3 id="참조">참조</h3>
<p><a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/classesandstructures">공식 문서 1</a>
<a href="https://developer.apple.com/documentation/swift/choosing-between-structures-and-classes">공식 문서 2</a>
<a href="https://icksw.tistory.com/256">핑구님 블로그</a></p>
]]></description>
        </item>
    </channel>
</rss>