<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Windows 11 on kenji.blog</title><link>http://kenji.blog/ko/tags/windows-11/</link><description>Recent content in Windows 11 on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>ko</language><copyright>kenjinote</copyright><lastBuildDate>Sat, 12 Sep 2026 22:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/ko/tags/windows-11/index.xml" rel="self" type="application/rss+xml"/><item><title>Windows 11용 MSIX 패키지 만드는 방법과 자체 서명 인증서의 함정</title><link>http://kenji.blog/ko/p/windows-11-msix-packaging-guide/</link><pubDate>Sat, 12 Sep 2026 22:00:00 +0900</pubDate><guid>http://kenji.blog/ko/p/windows-11-msix-packaging-guide/</guid><description>&lt;img src="http://kenji.blog/p/windows-11-msix-packaging-guide/img/eyecatch.jpg" alt="Featured image of post Windows 11용 MSIX 패키지 만드는 방법과 자체 서명 인증서의 함정" />&lt;p>Windows 11 시대에서 애플리케이션 배포 포맷으로 &amp;ldquo;MSIX&amp;quot;가 표준적인 선택지가 되어가고 있습니다. 기존의 MSI나 EXE 같은 인스톨러에는 많은 과제가 있었지만, MSIX는 그것들을 해결할 차세대 패키징 기술로서 기대받고 있습니다. 그러나 개발자가 막상 MSIX 패키지를 생성하여 조직 내나 테스트 환경에서 사이드로딩(Sideloading)을 하려고 하면 &amp;ldquo;자체 서명 인증서의 함정&amp;quot;에 빠지는 경우가 적지 않습니다.&lt;/p>
&lt;p>본 문서에서는 MSIX의 기술적인 상세 내용부터 Visual Studio나 커맨드 라인 툴을 사용한 패키지 생성 방법, 그리고 많은 개발자가 직면하는 자체 서명 인증서 관련 에러의 원인과 해결책까지 아주 상세하게 해설합니다. Windows 앱 개발자, 인프라 관리자, 그리고 패키징 담당자에게 필독 가이드가 되기를 바랍니다.&lt;/p>
&lt;h2 id="1-msix란-무엇인가-기존-msiexe와의-비교">1. MSIX란 무엇인가? 기존 MSI/EXE와의 비교
&lt;/h2>&lt;p>MSIX는 Microsoft가 제공하는 Windows용 최신 애플리케이션 패키지 형식입니다. 기존부터 존재했던 MSI(Microsoft Installer), .exe 기반의 커스텀 인스톨러, App-V(Application Virtualization), 그리고 Windows 8 이후 도입된 AppX(Universal Windows Platform 앱 패키지)의 모든 뛰어난 기능과 개념을 통합하고 나아가 현대의 보안과 배포 요건에 맞춰 진화시킨 것입니다.&lt;/p>
&lt;h3 id="기존-인스톨러msiexe의-과제">기존 인스톨러(MSI/EXE)의 과제
&lt;/h3>&lt;p>오랜 기간 Windows의 표준적인 설치 형식으로 사용되어 온 MSI나 EXE에는 다음과 같은 근본적인 문제가 있었습니다.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Win Rot(Windows 노후화) 현상&lt;/strong>: 애플리케이션의 설치와 제거를 반복하는 과정에서 레지스트리에 불필요한 키가 남고 시스템 폴더(&lt;code>C:\Windows\System32&lt;/code> 등)에 DLL이 남겨지는 문제입니다. 이로 인해 OS 자체의 동작이 점차 느려지고 불안정해지는 현상이 발생합니다.&lt;/li>
&lt;li>&lt;strong>DLL 지옥(DLL Hell)&lt;/strong>: 여러 애플리케이션이 동일한 이름의 DLL(단, 버전은 다름)을 공유 시스템 디렉터리에 설치하려고 할 경우, 나중에 설치된 앱이 기존의 DLL을 덮어써서 먼저 설치되어 있던 앱이 정상적으로 동작하지 않게 되는 문제입니다.&lt;/li>
&lt;li>&lt;strong>커스텀 액션에 의한 불안정성&lt;/strong>: MSI 패키지에서는 &amp;ldquo;커스텀 액션&amp;quot;이라고 불리는 임의의 스크립트나 코드를 설치 중 및 제거 중에 시스템 권한으로 실행할 수 있습니다. 이로 인해 인스톨러가 도중에 크래시(Crash)되거나 시스템의 예기치 않은 설정 변경을 일으킬 위험이 있었습니다.&lt;/li>
&lt;/ol>
&lt;h3 id="msix의-컨테이너화-아키텍처를-통한-해결책">MSIX의 컨테이너화 아키텍처를 통한 해결책
&lt;/h3>&lt;p>MSIX는 애플리케이션을 경량화된 &amp;ldquo;컨테이너&amp;rdquo; 내에서 동작시킴으로써 이러한 과제들을 해결합니다. 이 컨테이너화 접근 방식에는 다음과 같은 절대적인 장점이 있습니다.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>깔끔한 제거&lt;/strong>: MSIX로 설치된 앱은 파일 시스템이나 레지스트리에 대한 쓰기를 가상화(VFS: Virtual File System, VReg: Virtual Registry)하여 진행합니다. 따라서 제거 시에는 이 가상화된 컨테이너 자체가 삭제되므로 시스템에 어떠한 쓰레기(잔해)도 남기지 않습니다. Win Rot을 완벽하게 방지합니다.&lt;/li>
&lt;li>&lt;strong>분리와 보안(Isolation)&lt;/strong>: 각 앱은 자신만의 환경 내에서 동작하며, 다른 앱의 DLL이나 리소스를 직접 파괴하지 않습니다. 이로 인해 DLL 지옥에서 해방됩니다.&lt;/li>
&lt;li>&lt;strong>네트워크 대역폭 최적화&lt;/strong>: MSIX의 업데이트 메커니즘은 매우 우수하여 블록 레벨의 차분 업데이트(Differential Update)를 지원합니다. 바이너리 데이터 중 변경된 극히 일부의 블록만을 다운로드하므로, 대용량 앱의 업데이트에서도 네트워크에 미치는 부하를 최소화할 수 있습니다.&lt;/li>
&lt;li>&lt;strong>확실한 설치 상태&lt;/strong>: 패키지에는 매니페스트 파일(&lt;code>AppxManifest.xml&lt;/code>)이 포함되어 있으며, 설치 트랜잭션이 OS 레벨에서 엄격하게 관리됩니다. 실패할 경우에는 원래 상태로 완전히 롤백됩니다.&lt;/li>
&lt;/ul>
&lt;h2 id="2-msix-패키지-생성의-전체-그림과-툴체인">2. MSIX 패키지 생성의 전체 그림과 툴체인
&lt;/h2>&lt;p>MSIX 패키지를 생성하기 위해서는 크게 두 가지 접근 방식이 있습니다. 하나는 Visual Studio의 통합 개발 환경(IDE)을 이용하는 방법이고, 다른 하나는 Windows SDK에 동봉된 커맨드 라인 툴(&lt;code>MakeAppx.exe&lt;/code>나 &lt;code>SignTool.exe&lt;/code>)을 활용하는 방법입니다.&lt;/p>
&lt;p>다음의 Mermaid 다이어그램은 소스 파일에서 최종적으로 서명된 MSIX 패키지가 생성되기까지의 프로세스를 보여줍니다.&lt;/p>
&lt;pre class="mermaid">
flowchart TD
A[&amp;#34;소스 파일 (EXE, DLL, Assets 등)&amp;#34;] --&amp;gt; B[&amp;#34;AppxManifest.xml (매니페스트 정의)&amp;#34;]
B --&amp;gt; C[&amp;#34;MakeAppx.exe (MSIX 패키저)&amp;#34;]
C --&amp;gt; D[&amp;#34;서명되지 않은 MSIX 패키지 (.msix)&amp;#34;]
E[&amp;#34;디지털 인증서 (.pfx)&amp;#34;] --&amp;gt; F[&amp;#34;SignTool.exe (디지털 서명 툴)&amp;#34;]
D --&amp;gt; F
F --&amp;gt; G[&amp;#34;서명된 MSIX 패키지 (배포 준비 완료)&amp;#34;]
style A fill:#f9f9f9,stroke:#333
style B fill:#e6f7ff,stroke:#333
style D fill:#ffcccb,stroke:#333
style G fill:#d4edda,stroke:#333
&lt;/pre>
&lt;p>이 프로세스에서 알 수 있듯이, 단순히 파일들을 모아 하나로 묶는(패키징) 것만으로는 충분하지 않으며, 반드시 &amp;ldquo;디지털 서명&amp;rdquo; 단계가 필요합니다. Windows 11은 서명이 없는 MSIX 패키지의 설치를 보안상의 이유로 일절 허용하지 않습니다.&lt;/p>
&lt;h2 id="3-접근-방식-a-visual-studio를-이용한-msix-생성">3. 접근 방식 A: Visual Studio를 이용한 MSIX 생성
&lt;/h2>&lt;p>가장 쉽고 일반적인 방법은 Visual Studio의 &amp;ldquo;Windows 애플리케이션 패키지 프로젝트 (Windows Application Packaging Project - WAP)&amp;ldquo;를 사용하는 것입니다. 이 프로젝트 템플릿을 사용하면 WPF, Windows Forms, WinUI 3, 심지어 C++의 레거시 Win32 앱이라 하더라도 쉽게 MSIX화 할 수 있습니다.&lt;/p>
&lt;h3 id="단계별-가이드">단계별 가이드
&lt;/h3>&lt;ol>
&lt;li>&lt;strong>WAP 프로젝트 추가&lt;/strong>: 기존 Visual Studio 솔루션을 마우스 오른쪽 버튼으로 클릭하고, &amp;ldquo;새 프로젝트 추가&amp;quot;에서 &amp;ldquo;Windows 애플리케이션 패키지 프로젝트&amp;quot;를 선택합니다.&lt;/li>
&lt;li>&lt;strong>대상 플랫폼 선택&lt;/strong>: 앱이 지원할 Windows 10/11의 최소 버전과 대상 버전을 지정합니다.&lt;/li>
&lt;li>&lt;strong>애플리케이션 참조&lt;/strong>: 패키지 프로젝트의 &amp;ldquo;애플리케이션&amp;rdquo; 노드를 마우스 오른쪽 버튼으로 클릭하고 &amp;ldquo;참조 추가&amp;quot;를 통해 패키징하고 싶은 메인 프로젝트(예: WPF 프로젝트)를 선택합니다.&lt;/li>
&lt;li>&lt;strong>매니페스트 설정&lt;/strong>: &lt;code>Package.appxmanifest&lt;/code> 파일을 더블 클릭하여 비주얼 디자이너를 엽니다. 여기서 앱의 표시 이름, 설명, 로고 이미지, 그리고 가장 중요한 &amp;ldquo;패키지 이름(Identity Name)&amp;ldquo;과 &amp;ldquo;게시자(Publisher)&amp;ldquo;를 설정합니다.&lt;/li>
&lt;li>&lt;strong>패키지 생성&lt;/strong>: 프로젝트를 마우스 오른쪽 버튼으로 클릭하고, &amp;ldquo;게시&amp;rdquo; -&amp;gt; &amp;ldquo;앱 패키지 만들기&amp;quot;를 선택합니다. &amp;ldquo;사이드로딩용&amp;quot;을 선택하고 아키텍처(x64, ARM64 등)를 선택하면, Visual Studio가 자동으로 컴파일, &lt;code>MakeAppx&lt;/code>를 통한 패키징, 그리고 자체 서명 인증서 생성과 서명까지 일괄적으로 처리해 줍니다.&lt;/li>
&lt;/ol>
&lt;p>매우 매끄럽지만, 여기서 Visual Studio가 자동 생성한 자체 서명 인증서(Test Certificate)를 사용하면 나중에 설명할 &amp;ldquo;함정&amp;quot;에 빠지게 됩니다.&lt;/p>
&lt;h2 id="4-접근-방식-b-커맨드-라인makeappxexe을-이용한-생성">4. 접근 방식 B: 커맨드 라인(MakeAppx.exe)을 이용한 생성
&lt;/h2>&lt;p>CI/CD 파이프라인에서의 자동화나, 기존 인스톨러에서 수동으로 파일들을 재패키징하는 경우 등에는 커맨드 라인 툴이 필요합니다. Windows SDK가 설치된 환경이라면 개발자 명령 프롬프트에서 다음의 툴에 접근할 수 있습니다.&lt;/p>
&lt;h3 id="1-매니페스트-파일-준비">1. 매니페스트 파일 준비
&lt;/h3>&lt;p>패키지의 루트 디렉터리에 최소한의 정보를 기술한 &lt;code>AppxManifest.xml&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;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;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;utf-8&amp;#34;?&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;Package&lt;/span> &lt;span class="na">xmlns=&lt;/span>&lt;span class="s">&amp;#34;http://schemas.microsoft.com/appx/manifest/foundation/windows10&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">xmlns:uap=&lt;/span>&lt;span class="s">&amp;#34;http://schemas.microsoft.com/appx/manifest/uap/windows10&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">xmlns:rescap=&lt;/span>&lt;span class="s">&amp;#34;http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&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="nt">&amp;lt;Identity&lt;/span> &lt;span class="na">Name=&lt;/span>&lt;span class="s">&amp;#34;MyCompany.AwesomeApp&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">Publisher=&lt;/span>&lt;span class="s">&amp;#34;CN=MyCompany Self-Signed, O=MyCompany&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">Version=&lt;/span>&lt;span class="s">&amp;#34;1.0.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">ProcessorArchitecture=&lt;/span>&lt;span class="s">&amp;#34;x64&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&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="nt">&amp;lt;Properties&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;DisplayName&amp;gt;&lt;/span>Awesome App&lt;span class="nt">&amp;lt;/DisplayName&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;PublisherDisplayName&amp;gt;&lt;/span>My Company&lt;span class="nt">&amp;lt;/PublisherDisplayName&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;Logo&amp;gt;&lt;/span>Assets\StoreLogo.png&lt;span class="nt">&amp;lt;/Logo&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/Properties&amp;gt;&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="nt">&amp;lt;Resources&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;Resource&lt;/span> &lt;span class="na">Language=&lt;/span>&lt;span class="s">&amp;#34;en-us&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;Resource&lt;/span> &lt;span class="na">Language=&lt;/span>&lt;span class="s">&amp;#34;ja-jp&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/Resources&amp;gt;&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="nt">&amp;lt;Dependencies&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;TargetDeviceFamily&lt;/span> &lt;span class="na">Name=&lt;/span>&lt;span class="s">&amp;#34;Windows.Desktop&amp;#34;&lt;/span> &lt;span class="na">MinVersion=&lt;/span>&lt;span class="s">&amp;#34;10.0.17763.0&amp;#34;&lt;/span> &lt;span class="na">MaxVersionTested=&lt;/span>&lt;span class="s">&amp;#34;10.0.22000.0&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/Dependencies&amp;gt;&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="nt">&amp;lt;Capabilities&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;rescap:Capability&lt;/span> &lt;span class="na">Name=&lt;/span>&lt;span class="s">&amp;#34;runFullTrust&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/Capabilities&amp;gt;&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="nt">&amp;lt;Applications&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;Application&lt;/span> &lt;span class="na">Id=&lt;/span>&lt;span class="s">&amp;#34;AwesomeApp&amp;#34;&lt;/span> &lt;span class="na">Executable=&lt;/span>&lt;span class="s">&amp;#34;AwesomeApp.exe&amp;#34;&lt;/span> &lt;span class="na">EntryPoint=&lt;/span>&lt;span class="s">&amp;#34;Windows.FullTrustApplication&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;uap:VisualElements&lt;/span> &lt;span class="na">DisplayName=&lt;/span>&lt;span class="s">&amp;#34;Awesome App&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">Description=&lt;/span>&lt;span class="s">&amp;#34;The best app ever.&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">BackgroundColor=&lt;/span>&lt;span class="s">&amp;#34;transparent&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">Square150x150Logo=&lt;/span>&lt;span class="s">&amp;#34;Assets\Square150x150Logo.png&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">Square44x44Logo=&lt;/span>&lt;span class="s">&amp;#34;Assets\Square44x44Logo.png&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/uap:VisualElements&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/Application&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/Applications&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/Package&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>여기서 중요한 것은 &lt;code>&amp;lt;Identity Publisher=&amp;quot;...&amp;quot; /&amp;gt;&lt;/code> 의 값이 나중에 서명에 사용할 인증서의 Subject와 완벽하게 일치해야 한다는 점입니다.&lt;/p>
&lt;h3 id="2-makeappx를-통한-패키징">2. MakeAppx를 통한 패키징
&lt;/h3>&lt;p>명령 프롬프트에서 다음 명령어를 실행하여 디렉터리를 MSIX 파일로 압축합니다.&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-cmd" data-lang="cmd">&lt;span class="line">&lt;span class="cl">MakeAppx.exe pack /d &lt;span class="s2">&amp;#34;C:\Path\To\AppFolder&amp;#34;&lt;/span> /p &lt;span class="s2">&amp;#34;C:\Path\To\Output\AwesomeApp_1.0.0.0_x64.msix&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>이것으로 서명되지 않은 MSIX 파일이 완성되지만, 이 상태에서는 Windows에 설치할 수 없습니다.&lt;/p>
&lt;h2 id="5-디지털-서명과-암호-기술의-수학적-배경">5. 디지털 서명과 암호 기술의 수학적 배경
&lt;/h2>&lt;p>MSIX 패키지에 왜 서명이 필요한지를 깊이 이해하기 위해서는 디지털 서명 이면에 있는 암호학적 메커니즘을 이해해야 합니다. 디지털 서명은 패키지가 &amp;ldquo;확실하게 지정된 게시자에 의해 생성되었다는 것(인증)&amp;ldquo;과 &amp;ldquo;생성 후부터 현재까지 제3자에 의해 변조되지 않았다는 것(무결성)&amp;ldquo;을 보증합니다.&lt;/p>
&lt;p>MSIX 서명에는 일반적으로 RSA 암호와 SHA-256(Secure Hash Algorithm 256-bit)이 결합되어 사용됩니다.&lt;/p>
&lt;h3 id="해시-함수의-적용">해시 함수의 적용
&lt;/h3>&lt;p>먼저 MSIX 패키지의 바이너리 전체(내용물)를 메시지 $M$이라고 합시다. 서명 툴(SignTool.exe)은 이 메시지 $M$에 대해 암호학적 해시 함수인 SHA-256을 적용하여 고정 길이(256비트)의 해시값 $H(M)$을 계산합니다.&lt;/p>
&lt;h3 id="서명-생성게시자">서명 생성(게시자)
&lt;/h3>&lt;p>다음으로 게시자는 자신의 &amp;ldquo;개인 키(Private Key)&amp;rdquo; $d$를 사용하여 해시값을 암호화하고 디지털 서명 $\sigma$를 생성합니다. RSA 알고리즘의 맥락에서 이는 모듈러 거듭제곱 연산으로서 다음과 같이 표현됩니다.&lt;/p>
$$ \sigma \equiv (H(M))^d \pmod n $$&lt;p>여기서 $n$은 RSA 모듈러스(두 개의 거대한 소수의 곱)입니다. 이 서명 $\sigma$와 게시자의 &amp;ldquo;공개 키(Public Key)&amp;rdquo; $e$를 포함하는 인증서(X.509 형식)가 MSIX 패키지의 일부(&lt;code>AppxSignature.p7x&lt;/code>)로 임베드됩니다.&lt;/p>
&lt;h3 id="서명-검증windows-os">서명 검증(Windows OS)
&lt;/h3>&lt;p>사용자가 MSIX를 설치하려고 할 때, Windows OS는 패키지 내의 인증서에서 공개 키 $e$를 추출하고 다음의 계산을 수행하여 해시값 $H'(M)$을 복원합니다.&lt;/p>
$$ H'(M) \equiv \sigma^e \pmod n $$&lt;p>동시에 OS는 스스로 다운로드한 MSIX 패키지 $M$ 전체의 해시값 $H(M)$을 다시 계산합니다.
최종적으로 복원한 해시값과 다시 계산한 해시값이 동일한지($H(M) = H'(M)$)를 검증합니다. 이 등식이 성립하면 &amp;ldquo;서명 이후 파일이 1비트도 변조되지 않았다&amp;quot;는 것이 수학적으로 증명된 셈입니다.&lt;/p>
&lt;h2 id="6-최대의-장벽-자체-서명-인증서의-함정">6. 최대의 장벽: &amp;ldquo;자체 서명 인증서의 함정&amp;rdquo;
&lt;/h2>&lt;p>위의 수학적 증명이 완벽하더라도 Windows 11은 그것만으로 설치를 허용하지 않습니다. 왜냐하면 &amp;ldquo;해당 공개 키(인증서)의 소유자가 정말로 표방하는 바와 같은 안전한 조직이나 인물인가?&amp;ldquo;라는 &amp;lsquo;신뢰망(Chain of Trust)&amp;lsquo;을 검증해야 하기 때문입니다.&lt;/p>
&lt;p>인증서가 VeriSign이나 DigiCert와 같이 OS에 미리 신뢰되어 있는 공인된 루트 인증 기관(Root CA)으로부터 발급된 것이라면 문제없이 설치할 수 있습니다(Microsoft Store를 통해 배포되는 앱도 마찬가지로 Microsoft의 루트 인증서로 신뢰됩니다).&lt;/p>
&lt;p>하지만 개발 중이나 사내 전용 툴 등, 공인 인증서를 구매할 비용을 들이기 어려운 경우, 개발자는 스스로 인증서를 발급합니다. 이것이 &amp;ldquo;자체 서명 인증서(Self-Signed Certificate)&amp;ldquo;입니다.&lt;/p>
&lt;p>다음의 시퀀스 다이어그램은 자체 서명 인증서로 서명된 MSIX 패키지를 설치하려고 할 때 OS의 동작을 보여줍니다.&lt;/p>
&lt;pre class="mermaid">
sequenceDiagram
autonumber
participant U as &amp;#34;사용자 (Windows 11)&amp;#34;
participant P as &amp;#34;MSIX 앱 인스톨러&amp;#34;
participant C as &amp;#34;Windows 인증서 저장소&amp;#34;
U-&amp;gt;&amp;gt;P: &amp;#34;.msix 파일 더블 클릭&amp;#34;
P-&amp;gt;&amp;gt;P: &amp;#34;AppxSignature.p7x 읽기&amp;#34;
P-&amp;gt;&amp;gt;P: &amp;#34;서명 및 인증서 추출&amp;#34;
P-&amp;gt;&amp;gt;P: &amp;#34;수학적 검증 (해시 일치 여부 확인?)&amp;#34;
P-&amp;gt;&amp;gt;C: &amp;#34;게시자 인증서 신뢰 확인&amp;#34;
alt &amp;#34;인증서가 신뢰할 수 있는 루트 CA 저장소에 있음&amp;#34;
C--&amp;gt;&amp;gt;P: &amp;#34;신뢰 검증 성공&amp;#34;
P-&amp;gt;&amp;gt;U: &amp;#34;설치 프롬프트 표시 (설치 버튼 활성화됨)&amp;#34;
U-&amp;gt;&amp;gt;P: &amp;#34;설치 클릭&amp;#34;
P-&amp;gt;&amp;gt;U: &amp;#34;설치 성공&amp;#34;
else &amp;#34;인증서가 신뢰되지 않음 (함정)&amp;#34;
C--&amp;gt;&amp;gt;P: &amp;#34;신뢰 검증 실패 (0x800B0109)&amp;#34;
P-&amp;gt;&amp;gt;U: &amp;#34;에러 표시: 인증서 체인을 처리했지만, 신뢰할 수 없는 루트 인증서에서 종료되었습니다&amp;#34;
P-&amp;gt;&amp;gt;U: &amp;#34;설치 버튼 비활성화됨&amp;#34;
end
&lt;/pre>
&lt;p>바로 이것이 &amp;ldquo;함정&amp;quot;입니다. 개발자 자신이 작성하고 올바르게 서명했음에도 불구하고, Windows 11의 기본 상태에서는 해당 자체 서명 인증서를 알지 못하기(신뢰하지 않기) 때문에 에러 코드 &lt;code>0x800B0109&lt;/code>와 함께 설치가 차단되어 버립니다. 인스톨러의 &amp;ldquo;설치&amp;rdquo; 버튼은 회색으로 비활성화되어 누를 수 없습니다.&lt;/p>
&lt;p>많은 개발자가 이 에러에 직면하고 &amp;ldquo;MSIX는 버그투성이다&amp;rdquo;, &amp;ldquo;설정이 잘못된 것이 틀림없다&amp;quot;라며 매니페스트 파일 등을 몇 번이나 다시 쓰는 미궁에 빠지지만, 문제는 패키지의 구조가 아니라 OS의 인증서 저장소(Certificate Store)에 등록되어 있는지 여부에 있습니다.&lt;/p>
&lt;h2 id="7-해결책-powershell을-이용한-자체-서명-인증서-생성-및-배포">7. 해결책: PowerShell을 이용한 자체 서명 인증서 생성 및 배포
&lt;/h2>&lt;p>이 문제를 해결하려면 다음의 두 단계를 확실하게 실행해야 합니다.&lt;/p>
&lt;ol>
&lt;li>유효한 자체 서명 인증서를 생성하고, 개인 키를 포함한 PFX 파일을 내보낸다.&lt;/li>
&lt;li>생성한 인증서의 공개 키 부분(CER 파일)을 대상이 되는 &lt;strong>모든 PC의 &amp;ldquo;신뢰할 수 있는 루트 인증 기관(Trusted 일 루트 인증 기관(Trusted Root Certification Authorities)&amp;rdquo; 저장소에 설치한다&lt;/strong>.&lt;/li>
&lt;/ol>
&lt;p>이러한 작업들은 PowerShell을 사용함으로써 확실하고 자동으로 처리할 수 있습니다.&lt;/p>
&lt;h3 id="단계-1-자체-서명-인증서-생성-및-내보내기">단계 1: 자체 서명 인증서 생성 및 내보내기
&lt;/h3>&lt;p>먼저 관리자 권한으로 PowerShell을 실행하고, 다음 스크립트를 실행하여 인증서를 생성합니다. 여기서는 코드 서명(Code Signing) 용도에 특화된 인증서를 생성합니다.&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;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># 1. 파라미터 정의&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$SubjectName&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;CN=MyCompany Self-Signed, O=MyCompany&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$CertStoreLocation&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;Cert:\CurrentUser\My&amp;#34;&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="c"># 2. 자체 서명 인증서 생성 (코드 서명 용도: 1.3.6.1.5.5.7.3.3)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$Cert&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nb">New-SelfSignedCertificate&lt;/span> &lt;span class="n">-Type&lt;/span> &lt;span class="n">Custom&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">-Subject&lt;/span> &lt;span class="nv">$SubjectName&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">-KeyUsage&lt;/span> &lt;span class="n">DigitalSignature&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">-FriendlyName&lt;/span> &lt;span class="s2">&amp;#34;MyCompany MSIX Signing Cert&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="n">-CertStoreLocation&lt;/span> &lt;span class="nv">$CertStoreLocation&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">-TextExtension&lt;/span> &lt;span class="vm">@&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;2.5.29.37={text}1.3.6.1.5.5.7.3.3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;2.5.29.19={text}&amp;#34;&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="nb">Write-Host&lt;/span> &lt;span class="s2">&amp;#34;인증서가 생성되었습니다. Thumbprint: &lt;/span>&lt;span class="p">$(&lt;/span>&lt;span class="nv">$Cert&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Thumbprint&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="s2">&amp;#34;&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="c"># 3. PFX(개인 키 포함) 내보내기용 암호 생성&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$Password&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nb">ConvertTo-SecureString&lt;/span> &lt;span class="n">-String&lt;/span> &lt;span class="s2">&amp;#34;YourSecurePassword123!&amp;#34;&lt;/span> &lt;span class="n">-Force&lt;/span> &lt;span class="n">-AsPlainText&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="c"># 4. PFX 파일 내보내기 (SignTool에서의 서명용)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$PfxPath&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;C:\Path\To\Output\MyCompanyCert.pfx&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">Export-PfxCertificate&lt;/span> &lt;span class="n">-Cert&lt;/span> &lt;span class="nv">$Cert&lt;/span> &lt;span class="n">-FilePath&lt;/span> &lt;span class="nv">$PfxPath&lt;/span> &lt;span class="n">-Password&lt;/span> &lt;span class="nv">$Password&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="c"># 5. CER(공개 키만) 내보내기 (클라이언트 PC 설치용)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$CerPath&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;C:\Path\To\Output\MyCompanyCert.cer&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">Export-Certificate&lt;/span> &lt;span class="n">-Cert&lt;/span> &lt;span class="nv">$Cert&lt;/span> &lt;span class="n">-FilePath&lt;/span> &lt;span class="nv">$CerPath&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>여기서 생성한 &lt;code>$PfxPath&lt;/code>의 파일을 사용하여 MSIX 패키지에 서명을 진행합니다.&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-cmd" data-lang="cmd">&lt;span class="line">&lt;span class="cl">SignTool.exe sign /fd SHA256 /a /f &lt;span class="s2">&amp;#34;C:\Path\To\Output\MyCompanyCert.pfx&amp;#34;&lt;/span> /p &lt;span class="s2">&amp;#34;YourSecurePassword123!&amp;#34;&lt;/span> &lt;span class="s2">&amp;#34;C:\Path\To\Output\AwesomeApp_1.0.0.0_x64.msix&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="단계-2-클라이언트-pc에-인증서-설치함정-해제">단계 2: 클라이언트 PC에 인증서 설치(함정 해제)
&lt;/h3>&lt;p>서명된 MSIX를 다른 PC(또는 가상 환경)로 가져가서 그대로 더블 클릭해도 앞서 설명한 바와 같이 설치할 수 없습니다. 사전에(또는 동시에) 방금 내보낸 &lt;code>$CerPath&lt;/code> 파일을 &amp;ldquo;로컬 컴퓨터&amp;quot;의 &amp;ldquo;신뢰할 수 있는 루트 인증 기관&amp;quot;에 설치해야 합니다.&lt;/p>
&lt;p>이를 수행하려면 배포 대상 PC에서 &lt;strong>관리자 권한&lt;/strong>의 PowerShell을 열고, 다음 명령어를 실행합니다.&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-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># CER 파일의 경로&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$CerPath&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;C:\Path\To\Output\MyCompanyCert.cer&amp;#34;&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="c"># 로컬 머신의 &amp;#34;신뢰할 수 있는 루트 인증 기관&amp;#34;으로 가져오기&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">Import-Certificate&lt;/span> &lt;span class="n">-FilePath&lt;/span> &lt;span class="nv">$CerPath&lt;/span> &lt;span class="n">-CertStoreLocation&lt;/span> &lt;span class="s2">&amp;#34;Cert:\LocalMachine\Root&amp;#34;&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="nb">Write-Host&lt;/span> &lt;span class="s2">&amp;#34;인증서를 신뢰할 수 있는 루트 인증 기관에 설치했습니다.&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>[!CAUTION]
&amp;ldquo;로컬 컴퓨터(&lt;code>LocalMachine&lt;/code>)&amp;ldquo;의 루트 인증 기관 저장소에 추가하는 작업은 관리자 권한이 필수입니다. 사용자 개인의 저장소(&lt;code>CurrentUser&lt;/code>)에 넣어도 App Installer의 권한 컨텍스트 때문에 인식되지 않는 경우가 있으므로 주의가 필요합니다.&lt;/p>
&lt;/blockquote>
&lt;p>이 스크립트가 성공한 직후, 방금 전 에러가 났던 MSIX 파일을 다시 더블 클릭해 보십시오. 마치 마법처럼 에러 메시지가 사라지고 선명한 파란색의 활성화된 &amp;ldquo;설치&amp;rdquo; 버튼이 표시될 것입니다. 이로써 &amp;ldquo;자체 서명 인증서의 함정&amp;quot;을 완전히 돌파했습니다.&lt;/p>
&lt;h2 id="8-엔터프라이즈-환경에서의-운영-및-모범-사례">8. 엔터프라이즈 환경에서의 운영 및 모범 사례
&lt;/h2>&lt;p>개발자의 로컬 테스트라면 위 절차로 충분하지만, 사내의 수십 대, 수백 대의 PC에 사이드로딩 앱을 배포할 경우, 사용자 한 명 한 명에게 인증서 설치 스크립트를 실행하게 하는 것은 비현실적이며 보안 위험도 따릅니다.&lt;/p>
&lt;p>엔터프라이즈 환경에서의 모범 사례는 다음과 같습니다.&lt;/p>
&lt;h3 id="1-active-directory-그룹-정책gpo-활용">1. Active Directory 그룹 정책(GPO) 활용
&lt;/h3>&lt;p>사내에 Active Directory가 도입되어 있는 경우, GPO의 &amp;ldquo;공개 키 정책&amp;quot;을 사용하여 자체 서명 인증서(CER 파일)를 도메인에 가입된 모든 PC의 &amp;ldquo;신뢰할 수 있는 루트 인증 기관&amp;quot;에 자동으로 배포할 수 있습니다. 이를 통해 직원은 인증서에 대해 전혀 신경 쓸 필요 없이 공유 폴더의 MSIX 파일을 더블 클릭하는 것만으로 설치가 가능해집니다.&lt;/p>
&lt;h3 id="2-microsoft-intunemdm을-통한-배포">2. Microsoft Intune(MDM)을 통한 배포
&lt;/h3>&lt;p>모던한 환경에서는 Microsoft Intune을 사용하여 디바이스 관리를 수행합니다. Intune에서는 &amp;ldquo;구성 프로필&amp;rdquo; 기능을 사용하여 신뢰할 수 있는 인증서(.cer)를 엔드포인트에 푸시 배포할 수 있습니다. 그 후 LOB(Line of Business) 애플리케이션으로서 MSIX 패키지 자체를 백그라운드 설치(Silent Install)로 배포하는 것이 가능합니다.&lt;/p>
&lt;h3 id="3-app-installer-파일appinstaller을-통한-자동-업데이트">3. App Installer 파일(.appinstaller)을 통한 자동 업데이트
&lt;/h3>&lt;p>MSIX에는 앱 업데이트를 자동화하는 강력한 기능이 갖추어져 있습니다. XML 기반의 &lt;code>.appinstaller&lt;/code> 파일을 생성하고 웹 서버나 SMB 공유 폴더에 배치해 둠으로써, 앱 시작 시 백그라운드에서 새 버전의 MSIX가 있는지 확인하고 자동으로 업데이트를 적용시킬 수 있습니다.&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-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;utf-8&amp;#34;?&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;AppInstaller&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">Uri=&lt;/span>&lt;span class="s">&amp;#34;https://internal.mycompany.com/apps/AwesomeApp.appinstaller&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">Version=&lt;/span>&lt;span class="s">&amp;#34;1.0.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">xmlns=&lt;/span>&lt;span class="s">&amp;#34;http://schemas.microsoft.com/appx/appinstaller/2018&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;MainPackage&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">Name=&lt;/span>&lt;span class="s">&amp;#34;MyCompany.AwesomeApp&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">Publisher=&lt;/span>&lt;span class="s">&amp;#34;CN=MyCompany Self-Signed, O=MyCompany&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">Version=&lt;/span>&lt;span class="s">&amp;#34;1.0.0.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">ProcessorArchitecture=&lt;/span>&lt;span class="s">&amp;#34;x64&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">Uri=&lt;/span>&lt;span class="s">&amp;#34;https://internal.mycompany.com/apps/AwesomeApp_1.0.0.0_x64.msix&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;UpdateSettings&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;OnLaunch&lt;/span> &lt;span class="na">HoursBetweenUpdateChecks=&lt;/span>&lt;span class="s">&amp;#34;0&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/UpdateSettings&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/AppInstaller&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>이 파일을 사용자에게 배포하고 설치하게 함으로써, 이후에는 서버 상의 MSIX 파일을 교체하고 &lt;code>.appinstaller&lt;/code>의 버전 번호를 업데이트하는 것만으로 모든 사용자의 앱이 자동으로 업데이트되게 됩니다.&lt;/p>
&lt;h2 id="9-문제-해결-인증서-관련-자주-발생하는-에러">9. 문제 해결: 인증서 관련 자주 발생하는 에러
&lt;/h2>&lt;p>마지막으로 인증서나 서명과 관련하여 발생할 수 있는 기타 일반적인 에러와 해결책을 정리해 둡니다.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>0x800B0101&lt;/strong>: 서명에 사용된 인증서의 유효 기간이 만료되었습니다. 새 인증서를 다시 발급받거나 서명 시에 타임스탬프 서버(예: &lt;code>http://timestamp.digicert.com&lt;/code>)를 사용하여, 인증서 유효 기간 내에 서명되었음을 증명할 수 있도록 하십시오(타임스탬프를 부여하면 인증서 자체의 기한이 만료되더라도 서명은 유효한 것으로 간주됩니다).&lt;/li>
&lt;li>&lt;strong>0x80080204&lt;/strong>: &lt;code>AppxManifest.xml&lt;/code>에 기재된 &lt;code>Publisher&lt;/code>의 값과 인증서의 &lt;code>Subject&lt;/code> 값이 완벽하게 일치하지 않습니다. 쉼표 뒤의 공백 유무 등, 문자열로서 완전히 일치하는지 엄밀하게 확인하십시오.&lt;/li>
&lt;li>&lt;strong>이벤트 뷰어 확인&lt;/strong>: 에러의 더 상세한 원인을 찾으려면 Windows 이벤트 뷰어를 열고, &amp;ldquo;응용 프로그램 및 서비스 로그&amp;rdquo; -&amp;gt; &amp;ldquo;Microsoft&amp;rdquo; -&amp;gt; &amp;ldquo;Windows&amp;rdquo; -&amp;gt; &amp;ldquo;AppxPackagingOM&amp;rdquo; 또는 &amp;ldquo;AppXDeployment-Server&amp;quot;의 로그를 확인하는 것이 매우 중요합니다.&lt;/li>
&lt;/ul>
&lt;h2 id="10-요약">10. 요약
&lt;/h2>&lt;p>Windows 11을 겨냥한 MSIX 패키징은 애플리케이션의 수명 주기 관리를 비약적으로 향상시키는 강력한 기술입니다. Win Rot이나 DLL 지옥에서 해방되어 깔끔하고 안전한 환경을 사용자에게 제공할 수 있습니다.&lt;/p>
&lt;p>한편으로는 보안 모델이 엄격해졌기 때문에 디지털 서명과 인증서의 &amp;ldquo;신뢰망&amp;quot;에 대한 깊은 이해가 필수적입니다. &amp;ldquo;자체 서명 인증서의 함정&amp;quot;은 MSIX 기술을 처음 접하는 개발자가 반드시 직면하게 되는 관문입니다. 본 문서에서 해설한 인증서의 생성, 내보내기, 그리고 적절한 저장소로의 가져오기 메커니즘을 이해하고, 스크립트나 GPO를 활용하여 자동화함으로써 MSIX의 잠재력을 최대한으로 끌어낸 원활한 배포를 실현할 수 있을 것입니다.&lt;/p>
&lt;p>이 지식을 활용하여 차세대 클린 Windows 애플리케이션 배포 환경을 구축하시기 바랍니다.&lt;/p></description></item><item><title>Windows 레지스트리의 기초 지식과 프로그래밍 가능한 안전한 편집 방법</title><link>http://kenji.blog/ko/p/windows-registry-safe-programmable-editing/</link><pubDate>Sat, 12 Sep 2026 12:00:00 +0900</pubDate><guid>http://kenji.blog/ko/p/windows-registry-safe-programmable-editing/</guid><description>&lt;img src="http://kenji.blog/p/windows-registry-safe-programmable-editing/img/eyecatch.jpg" alt="Featured image of post Windows 레지스트리의 기초 지식과 프로그래밍 가능한 안전한 편집 방법" />&lt;h1 id="windows-레지스트리의-기초-지식과-프로그래밍-가능한-안전한-편집-방법">Windows 레지스트리의 기초 지식과 프로그래밍 가능한 안전한 편집 방법
&lt;/h1>&lt;p>Windows 운영 체제에서 &amp;ldquo;레지스트리(Registry)&amp;ldquo;는 시스템 및 애플리케이션의 다양한 설정을 저장하는 거대한 계층형 데이터베이스입니다. 본 기사에서는 Windows 레지스트리의 기초적인 아키텍처부터 PowerShell이나 C#을 사용한 프로그래밍 가능하고 안전한 레지스트리 편집 방법에 대해 매우 자세히 해설합니다.&lt;/p>
&lt;h2 id="1-시작하며-windows-레지스트리의-역사와-진화">1. 시작하며: Windows 레지스트리의 역사와 진화
&lt;/h2>&lt;p>Windows의 초기 버전(Windows 3.x 시대)에서는 시스템이나 애플리케이션의 설정은 주로 &lt;code>.ini&lt;/code>(초기화 파일)에 저장되었습니다. 그러나 애플리케이션마다 무수히 많은 INI 파일이 시스템 전체에 흩어지게 되어 관리가 현저히 번거로워졌습니다. 또한 INI 파일은 일반 텍스트 기반이기 때문에 바이너리 데이터의 저장이 어렵고, 액세스 제어(보안) 구조도 존재하지 않았습니다. 파일 파싱 속도도 느려 대규모 설정을 저장하는 데는 적합하지 않았습니다.&lt;/p>
&lt;p>이러한 문제들을 근본적으로 해결하기 위해 Windows NT 및 Windows 95 이후 중앙 집중식 설정 데이터베이스로서 &amp;ldquo;레지스트리&amp;quot;가 본격적으로 채택되었습니다. 레지스트리는 계층화된 데이터베이스로, 강력한 타입 지정, 바이너리 데이터 지원 및 접근 제어 목록(ACL)에 의한 견고한 보안 기능을 제공합니다. 이를 통해 OS 커널에서 사용자 공간의 애플리케이션까지 모든 구성 요소가 통일된 인터페이스(Win32 API의 &lt;code>Reg*&lt;/code> 함수군)로 설정을 읽고 쓸 수 있게 되었습니다.&lt;/p>
&lt;p>현대의 Windows 11에 이르기까지 레지스트리는 OS의 심장부로 계속 기능하고 있습니다. 하드웨어 구성, 디바이스 드라이버 로드 순서, 사용자의 데스크톱 환경, 설치된 소프트웨어 목록 등 시스템 동작에 필요한 모든 메타데이터가 레지스트리에 집약되어 있습니다.&lt;/p>
&lt;h2 id="2-아키텍처의-심층-레지스트리-하이브의-실체와-메모리-매핑">2. 아키텍처의 심층: 레지스트리 하이브의 실체와 메모리 매핑
&lt;/h2>&lt;p>레지스트리는 논리적으로 하나의 거대한 트리 구조로 보이지만, 물리적으로는 &amp;ldquo;하이브(Hive)&amp;ldquo;라고 불리는 여러 파일로 분할되어 디스크에 저장됩니다. 이를 통해 시스템 전체의 설정과 사용자 고유의 설정이 분리되어 효율적인 읽기가 가능해집니다.&lt;/p>
&lt;p>주요 하이브 파일은 보통 &lt;code>%SystemRoot%\System32\config&lt;/code> 디렉토리에 존재합니다.&lt;/p>
&lt;ul>
&lt;li>&lt;code>SYSTEM&lt;/code>: 운영 체제 부팅에 필요한 중요한 설정 (드라이버, 서비스, 부팅 구성 등).&lt;/li>
&lt;li>&lt;code>SOFTWARE&lt;/code>: 설치된 소프트웨어의 시스템 전체 설정. 서드파티 앱 설정의 대부분이 여기에 들어갑니다.&lt;/li>
&lt;li>&lt;code>SAM&lt;/code>: Security Accounts Manager (로컬 사용자 계정과 암호 해시).&lt;/li>
&lt;li>&lt;code>SECURITY&lt;/code>: 로컬 보안 정책 및 권한 할당.&lt;/li>
&lt;li>&lt;code>DEFAULT&lt;/code>: 기본 사용자 프로필 (새 사용자 생성 시 템플릿).&lt;/li>
&lt;/ul>
&lt;p>사용자 개별 하이브 파일은 사용자의 프로필 디렉토리(예: &lt;code>C:\Users\Username&lt;/code>)에 숨김 파일로 존재합니다.&lt;/p>
&lt;ul>
&lt;li>&lt;code>NTUSER.DAT&lt;/code>: 해당 사용자의 기본 설정 (HKCU의 대부분).&lt;/li>
&lt;li>&lt;code>UsrClass.dat&lt;/code>: 해당 사용자의 파일 확장자 연결 설정 (&lt;code>AppData\Local\Microsoft\Windows&lt;/code> 내에 존재).&lt;/li>
&lt;/ul>
&lt;p>이 파일들은 OS 부팅 시 커널의 &amp;ldquo;Configuration Manager (CM)&amp;ldquo;에 의해 커널 페이지 풀 메모리에 매핑됩니다. Configuration Manager는 레지스트리의 읽기/쓰기 요청을 처리하는 커널 모드 구성 요소입니다.&lt;/p>
&lt;p>특기할 점은 모든 레지스트리 데이터가 디스크 상에 존재하는 것은 아니라는 것입니다. 예를 들어 &lt;code>HARDWARE&lt;/code> 하이브는 휘발성(Volatile)이며 디스크의 파일에는 전혀 저장되지 않습니다. OS가 부팅되고 플러그 앤 플레이(PnP) 관리자가 하드웨어를 감지할 때마다 메모리 상에서 동적으로 재구성됩니다.&lt;/p>
&lt;p>또한 최신 Windows에서는 레지스트리의 신뢰성을 높이기 위해 트랜잭션 로깅이 구현되어 있습니다. 하이브 파일에 대한 변경 사항은 데이터 파일에 직접 기록되는 것이 아니라 먼저 트랜잭션 로그(&lt;code>.log1&lt;/code>, &lt;code>.log2&lt;/code>)에 기록됩니다. 이를 통해 쓰기 중 예상치 못한 전원 손실이나 시스템 크래시 시의 데이터 손상(Corruption)을 방지하고, 데이터베이스의 무결성을 ACID 특성에 가까운 형태로 보장합니다.&lt;/p>
&lt;h2 id="3-레지스트리-키와-값의-계층-구조">3. 레지스트리 키와 값의 계층 구조
&lt;/h2>&lt;p>레지스트리는 파일 시스템과 매우 유사한 계층 구조를 가지고 있습니다. 루트가 되는 노드는 &amp;ldquo;루트 키&amp;rdquo; 또는 &amp;ldquo;하이브&amp;quot;라고 불리며, 그 아래에 &amp;ldquo;키&amp;rdquo;, &amp;ldquo;서브 키&amp;rdquo;, 그리고 데이터의 실체인 &amp;ldquo;값(Value)&amp;ldquo;이 저장됩니다. 키는 디렉토리에, 값은 파일에 해당한다고 생각하면 이해하기 쉽습니다.&lt;/p>
&lt;p>주요 루트 키는 다음 5가지로 분류됩니다.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>HKEY_LOCAL_MACHINE (HKLM)&lt;/strong>: 컴퓨터 전체(모든 사용자)에 적용되는 시스템 설정이나 소프트웨어 설정이 저장됩니다. 변경하려면 관리자 권한이 필요합니다.&lt;/li>
&lt;li>&lt;strong>HKEY_CURRENT_USER (HKCU)&lt;/strong>: 현재 로그온한 사용자 고유의 설정이 저장됩니다. 사실 이는 독립된 데이터베이스가 아니라 &lt;code>HKEY_USERS&lt;/code> 아래에 있는 해당 사용자의 SID(보안 식별자) 키에 대한 심볼릭 링크(별칭)에 불과합니다.&lt;/li>
&lt;li>&lt;strong>HKEY_CLASSES_ROOT (HKCR)&lt;/strong>: 파일 확장자 연결, COM(Component Object Model) 클래스의 등록 정보, 셸 확장이 저장됩니다. 이 키는 특수하여 &lt;code>HKLM\SOFTWARE\Classes&lt;/code> (시스템 전체)와 &lt;code>HKCU\Software\Classes&lt;/code> (현재 사용자)를 Configuration Manager가 병합(통합)하여 표시하는 가상 뷰입니다. 충돌이 있는 경우 사용자 고유의 설정(HKCU)이 우선됩니다.&lt;/li>
&lt;li>&lt;strong>HKEY_USERS (HKU)&lt;/strong>: 시스템의 모든 사용자 프로필(현재 메모리에 로드된 것) 설정이 저장됩니다. SID 기반으로 계층화되어 있습니다.&lt;/li>
&lt;li>&lt;strong>HKEY_CURRENT_CONFIG (HKCC)&lt;/strong>: 현재 하드웨어 프로필에 관한 설정. 실체는 &lt;code>HKLM\SYSTEM\CurrentControlSet\Hardware Profiles\Current&lt;/code>에 대한 링크입니다.&lt;/li>
&lt;/ol>
&lt;p>이 복잡한 계층 구조와 링크 관계를 시각화하면 다음과 같습니다.&lt;/p>
&lt;pre class="mermaid">
mindmap
root((&amp;#34;Windows 레지스트리&amp;#34;))
HKLM((&amp;#34;HKEY_LOCAL_MACHINE&amp;#34;))
SAM((&amp;#34;SAM (보안 계정)&amp;#34;))
SECURITY((&amp;#34;SECURITY&amp;#34;))
SOFTWARE((&amp;#34;SOFTWARE&amp;#34;))
SYSTEM((&amp;#34;SYSTEM&amp;#34;))
HARDWARE((&amp;#34;HARDWARE (휘발성)&amp;#34;))
HKCU((&amp;#34;HKEY_CURRENT_USER (링크)&amp;#34;))
AppEvents((&amp;#34;AppEvents&amp;#34;))
Console((&amp;#34;Console&amp;#34;))
Software((&amp;#34;Software&amp;#34;))
System((&amp;#34;System&amp;#34;))
HKCR((&amp;#34;HKEY_CLASSES_ROOT (병합된 뷰)&amp;#34;))
HKU((&amp;#34;HKEY_USERS&amp;#34;))
SID((&amp;#34;사용자 SID...&amp;#34;))
HKCC((&amp;#34;HKEY_CURRENT_CONFIG (링크)&amp;#34;))
&lt;/pre>
&lt;h2 id="4-레지스트리의-데이터-형식-상세-해설">4. 레지스트리의 데이터 형식 (상세 해설)
&lt;/h2>&lt;p>레지스트리의 &amp;ldquo;값&amp;quot;에는 각각 엄격한 데이터 형식이 정의되어 있습니다. 프로그래밍을 통해 레지스트리를 조작할 경우, 이러한 형식을 올바르게 이해하고 적절한 형식으로 데이터를 쓰는 것이 필수적입니다. 잘못된 형식으로 쓰면 애플리케이션에서 예외가 발생하거나 OS 기능이 정지하는 원인이 됩니다.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>REG_SZ (문자열 값)&lt;/strong>: 가장 일반적인 데이터 형식입니다. NULL로 끝나는 유니코드 문자열(UTF-16LE)을 저장합니다. 파일 경로, URL, UI의 표시 이름 등에 사용됩니다.&lt;/li>
&lt;li>&lt;strong>REG_DWORD (32비트 정수 값)&lt;/strong>: 32비트(4바이트)의 부호 없는 정수 값입니다. 부울 값(0=비활성화, 1=활성화)이나 밀리초 단위의 타임아웃 값, 오류 코드 설정 등에 자주 사용됩니다. Windows는 리틀 엔디안 아키텍처이기 때문에 디스크 상에서는 하위 바이트부터 순서대로(예: 0x12345678은 &lt;code>78 56 34 12&lt;/code>) 저장됩니다.&lt;/li>
&lt;li>&lt;strong>REG_QWORD (64비트 정수 값)&lt;/strong>: 64비트(8바이트)의 정수 값입니다. 64비트 아키텍처가 보급됨에 따라 거대한 수치(디스크 할당량이나 대용량 메모리 크기 지정 등)나 포인터 크기의 설정을 저장하기 위해 사용됩니다.&lt;/li>
&lt;li>&lt;strong>REG_MULTI_SZ (다중 문자열 값)&lt;/strong>: 여러 개의 NULL로 끝나는 문자열을 연속해서 저장하고, 마지막에 빈 NULL 종료 문자(Double NULL)를 두어 끝을 나타내는 형식입니다. IP 주소 목록, 종속성이 있는 서비스 목록, 바인딩 순서 등 배열적인 데이터를 저장하는 데 적합합니다.&lt;/li>
&lt;li>&lt;strong>REG_EXPAND_SZ (확장 가능한 문자열 값)&lt;/strong>: &lt;code>%USERPROFILE%&lt;/code>이나 &lt;code>%SystemRoot%&lt;/code>와 같은 확장되지 않은 환경 변수 문자열을 포함하는 특수한 문자열 형식입니다. 애플리케이션이 &lt;code>RegQueryValueEx&lt;/code> API를 통해 읽을 때, 또는 &lt;code>ExpandEnvironmentStrings&lt;/code> API를 호출함으로써 OS에 의해 동적으로 실제 절대 경로로 확장됩니다.&lt;/li>
&lt;li>&lt;strong>REG_BINARY (이진 값)&lt;/strong>: 임의의 원시 바이너리 데이터 스트림입니다. 암호화된 비밀번호(LSA Secrets 등), 디지털 인증서, 애플리케이션 고유의 복잡한 구조체나 직렬화된 데이터가 저장됩니다.&lt;/li>
&lt;li>&lt;strong>REG_NONE&lt;/strong>: 형식이 정의되지 않은 데이터입니다. 매우 드물지만 암호화 키의 예약 영역 등에 사용됩니다.&lt;/li>
&lt;li>&lt;strong>REG_RESOURCE_LIST&lt;/strong> / &lt;strong>REG_FULL_RESOURCE_DESCRIPTOR&lt;/strong>: 디바이스 드라이버가 하드웨어 리소스(IRQ, I/O 포트, DMA 채널)의 할당 정보를 기록하기 위해 사용하는 커널 전용의 고급 형식입니다.&lt;/li>
&lt;/ul>
&lt;h2 id="5-운영-체제에서의-레지스트리-수리-모델과-성능">5. 운영 체제에서의 레지스트리 수리 모델과 성능
&lt;/h2>&lt;p>레지스트리는 OS의 성능(특히 부팅 시간과 프로세스 초기화 속도)에 직결되기 때문에, 내부적으로는 &amp;ldquo;Cell Index&amp;quot;라고 불리는 B-Tree(B-트리)와 유사한 고도의 데이터 구조를 사용하여 최적화되어 있습니다.&lt;/p>
&lt;h3 id="검색-시간-복잡도-time-complexity">검색 시간 복잡도 (Time Complexity)
&lt;/h3>&lt;p>레지스트리 내의 특정 키(경로)를 검색할 때의 시간 복잡도 $T_{\text{search}}$는 트리의 깊이와 각 계층의 노드 수에 따라 달라집니다. 깊이가 $d$인 서브 키(예: &lt;code>A\B\C\D&lt;/code>라면 $d=4$)를 탐색할 경우의 계산 복잡도는 이론상 다음과 같이 모델링할 수 있습니다.&lt;/p>
$$
T_{\text{search}}(d, L) = \sum_{i=1}^{d} O(\log(C_i) \cdot L_i)
$$&lt;p>여기서 $C_i$는 깊이 $i$에서의 자식 노드(서브 키 또는 값)의 수, $L_i$는 비교 대상이 되는 문자열의 길이(문자 수)입니다. 레지스트리의 실체인 하이브 파일 내에서 서브 키 목록은 이름의 해시 값 또는 알파벳 순으로 정렬된 인덱스로 유지됩니다. 따라서 단순한 선형 탐색 $O(C_i)$가 아닌 이진 탐색 $O(\log(C_i))$가 가능하며, 하나의 키 아래에 수만 개의 서브 키가 존재하더라도 매우 빠른 액세스를 실현합니다.&lt;/p>
&lt;h3 id="스토리지-풋프린트-space-complexity">스토리지 풋프린트 (Space Complexity)
&lt;/h3>&lt;p>레지스트리의 전체 크기(물리 디스크 상의 점유량)는 각 하이브의 합계로 계산됩니다.&lt;/p>
$$
\text{Size}_{\text{Total}} = \sum_{h \in \text{Hives}} \left( N_{h} \times S_{\text{key\_metadata}} + \sum_{v \in h} S_{\text{value}}(v) \right) + S_{\text{overhead}}
$$&lt;p>$N_h$는 하이브 $h$ 내의 키 수, $S_{\text{key\_metadata}}$는 키 1개당 메타데이터(마지막 쓰기 타임스탬프, 보안 설명자에 대한 포인터, 부모 키에 대한 포인터 등)의 크기, $S_{\text{value}}(v)$는 값 $v$의 페이로드 크기입니다. 트랜잭션 로그나 불필요해진 빈 셀(단편화)로 인한 오버헤드 $S_{\text{overhead}}$도 포함됩니다. 레지스트리에 불필요한 데이터(제거가 완전히 되지 않은 소프트웨어의 잔해 등)를 장기간 방치하면 이 풋프린트가 증가하여 OS의 페이지 풀 메모리를 압박하고 성능 저하를 초래할 수 있습니다.&lt;/p>
&lt;h2 id="6-시스템의-견고성을-위협하는-수동-편집의-위험과-손상-확률">6. 시스템의 견고성을 위협하는 수동 편집의 위험과 손상 확률
&lt;/h2>&lt;p>레지스트리 편집기(&lt;code>regedit.exe&lt;/code>)를 이용한 수작업 편집은 시스템 관리의 최후의 수단으로 간주되어야 합니다. 레지스트리에는 일반적인 문서 편집기와 같은 &amp;ldquo;실행 취소(Undo)&amp;rdquo; 기능이 내장되어 있지 않으며, 값의 변경이나 키의 삭제는 Configuration Manager를 통해 즉시 시스템에 반영됩니다.&lt;/p>
&lt;p>특히 시스템 부팅에 필수적인 중요 키(예: &lt;code>HKLM\SYSTEM\CurrentControlSet\Services&lt;/code> 아래의 디스크 컨트롤러 드라이버 설정이나 &lt;code>HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon&lt;/code>의 &lt;code>Userinit&lt;/code> 값 등)를 한 글자라도 잘못 편집하거나 삭제할 경우, OS가 블루스크린(BSoD)을 일으켜 부팅할 수 없게 되거나 로그인 화면에서 더 이상 진행되지 않는(블랙스크린) 치명적인 위험이 있습니다.&lt;/p>
&lt;h3 id="손상-확률의-수학적-모델">손상 확률의 수학적 모델
&lt;/h3>&lt;p>레지스트리 내의 키를 무작위로 변경하거나 삭제했을 경우의 시스템 장애 발생 확률을 생각해 봅시다. 시스템이 정상적으로 동작하기 위해 필수적인 중요 키의 집합을 $C$, 그 총수를 $N_c = |C|$라고 합시다. 레지스트리 전체의 키 총수를 $N_{\text{total}}$이라고 합니다.
무작위로 $k$개의 키를 삭제하거나 파괴했을 때, 적어도 하나의 중요 키가 손상될 확률 $P_{\text{failure}}$는 비복원 추출(Sampling without replacement)의 확률 계산에 의해 다음과 같이 표현됩니다.&lt;/p>
$$
P_{\text{failure}} = 1 - \frac{\binom{N_{\text{total}} - N_c}{k}}{\binom{N_{\text{total}}}{k}} = 1 - \prod_{i=0}^{k-1} \left( 1 - \frac{N_c}{N_{\text{total}} - i} \right)
$$&lt;p>레지스트리 전체의 키 수 $N_{\text{total}}$은 수십만에서 수백만의 규모지만, $N_c$ 역시 수만의 규모로 존재합니다. 수학적으로 무작위 조작이라도 $k$가 증가하면 장애 확률은 급격히 상승합니다. 게다가 실제 수동 조작에서 사용자는 &amp;ldquo;무작위&amp;quot;로 편집하는 것이 아니라 시스템의 설정이나 소프트웨어의 동작과 직접 관련된 부분을 의도적으로(튜토리얼 사이트 등을 보면서) 조작하기 때문에, 중요 키를 건드릴 확률은 위의 이론값보다 훨씬 높아집니다.&lt;/p>
&lt;h2 id="7-레지스트리-가상화와-wow64-아키텍처">7. 레지스트리 가상화와 WOW64 아키텍처
&lt;/h2>&lt;p>Windows는 레거시 애플리케이션의 호환성을 유지하기 위해 레지스트리 액세스에 대해 몇 가지 고도의 &amp;ldquo;가상화(리디렉션)&amp;rdquo; 메커니즘을 구현하고 있습니다. 이를 이해하지 않고 프로그래밍을 수행하면 심각한 버그를 유발하는 원인이 됩니다.&lt;/p>
&lt;h3 id="uac-레지스트리-가상화-registry-virtualization">UAC 레지스트리 가상화 (Registry Virtualization)
&lt;/h3>&lt;p>Windows Vista 이후 사용자 계정 컨트롤(UAC)이 도입되었습니다. Windows XP 시대에 만들어진 오래된 애플리케이션(표준 사용자 권한으로 동작)이 본래 관리자 권한이 필요한 &lt;code>HKLM\SOFTWARE&lt;/code> 등의 보호된 키에 쓰기를 시도할 경우, 액세스 거부(Access Denied) 오류로 크래시되는 것을 방지하기 위해 Windows는 그 쓰기 작업을 몰래 사용자 프로필 내의 가상 스토어 &lt;code>HKCU\Software\Classes\VirtualStore\MACHINE\SOFTWARE&lt;/code>로 리디렉션합니다. 읽기 시에도 원래 위치와 가상 스토어 양쪽을 병합하여 반환합니다. 이를 통해 애플리케이션은 오류를 감지하지 않고 정상적으로 동작을 계속할 수 있습니다.
단, 프로그래밍 방식으로 시스템 전체의 설정을 변경하는 도구를 개발할 경우에는 매니페스트 파일에 &lt;code>&amp;lt;requestedExecutionLevel level=&amp;quot;requireAdministrator&amp;quot; /&amp;gt;&lt;/code>를 지정하여 이 가상화를 비활성화해야 합니다.&lt;/p>
&lt;h3 id="wow64-windows-32-bit-on-windows-64-bit-의-리디렉션">WOW64 (Windows 32-bit on Windows 64-bit) 의 리디렉션
&lt;/h3>&lt;p>64비트 버전의 Windows(현재 주류)에서 기존 32비트 애플리케이션을 실행할 때, 32비트 앱이 64비트의 네이티브 시스템 설정을 잘못 덮어쓰거나 호환되지 않는 64비트 DLL을 로드하지 않도록 특정 레지스트리 키는 자동으로 분리 및 리디렉션됩니다.
예를 들어 32비트 앱이 &lt;code>HKLM\SOFTWARE\Vendor\App&lt;/code>에 액세스하려고 하면 OS는 투명하게 &lt;code>HKLM\SOFTWARE\WOW6432Node\Vendor\App&lt;/code>으로 리디렉션합니다.&lt;/p>
&lt;pre class="mermaid">
flowchart TD
App32[&amp;#34;32비트 애플리케이션&amp;#34;]
App64[&amp;#34;64비트 애플리케이션&amp;#34;]
RegAPI[&amp;#34;레지스트리 API (Advapi32.dll)&amp;#34;]
CM[&amp;#34;Configuration Manager (커널)&amp;#34;]
HKLM_Soft[&amp;#34;HKLM\SOFTWARE&amp;#34;]
HKLM_WOW64[&amp;#34;HKLM\SOFTWARE\WOW6432Node&amp;#34;]
App32 --&amp;gt;| RegOpenKeyEx() | RegAPI
App64 --&amp;gt;| RegOpenKeyEx() | RegAPI
RegAPI --&amp;gt; CM
CM --&amp;gt;| 64비트 프로세스인 경우 | HKLM_Soft
CM --&amp;gt;| 32비트 프로세스인 경우 (리디렉션) | HKLM_WOW64
&lt;/pre>
&lt;p>PowerShell 스크립트나 C# 애플리케이션에서 레지스트리를 편집할 때는 실행 중인 프로세스 자체가 32비트인지 64비트인지를 강하게 의식해야 합니다. 그렇지 않으면 &amp;ldquo;분명히 쓴 설정이 탐색기에서 보이지 않는다(다른 곳에 쓰여 있다)&amp;ldquo;는 성가신 문제를 일으키게 됩니다.&lt;/p>
&lt;h2 id="8-powershell을-통한-프로그래밍-가능한-안전한-편집">8. PowerShell을 통한 프로그래밍 가능한 안전한 편집
&lt;/h2>&lt;p>레지스트리를 수동으로 편집하는 위험을 최소화하려면 PowerShell 스크립트를 사용하여 조작을 코드화(Infrastructure as Code)하고 자동화, 재현성, 테스트 가능성을 확보하는 것이 현대의 모범 사례입니다. PowerShell은 &amp;ldquo;Registry Provider&amp;quot;를 갖추고 있어 파일 시스템(C: 드라이브 등)을 조작하는 것과 완전히 동일한 cmdlet(&lt;code>Get-ChildItem&lt;/code>, &lt;code>Get-ItemProperty&lt;/code>, &lt;code>New-Item&lt;/code> 등)으로 레지스트리를 투명하게 조작할 수 있습니다.&lt;/p>
&lt;p>PowerShell에서는 기본적으로 &lt;code>HKLM:&lt;/code>이나 &lt;code>HKCU:&lt;/code>라는 전용 PSDrive(드라이브 문자 같은 것)가 마운트되어 있습니다.&lt;/p>
&lt;h3 id="기본적인-crud-조작">기본적인 CRUD 조작
&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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># 1. 존재 확인 (Read)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$keyPath&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;HKCU:\Software\MyCustomApp&amp;#34;&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">-Not&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nb">Test-Path&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="nv">$keyPath&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="c"># 2. 새로운 키 생성 (Create)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">New-Item&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="s2">&amp;#34;HKCU:\Software&amp;#34;&lt;/span> &lt;span class="n">-Name&lt;/span> &lt;span class="s2">&amp;#34;MyCustomApp&amp;#34;&lt;/span> &lt;span class="n">-Force&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="nb">Out-Null&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">Write-Host&lt;/span> &lt;span class="s2">&amp;#34;키를 생성했습니다.&amp;#34;&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="c"># 3. 값 쓰기/업데이트 (Update) - REG_DWORD로 1을 쓰기&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">Set-ItemProperty&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="nv">$keyPath&lt;/span> &lt;span class="n">-Name&lt;/span> &lt;span class="s2">&amp;#34;EnableDebug&amp;#34;&lt;/span> &lt;span class="n">-Value&lt;/span> &lt;span class="mf">1&lt;/span> &lt;span class="n">-Type&lt;/span> &lt;span class="n">DWord&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="c"># 4. 값 읽기 (Read)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$debugFlag&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nb">Get-ItemProperty&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="nv">$keyPath&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="py">EnableDebug&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">Write-Host&lt;/span> &lt;span class="s2">&amp;#34;현재 디버그 플래그: &lt;/span>&lt;span class="nv">$debugFlag&lt;/span>&lt;span class="s2">&amp;#34;&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="c"># 5. 값 삭제 (Delete)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">Remove-ItemProperty&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="nv">$keyPath&lt;/span> &lt;span class="n">-Name&lt;/span> &lt;span class="s2">&amp;#34;EnableDebug&amp;#34;&lt;/span> &lt;span class="n">-Force&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="실전-예제-1-개발-환경-자동-설정-환경-변수-path-추가">실전 예제 1: 개발 환경 자동 설정 (환경 변수 PATH 추가)
&lt;/h3>&lt;p>다음 스크립트는 개발자가 새로운 Windows 머신을 설정할 때 사용자 환경 변수 &lt;code>PATH&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;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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$envKey&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;HKCU:\Environment&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$newPath&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;C:\tools\bin&amp;#34;&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="c"># 현재 PATH 읽기 (오류를 억제하여 안전하게 가져옴)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$currentPathInfo&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nb">Get-ItemProperty&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="nv">$envKey&lt;/span> &lt;span class="n">-Name&lt;/span> &lt;span class="s2">&amp;#34;Path&amp;#34;&lt;/span> &lt;span class="n">-ErrorAction&lt;/span> &lt;span class="n">SilentlyContinue&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$currentPath&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nv">$currentPathInfo&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nv">$currentPathInfo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="py">Path&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&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="c"># 이미 포함되어 있는지 정규 표현식으로 확인&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="nv">$currentPath&lt;/span> &lt;span class="o">-notmatch&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="no">regex&lt;/span>&lt;span class="p">]::&lt;/span>&lt;span class="n">Escape&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nv">$newPath&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="c"># 끝에 세미콜론이 없으면 추가하여 결합&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="nv">$currentPath&lt;/span> &lt;span class="o">-and&lt;/span> &lt;span class="nv">$currentPath&lt;/span> &lt;span class="o">-notmatch&lt;/span> &lt;span class="s2">&amp;#34;;$&amp;#34;&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="nv">$currentPath&lt;/span> &lt;span class="p">+=&lt;/span> &lt;span class="s2">&amp;#34;;&amp;#34;&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="nv">$updatedPath&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nv">$currentPath&lt;/span> &lt;span class="p">+&lt;/span> &lt;span class="nv">$newPath&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="c"># REG_EXPAND_SZ 형식으로 쓰기 (중요)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">Set-ItemProperty&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="nv">$envKey&lt;/span> &lt;span class="n">-Name&lt;/span> &lt;span class="s2">&amp;#34;Path&amp;#34;&lt;/span> &lt;span class="n">-Value&lt;/span> &lt;span class="nv">$updatedPath&lt;/span> &lt;span class="n">-Type&lt;/span> &lt;span class="n">ExpandString&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">Write-Host&lt;/span> &lt;span class="s2">&amp;#34;PATH 환경 변수를 업데이트했습니다: &lt;/span>&lt;span class="nv">$newPath&lt;/span>&lt;span class="s2">&amp;#34;&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="c"># 실행 중인 프로세스에 환경 변수 변경을 알림 (WM_SETTINGCHANGE)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c"># 이를 통해 재부팅 없이 새로운 탐색기 등에 반영됨&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="no">Environment&lt;/span>&lt;span class="p">]::&lt;/span>&lt;span class="n">SetEnvironmentVariable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Path&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nv">$updatedPath&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="no">EnvironmentVariableTarget&lt;/span>&lt;span class="p">]::&lt;/span>&lt;span class="n">User&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">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">Write-Host&lt;/span> &lt;span class="s2">&amp;#34;PATH가 이미 추가되어 있습니다.&amp;#34;&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="실전-예제-2-상황에-맞는-메뉴context-menu에-사용자-지정-작업-추가">실전 예제 2: 상황에 맞는 메뉴(Context Menu)에 사용자 지정 작업 추가
&lt;/h3>&lt;p>특정 파일이나 디렉토리를 마우스 오른쪽 버튼으로 클릭했을 때의 상황에 맞는 메뉴에 &amp;ldquo;My IDE로 열기&amp;quot;라는 고유 항목을 추가하는 스크립트입니다.&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;span class="lnt">23
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># 디렉토리의 배경(여백 부분)을 마우스 오른쪽 버튼으로 클릭했을 때의 메뉴&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$menuPath&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;HKCR:\Directory\Background\shell\OpenWithMyIDE&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$commandPath&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$menuPath&lt;/span>&lt;span class="s2">\command&amp;#34;&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="c"># 메뉴 항목의 부모 키 생성&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">New-Item&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="nv">$menuPath&lt;/span> &lt;span class="n">-Force&lt;/span> &lt;span class="n">-ErrorAction&lt;/span> &lt;span class="n">Stop&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="nb">Out-Null&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="c"># (default) 값에 표시 이름 설정&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">Set-ItemProperty&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="nv">$menuPath&lt;/span> &lt;span class="n">-Name&lt;/span> &lt;span class="s2">&amp;#34;(default)&amp;#34;&lt;/span> &lt;span class="n">-Value&lt;/span> &lt;span class="s2">&amp;#34;My IDE로 열기&amp;#34;&lt;/span> &lt;span class="n">-Type&lt;/span> &lt;span class="n">String&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="c"># 아이콘 설정 (선택 사항)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">Set-ItemProperty&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="nv">$menuPath&lt;/span> &lt;span class="n">-Name&lt;/span> &lt;span class="s2">&amp;#34;Icon&amp;#34;&lt;/span> &lt;span class="n">-Value&lt;/span> &lt;span class="s2">&amp;#34;C:\Program Files\MyIDE\ide.exe,0&amp;#34;&lt;/span> &lt;span class="n">-Type&lt;/span> &lt;span class="n">String&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="c"># command 서브 키를 생성하고, 실행될 명령줄을 설정&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c"># %V 는 현재 디렉토리 경로로 확장되는 변수&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">New-Item&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="nv">$commandPath&lt;/span> &lt;span class="n">-Force&lt;/span> &lt;span class="n">-ErrorAction&lt;/span> &lt;span class="n">Stop&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="nb">Out-Null&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">Set-ItemProperty&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="nv">$commandPath&lt;/span> &lt;span class="n">-Name&lt;/span> &lt;span class="s2">&amp;#34;(default)&amp;#34;&lt;/span> &lt;span class="n">-Value&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">`&amp;#34;&lt;/span>&lt;span class="s2">C:\Program Files\MyIDE\ide.exe&lt;/span>&lt;span class="se">`&amp;#34;&lt;/span>&lt;span class="s2"> &lt;/span>&lt;span class="se">`&amp;#34;&lt;/span>&lt;span class="s2">%V&lt;/span>&lt;span class="se">`&amp;#34;&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="n">-Type&lt;/span> &lt;span class="n">String&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="nb">Write-Host&lt;/span> &lt;span class="s2">&amp;#34;상황에 맞는 메뉴를 추가했습니다.&amp;#34;&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>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">Write-Error&lt;/span> &lt;span class="s2">&amp;#34;레지스트리 변경에 실패했습니다. 관리자 권한으로 실행 중인지 확인하세요. 오류: &lt;/span>&lt;span class="nv">$_&lt;/span>&lt;span class="s2">&amp;#34;&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="powershell에서의-레지스트리-액세스-내부-시퀀스">PowerShell에서의 레지스트리 액세스 내부 시퀀스
&lt;/h3>&lt;p>PowerShell 스크립트가 레지스트리를 변경할 때의 OS 내부 동작 시퀀스를 아래에 나타냅니다.&lt;/p>
&lt;pre class="mermaid">
sequenceDiagram
participant U as &amp;#34;관리자 / PowerShell&amp;#34;
participant PS as &amp;#34;Registry Provider (.NET)&amp;#34;
participant CM as &amp;#34;Configuration Manager (커널)&amp;#34;
participant Disk as &amp;#34;NTFS의 하이브 파일&amp;#34;
U-&amp;gt;&amp;gt;PS: &amp;#34;Set-ItemProperty -Path ... -Value ...&amp;#34;
PS-&amp;gt;&amp;gt;PS: &amp;#34;경로 파싱 및 데이터 형식 검증&amp;#34;
PS-&amp;gt;&amp;gt;CM: &amp;#34;NtSetValueKey (시스템 호출)&amp;#34;
CM-&amp;gt;&amp;gt;CM: &amp;#34;키 ACL에 대해 액세스 토큰 확인&amp;#34;
alt &amp;#34;액세스 허용 (관리자 토큰)&amp;#34;
CM-&amp;gt;&amp;gt;CM: &amp;#34;휘발성 캐시에 셀 할당&amp;#34;
CM-&amp;gt;&amp;gt;Disk: &amp;#34;트랜잭션 로그(.log1)에 플러시&amp;#34;
Disk--&amp;gt;&amp;gt;CM: &amp;#34;로그 쓰기 성공&amp;#34;
CM-&amp;gt;&amp;gt;Disk: &amp;#34;하이브 데이터 파일에 지연 쓰기 (백그라운드)&amp;#34;
CM--&amp;gt;&amp;gt;PS: &amp;#34;STATUS_SUCCESS (0x00000000)&amp;#34;
PS--&amp;gt;&amp;gt;U: &amp;#34;명령 완료&amp;#34;
else &amp;#34;액세스 거부 (표준 사용자)&amp;#34;
CM--&amp;gt;&amp;gt;PS: &amp;#34;STATUS_ACCESS_DENIED (0xC0000022)&amp;#34;
PS--&amp;gt;&amp;gt;U: &amp;#34;UnauthorizedAccessException 발생&amp;#34;
end
&lt;/pre>
&lt;h2 id="9-c-net을-통한-견고한-레지스트리-액세스">9. C# (.NET)을 통한 견고한 레지스트리 액세스
&lt;/h2>&lt;p>.NET 애플리케이션(C# 등)에서 레지스트리에 액세스할 경우 &lt;code>Microsoft.Win32.Registry&lt;/code> 클래스 및 &lt;code>RegistryKey&lt;/code> 클래스를 사용합니다.
C#을 이용하는 가장 큰 장점은 강력한 예외 처리(&lt;code>try-catch&lt;/code>)에 의한 견고한 오류 처리, 엄격한 타입 검사, 그리고 &lt;code>RegistryView&lt;/code> 열거형을 사용한 명시적인 32비트/64비트 뷰 지정이 가능하다는 점입니다.&lt;/p>
&lt;p>아래는 64비트 OS 환경에서 확실하게 64비트 측의 레지스트리(WOW6432Node의 리디렉션을 회피)를 읽고 쓰는 C# 코드 예제입니다.&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;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;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-csharp" data-lang="csharp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="nn">System&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="nn">System.Security&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="nn">Microsoft.Win32&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="k">class&lt;/span> &lt;span class="nc">RegistryEditor&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="kd">static&lt;/span> &lt;span class="k">void&lt;/span> &lt;span class="n">Main&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="c1">// HKLM 아래의 경로 (관리자 권한 필요)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">string&lt;/span> &lt;span class="n">keyPath&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s">@&amp;#34;SOFTWARE\MyEnterpriseApp\Settings&amp;#34;&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">// RegistryView.Registry64 를 지정하여 64비트 네이티브 뷰 열기&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// using 문을 사용하여 레지스트리 키의 핸들(비관리 리소스)을 확실하게 Dispose함&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&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="k">using&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">RegistryKey&lt;/span> &lt;span class="n">baseKey&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">RegistryKey&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">OpenBaseKey&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">RegistryHive&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">LocalMachine&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">RegistryView&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Registry64&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="c1">// 쓰기 권限 (writable: true) 으로 키를 엽니다. 존재하지 않으면 생성.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">using&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">RegistryKey&lt;/span> &lt;span class="n">subKey&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">baseKey&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">CreateSubKey&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">keyPath&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">writable&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&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="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">subKey&lt;/span> &lt;span class="p">!=&lt;/span> &lt;span class="kc">null&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="c1">// REG_DWORD 로 값 쓰기&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">subKey&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SetValue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;MaxConnections&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">100&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">RegistryValueKind&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DWord&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">// REG_SZ 로 값 쓰기&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">subKey&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SetValue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;ApiEndpoint&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;https://api.example.com&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">RegistryValueKind&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">String&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">// REG_BINARY 로 바이트 배열 쓰기&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">byte&lt;/span>&lt;span class="p">[]&lt;/span> &lt;span class="n">secretData&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="m">0x01&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">0x02&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">0x0A&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="m">0xFF&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">subKey&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SetValue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;BinarySecret&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">secretData&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">RegistryValueKind&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Binary&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">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;레지스트리 쓰기에 성공했습니다.&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;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;span class="line">&lt;span class="cl"> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">UnauthorizedAccessException&lt;/span> &lt;span class="n">ex&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="c1">// 관리자 권한으로 실행하지 않았을 때 자주 발생&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">$&amp;#34;권한 오류: 프로그램을 &amp;#39;관리자 권한으로 실행&amp;#39;해 주세요. 상세: {ex.Message}&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="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">SecurityException&lt;/span> &lt;span class="n">ex&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="c1">// .NET의 코드 액세스 보안(CAS)에 의해 차단된 경우&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">$&amp;#34;보안 예외: {ex.Message}&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="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">Exception&lt;/span> &lt;span class="n">ex&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="c1">// 기타 예기치 않은 IO 오류 등&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">$&amp;#34;예기치 않은 오류: {ex.Message}&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;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>레지스트리의 키를 열었을 때 OS로부터 반환되는 &amp;ldquo;핸들&amp;quot;은 메모리와 시스템 리소스를 소비하는 비관리 리소스입니다. 따라서 &lt;code>using&lt;/code> 블록을 사용하거나 &lt;code>finally&lt;/code> 블록 내에서 명시적으로 &lt;code>.Dispose()&lt;/code> (또는 &lt;code>.Close()&lt;/code>)를 호출하여 핸들 누수를 확실하게 방지하는 것이 C# 프로그래밍의 철칙입니다.&lt;/p>
&lt;h2 id="10-레지스트리-백업-및-복원-방법">10. 레지스트리 백업 및 복원 방법
&lt;/h2>&lt;p>스크립트나 프로그램에 의한 자동화라 하더라도 중요한 변경을 가하기 전에는 백업을 확보하는 것이 절대적으로 필수적입니다.&lt;/p>
&lt;h3 id="reg-파일을-통한-백업-및-가져오기">.reg 파일을 통한 백업 및 가져오기
&lt;/h3>&lt;p>가장 고전적이고 범용적인 방법은 &lt;code>.reg&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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Windows Registry Editor Version 5.00
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[HKEY_CURRENT_USER\Software\MyCustomApp]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;EnableDebug&amp;#34;=dword:00000001
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;ApiEndpoint&amp;#34;=&amp;#34;https://api.example.com&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;BinaryData&amp;#34;=hex:01,02,0a,ff
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;em>참고: 바이너리 데이터는 &lt;code>hex:&lt;/code>에 이어지는 쉼표로 구분된 16진수로 표현됩니다.&lt;/em>&lt;/p>
&lt;p>명령줄 도구 &lt;code>reg.exe&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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cmd" data-lang="cmd">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">REM 지정한 키를 백업 (서브 키도 재귀적으로 내보내짐)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">reg export HKLM\SOFTWARE\MyEnterpriseApp C:\backup\myapp_backup.reg /y
&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">REM 백업 복원&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">reg import C:\backup\myapp_backup.reg
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="powershell을-이용한-더-고급-백업-방법">PowerShell을 이용한 더 고급 백업 방법
&lt;/h3>&lt;p>단순한 텍스트로서가 아니라 PowerShell의 객체 지향성을 살려 레지스트리의 객체를 내보내고 XML 형식(CliXML)으로 저장하는 것도 가능합니다. 이를 통해 복원 시 문자열 파싱에 의존하지 않고 형식 정보를 유지한 채 처리할 수 있습니다.&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-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># 백업 확보 (속성을 XML로 저장)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">Get-ItemProperty&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="s2">&amp;#34;HKCU:\Software\MyCustomApp&amp;#34;&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="nb">Export-Clixml&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="s2">&amp;#34;C:\backup\reg_backup.xml&amp;#34;&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="c"># 복원의 개념&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">$backup&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nb">Import-Clixml&lt;/span> &lt;span class="n">-Path&lt;/span> &lt;span class="s2">&amp;#34;C:\backup\reg_backup.xml&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># $backup 에는 복원된 사용자 지정 PSObject가 저장되어 있으므로,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># 그 속성을 순회하여 Set-ItemProperty 로 다시 적용하는 로직을 구성할 수 있습니다.&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="11-sysinternals-process-monitor-procmon을-활용한-문제-해결">11. Sysinternals Process Monitor (Procmon)을 활용한 문제 해결
&lt;/h2>&lt;p>프로그램이 레지스트리의 어디에 쓰고 있는지 알 수 없는 경우나 &amp;ldquo;Access Denied&amp;quot;의 원인을 찾을 때, Microsoft가 무료로 제공하는 Sysinternals 도구인 &lt;strong>Process Monitor (Procmon)&lt;/strong> 가 매우 강력합니다.
Procmon을 사용하면 OS 상에서 발생하는 모든 레지스트리 API 호출(&lt;code>RegOpenKey&lt;/code>, &lt;code>RegQueryValue&lt;/code>, &lt;code>RegSetValue&lt;/code> 등)을 실시간으로 캡처하고 다음과 같은 고급 필터링으로 문제 해결이 가능합니다.&lt;/p>
&lt;ul>
&lt;li>&lt;code>Process Name&lt;/code> is &lt;code>powershell.exe&lt;/code>&lt;/li>
&lt;li>&lt;code>Operation&lt;/code> begins with &lt;code>Reg&lt;/code>&lt;/li>
&lt;li>&lt;code>Result&lt;/code> is &lt;code>ACCESS DENIED&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>이를 통해 어느 키의 ACL 설정이 부족한지, 혹은 WOW6432Node로 잘못 리디렉션되고 있는지를 순식간에 특정할 수 있습니다.&lt;/p>
&lt;h2 id="12-보안과-모범-사례">12. 보안과 모범 사례
&lt;/h2>&lt;p>마지막으로 레지스트리를 다룰 때의 중요한 설계 원칙과 모범 사례를 정리합니다.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>최소 권한의 원칙을 철저히 한다&lt;/strong>: 애플리케이션이나 스크립트의 설정은 가능한 한 &lt;code>HKCU&lt;/code>(현재 사용자) 내의 &lt;code>Software&lt;/code> 키 아래에 저장해야 합니다. &lt;code>HKLM&lt;/code>에 대한 쓰기는 UAC에 의한 관리자 권한 상승을 요구하므로 보안상의 공격 표면(Attack Surface)을 넓히고 사용자 경험을 손상시킵니다.&lt;/li>
&lt;li>&lt;strong>감사 (Auditing) 활성화&lt;/strong>: 보안상 매우 중요한 키(예: 자동 시작을 관장하는 &lt;code>Run&lt;/code> 키나 서비스 설정 키 등)에 대해서는 SACL(System Access Control List)을 구성하여, 누가 언제 값을 변경하거나 삭제했는지를 Windows 이벤트 뷰어의 &amp;ldquo;보안 로그&amp;quot;에 기록(감사)하도록 설정합니다.&lt;/li>
&lt;li>&lt;strong>트랜잭션 기능의 사용 중단에 대응&lt;/strong>: 과거 Windows Vista에서 도입된 &amp;ldquo;커널 트랜잭션 관리자(KTM)&amp;ldquo;를 이용한 레지스트리 트랜잭션 기능(TxR)은 Windows 10 이후 더 이상 사용되지 않습니다(Deprecated). 애플리케이션 측에서 독자적으로 백업 및 롤백 메커니즘(변경 전에 원래 값을 읽어 메모리에 보관해 두는 등)을 구현해야 합니다.&lt;/li>
&lt;li>&lt;strong>그룹 정책 (GPO)과의 충돌 주의&lt;/strong>: &lt;code>HKLM\SOFTWARE\Policies&lt;/code>나 &lt;code>HKCU\Software\Policies&lt;/code> 영역은 Active Directory의 그룹 정책에 의해 중앙 집중식으로 관리되어야 하는 영역입니다. 스크립트에서 직접 이러한 키를 수정하더라도, 다음번 그룹 정책 백그라운드 업데이트 주기(일반적으로 90분~120분 간격)에 도메인 컨트롤러의 설정에 의해 강제로 덮어씌워지므로 설정이 영구적으로 유지되지 않는 원인이 됩니다.&lt;/li>
&lt;/ol>
&lt;h2 id="요약">요약
&lt;/h2>&lt;p>Windows 레지스트리는 OS의 모든 동작과 애플리케이션 설정을 통합적으로 관리하는 강력하고 복잡한 기반 시스템입니다. 수동에 의한 무질서한 편집에는 수학적으로도 입증되는 높은 시스템 손상 위험이 따릅니다. 따라서 PowerShell이나 C# 등의 프로그래밍 가능한 수단을 사용하여 Infrastructure as Code의 원칙에 따라 안전하고 테스트 가능하며 재현성 있는 형태로 구성 관리를 수행하는 것이 현대의 시스템 관리와 개발에 있어 필수적입니다. 본 기사에서 해설한 깊은 아키텍처 이해와 구현 패턴을 활용하여 더욱 견고하고 안전한 Windows 환경 구축을 목표로 해 보세요.&lt;/p></description></item></channel></rss>