<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Legacy Code on kenji.blog</title><link>http://kenji.blog/ko/tags/legacy-code/</link><description>Recent content in Legacy Code on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>ko</language><copyright>kenjinote</copyright><lastBuildDate>Sat, 12 Sep 2026 20:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/ko/tags/legacy-code/index.xml" rel="self" type="application/rss+xml"/><item><title>리팩터링의 비결: 레거시 C++ 코드를 안전하게 개선하기</title><link>http://kenji.blog/ko/p/refactoring-legacy-cpp-code-safely/</link><pubDate>Sat, 12 Sep 2026 20:00:00 +0900</pubDate><guid>http://kenji.blog/ko/p/refactoring-legacy-cpp-code-safely/</guid><description>&lt;img src="http://kenji.blog/p/refactoring-legacy-cpp-code-safely/img/eyecatch.jpg" alt="Featured image of post 리팩터링의 비결: 레거시 C++ 코드를 안전하게 개선하기" />&lt;h1 id="리팩터링의-비결-레거시-c-코드를-안전하게-개선하기">리팩터링의 비결: 레거시 C++ 코드를 안전하게 개선하기
&lt;/h1>&lt;p>현대의 소프트웨어 개발에서 &amp;lsquo;레거시 코드&amp;rsquo;와의 싸움은 피할 수 없는 길입니다. 특히 C++라는 언어에서 레거시 코드는 다른 언어의 그것과는 비교할 수 없을 정도의 위협을 가집니다. 수동 메모리 관리(원시 포인터와 &lt;code>new&lt;/code> / &lt;code>delete&lt;/code>의 폭풍), 전역 변수의 남용, 예외 안전성의 결여, 그리고 무엇보다 &amp;lsquo;테스트가 없다&amp;rsquo;는 사실. 마이클 페더스는 그의 명저 『레거시 코드 활용 전략(Working Effectively with Legacy Code)』에서 &amp;ldquo;테스트가 없는 코드는 레거시 코드이다&amp;quot;라고 단언했습니다.&lt;/p>
&lt;p>이 글에서는 수십 년에 걸쳐 축적된 레거시 C++ 코드베이스를 안전하고 확실하게 Modern C++ (C++11/14/17/20)로 마이그레이션하고 리팩터링하기 위한 비결을 이론과 실제 양면에서 철저하게 해설합니다. 기술 부채의 수학적 모델에서 시작하여 안전한 의존성 분리, 그리고 모던한 언어 기능을 사용한 코드 정화까지 실용적인 접근 방식을 망라합니다.&lt;/p>
&lt;hr>
&lt;h2 id="1-복잡도와-기술-부채의-수학적-모델">1. 복잡도와 기술 부채의 수학적 모델
&lt;/h2>&lt;p>리팩터링을 정당화하려면 현재 코드베이스가 안고 있는 문제를 정량화해야 합니다. 코드의 구조적인 복잡성을 측정하는 지표로서 가장 일반적인 것이 &amp;lsquo;순환 복잡도(Cyclomatic Complexity)&amp;lsquo;입니다. 이 복잡도는 제어 흐름 그래프의 그래프 이론에 기초하여 다음 수식으로 정의됩니다.&lt;/p>
$$ M = E - N + 2P $$&lt;p>여기서,&lt;/p>
&lt;ul>
&lt;li>$M$ 은 순환 복잡도&lt;/li>
&lt;li>$E$ 는 그래프의 엣지(처리 흐름, 전이)의 수&lt;/li>
&lt;li>$N$ 은 그래프의 노드(처리의 기본 블록)의 수&lt;/li>
&lt;li>$P$ 는 연결 요소의 수(보통 단일 함수나 메서드에서는 $P=1$)&lt;/li>
&lt;/ul>
&lt;p>복잡도 $M$ 이 커질수록 해당 함수를 포괄적으로 테스트하는 데 필요한 테스트 케이스의 수는 선형적으로, 혹은 조건 분기의 조합에 따라 지수 함수적으로 증가합니다. 또한 버그 발생 확률 $P(bug)$ 는 복잡도 $M$ 에 대해 지수 함수적으로 증가한다는 경험 법칙이 있습니다. 이를 포아송 분포와 비슷한 형태로 모델화하면 다음과 같습니다.&lt;/p>
$$ P(bug) = 1 - e^{-\lambda \cdot M} $$&lt;p>(여기서 $\lambda$ 는 개발 팀의 기술이나 도메인의 난이도에 의존하는 상수입니다.)&lt;/p>
&lt;p>또한 기술 부채의 비용은 복리로 증대됩니다. 초기의 기술 부채를 $C_0$, 반복(Iteration)별 이자율(코드의 변경 어려움으로 인한 생산성 저하 비율)을 $r$ 이라고 했을 때, $t$ 기간 후의 개수(수정) 비용 $Cost(t)$ 는 다음과 같이 표현할 수 있습니다.&lt;/p>
$$ Cost(t) = C_0 \times (1 + r)^t $$&lt;p>이 수식이 명확하게 보여주는 것은 &amp;ldquo;레거시 코드의 방치는 시간 경과와 함께 지수 함수적인 비용 증대를 초래한다&amp;quot;는 잔혹한 사실입니다. 따라서 부채는 조기에 상환(리팩터링)할 필요가 있는 것입니다.&lt;/p>
&lt;hr>
&lt;h2 id="2-리팩터링의-절대-원칙-테스트-퍼스트">2. 리팩터링의 절대 원칙: &amp;lsquo;테스트 퍼스트&amp;rsquo;
&lt;/h2>&lt;p>레거시 코드를 변경할 때 가장 두려운 점은 &amp;lsquo;기존의 정상적인 동작을 망가뜨리는(회귀 버그를 일으키는) 것은 아닐까&amp;rsquo; 하는 점에 있습니다. 이 두려움을 불식시킬 유일한 방법이 &amp;lsquo;자동화된 테스트&amp;rsquo;입니다.&lt;/p>
&lt;p>그러나 레거시 코드에는 애초에 테스트가 없습니다. 그래서 중요해지는 것이 &amp;lsquo;특성화 테스트(Characterization Test)&amp;lsquo;의 도입입니다. 특성화 테스트란, 시스템이 &amp;lsquo;본래 어떻게 동작해야 하는가&amp;rsquo;가 아니라, &amp;lsquo;현재 어떻게 동작하고 있는가&amp;rsquo;를 있는 그대로 기록하는 테스트를 말합니다.&lt;/p>
&lt;p>다음 순서도는 안전한 리팩터링의 수명 주기를 보여줍니다.&lt;/p>
&lt;pre class="mermaid">
flowchart TD
A[&amp;#34;대상 레거시 코드를 식별한다&amp;#34;] --&amp;gt; B[&amp;#34;특성화 테스트를 작성한다&amp;#34;]
B --&amp;gt; C[&amp;#34;테스트가 모두 통과하는지 확인&amp;#34;]
C --&amp;gt; D[&amp;#34;작은 리팩터링을 실시&amp;#34;]
D --&amp;gt; E[&amp;#34;테스트 재실행&amp;#34;]
E -- &amp;#34;실패 (Red)&amp;#34; --&amp;gt; F[&amp;#34;변경 사항을 되돌림 (Revert)&amp;#34;]
F --&amp;gt; D
E -- &amp;#34;성공 (Green)&amp;#34; --&amp;gt; G[&amp;#34;코드를 커밋한다&amp;#34;]
G --&amp;gt; H{&amp;#34;다음 개선 사항이 있는가?&amp;#34;}
H -- &amp;#34;Yes&amp;#34; --&amp;gt; D
H -- &amp;#34;No&amp;#34; --&amp;gt; I[&amp;#34;리팩터링 완료&amp;#34;]
&lt;/pre>
&lt;p>이 사이클을 돌림으로써 개발자는 항상 안전망 위에서 코드를 변경할 수 있습니다. 테스트가 실패한 경우 원인을 깊게 파고들지 않고 즉시 &lt;code>Revert&lt;/code>(원상 복구)하는 것이 중요합니다.&lt;/p>
&lt;hr>
&lt;h2 id="3-테스트-가능성을-창출하는-이음새seams의-개념">3. 테스트 가능성을 창출하는 &amp;lsquo;이음새(Seams)&amp;lsquo;의 개념
&lt;/h2>&lt;p>레거시 코드에 테스트 추가를 시도할 때 가장 먼저 직면하는 장벽이 &amp;lsquo;의존성&amp;rsquo;입니다. 데이터베이스에 대한 직접 연결, 네트워크 통신, 하드코딩된 파일 시스템에 대한 액세스 등이 단단히 결합되어 있으면 단위 테스트(Unit Test)를 작성하는 것이 불가능합니다.&lt;/p>
&lt;p>여기서 등장하는 것이 &amp;lsquo;이음새(Seam)&amp;lsquo;라는 개념입니다. 이음새란 &amp;lsquo;코드 자체를 편집하지 않고도 시스템의 동작을 변경할 수 있는 위치&amp;rsquo;를 가리킵니다. C++에서는 주로 다음의 3가지 이음새를 이용합니다.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>객체 이음새 (Object Seams)&lt;/strong>: 가상 함수 (Virtual Functions)를 이용한 다형성.&lt;/li>
&lt;li>&lt;strong>컴파일 시점 이음새 (Compile-time Seams)&lt;/strong>: 템플릿 (Templates)이나 &lt;code>#include&lt;/code>의 전환.&lt;/li>
&lt;li>&lt;strong>링크 시점 이음새 (Link-time Seams)&lt;/strong>: 빌드 시 링크하는 라이브러리나 객체 파일의 전환.&lt;/li>
&lt;/ol>
&lt;p>이들을 구사하여 프로덕션 환경의 모듈을 테스트 환경용 모의(Mock) 객체로 바꿔치기함으로써 의존성을 격리합니다.&lt;/p>
&lt;hr>
&lt;h2 id="4-강결합의-타파-의존성-주입-dependency-injection">4. 강결합의 타파: 의존성 주입 (Dependency Injection)
&lt;/h2>&lt;p>의존성 주입(DI: Dependency Injection)은 객체의 생성 책임을 클래스 내부에서 외부로 떼어내기 위한 강력한 패턴입니다.&lt;/p>
&lt;p>먼저, 레거시하고 강하게 결합된 C++의 클래스 설계를 살펴보겠습니다.&lt;/p>
&lt;pre class="mermaid">
classDiagram
class LegacyOrderProcessor {
-DatabaseConnection* db
-FileLogger* logger
+LegacyOrderProcessor()
+processOrder(int orderId) void
}
class DatabaseConnection {
+DatabaseConnection()
+save(int orderId) void
}
class FileLogger {
+FileLogger()
+log(string msg) void
}
LegacyOrderProcessor --&amp;gt; DatabaseConnection : &amp;#34;직접 생성 (new 사용)&amp;#34;
LegacyOrderProcessor --&amp;gt; FileLogger : &amp;#34;직접 생성 (new 사용)&amp;#34;
&lt;/pre>
&lt;p>이 &lt;code>LegacyOrderProcessor&lt;/code>는 생성자 내에서 &lt;code>DatabaseConnection&lt;/code>이나 &lt;code>FileLogger&lt;/code>를 직접 &lt;code>new&lt;/code>하고 있기 때문에 모의 객체로 교체할 이음새가 존재하지 않습니다. 이를 인터페이스(순수 가상 클래스)를 사용하여 느슨한 결합으로 리팩터링합니다.&lt;/p>
&lt;pre class="mermaid">
classDiagram
class IDatabase {
&amp;lt;&amp;lt;interface&amp;gt;&amp;gt;
+save(int orderId) void
}
class ILogger {
&amp;lt;&amp;lt;interface&amp;gt;&amp;gt;
+log(string msg) void
}
class ModernOrderProcessor {
-std::unique_ptr~IDatabase~ db
-std::shared_ptr~ILogger~ logger
+ModernOrderProcessor(std::unique_ptr~IDatabase~ db, std::shared_ptr~ILogger~ logger)
+processOrder(int orderId) void
}
class DatabaseConnection {
+save(int orderId) void
}
class FileLogger {
+log(string msg) void
}
IDatabase &amp;lt;|.. DatabaseConnection : &amp;#34;구현&amp;#34;
ILogger &amp;lt;|.. FileLogger : &amp;#34;구현&amp;#34;
ModernOrderProcessor --&amp;gt; IDatabase : &amp;#34;DI (생성자 주입)&amp;#34;
ModernOrderProcessor --&amp;gt; ILogger : &amp;#34;DI (생성자 주입)&amp;#34;
&lt;/pre>
&lt;h3 id="레거시-코드의-예-c03">레거시 코드의 예 (C++03)
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">LegacyOrderProcessor&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DatabaseConnection&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">db_&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">FileLogger&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">logger_&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">LegacyOrderProcessor&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">db_&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">DatabaseConnection&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;localhost&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3306&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logger_&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">FileLogger&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;/var/log/app.log&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">~&lt;/span>&lt;span class="n">LegacyOrderProcessor&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">delete&lt;/span> &lt;span class="n">db_&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">delete&lt;/span> &lt;span class="n">logger_&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="nf">processOrder&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">orderId&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 처리...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">db_&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">save&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">orderId&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logger_&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Order processed&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="리팩터링-후-modern-c">리팩터링 후 (Modern C++)
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 인터페이스 정의 (객체 이음새)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">class&lt;/span> &lt;span class="nc">IDatabase&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="o">~&lt;/span>&lt;span class="n">IDatabase&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">default&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">save&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">orderId&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">ILogger&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="o">~&lt;/span>&lt;span class="n">ILogger&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">default&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">msg&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 의존성을 외부에서 주입하는 설계
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">class&lt;/span> &lt;span class="nc">ModernOrderProcessor&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">unique_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">IDatabase&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">db_&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">shared_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">ILogger&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">logger_&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 생성자 주입 (Constructor Injection)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">ModernOrderProcessor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">unique_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">IDatabase&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">db&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">shared_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">ILogger&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">logger&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">:&lt;/span> &lt;span class="n">db_&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">move&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">db&lt;/span>&lt;span class="p">)),&lt;/span> &lt;span class="n">logger_&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">move&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">logger&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="nf">processOrder&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">orderId&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">db_&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">save&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">orderId&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logger_&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Order processed&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>이와 같이 설계를 고침으로써, Google Mock (gmock) 등의 프레임워크를 사용하여 &lt;code>IDatabase&lt;/code>의 모의 객체를 쉽게 작성할 수 있어 테스트 주도 개발(TDD)이 가능해집니다.&lt;/p>
&lt;hr>
&lt;h2 id="5-마의-전역-변수와-싱글톤의-해체">5. 마의 전역 변수와 싱글톤의 해체
&lt;/h2>&lt;p>레거시 C++에서 가장 골치를 앓는 것이 전역 변수와 &amp;lsquo;싱글톤(Singleton) 패턴&amp;rsquo;의 남용입니다. 싱글톤은 언뜻 보기에 편리한 디자인 패턴처럼 보이지만, 실상은 &amp;lsquo;객체 지향의 탈을 쓴 전역 변수&amp;rsquo;에 지나지 않습니다.&lt;/p>
&lt;p>전역 상태는 테스트 케이스 간에 상태를 공유해 버리기 때문에 테스트의 병렬 실행을 불가능하게 하고, 원인을 알 수 없는 불안정한 테스트(Flaky Tests)를 유발합니다.&lt;/p>
&lt;p>해결책은 암묵적인 전역 상태에 대한 의존성을 배제하고, 필요한 상태를 함수의 인수로 명시적으로 전달하는 것(매개변수화)입니다. 이를 &amp;lsquo;컨텍스트 전달&amp;rsquo;이라고 부릅니다.&lt;/p>
&lt;hr>
&lt;h2 id="6-메모리-관리의-현대화와-raii의-진수">6. 메모리 관리의 현대화와 RAII의 진수
&lt;/h2>&lt;p>C++98/03 시절의 코드는 &lt;code>new&lt;/code>와 &lt;code>delete&lt;/code>가 코드 곳곳에 흩어져 있어 메모리 누수나 댕글링 포인터의 온상이 되고 있습니다. Modern C++ (C++11 이후)에서는 &lt;strong>소유권 (Ownership)&lt;/strong> 의 개념이 언어 수준에서 지원되어, 스마트 포인터를 사용한 안전한 리소스 관리가 표준이 되었습니다.&lt;/p>
&lt;h3 id="raii-resource-acquisition-is-initialization">RAII (Resource Acquisition Is Initialization)
&lt;/h3>&lt;p>RAII는 C++에서 가장 중요한 관용구(Idiom)입니다. 리소스의 확보를 객체의 초기화(생성자)와 결부시키고, 리소스의 해제를 객체의 파괴(소멸자)와 결부시킴으로써, 스코프를 벗어날 때 확실하게 리소스가 해제되는 것을 보장합니다.&lt;/p>
&lt;p>예외(Exceptions)가 발생한 경우라도 스택 언와인딩(Stack Unwinding) 과정에서 지역 변수의 소멸자가 자동으로 호출되므로 리소스 누수를 방지할 수 있습니다.&lt;/p>
&lt;p>&lt;strong>Before (위험한 레거시 코드)&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">processFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="kt">char&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">filename&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">FILE&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">file&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">fopen&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">filename&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;r&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Data&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">Data&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">readData&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">delete&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 잊기 쉬움
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">fclose&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// 잊기 쉬움
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">process&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(...)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">delete&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 예외 발생 시 메모리 누수 회피
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">fclose&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">delete&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fclose&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>이 코드는 제어 흐름의 모든 분기에서 수동으로 리소스를 해제해야 하므로 매우 취약한 구조입니다.&lt;/p>
&lt;p>&lt;strong>After (RAII와 스마트 포인터의 활용)&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">processFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">filename&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// std::ifstream은 파일 핸들을 RAII로 관리한다
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">ifstream&lt;/span> &lt;span class="n">file&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">filename&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">is_open&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// std::unique_ptr은 힙 메모리를 RAII로 관리하는 독점적 소유자
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">auto&lt;/span> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">make_unique&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Data&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">readData&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 스코프를 벗어나는 시점에 자동으로 해제된다
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 예외가 발생하더라도 unique_ptr과 ifstream의 소멸자가
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// 확실하게 리소스를 해제하므로 안전하다 (메모리 누수 제로 보장)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">process&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>이 리팩터링을 통해 코드 양은 대폭 감소하고 의도도 명확해지며, 무엇보다 예외 안전성(Exception Safety)이 완벽하게 보장되게 되었습니다.&lt;/p>
&lt;hr>
&lt;h2 id="7-modern-c-기능군에-의한-표현력-향상">7. Modern C++ 기능군에 의한 표현력 향상
&lt;/h2>&lt;p>레거시 코드의 리팩터링에서는 언어 기능의 업데이트에 따른 혜택을 적극적으로 활용해야 합니다.&lt;/p>
&lt;h3 id="71-auto-를-통한-타입-추론">7.1. &lt;code>auto&lt;/code> 를 통한 타입 추론
&lt;/h3>&lt;p>긴 반복자의 타입명 등 장황한 기술을 &lt;code>auto&lt;/code>로 대체함으로써 가독성이 향상됩니다. 그러나 무조건 &lt;code>auto&lt;/code>로 하는 것이 아니라, &amp;lsquo;우변을 보면 타입이 자명한 경우&amp;rsquo;로 한정하는 것이 모범 사례입니다.&lt;/p>
&lt;h3 id="72-constexpr-와-consteval-에-의한-컴파일-시점-계산">7.2. &lt;code>constexpr&lt;/code> 와 &lt;code>consteval&lt;/code> 에 의한 컴파일 시점 계산
&lt;/h3>&lt;p>실행 시의 오버헤드를 줄이고 컴파일 시점에 오류를 검출하기 위해 &lt;code>constexpr&lt;/code>을 적극적으로 활용합니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 레거시 코드 (매크로나 실행 시 계산)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#define MAX_BUFFER_SIZE 1024
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="kt">double&lt;/span> &lt;span class="n">PI&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">3.1415926535&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">double&lt;/span> &lt;span class="nf">calculateCircleArea&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">double&lt;/span> &lt;span class="n">radius&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">PI&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">radius&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">radius&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Modern C++ (C++20 이후) 의 스타일
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">constexpr&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">size_t&lt;/span> &lt;span class="n">MaxBufferSize&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1024&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">constexpr&lt;/span> &lt;span class="kt">double&lt;/span> &lt;span class="n">Pi&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">3.14159265358979323846&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 컴파일 시점에 평가 가능함을 보장하는 consteval (C++20)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">consteval&lt;/span> &lt;span class="kt">double&lt;/span> &lt;span class="nf">calculateCircleArea&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">double&lt;/span> &lt;span class="n">radius&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">Pi&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">radius&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">radius&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 실행 시의 비용은 제로. 컴파일 시점에 결과 상수가 직접 바이너리에 내장된다.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">constexpr&lt;/span> &lt;span class="kt">double&lt;/span> &lt;span class="n">area&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">calculateCircleArea&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">10.0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="73-nodiscard-속성">7.3. &lt;code>[[nodiscard]]&lt;/code> 속성
&lt;/h3>&lt;p>함수의 반환값(특히 에러 코드나 중요한 상태)을 무시해 버리는 버그를 방지하기 위해 &lt;code>[[nodiscard]]&lt;/code> 속성을 부여합니다. 이를 통해 반환값을 받지 않는 호출에 대해 컴파일러가 경고를 발생시킵니다.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="na">[[nodiscard]]&lt;/span> &lt;span class="kt">bool&lt;/span> &lt;span class="n">initializeSystem&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="c1">// 반환값 무시를 금지한다
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="8-자동화-도구의-활용과-지속적인-개선">8. 자동화 도구의 활용과 지속적인 개선
&lt;/h2>&lt;p>대규모 레거시 코드베이스를 수작업으로 수정하는 것은 비현실적입니다. 툴체인의 힘을 빌리는 것이 성공으로 가는 지름길이 됩니다.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Clang-Tidy&lt;/strong>: 강력한 C++용 린터 및 정적 분석 도구. &lt;code>modernize-*&lt;/code> 계열의 검사를 활성화하여 &lt;code>auto&lt;/code>의 적용, &lt;code>nullptr&lt;/code>로의 대체, &lt;code>override&lt;/code> 부여 등을 자동으로 적용(Fix-it)해 줍니다.&lt;/li>
&lt;li>&lt;strong>AddressSanitizer (ASan)&lt;/strong>: 컴파일 옵션(&lt;code>-fsanitize=address&lt;/code>)으로 통합하여 실행 시의 메모리 누수나 버퍼 오버런을 정확하게 식별합니다. 테스트 실행 시에는 반드시 활성화해야 합니다.&lt;/li>
&lt;li>&lt;strong>CI/CD 파이프라인 구축&lt;/strong>: GitHub Actions나 GitLab CI를 사용하여 모든 풀 리퀘스트에 대해 빌드와 자동 테스트, 정적 분석을 실행하고 새로운 기술 부채의 유입을 방지합니다.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="9-결론">9. 결론
&lt;/h2>&lt;p>레거시 C++ 코드의 리팩터링은 결코 하루아침에 완료되는 것이 아닙니다. 그것은 시스템에 외과 수술을 하는 것과 같은, 섬세하고 대담한 작업입니다.&lt;/p>
&lt;p>이 글에서 해설한 다음의 단계들을 마음속에 새겨두십시오.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>복잡도를 측정하고, 사실에 근거하여 전략을 세운다&lt;/strong>&lt;/li>
&lt;li>&lt;strong>이음새를 찾아내고, 특성화 테스트로 시스템을 보호한다&lt;/strong>&lt;/li>
&lt;li>&lt;strong>DI를 통해 강결합을 타파하고, 전역 상태를 근절한다&lt;/strong>&lt;/li>
&lt;li>&lt;strong>RAII와 스마트 포인터를 통해 메모리 관리에 대한 불안을 제거한다&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Modern C++의 기능을 활용하여, 컴파일러가 일을 하게 한다&lt;/strong>&lt;/li>
&lt;/ol>
&lt;p>&amp;lsquo;보이스카우트 규칙(캠프장을 왔을 때보다 더 깨끗하게 하고 떠난다)&amp;lsquo;의 정신을 갖고, 일상적인 개발 작업 속에서 조금씩 그러나 착실하게 코드를 계속 개선하는 것이 리팩터링의 진정한 비결입니다.&lt;/p></description></item></channel></rss>