<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://blog.noobth.dev/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog.noobth.dev/" rel="alternate" type="text/html" /><updated>2026-07-19T21:39:47+09:00</updated><id>https://blog.noobth.dev/feed.xml</id><title type="html">SeungYeop Ryu</title><subtitle>Personal blog by SeungYeop Ryu</subtitle><author><name>SeungYeop Ryu</name><email>n006h3417h@gmail.com</email></author><entry><title type="html">C++ map contains vs find</title><link href="https://blog.noobth.dev/2026/07/19/Cpp-Hashmap-contains-find.html" rel="alternate" type="text/html" title="C++ map contains vs find" /><published>2026-07-19T00:00:00+09:00</published><updated>2026-07-19T00:00:00+09:00</updated><id>https://blog.noobth.dev/2026/07/19/Cpp-Hashmap-contains-find</id><content type="html" xml:base="https://blog.noobth.dev/2026/07/19/Cpp-Hashmap-contains-find.html"><![CDATA[<p>C++의 표준 라이브러리인 <code class="language-plaintext highlighter-rouge">std::map</code>, <code class="language-plaintext highlighter-rouge">std::unordered_map</code>에서 <code class="language-plaintext highlighter-rouge">contains()</code>와 <code class="language-plaintext highlighter-rouge">find()</code>의 차이를 정리했다.</p>

<h2 id="1-map--unordered_map">1. map &amp; unordered_map</h2>

<p><code class="language-plaintext highlighter-rouge">std::map</code>과 <code class="language-plaintext highlighter-rouge">std::unordered_map</code>은 모두 Key-Value 형태의 연관 컨테이너지만 내부 구현이 다르다.</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">std::map</code>은 일반적으로 Red-Black Tree로 구현되며, 키가 항상 정렬된 상태를 유지한다.</li>
  <li><code class="language-plaintext highlighter-rouge">std::unordered_map</code>은 Hash Table로 구현되며, 키의 순서를 보장하지 않는다.</li>
</ul>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">std</span><span class="o">::</span><span class="n">map</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">contacts</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">unordered_map</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="p">,</span> <span class="n">Point</span><span class="o">&gt;</span> <span class="n">users</span><span class="p">;</span>
</code></pre></div></div>

<h2 id="2-key를-조회하는-방법">2. Key를 조회하는 방법</h2>

<h3 id="21-operator">2.1. operator[]</h3>

<p>가장 단순한 방법이다.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">auto</span> <span class="n">phone</span> <span class="o">=</span> <span class="n">contacts</span><span class="p">[</span><span class="s">"SeungYeop"</span><span class="p">];</span>
</code></pre></div></div>
<p>이 방법은 해당 키가 존재하지 않으면 새로운 키를 저장한다.<br />
따라서 단순히 존재 여부만 확인하려는 경우에는 불필요한 삽입과 생성이 발생할 수 있으므로 주의해야 한다.</p>

<h3 id="22-find-함수">2.2. find() 함수</h3>

<p>C++20 이전에는 키의 존재 여부를 확인할 때 가장 많이 사용하던 방법이다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">contacts</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="s">"SeungYeop"</span><span class="p">);</span>

<span class="k">if</span> <span class="p">(</span><span class="n">it</span> <span class="o">!=</span> <span class="n">contacts</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="p">{</span>
    <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"Found</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"Not Found</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">find()</code>는 키를 찾으면 해당 원소를 가리키는 iterator를 반환하고, 찾지 못하면 <code class="language-plaintext highlighter-rouge">end()</code>를 반환한다.</p>

<p>값을 바로 사용할 예정이라면 <code class="language-plaintext highlighter-rouge">find()</code>가 가장 효율적인 선택이다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">contacts</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="s">"SeungYeop"</span><span class="p">);</span> <span class="n">it</span> <span class="o">!=</span> <span class="n">contacts</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="p">{</span>
    <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">it</span><span class="o">-&gt;</span><span class="n">second</span> <span class="o">&lt;&lt;</span> <span class="sc">'\n'</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>탐색을 한 번만 수행하기 때문이다.</p>

<h3 id="23-contains-함수">2.3. contains() 함수</h3>

<p>C++20에 추가된 함수다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="n">contacts</span><span class="p">.</span><span class="n">contains</span><span class="p">(</span><span class="s">"SeungYeop"</span><span class="p">))</span> <span class="p">{</span>
    <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"Found</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"Not Found</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>반환값이 <code class="language-plaintext highlighter-rouge">bool</code>이라 존재 여부만 확인할 때는 <code class="language-plaintext highlighter-rouge">find()</code>보다 코드가 훨씬 간결하다.</p>

<p>하지만 값을 바로 사용할 예정이라면 주의해야 한다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="n">contacts</span><span class="p">.</span><span class="n">contains</span><span class="p">(</span><span class="s">"SeungYeop"</span><span class="p">))</span> <span class="p">{</span>
    <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">contacts</span><span class="p">[</span><span class="s">"SeungYeop"</span><span class="p">]</span> <span class="o">&lt;&lt;</span> <span class="sc">'\n'</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>위 코드는 <code class="language-plaintext highlighter-rouge">contains()</code>에서 한 번 탐색하고, <code class="language-plaintext highlighter-rouge">operator[]</code>에서 다시 한 번 탐색한다.<br />
즉, 같은 키를 두 번 조회하게 된다.</p>

<h2 id="-여담">%. 여담</h2>
<p>사소한 내용이지만, 가장 좋아하는 언어인 C++에 대해 조금씩이라도 기록을 남기고 싶어 정리했다.<br />
최근에는 Spring과 Rust를 주로 공부하다 보니 C++를 다룰 시간이 많이 줄었는데, 가끔은 이렇게 기본 라이브러리나 언어 자체를 다시 살펴보는 것도 재미있는 것 같다.</p>]]></content><author><name>SeungYeop Ryu</name><email>n006h3417h@gmail.com</email></author><category term="cpp" /><summary type="html"><![CDATA[C++의 표준 라이브러리인 std::map, std::unordered_map에서 contains()와 find()의 차이를 정리했다.]]></summary></entry><entry><title type="html">C++ vector push_back() vs emplace_back()</title><link href="https://blog.noobth.dev/2026/07/19/Cpp-Vector-push_back-emplace_back.html" rel="alternate" type="text/html" title="C++ vector push_back() vs emplace_back()" /><published>2026-07-19T00:00:00+09:00</published><updated>2026-07-19T00:00:00+09:00</updated><id>https://blog.noobth.dev/2026/07/19/Cpp-Vector-push_back-emplace_back</id><content type="html" xml:base="https://blog.noobth.dev/2026/07/19/Cpp-Vector-push_back-emplace_back.html"><![CDATA[<p>옛날에 본 영상 하나가 있었는데, <code class="language-plaintext highlighter-rouge">emplace_back()</code>을 무작정 옹호하는 내용이였다.<br />
어떻게 보면 대부분의 경우 <code class="language-plaintext highlighter-rouge">push_back()</code>이 더 의도가 명확하고, <code class="language-plaintext highlighter-rouge">emplace_back()</code>이 반드시 더 좋은 것은 아니다.</p>

<h2 id="1-이미-객체가-있는-경우">1. 이미 객체가 있는 경우</h2>

<p>이미 객체가 있는 경우, <code class="language-plaintext highlighter-rouge">push_back()</code>이 더 알맞은 선택이다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">v</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">s</span> <span class="o">=</span> <span class="s">"hello"</span><span class="p">;</span>

<span class="n">v</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">s</span><span class="p">);</span>
<span class="n">v</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">s</span><span class="p">));</span>
</code></pre></div></div>

<p>이때 <code class="language-plaintext highlighter-rouge">push_back()</code>이 아닌, <code class="language-plaintext highlighter-rouge">emplace_back()</code>을 쓰더라도 결과는 거의 동일하다. <code class="language-plaintext highlighter-rouge">emplace_back()</code>은 전달받은 인자를 그대로 생성자의 인자로 넘겨줄 뿐이므로, 내부적으로 <code class="language-plaintext highlighter-rouge">std::string(s);</code>를 호출하는 것과 같다.</p>

<p>즉, 이미 객체가 있다면 <code class="language-plaintext highlighter-rouge">push_back()</code>이 의미를 더 잘 드러낸다.</p>

<h2 id="2-생성자를-직접-호출하는-경우">2. 생성자를 직접 호출하는 경우</h2>

<p>이때는 <code class="language-plaintext highlighter-rouge">emplace_back()</code>이 분명히 나은 선택일 것이다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">pair</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&gt;</span> <span class="n">v</span><span class="p">;</span>
<span class="n">v</span><span class="p">.</span><span class="n">emplace_back</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"hello"</span><span class="p">);</span>
</code></pre></div></div>

<p>두 값을 넣는다는 의미를 더 잘 드러낸다.</p>

<p>만약 <code class="language-plaintext highlighter-rouge">push_back()</code>이라면</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">v</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">pair</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s">"hello"</span><span class="p">));</span>
</code></pre></div></div>
<p>처럼 임시 객체를 만들어 넣어야 한다.</p>

<h2 id="3-overload-문제">3. overload 문제</h2>

<p><code class="language-plaintext highlighter-rouge">emplace_back()</code>은 <strong>perfect forwarding</strong>이라는 템플릿 기법을 사용하기 때문에 생각보다 이상한 생성자가 선택될 수도 있다.</p>

<p>예를 들면, 아래와 같은 코드가 있다.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&gt;</span> <span class="n">v</span><span class="p">;</span>
<span class="n">v</span><span class="p">.</span><span class="n">emplace_back</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">20</span><span class="p">);</span>
</code></pre></div></div>
<p>우리는 <code class="language-plaintext highlighter-rouge">std::vector&lt;int, int&gt;{10, 20}</code>을 기대했겠지만, 내부적으론 <code class="language-plaintext highlighter-rouge">std::vector&lt;int&gt;(10, 20)</code>이 생성된다.</p>

<p>여기서 <code class="language-plaintext highlighter-rouge">push_back()</code>을 사용한다면</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>v.push_back({10, 20});
</code></pre></div></div>
<p>우리가 원하는 결과가 들어간다.</p>

<p>이처럼 <code class="language-plaintext highlighter-rouge">emplace_back()</code>은 생성자 오버로드 때문에 의도와 다른 동작을 만들기 쉽다.</p>

<h2 id="4-성능-차이는-대부분-없다">4. 성능 차이는 대부분 없다</h2>

<p>많은 사람들이 <code class="language-plaintext highlighter-rouge">emplace_back()</code>이 항상 더 빠르다고 생각하지만 대부분 틀린 이야기다.</p>

<p>예를 들면</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">s</span> <span class="o">=</span> <span class="s">"abc"</span><span class="p">;</span>
<span class="n">v</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">s</span><span class="p">));</span>
<span class="n">v</span><span class="p">.</span><span class="n">emplace_back</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">s</span><span class="p">));</span>
</code></pre></div></div>

<p>두 가지 방법 모두 거의 동일한 코드가 생성된다.</p>

<p>또한 아래처럼 임시 객체를 넣는 경우에도</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">v</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">A</span><span class="p">{</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">});</span>
</code></pre></div></div>
<p>C++17 이후에는 복사 생략과 이동 최적화 덕분에 <code class="language-plaintext highlighter-rouge">emplace_back()</code>과 차이가 없는 경우가 많다.</p>

<p>실제로 성능 차이가 나는 대표적인 경우는</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">v</span><span class="p">.</span><span class="n">emplace_back</span><span class="p">(</span><span class="n">arg1</span><span class="p">,</span> <span class="n">arg2</span><span class="p">,</span> <span class="n">arg3</span><span class="p">);</span>
</code></pre></div></div>

<p>처럼 임시 객체 자체를 만들지 않고, 컨테이너 내부에서 직접 생성할 때다.</p>

<h2 id="-결론">%. 결론</h2>

<p>쉽게 말해 이미 객체가 있으면 <code class="language-plaintext highlighter-rouge">push_back()</code>, 객체를 인라인으로 생성한다면 <code class="language-plaintext highlighter-rouge">emplace_back()</code>을 쓰면 된다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">s</span> <span class="o">=</span> <span class="s">"hello"</span><span class="p">;</span>

<span class="n">v</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">s</span><span class="p">);</span>
<span class="n">v</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">s</span><span class="p">));</span>

<span class="n">v</span><span class="p">.</span><span class="n">emplace_back</span><span class="p">(</span><span class="s">"hello"</span><span class="p">);</span>
<span class="n">v</span><span class="p">.</span><span class="n">emplace_back</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="sc">'a'</span><span class="p">);</span>

<span class="n">v</span><span class="p">.</span><span class="n">emplace_back</span><span class="p">(</span><span class="n">s</span><span class="p">);</span> <span class="c1">// 이것도 가능하지만</span>
<span class="n">v</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">s</span><span class="p">);</span>    <span class="c1">// 이게 읽기가 더 좋다</span>
</code></pre></div></div>

<p>즉, <code class="language-plaintext highlighter-rouge">emplace_back()</code>은 <code class="language-plaintext highlighter-rouge">push_back()</code>의 상위호환이라기보다, 생성자 인자를 직접 전달해 컨테이너 안에서 객체를 생성하고 싶을 때 쓰는 함수다.</p>]]></content><author><name>SeungYeop Ryu</name><email>n006h3417h@gmail.com</email></author><category term="cpp" /><summary type="html"><![CDATA[대부분의 경우 push_back()이 더 의도가 명확하고, emplace_back()이 반드시 더 좋은 것은 아니다. 그 이유를 알아보자.]]></summary></entry><entry><title type="html">The Practice of Programming</title><link href="https://blog.noobth.dev/2026/07/18/The-Practice-of-Programming.html" rel="alternate" type="text/html" title="The Practice of Programming" /><published>2026-07-18T00:00:00+09:00</published><updated>2026-07-18T00:00:00+09:00</updated><id>https://blog.noobth.dev/2026/07/18/The-Practice-of-Programming</id><content type="html" xml:base="https://blog.noobth.dev/2026/07/18/The-Practice-of-Programming.html"><![CDATA[<p><strong>Helping make individual programmers more effective and productive, this book contains practical advice and real-world examples in C, C++, Java, and a variety of special-purpose languages.</strong></p>

<p>프로그래머를 더욱 효과적이고 생산적으로 만들어 주는 실용적인 프로그래밍 지침서라고 한다.</p>

<p>학기말과 방학을 빌려서 몇년은 미룬 책을 드디어 읽었다. 분량이 많기 때문에 핵심적인 내용만 추려 정리했다.</p>

<h2 id="1-style">1. Style</h2>

<p>코드를 작성할 때, 컴퓨터가 아닌 사람이 읽기 쉽게 작성해야 한다.</p>

<ul>
  <li>의미 있는 이름을 사용한다.</li>
  <li>함수는 짧게 만든다.</li>
  <li>함수는 한 가지 일만 한다.</li>
  <li>불필요한 트릭을 쓰지 않는다.</li>
  <li>일관성을 유지한다.</li>
</ul>

<p>예를 들어 배열의 평균을 구하는 함수를 작성한다면</p>

<p>나쁜 예:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">double</span> <span class="nf">f</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">a</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">double</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">a</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">x</span> <span class="o">+=</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">a</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">?</span> <span class="n">x</span> <span class="o">/</span> <span class="n">a</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">:</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>좋은 예:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">double</span> <span class="nf">calculate_average</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&amp;</span> <span class="n">numbers</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">numbers</span><span class="p">.</span><span class="n">empty</span><span class="p">())</span> <span class="p">{</span>
        <span class="k">return</span> <span class="mf">0.0</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="kt">int</span> <span class="n">sum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">number</span> <span class="o">:</span> <span class="n">numbers</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">sum</span> <span class="o">+=</span> <span class="n">number</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">double</span><span class="o">&gt;</span><span class="p">(</span><span class="n">sum</span><span class="p">)</span> <span class="o">/</span> <span class="n">numbers</span><span class="p">.</span><span class="n">size</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<p>누구나 알고는 있지만, 잘 지켜지지 않는 당연한 규칙이다.</p>

<h2 id="2-data-structures--algorithms">2. Data Structures &amp; Algorithms</h2>

<p>컴퓨터는 근본적으로 데이터 처리 장치다. 떄문에 문제를 해결하기 위한 알고리즘을 미세 최적화하는 것보다, 데이터를 다루는 자료구조를 문제에 맞게 선택하는 것이 더 중요하다.</p>

<p>회원이 차단 목록에 있는지 조회하는 함수를 <code class="language-plaintext highlighter-rouge">vector</code> 조회 방식으로 만들었다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">bool</span> <span class="nf">IsBlacklisted</span><span class="p">(</span>
    <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">blacklist</span><span class="p">,</span>
    <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">user</span>
<span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">std</span><span class="o">::</span><span class="n">find</span><span class="p">(</span>
        <span class="n">blacklist</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span>
        <span class="n">blacklist</span><span class="p">.</span><span class="n">end</span><span class="p">(),</span>
        <span class="n">user</span>
    <span class="p">)</span> <span class="o">!=</span> <span class="n">blacklist</span><span class="p">.</span><span class="n">end</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<p>이걸 <code class="language-plaintext highlighter-rouge">unordered_set</code> 기반으로 바꾸면</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">bool</span> <span class="nf">IsBlacklisted</span><span class="p">(</span>
    <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">unordered_set</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;&amp;</span> <span class="n">blacklist</span><span class="p">,</span>
    <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&amp;</span> <span class="n">user</span>
<span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">blacklist</span><span class="p">.</span><span class="n">contains</span><span class="p">(</span><span class="n">user</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>이렇게나 간결해진다.</p>

<h2 id="3-design">3. Design</h2>

<p>처음부터 완벽한 설계는 불필요하다. 일단 간단하게 만들고, 사용해보고, 고치는 과정을 반복하는 <strong>점진적 개선(iterative refinement)</strong>이 중요하다.</p>

<h3 id="31-처음-설계는-거의-틀린다">3.1. 처음 설계는 거의 틀린다</h3>

<p>우리는 처음부터 문제를 완벽하게 이해할 수가 없다. 처음에는 요구사항도, 문제의 본질도 완전히 알 수 없다.</p>

<p>예를 들어 학생 관리 프로그램을 만들어본다고 하면 처음에는 아래와 같이 생각할 수 있다.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Student
 ㄴ name
 ㄴ age
 ㄴ grade
</code></pre></div></div>

<p>처음에는 충분하다고 생각할 수도 있는데, 개발하다 보면 복수 전공, 휴학생, 학점 계산 등 다양한 로직의 필요성이 눈에 보일 것이다.</p>

<h3 id="32-작은-변경보다-큰-변경-비용이-더-비싸다">3.2. 작은 변경보다 큰 변경 비용이 더 비싸다</h3>

<p>처음부터 모든 경우를 고려하려고 하다 보면 아래와 같이 코드가 복잡해진다.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if (mode == A) ...
else if (mode == B) ...
else if (mode == C) ...
else if (mode == D) ...
// E, F, G, ......
</code></pre></div></div>

<p>전부 다 고려했지만, 실제로는 A와 B만 필요한 경우가 많다. 필요해질 때 추가하면 코드도 단순하고, 잘못된 가정을 할 가능성도 줄어드는데, 이를 흔히 <strong>YAGNI(You Aren’t Gonna Need It)</strong> 원칙이라고도 한다.</p>

<h3 id="33-피드백이-최고의-설계-방법이다">3.3. 피드백이 최고의 설계 방법이다</h3>

<p>설계할 때는 몰랐지만, 코드를 직접 써 보면서 알게 되는 것들이 있다.</p>

<p>예를 들어 아래와 같이 API를 설계했다고 하자.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">db</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">user</span><span class="p">);</span>
</code></pre></div></div>

<p>처음에는 좋아 보였지만, 실제로 써 보니</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">db</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">user</span><span class="p">);</span>
<span class="n">db</span><span class="p">.</span><span class="n">commit</span><span class="p">();</span>
</code></pre></div></div>

<p>을 매번 호출해야 해서 불편하다.</p>

<p>그러면</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>db.insert(user);
</code></pre></div></div>
<p>호출 한번으로 자동 커밋하도록 바꿀 수도 있다.</p>

<h3 id="34-리팩토링은-실패가-아니라-과정이다">3.4. 리팩토링은 실패가 아니라 과정이다</h3>

<p>코딩을 처음 접하면 처음부터 예쁘게 해야 한다는 강박이 있는 경우가 있다. 하지만 경험이 많은 개발자라면 일단 돌아가게 만들고, 이해가 생긴 후 구조를 개선한다는 접근을 한다.</p>

<p>최댓값을 찾는 함수를 일단 돌아가게, 단순무식하게 구현한다면 아래와 같이 반복+분기로 작성할 수 있다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">find_max</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&amp;</span> <span class="n">numbers</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">m</span> <span class="o">=</span> <span class="n">numbers</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">numbers</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">numbers</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">m</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">m</span> <span class="o">=</span> <span class="n">numbers</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">m</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>처음에는 동작하는 것이 목표였기 때문에 특별한 추상화는 없다.<br />
여기서 최댓값을 찾는다는 의도가 STL에서 제공하는 알고리즘 함수와 정확히 일치한다는 것을 알 수 있다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">find_max</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&amp;</span> <span class="n">numbers</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="o">*</span><span class="n">std</span><span class="o">::</span><span class="n">max_element</span><span class="p">(</span><span class="n">numbers</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">numbers</span><span class="p">.</span><span class="n">end</span><span class="p">());</span>
<span class="p">}</span>
</code></pre></div></div>

<p>즉, 이렇게 리팩토링이 가능하다.</p>

<h3 id="35-소프트웨어는-계속-변한다">3.5. 소프트웨어는 계속 변한다</h3>

<p>현실에서는 요구사항이 계속 바뀐다. 새로운 기능 추가, 버그 수정, 성능 개선, 사용자 피드백 반영 등. 한번에 완벽한 설계를 만들려고 하기보다, 변화하기 쉬운 구조를 유지하는 것이 더 중요하다.</p>

<h2 id="4-interfaces">4. Interfaces</h2>

<p>구현보다 중요한 건 인터페이스다. 좋은 API는 사용하기 쉽고, 실수하기는 어렵고, 내부 구현을 숨긴다.</p>

<p>예를 들어 정수 벡터에 새로운 값을 집어넣는 코드를 보자.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">std</span><span class="o">::</span><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">vec</span><span class="p">;</span>
<span class="n">vec</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span>
</code></pre></div></div>
<p>이런 코드를 사용할 때 우리는 벡터가 메모리를 언제 재할당하는지, 용량(capacity)을 어떻게 늘리는지, 기존 원소를 어떻게 이동하는지 알 필요가 없다. <code class="language-plaintext highlighter-rouge">push_back</code>이라는 인터페이스가 이런 구현 세부사항을 모두 감춰 주기 때문이다.</p>

<p>만약 추상화가 없었다면 <code class="language-plaintext highlighter-rouge">push_back</code>의 인터페이스(시그니처)는 아마 이렇게 끔찍했을 것이다.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">push_back</span><span class="p">(</span>
    <span class="kt">int</span><span class="o">*&amp;</span> <span class="n">data</span><span class="p">,</span>
    <span class="n">std</span><span class="o">::</span><span class="kt">size_t</span><span class="o">&amp;</span> <span class="n">size</span><span class="p">,</span>
    <span class="n">std</span><span class="o">::</span><span class="kt">size_t</span><span class="o">&amp;</span> <span class="n">capacity</span><span class="p">,</span>
    <span class="kt">int</span> <span class="n">value</span>
<span class="p">);</span>
</code></pre></div></div>

<p>사용자는 값을 추가하기 위해 벡터의 내부 상태(data, size, capacity)까지 직접 관리해야 한다.<br />
좋은 인터페이스는 사용자의 부담을 줄이고, 실수할 여지를 없애며, 구현을 바꿔도 사용 코드는 그대로 둘 수 있게 만든다.</p>

<h2 id="5-debugging">5. Debugging</h2>

<p>아마 대부분의 초보 개발자는 아래와 같은 방법을 사용할 것이다.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>코드 수정 -&gt; 실행 -&gt; 기도
</code></pre></div></div>
<p>가끔은 이런 식의 삽질이 문제를 해결하기도 하지만, 개인적으론 영 별로다.</p>

<p>버그는 다음 순서로 접근하는 것이 훨씬 효과적이다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">재현</span> <span class="o">-&gt;</span> <span class="err">가설</span> <span class="o">-&gt;</span> <span class="err">실험</span> <span class="o">-&gt;</span> <span class="err">원인</span> <span class="err">확인</span>
</code></pre></div></div>

<ul>
  <li><strong>재현:</strong> 언제, 어떤 조건에서 버그가 발생하는지 확인한다.</li>
  <li><strong>가설:</strong> 원인이 될 만한 부분을 추측한다.</li>
  <li><strong>실험:</strong> 최소한의 변경으로 검증한다.</li>
  <li><strong>원인 확인:</strong> 실제 원인을 확인한 후 수정한다.</li>
</ul>

<p>내 경험상 가장 어려운 단계는 버그를 안정적으로 재현하는 것이다. 재현이 되지 않으면 가설도 세우기 어렵고, 실험 결과 역시 신뢰하기 힘들다. 반대로 재현만 확실하다면 원인을 찾는 과정은 훨씬 수월해진다.</p>

<h2 id="6-testing">6. Testing</h2>

<p>테스트는 버그픽스가 아닌, 신뢰를 위한 것이다.<br />
엣지케이스나 NULL, 엄청 큰 입력, 예외 상황 등을 테스트하고 검증해야 한다.</p>

<p>예를 들어 <code class="language-plaintext highlighter-rouge">max()</code> 함수를 구현했다면 다음과 같은 테스트를 작성할 수 있다.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">EXPECT_EQ</span><span class="p">(</span><span class="n">max</span><span class="p">({</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">}),</span> <span class="mi">3</span><span class="p">);</span>
<span class="n">EXPECT_EQ</span><span class="p">(</span><span class="n">max</span><span class="p">({</span><span class="mi">5</span><span class="p">}),</span> <span class="mi">5</span><span class="p">);</span>
<span class="n">EXPECT_EQ</span><span class="p">(</span><span class="n">max</span><span class="p">({</span><span class="o">-</span><span class="mi">3</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">5</span><span class="p">}),</span> <span class="o">-</span><span class="mi">1</span><span class="p">);</span>
<span class="n">EXPECT_EQ</span><span class="p">(</span><span class="n">max</span><span class="p">({</span><span class="n">INT_MIN</span><span class="p">,</span> <span class="n">INT_MAX</span><span class="p">}),</span> <span class="n">INT_MAX</span><span class="p">);</span>
<span class="n">EXPECT_THROW</span><span class="p">(</span><span class="n">max</span><span class="p">({}),</span> <span class="n">std</span><span class="o">::</span><span class="n">invalid_argument</span><span class="p">);</span>
</code></pre></div></div>

<p>정상적인 경우뿐 아니라, 실패하거나 경계에 있는 경우까지 검증해야 코드에 대한 신뢰도가 올라간다.</p>

<h2 id="7-performance">7. Performance</h2>

<p>측정하기 전에는 최적화하면 안 된다. 많은 사람들이 병목이 있다고 추측하는 부분은 실제 병목과 다른 경우가 많다. 직감에 의존한 최적화는 시간만 낭비하고, 오히려 코드를 더 복잡하게 만들기도 한다.</p>

<p>최적화의 순서는 아래와 같다.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>프로그램 작성 -&gt; 측정 -&gt; 병목 발견 -&gt; 그 부분만 최적화
</code></pre></div></div>

<p>성능이 느리다는 이유만으로 캐시를 추가하거나, 큐를 도입하거나, 복잡한 자료구조로 바꾸는 경우가 있다. 하지만 프로파일링을 해 보면 실제 병목은 전혀 다른 함수나 I/O, 데이터베이스, 네트워크 통신인 경우도 흔하다.</p>

<p>최적화는 필요한 곳에만, 측정 결과를 근거로 수행해야 한다.</p>

<h2 id="8-portability">8. Portability</h2>

<p>운영체제나 컴파일러에 의존하면 안 된다. 또한, 단일 플랫폼에서만 돌아가는 코드를 작성하거나 특정 컴파일러만 지원하는 문법은 최소화해야 한다.</p>

<p>가장 대표적으로 타입의 크기 계산 문제다.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">x</span><span class="p">;</span>
<span class="k">static_assert</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span> <span class="o">==</span> <span class="mi">4</span><span class="p">);</span>
</code></pre></div></div>

<p>다음은 WinAPI에 의존하는 경우다.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Sleep</span><span class="p">(</span><span class="mi">1000</span><span class="p">);</span>
</code></pre></div></div>

<p>마지막으로 컴파일러 확장인데, 대부분이 가장 처음 접했던 <code class="language-plaintext highlighter-rouge">scanf_s()</code> 정도가 있다.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>scanf_s("%d", &amp;x);
</code></pre></div></div>

<p>가능한 아래처럼 표준 라이브러리 기능을 우선 사용하는 것이 좋다.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">std</span><span class="o">::</span><span class="kt">int32_t</span> <span class="n">value</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">this_thread</span><span class="o">::</span><span class="n">sleep_for</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">chrono</span><span class="o">::</span><span class="n">seconds</span><span class="p">(</span><span class="mi">1</span><span class="p">));</span>
<span class="n">std</span><span class="o">::</span><span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">value</span><span class="p">;</span>
</code></pre></div></div>
<p>플랫폼에 종속된 코드는 꼭 필요한 경우에만 사용하는 것이 바람직하다.</p>

<h2 id="9-notation">9. Notation</h2>

<p>복잡한 문제는 곧바로 코드로 옮기려 하지 말고, 먼저 좋은 표기법을 만드는 것부터 시작하면 쉬워진다.</p>

<p>예를 들어 좌표를 x1, x2, x3, y1, y2, y3처럼 흩어져 표현하는 것보다</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="nc">Point</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">x</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">y</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>

<p>처럼 의미를 담은 자료형으로 표현하면 코드의 의도가 훨씬 명확해진다.</p>

<h2 id="-총평">%. 총평</h2>

<p>AI의 영향으로 이런 내용이 요즘 개발 트렌드와는 다소 거리가 있어 보일 수도 있다. 하지만 원칙 자체는 여전히 유효하고, 한 번쯤은 차분히 읽어볼 가치가 있는 책이라고 생각한다.</p>

<p>특히 아직 프로그래밍에 익숙하지 않은 사람이라면 흔히 저지르는 실수를 돌아보고, 좋은 개발 습관을 익히는 데 큰 도움이 될 것 같다.</p>]]></content><author><name>SeungYeop Ryu</name><email>n006h3417h@gmail.com</email></author><category term="book" /><summary type="html"><![CDATA[프로그래머를 더욱 효과적이고 생산적으로 만들어 주는 실용적인 프로그래밍 지침서라고 한다.]]></summary></entry></feed>