<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>NOYECUBE</title>
    <link>https://noyecube.tistory.com/</link>
    <description>컴퓨터소프트웨어(프로그래밍, 시스템트레이딩 등)</description>
    <language>ko</language>
    <pubDate>Tue, 7 Jul 2026 10:01:43 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>noyecube</managingEditor>
    <item>
      <title>[MFC] Parquet 파일 생성(Apache Arrow 기반)</title>
      <link>https://noyecube.tistory.com/entry/MFC-Apache-Arrow-%EA%B8%B0%EB%B0%98-CSV-Parquet-%ED%8C%8C%EC%9D%BC-%EC%9D%BD%EA%B3%A0-%EC%93%B0%EA%B8%B0-zstd-%EC%95%95%EC%B6%95-%ED%8F%AC%ED%95%A8</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번&amp;nbsp;포스팅에서는&amp;nbsp;Apache&amp;nbsp;Arrow를&amp;nbsp;정적&amp;nbsp;라이브러리&amp;nbsp;형태로&amp;nbsp;MFC&amp;nbsp;프로젝트에&amp;nbsp;연동하여&amp;nbsp;CSV&amp;nbsp;파일을&amp;nbsp;읽고,&amp;nbsp;이를&amp;nbsp;Parquet&amp;nbsp;파일로&amp;nbsp;zstd&amp;nbsp;압축을&amp;nbsp;통해&amp;nbsp;저장하는&amp;nbsp;구체적인&amp;nbsp;예제를&amp;nbsp;제공합니다.&amp;nbsp;반대로,&amp;nbsp;압축된&amp;nbsp;Parquet&amp;nbsp;파일을&amp;nbsp;다시&amp;nbsp;읽어오는&amp;nbsp;예제까지&amp;nbsp;실습하여&amp;nbsp;데이터를&amp;nbsp;효율적으로&amp;nbsp;처리하는&amp;nbsp;방법을&amp;nbsp;단계별로&amp;nbsp;안내드릴&amp;nbsp;예정입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Apache Arrow란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apache Arrow는 컬럼 기반 메모리 데이터 표현을 표준화한 오픈소스 프로젝트로, 데이터를 효율적으로 처리하고 다양한 언어 간 빠른 데이터 교환을 지원합니다. 데이터 분석 성능을 크게 향상시키며, 특히 데이터가 많은 환경에서 빛을 발하는 라이브러리입니다.&lt;br /&gt;Apache&amp;nbsp;Arrow는&amp;nbsp;Parquet,&amp;nbsp;CSV&amp;nbsp;등&amp;nbsp;여러&amp;nbsp;데이터&amp;nbsp;형식의&amp;nbsp;파일을&amp;nbsp;효과적으로&amp;nbsp;다룰&amp;nbsp;수&amp;nbsp;있도록&amp;nbsp;설계되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Arrow는 내부적으로 다음과 같은 주요 패키지 및 외부 의존성을 포함하고 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Parquet:&amp;nbsp;컬럼&amp;nbsp;기반의&amp;nbsp;파일&amp;nbsp;형식으로,&amp;nbsp;압축률이&amp;nbsp;뛰어나고&amp;nbsp;쿼리&amp;nbsp;성능이&amp;nbsp;우수합니다.&lt;/li&gt;
&lt;li&gt;zstd 압축 라이브러리: Parquet 파일 저장 시 데이터를 zstd 포맷으로 압축하여 저장하면 더 작은 파일 크기와 빠른 데이터 접근 속도를 보장합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 내용은 다음 경로에서 확인 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;arrow&quot; href=&quot;https://arrow.apache.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://arrow.apache.org/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1744693835873&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Apache Arrow&quot; data-og-description=&quot;The universal columnar format and multi-language toolbox for fast data interchange and in-memory analytics&quot; data-og-host=&quot;arrow.apache.org&quot; data-og-source-url=&quot;https://arrow.apache.org/&quot; data-og-url=&quot;https://arrow.apache.org/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bCBFbN/hyYChv8pzL/XxEd6Ut899rPKRmv4pCtUK/img.png?width=1800&amp;amp;height=936&amp;amp;face=0_0_1800_936,https://scrap.kakaocdn.net/dn/d3LLJY/hyYFA8Gda8/KQBEfYHYkvfgAMQ78iHdTk/img.png?width=1800&amp;amp;height=936&amp;amp;face=0_0_1800_936,https://scrap.kakaocdn.net/dn/cf830k/hyYCk7pYL0/oTLR9aRnsed36jVY9C6CK1/img.png?width=1978&amp;amp;height=723&amp;amp;face=0_0_1978_723&quot;&gt;&lt;a href=&quot;https://arrow.apache.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://arrow.apache.org/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bCBFbN/hyYChv8pzL/XxEd6Ut899rPKRmv4pCtUK/img.png?width=1800&amp;amp;height=936&amp;amp;face=0_0_1800_936,https://scrap.kakaocdn.net/dn/d3LLJY/hyYFA8Gda8/KQBEfYHYkvfgAMQ78iHdTk/img.png?width=1800&amp;amp;height=936&amp;amp;face=0_0_1800_936,https://scrap.kakaocdn.net/dn/cf830k/hyYCk7pYL0/oTLR9aRnsed36jVY9C6CK1/img.png?width=1978&amp;amp;height=723&amp;amp;face=0_0_1978_723');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Apache Arrow&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The universal columnar format and multi-language toolbox for fast data interchange and in-memory analytics&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;arrow.apache.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. What&amp;nbsp;is&amp;nbsp;Parquet?&amp;nbsp;What&amp;nbsp;are&amp;nbsp;its&amp;nbsp;differences&amp;nbsp;compared&amp;nbsp;to&amp;nbsp;the&amp;nbsp;CSV&amp;nbsp;format?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Parquet는&amp;nbsp;컬럼&amp;nbsp;기반의&amp;nbsp;저장&amp;nbsp;포맷으로,&amp;nbsp;빅데이터&amp;nbsp;환경에서&amp;nbsp;특히&amp;nbsp;많이&amp;nbsp;사용되는&amp;nbsp;데이터&amp;nbsp;파일&amp;nbsp;형식입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Parquet&amp;nbsp;파일은&amp;nbsp;데이터를&amp;nbsp;컬럼&amp;nbsp;단위로&amp;nbsp;저장하기&amp;nbsp;때문에&amp;nbsp;열&amp;nbsp;기반&amp;nbsp;분석&amp;nbsp;및&amp;nbsp;압축&amp;nbsp;효율성이&amp;nbsp;뛰어나며,&amp;nbsp;쿼리&amp;nbsp;속도&amp;nbsp;또한&amp;nbsp;매우&amp;nbsp;빠릅니다. &lt;br /&gt;반면, CSV(Comma-Separated Values) 파일은 행 기반 저장 방식으로 데이터를 단순히 쉼표로 구분하여 저장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSV는 단순하고 직관적이지만, 대규모 데이터 처리에서는 성능 저하가 발생할 수 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.5426%; text-align: center;&quot;&gt;특징&lt;/td&gt;
&lt;td style=&quot;width: 38.1007%; text-align: center;&quot;&gt;Parquet&lt;/td&gt;
&lt;td style=&quot;width: 41.3566%; text-align: center;&quot;&gt;CSV&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.5426%; text-align: center;&quot;&gt;데이터 저장 방식&lt;/td&gt;
&lt;td style=&quot;width: 38.1007%; text-align: center;&quot;&gt;컬럼(columnar) 기반 저장&lt;/td&gt;
&lt;td style=&quot;width: 41.3566%; text-align: center;&quot;&gt;행(row) 기반 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.5426%; text-align: center;&quot;&gt;성능 및 속도&lt;/td&gt;
&lt;td style=&quot;width: 38.1007%; text-align: center;&quot;&gt;빠른 쿼리 속도, 높은 압축률&lt;/td&gt;
&lt;td style=&quot;width: 41.3566%; text-align: center;&quot;&gt;느린 쿼리 속도, 낮은 압축률&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.5426%; text-align: center;&quot;&gt;용도&lt;/td&gt;
&lt;td style=&quot;width: 38.1007%; text-align: center;&quot;&gt;빅데이터, 분석 작업에 최적화된 환경에 추천&lt;/td&gt;
&lt;td style=&quot;width: 41.3566%; text-align: center;&quot;&gt;작고 단순한 데이터에 적합&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Building Arrow C++&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;윈도우기반 C++용 Arrow를 빌드해봅니다.&amp;nbsp;빌드에 대한 자세한 내용은 다음의 링크에서 확인하실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;윈도우에서 Arrow 라이브러리를 사용하는 방법은 크게 vcpkg를 이용하여 참조하는 방법과 cmake로 컴파일하여 프로젝트에 라이브러리 파일을 포함하는 방법이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 cmake 컴파일 방식을 3일넘게 시도하다가 포기하고 vcpkg로 install 후 integrate install을 하였습니다. 의존성 걸린 모듈이 여러개 있는데 모든 모듈을 MT_StaticLibrary 방식으로 빌드되도록 /MT 옵션을 주어도 arrow가 자체 빌드하는 몇몇 모듈들은 /MT 적용이 안되고 /MD로만 빌드가 되어 직접 빌드하는 것은 포기하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;Arrow C++&quot; href=&quot;https://arrow.apache.org/docs/developers/cpp/building.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://arrow.apache.org/docs/developers/cpp/building.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1744694942247&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Building Arrow C++ &amp;mdash; Apache Arrow v19.0.1&quot; data-og-description=&quot;By default, the C++ build system creates a fairly minimal build. We have several optional system components which you can opt into building by passing boolean flags to cmake. Some features of the core Arrow shared library can be switched off for improved b&quot; data-og-host=&quot;arrow.apache.org&quot; data-og-source-url=&quot;https://arrow.apache.org/docs/developers/cpp/building.html&quot; data-og-url=&quot;https://arrow.apache.org/docs/developers/cpp/building.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://arrow.apache.org/docs/developers/cpp/building.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://arrow.apache.org/docs/developers/cpp/building.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Building Arrow C++ &amp;mdash; Apache Arrow v19.0.1&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;By default, the C++ build system creates a fairly minimal build. We have several optional system components which you can opt into building by passing boolean flags to cmake. Some features of the core Arrow shared library can be switched off for improved b&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;arrow.apache.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.1. System setup&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Arrow 소스코드 내의 예제파일 등을 참조하실 분은 다음과 같이 git에서 arrow 소스코드를 받아보시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745065062891&quot; class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;D:\git&amp;gt;git clone https://github.com/apache/arrow.git
Cloning into 'arrow'...
remote: Enumerating objects: 260054, done.
remote: Counting objects: 100% (96/96), done.
remote: Compressing objects: 100% (66/66), done.
remote: Total 260054 (delta 57), reused 32 (delta 30), pack-reused 259958 (from 2)
Receiving objects: 100% (260054/260054), 203.69 MiB | 10.84 MiB/s, done.
Resolving deltas: 100% (183999/183999), done.
Updating files: 100% (5677/5677), done.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Arrow를 빌드하려면 의존성 걸려있는 패키지 라이브러리들이 필요한데 윈도우에서는 vcpkg로 관리하는게 편하므로 vcpkg가 설치되어 있는지 확인합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745064581128&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// vcpkg 여부 확인
D:\&amp;gt;where vcpkg
H:\vcpkg\vcpkg.exe
D:\&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;없으면 vcpkg를 다운받고 설치스크립트를 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vcpkg가 visual studio 경로에 번들설치된게 있는데 저처럼 별도의 vcpkg를 사용하고 싶으면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PowerShell에서 환경변수 VCPKG_ROOT를 수정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1745201230035&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 환경변수 설정 예제(Powershell)
&amp;gt; SETX VCPKG_ROOT D:\vcpkg&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VS Prompt는 VCPKG_ROOT값이 Visual Stduio 경로로 덮어쓰기 되므로 CMD창 실행시 set 명령으로 VCPKG_ROOT를 수정하고 시작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 어떤 vcpkg를 참조하는지 확신이 안서면 vcpkg install 명령 실행시 다음과 같이 &quot; --vcpkg-root&quot; 옵션으로 vcpkg 경로를 입력해줍니다. 그렇지 않으면 다음과 같이 두 경로의 vcpkg 충돌 경고가 발생합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1745064902813&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;warning: The vcpkg H:\vcpkg\vcpkg.exe is using detected vcpkg root H:\vcpkg and ignoring mismatched VCPKG_ROOT environment value C:\Program Files\Microsoft Visual Studio\2022\Community\VC\vcpkg. To suppress this message, unset the environment variable or use the --vcpkg-root command line switch.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2. Building&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;윈도우 기본 CMD창이 아닌 Visual Studio Developer Command Prompt를 띄워서 작업해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Arrow 루트 경로의 cpp 폴더로 들어가서 폴더를 하나 생성하고 그 안에서 cmake 명령으로 비주얼스튜디오 프로젝트를 생성합니다. 이 때 사용할 컴포넌트에 대한 옵션을 켜줍니다. 저는 Parquet, CSV 를 켜고 압축기능도 전부 켰습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1744954444739&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cmake .. -G &quot;Visual Studio 17 2022&quot; -A x64 -DCMAKE_TOOLCHAIN_FILE=H:\vcpkg\scripts\buildsystems\vcpkg.cmake -DCMAKE_INSTALL_PREFIX=../install -DARROW_BUILD_SHARED=OFF -DARROW_BUILD_STATIC=ON -DARROW_USE_STATIC_CRT=ON -DARROW_DEPENDENCY_SOURCE=VCPKG -DARROW_BUILD_TESTS=OFF -DCMAKE_UNITY_BUILD=ON -DMSVC_LINK_VERBOSE=ON -DVCPKG_TARGET_TRIPLET=x64-windows-static -DARROW_DEPENDENCY_USE_SHARED=OFF -DARROW_JSON=ON -DARROW_FILESYSTEM=ON -DARROW_CSV=ON -DARROW_PARQUET=ON -DARROW_DATASET=ON -DARROW_WITH_SNAPPY=ON -DARROW_WITH_ZLIB=ON -DARROW_WITH_BZ2=ON -DARROW_WITH_BROTLI=ON -DARROW_WITH_LZ4=ON -DARROW_WITH_ZSTD=ON

// triplet을 x64-windows-static로 지정해도 x64-windows(shared)도 생성됩니다.
E:\git\arrow\cpp\build&amp;gt;cmake .. -G &quot;Visual Studio 17 2022&quot; -A x64 -DCMAKE_TOOLCHAIN_FILE=H:\vcpkg\scripts\buildsystems\vcpkg.cmake -DCMAKE_INSTALL_PREFIX=../install -DARROW_BUILD_SHARED=OFF -DARROW_BUILD_STATIC=ON -DARROW_USE_STATIC_CRT=ON -DARROW_DEPENDENCY_SOURCE=VCPKG -DARROW_BUILD_TESTS=OFF -DCMAKE_UNITY_BUILD=ON -DMSVC_LINK_VERBOSE=ON -DVCPKG_TARGET_TRIPLET=x64-windows-static -DARROW_DEPENDENCY_USE_SHARED=OFF -DARROW_JSON=ON -DARROW_FILESYSTEM=ON -DARROW_CSV=ON -DARROW_PARQUET=ON -DARROW_DATASET=ON -DARROW_WITH_SNAPPY=ON -DARROW_WITH_ZLIB=ON -DARROW_WITH_BZ2=ON -DARROW_WITH_BROTLI=ON -DARROW_WITH_LZ4=ON -DARROW_WITH_ZSTD=ON
-- Building using CMake version: 3.31.7
-- Using vcpkg to find dependencies
-- Using CMAKE_TOOLCHAIN_FILE: H:\vcpkg\scripts\buildsystems\vcpkg.cmake
-- Using VCPKG_ROOT: H:/vcpkg
-- vcpkg.json manifest found. Using VCPKG_MANIFEST_MODE: ON
-- Using vcpkg installed libraries directory:
-- Using VCPKG_TARGET_TRIPLET: x64-windows-static
-- Using VCPKG_BUILD_TYPE: release
-- Using VCPKG_LIBRARY_LINKAGE: static
-- Running vcpkg install
Detecting compiler hash for triplet x64-windows...
Compiler found: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.43.34808/bin/Hostx64/x64/cl.exe
Detecting compiler hash for triplet x64-windows-static...
Compiler found: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.43.34808/bin/Hostx64/x64/cl.exe
The following packages will be built and installed:&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 생성이 완료되었으면 Arrow 소스코드를 빌드하여 라이브러리 파일을 생성할 차례입니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1744954517084&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Debug 라이브러리 생성
cmake --build . --config Debug--target INSTALL

// Release 라이브러리 생성
cmake --build . --config Release --target INSTALL&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.3. Arrow Install&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vcpkg로 Arrow를 설치하려면 다음과 같이 하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vcpkg 공지사항에 따르면 visual studio를 설치할 당시에 번들로 설치한 vcpkg를 이용할 경우에만 vcpkg integrate install 명령으로 모든 visual studio가 자동으로 헤더파일과 라이브러리 링크 기능을 사용할 수있고 별도로 설치한 vcpkg는 헤더파일 경로와 라이브러리파일 경로를 수동으로 설정해줘야 한다고 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1745201614346&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt; git clone https://github.com/Microsoft/vcpkg.git
&amp;gt; cd vcpkg
&amp;gt; bootstrap-vcpkg.bat

// Visual studio 경로의 기본 vcpkg를 사용할 경우에만 유효한 명령
&amp;gt; vcpkg integrate install

// 기본적으로 triplet x64-windows 가 적용되며 동적빌드버전(MD_Dynamic)
&amp;gt; vcpkg install arrow

// triplet이 x64-windows-static 적용되며 MT_Static
&amp;gt; vcpkg install arrow:x64-windows-static&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vcpkg로 설치한 패키지들은 다음의 경로에 존재합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1745201699597&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// debug 모드 파일
%VCPKG_ROOT%\installed\&amp;lt;triplet&amp;gt;\debug\lib

// release 모드 파일
%VCPKG_ROOT%\installed\&amp;lt;triplet&amp;gt;\lib&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. Linking Arrow C++&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라이브러리 파일이 생성되었으면 MFC 프로젝트에 포함할 차례입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 생성된 라이브러리 파일을 확인하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bundled의 의미는 arrow 빌드에 필요한 패키지를 찾지 못하여 arrow가 내장하고 있는 번들 라이브러리를 사용했다는 의미입니다. vcpkg install로 모든 라이브러리 파일을 생성해 두었으므로 해당 라이브러리가 사용되어야 설정한 CRT 모드가 일치하고 그렇지 않을 경우에는 CRT 모드가 충돌하여 링크 에러가 발생할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cmake build 명령 실행 전 &quot;-DARROW_DEPENDENCY_SOURCE=VCPKG&quot;로 설정되었는지 확인해야 하고 ARROW_DEPENDENCY_SOURCE 값이 SYSTEM 이거나 기타 다른값이면 cmake config 단계에서 vcpkg 경로를 찾지 못하여 기본값으로 설정된 것입니다. 이 경우에는 Cmake가 vcpkig toolchain file을 인식하지 못한 것이므로&amp;nbsp; CMake 호출 시 &quot;-DCMAKE_TOOLCHAIN_FILE=&amp;lt;경로&amp;gt;&quot; 를 추가로 설정해줍니다. 이 때 CMake 캐시 파일이 이미 생성되어 있으면 설정이 바뀌지 않으므로 CMakeCache.txt를 지우거나 빌드 폴더를 통째로 삭제합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;962&quot; data-origin-height=&quot;243&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bS4HmJ/btsNrytTCYP/0EfGVVLCuy3WaSBFfsMiS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bS4HmJ/btsNrytTCYP/0EfGVVLCuy3WaSBFfsMiS0/img.png&quot; data-alt=&quot;Arrow c++ library files&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bS4HmJ/btsNrytTCYP/0EfGVVLCuy3WaSBFfsMiS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbS4HmJ%2FbtsNrytTCYP%2F0EfGVVLCuy3WaSBFfsMiS0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;962&quot; height=&quot;243&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;962&quot; data-origin-height=&quot;243&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Arrow c++ library files&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Debug/Release 각각 생성하였는데 Debug 모드의 arrow_static.lib 사이즈가 750MB나 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;헤더파일은 396개가 생성되었고 용량은 5MB정도 됩니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.1. 소스 트리 구성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 프로젝트의 하위 폴더에 파일들을 복사 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (1).png&quot; data-origin-width=&quot;553&quot; data-origin-height=&quot;217&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/v3B4G/btsNqULqtkC/eWMq12OVPhiS164VamVbpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/v3B4G/btsNqULqtkC/eWMq12OVPhiS164VamVbpK/img.png&quot; data-alt=&quot;prebuilt/arrow&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/v3B4G/btsNqULqtkC/eWMq12OVPhiS164VamVbpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fv3B4G%2FbtsNqULqtkC%2FeWMq12OVPhiS164VamVbpK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;553&quot; height=&quot;217&quot; data-filename=&quot;image (1).png&quot; data-origin-width=&quot;553&quot; data-origin-height=&quot;217&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;prebuilt/arrow&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;폴더 트리 구조는 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (2).png&quot; data-origin-width=&quot;268&quot; data-origin-height=&quot;802&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DRMDN/btsNnomxffJ/GVkFQURyg7DmdZZB9UsSS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DRMDN/btsNnomxffJ/GVkFQURyg7DmdZZB9UsSS1/img.png&quot; data-alt=&quot;folder tree&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DRMDN/btsNnomxffJ/GVkFQURyg7DmdZZB9UsSS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDRMDN%2FbtsNnomxffJ%2FGVkFQURyg7DmdZZB9UsSS1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;268&quot; height=&quot;802&quot; data-filename=&quot;image (2).png&quot; data-origin-width=&quot;268&quot; data-origin-height=&quot;802&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;folder tree&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.2. Include Directory 설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 속성창 Configuration Properties &amp;gt; C/C++ &amp;gt; General &amp;gt; Additonal Include Directories에 헤더파일 폴더 위치를 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.3. 전처리기 정의 추가&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 라이브러리로 Arrow를 사용하려면 Arrow 헤더 파일이 DLL 가져오기(dllimport/dllexport) 지시어 없이 올바르게 작동하도록 하기 위해 ARROW_STATIC 매크로를 정의해야 합니다. Arrow Flight 모듈을 사용할거면 ARROW_FLIGHT_STATIC과 ARROW_FLIGHT_SQL_STATIC도 정의하라고 하는데 저는 그냥 다 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 속성창을 띄워서 Configuration Properties &amp;gt; C/C++ &amp;gt; Preprocessor 항목의 Preprocessor Definitions에 다음 내용을 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744980677663&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ARROW_STATIC;ARROW_FLIGHT_STATIC;ARROW_FLIGHT_SQL_STATIC&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (1).png&quot; data-origin-width=&quot;971&quot; data-origin-height=&quot;448&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0Majc/btsNrwD4WDc/kuk2rZVP33O8dJSU5o1dTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0Majc/btsNrwD4WDc/kuk2rZVP33O8dJSU5o1dTk/img.png&quot; data-alt=&quot;전처리기 정의 추가&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0Majc/btsNrwD4WDc/kuk2rZVP33O8dJSU5o1dTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0Majc%2FbtsNrwD4WDc%2Fkuk2rZVP33O8dJSU5o1dTk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;971&quot; height=&quot;448&quot; data-filename=&quot;image (1).png&quot; data-origin-width=&quot;971&quot; data-origin-height=&quot;448&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;전처리기 정의 추가&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.4. 라이브러리 경로 및 종속성 추가&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드의 결과물로 lib 폴더에 생성된 라이브러리 파일들을 프로젝트에 링크하도록 설정해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 속성창 Configuration Properties &amp;gt; Linker &amp;gt; General 항목의 Additional Library Directories에 Arrow 라이브러리 파일이 있는 폴더의 경로를 추가합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (2).png&quot; data-origin-width=&quot;883&quot; data-origin-height=&quot;583&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKZrEp/btsNsNdzOwF/iK515FzLxqlIPDD4W3of70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKZrEp/btsNsNdzOwF/iK515FzLxqlIPDD4W3of70/img.png&quot; data-alt=&quot;라이브러리 경로 추가(Debug/Release 구분)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKZrEp/btsNsNdzOwF/iK515FzLxqlIPDD4W3of70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKZrEp%2FbtsNsNdzOwF%2FiK515FzLxqlIPDD4W3of70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;883&quot; height=&quot;583&quot; data-filename=&quot;image (2).png&quot; data-origin-width=&quot;883&quot; data-origin-height=&quot;583&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;라이브러리 경로 추가(Debug/Release 구분)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linker &amp;gt; Input 항목으로 이동하여 Additional Dependencies 항목에 라이브러리 파일 이름들을 추가합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image (3).png&quot; data-origin-width=&quot;883&quot; data-origin-height=&quot;403&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kzWMp/btsNrWo3wbv/uWfI2mjIg6fTtyQWr165s1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kzWMp/btsNrWo3wbv/uWfI2mjIg6fTtyQWr165s1/img.png&quot; data-alt=&quot;Additional Dependencies&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kzWMp/btsNrWo3wbv/uWfI2mjIg6fTtyQWr165s1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkzWMp%2FbtsNrWo3wbv%2FuWfI2mjIg6fTtyQWr165s1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;883&quot; height=&quot;403&quot; data-filename=&quot;image (3).png&quot; data-origin-width=&quot;883&quot; data-origin-height=&quot;403&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Additional Dependencies&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.5. 버전 출력 테스트 코드 작성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 빌드정보를 출력해보았습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1745066806659&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;arrow/api.h&amp;gt;

arrow::BuildInfo stArrow = arrow::GetBuildInfo();

DPRINTF(&quot;[INFO] Arrow Build(%s) Version(%d)\nArrow compiler id(%s) version(%s) flags(%s)\nArrow git_id(%s) git_desc(%s) git_package(%s)\n&quot;,
	(LPCTSTR)stArrow.build_type.c_str(), stArrow.version, (LPCTSTR)stArrow.compiler_id.c_str(), (LPCTSTR)stArrow.compiler_version.c_str(), (LPCTSTR)stArrow.compiler_flags.c_str(), (LPCTSTR)stArrow.git_id.c_str(), (LPCTSTR)stArrow.git_description.c_str(), (LPCTSTR)stArrow.package_kind.c_str());&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;arrow.png&quot; data-origin-width=&quot;862&quot; data-origin-height=&quot;99&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BaosG/btsNtebYxJw/9CRbQVlJADmcuTZg3Cidv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BaosG/btsNtebYxJw/9CRbQVlJADmcuTZg3Cidv0/img.png&quot; data-alt=&quot;arrow::GetBuildInfo&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BaosG/btsNtebYxJw/9CRbQVlJADmcuTZg3Cidv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBaosG%2FbtsNtebYxJw%2F9CRbQVlJADmcuTZg3Cidv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;862&quot; height=&quot;99&quot; data-filename=&quot;arrow.png&quot; data-origin-width=&quot;862&quot; data-origin-height=&quot;99&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;arrow::GetBuildInfo&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.6. 링크 오류 해결&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;arrow 헤더파일 추가시 몇 가지 매크로가 타 서드파티 라이브러리의 매크로와 동일한 이름으로 충돌이 발생할 수 있습니다. 저의 경우에는 ta-lib 등의 매크로와 이름이 같아서 충돌이 발생했습니다. 이 때에는 같은 .cpp 파일에서 두 라이브러리의 헤더파일을 참조하지 않도록 .cpp 파일을 분리하는 것이 #pragma 지시어를 사용하지 않고 가장 간단한 해결방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 max 매크로 충돌이 발생할 경우에는 기본 min/max 를 사용하지 않도록 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1745065387808&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// max 매크로 충돌 발생시 프로젝트 전체에서 Windows의 min/max 매크로를 막습니다.
constexpr int64_t kBinaryMemoryLimit = std::numeric_limits&amp;lt;int32_t&amp;gt;::max() - 1;

// 프로젝트 속성 C/C++ &amp;gt; Preprocessor &amp;gt; Preprocessor Definitions에 &quot;NOMINMAX&quot; 추가
// 또는 &quot;afxwin.h&quot; 파일을 include 하는 코드 최상단에 가서 다음 코드 추가
// MFC signle doc project 에서는 framework.h 파일에 있습니다.
#ifndef NOMINMAX
#  define NOMINMAX
#endif
#include &amp;lt;afxwin.h&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;2011&quot; data-start=&quot;1775&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2011&quot; data-start=&quot;1775&quot; data-ke-size=&quot;size16&quot;&gt;pkg-config 오류 발생시 &quot;PKG_CONFIG_PATH&quot;에 경로 설정을 해줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1745128869128&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 다음과 같은 pkgconf 오류 발생시
pkg-config package for libzstd that is used by arrow for static link isn't found
// 위 오류 무시하고 진행시 링킹단계에서 undefined refer 오류 발생. 라이브러리 파일이 깡통으로 생성됩니다.

// debug 용 라이브러리 경로
&amp;gt; set PKG_CONFIG_PATH=vcpkg_installed\x64-windows-static\debug\lib\pkgconfig

// release용 라이브러리 경로
&amp;gt; set PKG_CONFIG_PATH=vcpkg_installed\x64-windows-static\lib\pkgconfig&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5. Parquet 파일 생성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리상의 데이터를 Apache Arrow의 columnar 형식으로 변환하여 Paruqet 파일로 저장해봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apache Arrow는 데이터를 효율적으로 처리하기 위해 columnar 형식으로 데이터를 표현합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 행 기반 형식과 달리 각 칼럼의 데이터가 연속적인 메모리 블록에 저장되어 있어, 특정 컬럼에 대한 연산을 매우 빠르게 수행할 수 있도록 합니다. Parquet은 이러한 columnar 데이터를 저장하기 위한 효율적인 파일 포맷입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 저장 과정은 다음과 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1745211502267&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// data structure 예시
typedef struct record_s {
    int32_t varA;
    int64_t varB;
} TST_RECORD;&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Arrow Array Builder&amp;nbsp; 생성&lt;/li&gt;
&lt;li&gt;Builder를 Arrow Array로 변환&lt;/li&gt;
&lt;li&gt;Arrow Schema 정의&lt;/li&gt;
&lt;li&gt;Arrow Table 생성&lt;/li&gt;
&lt;li&gt;Parquet Writer 옵션 설정&lt;/li&gt;
&lt;li&gt;파일 스트림에 기록 후 저장&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1745215665503&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;memory&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;arrow/api.h&amp;gt;
#include &amp;lt;arrow/io/api.h&amp;gt;
#include &amp;lt;parquet/arrow/writer.h&amp;gt;

// Arrow Array Builder 생성
arrow::Int32Builder builder_a;
arrow::Int64Builder builder_b;

// builder에 메모리 데이터 저장
for (const auto&amp;amp; rec : data) {
    builder_a.Append(rec.a);
    builder_b.Append(rec.b);
}

// Builder를 실제 Array로 변환
 std::shared_ptr&amp;lt;arrow::Array&amp;gt; array_a, array_b;
 builder_a.Finish(&amp;amp;array_a);
 builder_b.Finish(&amp;amp;array_b);
 
 // Schema 정의 (필드 이름과 타입)
 auto schema = arrow::schema({
 	arrow::field(&quot;col_a&quot;, arrow::int32()),
    arrow::field(&quot;col_b&quot;, arrow::int64())
 });
 
 // Arrow Table 생성
 auto table = arrow::Table::Make(schema, { array_a, array_b });
   
 // Parquet Writer 옵션(압축 지정)
 parquet::WriterProperties::Builder prop_builder;
 prop_builder.compression(parquet::Compression::ZSTD);
 std::shared_ptr&amp;lt;parquet::WriterProperties&amp;gt; props = prop_builder.build();
 
 // 파일 출력 스트림 열기
 std::shared_ptr&amp;lt;arrow::io::FileOutputStream&amp;gt; outfile;
 PARQUET_ASSIGN_OR_THROW(outfile, arrow::io::FileOutputStream::Open(filename));

// Table 전체 Flush
const int64_t chunk_size = 1024;
PARQUET_THROW_NOT_OK(parquet::arrow::WriteTable(*table, arrow::default_memory_pool(),
outfile, chunk_size, props));
outfile-&amp;gt;Close();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. vcpkg 사용법 추가&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio 설치 시 함께 포함된 내장형 vcpkg는 일반적인 클래식모드를 지원하지 않거나 제한적으로만 동작하기 때문에 주로 프로젝트 폴더 내의 vcpkg.json 파일을 읽어들이는 매니페스트 모드 전용으로 구성되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vcpkg install 명령어를 통해 라이브러리를 직접 관리하려면 vcpkg를 별도로 설치(git clone)하여 사용하는 것이 권장됩니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;적당한 폴더로 이동&lt;/li&gt;
&lt;li&gt;vcpkg 클론: git clone https://github.com/microsoft/vcpkg.git&lt;/li&gt;
&lt;li&gt;부트스트랩 실행(빌드): cd vcpkg, ./bootstrap-vcpkg.bat&lt;/li&gt;
&lt;li&gt;통합(integration) 실행: 이 명령어를 실행하면 Visual Studio가 방금 설치한 이 vcpkg를 인식하게 됩니다.&lt;br /&gt;./vcpkg integrate install&lt;/li&gt;
&lt;li&gt;Arrow 설치 재시도&lt;br /&gt;./vcpkg install arrow:x64-windows-static&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 Visual Studio에서 별도의 설정 없이도 #include &amp;lt;arrow/api.h&amp;gt; 등을 바로 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 저는 프로젝트 단위로 패키지를 관리하고 싶어서 다음과 같이 했습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;개발하려는 프로젝트의 루프 폴더(.vcxproj 위치)로 이동&lt;/li&gt;
&lt;li&gt;vcpkg new --application 명령어로 vcpkg.json 파일을 생성&lt;/li&gt;
&lt;li&gt;vcpkg add port arrow 명령으로 라이브러리 추가&lt;br /&gt;(static 버전을 원하더라도 여기서는 arrow 만 기입 후 Visual Studio Project 속성에서 설정)&lt;/li&gt;
&lt;li&gt;프로젝트 속성에서 vcpkg 쪽 가면 static 또는 dynamic 빌드를 선택할 수 있습니다.&lt;/li&gt;
&lt;li&gt;그리고 Use vcpkg manifest 항목을 yes로 해주셔야 합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하고 빌드를 시작하면 vcpkg 먼저 다운로드 되고 빌드됩니다.&lt;/p&gt;</description>
      <category>프로그래밍/C | C++</category>
      <category>apache arrow</category>
      <category>csv</category>
      <category>parquet</category>
      <category>zstd</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/70</guid>
      <comments>https://noyecube.tistory.com/entry/MFC-Apache-Arrow-%EA%B8%B0%EB%B0%98-CSV-Parquet-%ED%8C%8C%EC%9D%BC-%EC%9D%BD%EA%B3%A0-%EC%93%B0%EA%B8%B0-zstd-%EC%95%95%EC%B6%95-%ED%8F%AC%ED%95%A8#entry70comment</comments>
      <pubDate>Tue, 15 Apr 2025 14:02:51 +0900</pubDate>
    </item>
    <item>
      <title>[MFC] zstd 압축 예제코드, 직접 써보며 배우는 실전 활용법</title>
      <link>https://noyecube.tistory.com/entry/MFC-zstd-%EC%95%95%EC%B6%95-%EC%98%88%EC%A0%9C%EC%BD%94%EB%93%9C-%EC%A7%81%EC%A0%91-%EC%8D%A8%EB%B3%B4%EB%A9%B0-%EB%B0%B0%EC%9A%B0%EB%8A%94-%EC%8B%A4%EC%A0%84-%ED%99%9C%EC%9A%A9%EB%B2%95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요, 오늘은 제가 최근에 작업한 프로젝트를 바탕으로 zstd 라이브러리를 이용한 압축 방법을 예제와 함께 공유하려고 합니다. &lt;br /&gt;&lt;br /&gt;요즘 제가 주식 틱체결 데이터를 수집 중인데요, 체결틱, 호가틱 등 하루치 데이터가 무려 종목당 수십MB까지 커지다 보니 이걸 그냥 저장하면 디스크 공간을 만만치 않게 차지하더라고요.&amp;nbsp; &lt;br /&gt;그런데 압축을 해보니까 용량이 단 10% 수준으로 줄어드는 겁니다.&amp;nbsp; &lt;br /&gt;이렇게&amp;nbsp;용량을&amp;nbsp;줄이면&amp;nbsp;데이터&amp;nbsp;관리&amp;nbsp;비용을&amp;nbsp;상당히&amp;nbsp;절약할&amp;nbsp;수&amp;nbsp;있겠죠? &lt;br /&gt;&lt;br /&gt;하지만&amp;nbsp;매번&amp;nbsp;윈도우&amp;nbsp;탐색기에서&amp;nbsp;압축을&amp;nbsp;하는&amp;nbsp;게&amp;nbsp;여간&amp;nbsp;번거로운&amp;nbsp;일이&amp;nbsp;아닙니다.&amp;nbsp; &lt;br /&gt;그래서 &quot;데이터 저장 단계에서 바로 압축하여 저장하면 어떨까?&quot;라는 생각을 하게 되었고,&amp;nbsp; &lt;br /&gt;그&amp;nbsp;결과&amp;nbsp;선택한&amp;nbsp;라이브러리가&amp;nbsp;바로&amp;nbsp;zstd였습니다. &lt;br /&gt;&lt;br /&gt;막상&amp;nbsp;zstd를&amp;nbsp;쓰려고&amp;nbsp;보니,&amp;nbsp;유명한&amp;nbsp;라이브러리임에도&amp;nbsp;불구하고&amp;nbsp;정작&amp;nbsp;실제&amp;nbsp;MFC&amp;nbsp;프로젝트에서&amp;nbsp;구체적으로&amp;nbsp;적용한&amp;nbsp;사례나&amp;nbsp;친절한&amp;nbsp;예제코드는&amp;nbsp;거의&amp;nbsp;없더라고요.&amp;nbsp; &lt;br /&gt;대부분의 포스팅이 공식 홈페이지 설명이나 벤치마크만 가져와서 있어 보이기만 하는 글들이 많았습니다.&amp;nbsp; &lt;br /&gt;결국&amp;nbsp;저는&amp;nbsp;직접&amp;nbsp;소스코드를&amp;nbsp;Git에서&amp;nbsp;다운로드하여&amp;nbsp;빌드한&amp;nbsp;뒤&amp;nbsp;라이브러리를&amp;nbsp;추가해,&amp;nbsp;압축과&amp;nbsp;해제를&amp;nbsp;코드로&amp;nbsp;구현했습니다.&amp;nbsp; &lt;br /&gt;오늘은&amp;nbsp;그&amp;nbsp;핵심&amp;nbsp;내용을&amp;nbsp;쉽게&amp;nbsp;정리해서&amp;nbsp;공유해&amp;nbsp;드리려고&amp;nbsp;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. zstd란 무엇인가? 왜 zstd를 선택했나?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;zstd (Zstandard)는 페이스북에서 개발한 오픈소스 압축 라이브러리로, 뛰어난 속도와 압축률의 균형을 갖춘 것으로 유명합니다. 사실 파일 압축 포맷은 Zip, Tar.gz, 7z, LZ4 등 다양합니다. 그럼에도 제가 zstd를 선택한 이유는 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;압축&amp;nbsp;해제&amp;nbsp;속도가&amp;nbsp;뛰어나다:&amp;nbsp;데이터를&amp;nbsp;압축할&amp;nbsp;때&amp;nbsp;걸리는&amp;nbsp;시간보다,&amp;nbsp;나중에&amp;nbsp;데이터를&amp;nbsp;사용할&amp;nbsp;때&amp;nbsp;압축을&amp;nbsp;해제하는&amp;nbsp;속도가&amp;nbsp;훨씬&amp;nbsp;중요합니다.&amp;nbsp;zstd는&amp;nbsp;압축&amp;nbsp;해제&amp;nbsp;속도가&amp;nbsp;매우&amp;nbsp;빨라&amp;nbsp;효율적입니다.&lt;/li&gt;
&lt;li&gt;스트리밍&amp;nbsp;압축&amp;nbsp;지원:&amp;nbsp;스트리밍&amp;nbsp;압축&amp;nbsp;방식을&amp;nbsp;지원하여&amp;nbsp;메모리&amp;nbsp;사용량을&amp;nbsp;줄이고&amp;nbsp;임시&amp;nbsp;데이터&amp;nbsp;저장&amp;nbsp;공간도&amp;nbsp;효율적으로&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/li&gt;
&lt;li&gt;압축률과&amp;nbsp;속도를&amp;nbsp;자유롭게&amp;nbsp;조정&amp;nbsp;가능:&amp;nbsp;압축&amp;nbsp;속도와&amp;nbsp;압축률&amp;nbsp;사이에서&amp;nbsp;1~22&amp;nbsp;단계까지&amp;nbsp;자유롭게&amp;nbsp;선택할&amp;nbsp;수&amp;nbsp;있어&amp;nbsp;원하는&amp;nbsp;상황에&amp;nbsp;맞게&amp;nbsp;최적의&amp;nbsp;성능을&amp;nbsp;구현할&amp;nbsp;수&amp;nbsp;있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 제가 사용하는 환경, 저장된 데이터 기반으로 시뮬레이션과 백테스팅을 돌리는 환경에서는 압축 해제 속도가 중요했기 때문에, zstd가 가장 적합한 선택이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. zstd 소스코드 빌드 및 MFC 프로젝트 적용법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선, zstd 소스코드를 Git에서 다운받아 빌드한 뒤, 헤더파일과 생성된 라이브러리파일을 MFC 프로젝트에 추가해 사용합니다. Debug 버전과 Release 버전 각각 빌드하여 프로젝트에 추가합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;2.1. zstd 소스코드 다운로드&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음의 주소에서 소스코드를 다운받습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/facebook/zstd.git&quot;&gt;https://github.com/facebook/zstd.git&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1744170198655&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - facebook/zstd: Zstandard - Fast real-time compression algorithm&quot; data-og-description=&quot;Zstandard - Fast real-time compression algorithm. Contribute to facebook/zstd development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/facebook/zstd.git&quot; data-og-url=&quot;https://github.com/facebook/zstd&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bz1y1a/hyYClD4aRk/VuQ3n3fGsda98w5SI6kr31/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/rYrAC/hyYBeTo922/6Haoun2VFlQAZjKlZcBl20/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/facebook/zstd.git&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/facebook/zstd.git&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bz1y1a/hyYClD4aRk/VuQ3n3fGsda98w5SI6kr31/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/rYrAC/hyYBeTo922/6Haoun2VFlQAZjKlZcBl20/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - facebook/zstd: Zstandard - Fast real-time compression algorithm&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Zstandard - Fast real-time compression algorithm. Contribute to facebook/zstd development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC에 git 클라이언트가 설치되어 있으면 다음 명령어로 다운로드 가능합니다.&lt;br /&gt;&quot;git clone &lt;a href=&quot;https://github.com/facebook/zstd.git&quot;&gt;https://github.com/facebook/zstd.git&lt;/a&gt;&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.2. zstd 소스코드 빌드&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MFC 프로젝트에 적용하기 위해서는 visual studio 용 빌드 스크립트를 실행하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;zstd\build\VS_scripts&quot;경로에 있습니다. 빌드 스크립트 버전이 VS2010부터 VS2017까지만 있는데 저는 VS2022에서 사용할 것이므로 &quot;build.generic.cmd&quot; 스크립트에 다음과 같은 파라미터를 전달하여 빌드합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파라미터는 &quot;&amp;lt;VS버전&amp;gt; &amp;lt;아키텍처&amp;gt; &amp;lt;빌드모드&amp;gt; &amp;lt;컴파일러버전&amp;gt;&quot;입니다. 이 스크립트가 실행되면 시스템에 설치된 VS 컴파일러의 경로를 찾아내서 빌드합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744170656474&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// x64, Debug for VS2022
// zstd\build\VS_scripts\bin/Debug/x64/ 경로에 생성
&amp;gt; build.generic.cmd VS2022 x64 Debug V143

// x64, Release for VS2022
// zstd\build\VS_scripts\bin/Release/x64/ 경로에 생성
&amp;gt; build.generic.cmd VS2022 x64 Release V143

// Win32, Debug for VS2022
&amp;gt; build.generic.cmd VS2022 Win32 Debug V143&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;zstd_build.png&quot; data-origin-width=&quot;623&quot; data-origin-height=&quot;387&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b65SGw/btsNdVw5NIu/maRePdkYUymDTTOz8CdmPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b65SGw/btsNdVw5NIu/maRePdkYUymDTTOz8CdmPK/img.png&quot; data-alt=&quot;zstd library 파일(디버깅용 심볼 정보를 담고 있는 pdb 파일도 챙기세요)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b65SGw/btsNdVw5NIu/maRePdkYUymDTTOz8CdmPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb65SGw%2FbtsNdVw5NIu%2FmaRePdkYUymDTTOz8CdmPK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;623&quot; height=&quot;387&quot; data-filename=&quot;zstd_build.png&quot; data-origin-width=&quot;623&quot; data-origin-height=&quot;387&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;zstd library 파일(디버깅용 심볼 정보를 담고 있는 pdb 파일도 챙기세요)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※&amp;nbsp; CRT 옵션(런타임 라이브러리 옵션) 유의사항&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트의 CRT 옵션(프로젝트 속성&amp;gt;C/C++&amp;gt;Code Generation&amp;gt;Runtime Library)에 따라 LNK4098 경고가 발생할 수 있습니다. 그 이유는 프로젝트의 라이브러리 설정과 zstd 라이브러리가 빌드될 때 사용된 CRT 설정이 서로 다를 수 있기 때문입니다. zstd의 Debug 모드 RuntimeLibrary 속성값은 /MDd에 해당하는 &quot;MultiThreadedDebugDLL&quot;이고 Release 모드의 RuntimeLibrary 속성값은 /MTd에 해당하는 &quot;MultiThreaded&quot;입니다. 저는 Visual Studio가 설치되어 있지 않은 환경에서도 제 프로그램 실행파일이 동작하도록 Debug/Release 모두 &quot;/MTd 및 /MT&quot;로 설정해두었기 때문에 Debug 모드에서 LNK4098 경고 발생을 피하려면 zstd의 &quot;libzstd.vcxproj&quot;의 RuntimeLibrary 속성값을 &quot;MultiThreadedDebugDLL&quot;에서 &quot;MultiThreadedDebug&quot;로 변경합니다. Visual Studio Native Command Prompt 에서 &quot;dumpbin&quot;을 했을 때 &quot;/DEFAULTLIB:MSVCRTD&quot; 값이 아닌 &quot;/DEFAULTLIB:LIBCMTD&quot;라고 나오면 MT 모드로 빌드가 된 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;linkerror.png&quot; data-origin-width=&quot;1339&quot; data-origin-height=&quot;275&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vTmdW/btsNeaBqHlR/vValHPZcnyT7pYn4DyohI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vTmdW/btsNeaBqHlR/vValHPZcnyT7pYn4DyohI1/img.png&quot; data-alt=&quot;LNK 경고&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vTmdW/btsNeaBqHlR/vValHPZcnyT7pYn4DyohI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvTmdW%2FbtsNeaBqHlR%2FvValHPZcnyT7pYn4DyohI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1339&quot; height=&quot;275&quot; data-filename=&quot;linkerror.png&quot; data-origin-width=&quot;1339&quot; data-origin-height=&quot;275&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;LNK 경고&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;libcmtd.png&quot; data-origin-width=&quot;743&quot; data-origin-height=&quot;278&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bisBjI/btsNesBIBGI/cWheCKkTQztWhZMJPwbOnk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bisBjI/btsNesBIBGI/cWheCKkTQztWhZMJPwbOnk/img.png&quot; data-alt=&quot;LIBCMTD 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bisBjI/btsNesBIBGI/cWheCKkTQztWhZMJPwbOnk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbisBjI%2FbtsNesBIBGI%2FcWheCKkTQztWhZMJPwbOnk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;743&quot; height=&quot;278&quot; data-filename=&quot;libcmtd.png&quot; data-origin-width=&quot;743&quot; data-origin-height=&quot;278&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;LIBCMTD 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.3. MFC 프로젝트 적용&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 라이브러리 파일과 헤더파일 3개(zstd.h, zstd_errors.h, zdict.h)를 프로젝트 소스코드에 포함시킵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;prebuilt.png&quot; data-origin-width=&quot;567&quot; data-origin-height=&quot;539&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZzpyB/btsNda20CUZ/nswJk1hGEMmJ0S3mjBRUTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZzpyB/btsNda20CUZ/nswJk1hGEMmJ0S3mjBRUTk/img.png&quot; data-alt=&quot;프로젝트 소스트리에 라이브러리 파일 및 헤더파일 추가&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZzpyB/btsNda20CUZ/nswJk1hGEMmJ0S3mjBRUTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZzpyB%2FbtsNda20CUZ%2FnswJk1hGEMmJ0S3mjBRUTk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;567&quot; height=&quot;539&quot; data-filename=&quot;prebuilt.png&quot; data-origin-width=&quot;567&quot; data-origin-height=&quot;539&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;프로젝트 소스트리에 라이브러리 파일 및 헤더파일 추가&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 프로젝트 속성창을 띄워서 링커 옵션을 수정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;링커 옵션에 새로운 zstd 라이브러리 파일 경로를 추가합니다. Debug/Release 모두 경로가 같으므로 &quot;Configuration&quot;은 &quot;All Configuration&quot;으로 두고 &quot;Platform&quot;은 &quot;x64&quot; 확인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linker&amp;gt;General&amp;gt;Additional Library Directories 항목에 새로운 zstd 경로를 추가합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;linker_path.png&quot; data-origin-width=&quot;776&quot; data-origin-height=&quot;277&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GS4zD/btsNe4NA1DO/JK2KdqfAdkbtYqfk4h5av0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GS4zD/btsNe4NA1DO/JK2KdqfAdkbtYqfk4h5av0/img.png&quot; data-alt=&quot;라이브러리 경로 추가&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GS4zD/btsNe4NA1DO/JK2KdqfAdkbtYqfk4h5av0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGS4zD%2FbtsNe4NA1DO%2FJK2KdqfAdkbtYqfk4h5av0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;776&quot; height=&quot;277&quot; data-filename=&quot;linker_path.png&quot; data-origin-width=&quot;776&quot; data-origin-height=&quot;277&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;라이브러리 경로 추가&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 링킹 단계에서 사용할 라이브러리 파일명을 적어줘야 하는데 Debug/Release 에 사용할 파일이 다르므로 이에 유의합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;lib_input.png&quot; data-origin-width=&quot;776&quot; data-origin-height=&quot;487&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0XTOP/btsNe83roUn/Q0fJMrxt9iR3JBCPDkk7bK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0XTOP/btsNe83roUn/Q0fJMrxt9iR3JBCPDkk7bK/img.png&quot; data-alt=&quot;라이브러리 파일명 추가&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0XTOP/btsNe83roUn/Q0fJMrxt9iR3JBCPDkk7bK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0XTOP%2FbtsNe83roUn%2FQ0fJMrxt9iR3JBCPDkk7bK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;776&quot; height=&quot;487&quot; data-filename=&quot;lib_input.png&quot; data-origin-width=&quot;776&quot; data-origin-height=&quot;487&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;라이브러리 파일명 추가&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 설정이 완료되었으면 라이브러리를 사용할 준비가 되었는지 테스트가 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;zstd API를 사용하는 헬로월드 수준의 코드를 작성 후 프&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;로젝트를 빌드/실행하여 오류가 발생하는지 확인합니다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1744183639399&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;prebuilt/zstd/include/zstd.h&quot;
#include &quot;prebuilt/zstd/include/zstd_errors.h&quot;
#include &quot;prebuilt/zstd/include/zdict.h&quot;

BOOL CNoyecubeApp::InitInstance() {
	ZSTD_CCtx* cctx = ZSTD_createCCtx();
	if (cctx == NULL) {
		printf(&quot;ZSTD_CCtx 생성 실패!\n&quot;);
		return 1;
	}
	printf(&quot;ZSTD_CCtx 생성 성공!\n&quot;);

	printf(&quot;ZSTD Library Version: %d.%d.%d\n&quot;,
		ZSTD_VERSION_MAJOR, ZSTD_VERSION_MINOR, ZSTD_VERSION_RELEASE);

	ZSTD_freeCCtx(cctx);
    
    return TRUE;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;test.png&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;70&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVlb7x/btsNe2QdeH5/MtIuEYwJSClenjd8gLIk40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVlb7x/btsNe2QdeH5/MtIuEYwJSClenjd8gLIk40/img.png&quot; data-alt=&quot;zstd 버전 출력&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVlb7x/btsNe2QdeH5/MtIuEYwJSClenjd8gLIk40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVlb7x%2FbtsNe2QdeH5%2FMtIuEYwJSClenjd8gLIk40%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;70&quot; data-filename=&quot;test.png&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;70&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;zstd 버전 출력&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. zstd Compression/Decompression - HowTo&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;zstd는 Simple API와 Streaming API를 지원합니다. Simple API는 주어진 입력 버퍼 전체를 하나의 완전한 압축 프레임으로 처리합니다. 이 프레임에는 압축 헤더와 블록 관리 정보가 포함되므로, 만약 100MB의 데이터를 10MB씩 잘라서 압축하면, 각각의 10MB 블록이 독립적인 압축 프레임이 되어 각 프레임마다 헤더가 기록됩니다. 따라서 전체 100MB 데이터를 한 번에 압축한다면, 하나의 압축 프레임(헤더가 단 한번 기록됨)이 생성되고, 10MB씩 독립적으로 압축하면 10개의 별도의 프레임이 생성되므로 10개의 헤더가 하나의 파일에 기록됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리 여유가 있다면 100MB 전체 데이터를 한 번에 Simple API로 압축하는 것이 헤더 오버헤드를 줄이고 압축 효율을 높일 수 있습니다. 만약 메모리 제한이나 실시간 데이터 수집 등의 이유로 10MB 단위로 데이터를 모아서 압축해야 한다면, Simple API를 여러번 호출하게 되는데 이 경우 각 10MB 청크마다 독립적 압축 프레임이 생성되고 헤더가 중복 기록됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Streaming API는 데이터를 점진적으로 입력받아 하나의 압축 프레임으로 처리할 수 있도록 설계되어 있습니다. 10MB 단위로 데이터를 모으더라도, Streaming API를 사용하면 내부적으로 계속 데이터를 누적하면서 단 하나의 압축 프레임(헤더가 한번만 작성됨)으로 출력할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;압축코드를 작성하기전, plain text(CSV)로 저장하던 기존 코드 먼저 간략하게 살펴보겠습니다. 주식 체결데이터를 저장하는 부분을 예로 들겠습니다. 제 프로그램의 구조는 수신부에서 텍스트기반의 체결 데이터를 모은뒤 정수형, 실수형 타입의 바이너리 형태로 메모리에 레코드 단위로 저장했다가 파일에 CSV 또는 Parquet 포맷으로 저장합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일에 저장할 때에는 zstd의 단순압축 API를 사용하고, 향후 임시 버퍼로 사용하는 메모리 공간의 절약을 위해 스트리밍 API를 적용할 예정입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744245646655&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* 주식체결 샘플
* vProcessMsgRealData:70][22548] RealType(0) sRealKey(012450) FidCnt(25) 체결시간 (100045) 현재가 (-277500) 전일대비 (-1500)
등락율 (-0.54) 최우선매도호가(-277500) 최우선매수호가 (-277000) 체결량 (+3) 누적체결량(307074) 누적거래대금(84927) 시가( 279000) 고가(+281000) 저가(-270000)
전일대비기호 (5) 전일거래량대비(-422004) 거래대금증감 (-120913983500)
전일거래량대비(비율)(-42.12) 거래회전율 (0.67) 거래비용(584) 체결강도(96.10) 시가총액(억)(126488) 장구분(2) KO접근도(0) 상한가발생시간(000000) 하한가발생시간 (000000)
전일동시간거래량비율 (10218)
*/
((CKiwoomRealMsgNode*)pNode)-&amp;gt;m_ullSeqNo = ++(p-&amp;gt;_m_ullSeqNo);
((CKiwoomRealMsgNode*)pNode)-&amp;gt;m_sRealKey = sRealKey;
((CKiwoomRealMsgNode*)pNode)-&amp;gt;m_arCString.SetAt(0, _T(&quot;100045&quot;));
((CKiwoomRealMsgNode*)pNode)-&amp;gt;m_arCString.SetAt(1, _T(&quot;-277500&quot;));
((CKiwoomRealMsgNode*)pNode)-&amp;gt;m_arCString.SetAt(2, _T(&quot;-1500&quot;));
((CKiwoomRealMsgNode*)pNode)-&amp;gt;m_arCString.SetAt(3, _T(&quot;-0.54&quot;));
((CKiwoomRealMsgNode*)pNode)-&amp;gt;m_arCString.SetAt(4, _T(&quot;-277500&quot;));
((CKiwoomRealMsgNode*)pNode)-&amp;gt;m_arCString.SetAt(5, _T(&quot;-277000&quot;));
((CKiwoomRealMsgNode*)pNode)-&amp;gt;m_arCString.SetAt(6, _T(&quot;+3&quot;));
((CKiwoomRealMsgNode*)pNode)-&amp;gt;m_arCString.SetAt(7, _T(&quot;307074&quot;));
((CKiwoomRealMsgNode*)pNode)-&amp;gt;m_arCString.SetAt(8, _T(&quot;84927&quot;));
((CKiwoomRealMsgNode*)pNode)-&amp;gt;m_arCString.SetAt(9, _T(&quot; 279000&quot;));
((CKiwoomRealMsgNode*)pNode)-&amp;gt;m_arCString.SetAt(10, _T(&quot;+281000&quot;));
((CKiwoomRealMsgNode*)pNode)-&amp;gt;m_arCString.SetAt(11, _T(&quot;-270000&quot;));
((CKiwoomRealMsgNode*)pNode)-&amp;gt;m_arCString.SetAt(12, _T(&quot;5&quot;));
((CKiwoomRealMsgNode*)pNode)-&amp;gt;m_arCString.SetAt(13, _T(&quot;-422004&quot;));
((CKiwoomRealMsgNode*)pNode)-&amp;gt;m_arCString.SetAt(14, _T(&quot;-120913983500&quot;));
((CKiwoomRealMsgNode*)pNode)-&amp;gt;m_arCString.SetAt(15, _T(&quot;-42.12&quot;));
((CKiwoomRealMsgNode*)pNode)-&amp;gt;m_arCString.SetAt(16, _T(&quot;0.67&quot;));
((CKiwoomRealMsgNode*)pNode)-&amp;gt;m_arCString.SetAt(17, _T(&quot;584&quot;));
((CKiwoomRealMsgNode*)pNode)-&amp;gt;m_arCString.SetAt(18, _T(&quot;96.10&quot;));
((CKiwoomRealMsgNode*)pNode)-&amp;gt;m_arCString.SetAt(19, _T(&quot;126488&quot;));
((CKiwoomRealMsgNode*)pNode)-&amp;gt;m_arCString.SetAt(20, _T(&quot;2&quot;));
((CKiwoomRealMsgNode*)pNode)-&amp;gt;m_arCString.SetAt(21, _T(&quot;0&quot;));
((CKiwoomRealMsgNode*)pNode)-&amp;gt;m_arCString.SetAt(22, _T(&quot;000000&quot;));
((CKiwoomRealMsgNode*)pNode)-&amp;gt;m_arCString.SetAt(23, _T(&quot;000000&quot;));
((CKiwoomRealMsgNode*)pNode)-&amp;gt;m_arCString.SetAt(24, _T(&quot;10218&quot;));

ptheDlgKiwoomMsg-&amp;gt;vPushMsgPump(pNode);

////////////////////// 이하 파일 저장 부분 /////////////////////
CFile* pcfp = nullptr;
CArchive* pcarpCsv = nullptr;
/* Open file for CSV */
if (TRUE == pWp-&amp;gt;boFlushToCSV) {
	cstrFileFullPath.Format(_T(&quot;%s\\%s.csv&quot;), szFinalPath, cstrFileName);
	DPRINTF(&quot;[INFO] Fullpath (%s)\n&quot;, (LPCTSTR)cstrFileFullPath);
	pcfp = new CFile();
	boRet = pcfp-&amp;gt;Open(cstrFileFullPath,
		CFile::modeWrite | CFile::modeCreate | CFile::shareDenyNone);	// delete old file if exist with same file name
	if (FALSE == boRet) {
		DPRINTF(&quot;[ERR]: Fail to create file(%s)\n&quot;, (LPCTSTR)cstrFileFullPath);
		delete pcfp;
		pcfp = nullptr;
	}
	/* Check CFile Pointer */
	if (pcfp) {
		pcarpCsv = new CArchive(pcfp, CArchive::store, 4096 * 10);
		if (!pcarpCsv) {
			if (nullptr != pcfp) { pcfp-&amp;gt;Flush(); pcfp-&amp;gt;Abort(); delete pcfp; pcfp = nullptr; }
		}
	}
	else {
		pcarpCsv = nullptr;
	}
}
///// 생략 //////
if (cstrCsvBuf.GetLength() &amp;gt; 0) {
	cstrCsvBuf.SetAt(cstrCsvBuf.GetLength() - 1, _T('\0'));
	if (pcarpCsv) {	// FIXME: optimization needed? 
		pcarpCsv-&amp;gt;WriteString(cstrCsvBuf);
	}
}
///// 생략 /////
/* Flush buffer &amp;amp; Close Archive &amp;amp; File */
if (nullptr != pcarpCsv) { pcarpCsv-&amp;gt;Flush();	pcarpCsv-&amp;gt;Abort();	delete pcarpCsv; pcarpCsv = nullptr; }
if (nullptr != pcfp) { pcfp-&amp;gt;Flush(); pcfp-&amp;gt;Abort(); delete pcfp; pcfp = nullptr; }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 코드에서는 CFile과 CArchive를 활용하여 체결데이터를 레코드 단위로 파일에 저장하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;압축효율을 높이려면 압축대상이 되는 단위 버퍼에 중복되는 내용이 많아야 하는데 버퍼 사이즈가 작으면 압축효율이 떨어집니다. 따라서 기존과 같이 레코드 단위로 압축을 진행하게 되면 CPU 자원소모는 늘어나고 압축률은 떨어지게 되므로 파일 출력 내용을 메모리에 버퍼링을 해뒀다가 일정 수준에 도달했을 때 압축을 진행하는 것이 좋습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.1. Simple Compression/Decompression&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Simple 방식은 너무 간단하여 필요한분 댓글 남겨주시면 적겠습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2. Streaming Compression/Decompression&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주식전종목 2500여개의 체결데이터, 호가데이터 등의 압축작업은 수천번 수행되어야 하므로 ZSTD_CCtx 객체와 input, output 버퍼는 스레드 단위별로 재활용하는것이 좋습니다. 그래서 다음과 같은 구조체를 정의하고 각 스레드(IOCP Worker)별로 객체를 생성하고 초기화 하였습니다. ZSTD_CStreamInSize()와 ZSTD_CStreamOutSize()로 ZSTD가 추천하는 버퍼 사이즈를 얻을 수 있으며 확인 결과 약 128KB 정도의 크기였습니다. 버퍼크기는 성능관점에서 거거익선이기 때문에 저는 이 기본사이즈 10배수의 버퍼를 할당하였습니다. ZSTD 예제코드에는 모든 API 호출시 에러가 발생하면 프로그램을 종료시키는데 실제 개발하는 수집 프로그램에서는 그렇게 하면 안되기 때문에 초기화 실패시에만 프로그램을 종료하고 나머지 경우에는 경고 출력 및 로그에 사유를 남기고 계속 동작하도록 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 ZSTD_CCtx 객체를 재사용하기 위해서는 ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only); 을 새로운 세션 시작전에 호출해줘야 합니다. 세션만 리셋하기 때문에 옵션으로 설정한 압축레벨과 압축파일에 체크섬값 포함 여부는 삭제되지 않습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744613109427&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;typedef struct _COMP_KEY_FLUSH_REALDATA_CACHE_TO_FILE_S {
	DWORD dwIocpTrBytes;
	void* const buffIn;
	void* const buffOut;
	size_t const buffInSize;
	size_t const buffOutSize;
	size_t buffInUsedSizeBytes;
	ZSTD_CCtx* cctx;
	_COMP_KEY_FLUSH_REALDATA_CACHE_TO_FILE_S() :buffIn(nullptr), buffOut(nullptr), buffInSize(0), buffOutSize(0) {
		dwIocpTrBytes = 0;
		buffInUsedSizeBytes = 0;
		cctx = nullptr;
	}
	void zstdResetSession(void) {
		buffInUsedSizeBytes = 0;
		if (cctx) {
			ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only);
		}
	}
} TST_LPARAM_COMP_KEY_FLUSH_REALDATA_CACHE_TO_FILE, * PTST_LPARAM_COMP_KEY_FLUSH_REALDATA_CACHE_TO_FILE;

/////// 스레드 초기화 코드 부분(스레드 루프 진입 직전) ///////
size_t r;
KIWOOM::TST_LPARAM_COMP_KEY_FLUSH_REALDATA_CACHE_TO_FILE stLp;

SetEvent((HANDLE)(pParams-&amp;gt;lpOpaque_3));	// send parameter copy completed signal to thread creator

/* [START] Create the input and output buffers and zstd context for CSV Compression */
(size_t)(stLp.buffInSize) = ZSTD_CStreamInSize() * 10;   /* can always read one full block */
(size_t)(stLp.buffOutSize) = ZSTD_CStreamOutSize() * 10;   /* can always read one full block */

(void*)(stLp.buffIn) = malloc(stLp.buffInSize);
if (nullptr == stLp.buffIn) {
	DPRINTF(&quot;[ERROR] malloc fail. Program Exit.\n&quot;);
	ExitProcess(EXIT_FAILURE);
}
(void*)(stLp.buffOut) = malloc(stLp.buffOutSize);
if (nullptr == stLp.buffOut) {
	DPRINTF(&quot;[ERROR] malloc fail. Exit Program.\n&quot;);
	ExitProcess(EXIT_FAILURE);
}

(ZSTD_CCtx*)stLp.cctx = ZSTD_createCCtx();
if (nullptr == stLp.cctx) {
	DPRINTF(&quot;[ERROR] ZSTD_createCCtx fail. Exit Program.\n&quot;);
	ExitProcess(EXIT_FAILURE);
}

r = ZSTD_CCtx_setParameter(stLp.cctx, ZSTD_c_compressionLevel, ZSTD_CLEVEL_DEFAULT);	/* Set compression parameters according to pre-defined cLevel table. */
if (ZSTD_isError(r)) {
	DPRINTF(&quot;[WARN] ZSTD_CCtx_setParameter fail ZSTD_c_compressionLevel.\n&quot;);
}

r = ZSTD_CCtx_setParameter(stLp.cctx, ZSTD_c_checksumFlag, TRUE);	/* A 32-bits checksum of content is written at end of frame (default:FALSE) */
if (ZSTD_isError(r)) {
	DPRINTF(&quot;[WARN] ZSTD_CCtx_setParameter fail ZSTD_c_checksumFlag.\n&quot;);
}

DPRINTF(&quot;[INFO] ZSTD buffInSize(%zd) buffOutSize(%zd) for IOCP thread(%d)\n&quot;, stLp.buffInSize, stLp.buffOutSize, GetCurrentThreadId());
/* [END] Create the input and output buffers and zstd context for CSV Compression */&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램상에 CSV 포맷의 레코드를 압축 할 것인지 말것인지를 결정할 수 있도록 했는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;압축모드일 경우에는 파일명의 확장자를 &quot;.zst&quot;로 바꾸고 CFile 오픈시 CFile::typeBinary 모드를 추가하였습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1744613421675&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (TRUE == pWp-&amp;gt;boCSV_Compress) {	/* CFile Open with typeBinary, no CArchive */
	cstrFileFullPath.Format(_T(&quot;%s\\%s.csv.zst&quot;), szFinalPath, cstrFileName);
	DPRINTF(&quot;[INFO] Fullpath for compressed CSV (%s)\n&quot;, (LPCTSTR)cstrFileFullPath);
	pcfp = new CFile();
	boRet = pcfp-&amp;gt;Open(cstrFileFullPath,
		CFile::modeWrite | CFile::modeCreate | CFile::shareDenyNone | CFile::typeBinary);	// delete old file if exist with same file name
}
else {	/* pain text mode. CFile Open , connect with CArchive */
	cstrFileFullPath.Format(_T(&quot;%s\\%s.csv&quot;), szFinalPath, cstrFileName);
	DPRINTF(&quot;[INFO] Fullpath (%s)\n&quot;, (LPCTSTR)cstrFileFullPath);
	pcfp = new CFile();
	boRet = pcfp-&amp;gt;Open(cstrFileFullPath,
		CFile::modeWrite | CFile::modeCreate | CFile::shareDenyNone);	// delete old file if exist with same file name
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CFile Open이 성공했으면 버퍼링 저장이 가능하도록 CArchive 객체에 연결합니다. 이 때 모드는 CArchive::store이며 버퍼사이즈는 기본 4KB가 아닌 ZSTD의 output 버퍼 사이즈 + 4KB로 하였습니다. CString cstrCsvBuf에 레코드 단위의 데이터가 담겨 있습니다. 압축모드가 아닐 경우에는 문자열 맨 끝의 ',' 문자를 '\0'로 교체 후 문자열 기록을 하고, 압축모드일 경우에는 문자열 맨 끝의 ',' 직전까지의 사이즈를 계산하여 버퍼에 메모리 복사를 수행합니다. 다음의 코드는 CSV 파일의 첫 행을 기록하는 코드입니다. pLp-&amp;gt;zstdResetSession();이 호출됨에 유의해주세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 말한 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only);가 호출됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744613625427&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* Check CFile Pointer, Open CArchive*/
if (pcfp) {
	pcarpCsv = new CArchive(pcfp, CArchive::store, (int)(pLp-&amp;gt;buffOutSize + 4096));
	if (!pcarpCsv) {
		if (nullptr != pcfp) { pcfp-&amp;gt;Flush(); pcfp-&amp;gt;Abort(); delete pcfp; pcfp = nullptr; }
	}
	else {	// everything successfully opened for CSV
		iLen = cstrCsvBuf.GetLength();
		if (TRUE == pWp-&amp;gt;boCSV_Compress) {
			pLp-&amp;gt;zstdResetSession();	// re-use zstd resource
			if (iLen &amp;gt; 0) {
				sizeToWrite = (iLen - 1) * sizeof(TCHAR);	// (iLen - 1) : remove last tail char ','
				pLp-&amp;gt;buffInUsedSizeBytes = sizeToWrite;	// init buffInUsedSizeBytes with sizeToWrite
				CopyMemory(pLp-&amp;gt;buffIn, (LPCTSTR)cstrCsvBuf, sizeToWrite);
			}
		}
		else {
			if (iLen &amp;gt; 0) {
				cstrCsvBuf.SetAt(iLen - 1, _T('\0'));
				pcarpCsv-&amp;gt;WriteString(cstrCsvBuf);
			}
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스트리밍 압축모드에서는 input 버퍼에 더 이상 레코드 단위의 데이터를 복사할 수 없을 때 실제 압축을 수행 후 파일에 기록합니다. 다음 코드에서 buffInUsedSizeBytes가 실제 input 버퍼의 사용량을 관리하는 변수이며, 압축 수행 후 파일에 기록이 완료되면 이 값을 0으로 초기화 해줘야 input 버퍼를 처음부터 기록할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;압축을 진행하기전 ZSTD_inBuffer와 ZSTD_outBuffer를 초기화합니다. 각 객체의 pos 값은 0으로 설정하며 inBuffer의 사이즈는 inBuffer에 전체 사이즈가 아닌 유효 데이터가 위치한 offset, outBuffer의 사이즈는 outBuffer의 전체 사이즈를 입력합니다. 그리고나서 ZSTD_compressStream2를 호출하여 inBuffer의 내용을 outBuffer로 압축저장합니다. 사양서에 따라 오류가 발생할 수 있기 때문에 리턴값은 ZSTD_isError()로 확인해주고 에러가 아닐 경우 실제 파일 버퍼에 기록합니다. 스트리밍 압축이기 때문에(끝이 어디인지 모르기 때문에) ZSTD_e_continue 모드로 호출합니다. inBuffer를 다 읽지 못했으면 이 과정을 계속 반복해야 하고 pos 값이 size에 도달했을 때 압축 루프를 탈출합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744613912287&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ZSTD_inBuffer stZstdInObj;
ZSTD_outBuffer stZstdOutObj;

iLen = cstrCsvBuf.GetLength();
if (iLen &amp;gt; 0) {
	if (TRUE == pWp-&amp;gt;boCSV_Compress) {
		if (iLen &amp;lt; (pLp-&amp;gt;buffInSize - pLp-&amp;gt;buffInUsedSizeBytes)) {
			sizeToWrite = (iLen - 1) * sizeof(TCHAR);	// (iLen - 1) : remove last tail char ','
			CopyMemory((char*)pLp-&amp;gt;buffIn + pLp-&amp;gt;buffInUsedSizeBytes, (LPCTSTR)cstrCsvBuf, sizeToWrite);
			pLp-&amp;gt;buffInUsedSizeBytes += sizeToWrite;	// 
		}
		else {	// need to flush
			stZstdInObj.src = pLp-&amp;gt;buffIn;	stZstdInObj.size = pLp-&amp;gt;buffInUsedSizeBytes; stZstdInObj.pos = 0;

			do {
				stZstdOutObj.dst = pLp-&amp;gt;buffOut; stZstdOutObj.size = pLp-&amp;gt;buffOutSize; stZstdOutObj.pos = 0;

				sizeRemaining = ZSTD_compressStream2(pLp-&amp;gt;cctx, &amp;amp;stZstdOutObj, &amp;amp;stZstdInObj, ZSTD_e_continue);
				if (ZSTD_isError(sizeRemaining)) {
					DPRINTF(&quot;[ERROR] ZSTD_compressStream2 (%zd)(%s).\n&quot;, sizeRemaining, ZSTD_getErrorName(sizeRemaining));
					/* TODO: Report this at Logfile */
                    break; // escape loop anyway
				}
				else {
					pcarpCsv-&amp;gt;Write(pLp-&amp;gt;buffOut, (UINT)(stZstdOutObj.pos));
				}

				if (stZstdInObj.pos == stZstdInObj.size) {
					break;
				}
			} while (TRUE);
			pLp-&amp;gt;buffInUsedSizeBytes = 0;	// input buffer flushed. reset used offset.
		}
	}
	else {
		cstrCsvBuf.SetAt(iLen - 1, _T('\0'));	// mark end of string by '\0'
		pcarpCsv-&amp;gt;WriteString(cstrCsvBuf);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 압축파일을 닫는 과정입니다. 나머지 데이터가 있건 없건 ZSTD_e_end 플래그로 ZSTD_compressStream2를 호출하면 압축파일의 끝이 정상적으로 마무리됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1744614613006&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* [START] Flush the remaining data of stZstdInObj in ZSTD_e_end mode. */
if (TRUE == pWp-&amp;gt;boCSV_Compress) {
	stZstdInObj.src = pLp-&amp;gt;buffIn;	stZstdInObj.size = pLp-&amp;gt;buffInUsedSizeBytes; stZstdInObj.pos = 0;
	do {
		stZstdOutObj.dst = pLp-&amp;gt;buffOut; stZstdOutObj.size = pLp-&amp;gt;buffOutSize; stZstdOutObj.pos = 0;
		sizeRemaining = ZSTD_compressStream2(pLp-&amp;gt;cctx, &amp;amp;stZstdOutObj, &amp;amp;stZstdInObj, ZSTD_e_end);
		if (ZSTD_isError(sizeRemaining)) {
			DPRINTF(&quot;[ERROR] ZSTD_compressStream2 (%zd)(%s).\n&quot;, sizeRemaining, ZSTD_getErrorName(sizeRemaining));
			/* TODO: Report this at Logfile */
            break; // escape loop anyway
		}
		else {
			pcarpCsv-&amp;gt;Write(pLp-&amp;gt;buffOut, (UINT)(stZstdOutObj.pos));
		}

		if (0 == sizeRemaining) {
			break;
		}
	} while (TRUE);
	pLp-&amp;gt;buffInUsedSizeBytes = 0;	// input buffer flushed. reset used offset.
}
/* [END] Flush the remaining data of stZstdInObj in ZSTD_e_end mode. */

/* Flush buffer &amp;amp; Close Archive &amp;amp; File */
if (nullptr != pcarpCsv) { pcarpCsv-&amp;gt;Flush();	pcarpCsv-&amp;gt;Abort();	delete pcarpCsv; pcarpCsv = nullptr; }
if (nullptr != pcfp) { pcfp-&amp;gt;Flush(); pcfp-&amp;gt;Abort(); delete pcfp; pcfp = nullptr; }&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 결론: 압축은 비용을 절약하는 가장 현명한 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;압축정도를 결정하는 압축레벨 22단계 중 3단계 압축 진행시 다음과 같은 압축 결과를 얻었습니다. 원본파일의 17% 수준 용량입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;res.png&quot; data-origin-width=&quot;655&quot; data-origin-height=&quot;78&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhcxq3/btsNg6sXrRX/PC8xh3wfpiI3cYJYJth5x0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhcxq3/btsNg6sXrRX/PC8xh3wfpiI3cYJYJth5x0/img.png&quot; data-alt=&quot;CSV vs ZSTD&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhcxq3/btsNg6sXrRX/PC8xh3wfpiI3cYJYJth5x0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbhcxq3%2FbtsNg6sXrRX%2FPC8xh3wfpiI3cYJYJth5x0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;655&quot; height=&quot;78&quot; data-filename=&quot;res.png&quot; data-origin-width=&quot;655&quot; data-origin-height=&quot;78&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CSV vs ZSTD&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 압축 라이브러리를 활용하면 메모리 및 디스크 공간을 90%까지 절약할 수 있으며, 이는 결국 데이터 관리 비용 절감과 직결됩니다. 여러분도 zstd와 같은 압축 라이브러리를 활용하여 효율적인 데이터 관리를 시작해 보시는 건 어떨까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅은 이론적인 소개가 아니라 직접 프로젝트에 적용해본 실제 사례를 공유했다는 점에서 더욱 유익한 정보가 되었기를 바랍니다. 앞으로도 실질적인 개발 노하우를 공유하겠습니다. 감사합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>프로그래밍/C | C++</category>
      <category>lnk4098</category>
      <category>lnk4217</category>
      <category>lnk4286</category>
      <category>mfc</category>
      <category>zstandard</category>
      <category>zstd</category>
      <category>zstd_cctx</category>
      <category>zstd_compressstream2</category>
      <category>압축 소스코드</category>
      <category>압축예제</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/69</guid>
      <comments>https://noyecube.tistory.com/entry/MFC-zstd-%EC%95%95%EC%B6%95-%EC%98%88%EC%A0%9C%EC%BD%94%EB%93%9C-%EC%A7%81%EC%A0%91-%EC%8D%A8%EB%B3%B4%EB%A9%B0-%EB%B0%B0%EC%9A%B0%EB%8A%94-%EC%8B%A4%EC%A0%84-%ED%99%9C%EC%9A%A9%EB%B2%95#entry69comment</comments>
      <pubDate>Wed, 9 Apr 2025 16:34:22 +0900</pubDate>
    </item>
    <item>
      <title>[NAS] 자작나스 NAS DIY 구축 - UNRAID 및 포토앱 구축</title>
      <link>https://noyecube.tistory.com/entry/NAS-%EC%9E%90%EC%9E%91%EB%82%98%EC%8A%A4-NAS-DIY-%EA%B5%AC%EC%B6%95-UNRAID-%EB%B0%8F-%ED%8F%AC%ED%86%A0%EC%95%B1-%EC%84%A4%EC%A0%95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;자작나스의 OS로 UNRAID를 설치했는데 하드웨어 구성을 마치고 휴대폰과 카메라의 사진과 동영상을 관리할 수 있는 어플로 IMMICH를 설정해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 디스크 어레이 및 풀 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UNRAID에서 디스크 어레이는 하드디스크 여러 개를 하나의 어레이로 묶고 패리티 디스크를 2개까지 설정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(디스크 2개까지 고장 허용) 어레이는 1개만 구성할 수 있으며 풀은 여러개를 구성할 수 있습니다. 어레이는 하드디스크, Pool은 SSD를 권장하며 Pool을 구성하면 캐시로 활용하거나 도커나 VM 이미지를 빠르게 구동할 수 있습니다. 캐시의 파일시스템을 BTRFS로 설정시 디스크 사이즈가 일치하지 않아도 RAID1으로 구성하여 사용할 수 있습니다. UNRAID의 디스크 어레이는 일정시간 미사용시 하드디스크를 정지시킬 수 있어 전력을 절감하는데 유리합니다. 캐시를 구성해두면 쌓이는 데이터를 캐시에 담아뒀다가 지정한 시각에 배치작업으로 디스크를 사용합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;array_cache.webp&quot; data-origin-width=&quot;1028&quot; data-origin-height=&quot;460&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bln9Ui/btsJbfzv8rL/hJVmMym1wA410KWZIyBZsk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bln9Ui/btsJbfzv8rL/hJVmMym1wA410KWZIyBZsk/img.webp&quot; data-alt=&quot;디스크 어레이 및 캐시풀 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bln9Ui/btsJbfzv8rL/hJVmMym1wA410KWZIyBZsk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbln9Ui%2FbtsJbfzv8rL%2FhJVmMym1wA410KWZIyBZsk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1028&quot; height=&quot;460&quot; data-filename=&quot;array_cache.webp&quot; data-origin-width=&quot;1028&quot; data-origin-height=&quot;460&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;디스크 어레이 및 캐시풀 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하드디스크가 유휴상태(standby)일 때는 TEMP, SMART는 미표기입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. SHARES 폴더 생성 및 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UNRAID를 설치하면 appdata, domains, isos, system shares가 기본으로 생성됩니다. system shares에는 docker 이미지 파일이 생성되며 database의 datafile 등은 appdata에 생성하면 빠른 구동이 가능합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 사진과 영상 파일이 저장될 공간은 디스크 어레이에 gallery 이름으로 생성하였습니다. ssd 캐시를 지정했는데 지정하지 않고 바로 어레이에 기록하도록 설정해도 됩니다. 캐시를 지정하면 사진을 저장할 때 디스크를 구동하지 않고 모아뒀다가 정해진 시각에 배치작업으로 복사합니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;s.webp&quot; data-origin-width=&quot;1688&quot; data-origin-height=&quot;399&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xSdXM/btsJciviNXm/sKZs3NknKEZ7EPKFpQkn70/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xSdXM/btsJciviNXm/sKZs3NknKEZ7EPKFpQkn70/img.webp&quot; data-alt=&quot;gallery array&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xSdXM/btsJciviNXm/sKZs3NknKEZ7EPKFpQkn70/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxSdXM%2FbtsJciviNXm%2FsKZs3NknKEZ7EPKFpQkn70%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1688&quot; height=&quot;399&quot; data-filename=&quot;s.webp&quot; data-origin-width=&quot;1688&quot; data-origin-height=&quot;399&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;gallery array&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 갤러리 shares 하위에 immich 폴더를 추가 생성합니다. immich 앱의 데이터 경로로 잡아주기 위함입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해서는 갤러리 shares의 &quot;SMB Security Settings&quot;에서 Export 설정을 &quot;Yes&quot;로 바꾸고 PC에서 네트워크 드라이브를 연결해야 합니다. Security를 private으로 설정하면 지정된 사용자만 접근할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;smb.webp&quot; data-origin-width=&quot;990&quot; data-origin-height=&quot;770&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/reBxZ/btsJbeN9N1Z/EeFqiZN8Kr2kKvf6BqRVNk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/reBxZ/btsJbeN9N1Z/EeFqiZN8Kr2kKvf6BqRVNk/img.webp&quot; data-alt=&quot;smb 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/reBxZ/btsJbeN9N1Z/EeFqiZN8Kr2kKvf6BqRVNk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FreBxZ%2FbtsJbeN9N1Z%2FEeFqiZN8Kr2kKvf6BqRVNk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;990&quot; height=&quot;770&quot; data-filename=&quot;smb.webp&quot; data-origin-width=&quot;990&quot; data-origin-height=&quot;770&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;smb 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gallery shares를 네트워크 드라이브 연결 후 &quot;immich&quot; 폴더를 생성하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;immich 구동에 postgresql이 추가로 설치되는데 pgdata 경로설정을 위하여 캐시풀 영역인 appdata 경로에 immich_pgdata 폴더를 생성하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;folder.webp&quot; data-origin-width=&quot;982&quot; data-origin-height=&quot;212&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bznyNQ/btsJabZerlK/Ec6uw5MkNzNjzLkjw54azk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bznyNQ/btsJabZerlK/Ec6uw5MkNzNjzLkjw54azk/img.webp&quot; data-alt=&quot;immich folder&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bznyNQ/btsJabZerlK/Ec6uw5MkNzNjzLkjw54azk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbznyNQ%2FbtsJabZerlK%2FEc6uw5MkNzNjzLkjw54azk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;982&quot; height=&quot;212&quot; data-filename=&quot;folder.webp&quot; data-origin-width=&quot;982&quot; data-origin-height=&quot;212&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;immich folder&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;pg.webp&quot; data-origin-width=&quot;501&quot; data-origin-height=&quot;312&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yMzkn/btsJaU3uBqj/g1qBoK6Yb7jWHwMzvtowZk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yMzkn/btsJaU3uBqj/g1qBoK6Yb7jWHwMzvtowZk/img.webp&quot; data-alt=&quot;캐시풀 영역 appdata 하위에 immich_pgdata 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yMzkn/btsJaU3uBqj/g1qBoK6Yb7jWHwMzvtowZk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyMzkn%2FbtsJaU3uBqj%2Fg1qBoK6Yb7jWHwMzvtowZk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;501&quot; height=&quot;312&quot; data-filename=&quot;pg.webp&quot; data-origin-width=&quot;501&quot; data-origin-height=&quot;312&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;캐시풀 영역 appdata 하위에 immich_pgdata 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. IMMICH 앱 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;immich는 개인 클라우드 스토리지 솔루션으로서 사진과 동영상을 백업하고 관리할 수 있도록 도와주는 오픈 소스 앱입니다. 다음의 공식 홈페이지를 참조하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://immich.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://immich.app/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1724249652565&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Home | Immich&quot; data-og-description=&quot;immich Self-hosted photo and video backup solution directly from your mobile phone&quot; data-og-host=&quot;immich.app&quot; data-og-source-url=&quot;https://immich.app/&quot; data-og-url=&quot;https://immich.app/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eyK1kk/hyWSdHRoJi/FbIjqBSqydNKk9qduYwDvK/img.png?width=3038&amp;amp;height=742&amp;amp;face=0_0_3038_742&quot;&gt;&lt;a href=&quot;https://immich.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://immich.app/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eyK1kk/hyWSdHRoJi/FbIjqBSqydNKk9qduYwDvK/img.png?width=3038&amp;amp;height=742&amp;amp;face=0_0_3038_742');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Home | Immich&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;immich Self-hosted photo and video backup solution directly from your mobile phone&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;immich.app&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UNRAID에서 immich를 사용하는 방법은 UNRAID 커뮤니티 앱을 설치하거나 IMMICH가 제공하는 도커를 설치하여 구동하는 방법이 있습니다. 도커 방식을 사용하기 위해 먼저 UNRAID에 도커 구성관리 플러그인 도커컴포즈를 설치합니다. 도커 컴포즈 공식 문서페이지는 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.docker.com/compose/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.docker.com/compose/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1724250244165&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Docker Compose overview&quot; data-og-description=&quot;Learn how to use Docker Compose to define and run multi-container applications with this detailed introduction to the tool.&quot; data-og-host=&quot;docs.docker.com&quot; data-og-source-url=&quot;https://docs.docker.com/compose/&quot; data-og-url=&quot;https://docs.docker.com/compose/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bTUpz0/hyWSktqrUJ/2xKa4bfG42DNXCyPcMWrp1/img.jpg?width=2400&amp;amp;height=1260&amp;amp;face=0_0_2400_1260&quot;&gt;&lt;a href=&quot;https://docs.docker.com/compose/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.docker.com/compose/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bTUpz0/hyWSktqrUJ/2xKa4bfG42DNXCyPcMWrp1/img.jpg?width=2400&amp;amp;height=1260&amp;amp;face=0_0_2400_1260');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Docker Compose overview&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn how to use Docker Compose to define and run multi-container applications with this detailed introduction to the tool.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.docker.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.1. docker compose manager 설치&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UNRAID의 APPS 탭에서 &quot;Community Applications plugin&quot;을 먼저 설치하면 앱 플러그인을 통해 도커컴포즈를 설치할 수 있습니다. APPS 탭의 검색창에 &quot;docker compose manager&quot;를 입력하면 설치 아이콘이 나타납니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;docker.webp&quot; data-origin-width=&quot;827&quot; data-origin-height=&quot;531&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwAcmK/btsJanrwdgQ/kwdm5XRMyNxdCtC7PfnszK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwAcmK/btsJanrwdgQ/kwdm5XRMyNxdCtC7PfnszK/img.webp&quot; data-alt=&quot;Docker Compose Manager 설치&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwAcmK/btsJanrwdgQ/kwdm5XRMyNxdCtC7PfnszK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwAcmK%2FbtsJanrwdgQ%2Fkwdm5XRMyNxdCtC7PfnszK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;827&quot; height=&quot;531&quot; data-filename=&quot;docker.webp&quot; data-origin-width=&quot;827&quot; data-origin-height=&quot;531&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Docker Compose Manager 설치&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2. Immich Stack 추가&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PLUGINS 탭에서 Compose.Manager 아이콘 클릭으로 매니저 화면 진입 후 &quot;Add New Compose Stack&quot; 창을 띄우고 immich의 스택을 추가해 줍니다. 이렇게 추가한 스택 설정파일은 USB 메모리 스틱에 저장됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;666&quot; data-origin-height=&quot;411&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcJFtU/btsJchwnHhc/b6e9IJDkhyVHbxlk3O8Oa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcJFtU/btsJchwnHhc/b6e9IJDkhyVHbxlk3O8Oa0/img.png&quot; data-alt=&quot;Compose Manager 플러그인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcJFtU/btsJchwnHhc/b6e9IJDkhyVHbxlk3O8Oa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcJFtU%2FbtsJchwnHhc%2Fb6e9IJDkhyVHbxlk3O8Oa0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;666&quot; height=&quot;411&quot; data-origin-width=&quot;666&quot; data-origin-height=&quot;411&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Compose Manager 플러그인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;compose.webp&quot; data-origin-width=&quot;554&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfWUMG/btsJaZ4LoU8/xVuXGMchWiio8KR709WKtK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfWUMG/btsJaZ4LoU8/xVuXGMchWiio8KR709WKtK/img.webp&quot; data-alt=&quot;Add New Compose Stack&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfWUMG/btsJaZ4LoU8/xVuXGMchWiio8KR709WKtK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfWUMG%2FbtsJaZ4LoU8%2FxVuXGMchWiio8KR709WKtK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;554&quot; height=&quot;416&quot; data-filename=&quot;compose.webp&quot; data-origin-width=&quot;554&quot; data-origin-height=&quot;416&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Add New Compose Stack&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.3. Compose File 수정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가된 immich 스택 화면에서 설정버튼(톱니바퀴 아이콘)을 누르고 &quot;Edit Stack&quot;을 누릅니다. 그리고 수정할 스택 목록에서 &quot;COMPOSE FILE&quot;을 클릭합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;cog.webp&quot; data-origin-width=&quot;781&quot; data-origin-height=&quot;312&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2SBuC/btsJaBQC3i6/m7n0Dmb45p9CUNcUrj34bk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2SBuC/btsJaBQC3i6/m7n0Dmb45p9CUNcUrj34bk/img.webp&quot; data-alt=&quot;EDIT Immich Stack&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2SBuC/btsJaBQC3i6/m7n0Dmb45p9CUNcUrj34bk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2SBuC%2FbtsJaBQC3i6%2Fm7n0Dmb45p9CUNcUrj34bk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;781&quot; height=&quot;312&quot; data-filename=&quot;cog.webp&quot; data-origin-width=&quot;781&quot; data-origin-height=&quot;312&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;EDIT Immich Stack&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 다음 링크의 immich docker compose yml 파일 전체를 붙여넣기 합니다. 붙여넣기 전에 기존에 있는 내용은 전부 삭제하고 새로 붙여 넣습니다. 그리고 저장버튼을 누르는데 이 때 발생하는 UI 라벨 설정창은 무시하고 확인을 누릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UNRAID 버전 6.12.11기준으로 도커엔진 버전이 24인데 immich의 최신 컴포즈파일은 도커엔진 버전 25기준으로 작성되어 있습니다. 24버전에서 지원되지 않는 DB 헬스체크 부분 &quot;start_interval&quot;과 &quot;start_period&quot; 항목은 주석처리합니다. DB 도커가 먼저 올라오고 immich가 올라가야 하는데 서로 동기화가 안되는 단점은 감수해야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;25.webp&quot; data-origin-width=&quot;392&quot; data-origin-height=&quot;142&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TBGMJ/btsJbO2yfev/o316UCT8YVafPbBqhPFGqk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TBGMJ/btsJbO2yfev/o316UCT8YVafPbBqhPFGqk/img.webp&quot; data-alt=&quot;immich compose file 수정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TBGMJ/btsJbO2yfev/o316UCT8YVafPbBqhPFGqk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTBGMJ%2FbtsJbO2yfev%2Fo316UCT8YVafPbBqhPFGqk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;392&quot; height=&quot;142&quot; data-filename=&quot;25.webp&quot; data-origin-width=&quot;392&quot; data-origin-height=&quot;142&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;immich compose file 수정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.4. Env File 수정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;Edit Stack&quot; 창에서 &quot;Env File&quot;을 클릭합니다. 그리고 다음 링크의 immich env 파일 내용을 그대로 복사 붙여넣기 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/immich-app/immich/releases/latest/download/example.env&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/immich-app/immich/releases/latest/download/example.env&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 다음 옵션 항목을 설정(수정)합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;UPLOAD_LOCATION: 앞서 생성한 &quot;gallery&quot; shares 내의 &quot;immich&quot; 폴더 경로를 확인 후 절대경로로 적어줍니다.&lt;/li&gt;
&lt;li&gt;DB_DATA_LOCATION: 앞서 생성한 &quot;appdata&quot; shares 내의 &quot;immich_pgdata&quot; 폴더 경로를 확인 후&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;절대경로로 적어줍니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;mnt.webp&quot; data-origin-width=&quot;509&quot; data-origin-height=&quot;278&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Nh5FC/btsJa1O3GRQ/EJdHrmXFTuUDutC3iZBjck/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Nh5FC/btsJa1O3GRQ/EJdHrmXFTuUDutC3iZBjck/img.webp&quot; data-alt=&quot;shares &amp;amp;gt; gallery &amp;amp;gt; immich 경로 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Nh5FC/btsJa1O3GRQ/EJdHrmXFTuUDutC3iZBjck/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNh5FC%2FbtsJa1O3GRQ%2FEJdHrmXFTuUDutC3iZBjck%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;509&quot; height=&quot;278&quot; data-filename=&quot;mnt.webp&quot; data-origin-width=&quot;509&quot; data-origin-height=&quot;278&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;shares &amp;gt; gallery &amp;gt; immich 경로 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;oxi.png&quot; data-origin-width=&quot;1578&quot; data-origin-height=&quot;568&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nQVIK/btsI99ty5RK/IKBbXZlHxrCSFigxJgHtdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nQVIK/btsI99ty5RK/IKBbXZlHxrCSFigxJgHtdK/img.png&quot; data-alt=&quot;immich.env 변경 사항&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nQVIK/btsI99ty5RK/IKBbXZlHxrCSFigxJgHtdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnQVIK%2FbtsI99ty5RK%2FIKBbXZlHxrCSFigxJgHtdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1578&quot; height=&quot;568&quot; data-filename=&quot;oxi.png&quot; data-origin-width=&quot;1578&quot; data-origin-height=&quot;568&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;immich.env 변경 사항&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.5. immich 도커 생성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker 탭에 생성되어 있는 immich 의 명령 버튼에 &quot;COMPOSE UP&quot;이 있습니다. 이 버튼을 누르면 도커 생성작업을 시작합니다. 완료 화면이 뜰 때까지 대기 후 완료되면 &quot;DONE&quot; 버튼을 누르고 다시 Docker 탭 화면으로 이동합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;composeup.webp&quot; data-origin-width=&quot;1296&quot; data-origin-height=&quot;198&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kID9R/btsJazZzgQr/GizK51Q3NqOXOLwqbToP6k/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kID9R/btsJazZzgQr/GizK51Q3NqOXOLwqbToP6k/img.webp&quot; data-alt=&quot;Compose Up&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kID9R/btsJazZzgQr/GizK51Q3NqOXOLwqbToP6k/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkID9R%2FbtsJazZzgQr%2FGizK51Q3NqOXOLwqbToP6k%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1296&quot; height=&quot;198&quot; data-filename=&quot;composeup.webp&quot; data-origin-width=&quot;1296&quot; data-origin-height=&quot;198&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Compose Up&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;immichup.webp&quot; data-origin-width=&quot;1824&quot; data-origin-height=&quot;1235&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5KUtI/btsJaBwj1IC/Av4aWNYh0bKYtpmFRvVFJ0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5KUtI/btsJaBwj1IC/Av4aWNYh0bKYtpmFRvVFJ0/img.webp&quot; data-alt=&quot;Docker COMPOSE 완료 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5KUtI/btsJaBwj1IC/Av4aWNYh0bKYtpmFRvVFJ0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5KUtI%2FbtsJaBwj1IC%2FAv4aWNYh0bKYtpmFRvVFJ0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1824&quot; height=&quot;1235&quot; data-filename=&quot;immichup.webp&quot; data-origin-width=&quot;1824&quot; data-origin-height=&quot;1235&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Docker COMPOSE 완료 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.6. immich 관리 페이지 접속&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 목록에서 immich_server의 포트매핑 정보를 확인 후 브라우저로 접속했을 때 immich 환영페이지가 뜨면 정상입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;port.webp&quot; data-origin-width=&quot;1265&quot; data-origin-height=&quot;137&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l9WLb/btsJbNo2MB9/UUTHlw4sJJejo23ZxTb2vk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l9WLb/btsJbNo2MB9/UUTHlw4sJJejo23ZxTb2vk/img.webp&quot; data-alt=&quot;docker port mappings&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l9WLb/btsJbNo2MB9/UUTHlw4sJJejo23ZxTb2vk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl9WLb%2FbtsJbNo2MB9%2FUUTHlw4sJJejo23ZxTb2vk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1265&quot; height=&quot;137&quot; data-filename=&quot;port.webp&quot; data-origin-width=&quot;1265&quot; data-origin-height=&quot;137&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;docker port mappings&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;im.webp&quot; data-origin-width=&quot;1064&quot; data-origin-height=&quot;748&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qDla7/btsJanyllU5/G9rg6k0oDMNiLqZ2KKMqp1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qDla7/btsJanyllU5/G9rg6k0oDMNiLqZ2KKMqp1/img.webp&quot; data-alt=&quot;immich welcome page&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qDla7/btsJanyllU5/G9rg6k0oDMNiLqZ2KKMqp1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqDla7%2FbtsJanyllU5%2FG9rg6k0oDMNiLqZ2KKMqp1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1064&quot; height=&quot;748&quot; data-filename=&quot;im.webp&quot; data-origin-width=&quot;1064&quot; data-origin-height=&quot;748&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;immich welcome page&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;레퍼런스&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=LtNWxxM5Mzg&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=LtNWxxM5Mzg&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=LtNWxxM5Mzg&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/uTnNj/hyWSaj4Aoz/djI2JOIrVIPkjmoLfZAyo1/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=622_302_984_514&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;Immich Pt 2 - Easy Setup- Install, Connect, and Upload in Minutes!&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/LtNWxxM5Mzg&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://immich.app/docs/install/post-install/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://immich.app/docs/install/post-install/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1724267459571&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Post Install Steps | Immich&quot; data-og-description=&quot;A list of common steps to take after installing Immich include:&quot; data-og-host=&quot;immich.app&quot; data-og-source-url=&quot;https://immich.app/docs/install/post-install/&quot; data-og-url=&quot;https://immich.app/docs/install/post-install&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kwOSZ/hyWSfFHSyv/gK1MJgDFb0kykqqyAKuph0/img.png?width=3038&amp;amp;height=742&amp;amp;face=0_0_3038_742&quot;&gt;&lt;a href=&quot;https://immich.app/docs/install/post-install/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://immich.app/docs/install/post-install/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kwOSZ/hyWSfFHSyv/gK1MJgDFb0kykqqyAKuph0/img.png?width=3038&amp;amp;height=742&amp;amp;face=0_0_3038_742');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Post Install Steps | Immich&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A list of common steps to take after installing Immich include:&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;immich.app&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://immich.app/docs/install/unraid&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://immich.app/docs/install/unraid&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1724267476344&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Unraid | Immich&quot; data-og-description=&quot;Immich can easily be installed and updated on Unraid via:&quot; data-og-host=&quot;immich.app&quot; data-og-source-url=&quot;https://immich.app/docs/install/unraid&quot; data-og-url=&quot;https://immich.app/docs/install/unraid&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bxpneL/hyWShjc8m7/Zlu0bXaJMsXsZdPIRwoy9k/img.png?width=3038&amp;amp;height=742&amp;amp;face=0_0_3038_742&quot;&gt;&lt;a href=&quot;https://immich.app/docs/install/unraid&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://immich.app/docs/install/unraid&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bxpneL/hyWShjc8m7/Zlu0bXaJMsXsZdPIRwoy9k/img.png?width=3038&amp;amp;height=742&amp;amp;face=0_0_3038_742');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Unraid | Immich&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Immich can easily be installed and updated on Unraid via:&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;immich.app&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Tech</category>
      <category>immich</category>
      <category>unraid</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/68</guid>
      <comments>https://noyecube.tistory.com/entry/NAS-%EC%9E%90%EC%9E%91%EB%82%98%EC%8A%A4-NAS-DIY-%EA%B5%AC%EC%B6%95-UNRAID-%EB%B0%8F-%ED%8F%AC%ED%86%A0%EC%95%B1-%EC%84%A4%EC%A0%95#entry68comment</comments>
      <pubDate>Thu, 22 Aug 2024 03:36:14 +0900</pubDate>
    </item>
    <item>
      <title>[NAS] 자작나스 NAS DIY 구축 - 하드웨어 조립</title>
      <link>https://noyecube.tistory.com/entry/NAS-%EC%9E%90%EC%9E%91%EB%82%98%EC%8A%A4-NAS-DIY-%EA%B5%AC%EC%B6%95-%ED%95%98%EB%93%9C%EC%9B%A8%EC%96%B4-%EC%A1%B0%EB%A6%BD</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;TrueNAS SCALE 또는 UNRAID를 운영하기 위한 NAS를 만들어 보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서는 대당 수천만 원을 호가하는 NetApp, EMC 스토리지를 마음 편하게 사용하고 있지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가정에서 내 돈을 들여 NAS를 운영하려니 시놀로지는 하드웨어 성능에 비해 가격이 너무 비싼 것 같아(소프트웨어 편의성은 인정) 직접 만들어 사용하기로 하였습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영용 NAS는 UNRAID OS를 설치할 예정이고 가족사진/영상 저장용 1대, 주식/코인 차트 데이터 저장 및 데이터베이스 도커 운영용 NAS 1대를 구축하였고 백업용 NAS는 TrueNAS를 설치할 예정이고 케이스를 제외한 부품은 전부 당근 또는 기타 커뮤니티에서 중고로 드래곤볼한 제온시스템입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 시스템 요구사항&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;낮은 소비전력: 집에서 운영할 것이기 때문에 소비전력이 적어야 합니다.&lt;/li&gt;
&lt;li&gt;ECC 메모리 지원: TrueNAS는 ECC 메모리가 권장이고 24시간 365일 운영할 것이기 때문에 ECC 메모리는 일종의 보험이라고 생각합니다. 시놀로지는 가격에서부터 탈락했지만 이 부분도 마음에 들지 않았습니다. 또한 시놀로지는 부품 수급도 3년이 지나면 잘 되지 않는 점도 자작나스를 만들어야 하는 이유를 만들어 주었습니다.&lt;/li&gt;
&lt;li&gt;작은 크기와 예쁜 디자인: 시놀로지같은 작고 예쁜 모양의 케이스가 필요합니다.&lt;/li&gt;
&lt;li&gt;6개 이상의 하드디스크 드라이브 공간&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;부품사진.webp&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;1050&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/doZCGF/btsIQoXkBIh/6k7BoNCgYFkhz5KCp0GSy0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/doZCGF/btsIQoXkBIh/6k7BoNCgYFkhz5KCp0GSy0/img.webp&quot; data-alt=&quot;NAS 조립을 위한 부품들&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/doZCGF/btsIQoXkBIh/6k7BoNCgYFkhz5KCp0GSy0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdoZCGF%2FbtsIQoXkBIh%2F6k7BoNCgYFkhz5KCp0GSy0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1400&quot; height=&quot;1050&quot; data-filename=&quot;부품사진.webp&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;1050&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;NAS 조립을 위한 부품들&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. NAS 케이스 선정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 JONSBO의 NAS 케이스를 구매검토 하였으나 여러 커뮤니티의 후기를 알아본 결과 다음과 같은 단점이 존재했습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SFX(작은 만큼 더 비싼) 규격의 파워서플라이&lt;/li&gt;
&lt;li&gt;디자인은 예쁘지만 하드디스크 베이의 간격이 너무 촘촘하여 실 사용 시 냉각에 문제 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 가격은 조금 더 비싸지만 표준 규격의 파워서플라이를 사용할 수 있고 하드디스크 냉각에 문제가 없으며 모양도 예쁜 6개의 디스크를 장착할 수 있는 Fractal Design Node 304를 구입하였습니다. 두 대 중 한대는 하드디스크 베이가 8개 있고 mATX 사이즈의 보드를 장착할 수 있는 Node 804를 원했으나 단종되어 구하기 어려워서 아쉽지만 304로 하고 향후 데이터가 늘어나면 케이스를 바꾸거나 디스크 용량을 증설하기로 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백업용 NAS 케이스는 사진에는 나오지 않았지만 브라보텍의 EX270을 구입하였습니다. 하드디스크 베이가 8개 있고 5.25인치 베이가 3개 있어서 디스크를 추가 장착할 수 있습니다. 가격은 리퍼로 4만원대에 구매하여 가성비가 뛰어납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 보드 및 CPU 선정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저전력을 위해 CPU와 보드가 통합되어 있는 제품을 알아보았습니다. 알리익스프레스에 관련 제품이 많았지만 실 사용 후기를 보면 전력효율이 좋지 못하여(이더넷 포트가 6개나 달려있고 여기서 Idle 파워를 많이 소비함 등) 제외하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싱글보드 컴퓨터 중 가장 우수하다고 생각되는, 제가 학생때부터 프로젝트에 활용하던 하드커널사의 오드로이드를 구입하였습니다. 원래 SATA포트가 2개밖에 없어서 NAS 목적으로는 부적합했는데 최근 새로 출시된 H4 시리즈는 SATA 포트가 4개이고 NVME M.2 슬롯에 ASM1166 SATA확장카드를 장착하여 SATA 포트를 6개까지 추가할 수 있습니다. 더 놀라운 점은 NVME M.2 슬롯 확장카드가 출시되었는데 M.2 슬롯을 2개 또는 4개로 확장하여(대역폭은 공유하지만 그래도 충분히 빠름) 각종 장치를 주렁주렁 연결할 수 있게 되었습니다. 또한 DDR5 메모리를 지원하며 메모리 공간의 10%를 In-Band ECC 메모리 영역으로 할당하여 일반 메모리를 ECC 메모리로 활용할 수 있는 점이 특징입니다. 저는 ECC 광신도이기 때문에 24시간 구동하는 시스템은 ECC RAM이 탑재되는 시스템만 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 ODROID H4+ 및 ODROID H4 Ultra를 1대씩 2대 구입하였는데 사양은 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image.webp&quot; data-origin-width=&quot;701&quot; data-origin-height=&quot;304&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u45tQ/btsIOIv9TNm/gB4OYt2lyXfnC6sdxHKb81/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u45tQ/btsIOIv9TNm/gB4OYt2lyXfnC6sdxHKb81/img.webp&quot; data-alt=&quot;ODROID H4 ULTRA&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u45tQ/btsIOIv9TNm/gB4OYt2lyXfnC6sdxHKb81/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu45tQ%2FbtsIOIv9TNm%2FgB4OYt2lyXfnC6sdxHKb81%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;701&quot; height=&quot;304&quot; data-filename=&quot;image.webp&quot; data-origin-width=&quot;701&quot; data-origin-height=&quot;304&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ODROID H4 ULTRA&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Intel N97(4Core/4Thread) 또는 N305(8 Core/8 Thread)&lt;/li&gt;
&lt;li&gt;4 x SATA, 1 x M.2 NVMe(PCIe 3.0 x 1) 2280&lt;/li&gt;
&lt;li&gt;2 x 2.5 Gbps Ethernet&lt;/li&gt;
&lt;li&gt;1 x Sodimm DDR5 4800(최대 48GB), In-Band ECC 설정 가능&lt;/li&gt;
&lt;li&gt;미니 ITX 보드로 활용 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 기타 부품 구입 경로 및 가격&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.1. 하드디스크&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가족사진/영상 저장용 디스크는 4TB CCTV용 디스크 새 거를 7.5만원에 2개 구입&lt;/li&gt;
&lt;li&gt;트레이딩 데이터(틱데이터 등) 저장용 디스크는 16TB 엔터프라이즈 등급 디스크 중고를 19만원에 3개 구입&lt;/li&gt;
&lt;li&gt;백업용 디스크는 레이드 깨지면 바로 다시 구축할 요량으로 10년 된 2TB 엔터프라이즈 등급 디스크를 5천원에 7개&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.2. NVMe SSD (캐시 및 VM 영역)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 대용량을 장착하지 않고 최대한 가성비로 장착 후 노후도를 파악하여 교체할 요량으로 부품을 구입하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신규 노트북에서 적출한 벌크 SSD 256GB 디램리스를 개당 2만1천원에 구입하였습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;VM 영역에 사용할 SSD는 디램이 있는 하이닉스 P31 512GB를 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.3. SATA 확장 카드&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;imagea.jpg&quot; data-origin-width=&quot;583&quot; data-origin-height=&quot;133&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxB1ZO/btsIPUbnh4Q/Nqwy5ZDobP2uWHf7tYGGv0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxB1ZO/btsIPUbnh4Q/Nqwy5ZDobP2uWHf7tYGGv0/img.jpg&quot; data-alt=&quot;ASM1166 SATA 라이저 카드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxB1ZO/btsIPUbnh4Q/Nqwy5ZDobP2uWHf7tYGGv0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxB1ZO%2FbtsIPUbnh4Q%2FNqwy5ZDobP2uWHf7tYGGv0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;583&quot; height=&quot;133&quot; data-filename=&quot;imagea.jpg&quot; data-origin-width=&quot;583&quot; data-origin-height=&quot;133&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ASM1166 SATA 라이저 카드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;알리에서 ASM1166 칩셋 기반의 SATA 확장 카드를 구입(2W 소비전력)&lt;/li&gt;
&lt;li&gt;알리에서 SAS 6G 어댑터(DELL PERC 310) 구입(백업 NAS용): 6G의 경우 SFF-8086 또는 SFF-8087 케이블 필요. 12G의 경우 SFF-8643 규격의 케이블 필요(6G 어댑터 소비전력 6W, 12G 어댑터 10W, 별도 쿨링팬 장착 필요)&lt;/li&gt;
&lt;li&gt;알리에서 SATA 4포트/6포트 케이블 구입&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.4. 램&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대 용량 48GB 지원이라 48GB 램을 알아보니 특수규격이라 단가가 너무 비싸서 팀그룹 16GB와 32GB를 구입. 삼성 시금치램은 중고로 8만원도 보았으나 삼성 DDR5 불량률이 매우 높다고 들어서 팀그룹(하이닉스 램 중 오버클럭 잘되는 양품 선별 제품) 신품으로 구매&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.5. 파워서플라이&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신품 17만원짜리 FSP HYDRO G PRO 850W를 보증 7년 남은 거 중고로 5만원씩에 3개 구입&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.6. 기타 부품&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PWM 팬허브: 하드디스크 유휴일 때 케이스 팬 3개 작동되지 않도록 CPU 팬과 연동하기 위하여 구입&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.7. NAS OS&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영 NAS에는 UNRAID, 백업 NAS에는 TrueNAS Scale 적용&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;UNRAID: 유료 NAS OS. 일반적인 RAID처럼 패리티 데이터를 각 디스크에 분산저장하지 않고 패리티 디스크를 별도로 둡니다. 때문에 파일 접근 시 해당 파일이 존재하는 디스크만 작동되므로 전력을 절감할 수 있고 파일이 분산 저장되지 않으므로 최악의 경우 고장난 디스크를 제외한 나머지 디스크의 데이터는 온전하게 보존됩니다. 캐시디스크를 SSD로 지정해 두면 업데이트 작업 시 하드디스크를 바로 구동하지 않고 SSD 누적시켰다가 나중에 배치 작업으로 한 번에 업데이트하는 방식으로 전력을 추가 절감합니다. 서로 다른 용량의 디스크를 갖다 붙여도 모든 용량을 활용할 수 있습니다. 도커 및 VM 운영이 가능하며 시놀로지의 편의성을 제공하는 것과 유사한 각종 앱을 사용할 수 있습니다. 유료이지만 위와 같은 장점으로 개인이 운영하기에 정말 좋습니다.&lt;/li&gt;
&lt;li&gt;TrueNAS Scale: 매우 높은 압축률을 자랑하는 ZFS 파일시스템 사용으로 디스크 공간을 많이 절약할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상으로 NAS를 직접 구축하기 위한 하드웨어와 OS 선정에 대해 알아보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 시놀로지와 가성비 비교&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시놀로지 4베이 나스 DS423+와의 비교입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;imageoxy.png&quot; data-origin-width=&quot;1015&quot; data-origin-height=&quot;348&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mWdFO/btsIQ18rEQ2/EXuzKD8iabm4LNTQaRPfKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mWdFO/btsIQ18rEQ2/EXuzKD8iabm4LNTQaRPfKk/img.png&quot; data-alt=&quot;DS423+ 스펙 및 가격&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mWdFO/btsIQ18rEQ2/EXuzKD8iabm4LNTQaRPfKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmWdFO%2FbtsIQ18rEQ2%2FEXuzKD8iabm4LNTQaRPfKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1015&quot; height=&quot;348&quot; data-filename=&quot;imageoxy.png&quot; data-origin-width=&quot;1015&quot; data-origin-height=&quot;348&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;DS423+ 스펙 및 가격&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5.1. 자작나스 제작비&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8코어 32GB 고성능 지향 NAS의 견적은 다음과 같습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 162px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 20px;&quot;&gt;항목&lt;/td&gt;
&lt;td style=&quot;width: 24.3023%; text-align: center; height: 20px;&quot;&gt;부품명&lt;/td&gt;
&lt;td style=&quot;width: 25.6977%; text-align: center; height: 20px;&quot;&gt;수량&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 20px;&quot;&gt;가격&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;CPU 및 보드&lt;/td&gt;
&lt;td style=&quot;width: 24.3023%; height: 17px; text-align: center;&quot;&gt;ODROID H4 ULTRA&lt;/td&gt;
&lt;td style=&quot;width: 25.6977%; height: 17px; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;297,000원(신품)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;메모리&lt;/td&gt;
&lt;td style=&quot;width: 24.3023%; height: 17px;&quot;&gt;DDR5 PC5-44800 32GB&lt;/td&gt;
&lt;td style=&quot;width: 25.6977%; height: 17px; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;117,000원(신품)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;케이스&lt;/td&gt;
&lt;td style=&quot;width: 24.3023%; height: 17px; text-align: center;&quot;&gt;Fractal Design Node 304&lt;/td&gt;
&lt;td style=&quot;width: 25.6977%; height: 17px; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;97,000원(리퍼)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;파워서플라이&lt;/td&gt;
&lt;td style=&quot;width: 24.3023%; height: 17px; text-align: center;&quot;&gt;FSP HYDRO G PRO 850W&lt;/td&gt;
&lt;td style=&quot;width: 25.6977%; height: 17px; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;50,000원(중고)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;SSD&lt;/td&gt;
&lt;td style=&quot;width: 24.3023%; height: 17px; text-align: center;&quot;&gt;WD BLUE 1TB, P31 512GB&lt;/td&gt;
&lt;td style=&quot;width: 25.6977%; height: 17px; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;163,000원(신품)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 20px;&quot;&gt;하드디스크&lt;/td&gt;
&lt;td style=&quot;width: 24.3023%; height: 20px; text-align: center;&quot;&gt;WD ULTRASTART 16TB&lt;/td&gt;
&lt;td style=&quot;width: 25.6977%; height: 20px; text-align: center;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 20px; text-align: center;&quot;&gt;570,000원(중고)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 20px;&quot;&gt;케이블 및 악세사리&lt;/td&gt;
&lt;td style=&quot;width: 24.3023%; height: 20px; text-align: center;&quot;&gt;케이블, ASM1166, 팬허브&lt;/td&gt;
&lt;td style=&quot;width: 25.6977%; height: 20px; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 20px; text-align: center;&quot;&gt;43,000원(신품)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 17px;&quot;&gt;OS&lt;/td&gt;
&lt;td style=&quot;width: 24.3023%; height: 17px; text-align: center;&quot;&gt;UNRAID STARTER&lt;/td&gt;
&lt;td style=&quot;width: 25.6977%; height: 17px; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;65,000원(신품)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;합계: 1,402,000원, 하드디스크 제외 하드웨어 비용:832,000원&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4코어 16GB 저전력 지향 NAS의 견적은 다음과 같습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 162px;&quot; border=&quot;1&quot; data-ke-style=&quot;style8&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;항목&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;부품명&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;수량&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;가격&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;CPU 및 보드&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;ODROID H4 PLUS&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;187,700원(신품)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;메모리&lt;/td&gt;
&lt;td&gt;DDR5 PC5-44800 16GB&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;62,500원(신품)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;케이스&lt;/td&gt;
&lt;td style=&quot;width: 24.3023%; height: 17px; text-align: center;&quot;&gt;Fractal Design Node 304&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;97,000원(리퍼)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;파워서플라이&lt;/td&gt;
&lt;td style=&quot;width: 24.3023%; height: 17px; text-align: center;&quot;&gt;FSP HYDRO G PRO 850W&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;50,000원(중고)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;SSD&lt;/td&gt;
&lt;td style=&quot;width: 24.3023%; height: 17px; text-align: center;&quot;&gt;PM9B1 256, SN740 256&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;42,000원(벌크)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;하드디스크&lt;/td&gt;
&lt;td style=&quot;width: 24.3023%; height: 20px; text-align: center;&quot;&gt;시게이트 스카이호크 4TB&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;150,000원(신품)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;케이블 및 악세사리&lt;/td&gt;
&lt;td style=&quot;width: 24.3023%; height: 20px; text-align: center;&quot;&gt;케이블, ASM1166, 팬허브&lt;/td&gt;
&lt;td style=&quot;width: 25.6977%; height: 20px; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 20px; text-align: center;&quot;&gt;43,000원(신품)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;OS&lt;/td&gt;
&lt;td style=&quot;width: 24.3023%; height: 17px; text-align: center;&quot;&gt;UNRAID STARTER&lt;/td&gt;
&lt;td style=&quot;width: 25.6977%; height: 17px; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;65,000원(신품)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;합계: 697,200원, 하드디스크 제외 하드웨어 비용:547,200원&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;4코어(8스레드) 32GB 백업 NAS의 견적은 다음과 같습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 162px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;항목&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;부품명&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;수량&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;가격&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;CPU&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;Xeon E3-1280v5&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;5,000원(중고)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;보드&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;ASUS P10S-M WS&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;85,000원(중고)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;메모리&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;DDR4 16GB ECC/REG&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;93,000원(중고)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;케이스&lt;/td&gt;
&lt;td style=&quot;width: 24.3023%; height: 17px; text-align: center;&quot;&gt;브라보텍 EX270&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;47,000원(리퍼)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;파워서플라이&lt;/td&gt;
&lt;td style=&quot;width: 24.3023%; height: 17px; text-align: center;&quot;&gt;FSP HYDRO G PRO 850W&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;50,000원(중고)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;SSD&lt;/td&gt;
&lt;td style=&quot;width: 24.3023%; height: 17px; text-align: center;&quot;&gt;PM9B1 256GB&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;21,000원(벌크)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;하드디스크&lt;/td&gt;
&lt;td style=&quot;width: 24.3023%; height: 20px; text-align: center;&quot;&gt;시게이트 기업용 2TB&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;7&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;35,000원(중고)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;그래픽카드&lt;/td&gt;
&lt;td style=&quot;width: 24.3023%; text-align: center;&quot;&gt;RX550&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;26,000(중고)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;케이블 및 악세사리&lt;/td&gt;
&lt;td style=&quot;width: 24.3023%; height: 20px; text-align: center;&quot;&gt;케이블, PERC 310, 녹투아&lt;/td&gt;
&lt;td style=&quot;width: 25.6977%; height: 20px; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 20px; text-align: center;&quot;&gt;70,000원(신품)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;OS&lt;/td&gt;
&lt;td style=&quot;width: 24.3023%; height: 17px; text-align: center;&quot;&gt;TrueNAS SCALE&lt;/td&gt;
&lt;td style=&quot;width: 25.6977%; height: 17px; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;0원&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;합계: 432,000원&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;제작비용 697,200원짜리 자작나스와 DS423+ 비교입니다.&lt;/p&gt;
&lt;table style=&quot;color: #333333; text-align: start; border-collapse: collapse; width: 100%; height: 200px;&quot; border=&quot;1&quot; data-ke-style=&quot;style12&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;text-align: center; width: 14.5348%; height: 20px;&quot;&gt;비교 항목&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 28.7211%; height: 20px;&quot;&gt;자작나스(ODROID H4+ 기반)&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 28.0232%; height: 20px;&quot;&gt;시놀로지 DS423+&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 28.6047%; height: 20px;&quot;&gt;결과&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;text-align: center; width: 14.5348%; height: 20px;&quot;&gt;가격&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 28.7211%; height: 20px;&quot;&gt;697,200원&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 28.0232%; height: 20px;&quot;&gt;778,000원&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 28.6047%; height: 20px;&quot;&gt;80,800원 저렴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;text-align: center; width: 14.5348%; height: 20px;&quot;&gt;하드베이&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 28.7211%; height: 20px;&quot;&gt;6베이&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 28.0232%; height: 20px;&quot;&gt;4베이&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 28.6047%; height: 20px;&quot;&gt;2베이 더 많음, 케이스 교체시 10베이 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;text-align: center; width: 14.5348%; height: 20px;&quot;&gt;CPU&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 28.7211%; height: 20px;&quot;&gt;4C4T 3.6GHz 6MB 12W TDP&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 28.0232%; height: 20px;&quot;&gt;4C4T 2.7GHz 4MB 10W TDP&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 28.6047%; height: 20px;&quot;&gt;전성비 우위&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;text-align: center; width: 14.5348%; height: 20px;&quot;&gt;&lt;span style=&quot;background-color: #efefef; color: #333333; text-align: center;&quot;&gt;메모리&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 28.7211%; height: 20px;&quot;&gt;16GB&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 28.0232%; height: 20px;&quot;&gt;2GB&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 28.6047%; height: 20px;&quot;&gt;10GB 더 많아 도커, VM 운영원활, ECC 메모리 기능 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;text-align: center; width: 14.5348%; height: 20px;&quot;&gt;랜포트&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 28.7211%; height: 20px;&quot;&gt;2.5Gbps x 2 포트&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 28.0232%; height: 20px;&quot;&gt;1Gbps x 2 포트&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 28.6047%; height: 20px;&quot;&gt;더 빠른 데이터 전송 속도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;text-align: center; width: 14.5348%; height: 20px;&quot;&gt;NVMe캐시&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 28.7211%; height: 20px;&quot;&gt;지원&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 28.0232%; height: 20px;&quot;&gt;미지원&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 28.6047%; height: 20px;&quot;&gt;시놀로지는 M.2 SATA 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;text-align: center; width: 14.5348%; height: 20px;&quot;&gt;앱 생태계&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 28.7211%; height: 20px;&quot;&gt;UNRAID 생태계&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 28.0232%; height: 20px;&quot;&gt;DSM 생태계&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 28.6047%; height: 20px;&quot;&gt;러닝커브 좀 더 높음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;6. 총평&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;고성능 지향 NAS의 경우 시놀로지와 하드웨어성능/가격 비교시 가격 대 성능비가 압도적이므로 자작을 권장&lt;/li&gt;
&lt;li&gt;저전력 지향 NAS의 경우 하드웨어 가격은 비슷함. CPU의 TDP는 자작나스가 2W 더 높으나 시놀로지는 RAID 풀의 모든 디스크가 상시 구동되므로 실제 소비전력은 시놀로지가 더 높을 것으로 예상. 활발한 국내 커뮤니티와 편리한 앱 접근성, 성능이 크게 중요하지 않을 경우 시놀로지 구매해도 무방. 다만 4베이로 한정지었을 경우이고 자작NAS는 케이스갈이 할 경우 M.2 to SATA 모듈로 10베이 이상 구성하여 사용 가능.&lt;/li&gt;
&lt;li&gt;백업용 NAS는 중고 부품으로만 드래곤볼 해도 충분&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Tech</category>
      <category>diy nas</category>
      <category>NAS</category>
      <category>nas diy</category>
      <category>truenas</category>
      <category>unraid</category>
      <category>나스 부품</category>
      <category>자작나스</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/67</guid>
      <comments>https://noyecube.tistory.com/entry/NAS-%EC%9E%90%EC%9E%91%EB%82%98%EC%8A%A4-NAS-DIY-%EA%B5%AC%EC%B6%95-%ED%95%98%EB%93%9C%EC%9B%A8%EC%96%B4-%EC%A1%B0%EB%A6%BD#entry67comment</comments>
      <pubDate>Sat, 27 Jul 2024 02:52:58 +0900</pubDate>
    </item>
    <item>
      <title>블록암호 알고리즘 모듈(LEA) 적용 및 테스트</title>
      <link>https://noyecube.tistory.com/entry/%EB%B8%94%EB%A1%9D%EC%95%94%ED%98%B8-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%AA%A8%EB%93%88LEA-%EC%A0%81%EC%9A%A9-%EB%B0%8F-%ED%85%8C%EC%8A%A4%ED%8A%B8</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 Windows C/C++ 프로젝트에 블록암호 알고리즘(LEA) 모듈을 적용하기 위한 기초 내용 조사 및 실제 적용 테스트에 관한 내용을 다룹니다. 저는 클라이언트 프로그램에 서버 계정과 패스워드를 저장하여 자동 로그인 기능을 구현하기 위해 블록암호 알고리즘 모듈을 적용하려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 암호알고리즘 용어 정리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;평문(Plaintext, P) : 암호화되지 않은 평범한 데이터 또는 메시지&lt;/li&gt;
&lt;li&gt;암호문(Ciphertext, C) : 암호화된 형태의 데이터 또는 메시지&lt;/li&gt;
&lt;li&gt;암호화(Encryption, E) : 평문을 암호문으로 변환하는 과정&lt;/li&gt;
&lt;li&gt;복호화(Decryption, D) : 암호문을 본래의 평문으로 복원하는 과정&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 각 기준별 암호알고리즘 분류&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공개키암호: RSAES(2048, 3072)&lt;/li&gt;
&lt;li&gt;키 설정: DH, ECDH&lt;/li&gt;
&lt;li&gt;전자서명: RSA-PSS, KCDSA, EC-KCDSA, ECDSA&lt;/li&gt;
&lt;li&gt;블록암호: SEED, LEA, HIGHT, ARIA&lt;/li&gt;
&lt;li&gt;운영모드: ECB, CBC, CFB, OFB, CTR, CCM, GCM&lt;/li&gt;
&lt;li&gt;해시함수: LSH, SHA-2, SHA-3&lt;/li&gt;
&lt;li&gt;메시지인증: GMAC, HMAC, CMAC&lt;/li&gt;
&lt;li&gt;난수발생기: HMAC_DRBG, CTR_DRBG, HASH_DRBG&lt;/li&gt;
&lt;li&gt;키 유도: KBKDF(HMAC, CMAC), PBKDF(HMAC)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 블록암호와 운영모드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블록암호에는 대칭키암호, 비밀키암호, 관용 암호 방식이 있으며 현재 널리 사용되는 블록 크기는 128비트 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;암호 알고리즘의 비밀 정도는 키의 비밀성에 의존하므로 키 관리를 잘 해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고정된 크기의 블록 단위로 데이터를 암호화하므로 실제 암호화 할 데이터 크기가 작으면 임의의 패딩데이터를 삽입합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;암호화 키와 복호화 키가 같으며 비밀키를 사용하여 동일한 길이(블록단위)의 비트 문자열을 고정된 길이의 비트 문자열로 변환하는 암호입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 블록암호는 DES, AES, ARIA, SEED, LEA, HIGHT 등이 존재합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;블록암호&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;표준화 연도&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;블록길이&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;키 길이&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;SEED&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;1999년(TTA)&lt;br /&gt;2005년(ISO)&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;128 비트&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;128 비트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;ARIA&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;2004년(KS)&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;128 비트&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;128, 192, 256 비트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;HIGHT&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;2006년(TTA)&lt;br /&gt;2010년(ISO)&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;64 비트&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;128 비트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;LEA&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;2013년(TTA)&lt;br /&gt;2019년(ISO)&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;128 비트&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;128, 192, 256 비트&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블록암호 자체만으로는 정해진 입력 길이(LEA의 경우 128비트 = 16바이트)의 데이터에 대한 암호화 및 복호화만 가능합니다. 하지만 현실 세계의 데이터는 대부분 가변 길이이기 때문에 블록 암호만으로는 충분하지 않습니다. 이때 등장하는 것이 바로 블록암호 운영모드입니다. 운영모드는 블록암호를 반복적으로 적용하여 가변 길이 데이터를 안전하게 암호화하는 방법을 제공합니다. 운영모드는 여러 모드가 있으며 각기 다른 안정성과 특징을 가지고 있기 때문에 환경에 따라서 적절한 운영모드를 적용할 필요가 있습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;운영 모드&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;특징&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;장점&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;단점&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: left;&quot;&gt;ECB(Electronic CodeBook)&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;가장 간단한 모드.&lt;br /&gt;각 블록을 독립적으로 암호화&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;구현이 간편&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;동일한 평문 블록은 동일한 암호문으로 변환되어 패턴 노출 가능성이 높음. &lt;br /&gt;암호 분석에 취약&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: left;&quot;&gt;CBC(Cipher Block Chaining)&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;이전 암호문 블록을 다음 블록 암호화에 활용. 연속된 블록 간 상관관계 형성&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;ECB보다 강력한 암호화 제공&lt;br /&gt;오류전파 가능성 감소&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;복잡도 상승&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: left;&quot;&gt;CFB(Cipher Feedback)&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;이전 암호문 블록을 XOR 연산에 활용하여 현재 블록 암호화&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;CBC와 유사한 강력한 암호화&lt;br /&gt;초기값 공유 필애&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;&lt;span style=&quot;background-color: #f9f9f9; color: #333333; text-align: start;&quot;&gt;복잡도 상승&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: left;&quot;&gt;OFB(Output Feedback)&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;암호화된 블록을 누적하여 다음 블록 암호화에 활용&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;암호화와 복호화 동작이 동일&lt;br /&gt;초기값 공유 필요&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;복잡도 상승&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: left;&quot;&gt;CTR(Counter)&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;카운터 값을 암호화하여 암호 스트림 생성&lt;br /&gt;평문과 XOR 연산 수행&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;초기값 공유만으로도 동기화&lt;br /&gt;암호화와 복호화 속도가 빠름&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;복잡도 상승&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: left;&quot;&gt;CCM&lt;br /&gt;(Counter with CBC-MAC)&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;CTR 모드의 장점에 MAC 인증 추가&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;암호화와 인증을 동시에 제공&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;복잡도 상승&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: left;&quot;&gt;GCM(Galois/Counter Mode)&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;CTR 모드의 장점에 AAD(Additional Authentication Data) 인증 기능 추가&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;CCM보다 향상된 인증 성능 제공&lt;br /&gt;NIST에서 GCM-AES 권장&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;복잡도 상승&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: left;&quot;&gt;CMAC(Cipher-based Message Authentication Code)&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;CBC 모드 기반 MAC 코드 생성&lt;br /&gt;메시지 인증에 특화&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;높은 인증 보안 제공&lt;/td&gt;
&lt;td style=&quot;width: 25%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 운영모드로 GCM을 선택하여 C/C++ 프로젝트에 적용해 보겠습니다. GCM 선택 이유는 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GCM은 CTR 모드에 인증 기능을 추가하여 데이터 무결성과 진위 여부를 동시에 보장하며 비밀번호와 같은 민감 정보 보호에 적합&lt;/li&gt;
&lt;li&gt;NIST 권장: 미국 국립 표준 기술원에서 GCM-AES를 표준 암호화 알고리즘으로 권장하고 있음&lt;/li&gt;
&lt;li&gt;효율성: GCM은 암호화와 인증을 동시에 수행하며, GCM-AES는 하드웨어 가속이 지원되어 빠른 처리속도 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 소프트웨어에 블록암호 적용시 유의사항&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;무결성 검증이 필요하며 무결성 검증키는 암호모듈 내부에 분산시켜 저장해야 함. 키를 분산 저장하지 않을 경우 hex dump를 통해서 쉽게 노출됨. 특히 키값을 전역변수로 저장하면 바이너리에 키값이 연속적으로 노출되므로 지역변수로 여기저기 분산하여 저장해야 함&lt;/li&gt;
&lt;li&gt;암호모듈 구성요소에 대한 무결성 검증은 동작 전 자가테스트에서 수행하고 검증이 실패할 경우 오류 상태로 천이하고 어떠한 암호연산도 수행되지 않아야 하며 데이터 출력 인터페이스를 통한 모든 데이터의 출력도 금지해야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 암호알고리즘 소스코드 다운로드 및 프로젝트 적용&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5.1. 소스코드 다운로드&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;국산 암호 알고리즘(SEED, HIGHT, ARIA, LEA, HAS-160, LSH, KCDSA/EC-KCDSA)은 KISA 암호이용활성화 페이지에서 설명서와 소스코드를 다운받을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;암호이용활성화&quot; href=&quot;https://seed.kisa.or.kr/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://seed.kisa.or.kr/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 중 블록암호 LEA 소스코드를 다운받아서 MFC 프로젝트에 적용해 보겠습니다. C 프로젝트 뿐 아니라 Java, Python에서도 사용할 수 있는 소스코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5.2. 라이브러리 파일 생성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows 용 MSVC 솔루션 프로젝트 파일이 있습니다. &quot;vs&quot;와 &quot;vs2008&quot;이 있는데 &quot;vs&quot;의 프로젝트 파일을 최신 버전으로 업그레이드하여 디버그 버전과 릴리즈 버전을 각각 빌드합니다. 릴리즈 버전 빌드시 에러가 발생하여 확인해본 결과 프로젝트 속성에 컴파일러가 MSVC 2013으로 고정되어 있었습니다. 이를 정정 후 빌드하면 &quot;bin&quot; 폴더와 &quot;Debug&quot; 폴더에 결과물이 생성됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;20240509_1.png&quot; data-origin-width=&quot;624&quot; data-origin-height=&quot;365&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/phvVN/btsHiI5fKbh/6Ck3deMKKzTYnVlNEewXCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/phvVN/btsHiI5fKbh/6Ck3deMKKzTYnVlNEewXCK/img.png&quot; data-alt=&quot;libLEA 결과 파일 폴더&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/phvVN/btsHiI5fKbh/6Ck3deMKKzTYnVlNEewXCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FphvVN%2FbtsHiI5fKbh%2F6Ck3deMKKzTYnVlNEewXCK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;422&quot; height=&quot;247&quot; data-filename=&quot;20240509_1.png&quot; data-origin-width=&quot;624&quot; data-origin-height=&quot;365&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;libLEA 결과 파일 폴더&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5.3. 프로젝트 설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 결과 파일을 프로젝트 경로에 복사합니다. 저는 &quot;prebuilt\LEA&quot; 경로에 복사하였습니다. 헤더파일은 &quot;lea.h&quot;와 &quot;config.h&quot;가 필요합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;20240509_2.png&quot; data-origin-width=&quot;619&quot; data-origin-height=&quot;308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/elP5v7/btsHh7YMbmo/E7LLF1xvSiwGa9KwSL8YYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/elP5v7/btsHh7YMbmo/E7LLF1xvSiwGa9KwSL8YYk/img.png&quot; data-alt=&quot;프로젝트 경로에 dbg, rel 버전 각각 복사&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/elP5v7/btsHh7YMbmo/E7LLF1xvSiwGa9KwSL8YYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FelP5v7%2FbtsHh7YMbmo%2FE7LLF1xvSiwGa9KwSL8YYk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;619&quot; height=&quot;308&quot; data-filename=&quot;20240509_2.png&quot; data-origin-width=&quot;619&quot; data-origin-height=&quot;308&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;프로젝트 경로에 dbg, rel 버전 각각 복사&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;프로젝트 속성에 디버그모드와 릴리즈모드를 구분하여 각 라이브러리 파일에 대해 링커 설정을 해줍니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C/C++ &amp;gt; General &amp;gt; Additional Include Directories: $(ProjectDir)\prebuilt\ (Debug/Release 공통)&lt;/li&gt;
&lt;li&gt;Linker &amp;gt; General &amp;gt; Additional Library Directories: $(ProjectDir)\prebuilt\LEA (Debug/Release 공통)&lt;/li&gt;
&lt;li&gt;Linker &amp;gt; Input &amp;gt; Additional Dependencies: libLEA_dbg.lib 추가 (Debug)&lt;/li&gt;
&lt;li&gt;Linker &amp;gt; Input &amp;gt; Additional Dependencies: libLEA_rel.lib 추가 (Release)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5.4. 빌드 테스트&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MFC 기준 APP::InitInstance() 에서 다음의 테스트 코드로 빌드 테스트를 하였습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1715240335336&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;prebuilt/LEA/lea.h&quot;

/* LEA Test */
LEA_GCM_CTX ctx;
unsigned char mk[16] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF };
int mk_len = 16;

lea_gcm_init(&amp;amp;ctx, mk, mk_len);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상으로 국산 경량 블록암호알고리즘 모듈인 LEA에 대해 알아보았습니다.&lt;/p&gt;</description>
      <category>프로그래밍/C | C++</category>
      <category>ARIA</category>
      <category>LEA</category>
      <category>liblea</category>
      <category>SEED</category>
      <category>블록암호</category>
      <category>암호모듈</category>
      <category>암호알고리즘</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/66</guid>
      <comments>https://noyecube.tistory.com/entry/%EB%B8%94%EB%A1%9D%EC%95%94%ED%98%B8-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%AA%A8%EB%93%88LEA-%EC%A0%81%EC%9A%A9-%EB%B0%8F-%ED%85%8C%EC%8A%A4%ED%8A%B8#entry66comment</comments>
      <pubDate>Fri, 10 May 2024 11:08:41 +0900</pubDate>
    </item>
    <item>
      <title>[MFC] SQLite 연동 및 사용법</title>
      <link>https://noyecube.tistory.com/entry/MFC-SQLite-%EC%97%B0%EB%8F%99-%EB%B0%8F-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;SQLite는 작고, 빠르고, 믿을 수 있는 세계에서 가장 많이 사용되는 관계형 데이터베이스로서 1조 개가 넘는 SQLite 데이터베이스가 사용되고 있다고 합니다. 또한 모든 iOS, Android, Mac, Windows 디바이스 및 인터넷브라우저와 IoT 디바이스에서 사용할 수 있습니다. 단일 데이터베이스 파일의 최대 크기는 281TB라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. SQLite 특징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작은 사이즈: MySQL이나 PostgreSQL과 같은 데이터베이스는 클라이언트/서버 데이터베이스서 일반적으로 자체 디스크와 메모리가 있는 서버에서 별도의 프로세스로 실행해야 합니다. SQLite는 이와 반대로 임베디드 데이터베이스입니다. 즉 데이터베이스가 디바이스, 서버 또는 스마트폰 자체에 내장됩니다. SQLite 데이터베이스는 독립적인 단일 디스크 파일이며 크로스 플랫폼이기 때문에 별도의 서버 구성이 필요 없습니다. SQLite 데이터베이스를 열고, 읽고, 쓰기 위해서는 750KB 이하의 초소형 SQLite 라이브러리만 있으면 됩니다.&lt;/li&gt;
&lt;li&gt;빠른 속도: SQLite는 별도의 서버가 필요하지 않고 단일 디스크 파일이기 때문에 데이터 사이에서 네트워크 지연이 없습니다. 그리고 fread(), fwrite() 같은 C 함수를 사용하여 파일시스템에 직접 쓸 때보다 35% 더 빠를 수 있다고 합니다.&lt;/li&gt;
&lt;li&gt;높은 신뢰성: SQLite는 세 명의 챔피언급 개발자가 개발 및 유지관리하고 있으며 다른 오픈소스 프로젝트처럼 임의의 사람들로부터 코드 기여를 받지 않고 있습니다. 덕분에 XZ 백도어 사건과 같이 외부인으로부터 코드가 오염될 경로가 원천 차단되어 보안에도 좋은 장점이 있습니다. 개발자들은 최소 2050년까지 SQLite를 지원할 것이라고 하였으며 문서화, 주석 처리, 크로스 플랫폼 및 하위 호환성을 유지하겠다고 약속했습니다. 또한 코드가 완전히 테스트된 상태로 유지되고 있는데&amp;nbsp; SQLite 소스코드가 15만 5천 개 이상의 소스 코드 줄로 구성되어 있을 때 테스트 코드와 테스트 스크립트의 소스코드는 9,200만 줄입니다. 9,200만 개의 라인의 테스트 소스코드로 15만 5천 개의 소스코드 줄을 테스트하는 것입니다. 즉, 코드보다 600배나 많은 테스트로 신뢰성을 보장하고 있습니다. 이를 위해 개발자들은 1년 내내 주당 60시간씩 일을 했다고 하네요.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. SQLite 다운로드 및 라이브러리 파일 연동하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows 환경의 MFC 프로젝트 기준으로 설명합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQLite 공식 홈페이지에서는 풀 소스코드, 아말감 버전 소스코드(모든 내용을 하나의 파일에 취합한 버전), x86/x64 DLL 을 다운로드할 수 있습니다. MFC를 위한 정적라이브러리 파일 생성은 매뉴얼에 내용이 없고 아말감 버전의 소스코드도 MFC 프로젝트에서는 일반적인 방법으로는 빌드가 안 되는 것을 확인하였습니다. 따라서 DLL을 다운로드하여 사용합니다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.1. 소스코드 등 다운로드&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;SQLite 홈페이지&quot; href=&quot;http://sqlite.org&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;http://sqlite.org&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1715149173402&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;SQLite Home Page&quot; data-og-description=&quot;SQLite is a C-language library that implements a small, fast, self-contained, high-reliability, full-featured, SQL database engine. SQLite is the most used database engine in the world. SQLite is built into all mobile phones and most computers and comes bu&quot; data-og-host=&quot;sqlite.org&quot; data-og-source-url=&quot;http://sqlite.org&quot; data-og-url=&quot;https://sqlite.org/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;http://sqlite.org&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;http://sqlite.org&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;SQLite Home Page&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;SQLite is a C-language library that implements a small, fast, self-contained, high-reliability, full-featured, SQL database engine. SQLite is the most used database engine in the world. SQLite is built into all mobile phones and most computers and comes bu&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;sqlite.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 사이트의 SQLite Download Page에서 amalgamation 타입의 Source Code와 Precompiled Binaries for Windows를 다운로드 받습니다. 저는 32-bit, 64-bit 프로젝트에 사용할 예정이라 32-bit도 받았으며 작성일 기준 파일명은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;sqlite-amalgamation-3450300.zip : sqlite3.h 헤더파일 들어있음&lt;/li&gt;
&lt;li&gt;sqlite-dll-win-x64-3450300.zip : sqlite3.dll(64비트)과 sqlite3.def 파일 들어있음&lt;/li&gt;
&lt;li&gt;sqlite-dll-win-x86-3450300.zip : sqlite3.dll(32비트)과 sqlite3.def 파일 들어있음&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.2. .lib 파일 생성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQLite의 DLL파일은 C언어로 작성되었지만 MFC 프로젝트는 C++ 언어로 작성됩니다. 따라서 C언어 함수를 C++ 프로그램에서 사용하기 위해서는 함수 이름과 매개 변수 형식을 C++ 컴파일러에게 알려줘야 하는데 이 역할을 하는 것이 sqlite3.def 파일입니다. C++ 컴파일러는 .def 파일을 통해 DLL 함수를 어떻게 사용해야 하는지 파악하고, 프로그램 코드를 적절하게 연결합니다. 따라서 이 과정에서 생성되는 .lib 파일은 독립적으로 사용할 수 있는 정적라이브러리 파일이 아니라 .def 파일을 기반으로 DLL 파일의 정보를 담은 파일이기 때문에 컴파일러는 .lib파일을 통해 DLL 함수에 대한 정보를 얻고 프로그램 코드와 DLL 함수를 연결합니다. MSVC 설치 시 패키지로 함께 설치되는 Developer Command Prompt를 활용하여 .lib 파일을 생성할 수 있습니다. &quot;lib&quot; 명령어를 사용하며 다음 x86 버전 생성 예제를 확인합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.jpg&quot; data-origin-width=&quot;388&quot; data-origin-height=&quot;189&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chyMj9/btsHiHYE835/F9TIugx3qscp2b5OAy8VUK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chyMj9/btsHiHYE835/F9TIugx3qscp2b5OAy8VUK/img.jpg&quot; data-alt=&quot;Developer Command Prompt&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chyMj9/btsHiHYE835/F9TIugx3qscp2b5OAy8VUK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchyMj9%2FbtsHiHYE835%2FF9TIugx3qscp2b5OAy8VUK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;388&quot; height=&quot;189&quot; data-filename=&quot;1.jpg&quot; data-origin-width=&quot;388&quot; data-origin-height=&quot;189&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Developer Command Prompt&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1715152069582&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;x86버전 &amp;gt; lib /def:sqlite3.def /machine:x86
x64버전 &amp;gt; lib /def:sqlite3.def /machine:x64

D:\dev\util\sqllite\sqlite-dll-win-x86-3450300&amp;gt;dir
D:\dev\util\sqllite\sqlite-dll-win-x86-3450300 디렉터리

2024-05-08  오후 03:43             8,226 sqlite3.def
2024-05-08  오후 03:43         2,485,760 sqlite3.dll
               2개 파일           2,493,986 바이트
               2개 디렉터리  972,652,843,008 바이트 남음

D:\dev\util\sqllite\sqlite-dll-win-x86-3450300&amp;gt;lib /def:sqlite3.def /machine:x86
Microsoft (R) Library Manager Version 14.38.33134.0
Copyright (C) Microsoft Corporation.  All rights reserved.

   Creating library sqlite3.lib and object sqlite3.exp

D:\dev\util\sqllite\sqlite-dll-win-x86-3450300&amp;gt;dir
D:\dev\util\sqllite\sqlite-dll-win-x86-3450300 디렉터리

2024-05-08  오후 03:43             8,226 sqlite3.def
2024-05-08  오후 03:43         2,485,760 sqlite3.dll
2024-05-08  오후 04:06            51,632 sqlite3.exp
2024-05-08  오후 04:06            86,004 sqlite3.lib
               4개 파일           2,631,622 바이트
               2개 디렉터리  972,652,703,744 바이트 남음

D:\dev\util\sqllite\sqlite-dll-win-x86-3450300&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sqlite.lib 파일을 확인할 수 있습니다.&amp;nbsp;헤더파일 사이즈는 627KB네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. MFC 프로젝트 적용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 프로젝트 파일 경로에 sqlite3.lib, sqlite3.h 파일을 복사하고 프로젝트 설정에서 참조 경로를 잡습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소스코드 경로에 &quot;prebuilt/SQLite&quot; 경로를 신규 생성하고 헤더파일과 라이브러리 파일을 복사합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행파일 존재 경로에 sqlite3.dll 파일을 복사합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2 (1).png&quot; data-origin-width=&quot;641&quot; data-origin-height=&quot;187&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/burkoA/btsHg8piwDy/bf5EGSx85t5agMh85R7Cb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/burkoA/btsHg8piwDy/bf5EGSx85t5agMh85R7Cb0/img.png&quot; data-alt=&quot;sqlite3.lib 파일과 sqlite3.h 를 프로젝트 폴더에 복사&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/burkoA/btsHg8piwDy/bf5EGSx85t5agMh85R7Cb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FburkoA%2FbtsHg8piwDy%2Fbf5EGSx85t5agMh85R7Cb0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;641&quot; height=&quot;187&quot; data-filename=&quot;2 (1).png&quot; data-origin-width=&quot;641&quot; data-origin-height=&quot;187&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;sqlite3.lib 파일과 sqlite3.h 를 프로젝트 폴더에 복사&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3 (1).png&quot; data-origin-width=&quot;393&quot; data-origin-height=&quot;219&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bymaEP/btsHj04kfVO/kvQTAud4cpkB6MWmnuJxD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bymaEP/btsHj04kfVO/kvQTAud4cpkB6MWmnuJxD1/img.png&quot; data-alt=&quot;프로젝트 솔루션탐색기에 헤더파일 추가&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bymaEP/btsHj04kfVO/kvQTAud4cpkB6MWmnuJxD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbymaEP%2FbtsHj04kfVO%2FkvQTAud4cpkB6MWmnuJxD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;393&quot; height=&quot;219&quot; data-filename=&quot;3 (1).png&quot; data-origin-width=&quot;393&quot; data-origin-height=&quot;219&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;프로젝트 솔루션탐색기에 헤더파일 추가&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 속성창에서 다음 항목을 설정합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Linker &amp;gt; General &amp;gt; Additional Library Directories: $(ProjectDir)\prebuilt\sqlite\&lt;/li&gt;
&lt;li&gt;Linker &amp;gt; Input &amp;gt; Additional Dependencies: sqlite3.lib&lt;/li&gt;
&lt;li&gt;C/C++ &amp;gt; Additional Include Directories: $(ProjectDir)\prebuilt\&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 테스트 코드 작성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQLite 버전을 출력하는 테스트 코드를 작성해 봅니다. 임의의 .cpp 파일에 다음의 내용을 작성 후 호출하여 결과를 확인해 봅니다.&lt;/p&gt;
&lt;pre id=&quot;code_1715154649695&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;prebuilt/SQLite/sqlite3.h&quot;

printf(&quot;[INFO] SQLite version (%s)\n&quot;, sqlite3_libversion());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5 (1).png&quot; data-origin-width=&quot;725&quot; data-origin-height=&quot;81&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bk0Egc/btsHievKenm/wjjPXx8hM1UCYL5Nn2q3T0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bk0Egc/btsHievKenm/wjjPXx8hM1UCYL5Nn2q3T0/img.png&quot; data-alt=&quot;테스트 코드 결과 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bk0Egc/btsHievKenm/wjjPXx8hM1UCYL5Nn2q3T0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbk0Egc%2FbtsHievKenm%2FwjjPXx8hM1UCYL5Nn2q3T0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;725&quot; height=&quot;81&quot; data-filename=&quot;5 (1).png&quot; data-origin-width=&quot;725&quot; data-origin-height=&quot;81&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;테스트 코드 결과 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상으로 MFC 프로젝트에서 SQLite를 사용하는 방법을 알아보았습니다.&lt;/p&gt;</description>
      <category>프로그래밍/C | C++</category>
      <category>MFC SQLite</category>
      <category>SQLite</category>
      <category>sqlite .lib</category>
      <category>sqlite dll</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/65</guid>
      <comments>https://noyecube.tistory.com/entry/MFC-SQLite-%EC%97%B0%EB%8F%99-%EB%B0%8F-%EC%82%AC%EC%9A%A9%EB%B2%95#entry65comment</comments>
      <pubDate>Wed, 8 May 2024 17:50:08 +0900</pubDate>
    </item>
    <item>
      <title>[MFC] Worker Thread 동적 관리기능 구현</title>
      <link>https://noyecube.tistory.com/entry/MFC-Worker-Thread-%EB%8F%99%EC%A0%81-%EA%B4%80%EB%A6%AC%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Thread를 원하는 시간에만 동작시키려면? 특정 조건에서 멈추거나 종료하려면? Thread 동작 상태를 파악하려면? Thread 동적 관리 기능이 필요합니다. 이번 포스팅에서는 프로그램 개발단계부터 디버깅 용도로 유용하게 활용할 수 있는 Thread 관리기능(생성, 서스펜션, 디스트로이) 구현 방안을 예제 기반으로 설명해 보겠습니다. 이하 본문에서는 Thread를 태스크(TASK)라 지칭하도록 하겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.&amp;nbsp; 동적 관리기능 요구사항&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;태스크 동적 관리기능의 구현에 앞서 반영할 요구사항은 다음과 같습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;프로그램 동작 상태에서 동적으로 태스크를 생성, 중지, 종료할 수 있어야 한다.&lt;/li&gt;
&lt;li&gt;활용 가능한 태스크 목록을 관리하고 정보를 제공할 수 있어야 한다.&lt;/li&gt;
&lt;li&gt;태스크 상태를 확인할 수 있어야 한다.&lt;/li&gt;
&lt;li&gt;태스크 추가/삭제가 용이하도록 일관된 인터페이스를 제공해야 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 요구사항에 맞춘 결과물(GUI화면)을 먼저 확인해 보도록 하겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;630&quot; data-origin-height=&quot;547&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8eML0/btsGTiFB5F9/uY3Wj0uJT3NaHVlTRZ7hck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8eML0/btsGTiFB5F9/uY3Wj0uJT3NaHVlTRZ7hck/img.png&quot; data-alt=&quot;Task 관리기능 사용 예&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8eML0/btsGTiFB5F9/uY3Wj0uJT3NaHVlTRZ7hck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8eML0%2FbtsGTiFB5F9%2FuY3Wj0uJT3NaHVlTRZ7hck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;630&quot; height=&quot;547&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;630&quot; data-origin-height=&quot;547&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Task 관리기능 사용 예&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;task list: 사용할 수 있는(프로그램에 탑재 되어있는 태스크) 태스크 목록을 보여줍니다. 이 예제에서는 &quot;data_collector&quot;라는 이름의 태스크 1개가 탑재되어 있습니다.&lt;/li&gt;
&lt;li&gt;task status: 생성되어 있는 태스크를 나열하고, 생성시각, 태스크 타입, 태스크 ID, 태스크 상태, 태스크명을 보여줍니다.&lt;/li&gt;
&lt;li&gt;task create -a &amp;lt;태스크명&amp;gt;: 태스크를 생성합니다.&lt;/li&gt;
&lt;li&gt;task [suspend | destroy] -a &amp;lt;TID&amp;gt;: 태스크를 중지 또는 종료합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 태스크 인터페이스&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 태스크들의 아버지라 할 수 있는 태스크 아빠 클래스(CTaskFather)를 구현하여 개별 태스크 클래스 구현시 태스크 아빠 클래스를 무조건 상속받도록 합니다. 또한 태스크&amp;nbsp;관리 클래스(CTaskMgr)를 구현하여 사용가능한 태스크 목록, 태스크 이름, 태스크 객체 관리 구조체 등을 관리하고 생성, 중지, 종료 등의 인터페이스를 구현합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;태스크 모듈에서 사용할 에러번호는 다음과 같이 정의하여 사용하기로 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1713946692690&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

/* ErrorNo for Task */
namespace TASK_ERR
{
	constexpr int ESUCCESS = 0;
	constexpr int EALREADY_CREATED = -1;
	constexpr int EBEGIN_THREAD_FAILURE = -2;
	constexpr int EHANDLE_NULLPTR = -3;
	constexpr int EPARAMETER_OUTOFRANGE = -4;
	constexpr int EOUTOF_RESOURCE = -5;
	constexpr int EDUMMY_FUNCTION = -6;	/* Not implemented function */
	constexpr int ETASK_ID_LOOKUP_FAILURE = -7;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.1. CTaskFather&lt;/h4&gt;
&lt;pre id=&quot;code_1713946734718&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;task_errno.h&quot;

class CTaskFather;

namespace TASK_FATHER
{
	constexpr int TASK_MSG_ESCAPE_LOOPER = (WM_APP)+1;

	typedef struct _LPVOID_PARAMS {
		LPVOID lpOpaque_1;
		LPVOID lpOpaque_2;
		LPVOID lpOpaque_3;
		LPVOID lpOpaque_4;
		LPVOID lpOpaque_5;
		_LPVOID_PARAMS() {
			ZeroMemory(this, sizeof(struct _LPVOID_PARAMS));
		}
	} TST_LPVOID_PARAMS, * PTST_LPVOID_PARAMS;	// LPVOID PARAMS TYPE for THREAD Creation

	typedef enum class enTaskStatus {
		UNKNOWN,
		RUNNING,
		SUSPENDED,
		SUSPENDED_ERR,	/* SUSPEND caused by Error */
		WAIT_REAP,		/* Escaped loop, waiting for Join() */
		MAX
	} TEN_TASK_STATUS;

	typedef struct _task_control_block_root {
		SRWLOCK srwlock;
		LPVOID ptheApp;
		CTaskFather* pTaskFather;
		TEN_TASK_STATUS enTaskStatus;
		AFX_THREADPROC pfnThreadProc;
		CWinThread* pThdLooper;
		HANDLE hEvent_Resume;
		HANDLE hEvent_Suspend;	/* Temporary usage as a creation event in the thread creation sequence */
		HANDLE hEvent_QuitThread;
		HANDLE hEvent_Exec;
		DWORD dwMsecInterval;			/* Timeout Milliseconds value for xxWaitForxxObjectsxx() */
		DWORD dwGetLastErrorValue;
		LPVOID lpOpaque_derived_1;	/* reserved for derived class */
		LPVOID lpOpaque_derived_2;	/* reserved for derived class */
		LPVOID lpOpaque_derived_3;	/* reserved for derived class */

		_task_control_block_root() {
			ZeroMemory(this, sizeof(struct _task_control_block_root));
			InitializeSRWLock(&amp;amp;srwlock);
		}
	} TST_TCB_ROOT, *PTST_TCB_ROOT;
}

class CTaskFather
{
protected:	/* Prevent create CTaskFather Without Derived child class */
	CTaskFather();
public:
	virtual ~CTaskFather();

public:
	virtual int iRun(void) { return TASK_ERR::EDUMMY_FUNCTION; };
	virtual int iCreate(void);
	virtual void vSuspend(void) { SetEvent(_m_stTcbRoot.hEvent_Suspend); };
	virtual void vResume(void) { SetEvent(_m_stTcbRoot.hEvent_Resume); };
	virtual int iDestroy(void);

	DWORD dwGetTaskId(void);
	inline TASK_FATHER::TEN_TASK_STATUS enGetTaskStatus(void) { return _m_stTcbRoot.enTaskStatus; }

protected:
	TASK_FATHER::TST_TCB_ROOT _m_stTcbRoot;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1713946833352&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;pch.h&quot;
#include &quot;CTaskFather.h&quot;

#include &quot;Noyecube.h&quot;

#include &quot;util/CAutolock.h&quot;

CTaskFather::CTaskFather() 
{
	CString cstrEventResume;
	CString cstrEventSuspend;
	CString cstrEventQuitThread;
	CString cstrEventExec;

	CAutoScopeSRWLOCKExclusive lock(&amp;amp;(_m_stTcbRoot.srwlock));

	_m_stTcbRoot.enTaskStatus = TASK_FATHER::TEN_TASK_STATUS::UNKNOWN;
	_m_stTcbRoot.pThdLooper = nullptr;

	cstrEventResume.Format(_T(&quot;CTaskFather_hEvent_Resume_%p&quot;), this);
	cstrEventSuspend.Format(_T(&quot;CTaskFather_hEvent_Suspend_%p&quot;), this);
	cstrEventQuitThread.Format(_T(&quot;CTaskFather_hEvent_QuitThread_%p&quot;), this);
	cstrEventExec.Format(_T(&quot;CTaskFather_hEvent_Exec_%p&quot;), this);

	_m_stTcbRoot.hEvent_Resume = CreateEvent(NULL, CREATE_EVENT_MANUAL_RESET, FALSE, cstrEventResume);
	_m_stTcbRoot.hEvent_Suspend = CreateEvent(NULL, CREATE_EVENT_MANUAL_RESET, FALSE, cstrEventSuspend);
	_m_stTcbRoot.hEvent_QuitThread = CreateEvent(NULL, CREATE_EVENT_MANUAL_RESET, FALSE, cstrEventQuitThread);
	_m_stTcbRoot.hEvent_Exec = CreateEvent(NULL, CREATE_EVENT_MANUAL_RESET, FALSE, cstrEventExec);

	if ((nullptr == _m_stTcbRoot.hEvent_Resume) || (nullptr == _m_stTcbRoot.hEvent_Suspend) ||
		(nullptr == _m_stTcbRoot.hEvent_Exec) || (nullptr == _m_stTcbRoot.hEvent_QuitThread)) {
		AfxMessageBox(_T(&quot;Fail:NoyeCube:CTaskFather(TID:%d).CreateEvent&quot;), GetCurrentThreadId());
		PostQuitMessage(0);
	}
};

CTaskFather::~CTaskFather()
{
	DPRINTF(&quot;ENTER\n&quot;);
	if (_m_stTcbRoot.pThdLooper) {
		DPRINTF(&quot;[DBG]  ==&amp;gt; SetEvent(_m_stTcb.hEvent_QuitThread);...\n&quot;);
		SetEvent(_m_stTcbRoot.hEvent_QuitThread);
		/*_m_stTcb.pThdLooper-&amp;gt;PostThreadMessage(TASK_FATHER::TASK_MSG_ESCAPE_LOOPER, 0, 0);*/
		DPRINTF(&quot;[DBG]  ==&amp;gt; Before WaitForSingleObject(_m_stTcb.pThdLooper-&amp;gt;m_hThread, INFINITE);...\n&quot;);
		WaitForSingleObject(_m_stTcbRoot.pThdLooper-&amp;gt;m_hThread, INFINITE);
		DPRINTF(&quot;[DBG]  ==&amp;gt; After WaitForSingleObject(_m_stTcb.pThdLooper-&amp;gt;m_hThread, INFINITE);...\n&quot;);
		_m_stTcbRoot.pThdLooper = nullptr;
	}

	_m_stTcbRoot.enTaskStatus = TASK_FATHER::TEN_TASK_STATUS::UNKNOWN;
	_m_stTcbRoot.dwGetLastErrorValue = 0;

	if (_m_stTcbRoot.hEvent_Exec) {
		CloseHandle(_m_stTcbRoot.hEvent_Exec);
		_m_stTcbRoot.hEvent_Exec = nullptr;
	}
	if (_m_stTcbRoot.hEvent_QuitThread) {
		CloseHandle(_m_stTcbRoot.hEvent_QuitThread);
		_m_stTcbRoot.hEvent_QuitThread = nullptr;
	}
	if (_m_stTcbRoot.hEvent_Resume) {
		CloseHandle(_m_stTcbRoot.hEvent_Resume);
		_m_stTcbRoot.hEvent_Resume = nullptr;
	}
	if (_m_stTcbRoot.hEvent_Suspend) {
		CloseHandle(_m_stTcbRoot.hEvent_Suspend);
		_m_stTcbRoot.hEvent_Suspend = nullptr;
	}

	DPRINTF(&quot;LEAVE\n&quot;);
}

int CTaskFather::iCreate(void)
{
	CAutoScopeSRWLOCKExclusive lock(&amp;amp;(_m_stTcbRoot.srwlock));

	if (nullptr != _m_stTcbRoot.pThdLooper) {
		return TASK_ERR::EALREADY_CREATED;
	}

	if (nullptr == _m_stTcbRoot.pfnThreadProc) {
		return TASK_ERR::EHANDLE_NULLPTR;
	}

	if ((nullptr == _m_stTcbRoot.hEvent_Resume) || (nullptr == _m_stTcbRoot.hEvent_Suspend)) {
		return TASK_ERR::EHANDLE_NULLPTR;
	}

	_m_stTcbRoot.ptheApp = (LPVOID)(&amp;amp;theApp);
	_m_stTcbRoot.pTaskFather = this;

	DPRINTF(&quot;[DEBUG] pTaskFather (%p)\n&quot;, this);

	ResetEvent(_m_stTcbRoot.hEvent_Resume);
	ResetEvent(_m_stTcbRoot.hEvent_Suspend);
	ResetEvent(_m_stTcbRoot.hEvent_QuitThread);
	ResetEvent(_m_stTcbRoot.hEvent_Exec);

	_m_stTcbRoot.pThdLooper = AfxBeginThread(_m_stTcbRoot.pfnThreadProc, (LPVOID)&amp;amp;_m_stTcbRoot);
	if (nullptr == _m_stTcbRoot.pThdLooper) {
		return TASK_ERR::EBEGIN_THREAD_FAILURE;
	}

	if (nullptr != _m_stTcbRoot.hEvent_Suspend) {	/* Temporary usage as a creation event in the thread creation sequence */
		DPRINTF(&quot;WAITING ThdLooper Creation...&quot;);
		WaitForSingleObject(_m_stTcbRoot.hEvent_Suspend, INFINITE);
		ResetEvent(_m_stTcbRoot.hEvent_Suspend);
		DPRINTF(&quot;Completed.\n&quot;);
	}

	SetEvent(_m_stTcbRoot.hEvent_Resume);	/* Signal to Entering ThdLooper Thread Loop*/

	return TASK_ERR::ESUCCESS;
};

int CTaskFather::iDestroy(void)
{
	CAutoScopeSRWLOCKExclusive lock(&amp;amp;(_m_stTcbRoot.srwlock));

	if (nullptr == _m_stTcbRoot.pThdLooper) {
		return TASK_ERR::EHANDLE_NULLPTR;
	}

	DPRINTF(&quot;[DBG]  ==&amp;gt; SetEvent(_m_stTcb.hEvent_QuitThread);...\n&quot;);
	SetEvent(_m_stTcbRoot.hEvent_QuitThread);
	/*_m_stTcb.pThdLooper-&amp;gt;PostThreadMessage(TASK_FATHER::TASK_MSG_ESCAPE_LOOPER, 0, 0);*/
	DPRINTF(&quot;[DBG]  ==&amp;gt; Before WaitForSingleObject(_m_stTcb.pThdLooper-&amp;gt;m_hThread, INFINITE);...\n&quot;);
	WaitForSingleObject(_m_stTcbRoot.pThdLooper-&amp;gt;m_hThread, INFINITE);
	DPRINTF(&quot;[DBG]  ==&amp;gt; After WaitForSingleObject(_m_stTcb.pThdLooper-&amp;gt;m_hThread, INFINITE);...\n&quot;);
	
	_m_stTcbRoot.pThdLooper = nullptr;
	_m_stTcbRoot.enTaskStatus = TASK_FATHER::TEN_TASK_STATUS::UNKNOWN;
	_m_stTcbRoot.dwGetLastErrorValue = 0;

	return TASK_ERR::ESUCCESS;
}

DWORD CTaskFather::dwGetTaskId(void)
{
	CAutoScopeSRWLOCKShared lock(&amp;amp;(_m_stTcbRoot.srwlock));

	if (_m_stTcbRoot.pThdLooper)
		return _m_stTcbRoot.pThdLooper-&amp;gt;m_nThreadID;
	else
		return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.2. CTaskMgr&lt;/h4&gt;
&lt;pre id=&quot;code_1713947099362&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CTaskFather.h&quot;

#include &quot;task_errno.h&quot;

#include &quot;DataCollector/CThdDataCollector.h&quot;


#define N_MAX_TASK_OBJECT	(16)

namespace TASK_MGR 
{
	typedef enum class _enTaskCategory {
		DATA_COLLECTOR,
		MAX
	} TEN_TASK_CAT;

	typedef struct _task_property {
		const TEN_TASK_CAT enTaskCat;
		TCHAR tchTaskName[64];
	} TST_TASK_PROP, *PTST_TASK_PROP;

	typedef struct _task_object_entity {
		BOOL boUsed;
		TEN_TASK_CAT enTaskCat;
		CTaskFather* pTaskFather;
		DWORD dwLooperTid;
		SYSTEMTIME tmTagCreated;
		//SYSTEMTIME tmTagCompleted;
		ULONGLONG ullTickTagCreated;
		_task_object_entity() {
			ZeroMemory(this, sizeof(_task_object_entity));
		}

		void EraseData(void) {
			ZeroMemory(this, sizeof(_task_object_entity));
		}
	} TST_TASK_OBJECT, *PTST_TASK_OBJECT;

	typedef struct _task_object_entity_pool {
		SRWLOCK srwlock;
		TST_TASK_OBJECT _m_astTaskObj[N_MAX_TASK_OBJECT];
		_task_object_entity_pool() {
			InitializeSRWLock(&amp;amp;srwlock);
		}
	} TST_TASK_OBJECT_POOL, *PTST_TASK_OBJECT_POOL;
}

using namespace TASK_MGR;
class CTaskMgr
{
public:
	CTaskMgr();
	~CTaskMgr();

public:
	int CreateTask(TEN_TASK_CAT enTaskCat);
	int CreateTask(LPCTSTR lpTaskName);
	int DestroyTask(TEN_TASK_CAT enTaskCat);
	int DestroyTask(LPCTSTR lpTaskName);

	int DestroyTask(DWORD dwTaskId);
	int SuspendTask(DWORD dwTaskId);
	int ResumeTask(DWORD dwTaskId);

public:
	PTST_TASK_OBJECT_POOL pRefer_PTST_TASK_OBJECT_POOL(void) { return (&amp;amp;_m_stTaskObjPool); }
	PTST_TASK_PROP pRefer_PTST_TASK_PROP(void) { return _m_astTaskProp; }
private:
	int _iAcquireSlot_TaskObjPool(void);

private:
	//CThdDataCollector* _m_pDataCollector;

	TST_TASK_PROP _m_astTaskProp[static_cast&amp;lt;unsigned int&amp;gt;(TEN_TASK_CAT::MAX)] = {
		{ TEN_TASK_CAT::DATA_COLLECTOR, _T(&quot;data_collector&quot;)}
	};

	TST_TASK_OBJECT_POOL _m_stTaskObjPool;

};&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1713947166042&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;pch.h&quot;
#include &quot;CTaskMgr.h&quot;


CTaskMgr::CTaskMgr()
{

}

CTaskMgr::~CTaskMgr()
{

}

int CTaskMgr::_iAcquireSlot_TaskObjPool(void)
{
	int iSlot = TASK_ERR::EOUTOF_RESOURCE;

	AcquireSRWLockExclusive(&amp;amp;(_m_stTaskObjPool.srwlock));
		for (int i = 0; i &amp;lt; N_MAX_TASK_OBJECT; i++) {
			if (FALSE == _m_stTaskObjPool._m_astTaskObj[i].boUsed) {
				_m_stTaskObjPool._m_astTaskObj[i].boUsed = TRUE;
				iSlot = i;
				break;
			}
		}
	ReleaseSRWLockExclusive(&amp;amp;(_m_stTaskObjPool.srwlock));

	return iSlot;
}

int CTaskMgr::CreateTask(TEN_TASK_CAT enTaskCat)
{
	int iPoolIndex;
	int iRet = TASK_ERR::EDUMMY_FUNCTION;

	if (TEN_TASK_CAT::MAX == enTaskCat) {
		return TASK_ERR::EPARAMETER_OUTOFRANGE;
	}

	iPoolIndex = this-&amp;gt;_iAcquireSlot_TaskObjPool();
	if (iPoolIndex &amp;lt; 0) {
		return TASK_ERR::EOUTOF_RESOURCE;
	}

	PTST_TASK_OBJECT p = &amp;amp;(_m_stTaskObjPool._m_astTaskObj[iPoolIndex]);

	switch (enTaskCat) {
	case TEN_TASK_CAT::DATA_COLLECTOR:
		p-&amp;gt;enTaskCat = TEN_TASK_CAT::DATA_COLLECTOR;
		p-&amp;gt;ullTickTagCreated = GetTickCount64();
		GetLocalTime(&amp;amp;(p-&amp;gt;tmTagCreated));

		p-&amp;gt;pTaskFather = new CThdDataCollector();

		iRet = p-&amp;gt;pTaskFather-&amp;gt;iCreate();
		if (iRet &amp;lt; 0) {
			DPRINTF(&quot;[ERROR] CreateTask (%s) Failure(%d).\n&quot;, _m_astTaskProp[static_cast&amp;lt;UINT&amp;gt;(enTaskCat)].tchTaskName, iRet);
		}
		else {
			p-&amp;gt;dwLooperTid = p-&amp;gt;pTaskFather-&amp;gt;dwGetTaskId();
			DPRINTF(&quot;[INFO] Task (%s) (tid:%u) created\n&quot;, _m_astTaskProp[static_cast&amp;lt;UINT&amp;gt;(enTaskCat)].tchTaskName, p-&amp;gt;dwLooperTid);
		}
		break;
	default:
		;
	}

	return iRet;
}

int CTaskMgr::CreateTask(LPCTSTR lpTaskName)
{
	TEN_TASK_CAT enCat = TEN_TASK_CAT::MAX;
	for (int i = 0; i &amp;lt; sizeof(_m_astTaskProp) / sizeof(_m_astTaskProp[0]); i++) {
		if (0 == _tcsncmp(_m_astTaskProp[i].tchTaskName, lpTaskName, _tcslen(_m_astTaskProp[i].tchTaskName))) {
			enCat = _m_astTaskProp[i].enTaskCat;
			break;
		}
	}

	return this-&amp;gt;CreateTask(enCat);
}

int CTaskMgr::DestroyTask(TEN_TASK_CAT enTaskCat)
{
	return TASK_ERR::EDUMMY_FUNCTION;
}

int CTaskMgr::DestroyTask(LPCTSTR lpTaskName)
{
	return TASK_ERR::EDUMMY_FUNCTION;
}

int CTaskMgr::DestroyTask(DWORD dwTaskId)
{
	int iPoolIdx = -1;

	AcquireSRWLockShared(&amp;amp;(_m_stTaskObjPool.srwlock));
	for (int i = 0; i &amp;lt; N_MAX_TASK_OBJECT; i++) {
		if (dwTaskId == _m_stTaskObjPool._m_astTaskObj[i].dwLooperTid) {
			iPoolIdx = i;
		}
	}
	ReleaseSRWLockShared(&amp;amp;(_m_stTaskObjPool.srwlock));

	if (iPoolIdx &amp;lt; 0) {
		return TASK_ERR::ETASK_ID_LOOKUP_FAILURE;
	}

	AcquireSRWLockExclusive(&amp;amp;(_m_stTaskObjPool.srwlock));
	_m_stTaskObjPool._m_astTaskObj[iPoolIdx].pTaskFather-&amp;gt;iDestroy();
	delete _m_stTaskObjPool._m_astTaskObj[iPoolIdx].pTaskFather;
	_m_stTaskObjPool._m_astTaskObj[iPoolIdx].EraseData();
	ReleaseSRWLockExclusive(&amp;amp;(_m_stTaskObjPool.srwlock));

	return TASK_ERR::ESUCCESS;
}

int CTaskMgr::SuspendTask(DWORD dwTaskId)
{
	int iPoolIdx = -1;

	AcquireSRWLockShared(&amp;amp;(_m_stTaskObjPool.srwlock));
	for (int i = 0; i &amp;lt; N_MAX_TASK_OBJECT; i++) {
		if (dwTaskId == _m_stTaskObjPool._m_astTaskObj[i].dwLooperTid) {
			iPoolIdx = i;
		}
	}
	ReleaseSRWLockShared(&amp;amp;(_m_stTaskObjPool.srwlock));

	if (iPoolIdx &amp;lt; 0) {
		return TASK_ERR::ETASK_ID_LOOKUP_FAILURE;
	}

	_m_stTaskObjPool._m_astTaskObj[iPoolIdx].pTaskFather-&amp;gt;vSuspend();

	return TASK_ERR::ESUCCESS;
}

int CTaskMgr::ResumeTask(DWORD dwTaskId)
{
	int iPoolIdx = -1;

	AcquireSRWLockShared(&amp;amp;(_m_stTaskObjPool.srwlock));
	for (int i = 0; i &amp;lt; N_MAX_TASK_OBJECT; i++) {
		if (dwTaskId == _m_stTaskObjPool._m_astTaskObj[i].dwLooperTid) {
			iPoolIdx = i;
		}
	}
	ReleaseSRWLockShared(&amp;amp;(_m_stTaskObjPool.srwlock));

	if (iPoolIdx &amp;lt; 0) {
		return TASK_ERR::ETASK_ID_LOOKUP_FAILURE;
	}

	_m_stTaskObjPool._m_astTaskObj[iPoolIdx].pTaskFather-&amp;gt;vResume();

	return TASK_ERR::ESUCCESS;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 샘플 태스크&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 수집을 수행하는 역할의 태스크(&quot;data_collector&quot;)를 위 인터페이스를 적용하여 구현해 보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무 내용도 없는 더미 태스크라고 보면 됩니다. CTaskFater를 상속받아 virtual function을 override 하였기 때문에 CTaskFather의 포인터로 제어 가능합니다. Loop 내에서 MsgWaitForMultipleObjectsEx를 적용하였기 때문에 중지, 종료 이벤트뿐 아니라 다양한 MSG를 수신할 수 있습니다. 또한 State-Machine 방식으로 동작하도록 사전에 각 State를 정의하였으며 Main-State가 있고 각 Main-State에 대한 Sub-State가 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1713947392655&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;task/CTaskFather.h&quot;

namespace TASK_DATA_COLLECTOR
{
	typedef enum class _enStateMain {
		UNKNOWN,
		INIT,		/* Check requirement */
		REAL_DATA_RECV,
		HISTORICAL_DATA_COLLECT,
		REAL_DATA_CORRECTION,
		IDLE_WAIT,
		MAX
	} TEN_STAT_MAIN;

	typedef enum class _enStateSub_INIT {
		ENTRY,
		MAX
	} TEN_STAT_SUB_INIT;

	typedef enum class _enStateSub_REAL_DATA_RECV {
		ENTRY,
		MAX
	} TEN_STAT_SUB_REAL_D_RCV;

	typedef enum class _enStateSub_HISTORICAL_DATA_COLLECT {
		ENTRY,
		MAX
	} TEN_STAT_SUB_HIST_D_COL;

	typedef enum class _enStateSub_REAL_DATA_CORRECTION {
		ENTRY,
		MAX
	} TEN_STAT_SUB_REAL_D_COR;

	typedef struct _task_control_block {
		SRWLOCK srwlock;

		BOOL boNoReturn;

		TEN_STAT_MAIN enStat_Main;
		TEN_STAT_MAIN enStatOld_Main;

		TEN_STAT_SUB_INIT enSub_INIT;
		TEN_STAT_SUB_INIT enSubOld_INIT;

		TEN_STAT_SUB_REAL_D_RCV enSub_REAL_D_RCV;
		TEN_STAT_SUB_REAL_D_RCV enSubOld_REAL_D_RCV;

		TEN_STAT_SUB_HIST_D_COL enSub_HIST_D_COL;
		TEN_STAT_SUB_HIST_D_COL enSubOld_HIST_D_COL;

		TEN_STAT_SUB_REAL_D_COR enSub_REAL_D_COR;
		TEN_STAT_SUB_REAL_D_COR enSubOld_REAL_D_COR;

		_task_control_block() {
			ZeroMemory(this, sizeof(struct _task_control_block));
			InitializeSRWLock(&amp;amp;srwlock);
		}
	} TST_TCB, * PTST_TCB;

}

using namespace TASK_DATA_COLLECTOR;

class CThdDataCollector : public CTaskFather
{
public:
	CThdDataCollector();
	~CThdDataCollector();

	int iRun(void) override;

	int iCreate(void) override;
	void vSuspend(void) override;
	void vResume(void) override;
	int iDestroy(void) override;
	static UINT ThdLooper(LPVOID pParam);

private:
	TST_TCB _m_stTcb;

private:
	void vRun_UNKNOWN(void);
	void vRun_INIT(void);
	void vRun_REAL_D_RCV(void);
	void vRun_HIST_D_COL(void);
	void vRun_REAL_D_COR(void);


};&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1713947403107&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;pch.h&quot;
#include &quot;CThdDataCollector.h&quot;

#include &quot;Noyecube.h&quot;

#include &quot;util/CAutolock.h&quot;

using namespace TASK_DATA_COLLECTOR;
using namespace TASK_FATHER;
using namespace TASK_ERR;

CThdDataCollector::CThdDataCollector()
{
	_m_stTcbRoot.pfnThreadProc = this-&amp;gt;ThdLooper;

	_m_stTcbRoot.lpOpaque_derived_1 = (LPVOID)(this);
	_m_stTcbRoot.lpOpaque_derived_2 = (LPVOID)(theApp.m_pXingMgr);

}

CThdDataCollector::~CThdDataCollector()
{

}

int CThdDataCollector::iCreate(void)	// override
{
	return CTaskFather::iCreate();
}

void CThdDataCollector::vSuspend(void)	// override
{
	CTaskFather::vSuspend();
}

void CThdDataCollector::vResume(void)	// override
{
	CTaskFather::vResume();
}

int CThdDataCollector::iDestroy(void)	// override
{
	return CTaskFather::iDestroy();
}

int CThdDataCollector::iRun(void)	// override
{

	do {

		_m_stTcb.boNoReturn = FALSE;

		switch (_m_stTcb.enStat_Main) {
		case TEN_STAT_MAIN::UNKNOWN:
			this-&amp;gt;vRun_UNKNOWN();
			break;
		case TEN_STAT_MAIN::INIT:
			this-&amp;gt;vRun_INIT();
			break;
		case TEN_STAT_MAIN::REAL_DATA_RECV:
			this-&amp;gt;vRun_REAL_D_RCV();
			break;
		case TEN_STAT_MAIN::HISTORICAL_DATA_COLLECT:
			this-&amp;gt;vRun_HIST_D_COL();
			break;
		case TEN_STAT_MAIN::REAL_DATA_CORRECTION:
			this-&amp;gt;vRun_REAL_D_COR();
			break;
		case TEN_STAT_MAIN::IDLE_WAIT:
			;
			break;
		default:
			;
		}

		if (TRUE == _m_stTcb.boNoReturn) {
			continue;
		}

		break;

	} while (TRUE);

	return TASK_ERR::EDUMMY_FUNCTION;
}

UINT CThdDataCollector::ThdLooper(LPVOID pParam)
{
	PTST_TCB_ROOT pTcb = (PTST_TCB_ROOT)pParam;
	
	SetEvent(pTcb-&amp;gt;hEvent_Suspend);

	WaitForSingleObject(pTcb-&amp;gt;hEvent_Resume, INFINITE);
	ResetEvent(pTcb-&amp;gt;hEvent_Resume);

	DPRINTF(&quot;ENTER Thread LOOP\n&quot;);

	DWORD dwWaitRet;
	MSG msg;
	constexpr DWORD dwNCOUNT = 3;
	HANDLE arrhEvents[dwNCOUNT] = {
		pTcb-&amp;gt;hEvent_Suspend,
		pTcb-&amp;gt;hEvent_Exec,
		pTcb-&amp;gt;hEvent_QuitThread
	};
	
	HANDLE arrhEventsSuspended[2] = {
		pTcb-&amp;gt;hEvent_Resume,
		pTcb-&amp;gt;hEvent_QuitThread
	};
	pTcb-&amp;gt;enTaskStatus = TASK_FATHER::TEN_TASK_STATUS::RUNNING;

	pTcb-&amp;gt;dwMsecInterval = 1000; /////////////// TSET VALUE !!!!! 
	while (TRUE) {
		dwWaitRet = MsgWaitForMultipleObjectsEx(dwNCOUNT, arrhEvents, pTcb-&amp;gt;dwMsecInterval, QS_POSTMESSAGE, MWMO_INPUTAVAILABLE);

		if (WAIT_TIMEOUT == dwWaitRet) {	/* Expired pTcb-&amp;gt;dwMsecInterval */
			DPRINTF(&quot;Tid(%d) Father(%p) hEvent(%p) Expired!! (%d)\n&quot;, GetCurrentThreadId(), pTcb-&amp;gt;pTaskFather, 
				pTcb-&amp;gt;hEvent_Suspend, pTcb-&amp;gt;dwMsecInterval);
		}
		else if ((WAIT_OBJECT_0 + 1) == dwWaitRet) {	/* hEvent_Exec */

		}
		else if ((WAIT_OBJECT_0 + dwNCOUNT) == dwWaitRet) {	/* QS_POSTMESSAGE for PeekMessage() */

		}
		else if ((WAIT_OBJECT_0 + 0) == dwWaitRet) {	/* hEvent_Suspend */
			DPRINTF(&quot;Before Suspend\n&quot;);
			pTcb-&amp;gt;enTaskStatus = TASK_FATHER::TEN_TASK_STATUS::SUSPENDED;
			dwWaitRet = WaitForMultipleObjects(2, arrhEventsSuspended, FALSE, INFINITE);
			if ((WAIT_OBJECT_0 + 0) == dwWaitRet) {	/* hEvent_Resume */
				pTcb-&amp;gt;enTaskStatus = TASK_FATHER::TEN_TASK_STATUS::RUNNING;
				DPRINTF(&quot;Escape Suspend\n&quot;);
				ResetEvent(pTcb-&amp;gt;hEvent_Suspend);
				ResetEvent(pTcb-&amp;gt;hEvent_Resume);
			}
			else if ((WAIT_OBJECT_0 + 1) == dwWaitRet) {	/* hEvent_QuitThread */
				pTcb-&amp;gt;enTaskStatus = TASK_FATHER::TEN_TASK_STATUS::WAIT_REAP;
				break;
			} 
			else if (WAIT_FAILED == dwWaitRet) {

			}
			else {
				;
			}

		}
		else if ((WAIT_OBJECT_0 + 2) == dwWaitRet) {	/* hEvent_QuitThread */
			pTcb-&amp;gt;enTaskStatus = TASK_FATHER::TEN_TASK_STATUS::WAIT_REAP;
			break;
		}
//		else if ((WAIT_OBJECT_0 + 0) == dwWaitRet) {	/* hEvent_Resume */


//		}
		else if (WAIT_FAILED == dwWaitRet) {
			DPRINTF(&quot;[ERROR] WAIT_FAILED with ErrorNo(%d)\n&quot;, GetLastError());
		}
		else {
			;
		}

		pTcb-&amp;gt;pTaskFather-&amp;gt;iRun();
	}

	/* TODO: Terminate Working Job */

	return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 진단(Diagnostics) 모듈과의 연결&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자에게 명령줄을 통한 입력을 받아 태스크를 제어하기 위한 간략 설명입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GUI 화면을 하나 만들어서 ListBox와 EditBox를 배치하여 ListBox에는 결과물 출력, EditBox는 명령어 입력을 받도록 합니다. 이 창에서는 가장 첫 단어만 파싱 및 나머지 파라미터를 관련 구조체에 담아 해당 명령 처리 모듈로 넘겨주는 역할을 합니다. 파라미터는 숫자타입 파라미터는 -0, -1, -2,... 뒤에 값을 넣고 문자열타입 파라미터는 -a, -b, -c,... 뒤에 문자타입 파라미터를 입력합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;1136&quot; data-origin-height=&quot;423&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yn7Hp/btsGT0keCBp/EvmpouTftOO3j8NMwsEnDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yn7Hp/btsGT0keCBp/EvmpouTftOO3j8NMwsEnDk/img.png&quot; data-alt=&quot;Diagnostics Dialog의 &amp;quot;task&amp;quot; 명령어 처리 부분&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yn7Hp/btsGT0keCBp/EvmpouTftOO3j8NMwsEnDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fyn7Hp%2FbtsGT0keCBp%2FEvmpouTftOO3j8NMwsEnDk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1136&quot; height=&quot;423&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;1136&quot; data-origin-height=&quot;423&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Diagnostics Dialog의 &quot;task&quot; 명령어 처리 부분&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 ProcessCommandLine이며 코드는 다음과 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1713948078810&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;regex&amp;gt;
BOOL CDlgLocalDiagConsole::ProcessCommandLine(int argc, TCHAR* argv[], LPVOID lpParam)
{
	PTST_DIAG_REQUEST_ITEM p = (PTST_DIAG_REQUEST_ITEM)lpParam;
	std::regex re(&quot;[-+]?[0-9]+&quot;);
#if 0
	for (int i = 0; i &amp;lt; argc; i++)
	{
		m_ctrXListBoxConsole.Printf(CXListBox::Black, CXListBox::White, 0,
			_T(&quot;\targv[%d]=&amp;lt;%s&amp;gt;\n&quot;), i, argv[i]);
	}
#endif
	if (nullptr == p) {
		return FALSE;
	}

	int c;
	optind = 1;		// this does not normally have to be done, but in
	// this app we may be calling getopt again

// In the following loop you would set/unset any global command 
// line flags and option arguments (usually in the CWinApp object) 
// as each option was found in the command line.  
//
// Here you could also enforce any required order in the options -
// for example, if option 'b' is seen, then option 'a' must be given
// first.  However, this is unusual and probably not a good idea
// in most apps.
//
// In general it is probably best to let ProcessCommandLine's caller
// sort out the command line arguments that were used, and whether 
// they are consistent.  In ProcessCommandLine, you want to save the
// options and the arguments, doing any conversion (atoi, etc.) that
// is necessary.
//
// Note in the optstring there are colons following the option letters
// (d and e) that take arguments.  Also note that option letters are 
// case sensitive.
//
// Normally you would have a case statement for each option letter.
// In this demo app, the case statement for option 'f' has been
// omitted on purpose, to show what will happen.

	while ((c = getopt(argc, argv, _T(&quot;0:1:2:3:4:5:6:7:a:b:c:d:e:f:g:h:&quot;))) != EOF)
	{
		switch (c)
		{
		case _T('0'):	/* long [0] */
		case _T('1'):	/* long [1] */
		case _T('2'):	/* long [2] */
		case _T('3'):	/* long [3] */
		case _T('4'):	/* long [4] */
		case _T('5'):	/* long [5] */
		case _T('6'):	/* long [6] */
		case _T('7'):	/* long [7] */
			m_ctrXListBoxConsole.Printf(CXListBox::Black, CXListBox::White, 0,
				_T(&quot;\toption a with value '%s'\n&quot;), optarg);
			if (regex_match(optarg, re)) {
				//if (TRUE) {
				p-&amp;gt;lOpaque[c - '0'] = _ttoi(optarg);
				p-&amp;gt;bolOpaque[c - '0'] = TRUE;
				//DPRINTF(&quot;Regex Match True _ttoi(%s)-&amp;gt;(%d)\n&quot;, optarg, p-&amp;gt;lOpaque[c-'0']);
			}
			else {
				DPRINTF(&quot;Regex Match False _ttoi(%s)-&amp;gt;(%d)\n&quot;, optarg, p-&amp;gt;lOpaque[c - '0']);
				m_ctrXListBoxConsole.Printf(CXListBox::Black, CXListBox::Yellow, 0, _T(&quot;Regex Match False _ttoi(%s)-&amp;gt;(%d)\n&quot;), optarg, p-&amp;gt;lOpaque[c - '0']);
				return FALSE;
			}

			break;
		case _T('a'):	/* TCHAR[[0] */
			m_ctrXListBoxConsole.Printf(CXListBox::Black, CXListBox::White, 0,
				_T(&quot;\toption a with value '%s'\n&quot;), optarg);
			_tcsncpy_s(p-&amp;gt;tchOpaque[0], _countof(TST_DIAG_REQUEST_ITEM::tchOpaque[0]), optarg, _countof(TST_DIAG_REQUEST_ITEM::tchOpaque[0]) - 1);
			p-&amp;gt;botchOpaque[0] = TRUE;
			break;
		case _T('b'):	/* TCHAR [1] */
			m_ctrXListBoxConsole.Printf(CXListBox::Black, CXListBox::White, 0,
				_T(&quot;\toption b with value '%s'\n&quot;), optarg);
			_tcsncpy_s(p-&amp;gt;tchOpaque[1], _countof(TST_DIAG_REQUEST_ITEM::tchOpaque[0]), optarg, _countof(TST_DIAG_REQUEST_ITEM::tchOpaque[0]) - 1);
			p-&amp;gt;botchOpaque[1] = TRUE;
			break;
		case _T('c'):	/* TCHAR [2] */
			m_ctrXListBoxConsole.Printf(CXListBox::Black, CXListBox::White, 0,
				_T(&quot;\toption c with value '%s'\n&quot;), optarg);
			_tcsncpy_s(p-&amp;gt;tchOpaque[2], _countof(TST_DIAG_REQUEST_ITEM::tchOpaque[0]), optarg, _countof(TST_DIAG_REQUEST_ITEM::tchOpaque[0]) - 1);
			p-&amp;gt;botchOpaque[2] = TRUE;
			break;
		case _T('d'):	/* TCHAR [3] */
			m_ctrXListBoxConsole.Printf(CXListBox::Black, CXListBox::White, 0,
				_T(&quot;\toption d with value '%s'\n&quot;), optarg);
			_tcsncpy_s(p-&amp;gt;tchOpaque[3], _countof(TST_DIAG_REQUEST_ITEM::tchOpaque[0]), optarg, _countof(TST_DIAG_REQUEST_ITEM::tchOpaque[0]) - 1);
			p-&amp;gt;botchOpaque[3] = TRUE;
			break;
		case _T('e'):	/* TCHAR [4] */
			m_ctrXListBoxConsole.Printf(CXListBox::Black, CXListBox::White, 0,
				_T(&quot;\toption e with value '%s'\n&quot;), optarg);
			_tcsncpy_s(p-&amp;gt;tchOpaque[4], _countof(TST_DIAG_REQUEST_ITEM::tchOpaque[0]), optarg, _countof(TST_DIAG_REQUEST_ITEM::tchOpaque[0]) - 1);
			p-&amp;gt;botchOpaque[4] = TRUE;
			break;
		case _T('f'):	/* TCHAR [5] */
			m_ctrXListBoxConsole.Printf(CXListBox::Black, CXListBox::White, 0,
				_T(&quot;\toption f with value '%s'\n&quot;), optarg);
			_tcsncpy_s(p-&amp;gt;tchOpaque[5], _countof(TST_DIAG_REQUEST_ITEM::tchOpaque[0]), optarg, _countof(TST_DIAG_REQUEST_ITEM::tchOpaque[0]) - 1);
			p-&amp;gt;botchOpaque[5] = TRUE;
			break;
		case _T('g'):	/* TCHAR [6] */
			m_ctrXListBoxConsole.Printf(CXListBox::Black, CXListBox::White, 0,
				_T(&quot;\toption g with value '%s'\n&quot;), optarg);
			_tcsncpy_s(p-&amp;gt;tchOpaque[6], _countof(TST_DIAG_REQUEST_ITEM::tchOpaque[0]), optarg, _countof(TST_DIAG_REQUEST_ITEM::tchOpaque[0]) - 1);
			p-&amp;gt;botchOpaque[6] = TRUE;
			break;
		case _T('h'):	/* TCHAR [7] */
			m_ctrXListBoxConsole.Printf(CXListBox::Black, CXListBox::White, 0,
				_T(&quot;\toption h with value '%s'\n&quot;), optarg);
			_tcsncpy_s(p-&amp;gt;tchOpaque[7], _countof(TST_DIAG_REQUEST_ITEM::tchOpaque[0]), optarg, _countof(TST_DIAG_REQUEST_ITEM::tchOpaque[0]) - 1);
			p-&amp;gt;botchOpaque[7] = TRUE;
			break;
		case _T('?'):		// illegal option - i.e., an option that was
			// not specified in the optstring

// Note: you may choose to ignore illegal options
#if 0	/* IGNORE */
			m_ctrListBoxConsole.Printf(CXListBox::Red, CXListBox::White, 0,
				_T(&quot;\tERROR:  illegal option %s\n&quot;), argv[optind - 1]);
			{
				CString str;
				str.LoadString(AFX_IDS_APP_TITLE);
				CString msg;
				msg = _T(&quot;Usage:  &quot; + str + &quot; -a -b -c -C -d AAA -e NNN -f&quot;);
				AfxMessageBox(msg);
			}
			return FALSE;
#endif
			break;

		default:		// legal option - it was specified in optstring,
			// but it had no &quot;case&quot; handler 

// Note: you may choose not to make this an error

			m_ctrXListBoxConsole.Printf(CXListBox::Red, CXListBox::White, 0,
				_T(&quot;\tWARNING:  no handler for option %c\n&quot;), c);
			return FALSE;
			break;
		}
	}

	if (optind &amp;lt; argc)
	{
		CString strArgs;
		strArgs = _T(&quot;&quot;);

		// In this loop you would save any extra arguments (e.g., filenames).
		while (optind &amp;lt; argc)
		{
			if (strArgs.IsEmpty())
				strArgs = _T(&quot;\tAdditional non-option arguments: &quot;);
			strArgs += _T(&quot;&amp;lt;&quot;);
			strArgs += argv[optind];
			strArgs += _T(&quot;&amp;gt; &quot;);
			optind++;
		}

		if (!strArgs.IsEmpty())
		{
			m_ctrXListBoxConsole.AddLine(CXListBox::Black, CXListBox::White, (LPCTSTR)strArgs);
		}
	}

	// all options processed, return success

	return TRUE;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ProcessCommandLine에서 파싱 한 파라미터들을 담아 Diagnostics 모듈로 메시지 전달을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Diagnostics 모듈의 태스크 명령어 처리 부분 코드는 다음과 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1713948173059&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void CDiagnostics::vExec_LocalDiagTask(PTST_DIAG_REQUEST_ITEM p)
{
	if (nullptr == p) {
		return;
	}

	DPRINTF(&quot;Cmd: %s\n&quot;, p-&amp;gt;tchDiagCmd);

	if (0 == _tcsncmp(_T(&quot;status&quot;), p-&amp;gt;tchDiagCmd, _tcsclen(_T(&quot;status&quot;)))) {
		DPRINTF(&quot;status\n&quot;);
		CString cstrTemp;
		PTST_TASK_OBJECT_POOL pObj = theApp.m_pTaskMgr-&amp;gt;pRefer_PTST_TASK_OBJECT_POOL();
		PTST_TASK_PROP pList = theApp.m_pTaskMgr-&amp;gt;pRefer_PTST_TASK_PROP();

		AcquireSRWLockShared(&amp;amp;(pObj-&amp;gt;srwlock));
		for (int i = 0; i &amp;lt; N_MAX_TASK_OBJECT; i++) {
			if (TRUE == pObj-&amp;gt;_m_astTaskObj[i].boUsed) {
				cstrTemp.Format(_T(&quot;[%d][%02d-%02d-%02d %02d:%02d:%02d] TASK[%d] TID[%d] &quot;),
					i, pObj-&amp;gt;_m_astTaskObj[i].tmTagCreated.wYear, pObj-&amp;gt;_m_astTaskObj[i].tmTagCreated.wMonth,
					pObj-&amp;gt;_m_astTaskObj[i].tmTagCreated.wDay, pObj-&amp;gt;_m_astTaskObj[i].tmTagCreated.wHour,
					pObj-&amp;gt;_m_astTaskObj[i].tmTagCreated.wMinute, pObj-&amp;gt;_m_astTaskObj[i].tmTagCreated.wSecond,
					pObj-&amp;gt;_m_astTaskObj[i].enTaskCat, pObj-&amp;gt;_m_astTaskObj[i].dwLooperTid);

				switch (pObj-&amp;gt;_m_astTaskObj[i].pTaskFather-&amp;gt;enGetTaskStatus()) {
				case TASK_FATHER::TEN_TASK_STATUS::RUNNING:
					cstrTemp.AppendFormat(_T(&quot;STAT[RUNNING]&quot;));
					break;
				case TASK_FATHER::TEN_TASK_STATUS::SUSPENDED:
					cstrTemp.AppendFormat(_T(&quot;STAT[SUSPENDED]&quot;));
					break;
				default:
					cstrTemp.AppendFormat(_T(&quot;STAT[UNKNOWN]&quot;));
				}
				
				cstrTemp.AppendFormat(_T(&quot;[%s]&quot;), pList[static_cast&amp;lt;UINT&amp;gt;(pObj-&amp;gt;_m_astTaskObj[i].enTaskCat)].tchTaskName);

				if (p-&amp;gt;pXListBox) {
					p-&amp;gt;pXListBox-&amp;gt;Printf(CXListBox::Black, CXListBox::White, 0, cstrTemp);
				}
				else {
					DPRINTF(&quot;%s\n&quot;, (LPCTSTR)cstrTemp);
				}
			}
		}
		ReleaseSRWLockShared(&amp;amp;(pObj-&amp;gt;srwlock));
	}
	else if (0 == _tcsncmp(_T(&quot;list&quot;), p-&amp;gt;tchDiagCmd, _tcsclen(_T(&quot;list&quot;)))) {
		PTST_TASK_PROP pList = theApp.m_pTaskMgr-&amp;gt;pRefer_PTST_TASK_PROP();
		for (int i = 0; i &amp;lt; static_cast&amp;lt;unsigned int&amp;gt;(TEN_TASK_CAT::MAX); i++) {
			if (p-&amp;gt;pXListBox) {
				p-&amp;gt;pXListBox-&amp;gt;Printf(CXListBox::Black, CXListBox::White, 0, _T(&quot;TEN_TASK_CAT[%d] %s&quot;), pList[i].enTaskCat, pList[i].tchTaskName);
			}
			else {
				DPRINTF(&quot;TEN_TASK_CAT[%d] (%s)\n&quot;, pList[i].enTaskCat, pList[i].tchTaskName);
			}
		}
	}
	else if (0 == _tcsncmp(_T(&quot;resume&quot;), p-&amp;gt;tchDiagCmd, _tcsclen(_T(&quot;resume&quot;)))) {
		DPRINTF(&quot;resume\n&quot;);
		theApp.m_pTaskMgr-&amp;gt;ResumeTask(_ttoi(p-&amp;gt;tchOpaque[0]));
	}
	else if (0 == _tcsncmp(_T(&quot;suspend&quot;), p-&amp;gt;tchDiagCmd, _tcsclen(_T(&quot;suspend&quot;)))) {
		DPRINTF(&quot;suspend\n&quot;);
		theApp.m_pTaskMgr-&amp;gt;SuspendTask(_ttoi(p-&amp;gt;tchOpaque[0]));
	}
	else if (0 == _tcsncmp(_T(&quot;create&quot;), p-&amp;gt;tchDiagCmd, _tcsclen(_T(&quot;create&quot;)))) {
		DPRINTF(&quot;create\n&quot;);
		theApp.m_pTaskMgr-&amp;gt;CreateTask(p-&amp;gt;tchOpaque[0]);
	}
	else if (0 == _tcsncmp(_T(&quot;destroy&quot;), p-&amp;gt;tchDiagCmd, _tcsclen(_T(&quot;destroy&quot;)))) {
		DPRINTF(&quot;destroy\n&quot;);
		theApp.m_pTaskMgr-&amp;gt;DestroyTask(_ttoi(p-&amp;gt;tchOpaque[0]));
	}
	else {
		DPRINTF(&quot;nop\n&quot;);
	}

	return;
}

void CDiagnostics::vExec_LocalDiagCommandLine(WPARAM wParam, LPARAM lParam)
{
	PTST_DIAG_REQUEST_ITEM p = (PTST_DIAG_REQUEST_ITEM)wParam;

	if (nullptr == p)
		return;

	/* Invoke Diag Request */
	switch (p-&amp;gt;enDaigCmdCat) {
	case tenDiagCmdCat::UNKNOWN:
		break;
	case tenDiagCmdCat::TASK:
		vExec_LocalDiagTask(p);
		break;
	case tenDiagCmdCat::DIAG:
		break;
	case tenDiagCmdCat::TR:
		vExec_LocalDiagTrCode(p);
		break;
	default:
		;
	}

	if (nullptr != p) {
		delete p;
		p = nullptr;
	}

	return;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도움이 되셨기를 바라며 이상 마칩니다.&amp;nbsp;&lt;/p&gt;</description>
      <category>프로그래밍/C | C++</category>
      <category>mfc thread</category>
      <category>worker thread</category>
      <category>스레드 관리</category>
      <category>스레드 생성</category>
      <category>스레드 중지</category>
      <category>작업 스레드</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/64</guid>
      <comments>https://noyecube.tistory.com/entry/MFC-Worker-Thread-%EB%8F%99%EC%A0%81-%EA%B4%80%EB%A6%AC%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84#entry64comment</comments>
      <pubDate>Thu, 25 Apr 2024 00:01:14 +0900</pubDate>
    </item>
    <item>
      <title>국내 주식 휴장일 영업일 캘린더 클래스 만들기</title>
      <link>https://noyecube.tistory.com/entry/%EA%B5%AD%EB%82%B4-%EC%A3%BC%EC%8B%9D-%ED%9C%B4%EC%9E%A5%EC%9D%BC-%EC%98%81%EC%97%85%EC%9D%BC-%EC%BA%98%EB%A6%B0%EB%8D%94-%ED%81%B4%EB%9E%98%EC%8A%A4-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;코인 거래소는 24시간 365일 거래일이지만 국내 주식 시장은 예정된 이벤트(선거일, 대체공휴일 등) 등으로 토요일과 일요일 이외에도 휴장 하게 됩니다. 파이썬은 관련 모듈이 제공되어 즉시 사용가능 하지만 C/C++ 플랫폼에서의 사용을 위하여 주식시장 휴일 캘린더 클래스를 직접 구현해 보았습니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 휴장일 데이터 확보하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;휴장일 정보는 한국거래소 홈페이지의&amp;nbsp; KRX 시장 &amp;gt; 시장동향 &amp;gt; 증시일정 &amp;gt; 휴장일 경로에서 확인할 수 있고 엑셀 파일로 다운로드 가능 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;URL 경로: &lt;a href=&quot;https://open.krx.co.kr/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://open.krx.co.kr/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 휴장일 데이터 가공 및 소스코드화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;KRX에서 제공하는 휴장일 데이터의 메타데이터는 &quot;일자 및 요일&quot;, &quot;요일구분&quot;, &quot;비고&quot;로서 데이터 샘플은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;2017-01-27&quot;, &quot;금요일&quot;, &quot;설날&quot;입니다. 프로그램이 휴장일 판단에 실제 필요한 부분은 &quot;일자 및 요일&quot;로서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DWORD 타입의 배열 및 맵으로 관리하기 위하여 엑셀 매크로를 활용하여 YYYYMMDD 형식으로 변환 후 헤더파일을 생성하였습니다. 다음과 같이 관리하면 휴장일이 추가/삭제되더라도 간단하게 변경사항 적용 가능합니다. 소스코드에 포함하는 휴장일 정보는 휴장일 설정파일이 없을 때 기본 설정파일을 생성하기 위한 용도이며 지정 경로에 설정파일이 존재하면 설정파일로부터 휴장일 정보를 읽습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;494&quot; data-origin-height=&quot;359&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dwO86i/btsGS50ws8O/PGJL1oLN9mCEwAoESI1uV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dwO86i/btsGS50ws8O/PGJL1oLN9mCEwAoESI1uV1/img.png&quot; data-alt=&quot;KRX 휴장일 원본데이터 형식&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dwO86i/btsGS50ws8O/PGJL1oLN9mCEwAoESI1uV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdwO86i%2FbtsGS50ws8O%2FPGJL1oLN9mCEwAoESI1uV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;494&quot; height=&quot;359&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;494&quot; data-origin-height=&quot;359&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;KRX 휴장일 원본데이터 형식&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;346&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxEcCN/btsGR0yFgej/ak6rf7CD1LsACwXWH7VJn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxEcCN/btsGR0yFgej/ak6rf7CD1LsACwXWH7VJn1/img.png&quot; data-alt=&quot;헤더파일, 설정파일 생성 매크로&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxEcCN/btsGR0yFgej/ak6rf7CD1LsACwXWH7VJn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxEcCN%2FbtsGR0yFgej%2Fak6rf7CD1LsACwXWH7VJn1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;832&quot; height=&quot;346&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;346&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;헤더파일, 설정파일 생성 매크로&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;618&quot; data-origin-height=&quot;190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yCWgE/btsGVeBXnTS/IeX2sgoVkkSjZspLoJByKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yCWgE/btsGVeBXnTS/IeX2sgoVkkSjZspLoJByKk/img.png&quot; data-alt=&quot;엑셀 매크로로 휴장일 배열 데이터 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yCWgE/btsGVeBXnTS/IeX2sgoVkkSjZspLoJByKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyCWgE%2FbtsGVeBXnTS%2FIeX2sgoVkkSjZspLoJByKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;618&quot; height=&quot;190&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;618&quot; data-origin-height=&quot;190&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;엑셀 매크로로 휴장일 배열 데이터 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;501&quot; data-origin-height=&quot;322&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yc4X0/btsGT0RJ8H8/srD5bRmBf7licGVPX2oxb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yc4X0/btsGT0RJ8H8/srD5bRmBf7licGVPX2oxb0/img.png&quot; data-alt=&quot;자동생성 헤더파일-휴장일 값 167개를 의미&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yc4X0/btsGT0RJ8H8/srD5bRmBf7licGVPX2oxb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fyc4X0%2FbtsGT0RJ8H8%2FsrD5bRmBf7licGVPX2oxb0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;501&quot; height=&quot;322&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;501&quot; data-origin-height=&quot;322&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;자동생성 헤더파일-휴장일 값 167개를 의미&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;490&quot; data-origin-height=&quot;503&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/duoqwZ/btsGTBxTM93/mDarbd1XILlXiEmBWgicj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/duoqwZ/btsGTBxTM93/mDarbd1XILlXiEmBWgicj1/img.png&quot; data-alt=&quot;휴장일 정보 배열 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/duoqwZ/btsGTBxTM93/mDarbd1XILlXiEmBWgicj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FduoqwZ%2FbtsGTBxTM93%2FmDarbd1XILlXiEmBWgicj1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;490&quot; height=&quot;503&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;490&quot; data-origin-height=&quot;503&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;휴장일 정보 배열 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;412&quot; data-origin-height=&quot;742&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XDkbA/btsGS99HgPQ/IMmawTBMRdB8RyOQfaRYG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XDkbA/btsGS99HgPQ/IMmawTBMRdB8RyOQfaRYG1/img.png&quot; data-alt=&quot;휴장일 정보 설정 파일&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XDkbA/btsGS99HgPQ/IMmawTBMRdB8RyOQfaRYG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXDkbA%2FbtsGS99HgPQ%2FIMmawTBMRdB8RyOQfaRYG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;412&quot; height=&quot;742&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;412&quot; data-origin-height=&quot;742&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;휴장일 정보 설정 파일&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;693&quot; data-origin-height=&quot;64&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/du6qDJ/btsGSh1qmu5/5JigTTzqrDfSidn98kGrL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/du6qDJ/btsGSh1qmu5/5JigTTzqrDfSidn98kGrL0/img.png&quot; data-alt=&quot;휴장일 배열 소스코드-휴장일 추가 삭제시 소스코드 수정이 필요 없음&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/du6qDJ/btsGSh1qmu5/5JigTTzqrDfSidn98kGrL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdu6qDJ%2FbtsGSh1qmu5%2F5JigTTzqrDfSidn98kGrL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;693&quot; height=&quot;64&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;693&quot; data-origin-height=&quot;64&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;휴장일 배열 소스코드-휴장일 추가 삭제시 소스코드 수정이 필요 없음&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. MarketCalendars 클래스&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BOOL boIsSession() 의 프로토타입으로 휴장일이면 FALSE, 개장일이면 TRUE를 반환하며 파라미터로 CTime, SYSTEMTIME, DWORD dwYYYYMMDD, LPCTSTR strYYYYMMDD를 받을 수 있도록 구현하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BOOL boNextOpenDay()은 오늘 날짜를 넣으면 다음 개장일을 입력 파라미터에 Overwrite 하여 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유의사항으로는 CTime과 SYSTEMTIME의 토요일, 일요일 값이 다릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진단 모듈등을 통한 설정파일 동적 reloading 기능을 위하여 생성자에서 초기화 하지 않고 별도의 Init, DeInit을 두었습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.1. CMarketCalendars.h&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1713936263253&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;auto-generated/_autogen_noedit_market_holiday_array_size_krx.h&quot;

#define N_CALENDARS_LOW_LIMIT_DATE	(19800101)
#define N_CALENDARS_HIGH_LIMIT_DATE	(21001231)

#define CTIME_DAYOFWEEK_SUNDAY		(1)
#define CTIME_DAYOFWEEK_SATURDAY	(7)

#define SYSTEMTIME_DAYOFWEEK_SUNDAY		(0)
#define SYSTEMTIME_DAYOFWEEK_SATURDAY	(6)

namespace MARKET_CALENDARS
{
	typedef enum class _enMarketCat {
		XKRX,
		MAX
	} TEN_MARKET_CAT;

	typedef CMap&amp;lt;DWORD, DWORD, BOOL, BOOL&amp;gt; TMAP_HOLIDAY_YYYYMMDD;
	typedef struct _market_holiday {
		SRWLOCK srwlock;
		DWORD dwEntityCnt;
		TMAP_HOLIDAY_YYYYMMDD map;
		_market_holiday() {
			dwEntityCnt = 0;
			map.RemoveAll();
			InitializeSRWLock(&amp;amp;srwlock);
		}

		void EraseData(void) {
			dwEntityCnt = 0;
			map.RemoveAll();
		}
	} TST_MARKET_HOLIDAY, *PTST_MARKET_HOLIDAY;
}

using namespace MARKET_CALENDARS;
class CMarketCalendars
{
public:
	CMarketCalendars();
	~CMarketCalendars();

	void vInit(void);
	void vDeInit(void);

	void vLoadMarketHoliday(void);

public:
	BOOL boIsSession(TEN_MARKET_CAT enMarket, const DWORD dwYYYYMMDD);
	BOOL boIsSession(TEN_MARKET_CAT enMarket, CTime&amp;amp; t);
	BOOL boIsSession(TEN_MARKET_CAT enMarket, SYSTEMTIME&amp;amp; t);
	BOOL boIsSession(TEN_MARKET_CAT enMarket, LPCTSTR strYYYYMMDD);

	/* next_open(), return next open day */
	BOOL boNextOpenDay(CTime&amp;amp; t);
	BOOL boNextOpenDay(SYSTEMTIME&amp;amp; t);

	static const DWORD m_arrKRX_Holiday[N_DFLT_ENTITY_MARKET_HOLIDAY_KRX];

private:
	TST_MARKET_HOLIDAY _m_astHolidayMap[static_cast&amp;lt;UINT&amp;gt;(TEN_MARKET_CAT::MAX)];
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2. void CMarketCalendars::vLoadMarketHoliday(void)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정파일을 읽어서 맵에 적재하는 코드입니다. 정적 배열과 설정파일의 내용을 합집합으로 관리합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1713937119514&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void CMarketCalendars::vLoadMarketHoliday(void)
{
	/* Check market_holiday_xxx.csv file */
	CString cstrFilePath;
	CFileFind cffFindFile;
	CFile* pcfp = nullptr;
	BOOL boRet;
	PTST_MARKET_HOLIDAY p = nullptr;
	char chBuf[128];

	/* [START] Build KRX Holiday Query Map */
	p = &amp;amp;(_m_astHolidayMap[static_cast&amp;lt;UINT&amp;gt;(TEN_MARKET_CAT::XKRX)]);
	AcquireSRWLockExclusive(&amp;amp;(p-&amp;gt;srwlock));
	p-&amp;gt;EraseData();
	for (int i = 0; i &amp;lt; N_DFLT_ENTITY_MARKET_HOLIDAY_KRX; i++) {	/* KRX */
		p-&amp;gt;map.SetAt(m_arrKRX_Holiday[i], TRUE);
		p-&amp;gt;dwEntityCnt++;
	}
	ReleaseSRWLockExclusive(&amp;amp;(p-&amp;gt;srwlock));


	cstrFilePath.Format(_T(&quot;C:\\noyecube\\market_holiday_krx.csv&quot;));

	boRet = cffFindFile.FindFile(cstrFilePath);
	if (!boRet) {
		DPRINTF(&quot;[WARN] C:\\noyecube\\market_holiday_krx.csv Lookup Fail. New market_holiday_krx.csv created with default values.\n&quot;);
		pcfp = new CFile();
		boRet = pcfp-&amp;gt;Open(cstrFilePath, CFile::modeWrite | CFile::modeCreate | CFile::modeNoTruncate | CFile::shareDenyNone);
		if (FALSE == boRet) {
			;	// do nothing
		}
		else {

			for (int i = 0; i &amp;lt; N_DFLT_ENTITY_MARKET_HOLIDAY_KRX; i++) {
				sprintf_s(chBuf, sizeof(chBuf), &quot;%u\n&quot;, m_arrKRX_Holiday[i]);
				pcfp-&amp;gt;Write(chBuf, 9);
			}

			pcfp-&amp;gt;Flush();
			pcfp-&amp;gt;Abort();
			delete pcfp;
			pcfp = nullptr;
		}
	}
	else {
		CStdioFile stdFp;
		DWORD dwTemp;
		boRet = stdFp.Open(cstrFilePath, CFile::modeRead | CFile::shareDenyNone | CFile::typeText);
		if (TRUE == boRet) {
			AcquireSRWLockExclusive(&amp;amp;(p-&amp;gt;srwlock));
			while (stdFp.ReadString(chBuf, sizeof(chBuf) - 1)) {
				//DPRINTF(&quot;INFO %s&quot;, chBuf);
				dwTemp = (DWORD)_ttoi(chBuf);
				if ((dwTemp &amp;lt; N_CALENDARS_LOW_LIMIT_DATE) || (dwTemp &amp;gt; N_CALENDARS_HIGH_LIMIT_DATE)) {	/* Range filtering */
					continue;
				}
				DPRINTF(&quot;INFO %u\n&quot;, dwTemp);
				p-&amp;gt;map.SetAt(dwTemp, TRUE);
			}
			ReleaseSRWLockExclusive(&amp;amp;(p-&amp;gt;srwlock));
			stdFp.Close();
		}
	}
	/* [END] Build KRX Holiday Query Map */

	return;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.3.&amp;nbsp; BOOL CMarketCalendars::boIsSession(TEN_MARKET_CAT enMarket, LPCTSTR strYYYYMMDD)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;YYYYMMDD 형식의 문자열을 받으면 형식이 맞는지 길이를 체크 후 연도, 월, 일을 구한 뒤 CTime 객체를 생성하여 요일을 구합니다. 요일이 토요일, 일요일에 해당하면 즉시 FALSE를 반환하고 월화수목금에 해당하면 휴장일 맵을 체크 하여 휴장일 맵에 존재하는 날짜면 FALSE를 반환, 그렇지 않으면 TRUE를 반환합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1713937295292&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;BOOL CMarketCalendars::boIsSession(TEN_MARKET_CAT enMarket, LPCTSTR strYYYYMMDD)
{
	DWORD dwTemp;
	BOOL boRet = FALSE;
	BOOL boRvalue;
	int iSizeOfString;
	int iDayofWeeek;
	PTST_MARKET_HOLIDAY p = nullptr;

	CString cstrTemp = strYYYYMMDD;
	cstrTemp.Trim();

	iSizeOfString = cstrTemp.GetLength() + 1;	/* count including '\0' + 1 */

	DPRINTF(&quot;[INFO] iSizeOfString (%d)\n&quot;, iSizeOfString);
	if (iSizeOfString != 9) {	/* Checking string format YYYYMMDD */
		return FALSE;
	}
	
	TCHAR tchBuf[9];
	_tcscpy_s(tchBuf, 9, cstrTemp);

	dwTemp = (DWORD)_ttoi(cstrTemp);
	DPRINTF(&quot;[INFO] dwTemp = %d\n&quot;, dwTemp);
	if ((dwTemp &amp;lt; N_CALENDARS_LOW_LIMIT_DATE) || (dwTemp &amp;gt; N_CALENDARS_HIGH_LIMIT_DATE)) {	/* Range filtering */
		return FALSE;
	}

	WORD wYear;
	WORD wMonth;
	WORD wDay;

	wDay = _ttoi(tchBuf + 6);
	tchBuf[6] = '\0';
	wMonth = _ttoi(tchBuf + 4);
	tchBuf[4] = '\0';
	wYear = _ttoi(tchBuf);
	
	DPRINTF(&quot;[INFO] boIsSession LPCTSTR [%d], [%d] [%d] [%d]\n&quot;, dwTemp, wYear, wMonth, wDay);

	CTime ct(wYear, wMonth, wDay, 9, 0, 0);
	DPRINTF(&quot;[INFO] boIsSession CTime::DayOfWeek (%d)\n&quot;, ct.GetDayOfWeek());

	switch (enMarket) {
	case TEN_MARKET_CAT::XKRX:
		/* Check SUNDAY OR SATURDAY */
		iDayofWeeek = ct.GetDayOfWeek();
		if ((CTIME_DAYOFWEEK_SATURDAY == iDayofWeeek) || (CTIME_DAYOFWEEK_SUNDAY == iDayofWeeek)) {
			return FALSE;
		}

		p = &amp;amp;(_m_astHolidayMap[static_cast&amp;lt;UINT&amp;gt;(TEN_MARKET_CAT::XKRX)]);

		AcquireSRWLockShared(&amp;amp;(p-&amp;gt;srwlock));
		boRet = p-&amp;gt;map.Lookup(dwTemp, boRvalue);
		ReleaseSRWLockShared(&amp;amp;(p-&amp;gt;srwlock));
		if (TRUE == boRet) { /* Hit Holiday */
			return FALSE;
		}
		else {
			return TRUE;
		}
		break;
	default:
		break;
	}

	return boRet;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.4. 샘플 데이터 결과 확인&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출 코드와 디버깅 메시지 결과입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1713937558422&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;m_MkCalendars.vInit();	/* CMarketCalendars */
/* FIXME: Test Code */
LPSTR lpsz = _T(&quot;20200501&quot;);
if (TRUE == m_MkCalendars.boIsSession(TEN_MARKET_CAT::XKRX, lpsz)) {
	DPRINTF(&quot;[INFO] (%s) is Session Day\n&quot;, lpsz);
}
else {
	DPRINTF(&quot;[INFO] (%s) is HoliDay\n&quot;,
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;97&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfT99M/btsGTB5QG3c/kSu79Wjz3hK5PsNmG0HfK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfT99M/btsGTB5QG3c/kSu79Wjz3hK5PsNmG0HfK1/img.png&quot; data-alt=&quot;20200501 결과-금요일인데 휴장일로 판단&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfT99M/btsGTB5QG3c/kSu79Wjz3hK5PsNmG0HfK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfT99M%2FbtsGTB5QG3c%2FkSu79Wjz3hK5PsNmG0HfK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;646&quot; height=&quot;97&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;97&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;20200501 결과-금요일인데 휴장일로 판단&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상으로 C++ 환경에서 주식 휴장일 캘린더 클래스를 만들어 보았습니다.&lt;/p&gt;</description>
      <category>트레이딩/시스템트레이딩</category>
      <category>abstractholidaycalendar</category>
      <category>custombusinessday</category>
      <category>krx 휴장일</category>
      <category>주식 휴장일</category>
      <category>휴장일</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/63</guid>
      <comments>https://noyecube.tistory.com/entry/%EA%B5%AD%EB%82%B4-%EC%A3%BC%EC%8B%9D-%ED%9C%B4%EC%9E%A5%EC%9D%BC-%EC%98%81%EC%97%85%EC%9D%BC-%EC%BA%98%EB%A6%B0%EB%8D%94-%ED%81%B4%EB%9E%98%EC%8A%A4-%EB%A7%8C%EB%93%A4%EA%B8%B0#entry63comment</comments>
      <pubDate>Wed, 24 Apr 2024 14:51:46 +0900</pubDate>
    </item>
    <item>
      <title>용문동4구역 재개발 주민설명회 후기</title>
      <link>https://noyecube.tistory.com/entry/%EC%9A%A9%EB%AC%B8%EB%8F%994%EA%B5%AC%EC%97%AD-%EC%9E%AC%EA%B0%9C%EB%B0%9C-%EC%A3%BC%EB%AF%BC%EC%84%A4%EB%AA%85%ED%9A%8C-%ED%9B%84%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 대전 용문동4구역 재개발 주민설명회 내용을 공유해 드리겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추진준비위원회, 사업개요, 조합원 혜택에 대한 정보를 확인하실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주민설명회는 2024년 4월 9일 오후 7시 용문교회에서 개최되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;용문동4구역은 용문동123구역의 둔산더샵엘리프 바로 옆에 위치한 구역입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;용문동123구역이 본격 추진될 때 용문동4구역은 원룸건물 등 다세대를 처지어 올려서 지분 부풀리기, 쪼개기를 시도한&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외지인들 때문에 노후도 점수에 악영향을 받아서 같이 진행되지 못하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 코로나 시국으로 인하여 총회 등 주민의견수렴에 차질이 발생하여 이제야 본격적인 활동을 시작한 시점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;용문4구역의 재개발이 순탄하게 진행되기 위해서는 서구청에 지속적으로 악성민원을 넣어 진행을 방해하는 세력에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대항하여 조속한 진행을 요청하는 응원과 격려 민원 또한 넣어야 할 것으로 생각됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. (가칭)용문동4구역 추진준비위원회 활동 이력&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;현수막.jpg&quot; data-origin-width=&quot;1045&quot; data-origin-height=&quot;181&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Z3pPk/btsGu06ucuS/V9zALejG0HPadPSbK3KS60/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Z3pPk/btsGu06ucuS/V9zALejG0HPadPSbK3KS60/img.jpg&quot; data-alt=&quot;용문동4구역 주민설명회 현수막&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Z3pPk/btsGu06ucuS/V9zALejG0HPadPSbK3KS60/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZ3pPk%2FbtsGu06ucuS%2FV9zALejG0HPadPSbK3KS60%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1045&quot; height=&quot;181&quot; data-filename=&quot;현수막.jpg&quot; data-origin-width=&quot;1045&quot; data-origin-height=&quot;181&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;용문동4구역 주민설명회 현수막&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추진준비위원회는 두 세력이 서로 추진준비위원회라며 대립하고 있는 상황이며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;양쪽의 말을 비교해본 결과 추진방향은 다음과 같습니다.(아닐 수 있음)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;토착 할아버지 세력: 최대한 빽빽하게 국평 짓고 최대한 조합원 이익을 늘려주겠다.&lt;/li&gt;
&lt;li&gt;재개발 전문 업체: 기존 용문123 및 기타 트렌드에 맞춰 명품단지를 표방(넓은 동간거리, 대형평수 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1.1. 2022년&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;8월: 조례개정 자료 반영(사전 의견 제출)&lt;/li&gt;
&lt;li&gt;10월 21일~22일: 주민공람 공고&lt;/li&gt;
&lt;li&gt;10월 27일: 주민설명회 개최(서구청 주최)&lt;/li&gt;
&lt;li&gt;11월 18일: 구의회 의견청취&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1.2. 2023년&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2월 23일: 조례개정 공포(노후도 완화)&lt;/li&gt;
&lt;li&gt;5월: 사전심의 의견으로 구역확대 협의&lt;/li&gt;
&lt;li&gt;5월 12일~19일: 심의 사전 검토&lt;/li&gt;
&lt;li&gt;5월 25일: 사전심의 의견 반영 구역 확대 결정&lt;/li&gt;
&lt;li&gt;6월 11일: 구역확대주민설명회 개최(추진위)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 설명회 발표 자료 및 설명&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.1. 입지&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;용문동4구역은 둔산권 마지막 재개발지역이라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학군이 탄방중, 삼천중, 문정중에 배정되니 틀린 말은 아닌 것 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;둔산권마지막.jpg&quot; data-origin-width=&quot;608&quot; data-origin-height=&quot;398&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SjBy4/btsGt7FkbVM/dxdQCIR4aBJ07isAO80KK1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SjBy4/btsGt7FkbVM/dxdQCIR4aBJ07isAO80KK1/img.jpg&quot; data-alt=&quot;용문동4구역 입지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SjBy4/btsGt7FkbVM/dxdQCIR4aBJ07isAO80KK1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSjBy4%2FbtsGt7FkbVM%2FdxdQCIR4aBJ07isAO80KK1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;608&quot; height=&quot;398&quot; data-filename=&quot;둔산권마지막.jpg&quot; data-origin-width=&quot;608&quot; data-origin-height=&quot;398&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;용문동4구역 입지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반포, 압구정에 안 살아 봤지만 학군은 둔산 학군이고, 유등천변에 바로 인접하고 있으며 지하철 역세권(용문역)이고 도보로 롯데백화점(언제 망할지 모르지만 성심당 입점시킴)과 병원, 스타벅스, 남선공원 등 부족함 없는 생활권입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;반포한남.jpg&quot; data-origin-width=&quot;1063&quot; data-origin-height=&quot;727&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bG1ebc/btsGvw45iVE/Pk0DIIM3M2OjxiSLQkG4u1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bG1ebc/btsGvw45iVE/Pk0DIIM3M2OjxiSLQkG4u1/img.jpg&quot; data-alt=&quot;용문4구역 입지 비유&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bG1ebc/btsGvw45iVE/Pk0DIIM3M2OjxiSLQkG4u1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbG1ebc%2FbtsGvw45iVE%2FPk0DIIM3M2OjxiSLQkG4u1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1063&quot; height=&quot;727&quot; data-filename=&quot;반포한남.jpg&quot; data-origin-width=&quot;1063&quot; data-origin-height=&quot;727&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;용문4구역 입지 비유&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.2. 사업개요&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현시점의 사업개요는 계획변경을 통해 얼마든지 변경될 수 있는 사항이지만 가장 최신의 더 나아간 구체적 진행 계획이라는 점에서 의의가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총 1,555세대 계획 중 34평형을 초과하는 39평형과 44평형 물량을 충분히 확보하여 대형평수 수요를 충족시켜 주겠다는 전략입니다. 발표자료의 &quot;명품단지&quot; 단어가 무색하지 않으려면 입지, 층간소음등급, 주차대수라고 생각하는데요, 계획되어 있는 주차대수는 2,490대로서 세대당 1.6대의 주차면을 확보하게 됩니다. 유성온천역과 각종 대중교통이 충분한 유성 호반베르디움의 주차대수가 세대당 1.3대 정도인데도 주차장에 모자람이 없는 것을 보면 유사하게 지하철역과 대중교통 노선이 좋은 용문4구역도 24시간 여유 있는 주차공간이 유지될 것으로 판단됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;35층까지의 건물이면 라인당 엘베가 2대 이상에 알고리즘으로 동작해야 출입에 문제가 없을 것으로 생각됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유등천변 쪽 약 5M 폭의 긴 둑이 용적률 적용을 받지 않아서 커뮤니티 센터 공간 확보에 추가적인 이득이 발생한다고 하니 참고하시기 바랍니다,&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1555세대.jpg&quot; data-origin-width=&quot;929&quot; data-origin-height=&quot;626&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZm133/btsGwueEHx5/iIcs6OuQmou7O5biFDENlk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZm133/btsGwueEHx5/iIcs6OuQmou7O5biFDENlk/img.jpg&quot; data-alt=&quot;용문4구역 사업개요&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZm133/btsGwueEHx5/iIcs6OuQmou7O5biFDENlk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZm133%2FbtsGwueEHx5%2FiIcs6OuQmou7O5biFDENlk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;929&quot; height=&quot;626&quot; data-filename=&quot;1555세대.jpg&quot; data-origin-width=&quot;929&quot; data-origin-height=&quot;626&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;용문4구역 사업개요&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;건축사무소에서 제시한 조감도입니다. 토착 원주민 세력 측에서는 동간 간격등을 더 좁히고 더 많이 지어서 조합원 이득을 높여준다고 꼬시는 것 같은데(아님 말고~) 이는 소탐대실 전략으로서 한탕 해먹고 튀려는 것으로 보여서 저는 반대입니다. 분담금을 더 내더라도 프라이버시 보장되고 쾌적한 곳에서 살고 싶고 이러한 장점은 결국 나중에 타 단지보다 높은 시세로 반영될 수 있는 요소가 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;조감도.jpg&quot; data-origin-width=&quot;1003&quot; data-origin-height=&quot;710&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2TO9M/btsGwsup5ip/oUfH6htqV9NTkRuUgvFEOk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2TO9M/btsGwsup5ip/oUfH6htqV9NTkRuUgvFEOk/img.jpg&quot; data-alt=&quot;용문동4구역 조감도&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2TO9M/btsGwsup5ip/oUfH6htqV9NTkRuUgvFEOk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2TO9M%2FbtsGwsup5ip%2FoUfH6htqV9NTkRuUgvFEOk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1003&quot; height=&quot;710&quot; data-filename=&quot;조감도.jpg&quot; data-origin-width=&quot;1003&quot; data-origin-height=&quot;710&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;용문동4구역 조감도&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;39타입은 임대세대로서 96세대이고 분양세대는 1,459세대로 총 1,555세대입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;용문 123구역은 대형평수 물량이 부족하여 전부 조합원선에서 분양이 되었는데 용문4구역은 대형평수 물량이 넉넉하여 일반분양도 가능해 보입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;배치도.jpg&quot; data-origin-width=&quot;1242&quot; data-origin-height=&quot;876&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOayI5/btsGu1K3FvV/9SCLLFXOUW0kNTT4eDJlJk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOayI5/btsGu1K3FvV/9SCLLFXOUW0kNTT4eDJlJk/img.jpg&quot; data-alt=&quot;용문4구역 배치도&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOayI5/btsGu1K3FvV/9SCLLFXOUW0kNTT4eDJlJk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOayI5%2FbtsGu1K3FvV%2F9SCLLFXOUW0kNTT4eDJlJk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1242&quot; height=&quot;876&quot; data-filename=&quot;배치도.jpg&quot; data-origin-width=&quot;1242&quot; data-origin-height=&quot;876&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;용문4구역 배치도&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.3. 향후 계획&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서구청에 반대의견으로 악성 민원을 넣는 작전세력들의 악영향을 안 받게 될 경우를 가정한 일정입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구역에 관심이 있어서 매수를 하시려면 서구청에 조속한 진행을 요청하셔야 재산권 침해 없이 진행될 것입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2024.04.: 조치계획 제출 및 대전시 이관&lt;/li&gt;
&lt;li&gt;2024.05.: 도시계획 위원회 심의 사전검토&lt;/li&gt;
&lt;li&gt;2024.06.: 도시계획 위원회 심의&lt;/li&gt;
&lt;li&gt;2024.07.: 정비구역지정 고시&lt;/li&gt;
&lt;li&gt;2024.09.: 추진위원회 구성&lt;/li&gt;
&lt;li&gt;2024.12.: 창립총회&lt;/li&gt;
&lt;li&gt;2025.01.: 조합설립 인가&lt;/li&gt;
&lt;li&gt;2025.04.: 시공사 선정, 건축심의&lt;/li&gt;
&lt;li&gt;2027: 사업시행인가, 분양신청&lt;/li&gt;
&lt;li&gt;2028: 관리처분인가, 이주&lt;/li&gt;
&lt;li&gt;2029: 착공, 일반분양&lt;/li&gt;
&lt;li&gt;2031: 준공 및 입주&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조합원분들께서는 당장의 이득보다는 분담금이 조금 더 들더라도 층간소음, 주차대수, 내장재 등에 약점이 없는(악플러가 호갱노노 등 부동산 어플에 약 올리지 못하는) 아파트가 건설될 수 있도록 앞으로의 의사결정에 현명한 판단 부탁드립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(다수결이 꼭 현명한 판단 결과가 되는 것은 아니므로 전문가들의 의견에 귀 기울여 경청하시고 공부를 많이 하시기 바랍니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간이 지나면 용문4구역 아파트의 시세가 그에 화답을 해 줄 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.4. 평면정보&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현시점의 평면정보는 어디까지나 안으로서의 의미가 있는 자료이고 확정이 아닙니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;59.jpg&quot; data-origin-width=&quot;917&quot; data-origin-height=&quot;826&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/etMc7K/btsGt7ZGrYr/zuAVFXQcvqKi1IG3X44kg0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/etMc7K/btsGt7ZGrYr/zuAVFXQcvqKi1IG3X44kg0/img.jpg&quot; data-alt=&quot;용문4구역 59타입 평면도&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/etMc7K/btsGt7ZGrYr/zuAVFXQcvqKi1IG3X44kg0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FetMc7K%2FbtsGt7ZGrYr%2FzuAVFXQcvqKi1IG3X44kg0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;917&quot; height=&quot;826&quot; data-filename=&quot;59.jpg&quot; data-origin-width=&quot;917&quot; data-origin-height=&quot;826&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;용문4구역 59타입 평면도&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;74.jpg&quot; data-origin-width=&quot;903&quot; data-origin-height=&quot;822&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qD7sh/btsGwv5Jyid/7JpXLOFJ5yRhHYv5aFkf51/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qD7sh/btsGwv5Jyid/7JpXLOFJ5yRhHYv5aFkf51/img.jpg&quot; data-alt=&quot;용문4구역 74타입 평면도&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qD7sh/btsGwv5Jyid/7JpXLOFJ5yRhHYv5aFkf51/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqD7sh%2FbtsGwv5Jyid%2F7JpXLOFJ5yRhHYv5aFkf51%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;903&quot; height=&quot;822&quot; data-filename=&quot;74.jpg&quot; data-origin-width=&quot;903&quot; data-origin-height=&quot;822&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;용문4구역 74타입 평면도&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;84A.jpg&quot; data-origin-width=&quot;1168&quot; data-origin-height=&quot;996&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eEUzwf/btsGwz1kcAb/AVfQ9XheiqbfJwA3IkleHK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eEUzwf/btsGwz1kcAb/AVfQ9XheiqbfJwA3IkleHK/img.jpg&quot; data-alt=&quot;용문4구역 84A타입 평면도&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eEUzwf/btsGwz1kcAb/AVfQ9XheiqbfJwA3IkleHK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeEUzwf%2FbtsGwz1kcAb%2FAVfQ9XheiqbfJwA3IkleHK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1168&quot; height=&quot;996&quot; data-filename=&quot;84A.jpg&quot; data-origin-width=&quot;1168&quot; data-origin-height=&quot;996&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;용문4구역 84A타입 평면도&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;84b.jpg&quot; data-origin-width=&quot;950&quot; data-origin-height=&quot;966&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TrVTf/btsGxBjV4sY/hnbc594u4IhBkgsJZDux51/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TrVTf/btsGxBjV4sY/hnbc594u4IhBkgsJZDux51/img.jpg&quot; data-alt=&quot;용문4구역 84B타입 평면도&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TrVTf/btsGxBjV4sY/hnbc594u4IhBkgsJZDux51/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTrVTf%2FbtsGxBjV4sY%2Fhnbc594u4IhBkgsJZDux51%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;950&quot; height=&quot;966&quot; data-filename=&quot;84b.jpg&quot; data-origin-width=&quot;950&quot; data-origin-height=&quot;966&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;용문4구역 84B타입 평면도&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;99A.jpg&quot; data-origin-width=&quot;1003&quot; data-origin-height=&quot;923&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n69Pu/btsGvsPwuVU/o1vJCICIqx3DtQVKnO6i5k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n69Pu/btsGvsPwuVU/o1vJCICIqx3DtQVKnO6i5k/img.jpg&quot; data-alt=&quot;용문4구역 99A타입 평면도&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n69Pu/btsGvsPwuVU/o1vJCICIqx3DtQVKnO6i5k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn69Pu%2FbtsGvsPwuVU%2Fo1vJCICIqx3DtQVKnO6i5k%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1003&quot; height=&quot;923&quot; data-filename=&quot;99A.jpg&quot; data-origin-width=&quot;1003&quot; data-origin-height=&quot;923&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;용문4구역 99A타입 평면도&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;114.jpg&quot; data-origin-width=&quot;1008&quot; data-origin-height=&quot;870&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btG8Lz/btsGwcrNSFp/ykP48tf4wfD6KI7yQpWxQK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btG8Lz/btsGwcrNSFp/ykP48tf4wfD6KI7yQpWxQK/img.jpg&quot; data-alt=&quot;용문4구역 114타입 평면도&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btG8Lz/btsGwcrNSFp/ykP48tf4wfD6KI7yQpWxQK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtG8Lz%2FbtsGwcrNSFp%2FykP48tf4wfD6KI7yQpWxQK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1008&quot; height=&quot;870&quot; data-filename=&quot;114.jpg&quot; data-origin-width=&quot;1008&quot; data-origin-height=&quot;870&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;용문4구역 114타입 평면도&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로 대형평수는 거실이나 안방 크기만 잡아서 늘릴게 아니라 그에 맞는 창고공간도 충분히 확보되었으면 좋겠습니다.(아니면 소화전 옆에 공용 창고 공간을 만들어 주시거나)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 조합원의 혜택&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조합원의 혜택은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;낮은 분양가격: 현시점 기준 일반 분양가가 평당 2,200만원 일 때, 조합원 분양가는 1,020만원 할인된 1,180만원 입니다. 감정평가금액이 3억인 주택을 피 2억을 주고 5억에 구입해서 34평을 신청하게 되면, 비례율 130%(평당 2,200 일반분양시 기준)를 가정할 때 일반분양 74,800만원 대비 34,680만원 싼 40,120에 분양받아서 피 2억을 계상하면 60,120원으로 일반분양가에 비해 1억4천6백80만원 저렴하게 내집마련을 함과 동시에 조합원 분양세대의 프리미엄을 추가로 누리실 수 있습니다. 피가 2억 미만이면 더 이득인 것은 말할 것도 없겠죠?&lt;/li&gt;
&lt;li&gt;조합원 특별 마감재: 조합원 세대는 일반분양 세대보다 고급 마감재를 사용하여 시공됩니다.&lt;/li&gt;
&lt;li&gt;좋은 위치 및 평형 우선배정: 용문123 구역의 조합원들이 대형평수를 싹쓸이한 것처럼 원하는 위치의 세대를 골라가질 수 있습니다. 대지 지분이 큰 순서로 선택 우선권을 가지게 됩니다.&lt;/li&gt;
&lt;li&gt;사업이익 배당: 일반분양을 해서 그 이득을 배당받습니다. 40평 대지지분의 감정평가를 3억으로 산정받게 되면 비례율 130% 기준으로 34평은 추가부담금 약 1천만원만 내면 됩니다. 감정평가는 서구청에서 감정평가사 2인을 임명하며 2인의 감정평가금액 평균으로 산출됩니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 평가금액은 얼마?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;감정 평가시점은 사업시행인가시점이며 약 2026년 기준으로 평가될 것으로 예상되고 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공시지가로 평가되는 것이 아니며 해당 시점의 시세를 반영하여 평가되고 구청의 감정평가사가 평가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 용문4구역의 대지지분 40평 단독주택의 공시지가는 1억 미만입니다. 이자부담 없이 진입하실 수 있는 분은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;감정평가전에 진입하시는 것이 이득일듯 하네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 추가부담금의 결정 시기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가부담금이 발생할 수 있습니다. 서울 둔촌주공이 코로나 시국에 급상승한 공사비 때문에&amp;nbsp; 난리 났었죠.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가부담금은 관리처분인가시 확정되며 평가금액, 공사비 등의 사업비를 반영하여 비례율 산정 후 산출됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 현장 분위기를 전합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;용문동의 역사가 오래된 만큼 연로하신 분들이 대부분이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로의 의사결정에 실거주를 희망하시는 젊으신 분들도 많이 참여하여 제대로 된 재개발이 되기를 희망합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;틀딱.jpg&quot; data-origin-width=&quot;1053&quot; data-origin-height=&quot;1289&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rqwtl/btsGurDx9It/nytgLrNHL5q5cOaohWBJZ1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rqwtl/btsGurDx9It/nytgLrNHL5q5cOaohWBJZ1/img.jpg&quot; data-alt=&quot;총회 진행방해 세력&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rqwtl/btsGurDx9It/nytgLrNHL5q5cOaohWBJZ1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Frqwtl%2FbtsGurDx9It%2FnytgLrNHL5q5cOaohWBJZ1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1053&quot; height=&quot;1289&quot; data-filename=&quot;틀딱.jpg&quot; data-origin-width=&quot;1053&quot; data-origin-height=&quot;1289&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;총회 진행방해 세력&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상으로 대전 용문동4구역 재개발 주민설명회 내용을 공유해 드렸습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 발로 뛰어서 열심히 취재하여 적었습니다. 도움이 되셨다면 광고도 한 번씩 구경하고 가주시면 감사하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나만 알고 둘은 모르는 분들이 재개발 반대 외치고 다니시던데 자신의 무지몽매함으로인해 사업이 지연되면 지연될수록 공사비는 증가하여 차익은 적어진다는 사실만 알고 계십시오. 2017년에는 대전 유성구 아파트 34평 분양가가 3억선이었습니다. 지금 통화량이 증가하고 물가상승분이 반영되어 최소 6억이 되었습니다. 신규 분양 아파트들도 분양가가 6억 7억부터 시작합니다. 그럼 또 반대하는 분들은 너무 올라서 먹을게 없다 이제는 재개발 어렵다고 하시죠. 시간이 지금 멈춰있는게 아닌데 왜 그런말을 하시는거죠? 늦었다고 생각할때가 가장 빠를 때 입니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image.jpg&quot; data-origin-width=&quot;906&quot; data-origin-height=&quot;861&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/se0TP/btsOcTEroxi/oHC5KAuw7lJKFtKgNsk9xK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/se0TP/btsOcTEroxi/oHC5KAuw7lJKFtKgNsk9xK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/se0TP/btsOcTEroxi/oHC5KAuw7lJKFtKgNsk9xK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fse0TP%2FbtsOcTEroxi%2FoHC5KAuw7lJKFtKgNsk9xK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;906&quot; height=&quot;861&quot; data-filename=&quot;image.jpg&quot; data-origin-width=&quot;906&quot; data-origin-height=&quot;861&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Personal</category>
      <category>대전용문동4구역</category>
      <category>용문4구역</category>
      <category>용문4구역 분양</category>
      <category>용문4구역 재개발</category>
      <category>용문4구역 조감도</category>
      <category>용문동4구역</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/62</guid>
      <comments>https://noyecube.tistory.com/entry/%EC%9A%A9%EB%AC%B8%EB%8F%994%EA%B5%AC%EC%97%AD-%EC%9E%AC%EA%B0%9C%EB%B0%9C-%EC%A3%BC%EB%AF%BC%EC%84%A4%EB%AA%85%ED%9A%8C-%ED%9B%84%EA%B8%B0#entry62comment</comments>
      <pubDate>Tue, 9 Apr 2024 23:34:34 +0900</pubDate>
    </item>
    <item>
      <title>[MFC] CMap class 사용법 예제</title>
      <link>https://noyecube.tistory.com/entry/MFC-CMap-class-%EC%82%AC%EC%9A%A9%EB%B2%95-%EC%98%88%EC%A0%9C</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;MFC의 CMap 클래스는 키와 값의 쌍으로 이루어진 맵을 구현하는 클래스입니다. C++ 표준라이브러리의 map 클래스와 유사하지만 MFC에 특화된 기능을 추가로 제공합니다. CMap 클래스는 MFC에 종속적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 CMap 클래스 초기화, 삽입, 탐색, 삭제 등을 예제 코드로 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. CMap 클래스 사용사례&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열이나 리스트로 자료를 관리해야 할 때 특정 값이 어느 인덱스에 있는지 찾으려면 탐색을 해야 하는데 최악의 경우 N-1까지 탐색을 해야 합니다. 이때 찾으려는 값을 KEY, 인덱스를 VALUE로 CMap에 저장해 두면 deterministic 하게 탐색 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. CMap 클래스 설명&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CMap을 사용하려면 CMap 인스턴스를 생성해야 하는데 공식 MSDN 구문은 다음과 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1703888217915&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;class KEY, class ARG_KEY, class VALUE, class ARG_VALUE&amp;gt;class CMap : public CObject&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MSDN 설명으로는 잘 모르겠으니 실제 작성 코드를 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;KEY, VALUE가 필요한데 2개씩 중복이 되어 있는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;KEY-원형, KEY-레퍼런스타입파라미터, VALUE-원형, VALUE_레퍼런스타입파라미터라고 보시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열은 MFC에서 LPCTSTR으로 참조하니까 CString의 경우에도 LPCTSTR으로 해주시면 되고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열이 아닌 다른 타입들은 원형과 레퍼런스 타입을 똑같이 기입해 주시면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1703888338805&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;typedef CMap&amp;lt;CString, LPCTSTR, int, int&amp;gt; TMAP_DS_STOCK_INDEX;
typedef CMap&amp;lt;CString, LPCTSTR, TST__DS_BALANCE_OCCURS*, TST__DS_BALANCE_OCCURS*&amp;gt; TMAP_DS_BALANCE_OCCURS;   /* KEY: LPCTSTR SH CODE. */
typedef CMap&amp;lt;long, long, TST__DS_UNEXECUTED_ORDER*, TST__DS_UNEXECUTED_ORDER*&amp;gt; TMAP_DS_UNEXECUTED_ORDER;   /* KEY: LONG OrderNumber */
typedef CMap&amp;lt;CString, LPCTSTR, PTST_RIL_SESSION_NODE, PTST_RIL_SESSION_NODE&amp;gt; TMAP_PTST_RIL_SESSION_NODE_BY_tchNoyeNetId;
typedef CMap&amp;lt;SOCKET, SOCKET, PTST_RIL_SESSION_NODE, PTST_RIL_SESSION_NODE&amp;gt; TMAP_PTST_RIL_SESSION_NODE_BY_SOCKET;
typedef CMap&amp;lt;PTST_RIL_SESSION_NODE, PTST_RIL_SESSION_NODE, SOCKET, SOCKET&amp;gt; TMAP_PTST_RIL_SESSION_NODE_POOL;
typedef CMap&amp;lt;UINT, UINT, PTST_RIL_SESSION_NODE, PTST_RIL_SESSION_NODE&amp;gt; TMAP_PTST_RIL_SESSION_NODE_ALLOC_POOL;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MSDN에 나와 있는 공용 메서드 목록은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CMap::GetCount 이&amp;nbsp;맵의&amp;nbsp;요소&amp;nbsp;수를&amp;nbsp;반환합니다.&lt;/li&gt;
&lt;li&gt;CMap::GetHashTableSize 해시 테이블의 요소 수를 반환합니다.&lt;/li&gt;
&lt;li&gt;CMap::GetNextAssoc 반복할 다음 요소를 가져옵니다.&lt;/li&gt;
&lt;li&gt;CMap::GetSize 이 맵의 요소 수를 반환합니다.&lt;/li&gt;
&lt;li&gt;CMap::GetStartPosition 첫 번째 요소의 위치를 반환합니다.&lt;/li&gt;
&lt;li&gt;CMap::InitHashTable 해시 테이블을 초기화하고 해당 크기를 지정합니다.&lt;/li&gt;
&lt;li&gt;CMap::IsEmpty 빈 맵 조건(요소 없음)에 대한 테스트입니다.&lt;/li&gt;
&lt;li&gt;CMap::Lookup 지정된 키에 매핑된 값을 조회합니다.&lt;/li&gt;
&lt;li&gt;CMap::PGetFirstAssoc 첫 번째 요소에 대한 포인터를 반환합니다.&lt;/li&gt;
&lt;li&gt;CMap::PGetNextAssoc 반복할 다음 요소에 대한 포인터를 가져옵니다.&lt;/li&gt;
&lt;li&gt;CMap::PLookup 값이 지정된 값과 일치하는 키에 대한 포인터를 반환합니다.&lt;/li&gt;
&lt;li&gt;CMap::RemoveAll 이 맵에서 모든 요소를 제거합니다.&lt;/li&gt;
&lt;li&gt;CMap::RemoveKey 키로 지정된 요소를 제거합니다.&lt;/li&gt;
&lt;li&gt;CMap::SetAt 맵에 요소를 삽입합니다. 는 일치하는 키가 있으면 기존 요소를 대체합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. CMap 클래스 사용 예제&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.1. 메모리 풀 구현에 CMap 클래스 사용&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TST_RIL_SESSION_NODE 타입을 배열로 여러개 만들어두고 메모리 풀로 관리하려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점유되지 않은 슬롯을 찾기 위해 탐색을 해야 하는데 이 때 CMap이 유용하게 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TST_RIL_SESSION_NODE_POOL에 mapFreeNodesPool, mapAllocNodesPool을 두었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 구조체 생성자에서 두 맵을 .InitHashTable() 로 크기를 할당합니다. InitHashTable은 호출해도 되고 안 해도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어차피 동적으로 사용 가능합니다. 그 다음 for() 문에서 &quot;mapFreeNodesPool[&amp;amp;(astNodes[i])] = i;&quot; 코드가 초기화의 핵심입니다. P&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;TST_RIL_SESSION_NODE 를 map 인덱스로 하여 배열 인덱스를 저장하고 있습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&quot;mapFreeNodesPool.SetAt(&amp;amp;(astNodes[i]), i);&quot;으로 사용하는 것과 동일합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1703889513416&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* 메모리 객체 구조체 타입 */
typedef struct _ril_session_node : OVERLAPPED {
	const WORD __wMemPoolIndex;
	const BOOL __boDynamicallyAllocated;
	SRWLOCK srwlock;

	SOCKET _sock;
	char _buff[RIL_SESSION_NODE_BUF_LEN];
	DWORD dwUsedLen;	// used length of _buff. purpose: resuming truncated packets

	_ril_session_node():__wMemPoolIndex(0), __boDynamicallyAllocated(FALSE) {
		ZeroMemory(this, sizeof(_ril_session_node));
		_sock = INVALID_SOCKET;
		InitializeSRWLock(&amp;amp;srwlock);
	}

} TST_RIL_SESSION_NODE, * PTST_RIL_SESSION_NODE;

/* PTST_RIL_SESSION_NODE를 KEY로 갖는 CMap 클래스 타입 */
typedef CMap&amp;lt;PTST_RIL_SESSION_NODE, PTST_RIL_SESSION_NODE, UINT, UINT&amp;gt; TMAP_PTST_RIL_SESSION_NODE_ALLOC_POOL;

/* PTST_RIL_SESSION_NODE 를 관리할 메모리풀 구조체 타입 */
typedef struct _ril_session_node_pool {
	SRWLOCK srwlock;
	WORD wFreeNodesCnt;
	TMAP_PTST_RIL_SESSION_NODE_ALLOC_POOL mapFreeNodesPool;	/* Free Nodes */
	TMAP_PTST_RIL_SESSION_NODE_ALLOC_POOL mapAllocNodesPool;	/* Allocated Nodes */
	TST_RIL_SESSION_NODE astNodes[MAX_CAPACITY_COUNTOF_SESSION_NODE];
	_ril_session_node_pool() {
		wFreeNodesCnt = sizeof(astNodes) / sizeof(TST_RIL_SESSION_NODE);
		mapFreeNodesPool.InitHashTable((sizeof(astNodes) / sizeof(TST_RIL_SESSION_NODE)));
		mapAllocNodesPool.InitHashTable((sizeof(astNodes) / sizeof(TST_RIL_SESSION_NODE)));

		for (int i = 0; i &amp;lt; (sizeof(astNodes) / sizeof(TST_RIL_SESSION_NODE)); i++) {
			(WORD)(astNodes[i].__wMemPoolIndex) = (WORD)i;
			mapFreeNodesPool[&amp;amp;(astNodes[i])] = i;
			/* Remove Usage: mapFreeNodesPool.RemoveKey[i]; */
		}
		InitializeSRWLock(&amp;amp;srwlock);
	}
} TST_RIL_SESSION_NODE_POOL, * PTST_RIL_SESSION_NODE_POOL;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 위에서 정의한 메모리 풀을 사용하기 위해 메모리 풀 인스턴스 생성 후 할당 함수 코드를 작성해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;new TST_RIL_SESSION_NODE&quot; 호출 대신, &quot;pAlloc_PTST_RIL_SESSION_NODE()&quot;호출로 메모리풀에서 할당받을 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;_m_stPrsnAllocPool.IsEmpty() 호출 대신 Diagnostics 용도의 wFreeNodesCnt를 체크하여 &quot;_m_stPrsnAllocPool.wFreeNodesCnt &amp;lt;= 0&quot; 코드로 Empty여부를 판단하는데 여러분 자유입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;할당은 비어있는 아무 슬롯을 최대한 빨리 찾아 주면 되기 때문에 &quot;PGetFirstAssoc()&quot; 메서드를 사용합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;CPair&quot;가 핵심입니다. CPair* p에는 KEY와 VALUE가 들어있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제코드에서의 p-&amp;gt;key는 PTST_RIL_SESSION_NODE입니다. p-&amp;gt;key-&amp;gt;를 통해 PTST_RIL_SESSION_NODE의 각 필드에도 접근할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1703890103624&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* 메모리 풀 인스턴스 생성 */
TST_RIL_SESSION_NODE_POOL _m_stPrsnAllocPool;

PTST_RIL_SESSION_NODE CRilSession::pAlloc_PTST_RIL_SESSION_NODE(void)
{
	CAutoScopeSRWLOCKExclusive lock(&amp;amp;(_m_stPrsnAllocPool.srwlock));

	if (_m_stPrsnAllocPool.wFreeNodesCnt &amp;lt;= 0) {
		return nullptr;
	}

	TMAP_PTST_RIL_SESSION_NODE_ALLOC_POOL::CPair* p = _m_stPrsnAllocPool.mapFreeNodesPool.PGetFirstAssoc();
	_m_stPrsnAllocPool.mapFreeNodesPool.RemoveKey(p-&amp;gt;key);
	_m_stPrsnAllocPool.mapAllocNodesPool.SetAt(p-&amp;gt;key, p-&amp;gt;value);
	_m_stPrsnAllocPool.wFreeNodesCnt--;

	return (p-&amp;gt;key);
}

void CRilSession::vFree_PTST_RIL_SESSION_NODE(PTST_RIL_SESSION_NODE pKey)
{
	if (nullptr == pKey) {
		return;
	}

	CAutoScopeSRWLOCKExclusive lock(&amp;amp;(_m_stPrsnAllocPool.srwlock));

	BOOL boRet = _m_stPrsnAllocPool.mapAllocNodesPool.RemoveKey(pKey);
	
	if (FALSE == boRet) {
		if (TRUE == pKey-&amp;gt;__boDynamicallyAllocated) {	// allocated dynamically by &quot;new TST_RIL_SESSION_NODE&quot;
			delete pKey;
		}
		return;
	}

	pKey-&amp;gt;vResetFiledData();
	pKey-&amp;gt;_sock = INVALID_SOCKET;

	_m_stPrsnAllocPool.mapAllocNodesPool.SetAt(pKey, pKey-&amp;gt;__wMemPoolIndex);
	_m_stPrsnAllocPool.wFreeNodesCnt++;

	return;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2. Key값에 대응하는 Value 얻기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lookup메서드를 사용합니다. 파라미터로 넘기는 Key, Value 둘 다 레퍼런스 타입임에 유의하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포인터를 넘기지 않습니다. Lookup에 성공하면 TRUE를 반환하므로 이때 Value 값이 유효합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1703890546788&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int CDataStore::iGet_TST_DS_STOCK_MASTER_expCodeIndex(LPCTSTR expCode)
{
	int iIndex;
	BOOL boRet;

	AcquireSRWLockShared(&amp;amp;(_m_stDS_STOCK_INDEX.srwlock));
	boRet = _m_stDS_STOCK_INDEX.mapIndexEXP.Lookup(expCode, iIndex);
	ReleaseSRWLockShared(&amp;amp;(_m_stDS_STOCK_INDEX.srwlock));

	if (TRUE == boRet)
		return iIndex;
	else
		return -1;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;3.3. CMap 훑어보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CMap 클래스 내부의 전체 키와 값을 출력하려면 CMap 클래스의 GetStartPosition() 메서드와 GetNextAssoc() 메서드를 사용하여 가능합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1703891159685&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;POSITION pos;
PTST__DS_BALANCE_OCCURS p = nullptr;
posOccurs = pBalanceObj-&amp;gt;pMapOccurs-&amp;gt;GetStartPosition();
while (pos) {
			pBalanceObj-&amp;gt;pMapOccurs-&amp;gt;GetNextAssoc(posOccurs, cstrTemp, p);

			/* Do Something~~ */
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상으로 MFC에서 CMap 클래스를 사용하는 법을 알아보았습니다.&lt;/p&gt;</description>
      <category>프로그래밍/C | C++</category>
      <category>CMap class</category>
      <category>CMap GetNextAssoc</category>
      <category>CMap SetAt</category>
      <category>CPair</category>
      <category>MFC CMap</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/60</guid>
      <comments>https://noyecube.tistory.com/entry/MFC-CMap-class-%EC%82%AC%EC%9A%A9%EB%B2%95-%EC%98%88%EC%A0%9C#entry60comment</comments>
      <pubDate>Sat, 30 Dec 2023 08:07:56 +0900</pubDate>
    </item>
    <item>
      <title>[WINAPI] 비동기 소켓 재활용 예제</title>
      <link>https://noyecube.tistory.com/entry/WINAPI-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%86%8C%EC%BC%93-%EC%9E%AC%ED%99%9C%EC%9A%A9-%EC%98%88%EC%A0%9C</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;소켓 재활용은 이미 연결이 끊어진 소켓을 재사용하는 기술입니다. 소켓을 재활용하면 새로운 소켓을 생성하지 않고 기존 소켓을 활용하여 새로운 연결을 맺을 수 있습니다. 즉 소켓 생성 및 소켓 핸들 관리에 필요한 시스템 리소스를 절약할 수 있습니다. 소켓을 재활용하기 위해서는 소켓 연결이 끊어진 상태여야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 관점에서 클라이언트의 접속을 비동기로 처리하는 과정은 다음과 같습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;리슨 소켓 생성 및 리슨 포트 바인드&lt;/li&gt;
&lt;li&gt;소켓 미리 생성 후 대기 소켓 풀에 등록&lt;/li&gt;
&lt;li&gt;클라이언트 접속 종료 시 사용한 소켓 재사용처리 후 대기 소켓 풀에 등록&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제 코드는 IOCP 기반입니다. 이해가 되지 않으면 다음의 IOCP 프로토타입 포스팅을 참고하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blog.noyecube.com/entry/aa&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2023.12.13 - [프로그래밍/C | C++] - IOCP 기반 다중 접속 및 데이터 처리-1&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1703428874411&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;IOCP 기반 다중 접속 및 데이터 처리-1&quot; data-og-description=&quot;윈도 환경에서 다중 클라이언트의 접속을 처리하는 네트워크 서버를 구현하기 위해서는 IOCP 사용이 필수라고 생각합니다. 에코 서버를 IOCP 기반으로 구현한 예제는 많지만 실제 현업에서 다중 &quot; data-og-host=&quot;blog.noyecube.com&quot; data-og-source-url=&quot;https://blog.noyecube.com/entry/aa&quot; data-og-url=&quot;https://blog.noyecube.com/entry/aa&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bYzrjQ/hyUPKPle5k/5cPzdUlaxUk0MEFXcIMtmk/img.png?width=663&amp;amp;height=338&amp;amp;face=0_0_663_338,https://scrap.kakaocdn.net/dn/bvhErF/hyUPzmMiM0/Bqno5hZu4V8oKLUuQS6KB1/img.png?width=663&amp;amp;height=338&amp;amp;face=0_0_663_338,https://scrap.kakaocdn.net/dn/hwAuU/hyUPAFYdy3/lbkyvPHsFpAAKcMnVKOPc1/img.png?width=1162&amp;amp;height=852&amp;amp;face=0_0_1162_852&quot;&gt;&lt;a href=&quot;https://blog.noyecube.com/entry/aa&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://blog.noyecube.com/entry/aa&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bYzrjQ/hyUPKPle5k/5cPzdUlaxUk0MEFXcIMtmk/img.png?width=663&amp;amp;height=338&amp;amp;face=0_0_663_338,https://scrap.kakaocdn.net/dn/bvhErF/hyUPzmMiM0/Bqno5hZu4V8oKLUuQS6KB1/img.png?width=663&amp;amp;height=338&amp;amp;face=0_0_663_338,https://scrap.kakaocdn.net/dn/hwAuU/hyUPAFYdy3/lbkyvPHsFpAAKcMnVKOPc1/img.png?width=1162&amp;amp;height=852&amp;amp;face=0_0_1162_852');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;IOCP 기반 다중 접속 및 데이터 처리-1&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;윈도 환경에서 다중 클라이언트의 접속을 처리하는 네트워크 서버를 구현하기 위해서는 IOCP 사용이 필수라고 생각합니다. 에코 서버를 IOCP 기반으로 구현한 예제는 많지만 실제 현업에서 다중&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;blog.noyecube.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blog.noyecube.com/entry/IOCP-%EA%B8%B0%EB%B0%98-%EB%8B%A4%EC%A4%91-%EC%A0%91%EC%86%8D-%EB%B0%8F-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B2%98%EB%A6%AC-2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2023.12.14 - [프로그래밍/C | C++] - IOCP 기반 다중 접속 및 데이터 처리-2&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1703428885329&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;IOCP 기반 다중 접속 및 데이터 처리-2&quot; data-og-description=&quot;이전 포스팅에서 IOCP 서버 스켈레톤 코드를 작성해 보았습니다. 이번 포스팅에서는 IOCP 서버를 위한 스레드풀 초기화 코드를 살펴보겠습니다. 1. IOCP 서버 실행 결과 실행결과 먼저 살펴보겠습니&quot; data-og-host=&quot;blog.noyecube.com&quot; data-og-source-url=&quot;https://blog.noyecube.com/entry/IOCP-%EA%B8%B0%EB%B0%98-%EB%8B%A4%EC%A4%91-%EC%A0%91%EC%86%8D-%EB%B0%8F-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B2%98%EB%A6%AC-2&quot; data-og-url=&quot;https://blog.noyecube.com/entry/IOCP-%EA%B8%B0%EB%B0%98-%EB%8B%A4%EC%A4%91-%EC%A0%91%EC%86%8D-%EB%B0%8F-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B2%98%EB%A6%AC-2&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/AIXty/hyUTJH0veL/TzinpygtsE2DOwcuj4gEyK/img.png?width=800&amp;amp;height=302&amp;amp;face=0_0_800_302,https://scrap.kakaocdn.net/dn/bZEBYs/hyUPyVGap8/3AslP074ptFP0kqUmkp9uK/img.png?width=800&amp;amp;height=302&amp;amp;face=0_0_800_302,https://scrap.kakaocdn.net/dn/VTIfe/hyUPyuCzhh/sb3VQjFPeyixnlZQkAaEU1/img.png?width=1086&amp;amp;height=1426&amp;amp;face=0_0_1086_1426&quot;&gt;&lt;a href=&quot;https://blog.noyecube.com/entry/IOCP-%EA%B8%B0%EB%B0%98-%EB%8B%A4%EC%A4%91-%EC%A0%91%EC%86%8D-%EB%B0%8F-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B2%98%EB%A6%AC-2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://blog.noyecube.com/entry/IOCP-%EA%B8%B0%EB%B0%98-%EB%8B%A4%EC%A4%91-%EC%A0%91%EC%86%8D-%EB%B0%8F-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B2%98%EB%A6%AC-2&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/AIXty/hyUTJH0veL/TzinpygtsE2DOwcuj4gEyK/img.png?width=800&amp;amp;height=302&amp;amp;face=0_0_800_302,https://scrap.kakaocdn.net/dn/bZEBYs/hyUPyVGap8/3AslP074ptFP0kqUmkp9uK/img.png?width=800&amp;amp;height=302&amp;amp;face=0_0_800_302,https://scrap.kakaocdn.net/dn/VTIfe/hyUPyuCzhh/sb3VQjFPeyixnlZQkAaEU1/img.png?width=1086&amp;amp;height=1426&amp;amp;face=0_0_1086_1426');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;IOCP 기반 다중 접속 및 데이터 처리-2&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이전 포스팅에서 IOCP 서버 스켈레톤 코드를 작성해 보았습니다. 이번 포스팅에서는 IOCP 서버를 위한 스레드풀 초기화 코드를 살펴보겠습니다. 1. IOCP 서버 실행 결과 실행결과 먼저 살펴보겠습니&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;blog.noyecube.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. LISTEN SOCKET 생성하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 443번 포트로 서비스를 하려면 443번의 포트로의 접속 요청을 현재 프로세스가 소유한 소켓 핸들과 연관 관계를 맺어 주어야 합니다. 이를 소켓 바인딩이라고 합니다. 빈 소켓을 생성 후 포트번호와 함께 바인드함수를 호출하면 443번 포트는 현재 프로세스의 소켓 핸들과 연결되어 컨트롤 가능해집니다. 그다음 소켓을 LISTEN 상태로 설정하여 클라이언트의 연결 요청을 받을 수 있는 상태로 변경하면 리슨 소켓 생성 단계가 완료됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1703430613041&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// TCP/IP 속성의 소켓을 생성
SOCKET hsoListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

// 소켓 생성 확인
if (hsoListen == INVALID_SOCKET)
{
	DPRINTF(&quot;[ERR] socket failed, code : (%d)\n&quot;, WSAGetLastError());
	return INVALID_SOCKET;
}

// shPortNo: 포트번호
// 위에서 생성한 소켓을 포트번호와 bind
SOCKADDR_IN	sa;
memset(&amp;amp;sa, 0, sizeof(SOCKADDR_IN));
sa.sin_family = AF_INET;
sa.sin_port = htons(shPortNo);
sa.sin_addr.s_addr = htonl(INADDR_ANY);
LONG lSockRet = bind(hsoListen, (PSOCKADDR)&amp;amp;sa, sizeof(SOCKADDR_IN));
if (lSockRet == SOCKET_ERROR)
{
	DPRINTF(&quot;[ERR] bind failed, code : (%d)\n&quot;, WSAGetLastError());
	closesocket(hsoListen);
	return INVALID_SOCKET;
}
DPRINTF(&quot;INFO] listen() setup with nBacklog-SOMAXCONN (%x)-(%x)\n&quot;, nBacklog, SOMAXCONN);

// 포트를 리슨상태로 변경
lSockRet = listen(hsoListen, nBacklog);
if (lSockRet == SOCKET_ERROR)
{
	DPRINTF(&quot;[ERR] listen failed, code : (%d)\n&quot;, WSAGetLastError());
	closesocket(hsoListen);
	return INVALID_SOCKET;
}

// shPortNo 포트로 접속 대기 상태가 됨&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. IOCP에 LISTEN SOCKET 등록하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 LISTEN SOCKET에 클라이언트 접속 이벤트가 발생하면 IOCP 콜백 함수가 호출되도록 LISTEN SOCKET으로 IOCP 핸들을 생성합니다. 다음의 코드는 IOCP 핸들을 생성하는 동시에 파라미터로 전달한 hsoListen 소켓 핸들을 IOCP에 등록하고 IOCP 콜백 호출 시 IOKEY_LISTEN 값을 파라미터로 전달합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1703431190836&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#define IOKEY_LISTEN	1

HANDLE hIocp = INVALID_HANDLE_VALUE;

hIocp = CreateIoCompletionPort((HANDLE)hsoListen, NULL, IOKEY_LISTEN, (stSysInfo.dwNumberOfProcessors / 2));
if (NULL == hIocp) {
	DPRINTF(&quot;[ERR] CreateIoCompletionPort failed, code : (%d)\n&quot;, GetLastError());
	return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. IOCP LISTEN SOCKET에 ACCEPT 소켓 미리 생성하여 할당하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Accept() 함수는 동기함수이기 때문에 호출시 클라이언트가 접속할 때까지 호출위치에서 블로킹상태에 빠집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트의 접속 요청을 비동기적으로 처리하기 위해서는 소켓을 미리 생성 후 IOCP에 등록된 LISTEN SOCKET과 BIND 해두면 접속 이벤트 발생 시 IOCP 콜백함수가 호출되게 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1703431862220&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// AcceptEx 함수 포인터 획득
LPFN_ACCEPTEX pfnAcceptEx = (LPFN_ACCEPTEX)
	GetSockExtAPI(hsoListen, WSAID_ACCEPTEX);
   
int nPooledCnt = 0;
int nIncCnt = 10;	// 미리 생성할 소켓 수 10개로 설정
for (; nPooledCnt &amp;lt; nIncCnt; nPooledCnt++)
{
	// 빈 소켓을 미리 생성한다.
	SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (sock == INVALID_SOCKET)
		break;
        
    // OVERLAPPED 구조체를 상속한 소켓 노드 구조체
    // typedef struct _ril_session_node : OVERLAPPED
    // 구조체 내부에 SOCKET _sock; 을 필드로 갖고 있음
	PTST_RIL_SESSION_NODE prsn = new TST_RIL_SESSION_NODE;
	prsn-&amp;gt;_sock = sock;
    
    // AcceptEx 호출. 리슨소켓에 빈 소켓을 바인딩함과 동시에 OVERLAPPED 구조체를 넘겨서
    // listen socket이 등록된 IOCP 핸들과 연결 한다.
    // 즉, listen socket에 접속 이벤트가 발생하면 미리 생성한 빈 소켓에 Accept가 되면서
    // IOCP 콜백함수가 호출되며 이 때의 파라미터는 CreateIoCompletionPort 호출시에 넘긴
    // IOKEY_LISTEN 값이 되므로 이를 통해 새로운 접속이벤트를 처리할 수 있다.
	BOOL bIsOK = pfnAcceptEx
	(
		hsoListen, sock, prsn-&amp;gt;_buff, ACCEPTEX_INITIAL_DWRECEIVEDATALEN,
		sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16,
		NULL, (LPOVERLAPPED)prsn
	);
    
	if (bIsOK == FALSE)
	{
		if (WSAGetLastError() != WSA_IO_PENDING)
		{
			DPRINTF(&quot;[ERR] AcceptEx failed, code : (%d)\n&quot;, WSAGetLastError());
			closesocket(prsn-&amp;gt;_sock);
			delete prsn;
			break;
		}
	}
    
    // 미리 생성해둔 소켓은 별도 소켓 풀에 등록해 둠
	AcquireSRWLockExclusive(&amp;amp;(_m_stSessionObj.srwlock));
	_m_stSessionObj.mapAcceptExPool.SetAt(prsn, prsn-&amp;gt;_sock);
	ReleaseSRWLockExclusive(&amp;amp;(_m_stSessionObj.srwlock));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. IOCP 콜백 함수에서 클라이언트 Connect, Disconnect 감지&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ThdRilIocp() 는 IOCP 스레드풀의 Worker Thread입니다. 이벤트 발생 시 호출되는 Callback Thread입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Connect&amp;nbsp; 감지는 IOKEY_LISTEN 값을 파라미터로 받았을 때 입니다. Accept 완료 상태이므로 Accept 소켓을 IOCP에 등록합니다. 접속한 클라이언트가 패킷 데이터를 송신하면 역시 IOCP 콜백스레드가 호출되며 else {} 구문으로 빠지게 됩니다. else {} 구문 진입했는데 전송받은 데이터가 0바이트이면 접속 종료로 처리하고 그게 아니면 데이터를 처리하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1703432284979&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;UINT CRilServer::ThdRilIocp(LPVOID pParam)
{
...생략...

	PTST_RIL_SESSION_NODE prsn = NULL;

	int nErrCode;

	while (TRUE) {
		try
		{
			BOOL bIsOK = GetQueuedCompletionStatus(hIocp, &amp;amp;dwTrBytes, &amp;amp;upDevKey, (LPOVERLAPPED*)&amp;amp;prsn, INFINITE);

			if (FALSE == bIsOK)
			{
				...생략...
			}

			// 신규 클라이언트 Connect 이벤트 감지 부분
			if (IOKEY_LISTEN == upDevKey)
			{
            	// 신규 접속이므로 미리 생성된 소켓을 IOCP에 등록해주고 이벤트 키는 IOKEY_CHILD로 등록
				CreateIoCompletionPort((HANDLE)prsn-&amp;gt;_sock, hIocp, IOKEY_CHILD, 0);
				DPRINTF(&quot;[INFO] ==&amp;gt; New client, connected...\n&quot;);
				pRilObj-&amp;gt;_m_pThdRilServerMgr-&amp;gt;PostThreadMessageA(MSG_ID_CRILSEREVR_SOCK_CONNECTED, 0, (LPARAM)prsn);
			}
			else
			{
            	// 클라이언트 Disconnect 이벤트 감지 부분
				if (0 == dwTrBytes) {
					throw (INT)ERROR_SUCCESS;	/* Socket disconnected from client */
				}
                
               ...생략...
               
             }
....생략
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;catch 문에서 처리하는 STATUS 값은 다음과 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1703432435634&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#ifndef NTSTATUS_SOCK_LOCAL_DISCONNECT
#	define NTSTATUS_SOCK_LOCAL_DISCONNECT	((LONG)0xC000013BL)	//ERROR_NETNAME_DELETED
#endif
#ifndef NTSTATUS_SOCK_REMOTE_DISCONNECT
#	define NTSTATUS_SOCK_REMOTE_DISCONNECT	((LONG)0xC000013CL)	//ERROR_NETNAME_DELETED
#endif
#ifndef NTSTATUS_SOCK_CONNECTION_RESET
#	define NTSTATUS_SOCK_CONNECTION_RESET	((LONG)0xC000020DL)	//ERROR_NETNAME_DELETED
#endif
#ifndef NTSTATUS_SOCK_CANCELLED
#	define NTSTATUS_SOCK_CANCELLED			((LONG)0xC0000120L)	//ERROR_OPERATION_ABORTED
#endif

catch (int ex)
{
	if ((ex == NTSTATUS_SOCK_LOCAL_DISCONNECT) || (ex == NTSTATUS_SOCK_CANCELLED))
	{
		DPRINTF(&quot;[DBG]  ==&amp;gt; Child socket closed.\n&quot;);
		continue;
	}
	if ((ex == ERROR_SUCCESS) || (ex == NTSTATUS_SOCK_REMOTE_DISCONNECT)) {
		DPRINTF(&quot;[DBG]  ==&amp;gt;  ==&amp;gt; Client  disconnected...\n&quot;);
	}
	else if (ex == NTSTATUS_SOCK_CONNECTION_RESET) {
		DPRINTF(&quot;[DBG]  ==&amp;gt; Pending Client disconnected...\n&quot;);
	}
	else {
		DPRINTF(&quot;[DBG] ==&amp;gt; Client has error(%d)...\n&quot;, ex);
	}
	pRilObj-&amp;gt;_m_pThdRilServerMgr-&amp;gt;PostThreadMessageA(MSG_ID_CRILSERVER_SOCK_DISCONNECTED, ex, (LPARAM)prsn);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 접속, 종료 세션 관리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 접속 세션 객체를 리스트로 관리했는데 세션을 관리하는 자료구조는 프로그램 특성에 따라 자유롭게 개발하면 됩니다. IOCP 스레드에서 접속, 종료에 따라 이벤트 메시지를 전송하면 아래와 같이 세션에 등록하거나 세션에서 해제합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1703432793822&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PTST_RIL_SESSION_NODE prsn = (PTST_RIL_SESSION_NODE)msg.lParam;
if (MSG_ID_CRILSEREVR_SOCK_CONNECTED == msg.message) {
	pthis-&amp;gt;m_RilSession.vRegisterSocketToSessionObj(prsn);
}
else if (MSG_ID_CRILSERVER_SOCK_DISCONNECTED == msg.message) {
	pthis-&amp;gt;m_RilSession.vUnregisterSocketFromSessionObj(hsoListen, prsn);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. 소켓 재사용 처리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료세션 함수 vUnregisterSocketFromSessionObj()의 코드 중 소켓 재사용 처리하는 부분입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;close 함수 대신 disconnectex를 통해 접속만 종료 후 다시 acceptex를 호출하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재사용이기 때문에 상위 응용 스레드에서는 소켓 핸들값만 가지고 클라이언트의 변경을 감지하기 어렵습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 세션을 관리하는 자료구조에서는 클라이언트 접속시 클라이언트 고유 번호를 부여하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접속 종료 이벤트 발생시 해당 클라이언트가 처리 중인 송수신 패킷을 다른 클라이언트에 이관하거나 상위 응용 스레드에 Abort 이벤트를 전달하고 클라이언트 고유번호를 갱신해야 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 클라이언트 A가 서버로 데이터를 요청하여 서버가 데이터를 준비하고 있는 동안 클라이언트 A가 접속 종료를 해버리고 A가 사용하던 소켓 핸들을 재사용하여 클라이언트 B가 접속했는데 서버가 A에 응답하려던 패킷을 클라이언트 B가 받지 않도록 처리를 해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1703432987563&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 소켓을 close 하지 않고 disconnect 합니다.
LPFN_DISCONNECTEX pfnDisconnectEx = (LPFN_DISCONNECTEX)GetSockExtAPI(prsn-&amp;gt;_sock, WSAID_DISCONNECTEX);
pfnDisconnectEx(prsn-&amp;gt;_sock, NULL, TF_REUSE_SOCKET, 0);
prsn-&amp;gt;pNext = nullptr;
prsn-&amp;gt;pPrev = nullptr;
ZeroMemory(prsn-&amp;gt;_buff, sizeof(TST_RIL_SESSION_NODE::_buff));
prsn-&amp;gt;dwUsedLen = 0;
prsn-&amp;gt;pStreamHead = nullptr;	/* should be called free function or pass pointer to worker thread before this line */
prsn-&amp;gt;pStreamTail = nullptr;
		
ZeroMemory(&amp;amp;(prsn-&amp;gt;saLocal), sizeof(SOCKADDR_IN));
ZeroMemory(&amp;amp;(prsn-&amp;gt;saRemote), sizeof(SOCKADDR_IN));
ZeroMemory(&amp;amp;(prsn-&amp;gt;sysTmLoggedIn), sizeof(SYSTEMTIME));
prsn-&amp;gt;ullCntTx = 0;
prsn-&amp;gt;ullCntRx = 0;
prsn-&amp;gt;uRtt = 0;
ZeroMemory(prsn-&amp;gt;tchNoyeNetId, sizeof(TST_RIL_SESSION_NODE::tchNoyeNetId));
prsn-&amp;gt;uTxPriority = 0;
prsn-&amp;gt;enApiCat = TEN_STOCK_API_CAT::UNKNOWN;
prsn-&amp;gt;u8TradeSupportMask = 0;

// 다시 비동기 accept 호출하여 리슨 소켓에 등록해 두고 접속 이벤트 발생시 iocp 콜백 호출 되도록 합니다.
LPFN_ACCEPTEX pfnAcceptEx = (LPFN_ACCEPTEX)GetSockExtAPI(hsoListen, WSAID_ACCEPTEX);
boRet = pfnAcceptEx(hsoListen, prsn-&amp;gt;_sock, prsn-&amp;gt;_buff, ACCEPTEX_INITIAL_DWRECEIVEDATALEN, sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16,	NULL, (LPOVERLAPPED)prsn);
if (FALSE == boRet) {
	int nErrCode = WSAGetLastError();
	if (nErrCode != WSA_IO_PENDING)
	{
		DPRINTF(&quot;[ERR] AcceptEx failed, code : (%d)\n&quot;, GetLastError());
		closesocket(prsn-&amp;gt;_sock);
		delete prsn;
		return;
	}
}

_m_stSessionObj.mapAcceptExPool.SetAt(prsn, prsn-&amp;gt;_sock);
DPRINTF(&quot;DBG] Released socket. AcceptExPool(%Iu) LoggedinSessions(%Iu) ConnectedOnly(%Iu)\n&quot;, _m_stSessionObj.mapAcceptExPool.GetCount(), _m_stSessionObj.mapTchNoyeNetId.GetCount(), _m_stSessionObj.mapConnectedOnlyPool.GetCount());
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. 대기 소켓 풀 증가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제 코드에서는 소켓 10개를 미리 생성하여 IOCP에 등록해두었습니다. 10개의 클라이언트까지는 IOCP 콜백이 호출되어 처리되지만 미리 생성한 소켓을 다 소진했을 때 추가적인 클라이언트 접속 요청을 받으면 미리 생성해 둔 소켓이 없기 때문에 LISTEN SOCKET에 이벤트가 발생하게 됩니다. IOCP 스레드가 아닌 관리 스레드에 LISTEN SOCKET에 대한 ACCEPT 이벤트를 대기하고 있다가 ACCEPT 이벤트 발생 시 소켓을 미리 생성하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제 코드에서의 핵심은 WSAEventSelect에서 FD_ACCEPT 플래그를 LISTEN SOCKET에 등록한 이벤트 hWSAEvent 핸들을 통해 ACCEPT를 감지하고 WSAEnumNetworkEvents에서 FD_ACCEPT 여부를 필터링하여 소켓 생성함수를 다시 호출하는 부분입니다. WSAEnumNetworkEvents 호출시 hWSAEvent 핸들에서 FD_ACCEPT 이벤트는 RESET 되어 다시 새로운 ACCEPT 이벤트를 감지할 수 있는 상태가 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1703433564627&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;	WSAEVENT hWSAEvent = WSACreateEvent();
	WSAEventSelect(hsoListen, hWSAEvent, FD_ACCEPT);
	constexpr DWORD dwNCOUNT= 1;
	DWORD dwWaitRet;
	MSG msg;
	while (TRUE) {
		dwWaitRet = MsgWaitForMultipleObjectsEx(1, &amp;amp;hWSAEvent, RIL_SERVER_SOCKET_POOL_REFRESH_INTERVAL, QS_POSTMESSAGE, MWMO_INPUTAVAILABLE);

		if ((WAIT_OBJECT_0 + 0) == dwWaitRet) {	/* FD_ACCEPT event at hWSAEvent by hsoListen */
			WSANETWORKEVENTS ne;
			WSAEnumNetworkEvents(hsoListen, hWSAEvent, &amp;amp;ne);
			if (ne.lNetworkEvents &amp;amp; FD_ACCEPT) {

				int iRet;
				iRet = pthis-&amp;gt;m_RilSession.IncreaseAcceptSockets(hsoListen);
				DPRINTF(&quot;hsoListen event signaled, iRet(%d) PoolCnt(%Iu), SessionCnt(%Iu)\n&quot;, iRet, pthis-&amp;gt;m_RilSession.iGetCount_AcceptExPOOL(), pthis-&amp;gt;m_RilSession.iGetCount_ConnectedSessions());
			}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 IOCP 기반의 소켓 접속, 종료, 재사용을 알아보았습니다.&lt;/p&gt;</description>
      <category>프로그래밍/C | C++</category>
      <category>AcceptEx</category>
      <category>disconnectex</category>
      <category>GetQueuedCompletionStatus</category>
      <category>iocp</category>
      <category>msgwaitformultipleobjectsex</category>
      <category>overlapped</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/59</guid>
      <comments>https://noyecube.tistory.com/entry/WINAPI-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%86%8C%EC%BC%93-%EC%9E%AC%ED%99%9C%EC%9A%A9-%EC%98%88%EC%A0%9C#entry59comment</comments>
      <pubDate>Mon, 25 Dec 2023 01:04:05 +0900</pubDate>
    </item>
    <item>
      <title>[SQL] SELECT SQL 예제</title>
      <link>https://noyecube.tistory.com/entry/SQL-SELECT-SQL-%EC%98%88%EC%A0%9C</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 데이터를 추출하거나 조회할 때 사용하는 SELECT SQL을 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제 SQL 구문을 작성하기 위한 샘플 데이터 테이블 구조는 아래 그림을 참고해 주세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구입한 책을 보며 직접 따라한 결과를 리마인드 할 겸 일기 형식으로 기록한 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블 구조는 행(ROW)과 열(COLUMN)의 이차원 구조로 이루어져 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;267&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqHEQs/btsCkrzb65S/DM7Mh32YFKdZi9kc4qu6F0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqHEQs/btsCkrzb65S/DM7Mh32YFKdZi9kc4qu6F0/img.png&quot; data-alt=&quot;데이터분석 책에서 제공받은 데이터 테이블명 'stock' 조회 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqHEQs/btsCkrzb65S/DM7Mh32YFKdZi9kc4qu6F0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqHEQs%2FbtsCkrzb65S%2FDM7Mh32YFKdZi9kc4qu6F0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;587&quot; height=&quot;267&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;267&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;데이터분석 책에서 제공받은 데이터 테이블명 'stock' 조회 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SELECT SQL 기본 문법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 'stock' 테이블을 조회하려면 다음의 SQL 쿼리를 실행하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702972415079&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT *
FROM STOCK;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿼리문 작성 시 대소문자는 구분하지 않습니다. 또한 쿼리문의 끝은 다른 프로그래밍 언어와 유사하게 세미콜론(';')으로 구분합니다. 단어와 단어 사이는 띄어쓰기를 하거나 줄 바꿈을 하거나 상관없고 세미콜론으로 마침표만 찍어주면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 쿼리문에서 &quot;SELECT&quot;, &quot;FROM&quot;은 '절(Clause)'로 구분되며 테이블에서 데이터를 추출하려면 SELECT 절과 FROM 절이 필요합니다. '*' 문자는 &quot;모든&quot;것을 뜻하며 SELECT 뒤에 올 경우 모든 열을 선택하겠다는 의미입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'*' 대신 &quot;STK_NM&quot;으로 조회할 경우 아래와 같이 해당 열만 선택되어 조회됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;306&quot; data-origin-height=&quot;241&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bT1ALg/btsCiM4YAEE/mMxGdmrSq230nOpbgoHh0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bT1ALg/btsCiM4YAEE/mMxGdmrSq230nOpbgoHh0K/img.png&quot; data-alt=&quot;SELECT 실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bT1ALg/btsCiM4YAEE/mMxGdmrSq230nOpbgoHh0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbT1ALg%2FbtsCiM4YAEE%2FmMxGdmrSq230nOpbgoHh0K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;306&quot; height=&quot;241&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;306&quot; data-origin-height=&quot;241&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SELECT 실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ORDER BY 절&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ORDER BY 절은 조회 데이터의 정렬 순서를 지정할 때 사용하며 SELECT 구문의 마지막 절로 붙여 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제 테이블에서 STK_NM을 기준으로 정렬하여 데이터 조회를 하려면 다음과 같이 조회합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 확인하면 예제 테이블과 다른 순서로 데이터가 조회된 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702973234820&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT *
FROM STOCK
ORDER BY STK_NM;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;571&quot; data-origin-height=&quot;263&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blv4JN/btsCbdbJcaX/1uI5Yh9i9JtZsnCnFWCvck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blv4JN/btsCbdbJcaX/1uI5Yh9i9JtZsnCnFWCvck/img.png&quot; data-alt=&quot;STK_NM 정렬로 조회한 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blv4JN/btsCbdbJcaX/1uI5Yh9i9JtZsnCnFWCvck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fblv4JN%2FbtsCbdbJcaX%2F1uI5Yh9i9JtZsnCnFWCvck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;571&quot; height=&quot;263&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;571&quot; data-origin-height=&quot;263&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;STK_NM 정렬로 조회한 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬 기준을 설정하지 않으면 기본적으로 오름차순 정렬로 조회됩니다. 정렬기준은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ASC: 오름차순(Ascending)&lt;/li&gt;
&lt;li&gt;DESC: 내림차순(Descending)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 조회구문에 내림차순 기준을 적용한 쿼리문은 다음과 같습니다. 맨 뒤에 정렬 기준을 적어주면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702973632190&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT *
FROM STOCK
ORDER BY STK_NM DESC;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ORDER BY 절은 다음과 같이 여러 칼럼을 지정하여 사용할 수 있습니다. 먼저 나열된 칼럼의 값이 같을 때 뒤쪽에 나열된 칼럼으로 정렬됩니다. 아래 예에서는 종목코드가 같을 수 없기 때문에 뒤쪽의 ORDER BY STK_NM ASC 조건은 무시되어 처리됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1703037675065&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT *
FROM STOCK
WHERE SEC_NM IN ('자동차부품', '기계와장비')
ORDER BY STK_CD ASC, STK_NM ASC;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;WHERE 절&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WHERE 절은 테이블에서 필요한 데이터만 조회하기 위해 조건식 역할을 하는 절입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제 테이블에서 STK_TP_NM이 우선주인 종목만 조회하려면 WHERE 절을 추가하여 다음과 같이 조회합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702974195813&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT *
FROM STOCK
WHERE STK_TP_NM='우선주'
ORDER BY STK_NM;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 구문에서 사용한 '='의 의미는 &quot;같다&quot;입니다. 프로그래밍 언어에서 '=='로 표현되는 것과 차이가 있습니다. 나머지 조건 연산자는 프로그래밍 언어에서 사용하는 것과 동일하며 다음과 같습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.1705%; text-align: center;&quot;&gt;연산자&lt;/td&gt;
&lt;td style=&quot;width: 28.3333%; text-align: center;&quot;&gt;의미&lt;/td&gt;
&lt;td style=&quot;width: 59.4961%; text-align: center;&quot;&gt;상세 설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.1705%; text-align: center;&quot;&gt;=&lt;/td&gt;
&lt;td style=&quot;width: 28.3333%; text-align: left;&quot;&gt;equal to&lt;/td&gt;
&lt;td style=&quot;width: 59.4961%; text-align: left;&quot;&gt;두 값이 동일하면 참&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.1705%; text-align: center;&quot;&gt;!=&lt;/td&gt;
&lt;td style=&quot;width: 28.3333%; text-align: left;&quot;&gt;not equal to&lt;/td&gt;
&lt;td style=&quot;width: 59.4961%; text-align: left;&quot;&gt;두 값이 다르면 참&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.1705%; text-align: center;&quot;&gt;&amp;lt;&lt;/td&gt;
&lt;td style=&quot;width: 28.3333%; text-align: left;&quot;&gt;less than&lt;/td&gt;
&lt;td style=&quot;width: 59.4961%; text-align: left;&quot;&gt;첫 번째 값이 두번째 값보다 작으면 참&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.1705%; text-align: center;&quot;&gt;&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 28.3333%; text-align: left;&quot;&gt;greater than&lt;/td&gt;
&lt;td style=&quot;width: 59.4961%; text-align: left;&quot;&gt;첫 번째 값이 두번째 값보다 크면 참&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.1705%; text-align: center;&quot;&gt;&amp;lt;=&lt;/td&gt;
&lt;td style=&quot;width: 28.3333%; text-align: left;&quot;&gt;less than or equal to&lt;/td&gt;
&lt;td style=&quot;width: 59.4961%; text-align: left;&quot;&gt;첫 번째 값이 두 번째 값보다 작거나 같으면 참&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 12.1705%; text-align: center;&quot;&gt;&amp;gt;=&lt;/td&gt;
&lt;td style=&quot;width: 28.3333%; text-align: left;&quot;&gt;greater than or equal to&lt;/td&gt;
&lt;td style=&quot;width: 59.4961%; text-align: left;&quot;&gt;첫 번째 값이 두 번째 값보다 크거나 같으면 참&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-2965295859199035&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건 연산자를 하나씩 직접 응용해 본 SELECT 문은 다음과 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702975609826&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 우선주만 조회
SELECT *
FROM STOCK
WHERE STK_TP_NM='우선주';

# STK_TP_NM 이 null 인 행만 조회
SELECT *
FROM STOCK
WHERE STK_TP_NM is null;

# STK_TP_NM이 null 이 아닌 행만 조회
SELECT *
FROM STOCK
WHERE STK_TP_NM is not null;

# 섹터가 화장품이 아닌 행만 조회
SELECT *
FROM STOCK
WHERE SEC_NM!='화장품';

# 종목코드가 051905보다 작거나 같은 행만 조회
SELECT *
FROM STOCK
WHERE STK_CD&amp;lt;='051905';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;조건 연산자와 AND, OR를 조합한 중첩 조건문&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C 언어에서 여러 조건을 연결하려면 AND와 OR를 &quot;&amp;amp;&amp;amp;&quot;, &quot;||&quot;로 표현하는데 SQL에서는 문자 그대로(AND, OR) 사용합니다. AND는 연결된 모든 조건을 만족해야 하고 OR는 나열된 조건 중 하나만 만족해도 참이 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제 테이블에서 종목섹터가 화학이면서 코스닥에 상장된 종목을 조회하려면 다음과 같이 AND 조건을 사용하여 조회합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702976348861&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT *
FROM STOCK
WHERE SEC_NM='화학'
AND EX_CD='KD';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제 테이블에서 종목섹터가 기계와장비 또는 자동차부품인 종목을 조회하려면 다음과 같이 OR 조건을 사용하여 조회합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702976541424&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT *
FROM STOCK
WHERE SEC_NM='기계와장비'
OR SEC_NM='자동차부품';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 개의 AND, OR연산자를 사용할 때는 구문별로 괄호를 사용해야 예상하지 못한 결과를 피할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702976862261&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 코스피 종목이면서 섹터가 기계와장비거나 자동자부품인 종목 조회
# 쿼리 작성 의도를 반영하여 괄호로 묶습니다.
SELECT *
FROM STOCK
WHERE EX_CD='KP'
AND (SEC_NM='기계와장비' OR SEC_NM='자동차부품');&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;LIKE 조건 연산자&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스 콘솔 프롬프트에서 &quot;ls *.sh&quot; 명령은 확장자가 &quot;.sh&quot;인 모든 파일을 나열합니다. 또는 &quot;ls file_?.sh&quot;는 중간에 한 글자가 아무 문자나 해당되는 파일을 나열합니다. 이와 유사한 결과를 얻기 위해 SQL에서도 LIKE 조건 연산자를 사용할 수 있습니다. LIKE 연산자는 문자열의 패턴을 지정하여 데이터를 검색하는 데 사용되며 숫자 타입에는 사용 불가능합니다. 또는 LIKE 연산자의 기본 구문은 다음과 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702978535426&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;WHERE 컬럼명 LIKE 패턴;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패턴은 문자열로 지정되며 와일드카드 문자를 사용하여 지정할 수 있고 와일드카드와 의미는 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;%: 0개 이상의 문자를 나타냅니다.&lt;/li&gt;
&lt;li&gt;_: 1개의 문자를 나타냅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 쿼리문은 STOCK 테이블에서 NM 칼럼의 값이 &quot;삼성&quot;으로 시작(포함)하는 모든 데이터를 검색합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702978749632&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT *
FROM STOCK
WHERE STK_NM LIKE '삼성%';&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;528&quot; data-origin-height=&quot;231&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kWwLy/btsCk5izrCk/DWsZA15KCZ7Psje7Gs7fL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kWwLy/btsCk5izrCk/DWsZA15KCZ7Psje7Gs7fL0/img.png&quot; data-alt=&quot;WHERE STK_NM LIKE '삼성%' 조회 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kWwLy/btsCk5izrCk/DWsZA15KCZ7Psje7Gs7fL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkWwLy%2FbtsCk5izrCk%2FDWsZA15KCZ7Psje7Gs7fL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;528&quot; height=&quot;231&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;528&quot; data-origin-height=&quot;231&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;WHERE STK_NM LIKE '삼성%' 조회 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포함하지 않는 데이터 조회 시 NOT LIKE를 사용합니다. 섹터가 &quot;반도체&quot;인데 종목명에 &quot;전자&quot;가 없는 종목 조회 방법은 다음과 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702979090335&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT *
FROM STOCK
WHERE SEC_NM='반도체'
AND STK_NM NOT LIKE '%전자%';&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;233&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbODoa/btsCfZ5bkmo/BufeZWZkKpbtHpI4cHQSv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbODoa/btsCfZ5bkmo/BufeZWZkKpbtHpI4cHQSv0/img.png&quot; data-alt=&quot;NOT LIKE 조회 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbODoa/btsCfZ5bkmo/BufeZWZkKpbtHpI4cHQSv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbODoa%2FbtsCfZ5bkmo%2FBufeZWZkKpbtHpI4cHQSv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;233&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;233&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;NOT LIKE 조회 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;IN 조건 연산자&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;섹터가 &quot;증권&quot;, &quot;금융&quot;, &quot;은행&quot;인 종목을 OR 연산자를 활용하여 조회하는 구문은 다음과 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702979474194&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT *
FROM STOCK
WHERE SEC_NM='증권'
OR SEC_NM='금융'
OR SEC_NM='은행';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 구문을 IN 연산자를 적용하면 다음과 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702979541033&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT *
FROM STOCK
WHERE SEC_NM IN ('증권','금융','은행');&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 섹터가 &quot;증권&quot;, &quot;금융&quot;, &quot;은행&quot;이 아닌 종목 조회는 NOT IN을 사용하여 다음과 같이 조회합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702979594098&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT *
FROM STOCK
WHERE SEC_NM NOT IN ('증권','금융','은행');&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;BETWEEN 조건 연산자&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IN 연산자가 OR 쿼리문을 최적화하였다면 BETWEEN 연산자는 AND 쿼리문을 최적화할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음의 쿼리문으로 BETWEEN을 사용했을 때 어떤 느낌인지 감을 잡아볼 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1703037085779&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT *
FROM STOCK
WHERE STK_CD&amp;gt;='002820'
AND STK_CD&amp;lt;='002990';

# BETWEEN 사용
SELECT *
FROM STOCK
WHERE STK_CD BETWEEN '002820' AND '002990';&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;자료형(Data Type)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Database에는 다양한 데이터 타입이 있지만 그중에서 문자, 숫자 데이터 타입이 가장 일반적으로 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자 데이터 타입을 조회할 때는 싱글 따옴표(')를 사용해야 합니다. 12345는 12,345의 값을 가진 숫자이고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'12345'는 문자열입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자 데이터 타입은 문자로 조회하고 숫자 데이터 타입은 숫자로 조회해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상으로 SELECT SQL문에 대한 기본적인 사용 방법을 알아보았습니다.&lt;/p&gt;</description>
      <category>Database/Oracle</category>
      <category>order by</category>
      <category>select sql</category>
      <category>select where</category>
      <category>SELECT 기초</category>
      <category>SQL IN</category>
      <category>SQL like</category>
      <category>SQL 예제</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/58</guid>
      <comments>https://noyecube.tistory.com/entry/SQL-SELECT-SQL-%EC%98%88%EC%A0%9C#entry58comment</comments>
      <pubDate>Wed, 20 Dec 2023 00:15:40 +0900</pubDate>
    </item>
    <item>
      <title>[Oracle] legacy exp dump파일 data pump expdp import 방법</title>
      <link>https://noyecube.tistory.com/entry/Oracle-dump%ED%8C%8C%EC%9D%BC-import-%EB%B0%A9%EB%B2%95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅 주제는 &quot;Importing Dump Files into Oracle Database Using SQL Developer, Starting with Tablespaces and Users&quot;입니다. 제가 얼마 전 SQL 공부를 위해 &quot;평생 필요한 데이터 분석&quot;이라는 제목의 책을 샀는데 실습을 위해서는 Oracle Database를 설치하고 책 저자님이 제공해 주시는 샘플 데이터를 import 해야 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블스페이스와 사용자 계정을 생성하고 제공받은 덤프 파일을 리눅스에 설치한 Oracle DB에 IMPORT 해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.제공받은 dump 파일을 import 해야 할 때 사전 검토 사항&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;남으로부터 제공받은 dump 파일을 import 하려면 다음의 사항을 사전에 검토해야 합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;계정명, 테이블스페이스명, 테이블스페이스 용량&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;계정명은 반드시 일치해야 합니다.&lt;/li&gt;
&lt;li&gt;테이블스페이스명은 일치하지 않아도 됩니다.&lt;/li&gt;
&lt;li&gt;테이블스페이스 용량이 제공받은 dump 파일의 용량 이상이어야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;캐릭터 셋&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dump 파일의 캐릭터 셋과 대상 DB의 캐릭터 셋이 다를 경우 데이터가 깨질 수 있습니다.&lt;/li&gt;
&lt;li&gt;dump 파일의 캐릭터 셋을 사전에 체크하여 대상 DB의 캐릭터 셋으로 변경해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저의 경우, 제공받은 dump 파일의 캐릭터 셋이 AL32UTF이고 대상 DB의 캐릭터셋이 KO16MSWIN949라서 캐릭터셋 변경이 필요합니다. 덤프 파일의 확장자가 .dump이면 구형 유틸리티 exp/imp를 사용하였기 때문에 캐릭터셋이 일치해야 합니다. 확장자가 .dmp이면 DATA PUMP 유틸리티 expdp/impdp를 사용하였기 때문에 캐릭터셋이 일치하지 않아도 &quot;possible data loss&quot;경고만 뜨고 import는 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버추얼박스에서 AL32UTF8 캐릭터셋 DB를 빠르게 설치 후 그 DB에 제공받은 덤프 파일을 import 후 다시 data pump 유틸리티 expdp로 export 후 캐릭터셋이 KO16MSWIN949인 제 DB에 import 해보겠습니다. AL32UTF8 캐릭터 셋은 전 세계의 모든 언어를 표현할 수 있는 대신 한글도 1글자에 3바이트를 소비하여 비효율적입니다. KO16MSWIN949는 한글 1글자에 2바이트가 필요하고 영어도 표현되므로 아랍어, 러시아어, 중국어 같은 문자까지 저장해야 하는 DB가 아니면 대한민국에서는 KO16MSWIN949를 추천합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. exp/imp 와 expdp/impdp&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;exp/imp는 Oralce 8i 이전 버전부터 사용된 데이터 백업 및 복원 유틸리티입니다. exp/imp는 단일 프로세스로 데이터를 export 및 import 하므로, 대규모 데이터베이스의 경우 시간이 오래 걸리고 자원 사용량이 많다는 단점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;expdp/impdp는 Oracle 10g부터 도입된 데이터 백업 및 복원 유틸리티입니다. expdp/impdp는 다중 프로세스로 데이터를 export 및 import하므로 exp/imp에 비해 성능이 우수하고 자원 사용량이 적습니다. 특히 expdp/impdp는 데이터베이스 전체를 export 하거나 import 할 수 있으며 데이터 압축 및 암호화 기능을 제공합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 테이블스페이스, 계정 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제공받은 dump 파일의 계정명은 &quot;MYSTKDB&quot;이고 대상 DB의 &quot;DATA_DIR=/oradata/NOYEORA/&quot;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 참고 화면과 같이 SQL Developer를 통해 DB 서버에 접속합니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;858&quot; data-origin-height=&quot;536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bc8orG/btsB7TcVP6c/SAmSxgjJofWq2PZKIAXgak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bc8orG/btsB7TcVP6c/SAmSxgjJofWq2PZKIAXgak/img.png&quot; data-alt=&quot;SQL Developer 데이터베이스 접속 창&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bc8orG/btsB7TcVP6c/SAmSxgjJofWq2PZKIAXgak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbc8orG%2FbtsB7TcVP6c%2FSAmSxgjJofWq2PZKIAXgak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;607&quot; height=&quot;379&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;858&quot; data-origin-height=&quot;536&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SQL Developer 데이터베이스 접속 창&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;워크시트에서 아래 스크립트를 참고하여 테이블스페이스와 계정을 생성하고 권한을 부여합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702885991316&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 테이블 스페이스 생성
create tablespace ts_mystkdb 
datafile '/oradata/NOYEORA/mystkdb01.dbf' 
size 10G autoextend on next 500M;
# 정상출력: Tablespace TS_MYSTKDB이(가) 생성되었습니다.

# 임시 테이블 스페이스 생성
create temporary tablespace ts_mystkdb_tmp 
tempfile '/oradata/NOYEORA/mystkdb_tmp01.dbf'
size 10G autoextend on next 500M;
# 정상출력: Tablespace TS_MYSTKDB_TMP이(가) 생성되었습니다.

# 계정 생성 및 테이블 스페이스 할당
create user mystkdb identified by &quot;mystkdb&quot;
default tablespace ts_mystkdb
temporary tablespace ts_mystkdb_tmp;
# 정상출력: User MYSTKDB이(가) 생성되었습니다.

# 계정 속성 설정
alter user mystkdb default tablespace ts_mystkdb quota unlimited on ts_mystkdb;
grant create view to mystkdb;
alter user mystkdb account unlock;
grant connect, resource to mystkdb;
# 정상출력
# User MYSTKDB이(가) 변경되었습니다.
# Grant을(를) 성공했습니다.
# User MYSTKDB이(가) 변경되었습니다.
# Grant을(를) 성공했습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQL Developer의 메뉴에서 &quot;보기-DBA-접속-저장 영역-테이블스페이스&quot;를 따라가서 테이블스페이스가 정상적으로 생성되었는지 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;692&quot; data-origin-height=&quot;242&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pLdRd/btsCfyMl3ah/eGy1S8yubFkTLKFW3sJNvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pLdRd/btsCfyMl3ah/eGy1S8yubFkTLKFW3sJNvK/img.png&quot; data-alt=&quot;테이블스페이스 생성 결과 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pLdRd/btsCfyMl3ah/eGy1S8yubFkTLKFW3sJNvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpLdRd%2FbtsCfyMl3ah%2FeGy1S8yubFkTLKFW3sJNvK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;692&quot; height=&quot;242&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;692&quot; data-origin-height=&quot;242&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;테이블스페이스 생성 결과 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. legacy export 파일(.dump)을 datapump export 파일(.dmp)로 바꾸기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제공받은 덤프파일의 캐릭터 셋이 AL32UTF8이고 import 대상 DB의 캐릭터셋이 KO16MSWIN949인데 legacy exp로 생성한 덤프 파일이 때문에 impdp를 사용할 수 없으므로 버추얼박스를 활용하여 AL32UTF8 DB를 빠르게 설치 후 expdp로 덤프파일을 재생성합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;캐릭터 셋 확인하기&lt;/h4&gt;
&lt;pre id=&quot;code_1702964714307&quot; class=&quot;asciidoc&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;# 서버의 캐릭터셋 확인명령
SQL&amp;gt; select * from nls_database_parameters where parameter like '%CHAR%';

# 출력 중 NLS_CHARACTERSET 값 확인 - AL32UTF8
PARAMETER
--------------------------------------------------------------------------------
VALUE
--------------------------------------------------------------------------------
NLS_NCHAR_CONV_EXCP
FALSE
NLS_NUMERIC_CHARACTERS
.,
NLS_NCHAR_CHARACTERSET
AL16UTF16
PARAMETER
--------------------------------------------------------------------------------
VALUE
--------------------------------------------------------------------------------
NLS_CHARACTERSET
AL32UTF8
SQL&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;imp 명령으로 import 하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;imp 명령을 bash 쉘에서 oracle 계정으로 실행합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702964701369&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;imp mystkdb/mystkdb@noyeora file=MYSTKDB_20220628_Oracle.dump

# 위 명령 실행시 아래와 같이
# Import terminated successfully without warnings. 나오면 정상
Import: Release 19.0.0.0.0 - Production on Tue Dec 19 11:15:35 2023
Version 19.3.0.0.0

Copyright (c) 1982, 2019, Oracle and/or its affiliates.  All rights reserved.


Connected to: Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0

Export file created by EXPORT:V19.00.00 via conventional path
import done in US7ASCII character set and AL16UTF16 NCHAR character set
import server uses AL32UTF8 character set (possible charset conversion)
export client uses KO16MSWIN949 character set (possible charset conversion)
. importing MYSTKDB's objects into MYSTKDB
. . importing table                     &quot;BASECODE&quot;          6 rows imported
. . importing table                  &quot;BASECODE_DV&quot;          2 rows imported
. . importing table                    &quot;FINANCE_Y&quot;      34334 rows imported
. . importing table                   &quot;HISTORY_DT&quot;    1644248 rows imported
. . importing table                   &quot;HISTORY_YM&quot;     271003 rows imported
. . importing table                        &quot;STOCK&quot;       2387 rows imported
Import terminated successfully without warnings.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;expdp 명령으로 export 하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;impdp/expdp 명령은 &quot;dba_directories&quot;에서 수행해야 하므로 &quot;dba_directories&quot;를 하나 생성해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bash 쉘에서 &quot;mkdir -p /home/oracle/datapump_dir&quot;를 실행하여 디렉터리 생성 후 SQL 쉘에서 다음 명령을 수행합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702965043372&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# expdp/impdp 용도로 사용할 디렉토리 생성
SQL&amp;gt; create directory datapump_dir as '/home/oracle/datapump_dir';

# 디렉토리 사용할 유저 계정에 읽기/쓰기 권한 부여
SQL&amp;gt; grant read, write on directory datapump_dir to mystkdb;

# 생성된 디렉토리 확인, directory_name은 대문자여야 함
SQL&amp;gt; select directory_name, directory_path from dba_directories 
where directory_name='DATAPUMP_DIR';

SQL&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-2965295859199035&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bash 쉘에서 다음의 expdp 구문을 실행합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702965166577&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 문법 expdp [계정명]/[계정비번] dumpfile=[생성할덤프파일명] directory=[생성한 디렉토리명]
expdp mystkdb/mystkdb dumpfile=mystkdb.dmp directory=datapump_dir&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.webp&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;506&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXjmvi/btsCixT7N01/lsY4JR5qkrtVR4tKsG4bjk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXjmvi/btsCixT7N01/lsY4JR5qkrtVR4tKsG4bjk/img.webp&quot; data-alt=&quot;expdp 실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXjmvi/btsCixT7N01/lsY4JR5qkrtVR4tKsG4bjk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXjmvi%2FbtsCixT7N01%2FlsY4JR5qkrtVR4tKsG4bjk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;938&quot; height=&quot;506&quot; data-filename=&quot;1.webp&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;506&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;expdp 실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. impdp로 대상 DB에 import 하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 단계에서 생성된 .dmp 파일을 대상 DB 서버에 업로드합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 위 단계와 동일하게 &quot;datapump_dir&quot;을 생성해 둡니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 파일질라를 사용하여 SFTP로 업로드하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;178&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ohME1/btsCguKkFNd/6skRyeK5YTZxwJM5TrYob0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ohME1/btsCguKkFNd/6skRyeK5YTZxwJM5TrYob0/img.webp&quot; data-alt=&quot;datapump_dir 경로에 .dmp 파일 업로드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ohME1/btsCguKkFNd/6skRyeK5YTZxwJM5TrYob0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FohME1%2FbtsCguKkFNd%2F6skRyeK5YTZxwJM5TrYob0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;178&quot; data-filename=&quot;2.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;178&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;datapump_dir 경로에 .dmp 파일 업로드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;impdp 명령을 실행합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702965600047&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[oracle@noyeora ~]$ ls /oracle/app/oracle/oradata/datapump
export.log  import.log  MYSTKDB_20220628_Oracle.dump  mystkdb.dmp

# 문법: impdp [계정명]/[계정비번] dumpfile=[덤프파일명] directory=디렉토리명
[oracle@noyeora ~]$ impdp mystkdb/mystkdb dumpfile=mystkdb.dmp directory=datapump_dir

Import: Release 19.0.0.0.0 - Production on Tue Dec 19 13:14:16 2023
Version 19.21.0.0.0

Copyright (c) 1982, 2019, Oracle and/or its affiliates.  All rights reserved.

Connected to: Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Master table &quot;MYSTKDB&quot;.&quot;SYS_IMPORT_FULL_01&quot; successfully loaded/unloaded
import done in KO16MSWIN949 character set and AL16UTF16 NCHAR character set
export done in AL32UTF8 character set and AL16UTF16 NCHAR character set
Warning: possible data loss in character set conversions
Starting &quot;MYSTKDB&quot;.&quot;SYS_IMPORT_FULL_01&quot;:  mystkdb/******** dumpfile=mystkdb.dmp directory=prod_dir
Processing object type SCHEMA_EXPORT/PRE_SCHEMA/PROCACT_SCHEMA
Processing object type SCHEMA_EXPORT/TABLE/TABLE
Processing object type SCHEMA_EXPORT/TABLE/TABLE_DATA
. . imported &quot;MYSTKDB&quot;.&quot;HISTORY_DT&quot;                      164.6 MB 1644248 rows
. . imported &quot;MYSTKDB&quot;.&quot;HISTORY_YM&quot;                      10.38 MB  271003 rows
. . imported &quot;MYSTKDB&quot;.&quot;FINANCE_Y&quot;                       3.026 MB   34334 rows
. . imported &quot;MYSTKDB&quot;.&quot;STOCK&quot;                           111.0 KB    2387 rows
. . imported &quot;MYSTKDB&quot;.&quot;BASECODE&quot;                        6.507 KB       6 rows
. . imported &quot;MYSTKDB&quot;.&quot;BASECODE_DV&quot;                     5.546 KB       2 rows
Processing object type SCHEMA_EXPORT/TABLE/COMMENT
Processing object type SCHEMA_EXPORT/TABLE/INDEX/INDEX
Processing object type SCHEMA_EXPORT/TABLE/CONSTRAINT/CONSTRAINT
Processing object type SCHEMA_EXPORT/TABLE/INDEX/STATISTICS/INDEX_STATISTICS
Processing object type SCHEMA_EXPORT/TABLE/STATISTICS/TABLE_STATISTICS
Processing object type SCHEMA_EXPORT/STATISTICS/MARKER
Job &quot;MYSTKDB&quot;.&quot;SYS_IMPORT_FULL_01&quot; successfully completed at Tue Dec 19 13:14:41 2023 elapsed 0 00:00:24

[oracle@noyeora ~]$&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;519&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZLIBO/btsCbe9pkrn/fyDvBXN0jPjMtLIkbvChKK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZLIBO/btsCbe9pkrn/fyDvBXN0jPjMtLIkbvChKK/img.webp&quot; data-alt=&quot;impdp 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZLIBO/btsCbe9pkrn/fyDvBXN0jPjMtLIkbvChKK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZLIBO%2FbtsCbe9pkrn%2FfyDvBXN0jPjMtLIkbvChKK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;519&quot; data-filename=&quot;3.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;519&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;impdp 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;impdp 로그를 보면 export 캐릭터 셋이 AL32UTF8이고 import 캐릭터셋이 KO16MSWIN949임을 확인할 수 있으며 캐릭터셋 변환으로 인해 데이터 손실이 발생할 수 있다는 경고문이 있습니다. 어쨌든 성공했으니 실제 데이터를 확인해 보면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.webp&quot; data-origin-width=&quot;656&quot; data-origin-height=&quot;481&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3h0f4/btsCext3kra/Kp6WX8rkA9rP86Li4R74S1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3h0f4/btsCext3kra/Kp6WX8rkA9rP86Li4R74S1/img.webp&quot; data-alt=&quot;테이블 조회 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3h0f4/btsCext3kra/Kp6WX8rkA9rP86Li4R74S1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3h0f4%2FbtsCext3kra%2FKp6WX8rkA9rP86Li4R74S1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;656&quot; height=&quot;481&quot; data-filename=&quot;4.webp&quot; data-origin-width=&quot;656&quot; data-origin-height=&quot;481&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;테이블 조회 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블 조회 결과 한글이 깨지지 않고 정상적으로 표기되고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상으로 AL32UTF8 캐릭터셋에서 legacy export 덤프파일을 버추얼박스로 짧은 시간에 구축한 AL32UTF8 DB에 import 후 다시 expdp로 data pump 수행 후 KO16WIN949 환경에 impdp를 수행해 보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련 포스팅 링크를 첨부합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버추얼박스에 Oracle 19c 설치하기:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blog.noyecube.com/entry/linux%EC%97%90-oracle-19c-%EC%84%A4%EC%B9%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2023.12.06 - [Database/Oracle] - linux에 oracle 19c 설치&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Database/Oracle</category>
      <category>AL32UTF8</category>
      <category>datapump</category>
      <category>expdp</category>
      <category>impdp</category>
      <category>KO16MSWIN949</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/57</guid>
      <comments>https://noyecube.tistory.com/entry/Oracle-dump%ED%8C%8C%EC%9D%BC-import-%EB%B0%A9%EB%B2%95#entry57comment</comments>
      <pubDate>Mon, 18 Dec 2023 18:33:09 +0900</pubDate>
    </item>
    <item>
      <title>[Oracle] 계정 관리 테이블 스페이스 관리</title>
      <link>https://noyecube.tistory.com/entry/Oracle-%EA%B3%84%EC%A0%95-%EA%B4%80%EB%A6%AC-%ED%85%8C%EC%9D%B4%EB%B8%94-%EC%8A%A4%ED%8E%98%EC%9D%B4%EC%8A%A4-%EA%B4%80%EB%A6%AC</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅 주제는 &quot; Simple Techniques for Oracle Database User and Tablespace Management&quot;입니다. 유저 계정과 테이블스페이스 관리 명령어를 알아봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포스팅에서 Oracle Database 19c 설치를 다뤘는데 Database를 사용하기 위해서는 User와 Tablespace 생성이 필요합니다. Oracle Database 설치 방법에 대한 포스팅은 맨 아래에 링크가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. DBA 계정으로 Oracle 접속하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계정과 테이블스페이스 관리를 위해서는 sysdba로 접속이 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 명령어로 접속합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702608396413&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sqlplus / as sysdba&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Oracle 계정 및 테이블스페이스 관리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Oracle Database를 설치하면 사용자용 계정과 테이블 스페이스가 없는 상태입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 tablespace를 먼저 생성하고 그다음 user를 생성합니다. user를 생성할 때 파라미터로 생성해 둔 tablespace를 user와 연결해 주고 tablespace 사용에 필요한 권한을 부여하는 방법을 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제 명령어들은 데이터 파일이 생성되는 위치를 &quot;/oradata&quot;, SID는 &quot;noyeora&quot;로 가정하고 작성되었습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;테이블스페이스 생성 하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tablespace 생성 문법은 아래와 같습니다. 저는 권장방법과 같이 데이터파일의 위치를 정확하게 지정 후 임시 테이블스페이스도 세트로 생성하는 것을 추천합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1702618675205&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 문법
create tablespace [테이블스페이스명] datafile [데이터파일명] size [사이즈];

# 예시. Oracle이 기본으로 제공하는 데이터 파일 그룹 사용
# 테이블스페이스명: TB_SAMPLE
# 데이터파일 사이즈: 1G
create tablespace tb_sample datafile '+DATA' size 1G;

# 예시. 데이터 파일 용량을 자동으로 확장되게 설정
create tablespace tb_sample datafile '+DATA' size 1G autoextend on;

# 권장방법. 데이터 파일 위치 지정하여 생성
# 임시 테이블스페이스도 생성하여 시스템 테이블스페이스 단편화 예방
create tablespace tb_sample datafile '/oradata/noyeora/tb_sample01.dbf' autoextend on;
create temporary tablespace tb_sample_tmp tempfile '/oradata/noyeora/tb_sample_tmp01.dbf' autoextend on;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터파일의 위치를 지정하면 관리 및 유지보수에 더 유리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임시 테이블스페이스는 데이터베이스에서 임시로 데이터를 저장하는 공간으로 정렬, 집계, 조인, 인덱스 생성 작업에 사용됩니다. 임시 테이블스페이스가 없으면 이러한 작업에 시스템 테이블스페이스를 사용하게 되는데 시스템 테이블스페이스는 데이터베이스의 중요한 정보가 저장되는 공간이기 때문에&amp;nbsp; 이러한 공간을 임시 데이터 저장에 사용하면 단편화 발생 및 성능저하의 단점이 있습니다. 그러므로 운영환경이라면 임시 테이블스페이스도 같이 생성하여 사용하길 권장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 몸담고 있는 조직에서는 autoextend 옵션도 사용하는데 이 옵션도 권장합니다. 용량 관리는 어드민이 모니터링을 하거나 인프라 관리자가 스토리지 모니터링을 통해서 하면 되는 것이지 데이터파일에 용량을 제한 걸어 놓고 테이블스페이스 하나하나 신경 쓸 필요는 없는 것 같네요.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;유저 계정 생성 하기&lt;/h4&gt;
&lt;pre id=&quot;code_1702621907323&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 문법
create user [사용자계정명] identified by [사용자계정패스워드]
default tablespace [테이블스페이스명]
temporary tablespace [임시테이블스페이스명];

# 예시
create user noye identified by 123
default tablespace tb_sample
temporary tablespace tb_sample_tmp;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;유저 계정 권한 속성 수정 하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블스페이스를 생성하고 사용자 계정에 테이블스페이스를 지정했습니다. 이제 사용 가능할까요? 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무런 권한이 없기 때문에 사용에 필요한 적절한 권한을 부여해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Oracle Database에서 권한은 크게 시스템 권한과 객체 권한이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템 권한은 데이터베이스 시스템을 관리하고 조작하는 데 필요한 권한으로 다음과 같이 구분됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DBA 권한: 데이터베이스 시스템을 완전히 관리할 수 있는 권한입니다. 우리가 데이터베이스 관리자로 접속할 때 역할(ROLE) 선택을 &quot;sysdba&quot;로 선택 후 접속하는데 &quot;sysdba&quot;가 DBA 권한을 갖고 있습니다.&lt;/li&gt;
&lt;li&gt;System 권한: 데이터베이스 시스템을 부분적으로 관리할 수 있는 권한입니다.&lt;/li&gt;
&lt;li&gt;User 권한: 데이터베이스 사용자에게 부여되는 권한입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 권한은 데이터베이스 객체에 대한 작업을 수행할 수 있는 권한으로 다음과 같이 구분됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Data Manipulation Language(DML) 권한: 테이블, 인덱스, 시퀀스 등의 데이터 객체에 대한 데이터 조작을 수행할 수 있는 권한입니다.&lt;/li&gt;
&lt;li&gt;Data Definition Language(DDL) 권한: 테이블, 인덱스, 시퀀스 등의 데이터 객체를 생성, 수성, 삭제할 수 있는 권한입니다.&lt;/li&gt;
&lt;li&gt;Data Control Language(DCL) 권한: 사용자, 역할, 권한 등을 관리할 수 있는 권한입니다.&lt;/li&gt;
&lt;li&gt;System Privilege: 테이블, 인덱스, 시퀀스 등의 데이터 객체에 대한 데이터 조작을 수행할 수 있는 권한입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;User 계정에 부여할 수 있는 권한은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;connect: 데이터베이스에 연결할 수 있는 권한으로 이 권한이 없으면 데이터베이스에 연결할 수 없습니다.&lt;/li&gt;
&lt;li&gt;resource: 테이블, 인덱스, 시퀀스 등의 데이터 객체를 생성할 수 있느 권한입니다.&lt;/li&gt;
&lt;li&gt;unlimited tablespace: 모든 테이블스페이스에 데이터를 저장할 수 있는 권한입니다. 이 권한이 없으면 지정된 테이블스페이스에만 저장할 수 있습니다.&lt;/li&gt;
&lt;li&gt;create view: 뷰를 생성할 수 있는 권한입니다.&lt;/li&gt;
&lt;li&gt;create sequence: 시퀀스는 자동으로 증가하는 숫자를 생성하는 객체로서 시퀀스를 생성할 수 있는 권한입니다.&lt;/li&gt;
&lt;li&gt;create synonym: 다른 사용자의 객체에 대한 별칭을 생성할 수 있는 권한입니다.&lt;/li&gt;
&lt;li&gt;procedure: 프로시저는 데이터베이스 작업을 자동화하는 객체로서 프로시저를 생성할 수 있는 권한입니다.&lt;/li&gt;
&lt;li&gt;exp_full: exp 명령을 사용하여 데이터를 백업할 수 있는 권한입니다.&lt;/li&gt;
&lt;li&gt;imp_full: imp 명령을 사용하여 데이터를 복원할 수 있는 권한입니다.&lt;/li&gt;
&lt;li&gt;grant any: 다른 사용자에게 권한을 부여할 수 있는 권한입니다.&lt;/li&gt;
&lt;li&gt;revoke: 다른 사용자에게 부여된 권한을 취소할 수 있는 권한입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부여할 수 있는 권한은 이외에도 더 있지만 권한을 부여할 때는 필요한 만큼만 부여해야 보안 위험을 줄일 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;권한을 부여하는 명령어 문법은 다음과 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702624265056&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 문법
grant 권한 [, 권한] ... to 사용자;

# 예시. 사용자 noye에 connect, resource 권한 부여
grant connect, resource to noye;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;권한을 회수하는 명령어 문법은 다음과 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702624342878&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 문법
revoke 권한 [, 권한] ... from 사용자;

# 예시. 사용자 noye에 connect, resource 권한 회수
revoke connect, resource from noye;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시를 모두 모아 테이블스페이스 생성, 사용자 계정생성, 계정 권한부여까지 한 번에 진행하는 예제는 아래와 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702624489180&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 테이블스페이스 생성
create tablespace tb_sample datafile '/oradata/noyeora/tb_sample01.dbf' autoextend on;
create temporary tablespace tb_sample_tmp tempfile '/oradata/noyeora/tb_sample_tmp01.dbf' autoextend on;

# 사용자 계정 생성
create user noye identified by 123
default tablespace tb_sample
temporary tablespace tb_sample_tmp;

# 사용자 계정 권한 부여
grant connect, resource to noye;
grant create database link to noye;
grant create view to noye;
grant create procedure to noye;
grant create sequence to noye;
grant create synonym to noye;
grant unlimited tablespace to noye;
grant exp_full_database to noye;
grant imp_full_database to noye;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;계정/테이블스페이스 조회 방법&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계정을 제대로 사용할 수 있는지, 어떤 권한을 가지고 있는지, 테이블 스페이스는 어떤 것들이 있는지 등을 알아보는 방법입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702624826574&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 사용자 계정 목록 조회
show user&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1702625039570&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 계정-기본 테이블스페이스 조회

col username format a30
select username, default_tablespace from dba_users&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1702625139004&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 계정 권한 확인하기
select grantee, privilege from dba_sys_privs where grantee='[계정명]';&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1702624911261&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 로컬에서 계정 접속 확인하기
conn [사용자계정명]/[계정패스워드]

# 원격에서 계정 접속 확인하기
conn [사용자계정명]/[계정패스워드]@[SID명]&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1702624977356&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 테이블스페이스와 데이터파일 조회

set line 120
col tablespace_name format a20
col file_name format a40
select tablespace_name, file_name, bytes/1024/1024, autoextensible from dba_data_files order by 1,2;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1702625091736&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 전반적인 계정 상태 체크

col username for a20
col machine for a30
col program for a30
set line 200
set pagesize 200
select inst_id,username,machine,program,count(*) from gv$session where username not in ('SYS', 'SYSTEM')
group by inst_id,username,machine,program
order by 1,2,3;

# 파라미터 링크 체크
SQL&amp;gt; show parameter link;

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
allow_global_dblinks                 boolean     FALSE
open_links                           integer     4
open_links_per_instance              integer     4
outbound_dblink_protocols            string      ALL
standby_pdb_source_file_dblink       string

# 파라미터 오픈 체크
SQL&amp;gt; show parameter open;

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
open_cursors                         integer     300
open_links                           integer     4
open_links_per_instance              integer     4
read_only_open_delayed               boolean     FALSE
session_max_open_files               integer     10
SQL&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1702625368940&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# database session 확인하기

col username for a20
col machine for a30
col program for a30
set line 200
set pagesize 200
select inst_id,username,machine,program,count(*) from gv$session
where username not in ('SYS', 'SYSTEM')
group by inst_id,username,machine,program
order by 1,2,3;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1702625308644&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# dblink 제한 걸기
alter system set open_links=10 scope=spfile;
alter system set open_links_per_instance=10 scope=spfile;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-2965295859199035&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;계정/테이블스페이스 삭제 방법&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 계정 삭제는 다음 명령으로 가능합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702625660310&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;drop user [사용자계정명] [옵션];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블 삭제는 다음 명령으로 가능합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702625716164&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;drop tablespace [테이블스페이스명] including contents and datafiles;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 계정을 삭제하면서 동시에 계정에 연결된 모든 테이블, 인덱스, 시퀀스 등의 데이터 객체를 삭제하는 방법은 &quot;cascade&quot; 옵션으로 가능합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702625764450&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;drop user [사용자계정명] cascade;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령을 실행하면 다음과 같은 작업이 수행됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자 계정 및 사용자 암호 삭제&lt;/li&gt;
&lt;li&gt;사용자 계정에 부여된 권한 취소&lt;/li&gt;
&lt;li&gt;사용자 계정에 생성된 데이터 객체 삭제&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블스페이스를 삭제하지 않으면서 &quot;cascade&quot; 옵션을 사용하지 않고 사용자 계정만 삭제하면 데이터 객체는 고아(orphaned) 상태로 남습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 Oracle 19c 설치 방법은 아래 링크의 포스팅을 참고해 주세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://noyecube.com/entry/linux%EC%97%90-oracle-19c-%EC%84%A4%EC%B9%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2023.12.06 - [Database/Oracle] - linux에 oracle 19c 설치&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 포스팅의 내용 정도만 숙지하시면 일반적인 범위 내에서 DB를 활용한 데이터 저장은 충분히 가능합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상으로 Oracle Database에서 테이블스페이스와 사용자 계정을 관리하는 방법을 알아보았습니다.&lt;/p&gt;</description>
      <category>Database/Oracle</category>
      <category>autoextend</category>
      <category>datafile</category>
      <category>tablespace</category>
      <category>테이블스페이스</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/56</guid>
      <comments>https://noyecube.tistory.com/entry/Oracle-%EA%B3%84%EC%A0%95-%EA%B4%80%EB%A6%AC-%ED%85%8C%EC%9D%B4%EB%B8%94-%EC%8A%A4%ED%8E%98%EC%9D%B4%EC%8A%A4-%EA%B4%80%EB%A6%AC#entry56comment</comments>
      <pubDate>Sat, 16 Dec 2023 00:10:23 +0900</pubDate>
    </item>
    <item>
      <title>오라클 데이터베이스 시작 및 종료 방법</title>
      <link>https://noyecube.tistory.com/entry/%EC%98%A4%EB%9D%BC%ED%81%B4-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%8B%9C%EC%9E%91-%EB%B0%8F-%EC%A2%85%EB%A3%8C-%EB%B0%A9%EB%B2%95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Starting and Stopping Oracle Database: A Step-by-Step Guide.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Oracle Database의 인스턴스와 리스너 시작/종료 방법을 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 DB 인스턴스와 리스너의 시작/종료 순서는 상관없다는 점을 알아두세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Oracle Database의 State 정의&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Oracle Database는 그 동작 상태를 네 가지 상태로 구분할 수 있습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Shutdown: Database가 완전히 종료된 상태로 DB파일이 메모리에서 해제되고 DB 프로세스는 종료됨.&lt;/li&gt;
&lt;li&gt;Nomount: DB파일이 메모리에 로드는 됐지만 마운트는 안된 상태. DB 프로세스 종료 상태.&lt;/li&gt;
&lt;li&gt;Mount: DB파일이 마운트된 상태이지만 아직 열리지 않은 상태.&lt;/li&gt;
&lt;li&gt;Open: Database 프로세스가 시작되고 사용자가 접속하여 데이터를 조회하거나 수정할 수 있는 상태.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 로컬에서의 DB 접속 방법 및 팁&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 가장 많이 사용하는 명령어 입니다. oracle 계정에서 이 명령을 입력하면 DB에 접속할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702528267230&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sqlplus / as sysdba&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 명령어를 oracle 계정의 환경변수 파일 &quot;~/.bash_profile&quot;에 alias 등록을 해두면 편리합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702528992664&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 두 글자 &quot;ss&quot;만 입력하여 DB 접속하기
alias ss='sqlplus / as sysdba'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1702532111929&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;alias ss='sqlplus / as sysdba'
[oracle@noyeora ~]$ ss
SQL*Plus: Release 19.0.0.0.0 - Production on Thu Dec 14 14:34:26 2023
Version 19.21.0.0.0
Copyright (c) 1982, 2022, Oracle.  All rights reserved.
Connected to an idle instance.
SQL&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접속에 성공했는데 &quot;Connected to an idle instance&quot; 메시지를 확인할 수 있습니다. 앞서 말씀드린 동작 상태 중 Shutdown 상태에 해당합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Oracle Database 시작 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 명령어와 옵션은 아래와 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702532440851&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;startup [nomount | mount | open]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단계별로 시작해 보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702532370442&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SQL&amp;gt; startup nomount
ORACLE instance started.

Total System Global Area 3556767720 bytes
Fixed Size                  8931304 bytes
Variable Size             754974720 bytes
Database Buffers         2785017856 bytes
Redo Buffers                7843840 bytes

SQL&amp;gt; select status from v$instance;

STATUS
------------------------
STARTED

SQL&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&quot;nomount&quot; 옵션으로 시작 후 &quot;status&quot;를 확인하면 STARTED 상태가 확인됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1702532647890&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SQL&amp;gt; alter database mount;
Database altered.
SQL&amp;gt;  select status from v$instance;
STATUS
------------
MOUNTED
SQL&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;startup 상태이므로 다음 명령어는 alter를 사용하여 mount 단계로 바꿔봅니다. 관리자가 백업이나 기타 이유로 DB파일을 컨트롤해야 하는데 서비스는 하면 안 될 때 mount까지만 기동 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1702532784867&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SQL&amp;gt; alter database open;
Database altered.
SQL&amp;gt; select status from v$instance;
STATUS
------------
OPEN
SQL&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Database 기동의 최종 단계인 OPEN까지 완료된 상태입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기동의 모든 단계를 한번에 하려면 &quot;startup&quot;만 입력하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702533963556&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;startup force&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;번외로 &quot;force&quot; 옵션을 사용하면 재시작됩니다. &quot;shutdown abort&quot; 후 &quot;startup&quot;이 연속적으로 이루어집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU 사용률이 100%이거나 무응답일 때 사용합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Oracle Database 종료 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 명령어와 옵션은 아래와 같습니다. 옵션을 명시하지 않을 때 기본 적용되는 옵션은 &quot;normal&quot;입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702533076186&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# option : immediate, abort, normal, transactional
# shutdown 축약어로 shu 도 가능합니다.
shutdown [option]&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;immediate: 접속중인 세션과 트랜잭션을 모두 강제 종료 합니다.&lt;/li&gt;
&lt;li&gt;abort: DB자체를 강제 종료합니다. DB가 먹통일 때 주로 사용합니다.&lt;/li&gt;
&lt;li&gt;normal: 접속중인 세션과 요청받은 트랜잭션이 모두 종료될 때까지 기다립니다.&lt;/li&gt;
&lt;li&gt;transactional: 접속 중인 세션은 강제로 종료하고 트랜잭션은 모두 종료될 때까지 기다립니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 리스너 시작과 종료 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Oracle Database 리스너의 기본 서비스 포트는 1521입니다. 외부 인터넷 환경에서 운영시 기본 포트를 다른 포트로 변경 후 사용하는 것을 추천합니다. 리스너가 기동 되지 않으면 DB 인스턴스가 기동 중이어도 외부에서 접속이 불가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스너 실행파일명은 &quot;lsnrctl&quot; 입니다. &quot;help&quot;로 사용 가능한 명령어 옵션을 확인할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702535353098&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[oracle@noyeora dbs]$ lsnrctl help

LSNRCTL for Linux: Version 19.0.0.0.0 - Production on 14-DEC-2023 15:28:58

Copyright (c) 1991, 2023, Oracle.  All rights reserved.

The following operations are available
An asterisk (*) denotes a modifier or extended command:

start           stop            status          services
servacls        version         reload          save_config
trace           spawn           quit            exit
set*            show*

[oracle@noyeora dbs]$&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1702535639141&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[oracle@noyeora admin]$ lsnrctl
LSNRCTL for Linux: Version 19.0.0.0.0 - Production on 14-DEC-2023 15:33:36
Copyright (c) 1991, 2023, Oracle.  All rights reserved.
Welcome to LSNRCTL, type &quot;help&quot; for information.
LSNRCTL&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵션 파라미터 없이 &quot;lsnrctl&quot;만 실행하면 리스너의 프롬프트로 진입하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 사용하는 명령은 &quot;start&quot;, &quot;stop&quot;, &quot;status&quot;입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702538816909&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[oracle@noyeora admin]$ lsnrctl status

LSNRCTL for Linux: Version 19.0.0.0.0 - Production on 14-DEC-2023 16:26:24

Copyright (c) 1991, 2023, Oracle.  All rights reserved.

Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=noyeora)(PORT=1521)))
STATUS of the LISTENER
------------------------
Alias                     LISTENER
Version                   TNSLSNR for Linux: Version 19.0.0.0.0 - Production
Start Date                14-DEC-2023 16:18:11
Uptime                    0 days 0 hr. 8 min. 13 sec
Trace Level               off
Security                  ON: Local OS Authentication
SNMP                      OFF
Listener Parameter File   /oracle/app/oracle/product/19c/network/admin/listener.ora
Listener Log File         /oracle/app/oracle/diag/tnslsnr/noyeora/listener/alert/log.xml
Listening Endpoints Summary...
  (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=noyeora)(PORT=1521)))
  (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1521)))
Services Summary...
Service &quot;noyeora&quot; has 1 instance(s).
  Instance &quot;noyeora&quot;, status READY, has 1 handler(s) for this service...
Service &quot;noyeoraXDB&quot; has 1 instance(s).
  Instance &quot;noyeora&quot;, status READY, has 1 handler(s) for this service...
The command completed successfully
[oracle@noyeora admin]$&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Listening Endpoints Summary에 HOST와 PORT값이 정상인지 확인합니다. HOST명 대신 IP를 적어도 되고 HOST명을 적으면 /etc/hosts 파일에 정확한 IP가 적혀있어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 하셨으면 아래와 같이 &quot;SYSDBA&quot; 롤 선택 후 정보 넣으시고 &quot;테스트&quot; 버튼 눌러서 &quot;상태 : 성공&quot; 확인되면 정상입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;758&quot; data-origin-height=&quot;476&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dpGn1T/btsBXOXeb6g/4KqaYhouxS02WMzkdXxkO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dpGn1T/btsBXOXeb6g/4KqaYhouxS02WMzkdXxkO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dpGn1T/btsBXOXeb6g/4KqaYhouxS02WMzkdXxkO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdpGn1T%2FbtsBXOXeb6g%2F4KqaYhouxS02WMzkdXxkO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;758&quot; height=&quot;476&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;758&quot; data-origin-height=&quot;476&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Database/Oracle</category>
      <category>lsnrctl</category>
      <category>Oracle Database</category>
      <category>SID</category>
      <category>sqlplus</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/55</guid>
      <comments>https://noyecube.tistory.com/entry/%EC%98%A4%EB%9D%BC%ED%81%B4-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%8B%9C%EC%9E%91-%EB%B0%8F-%EC%A2%85%EB%A3%8C-%EB%B0%A9%EB%B2%95#entry55comment</comments>
      <pubDate>Fri, 15 Dec 2023 08:01:13 +0900</pubDate>
    </item>
    <item>
      <title>IOCP 기반 다중 접속 및 데이터 처리-3</title>
      <link>https://noyecube.tistory.com/entry/IOCP-%EA%B8%B0%EB%B0%98-%EB%8B%A4%EC%A4%91-%EC%A0%91%EC%86%8D-%EB%B0%8F-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B2%98%EB%A6%AC-3</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafbfd; color: #333333; text-align: left;&quot;&gt;이번 포스팅에서는 IOCP 서버에서 소켓 풀을 관리하는 방법에 대해 알아보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafbfd;&quot;&gt;이전 포스팅에서 IOCP 뼈대 코드를 작성하고 초기화하는 과정을 알아보았습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blog.noyecube.com/entry/aa&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2023.12.13 - [프로그래밍/C | C++] - IOCP 기반 다중 접속 및 데이터 처리-1&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1702473052171&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;IOCP 기반 다중 접속 및 데이터 처리-1&quot; data-og-description=&quot;윈도 환경에서 다중 클라이언트의 접속을 처리하는 네트워크 서버를 구현하기 위해서는 IOCP 사용이 필수라고 생각합니다. 에코 서버를 IOCP 기반으로 구현한 예제는 많지만 실제 현업에서 다중 &quot; data-og-host=&quot;blog.noyecube.com&quot; data-og-source-url=&quot;https://blog.noyecube.com/entry/aa&quot; data-og-url=&quot;https://blog.noyecube.com/entry/aa&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cImh9j/hyULRNpQ9D/LZeIrc8h5Fyj3FV87tl9v1/img.png?width=663&amp;amp;height=338&amp;amp;face=0_0_663_338,https://scrap.kakaocdn.net/dn/sFoQu/hyULTYLpLS/3RsWbMyT449xGzm66mCKiK/img.png?width=663&amp;amp;height=338&amp;amp;face=0_0_663_338,https://scrap.kakaocdn.net/dn/bclLmQ/hyUL3UDhQD/AFIPkIP3wcb7d3VMFCFfZ1/img.png?width=681&amp;amp;height=814&amp;amp;face=0_0_681_814&quot;&gt;&lt;a href=&quot;https://blog.noyecube.com/entry/aa&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://blog.noyecube.com/entry/aa&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cImh9j/hyULRNpQ9D/LZeIrc8h5Fyj3FV87tl9v1/img.png?width=663&amp;amp;height=338&amp;amp;face=0_0_663_338,https://scrap.kakaocdn.net/dn/sFoQu/hyULTYLpLS/3RsWbMyT449xGzm66mCKiK/img.png?width=663&amp;amp;height=338&amp;amp;face=0_0_663_338,https://scrap.kakaocdn.net/dn/bclLmQ/hyUL3UDhQD/AFIPkIP3wcb7d3VMFCFfZ1/img.png?width=681&amp;amp;height=814&amp;amp;face=0_0_681_814');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;IOCP 기반 다중 접속 및 데이터 처리-1&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;윈도 환경에서 다중 클라이언트의 접속을 처리하는 네트워크 서버를 구현하기 위해서는 IOCP 사용이 필수라고 생각합니다. 에코 서버를 IOCP 기반으로 구현한 예제는 많지만 실제 현업에서 다중&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;blog.noyecube.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blog.noyecube.com/entry/IOCP-%EA%B8%B0%EB%B0%98-%EB%8B%A4%EC%A4%91-%EC%A0%91%EC%86%8D-%EB%B0%8F-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B2%98%EB%A6%AC-2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2023.12.14 - [프로그래밍/C | C++] - IOCP 기반 다중 접속 및 데이터 처리-2&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #fafbfd; color: #333333; text-align: left;&quot;&gt;1. 리슨소켓 Accept 이벤트 감지&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;793&quot; data-origin-height=&quot;345&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brL5Ej/btsBR8O0Dlt/vg4zJ46YsT24HjukUSgqH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brL5Ej/btsBR8O0Dlt/vg4zJ46YsT24HjukUSgqH0/img.png&quot; data-alt=&quot;Accept 이벤트 감지 처리&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brL5Ej/btsBR8O0Dlt/vg4zJ46YsT24HjukUSgqH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrL5Ej%2FbtsBR8O0Dlt%2Fvg4zJ46YsT24HjukUSgqH0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;793&quot; height=&quot;345&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;793&quot; data-origin-height=&quot;345&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Accept 이벤트 감지 처리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;WSAEVENT hWSAEvent = WSACreateEvent() &lt;br /&gt;상태 변경에 대한 통지를 받을 이벤트 커널 객체 생성(초기: 넌시그널) 코드입니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;WSAEventSelect(hsoListen, hWSAEvent, FD_ACCEPT)&lt;br /&gt;리슨소켓에 ACCEPT 이벤트가 발생하면 hWSAEvent가 Set 상태가 되도록 설정합니다.&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt; WSAEnumNetworkEvents(hsoListen, hWSAEvent, &amp;amp;ne)&lt;br /&gt;소켓 변경사항 체크 및 네트워크 이벤트 레코드 클리어, 이벤트 커널 객체 리셋을 수행합니다.&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 소켓 풀 생성/재사용 및 비동기 접속 처리&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;885&quot; data-origin-height=&quot;874&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7iZl3/btsBUbSBiqa/X9MpcqXLyqXEXIAa87prYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7iZl3/btsBUbSBiqa/X9MpcqXLyqXEXIAa87prYk/img.png&quot; data-alt=&quot;IncreaseAcceptSockets&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7iZl3/btsBUbSBiqa/X9MpcqXLyqXEXIAa87prYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7iZl3%2FbtsBUbSBiqa%2FX9MpcqXLyqXEXIAa87prYk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;885&quot; height=&quot;874&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;885&quot; data-origin-height=&quot;874&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;IncreaseAcceptSockets&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리슨소켓(hsoListen)으로부터 들어오는 접속을 수용할 신규 소켓(sock)을 미리 생성하고 OVERLAPPED 구조체 포인터 변수와 함께 AcceptEx()를 호출하여 비동기 접속을 수용합니다. 한 번에 nIncCnt 값만큼 소켓을 미리 생성해 둡니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AcceptEx 함수 호출 시 OVERLAPPED 구조체를 이용하면 접속이 들어왔을 때 리슨 소켓의 핸들 자체가 시그널 상태가 됩니다. 이때&amp;nbsp;미리 생성해 둔 소켓이 없으면 WSAEventSelect 함수로 전달된 이벤트가 시그널 상태가 되어 미리 생성해둔 소켓이 모두 소진되었음을 알립니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;1129&quot; data-origin-height=&quot;203&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/smfJN/btsBY4EpJ43/GE4ylbOAk5KJ9Ia0g3vonk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/smfJN/btsBY4EpJ43/GE4ylbOAk5KJ9Ia0g3vonk/img.png&quot; data-alt=&quot;GetListenSocket&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/smfJN/btsBY4EpJ43/GE4ylbOAk5KJ9Ia0g3vonk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsmfJN%2FbtsBY4EpJ43%2FGE4ylbOAk5KJ9Ia0g3vonk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1129&quot; height=&quot;203&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;1129&quot; data-origin-height=&quot;203&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;GetListenSocket&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 리슨 소켓을 생성 후 리슨 소켓 핸들로 IOCP 핸들을 생성합니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1053&quot; data-origin-height=&quot;568&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOzEDI/btsBVBCXnOD/7X9t96iloKlw6TpY5CR4K1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOzEDI/btsBVBCXnOD/7X9t96iloKlw6TpY5CR4K1/img.png&quot; data-alt=&quot;GetQueuedCompletionStatus&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOzEDI/btsBVBCXnOD/7X9t96iloKlw6TpY5CR4K1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOzEDI%2FbtsBVBCXnOD%2F7X9t96iloKlw6TpY5CR4K1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1053&quot; height=&quot;568&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1053&quot; data-origin-height=&quot;568&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;GetQueuedCompletionStatus&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hsoListen이 IOKEY_LISTION 키값으로 IOCP 등록되어 있으므로 GetQueuedCompletionStatus에서 신규 접속 클라이언트 이벤트를 감지할 수 있습니다. 신규 접속이 감지되면 CreateIoCompletionPort 호출로 접속한 클라이언트의 소켓에 대해 데이터 수신 이벤트도 감지 등록합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;1092&quot; data-origin-height=&quot;929&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s3Qto/btsBUcqp0ae/8gM44UeHX5dCuZojDjoC0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s3Qto/btsBUcqp0ae/8gM44UeHX5dCuZojDjoC0k/img.png&quot; data-alt=&quot;소켓 재사용 코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s3Qto/btsBUcqp0ae/8gM44UeHX5dCuZojDjoC0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs3Qto%2FbtsBUcqp0ae%2F8gM44UeHX5dCuZojDjoC0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1092&quot; height=&quot;929&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;1092&quot; data-origin-height=&quot;929&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;소켓 재사용 코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트가 접속을 종료하면 Socket Close를 호출하는 대신 DisconnectEx를 호출합니다. 그럼 소켓이 버려지는 것이 아니고 연결만 닫히게 됩니다. 이를 통해 소켓을 생성/소멸하는데 필요한 오버헤드를 줄일 수 있습니다. Disconnect가 완료되면 다시 AcceptEx를 호출하여 소켓을 재사용합니다. 유의 사항으로는 소켓이 재사용되므로 상위 레벨에서 클라이언트의 변경을 알아차릴 수 있는 자료구조를 제공해야 합니다.&amp;nbsp;&lt;/p&gt;</description>
      <category>프로그래밍/C | C++</category>
      <category>AcceptEx</category>
      <category>disconnectex</category>
      <category>iocp</category>
      <category>overlapped</category>
      <category>wsaeventselect</category>
      <category>소켓풀</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/41</guid>
      <comments>https://noyecube.tistory.com/entry/IOCP-%EA%B8%B0%EB%B0%98-%EB%8B%A4%EC%A4%91-%EC%A0%91%EC%86%8D-%EB%B0%8F-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B2%98%EB%A6%AC-3#entry41comment</comments>
      <pubDate>Fri, 15 Dec 2023 00:11:19 +0900</pubDate>
    </item>
    <item>
      <title>[Linux] 리눅스 절전 모드 방지 설정</title>
      <link>https://noyecube.tistory.com/entry/Linux-%EB%A6%AC%EB%88%85%EC%8A%A4-%EC%A0%88%EC%A0%84-%EB%AA%A8%EB%93%9C-%EB%B0%A9%EC%A7%80-%EC%84%A4%EC%A0%95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;노트북에 리눅스를 설치하고 oracle database를 설치하고 전원을 켜뒀는데 다음날 확인하니 절전모드에 빠진 상태로 원격 접속이 안됩니다. 이번 포스팅에서는 리눅스 절전 모드 방지 설정을 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 노트북 덮개 동작 무시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노트북을 서버로 사용하기 위해서는 24시간 전원을 켜두어야 하는데 설치 후 기본 상태로 두거나 덮개를 만지면 절전 모드로 진입하게 됩니다. 아래와 같이 설정 파일을 수정 후 데몬을 재기동합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702521155235&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# logind.conf 파일을 오픈
vi /etc/systemd/logind.conf

# 아래 두 옵션의 주석을 해제하고 아래와 같이 ignore 설정
HandleLidSwitch=ignore
HandleLidSwitchDocked=ignore

#파일 저장 후 데몬 재시작
systemctl restart systemd-logind&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/etc/systemd/logind.conf 파일은 systemd의 로그인 데몬인 systemd-logind의 설정을 관리하는 파일입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일에는 사용자 로그인, 세션 관리, 전원 관리와 관련된 다양한 옵션이 포함되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HandleLidSwitch 옵션은 덮개를 닫을 때 시스템의 동작을 설정하며 다음과 같은 값을 사용할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;suspend: 시스템 일시 정지&lt;/li&gt;
&lt;li&gt;hybrid-sleep: 절전모드로 전환&lt;/li&gt;
&lt;li&gt;poweroff: 시스템 종료&lt;/li&gt;
&lt;li&gt;ignore: 무시&lt;/li&gt;
&lt;li&gt;default: 시스템 기본 동작 사용&lt;/li&gt;
&lt;li&gt;poweroff-after-timeout: 지정 시간 경과 후 시스템 종료&lt;/li&gt;
&lt;li&gt;hybrid-sleep-after-timeout: 지정 시간 경과 후 절전 모드 전환&lt;/li&gt;
&lt;li&gt;suspend-after-timeout: 지정 시간 경과 후 시스템 일시 정지&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 절전 모드 비활성화 설정하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령 프롬프트에서 아래를 입력하세요. root권한으로 진행합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702523078081&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target

#아래는 출력
Created symlink /etc/systemd/system/sleep.target &amp;rarr; /dev/null.
Created symlink /etc/systemd/system/suspend.target &amp;rarr; /dev/null.
Created symlink /etc/systemd/system/hibernate.target &amp;rarr; /dev/null.
Created symlink /etc/systemd/system/hybrid-sleep.target &amp;rarr; /dev/null.

#결과 확인하기
systemctl status sleep.target suspend.target hibernate.target hybrid-sleep.target

#아래는 출력
● sleep.target
   Loaded: masked (Reason: Unit sleep.target is masked.)
   Active: inactive (dead)

Dec 14 09:51:03 noyeora systemd[1]: Reached target Sleep.
Dec 14 11:11:10 noyeora systemd[1]: Stopped target Sleep.

● suspend.target
   Loaded: masked (Reason: Unit suspend.target is masked.)
   Active: inactive (dead)

Dec 14 11:11:10 noyeora systemd[1]: Reached target Suspend.
Dec 14 11:11:10 noyeora systemd[1]: Stopped target Suspend.

● hibernate.target
   Loaded: masked (Reason: Unit hibernate.target is masked.)
   Active: inactive (dead)

● hybrid-sleep.target
   Loaded: masked (Reason: Unit hybrid-sleep.target is masked.)
   Active: inactive (dead)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상으로 리눅스에서 절전 모드 진입을 방지하는 방법을 알아보았습니다.&lt;/p&gt;</description>
      <category>Windows | Linux</category>
      <category>HandleLidSwitch</category>
      <category>HandleLidSwitchDocked</category>
      <category>logind</category>
      <category>리눅스 절전모드</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/54</guid>
      <comments>https://noyecube.tistory.com/entry/Linux-%EB%A6%AC%EB%88%85%EC%8A%A4-%EC%A0%88%EC%A0%84-%EB%AA%A8%EB%93%9C-%EB%B0%A9%EC%A7%80-%EC%84%A4%EC%A0%95#entry54comment</comments>
      <pubDate>Thu, 14 Dec 2023 12:07:07 +0900</pubDate>
    </item>
    <item>
      <title>IOCP 기반 다중 접속 및 데이터 처리-2</title>
      <link>https://noyecube.tistory.com/entry/IOCP-%EA%B8%B0%EB%B0%98-%EB%8B%A4%EC%A4%91-%EC%A0%91%EC%86%8D-%EB%B0%8F-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B2%98%EB%A6%AC-2</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전 포스팅에서 IOCP 서버 스켈레톤 코드를 작성해 보았습니다. 이번 포스팅에서는 IOCP 서버를 위한 스레드풀 초기화 코드를 살펴보겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. IOCP 서버 실행 결과&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행결과 먼저 살펴보겠습니다. 실행환경은 8 코어 16 스레드 CPU입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IOCP Worker Thread 16개, IOCP RIL Thread 16개씩 생성하였고 동시실행 가능 스레드 수는 각 8개씩 설정하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스레드는 실행중 I/O 나 기타 사유로 Pending 상태에 빠질 수 있기 때문에 동시 실행 가능한 숫자보다 많은 수의 스레드를 생성하여 스레드풀에 등록해 둡니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1095&quot; data-origin-height=&quot;933&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vYGCS/btsBQk97SNb/0F9ZwmS5vkrpUANSom3Tc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vYGCS/btsBQk97SNb/0F9ZwmS5vkrpUANSom3Tc1/img.png&quot; data-alt=&quot;Start Server Debug Msg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vYGCS/btsBQk97SNb/0F9ZwmS5vkrpUANSom3Tc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvYGCS%2FbtsBQk97SNb%2F0F9ZwmS5vkrpUANSom3Tc1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;696&quot; height=&quot;593&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1095&quot; data-origin-height=&quot;933&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Start Server Debug Msg&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 초기화 코드 호출 구조 및 예제 코드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RilInit 함수에서 서버를 초기화합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;작업 스레드풀을 생성하고 작업 스레드들을 대기시킵니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;송신 스레드 RilTx를 생성하고 이벤트 대기상태로 만듭니다.&lt;/li&gt;
&lt;li&gt;서버 매니저 스레드를 생성하고 IOCP 스레드풀을 생성하고 대기시킵니다.&lt;/li&gt;
&lt;li&gt;클라이언트 접속/종료 처리는 서버 매니저 스레드에서 처리합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;2002&quot; data-origin-height=&quot;756&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E3Q5c/btsBRQBbJlE/ezpGETt3CTJSaUK44iQ4d0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E3Q5c/btsBRQBbJlE/ezpGETt3CTJSaUK44iQ4d0/img.png&quot; data-alt=&quot;Call Tree of RilInit&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E3Q5c/btsBRQBbJlE/ezpGETt3CTJSaUK44iQ4d0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE3Q5c%2FbtsBRQBbJlE%2FezpGETt3CTJSaUK44iQ4d0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2002&quot; height=&quot;756&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;2002&quot; data-origin-height=&quot;756&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Call Tree of RilInit&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 콜 트리에 대응되는 예제 코드입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;867&quot; data-origin-height=&quot;125&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/80k5k/btsBQpXTN3A/R1cKHD8xLTpkm46hwqWkR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/80k5k/btsBQpXTN3A/R1cKHD8xLTpkm46hwqWkR0/img.png&quot; data-alt=&quot;RIL Callback Init Call&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/80k5k/btsBQpXTN3A/R1cKHD8xLTpkm46hwqWkR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F80k5k%2FbtsBQpXTN3A%2FR1cKHD8xLTpkm46hwqWkR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;867&quot; height=&quot;125&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;867&quot; data-origin-height=&quot;125&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RIL Callback Init Call&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tx 스레드를 생성하는 코드입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;845&quot; data-origin-height=&quot;199&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dS0zT8/btsBRSluRHb/SeZ4qIvTnKpGdsV50Sh9Q1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dS0zT8/btsBRSluRHb/SeZ4qIvTnKpGdsV50Sh9Q1/img.png&quot; data-alt=&quot;Creation of RilTX&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dS0zT8/btsBRSluRHb/SeZ4qIvTnKpGdsV50Sh9Q1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdS0zT8%2FbtsBRSluRHb%2FSeZ4qIvTnKpGdsV50Sh9Q1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;845&quot; height=&quot;199&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;845&quot; data-origin-height=&quot;199&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Creation of RilTX&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 매니저 스레드를 생성하는 코드입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;973&quot; data-origin-height=&quot;229&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rFw6s/btsBQtzkaXA/jj02sdhJmdXGKq1obeBXc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rFw6s/btsBQtzkaXA/jj02sdhJmdXGKq1obeBXc1/img.png&quot; data-alt=&quot;Creation of RIL Server Manager&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rFw6s/btsBQtzkaXA/jj02sdhJmdXGKq1obeBXc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrFw6s%2FbtsBQtzkaXA%2Fjj02sdhJmdXGKq1obeBXc1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;973&quot; height=&quot;229&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;973&quot; data-origin-height=&quot;229&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Creation of RIL Server Manager&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU 코어 수를 읽어서 IOCP 스레드 풀의 스레드 동시 실행 가능 숫자를 코어 수의 절반만큼 설정하는 코드입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;1001&quot; data-origin-height=&quot;247&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0zRye/btsBMPWTtIS/KqWoMjokvz2uR0Tjr12F4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0zRye/btsBMPWTtIS/KqWoMjokvz2uR0Tjr12F4K/img.png&quot; data-alt=&quot;Creation of IOCP Worker Threads&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0zRye/btsBMPWTtIS/KqWoMjokvz2uR0Tjr12F4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0zRye%2FbtsBMPWTtIS%2FKqWoMjokvz2uR0Tjr12F4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1001&quot; height=&quot;247&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;1001&quot; data-origin-height=&quot;247&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Creation of IOCP Worker Threads&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU 코어 수만큼 스레드를 생성하는 코드입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;1117&quot; data-origin-height=&quot;292&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cokEH1/btsBMSeYnIU/9ZGEQAykCi3A9iiS2JWqG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cokEH1/btsBMSeYnIU/9ZGEQAykCi3A9iiS2JWqG1/img.png&quot; data-alt=&quot;Dynamic allocation of Thread Handle&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cokEH1/btsBMSeYnIU/9ZGEQAykCi3A9iiS2JWqG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcokEH1%2FbtsBMSeYnIU%2F9ZGEQAykCi3A9iiS2JWqG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1117&quot; height=&quot;292&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;1117&quot; data-origin-height=&quot;292&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Dynamic allocation of Thread Handle&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tx 스레드 Loop 부분입니다. WaitForMultipleObjects 함수에서 Tx 이벤트와 스레드 종료 이벤트를 대기합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;1086&quot; data-origin-height=&quot;1426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2njMt/btsBUDAOYcZ/WTfpHNhPy4mHtpbwWecb2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2njMt/btsBUDAOYcZ/WTfpHNhPy4mHtpbwWecb2k/img.png&quot; data-alt=&quot;RIL Tx Loop Code snippet&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2njMt/btsBUDAOYcZ/WTfpHNhPy4mHtpbwWecb2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2njMt%2FbtsBUDAOYcZ%2FWTfpHNhPy4mHtpbwWecb2k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1086&quot; height=&quot;1426&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;1086&quot; data-origin-height=&quot;1426&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RIL Tx Loop Code snippet&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 매니저 스레드의 내부 초기화 순서입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;1432&quot; data-origin-height=&quot;697&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eKf3F5/btsBQqid9cr/c9kYZ8JhymEkDypPaYTq6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eKf3F5/btsBQqid9cr/c9kYZ8JhymEkDypPaYTq6K/img.png&quot; data-alt=&quot;RIL Server Manager Init Sequence&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eKf3F5/btsBQqid9cr/c9kYZ8JhymEkDypPaYTq6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeKf3F5%2FbtsBQqid9cr%2Fc9kYZ8JhymEkDypPaYTq6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1432&quot; height=&quot;697&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;1432&quot; data-origin-height=&quot;697&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RIL Server Manager Init Sequence&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 작업 스레드와 마찬가지로 CPU 코어 숫자의 절반만큼 동시 실행가능한 IOCP 스레드 설정을 하고 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;1118&quot; data-origin-height=&quot;284&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CVsBt/btsBVC22nxR/XaDxJlM9QkzBTFwaxBKTOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CVsBt/btsBVC22nxR/XaDxJlM9QkzBTFwaxBKTOk/img.png&quot; data-alt=&quot;리슨 소켓으로 IOCP Handle 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CVsBt/btsBVC22nxR/XaDxJlM9QkzBTFwaxBKTOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCVsBt%2FbtsBVC22nxR%2FXaDxJlM9QkzBTFwaxBKTOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1118&quot; height=&quot;284&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;1118&quot; data-origin-height=&quot;284&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;리슨 소켓으로 IOCP Handle 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 매니저 스레드에서는 MsgWaitForMultipleObjectEx로 Loop를 구현합니다. PeekMessage 함수로 메시지도 수신할 수 있고 RIL_SERVER_SOCKET_POOL_REFRESH_INTERVAL에 해당하는 시간이 경과하면 타임아웃 이벤트가 발생하여 별도 IDLE에서의 코드 처리를 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;1365&quot; data-origin-height=&quot;488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIfs3g/btsBRRfQtIg/91N2u4gyEvVhWlzmDSz9S1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIfs3g/btsBRRfQtIg/91N2u4gyEvVhWlzmDSz9S1/img.png&quot; data-alt=&quot;이벤트 대기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIfs3g/btsBRRfQtIg/91N2u4gyEvVhWlzmDSz9S1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIfs3g%2FbtsBRRfQtIg%2F91N2u4gyEvVhWlzmDSz9S1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1365&quot; height=&quot;488&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;1365&quot; data-origin-height=&quot;488&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이벤트 대기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상으로 IOCP 서버를 위하여 IOCP 스레드 풀을 초기화하는 과정을 알아보았습니다.&lt;/p&gt;</description>
      <category>프로그래밍/C | C++</category>
      <category>concurrent_queue</category>
      <category>iocp</category>
      <category>radio interface layer</category>
      <category>WaitForMultipleObjects</category>
      <category>WaitForSingleObject</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/40</guid>
      <comments>https://noyecube.tistory.com/entry/IOCP-%EA%B8%B0%EB%B0%98-%EB%8B%A4%EC%A4%91-%EC%A0%91%EC%86%8D-%EB%B0%8F-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B2%98%EB%A6%AC-2#entry40comment</comments>
      <pubDate>Thu, 14 Dec 2023 00:01:12 +0900</pubDate>
    </item>
    <item>
      <title>IOCP 기반 다중 접속 및 데이터 처리-1</title>
      <link>https://noyecube.tistory.com/entry/aa</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;윈도 환경에서 다중 클라이언트의 접속을 처리하는 네트워크 서버를 구현하기 위해서는 IOCP 사용이 필수라고 생각합니다. 에코 서버를 IOCP 기반으로 구현한 예제는 많지만 실제 현업에서 다중 클라이언트 접속을 처리하기 위한 아키텍처를 어떤 방식으로 접근하면 좋을지 고민해 보는 차원에서 IOCP 기반 다중 접속 처리 및 데이터 처리 프로토타입 구상을 위해 이번 포스팅을 작성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 리눅스 epoll 대비 IOCP(I/O Completion Port) 장점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 유명한 게임서버는 Windows Server 기반이라는 사실을 알고 계신가요?&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 리눅스에서 다중 접속을 처리하기 위해 사용가능한 epoll 보다 Windows의 IOCP가 더 나은 장점이 있기 때문입니다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;윈도 표준 API로 윈도 응용 프로그램과의 호환성&amp;nbsp;&lt;/li&gt;
&lt;li&gt;이벤트 큐 탐색 방식이 epoll에 비해 효율적이고 이벤트 큐 크기 제한이 없어 대규모 I/O 작업 처리에 적합&lt;/li&gt;
&lt;li&gt;스레드 풀과 함께 사용하기 편리합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. IOCP 프로토타입 소스파일 구조&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 프로그램의 프로토타입을 클래스별로 구조화 하여 스켈레톤 코드를 작성하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;338&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DPugt/btsvlv3r1IN/qJUPdMTJkECwic3FAJnGwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DPugt/btsvlv3r1IN/qJUPdMTJkECwic3FAJnGwk/img.png&quot; data-alt=&quot;프로토타입 소스코드 파일&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DPugt/btsvlv3r1IN/qJUPdMTJkECwic3FAJnGwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDPugt%2Fbtsvlv3r1IN%2FqJUPdMTJkECwic3FAJnGwk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;663&quot; height=&quot;338&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;338&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;프로토타입 소스코드 파일&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DmssItem Class: 패킷버퍼를 사전 할당한 메모리풀로 관리하는 클래스&lt;/li&gt;
&lt;li&gt;RilCbWorker Class: 다중 클라이언트가 전송한 패킷을 처리 후 콜백함수 호출하는 스레드 풀 구성&lt;/li&gt;
&lt;li&gt;RilServer Class: 부모 클래스로서 매니저 스레드, Iocp 스레드, Tx 스레드를 생성&lt;/li&gt;
&lt;li&gt;RilSession Class: 다중 접속 클라이언트의 소켓을 관리하고 세션을 관리&lt;/li&gt;
&lt;li&gt;ril_protocol Header: 클라이언트와 서버가 서로 공유하는 프로토콜 정의 헤더 파일&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.1. RIL Server 설명&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Radio Interface Layer(이하 RIL) 모듈의 부모 클래스로서 RIL을 구성하는 하위 모듈의 인스턴스를 갖고 자식 스레드를 관리합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;681&quot; data-origin-height=&quot;814&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GyX9C/btsBQmfENCQ/UVLrgrUbDQRZbLoTyKaN5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GyX9C/btsBQmfENCQ/UVLrgrUbDQRZbLoTyKaN5k/img.png&quot; data-alt=&quot;RIL Server Class&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GyX9C/btsBQmfENCQ/UVLrgrUbDQRZbLoTyKaN5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGyX9C%2FbtsBQmfENCQ%2FUVLrgrUbDQRZbLoTyKaN5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;681&quot; height=&quot;814&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;681&quot; data-origin-height=&quot;814&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RIL Server Class&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.2. RIL Session 설명&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다중 클라이언트를 관리하기 위한 클래스입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;링크드리스트로 관리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트가 접속을 끊으면 소켓을 종료하지 않고 재사용하여 오버헤드를 줄입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트가 전송중인 패킷을 임시 저장할 공간을 가지고 있습니다. 일종의 링버퍼 역할인데 프로토콜에 특화된 자료구조를 적용하여 링버퍼의 탐색 단위 1바이트보다 더 큰 블록 단위로 처리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래쪽의 CMap 클래스는 미리 생성해 둔 소켓을 관리하는 목적입니다. 클라이언트가 접속하는 순간에 소켓을 생성하는 것이 아니고 미리 생성해 둔 소켓 풀에서 소켓을 가져오고 접속이 종료되면 소켓을 닫는 것이 아니라 소켓풀에 다시 넣어둡니다. 소켓을 재사용하면 클라이언트의 빈번한 접속/종료에 대한 성능 오버헤드를 줄일 수 있는 장점이 있지만 재사용된 소켓을 같은 클라이언트로 인식하지 않도록 별도의 관리 기법 적용이 필요합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;1162&quot; data-origin-height=&quot;852&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kK7BZ/btsBR7PSxJd/fK6bOTvIG8ZIcRIr84mPW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kK7BZ/btsBR7PSxJd/fK6bOTvIG8ZIcRIr84mPW1/img.png&quot; data-alt=&quot;RIL Session Data Structure&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kK7BZ/btsBR7PSxJd/fK6bOTvIG8ZIcRIr84mPW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkK7BZ%2FbtsBR7PSxJd%2FfK6bOTvIG8ZIcRIr84mPW1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1162&quot; height=&quot;852&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;1162&quot; data-origin-height=&quot;852&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RIL Session Data Structure&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 자료구조를 사용하여 아래의 세션 리스트를 구성합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1019&quot; data-origin-height=&quot;483&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmQR7o/btsBT0i6t0O/sclVm1kKFBuntNlGsPKWJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmQR7o/btsBT0i6t0O/sclVm1kKFBuntNlGsPKWJk/img.png&quot; data-alt=&quot;RIL Session List&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmQR7o/btsBT0i6t0O/sclVm1kKFBuntNlGsPKWJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmQR7o%2FbtsBT0i6t0O%2FsclVm1kKFBuntNlGsPKWJk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1019&quot; height=&quot;483&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1019&quot; data-origin-height=&quot;483&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RIL Session List&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세션 클래스 코드는 아래와 같이 작성하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;945&quot; data-origin-height=&quot;841&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1EnxH/btsBMSTue3D/Uaqlikn0GDcIXCd3e5pQ5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1EnxH/btsBMSTue3D/Uaqlikn0GDcIXCd3e5pQ5k/img.png&quot; data-alt=&quot;RIL Session Class&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1EnxH/btsBMSTue3D/Uaqlikn0GDcIXCd3e5pQ5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1EnxH%2FbtsBMSTue3D%2FUaqlikn0GDcIXCd3e5pQ5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;945&quot; height=&quot;841&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;945&quot; data-origin-height=&quot;841&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RIL Session Class&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.3. RIL Callback Worker&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수신데이터를 처리하는 스레드로서 스레드 풀을 구성합니다. 스레드 풀의 규모는 동적으로 결정됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 CWinThread** _m_pparrThread, HANDLE* _m_phThreads; 로 포인터 변수를 두고 CPU의 코어 수에 의해 스레드풀 규모가 결정되면 그때 동적으로 할당 후 스레드를 생성합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;962&quot; data-origin-height=&quot;484&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l3ua7/btsBXGcRTpn/KlAnKakWkx28UhBniekTgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l3ua7/btsBXGcRTpn/KlAnKakWkx28UhBniekTgK/img.png&quot; data-alt=&quot;RIL Callback Worker Class&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l3ua7/btsBXGcRTpn/KlAnKakWkx28UhBniekTgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl3ua7%2FbtsBXGcRTpn%2FKlAnKakWkx28UhBniekTgK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;962&quot; height=&quot;484&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;962&quot; data-origin-height=&quot;484&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RIL Callback Worker Class&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.4. DMSS Item Class&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DMSS는 Data Management Services(DMSs)의 약자입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패킷 데이터 송수신에 사용하는 자료구조 TST_DSM_ITEM을 정의하고 DSM_ITEM POOL을 관리합니다. 프로그램 시작시 정적 메모리 영역에 할당이 되었다가 동작 중 풀 용량이 부족하면 힙 메모리 영역에서 추가로 할당하여 사용합니다. IDLE 타임에는 남는 노드를 메모리 반환합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;912&quot; data-origin-height=&quot;239&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cumSla/btsBXKzlb3S/Z6MJphQk9BO7FbKVeP2xu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cumSla/btsBXKzlb3S/Z6MJphQk9BO7FbKVeP2xu1/img.png&quot; data-alt=&quot;TST_DSM_ITEM&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cumSla/btsBXKzlb3S/Z6MJphQk9BO7FbKVeP2xu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcumSla%2FbtsBXKzlb3S%2FZ6MJphQk9BO7FbKVeP2xu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;912&quot; height=&quot;239&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;912&quot; data-origin-height=&quot;239&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;TST_DSM_ITEM&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;837&quot; data-origin-height=&quot;148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgg9Mi/btsBMSy9KpY/4XkEMKgqk6lEUzzOxMPEL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgg9Mi/btsBMSy9KpY/4XkEMKgqk6lEUzzOxMPEL0/img.png&quot; data-alt=&quot;TST_DSM_ITEM_POOL&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgg9Mi/btsBMSy9KpY/4XkEMKgqk6lEUzzOxMPEL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcgg9Mi%2FbtsBMSy9KpY%2F4XkEMKgqk6lEUzzOxMPEL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;837&quot; height=&quot;148&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;837&quot; data-origin-height=&quot;148&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;TST_DSM_ITEM_POOL&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;1285&quot; data-origin-height=&quot;660&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SPMDV/btsBT19E2m2/iUpYkdE4FsENIBZVni7bl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SPMDV/btsBT19E2m2/iUpYkdE4FsENIBZVni7bl0/img.png&quot; data-alt=&quot;TST_DSM_HEADER_ITEM&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SPMDV/btsBT19E2m2/iUpYkdE4FsENIBZVni7bl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSPMDV%2FbtsBT19E2m2%2FiUpYkdE4FsENIBZVni7bl0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1285&quot; height=&quot;660&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;1285&quot; data-origin-height=&quot;660&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;TST_DSM_HEADER_ITEM&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;538&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FdcOQ/btsBVE0GVfJ/3uhm4lEij2xWjJjjJ9o07k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FdcOQ/btsBVE0GVfJ/3uhm4lEij2xWjJjjJ9o07k/img.png&quot; data-alt=&quot;DMSS Item Class&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FdcOQ/btsBVE0GVfJ/3uhm4lEij2xWjJjjJ9o07k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFdcOQ%2FbtsBVE0GVfJ%2F3uhm4lEij2xWjJjjJ9o07k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;538&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;538&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;DMSS Item Class&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.5. RIL 프로토콜 헤더&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 프로그래밍 서적 또는 게임 서버 개발 서적을 보면 서버와 클라이언트 통신을 위한 필수 과정으로 패킷의 직렬화/역직렬화 이야기가 나옵니다. 개발 공수는 더 들지만 아래와 같이 1바이트로 패딩 한 구조체를 사용하면 가장 빠릅니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;1064&quot; data-origin-height=&quot;1494&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dgrBwF/btsBRQVmtrH/kblGQcWCUq6PkY4n4q2sK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dgrBwF/btsBRQVmtrH/kblGQcWCUq6PkY4n4q2sK0/img.png&quot; data-alt=&quot;RIL PROTOCOL 헤더&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dgrBwF/btsBRQVmtrH/kblGQcWCUq6PkY4n4q2sK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdgrBwF%2FbtsBRQVmtrH%2FkblGQcWCUq6PkY4n4q2sK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1064&quot; height=&quot;1494&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;1064&quot; data-origin-height=&quot;1494&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RIL PROTOCOL 헤더&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;1006&quot; data-origin-height=&quot;960&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G5ScD/btsBMSFW3wP/0xAFbh42OG3rZ1zdlsK8Dk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G5ScD/btsBMSFW3wP/0xAFbh42OG3rZ1zdlsK8Dk/img.png&quot; data-alt=&quot;RIL PROTOCOL 헤더 2&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G5ScD/btsBMSFW3wP/0xAFbh42OG3rZ1zdlsK8Dk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG5ScD%2FbtsBMSFW3wP%2F0xAFbh42OG3rZ1zdlsK8Dk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1006&quot; height=&quot;960&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;1006&quot; data-origin-height=&quot;960&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RIL PROTOCOL 헤더 2&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.6. 소켓 유틸리티&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 소켓을 IOCP와 연계 활용하기 위한 유틸 함수입니다. 아래 정의해둔 NTSTATUS 값을 참고하기 바랍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;1152&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blHzgq/btsBR882wnQ/cvrvO8OekNAaTpkpKbzq10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blHzgq/btsBR882wnQ/cvrvO8OekNAaTpkpKbzq10/img.png&quot; data-alt=&quot;SOCK_ITEM&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blHzgq/btsBR882wnQ/cvrvO8OekNAaTpkpKbzq10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblHzgq%2FbtsBR882wnQ%2FcvrvO8OekNAaTpkpKbzq10%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;755&quot; height=&quot;1152&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;1152&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SOCK_ITEM&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;14.png&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;748&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpHxGG/btsBQRfuerS/OncPFcLo7M2oSDZiEr1Zak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpHxGG/btsBQRfuerS/OncPFcLo7M2oSDZiEr1Zak/img.png&quot; data-alt=&quot;GetListenSocket&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpHxGG/btsBQRfuerS/OncPFcLo7M2oSDZiEr1Zak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpHxGG%2FbtsBQRfuerS%2FOncPFcLo7M2oSDZiEr1Zak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;914&quot; height=&quot;748&quot; data-filename=&quot;14.png&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;748&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;GetListenSocket&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 소켓 API는 직접 호출이 아니라 API 함수에 대한 포인터 획득 후 함수포인터로 호출하는 방식입니다. 그래서 아래의 유틸 함수를 사용합니다. 윈도 시스템 프로그래밍 책에서 발췌하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;15.png&quot; data-origin-width=&quot;753&quot; data-origin-height=&quot;363&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eyLnJQ/btsBXGRti7y/hX75ehzH2IqdxU7kZrpLBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eyLnJQ/btsBXGRti7y/hX75ehzH2IqdxU7kZrpLBk/img.png&quot; data-alt=&quot;GetSockExtAPI&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eyLnJQ/btsBXGRti7y/hX75ehzH2IqdxU7kZrpLBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeyLnJQ%2FbtsBXGRti7y%2FhX75ehzH2IqdxU7kZrpLBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;753&quot; height=&quot;363&quot; data-filename=&quot;15.png&quot; data-origin-width=&quot;753&quot; data-origin-height=&quot;363&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;GetSockExtAPI&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 소켓풀을 사용한다고 말씀드렸습니다. 아래 함수는 소켓풀에 있는 소켓이 전부 소진되었을 때 추가로 소켓풀에 소켓을 할당하는 함수입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;16.png&quot; data-origin-width=&quot;1016&quot; data-origin-height=&quot;854&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dACHLk/btsBMSFW3xP/ONYudwfVhwoD0MfjHIO0QK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dACHLk/btsBMSFW3xP/ONYudwfVhwoD0MfjHIO0QK/img.png&quot; data-alt=&quot;IncreaseAcceptSockets&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dACHLk/btsBMSFW3xP/ONYudwfVhwoD0MfjHIO0QK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdACHLk%2FbtsBMSFW3xP%2FONYudwfVhwoD0MfjHIO0QK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1016&quot; height=&quot;854&quot; data-filename=&quot;16.png&quot; data-origin-width=&quot;1016&quot; data-origin-height=&quot;854&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;IncreaseAcceptSockets&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상으로 IOCP기반 다중 접속 처리를 위한 프로토타입을 작성해 보았습니다.&lt;/p&gt;</description>
      <category>프로그래밍/C | C++</category>
      <category>iocp</category>
      <category>LPFN_ACCEPTEX</category>
      <category>Socket</category>
      <category>Thread pool</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/39</guid>
      <comments>https://noyecube.tistory.com/entry/aa#entry39comment</comments>
      <pubDate>Wed, 13 Dec 2023 17:28:12 +0900</pubDate>
    </item>
    <item>
      <title>[WINDOWS] 네트워크 어댑터 우선순위 변경하기</title>
      <link>https://noyecube.tistory.com/entry/WINDOWS-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%96%B4%EB%8C%91%ED%84%B0-%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84-%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;윈도에서 네트워크 어댑터가 두 개 이상 활성화되어 있으면 우선순위가 자동으로 설정되는데요. 그 우선순위에 따라 인터넷이 잘 될 수도 있고 안될 수도 있습니다. 이번 포스팅에서는 그 우선순위를 점검하고 수정하는 방법을 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제 컴퓨터의 네트워크 어댑터 현황을 예를 들어서 설명하겠습니다. 저는 무선랜을 사용하여 외부 인터넷을 사용하고 있는데 유선랜을 유선공유기와 연결하여 다른 장치와 사설망으로 통신을 하려고 합니다. 그런데 웹브라우저로 인터넷을 시도하면 무선랜으로 패킷이 나가는 게 아니라 인터넷이 불가능한 유선공유기 쪽으로 인터넷연결 시도가 먼저 이루어졌다가 타임아웃이 발생하면 무선랜으로 시도하여 답답함을 느껴서 네트워크 어댑터의 라우팅 테이블을 점검하고 우선순위를 변경하기로 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 두개 이상의 네트워크 어댑터 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 및 인터넷-&amp;gt;고급 네트워크 설정에서 사용 중인 네트워크 어댑터 확인이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저의 경우 아래와 같이 유선 이더넷 2개와 Wi-Fi 1개가 확인됩니다. 현재 이더넷 1번에 유선 공유기가 연결되어 있고 무선랜에 인터넷이 연결되어 있는 상태입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1009&quot; data-origin-height=&quot;345&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRiZRu/btsBKjjlmDA/JdbACV7r3VK7OSMGzfmyEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRiZRu/btsBKjjlmDA/JdbACV7r3VK7OSMGzfmyEK/img.png&quot; data-alt=&quot;네트워크 어댑터 목록&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRiZRu/btsBKjjlmDA/JdbACV7r3VK7OSMGzfmyEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRiZRu%2FbtsBKjjlmDA%2FJdbACV7r3VK7OSMGzfmyEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1009&quot; height=&quot;345&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1009&quot; data-origin-height=&quot;345&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;네트워크 어댑터 목록&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 라우팅 테이블 확인&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;라우팅 테이블&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;라우팅 테이블은 컴퓨터 네트워크에서 목적지 주소를 목적지에 도달하기 위한 네트워크 노선으로 변환시키는 목적으로 사용되는 테이블입니다. 라우팅 프로토콜의 가장 중요한 목적이 바로 이러한 라우팅 테이블의 구성입니다. 라우팅 테이블은 다음과 같은 세 가지 정보가 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목적지 주소: 패킷을 전송할 목적지 네트워크의 주소입니다.&lt;/li&gt;
&lt;li&gt;게이트웨이 주소: 목적지 네트워크에 도달하기 위한 다음 라우터의 주소입니다.&lt;/li&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;메트릭&lt;/b&gt;&lt;/u&gt;: 목적지 네트워크에 도달하기 위한 경로의 비용을 나타내는 숫자로서 &lt;u&gt;&lt;b&gt;이 값이 작을수록 우선순위가 높습니다.&lt;/b&gt;&lt;/u&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바탕화면 작업표시줄의 검색창에 &quot;cmd&quot;를 입력하면 &quot;명령 프롬프트&quot;를 실행할 수 있습니다. 그냥 실행하지 마시고 마우스 우클릭 후 &quot;관리자 권한으로 실행&quot;을 눌러서 창을 띄워 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;어댑터별 MAC 주소 및 IP 주소 확인&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령 프롬프트에서 다음 명령어를 입력하여 IP와 MAC을 확인합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702441185668&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt; ipconfig /all&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제 컴퓨터에서는 아래와 같이 나왔습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이더넷 어댑터 2번은 연결이 안되어 있어서 미디어 연결 끊김으로 나타나 있고 살펴볼 부분은 이더넷 어댑터 이더넷과 무선 LAN 어댑터 Wi-Fi입니다. 여기에서 물리적 주소와 IPv4 주소를 기억하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안상 공인 IP부분은 삭제처리 하였음을 감안하고 봐주시면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;604&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PfMcl/btsBUCVMLQa/xdQKesmPpE010uZpbb4wD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PfMcl/btsBUCVMLQa/xdQKesmPpE010uZpbb4wD1/img.png&quot; data-alt=&quot;ipconfig /all 실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PfMcl/btsBUCVMLQa/xdQKesmPpE010uZpbb4wD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPfMcl%2FbtsBUCVMLQa%2FxdQKesmPpE010uZpbb4wD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;604&quot; height=&quot;720&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;604&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ipconfig /all 실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물리적 주소는 MAC 주소라고도 하며 이더넷 어댑터 생산 공장에서 입력해 둔 자체 주소입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응용프로그램에서는 IP 정보로 통신하지만 물리계층에서는 IP 주소와 MAC 주소 정보가 있는 ARP 테이블을 조회하여 실제 물리주소로 통신한다는 점을 참고하시면 됩니다. 위에서 알아낸 정보를 바탕으로 라우팅 테이블을 확인할 차례입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;라우팅 테이블 조회하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우팅 테이블 조회를 위해 명령 프롬프트에 &quot;route print&quot; 명령어를 입력해 봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 어댑터 목록과 네트워크 경로 테이블을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;643&quot; data-origin-height=&quot;573&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBXoRC/btsBOhy1x5Y/S5BgLbq7yxYkgwKI4Bzj5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBXoRC/btsBOhy1x5Y/S5BgLbq7yxYkgwKI4Bzj5k/img.png&quot; data-alt=&quot;route print 실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBXoRC/btsBOhy1x5Y/S5BgLbq7yxYkgwKI4Bzj5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBXoRC%2FbtsBOhy1x5Y%2FS5BgLbq7yxYkgwKI4Bzj5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;643&quot; height=&quot;573&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;643&quot; data-origin-height=&quot;573&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;route print 실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로 테이블에서 활성 경로와 영구 경로를 확인할 수 있습니다. 영구 경로는 고정 IP 설정의 어댑터 게이트웨이가 잡혀있습니다. 활성 경로는 윈도 부팅 시 결정되는 경로 테이블입니다. 다른 줄은 볼 필요 없이 서브넷 마스크 값 &quot;255.255.255.0&quot;을 가진 라인을 확인하면 됩니다. 무선 LAN으로 인터넷과 연결되어 있는 IP의 메트릭 값이 &quot;301&quot;, 유선 LAN으로 공유기와 연결되어 있는(인터넷 연결이 없는) IP의 메트릭 값이 &quot;281&quot;로 확인됩니다. 앞서 메트릭 값이 작을수록 우선순위가 높다고 하였는데 인터넷 연결에 지연이 발생하는 원인은 인터넷 연결이 없는 메트릭 값 &quot;281&quot;이 우선순위가 더 높기 때문입니다. 그럼 무선 LAN 어댑터의 메트릭 값을 281보다 작은 값으로 설정하면 인터넷 지연이 해결될 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 라우팅 우선순위 변경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우팅 우선순위는 메트릭(Metric) 값의 변경으로 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 순위 조정이 필요한 인터페이스가 모두 고정 IP 설정이라면 영구 경로에 라우팅 설정을 하면 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저와 같이 유동 IP를 사용하는 어댑터의 경우에는 IP가 변경되어도 라우팅 우선순위에 영향이 없도록 어댑터 자체의 메트릭 값을 바꿔주도록 하겠습니다. 그럼 부팅 시 어댑터에 부여된 메트릭 값에 따라 활성 경로의 메트릭이 적절하게 조정될 것입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;어댑터 설정에서 메트릭 변경하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 네트워크 어댑터 창을 띄웁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;윈도11에서는 &quot;고급 네트워크 설정-관련 설정-기타 네트워크 어댑터 옵션&quot;에서 가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;770&quot; data-origin-height=&quot;209&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YyjwC/btsBSaliyEM/DvWJGaLlJVCAqejbq3n9bK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YyjwC/btsBSaliyEM/DvWJGaLlJVCAqejbq3n9bK/img.png&quot; data-alt=&quot;네트워크 어댑터 상세 목록&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YyjwC/btsBSaliyEM/DvWJGaLlJVCAqejbq3n9bK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYyjwC%2FbtsBSaliyEM%2FDvWJGaLlJVCAqejbq3n9bK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;770&quot; height=&quot;209&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;770&quot; data-origin-height=&quot;209&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;네트워크 어댑터 상세 목록&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 그림을 참고하여 &quot;이더넷 속성-인터넷 프로토콜 버전 속성-고급-인터페이스 메트릭&quot;을 수동으로 설정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선순위를 &lt;b&gt;&lt;u&gt;높일 어댑터의 메트릭 값을 낮게, 낮출 어댑터의 메트릭 값을 높게&lt;/u&gt;&lt;/b&gt; 설정합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;1109&quot; data-origin-height=&quot;475&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4WgzF/btsBQYFSRPL/3Kh3oJFPkpZEuqYOn5LTd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4WgzF/btsBQYFSRPL/3Kh3oJFPkpZEuqYOn5LTd0/img.png&quot; data-alt=&quot;인터페이스 메트릭 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4WgzF/btsBQYFSRPL/3Kh3oJFPkpZEuqYOn5LTd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4WgzF%2FbtsBQYFSRPL%2F3Kh3oJFPkpZEuqYOn5LTd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1109&quot; height=&quot;475&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;1109&quot; data-origin-height=&quot;475&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;인터페이스 메트릭 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;라우팅 테이블 우선순위 재 확인&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 어댑터의 인터페이스 메트릭 값을 조정했으면 &quot;route print&quot; 명령으로 라우팅 테이블을 재조회 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;629&quot; data-origin-height=&quot;103&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lpGdV/btsBQQgm989/Wcb3mdV5oPW1OqkzpCsq6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lpGdV/btsBQQgm989/Wcb3mdV5oPW1OqkzpCsq6k/img.png&quot; data-alt=&quot;route print 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lpGdV/btsBQQgm989/Wcb3mdV5oPW1OqkzpCsq6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlpGdV%2FbtsBQQgm989%2FWcb3mdV5oPW1OqkzpCsq6k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;629&quot; height=&quot;103&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;629&quot; data-origin-height=&quot;103&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;route print 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터페이스 메트릭 조정 후 조회한 라우팅 테이블에서의 메트릭 값은 무선 LAN 우선순위가 더 높게 변경되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 인터넷 브라우저로 인터넷을 시도해 보니 이전과 다르게 지연 현상이 사라졌습니다.&lt;/p&gt;</description>
      <category>Windows | Linux</category>
      <category>IPv4경로</category>
      <category>route</category>
      <category>네트워크 우선순위</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/53</guid>
      <comments>https://noyecube.tistory.com/entry/WINDOWS-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%96%B4%EB%8C%91%ED%84%B0-%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84-%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0#entry53comment</comments>
      <pubDate>Wed, 13 Dec 2023 14:18:44 +0900</pubDate>
    </item>
    <item>
      <title>[VBA] 날짜 중복 검출하기(DATE OVERLAP)</title>
      <link>https://noyecube.tistory.com/entry/VBA-%EB%82%A0%EC%A7%9C-%EC%A4%91%EB%B3%B5-%EA%B2%80%EC%B6%9C%ED%95%98%EA%B8%B0DATE-OVERLAP</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 직원별 휴가 시작일과 종료일을 관리하는 엑셀을 작성했다고 가정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원본 데이터를 하나의 워크시트에 취합했는데 겹치는 날짜가 있는데 찾기가 어렵습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VBA를 활용하여 간단하게 중복되는 날짜를 검출해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 예제 데이터 시트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 예제 데이터 시트를 보면 소속사, 성명, 시작일, 종료일이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 배경색의 행끼리 날짜가 중복되고 있음을 확인할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 행이 수천 행이 존재한다면? 눈으로 검증할 생각을 하면 생각만 해도 퇴사 마렵습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;worksheet_master.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;383&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YJCd9/btsBMRdVwxr/HUsIEAKtf290y4Vn0r67W1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YJCd9/btsBMRdVwxr/HUsIEAKtf290y4Vn0r67W1/img.webp&quot; data-alt=&quot;예제 데이터 시트-예제데이터.xlsx&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YJCd9/btsBMRdVwxr/HUsIEAKtf290y4Vn0r67W1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYJCd9%2FbtsBMRdVwxr%2FHUsIEAKtf290y4Vn0r67W1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;383&quot; data-filename=&quot;worksheet_master.webp&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;383&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;예제 데이터 시트-예제데이터.xlsx&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. VBA 매크로 파일 만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 그림과 같이 매크로 파일을 하나 만들어 줍니다. 매크로 파일을 만드는 자세한 방법은 이전 포스팅을 참조하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blog.noyecube.com/entry/VBA-%EC%97%91%EC%85%80-VBA-%EC%82%AC%EC%9A%A9%EB%B2%95&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2023.12.01 - [프로그래밍/Excel VBA] - [VBA] 엑셀 VBA 사용법&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파라미터를 4개 입력받도록 하였습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Target File Path: 검증할 엑셀 파일이 존재하는 파일 경로를 입력합니다.&lt;/li&gt;
&lt;li&gt;Target File Name: 검증할 엑셀 파일명을 입력합니다.&lt;/li&gt;
&lt;li&gt;Target Worksheet: 검증할 엑셀 파일명 내의 워크시트명을 입력합니다.&lt;/li&gt;
&lt;li&gt;Data Entry Row No: 데이터가 시작하는 행 번호를 입력합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Summary Report에는 날짜 중복이 검출되었는지, 몇 개 검출되었는지 알려줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;vba_macro.webp&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;641&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1hDf0/btsBDQBkkEf/h0QafGjojlQKQvzlyOuG40/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1hDf0/btsBDQBkkEf/h0QafGjojlQKQvzlyOuG40/img.webp&quot; data-alt=&quot;VBA-매크로 파일&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1hDf0/btsBDQBkkEf/h0QafGjojlQKQvzlyOuG40/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1hDf0%2FbtsBDQBkkEf%2Fh0QafGjojlQKQvzlyOuG40%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;641&quot; data-filename=&quot;vba_macro.webp&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;641&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;VBA-매크로 파일&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 날짜 중복 여부 검출 알고리즘&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제 데이터 시트의 5행과 7행은 날짜가 겹치는 상황입니다. 5행을 원본데이터, 7행을 비교데이터라고 할 때 슈도코드는 아래와 같습니다. 원본데이터와 비교데이터가 서로 바뀌어도 상관없습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702194796709&quot; class=&quot;vbnet&quot; data-ke-language=&quot;vbnet&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;' DATE OVERLAP 검출 슈도 코드
If 원본시작날짜 &amp;lt;= 비교종료날짜 And 비교시작날짜 &amp;lt;= 원본종료날짜 Then
	날짜겹칩(DATE OVERLAP) 발생!!
 End If&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 실행결과 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 실행 결과를 예제 데이터와 비교해 보면 중복이 발생한 행 정보를 Row index에 표기해 주고 있음을 확인할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;result.webp&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;446&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGvoXe/btsBC2Wp9Ak/ceOYPBGZMRrGi8kzkmNWfK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGvoXe/btsBC2Wp9Ak/ceOYPBGZMRrGi8kzkmNWfK/img.webp&quot; data-alt=&quot;실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGvoXe/btsBC2Wp9Ak/ceOYPBGZMRrGi8kzkmNWfK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGvoXe%2FbtsBC2Wp9Ak%2FceOYPBGZMRrGi8kzkmNWfK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;446&quot; data-filename=&quot;result.webp&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;446&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 검증매크로 소스코드&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매크로파일의 소스코드 전체를 공개합니다. [START] Data Validation이 날짜 중복 여부 검출 부분입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702195148997&quot; class=&quot;vbnet&quot; data-ke-language=&quot;vbnet&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Public Function FileExists(ByVal path_ As String) As Boolean
    FileExists = (Dir(path_, vbDirectory) &amp;lt;&amp;gt; &quot;&quot;)
End Function

Sub VacationDupCheck_Click()

    Dim shtThisVBA As Worksheet
    Set shtThisVBA = ThisWorkbook.Worksheets(&quot;vba&quot;)
    
    ' Define User-friendly Color Index variables
    Const COLORINDEX_WHITE As Integer = 0
    Const COLORINDEX_RED As Integer = 3
    Const COLORINDEX_GREEN As Integer = 4
    Const COLORINDEX_BLUE As Integer = 5
    Const COLORINDEX_YELLOW As Integer = 6
    Const COLORINDEX_PINK As Integer = 7
    Const COLORINDEX_ORANGE As Integer = 46
    
    
    ' Declare a string variable to store the path of the file to be validated
    Dim strTargetFilePath As String
    Dim strTargetFileName As String
    Dim strTargetWorksheetName As String
    Dim lEntryRowNo As Long
    Dim lLastRowNo As Long
    strTargetFilePath = shtThisVBA.Range(&quot;E4&quot;).Value
    strTargetFileName = shtThisVBA.Range(&quot;E5&quot;).Value
    strTargetWorksheetName = shtThisVBA.Range(&quot;E6&quot;).Value
    lEntryRowNo = shtThisVBA.Range(&quot;E7&quot;).Value
    
    
    ' Concatenate the file path and filename to complete the full path.
    Dim strTargetFullPath As String
    strTargetFullPath = strTargetFilePath + &quot;\&quot; + strTargetFileName
    
    Debug.Print &quot;[INFO] strTargetFilePath: &quot; &amp;amp; strTargetFilePath
    Debug.Print &quot;[INFO] strTargetFileName: &quot; &amp;amp; strTargetFileName
    Debug.Print &quot;[INFO] strTargetWorksheetName: &quot; &amp;amp; strTargetWorksheetName
    Debug.Print &quot;[INFO] strTargetFullPath: &quot; &amp;amp; strTargetFullPath
    Debug.Print &quot;[INFO] lEntryRowNo: &quot; &amp;amp; lEntryRowNo
    
    ' [START] Verify the existence of the target file. and target worksheet
    Dim wbTargetWorkBook As Workbook
    Dim wsTargetWorksheet As Worksheet
    
    If FileExists(strTargetFullPath) Then
        Set wbTargetWorkBook = Workbooks.Open(strTargetFullPath)
    Else
        MsgBox &quot;[ERROR] No Such Target File Path&quot; &amp;amp; vbNewLine &amp;amp; _
                strTargetFullPath, vbCritical
        Exit Sub
    End If
    
    ' Verify the existence of the target worksheet.
    Dim boVerified As Boolean
    boVerified = False
    For Each wsTargetWorksheet In wbTargetWorkBook.Worksheets
        If wsTargetWorksheet.Name = strTargetWorksheetName Then
            boVerified = True
            Exit For
        End If
    Next wsTargetWorksheet
    
    If boVerified Then
        Debug.Print &quot;[INFO] Worksheet Lookup Success: &quot; &amp;amp; strTargetWorksheetName
    Else
        MsgBox &quot;[ERROR] No Such Target Worksheet Name&quot; &amp;amp; vbNewLine &amp;amp; _
                strTargetWorksheetName, vbCritical
        Exit Sub
    End If
    ' [END] Verify the existence of the target file. and target worksheet

    lLastRowNo = wsTargetWorksheet.Cells(wsTargetWorksheet.Rows.Count, 1).End(xlUp).Row
    Debug.Print &quot;[INFO] lLastRowNo: &quot; &amp;amp; lLastRowNo
    
    Dim i As Long
    Dim j As Long
    Dim sDateRoot As Date
    Dim eDateRoot As Date
    Dim strIdNameRoot As String
    Dim sDateCmp As Date
    Dim eDateCmp As Date
    Dim strIdNameCmp As String
    Dim lCntError As Long
    Dim lReportIndex As Long: lReportIndex = 16
    
    Dim strTimeStampStart As String
    strTimeStampStart = Format(Now, &quot;yyyy-MM-dd hh:mm:ss&quot;)
    Debug.Print &quot;[INFO] strTimeStampStart: &quot; &amp;amp; strTimeStampStart

    lCntError = 0
    ' [START] Data validation
    For i = lEntryRowNo To lLastRowNo - 1
        strIdNameRoot = wsTargetWorksheet.Cells(i, 3).Value
        strIdNameRoot = strIdNameRoot + &quot;-&quot; + wsTargetWorksheet.Cells(i, 4).Value
        sDateRoot = wsTargetWorksheet.Cells(i, 5).Value
        eDateRoot = wsTargetWorksheet.Cells(i, 7).Value
        ' Debug.Print &quot;[INFO] strIdNameRoot: &quot; &amp;amp; strIdNameRoot
        
        For j = i + 1 To lLastRowNo
            strIdNameCmp = wsTargetWorksheet.Cells(j, 3).Value
            strIdNameCmp = strIdNameCmp + &quot;-&quot; + wsTargetWorksheet.Cells(j, 4).Value
            sDateCmp = wsTargetWorksheet.Cells(j, 5).Value
            eDateCmp = wsTargetWorksheet.Cells(j, 7).Value
            
            If sDateRoot &amp;lt;= eDateCmp And sDateCmp &amp;lt;= eDateRoot Then
                lCntError = lCntError + 1
                shtThisVBA.Cells(lReportIndex, &quot;D&quot;) = &quot;Date Overlap&quot;
                shtThisVBA.Cells(lReportIndex, &quot;E&quot;) = strIdNameRoot
                shtThisVBA.Cells(lReportIndex, &quot;F&quot;) = i &amp;amp; &quot;, &quot; &amp;amp; j
                lReportIndex = lReportIndex + 1
                Debug.Print &quot;[HIT] strIdNameRoot: &quot; &amp;amp; strIdNameRoot &amp;amp; &quot;: overlap &quot; &amp;amp; i &amp;amp; &quot; with &quot;; j
            End If
            
        Next
        
    Next
    ' [END] Data validation
    
    ' Write Summary Report
   
    shtThisVBA.Cells(11, &quot;E&quot;) = strTimeStampStart
    shtThisVBA.Cells(12, &quot;E&quot;) = lCntError
    If lCntError Then
        shtThisVBA.Cells(12, &quot;E&quot;).Interior.ColorIndex = COLORINDEX_RED
    Else
        shtThisVBA.Cells(12, &quot;E&quot;).Interior.ColorIndex = COLORINDEX_GREEN
    End If
    
End Sub&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상으로 엑셀 VBA 매크로를 활용하여 날짜(DATE) 속성 데이터의 시작일, 종료일 간 중복 여부를 검출하는 코드를 작성해 보았습니다.&lt;/p&gt;</description>
      <category>프로그래밍/Excel VBA</category>
      <category>vba</category>
      <category>VBA DATE</category>
      <category>VBA 날짜 겹침</category>
      <category>날짜 중복</category>
      <category>날짜계산</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/52</guid>
      <comments>https://noyecube.tistory.com/entry/VBA-%EB%82%A0%EC%A7%9C-%EC%A4%91%EB%B3%B5-%EA%B2%80%EC%B6%9C%ED%95%98%EA%B8%B0DATE-OVERLAP#entry52comment</comments>
      <pubDate>Sun, 10 Dec 2023 17:01:16 +0900</pubDate>
    </item>
    <item>
      <title>Auto Lock/Unlock for Thread Syncronization</title>
      <link>https://noyecube.tistory.com/entry/Auto-LockUnlock-for-Thread-Syncronization</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;스레드 간 동기화를 위해서는 락 메커니즘을 활용합니다. 락을 획득한 상태에서 예외가 발생하면 락을 반환해야 하는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예외 케이스가 다양할 경우 각 예외 구문마다 락 반환코드를 넣는 것은 번거롭습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 지역변수의 스코프 내 유효 특성을 이용하여 함수 종료 시 획득한 락도 자동으로 반환되는 일명 Autolock class에 대해 예제기반으로 설명합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 커널모드, 유저모드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;락은 크게 커널모드 동작방식, 유저모드 동작방식으로 구분되며 커널모드의 대표적인 락은 뮤텍스이며 중첩락을 허용하는 특징이 있습니다. 유저모드의 대표적인 락은 크리티컬섹션입니다. 현업에서 뮤텍스, 크리티컬섹션의 사용 사례를 다양하게 접했지만 뮤텍스의 중첩을 활용하는 경우는 거의 보지 못하였습니다. 또한 Lock/Unlock이 빈번할 경우 커널모드 동작방식의 락이 퍼포먼스 측면에서 많이 불리하므로 커널모드 방식은 필요할 때만 사용하는 것이 적절합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. CriticalSection과 Slim Reader/Writer (SRW) Locks&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공유메모리 역할의 SharedArray가 있고 데이터를 기록하는 스레드를 WriterA, 데이터를 읽어가는 스레드가 ReaderB, ReaderC, ReaderD가 있다고 가정합니다. CriticalSection을 사용하면 ReaderB, ReaderC, ReaderD 간에도 경쟁이 발생하여 읽기 작업에서도 Serialized Section이 발생합니다. 이를 개선할 수 있는 방식이 Slim Reader/Writer Lock(이하 SRW 락)입니다. SRW 락은 유저모드로 동작하므로 스레드 간 공유자원에 사용할 수 있고 프로세스 간 공유자원에는 사용할 수 없습니다. 대신 속도가 빠르고 메모리 점유가 적은 장점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRW 락은 두 가지 모드를 제공합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Shared mode : reader thread들이 shared mode로 접근하면 모든 스레드가 동시에 접근할 수 있어 citical section보다 퍼포먼스가 향상됩니다.&lt;/li&gt;
&lt;li&gt;Exclusive mode : 이 모드에서는 읽기/쓰기를 동시에 수행합니다. Shared mode보다 우선이기 때문에 오직 한 스레드만 진입 가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. SRW Lock 함수&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AcquireSRWLockExclusive, ReleaseSRWLockExclusive&lt;/li&gt;
&lt;li&gt;AcquireSRWLockShared, ReleaseSRWLockShared&lt;/li&gt;
&lt;li&gt;InitializeSRWLock&lt;/li&gt;
&lt;li&gt;SleepConditionVariableSRW&lt;/li&gt;
&lt;li&gt;TryAcquireSRWLockExclusive&lt;/li&gt;
&lt;li&gt;TryAcquireSRWLockShared&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. SRW Lock 예제 코드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SRW Lock 사용을 위해서는 SRWLOCK 변수가 필요합니다. 공유 구조체의 경우 맨 보통 맨 위에 SRWLOCK 타입 변수를 선언하여 사용합니다. 구조체 생성자에서 InitializeSRWLock으로 SRWLOCK 변수를 초기화 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702188398495&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;typedef struct _DS_CONF_SERVER {
	SRWLOCK srwlock;
	WORD wPortNo;	// service port number
	WORD wProtocolVersionMajor;
	WORD wProtocolVersionMinor;
	WORD wProtocolVersionPatch;
	_DS_CONF_SERVER() {
		ZeroMemory(this, sizeof(struct _DS_CONF_SERVER));
		InitializeSRWLock(&amp;amp;srwlock);
	}
} TST_DS_CONF_SERVER, *PTST_DS_CONF_SERVER;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SharedLock 사용 코드입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702188599423&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;LPVOID CDiagnostics::fpLookupDiagHandlerbyTrCode(LPCTSTR lpstrTrCode)
{
	LPVOID lp;
	BOOL boRet;

	AcquireSRWLockShared(&amp;amp;(_m_stTrHandlerMap.srwlock));
	boRet = _m_stTrHandlerMap.map.Lookup(lpstrTrCode, lp);
	ReleaseSRWLockShared(&amp;amp;(_m_stTrHandlerMap.srwlock));

	if (FALSE == boRet)
		lp = nullptr;

	return lp;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. AutoScopeLock 필요성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 진입 후 특정 자료구조의 데이터가 정상인지 하나하나 체크한다고 가정할 때 각 if문에서 예외가 발생하면 예외문마다 ReleaseLock 함수를 호출해야 하는데 코딩 실수가 발생하여 버그를 생성할 수 있고 타이핑 노력이 많이 들어갑니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지역변수의 스코프 특성을 활용하여 아래와 같이 CAutoScopeSRWLOCKExclusive, CAutoScopeSRWLockShared를 만들어 쓰면 편리합니다. 함수 내 return 포인트가 여러 곳에 존재하지만 Lock 사용 코드는 함수 진입점에 한 번만 기술되어 있습니다.&amp;nbsp; lock 변수가 지역변수로 생성될 때 생성자가 락을 획득하고, 지역변수가 해제될 때 소멸자가 호출되면서 소멸자에서 락을 릴리스합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702189058947&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PTST_DSM_ITEM CDmssItem::pAlloc_PTST_DSM_ITEM_CHAIN(const WORD wQuantity)
{
...생략...

	CAutoScopeSRWLOCKExclusive lock(&amp;amp;(pPool-&amp;gt;srwlock));

	if (pPool-&amp;gt;wFreeItemsCnt &amp;lt; wQuantity) {	/* Dynamic allocation */
		DPRINTF(&quot;[WARN] pAlloc_PTST_DSM_ITEM_CHAIN wQuantity(%d), pPool-&amp;gt;wFreeItemsCnt(%d)\n&quot;, wQuantity, pPool-&amp;gt;wFreeItemsCnt);
		pHead = new TST_DSM_ITEM;
		pTail = pHead;
		if (nullptr == pTail) {
			return nullptr;
		}
		wAssigned = 1;

		while (wAssigned &amp;lt; wQuantity) {
			pTail-&amp;gt;pNext = new TST_DSM_ITEM;
			if (nullptr == pTail-&amp;gt;pNext) {
				break;
			}

			pTail = pTail-&amp;gt;pNext;
			wAssigned++;
		};

		if (wQuantity == wAssigned) {
			return pHead;	/* return dynamic allocated memory */
		}
		else {	/* Cancel dynamic allocation */
			while (pHead) {
				pTail = pHead-&amp;gt;pNext;
				delete pHead;
				pHead = pTail;
			}
		}

		return nullptr;
	}

...생략...
	return pHead;

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6.&amp;nbsp; CAutoScope class 코드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 헤더파일을 프로그램에 추가 후 사용하면 됩니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;6.1 CAutolock.h&lt;/h4&gt;
&lt;pre id=&quot;code_1702189211578&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

namespace
{
	const BYTE LOCKTYPE_NONE = 0;
	const BYTE LOCKTYPE_EXCLUSIVE = 1;
	const BYTE LOCKTYPE_SHARED = 2;
}

class CAutoScopeSRWLOCKShared
{
private:
	PSRWLOCK m_psrwlock;

protected:
	CAutoScopeSRWLOCKShared() { m_psrwlock = NULL; }

public:
	CAutoScopeSRWLOCKShared(PSRWLOCK psrwlock) { AcquireSRWLockShared(psrwlock); m_psrwlock = psrwlock; }
	~CAutoScopeSRWLOCKShared() { ReleaseSRWLockShared(m_psrwlock); m_psrwlock = NULL; }

	static void Init(PSRWLOCK psrwlock)	{ InitializeSRWLock(psrwlock); }

};

class CAutoScopeSRWLOCKExclusive
{
private:
	PSRWLOCK m_psrwlock;

protected:
	CAutoScopeSRWLOCKExclusive() { m_psrwlock = NULL; }

public:
	CAutoScopeSRWLOCKExclusive(PSRWLOCK psrwlock) { AcquireSRWLockExclusive(psrwlock); m_psrwlock = psrwlock; }
	~CAutoScopeSRWLOCKExclusive() { ReleaseSRWLockExclusive(m_psrwlock); m_psrwlock = NULL; }

	static void Init(PSRWLOCK psrwlock) { InitializeSRWLock(psrwlock); }

};&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상으로 지역변수의 스코프 특성을 활용하여 클래스 소멸자에서 자동으로 락을 해제할 수 있는 방법을 알아보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>프로그래밍/C | C++</category>
      <category>AcquireSRWLock</category>
      <category>Critical Section</category>
      <category>ReleaseSRWLock</category>
      <category>semaphore</category>
      <category>slim reader-writer lock</category>
      <category>SRWLock</category>
      <category>크리티컬섹션</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/51</guid>
      <comments>https://noyecube.tistory.com/entry/Auto-LockUnlock-for-Thread-Syncronization#entry51comment</comments>
      <pubDate>Sun, 10 Dec 2023 15:21:48 +0900</pubDate>
    </item>
    <item>
      <title>[MFC] MFC 스레드 예제</title>
      <link>https://noyecube.tistory.com/entry/MFC-MFC-%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%98%88%EC%A0%9C</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 윈도 MFC 환경에서 사용가능한 두 가지 타입의 스레드에 대해 알아보고 생성/동기화/종료 방법을 예제를 통해 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스 환경에서 thread를 사용해 보신 분들은 pthread_create와 같은 POSIX Thread 함수에 익숙하실 텐데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;윈도 MFC 환경에서는 두 가지 타입의 스레드가 존재하며 사용 목적과 프로그램 성격에 따라 타입을&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구분하여 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 스레드를 사용하는 이유&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램 실행 시 프로그램의 진입점인 main 함수를 실행하는 한 개의 메인 스레드가 생성됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메인 스레드만 사용하여 프로그램을 개발하려고 하면 다음과 같은 문제점들이 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 작업을 백그라운드화 하지 못해 작업 끝날 때까지 사용자와 상호작용 불가능(화면 멈춤)&lt;/li&gt;
&lt;li&gt;싱글 코어 사용으로 인한 퍼포먼스 저하 및 컴퓨팅 자원 낭비&lt;/li&gt;
&lt;li&gt;타이머 등 비동기 메커니즘 구현 불가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현실에 빗대어 키오스크가 고장 난 맥도널드로 간 상황을 설명해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카운터에서 캐셔(UI Thread)에게 햄버거 세트를 주문했는데 주문을 접수받은 캐셔가 주방으로 가서 햄버거도 만들고 감자도 튀기고 콜라도 따르는 상황을 가정해 봅시다. 그럼 내 뒤로 주문하러 방문한 사람은 캐셔가 주방에서 일하는 동안 대기하고 있어야 합니다. 주방에 별도로 일하는 조리사(Worker Thread)가 있다면 캐셔(UI Thread)는 카운터에서 주문을 받고 조리사(Worker Thread)가 완성하는 음식을 손님에게 내어주면 됩니다. 만약 주방에 조리가가 1명이 아니라 다수라면 음식이 나오는 속도는 더 빨라질 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 스레드를 활용한 병렬 처리 유의점과 한계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스레드를 사용하면 필연적으로 스레드 간 동기화 기법을 사용해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스레드 동기화 메커니즘에는 세마포어, 뮤텍스, 스핀락, Reader-Writer Lock 등이 있으며 각 메커니즘에는 퍼포먼스 차이와 함께 장단점이 존재하므로 적절한 메커니즘을 선택하여 사용해야 합니다. 동기화 설계가 잘못되면 스레드 간 서로의 락을 획득하기 위해 무한정 대기하게 되는 교착상태(데드락)에 빠질 수 있음에 유의해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 암달의 법칙(Amdal's Law)도 참고하며 스레드 프로그램을 개발하길 권장합니다. 암달의 법칙은 코어 수를 2배로 늘려도 처리 성능은 2배가 되지 못하고 20~40% 정도의 성능 손실이 발생한다는 법칙입니다. 아무리 병렬화를 한다고 해도 결국에는 직렬화 구간이 존재할 수밖에 없으며 이 때문에 한계가 존재한다는 법칙입니다. 이를 프로그램 개발과 연관 지어 생각하면 스레드 프로그램 개발 시 직렬화 구간에서는 시간이 오래 걸리는 작업을 최소화하고 병렬로 처리할 수 있는 부분은 최대한 병렬화 하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. User-Interface thread, Worker thread 비교&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MFC에는 두 가지 타입의 스레드, User-Interface thread(이하 UI thread)와 Worker thread, 를 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UI thread는 주로 사용자와의 입출력을 처리하는데 많이 쓰입니다. Worker thread는 사용자와의 입출력이 필요 없이 백그라운드에서 계산작업과 같은 역할이 필요할 때 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UI Thread는 CWinThread 클래스를 상속받아 구현하는데 Worker thread와의 차이점은 사용자로부터의 메시지 이벤트를 수신하는 메시지펌프가 기본적으로 구현되어 있습니다. 그래서 While문 등을 사용하여 별도의 메시지 처리 Loop를 구현하지 않고 이벤트 메시지와 이벤트 메시지 핸들러만 정의하여 메시지를 처리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 간략한 UI Thread 코드입니다. 메시지맵에 메시지와 핸들러를 등록해 두면 다른 스레드에서 PostThreadMessage() 호출을 통하여 UI Thread의 메시지핸들러를 호출할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트와 달리 메시지는 메시지큐에 쌓여서 순차처리 되므로 대량의 패킷 데이터 처리에는 메시지 처리 오버헤드도 더 발생하여 퍼포먼스에 악영향을 미칠 수 있으므로 권장하지 않습니다. 예를 들어 천 개의 패킷이 도착했으면 메시지도 천 개가 발생하여 쌓이게 됩니다. 이벤트로 처리할 경우에는 패킷이 도착했음을 한 번만 이벤트 설정하면 수신 스레드에서 Queue가 Empty 상태일 때까지 알아서 동작합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702132024033&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* .h */
class CMiscEventLoop : public CWinThread
{
	DECLARE_DYNCREATE(CMiscEventLoop)

public:
	CMiscEventLoop();           // protected constructor used by dynamic creation
	virtual ~CMiscEventLoop();

public:
	virtual BOOL InitInstance();
	virtual int ExitInstance();

protected:
	DECLARE_MESSAGE_MAP()
public:
	void OnMsgLocalDiagConsole(WPARAM wParam, LPARAM lParam);
};

/* .cpp */
BEGIN_MESSAGE_MAP(CMiscEventLoop, CWinThread)
	ON_THREAD_MESSAGE(MSG_ID_MISCEVENT_LOCAL_DIAG_CONSOLE, OnMsgLocalDiagConsole)
END_MESSAGE_MAP()


// CMiscEventLoop message handlers
void CMiscEventLoop::OnMsgLocalDiagConsole(WPARAM wParam, LPARAM lParam)
{
	DPRINTF(&quot;ENTER\n&quot;);
	theApp.m_Diag.vExec_LocalDiagCommandLine(wParam, lParam);
	DPRINTF(&quot;LEAVE\n&quot;);
}

/* view.cpp */
theApp.m_pMiscEventLoop-&amp;gt;PostThreadMessage(MSG_ID_MISCEVENT_LOCAL_DIAG_CONSOLE, (WPARAM)p, 0); /* WPARAM: TST_DIAG_REQUEST_ITEM, TODO: LPARAM:DESTINATION INFO STRUCTURE */&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 Worker thread 예제 코드입니다. Worker thread의 함수 원형은 static UINT 함수명(LPVOID)입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스 내부에 정의해도 static 타입이므로 클래스 인스턴스에 포함되지 않습니다. 그래서 스레드 생성 시 파라미터로 theApp 등 필요한 객체 정보를 전달해 주어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Worker thread 구현을 보면 while (TRUE) 구문으로 Loop를 구성하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예에서는 Worker thread도 PeekMessage 사용을 통해 UI thread처럼 메시지를 수신받고 처리할 수 있음을 보여줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 대기를 위해 사용할 수 있는 동기화 함수는 WaitForSingleObject(), WaitForMultipleObjects(), MsgWaitForMultipleObjectsEx(), GetQueuedCompletionStatus() 등이 있으며 목적에 따라 적절하게 선택하여 사용하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702132865537&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* .h */
class CRilServer
{
public:
	static UINT ThdRilServerMgr(LPVOID pParam);
};

/* .cpp */
UINT CRilServer::ThdRilServerMgr(LPVOID pParam)
{
	PTST_LPVOID_PARAMS pParams = (PTST_LPVOID_PARAMS)pParam;

	SetEvent((HANDLE)(pParams-&amp;gt;lpOpaque_3));	// send parameter copy completed signal to thread creator

	DWORD dwWaitRet;
	MSG msg;
	while (TRUE) {
		dwWaitRet = MsgWaitForMultipleObjectsEx(1, &amp;amp;hWSAEvent, RIL_SERVER_SOCKET_POOL_REFRESH_INTERVAL, QS_POSTMESSAGE, MWMO_INPUTAVAILABLE);

		if ((WAIT_OBJECT_0 + 0) == dwWaitRet) {	/* FD_ACCEPT event at hWSAEvent by hsoListen */
			WSANETWORKEVENTS ne;
			WSAEnumNetworkEvents(hsoListen, hWSAEvent, &amp;amp;ne);
			if (ne.lNetworkEvents &amp;amp; FD_ACCEPT) {

				int iRet;
				iRet = pthis-&amp;gt;m_RilSession.IncreaseAcceptSockets(hsoListen);
				DPRINTF(&quot;hsoListen event signaled, iRet(%d) PoolCnt(%Iu), SessionCnt(%Iu)\n&quot;, iRet, pthis-&amp;gt;m_RilSession.iGetCount_AcceptExPOOL(), pthis-&amp;gt;m_RilSession.iGetCount_ConnectedSessions());
			}
		} else if ((WAIT_OBJECT_0 + dwNCOUNT) == dwWaitRet) {	/* Thread Msg Event via PostThreadMessage Call */

			if (!PeekMessage(&amp;amp;msg, NULL, 0, 0, PM_REMOVE)) {
				continue;
			}

			if (MSG_ID_CRILSERVER_QUIT == msg.message) {
				RIL_LOG_INFO(&quot;MSG_ID_CRILSERVER_QUIT, ThdRilServerMgr Exit.&quot;);
				break;
			}

			PTST_RIL_SESSION_NODE prsn = (PTST_RIL_SESSION_NODE)msg.lParam;
			if (MSG_ID_CRILSEREVR_SOCK_CONNECTED == msg.message) {
				pthis-&amp;gt;m_RilSession.vRegisterSocketToSessionObj(prsn);
			}
			else if (MSG_ID_CRILSERVER_SOCK_DISCONNECTED == msg.message) {
				pthis-&amp;gt;m_RilSession.vUnregisterSocketFromSessionObj(hsoListen, prsn);
			}
		}
		else if (WAIT_TIMEOUT == dwWaitRet) {
			/* FIXME: Pool Optimization at Server System Idle, not user defined time interval fixed periodically */
			pthis-&amp;gt;m_RilSession.vOptimizeAcceptExSocketPool();
			continue;
		}
		else if (WAIT_FAILED == dwWaitRet) {
			RIL_LOG_INFO(&quot;MsgWaitForMultipleObjectsEx WAIT_FAILED(%d). Exit Thread Loop.&quot;, GetLastError());
			break;
		}
		else {
			RIL_LOG_ERROR(&quot;MsgWaitForMultipleObjectsEx return not expected value (%d)&quot;, dwWaitRet);
		}
	}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. UI thread 사용 방법&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.1 클래스마법사로 UI thread 코드 생성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스마법사에서 Add MFC Class 창을 띄워서 Base class를 CWinThread로 지정하여 생성하면 UI thread 코드를 작성할 수 있습니다. 메서드를 정의하고 해당 메서드와 메시지를 메시지 맵에 등록하면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;class_wizard.webp&quot; data-origin-width=&quot;1023&quot; data-origin-height=&quot;532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FNBtQ/btsBGm0oblV/uvLcGFKyCkas3dRwWJ3941/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FNBtQ/btsBGm0oblV/uvLcGFKyCkas3dRwWJ3941/img.webp&quot; data-alt=&quot;UI Thread 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FNBtQ/btsBGm0oblV/uvLcGFKyCkas3dRwWJ3941/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFNBtQ%2FbtsBGm0oblV%2FuvLcGFKyCkas3dRwWJ3941%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1023&quot; height=&quot;532&quot; data-filename=&quot;class_wizard.webp&quot; data-origin-width=&quot;1023&quot; data-origin-height=&quot;532&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;UI Thread 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.2 UI thread 인스턴스 생성 및 종료&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;App.cpp의 InitInstance()에서 생성한다고 가정한 예제 코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;헤더파일에 UI thread 헤더파일을 참조 후 UI thread 포인터 변수를 선언합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 cpp에서 객체 생성 후 CreateThread() 메서드를 호출하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UI thread 종료는 DestoryWindow()를 호출합니다. 종료 처리는 ExitInstance() 메서드에 구현합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702135300157&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* App.h */
#include &quot;CUiThread.h&quot;

class App : public CWinApp
{
public:
	App() noexcept;

	CUiThread* m_pCUiThread;
}

/* App.cpp */
BOOL App::InitInstance()
{
	/* Create UI thread */
	m_pCUiThread = new CUiThread();
	m_pCUiThread-&amp;gt;CreateThread();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;UI Thread Context에서는 Sleep(), WaitForSingleObject() 호출하지 않도록 합니다. 다른 UI Thread도 함께 더 상위 레벨에서 윈도우 메시지 처리가 블로킹되어 UI Thread간 병렬처리가 되지 않고 순차처리 됩니다.&lt;/u&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. Worker thread 사용 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;worker thread 함수를 작성하고 생성과 종료 방법을 알아보겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5.1 Worker thread 코드 작성 및 스레드 생성/종료&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;static UINT 함수명(LPVOID) 형식의 함수를 작성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LPVOID 타입의 파라미터로 넘길 정보가 여러개일 경우를 대비하여 별도의 구조체 타입을 아래와 같이 정의하여 사용하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702136216532&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;typedef struct _LPVOID_PARAMS {
	LPVOID lpOpaque_1;
	LPVOID lpOpaque_2;
	LPVOID lpOpaque_3;
	LPVOID lpOpaque_4;
	LPVOID lpOpaque_5;
	_LPVOID_PARAMS() {
		ZeroMemory(this, sizeof(struct _LPVOID_PARAMS));
	}
} TST_LPVOID_PARAMS, * PTST_LPVOID_PARAMS;	// LPVOID PARAMS TYPE for THREAD Creation&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부모 스레드는 자식 스레드 생성코드 호출 후 바로 다음 작업을 하는 것이 아니라 자식 스레드로부터 생성완료 이벤트를 기다렸다가 다시 자식 스레드에게 시작 이벤트를 전송 후 다음 작업으로 넘어가는 것이 정석적인 방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음의 Worker thread 생성 코드를 참조하세요. 예제 스레드 원형은 staic UINT ThdRilTx(LPVOID)입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부모와 자식 간 동기화를 위해서 임시 이벤트 핸들 Start, Created를 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부모와 자식간 동기화는 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;부모: AfxBeginThread 호출-&amp;gt;WaitForSingleObject(Created)-&amp;gt;SetEvent(Start)&lt;/li&gt;
&lt;li&gt;자식: SetEvent(Created)-&amp;gt;WaitForSingleObject(Start)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1702136600162&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CWinThread* _m_pThdRilTx;
noyecube_errno_t CRilServer::RilInit(unsigned short shPortNo)
{
....생략...
	/* Create TX Thread */
	HANDLE hEvent_Thread_Start;
	HANDLE hEvent_Thread_Created;

	hEvent_Thread_Start = CreateEvent(NULL, CREATE_EVENT_MANUAL_RESET, FALSE, _T(&quot;hEvent_Thread_Start_Ril&quot;));
	if (NULL == hEvent_Thread_Start) {
		AfxMessageBox(_T(&quot;Fail:NoyeCube:RilInit.hEvent_Thread_Start&quot;));
		this-&amp;gt;RilDeInit();
		return (-NOYECUBE_ECREATIONFAIL);
	}

	hEvent_Thread_Created = CreateEvent(NULL, CREATE_EVENT_MANUAL_RESET, FALSE, _T(&quot;hEvent_Thread_Created_Ril&quot;));
	if (NULL == hEvent_Thread_Created) {
		AfxMessageBox(_T(&quot;Fail:NoyeCube:RilInit.hEvent_Thread_Created&quot;));
		this-&amp;gt;RilDeInit();
		return (-NOYECUBE_ECREATIONFAIL);
	}

	this-&amp;gt;m_hEvent_Wakeup_Tx = CreateEvent(NULL, CREATE_EVENT_MANUAL_RESET, FALSE, _T(&quot;hEvent_Wakeup_Tx_Ril&quot;));
	if (NULL == (this-&amp;gt;m_hEvent_Wakeup_Tx)) {
		AfxMessageBox(_T(&quot;Fail:NoyeCube:ThdRilServerMgr.m_hEvent_Wakeup_Tx.&quot;));
		this-&amp;gt;RilDeInit();
		return (-NOYECUBE_ECREATIONFAIL);
	}

	this-&amp;gt;m_hEvent_Thread_Exit_Tx = CreateEvent(NULL, CREATE_EVENT_MANUAL_RESET, FALSE, _T(&quot;m_hEvent_Thread_Exit_Tx_Ril&quot;));
	if (NULL == (this-&amp;gt;m_hEvent_Thread_Exit_Tx)) {
		AfxMessageBox(_T(&quot;Fail:NoyeCube:ThdRilServerMgr.m_hEvent_Thread_Exit.&quot;));
		this-&amp;gt;RilDeInit();
		return (-NOYECUBE_ECREATIONFAIL);
	}

	TST_LPVOID_PARAMS st_lpParams;
	st_lpParams.lpOpaque_1 = (LPVOID)(&amp;amp;theApp);
	st_lpParams.lpOpaque_2 = (LPVOID)(&amp;amp;(theApp.m_RilSever));
	st_lpParams.lpOpaque_3 = (LPVOID)hEvent_Thread_Created;
	st_lpParams.lpOpaque_4 = (LPVOID)hEvent_Thread_Start;

	_m_pThdRilTx = AfxBeginThread(theApp.m_RilSever.ThdRilTx, (LPVOID)&amp;amp;st_lpParams);
	if (nullptr == _m_pThdRilTx) {
		AfxMessageBox(_T(&quot;AfxBeginThread ThdRilTx Error.\nQuit Program.&quot;));
		RIL_LOG_ERROR(&quot;AfxBeginThread ThdRilTx Error.&quot;);
		this-&amp;gt;RilDeInit();
		return (-NOYECUBE_ECREATIONFAIL);
	}
    
	if (NULL != hEvent_Thread_Created) {
		DPRINTF(&quot;WAITING ThdRilTx ...&quot;);
		WaitForSingleObject(hEvent_Thread_Created, INFINITE);
		ResetEvent(hEvent_Thread_Created);
		DPRINTF(&quot;Completed ThdRilTx.\n&quot;);
	}

	SetEvent(hEvent_Thread_Start);	/* Signal to Entering TX Thread Loop*/
	if (NULL != hEvent_Thread_Start)
		CloseHandle(hEvent_Thread_Start);

	if (NULL != hEvent_Thread_Created)
		CloseHandle(hEvent_Thread_Created);
...생략...
     return NOYECUBE_ESUCCESS;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Worker thread에서 SetEvent((HANDLE)(pParams-&amp;gt;lpOpaque_3)) 코드가 생성완료 이벤트를 부모에 전달하는 코드입니다. WaitForSingleObject((HANDLE) pParams-&amp;gt;lpOpaque_4)) 코드가 부모로부터 시작 명령을 대기하는 코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;while loop에서 WaitForMultipleObjects()로 이벤트 두 개를 사용하는데 그중 m_hEvent_Thread_Exit_Tx 이벤트가 발생하면 loop를 빠져나가고 스레드를 종료하도록 되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 이 Worker thread를 종료하려면&amp;nbsp;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;SetEvent( &lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;m_hEvent_Thread_Exit_Tx)를 호출하면 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1702137125217&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;UINT CRilServer::ThdRilTx(LPVOID pParam)	// tx thread
{
	PTST_LPVOID_PARAMS pParams = (PTST_LPVOID_PARAMS)pParam;
	PTST_DSM_HEADER_ITEM p;
	CHAR pktBuf[RIL_MSS_SIZE];
	UINT16 pktBufUsedLen = 0;

	const CNoyecubeApp* ptheApp = (CNoyecubeApp*)(pParams-&amp;gt;lpOpaque_1);
	CRilServer* pRilObj = (CRilServer*)(pParams-&amp;gt;lpOpaque_2);
	Concurrency::concurrent_queue&amp;lt;PTST_DSM_HEADER_ITEM&amp;gt;* pqDataTx = &amp;amp;(pRilObj-&amp;gt;m_qDataTx);

	BOOL boTryPopResult;
	HANDLE arrhEvents[2];
	DWORD dwEvent;
	int iRet;

	arrhEvents[0] = pRilObj-&amp;gt;m_hEvent_Wakeup_Tx;
	arrhEvents[1] = pRilObj-&amp;gt;m_hEvent_Thread_Exit_Tx;

	DPRINTF(&quot;Thread (TX TID:%d) created successfully.\n&quot;, GetCurrentThreadId());
	SetEvent((HANDLE)(pParams-&amp;gt;lpOpaque_3));	// send parameter copy completed signal to thread creator

	WaitForSingleObject((HANDLE)(pParams-&amp;gt;lpOpaque_4), INFINITE);
	DPRINTF(&quot;hEvent_Thread_Start triggered. Enter Loop.(TX TID:%d)\n&quot;, GetCurrentThreadId());

	ZeroMemory(pktBuf, sizeof(pktBuf));
	pktBufUsedLen = 0;

	while (TRUE) {
		ResetEvent(arrhEvents[0]);	/* m_hEvent_Wakeup_Tx */

		p = nullptr;
		boTryPopResult = pqDataTx-&amp;gt;try_pop(p);
		if (FALSE == boTryPopResult) {
			dwEvent = WaitForMultipleObjects(2, arrhEvents, FALSE, INFINITE);

			if ((WAIT_OBJECT_0 + 0) == dwEvent) {	// m_hEvent_Wakeup_Tx
				continue;
			}
			else if ((WAIT_OBJECT_0 + 1) == dwEvent) {	// m_hEvent_Thread_Exit
				DPRINTF(&quot;m_hEvent_Thread_Exit_Tx triggered. Exit Loop.(TX TID:%d)\n&quot;, GetCurrentThreadId());
				break;	// thread exit
			}
			else if (WAIT_FAILED == dwEvent) {
				RIL_LOG_ERROR(&quot;WaitForMultipleObjects WAIT_FAILED(%d) but Ignored.&quot;, GetLastError());
				continue;
			}
			else if (WAIT_TIMEOUT == dwEvent) {
				RIL_LOG_ERROR(&quot;WaitForMultipleObjects WAIT_TIMEOUT but not defined any action Ignored.&quot;);
				continue;
			}
			else {
				RIL_LOG_ERROR(&quot;WaitForMultipleObjects return (%d). not defined return value.&quot;, dwEvent);
				continue;
			}
		}
		else {	// TRUE == boTryPopResult
 ...생략...
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 Worker thread에게 종료이벤트를 전송하고 종료처리를 하는 예제코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CWinThread 타입의 _m_pThdRilTx가 nullptr이 아니면 현재 생성된 Worker thread를 가리키고 있는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 언급했듯이 SetEvent(m_hEvent_Thread_Exit_Tx)를 호출하면 Worker thread로 종료이벤트가 전달되고 loop를 탈출하며 thread가 종료됩니다. 스레드가 완전히 종료되었음은 WaitForSingleObject()로 스레드핸들에 발생하는 이벤트를 대기하고 있으면 됩니다. 사용한 이벤트 핸들 변수를 닫아주면 마무리됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702137745623&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;	if (nullptr != _m_pThdRilTx) {
		DPRINTF(&quot;[DBG]  ==&amp;gt; _m_pThdRilTx-&amp;gt;SetEvent(m_hEvent_Thread_Exit_Tx)\n&quot;);
		SetEvent(m_hEvent_Thread_Exit_Tx);
		DPRINTF(&quot;[DBG]  ==&amp;gt; Before WaitForSingleObject(_m_pThdRilTx-&amp;gt;m_hThread, INFINITE)...\n&quot;);
		WaitForSingleObject(_m_pThdRilTx-&amp;gt;m_hThread, INFINITE);
		DPRINTF(&quot;[DBG]  ==&amp;gt; After WaitForSingleObject(_m_pThdRilTx-&amp;gt;m_hThread, INFINITE)...\n&quot;);
		_m_pThdRilTx = nullptr;
		if (INVALID_HANDLE_VALUE != m_hEvent_Thread_Exit_Tx) {
			CloseHandle(m_hEvent_Thread_Exit_Tx);
			m_hEvent_Thread_Exit_Tx = INVALID_HANDLE_VALUE;
		}
		if (INVALID_HANDLE_VALUE != m_hEvent_Wakeup_Tx) {
			CloseHandle(m_hEvent_Wakeup_Tx);
			m_hEvent_Wakeup_Tx = INVALID_HANDLE_VALUE;
		}
	}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 윈도 MFC 환경에서 User Interface Thread와 Worker Thread의 차이점과 각 스레드 타입을&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 생성하고 종료하는 방법을 예제코드를 통해 알아보았습니다.&lt;/p&gt;</description>
      <category>프로그래밍/C | C++</category>
      <category>AfxBeginThread</category>
      <category>CWinthread</category>
      <category>mfc thread</category>
      <category>MFC 스레드</category>
      <category>SetEvent</category>
      <category>Thread</category>
      <category>UI thread</category>
      <category>worker thread</category>
      <category>스레드 종료</category>
      <category>작업스레드</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/50</guid>
      <comments>https://noyecube.tistory.com/entry/MFC-MFC-%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%98%88%EC%A0%9C#entry50comment</comments>
      <pubDate>Sun, 10 Dec 2023 01:06:49 +0900</pubDate>
    </item>
    <item>
      <title>SQL Developer 설치 및 기본 설정</title>
      <link>https://noyecube.tistory.com/entry/SQL-Developer-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EA%B8%B0%EB%B3%B8-%EC%84%A4%EC%A0%95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Oracle SQL Developer는 Oracle Database에서 SQL 작업을 위한&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무료 통합 개발환경(IDE)입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Oracle SQL Developer는 다음과 같은 기능을 제공합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SQL 쿼리 작성, 실행, 디버깅&lt;/li&gt;
&lt;li&gt;데이터베이스 개체 탐색 및 관리&lt;/li&gt;
&lt;li&gt;데이터베이스 모델링&lt;/li&gt;
&lt;li&gt;데이터 시각화&lt;/li&gt;
&lt;li&gt;PL/SQL, JAVA, Python 등과 같은 프로그래밍 언어 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. SQL Developer 다운로드 및 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 경로에서 실행환경에 맞는 버전을 다운로드합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;압축파일을 다운로드하면 압축을 풉니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;SQL Developer&quot; href=&quot;https://www.oracle.com/database/sqldeveloper/technologies/download/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.oracle.com/database/sqldeveloper/technologies/download/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1701877648031&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Oracle SQL Developer Downloads&quot; data-og-description=&quot;This archive. will work on a 32 or 64 bit Windows OS. The bit level of the JDK you install will determine if it runs as a 32 or 64 bit application. This download does not include the required Oracle Java JDK. You will need to install it if it's not already&quot; data-og-host=&quot;www.oracle.com&quot; data-og-source-url=&quot;https://www.oracle.com/database/sqldeveloper/technologies/download/&quot; data-og-url=&quot;https://www.oracle.com/database/sqldeveloper/technologies/download/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.oracle.com/database/sqldeveloper/technologies/download/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.oracle.com/database/sqldeveloper/technologies/download/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Oracle SQL Developer Downloads&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;This archive. will work on a 32 or 64 bit Windows OS. The bit level of the JDK you install will determine if it runs as a 32 or 64 bit application. This download does not include the required Oracle Java JDK. You will need to install it if it's not already&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.oracle.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;압축을 풀면 최상위 폴더 이름이 &quot;sqldevelper&quot;입니다. 설치형이 아닌 Portable이라고 보면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 압축 푼 폴더를 통째로 C:\Program Files 경로에 복사합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.webp&quot; data-origin-width=&quot;770&quot; data-origin-height=&quot;264&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dtjsvS/btsBKe2ix8v/N278cGguYfLbOgEMmvBc41/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dtjsvS/btsBKe2ix8v/N278cGguYfLbOgEMmvBc41/img.webp&quot; data-alt=&quot;Program Files 밑에 복사&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dtjsvS/btsBKe2ix8v/N278cGguYfLbOgEMmvBc41/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdtjsvS%2FbtsBKe2ix8v%2FN278cGguYfLbOgEMmvBc41%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;770&quot; height=&quot;264&quot; data-filename=&quot;1.webp&quot; data-origin-width=&quot;770&quot; data-origin-height=&quot;264&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Program Files 밑에 복사&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 접속 포트 설정 확인 및 Oracle 서비스 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Oracle DBMS의 리스너 기본 포트는 1521입니다. VirtualBox에서 NAT 네트워크 사용 시, 또는 기타 방화벽이 존재할 경우 1521번 포트를 허용합니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;168&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/diLSJM/btsBJhdEFFO/AZUI89RLdMIzNQOykk48NK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/diLSJM/btsBJhdEFFO/AZUI89RLdMIzNQOykk48NK/img.webp&quot; data-alt=&quot;VirtualBox에서의 1521 포트 포워딩 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/diLSJM/btsBJhdEFFO/AZUI89RLdMIzNQOykk48NK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdiLSJM%2FbtsBJhdEFFO%2FAZUI89RLdMIzNQOykk48NK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;168&quot; data-filename=&quot;2.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;168&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;VirtualBox에서의 1521 포트 포워딩 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스너 서비스가 정상인지 확인합니다. db 인스턴스에 대한 서비스가 확인되어야 합니다. SID로 지정한 db 명이 안 보이면 서비스 기동이 안된 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;478&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWxV0N/btsBQpvpqbL/arwMp90k49DDU2Zgdw4zOk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWxV0N/btsBQpvpqbL/arwMp90k49DDU2Zgdw4zOk/img.webp&quot; data-alt=&quot;db 리스너 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWxV0N/btsBQpvpqbL/arwMp90k49DDU2Zgdw4zOk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWxV0N%2FbtsBQpvpqbL%2FarwMp90k49DDU2Zgdw4zOk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;478&quot; data-filename=&quot;3.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;478&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;db 리스너 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 기동이 필요하면 SQLPLUS 콘솔에서 startup을 실행합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.jpg&quot; data-origin-width=&quot;553&quot; data-origin-height=&quot;448&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cOlt0c/btsBQP8rvIH/jMXrzGhzMRYTXOkybE3pg0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cOlt0c/btsBQP8rvIH/jMXrzGhzMRYTXOkybE3pg0/img.jpg&quot; data-alt=&quot;startup 실행&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cOlt0c/btsBQP8rvIH/jMXrzGhzMRYTXOkybE3pg0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcOlt0c%2FbtsBQP8rvIH%2FjMXrzGhzMRYTXOkybE3pg0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;553&quot; height=&quot;448&quot; data-filename=&quot;4.jpg&quot; data-origin-width=&quot;553&quot; data-origin-height=&quot;448&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;startup 실행&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. SQL Developer 설정&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;433&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8ozDH/btsBKhkpRiZ/V6yUJQK4OpIxuVqWpak4Ak/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8ozDH/btsBKhkpRiZ/V6yUJQK4OpIxuVqWpak4Ak/img.webp&quot; data-alt=&quot;접속 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8ozDH/btsBKhkpRiZ/V6yUJQK4OpIxuVqWpak4Ak/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8ozDH%2FbtsBKhkpRiZ%2FV6yUJQK4OpIxuVqWpak4Ak%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;433&quot; data-filename=&quot;5.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;433&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;접속 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 이름에 &quot;sys&quot;와 패스워드를 입력하고 SID를 정확하게 입력 후 &quot;테스트&quot; 버튼을 눌러서 상태:성공을 확인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ORA-12505 에러 발생 시 SID 문제이므로 Oracle 서비스 확인 절차를 다시 수행하기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접속을 눌러 DB 서버에 접속합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접속에 성공하면 화면 최적화를 합니다. 좌측의 접속, 보고서 창을 접어서 작게 만들어 줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6.jpg&quot; data-origin-width=&quot;248&quot; data-origin-height=&quot;277&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGOyZe/btsBQsZWLBi/4Pk2Ku7koPxNuUd0SFvNBk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGOyZe/btsBQsZWLBi/4Pk2Ku7koPxNuUd0SFvNBk/img.jpg&quot; data-alt=&quot;빨간 박스 클릭&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGOyZe/btsBQsZWLBi/4Pk2Ku7koPxNuUd0SFvNBk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGOyZe%2FbtsBQsZWLBi%2F4Pk2Ku7koPxNuUd0SFvNBk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;248&quot; height=&quot;277&quot; data-filename=&quot;6.jpg&quot; data-origin-width=&quot;248&quot; data-origin-height=&quot;277&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;빨간 박스 클릭&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQL 쿼리를 작성하여 현재 접속 중인 계정을 확인해 봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿼리를 작성 후 해당 쿼리 라인에 커서를 두고 F5를 누르면 스크립트 출력창에 결과가 표기되고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;F9를 누르면 질의 결과창에 엑셀 양식으로 결과가 표기됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;7.webp&quot; data-origin-width=&quot;616&quot; data-origin-height=&quot;516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1DB6g/btsBRRkENTm/0OS6TaKE02vK8s9I3R5iYk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1DB6g/btsBRRkENTm/0OS6TaKE02vK8s9I3R5iYk/img.webp&quot; data-alt=&quot;SELECT USER FROM DUAL&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1DB6g/btsBRRkENTm/0OS6TaKE02vK8s9I3R5iYk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1DB6g%2FbtsBRRkENTm%2F0OS6TaKE02vK8s9I3R5iYk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;616&quot; height=&quot;516&quot; data-filename=&quot;7.webp&quot; data-origin-width=&quot;616&quot; data-origin-height=&quot;516&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SELECT USER FROM DUAL&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날짜형식을 바꿔줍니다. 기본설정은 아래와 같이 RR/MM/DD입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도구-환경설정-데이터베이스-NLS-날짜형식에 가서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존값 RR/MM/DD를 변경값 YYYY/MM/DD HH24:MI:SS로 적용해 줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;8.webp&quot; data-origin-width=&quot;495&quot; data-origin-height=&quot;183&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dck7tN/btsBQqnygWf/O5YhCCFaize6VEpykTXzb0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dck7tN/btsBQqnygWf/O5YhCCFaize6VEpykTXzb0/img.webp&quot; data-alt=&quot;날짜형식 RR/MM/DD&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dck7tN/btsBQqnygWf/O5YhCCFaize6VEpykTXzb0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdck7tN%2FbtsBQqnygWf%2FO5YhCCFaize6VEpykTXzb0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;495&quot; height=&quot;183&quot; data-filename=&quot;8.webp&quot; data-origin-width=&quot;495&quot; data-origin-height=&quot;183&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;날짜형식 RR/MM/DD&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.webp&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;194&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wArK2/btsBQTXhUFq/LrfqD3KzmD9h1hrtOKxYtK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wArK2/btsBQTXhUFq/LrfqD3KzmD9h1hrtOKxYtK/img.webp&quot; data-alt=&quot;날짜형식 YYYY/MM/DD HH24:MI:SS&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wArK2/btsBQTXhUFq/LrfqD3KzmD9h1hrtOKxYtK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwArK2%2FbtsBQTXhUFq%2FLrfqD3KzmD9h1hrtOKxYtK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;194&quot; data-filename=&quot;9.webp&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;194&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;날짜형식 YYYY/MM/DD HH24:MI:SS&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면을 다크모드로 변경합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도구-환경설정-코드 편집기-PL/SQL 구문 색상-구성표-Twilight&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;행번호를 표시합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도구-환경설정-코드 편집기-행 여백-행 번호 표시에 체크&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인출행 50을 200으로 변경&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQL 쿼리시 한 번에 50줄까지만 출력이 기본값인데 최대 가능값 200으로 바꿔줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도구-환경설정-데이터베이스-고급-SQL 배열 인출 크기를 200으로 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DBA창을 띄우고 인스턴스 뷰어를 띄우면 DB 성능 모니터링이 가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;10.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;459&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clBmrm/btsBQnK6GkT/N9o99NvNK4CHBhAp3tayWk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clBmrm/btsBQnK6GkT/N9o99NvNK4CHBhAp3tayWk/img.webp&quot; data-alt=&quot;인스턴스 뷰어 화면 일부&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clBmrm/btsBQnK6GkT/N9o99NvNK4CHBhAp3tayWk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclBmrm%2FbtsBQnK6GkT%2FN9o99NvNK4CHBhAp3tayWk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;459&quot; data-filename=&quot;10.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;459&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;인스턴스 뷰어 화면 일부&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상입니다.&lt;/p&gt;</description>
      <category>Database/Oracle</category>
      <category>sql developer</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/49</guid>
      <comments>https://noyecube.tistory.com/entry/SQL-Developer-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EA%B8%B0%EB%B3%B8-%EC%84%A4%EC%A0%95#entry49comment</comments>
      <pubDate>Fri, 8 Dec 2023 02:01:19 +0900</pubDate>
    </item>
    <item>
      <title>oracle OPatch Release Update(RU) Patch 적용</title>
      <link>https://noyecube.tistory.com/entry/oracle-OPatch-Release-UpdateRU-Patch-%EC%A0%81%EC%9A%A9</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 이전 포스팅에서 oracle 19c를 설치해 보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blog.noyecube.com/entry/linux%EC%97%90-oracle-19c-%EC%84%A4%EC%B9%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2023.12.06 - [Database/Oracle] - linux에 oracle 19c 설치&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1702294325264&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;linux에 oracle 19c 설치&quot; data-og-description=&quot;Oracle 19c는 Oracle에서 출시한 관계형 데이터베이스 관리 시스템(RDBMS)으로 2018년 12월 18일에 출시되었습니다. 이 글을 작성하는 현재(2023.12.) Oracle 19c 상세 버전은 아래와 같습니다. 구분 설치파일&quot; data-og-host=&quot;blog.noyecube.com&quot; data-og-source-url=&quot;https://blog.noyecube.com/entry/linux%EC%97%90-oracle-19c-%EC%84%A4%EC%B9%98&quot; data-og-url=&quot;https://blog.noyecube.com/entry/linux%EC%97%90-oracle-19c-%EC%84%A4%EC%B9%98&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/qQMIQ/hyULSkOmLa/m5CN0FMkS4w9Bqj9iIPuCK/img.png?width=800&amp;amp;height=148&amp;amp;face=0_0_800_148,https://scrap.kakaocdn.net/dn/hRIPn/hyULWnc2hi/wBvfpI7BZFcop0l2Yt8hq1/img.png?width=800&amp;amp;height=148&amp;amp;face=0_0_800_148,https://scrap.kakaocdn.net/dn/GWAMn/hyUIvdIWB3/HABFHElc1Frq7MX2Wda7HK/img.jpg?width=940&amp;amp;height=442&amp;amp;face=0_0_940_442&quot;&gt;&lt;a href=&quot;https://blog.noyecube.com/entry/linux%EC%97%90-oracle-19c-%EC%84%A4%EC%B9%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://blog.noyecube.com/entry/linux%EC%97%90-oracle-19c-%EC%84%A4%EC%B9%98&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/qQMIQ/hyULSkOmLa/m5CN0FMkS4w9Bqj9iIPuCK/img.png?width=800&amp;amp;height=148&amp;amp;face=0_0_800_148,https://scrap.kakaocdn.net/dn/hRIPn/hyULWnc2hi/wBvfpI7BZFcop0l2Yt8hq1/img.png?width=800&amp;amp;height=148&amp;amp;face=0_0_800_148,https://scrap.kakaocdn.net/dn/GWAMn/hyUIvdIWB3/HABFHElc1Frq7MX2Wda7HK/img.jpg?width=940&amp;amp;height=442&amp;amp;face=0_0_940_442');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;linux에 oracle 19c 설치&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Oracle 19c는 Oracle에서 출시한 관계형 데이터베이스 관리 시스템(RDBMS)으로 2018년 12월 18일에 출시되었습니다. 이 글을 작성하는 현재(2023.12.) Oracle 19c 상세 버전은 아래와 같습니다. 구분 설치파일&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;blog.noyecube.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 설치된 oracle 19c 버전은 19.3.0.0입니다. 현시점(2023.12.)의 oracle 19c 버전은 19.21.0.0입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 단순 학습목적이 아닌 실 사용을 위해서는 각종 버그 및 취약점이 존재 하는 19.3을 19.21로 버전 업데이트 할 필요가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 oracle19c 19.3 버전을 19.21로 업데이트하는 방법을 알아봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 설치 파일은 LINUX.X64_193000_db_home.zip 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 Release Update 파일은 현시점 기준으로 p35643107_190000_Linux-x86-64.zip입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일을 적용하기 위해서는 OPatch 유틸리티도 12.2.0.1.37 이상의 버전으로 업데이트해야 합니다.(기본 설치 시 OPatch 버전은 12.2.0.1.17입니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 OPatch 설치 파일명은 p6880880_190000_Linux-x86-64.zip입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 OPatch (p6880880_190000_Linux-x86-64.zip) 적용 후 Release Update(p35643107_190000_Linux-x86-64.zip)를 진행하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. OPatch&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 명령어로 기존 OPatch 버전을 확인해 봅니다. 12.2.0.1.17로 나옵니다. 버전 업데이트가 필요합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701863106532&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ $ORACLE_HOME/OPatch/opatch version
OPatch Version: 12.2.0.1.17
OPatch succeeded.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 OPatch를 백업합니다. 단순히 폴더명을 변경해 주면 됩니다. OPatch 폴더가 존재하는 경로에서 아래 명령어를 실행.&lt;/p&gt;
&lt;pre id=&quot;code_1701863183247&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ mv ./OPatch ./OPatch_231206&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 OPatch를 기존 OPatch 폴더 위치에 압축을 풀어 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저의 경우 VirtualBox 환경에서 호스트와의 공유폴더가 /media/sf_VmShare 경로에 마운트 되어 있음을 참고해 주세요.&lt;/p&gt;
&lt;pre id=&quot;code_1701863240815&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ unzip /media/sf_VmShare/p6880880_190000_Linux-x86-64.zip -d $ORACLE_HOME/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것으로 OPatch 업데이트가 끝났습니다. 변경된 OPatch 버전을 조회해 봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 12.2.0.1.17에서 12.2.0.1.40으로 변경되었습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701875709348&quot; class=&quot;vbnet&quot; data-ke-language=&quot;vbnet&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ORACLE_HOME/OPatch/opatch version
OPatch Version: 12.2.0.1.40&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Release Update Patch&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Oracle 엔진 백업을 합니다. 경로는 $ORACLE_HOME/../입니다. 저의 경우 tar.gz으로 압축하였습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701875843851&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tar cvfz oracle_home_20231207.tar.gz ./19.3.0/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패치 충돌검사를 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;p35643107_190000_Linux-x86-64.zip 파일 압축을 풀면 35643107 폴더가 있고 그 안에 패치 파일들이 존재합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701875944110&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ORACLE_HOME/OPatch/opatch prereq CheckConflictAgainstOHWithDetail -oh $ORACLE_HOME -phBaseDir /media/sf_VmShare/p35643107_190000_Linux-x86-64/35643107/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;passed 메시지를 확인하면 충돌이 없다는 뜻입니다. 정상적인 충돌검사 완료 메시지를 참고하십시오.&lt;/p&gt;
&lt;pre id=&quot;code_1701876049519&quot; class=&quot;vbnet&quot; data-ke-language=&quot;vbnet&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Oracle Interim Patch Installer version 12.2.0.1.40
Copyright (c) 2023, Oracle Corporation.  All rights reserved.

PREREQ session

Oracle Home       : /oracle/app/oracle/product/19.3.0
Central Inventory : /oracle/app/oraInventory
   from           : /oracle/app/oracle/product/19.3.0/oraInst.loc
OPatch version    : 12.2.0.1.40
OUI version       : 12.2.0.7.0
Log file location : /oracle/app/oracle/product/19.3.0/cfgtoollogs/opatch/opatch2023-12-06_13-58-20PM_1.log

Invoking prereq &quot;checkconflictagainstohwithdetail&quot;

Prereq &quot;checkConflictAgainstOHWithDetail&quot; passed.

OPatch succeeded.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB 업데이트 필요 공간을 확인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/tmp 경로에 txt 파일을 생성하고 거기에 패치파일 경로를 작성해 둡니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701876130861&quot; class=&quot;vbnet&quot; data-ke-language=&quot;vbnet&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cat /tmp/patch_list_db.txt
/media/sf_VmShare/p35643107_190000_Linux-x86-64/35643107/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 OPatch CheckSystemSpace 명령을 수행합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701876176196&quot; class=&quot;vbnet&quot; data-ke-language=&quot;vbnet&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ORACLE_HOME/OPatch/opatch prereq CheckSystemSpace -oh $ORACLE_HOME -phBaseFile /tmp/patch_list_db.txt
Oracle Interim Patch Installer version 12.2.0.1.40
Copyright (c) 2023, Oracle Corporation.  All rights reserved.

PREREQ session

Oracle Home       : /oracle/app/oracle/product/19.3.0
Central Inventory : /oracle/app/oraInventory
   from           : /oracle/app/oracle/product/19.3.0/oraInst.loc
OPatch version    : 12.2.0.1.40
OUI version       : 12.2.0.7.0
Log file location : /oracle/app/oracle/product/19.3.0/cfgtoollogs/opatch/opatch2023-12-06_14-03-29PM_1.log

Invoking prereq &quot;checksystemspace&quot;

Prereq &quot;checkSystemSpace&quot; passed.

OPatch succeeded.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 패치 파일경로로 이동하며 opatch apply 명령을 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그전에 리스너와 DB 인스턴스를 stop, shutdown 해두어야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701876232289&quot; class=&quot;vbnet&quot; data-ke-language=&quot;vbnet&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[oracle@noyeora p35643107_190000_Linux-x86-64]$ cd 35643107/
[oracle@noyeora 35643107]$ pwd
/media/sf_VmShare/p35643107_190000_Linux-x86-64/35643107
[oracle@noyeora 35643107]$ $ORACLE_HOME/OPatch/opatch apply
Oracle Interim Patch Installer version 12.2.0.1.40
Copyright (c) 2023, Oracle Corporation.  All rights reserved.


Oracle Home       : /oracle/app/oracle/product/19.3.0
Central Inventory : /oracle/app/oraInventory
   from           : /oracle/app/oracle/product/19.3.0/oraInst.loc
OPatch version    : 12.2.0.1.40
OUI version       : 12.2.0.7.0
Log file location : /oracle/app/oracle/product/19.3.0/cfgtoollogs/opatch/opatch2023-12-06_14-21-50PM_1.log

Verifying environment and performing prerequisite checks...
OPatch continues with these patches:   35643107

Do you want to proceed? [y|n]

Please shutdown Oracle instances running out of this ORACLE_HOME on the local system.
(Oracle Home = '/oracle/app/oracle/product/19.3.0')


Is the local system ready for patching? [y|n]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Release Update Patch가 정상적으로 완료되면 아래 메시지 확인 가능합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701876268488&quot; class=&quot;vbnet&quot; data-ke-language=&quot;vbnet&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Patch 35643107 successfully applied.
Sub-set patch [29517242] has become inactive due to the application of a super-set patch [35643107].
Please refer to Doc ID 2161861.1 for any possible further required actions.
Log file location: /oracle/app/oracle/product/19.3.0/cfgtoollogs/opatch/opatch2023-12-06_14-21-50PM_1.log

OPatch succeeded.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Patch 결과 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 명령어로 패치 버전을 확인할 수 있습니다. 패치 전 버전 19.3에서 패치 후 버전 19.21을 확인할 수 있습니다. OCW의 경우 싱글 인스턴스 DB 구성에서는 사용하지 않기 때문에 패치하지 않았습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701876371375&quot; class=&quot;vbnet&quot; data-ke-language=&quot;vbnet&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ORACLE_HOME/OPatch/opatch lspatches -oh $ORACLE_HOME
35643107;Database Release Update : 19.21.0.0.231017 (35643107)
29585399;OCW RELEASE UPDATE 19.3.0.0.0 (29585399)

OPatch succeeded.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상으로 Oracle 19c에 OPatch, Release Update를 진행해 보았습니다.&lt;/p&gt;</description>
      <category>Database/Oracle</category>
      <category>lspatches</category>
      <category>Opatch</category>
      <category>RU Patch</category>
      <category>오라클 업데이트</category>
      <category>오라클 패치</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/48</guid>
      <comments>https://noyecube.tistory.com/entry/oracle-OPatch-Release-UpdateRU-Patch-%EC%A0%81%EC%9A%A9#entry48comment</comments>
      <pubDate>Thu, 7 Dec 2023 00:28:01 +0900</pubDate>
    </item>
    <item>
      <title>linux에 oracle 19c 설치</title>
      <link>https://noyecube.tistory.com/entry/linux%EC%97%90-oracle-19c-%EC%84%A4%EC%B9%98</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Oracle 19c는 Oracle에서 출시한 관계형 데이터베이스 관리 시스템(RDBMS)으로 2018년 12월 18일에 출시되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글을 작성하는 현재(2023.12.) Oracle 19c 상세 버전은 아래와 같습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;설치파일명&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;버전&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;기본 설치 파일&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;LINUX.X64_193000_db_home.zip&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;19.3.0.0.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;OPatch 파일&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;p6880880_190000_Linux-x86-64.zip&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;12.2.0.1.40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;Release Update 파일&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;p35643107_190000_Linux-x86-64.zip&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;19.21.0.0.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 설치 파일의 버전과 현재 Release Update 파일의 버전 차이가 많이 납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기 릴리즈 버전 19.3은 상당한 양의 버그, 취약점 등을 포함하고 있으므로 단순 학습목적이 아니면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Release Update까지 진행 후 사용하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본 설치에서는 oracle linux 버전 8.9를 기준으로 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OPatch는 오라클 패치 관리 유틸리티로서 기본 설치 파일 19.3에 포함되어 있는 OPatch의 버전은 12.2.0.1.17입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 Release Update 19.21 적용을 위해서는 OPatch version 12.2.0.1.37 이상의 버전이 필요하다고 되어 있으므로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OPatch 업데이트 진행 후 RU(Release Update) Patch를 진행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;oracle 19c 설치에 앞서 이전 포스팅의 환경설정 적용이 필요하므로 환경설정이 안 되어 있으면 아래 글의 내용을 먼저 적용하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blog.noyecube.com/entry/oracle-linux-oracle-19c-%EC%84%A4%EC%B9%98%ED%99%98%EA%B2%BD-%EC%84%A4%EC%A0%95&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2023.11.29 - [Database/Oracle] - oracle linux oracle 19c 설치환경 설정&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. LINUX.X64_193000_db_home.zip 압축 해제 및 실행&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINUX.X64_193000_db_home.zip 파일은 구글에 &quot; LINUX.X64_193000_db_home.zip&quot; 문구로 검색하면 오라클 다운로드 링크를 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;oracle&quot; 계정으로 전환 후 $ORACLE_HOME 위치에 LINUX.X64_193000_db_home.zip 파일 압축을 풀어 줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.jpg&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;175&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciwUQP/btsBQV8GkYa/0LU5QaCuOlxFkh2vfpuIbK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciwUQP/btsBQV8GkYa/0LU5QaCuOlxFkh2vfpuIbK/img.jpg&quot; data-alt=&quot;$ORACLE_HOME에 압축 해제&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciwUQP/btsBQV8GkYa/0LU5QaCuOlxFkh2vfpuIbK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FciwUQP%2FbtsBQV8GkYa%2F0LU5QaCuOlxFkh2vfpuIbK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;175&quot; data-filename=&quot;1.jpg&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;175&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;$ORACLE_HOME에 압축 해제&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 아래 명령어로 Oracle Installer를 실행합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701854315544&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export CV_ASSUME_DISTID=OEL7
./runInstaller&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CV_ASSUME_DISTID는 oracle linux 버전을 7로 가정하는 환경변수입니다. 이 변수를 설정하지 않으면 Installer에서 다음과 같은 경고 메시지가 발생합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701854828202&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[INS-08101] Unexpected error while executing the action at state: 'supportedOSCheck'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 xdisplay 설정 문제로 GUI 창 안 뜨면 아래 명령어 다시 진행해 보세요.&lt;/p&gt;
&lt;pre id=&quot;code_1702271939808&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#  root 계정으로 진행
echo $DISPLAY
:0
# :0 이 아니면 :0 으로 바꾸세요.
xhost +
# access control disabled, clients can connect from any host
su - oracle
$export DISPLAY=:0.0 # 버추얼박스와 같은 NAT 환경일 경우
$export DISPLAY=192.168.x.x:0.0 # HOST PC를 통해서 oracle 설치 서버 접속이면 호스트IP
# oracle 계정에서 xhost 같은 명령어 실행하지 마세요.
$./runInstaller&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 Oracle Directory 구조 잠깐 보고 갈게요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ORACLE_BASE = /oracle/app/oracle입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701857129407&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/oracle/app/oracle --- /admin
                       /audit
                       /cfgtoollogs
                       /diag
                       /oradata
                       /product/19.3.0($ORACLE_HOME)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Configuration Option&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Oracle 19c Installer의 설치 옵션 항목은 총 18개입니다. 하나씩 살펴보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글씨체 Bold 적용이 이번 설치 진행 시 선택된 옵션입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Select any of the following install options (Step 1 of 18) : 싱글 인스턴스로 설치 선택
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;Create and configure a single instance database. This&amp;nbsp;option&amp;nbsp;create&amp;nbsp;a&amp;nbsp;start&amp;nbsp;database.&lt;/b&gt;&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;Set&amp;nbsp;Up&amp;nbsp;Software&amp;nbsp;Only&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Select System Class (Step 2 of 18) : 서버 클래스 선택
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Desktop&amp;nbsp;class&lt;br /&gt;Choose this option if you are installing on a laptop or desktop class system. &lt;br /&gt;This&amp;nbsp;option&amp;nbsp;includes&amp;nbsp;a&amp;nbsp;starter&amp;nbsp;database&amp;nbsp;and&amp;nbsp;allows&amp;nbsp;minimal&amp;nbsp;configuration.&lt;/li&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;Server class&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;Choose&amp;nbsp;this&amp;nbsp;option&amp;nbsp;if&amp;nbsp;you&amp;nbsp;are&amp;nbsp;installing&amp;nbsp;on&amp;nbsp;a&amp;nbsp;server&amp;nbsp;class&amp;nbsp;system,&amp;nbsp;which&amp;nbsp;Oracle&amp;nbsp;defines&amp;nbsp;as&amp;nbsp;a&amp;nbsp;system&amp;nbsp;used&amp;nbsp;in&amp;nbsp;a&amp;nbsp;production&amp;nbsp;data&amp;nbsp;center.&amp;nbsp;This&amp;nbsp;option&amp;nbsp;allows&amp;nbsp;for&amp;nbsp;more&amp;nbsp;advanced&amp;nbsp;configuration&amp;nbsp;options.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Select Database Edition (Step 3 of 18) : 엔터프라이즈 에디션 선택
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;Enterprise&amp;nbsp;Edition&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;Oracle&amp;nbsp;Database&amp;nbsp;19c&amp;nbsp;Enterprise&amp;nbsp;Edition&amp;nbsp;is&amp;nbsp;a&amp;nbsp;self-managing&amp;nbsp;database&amp;nbsp;that&amp;nbsp;has&amp;nbsp;the&amp;nbsp;scalability,&amp;nbsp;performance,&amp;nbsp;high&amp;nbsp;availability,&amp;nbsp;and&amp;nbsp;security&amp;nbsp;features&amp;nbsp;required&amp;nbsp;to&amp;nbsp;run&amp;nbsp;the&amp;nbsp;most&amp;nbsp;demanding,&amp;nbsp;mission-critical&amp;nbsp;applications.&lt;/li&gt;
&lt;li&gt;Standard&amp;nbsp;Edition&amp;nbsp;2&lt;br /&gt;Oracle&amp;nbsp;Database&amp;nbsp;19c&amp;nbsp;Standard&amp;nbsp;Edition&amp;nbsp;2&amp;nbsp;is&amp;nbsp;a&amp;nbsp;full-featured&amp;nbsp;data&amp;nbsp;management&amp;nbsp;solution&amp;nbsp;ideally&amp;nbsp;suited&amp;nbsp;to&amp;nbsp;the&amp;nbsp;needs&amp;nbsp;of&amp;nbsp;medium-sized&amp;nbsp;businesses.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Specify Installation Location(Step 4 of 18) : $ORACLE_BASE, $ORACLE_HOME 경로 설정 확인&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Specify&amp;nbsp;a&amp;nbsp;path&amp;nbsp;to&amp;nbsp;place&amp;nbsp;all&amp;nbsp;Oracle&amp;nbsp;software&amp;nbsp;and&amp;nbsp;configuration-related&amp;nbsp;files&amp;nbsp;installed&amp;nbsp;by&amp;nbsp;this&amp;nbsp;installation&amp;nbsp;owner.&amp;nbsp;This&amp;nbsp;location&amp;nbsp;is&amp;nbsp;the&amp;nbsp;Oracle&amp;nbsp;base&amp;nbsp;directory&amp;nbsp;for&amp;nbsp;the&amp;nbsp;installation&amp;nbsp;owner.&lt;br /&gt;&lt;u&gt;&lt;b&gt;Oracle&amp;nbsp;base:&amp;nbsp;/oracle/app/oracle&lt;/b&gt;&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;This&amp;nbsp;software&amp;nbsp;directory&amp;nbsp;is&amp;nbsp;the&amp;nbsp;Oracle&amp;nbsp;Database&amp;nbsp;home&amp;nbsp;directory.&lt;br /&gt;&lt;u&gt;&lt;b&gt;Software&amp;nbsp;location:&amp;nbsp;/oracle/app/oracle/product/19.3.0&lt;/b&gt;&lt;/u&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Create Inventory(Step 5 of 18) : 로그 저장 경로 설정 확인 및 접근 계정 그룹 &quot;dba&quot; 설정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;You&amp;nbsp;are&amp;nbsp;starting&amp;nbsp;your&amp;nbsp;first&amp;nbsp;installation&amp;nbsp;on&amp;nbsp;this&amp;nbsp;host.&amp;nbsp;Specify&amp;nbsp;a&amp;nbsp;directory&amp;nbsp;for&amp;nbsp;installation&amp;nbsp;metadata&amp;nbsp;files&amp;nbsp;(for&amp;nbsp;example,&amp;nbsp;install&amp;nbsp;log&amp;nbsp;files).&amp;nbsp;This&amp;nbsp;directory&amp;nbsp;is&amp;nbsp;called&amp;nbsp;the&amp;nbsp;&amp;ldquo;inventory&amp;nbsp;directory&amp;rdquo;.&amp;nbsp;The&amp;nbsp;installer&amp;nbsp;automatically&amp;nbsp;sets&amp;nbsp;up&amp;nbsp;subdirectories&amp;nbsp;for&amp;nbsp;each&amp;nbsp;product&amp;nbsp;contain&amp;nbsp;inventory&amp;nbsp;data.&amp;nbsp;The&amp;nbsp;subdirectory&amp;nbsp;for&amp;nbsp;each&amp;nbsp;product&amp;nbsp;typically&amp;nbsp;requires&amp;nbsp;150&amp;nbsp;kilobytes&amp;nbsp;of&amp;nbsp;disk&amp;nbsp;space.&lt;br /&gt;&lt;u&gt;&lt;b&gt;Inventory&amp;nbsp;Directory:&amp;nbsp;/oracle/app/oraInventory&lt;/b&gt;&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;Specify&amp;nbsp;on&amp;nbsp;operating&amp;nbsp;system&amp;nbsp;group&amp;nbsp;whose&amp;nbsp;members&amp;nbsp;have&amp;nbsp;write&amp;nbsp;permission&amp;nbsp;to&amp;nbsp;the&amp;nbsp;inventory&amp;nbsp;directory&amp;nbsp;(oraInventory)&lt;br /&gt;&lt;u&gt;&lt;b&gt;oraInventory&amp;nbsp;Group&amp;nbsp;Name:&amp;nbsp;dba&lt;/b&gt;&lt;/u&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Select Configuration Type (Step 6 of 18) : 데이터 분석 목적이라 Data Warehousing 선택. 일반목적은 General&amp;nbsp;&lt;br /&gt;Select&amp;nbsp;the&amp;nbsp;type&amp;nbsp;of&amp;nbsp;database&amp;nbsp;that&amp;nbsp;you&amp;nbsp;want&amp;nbsp;to&amp;nbsp;create&lt;br /&gt;Note:The preconfigured database types differ only in the values specified for some initialization parameters. The same data files are created and used for each type, and their disk space requirements are the same.&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;General&amp;nbsp;Purpose&amp;nbsp;/&amp;nbsp;Transaction&amp;nbsp;Processing&lt;br /&gt;A&amp;nbsp;starter&amp;nbsp;database&amp;nbsp;designed&amp;nbsp;for&amp;nbsp;general&amp;nbsp;purpose&amp;nbsp;use,&amp;nbsp;or&amp;nbsp;for&amp;nbsp;transaction-heavy&amp;nbsp;applications.&lt;/li&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;Data&amp;nbsp;Warehousing&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;A&amp;nbsp;starter&amp;nbsp;database&amp;nbsp;optimized&amp;nbsp;for&amp;nbsp;data&amp;nbsp;warehousing&amp;nbsp;applications.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Specify&amp;nbsp;Database&amp;nbsp;Identifiers&amp;nbsp;(Step&amp;nbsp;7&amp;nbsp;of&amp;nbsp;18)&lt;br /&gt;Provide&amp;nbsp;the&amp;nbsp;identifier&amp;nbsp;information&amp;nbsp;required&amp;nbsp;to&amp;nbsp;access&amp;nbsp;the&amp;nbsp;database&amp;nbsp;uniquely.&amp;nbsp;An&amp;nbsp;Oracle&amp;nbsp;database&amp;nbsp;is&amp;nbsp;uniquely&amp;nbsp;identified&amp;nbsp;by&amp;nbsp;a&amp;nbsp;Global&amp;nbsp;database&amp;nbsp;name,&amp;nbsp;typically&amp;nbsp;of&amp;nbsp;the&amp;nbsp;form&amp;nbsp;&amp;ldquo;name.domain&amp;rdquo;.&amp;nbsp;A&amp;nbsp;database&amp;nbsp;is&amp;nbsp;referenced&amp;nbsp;by&amp;nbsp;at&amp;nbsp;least&amp;nbsp;one&amp;nbsp;Oracle&amp;nbsp;instance&amp;nbsp;which&amp;nbsp;is&amp;nbsp;uniquely&amp;nbsp;identified&amp;nbsp;from&amp;nbsp;any&amp;nbsp;other&amp;nbsp;instance&amp;nbsp;on&amp;nbsp;this&amp;nbsp;computer&amp;nbsp;by&amp;nbsp;an&amp;nbsp;Oracle&amp;nbsp;system&amp;nbsp;identifier(SID).
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;Global database name:&amp;nbsp;&lt;/b&gt;&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;Oracle&amp;nbsp;system&amp;nbsp;identifier&amp;nbsp;(SID):&lt;/b&gt;&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;Create as Container database : &lt;u&gt;&lt;b&gt;no&lt;/b&gt;&lt;/u&gt; (멀티태넌트 필요 없음)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Specify&amp;nbsp;Configuration&amp;nbsp;Options&amp;nbsp;(Step&amp;nbsp;8&amp;nbsp;of&amp;nbsp;18)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Enable Automatic Memory Management : &lt;u&gt;&lt;b&gt;yes&lt;/b&gt;&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;Character sets : &lt;u&gt;&lt;b&gt;KO16MSWIN949&amp;nbsp;&amp;ndash;&amp;nbsp;MS&amp;nbsp;Windows&amp;nbsp;Code&amp;nbsp;Page&amp;nbsp;949&amp;nbsp;Korean&lt;/b&gt;&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;Sample schemas : &lt;u&gt;&lt;b&gt;no&lt;/b&gt;&lt;/u&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Specify&amp;nbsp;Database&amp;nbsp;Storage&amp;nbsp;Options(Step&amp;nbsp;9&amp;nbsp;of&amp;nbsp;18)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;File&amp;nbsp;system&lt;br /&gt;&lt;/b&gt;&lt;/u&gt;Specify&amp;nbsp;database&amp;nbsp;file&amp;nbsp;location:&lt;b&gt;&lt;/b&gt;&lt;u&gt;&lt;b&gt;&amp;nbsp;/oracle/app/oracle/oradata&lt;br /&gt;&lt;/b&gt;&lt;/u&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Specify&amp;nbsp;Recovery&amp;nbsp;Options(Step&amp;nbsp;11&amp;nbsp;of&amp;nbsp;18)&lt;br /&gt;Enable&amp;nbsp;or&amp;nbsp;disable&amp;nbsp;recovery&amp;nbsp;for&amp;nbsp;your&amp;nbsp;database.&amp;nbsp;If&amp;nbsp;you&amp;nbsp;choose&amp;nbsp;to&amp;nbsp;enable&amp;nbsp;recovery,&amp;nbsp;then&amp;nbsp;the&amp;nbsp;location&amp;nbsp;specified&amp;nbsp;will&amp;nbsp;be&amp;nbsp;used&amp;nbsp;as&amp;nbsp;recovery&amp;nbsp;area&amp;nbsp;storage.&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Enable Recovery : &lt;u&gt;&lt;b&gt;no&lt;br /&gt;&lt;/b&gt;&lt;/u&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Specify&amp;nbsp;Schema&amp;nbsp;Passwords(Step&amp;nbsp;12&amp;nbsp;of&amp;nbsp;18)&lt;br /&gt;The&amp;nbsp;starter&amp;nbsp;database&amp;nbsp;contains&amp;nbsp;pre-loaded&amp;nbsp;schemas,&amp;nbsp;most&amp;nbsp;of&amp;nbsp;which&amp;nbsp;have&amp;nbsp;passwords&amp;nbsp;that&amp;nbsp;are&amp;nbsp;expired&amp;nbsp;and&amp;nbsp;locked&amp;nbsp;at&amp;nbsp;the&amp;nbsp;end&amp;nbsp;of&amp;nbsp;installation.&amp;nbsp;After&amp;nbsp;installation&amp;nbsp;is&amp;nbsp;complete,&amp;nbsp;you&amp;nbsp;must&amp;nbsp;unlock&amp;nbsp;and&amp;nbsp;set&amp;nbsp;new&amp;nbsp;passwords&amp;nbsp;for&amp;nbsp;those&amp;nbsp;accounts&amp;nbsp;you&amp;nbsp;want&amp;nbsp;to&amp;nbsp;use.&amp;nbsp;Schemas&amp;nbsp;used&amp;nbsp;for&amp;nbsp;database&amp;nbsp;management&amp;nbsp;and&amp;nbsp;postinstallation&amp;nbsp;functions&amp;nbsp;are&amp;nbsp;left&amp;nbsp;unlocked,&amp;nbsp;and&amp;nbsp;passwords&amp;nbsp;for&amp;nbsp;these&amp;nbsp;accounts&amp;nbsp;will&amp;nbsp;not&amp;nbsp;expire.&amp;nbsp;Specify&amp;nbsp;the&amp;nbsp;passwords&amp;nbsp;for&amp;nbsp;these&amp;nbsp;accounts.&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Use the same password for all accounts : &lt;u&gt;&lt;b&gt;yes&lt;/b&gt;&lt;/u&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Privileged Operating System groups(step 13 of 18) : 모든 그룹권한을 &quot;dba&quot;로 설&lt;br /&gt;SYS&amp;nbsp;privileges&amp;nbsp;are&amp;nbsp;required&amp;nbsp;to&amp;nbsp;create&amp;nbsp;a&amp;nbsp;database&amp;nbsp;using&amp;nbsp;operating&amp;nbsp;system(OS)&amp;nbsp;authentication.&amp;nbsp;Membership&amp;nbsp;in&amp;nbsp;OS&amp;nbsp;Groups&amp;nbsp;grants&amp;nbsp;the&amp;nbsp;corresponding&amp;nbsp;SYS&amp;nbsp;privilege,&amp;nbsp;eg.&amp;nbsp;membership&amp;nbsp;in&amp;nbsp;OSDBA&amp;nbsp;grants&amp;nbsp;the&amp;nbsp;SYSDBA&amp;nbsp;privilege.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Database&amp;nbsp;Administrator&amp;nbsp;group:&amp;nbsp;dba&lt;/li&gt;
&lt;li&gt;Database&amp;nbsp;Operator&amp;nbsp;group:&amp;nbsp;dba &lt;br /&gt;Database&amp;nbsp;Backup&amp;nbsp;and&amp;nbsp;Recovery&amp;nbsp;Group:&amp;nbsp;dba &lt;br /&gt;Data&amp;nbsp;Guard&amp;nbsp;administrative&amp;nbsp;group:&amp;nbsp;dba &lt;br /&gt;Encryption&amp;nbsp;Key&amp;nbsp;management&amp;nbsp;administrative&amp;nbsp;group:&amp;nbsp;dba &lt;br /&gt;Real&amp;nbsp;Application&amp;nbsp;Cluster&amp;nbsp;administrative&amp;nbsp;group:&amp;nbsp;dba&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Root script execution configuration(Step 14 of 18) : root 계정 패스워드를 입력하거나 별도 콘솔창에서 root로 안내되는 명령어를 수행&lt;br /&gt;During&amp;nbsp;the&amp;nbsp;software&amp;nbsp;configuration,&amp;nbsp;certain&amp;nbsp;operations&amp;nbsp;have&amp;nbsp;to&amp;nbsp;be&amp;nbsp;performed&amp;nbsp;as&amp;nbsp;&amp;ldquo;root&amp;rdquo;&amp;nbsp;user.&amp;nbsp;You&amp;nbsp;can&amp;nbsp;choose&amp;nbsp;to&amp;nbsp;have&amp;nbsp;the&amp;nbsp;installer&amp;nbsp;perform&amp;nbsp;these&amp;nbsp;operations&amp;nbsp;automatically&amp;nbsp;by&amp;nbsp;specifying&amp;nbsp;inputs&amp;nbsp;for&amp;nbsp;one&amp;nbsp;of&amp;nbsp;the&amp;nbsp;options&amp;nbsp;below.&amp;nbsp;The&amp;nbsp;input&amp;nbsp;specified&amp;nbsp;will&amp;nbsp;also&amp;nbsp;be&amp;nbsp;used&amp;nbsp;by&amp;nbsp;the&amp;nbsp;installer&amp;nbsp;to&amp;nbsp;perform&amp;nbsp;additional&amp;nbsp;prerequisite&amp;nbsp;checks.&lt;/li&gt;
&lt;li&gt;설치가 완료된 화면 확인&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.jpg&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;442&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZNwZF/btsBHRfcXul/191903jm3DMyVYAIF8i6uK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZNwZF/btsBHRfcXul/191903jm3DMyVYAIF8i6uK/img.jpg&quot; data-alt=&quot;설치 완료 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZNwZF/btsBHRfcXul/191903jm3DMyVYAIF8i6uK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZNwZF%2FbtsBHRfcXul%2F191903jm3DMyVYAIF8i6uK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;442&quot; data-filename=&quot;2.jpg&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;442&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;설치 완료 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치 상세버전은 아래 명령어로 확인합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701861110807&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ORACLE_HOME/OPatch/opatch lspatches&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스너 상태 확인은 아래 명령어로 확인합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701861143444&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;netstat -plnt | grep tns&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모니터링 서비스가 5500 포트에서 제공되고 있는데 해당 서비스를 비활성화합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Oracle SQL에서 DUAL&amp;nbsp; 테이블은 단 하나의 행과 단 하나의 칼럼을 가진 가상의 테이블로서 DUAL 테이블은 데이터베이스에 저장되지 않고 오직 SELECT 문에서만 사용됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701861209647&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SQL&amp;gt; select dbms_xdb_config.gethttpsport() from dual;

DBMS_XDB_CONFIG.GETHTTPSPORT()
------------------------------
                          5500

SQL&amp;gt; exec dbms_xdb_config.sethttpsport(0);

PL/SQL procedure successfully completed.

SQL&amp;gt;  select dbms_xdb_config.gethttpsport() from dual;

DBMS_XDB_CONFIG.GETHTTPSPORT()
------------------------------
                             0

SQL&amp;gt; exit&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스너 컨트롤 옵션입니다. 문법과 옵션은 다음과 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701861290473&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$lsnrctl [옵션]&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;start: 리스너를 시작합니다.&lt;/li&gt;
&lt;li&gt;stop: 리스너를 중지합니다.&lt;/li&gt;
&lt;li&gt;status: 리스너의 상태를 표시합니다.&lt;/li&gt;
&lt;li&gt;config: 리스너의 구성을 표시합니다.&lt;/li&gt;
&lt;li&gt;log: 리스너의 로그를 표시합니다.&lt;/li&gt;
&lt;li&gt;trace: 리스너의 트레이스를 생성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;database 시작은 sqlplus에서 &quot;startup&quot;을 입력합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 database 종료는 &quot;shutdown&quot;이며 옵션이 존재합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701861395685&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ shutdown [옵션]&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;normal: 모든 세션을 종료하기 전에 모든 트랜잭션을 완료할 수 있도록 시간을 줍니다.&lt;/li&gt;
&lt;li&gt;immediate: 모든 세션을 즉시 종료합니다.&lt;/li&gt;
&lt;li&gt;transactional: 모든 트랜잭션을 완료하기 전에 모든 새 세션을 차단합니다.&lt;/li&gt;
&lt;li&gt;abort: 모든 세션을 강제로 종료합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스너와 DB엔진 start/stop 순서는 상관없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Oracle DB 설정 파라미터 값은 아래 파일에서 확인할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701997304922&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/etc/sysctl.d/99-sysctl.conf
/etc/security/limits.d/oracle-database-preinstall-19c.conf&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상으로 oracle 19c 설치과정을 알아보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 OPatch, Release Update 적용 방법을 알아보겠습니다.&lt;/p&gt;</description>
      <category>Database/Oracle</category>
      <category>KO16MSWIN949</category>
      <category>lsnrctl</category>
      <category>oracle 19c</category>
      <category>Oracle Linux</category>
      <category>sqlplus</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/47</guid>
      <comments>https://noyecube.tistory.com/entry/linux%EC%97%90-oracle-19c-%EC%84%A4%EC%B9%98#entry47comment</comments>
      <pubDate>Wed, 6 Dec 2023 20:19:30 +0900</pubDate>
    </item>
    <item>
      <title>[VBA] 엑셀 VBA 사용법</title>
      <link>https://noyecube.tistory.com/entry/VBA-%EC%97%91%EC%85%80-VBA-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;엑셀 VBA?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엑셀 VBA는 엑셀 매크로를 작성하기 위한 프로그래밍 언어입니다. VBA를 사용하면 매크로에 더 복잡한 기능을 추가할 수 있습니다. 엑셀의 기본 기능으로는 구현하기 어려운 작업을 수행하고자 할 때, 반복적인 작업을 자동화하여 시간을 절약하고 업무 효율을 높이고자 할 때 엑셀 VBA 추천합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;엑셀 개발도구 활성화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엑셀에서 VBA를 사용하기 위해서는 개발도구 메뉴를 활성화해야 아래 그림처럼 해당 기능 버튼이 활성화 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 엑셀에서 VBA를 사용하기 위한 개발도구 메뉴를 활성화 하는 방법을 알아보겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;menu.webp&quot; data-origin-width=&quot;675&quot; data-origin-height=&quot;157&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t1tvw/btsBDQVEijE/bgZscU5YjnnIe7m1S6PHBk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t1tvw/btsBDQVEijE/bgZscU5YjnnIe7m1S6PHBk/img.webp&quot; data-alt=&quot;엑셀 개발도구 메뉴&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t1tvw/btsBDQVEijE/bgZscU5YjnnIe7m1S6PHBk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft1tvw%2FbtsBDQVEijE%2FbgZscU5YjnnIe7m1S6PHBk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;675&quot; height=&quot;157&quot; data-filename=&quot;menu.webp&quot; data-origin-width=&quot;675&quot; data-origin-height=&quot;157&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;엑셀 개발도구 메뉴&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1단계. VBA 기능 추가하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 경로의 메뉴에 진입하여 &quot;분석 도구&quot;, &quot;분석 도구 - VBA&quot;를 체크하고 확인을 누릅니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701435421247&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;파일-&amp;gt;옵션-&amp;gt;추가기능-&amp;gt;이동 버튼 클릭 후 추가 기능 팝업창에서 추가 기능 추가&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;excel option.webp&quot; data-origin-width=&quot;551&quot; data-origin-height=&quot;784&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/B4pSN/btsBJeN6Qla/UpbqWdwKbvPsokwyf409I1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/B4pSN/btsBJeN6Qla/UpbqWdwKbvPsokwyf409I1/img.webp&quot; data-alt=&quot;Excel Option&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/B4pSN/btsBJeN6Qla/UpbqWdwKbvPsokwyf409I1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FB4pSN%2FbtsBJeN6Qla%2FUpbqWdwKbvPsokwyf409I1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;551&quot; height=&quot;784&quot; data-filename=&quot;excel option.webp&quot; data-origin-width=&quot;551&quot; data-origin-height=&quot;784&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Excel Option&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;extrafunc.webp&quot; data-origin-width=&quot;357&quot; data-origin-height=&quot;461&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dTLvTT/btsBC14drul/QU9h1GAZfRSMAmPZ9xVVfK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dTLvTT/btsBC14drul/QU9h1GAZfRSMAmPZ9xVVfK/img.webp&quot; data-alt=&quot;추가기능 선택&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dTLvTT/btsBC14drul/QU9h1GAZfRSMAmPZ9xVVfK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdTLvTT%2FbtsBC14drul%2FQU9h1GAZfRSMAmPZ9xVVfK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;357&quot; height=&quot;461&quot; data-filename=&quot;extrafunc.webp&quot; data-origin-width=&quot;357&quot; data-origin-height=&quot;461&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;추가기능 선택&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2단계. 메뉴에 개발 도구 추가하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 경로의 메뉴에 진입하여 &quot;개발 도구&quot;를 체크하고 확인을 누릅니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701435684342&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;파일-&amp;gt;옵션-&amp;gt;리본 사용자 지정-&amp;gt;개발 도구에 체크 후 확인&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ribbon.webp&quot; data-origin-width=&quot;1087&quot; data-origin-height=&quot;233&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNHSIH/btsBMV1MARp/8IL74FSG0hRewkH5TdGrZK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNHSIH/btsBMV1MARp/8IL74FSG0hRewkH5TdGrZK/img.webp&quot; data-alt=&quot;리본 사용자 지정-개발 도구&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNHSIH/btsBMV1MARp/8IL74FSG0hRewkH5TdGrZK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNHSIH%2FbtsBMV1MARp%2F8IL74FSG0hRewkH5TdGrZK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1087&quot; height=&quot;233&quot; data-filename=&quot;ribbon.webp&quot; data-origin-width=&quot;1087&quot; data-origin-height=&quot;233&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;리본 사용자 지정-개발 도구&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3단계. 편집창 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;편집창 옵션에서 &quot;자동 구문 검사&quot; 옵션을 해제해야 합니다. 그렇지 않으면 오타가 발생할 때마다 경고 팝업이 발생해서 정상적인 타이핑이 불가능 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 디버깅 메시지를 확인할 수 있는 &quot;직접 실행&quot;창과 디버깅시 각 변수 저장값을 확인할 수 있는 &quot;지역&quot;창을 띄웁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발도구-&amp;gt;Visual Basic 버튼을 눌러 편집기를 띄웁니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;dev.webp&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;144&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bthP6m/btsBHph2hyp/jHc4fJSyDCEgXuB91oGYVk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bthP6m/btsBHph2hyp/jHc4fJSyDCEgXuB91oGYVk/img.webp&quot; data-alt=&quot;개발도구-Visual Basic&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bthP6m/btsBHph2hyp/jHc4fJSyDCEgXuB91oGYVk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbthP6m%2FbtsBHph2hyp%2FjHc4fJSyDCEgXuB91oGYVk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;688&quot; height=&quot;144&quot; data-filename=&quot;dev.webp&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;144&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;개발도구-Visual Basic&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;editor.webp&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;256&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmmQeU/btsBFg7IsBK/MgHjiF7aPCTWnMCDOxZkLk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmmQeU/btsBFg7IsBK/MgHjiF7aPCTWnMCDOxZkLk/img.webp&quot; data-alt=&quot;편집기의 도구-옵션 창에서 &amp;quot;자동 구문 검사&amp;quot; 체크 해제&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmmQeU/btsBFg7IsBK/MgHjiF7aPCTWnMCDOxZkLk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmmQeU%2FbtsBFg7IsBK%2FMgHjiF7aPCTWnMCDOxZkLk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;890&quot; height=&quot;256&quot; data-filename=&quot;editor.webp&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;256&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;편집기의 도구-옵션 창에서 &quot;자동 구문 검사&quot; 체크 해제&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;exec.webp&quot; data-origin-width=&quot;458&quot; data-origin-height=&quot;257&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfIoOg/btsBOgY3dtt/UUreI0WRjBLcMZSHRh84oK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfIoOg/btsBOgY3dtt/UUreI0WRjBLcMZSHRh84oK/img.webp&quot; data-alt=&quot;보기-직접실행창, 지역창 클릭&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfIoOg/btsBOgY3dtt/UUreI0WRjBLcMZSHRh84oK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfIoOg%2FbtsBOgY3dtt%2FUUreI0WRjBLcMZSHRh84oK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;458&quot; height=&quot;257&quot; data-filename=&quot;exec.webp&quot; data-origin-width=&quot;458&quot; data-origin-height=&quot;257&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;보기-직접실행창, 지역창 클릭&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 내용까지 따라했으면 편집창은 아래와 같은 모습입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;final.webp&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;483&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0M1Ev/btsBJgynLKV/Hmkd8Xj5OZJl84oJ2t4KEK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0M1Ev/btsBJgynLKV/Hmkd8Xj5OZJl84oJ2t4KEK/img.webp&quot; data-alt=&quot;편집창 설정 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0M1Ev/btsBJgynLKV/Hmkd8Xj5OZJl84oJ2t4KEK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0M1Ev%2FbtsBJgynLKV%2FHmkd8Xj5OZJl84oJ2t4KEK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1500&quot; height=&quot;483&quot; data-filename=&quot;final.webp&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;483&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;편집창 설정 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4단계. xlsx 파일 포맷을 .xlsm 파일 포맷으로 변경 저장&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 확장자가 .xlsx인 Excel 통합 문서 양식에서는 VBA 매크로를 작성할 수 없습니다. 다른 이름으로 저장하기 창을 띄워서 파일 확장자가 .xlsm인 Excel 매크로 사용 통합 문서로 변경 저장합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5단계. 테스트 매크로 작성 및 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 매크로 코드를 작성하고 결과를 확인해 봅니다. 매크로 동작 버튼을 만들고 버튼을 클릭하면 Debug.Print와 MsgBox를 활용하여 메시지 출력을 해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엑셀 메뉴 삽입-도형에서 원하는 도형을 워크시트에 삽입하여 아래와 같이 클릭할 수 있게 만들어 둡니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;button.webp&quot; data-origin-width=&quot;216&quot; data-origin-height=&quot;186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o1S9x/btsBGX0pnSa/XZ27n3CD4PkEGBxAPV4KA1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o1S9x/btsBGX0pnSa/XZ27n3CD4PkEGBxAPV4KA1/img.webp&quot; data-alt=&quot;삽입-도형에서 삽입한 도형&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o1S9x/btsBGX0pnSa/XZ27n3CD4PkEGBxAPV4KA1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo1S9x%2FbtsBGX0pnSa%2FXZ27n3CD4PkEGBxAPV4KA1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;216&quot; height=&quot;186&quot; data-filename=&quot;button.webp&quot; data-origin-width=&quot;216&quot; data-origin-height=&quot;186&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;삽입-도형에서 삽입한 도형&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 그 옆에 대충 아래와 유사하게 샘플 데이터를 입력합니다. 이번 예제에서는 Option Value를 변수로 읽어와서 Debug.Print와 MsgBox를 활용하여 출력해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아 그리고 워크시트명은 &quot;vba&quot;로 해둡니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;testsample.webp&quot; data-origin-width=&quot;1376&quot; data-origin-height=&quot;294&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mD9sG/btsBCZZGqMq/Jxcpg1xTTPZTZwwkmuB460/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mD9sG/btsBCZZGqMq/Jxcpg1xTTPZTZwwkmuB460/img.webp&quot; data-alt=&quot;테스트 샘플 페이지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mD9sG/btsBCZZGqMq/Jxcpg1xTTPZTZwwkmuB460/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmD9sG%2FbtsBCZZGqMq%2FJxcpg1xTTPZTZwwkmuB460%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1376&quot; height=&quot;294&quot; data-filename=&quot;testsample.webp&quot; data-origin-width=&quot;1376&quot; data-origin-height=&quot;294&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;테스트 샘플 페이지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버튼역할을 할 도형을 우클릭하여 &quot;매크로 지정&quot; 창을 띄웁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매크로 지정창의 매크로 이름란에 &quot;test&quot;라고 입력 후 &quot;새로 만들기&quot;를 클릭합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;selectmacro.webp&quot; data-origin-width=&quot;725&quot; data-origin-height=&quot;391&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAlclT/btsBGyfxlz8/gxZmmyKhtWFlIn0U1nlYl0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAlclT/btsBGyfxlz8/gxZmmyKhtWFlIn0U1nlYl0/img.webp&quot; data-alt=&quot;test 매크로 새로 만들기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAlclT/btsBGyfxlz8/gxZmmyKhtWFlIn0U1nlYl0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAlclT%2FbtsBGyfxlz8%2FgxZmmyKhtWFlIn0U1nlYl0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;725&quot; height=&quot;391&quot; data-filename=&quot;selectmacro.webp&quot; data-origin-width=&quot;725&quot; data-origin-height=&quot;391&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;test 매크로 새로 만들기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 아래와 같이 VBA 창이 뜰겁니다. Sub test() ~ End Sub 사이에 테스크 코드를 작성할 겁니다. 테스트코드를 복사해서 붙여 넣어 보세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 들여쓰기는 Tab 버튼으로 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;sub.webp&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;292&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dsxiUN/btsBF3Umhul/lUfOVjsjeJlT7Qj3UyIUmk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dsxiUN/btsBF3Umhul/lUfOVjsjeJlT7Qj3UyIUmk/img.webp&quot; data-alt=&quot;Sub test() 코드 작성 창&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dsxiUN/btsBF3Umhul/lUfOVjsjeJlT7Qj3UyIUmk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdsxiUN%2FbtsBF3Umhul%2FlUfOVjsjeJlT7Qj3UyIUmk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1300&quot; height=&quot;292&quot; data-filename=&quot;sub.webp&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;292&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Sub test() 코드 작성 창&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1701437908545&quot; class=&quot;vbnet&quot; data-ke-language=&quot;vbnet&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    Dim shtThisVBA As Worksheet
    Set shtThisVBA = ThisWorkbook.Worksheets(&quot;vba&quot;)
    
    Dim strTargetFilePath As String
    Dim strTargetFileName As String
    Dim strTargetWorksheetName As String
    Dim lEntryRowNo As Long

    strTargetFilePath = shtThisVBA.Range(&quot;E4&quot;).Value
    strTargetFileName = shtThisVBA.Range(&quot;E5&quot;).Value
    strTargetWorksheetName = shtThisVBA.Range(&quot;E6&quot;).Value
    lEntryRowNo = shtThisVBA.Range(&quot;E7&quot;).Value
    
    Dim strTargetFullPath As String
    strTargetFullPath = strTargetFilePath + &quot;\&quot; + strTargetFileName
    
    Debug.Print &quot;[INFO] strTargetFilePath: &quot; &amp;amp; strTargetFilePath
    Debug.Print &quot;[INFO] strTargetFileName: &quot; &amp;amp; strTargetFileName
    Debug.Print &quot;[INFO] strTargetWorksheetName: &quot; &amp;amp; strTargetWorksheetName
    Debug.Print &quot;[INFO] strTargetFullPath: &quot; &amp;amp; strTargetFullPath
    
    MsgBox &quot;[INFO] lEntryRowNo: &quot; &amp;amp; vbNewLine &amp;amp; _
            lEntryRowNo, vbCritical&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드 입력 후 실행-Sub/사용자 정의 폼 실행 또는 F5를 누르시면 매크로가 실행되고 결과는 아래와 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;resultinfo.webp&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;272&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oVksH/btsBJdn7Hxh/K4xCo4LM9Az8z55VADk3h0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oVksH/btsBJdn7Hxh/K4xCo4LM9Az8z55VADk3h0/img.webp&quot; data-alt=&quot;실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oVksH/btsBJdn7Hxh/K4xCo4LM9Az8z55VADk3h0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoVksH%2FbtsBJdn7Hxh%2FK4xCo4LM9Az8z55VADk3h0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;715&quot; height=&quot;272&quot; data-filename=&quot;resultinfo.webp&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;272&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트코드의 기본적인 내용 간략히 설명드리고 마무리 하겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701438136269&quot; class=&quot;vbnet&quot; data-ke-language=&quot;vbnet&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    Dim shtThisVBA As Worksheet
    Set shtThisVBA = ThisWorkbook.Worksheets(&quot;vba&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dim 은 변수선언을 뜻합니다. 문법은 Dim &amp;lt;변수명&amp;gt; As &amp;lt;변수타입&amp;gt; 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Worksheet 변수타입의 shtThisVBA 변수를 선언하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 ThisWorkbook.Worksheets(&quot;vba&quot;)로 현재 열려있는 엑셀문서에서 vba 워크시트의 객체를 shtThisVBA에 저장하였습니다. ThisWorkbook 객체는 현재 엑셀 문서를 가리킵니다. ThisWorkbook.Worksheets는 현재 엑셀문서내의 워크시트를 반환하는 메서드인데 그 파라미터로 워크시트명을 전달해주면 그 워크시트의 워크시트 타입 객체변수를 반환해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수타입에는 객체타입변수와 일반변수가 있습니다. 일반변수는 그 값을 문자 또는 숫자로 표기 가능한 숫자나 문자 타입이고 엑셀문서, 워크시트와 같이 어떤 것을 가리키는 역할의 변수를 객체변수라고 합니다. 객체변수의 값을 바꿀 때는 객체변수 앞에 Set 을 붙여줘야 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 shtThisVBA 워크시트 객체변수를 통하여 vba 워크시트에 접근할 수 있게 되었습니다. 그 다음으로는 vba 워크시트에 입력해둔 옵션값을 읽어와 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1701438388479&quot; class=&quot;vbnet&quot; data-ke-language=&quot;vbnet&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    Dim strTargetFilePath As String
    Dim strTargetFileName As String
    Dim strTargetWorksheetName As String
    Dim lEntryRowNo As Long

    strTargetFilePath = shtThisVBA.Range(&quot;E4&quot;).Value
    strTargetFileName = shtThisVBA.Range(&quot;E5&quot;).Value
    strTargetWorksheetName = shtThisVBA.Range(&quot;E6&quot;).Value
    lEntryRowNo = shtThisVBA.Range(&quot;E7&quot;).Value&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 읽어올 값을 저장할 변수를 선언합니다. 테스트 샘플 페이지에서 Option Value를 4개 써놨는데 3개는 문자열(텍스트)이고 1개는 숫자입니다. 그러므로 문자열 변수를 3개 선언하고 양의정수 타입 변수를 1개 선언합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수선언은 Dim으로 한다고 말씀드렸습니다. 그리고 문자타입변수는 String 이고 양의정수 타입은 Long 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이로서 상단 4줄은 설명이 완료 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Range 메서드는 셀범위를 지정하는 메서드입니다. Range(&quot;E4&quot;)는 E4셀을 지정하는데 그뒤에 .Value를 붙이면 E4셀에 저장된 값을 읽게 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;strTargetFilePath = shtThisVBA.Range(&quot;E4&quot;).Value 는 결국 shtThisVBA 워크시트 객체 변수가 가리키는 vba 워크시트의 E4 셀에 저장된 값을 읽어서 strTatgetFilePath 변수에 저장하는 코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로 E5, E6의 문자열을 읽어서 해당 변수에 저장하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 lEntryRowNo는 Long 타입 변수입니다. 이 변수에는 셀 E7에 들어있는 숫자를 읽어서 저장하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;E4와 E5는 경로와 파일명을 의미하는 문자열인데 이 두 문자열을 더해 하나의 문자열로 만들어 보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701438804814&quot; class=&quot;vbnet&quot; data-ke-language=&quot;vbnet&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Dim strTargetFullPath As String
strTargetFullPath = strTargetFilePath + &quot;\&quot; + strTargetFileName&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 작성하면 두 문자열이 합쳐지고 중간에 역슬래시('\')가 추가되어 파일명을 포함한 전체 경로를 만들 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 변수에 값이 제대로 들어갔는지 디버깅 메시지로 확인할 차례입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701438856142&quot; class=&quot;vbnet&quot; data-ke-language=&quot;vbnet&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    Debug.Print &quot;[INFO] strTargetFilePath: &quot; &amp;amp; strTargetFilePath
    Debug.Print &quot;[INFO] strTargetFileName: &quot; &amp;amp; strTargetFileName
    Debug.Print &quot;[INFO] strTargetWorksheetName: &quot; &amp;amp; strTargetWorksheetName
    Debug.Print &quot;[INFO] strTargetFullPath: &quot; &amp;amp; strTargetFullPath
    
    MsgBox &quot;[INFO] lEntryRowNo: &quot; &amp;amp; vbNewLine &amp;amp; _
            lEntryRowNo, vbCritical&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Debug.Print 를 사용하면 코드 편집기의 &quot;직접 실행&quot; 창에 메시지를 출력할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수는 변수명을 그대로 쓰고, 문자열은 쌍따옴표 사이에 기입하면 됩니다. 문자열과 변수를 같이 출력하려면 그 중간에 &amp;amp; 문자를 두면 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MsgBox를 사용하면 팝업 메시지창을 발생시킵니다. Debug.Print와 마찬가지로 문자열은 쌍따옴표 사이에 쓰고, 변수는 &amp;amp; 문자로 연결합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 엑셀에서 VBA를 활용하여 엑셀 매크로를 작성하는 방법을 알아보았습니다.&lt;/p&gt;</description>
      <category>프로그래밍/Excel VBA</category>
      <category>vba</category>
      <category>엑셀 매크로</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/46</guid>
      <comments>https://noyecube.tistory.com/entry/VBA-%EC%97%91%EC%85%80-VBA-%EC%82%AC%EC%9A%A9%EB%B2%95#entry46comment</comments>
      <pubDate>Fri, 1 Dec 2023 23:01:52 +0900</pubDate>
    </item>
    <item>
      <title>oracle linux oracle 19c 설치환경 설정</title>
      <link>https://noyecube.tistory.com/entry/oracle-linux-oracle-19c-%EC%84%A4%EC%B9%98%ED%99%98%EA%B2%BD-%EC%84%A4%EC%A0%95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전 글에서는 oracle 19c를 위한 오라클리눅스를 설치하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blog.noyecube.com/entry/linux%EC%97%90-oracle-19c-%EC%84%A4%EC%B9%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2023.12.06 - [Database/Oracle] - linux에 oracle 19c 설치&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1701245001484&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;virtualbox 오라클리눅스 설치 oracle linux Server&quot; data-og-description=&quot;virtualbox 오라클리눅스 설치 oracle linux Server Oracle Linux는 Oracle Corporation에서 배포하는 무료 오픈소스 Linux 배포판입니다. Oracle Linux는 다음과 같은 특징을 가지고 있습니다. 안정성: Oracle Linux는 Red H&quot; data-og-host=&quot;blog.noyecube.com&quot; data-og-source-url=&quot;https://noyecube.tistory.com/entry/virtualbox-%EC%98%A4%EB%9D%BC%ED%81%B4%EB%A6%AC%EB%88%85%EC%8A%A4-%EC%84%A4%EC%B9%98-oracle-linux-Server-89&quot; data-og-url=&quot;https://blog.noyecube.com/entry/virtualbox-%EC%98%A4%EB%9D%BC%ED%81%B4%EB%A6%AC%EB%88%85%EC%8A%A4-%EC%84%A4%EC%B9%98-oracle-linux-Server-89&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bisfUs/hyUE5risRw/syVAbvOAQ6w2HqlKkmjDNk/img.png?width=800&amp;amp;height=429&amp;amp;face=0_0_800_429,https://scrap.kakaocdn.net/dn/bDazU3/hyUB3BSm6P/VOdOJ6gkxIUZ3xJwp66O81/img.png?width=800&amp;amp;height=429&amp;amp;face=0_0_800_429,https://scrap.kakaocdn.net/dn/v43rs/hyUB2JKZcI/jFl1zCtUaK3Q15KBP1pCaK/img.png?width=940&amp;amp;height=805&amp;amp;face=0_0_940_805&quot;&gt;&lt;a href=&quot;https://noyecube.tistory.com/entry/virtualbox-%EC%98%A4%EB%9D%BC%ED%81%B4%EB%A6%AC%EB%88%85%EC%8A%A4-%EC%84%A4%EC%B9%98-oracle-linux-Server-89&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://noyecube.tistory.com/entry/virtualbox-%EC%98%A4%EB%9D%BC%ED%81%B4%EB%A6%AC%EB%88%85%EC%8A%A4-%EC%84%A4%EC%B9%98-oracle-linux-Server-89&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bisfUs/hyUE5risRw/syVAbvOAQ6w2HqlKkmjDNk/img.png?width=800&amp;amp;height=429&amp;amp;face=0_0_800_429,https://scrap.kakaocdn.net/dn/bDazU3/hyUB3BSm6P/VOdOJ6gkxIUZ3xJwp66O81/img.png?width=800&amp;amp;height=429&amp;amp;face=0_0_800_429,https://scrap.kakaocdn.net/dn/v43rs/hyUB2JKZcI/jFl1zCtUaK3Q15KBP1pCaK/img.png?width=940&amp;amp;height=805&amp;amp;face=0_0_940_805');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;virtualbox 오라클리눅스 설치 oracle linux Server&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;virtualbox 오라클리눅스 설치 oracle linux Server Oracle Linux는 Oracle Corporation에서 배포하는 무료 오픈소스 Linux 배포판입니다. Oracle Linux는 다음과 같은 특징을 가지고 있습니다. 안정성: Oracle Linux는 Red H&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;blog.noyecube.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 oracle 19c를 위한 설치환경 설정을 해봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;X Window 프로그램 설치 및 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 소스포지 경로에서 Xming을 다운로드하고 옵션 변경 없이 설치까지 완료합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;Xming&quot; href=&quot;https://sourceforge.net/projects/xming/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://sourceforge.net/projects/xming/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1701245155266&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;product&quot; data-og-title=&quot;Xming X Server for Windows&quot; data-og-description=&quot;Download Xming X Server for Windows for free. X Window System Server for Windows. Xming is the leading X Window System Server for Microsoft Windows 8/7/Vista/XP (+ server 2012/2008/2003). It is fully featured, small and fast, simple to install and because &quot; data-og-host=&quot;sourceforge.net&quot; data-og-source-url=&quot;https://sourceforge.net/projects/xming/&quot; data-og-url=&quot;https://sourceforge.net/projects/xming/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b5Z92R/hyUE2ahpfo/9MtRKF2bpY1TZujBoOeNPK/img.png?width=48&amp;amp;height=48&amp;amp;face=0_0_48_48&quot;&gt;&lt;a href=&quot;https://sourceforge.net/projects/xming/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://sourceforge.net/projects/xming/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b5Z92R/hyUE2ahpfo/9MtRKF2bpY1TZujBoOeNPK/img.png?width=48&amp;amp;height=48&amp;amp;face=0_0_48_48');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Xming X Server for Windows&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Download Xming X Server for Windows for free. X Window System Server for Windows. Xming is the leading X Window System Server for Microsoft Windows 8/7/Vista/XP (+ server 2012/2008/2003). It is fully featured, small and fast, simple to install and because&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;sourceforge.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 서버 접속 클라이언트 Putty에 아래와 같이 X11 포워딩 설정을 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.webp&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEAS49/btsBRRdSIA5/pAL2RRNUHkIpsJDf9B1wM1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEAS49/btsBRRdSIA5/pAL2RRNUHkIpsJDf9B1wM1/img.webp&quot; data-alt=&quot;putty X11 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEAS49/btsBRRdSIA5/pAL2RRNUHkIpsJDf9B1wM1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEAS49%2FbtsBRRdSIA5%2FpAL2RRNUHkIpsJDf9B1wM1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;673&quot; height=&quot;622&quot; data-filename=&quot;1.webp&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;622&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;putty X11 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에 root로 접속하여 &quot;xhost&quot; 명령을 쳐봅니다. xhost 실행 시 xhost: command not found&quot;로 뜨면 아래 명령어로 x11 관련 패키지를 설치합니다. 그 뒤 &quot;xhost +&quot; 명령을 입력하면 &quot;client can connect from any host&quot; 메시지를 확인할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701676768331&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dnf install xorg*&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ssh 설정에 다음 옵션 주석처리 되어 있으면 주석을 풀어주고 변경한 설정을 재적용 해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일명은 /etc/ssh/sshd_config입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701681940256&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;X11Forwarding yes
X11DisplayOffset 10
X11UseLocalhost no&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 명령어로 ssh 설정 재적용&lt;/p&gt;
&lt;pre id=&quot;code_1701681994098&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;systemctl reload sshd&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HOSTS FILE 수정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hosts 파일은 로컬환경에서 DNS 역할을 하는 설정파일입니다. 이 파일에 hostname과 IP 정보를 추가합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.jpg&quot; data-origin-width=&quot;939&quot; data-origin-height=&quot;264&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bftSOJ/btsBKjvMrQl/8dpl35Guo3UdcJj8HwoLaK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bftSOJ/btsBKjvMrQl/8dpl35Guo3UdcJj8HwoLaK/img.jpg&quot; data-alt=&quot;hostname 과 IP 확인 후 /etc/hosts 파일 맨 아래에 추가 합니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bftSOJ/btsBKjvMrQl/8dpl35Guo3UdcJj8HwoLaK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbftSOJ%2FbtsBKjvMrQl%2F8dpl35Guo3UdcJj8HwoLaK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;939&quot; height=&quot;264&quot; data-filename=&quot;3.jpg&quot; data-origin-width=&quot;939&quot; data-origin-height=&quot;264&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;hostname 과 IP 확인 후 /etc/hosts 파일 맨 아래에 추가 합니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SELINUX 설정 변경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Oracle 설치를 위한 SELINUX 권장 설정은 기본값 enforcing이 아닌 permissive입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/etc/selinux/config 파일에서 아래와 같이 설정을 변경합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.jpg&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;408&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Z9pLh/btsBJg6P7Pv/K285cjuEJcP0Wi9CkqP3KK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Z9pLh/btsBJg6P7Pv/K285cjuEJcP0Wi9CkqP3KK/img.jpg&quot; data-alt=&quot;SELINUX 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Z9pLh/btsBJg6P7Pv/K285cjuEJcP0Wi9CkqP3KK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZ9pLh%2FbtsBJg6P7Pv%2FK285cjuEJcP0Wi9CkqP3KK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;798&quot; height=&quot;408&quot; data-filename=&quot;4.jpg&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;408&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SELINUX 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;유틸리티 패키지 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Oracle 설치에 필요한 유틸리티 패키지를 설치합니다. LINUX 설치 옵션에 따라 이미 설치되어 있을 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패키지 누락이 없도록 몇 군데 레퍼런스 사이트를 참조한 정보이므로 중복이 있을 수 있으나 상관없습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701246611235&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dnf install make perl xorg-x11-xauth xterm libGL libEGL nmap&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1701246625671&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dnf install bc binutils compat-openssl11 elfutils-libelf fontconfig glibc glibc-devel ksh libaio libasan libX11 libXau libXi libXrender libXtst libxcrypt-compat libgcc libibverbs libnsl librdmacm libstdc++ libxcb libvirt-libs make policycoreutilspolicycoreutils-python-utilssmartmontoolssysstat glibc-headers ipmiutil libnsl2 libnsl2-devel net-tools nfs-utils unixODBC&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1701246641643&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dnf install bc binutils compat-libcap1 compat-libstdc++-33 dtrace-modules dtrace-modules-headers dtrace-modules-provider-headers dtrace-utils elfutils-libelf elfutils-libelf-devel fontconfig-devel glibc glibc-devel ksh libaio libaio-devel libdtrace-ctf-devel libXrender libXrender-devel libX11 libXau libXi libXtst libgcc librdmacm-devel libstdc++ libstdc++-devel libxcb make net-tools nfs-utils python python-configshell python-rtslib python-six targetcli smartmontools sysstat unixODBC libnsl libnsl.i686 libnsl2 libnsl2.i686&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Automatic Setup 진행&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Oracle Database Preinstallation RPM을 활용하여 기본 설정을 진행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dnf search preinstall을 통해 현재 리눅스버전에서 지원하는 Preinstall RPM 목록을 확인가능 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;19c를 설치할 것이므로 oracle-database-preinstall-19c.x86_64를 설치합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6.jpg&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JliC5/btsBGVhRlMH/qpghTNYqn4i7Zaciam4Rk1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JliC5/btsBGVhRlMH/qpghTNYqn4i7Zaciam4Rk1/img.jpg&quot; data-alt=&quot;dnf install oracle-database-preinstall-19c.x86_64&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JliC5/btsBGVhRlMH/qpghTNYqn4i7Zaciam4Rk1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJliC5%2FbtsBGVhRlMH%2FqpghTNYqn4i7Zaciam4Rk1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;176&quot; data-filename=&quot;6.jpg&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;176&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;dnf install oracle-database-preinstall-19c.x86_64&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 RPM 설치 후 RPM 패키지 업데이트 진행합니다.(안 해도 됨)&lt;/p&gt;
&lt;pre id=&quot;code_1701247208244&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dnf update -y&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 oracle 계정이 생성되었는지 확인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 root권한으로 oracle 계정의 그룹을 &quot;oinstall&quot;에서 &quot;dba&quot;로 변경해 주고 패스워드도 변경해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;7.jpg&quot; data-origin-width=&quot;683&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bS2ql7/btsBQXSW2qU/EKivATsNl5MOhRpIkNX9Ak/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bS2ql7/btsBQXSW2qU/EKivATsNl5MOhRpIkNX9Ak/img.jpg&quot; data-alt=&quot;oracle 계정 생성 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bS2ql7/btsBQXSW2qU/EKivATsNl5MOhRpIkNX9Ak/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbS2ql7%2FbtsBQXSW2qU%2FEKivATsNl5MOhRpIkNX9Ak%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;683&quot; height=&quot;302&quot; data-filename=&quot;7.jpg&quot; data-origin-width=&quot;683&quot; data-origin-height=&quot;302&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;oracle 계정 생성 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1701246672879&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;usermod -g dba -G dba oracle&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;8.jpg&quot; data-origin-width=&quot;713&quot; data-origin-height=&quot;161&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GXk19/btsBFZLOBz5/AuOn8TBRR6F1Hm5LCyx37k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GXk19/btsBFZLOBz5/AuOn8TBRR6F1Hm5LCyx37k/img.jpg&quot; data-alt=&quot;oracle 패스워드 변경&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GXk19/btsBFZLOBz5/AuOn8TBRR6F1Hm5LCyx37k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGXk19%2FbtsBFZLOBz5%2FAuOn8TBRR6F1Hm5LCyx37k%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;713&quot; height=&quot;161&quot; data-filename=&quot;8.jpg&quot; data-origin-width=&quot;713&quot; data-origin-height=&quot;161&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;oracle 패스워드 변경&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$ORACLE_HOME(오라클 엔진 디렉터리)을 생성합니다.(기본 버전 19.3을 설치할 예정임을 참고)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;oracle 계정에 접근권한을 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701246688468&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mkdir -p /oracle/app/oracle/product/19.3.0&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.jpg&quot; data-origin-width=&quot;878&quot; data-origin-height=&quot;348&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WgsNe/btsBKdWFgAc/7ROrCHj6IM4nCY7RGcPrSK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WgsNe/btsBKdWFgAc/7ROrCHj6IM4nCY7RGcPrSK/img.jpg&quot; data-alt=&quot;엔진 디렉토리 생성 후 oracle 계정에 접근권한 부여&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WgsNe/btsBKdWFgAc/7ROrCHj6IM4nCY7RGcPrSK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWgsNe%2FbtsBKdWFgAc%2F7ROrCHj6IM4nCY7RGcPrSK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;878&quot; height=&quot;348&quot; data-filename=&quot;9.jpg&quot; data-origin-width=&quot;878&quot; data-origin-height=&quot;348&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;엔진 디렉토리 생성 후 oracle 계정에 접근권한 부여&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불필요 서비스를 확인하여 있으면 정리합니다. (블루투스 등)&lt;/p&gt;
&lt;pre id=&quot;code_1701246710310&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;서비스 확인: systemctl list-units --type=service --state=running
서비스 정지: systemctl stop &amp;lt;서비스명&amp;gt;.service
서비스 비활성화: systemctl disable &amp;lt;서비스명&amp;gt;.service&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;oracle 계정 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;root에서 su - oracle로 oracle 계정으로 전환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cd /home/oracle로 이동합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mkdir scripts로 /home/oracle/scripts 폴더를 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/home/oracle/scripts 경로에서 아래 구문을 복사하여 입력합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701246451129&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cat &amp;gt; /home/oracle/scripts/setEnv.sh &amp;lt;&amp;lt;EOF
# Oracle Settings

export ORACLE_BASE=/oracle/app/oracle
export ORACLE_HOME=\$ORACLE_BASE/product/19.3.0
export TNS_ADMIN=\$ORACLE_HOME/network/admin
export DATA_DIR=/oracle/app/oradata

export ORACLE_HOSTNAME=noyeora
export ORACLE_UNQNAME=noyedb
export ORACLE_SID=noyedb

export NLS_LANG=American_America.KO16MSWIN949
export DISPLAY=\$ORACLE_HOSTNAME:0.0
export ORA_NLS=\$ORACLE_HOME/admin/data

export PATH=\$ORACLE_HOME/bin:/bin:/usr/bin:/usr/sbin:.:\$PATH
export LD_LIBRARY_PATH=\$ORACLE_HOME/lib:/lib:/usr/lib
export CLASSPATH=\$ORACLE_HOME/jlib:\$ORACLE_HOME/rdbms/jlib

alias ss='sqlplus / as sysdba'
EOF&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/home/oracle/scripts/setEnv.sh 파일 내용을 확인해 봅니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;10.jpg&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;517&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d7gsnB/btsBG4eMG7b/Hkw5MM2KmkcKjIgZNBhqsk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d7gsnB/btsBG4eMG7b/Hkw5MM2KmkcKjIgZNBhqsk/img.jpg&quot; data-alt=&quot;bash_profile for oracle database&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d7gsnB/btsBG4eMG7b/Hkw5MM2KmkcKjIgZNBhqsk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd7gsnB%2FbtsBG4eMG7b%2FHkw5MM2KmkcKjIgZNBhqsk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;856&quot; height=&quot;517&quot; data-filename=&quot;10.jpg&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;517&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;bash_profile for oracle database&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 명령어로 ~/. bash_profile에 위 환경변수를 적용합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701246587527&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;echo &quot;. /home/oracle/scripts/setEnv.sh&quot; &amp;gt;&amp;gt; /home/oracle/.bash_profile&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;11.jpg&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;269&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ReACM/btsBOglb1Bq/pukTIk6wiFtpdCJj2YT5rK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ReACM/btsBOglb1Bq/pukTIk6wiFtpdCJj2YT5rK/img.jpg&quot; data-alt=&quot;bash_profile&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ReACM/btsBOglb1Bq/pukTIk6wiFtpdCJj2YT5rK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FReACM%2FbtsBOglb1Bq%2FpukTIk6wiFtpdCJj2YT5rK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;688&quot; height=&quot;269&quot; data-filename=&quot;11.jpg&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;269&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;bash_profile&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;oracle 계정에 환경변수를 적용하고 확인해 봅니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701246818106&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;. ~/.bash_profile
echo $ORACLE_SID
echo $ORACLE_BASE
echo $ORACLE_HOME&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;12.jpg&quot; data-origin-width=&quot;553&quot; data-origin-height=&quot;188&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWoiVD/btsBHqhB9Cg/4vaKX7hHUNqK1DuiMyAYm0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWoiVD/btsBHqhB9Cg/4vaKX7hHUNqK1DuiMyAYm0/img.jpg&quot; data-alt=&quot;환경변수 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWoiVD/btsBHqhB9Cg/4vaKX7hHUNqK1DuiMyAYm0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWoiVD%2FbtsBHqhB9Cg%2F4vaKX7hHUNqK1DuiMyAYm0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;553&quot; height=&quot;188&quot; data-filename=&quot;12.jpg&quot; data-origin-width=&quot;553&quot; data-origin-height=&quot;188&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;환경변수 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;startup/shutdown 스크립트 작성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;startup/shutdown 시 호출할 스크립트를 작성합니다. 작성 후 ownership과 실행권한 확인하고 수정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701246921803&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cat &amp;gt; /home/oracle/scripts/start_all.sh &amp;lt;&amp;lt;EOF
#!/bin/bash
. /home/oracle/scripts/setEnv.sh

export ORAENV_ASK=NO
. oraenv
export ORAENV_ASK=YES

dbstart \$ORACLE_HOME
EOF&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1701246940883&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cat &amp;gt; /home/oracle/scripts/stop_all.sh &amp;lt;&amp;lt;EOF
#!/bin/bash
. /home/oracle/scripts/setEnv.sh

export ORAENV_ASK=NO
. oraenv
export ORAENV_ASK=YES

dbshut \$ORACLE_HOME
EOF&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;13.jpg&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;377&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9BXue/btsBQsTaVfz/kh9BHjCScpCeyXxaCElEyk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9BXue/btsBQsTaVfz/kh9BHjCScpCeyXxaCElEyk/img.jpg&quot; data-alt=&quot;chmod 755 로 실행권한을 부여하고 결과를 확인합니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9BXue/btsBQsTaVfz/kh9BHjCScpCeyXxaCElEyk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9BXue%2FbtsBQsTaVfz%2Fkh9BHjCScpCeyXxaCElEyk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;377&quot; data-filename=&quot;13.jpg&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;377&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;chmod 755 로 실행권한을 부여하고 결과를 확인합니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;root 계정에서 oracle 계정에 virtualbox 공유폴더 접근권한을 부여 후 $ORACLE_HOME에 19c 설치파일을 복사합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701247061379&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;usermod -aG vboxsf oracle&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;14.jpg&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;228&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rqNv2/btsBJiDBD3a/KASkgxzjQnrE6KLK0SRXwK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rqNv2/btsBJiDBD3a/KASkgxzjQnrE6KLK0SRXwK/img.jpg&quot; data-alt=&quot;설치파일 복사&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rqNv2/btsBJiDBD3a/KASkgxzjQnrE6KLK0SRXwK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrqNv2%2FbtsBJiDBD3a%2FKASkgxzjQnrE6KLK0SRXwK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;228&quot; data-filename=&quot;14.jpg&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;228&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;설치파일 복사&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상으로 Oracle Linux(오라클리눅스)에 19c 설치준비를 마쳤습니다.&lt;/p&gt;</description>
      <category>Database/Oracle</category>
      <category>NLS_LANG</category>
      <category>oracle 19c</category>
      <category>Oracle Linux</category>
      <category>oracle_base</category>
      <category>ORACLE_HOME</category>
      <category>ORACLE_SID</category>
      <category>ORA_NLS</category>
      <category>TNS_ADMIN</category>
      <category>usermod</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/45</guid>
      <comments>https://noyecube.tistory.com/entry/oracle-linux-oracle-19c-%EC%84%A4%EC%B9%98%ED%99%98%EA%B2%BD-%EC%84%A4%EC%A0%95#entry45comment</comments>
      <pubDate>Wed, 29 Nov 2023 17:40:50 +0900</pubDate>
    </item>
    <item>
      <title>virtualbox 오라클리눅스 설치 oracle linux Server</title>
      <link>https://noyecube.tistory.com/entry/virtualbox-%EC%98%A4%EB%9D%BC%ED%81%B4%EB%A6%AC%EB%88%85%EC%8A%A4-%EC%84%A4%EC%B9%98-oracle-linux-Server-89</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;virtualbox 오라클리눅스 설치 oracle linux Server&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Oracle Linux는 Oracle Corporation에서 배포하는 무료 오픈소스 Linux 배포판입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Oracle Linux는 다음과 같은 특징을 가지고 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;안정성: Oracle Linux는 Red Hat Enterprise Linux와 동일한 코드베이스를 기반으로 하기 때문에 동일한 수준의 안정성을 제공합니다.&lt;/li&gt;
&lt;li&gt;성능: Oracle Linux는 Oracle의 기술지원을 통해 최적화된 성능을 제공합니다.&lt;/li&gt;
&lt;li&gt;보안: Oracle Linux는 Oracle의 보안 지원을 통해 최신 보안 패치를 제공합니다.&lt;/li&gt;
&lt;li&gt;확장성: Oraclie Linux는 다양한 하드웨어 및 소프트웨어 환경을 지원합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Oracle Linux는 Oracle 19c 설치에 권장되는 Linux 배포판입니다. Oracle Linux의 최신버전은 9.3이지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 Oracle 19c에 대한 certi는 버전 8까지 되어 있기 때문에 버전 8.9로 설치를 진행합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 성능상의 이점을 위하여 Desktop GUI 버전 대신 Command-Line 기반 Server로 설치합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;VirtualBox 다운로드 및 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;Download VirtualBox&quot; href=&quot;https://www.virtualbox.org/wiki/Downloads&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.virtualbox.org/wiki/Downloads&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1701160855993&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Downloads     &amp;ndash; Oracle VM VirtualBox&quot; data-og-description=&quot;Download VirtualBox Here you will find links to VirtualBox binaries and its source code. VirtualBox binaries By downloading, you agree to the terms and conditions of the respective license. If you're looking for the latest VirtualBox 6.1 packages, see Virt&quot; data-og-host=&quot;www.virtualbox.org&quot; data-og-source-url=&quot;https://www.virtualbox.org/wiki/Downloads&quot; data-og-url=&quot;https://www.virtualbox.org/wiki/Downloads&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.virtualbox.org/wiki/Downloads&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.virtualbox.org/wiki/Downloads&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Downloads &amp;ndash; Oracle VM VirtualBox&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Download VirtualBox Here you will find links to VirtualBox binaries and its source code. VirtualBox binaries By downloading, you agree to the terms and conditions of the respective license. If you're looking for the latest VirtualBox 6.1 packages, see Virt&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.virtualbox.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 링크에서 Windows hosts용 설치파일을 다운로드하고 설치합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 Add-On으로 제공되는 VM VirtualBox Extension Pack은 PUEL 라이선스를 따르므로 사업체에서 설치하면 오라클에서 저작권 현황관련 연락오므로 조심하시기 바랍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.webp&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;430&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/W7Ywl/btsBKeOLjv5/vQVtE5rjkAWss96Z2AnTOK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/W7Ywl/btsBKeOLjv5/vQVtE5rjkAWss96Z2AnTOK/img.webp&quot; data-alt=&quot;Oracle 라이선스 연락&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/W7Ywl/btsBKeOLjv5/vQVtE5rjkAWss96Z2AnTOK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FW7Ywl%2FbtsBKeOLjv5%2FvQVtE5rjkAWss96Z2AnTOK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;430&quot; data-filename=&quot;1.webp&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;430&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Oracle 라이선스 연락&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Oracle Linux 다운로드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;Oracle Linux Installation Media&quot; href=&quot;https://yum.oracle.com/oracle-linux-isos.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://yum.oracle.com/oracle-linux-isos.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1701161232425&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Oracle Linux ISOs | Oracle, Software. Hardware. Complete.&quot; data-og-description=&quot;Oracle Linux Installation Media Download Oracle Linux ISOs&quot; data-og-host=&quot;yum.oracle.com&quot; data-og-source-url=&quot;https://yum.oracle.com/oracle-linux-isos.html&quot; data-og-url=&quot;https://yum.oracle.com/oracle-linux-isos.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://yum.oracle.com/oracle-linux-isos.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://yum.oracle.com/oracle-linux-isos.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Oracle Linux ISOs | Oracle, Software. Hardware. Complete.&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Oracle Linux Installation Media Download Oracle Linux ISOs&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;yum.oracle.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 링크에서 UEK boot ISO 타입의 Oracle Linux 8.9를 다운로드 받습니다. 최신버전을 받아서 oracle 19c 설치를 시도했는데 별의별 오류가 발생하여 8.9로 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.jpg&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;265&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E3ujD/btsBGFl3Vvz/9SxHguYt0UdjaeIbE9g6s1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E3ujD/btsBGFl3Vvz/9SxHguYt0UdjaeIbE9g6s1/img.jpg&quot; data-alt=&quot;현재 최신 버전은 9.3이지만 oracle 19c 의 안정적인 설치를 위해 8.9를 다운받습니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E3ujD/btsBGFl3Vvz/9SxHguYt0UdjaeIbE9g6s1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE3ujD%2FbtsBGFl3Vvz%2F9SxHguYt0UdjaeIbE9g6s1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;265&quot; data-filename=&quot;2.jpg&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;265&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;현재 최신 버전은 9.3이지만 oracle 19c 의 안정적인 설치를 위해 8.9를 다운받습니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;VirtualBox Network 어댑터 설정 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트목적의 설치이므로 게스트OS에 고정 IP를 할당하는 대신 DHCP로 IP를 할당받도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VirtualBox NAT 네트워크 설정을 확인합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.webp&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;375&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cupht5/btsBFMzeVEg/retFLkBrRDCtHc3LfRKaUk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cupht5/btsBFMzeVEg/retFLkBrRDCtHc3LfRKaUk/img.webp&quot; data-alt=&quot;파일-도구-네트워크 관리자&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cupht5/btsBFMzeVEg/retFLkBrRDCtHc3LfRKaUk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcupht5%2FbtsBFMzeVEg%2FretFLkBrRDCtHc3LfRKaUk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;830&quot; height=&quot;375&quot; data-filename=&quot;2.webp&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;375&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;파일-도구-네트워크 관리자&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.webp&quot; data-origin-width=&quot;939&quot; data-origin-height=&quot;523&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8EJmN/btsBGxuLCm4/QKX9kHANnjgYApoJn2z311/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8EJmN/btsBGxuLCm4/QKX9kHANnjgYApoJn2z311/img.webp&quot; data-alt=&quot;NAT 네트워크의 DHCP 활성화 체크&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8EJmN/btsBGxuLCm4/QKX9kHANnjgYApoJn2z311/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8EJmN%2FbtsBGxuLCm4%2FQKX9kHANnjgYApoJn2z311%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;939&quot; height=&quot;523&quot; data-filename=&quot;3.webp&quot; data-origin-width=&quot;939&quot; data-origin-height=&quot;523&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;NAT 네트워크의 DHCP 활성화 체크&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAT 네트워크 탭에 네트워크 어댑터가 없으면 &quot;만들기&quot;로 새로운 NAT 어댑터를 생성하고 &quot;DHCP 활성화&quot;를 체크합니다. 그렇지 않으면 게스트 OS 내부에서 네트워크 설정을 해봤자 외부와 인터넷 연결이 불가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;가상 머신 만들기&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.webp&quot; data-origin-width=&quot;939&quot; data-origin-height=&quot;427&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kCiuN/btsBGEHsOd4/OX3fFSq2SkLx7lJ44ufVz0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kCiuN/btsBGEHsOd4/OX3fFSq2SkLx7lJ44ufVz0/img.webp&quot; data-alt=&quot;무인 설치 건너 뛰기 체크&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kCiuN/btsBGEHsOd4/OX3fFSq2SkLx7lJ44ufVz0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkCiuN%2FbtsBGEHsOd4%2FOX3fFSq2SkLx7lJ44ufVz0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;939&quot; height=&quot;427&quot; data-filename=&quot;4.webp&quot; data-origin-width=&quot;939&quot; data-origin-height=&quot;427&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;무인 설치 건너 뛰기 체크&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VirtualBox에서 가상머신 만들기를 위와 같이 진행합니다. &quot;무인 설치 건너뛰기&quot;에 체크합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하드웨어와 하드디스크 설정이 필요합니다. 아래를 참고하여 하드웨어 사양에 맞게 설정 후 저장해 줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.webp&quot; data-origin-width=&quot;905&quot; data-origin-height=&quot;223&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lDjqZ/btsBGEgopJL/N56kOkkrlaaaQcqJ4WJ9c0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lDjqZ/btsBGEgopJL/N56kOkkrlaaaQcqJ4WJ9c0/img.webp&quot; data-alt=&quot;하드웨어 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lDjqZ/btsBGEgopJL/N56kOkkrlaaaQcqJ4WJ9c0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlDjqZ%2FbtsBGEgopJL%2FN56kOkkrlaaaQcqJ4WJ9c0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;905&quot; height=&quot;223&quot; data-filename=&quot;5.webp&quot; data-origin-width=&quot;905&quot; data-origin-height=&quot;223&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;하드웨어 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6.webp&quot; data-origin-width=&quot;902&quot; data-origin-height=&quot;409&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bph4WW/btsBJcDppxA/HEU4LfiPPrn3k0YIrDuuXk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bph4WW/btsBJcDppxA/HEU4LfiPPrn3k0YIrDuuXk/img.webp&quot; data-alt=&quot;하드디스크 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bph4WW/btsBJcDppxA/HEU4LfiPPrn3k0YIrDuuXk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbph4WW%2FbtsBJcDppxA%2FHEU4LfiPPrn3k0YIrDuuXk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;902&quot; height=&quot;409&quot; data-filename=&quot;6.webp&quot; data-origin-width=&quot;902&quot; data-origin-height=&quot;409&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;하드디스크 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;7.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;205&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAVFKj/btsBJeOJO8n/jplen5pJzUrlcZF9HvoRRK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAVFKj/btsBJeOJO8n/jplen5pJzUrlcZF9HvoRRK/img.webp&quot; data-alt=&quot;가상머신 시작&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAVFKj/btsBJeOJO8n/jplen5pJzUrlcZF9HvoRRK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAVFKj%2FbtsBJeOJO8n%2Fjplen5pJzUrlcZF9HvoRRK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;205&quot; data-filename=&quot;7.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;205&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;가상머신 시작&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상머신이 생성되었으면 &quot;시작&quot;을 눌러서 부팅시킵니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;8.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;805&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOGBHD/btsBGjjdaPj/W2uL7Dd24nH5koWaPpOmhK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOGBHD/btsBGjjdaPj/W2uL7Dd24nH5koWaPpOmhK/img.webp&quot; data-alt=&quot;Install Oracle Linux 8.9.0 선택 후 엔터키 입력&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOGBHD/btsBGjjdaPj/W2uL7Dd24nH5koWaPpOmhK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOGBHD%2FbtsBGjjdaPj%2FW2uL7Dd24nH5koWaPpOmhK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;805&quot; data-filename=&quot;8.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;805&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Install Oracle Linux 8.9.0 선택 후 엔터키 입력&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게스트OS에 마우스커서가 갇혀있을 때는 모든 입력이 게스트 OS로만 전달됩니다. Escape Key 설정을 VMware와 동일하게 Ctrl+Alt 조합으로 변경하면 편리합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.webp&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;275&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dealpQ/btsBQX6u4Bg/3yXX56sBakA89uWyKeRikK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dealpQ/btsBQX6u4Bg/3yXX56sBakA89uWyKeRikK/img.webp&quot; data-alt=&quot;화면 탈출 단축키 변경&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dealpQ/btsBQX6u4Bg/3yXX56sBakA89uWyKeRikK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdealpQ%2FbtsBQX6u4Bg%2F3yXX56sBakA89uWyKeRikK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;742&quot; height=&quot;275&quot; data-filename=&quot;9.webp&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;275&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;화면 탈출 단축키 변경&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;What language would you like to use during the installation process? 질문에는 English로 선택합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 다음과 같은 INSTALLATION SUMMARY 화면이 뜹니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;10.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;577&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/edP34l/btsBGyAp5Cd/SWPw4ZhRqzt8vfHo3RZjL1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/edP34l/btsBGyAp5Cd/SWPw4ZhRqzt8vfHo3RZjL1/img.webp&quot; data-alt=&quot;INSTALLATION SUMMARY&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/edP34l/btsBGyAp5Cd/SWPw4ZhRqzt8vfHo3RZjL1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FedP34l%2FbtsBGyAp5Cd%2FSWPw4ZhRqzt8vfHo3RZjL1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;577&quot; data-filename=&quot;10.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;577&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;INSTALLATION SUMMARY&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LOCALIZATION - Language Support: English(United States). (한글로 설정 시 트러블슈팅 시 구글링 어렵습니다.)&lt;/li&gt;
&lt;li&gt;LOCALIZATION - Time &amp;amp; Date: Seoul.&lt;/li&gt;
&lt;li&gt;USER SETTINGS - Root Password: 설정&lt;/li&gt;
&lt;li&gt;USER SETTINGS - User Creation: 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;11.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;381&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ykhw0/btsBG5kuM6i/92aFSSAknyW5AF4OoOqA0k/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ykhw0/btsBG5kuM6i/92aFSSAknyW5AF4OoOqA0k/img.webp&quot; data-alt=&quot;User Creation Option&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ykhw0/btsBG5kuM6i/92aFSSAknyW5AF4OoOqA0k/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYkhw0%2FbtsBG5kuM6i%2F92aFSSAknyW5AF4OoOqA0k%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;381&quot; data-filename=&quot;11.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;381&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;User Creation Option&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SOFTWARE - Software Selection: Minimal Install with Standard Additional software.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;12.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;268&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/slVqC/btsBFJWQbN6/FthItzhEHl7esiBiBfrEg0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/slVqC/btsBFJWQbN6/FthItzhEHl7esiBiBfrEg0/img.webp&quot; data-alt=&quot;Minimal Install&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/slVqC/btsBFJWQbN6/FthItzhEHl7esiBiBfrEg0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FslVqC%2FbtsBFJWQbN6%2FFthItzhEHl7esiBiBfrEg0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;268&quot; data-filename=&quot;12.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;268&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Minimal Install&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SYSTEM - Installation Destination&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;13.webp&quot; data-origin-width=&quot;494&quot; data-origin-height=&quot;772&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byoIdn/btsBGV9YsCC/aEEue7HrWvxcS2jj5ot2dk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byoIdn/btsBGV9YsCC/aEEue7HrWvxcS2jj5ot2dk/img.webp&quot; data-alt=&quot;Custom으로 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byoIdn/btsBGV9YsCC/aEEue7HrWvxcS2jj5ot2dk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyoIdn%2FbtsBGV9YsCC%2FaEEue7HrWvxcS2jj5ot2dk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;494&quot; height=&quot;772&quot; data-filename=&quot;13.webp&quot; data-origin-width=&quot;494&quot; data-origin-height=&quot;772&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Custom으로 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;14.webp&quot; data-origin-width=&quot;927&quot; data-origin-height=&quot;455&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zj82B/btsBSaka5Xn/t6wMXL6kERMd5swkAiEQd0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zj82B/btsBSaka5Xn/t6wMXL6kERMd5swkAiEQd0/img.webp&quot; data-alt=&quot;Click here to create them automatically 로 우선 기본 파티션 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zj82B/btsBSaka5Xn/t6wMXL6kERMd5swkAiEQd0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fzj82B%2FbtsBSaka5Xn%2Ft6wMXL6kERMd5swkAiEQd0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;927&quot; height=&quot;455&quot; data-filename=&quot;14.webp&quot; data-origin-width=&quot;927&quot; data-origin-height=&quot;455&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Click here to create them automatically 로 우선 기본 파티션 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;15.webp&quot; data-origin-width=&quot;939&quot; data-origin-height=&quot;497&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkUAd3/btsBG3Uvs9l/srl7VYKyCPB8vqo4fLil4k/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkUAd3/btsBG3Uvs9l/srl7VYKyCPB8vqo4fLil4k/img.webp&quot; data-alt=&quot;ol-home, ol-root 삭제&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkUAd3/btsBG3Uvs9l/srl7VYKyCPB8vqo4fLil4k/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkUAd3%2FbtsBG3Uvs9l%2Fsrl7VYKyCPB8vqo4fLil4k%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;939&quot; height=&quot;497&quot; data-filename=&quot;15.webp&quot; data-origin-width=&quot;939&quot; data-origin-height=&quot;497&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ol-home, ol-root 삭제&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;16.webp&quot; data-origin-width=&quot;939&quot; data-origin-height=&quot;519&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lywL4/btsBQWNhp1O/F5tY1EcRx6foaEP1u8E8m0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lywL4/btsBQWNhp1O/F5tY1EcRx6foaEP1u8E8m0/img.webp&quot; data-alt=&quot;루트에 max로 남은용량 전부 할당&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lywL4/btsBQWNhp1O/F5tY1EcRx6foaEP1u8E8m0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlywL4%2FbtsBQWNhp1O%2FF5tY1EcRx6foaEP1u8E8m0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;939&quot; height=&quot;519&quot; data-filename=&quot;16.webp&quot; data-origin-width=&quot;939&quot; data-origin-height=&quot;519&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;루트에 max로 남은용량 전부 할당&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;swap 영역은 Oracle 19c 최대 권장인 16GB로 설정하고 ol-root를 max로 설정하여 남은 용량을 전부 몰아 줍니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SYSTEM - Network &amp;amp; Host Name&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;17.webp&quot; data-origin-width=&quot;939&quot; data-origin-height=&quot;225&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qFV6m/btsBGiECuKS/DiFXRAVOaRTK4UUcZEIg9K/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qFV6m/btsBGiECuKS/DiFXRAVOaRTK4UUcZEIg9K/img.webp&quot; data-alt=&quot;이더넷 ON&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qFV6m/btsBGiECuKS/DiFXRAVOaRTK4UUcZEIg9K/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqFV6m%2FbtsBGiECuKS%2FDiFXRAVOaRTK4UUcZEIg9K%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;939&quot; height=&quot;225&quot; data-filename=&quot;17.webp&quot; data-origin-width=&quot;939&quot; data-origin-height=&quot;225&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이더넷 ON&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;18.webp&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;72&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JrbU1/btsBQs6HHyB/EGlFY69yfGaGmKIk4x8bW1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JrbU1/btsBQs6HHyB/EGlFY69yfGaGmKIk4x8bW1/img.webp&quot; data-alt=&quot;Host Name 설정 후 Apply&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JrbU1/btsBQs6HHyB/EGlFY69yfGaGmKIk4x8bW1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJrbU1%2FbtsBQs6HHyB%2FEGlFY69yfGaGmKIk4x8bW1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;736&quot; height=&quot;72&quot; data-filename=&quot;18.webp&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;72&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Host Name 설정 후 Apply&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ethernet을 &quot;ON&quot;으로 해주고 Host Name을 변경해 줍니다. &quot;Connected&quot;로 뜨지 않으면 VirtualBox NAT 설정에서 케이블연결상태 재확인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Begin Installation Start&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치요약 화면에서 설정을 마쳤으면 설치시작 버튼으로 설치를 진행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;VirtualBox Guest Additions 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VirtualBox Guest Additions는 VirtualBox에서 실행되는 가상 머신에 설치되는 소프트웨어 패키지이며 가상 머신의 성능, 기능, 사용성을 향상시킵니다. 그러므로 모든 가상머신에 설치하여 사용하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;root로 전환하여 Guest Additions 설치에 필요한 기본 패키지를 설치해 줍니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 17px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 100%; height: 17px;&quot;&gt;$ su - root&amp;nbsp;&lt;br /&gt;# dnf install kernel-uek-devel-$(uname -r) gcc binutils automake make perl bzip2 elfutils-libelf-devel&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;19.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;81&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cf3KgI/btsBFG6PD7y/TNV5CC6jJ8AnH3pKIgyUW0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cf3KgI/btsBFG6PD7y/TNV5CC6jJ8AnH3pKIgyUW0/img.webp&quot; data-alt=&quot;위와 같이 Progress 진행이 안되면 네트워크 문제 입니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cf3KgI/btsBFG6PD7y/TNV5CC6jJ8AnH3pKIgyUW0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcf3KgI%2FbtsBFG6PD7y%2FTNV5CC6jJ8AnH3pKIgyUW0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;81&quot; data-filename=&quot;19.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;81&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;위와 같이 Progress 진행이 안되면 네트워크 문제 입니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #777777; text-align: center;&quot;&gt;&quot;Is this ok?&quot;와 같은 물음에는 항상 y로 진행합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 과정이 완료되면 VirtualBox 장치-게스트 확장 CD 이미지 삽입을 누릅니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;20.webp&quot; data-origin-width=&quot;606&quot; data-origin-height=&quot;391&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUW2OD/btsBGwWVzgF/kpmuYCJM3ZzUhru7lSYjG1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUW2OD/btsBGwWVzgF/kpmuYCJM3ZzUhru7lSYjG1/img.webp&quot; data-alt=&quot;게스트 확장 CD 이미지 삽입&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUW2OD/btsBGwWVzgF/kpmuYCJM3ZzUhru7lSYjG1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUW2OD%2FbtsBGwWVzgF%2FkpmuYCJM3ZzUhru7lSYjG1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;606&quot; height=&quot;391&quot; data-filename=&quot;20.webp&quot; data-origin-width=&quot;606&quot; data-origin-height=&quot;391&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;게스트 확장 CD 이미지 삽입&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;25.jpg&quot; data-origin-width=&quot;939&quot; data-origin-height=&quot;428&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NaTmh/btsBGnTsstq/9oA7TKjtkkMnUCQkGRk3sK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NaTmh/btsBGnTsstq/9oA7TKjtkkMnUCQkGRk3sK/img.jpg&quot; data-alt=&quot;./VBoxLinuxAdditions.run 실행&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NaTmh/btsBGnTsstq/9oA7TKjtkkMnUCQkGRk3sK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNaTmh%2FbtsBGnTsstq%2F9oA7TKjtkkMnUCQkGRk3sK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;939&quot; height=&quot;428&quot; data-filename=&quot;25.jpg&quot; data-origin-width=&quot;939&quot; data-origin-height=&quot;428&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;./VBoxLinuxAdditions.run 실행&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삽입된 CD 이미지는 /dev/cdrom 경로에 삽입되며 사용을 위해서는 다른 경로에 mount가 필요하므로 /media/cdrom 경로를 생성하고 다음 명령어로 마운트 합니다. 그리고 VBoxLinuxAdditions.run을 실행시킵니다. 설치가 완료되면 재부팅합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;# mount /dev/cdrom /media/cdrom&lt;br /&gt;# ./VBoxLinuxAdditions.run&lt;br /&gt;# reboot&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;27.jpg&quot; data-origin-width=&quot;511&quot; data-origin-height=&quot;125&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmM0Ix/btsBQV1UJdK/COHuZwN6tatx35aK7LZirk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmM0Ix/btsBQV1UJdK/COHuZwN6tatx35aK7LZirk/img.jpg&quot; data-alt=&quot;lsmod 로 vboxguest 프로세스 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmM0Ix/btsBQV1UJdK/COHuZwN6tatx35aK7LZirk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmM0Ix%2FbtsBQV1UJdK%2FCOHuZwN6tatx35aK7LZirk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;511&quot; height=&quot;125&quot; data-filename=&quot;27.jpg&quot; data-origin-width=&quot;511&quot; data-origin-height=&quot;125&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;lsmod 로 vboxguest 프로세스 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;VirtualBox 공유폴더 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 호스트 PC와 가상 머신 간 공유폴더를 설정합니다. 장치-공유폴더-공유폴더설에서 공유할 호스트 PC의 폴더경로를 지정하고 가상 머신에서 사용할 폴더이름을 입력합니다. &quot;자동마운트&quot;, &quot;항상 사용&quot;으로 설정합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;21.webp&quot; data-origin-width=&quot;939&quot; data-origin-height=&quot;548&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lAXHd/btsBHPocOfE/ci6Lz6TnQVfm4JYqy2ABI1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lAXHd/btsBHPocOfE/ci6Lz6TnQVfm4JYqy2ABI1/img.webp&quot; data-alt=&quot;공유 폴더 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lAXHd/btsBHPocOfE/ci6Lz6TnQVfm4JYqy2ABI1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlAXHd%2FbtsBHPocOfE%2Fci6Lz6TnQVfm4JYqy2ABI1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;939&quot; height=&quot;548&quot; data-filename=&quot;21.webp&quot; data-origin-width=&quot;939&quot; data-origin-height=&quot;548&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;공유 폴더 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 설정 후 가상 머신에서 /media 경로로 이동하면 &quot;sf_VmShare&quot; 폴더가 새로 보입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 권한이 root:vboxsf로 되어 있어 일반 계정으로 접근이 불가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;23.webp&quot; data-origin-width=&quot;725&quot; data-origin-height=&quot;448&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvpOWC/btsBQqVmudV/MVyk0tGMYUMAJZGuIG9Gk0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvpOWC/btsBQqVmudV/MVyk0tGMYUMAJZGuIG9Gk0/img.webp&quot; data-alt=&quot;vboxsf permission denied&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvpOWC/btsBQqVmudV/MVyk0tGMYUMAJZGuIG9Gk0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvpOWC%2FbtsBQqVmudV%2FMVyk0tGMYUMAJZGuIG9Gk0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;725&quot; height=&quot;448&quot; data-filename=&quot;23.webp&quot; data-origin-width=&quot;725&quot; data-origin-height=&quot;448&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;vboxsf permission denied&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 명령어로 vboxsf 그룹에 공유폴더 접근할 사용자 계정($USER)을 입력 후 재부팅합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;$ sudo usermod -aG vboxsf $USER&lt;br /&gt;$ sudo reboot&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;29.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;327&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYLoGt/btsBGhyV97V/1C5jvjHBHPkqfXqOH9Q9TK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYLoGt/btsBGhyV97V/1C5jvjHBHPkqfXqOH9Q9TK/img.webp&quot; data-alt=&quot;호스트 공유폴더에 가상 머신과 공유할 파일을 가져다 둡니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYLoGt/btsBGhyV97V/1C5jvjHBHPkqfXqOH9Q9TK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYLoGt%2FbtsBGhyV97V%2F1C5jvjHBHPkqfXqOH9Q9TK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;327&quot; data-filename=&quot;29.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;327&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;호스트 공유폴더에 가상 머신과 공유할 파일을 가져다 둡니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;28.jpg&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;328&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dDDkKK/btsBHR0ylyW/KLgyXwDllsvhqL4pGNmH60/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dDDkKK/btsBHR0ylyW/KLgyXwDllsvhqL4pGNmH60/img.jpg&quot; data-alt=&quot;호스트에서 공유한 파일을 가상머신에서 바로 접근 가능&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dDDkKK/btsBHR0ylyW/KLgyXwDllsvhqL4pGNmH60/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdDDkKK%2FbtsBHR0ylyW%2FKLgyXwDllsvhqL4pGNmH60%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;328&quot; data-filename=&quot;28.jpg&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;328&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;호스트에서 공유한 파일을 가상머신에서 바로 접근 가능&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SSH 설정 변경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSH의 기본 Port는 22번입니다. 하지만 해킹시도가 기본 포트로 많이 시도하므로 이 포트를 변경하고 &quot;root&quot;계정으로의 ssh 접속도 막을 필요가 있습니다. 더 나아가 ssh로 접속가능한 계정명을 설정파일에 명시할 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 ssh 기본 포트를 변경하고 root 계정의 ssh 접속을 차단하겠습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;$ sudo vi /etc/ssh/sshd_config&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;30.jpg&quot; data-origin-width=&quot;233&quot; data-origin-height=&quot;33&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvhUmF/btsBOhLb34N/T6wjehdUwpsHznBbwKMY4k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvhUmF/btsBOhLb34N/T6wjehdUwpsHznBbwKMY4k/img.jpg&quot; data-alt=&quot;SSH 기본 포트번호 변경&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvhUmF/btsBOhLb34N/T6wjehdUwpsHznBbwKMY4k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvhUmF%2FbtsBOhLb34N%2FT6wjehdUwpsHznBbwKMY4k%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;233&quot; height=&quot;33&quot; data-filename=&quot;30.jpg&quot; data-origin-width=&quot;233&quot; data-origin-height=&quot;33&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SSH 기본 포트번호 변경&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;31.jpg&quot; data-origin-width=&quot;291&quot; data-origin-height=&quot;31&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czavYc/btsBFDWyvj4/fAWAqeEpHcZST4V5b3h401/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czavYc/btsBFDWyvj4/fAWAqeEpHcZST4V5b3h401/img.jpg&quot; data-alt=&quot;root 로그인 no&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czavYc/btsBFDWyvj4/fAWAqeEpHcZST4V5b3h401/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczavYc%2FbtsBFDWyvj4%2FfAWAqeEpHcZST4V5b3h401%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;291&quot; height=&quot;31&quot; data-filename=&quot;31.jpg&quot; data-origin-width=&quot;291&quot; data-origin-height=&quot;31&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;root 로그인 no&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 다음과 같이 x11 설정을 찾아서 변경해 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1702292700575&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;X11Forwarding yes
X11DisplayOffset 10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 설정 후 ssh 데몬을 재기동하려고 하면 오류가 발생합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 SELINUX(시큐어리눅스) 설정에서 변경한 SSH 포트를 열어주지 않았기 때문입니다. 다음 명령으로 SELINUX에 변경포트를 허용할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;$ sudo semanage port -a -t ssh_port_t -p tcp 2222&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;하지만 oracle 19c 설치 권장사항에서 SELINUX를 ENFORCE 모드에서 PERMISSIVE(경고만 발생시키고 모두 통과) 모드로 변경하라고 되어 있으므로 위 명령어 적용 대신 SELINUX를 PERMISSIVE 모드로 변경합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;$ sudo vi /etc/selinux/config&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;32.jpg&quot; data-origin-width=&quot;656&quot; data-origin-height=&quot;298&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EW7J2/btsBRQzh6qm/NKZVIlXAzK5jZNA6k3LK8K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EW7J2/btsBRQzh6qm/NKZVIlXAzK5jZNA6k3LK8K/img.jpg&quot; data-alt=&quot;SELINUX=permissive&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EW7J2/btsBRQzh6qm/NKZVIlXAzK5jZNA6k3LK8K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEW7J2%2FbtsBRQzh6qm%2FNKZVIlXAzK5jZNA6k3LK8K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;656&quot; height=&quot;298&quot; data-filename=&quot;32.jpg&quot; data-origin-width=&quot;656&quot; data-origin-height=&quot;298&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SELINUX=permissive&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 selinux config 파일을 변경하였으면 아래 명령어를 적용하여 ssh 포트 변경을 확인합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;$ su - root&lt;br /&gt;# setenforce Permissive&lt;br /&gt;# systemctl stop firewalld&lt;br /&gt;# systemctl disable firewalld&lt;br /&gt;# systemctl restart sshd.service&lt;br /&gt;# netstat -an | grep LISTEN&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;33.jpg&quot; data-origin-width=&quot;930&quot; data-origin-height=&quot;223&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dHNfFP/btsBRRZfUUR/biVpyakyo0tku9PlVsxvl1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dHNfFP/btsBRRZfUUR/biVpyakyo0tku9PlVsxvl1/img.jpg&quot; data-alt=&quot;ssh 포트가 22에서 2222로 변경되었다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dHNfFP/btsBRRZfUUR/biVpyakyo0tku9PlVsxvl1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdHNfFP%2FbtsBRRZfUUR%2FbiVpyakyo0tku9PlVsxvl1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;930&quot; height=&quot;223&quot; data-filename=&quot;33.jpg&quot; data-origin-width=&quot;930&quot; data-origin-height=&quot;223&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ssh 포트가 22에서 2222로 변경되었다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 가상 머신으로 SSH 접속을 하기 위해서는 NAT의 포트포워딩 설정이 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스트 IP는 127.0.0.1이고 게스트 IP는 &quot;ip addr&quot; 또는 &quot;ifconfig&quot; 명령으로 IP를 확인하여 입력합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스트 TCP_2222로의 접속은 게스트 TCP_2222로 포워딩됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;44.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;161&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVIM2q/btsBRRSuqaE/7yTd3NlPyP1TD1AOeBIgE0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVIM2q/btsBRRSuqaE/7yTd3NlPyP1TD1AOeBIgE0/img.webp&quot; data-alt=&quot;포트포워딩 설정 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVIM2q/btsBRRSuqaE/7yTd3NlPyP1TD1AOeBIgE0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVIM2q%2FbtsBRRSuqaE%2F7yTd3NlPyP1TD1AOeBIgE0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;161&quot; data-filename=&quot;44.webp&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;161&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;포트포워딩 설정 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Putty 설정&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;45.webp&quot; data-origin-width=&quot;689&quot; data-origin-height=&quot;267&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQTy5s/btsBMQfJRbl/dS6rS76o76cIyIF4a8kLXK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQTy5s/btsBMQfJRbl/dS6rS76o76cIyIF4a8kLXK/img.webp&quot; data-alt=&quot;localhost:2222 로 접속 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQTy5s/btsBMQfJRbl/dS6rS76o76cIyIF4a8kLXK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQTy5s%2FbtsBMQfJRbl%2FdS6rS76o76cIyIF4a8kLXK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;689&quot; height=&quot;267&quot; data-filename=&quot;45.webp&quot; data-origin-width=&quot;689&quot; data-origin-height=&quot;267&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;localhost:2222 로 접속 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 접속에 성공하였습니다. 이제 원격에서 서버에 접속할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, Oracle 19c를 설치하려면 X Server 설정이 필요합니다. Putty로 콘솔기반 서버에 접속하여도 GUI 기반의 화면을&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;띄워줄 수 있게 됩니다. Oracle 19c 설치 시 설치프로그램이 GUI기반이기 때문에 필요합니다.&lt;br /&gt;(물론 사일런트 모드로 설치가능하신 전문가분들은 필요 없습니다만 저는 필요합니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;xming 설치&quot; href=&quot;https://sourceforge.net/projects/xming/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://sourceforge.net/projects/xming/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1701164614695&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;product&quot; data-og-title=&quot;Xming X Server for Windows&quot; data-og-description=&quot;Download Xming X Server for Windows for free. X Window System Server for Windows. Xming is the leading X Window System Server for Microsoft Windows 8/7/Vista/XP (+ server 2012/2008/2003). It is fully featured, small and fast, simple to install and because &quot; data-og-host=&quot;sourceforge.net&quot; data-og-source-url=&quot;https://sourceforge.net/projects/xming/&quot; data-og-url=&quot;https://sourceforge.net/projects/xming/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c3DNi8/hyUE5EFbNm/niYMaj1O5Fc6sXjG5kBIK0/img.png?width=48&amp;amp;height=48&amp;amp;face=0_0_48_48&quot;&gt;&lt;a href=&quot;https://sourceforge.net/projects/xming/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://sourceforge.net/projects/xming/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c3DNi8/hyUE5EFbNm/niYMaj1O5Fc6sXjG5kBIK0/img.png?width=48&amp;amp;height=48&amp;amp;face=0_0_48_48');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Xming X Server for Windows&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Download Xming X Server for Windows for free. X Window System Server for Windows. Xming is the leading X Window System Server for Microsoft Windows 8/7/Vista/XP (+ server 2012/2008/2003). It is fully featured, small and fast, simple to install and because&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;sourceforge.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 링크에서 설치파일 다운로드 후 설정변경 없이 계속 진행하여 설치를 완료합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Putty에서 아래와 같이 SSH-X11 설정을 해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;Enable X11 forwarding&quot; 체크해 주시고 x display location에 &quot;:0.0&quot; 입력합니다. 공백 없이 입력합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;47.webp&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dPmJOY/btsBOhdkVSX/2RfGLWKpKaPymNx0Zyeb2k/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dPmJOY/btsBOhdkVSX/2RfGLWKpKaPymNx0Zyeb2k/img.webp&quot; data-alt=&quot;x11 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dPmJOY/btsBOhdkVSX/2RfGLWKpKaPymNx0Zyeb2k/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdPmJOY%2FbtsBOhdkVSX%2F2RfGLWKpKaPymNx0Zyeb2k%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;673&quot; height=&quot;622&quot; data-filename=&quot;47.webp&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;622&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;x11 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 다시 Session 설정창에 돌아와서 아래와 같이 입력 후 &quot;Save&quot;를 눌러서 설정을 저장합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;49.webp&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;509&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1SldZ/btsBRP1ryV4/T11paakl1ZMn4ChfK1jaQk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1SldZ/btsBRP1ryV4/T11paakl1ZMn4ChfK1jaQk/img.webp&quot; data-alt=&quot;putty 설정 저장&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1SldZ/btsBRP1ryV4/T11paakl1ZMn4ChfK1jaQk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1SldZ%2FbtsBRP1ryV4%2FT11paakl1ZMn4ChfK1jaQk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;680&quot; height=&quot;509&quot; data-filename=&quot;49.webp&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;509&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;putty 설정 저장&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 시간에는 Oracle 19c를 설치해 보겠습니다.&lt;/p&gt;</description>
      <category>Windows | Linux</category>
      <category>19C</category>
      <category>oracle 19c</category>
      <category>Oracle Linux</category>
      <category>vboxsf</category>
      <category>VirtualBox</category>
      <category>virtualbox extension pack</category>
      <category>Virtualbox guest addition</category>
      <category>VirtualBox 공유폴더</category>
      <category>XMING</category>
      <category>오라클리눅스</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/44</guid>
      <comments>https://noyecube.tistory.com/entry/virtualbox-%EC%98%A4%EB%9D%BC%ED%81%B4%EB%A6%AC%EB%88%85%EC%8A%A4-%EC%84%A4%EC%B9%98-oracle-linux-Server-89#entry44comment</comments>
      <pubDate>Tue, 28 Nov 2023 18:45:50 +0900</pubDate>
    </item>
    <item>
      <title>Linux 스왑 공간 증가(Increase Swap Space)</title>
      <link>https://noyecube.tistory.com/entry/Linux-%EC%8A%A4%EC%99%91-%EA%B3%B5%EA%B0%84-%EC%A6%9D%EA%B0%80Increase-Swap-Space</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;리눅스에서의 스왑 공간(swap space)&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스에서 스왑 공간(swap space)은 물리적 메모리(RAM)의 용량이 가득 차게 될 경우 사용되는 여유 공간을 말합니다. 즉, 시스템이 처리하고 있는 데이터를 저장할 RAM이 충분하지 않을 때 스왑 공간에 이 데이터를 기록합니다. Ubuntu, Oracle Linux 설치 시 설치프로그램이 제안하는 기본 파티션으로 설치하면 스왑 공간 없이 OS 설치가 완료됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요즘같이 대용량 메모리를 사용하는 시대에 스왑 공간은 필요없을 수 있지만 DBMS와 같은 일부 응용프로그램에서는 설치단계에서 스왑 공간이 없으면 설치를 진행할 수 없습니다. 이럴 경우 스왑 파일을 직접 생성하고, 초기화 및 사용으로 설정하여 스왑 공간의 양을 늘릴 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;547&quot; data-origin-height=&quot;80&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IJD1Q/btsAuNlpgT1/HW6cebmqxAoIXNeSkVeERK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IJD1Q/btsAuNlpgT1/HW6cebmqxAoIXNeSkVeERK/img.png&quot; data-alt=&quot;스왑 공간이 없어서 설치를 못한다고 합니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IJD1Q/btsAuNlpgT1/HW6cebmqxAoIXNeSkVeERK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIJD1Q%2FbtsAuNlpgT1%2FHW6cebmqxAoIXNeSkVeERK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;547&quot; height=&quot;80&quot; data-origin-width=&quot;547&quot; data-origin-height=&quot;80&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;스왑 공간이 없어서 설치를 못한다고 합니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. root 계정으로 전환하여 스왑 공간의 양을 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(sudoers group에 포함되어 있는 계정에서) sudo -i 또는 sudo - root 실행 후 아래 명령어들로 스왑 공간을 확인합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;grep -i swap /proc/meminfo&lt;/li&gt;
&lt;li&gt;free -h&lt;/li&gt;
&lt;li&gt;swapon -s&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.jpg&quot; data-origin-width=&quot;656&quot; data-origin-height=&quot;80&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bB99T9/btsBOfzPO8Q/g7t73YkatL9HMypsfu970K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bB99T9/btsBOfzPO8Q/g7t73YkatL9HMypsfu970K/img.jpg&quot; data-alt=&quot;free 로 확인한 swap 공간 크기가 0&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bB99T9/btsBOfzPO8Q/g7t73YkatL9HMypsfu970K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbB99T9%2FbtsBOfzPO8Q%2Fg7t73YkatL9HMypsfu970K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;656&quot; height=&quot;80&quot; data-filename=&quot;1.jpg&quot; data-origin-width=&quot;656&quot; data-origin-height=&quot;80&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;free 로 확인한 swap 공간 크기가 0&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2. dd 명령을 사용하여 스왑 파일을 만들고 스왑파일 초기화 후 사용으로 설정&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&amp;nbsp;dd if=/dev/zero of=/.swapfile bs=4096 count=2097152&lt;/li&gt;
&lt;li&gt;mkswap /.swapfile&lt;/li&gt;
&lt;li&gt;chmod 600 /.swapfile&lt;/li&gt;
&lt;li&gt;swapon /.swapfile&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;if: 입력파일입니다. /dev/zero는 0바이트 스트림을 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;of: 출력파일입니다. 루트 디렉터리에 숨김파일로 swapfile을 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bs: 블록 크기(바이트 단위)입니다. 한 번에 입력 파일에서 읽고 출력 파일에 쓸 바이트 수입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;count: 읽고 쓸 블록 수입니다. 이 숫자에 bs 값을 곱해 파일 크기를 구하게 됩니다. 4k 쓰기 단위에서 1GB 스왑 파일을 만들려면 count가 262144인데 8GB 크기를 만들 것이므로 262144 x 8 = 2097152로 정했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mkswap으로 dd로 생성한 파일을 스왑파일 포맷으로 변경합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dd로 파일 생성 시 644 권한으로 생성되는데 insecure permissions 경고가 발생하므로 root만 swapfile을 읽고 쓸 수 있도록 600으로 권한을 변경합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;swapon으로 새 스왑 파일이 있음을 Linux에 알려줍니다. 그럼 swap 파일이 활성화됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;549&quot; data-origin-height=&quot;84&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/D09vR/btsAuJb74NF/allR2UAtbPBBKEgkqc2oH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/D09vR/btsAuJb74NF/allR2UAtbPBBKEgkqc2oH1/img.png&quot; data-alt=&quot;dd 실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/D09vR/btsAuJb74NF/allR2UAtbPBBKEgkqc2oH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FD09vR%2FbtsAuJb74NF%2FallR2UAtbPBBKEgkqc2oH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;549&quot; height=&quot;84&quot; data-origin-width=&quot;549&quot; data-origin-height=&quot;84&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;dd 실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;531&quot; data-origin-height=&quot;50&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dJFvrl/btsAxTdKr8D/ZkFi77bgVwts211lInSeNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dJFvrl/btsAxTdKr8D/ZkFi77bgVwts211lInSeNk/img.png&quot; data-alt=&quot;실제 생성된 swapfile 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dJFvrl/btsAxTdKr8D/ZkFi77bgVwts211lInSeNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdJFvrl%2FbtsAxTdKr8D%2FZkFi77bgVwts211lInSeNk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;531&quot; height=&quot;50&quot; data-origin-width=&quot;531&quot; data-origin-height=&quot;50&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실제 생성된 swapfile 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;98&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxwVCZ/btsAuyaZLcA/iYKaCQOoE9mrNNJPubFj5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxwVCZ/btsAuyaZLcA/iYKaCQOoE9mrNNJPubFj5k/img.png&quot; data-alt=&quot;swapon 까지 실행 후 free 로 확인하면 Swap 영역이 보입니다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxwVCZ/btsAuyaZLcA/iYKaCQOoE9mrNNJPubFj5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxwVCZ%2FbtsAuyaZLcA%2FiYKaCQOoE9mrNNJPubFj5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;646&quot; height=&quot;98&quot; data-origin-width=&quot;646&quot; data-origin-height=&quot;98&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;swapon 까지 실행 후 free 로 확인하면 Swap 영역이 보입니다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3. fstab에 스왑 파일(Swap file) 추가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(2)까지 진행 후 재부팅하고 보면 Swap Space가 비활성화되어 있습니다. 재부팅 후에도 스왑 영역이 유지되도록 /etc/fstab에 추가해야 합니다. &quot;vi /etc/fstab&quot;으로 파일을 열어 편집하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;/.swapfile none swap sw 0 0&quot; 을 맨 아랫줄에 추가하고 &quot;wq&quot; 명령으로 저장 후 종료합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.jpg&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;95&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6dkC9/btsBQjossKn/yLUhIDWcVIPTI1my4kFoU0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6dkC9/btsBQjossKn/yLUhIDWcVIPTI1my4kFoU0/img.jpg&quot; data-alt=&quot;/etc/fstab 맨 아랫줄 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6dkC9/btsBQjossKn/yLUhIDWcVIPTI1my4kFoU0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6dkC9%2FbtsBQjossKn%2FyLUhIDWcVIPTI1my4kFoU0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;754&quot; height=&quot;95&quot; data-filename=&quot;2.jpg&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;95&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;/etc/fstab 맨 아랫줄 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fstab 필드는 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파일 시스템: /.swapfile&lt;/li&gt;
&lt;li&gt;마운트 지점: 파일시스템처럼 마운트 하지 않았으므로 none&lt;/li&gt;
&lt;li&gt;유형: swap&lt;/li&gt;
&lt;li&gt;옵션: 기본값 sw&lt;/li&gt;
&lt;li&gt;덤프: 0 해당 없음&lt;/li&gt;
&lt;li&gt;통과: 0 해당 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;4. 스왑 사용량 확인&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;swapon --show로 확인가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;268&quot; data-origin-height=&quot;70&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bs0JDs/btsAut8yqkn/S7fzE9sdOAUsrukOpYZcsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bs0JDs/btsAut8yqkn/S7fzE9sdOAUsrukOpYZcsK/img.png&quot; data-alt=&quot;swapon --show&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bs0JDs/btsAut8yqkn/S7fzE9sdOAUsrukOpYZcsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbs0JDs%2FbtsAut8yqkn%2FS7fzE9sdOAUsrukOpYZcsK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;268&quot; height=&quot;70&quot; data-origin-width=&quot;268&quot; data-origin-height=&quot;70&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;swapon --show&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;systemctl reboot 명령으로 재부팅 후에도 정상여부 확인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상으로 스왑 공간 증가 방법에 대해 알아보았습니다.&lt;/p&gt;</description>
      <category>Windows | Linux</category>
      <category>fstab</category>
      <category>swap space</category>
      <category>스왑 영역</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/43</guid>
      <comments>https://noyecube.tistory.com/entry/Linux-%EC%8A%A4%EC%99%91-%EA%B3%B5%EA%B0%84-%EC%A6%9D%EA%B0%80Increase-Swap-Space#entry43comment</comments>
      <pubDate>Sat, 18 Nov 2023 00:45:15 +0900</pubDate>
    </item>
    <item>
      <title>Windows환경 INI 설정 파일 활용</title>
      <link>https://noyecube.tistory.com/entry/Windows%ED%99%98%EA%B2%BD-INI-%EC%84%A4%EC%A0%95-%ED%8C%8C%EC%9D%BC-%ED%99%9C%EC%9A%A9</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;INI&lt;span style=&quot;background-color: #ffffff; color: #4d5156; text-align: left;&quot;&gt;(Initialization)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;파일&lt;span style=&quot;background-color: #ffffff; color: #4d5156; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;포맷은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;설정 파일&lt;span style=&quot;background-color: #ffffff; color: #4d5156; text-align: left;&quot;&gt;에 대한 사실상 표준이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #4d5156; text-align: left;&quot;&gt;다른 포맷으로 xml, json, yaml도 있지만 windows 환경에서는 ini에 대한 WIN API가 제공되므로 간단한 내용이면 ini를 사용하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #4d5156; text-align: left;&quot;&gt;1. 요구사항&lt;/span&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;설정 파일 경로에 설정파일이 없으면 프로그램에 내장된 기본값으로 설정파일을 생성할 것(손상시 복구기능 포함)&lt;/li&gt;
&lt;li&gt;프로그램 구동중에도 동적으로 변경된 설정파일의 내용을 적용할 수 있을 것&lt;/li&gt;
&lt;li&gt;설정값의 추가/삭제에도 관련 코드 수정이 최소화 되도록 인터페이스화 할 것&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;362&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u3WSl/btsx1TuDAa9/3rS2f6LvPazXuox12RUeWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u3WSl/btsx1TuDAa9/3rS2f6LvPazXuox12RUeWk/img.png&quot; data-alt=&quot;샘플 설정 파일 내용&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u3WSl/btsx1TuDAa9/3rS2f6LvPazXuox12RUeWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu3WSl%2Fbtsx1TuDAa9%2F3rS2f6LvPazXuox12RUeWk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;362&quot; height=&quot;306&quot; data-origin-width=&quot;362&quot; data-origin-height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;샘플 설정 파일 내용&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. ini 파일 포맷 및 관련 WIN API&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ini 파일은 SECTION, KEY NAME, KEY VALUE로 구분된다. 샘플 파일에서 [SERVER], [PATH], [TASK_OHLCV]는 SECTION이고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'=' 문자가 포함된 줄은 KEY이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'=' 문자를 중심으로 KEY NAME과 KEY VALUE가 구분된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WIN API는 아래 세개를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AppName: SECTION&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;KeyName: '=' 문자 앞 문자열&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Default: KEY가 존재하지 않을 경우의 기본 값&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FileName: 설정파일 경로(드라이브명부터 전체 경로)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;250&quot; data-origin-height=&quot;91&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4hdqB/btsx3KDWpKj/LlTduvsgHMhkWTAbj3DTk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4hdqB/btsx3KDWpKj/LlTduvsgHMhkWTAbj3DTk0/img.png&quot; data-alt=&quot;BOOL WritePrivateProfileString&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4hdqB/btsx3KDWpKj/LlTduvsgHMhkWTAbj3DTk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4hdqB%2Fbtsx3KDWpKj%2FLlTduvsgHMhkWTAbj3DTk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;250&quot; height=&quot;91&quot; data-origin-width=&quot;250&quot; data-origin-height=&quot;91&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;BOOL WritePrivateProfileString&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;246&quot; data-origin-height=&quot;123&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JlDCO/btsydqK2raC/EBNkTen7R4tdR407vsqDKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JlDCO/btsydqK2raC/EBNkTen7R4tdR407vsqDKk/img.png&quot; data-alt=&quot;UINT GetPrivateProfileInt&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JlDCO/btsydqK2raC/EBNkTen7R4tdR407vsqDKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJlDCO%2FbtsydqK2raC%2FEBNkTen7R4tdR407vsqDKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;246&quot; height=&quot;123&quot; data-origin-width=&quot;246&quot; data-origin-height=&quot;123&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;UINT GetPrivateProfileInt&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;472&quot; data-origin-height=&quot;153&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wiBW6/btsycuAbfPv/ARqAX6nKDRLNByDnWBMoNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wiBW6/btsycuAbfPv/ARqAX6nKDRLNByDnWBMoNk/img.png&quot; data-alt=&quot;DWORD GetPrivateProfileString&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wiBW6/btsycuAbfPv/ARqAX6nKDRLNByDnWBMoNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwiBW6%2FbtsycuAbfPv%2FARqAX6nKDRLNByDnWBMoNk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;472&quot; height=&quot;153&quot; data-origin-width=&quot;472&quot; data-origin-height=&quot;153&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;DWORD GetPrivateProfileString&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 구현전략&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;In-Memory DB역할을 하는 DataStore 클래스에 DataStoreConfig 클래스를 추가한다.&lt;/li&gt;
&lt;li&gt;DataStoreConfig 클래스에 설정파일의 각 SECTION에 대응하는 Structure를 정의한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;설정값 읽기/쓰기시 Structure Field를 하드코딩하지 않고 별도 Mapping Table 활용하여 Pointer로 접근하는 인터페이스 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 예제코드&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DataStoreConfig.h&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;529&quot; data-origin-height=&quot;426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uk8tC/btsyaT1w5n4/KSH5aa9Ei7rUbuSHrDKozK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uk8tC/btsyaT1w5n4/KSH5aa9Ei7rUbuSHrDKozK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uk8tC/btsyaT1w5n4/KSH5aa9Ei7rUbuSHrDKozK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fuk8tC%2FbtsyaT1w5n4%2FKSH5aa9Ei7rUbuSHrDKozK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;529&quot; height=&quot;426&quot; data-origin-width=&quot;529&quot; data-origin-height=&quot;426&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정파일의 경로는 실행파일기준으로 ./config/setting.ini로 정했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스포트 기본값을 상수로 선언했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SECTION [SERVER], [PATH]에 대응하는 구조체를 정의했다. 구조체의 필드는 KEY에 대응한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;354&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhSGOb/btsyaUznDXi/CtaxowSzz9fvuXNJYyXayk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhSGOb/btsyaUznDXi/CtaxowSzz9fvuXNJYyXayk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhSGOb/btsyaUznDXi/CtaxowSzz9fvuXNJYyXayk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhSGOb%2FbtsyaUznDXi%2FCtaxowSzz9fvuXNJYyXayk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;486&quot; height=&quot;354&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;354&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mapping Table 생성을 위한 enum class와 struct를 정의했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;enum은 설정파일에 기술될 KEY와 1:1 대응되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;_DS_PROFILE의 필드 속성은 다음과 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;const BOOL boIsInt; : 숫자인지 문자인지&lt;br /&gt;BOOL boNeedToSaveChanges; : 동적으로 변경된 KEY VALUE를 프로그램 종료 전 설정파일에 update 할지 여부&lt;br /&gt;BOOL boFromConfigFile; : 설정파일로부터 읽힌 값인지 프로그램 내장 기본 값인지 여부&lt;br /&gt;const TCHAR tchAppName[32]; : SECTION NAME&lt;br /&gt;const TCHAR tchKeyName[32]; : KEY NAME&lt;br /&gt;const TCHAR tchDefault[64]; : Default KEY VALUE(문자열일 경우 사용)&lt;br /&gt;const INT iDefault; : Default KEY VALUE(숫자일 경우 사용)&lt;br /&gt;TCHAR tchReturnedString[64]; : 설정파일에서 읽은 문자열(GetPrivateProfileString 사용시)&lt;br /&gt;INT iReturnedInt; : 설정파일에서 읽은 숫자(GetPrivateProfileInt 사용시)&lt;br /&gt;LPVOID lpVar; : 설정값에 대응하는 구조체 인스턴스 필드의 주소 포인터&lt;br /&gt;size_t sizeof_lpVar; : 구조체 인스턴스 필드의 메모리 할당 사이즈&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;394&quot; data-origin-height=&quot;228&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Nv2eQ/btsx2N2cMP4/FgnkmAKCSixcS6Wws92O81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Nv2eQ/btsx2N2cMP4/FgnkmAKCSixcS6Wws92O81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Nv2eQ/btsx2N2cMP4/FgnkmAKCSixcS6Wws92O81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNv2eQ%2Fbtsx2N2cMP4%2FFgnkmAKCSixcS6Wws92O81%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;394&quot; height=&quot;228&quot; data-origin-width=&quot;394&quot; data-origin-height=&quot;228&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;void vInitDefaultConfig(void); : 설정파일 접근 전 Static Default 값을 Structure에 쓰기 작업&lt;br /&gt;void vCreateDefaultConfigFile(void); : 설정파일이 없으면 Default 값으로 설정파일 생성&lt;br /&gt;void vLoadConfigFromFile(void); : 설정파일이 있으면 설정값 메모리 로딩&lt;br /&gt;void vDumpDsConfStructures(void); : Structure 각 필드값 출력(디버깅용)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1617&quot; data-origin-height=&quot;144&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/balykK/btsx1RjiR8m/01kWEnLm6Xoz4cqDhlVyx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/balykK/btsx1RjiR8m/01kWEnLm6Xoz4cqDhlVyx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/balykK/btsx1RjiR8m/01kWEnLm6Xoz4cqDhlVyx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbalykK%2Fbtsx1RjiR8m%2F01kWEnLm6Xoz4cqDhlVyx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1617&quot; height=&quot;144&quot; data-origin-width=&quot;1617&quot; data-origin-height=&quot;144&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mapping Table에 각 SECTION:KEY, Default VALUE, 등 초기화&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DataStoreConfig.cpp&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;813&quot; data-origin-height=&quot;547&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhT4y2/btsx3MPkCG0/wfUSI1NcwzfM1YTNDMASo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhT4y2/btsx3MPkCG0/wfUSI1NcwzfM1YTNDMASo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhT4y2/btsx3MPkCG0/wfUSI1NcwzfM1YTNDMASo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhT4y2%2Fbtsx3MPkCG0%2FwfUSI1NcwzfM1YTNDMASo1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;813&quot; height=&quot;547&quot; data-origin-width=&quot;813&quot; data-origin-height=&quot;547&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자. 기본값 메모리 로딩 후 GetModuleFileName()으로 현재 경로를 얻고 \config\setting.ini 존재여부 체크 후 존재하지 않으면 기본 설정파일 생성 후 리턴, 존재하면 설정파일에서 설정값 로딩.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mapping Table 사용으로 SECTION 및 KEY 추가/삭제 여부와 관계없이 아래 구현부 코드는 변경할 필요 없음.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;179&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clh6JO/btsxSyYW0kU/KpDNUJIiaiGAwpjLwFFKfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clh6JO/btsxSyYW0kU/KpDNUJIiaiGAwpjLwFFKfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clh6JO/btsxSyYW0kU/KpDNUJIiaiGAwpjLwFFKfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fclh6JO%2FbtsxSyYW0kU%2FKpDNUJIiaiGAwpjLwFFKfk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1100&quot; height=&quot;179&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;179&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;274&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dnxEh5/btsyaSuN9xO/ywZDknzdwIjuUz07CeFb4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dnxEh5/btsyaSuN9xO/ywZDknzdwIjuUz07CeFb4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dnxEh5/btsyaSuN9xO/ywZDknzdwIjuUz07CeFb4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdnxEh5%2FbtsyaSuN9xO%2FywZDknzdwIjuUz07CeFb4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1212&quot; height=&quot;274&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;274&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;789&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ck1Lur/btsx0L4OTig/G3NnBwdIT7QjKDUACXZRnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ck1Lur/btsx0L4OTig/G3NnBwdIT7QjKDUACXZRnK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ck1Lur/btsx0L4OTig/G3NnBwdIT7QjKDUACXZRnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fck1Lur%2Fbtsx0L4OTig%2FG3NnBwdIT7QjKDUACXZRnK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1620&quot; height=&quot;789&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;789&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1023&quot; data-origin-height=&quot;673&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTQMpp/btsybyJIE4i/eEWGzY6jnbJKHqG3HnDWXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTQMpp/btsybyJIE4i/eEWGzY6jnbJKHqG3HnDWXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTQMpp/btsybyJIE4i/eEWGzY6jnbJKHqG3HnDWXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTQMpp%2FbtsybyJIE4i%2FeEWGzY6jnbJKHqG3HnDWXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1023&quot; height=&quot;673&quot; data-origin-width=&quot;1023&quot; data-origin-height=&quot;673&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1085&quot; data-origin-height=&quot;190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qmCA6/btsydrDoP06/XlDKLIqCBCpOCGU6bMcre0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qmCA6/btsydrDoP06/XlDKLIqCBCpOCGU6bMcre0/img.png&quot; data-alt=&quot;settings.ini 미 존재(초기 실행)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qmCA6/btsydrDoP06/XlDKLIqCBCpOCGU6bMcre0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqmCA6%2FbtsydrDoP06%2FXlDKLIqCBCpOCGU6bMcre0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1085&quot; height=&quot;190&quot; data-origin-width=&quot;1085&quot; data-origin-height=&quot;190&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;settings.ini 미 존재(초기 실행)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;932&quot; data-origin-height=&quot;289&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIfxgF/btsx470h51b/wnKQQSNJ1SuOYb4mUNTgL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIfxgF/btsx470h51b/wnKQQSNJ1SuOYb4mUNTgL0/img.png&quot; data-alt=&quot;settings.ini 존재시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIfxgF/btsx470h51b/wnKQQSNJ1SuOYb4mUNTgL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIfxgF%2Fbtsx470h51b%2FwnKQQSNJ1SuOYb4mUNTgL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;932&quot; height=&quot;289&quot; data-origin-width=&quot;932&quot; data-origin-height=&quot;289&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;settings.ini 존재시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>프로그래밍/C | C++</category>
      <category>GetModuleFileName</category>
      <category>GetPrivateProfileInt</category>
      <category>GetPrivateProfileString</category>
      <category>INI</category>
      <category>WritePrivateProfileString</category>
      <category>_tcsncpy_s</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/42</guid>
      <comments>https://noyecube.tistory.com/entry/Windows%ED%99%98%EA%B2%BD-INI-%EC%84%A4%EC%A0%95-%ED%8C%8C%EC%9D%BC-%ED%99%9C%EC%9A%A9#entry42comment</comments>
      <pubDate>Wed, 11 Oct 2023 21:58:36 +0900</pubDate>
    </item>
    <item>
      <title>Multiplexing Client Connections-1</title>
      <link>https://noyecube.tistory.com/entry/Multiplexing-Client-Connections-1</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 서버에 다수의 클라이언트가 접속했을 때의 패킷 처리 고려사항에 대해 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;다중 클라이언트 패킷 처리는 어떻게 해야 하나?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Multiplexing으로 검색하면 TCP/IP Transport Layer의 내용이 나오거나&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다중 클라이언트 접속처리 예제 코드만 나오고. 응용프로그램 레벨에서의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다중 클라이언트 패킷 처리 관련 내용을 못 찾겠네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 직접 설계해 보려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;303&quot; data-origin-height=&quot;76&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3R8Ln/btslVlcwI2O/jjkyI97ogIg0hevAz5FJkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3R8Ln/btslVlcwI2O/jjkyI97ogIg0hevAz5FJkk/img.png&quot; data-alt=&quot;1:1 연결&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3R8Ln/btslVlcwI2O/jjkyI97ogIg0hevAz5FJkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3R8Ln%2FbtslVlcwI2O%2FjjkyI97ogIg0hevAz5FJkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;303&quot; height=&quot;76&quot; data-origin-width=&quot;303&quot; data-origin-height=&quot;76&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;1:1 연결&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버와 클라이언트 관계가 1:1이면 앞선 글의 제목처럼 bridged 그 자체이므로 multiplexing에 대해 별다른 고려가 필요 없이 서버는 마치 내가 클라이언트인 것처럼 패킷을 주고받으면 그만입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;386&quot; data-origin-height=&quot;341&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZ7626/btslXm9768L/xqms9dJpxsqwtiyofhPFHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZ7626/btslXm9768L/xqms9dJpxsqwtiyofhPFHk/img.png&quot; data-alt=&quot;1:N 연결&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZ7626/btslXm9768L/xqms9dJpxsqwtiyofhPFHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZ7626%2FbtslXm9768L%2Fxqms9dJpxsqwtiyofhPFHk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;386&quot; height=&quot;341&quot; data-origin-width=&quot;386&quot; data-origin-height=&quot;341&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;1:N 연결&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 위 그림처럼 다수의 클라이언트가 접속하게 되면 추가적인 고려사항은 무엇있까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일상 속의 휴대폰 먼저 생각해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콜, SMS, 이더넷 어플들은 프로토콜별로 각각의 채널을 할당받게 되고 이를 Channel MUX에서 Multiplexing 과정을 거쳐 단일구간을 통해 모뎀으로 전송하고, 모뎀으로부터의 응답은 다시 Channel MUX에서 Demultiplexing 과정을 거쳐 각각의 채널을 통해 상위 어플 레벨로 올려 보냅니다. 타임아웃도 고려해야 하고 채널별 ID를 관리해야 하고 메시지 타입별 ID도 관리해야 하고 Request에 대한 응답인지, Notification, Indication에 대한 응답인지도 고려해야 하고(전자는 송신자가 먼저 요청한 패킷으로서 Solicitated response라고 하고 후자는 송신자가 요청하지 않은 패킷으로 Unsolicitated Response라고 합니다. 보통 송신 패킷은 Request ID가 있어서 응답이 올 때까지 RequestID를 들고 있다가 관리를 하는데 Unsol이면 Request ID가 없죠. ) 클라이언트 모두에게 멀티캐스팅을 할 것인지 특정 클라이언트에게만 보낼 것인지 특정 정보를 C1에 구독신청 해놨는데 접속이 끊겼으면 C2로 전환해야 하는데 이를 요청자에게 노티를 보낼 건지 안 보내고 프레임워크레벨에서 처리할 건지 공유 자원에 대해 Lock으로 잠기는 구간이 최소화되도록 해야 하고 머리가 아픕니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Network Bridged 자동매매 프로그램에서 접속가능 클라이언트 수가 N이 된다고 가정하면, 서버는 각 클라이언트의 계좌 상황을 알고 있어야 하고 매매 체결신호 수신 시 클라이언트를 구분할 수 있어야 합니다. 장 운영정보 같은 패킷은 클라이언트에 특정되지 않으니 이건 공통으로 빼고요. 그림에서는 서버 스레드를 하나로 표현했지만 매매로직당 Worker thread를 할당할 것이므로 각각의 Thread가 각각의 Request를 보낼 건데 그 응답에 대한 Notificaion을 송신 thread에게 callback 해줘야 하므로 이 또한 고려해야 하고 차트데이터를 수신할 때 T1, T2, T3가 같은 종목을 요청했으면 이걸 각각의 Thread가 메모리 할당해서 들고 있을 필요는 없잖아요, 그래서 공용 데이터를 관리할 Database 객체도 만들어줘야 합니다. 각 클라이언트별로 소켓객체를 담고 있는 세션관리노드가 있는데 여기에 패킷 ack 관리까지 하는 정보를 넣으면 한 객체에 너무 많은 정보가 담겨 접근하는 스레드가 늘어나고 그로 인해 단순 데이터 송수신하는 속도가 생명인 스레드가 lock 진입 전 대기시간이 길어질 수 있어서 이 또한 고려해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.webp&quot; data-origin-width=&quot;887&quot; data-origin-height=&quot;472&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNsea1/btsBQx2f71h/VcaGqYp8uLgnIKhIKR5cNK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNsea1/btsBQx2f71h/VcaGqYp8uLgnIKhIKR5cNK/img.webp&quot; data-alt=&quot;TR 데이터 흐름&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNsea1/btsBQx2f71h/VcaGqYp8uLgnIKhIKR5cNK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNsea1%2FbtsBQx2f71h%2FVcaGqYp8uLgnIKhIKR5cNK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;887&quot; height=&quot;472&quot; data-filename=&quot;2.webp&quot; data-origin-width=&quot;887&quot; data-origin-height=&quot;472&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;TR 데이터 흐름&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림은 대략적인 TR 송수신 흐름의 스케치입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버의 매매전략 스레드 T1이 TR요청을 한다고 가정하면 송신부터 수신까지 대략 9단계의 단계로 이루어집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1단계. TR Request 패킷 생성: TR의 Inblock을 생성하여 패킷 파라미터를 설정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2단계. 목적지 Client 선택: 가능한 클라이언트를 선택합니다. 클라이언트 모두에게 보낼 것인지, 특정 클라이언트에게 보낼 건지 정합니다. 매매신호는 모두에게 멀티캐스팅 해야 할 것이고 특정 종목의 차트는 가장 신뢰도가 높은 클라이언트를 선택하여 TR 패킷의 소켓정보를 설정합니다. 이 작업을 RIL_Tx 스레드에서 담당해도 되지만 다수의 매매 스레드에서 패킷을 요청할 때 RIL_Tx에 부하가 걸리므로 Caller가 목적지까지 정하여 패킷에 세팅합니다. 그리고 콜백을 받기 위해 자신의 TID와 MSG_ID, 그리고 LPARAM, RPARAM을 추가 셋팅합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3단계: Tx 스레드의 Tx Queue에 패킷을 Enqeue 합니다. 그리고 Tx 스레드에 Wakeup 이벤트를 발생시킵니다. 그럼 Tx 스레드가 깨어나서 Queue에서 패킷을 꺼냅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4단계: 패킷을 보내기 전, 패킷에게 ID를 할당하고 이를 패킷 헤더에 붙입니다. 그리고 패킷 자체를 Tx/Rx 관리테이블에 붙여둡니다. 클라이언트는 응답을 보낼 때 서버가 전송한 RequestID를 그대로 회신해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8단계:RX 스레드가 응답 패킷의 RequestID를 통해 송신 패킷 객체를 조회하고 그로부터 T1의 TID와 콜백 메시지 정보를 얻습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;9단계: 수신 데이터를 DataBase 객체에 저장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10단계: 8단계에서 조회한 T1의 정보로 T1에 콜백메시지와 파라미터를 전송합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;11단계: T1은 DabaBase를 조회하여 데이터를 획득합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별의별 예외상황이 있겠지만 송신단계에서 클라이언트 Disconnect는 송신 패킷을 Discard 하고 발신 스레드에는 콜백이벤트로 알립니다. 발신 스레드의 설정에 따라 Tx 스레드가 다른 클라이언트를 선택하게 할 수도 있습니다.&lt;/p&gt;</description>
      <category>프로그래밍/C | C++</category>
      <category>iocp</category>
      <category>multiplexing</category>
      <category>MUX</category>
      <category>tcpip</category>
      <category>windows iocp</category>
      <category>서버프로그래밍</category>
      <category>소켓프로그래밍</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/38</guid>
      <comments>https://noyecube.tistory.com/entry/Multiplexing-Client-Connections-1#entry38comment</comments>
      <pubDate>Fri, 30 Jun 2023 18:57:17 +0900</pubDate>
    </item>
    <item>
      <title>Network bridge 방식으로 증권사 API 활용하기-2</title>
      <link>https://noyecube.tistory.com/entry/Network-bridge-%EB%B0%A9%EC%8B%9D%EC%9C%BC%EB%A1%9C-%EC%A6%9D%EA%B6%8C%EC%82%AC-API-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0-2</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Network bridge 방식으로 증권사 API 활용하기-2-Memory Pool Implemantation&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 글에서 언급한 내용은 아래와 같습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;증권사 API 가 32비트로 제공되는데 따른 프로그램 개발 제약사항&lt;/li&gt;
&lt;li&gt;Network bridge 방식으로 트레이딩프로그램과 API커넥터 두 개의 프로그램 개발 필요성&lt;/li&gt;
&lt;li&gt;두 프로그램 간 데이터 통신용 패킷관리를 위한 메모리풀 구조 스케치&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f1f1f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번&amp;nbsp;글에서는&amp;nbsp;이전&amp;nbsp;글에서&amp;nbsp;언급한&amp;nbsp;메모리풀을&amp;nbsp;실제&amp;nbsp;코드로&amp;nbsp;구현하고&amp;nbsp;동작까지&amp;nbsp;확인하겠습니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f1f1f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f1f1f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;In the previous article, we mentioned the following:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #1f1f1f; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Program development constraints due to the 32-bit provision of the stock API&lt;/li&gt;
&lt;li&gt;The need to develop two programs, a trading program and an API connector, in a network bridge manner&lt;/li&gt;
&lt;li&gt;Sketch of the memory pool structure for packet management for data communication between the two programs&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f1f1f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;In this article, we will implement the memory pool mentioned in the previous article with actual code and verify its operation.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 데이터 타입 정의 및 Member Function(Init, Alloc, Free) 작성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 DMSS는 Data Management Services System의 약자라고 하였습니다. 그래서 관련 클래스파일 이름을 CDmssItem으로 하고 정의하는 데이터타입 명은 prefix DSM을 붙였습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CDmssItem.h&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;621&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/da1aJN/btsBJdC2UBd/hPipxFknlcrlSX5IPwZYF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/da1aJN/btsBJdC2UBd/hPipxFknlcrlSX5IPwZYF0/img.png&quot; data-alt=&quot;CDmssItem&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/da1aJN/btsBJdC2UBd/hPipxFknlcrlSX5IPwZYF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fda1aJN%2FbtsBJdC2UBd%2FhPipxFknlcrlSX5IPwZYF0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;657&quot; height=&quot;621&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;621&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CDmssItem&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1687319268310&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#define DMSS_DSM_ITEM_POOL_SIZE	(512)
#define DMSS_DSM_ITEM_PAYLOAD_SIZE	(1450)

typedef struct _dsm_item {
	_dsm_item* pNext;	다음 객체를 가리킬 포인터
	WORD wItemNo;			Array에서의 자기 자신 IndexNo, 진단, 디버깅 용도
	UINT16 uSizeAllocated;	lpPayload가 가리키고 있는 Chunk의 할당 사이즈
	UINT16 uUsedLenth;		lpPayload에 기록된 유효 데이터 Length
	PCHAR lpPayload;		Memory Chunk를 가리키는 포인터
	UINT8 uRefCnt;			이 객체를 참조하고 있는 레퍼런스 카운트(아직은 미사용)
	UINT8 uProtocol;		이 객체가 담고있는 데이터(패킷) 타입
	UINT8 uPriority;		이 객체의 전송큐 우선순위(아직은 미사용)

	_dsm_item() {
		ZeroMemory(this, sizeof(_dsm_item));
	}
} TST_DSM_ITEM, * PTST_DSM_ITEM;

typedef struct _dsm_item_pool {
	SRWLOCK srwlock;		Reader-Writer 락
	WORD wFreeItemsCnt;		메모리풀 가용량
	PTST_DSM_ITEM pFreeItems;	메모리 객체 head 포인터
	TST_DSM_ITEM arPool[DMSS_DSM_ITEM_POOL_SIZE];	메모리 객체 배열 512개
	CHAR arPayloadPool[DMSS_DSM_ITEM_POOL_SIZE][DMSS_DSM_ITEM_PAYLOAD_SIZE]; 메모리청크 512개
} TST_DSM_ITEM_POOL, *PTST_DSM_ITEM_POOL;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CDmssItem.cpp&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;420&quot; data-origin-height=&quot;139&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rsPHB/btsBHsHilsD/5pdkQKdArE3LVK3UEcNKkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rsPHB/btsBHsHilsD/5pdkQKdArE3LVK3UEcNKkk/img.png&quot; data-alt=&quot;생성자 호출되면 메모리풀 초기화 실행&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rsPHB/btsBHsHilsD/5pdkQKdArE3LVK3UEcNKkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrsPHB%2FbtsBHsHilsD%2F5pdkQKdArE3LVK3UEcNKkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;420&quot; height=&quot;139&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;420&quot; data-origin-height=&quot;139&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;생성자 호출되면 메모리풀 초기화 실행&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;1026&quot; data-origin-height=&quot;425&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqDLIA/btsBMP2XKf7/ngIvhRJrKXCim5TJmzJfzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqDLIA/btsBMP2XKf7/ngIvhRJrKXCim5TJmzJfzk/img.png&quot; data-alt=&quot;메모리풀 초기화&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqDLIA/btsBMP2XKf7/ngIvhRJrKXCim5TJmzJfzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqDLIA%2FbtsBMP2XKf7%2FngIvhRJrKXCim5TJmzJfzk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1026&quot; height=&quot;425&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;1026&quot; data-origin-height=&quot;425&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;메모리풀 초기화&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1687319844778&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void CDmssItem::_vInit_TST_DSM_ITEM_POOL(PTST_DSM_ITEM_POOL pPool)
{
	PTST_DSM_ITEM p;

	if (nullptr == pPool)
		return;

	InitializeSRWLock(&amp;amp;(pPool-&amp;gt;srwlock)); 락 초기화
    CAutoScopeSRWLOCKExclusive lock(&amp;amp;(pPool-&amp;gt;srwlock)); 쓰기락 걸고 초기화 시작
    p = nullptr; 단방향 linked list이므로 head가 아닌 tail부터 초기화 
    for (int i = DMSS_DSM_ITEM_POOL_SIZE - 1; i &amp;gt;= 0; i--) {
    pPool-&amp;gt;arPool[i].pNext = p;		첫 진입시 tail의 pNext는 nullptr로 초기화 됨
    p = &amp;amp;(pPool-&amp;gt;arPool[i]); 이후 N-1 노드가 N 노드를 가리키며 0노드까지 순환함				
		p-&amp;gt;wItemNo = i;		메모리풀에서의 ARRAY INDEX 저장(디버깅, 진단 목적)		
        p-&amp;gt;uSizeAllocated = DMSS_DSM_ITEM_PAYLOAD_SIZE;	할당 사이즈 1450 저장		
        p-&amp;gt;uUsedLenth = 0;													
        p-&amp;gt;lpPayload = pPool-&amp;gt;arPayloadPool[i];								
        p-&amp;gt;uRefCnt = 0;														
		p-&amp;gt;uProtocol = 0; /* 0 - UNKNOWN */									
        p-&amp;gt;uPriority = 0;													
	}
    pPool-&amp;gt;pFreeItems = p;
    pPool-&amp;gt;wFreeItemsCnt = DMSS_DSM_ITEM_POOL_SIZE;

	return;
   }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1039&quot; data-origin-height=&quot;632&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6alwF/btsBRR6Zbdr/s2dKk5ujlkkZQ5GFtoopb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6alwF/btsBRR6Zbdr/s2dKk5ujlkkZQ5GFtoopb1/img.png&quot; data-alt=&quot;pAlloc_PTST_DSM_ITEM_CHAIN&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6alwF/btsBRR6Zbdr/s2dKk5ujlkkZQ5GFtoopb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6alwF%2FbtsBRR6Zbdr%2Fs2dKk5ujlkkZQ5GFtoopb1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1039&quot; height=&quot;632&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1039&quot; data-origin-height=&quot;632&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;pAlloc_PTST_DSM_ITEM_CHAIN&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;alloc 함수의 인자는 할당할 메모리 사이즈를 의미하는 게 아닌 사전 할당된 규격의 chunk가 몇 개 연결된 Chain의 Head 포인터를 반환받을지를 의미한다.&lt;/p&gt;
&lt;pre id=&quot;code_1687320845874&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PTST_DSM_ITEM CDmssItem::pAlloc_PTST_DSM_ITEM_CHAIN(const WORD wQuantity)
{
	PTST_DSM_ITEM pHead = nullptr;
	PTST_DSM_ITEM pTail = nullptr;
	PTST_DSM_ITEM_POOL pPool = &amp;amp;_m_poolDSMITEM;
	WORD wAssigned;

	CAutoScopeSRWLOCKExclusive lock(&amp;amp;(pPool-&amp;gt;srwlock));

	if ((wQuantity &amp;lt;= 0) || (pPool-&amp;gt;wFreeItemsCnt &amp;lt; wQuantity)) {
		DPRINTF(&quot;[ERROR] pAlloc_PTST_DSM_ITEM_CHAIN wQuantity(%d), pPool-&amp;gt;wFreeItemsCnt(%d)\n&quot;, 
        wQuantity, pPool-&amp;gt;wFreeItemsCnt);
		return nullptr;
	}

	pHead = pPool-&amp;gt;pFreeItems;	후입선출로 반환이므로 얘는 Chain의 Head가 됨
	pTail = pPool-&amp;gt;pFreeItems;	얘는 wQuantity만큼 다음 객체로 넘어가서
    마지막에 도달하면 pNext를 nullptr 로 마무리함

	wAssigned = 0;
	while (pTail) {
		DPRINTF(&quot;DBG] Allocated PTST_DSM_ITEM::wItemNo(%d)\n&quot;, pTail-&amp;gt;wItemNo);
		wAssigned++; 루프 진입전 풀에서 헤드 할당 했으므로 1개 할당
		if (wQuantity == wAssigned) { 요청받은 수만큼 할당 됐나 확인
			break;
		}

		pTail = pTail-&amp;gt;pNext; 다음 메모리풀 객체로 이동
	};

	if (wAssigned &amp;lt; wQuantity) {	/* Should never reach here */ 
		DPRINTF(&quot;[ERROR] pAlloc_PTST_DSM_ITEM_CHAIN wQuantity(%d), wAssigned(%d), 
        pPool-&amp;gt;wFreeItemsCnt(%d)\n&quot;, wQuantity, wAssigned, pPool-&amp;gt;wFreeItemsCnt);
		return nullptr;
	}

	pPool-&amp;gt;pFreeItems = pTail-&amp;gt;pNext; 할당 체인의 다음 객체가 메모리풀의 헤드가 됨
	pPool-&amp;gt;wFreeItemsCnt -= wAssigned;할당한 만큼 빈 객체 수 차감
	pTail-&amp;gt;pNext = nullptr; Tail의 Next를 nullptr로 봉인함

	DPRINTF(&quot;DBG] Head ItemNo(%d) PTST_DSM_ITEM::wFreeItemsCnt(%d)\n&quot;, 
    pPool-&amp;gt;pFreeItems-&amp;gt;wItemNo, pPool-&amp;gt;wFreeItemsCnt);

	return pHead;

}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;943&quot; data-origin-height=&quot;483&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wOPU1/btsBQpJIkuD/JfafqKRgMrP4tIKmaKnKX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wOPU1/btsBQpJIkuD/JfafqKRgMrP4tIKmaKnKX1/img.png&quot; data-alt=&quot;vFree_PTST_DSM_ITEM_CHAIN&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wOPU1/btsBQpJIkuD/JfafqKRgMrP4tIKmaKnKX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwOPU1%2FbtsBQpJIkuD%2FJfafqKRgMrP4tIKmaKnKX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;943&quot; height=&quot;483&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;943&quot; data-origin-height=&quot;483&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;vFree_PTST_DSM_ITEM_CHAIN&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Free는 할당때 처럼 Linked List 순환 시 메모리풀에 락을 걸지 않고 먼저 파라미터로 입력받은 노드 체인의 Tail을 찾아낸 다음에 메모리풀에 락을 걸고 순식간에 반환하고 끝냅니다. 노드 1개씩 메모리풀에 반환하지 않고 사용했던 노드 체인의 맨 끝을 찾아내서 그걸 메모리풀 맨 앞에 붙이고 끝낸다는.&lt;/p&gt;
&lt;pre id=&quot;code_1687321001611&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void CDmssItem::vFree_PTST_DSM_ITEM_CHAIN(PTST_DSM_ITEM p)
{
	PTST_DSM_ITEM_POOL pPool = &amp;amp;_m_poolDSMITEM;
	PTST_DSM_ITEM pTail;
	WORD wFreeCnt = 0;

	if (nullptr == p)
		return;

	pTail = p;
	pTail-&amp;gt;uUsedLenth = 0;
	wFreeCnt++;
	DPRINTF(&quot;DBG] Being Free PTST_DSM_ITEM::wItemNo(%d)\n&quot;, pTail-&amp;gt;wItemNo);
	while (pTail-&amp;gt;pNext) {
		pTail = pTail-&amp;gt;pNext; 실제 메모리풀 건드리지 않고 일단 체인의 끝을 찾음
		pTail-&amp;gt;uUsedLenth = 0;
		wFreeCnt++;
		DPRINTF(&quot;DBG] Being Free PTST_DSM_ITEM::wItemNo(%d)\n&quot;, pTail-&amp;gt;wItemNo);
	};

	AcquireSRWLockExclusive(&amp;amp;(pPool-&amp;gt;srwlock)); 실제 메모리풀 건드릴것이므로 락검

	pTail-&amp;gt;pNext = pPool-&amp;gt;pFreeItems; 반환 체인의 다음을 FreeItem 연결함
	pPool-&amp;gt;pFreeItems = p; 메모리풀의 head를 반환체인의 head로 바꿈
	pPool-&amp;gt;wFreeItemsCnt += wFreeCnt; 체인에 포함된 객체 수 만큼 프리카운트 증가시킴

	ReleaseSRWLockExclusive(&amp;amp;(pPool-&amp;gt;srwlock)); 락 해제

	DPRINTF(&quot;DBG] Head ItemNo(%d) PTST_DSM_ITEM::wFreeItemsCnt(%d)\n&quot;, pPool-&amp;gt;pFreeItems-&amp;gt;wItemNo, pPool-&amp;gt;wFreeItemsCnt);

	return;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;2. 메모리 풀 사용한 트레이딩프로그램 Login 테스트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 커넥터가 서버(트레이딩 프로그램)로 접속시 가장 먼저 전송해야 할 Initial Packet은 로그인정보로 결정했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 커넥터가 트레이딩 프로그램으로 접속할 수 있는 조건은 증권사에 접속한 상태에서 증권사 서버에 조회한 사용자 닉네임과 계정명(그러므로 API 커넥터 실행자가 임의 조작 불가)과 트레이딩프로그램에서 별도 생성해 준 트레이딩프로그램의 계정과 비밀번호입니다. 트레이딩프로그램의 계정은 이메일주소 형식을 따를 예정이고 계정생성 시 이메일 인증할 예정입니다. 앞서 정의한 메모리풀과 별개로 로그인프로토콜을 정의해야 합니다. 로그인프로토콜 패킷이 메모리풀에 담겨 전송큐에 들어가면 전송스레드가 소켓을 통해 전송할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 프로토콜 내용은 말그대로 프로토콜이므로 서버와 클라이언트 양측이 똑같은 내용을 가지고 있어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로토콜 패킷 정의 부분은 메모리 최적화를 못하도록 컴파일러지시자 #pragma를 사용하여 메모리패킹값 1로 지정합니다.(#pragma pack(1))&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;559&quot; data-origin-height=&quot;804&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5Ftl7/btsBQm0uo2h/IlAR4LpwIgdKyKyOZ9r08k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5Ftl7/btsBQm0uo2h/IlAR4LpwIgdKyKyOZ9r08k/img.png&quot; data-alt=&quot;RIL Protocol, RIL_AUTH_REQUEST_PARAMS_TYPE&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5Ftl7/btsBQm0uo2h/IlAR4LpwIgdKyKyOZ9r08k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5Ftl7%2FbtsBQm0uo2h%2FIlAR4LpwIgdKyKyOZ9r08k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;559&quot; height=&quot;804&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;559&quot; data-origin-height=&quot;804&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RIL Protocol, RIL_AUTH_REQUEST_PARAMS_TYPE&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RIL은 Radio Interface Layer의 약자입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API커넥터가 서버 연결에 성공 후 가장 먼저 전송하는 패킷은 _auth_request_params_type이고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 측에서 프로토콜 파악을 할 수 있도록 패킷 가장 앞에 TEN_RIL_RQ_CAT을 두고 그 값은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ESTABLISH_CLIENT_CONNECTION으로 정했습니다. 서버 입장에서는 지금 접속한 API커넥터가 어느 증권사를 지원하는지 알아야 하므로 TEN_STOCK_API_CAT에 값 XING을 넘기기로 하였습니다. 그리고 증권사 계정별로 API에서 거래 가능한 마켓이 다르므로 거래가능 마켓은 MASK에 표기하여 서버측에 전달하기로 하였습니다. 그리고 네 개의 TCHAR 문자열은 계정명과 패스워드, 증권사 계정명과 닉네임입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트코드 작성에 앞서 테스트 시나리오를 적어봅니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;클라이언트가 지정된 서버 IP, 포트로 TCP 연결 체결, Connect함수 성공값 반환받으면&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Initial Packet(로그인 패킷, _auth_request_params_type) 작성하여 전송&lt;/li&gt;
&lt;li&gt;서버 측에서 클라이언트가 전송한 파라미터 그대로 알아들었는지 수신 패킷 디버그 메시지 출력&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트코드를 작성해 봅니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;454&quot; data-origin-height=&quot;223&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIVQS9/btsBJgzLHqf/qhHPIZOKKyjcvz9lCqFCC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIVQS9/btsBJgzLHqf/qhHPIZOKKyjcvz9lCqFCC1/img.png&quot; data-alt=&quot;API 커넥터 로그인 창&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIVQS9/btsBJgzLHqf/qhHPIZOKKyjcvz9lCqFCC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIVQS9%2FbtsBJgzLHqf%2FqhHPIZOKKyjcvz9lCqFCC1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;454&quot; height=&quot;223&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;454&quot; data-origin-height=&quot;223&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;API 커넥터 로그인 창&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API커넥터에 로그인창을 만들었습니다. 로그인 버튼을 누르면 위 테스트 코드가 실행됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;1079&quot; data-origin-height=&quot;686&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Tvdla/btsBGXOhu34/aZjWqrE7yZ5RmqZBIk8msk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Tvdla/btsBGXOhu34/aZjWqrE7yZ5RmqZBIk8msk/img.png&quot; data-alt=&quot;OnBnClickedNcBtnLogin()&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Tvdla/btsBGXOhu34/aZjWqrE7yZ5RmqZBIk8msk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTvdla%2FbtsBGXOhu34%2FaZjWqrE7yZ5RmqZBIk8msk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1079&quot; height=&quot;686&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;1079&quot; data-origin-height=&quot;686&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;OnBnClickedNcBtnLogin()&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1687322588043&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;	errRet = theApp.m_RilClient.RilInit(_T(&quot;127.0.0.1&quot;), 65500);
	if (NOYECUBE_ESUCCESS == errRet) {
		cstrTemp.Append(_T(&quot;Established. Account authentication in progress&quot;));

		PTST_DSM_ITEM pItem = theApp.m_RilClient.m_DMSS.pAlloc_PTST_DSM_ITEM_CHAIN(1);
		if (pItem != nullptr) {
			PTST_RIL_AUTH_REQUEST_PARAMS_TYPE p = reinterpret_cast&amp;lt;PTST_RIL_AUTH_REQUEST_PARAMS_TYPE&amp;gt;(pItem-&amp;gt;lpPayload + pItem-&amp;gt;uUsedLenth);

			p-&amp;gt;enRilRequest = TEN_RIL_RQ_CAT::ESTABLISH_CLIENT_CONNECTION;
			p-&amp;gt;enApiCat = TEN_STOCK_API_CAT::XING;
			p-&amp;gt;u8TradeSupportMask |= API_TRADE_SUPPORT_STOCK;

			_tcsncpy_s(p-&amp;gt;tchNoyeNetId, _countof(TST_RIL_AUTH_REQUEST_PARAMS_TYPE::tchNoyeNetId), _T(&quot;TEST_NAME&quot;), 10);
			_tcsncpy_s(p-&amp;gt;tchNoyeNetPasswd, _countof(TST_RIL_AUTH_REQUEST_PARAMS_TYPE::tchNoyeNetPasswd), _T(&quot;TEST_PASS&quot;), 10);
			_tcsncpy_s(p-&amp;gt;tchStockId, _countof(TST_RIL_AUTH_REQUEST_PARAMS_TYPE::tchStockId), _T(&quot;TEST_IDID&quot;), 10);
			_tcsncpy_s(p-&amp;gt;tchUserName, _countof(TST_RIL_AUTH_REQUEST_PARAMS_TYPE::tchUserName), _T(&quot;TEST_NICK&quot;), 10);

			pItem-&amp;gt;uUsedLenth += sizeof(TST_RIL_AUTH_REQUEST_PARAMS_TYPE);
			pItem-&amp;gt;uProtocol = static_cast&amp;lt;UINT8&amp;gt;(TEN_PROTO_CAT::TST_RIL_AUTH_REQUEST_PARAMS_TYPE);

			theApp.m_RilClient.m_qDataTx.push(pItem);
			SetEvent(theApp.m_RilClient.m_hEvent_Wakeup_Tx);
		}

	}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 연결에 성공하면 메모리풀에서 메모리객체 한 개를 할당받습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687322653685&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PTST_DSM_ITEM pItem = theApp.m_RilClient.m_DMSS.pAlloc_PTST_DSM_ITEM_CHAIN(1);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Chunk에 로그인패킷을 기록할 거니까 CHAR* 타입의 Chunk를 아래와 같이 타입 캐스팅 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐스팅할 때 포인터 시작점이 lpPayload가 아니라 lpPayload+uUsedLength 인 점 유의 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리풀의 객체는 패킷을 1개만 담는 용도가 아니라 가용가능한 1450 바이트의 사이즈까지 최대한 채우기 위한&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;용도로 정의되었으므로 새로운 데이터를 기록할 위치는 Chunk의 시작점이 아니라 Chunk의 시작점 + 유효데이터길이만큼 지난 그다음지점부터입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687322714397&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PTST_RIL_AUTH_REQUEST_PARAMS_TYPE p = reinterpret_cast&amp;lt;PTST_RIL_AUTH_REQUEST_PARAMS_TYPE&amp;gt;(pItem-&amp;gt;lpPayload + pItem-&amp;gt;uUsedLenth);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인 패킷 셋업 부분입니다. RIL Request는 ESTABLISH_CLIENT_CONNECTINON으로 지정했고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 카테고리는 xingAPI, 지원하는 마켓은 주식 현물을 의미하는 API_TRADE_SUPPORT_STOCK으로 지정했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나머지 네 줄은 계정정보인데 테스트값을 입력하였습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687322844515&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;			p-&amp;gt;enRilRequest = TEN_RIL_RQ_CAT::ESTABLISH_CLIENT_CONNECTION;
			p-&amp;gt;enApiCat = TEN_STOCK_API_CAT::XING;
			p-&amp;gt;u8TradeSupportMask |= API_TRADE_SUPPORT_STOCK;

			_tcsncpy_s(p-&amp;gt;tchNoyeNetId, _countof(TST_RIL_AUTH_REQUEST_PARAMS_TYPE::tchNoyeNetId), _T(&quot;TEST_NAME&quot;), 10);
			_tcsncpy_s(p-&amp;gt;tchNoyeNetPasswd, _countof(TST_RIL_AUTH_REQUEST_PARAMS_TYPE::tchNoyeNetPasswd), _T(&quot;TEST_PASS&quot;), 10);
			_tcsncpy_s(p-&amp;gt;tchStockId, _countof(TST_RIL_AUTH_REQUEST_PARAMS_TYPE::tchStockId), _T(&quot;TEST_IDID&quot;), 10);
			_tcsncpy_s(p-&amp;gt;tchUserName, _countof(TST_RIL_AUTH_REQUEST_PARAMS_TYPE::tchUserName), _T(&quot;TEST_NICK&quot;), 10);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로토콜 패킷(RIL Packet)이 아닌 메모리풀 관리 정보를 마지막에 메모리 객체에 입력해 줍니다. 현재 메모리 객체에 어떤 정보가 담겨있는지에 대한 정보입니다. 사용한만큼 uUsedLength 값을 증가시켜주고 프로토콜에 어떤 프로토콜인지 입력해줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687322937085&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;			pItem-&amp;gt;uUsedLenth += sizeof(TST_RIL_AUTH_REQUEST_PARAMS_TYPE);
			pItem-&amp;gt;uProtocol = static_cast&amp;lt;UINT8&amp;gt;(TEN_PROTO_CAT::TST_RIL_AUTH_REQUEST_PARAMS_TYPE);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패킷 작성이 완료되었으면 아래와 같이 패킷 Chain의 head를 전송큐에 push 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1687323317305&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;			theApp.m_RilClient.m_qDataTx.push(pItem);
			SetEvent(theApp.m_RilClient.m_hEvent_Wakeup_Tx);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전송큐에 데이터 삽입 후 전송스레드에 wakeup 이벤트를 발생시켜 깨우면 전송스레드가 깨어나서 패킷을 서버로 전송합니다. 아래와 같이 패킷 Chain head 포인터 p가 유효하면 uUsedLength 만큼씩 send를 호출하고 데이터 전송이 완료되면 루프를 탈출 후 vFree_PTST_DSM_ITEM_CHAIN을 호출하여 메모리 객체 노드를 다시 반환합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;873&quot; data-origin-height=&quot;198&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k4Y5O/btsBT1BgyKQ/psGezIFYxBCKb5KboxYKD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k4Y5O/btsBT1BgyKQ/psGezIFYxBCKb5KboxYKD1/img.png&quot; data-alt=&quot;Tx Thread 패킷 send 호출 부분&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k4Y5O/btsBT1BgyKQ/psGezIFYxBCKb5KboxYKD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk4Y5O%2FbtsBT1BgyKQ%2FpsGezIFYxBCKb5KboxYKD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;873&quot; height=&quot;198&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;873&quot; data-origin-height=&quot;198&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Tx Thread 패킷 send 호출 부분&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진짜 테스트, 메모리풀 제대로 동작하는지 보려고 데이터 Chunk 요청수를 1이 아닌 3으로 해보겠습니다. 그럼 Chunk 두 개는 데이터가 없이 0이므로 빈 패킷이 전송될 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 대기 화면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;1102&quot; data-origin-height=&quot;289&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8FJ2K/btsBQV2Ugjc/Iuw140srsEvOhrHGiMze4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8FJ2K/btsBQV2Ugjc/Iuw140srsEvOhrHGiMze4k/img.png&quot; data-alt=&quot;서버 측 대기 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8FJ2K/btsBQV2Ugjc/Iuw140srsEvOhrHGiMze4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8FJ2K%2FbtsBQV2Ugjc%2FIuw140srsEvOhrHGiMze4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1102&quot; height=&quot;289&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;1102&quot; data-origin-height=&quot;289&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;서버 측 대기 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트 접속 요청 화면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디버깅 메시지를 확인하면 vAllock_PTST_DSM_ITEM_CHAIN(3)이 호출되어 실제로 메모리 객체 0번, 1번, 2번 총 3개를 할당했고 1번 객체에 로그인패킷이 기록되어 서버로 전송되었습니다. 그 뒤에 다시 vFree_PTST_DSM_ITEM_CHAIN이 호출되어 0, 1, 2번 객체가 제대로 반환되어 wFreeItemCnt값이 전체 메모리 풀 객체 수 512를 표시하는 것을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;1103&quot; data-origin-height=&quot;605&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mYLZ3/btsBJfubCIz/qOeFKuUHC9YMf9jZ6ThL9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mYLZ3/btsBJfubCIz/qOeFKuUHC9YMf9jZ6ThL9k/img.png&quot; data-alt=&quot;네트워크 로그&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mYLZ3/btsBJfubCIz/qOeFKuUHC9YMf9jZ6ThL9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmYLZ3%2FbtsBJfubCIz%2FqOeFKuUHC9YMf9jZ6ThL9k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1103&quot; height=&quot;605&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;1103&quot; data-origin-height=&quot;605&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;네트워크 로그&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 서버 화면을 보면 클라이언트에서 전송한 로그인패킷을 제대로 전송받은 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;1086&quot; data-origin-height=&quot;606&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/elJ33B/btsBJdC2VBL/uT9TZrj21bIMbbQ4hfMw2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/elJ33B/btsBJdC2VBL/uT9TZrj21bIMbbQ4hfMw2k/img.png&quot; data-alt=&quot;클라이언트 접속 로그&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/elJ33B/btsBJdC2VBL/uT9TZrj21bIMbbQ4hfMw2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FelJ33B%2FbtsBJdC2VBL%2FuT9TZrj21bIMbbQ4hfMw2k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1086&quot; height=&quot;606&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;1086&quot; data-origin-height=&quot;606&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;클라이언트 접속 로그&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 측 테스트 코드입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;1238&quot; data-origin-height=&quot;180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNenYr/btsBQW8xDxV/vKGAMnSchOqClGohzvZm90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNenYr/btsBQW8xDxV/vKGAMnSchOqClGohzvZm90/img.png&quot; data-alt=&quot;서버 테스트 코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNenYr/btsBQW8xDxV/vKGAMnSchOqClGohzvZm90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNenYr%2FbtsBQW8xDxV%2FvKGAMnSchOqClGohzvZm90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1238&quot; height=&quot;180&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;1238&quot; data-origin-height=&quot;180&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;서버 테스트 코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1687323726483&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;				DPRINTF(&quot;[DBG][%d] *** Client sent data.(%s)\n&quot;, GetCurrentThreadId(), psi-&amp;gt;_buff);
				{
					PTST_RIL_AUTH_REQUEST_PARAMS_TYPE p = reinterpret_cast&amp;lt;PTST_RIL_AUTH_REQUEST_PARAMS_TYPE&amp;gt;(psi-&amp;gt;_buff);
					if (p-&amp;gt;enRilRequest == TEN_RIL_RQ_CAT::ESTABLISH_CLIENT_CONNECTION) {
						DPRINTF(&quot;TEN_RIL_RQ_CAT: (%d)\nTEN_STOCK_API_CAT: (%d)\nTradeSupportMask : (%d)\ntchNoyeNetId : (%s)\ntchNoyeNetPasswd: (%s)\ntchStockId: (%s)\ntchUserName: (%s)\n&quot;,
							static_cast&amp;lt;UINT&amp;gt;(p-&amp;gt;enRilRequest), static_cast&amp;lt;UINT&amp;gt;(p-&amp;gt;enApiCat), static_cast&amp;lt;UINT&amp;gt;(p-&amp;gt;u8TradeSupportMask),
							p-&amp;gt;tchNoyeNetId, p-&amp;gt;tchNoyeNetPasswd, p-&amp;gt;tchStockId, p-&amp;gt;tchUserName);
					}

				}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상으로 메모리 풀을 구현해 보았습니다.&lt;/p&gt;</description>
      <category>프로그래밍/C | C++</category>
      <category>Memory Pool</category>
      <category>Recv</category>
      <category>send</category>
      <category>SetEvent</category>
      <category>t8430</category>
      <category>XINGAPI</category>
      <category>링크드리스트</category>
      <category>메모리풀</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/35</guid>
      <comments>https://noyecube.tistory.com/entry/Network-bridge-%EB%B0%A9%EC%8B%9D%EC%9C%BC%EB%A1%9C-%EC%A6%9D%EA%B6%8C%EC%82%AC-API-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0-2#entry35comment</comments>
      <pubDate>Wed, 21 Jun 2023 12:48:11 +0900</pubDate>
    </item>
    <item>
      <title>Network bridge 방식으로 증권사 API 활용하기-1</title>
      <link>https://noyecube.tistory.com/entry/Network-bridge-%EB%B0%A9%EC%8B%9D%EC%9C%BC%EB%A1%9C-%EC%A6%9D%EA%B6%8C%EC%82%AC-API-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0-1</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;증권사에서 제공하는 COM, OCX, DLL 포맷의 API 라이브러리는 32비트로 개발되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 개발 프로그램에는 가상 메모리 공간 4GB 크기의 제약이 있고 64비트 기반의 최신 라이브러리와 연동하기 어려운 점이 있습니다. 그래서 API 라이브러리는 증권사 서버와의 커넥터 역할만 하도록 하고 별도의 로직 프로그램을 64비트로 개발하여 커넥터와 로직 프로그램을 연결하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC에 설치되어 있는 HTS의 OCX 모듈과 연계하는 방식이 아닌 DLL 연계 방식의 API는 커넥터를 여러 개 실행하여 다계정으로 API를 사용할 수 있게 되어 보다 고도화된 프로그램 로직 구현이 가능합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Network bridge 방식으로 증권사 API 활용하기-1-Overview&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;증권사에서 제공하는 API는 32 비트 기반이기 때문에 트레이딩 프로그램을 64 비트로 개발하려면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램을 API커넥터와 트레이딩프로그램 두 개로 분리하여 개발해야 합니다. 두 프로그램간의 데이터 교환을 위한 socket 기반 통신 프로토콜을 정의해 보고 실제 API커넥터가 트레이딩프로그램으로 접속패킷을 전송하는 과정을 살펴보겠습니다. API커넥터 분리를 통해 증권사 API별 커넥터가 트레이딩 프로그램에 접속하여 다양한 증권사의 데이터를 제공할 수 있고 API커넥터의 다계정 접속을 통하여 트레이딩 프로그램은 TR 전송수 제한의 제약으로부터 벗어날 수 있습니다. &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;또한 역으로 트레이딩 프로그램은 분산되어 있는 다수의 커넥터에게 카피트레이딩을 위한 매매신호를 제공할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f1f1f; text-align: start;&quot;&gt;The API provided by the stock company is based on 32-bit, so to develop the trading program in 64-bit, you must develop the program in two parts: API connector and trading program. Let's define a socket-based communication protocol for data exchange between the two programs, and look at the process of the actual API connector sending a connection packet to the trading program. By separating the API connector, a connector for each stock company API can connect to the trading program and provide data from various stock companies, and the trading program can escape from the constraints of the TR transmission limit through the multi-account access of the API connector. In addition, in reverse, the trading program can provide trading signals for copy trading to a large number of connectors that are distributed.&quot;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 증권사 API 제약사항&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OCX, COM, DLL 방식으로 제공: Python, Go 등 다양한 언어에서 Native로 개발 어려움&lt;/li&gt;
&lt;li&gt;32 비트 API만 제공: 64 비트로 개발 불가, 64 비트 외부 라이브러리 사용 불가, 개발 프로그램 메모리 한계로 무한 상상력을 동원한 개발 불가&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;2. Network bridge 방식의 증권사 API 활용(이베스트투자증권 xingAPI 기준으로 설명)&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;32 비트 API 커넥터 프로그램 개발 : xingAPI의 DLL 활용하여 증권사 서버와 TR 직접 송수신&lt;/li&gt;
&lt;li&gt;64 비트 트레이딩 프로그램 개발 : 증권사 서버로 TR 요청은 32 비트 API 커넥터 프로그램을 통해 요청&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;3. 서버-클라이언트 구조&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;서버: 트레이딩프로그램이 IOCP 방식 서버 역할&lt;/li&gt;
&lt;li&gt;클라이언트: API커넥터가 클라이언트 역할&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;4. 패킷(프로토콜) 설계 고려사항(주식 전종목조회 T8430 기준으로)&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;384&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Pte5n/btsBKgmwMei/KoKzw3o5KiuW1VAatI3lek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Pte5n/btsBKgmwMei/KoKzw3o5KiuW1VAatI3lek/img.png&quot; data-alt=&quot;t8430 패킷 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Pte5n/btsBKgmwMei/KoKzw3o5KiuW1VAatI3lek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPte5n%2FbtsBKgmwMei%2FKoKzw3o5KiuW1VAatI3lek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;890&quot; height=&quot;384&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;384&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;t8430 패킷 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;t8430OutBlock의 사이즈는 77바이트. 전종목 요청을 하면 상장종목이 2600개 이상이니까 77바이트 x 2600개의 OutBlock이 API커넥터(클라이언트)로 도착하게 되는데 이걸 그대로 트레이딩프로그램(서버)으로 전송하려면 어떻게 해야 할까요.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;77바이트 OutBlock 1개씩 2600번 send 호출&lt;/li&gt;
&lt;li&gt;77바이트 X 2600개 담긴 메모리 포인터 Send에 한 번에 담기&lt;/li&gt;
&lt;li&gt;TCP/IP MTU 사이즈 1500 고려하여 마진 50바이트 잡고 1450 바이트씩 나눠서 전송&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3번 방식으로 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OutBlock은 별도 패킷 구분 헤더가 없는데? API커넥터는 TR 요청당시 받아둔 RequestID를 갖고 있다가 도착한 OutBlock의 RequestID를 비교하여 어떤 TR의 OutBlock인지 구분 가능. API커넥터가 서버로 OutBlock을 전송할 때는 OutBlock을 한번 Wrapping 하여 추가 헤더를 붙이기로 결정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패킷 메모리관리는 동적할당? 정적할당? 대부분의 경우 패킷별로 동적할당 하여 송수신 후 메모리를 해제하지만 비슷한 데이터를 연속으로 수천 개씩 주고받으므로 메모리풀 관리방식을 사용하기로 결정합니다. 돌아다니는 테스트 결과에 따르면 new/malloc을 그냥 사용하면 메모리풀을 사용하는 것보다 20배 정도 느립니다. 또한 Outblock별로 사이즈가 다르므로 Heap영역의 메모리 단편화도 피할 수 없습니다. 절대적인 관점(사람이 느끼는)에서 프로그램은 그래도 충분히 빠르지만 프로그램을 분리하면서 데이터 송수신 퍼포먼스에 조금이라도 핸디캡을 갖게 되었으므로 패킷은 메모리풀방식으로 관리하고 캐시 hit rate 향상을 위해 LIFO로 관리하기로 합니다. Consumer는 각각 송/수신 스레드 1개이고 Producer도 현재는 1개일 건데 생산자:소비자=N:1로 할 것이므로 LIFO에서 ABA Problem은 발생하지 않는 구조. 서버/클라이언트에 빠른 디버깅을 위해 Diagnostics 모듈을 붙일 건데 이로 인한 메모리상태 열람에 따른 오버헤드를 최소화하기 위하여 메모리락은 Critical Section 대신 Reader-Writer 락을 사용하기로 결정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;5. 패킷 메모리풀 구조 설계&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다수의 패킷을 체인(Packet Chain) 방식으로 관리할 것입니다. 단, 서로 다른 패킷의 혼합(Packet Aggregation)은 나중에 최적화 구현 때 하기로 하고 일단은 하나의 체인은 공간이 남아있어도 한 종류의 데이터 타입만 담고 뒤의 다른 타입 데이터는 안 담기로 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드로 구현할 메모리풀은 Data Management Services System(이하 DMSS)이라고 하고 구조는 아래 그림과 같습니다. PAYLOAD는 1450바이트로 할 것이고 512개를 정적으로 할당하여 관리하겠습니다. 512개의 기준은 T8430 OutBlock 사이즈 77바이트 곱하기 2600개 했을 때 적당한 선인 것 같아서 그렇게 정했습니다. 향후 기능개선을 하려면 메모리풀에 high water mark, low water mark 플래그를 두고 메모리풀이 동적으로 관리될 수 있도록 하면 됩니다만 저는 귀찮아서 그냥 이렇게 하는 대신 실제 구동할 때는 64GB 메모리 서버에서 엄청 여유있게 할당하려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동작 방식은 다음과 같습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;DSM_ITEM 타입을 정의하고 Linked List로 관리할 수 있도록 Next 포인터를 둔다.&lt;/li&gt;
&lt;li&gt;실제 데이터를 담을 Payload Chunk를 가리키는 포인터를 둔다. DSM_ITEM안에 정적으로 할당해도 되지만 Chunk끼리 연속된 메모리 공간을 갖도록 별도로 할당하고 포인팅 하기로 함. 향후 TR 응답 패킷의 메모리는 Release 호출하기 전까지는 반환이 안되니까(Real과 서비스 TR T1857은 해당 없음) lpPayload 포인터가 TR 응답 패킷을 직접 가리키도록 해도 되는데 이건 나중에 더 최적화 필요성을 느낄 때.&lt;/li&gt;
&lt;li&gt;메모리풀 초기화 때 각 DSM_ITEM을 List로 연결하고 Head를 메모리풀에 연결해 둠.&lt;/li&gt;
&lt;li&gt;&amp;nbsp;DSM_ITEM x개를 할당요청받으면 head부터 x개만큼 빼서 할당해 주고 반납 시 역으로 chain의 tail을 head에 붙이고 chain의 head를 메모리풀의 head로 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 읽고 아래 그림을 봤는데 동작방식이 이해가 안 되면 뒤로 가기 누르시고 그냥 new/malloc 사용하시면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1191&quot; data-origin-height=&quot;334&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biYF1G/btskJtvczMA/kwnXykW0sOApHPbSYe2fzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biYF1G/btskJtvczMA/kwnXykW0sOApHPbSYe2fzk/img.png&quot; data-alt=&quot;Data Management Services System&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biYF1G/btskJtvczMA/kwnXykW0sOApHPbSYe2fzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiYF1G%2FbtskJtvczMA%2FkwnXykW0sOApHPbSYe2fzk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1191&quot; height=&quot;334&quot; data-origin-width=&quot;1191&quot; data-origin-height=&quot;334&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Data Management Services System&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>프로그래밍/C | C++</category>
      <category>LIFO</category>
      <category>Linked List</category>
      <category>Memory Pool</category>
      <category>t8430</category>
      <category>XINGAPI</category>
      <category>링크드리스트</category>
      <category>메모리풀</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/34</guid>
      <comments>https://noyecube.tistory.com/entry/Network-bridge-%EB%B0%A9%EC%8B%9D%EC%9C%BC%EB%A1%9C-%EC%A6%9D%EA%B6%8C%EC%82%AC-API-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0-1#entry34comment</comments>
      <pubDate>Wed, 21 Jun 2023 00:12:46 +0900</pubDate>
    </item>
    <item>
      <title>[MFC] Modeless Dialog 생성하기</title>
      <link>https://noyecube.tistory.com/entry/MFC-Modeless-Dialog-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 MFC 환경에서 Modeless Dialog를 생성하고 사용하는 법을 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dialog에는 Modal과 Modeless가 있는데 Modal은 부모 윈도를 제어할 수 없지만 Modeless는 각각 독립적으로 동작하는 특징이 있어서 저는 Modeless를 선호합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Modeless Dialog의 Base Class는 CDialog입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Dialog 비교&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Modeless Dialog는 CDialog 기본 생성자를 호출하여 Dialg object를 생성하지만 DoModal() 대신 CDialog:Create 멤버 함수 호출이 필요합니다. Create()는 리소스 ID를 매개 변수로 사용하고 Dialog가 화면에 계속 표시된 상태로 즉시 반환됩니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 54px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;Modal Dialog&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;Modeless Dialog&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;생성자 호출방식&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;생성자 + RESOURCE ID&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 20px; text-align: center;&quot;&gt;기본 생성자 호출&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;윈도우 생성 함수&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;DoModal()&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;Create() + RESOURCE ID&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;2. 사용자 정의 메시지 사용&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;Dialog에서 Windows 메시지를 View로 보내서 이벤트 처리. SendMessage() 또는 PostMessage() 사용합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;3. Modeless Dialog 생성 예제 코드&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;실제 동작하는 예제 코드를 처음부터 작성해 보겠습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;MFC SDI 프로젝트 기준입니다.&lt;/p&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;3.1. Dialog Resouce 생성&lt;/h4&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;- 리소스 편집기에 가서 IDD_EBEST_SETTINGS라는 Dialog를 하나 생성하고 아래와 같이 컨트롤을 배치했습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;Dialog Properties의 Behavior-Visible 기본값이 False인데 True로 해주세요. 안 그러면 Create() 호출해도 안보입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;855&quot; data-origin-height=&quot;467&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyYOy1/btsBJehGep1/WVRKlYMTKZEOnKpE9tVldk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyYOy1/btsBJehGep1/WVRKlYMTKZEOnKpE9tVldk/img.png&quot; data-alt=&quot;다이얼로그 컨트롤 배치&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyYOy1/btsBJehGep1/WVRKlYMTKZEOnKpE9tVldk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcyYOy1%2FbtsBJehGep1%2FWVRKlYMTKZEOnKpE9tVldk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;855&quot; height=&quot;467&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;855&quot; data-origin-height=&quot;467&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;다이얼로그 컨트롤 배치&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 컨트롤 아이디는 아래와 같이 정했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;442&quot; data-origin-height=&quot;385&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FS85v/btsBQTDJ6jr/2E89tTK6xpiab9ea01ZNKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FS85v/btsBQTDJ6jr/2E89tTK6xpiab9ea01ZNKK/img.png&quot; data-alt=&quot;Resource.h&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FS85v/btsBQTDJ6jr/2E89tTK6xpiab9ea01ZNKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFS85v%2FbtsBQTDJ6jr%2F2E89tTK6xpiab9ea01ZNKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;442&quot; height=&quot;385&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;442&quot; data-origin-height=&quot;385&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Resource.h&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2. Dialog Class 생성 및 Member Variable 추가&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 리소스편집기에서 IDD_EBEST_SETTINGS Dialog를 우클릭 후 &quot;Add Class&quot; 메뉴를 클릭하여 클래스를 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;CDlgEbestSettings&quot; 이름으로 클래스를 생성했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Class Wizard를 열어서 아래와 같이 위에서 만든 각 컨트롤에 대응되는 멤버변수를 CDlgEbestSettings 클래스에 만들어줍니다. 저는 Control 타입의 변수를 선호해서 아래와 같이 추가했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;789&quot; data-origin-height=&quot;589&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBcTEr/btsBGXHtum4/Y0J3YBwTnKeG1irfCb0bvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBcTEr/btsBGXHtum4/Y0J3YBwTnKeG1irfCb0bvK/img.png&quot; data-alt=&quot;Class Wizard&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBcTEr/btsBGXHtum4/Y0J3YBwTnKeG1irfCb0bvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBcTEr%2FbtsBGXHtum4%2FY0J3YBwTnKeG1irfCb0bvK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;789&quot; height=&quot;589&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;789&quot; data-origin-height=&quot;589&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Class Wizard&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.3. 코드 작성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dialog 클래스에서는 Dialog UI 관련 처리만 하고 로직은 View 클래스를 통해서 다른 클래스와 상호작용하는 방식으로 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CDlgEbestSettings Dialog는 프로그램 메뉴에서 버튼을 클릭하면 그 이벤트 핸들러를 CView클래스에 두고 거기서 CDlgEbestSettings Dialog를 생성할 겁니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 아래와 같이 CDlgEbestSettings.h에 CView* 를 파라미터로 받는 생성자를 하나 선언해 주고, Create() 함수도 선언해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Private 변수로 CView* _m_pNoyecubeView; 도 하나 정의했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;869&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eESEGs/btsBQkVTzu0/94kKeIWykVvxt04SE5jWv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eESEGs/btsBQkVTzu0/94kKeIWykVvxt04SE5jWv0/img.png&quot; data-alt=&quot;헤더파일 내 컨트롤 변수&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eESEGs/btsBQkVTzu0/94kKeIWykVvxt04SE5jWv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeESEGs%2FbtsBQkVTzu0%2F94kKeIWykVvxt04SE5jWv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;558&quot; height=&quot;869&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;869&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;헤더파일 내 컨트롤 변수&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cpp파일에는 기본 생성자에 _m_pNoyecubeView를 NULL로 초기화 해주고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선언한 두 개의 함수를 작성하는데 생성자에는 넘겨받은 CView 포인터를 멤버변수에 저장하고 Create함수에서는 Dialog Resouce ID를 넘겨서 Dialog를 생성합니다. 여기에서의 Create()가 호출되려면 선행조건으로 기본생성자가 호출되어 이 Dialog의 객체가 생성되어 있어야 합니다. 그건 CView 클래스에서 합니다. CView 클래스는 SDI프로젝트 생성 시 &amp;lt;프로젝트명&amp;gt;+&quot;View&quot;로 자동 생성됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;621&quot; data-origin-height=&quot;580&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8nWL5/btsBQnrzdbz/k5KbiF5tHHvTGRfZUPKsnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8nWL5/btsBQnrzdbz/k5KbiF5tHHvTGRfZUPKsnK/img.png&quot; data-alt=&quot;포인터 변수 초기화 코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8nWL5/btsBQnrzdbz/k5KbiF5tHHvTGRfZUPKsnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8nWL5%2FbtsBQnrzdbz%2Fk5KbiF5tHHvTGRfZUPKsnK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;621&quot; height=&quot;580&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;621&quot; data-origin-height=&quot;580&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;포인터 변수 초기화 코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트명이 Noyecube라서 NoyecubeView.cpp가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가 작성한 부분은 OnMenuFuncEbestSettings()와 OnInitialUpdate()입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OnMenuFuncEbestSettings()는 메뉴에서 Dialog 생성버튼을 눌렀을 때 호출되는 핸들러입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OnInitialUpdate()는 CView 클래스가 생성되고 나서 호출되는 버추얼함수입니다. 여기에서 CDlgEbestSettings Dialog의 객체를 생성하면서 자기 자신(CView)의 객체 포인터를 인자로 넘겨줍니다. 그럼 나중에 CDlgEbestSettings Dialog 객체에서 이 포인터를 갖고 있다가 이벤트 메시지와 파라미터를 View로 전달하여 View에서 처리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OnInitialUpdate() 함수 추가는 Class View에 가서 View클래스를 선택한 상태로 Properties-Overrides 목록에서 OnInitialUpdate를 클릭하면 생성됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;681&quot; data-origin-height=&quot;367&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0l2Q9/btsBOhSUinN/vhUfdNWkZ7hxLPAn4ZVwT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0l2Q9/btsBOhSUinN/vhUfdNWkZ7hxLPAn4ZVwT0/img.png&quot; data-alt=&quot;생성 코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0l2Q9/btsBOhSUinN/vhUfdNWkZ7hxLPAn4ZVwT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0l2Q9%2FbtsBOhSUinN%2FvhUfdNWkZ7hxLPAn4ZVwT0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;681&quot; height=&quot;367&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;681&quot; data-origin-height=&quot;367&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;생성 코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자, 소멸자에서는 아래와 같이 m_pDlgEbestSettings 변수를 초기화 및 메모리 해제 해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;594&quot; data-origin-height=&quot;238&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZAukJ/btsBGWV75Tj/S1vy90c5gOojcwEwPHAbiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZAukJ/btsBGWV75Tj/S1vy90c5gOojcwEwPHAbiK/img.png&quot; data-alt=&quot;생성자 및 소멸자 코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZAukJ/btsBGWV75Tj/S1vy90c5gOojcwEwPHAbiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZAukJ%2FbtsBGWV75Tj%2FS1vy90c5gOojcwEwPHAbiK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;594&quot; height=&quot;238&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;594&quot; data-origin-height=&quot;238&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;생성자 및 소멸자 코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.4. Menu 바 Resource&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 만든 Dialog를 열 수 있게 프로그램 메뉴에 버튼을 추가합니다. &quot;Ebest Settings&quot;를 우 클릭 후 &quot;Add Event Handler&quot;를 클릭하여 CView 클래스에 핸들러를 추가해 줍니다. 그럼 위 3.3. 과 같이 OnMenuFuncEbestSettings()가 생깁니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;482&quot; data-origin-height=&quot;253&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmLmq9/btsBGV3Y7FN/VxVDPXvD4DRFaz4yxZm7s1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmLmq9/btsBGV3Y7FN/VxVDPXvD4DRFaz4yxZm7s1/img.png&quot; data-alt=&quot;메뉴바에 버튼 추가&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmLmq9/btsBGV3Y7FN/VxVDPXvD4DRFaz4yxZm7s1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmLmq9%2FbtsBGV3Y7FN%2FVxVDPXvD4DRFaz4yxZm7s1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;482&quot; height=&quot;253&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;482&quot; data-origin-height=&quot;253&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;메뉴바에 버튼 추가&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 하고 빌드 후 실행 해보면 위에서 만든 Dialog가 열립니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;850&quot; data-origin-height=&quot;582&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eyBZvS/btsBR90BZFS/2AWhNIntqLGstkhqiq4UX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eyBZvS/btsBR90BZFS/2AWhNIntqLGstkhqiq4UX1/img.png&quot; data-alt=&quot;실행된 Modeless 다이얼로그&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eyBZvS/btsBR90BZFS/2AWhNIntqLGstkhqiq4UX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeyBZvS%2FbtsBR90BZFS%2F2AWhNIntqLGstkhqiq4UX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;850&quot; height=&quot;582&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;850&quot; data-origin-height=&quot;582&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행된 Modeless 다이얼로그&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 &quot;닫기&quot; 버튼을 눌러도 안 닫히고 우측상단 &quot;X&quot;를 눌러서 창을 닫으면 다시 안 열립니다. DestroyWindow()가 호출되지 않았기 때문입니다. 각 버튼에 대한 이벤트핸들러를 추가하고, &quot;X&quot;에 대한 핸들러도 추가하여 Dialog가 닫혔을 때 DestroyWindow() 호출하여 다시 열리도록 합니다. &quot;X&quot;를 누르면 호출되는 OnClose() 함수를 Override 하여 &quot;닫기&quot; 버튼을 눌렀을 때와 동일하게 처리하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.5. Close 처리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.5.1. 핸들러 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &quot;닫기&quot; 버튼의 이벤트 핸들러를 CDlgEbestSettings 클래스에 아래와 같이 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- CDlgEbestSettings Class 속성의 Messages 메뉴에서&amp;nbsp; WM_CLOSE 메시지에 대한 핸들러 OnClose()를 추가합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;528&quot; data-origin-height=&quot;407&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byvRpD/btsBOhFnHFl/EblDBdAvXK5oCxaKRmOSSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byvRpD/btsBOhFnHFl/EblDBdAvXK5oCxaKRmOSSk/img.png&quot; data-alt=&quot;Close 핸들러 등록&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byvRpD/btsBOhFnHFl/EblDBdAvXK5oCxaKRmOSSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyvRpD%2FbtsBOhFnHFl%2FEblDBdAvXK5oCxaKRmOSSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;528&quot; height=&quot;407&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;528&quot; data-origin-height=&quot;407&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Close 핸들러 등록&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;998&quot; data-origin-height=&quot;211&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDP0lO/btsBMRl9QLW/rcV4mjD5Igmr9s9gEQfUNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDP0lO/btsBMRl9QLW/rcV4mjD5Igmr9s9gEQfUNK/img.png&quot; data-alt=&quot;OnClose 등록&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDP0lO/btsBMRl9QLW/rcV4mjD5Igmr9s9gEQfUNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDP0lO%2FbtsBMRl9QLW%2FrcV4mjD5Igmr9s9gEQfUNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;998&quot; height=&quot;211&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;998&quot; data-origin-height=&quot;211&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;OnClose 등록&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.5.2. Dialog 핸들러에서 View Class에 이벤트 전달할 Message 정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;App.h 파일에 CDlgEbestSettings Dialog에서 CView로 던질 버튼클릭이벤트 윈도 메시지를 정의합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WM_USER 또는 WM_APP을 윈도메시지 Offset으로 잡으면 되는데 WM_USER는 다른 DLL에서 하드코딩값으로 사용하고 있어서 WM_APP을 기준으로 잡았습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;101&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkXEkK/btsBKevnQx0/Ya2hiBklxlO85ydDNukbpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkXEkK/btsBKevnQx0/Ya2hiBklxlO85ydDNukbpk/img.png&quot; data-alt=&quot;메시지 등록&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkXEkK/btsBKevnQx0/Ya2hiBklxlO85ydDNukbpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkXEkK%2FbtsBKevnQx0%2FYa2hiBklxlO85ydDNukbpk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;844&quot; height=&quot;101&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;101&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;메시지 등록&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MSG_ID_CDLGEBESTSETTINGS_EVENT 메시지를 던질 때 파라미터로 이벤트값을 넘겨줘야 하는데 Resource.h에 정의되어 있는 ID값으로 어떤 컨트롤에 이벤트가 발생했는지 알려줘도 되지만 저는 switch문의 점프테이블이 0번부터 깔끔하게 다시 정렬되는 것을 원하므로 아래와 같이 Resource.h의 ID에 대응되는 이벤트를 별도 정의하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;648&quot; data-origin-height=&quot;431&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/x70G7/btsBQSdOKhU/zdQJKN0U6UCsFjVnR263vk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/x70G7/btsBQSdOKhU/zdQJKN0U6UCsFjVnR263vk/img.png&quot; data-alt=&quot;이벤트 정의&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/x70G7/btsBQSdOKhU/zdQJKN0U6UCsFjVnR263vk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fx70G7%2FbtsBQSdOKhU%2FzdQJKN0U6UCsFjVnR263vk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;648&quot; height=&quot;431&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;648&quot; data-origin-height=&quot;431&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이벤트 정의&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.5.3. View Class에 3.5.1. 에서 정의한 user-defined message 핸들러 작성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.5.1. 에서 정의한 MSG_ID_CDLGEBESTSETTINGS_EVENT 메시지는 Class Wizard에서 지원하지 않기 때문에 직접 작성합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;View.h에 핸들러원형 선언,&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;14.png&quot; data-origin-width=&quot;1391&quot; data-origin-height=&quot;128&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCSs4E/btsBHpDKImC/upYjo0vLnBweN4igs7pDA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCSs4E/btsBHpDKImC/upYjo0vLnBweN4igs7pDA1/img.png&quot; data-alt=&quot;핸들러 원형&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCSs4E/btsBHpDKImC/upYjo0vLnBweN4igs7pDA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCSs4E%2FbtsBHpDKImC%2FupYjo0vLnBweN4igs7pDA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1391&quot; height=&quot;128&quot; data-filename=&quot;14.png&quot; data-origin-width=&quot;1391&quot; data-origin-height=&quot;128&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;핸들러 원형&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;View.cpp의 MESSAGE_MAP에 message ID와 핸들러 맵핑, 핸들러 코드 작성합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;15.png&quot; data-origin-width=&quot;972&quot; data-origin-height=&quot;150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c5Xmfv/btsBKfnzpTO/K1mhgxKXAqtkI2gVaqF2m1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c5Xmfv/btsBKfnzpTO/K1mhgxKXAqtkI2gVaqF2m1/img.png&quot; data-alt=&quot;메시지맵 등록&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c5Xmfv/btsBKfnzpTO/K1mhgxKXAqtkI2gVaqF2m1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc5Xmfv%2FbtsBKfnzpTO%2FK1mhgxKXAqtkI2gVaqF2m1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;972&quot; height=&quot;150&quot; data-filename=&quot;15.png&quot; data-origin-width=&quot;972&quot; data-origin-height=&quot;150&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;메시지맵 등록&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;16.png&quot; data-origin-width=&quot;1123&quot; data-origin-height=&quot;822&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YybYz/btsBR9NajTF/R4b2tb0ap1xCJGkeagvHu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YybYz/btsBR9NajTF/R4b2tb0ap1xCJGkeagvHu1/img.png&quot; data-alt=&quot;분기 코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YybYz/btsBR9NajTF/R4b2tb0ap1xCJGkeagvHu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYybYz%2FbtsBR9NajTF%2FR4b2tb0ap1xCJGkeagvHu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1123&quot; height=&quot;822&quot; data-filename=&quot;16.png&quot; data-origin-width=&quot;1123&quot; data-origin-height=&quot;822&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;분기 코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 Dialog.cpp의 핸들러로 가서 아래와 같이 코드 작성합니다. View로 사용자정의 메시지와 이벤트 파라미터를 PostMessage로 보냅니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;17.png&quot; data-origin-width=&quot;1088&quot; data-origin-height=&quot;578&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cK2Wfh/btsBJidhVZI/2WNVMWmzRAXlY7phY9bgtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cK2Wfh/btsBJidhVZI/2WNVMWmzRAXlY7phY9bgtk/img.png&quot; data-alt=&quot;PostMessage 호출&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cK2Wfh/btsBJidhVZI/2WNVMWmzRAXlY7phY9bgtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcK2Wfh%2FbtsBJidhVZI%2F2WNVMWmzRAXlY7phY9bgtk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1088&quot; height=&quot;578&quot; data-filename=&quot;17.png&quot; data-origin-width=&quot;1088&quot; data-origin-height=&quot;578&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;PostMessage 호출&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 하면 &quot;닫기&quot;, &quot;x&quot; 버튼으로 다이얼로그 종료 시 DestoryWindow()가 호출되어 다시 Create 호출이 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 Esc, Enter 키에 대한 예외처리도 해둬야 합니다. Esc는 OnCancel(), Enter는 OnOK() 함수를 오버라이딩 후 코드내용을 삭제하면 됩니다. 또는 PreTranslateMessage에서 VK_ESCAPE, VK_RETURN을 무시해도 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;18.png&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;230&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8nOIO/btsBQicJYLI/W7yDXsya3keP6jskgUhJkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8nOIO/btsBQicJYLI/W7yDXsya3keP6jskgUhJkk/img.png&quot; data-alt=&quot;이벤트 핸들러 오버라이드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8nOIO/btsBQicJYLI/W7yDXsya3keP6jskgUhJkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8nOIO%2FbtsBQicJYLI%2FW7yDXsya3keP6jskgUhJkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;360&quot; height=&quot;230&quot; data-filename=&quot;18.png&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;230&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이벤트 핸들러 오버라이드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;19.png&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;376&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bg69Kk/btsBTZKs0eo/TVJKmkkYi5LUmG6JKH0ukK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bg69Kk/btsBTZKs0eo/TVJKmkkYi5LUmG6JKH0ukK/img.png&quot; data-alt=&quot;이벤트 핸들러에서 해당 이벤트 무시 처리&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bg69Kk/btsBTZKs0eo/TVJKmkkYi5LUmG6JKH0ukK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbg69Kk%2FbtsBTZKs0eo%2FTVJKmkkYi5LUmG6JKH0ukK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;868&quot; height=&quot;376&quot; data-filename=&quot;19.png&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;376&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이벤트 핸들러에서 해당 이벤트 무시 처리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상으로 MFC 환경에서 Modeless Dialog 생성방법을 실제 예제코드 기반으로 알아보았습니다.&lt;/p&gt;</description>
      <category>프로그래밍/C | C++</category>
      <category>DestroyWindow</category>
      <category>message map</category>
      <category>modeless dialog</category>
      <category>모달리스</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/32</guid>
      <comments>https://noyecube.tistory.com/entry/MFC-Modeless-Dialog-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0#entry32comment</comments>
      <pubDate>Mon, 12 Sep 2022 02:23:57 +0900</pubDate>
    </item>
    <item>
      <title>[MFC] Build Openssl Statically Linked Against Windows</title>
      <link>https://noyecube.tistory.com/entry/MFC-Build-Openssl-Statically-Linked-Against-Windows</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;openssl 소스코드를 static library로 빌드하여 MFC 프로젝트에 통합하는 내용입니다. 그럼 따라와요 출발~&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(참고로 실패한 글입니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에 어떤 crypto library를 적용할지 각 라이브러리 비교 후 crypto++과 많은 고민 하다가 openssl로 결정.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 라이브러리 비교페이지는 아래 위키를 참조해주세요.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Comparison_of_cryptography_libraries&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://en.wikipedia.org/wiki/Comparison_of_cryptography_libraries&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1660616100064&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Comparison of cryptography libraries - Wikipedia&quot; data-og-description=&quot;From Wikipedia, the free encyclopedia Jump to navigation Jump to search The tables below compare cryptography libraries that deal with cryptography algorithms and have API function calls to each of the supported features. Cryptography libraries[edit] Name &quot; data-og-host=&quot;en.wikipedia.org&quot; data-og-source-url=&quot;https://en.wikipedia.org/wiki/Comparison_of_cryptography_libraries&quot; data-og-url=&quot;https://en.wikipedia.org/wiki/Comparison_of_cryptography_libraries&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Comparison_of_cryptography_libraries&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://en.wikipedia.org/wiki/Comparison_of_cryptography_libraries&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Comparison of cryptography libraries - Wikipedia&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;From Wikipedia, the free encyclopedia Jump to navigation Jump to search The tables below compare cryptography libraries that deal with cryptography algorithms and have API function calls to each of the supported features. Cryptography libraries[edit] Name&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;en.wikipedia.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. openssl 소스코드 다운로드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;For Production Use: &lt;a href=&quot;https://www.openssl.org/source/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.openssl.org/source/&lt;/a&gt; 에서 .tar.gz 을 받습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최신소스:&amp;nbsp;git://git.openssl.org 에서 다운로드합니다. git소스 빌드 해봤는데, 빌드에러가 나서 tar.gz으로 받았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 빌드환경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;윈도우 플랫폼 적용 가이드를 보고 따라 합니다. 얼마 전 프로젝트에 추가했던 simdjson이나 ta-lib 같은 라이브러리와 다르게 perl이랑 어셈블러도 설치해야 하고 이것저것 유의사항도 많고 현기증이 나려고 하지만 그렇다고 crypto 알고리즘을 직접 개발해서 사용할 능력은 전혀 안되고 시간은 금이므로 눈물을 머금고 침착하게 한 단계 한 단계 진행해 봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/openssl/openssl/blob/master/NOTES-WINDOWS.md&quot;&gt;https://github.com/openssl/openssl/blob/master/NOTES-WINDOWS.md&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1660624513639&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - openssl/openssl: TLS/SSL and crypto library&quot; data-og-description=&quot;TLS/SSL and crypto library. Contribute to openssl/openssl development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/openssl/openssl/blob/master/NOTES-WINDOWS.md&quot; data-og-url=&quot;https://github.com/openssl/openssl&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/iDnzD/hyPqWgMzQD/gi9rQ2TQCkjTMNJXssE0a0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/openssl/openssl/blob/master/NOTES-WINDOWS.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/openssl/openssl/blob/master/NOTES-WINDOWS.md&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/iDnzD/hyPqWgMzQD/gi9rQ2TQCkjTMNJXssE0a0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - openssl/openssl: TLS/SSL and crypto library&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;TLS/SSL and crypto library. Contribute to openssl/openssl development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.1. Perl 설치&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;We recommend Strawberry Perl, available from http://strawberryperl.com/&lt;br /&gt;Please read NOTES.PERL for more information, including the use of CPAN. An alternative is ActiveState Perl,&amp;nbsp;&lt;br /&gt;https://www.activestate.com/ActivePerl for which you may need to explicitly build the Perl module Win32/Console.pm via&amp;nbsp;https://platform.activestate.com/ActiveState and then download it.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딸기펄을 권장한다고 해서 딸기펄 홈페이지에 가보니 정말 먹음직스러운 딸기사진이 있네요. 10년전만 해도 파이썬보다는 펄이 유명했고 저 역시 회사에서 서버 관리 스크립트는 전부 펄을 사용했는데 정말 오랜만에 반가운 펄입니다. 금요일밤에 펄 스크립트 코딩하다가 날밤 새고 토요일 이른 아침이 되었는데 집에 가려고 보니 복도가 온통 물왁스칠이 되어 있고 여사님들이 안 마른 왁스 밟으면 안 된다고 도대체 어디서 튀어 나온 녀석인가 했던 기억이 납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;strawberry-perl&quot; 64bit 버전을 받아서 설치 했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.webp&quot; data-origin-width=&quot;1069&quot; data-origin-height=&quot;521&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nxjwb/btsBHPW0oDd/aC2khU6dBAfF8wqNf0hLQ1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nxjwb/btsBHPW0oDd/aC2khU6dBAfF8wqNf0hLQ1/img.webp&quot; data-alt=&quot;Perl for MS Windows&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nxjwb/btsBHPW0oDd/aC2khU6dBAfF8wqNf0hLQ1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fnxjwb%2FbtsBHPW0oDd%2FaC2khU6dBAfF8wqNf0hLQ1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1069&quot; height=&quot;521&quot; data-filename=&quot;1.webp&quot; data-origin-width=&quot;1069&quot; data-origin-height=&quot;521&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Perl for MS Windows&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.2. Microsoft Visual C Compiler 설치&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio를 설치했으면 같이 설치되어 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.3. Netwide Assembler (NASM) 설치&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;NASM is the only supported assembler. It is available from https://www.nasm.us&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 합니다. Perl은 딸기를 권장했지만 어셈블러는 NASM만 지원한다고 되어 있네요. 어셈블러도 참 오랜만에 받아 봅니다. 대학입학 전에 프로그램 crack버전이 너무 신기하여 알아보니 리버싱이라는 걸 해야 하고 그걸 하려면 어셈블리어를 알아야 한다고 하여 IBM 어셈블리어 책을 사서 공부하고 하필 그때 또 게임도사?라는 분이 크랙블로그를 운영하고 있어서 올리디버거랑 뭐랑 뭐랑 잡다한 툴 나름 따라 해보다가 배경지식이 없으니 때려치웠다가 다시 학부 2학년때 어셈블리어 강의 듣고 재밌어서 임베디드로 방향 잡고 대학원 프로젝트로 에뮬레이터 만들 때도 어셈블리어 공부 해뒀던 덕을 많이 봤었네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nasm-2.15.05-installer-x64.exe를 받아서 설치했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.4. PATH 환경변수 등록&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Make sure both Perl and NASM are on your %PATH%&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;perl -v&quot;와 &quot;nasm -v&quot;를 쳐보니 nasm이 PATH 등록이 안되어 있네요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;691&quot; data-origin-height=&quot;434&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2dPdG/btsBT186irm/I5WludwuAx8V3gjaaaeQpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2dPdG/btsBT186irm/I5WludwuAx8V3gjaaaeQpk/img.png&quot; data-alt=&quot;x64 Native Tools Command Prompt&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2dPdG/btsBT186irm/I5WludwuAx8V3gjaaaeQpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2dPdG%2FbtsBT186irm%2FI5WludwuAx8V3gjaaaeQpk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;691&quot; height=&quot;434&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;691&quot; data-origin-height=&quot;434&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;x64 Native Tools Command Prompt&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템 속성 창의 &quot;환경 변수&quot;에 가서 NASM 설치경로를 시스템 PATH에 추가해 줍니다. 그리고 prompt를 다시 열어서 &quot;nasm -v&quot; 입력하면 제대로 실행된 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;461&quot; data-origin-height=&quot;477&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cO0vrw/btsBOidf4qw/eQ4HZaJuSIqcKL3X8bLi0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cO0vrw/btsBOidf4qw/eQ4HZaJuSIqcKL3X8bLi0K/img.png&quot; data-alt=&quot;System Properties&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cO0vrw/btsBOidf4qw/eQ4HZaJuSIqcKL3X8bLi0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcO0vrw%2FbtsBOidf4qw%2FeQ4HZaJuSIqcKL3X8bLi0K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;461&quot; height=&quot;477&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;461&quot; data-origin-height=&quot;477&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;System Properties&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;518&quot; data-origin-height=&quot;408&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/leIxm/btsBKgfHXy2/bMVrwqhhY52iA6yxZ56S4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/leIxm/btsBKgfHXy2/bMVrwqhhY52iA6yxZ56S4K/img.png&quot; data-alt=&quot;NASM 등록&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/leIxm/btsBKgfHXy2/bMVrwqhhY52iA6yxZ56S4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FleIxm%2FbtsBKgfHXy2%2FbMVrwqhhY52iA6yxZ56S4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;518&quot; height=&quot;408&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;518&quot; data-origin-height=&quot;408&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;NASM 등록&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;14.png&quot; data-origin-width=&quot;589&quot; data-origin-height=&quot;203&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bygWZe/btsBQkhimLW/xisxIjZDkMPXArnyJ3IwVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bygWZe/btsBQkhimLW/xisxIjZDkMPXArnyJ3IwVK/img.png&quot; data-alt=&quot;nasm -v 실행 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bygWZe/btsBQkhimLW/xisxIjZDkMPXArnyJ3IwVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbygWZe%2FbtsBQkhimLW%2FxisxIjZDkMPXArnyJ3IwVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;589&quot; height=&quot;203&quot; data-filename=&quot;14.png&quot; data-origin-width=&quot;589&quot; data-origin-height=&quot;203&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;nasm -v 실행 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Native&amp;nbsp;builds&amp;nbsp;using&amp;nbsp;Visual&amp;nbsp;C++&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 윈도우 환경 빌드방법에 대한 가이드입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;15.png&quot; data-origin-width=&quot;565&quot; data-origin-height=&quot;350&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqowR9/btsBKiq4qCZ/pfMQ0Z1ZByREIImL2jp1JK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqowR9/btsBKiq4qCZ/pfMQ0Z1ZByREIImL2jp1JK/img.png&quot; data-alt=&quot;Build Guide&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqowR9/btsBKiq4qCZ/pfMQ0Z1ZByREIImL2jp1JK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqowR9%2FbtsBKiq4qCZ%2FpfMQ0Z1ZByREIImL2jp1JK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;565&quot; height=&quot;350&quot; data-filename=&quot;15.png&quot; data-origin-width=&quot;565&quot; data-origin-height=&quot;350&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Build Guide&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #ee2323;&quot;&gt;even if you are impatient!! 성질 급하신 분들도(저 포함) 꼭꼭 주의 깊게 읽어달라는 문구가 있네요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #000000;&quot;&gt;요약: 위 과정까지는 유저권한, 아래 &quot;nmake install&quot;은 prompt를 꼭꼭 관리자권한으로 상승시켜서 진행해 달라고 하네요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #000000;&quot;&gt;어차피 하다가 권한오류 나서 실패하면 엥? 모지 하면서 다시 관리자권한으로 재시도할 테니 큰 의미는 없어 보입니다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Installing OpenSSL ------------------ The following commands will install OpenSSL to a default system location. &lt;span style=&quot;color: #ee2323;&quot;&gt;**Danger Zone:**&lt;/span&gt; &lt;span style=&quot;color: #ee2323;&quot;&gt;even if you are impatient, please read the following two paragraphs carefully before you install OpenSSL.&lt;/span&gt; For security reasons the default system location is by default not writable for unprivileged users. So for the final installation step administrative privileges are required. The default system location and the procedure to obtain administrative privileges depends on the operating system. It is recommended to compile and test OpenSSL with normal user privileges and use administrative privileges only for the final installation step. On some platforms OpenSSL is preinstalled as part of the Operating System. In this case it is highly recommended not to overwrite the system versions, because other applications or libraries might depend on it. To avoid breaking other applications, install your copy of OpenSSL to a [different location](#installing-to-a-different-location) which is not in the global search path for system libraries. Finally, if you plan on using the FIPS module, you need to read the [Post-installation Notes](#post-installation-notes) further down.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 윈도 환경에서의 Make 가이드입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;16.png&quot; data-origin-width=&quot;616&quot; data-origin-height=&quot;437&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chDC3d/btsBT0CmjBN/1ugrsivRyfFKTBwKFqUlB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chDC3d/btsBT0CmjBN/1ugrsivRyfFKTBwKFqUlB0/img.png&quot; data-alt=&quot;Make Guide&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chDC3d/btsBT0CmjBN/1ugrsivRyfFKTBwKFqUlB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchDC3d%2FbtsBT0CmjBN%2F1ugrsivRyfFKTBwKFqUlB0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;616&quot; height=&quot;437&quot; data-filename=&quot;16.png&quot; data-origin-width=&quot;616&quot; data-origin-height=&quot;437&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Make Guide&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.1. Native Tools Command Prompt for Visual Studio를 실행&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio 설치 경로에 보시면 Command Prompt가 있습니다. x86과 x64가 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;17.png&quot; data-origin-width=&quot;297&quot; data-origin-height=&quot;289&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6NFqm/btsBUbjzveQ/quZ43JGZDFdKoRN5cAd4ak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6NFqm/btsBUbjzveQ/quZ43JGZDFdKoRN5cAd4ak/img.png&quot; data-alt=&quot;Visual studio 설치경로에 있습니다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6NFqm/btsBUbjzveQ/quZ43JGZDFdKoRN5cAd4ak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6NFqm%2FbtsBUbjzveQ%2FquZ43JGZDFdKoRN5cAd4ak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;297&quot; height=&quot;289&quot; data-filename=&quot;17.png&quot; data-origin-width=&quot;297&quot; data-origin-height=&quot;289&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Visual studio 설치경로에 있습니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2. 패키지 설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 에서 다운로드한 OpenSSL 소스패키지 압축을 풀어둔 경로로 이동합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;perl Configure&quot;를 실행할 건데 저는 64bit와 32bit용이 각각 필요하여 target option으로 &quot;VC-WIN64 A&quot;와 &quot;VC-WIN32&quot;를 입력할 겁니다. 그리고 &quot;--prefix&quot; 옵션으로 현재 openssl 소스패키지 경로를 입력하여 생성 라이브러리도 같은 경로에 생성되도록 하겠습니다. 다른 옵션에 대한 정보는 소스패키지 경로의 INSTALL.md 파일에 나와있는 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Default Build Type은 &quot;Debug&quot;라고 합니다. Release는 &quot;--release&quot; 추가해야 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &quot;/MTd&quot;, &quot;/MT&quot; 옵션 추가해줘야 하고 이 옵션으로 생성된 makefile 열어서 보면 flag에 &quot;/MD&quot; 라고 되어 있는 것 삭제해주면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패키지 경로는 최대한 짧게 해 주시고(구글에 관련 에러 사례 많음) 처음부터 관리자권한으로 실행해 주세요.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2.1. 유의사항&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- Native Tools Command Prompt를 처음부터 관리자권한으로 실행하세요. 유저모드에서 컴파일 에러 발생하네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 소스코드 경로를 너무 길게 하지 마세요.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- --prefix 경로 지정 시 소스코드 경로와 다른 곳을 지정해야 합니다. 안 그러면 install시 에러 발생함.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;18.png&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;431&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PGRxw/btsBUC83YAc/sUXiL2POR6b5w2YHsHlhqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PGRxw/btsBUC83YAc/sUXiL2POR6b5w2YHsHlhqK/img.png&quot; data-alt=&quot;openssl perl Configure&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PGRxw/btsBUC83YAc/sUXiL2POR6b5w2YHsHlhqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPGRxw%2FbtsBUC83YAc%2FsUXiL2POR6b5w2YHsHlhqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;582&quot; height=&quot;431&quot; data-filename=&quot;18.png&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;431&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;openssl perl Configure&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.3. OpenSSL 라이브러리 빌드&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;nmake&quot; 명령으로 OpenSSL 라이브러리를 빌드합니다. 컴파일 타임이 상당히 오래 걸리네요. 4가지 버전 만들어야 하는데 한번 할 때마다 30분 걸리네요.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;nmake&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간중간 fatal error u1077이랑&amp;nbsp; app.pdb파일을 열 수 없다 등이 발생하며 멈췄는데 이게 구문 오류가 아닌 윈도 환경과 병행성문제 등 Race Condition으로 밖에는 달리 설명이 안 되는 환경을 타는 것 같아, 오류발생시마다 그냥 계속 nmake 재 실행해서 결국 빌드 완성시켰습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;19.png&quot; data-origin-width=&quot;1069&quot; data-origin-height=&quot;197&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Xexbk/btsBMUQLc53/mjE1zM7IGXOTOFKTginw80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Xexbk/btsBMUQLc53/mjE1zM7IGXOTOFKTginw80/img.png&quot; data-alt=&quot;Build Report&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Xexbk/btsBMUQLc53/mjE1zM7IGXOTOFKTginw80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXexbk%2FbtsBMUQLc53%2FmjE1zM7IGXOTOFKTginw80%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1069&quot; height=&quot;197&quot; data-filename=&quot;19.png&quot; data-origin-width=&quot;1069&quot; data-origin-height=&quot;197&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Build Report&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;static library .lib은 --prefix 지정경로의 lib에, dynamic library .dll은 bin에 생성되고 지정경로의 include에 헤더파일이 생성됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;20.png&quot; data-origin-width=&quot;411&quot; data-origin-height=&quot;169&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxx0UT/btsBMXNqUT5/sSkPeStu3kjZsFN3jB5WB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxx0UT/btsBMXNqUT5/sSkPeStu3kjZsFN3jB5WB1/img.png&quot; data-alt=&quot;Lib Path&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxx0UT/btsBMXNqUT5/sSkPeStu3kjZsFN3jB5WB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbxx0UT%2FbtsBMXNqUT5%2FsSkPeStu3kjZsFN3jB5WB1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;411&quot; height=&quot;169&quot; data-filename=&quot;20.png&quot; data-origin-width=&quot;411&quot; data-origin-height=&quot;169&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Lib Path&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.3.1. x64 vs x86 결과물 비교&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른점은 .lib, .pdb, .dll 이 다르네요. 그리고 헤더파일 중 configuration.h 파일 1개만 아키텍처가 달리 적혀있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 프로젝트에 통합하여 버전별 맞는 파일이 연결되도록 할 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;21.png&quot; data-origin-width=&quot;1389&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qIU4D/btsBTWtsp5X/YQvZRx4Gp1kOXrXDO6ldVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qIU4D/btsBTWtsp5X/YQvZRx4Gp1kOXrXDO6ldVK/img.png&quot; data-alt=&quot;Folder Compare Result&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qIU4D/btsBTWtsp5X/YQvZRx4Gp1kOXrXDO6ldVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqIU4D%2FbtsBTWtsp5X%2FYQvZRx4Gp1kOXrXDO6ldVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1389&quot; height=&quot;500&quot; data-filename=&quot;21.png&quot; data-origin-width=&quot;1389&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Folder Compare Result&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 프로젝트에 라이브러리 링크&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트의 &quot;Prebuilt&quot;경로 하위에 아래 구조로 복사하였습니다. Static linking이라 bin폴더의 .dll은 제외.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;22.png&quot; data-origin-width=&quot;1042&quot; data-origin-height=&quot;609&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MWOg1/btsBTYku6yv/yixGXrfnl6uNPJ7mUnQKu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MWOg1/btsBTYku6yv/yixGXrfnl6uNPJ7mUnQKu0/img.png&quot; data-alt=&quot;Directory Tree&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MWOg1/btsBTYku6yv/yixGXrfnl6uNPJ7mUnQKu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMWOg1%2FbtsBTYku6yv%2FyixGXrfnl6uNPJ7mUnQKu0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1042&quot; height=&quot;609&quot; data-filename=&quot;22.png&quot; data-origin-width=&quot;1042&quot; data-origin-height=&quot;609&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Directory Tree&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.1. 프로젝트 속성 설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Debug/Release 공통)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4.1.1. C/C++-General-Additional Include Directories에 &quot;$(ProjectDir)\Prebuilt\OpenSSL\include\&quot; 추가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4.1.2. Linker-Additional Library Directories에 &quot;$(ProjectDir)\Prebuilt\openssl\lib\&quot; 추가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4.1.3. Linker-Input-Additional Dependencies에 &quot;libcrypto.lib;libssl.lib&quot; 추가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Debug)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4.1.4. Linker-Additional Library Directories에 &quot;$(ProjectDir)\Prebuilt\openssl\lib\debug\&quot; 추가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Release)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4.1.5. inker-Additional Library Directories에 &quot;$(ProjectDir)\Prebuilt\openssl\lib\release\&quot; 추가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 에러는 applink.c 파일명을 applink.cpp로 바꾸고 최상단에 &quot;#include &quot;pch.h&quot;&quot; 추가해 주면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;applink.c(9,10):&amp;nbsp;fatal&amp;nbsp;error&amp;nbsp;C1853:&amp;nbsp;'x64\Debug\Noyecube.pch'&amp;nbsp;precompiled&amp;nbsp;header&amp;nbsp;file&amp;nbsp;is&amp;nbsp;from&amp;nbsp;a&amp;nbsp;different&amp;nbsp;version&amp;nbsp;of&amp;nbsp;the&amp;nbsp;compiler,&amp;nbsp;or&amp;nbsp;the&amp;nbsp;precompiled&amp;nbsp;header&amp;nbsp;is&amp;nbsp;C++&amp;nbsp;and&amp;nbsp;you&amp;nbsp;are&amp;nbsp;using&amp;nbsp;it&amp;nbsp;from&amp;nbsp;C&amp;nbsp;(or&amp;nbsp;vice&amp;nbsp;versa)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;applink.cpp(145,1):&amp;nbsp;fatal&amp;nbsp;error&amp;nbsp;C1010:&amp;nbsp;unexpected&amp;nbsp;end&amp;nbsp;of&amp;nbsp;file&amp;nbsp;while&amp;nbsp;looking&amp;nbsp;for&amp;nbsp;precompiled&amp;nbsp;header.&amp;nbsp;Did&amp;nbsp;you&amp;nbsp;forget&amp;nbsp;to&amp;nbsp;add&amp;nbsp;'#include&amp;nbsp;&quot;pch.h&quot;'&amp;nbsp;to&amp;nbsp;your&amp;nbsp;source?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 에러는 프로젝트속성에서 SDL 체크 해제하면 됩니다. 이미 빌드된 라이브러리 안에서 fopen 등을 가져다 썼기 때문에 치환할 수 없음.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;applink.cpp(123,47):&amp;nbsp;error&amp;nbsp;C4996:&amp;nbsp;'fopen':&amp;nbsp;This&amp;nbsp;function&amp;nbsp;or&amp;nbsp;variable&amp;nbsp;may&amp;nbsp;be&amp;nbsp;unsafe.&amp;nbsp;Consider&amp;nbsp;using&amp;nbsp;fopen_s&amp;nbsp;instead.&amp;nbsp;To&amp;nbsp;disable&amp;nbsp;deprecation,&amp;nbsp;use&amp;nbsp;_CRT_SECURE_NO_WARNINGS.&amp;nbsp;See&amp;nbsp;online&amp;nbsp;help&amp;nbsp;for&amp;nbsp;details.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;applink.cpp(131,46):&amp;nbsp;error&amp;nbsp;C4996:&amp;nbsp;'_open':&amp;nbsp;This&amp;nbsp;function&amp;nbsp;or&amp;nbsp;variable&amp;nbsp;may&amp;nbsp;be&amp;nbsp;unsafe.&amp;nbsp;Consider&amp;nbsp;using&amp;nbsp;_sopen_s&amp;nbsp;instead.&amp;nbsp;To&amp;nbsp;disable&amp;nbsp;deprecation,&amp;nbsp;use&amp;nbsp;_CRT_SECURE_NO_WARNINGS.&amp;nbsp;See&amp;nbsp;online&amp;nbsp;help&amp;nbsp;for&amp;nbsp;details.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게. dll 사용이 아닌 정적. lib 생성 후 프로젝트에 포함시키려고 발버둥 쳐봤으나 결국은 .dll 의존성 에러가 뜨네요.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그냥 안 쓸랍니다. 리눅스환경에서는 몰라도 윈도우에서 .dll 말고 정적라이브러리 테스트는 제대로 안 해주는 것 같네요. 안 올리려다가 허비한 시간이 아까워서 글은 올립니다.&lt;/p&gt;</description>
      <category>프로그래밍/C | C++</category>
      <category>nmake</category>
      <category>openssl</category>
      <category>openssl mfc</category>
      <category>openssl windows</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/28</guid>
      <comments>https://noyecube.tistory.com/entry/MFC-Build-Openssl-Statically-Linked-Against-Windows#entry28comment</comments>
      <pubDate>Sat, 27 Aug 2022 15:37:00 +0900</pubDate>
    </item>
    <item>
      <title>[MFC] C++ MiniDumpWriteDump</title>
      <link>https://noyecube.tistory.com/entry/MFC-C-MiniDumpWriteDump</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램 개발 중 프로그램 에러는 컴파일 에러와 런타임 에러가 있습니다. 이번 포스팅에서는 런타임 에러 발생 시 디버깅에 필요한 정보를 파일로 저장하는 방법을 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 런타임 에러의 종류&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구문오류는 컴파일 타임에 컴파일러가 알려주는데 아래와 같은 런타임 에러는 프로그램이 그냥 꺼져버립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 별도로 그 순간의 스냅샷 정보를 저장하지 않으면 디버깅이 어렵습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;612&quot; data-origin-height=&quot;148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJAHiL/btsBQjbCrJj/Ayj5BZG4JOMki3vHkaeMl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJAHiL/btsBQjbCrJj/Ayj5BZG4JOMki3vHkaeMl0/img.png&quot; data-alt=&quot;런타임 에러 종류&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJAHiL/btsBQjbCrJj/Ayj5BZG4JOMki3vHkaeMl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJAHiL%2FbtsBQjbCrJj%2FAyj5BZG4JOMki3vHkaeMl0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;612&quot; height=&quot;148&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;612&quot; data-origin-height=&quot;148&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;런타임 에러 종류&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;런타임 에러의 분석과 해결을 위해서는 당시 메모리와 레지스터 등의 디버깅에 필요한 정보를 파일로 저장할 필요가 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/dxtecharts/crash-dump-analysis&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.microsoft.com/en-us/windows/win32/dxtecharts/crash-dump-analysis의&lt;/a&gt; Crash Dump Analysis가 그 방법을 알려주고 있습니다. 프로젝트에 Minidump.h 와 Minidump.cpp를 추가하고 소스코드를 복사 후 붙여넣기한 뒤 약간의 수정 후 결과를 확인해 보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 크래시 덤프 분석 코드 작성&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CALLBACK 등록하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SetUnhandledExceptionFilter() 를 호출하여 직접호출이 아닌 exception 발생 시 콜백으로 동작하도록 수정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;.dmp 파일 생성 경로 수정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행파일과 같은 경로에 생성되도록 수정하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;App의 Initinstance()에 콜백함수를 등록하고&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;676&quot; data-origin-height=&quot;90&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXZI0Z/btsBT4LwLvc/0UMtcMyduMvXsUxcPkHTC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXZI0Z/btsBT4LwLvc/0UMtcMyduMvXsUxcPkHTC1/img.png&quot; data-alt=&quot;콜백 함수 등록&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXZI0Z/btsBT4LwLvc/0UMtcMyduMvXsUxcPkHTC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXZI0Z%2FbtsBT4LwLvc%2F0UMtcMyduMvXsUxcPkHTC1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;676&quot; height=&quot;90&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;676&quot; data-origin-height=&quot;90&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;콜백 함수 등록&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 코드에 아래와 같이 0으로 나누는 코드를 삽입 후 실행해 보았습니다.(retCode 값이 0)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;566&quot; data-origin-height=&quot;215&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cW5EwN/btsBT0o4vAy/kqT9dngJH7cu5yBRmGUeh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cW5EwN/btsBT0o4vAy/kqT9dngJH7cu5yBRmGUeh0/img.png&quot; data-alt=&quot;작성한 테스트 코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cW5EwN/btsBT0o4vAy/kqT9dngJH7cu5yBRmGUeh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcW5EwN%2FbtsBT0o4vAy%2FkqT9dngJH7cu5yBRmGUeh0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;566&quot; height=&quot;215&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;566&quot; data-origin-height=&quot;215&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;작성한 테스트 코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트코드가 실행되자 프로그램이 멈추고 그냥 종료되었는데 아래와 같은. dmp 파일이 생성되었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;732&quot; data-origin-height=&quot;71&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ow7BJ/btsBUBCj2fM/QXXeuHhAkkk1t8kgDnEfn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ow7BJ/btsBUBCj2fM/QXXeuHhAkkk1t8kgDnEfn0/img.png&quot; data-alt=&quot;생성된 덤프 파일&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ow7BJ/btsBUBCj2fM/QXXeuHhAkkk1t8kgDnEfn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fow7BJ%2FbtsBUBCj2fM%2FQXXeuHhAkkk1t8kgDnEfn0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;732&quot; height=&quot;71&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;732&quot; data-origin-height=&quot;71&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;생성된 덤프 파일&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.dmp 파일을 더블클릭으로 실행하면 Dump summary의 Exception Information에 crash 원인이 정확하게 나오고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우측 Actions의 &quot;Debug with Native Only&quot;를 클릭하면 비주얼스튜디오가 디버깅모드로 실행되면서 에러 위치를 정확하게 표기해 줍니다. 이를 위해서는 실행파일 경로에 디버그 데이터베이스파일 .pdb가 있어야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1733&quot; data-origin-height=&quot;179&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HUtM6/btsBKiEAaiI/4k8oVb3e7GPGtQqLudYnrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HUtM6/btsBKiEAaiI/4k8oVb3e7GPGtQqLudYnrK/img.png&quot; data-alt=&quot;Dump Summary&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HUtM6/btsBKiEAaiI/4k8oVb3e7GPGtQqLudYnrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHUtM6%2FbtsBKiEAaiI%2F4k8oVb3e7GPGtQqLudYnrK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1733&quot; height=&quot;179&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1733&quot; data-origin-height=&quot;179&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Dump Summary&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;236&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfw2WD/btsBT3eMqH5/FOSck8UlBpCH1mvk7JkARk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfw2WD/btsBT3eMqH5/FOSck8UlBpCH1mvk7JkARk/img.png&quot; data-alt=&quot;디버깅에서 확인된 에러&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfw2WD/btsBT3eMqH5/FOSck8UlBpCH1mvk7JkARk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbfw2WD%2FbtsBT3eMqH5%2FFOSck8UlBpCH1mvk7JkARk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;752&quot; height=&quot;236&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;236&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;디버깅에서 확인된 에러&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 덤프파일 생성 예제 코드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 사용한 예제 코드를 붙입니다. 참고하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Minidump.h&lt;/p&gt;
&lt;pre id=&quot;code_1660291568703&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

LONG CALLBACK GenerateDump(EXCEPTION_POINTERS* pExceptionPointers);
void Register_MiniDumpWriteDump(void);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Minidump.cpp&lt;/p&gt;
&lt;pre id=&quot;code_1660291587929&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;pch.h&quot;
#include &quot;Minidump.h&quot;

/* https://docs.microsoft.com/en-us/windows/win32/dxtecharts/crash-dump-analysis */

#include &amp;lt;strsafe.h&amp;gt;
#include &amp;lt;DbgHelp.h&amp;gt;
#pragma comment (lib, &quot;DbgHelp&quot;)

static BOOL _boIsRegistered = FALSE;

LONG CALLBACK GenerateDump(EXCEPTION_POINTERS* pExceptionPointers)
{
    BOOL bMiniDumpSuccessful;
    CString cstrFilePath;
    TCHAR szPath[MAX_PATH];
    TCHAR szFileName[MAX_PATH];
    TCHAR* szAppName = _T(&quot;Noyecube&quot;);
    TCHAR* szVersion = _T(&quot;v1.0&quot;);
    DWORD dwBufferSize = MAX_PATH;
    HANDLE hDumpFile;
    SYSTEMTIME stLocalTime;
    MINIDUMP_EXCEPTION_INFORMATION ExpParam;

    GetLocalTime(&amp;amp;stLocalTime);

#if 0
    GetTempPath(dwBufferSize, szPath);

    StringCchPrintf(szFileName, MAX_PATH, _T(&quot;%s%s&quot;), szPath, szAppName);
    CreateDirectory(szFileName, NULL);

#
    StringCchPrintf(szFileName, MAX_PATH, _T(&quot;%s%s\\%s-%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp&quot;),
        szPath, szAppName, szVersion,
        stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
        stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond,
        GetCurrentProcessId(), GetCurrentThreadId());

#else
    GetModuleFileName(NULL, szPath, dwBufferSize);
    cstrFilePath.Format(&quot;%s&quot;, szPath);
    cstrFilePath = cstrFilePath.Left(cstrFilePath.ReverseFind(_T('\\')));
   
    StringCchPrintf(szFileName, MAX_PATH, _T(&quot;%s\\%s-%s-%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp&quot;),
        (LPCTSTR)cstrFilePath, szAppName, szVersion,
        stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
        stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond,
        GetCurrentProcessId(), GetCurrentThreadId());
#endif
    hDumpFile = CreateFile(szFileName, GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_WRITE | FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);

    ExpParam.ThreadId = GetCurrentThreadId();
    ExpParam.ExceptionPointers = pExceptionPointers;
    ExpParam.ClientPointers = TRUE;

    bMiniDumpSuccessful = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
        hDumpFile, MiniDumpWithDataSegs, &amp;amp;ExpParam, NULL, NULL);

    return EXCEPTION_EXECUTE_HANDLER;
}

void Register_MiniDumpWriteDump(void)
{
    if (TRUE == _boIsRegistered)
        return;

    _boIsRegistered = TRUE;

    SetUnhandledExceptionFilter(GenerateDump);
}

#if 0
void SomeFunction()
{
    __try
    {
        int* pBadPtr = NULL;
        *pBadPtr = 0;
    }
    __except (GenerateDump(GetExceptionInformation()))
    {
    }
}
#endif 

/* https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-2/compiler-error-c2707?view=msvc-170 */
#if 0
// C2707.cpp
#include &amp;lt;windows.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;

LONG MyFilter(LONG excode)
{
    return (excode == EXCEPTION_ACCESS_VIOLATION ?
        EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH);   // OK
}

LONG func(void)
{
    int x, y;
    return(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?  // C2707
        EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH);

    __try
    {
        y = 0;
        x = 4 / y;
        return 0;
    }

    __except (MyFilter(GetExceptionCode()))
    {
        return(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? // ok
            EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH);
    }
}

int main()
{
    __try
    {
        func();
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        printf_s(&quot;Caught exception\n&quot;);
    }
}
#endif&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MS의 공식 자료는 아래 링크에서 확인 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/dxtecharts/crash-dump-analysis&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.microsoft.com/en-us/windows/win32/dxtecharts/crash-dump-analysis&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1660290938183&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Crash Dump Analysis - Win32 apps&quot; data-og-description=&quot;This technical article provides info about how to write and use a minidump.&quot; data-og-host=&quot;docs.microsoft.com&quot; data-og-source-url=&quot;https://docs.microsoft.com/en-us/windows/win32/dxtecharts/crash-dump-analysis&quot; data-og-url=&quot;https://docs.microsoft.com/en-us/windows/win32/dxtecharts/crash-dump-analysis&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/pWc9K/hyPpNDsmZs/Vfk5S5kBgesVksuCVAQwOk/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400&quot;&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/dxtecharts/crash-dump-analysis&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.microsoft.com/en-us/windows/win32/dxtecharts/crash-dump-analysis&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/pWc9K/hyPpNDsmZs/Vfk5S5kBgesVksuCVAQwOk/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Crash Dump Analysis - Win32 apps&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;This technical article provides info about how to write and use a minidump.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-2/compiler-error-c2707?view=msvc-170&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-2/compiler-error-c2707?view=msvc-170&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1702386465062&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Compiler Error C2707&quot; data-og-description=&quot;Learn more about: Compiler Error C2707&quot; data-og-host=&quot;learn.microsoft.com&quot; data-og-source-url=&quot;https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-2/compiler-error-c2707?view=msvc-170&quot; data-og-url=&quot;https://learn.microsoft.com/en-us/cpp/error-messages/compiler-errors-2/compiler-error-c2707?view=msvc-170&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/0JJnh/hyUL1IZWR6/mgMWqh84goP5KeqkGJEbH0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-2/compiler-error-c2707?view=msvc-170&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-2/compiler-error-c2707?view=msvc-170&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/0JJnh/hyUL1IZWR6/mgMWqh84goP5KeqkGJEbH0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Compiler Error C2707&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn more about: Compiler Error C2707&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;learn.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상으로 런타임 에러 발생시 에러 발생 위치를 디버깅 모드에서 확인할 수 있도록 Crash Dump를 생성하는 방법과 사용 방법에 대하여 알아보았습니다.&lt;/p&gt;</description>
      <category>프로그래밍/C | C++</category>
      <category>crash dump</category>
      <category>EXCEPTION_POINTERS</category>
      <category>GenerateDump</category>
      <category>Memory Dump</category>
      <category>MiniDumpWriteDump</category>
      <category>Runtime Error</category>
      <category>SetUnhandledExceptionFilter</category>
      <category>런타임에러</category>
      <category>메모리덤프</category>
      <category>크래시덤프</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/26</guid>
      <comments>https://noyecube.tistory.com/entry/MFC-C-MiniDumpWriteDump#entry26comment</comments>
      <pubDate>Fri, 12 Aug 2022 17:12:23 +0900</pubDate>
    </item>
    <item>
      <title>[MFC] 로그(Log) 출력 Logger</title>
      <link>https://noyecube.tistory.com/entry/MFC-%EB%A1%9C%EA%B7%B8Log-%EC%B6%9C%EB%A0%A5-Logger</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 C/C++ GUI 개발 환경에서 로그 출력을 위한 Logger 매크로를 작성하여 효율적으로 디버깅 메시지를 출력하는 방법을 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Logger 요구사항은 다음과 같습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;MFC 프로그램 개발 시 콘솔 로그를 확인할 수 있어야 한다.&lt;/li&gt;
&lt;li&gt;로그가 파일로 저장되어야 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 콘솔창에 로그 출력하는 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MFC와 같은 GUI 개발환경에서 콘솔로 로그를 출력하려면 컴파일 단계에서 링커에게 콘솔을 띄우도록 알려줘야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 방법은 아래 코드를 참조하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;framework.h 에 아래 내용 추가 후 코드에서 DPRINTF 함수를 호출하면 별도 콘솔창에 로그가 출력됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1660270689944&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#ifdef _DEBUG
#ifdef _UNICODE
#define WIDEN(x)           L ## x
#define WIDEN2(x)         WIDEN(x)
#define __WFILE__          WIDEN2(__FILE__)
#define __WFUNCTION__ WIDEN2(__FUNCTION__)
#pragma comment(linker, &quot;/entry:wWinMainCRTStartup /subsystem:console&quot;)	/* OPEN CONSOLE DEBUG UNICODE */
#define DPRINTF(fmt, ...) do { _tprintf(_T(&quot;[%s:%d]&quot; fmt), __WFUNCTION__, __LINE__, __VA_ARGS__); } while(0)
#else
#pragma comment(linker, &quot;/entry:WinMainCRTStartup /subsystem:console&quot;)	/* OPEN CONSOLE DEBUG */
#define DPRINTF(fmt, ...) do { printf(_T(&quot;[%s:%d]&quot; fmt), __func__, __LINE__, __VA_ARGS__); } while(0)
#endif
#else
#define DPRINTF(fmt, ...)	do{;} while(0)
#endif /* _DEBUG */&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 로그를 파일에 저장하는 방법&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.1 로그 카테고리에 대해&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그를 카테고리별로 분류할 필요가 있습니다. 모듈별, 레벨별로 분류 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 저장 또는 소켓으로 출력할 수 있습니다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈은 모바일 기기를 예로 들면 전화, 문자, 인터넷 및 각 어플들이 각각의 모듈로 구분되고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레벨은 LOW, MID, HIGH, ERR, FATAL 등으로 나눌 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 구분하여 두면 로컬이 아닌 Diagnostics를 통한 소켓으로 전송 시 모듈과 레벨을 필터링할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 사람들이 로그 출력시 간과하는 사실 중 하나는 부하입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그래밍에서 가장 처음 배우는 게 printf(&quot;Hello World!&quot;);&amp;nbsp; 라서 그런지는 몰라도 printf를 가볍게 보는 것 같은데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가변인자를 파싱해서 메모리버퍼에 직렬화 후 마지막에는 1바이트 단위로 하나씩 하나씩 하드웨어 지정된 메모리에 쓰게 됩니다. (Trace32 물려서 어셈블리 레벨로 확인하였습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.2 로그 부하에 대해&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템 여기저기서 로그를 출력하면 메모리버퍼에 쓸 때까지는 몰라도 실제 화면(stdout) 또는 파일기록 시 상당한 부하가 걸립니다. 멀티코어, 고 클럭 하드웨어에서는 별로 표가 안 날 수 있지만 단일 스레드 레벨에서 보면 부하가 걸리는 건 맞습니다. 모 회사에서 아무리 최적화를 해도 시스템 속도 개선이 안되어 거액의 돈을 주고 외주를 맡겼는데 주요 부하 해결 요인 중 하나가 불필요 로그 출력 제거였습니다. 그렇기 때문에 로그를 출력할 경우에도 가급적 중간에 버퍼를 두고 로그가 print 호출과 함께 동기적으로 동작하여 그 자체가 주 로직이 되게 하는 것보다는 가급적 유휴타임에 주기적으로 flush 되도록 관리할 필요가 있습니다. CFile로 Open 한 핸들에 바로 기록하지 말고 CArchive에 한번 더 연결 후 메모리 버퍼를 충분히 주어 주기적으로 한 번씩 파일에 기록하도록 하는 것을 권장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 주고받는 객체 사이에는 한쪽에 대기가 걸리지 않도록 서로의 처리 속도를 보완해 줄 중간버퍼(Queue)는 필수인데 FTP 서버에 왜 큐를 두었는지 도무지 이해가 안 가네를 연발하시던 국내 유명 인공위성 개발업체 팀장님의 발언이 생각납니다. 맥도널드에서 햄버거 주문하는데 주문받는 사람이 주방에 가서 햄버거도 만들어서 갖다 주면 2번째 3번째 주문하려고 기다리는 사람들은 1번 햄버거 나올 때까지 주문 접수도 못하게 되는데 마찬가지로 디스크에 파일을 Read, Write 하는 스레드와 클라이언트 상대하는 스레드 사이에는 당연히 큐를 넣어야 하는 게 아닌지, 요즘도 그렇게 인공위성을 개발하고 계시는지 궁금합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.3 예제 코드&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- CLogger.h 예시&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일별로 라인 시퀀스넘버를 관리하는데 MAX_LINE_PER_FILE에 도달하면 현재 쓰는 파일핸들을 flush 후 닫고 새 파일을 엽니다. 향후 Diag을 통한 소켓으로 로그 전송 필요시 LOG_MSG 하단에서 버퍼와 모듈, 로그카테고리 정보를 관련 함수에 던져주면 됩니다. 소켓전송 시에는 compress, aggregation 고려 필요합니다. 더 고도화 시에는 파라미터 수를 제한시키고(매크로가 파라미터 0개, 1개, 2개, 3개짜리 있음) 각 로그 메시지에 ID 부여 후 ID와 파라미터 변수값만 전송합니다.(퀄컴 QXDM 문서 참조)&lt;/p&gt;
&lt;pre id=&quot;code_1660271157394&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once


#define MAX_PRINT_STR_LEN	(512)

typedef enum class _LOGGER_CATEGORY {
	NORMAL,
	TRADING_EVENT,
	NETWORK,
	DBG,	/* DEBUG */
	INFO,
	ERR,
	MAX
} TEN_LOGGER_CATEGORY;

typedef enum class _LOGGER_LEVEL {
	LOW,
	MID,
	HIGH,
	MAX
} TEN_LOGGER_LEVEL;

typedef struct _LOGGER_SET {
	CCriticalSection critSect;
	UINT32 u32SeqNo;
	CFile* pcfp;
	CArchive* pcarp;
	BOOL boIsInit;
	_LOGGER_SET() {
		u32SeqNo = 0;
		pcfp = nullptr;
		pcarp = nullptr;
		boIsInit = FALSE;
	}
} TST_LOGGER_SET, *PTST_LOGGER_SET;


class CLogger
{
public:
	CLogger();
	~CLogger();
private:
	const UINT32 MAX_LINE_PER_FILE;	/* To be initialzed by constructor */
	TST_LOGGER_SET astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::MAX)];
	void _logger_renew_logfile(TEN_LOGGER_CATEGORY enCat);
public:
	void logger_write_to_file(TEN_LOGGER_CATEGORY enCat, LPCTSTR buf, size_t bufcount);
};

void format_log_msg(char* buf_ptr, int buf_size, char* fmt, ...);
//void logger_msg_to_file(TEN_LOGGER_CATEGORY enCat, LPCTSTR buf, size_t countof);

#define LOG_MSG(ssid, cat, fmt, ... )                                       \
   {                                                                             \
	 SYSTEMTIME tm;																\
     char buf[MAX_PRINT_STR_LEN];                                                \
	 char newbuf[MAX_PRINT_STR_LEN]; \
     char *p_front = __FILE__;                                                   \
     char *p_end = p_front + strlen( p_front );                                  \
	GetLocalTime(&amp;amp;tm);															\
     while ( p_end != p_front )                                                  \
     {                                                                           \
       if ( ( *p_end == '\\' ) || ( *p_end == ':' ) || ( *p_end == '/') )        \
       {                                                                         \
         p_end++;                                                                \
         break;                                                                  \
       }                                                                         \
       p_end--;                                                                  \
     }                                                                           \
                                                                                 \
     /* Format message for logging */                                            \
     format_log_msg( buf, MAX_PRINT_STR_LEN, fmt, ##__VA_ARGS__ );              \
	(void) _stprintf_s((TCHAR*)newbuf, _countof(newbuf), &quot;\r\n%02d:%02d:%02d,%03ld,%d,%d,%s,%s,%d,%s&quot;, \
	tm.wHour, tm.wMinute, tm.wSecond, tm.wMilliseconds, ssid, cat, p_end, __func__, __LINE__, buf);		\
	theApp.Logger.logger_write_to_file(cat, newbuf, _tcslen(newbuf));	\
	(void)printf(&quot;%s\n&quot;, newbuf);	\
    /* (void)printf(&quot;%s:%s:%d] %s\n&quot;, p_end, __func__, __LINE__, buf);     */     \
     /* TODO: Send to socket, etc, check ssid, lvl ... */ \
   }

/* SSID (Service Set Identifier) */
#define SSID_NOYECUBE	(0)
#define SSID_x			(1)
#define SSID_xx			(2)
#define SSID_xxx		(3)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- CLogger.cpp 예시&lt;/p&gt;
&lt;pre id=&quot;code_1660272401620&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;pch.h&quot;
#include &quot;CLogger.h&quot;
#include &amp;lt;strsafe.h&amp;gt;

CLogger::CLogger():MAX_LINE_PER_FILE(4096)
{
	SYSTEMTIME tm;
	TCHAR szBuf[512];
	CString cstrFilePath;
	CString cstrTemp;
	CFileFind cffFindFile;
	CFile* pcfp = nullptr;
//	CArchive* pcarp = nullptr;
//	PTST_LOGGER_SET pstLogger = nullptr;
	BOOL boRet;

	GetLocalTime(&amp;amp;tm);

	memset(szBuf, 0x0, sizeof(szBuf));

	GetModuleFileName(NULL, szBuf, sizeof(szBuf));
	cstrFilePath.Format(&quot;%s&quot;, szBuf);

	cstrFilePath = cstrFilePath.Left(cstrFilePath.ReverseFind(_T('\\')));

	boRet = cffFindFile.FindFile(cstrFilePath + _T(&quot;\\logs&quot;));
	if (!boRet)
		boRet = CreateDirectory(cstrFilePath + _T(&quot;\\logs&quot;), NULL);

	boRet = cffFindFile.FindFile(cstrFilePath + _T(&quot;\\dumps&quot;));
	if (!boRet)
		boRet = CreateDirectory(cstrFilePath + _T(&quot;\\dumps&quot;), NULL);

	cstrTemp.Empty();
	cstrTemp = cstrFilePath;
	cstrTemp.AppendFormat(_T(&quot;\\logs\\log-normal-%04d-%02d-%02d-%02d-%02d-%02d-%03ld.csv&quot;),
		tm.wYear, tm.wMonth, tm.wDay, tm.wHour, tm.wMinute, tm.wSecond, tm.wMilliseconds);
	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::NORMAL)].pcfp = new CFile();
	pcfp = this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::NORMAL)].pcfp;
	boRet = pcfp-&amp;gt;Open(cstrTemp, CFile::modeWrite | CFile::modeCreate | CFile::modeNoTruncate | CFile::shareDenyNone);
	if (FALSE == boRet) {
		//TODO: erro handling 
	}
	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::NORMAL)].pcarp = new CArchive(pcfp, CArchive::store, 4096 * 10);
	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::NORMAL)].pcarp-&amp;gt;WriteString(_T(&quot;hhmmss,msec,ssid,category,file,func,line,msg&quot;));
	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::NORMAL)].boIsInit = TRUE;

	cstrTemp.Empty();
	cstrTemp = cstrFilePath;
	cstrTemp.AppendFormat(_T(&quot;\\logs\\log-trading-%04d-%02d-%02d-%02d-%02d-%02d-%03ld.csv&quot;),
		tm.wYear, tm.wMonth, tm.wDay, tm.wHour, tm.wMinute, tm.wSecond, tm.wMilliseconds);
	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::TRADING_EVENT)].pcfp = new CFile();
	pcfp = this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::TRADING_EVENT)].pcfp;
	boRet = pcfp-&amp;gt;Open(cstrTemp, CFile::modeWrite | CFile::modeCreate | CFile::modeNoTruncate | CFile::shareDenyNone);
	if (FALSE == boRet) {
		//TODO: erro handling 
	}
	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::TRADING_EVENT)].pcarp = new CArchive(pcfp, CArchive::store, 4096 * 10);
	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::TRADING_EVENT)].pcarp-&amp;gt;WriteString(_T(&quot;hhmmss,msec,ssid,category,file,func,line,msg&quot;));
	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::TRADING_EVENT)].boIsInit = TRUE;

	cstrTemp.Empty();
	cstrTemp = cstrFilePath;
	cstrTemp.AppendFormat(_T(&quot;\\logs\\log-network-%04d-%02d-%02d-%02d-%02d-%02d-%03ld.csv&quot;),
		tm.wYear, tm.wMonth, tm.wDay, tm.wHour, tm.wMinute, tm.wSecond, tm.wMilliseconds);
	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::NETWORK)].pcfp = new CFile();
	pcfp = this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::NETWORK)].pcfp;
	boRet = this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::NETWORK)].pcfp-&amp;gt;Open(cstrTemp,
		CFile::modeWrite | CFile::modeCreate | CFile::modeNoTruncate | CFile::shareDenyNone);
	if (FALSE == boRet) {
		//TODO: erro handling 
	}
	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::NETWORK)].pcarp = new CArchive(pcfp, CArchive::store, 4096 * 10);
	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::NETWORK)].pcarp-&amp;gt;WriteString(_T(&quot;hhmmss,msec,ssid,category,file,func,line,msg&quot;));
	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::NETWORK)].boIsInit = TRUE;

	cstrTemp.Empty();
	cstrTemp = cstrFilePath;
	cstrTemp.AppendFormat(_T(&quot;\\logs\\log-debug-%04d-%02d-%02d-%02d-%02d-%02d-%03ld.csv&quot;),
		tm.wYear, tm.wMonth, tm.wDay, tm.wHour, tm.wMinute, tm.wSecond, tm.wMilliseconds);
	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::DBG)].pcfp = new CFile();
	pcfp = this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::DBG)].pcfp;
	boRet = this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::DBG)].pcfp-&amp;gt;Open(cstrTemp,
		CFile::modeWrite | CFile::modeCreate | CFile::modeNoTruncate | CFile::shareDenyNone);
	if (FALSE == boRet) {
		//TODO: erro handling 
	}
	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::DBG)].pcarp = new CArchive(pcfp, CArchive::store, 4096 * 10);
	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::DBG)].pcarp-&amp;gt;WriteString(_T(&quot;hhmmss,msec,ssid,category,file,func,line,msg&quot;));
	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::DBG)].boIsInit = TRUE;

	cstrTemp.Empty();
	cstrTemp = cstrFilePath;
	cstrTemp.AppendFormat(_T(&quot;\\logs\\log-info-%04d-%02d-%02d-%02d-%02d-%02d-%03ld.csv&quot;),
		tm.wYear, tm.wMonth, tm.wDay, tm.wHour, tm.wMinute, tm.wSecond, tm.wMilliseconds);
	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::INFO)].pcfp = new CFile();
	pcfp = this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::INFO)].pcfp;
	boRet = this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::INFO)].pcfp-&amp;gt;Open(cstrTemp,
		CFile::modeWrite | CFile::modeCreate | CFile::modeNoTruncate | CFile::shareDenyNone);
	if (FALSE == boRet) {
		//TODO: erro handling 
	}
	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::INFO)].pcarp = new CArchive(pcfp, CArchive::store, 4096 * 10);
	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::INFO)].pcarp-&amp;gt;WriteString(_T(&quot;hhmmss,msec,ssid,category,file,func,line,msg&quot;));
	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::INFO)].boIsInit = TRUE;

	cstrTemp.Empty();
	cstrTemp = cstrFilePath;
	cstrTemp.AppendFormat(_T(&quot;\\logs\\log-error-%04d-%02d-%02d-%02d-%02d-%02d-%03ld.csv&quot;),
		tm.wYear, tm.wMonth, tm.wDay, tm.wHour, tm.wMinute, tm.wSecond, tm.wMilliseconds);
	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::ERR)].pcfp = new CFile();
	pcfp = this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::ERR)].pcfp;
	boRet = this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::ERR)].pcfp-&amp;gt;Open(cstrTemp,
		CFile::modeWrite | CFile::modeCreate | CFile::modeNoTruncate | CFile::shareDenyNone);
	if (FALSE == boRet) {
		//TODO: erro handling 
	}
	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::ERR)].pcarp = new CArchive(pcfp, CArchive::store, 4096 * 10);
	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::ERR)].pcarp-&amp;gt;WriteString(_T(&quot;hhmmss,msec,ssid,category,file,func,line,msg&quot;));
	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(TEN_LOGGER_CATEGORY::ERR)].boIsInit = TRUE;
}

CLogger::~CLogger()
{
	CFile* pcfp = nullptr;
	CArchive* pcarp = nullptr;
	for (int i = 0; i &amp;lt; static_cast&amp;lt;int&amp;gt;(TEN_LOGGER_CATEGORY::MAX); i++) {
		pcfp = this-&amp;gt;astLogger[i].pcfp;
		pcarp = this-&amp;gt;astLogger[i].pcarp;
		if (nullptr != pcarp) {
			pcarp-&amp;gt;Flush();
			pcarp-&amp;gt;Abort();
			delete pcarp;
			pcarp = nullptr;
		}

		if (nullptr != pcfp) {
			pcfp-&amp;gt;Flush();
			pcfp-&amp;gt;Abort();
			delete pcfp;
			pcfp = nullptr;
		}
		this-&amp;gt;astLogger[i].boIsInit = FALSE;
	}
}

void CLogger::_logger_renew_logfile(TEN_LOGGER_CATEGORY enCat)
{
	SYSTEMTIME tm;
	TCHAR szBuf[512];
	CString cstrFilePath;
	CString cstrTemp;
	CFileFind cffFindFile;
	CFile* pcfp = nullptr;
	//	CArchive* pcarp = nullptr;
	//	PTST_LOGGER_SET pstLogger = nullptr;
	BOOL boRet;

	PTST_LOGGER_SET p = &amp;amp;(this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(enCat)]);

	if (nullptr != p-&amp;gt;pcarp) {
		p-&amp;gt;pcarp-&amp;gt;Flush();
		p-&amp;gt;pcarp-&amp;gt;Abort();
		delete p-&amp;gt;pcarp;
		p-&amp;gt;pcarp = nullptr;
	}

	if (nullptr != p-&amp;gt;pcfp) {
		p-&amp;gt;pcfp-&amp;gt;Flush();
		p-&amp;gt;pcfp-&amp;gt;Abort();
		delete p-&amp;gt;pcfp;
		p-&amp;gt;pcfp = nullptr;
	}

	GetLocalTime(&amp;amp;tm);

	memset(szBuf, 0x0, sizeof(szBuf));

	GetModuleFileName(NULL, szBuf, sizeof(szBuf));
	cstrFilePath.Format(&quot;%s&quot;, szBuf);

	cstrFilePath = cstrFilePath.Left(cstrFilePath.ReverseFind(_T('\\')));

	boRet = cffFindFile.FindFile(cstrFilePath + _T(&quot;\\logs&quot;));
	if (!boRet)
		boRet = CreateDirectory(cstrFilePath + _T(&quot;\\logs&quot;), NULL);
	
	cstrTemp.Empty();
	cstrTemp = cstrFilePath;
	switch (enCat) {
	case TEN_LOGGER_CATEGORY::NORMAL:
		cstrTemp.AppendFormat(_T(&quot;\\logs\\log-normal-%04d-%02d-%02d-%02d-%02d-%02d-%03ld.csv&quot;),
			tm.wYear, tm.wMonth, tm.wDay, tm.wHour, tm.wMinute, tm.wSecond, tm.wMilliseconds);
		break;
	case TEN_LOGGER_CATEGORY::TRADING_EVENT:
		cstrTemp.AppendFormat(_T(&quot;\\logs\\log-trading-%04d-%02d-%02d-%02d-%02d-%02d-%03ld.csv&quot;),
			tm.wYear, tm.wMonth, tm.wDay, tm.wHour, tm.wMinute, tm.wSecond, tm.wMilliseconds);
		break;
	case TEN_LOGGER_CATEGORY::NETWORK:
		cstrTemp.AppendFormat(_T(&quot;\\logs\\log-network-%04d-%02d-%02d-%02d-%02d-%02d-%03ld.csv&quot;),
			tm.wYear, tm.wMonth, tm.wDay, tm.wHour, tm.wMinute, tm.wSecond, tm.wMilliseconds);
		break;
	case TEN_LOGGER_CATEGORY::DBG:
		cstrTemp.AppendFormat(_T(&quot;\\logs\\log-debug-%04d-%02d-%02d-%02d-%02d-%02d-%03ld.csv&quot;),
			tm.wYear, tm.wMonth, tm.wDay, tm.wHour, tm.wMinute, tm.wSecond, tm.wMilliseconds);
		break;
	case TEN_LOGGER_CATEGORY::INFO:
		cstrTemp.AppendFormat(_T(&quot;\\logs\\log-info-%04d-%02d-%02d-%02d-%02d-%02d-%03ld.csv&quot;),
			tm.wYear, tm.wMonth, tm.wDay, tm.wHour, tm.wMinute, tm.wSecond, tm.wMilliseconds);
		break;
	case TEN_LOGGER_CATEGORY::ERR:
		cstrTemp.AppendFormat(_T(&quot;\\logs\\log-error-%04d-%02d-%02d-%02d-%02d-%02d-%03ld.csv&quot;),
			tm.wYear, tm.wMonth, tm.wDay, tm.wHour, tm.wMinute, tm.wSecond, tm.wMilliseconds);
		break;
	default:
		cstrTemp.AppendFormat(_T(&quot;\\logs\\log-unknown-%04d-%02d-%02d-%02d-%02d-%02d-%03ld.csv&quot;),
			tm.wYear, tm.wMonth, tm.wDay, tm.wHour, tm.wMinute, tm.wSecond, tm.wMilliseconds);
	}

	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(enCat)].pcfp = new CFile();
	pcfp = this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(enCat)].pcfp;
	boRet = pcfp-&amp;gt;Open(cstrTemp, CFile::modeWrite | CFile::modeCreate | CFile::modeNoTruncate | CFile::shareDenyNone);
	if (FALSE == boRet) {
		//TODO: erro handling 
	}
	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(enCat)].pcarp = new CArchive(pcfp, CArchive::store, 4096 * 10);
	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(enCat)].pcarp-&amp;gt;WriteString(_T(&quot;hh:mm:ss,msec,ssid,category,file,func,line,msg&quot;));
	this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(enCat)].boIsInit = TRUE;

	return;
}

void CLogger::logger_write_to_file(TEN_LOGGER_CATEGORY enCat, LPCTSTR buf, size_t bufcount)
{
	PTST_LOGGER_SET p = &amp;amp;(this-&amp;gt;astLogger[static_cast&amp;lt;unsigned int&amp;gt;(enCat)]);

	p-&amp;gt;critSect.Lock();
	p-&amp;gt;pcarp-&amp;gt;Write(buf, (UINT)bufcount);
	p-&amp;gt;u32SeqNo++;

	if (MAX_LINE_PER_FILE &amp;lt; (p-&amp;gt;u32SeqNo)) {
		this-&amp;gt;_logger_renew_logfile(enCat);
		p-&amp;gt;u32SeqNo = 0;
	}
	p-&amp;gt;critSect.Unlock();
}

void format_log_msg(char* buf_ptr, int buf_size, char* fmt, ...)
{
	va_list arg_list;

	va_start(arg_list, fmt);
	(void)vsnprintf(buf_ptr, buf_size, fmt, arg_list);
	va_end(arg_list);

	return;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 모듈별 로그 매크로 예시입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신규 모듈을 추가하게 되면 CLogger.h 에 SSID를 추가하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자신의 헤더파일에 매크로를 등록하여 사용합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1017&quot; data-origin-height=&quot;736&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxF8kS/btsBOgsVkEp/heNheNKGNtOFaBcgVs86n0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxF8kS/btsBOgsVkEp/heNheNKGNtOFaBcgVs86n0/img.png&quot; data-alt=&quot;모듈별로 SSID를 부여하고 매크로를 만들어 둡니다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxF8kS/btsBOgsVkEp/heNheNKGNtOFaBcgVs86n0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxF8kS%2FbtsBOgsVkEp%2FheNheNKGNtOFaBcgVs86n0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1017&quot; height=&quot;736&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1017&quot; data-origin-height=&quot;736&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;모듈별로 SSID를 부여하고 매크로를 만들어 둡니다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;334&quot; data-origin-height=&quot;201&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cr8qKO/btsBQWgnzbM/J9f9BZXKX29S9BVB5pb7G1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cr8qKO/btsBQWgnzbM/J9f9BZXKX29S9BVB5pb7G1/img.png&quot; data-alt=&quot;레벨별로 로그파일이 분리 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cr8qKO/btsBQWgnzbM/J9f9BZXKX29S9BVB5pb7G1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcr8qKO%2FbtsBQWgnzbM%2FJ9f9BZXKX29S9BVB5pb7G1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;334&quot; height=&quot;201&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;334&quot; data-origin-height=&quot;201&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;레벨별로 로그파일이 분리 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;173&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOpTbg/btsBQSkxd1g/v3oywX8phL76qVG0Ar1KO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOpTbg/btsBQSkxd1g/v3oywX8phL76qVG0Ar1KO0/img.png&quot; data-alt=&quot;향후 별도 로그분석을 위해 csv로 저장. 보기만 할거면 쉼표가 아닌 예쁜 문자 사용&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOpTbg/btsBQSkxd1g/v3oywX8phL76qVG0Ar1KO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOpTbg%2FbtsBQSkxd1g%2Fv3oywX8phL76qVG0Ar1KO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;663&quot; height=&quot;173&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;173&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;향후 별도 로그분석을 위해 csv로 저장. 보기만 할거면 쉼표가 아닌 예쁜 문자 사용&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;67&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJM8Il/btsBQlmWBHX/xnjr2UbarnA4FfYU09S6s0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJM8Il/btsBQlmWBHX/xnjr2UbarnA4FfYU09S6s0/img.png&quot; data-alt=&quot;콘솔에 출력된 로그. 콘솔을 마우스 클릭하면 프로그램이 멈추니 유의.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJM8Il/btsBQlmWBHX/xnjr2UbarnA4FfYU09S6s0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJM8Il%2FbtsBQlmWBHX%2Fxnjr2UbarnA4FfYU09S6s0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;587&quot; height=&quot;67&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;67&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;콘솔에 출력된 로그. 콘솔을 마우스 클릭하면 프로그램이 멈추니 유의.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상으로 GUI 환경에서 콘솔로 디버깅 메시지를 출력하는 방법과 파일에 로그 저장 시 각 모듈별, 레벨별로 분류하고 부하를 줄이는 방식에 대해 알아보았습니다.&lt;/p&gt;</description>
      <category>프로그래밍/C | C++</category>
      <category>CArchive</category>
      <category>cfile</category>
      <category>flush</category>
      <category>GetModuleFileName</category>
      <category>Log 기록</category>
      <category>Log 파일</category>
      <category>Logger</category>
      <category>로그파일</category>
      <category>콘솔 로그</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/25</guid>
      <comments>https://noyecube.tistory.com/entry/MFC-%EB%A1%9C%EA%B7%B8Log-%EC%B6%9C%EB%A0%A5-Logger#entry25comment</comments>
      <pubDate>Fri, 12 Aug 2022 11:55:48 +0900</pubDate>
    </item>
    <item>
      <title>[MFC] simdjson library 설치</title>
      <link>https://noyecube.tistory.com/entry/MFC-simdjson-library-%EC%84%A4%EC%B9%98</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;MFC 기반 프로젝트에서 json을 파싱 하기 위해 가장 적절한 json 라이브러리를 알아보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과 simdjson이 가장 최신기술을 활용하여 개발된 것 같아서 프로젝트에 적용해 보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 32비트 프로젝트에서는 사용이 불가능합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. simdjson?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SIMD 기술을 적용하여 json 파서 중 가장 빠른 파서라고 하며 관련 논문도 찾아볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SIMD는 Single Instruction Multiple Data의 줄임말로서 병렬 컴퓨팅의 한 종류입니다. 하나의 명령어로 여러 개의 값을 동시에 계산하는 기술입니다. 하나의 명령어를 수행하는 데 걸리는 시간을 줄일 수 있기 때문에 연산이 많은 작업에서 성능을 향상할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/simdjson/simdjson&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/simdjson/simdjson에서&lt;/a&gt;&amp;nbsp; simdjson.h 파일과 simdjson.cpp 파일 단 2개만 다운로드하여&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에 포함시켜 컴파일하면 끝입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역시 똑똑한 박사들이 개발한 거라 그런지 사용법도 간단하네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 프로젝트에 헤더파일과 cpp파일만 추가 후 빌드합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;289&quot; data-origin-height=&quot;95&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eNDFOw/btrIMCgY4zr/6cpIvBenOqDMR1mHwzwwrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eNDFOw/btrIMCgY4zr/6cpIvBenOqDMR1mHwzwwrk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eNDFOw/btrIMCgY4zr/6cpIvBenOqDMR1mHwzwwrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeNDFOw%2FbtrIMCgY4zr%2F6cpIvBenOqDMR1mHwzwwrk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;289&quot; height=&quot;95&quot; data-origin-width=&quot;289&quot; data-origin-height=&quot;95&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;286&quot; data-origin-height=&quot;96&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kMCOx/btrIEY7boaN/CKEB8bRtYo3zOckaYMvyP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kMCOx/btrIEY7boaN/CKEB8bRtYo3zOckaYMvyP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kMCOx/btrIEY7boaN/CKEB8bRtYo3zOckaYMvyP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkMCOx%2FbtrIEY7boaN%2FCKEB8bRtYo3zOckaYMvyP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;286&quot; height=&quot;96&quot; data-origin-width=&quot;286&quot; data-origin-height=&quot;96&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 최신 c++17로 작성되어 있으므로 기본옵션 c++14를 c++17 이상 상위 버전으로 변경해 주어야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;83&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4rg80/btrIKm6PYA4/tvOP8mTG6JtJur6LEzAKk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4rg80/btrIKm6PYA4/tvOP8mTG6JtJur6LEzAKk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4rg80/btrIKm6PYA4/tvOP8mTG6JtJur6LEzAKk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4rg80%2FbtrIKm6PYA4%2FtvOP8mTG6JtJur6LEzAKk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;610&quot; height=&quot;83&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;83&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제 json 파일을 디스크에 저장 후 테스트코드를 넣고 실행하여 결과값 100이 나오면 제대로 설치가 된 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQ7TPM/btrIEXtDyMg/X7kk40iAGX1HYYQGi4gpyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQ7TPM/btrIEXtDyMg/X7kk40iAGX1HYYQGi4gpyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQ7TPM/btrIEXtDyMg/X7kk40iAGX1HYYQGi4gpyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQ7TPM%2FbtrIEXtDyMg%2FX7kk40iAGX1HYYQGi4gpyk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;890&quot; height=&quot;180&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;180&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상으로 c++로 개발되어 c/c++ 프로젝트에 활용할 수 있는 json 라이브러리인 simdjson을 프로젝트에 적용하는 방법에 대해 알아보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;The simdjson library&quot; href=&quot;https://simdjson.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;참고사이트: https://simdjson.org/&lt;/a&gt;&lt;/p&gt;</description>
      <category>프로그래밍/C | C++</category>
      <category>fastest json parser</category>
      <category>json 파서</category>
      <category>simdjson</category>
      <category>simdjson c project</category>
      <category>simdjson library</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/24</guid>
      <comments>https://noyecube.tistory.com/entry/MFC-simdjson-library-%EC%84%A4%EC%B9%98#entry24comment</comments>
      <pubDate>Mon, 1 Aug 2022 23:54:13 +0900</pubDate>
    </item>
    <item>
      <title>[MFC] TA-LIB 설치</title>
      <link>https://noyecube.tistory.com/entry/MFC-TA-LIB-%EC%84%A4%EC%B9%98</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;상용 HTS 프로그램을 사용하면 차트와 연계하여 이동평균선, MACD 등의 지표 정보를 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 xingAPI, Kiwoom Plus 같은 증권사 API를 이용하면 지표를 별도로 계산해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 지표에 대한 계산식은 쉽게 찾을 수 있지만 그 지표들를 직접 계산하는 코드를 작성하는 것은 시간낭비라고 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면 이미 누군가 만들어두었기 때문에 갖다 쓰면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 기술적 지표 분석 라이브러리 TA-LIB을 MFC 프로젝트에 설치하는 방법을 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. TA-LIB에 대하여&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TA-Lib은 트레이딩 소프트웨어 개발자들을 위한 기술적 분석 라이브러리로서 오픈소스입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 경로에서 TA-Lib 소스코드를 다운로드할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;Ta-Lib&quot; href=&quot;https://ta-lib.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ta-lib.org/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1702295105006&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;TA-Lib - Technical Analysis Library&quot; data-og-description=&quot;Use TA-Lib to add technical analysis to your own financial market trading applications 200 indicators such as ADX, MACD, RSI, Stochastic, Bollinger Bands etc... See complete list... Candlestick patterns recognition Core written in C/C++ with API also avail&quot; data-og-host=&quot;ta-lib.org&quot; data-og-source-url=&quot;https://ta-lib.org/&quot; data-og-url=&quot;https://ta-lib.org/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/buCEVn/hyUIDQnuKr/6QBPtb1NUh301fWcgK0MF1/img.png?width=733&amp;amp;height=230&amp;amp;face=0_0_733_230&quot;&gt;&lt;a href=&quot;https://ta-lib.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ta-lib.org/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/buCEVn/hyUIDQnuKr/6QBPtb1NUh301fWcgK0MF1/img.png?width=733&amp;amp;height=230&amp;amp;face=0_0_733_230');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;TA-Lib - Technical Analysis Library&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Use TA-Lib to add technical analysis to your own financial market trading applications 200 indicators such as ADX, MACD, RSI, Stochastic, Bollinger Bands etc... See complete list... Candlestick patterns recognition Core written in C/C++ with API also avail&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ta-lib.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 라이브러리 파일(.LIB) 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비주얼스튜디오의 Native Tools Command Prompt를 띄워서 nmake를 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prompt는 x86 버전과 x64 버전이 구분되어 있으므로 프로젝트에 맞는 비트를 선택해야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1029&quot; data-origin-height=&quot;163&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WK1dK/btrIA1cqcJp/VvReJiTrskrgq5UI5geXwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WK1dK/btrIA1cqcJp/VvReJiTrskrgq5UI5geXwk/img.png&quot; data-alt=&quot;nmake 실행 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WK1dK/btrIA1cqcJp/VvReJiTrskrgq5UI5geXwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWK1dK%2FbtrIA1cqcJp%2FVvReJiTrskrgq5UI5geXwk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1029&quot; height=&quot;163&quot; data-origin-width=&quot;1029&quot; data-origin-height=&quot;163&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;nmake 실행 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 LIB파일의 비트 확인은 dumpbin 명령으로 헤더 덤프를 수행하면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;617&quot; data-origin-height=&quot;317&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OHNEe/btsBJdCiRfq/QAhyju5oWBjkc3Dkaq4VL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OHNEe/btsBJdCiRfq/QAhyju5oWBjkc3Dkaq4VL0/img.png&quot; data-alt=&quot;dumpbin 실행화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OHNEe/btsBJdCiRfq/QAhyju5oWBjkc3Dkaq4VL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOHNEe%2FbtsBJdCiRfq%2FQAhyju5oWBjkc3Dkaq4VL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;617&quot; height=&quot;317&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;617&quot; data-origin-height=&quot;317&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;dumpbin 실행화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라이브러리가 정상인지 확인은 .LIB 빌드 때 같이 생성된 ta_regtest.exe를 실행시켜 봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;All tests succeeded 메시지를 확인하면 정상적으로 빌드된 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;611&quot; data-origin-height=&quot;520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CZtMk/btsBKh5MSyI/bRs4jIoydnPuGkjC5kh1Z1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CZtMk/btsBKh5MSyI/bRs4jIoydnPuGkjC5kh1Z1/img.png&quot; data-alt=&quot;ta_regtest 실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CZtMk/btsBKh5MSyI/bRs4jIoydnPuGkjC5kh1Z1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCZtMk%2FbtsBKh5MSyI%2FbRs4jIoydnPuGkjC5kh1Z1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;611&quot; height=&quot;520&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;611&quot; data-origin-height=&quot;520&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ta_regtest 실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. TA-Lib 빌드 결과물을 프로젝트에 추가하기&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.1 lib 파일 프로젝트 폴더에 복사&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 폴더에 &quot;prebuilt/ta-lib&quot; 폴더 생성 후 cmd, cmr을 복사합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;206&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5xX0J/btsBF3AI5ag/gt5AbPkSWbovgQQCte9YtK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5xX0J/btsBF3AI5ag/gt5AbPkSWbovgQQCte9YtK/img.png&quot; data-alt=&quot;cmr, cmd 복사 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5xX0J/btsBF3AI5ag/gt5AbPkSWbovgQQCte9YtK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5xX0J%2FbtsBF3AI5ag%2Fgt5AbPkSWbovgQQCte9YtK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;561&quot; height=&quot;206&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;206&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;cmr, cmd 복사 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2 헤더파일 복사&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;prebuilt/ta-lib/include&quot; 폴더에 헤더파일을 복사합니다. 응용 코드에서는 &quot;ta_libc.h&quot;만 인클루드 하면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;617&quot; data-origin-height=&quot;145&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LMmB7/btsBRScNF9P/WYd8HjpwokLk62WhO3faCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LMmB7/btsBRScNF9P/WYd8HjpwokLk62WhO3faCk/img.png&quot; data-alt=&quot;헤더파일 복사 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LMmB7/btsBRScNF9P/WYd8HjpwokLk62WhO3faCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLMmB7%2FbtsBRScNF9P%2FWYd8HjpwokLk62WhO3faCk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;617&quot; height=&quot;145&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;617&quot; data-origin-height=&quot;145&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;헤더파일 복사 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.3 프로젝트 설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.3.1. 디버그, 릴리즈 모드의 프로퍼티에 아래 .lib 파일명을 프로젝트 속성에 추가합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Debug-x64 : ta_libc_cmd.lib;ta_abstract_cmd.lib;ta_common_cmd.lib;ta_func_cmd.lib&lt;/li&gt;
&lt;li&gt;Release-x64 : ta_libc_cmr.lib;ta_abstract_cmr.lib;ta_common_cmr.lib;ta_func_cmr.lib&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;779&quot; data-origin-height=&quot;239&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EWzf2/btsBMQUmtyg/xj7GXcHfyqvRj1PNym90ZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EWzf2/btsBMQUmtyg/xj7GXcHfyqvRj1PNym90ZK/img.png&quot; data-alt=&quot;Property-Linker-Input-Additional Dependencies&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EWzf2/btsBMQUmtyg/xj7GXcHfyqvRj1PNym90ZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEWzf2%2FbtsBMQUmtyg%2Fxj7GXcHfyqvRj1PNym90ZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;779&quot; height=&quot;239&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;779&quot; data-origin-height=&quot;239&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Property-Linker-Input-Additional Dependencies&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.3.2. lib 파일 탐색 경로를 추가합니다.(디버그, 릴리즈 각각)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;647&quot; data-origin-height=&quot;131&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H2jQv/btsBHQgl58k/OK8zdcrreeXNqHkBn0HKkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H2jQv/btsBHQgl58k/OK8zdcrreeXNqHkBn0HKkk/img.png&quot; data-alt=&quot;Property-Linker-General-Additional Library Directories&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H2jQv/btsBHQgl58k/OK8zdcrreeXNqHkBn0HKkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH2jQv%2FbtsBHQgl58k%2FOK8zdcrreeXNqHkBn0HKkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;647&quot; height=&quot;131&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;647&quot; data-origin-height=&quot;131&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Property-Linker-General-Additional Library Directories&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.3.3. 솔루션 탐색기에 헤더파일을 추가합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;377&quot; data-origin-height=&quot;192&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/digtMq/btsBKhLtoZD/E6ur8iGk5b1JKliykFwil1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/digtMq/btsBKhLtoZD/E6ur8iGk5b1JKliykFwil1/img.png&quot; data-alt=&quot;솔루션 탐색기 헤더파일&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/digtMq/btsBKhLtoZD/E6ur8iGk5b1JKliykFwil1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdigtMq%2FbtsBKhLtoZD%2FE6ur8iGk5b1JKliykFwil1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;377&quot; height=&quot;192&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;377&quot; data-origin-height=&quot;192&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;솔루션 탐색기 헤더파일&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 테스트코드 작성 및 리턴값 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트코드 작성 파일에 #include &quot;ta_libc.h&quot; 구문을 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 OnAppAbout() 함수와 같은 임시 영역에 테스트 코드를 작성하고 실행해 봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리턴결과가 0이 나오면 정상입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리턴값 0은 ENUM_BEGIN에서 확인하면 TA_SUCCESS를 의미합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;429&quot; data-origin-height=&quot;263&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ufqOK/btsBKf7WCqD/dJWnEWqquhUYSZ7V2NfSsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ufqOK/btsBKf7WCqD/dJWnEWqquhUYSZ7V2NfSsK/img.png&quot; data-alt=&quot;TA-Lib 테스트 코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ufqOK/btsBKf7WCqD/dJWnEWqquhUYSZ7V2NfSsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FufqOK%2FbtsBKf7WCqD%2FdJWnEWqquhUYSZ7V2NfSsK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;429&quot; height=&quot;263&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;429&quot; data-origin-height=&quot;263&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;TA-Lib 테스트 코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;226&quot; data-origin-height=&quot;45&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBDlcl/btsBHR7pI9l/xFFzM3OsR8epQ0EFwvz8T0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBDlcl/btsBHR7pI9l/xFFzM3OsR8epQ0EFwvz8T0/img.png&quot; data-alt=&quot;TA_RetCode 0 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBDlcl/btsBHR7pI9l/xFFzM3OsR8epQ0EFwvz8T0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBDlcl%2FbtsBHR7pI9l%2FxFFzM3OsR8epQ0EFwvz8T0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;226&quot; height=&quot;45&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;226&quot; data-origin-height=&quot;45&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;TA_RetCode 0 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;671&quot; data-origin-height=&quot;41&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTIBEd/btsBF1iykpX/Qc5EpHVSRD9pVTZlCcHHo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTIBEd/btsBF1iykpX/Qc5EpHVSRD9pVTZlCcHHo1/img.png&quot; data-alt=&quot;TA_SUCCESS&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTIBEd/btsBF1iykpX/Qc5EpHVSRD9pVTZlCcHHo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTIBEd%2FbtsBF1iykpX%2FQc5EpHVSRD9pVTZlCcHHo1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;671&quot; height=&quot;41&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;671&quot; data-origin-height=&quot;41&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;TA_SUCCESS&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상으로 MFC 프로젝트에 TA-Lib를 적용하는 방법을 알아보았습니다.&lt;/p&gt;</description>
      <category>프로그래밍/C | C++</category>
      <category>ta-lib</category>
      <category>ta-lib install</category>
      <category>ta-lib 사용법</category>
      <category>ta-lib 설치</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/23</guid>
      <comments>https://noyecube.tistory.com/entry/MFC-TA-LIB-%EC%84%A4%EC%B9%98#entry23comment</comments>
      <pubDate>Mon, 1 Aug 2022 22:22:53 +0900</pubDate>
    </item>
    <item>
      <title>SOVE 실링팬 셀프 설치</title>
      <link>https://noyecube.tistory.com/entry/SOVE-%EC%8B%A4%EB%A7%81%ED%8C%AC-%EC%85%80%ED%94%84-%EC%84%A4%EC%B9%98</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;거실에 실링팬을 설치하였는데 만족도가 높아서 안방에도 방등 대신 실링팬을 셀프로 설치하며 남긴 기록입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알리에서 가성비가 좋기로 소문난 SOVE 실링팬을 구매하여 설치하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;거실에 루씨에어 실링팬을 설치해 봤는데 천장형 선풍기 치고는 가격거품이 너무 심한 제품인 것 같아&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안방에는 SOVE 실링팬을 설치한 것입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 루씨에어 vs SOVE&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;루씨에어 실링팬은 52인치 제품이 약 46만 원인데 반해 SOVE 실링팬은 52인치가 약 13만 원으로 3배 이상 저렴합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 SOVE 실링팬과 동일한 제품이 우리나라에서 다른 브랜드 이름을 달고 30만원대에 팔고 있다는 사실에도 놀랐습니다. 그러므로 SOVE는 직구하시는 것을 추천합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치한 결과는 대만족입니다. 방 가운데 있던 주광색의 등은 별로 안썼는데 실링팬은 여름철에 많이 사용하게 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나머지 작은방들도 도배 전에 보강을 해두었다면 작은 방에도 실링팬을 설치할 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;루씨에어에 비해 SOVE 단점은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;천장브라켓 나사 홀 범위가 조금 좁습니다. 별도로 천장 보강을 안 했다면 목상에 고정을 해야 하는데 목상 간격과 브래킷의 홀 간격이 잘 안 맞네요.&lt;/li&gt;
&lt;li&gt;설명서의 전선 커넥터가 없어(아래 사진 빨간 원) 별도의 커넥터를 사용해야 했습니다.&lt;/li&gt;
&lt;li&gt;루씨에어는 혼자서도 작업할 수 있도록 천장에 모터들 임시로 걸어 둘 수 있는 금속고리가 있는데 SOVE는 보조 1명 없으면 모터 장착할 때 어렵습니다.&lt;/li&gt;
&lt;li&gt;1단 스피드가 루씨에어에 비해 조금 더 빠릅니다. 살랑살랑 부는 약한 바람은 안됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;22.webp&quot; data-origin-width=&quot;435&quot; data-origin-height=&quot;412&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DBfl2/btsBGGkXe0e/cqegXWKTmtsvC5Pirnm0kk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DBfl2/btsBGGkXe0e/cqegXWKTmtsvC5Pirnm0kk/img.webp&quot; data-alt=&quot;SOVE 설명서 사진&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DBfl2/btsBGGkXe0e/cqegXWKTmtsvC5Pirnm0kk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDBfl2%2FbtsBGGkXe0e%2FcqegXWKTmtsvC5Pirnm0kk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;435&quot; height=&quot;412&quot; data-filename=&quot;22.webp&quot; data-origin-width=&quot;435&quot; data-origin-height=&quot;412&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SOVE 설명서 사진&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. SOVE 실링팬 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안방 천장에는 무거운 조명이 있던 자리라서 별도의 보강을 안해도 될 정도의 목재가 들어 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 평평한 합판이 아니고 각목 2개 였기 때문에 브래킷의 3개 구멍을 2개, 1개로 나눠서 나사를 박아야 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한쪽 각목에 나사 2개를 박으면 나머지 1개 구멍이 옆 각목에 닿지 않아서 이 문제는 드릴로 기존 나사 홀 옆에 추가 타공으로 해결하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;23.webp&quot; data-origin-width=&quot;505&quot; data-origin-height=&quot;579&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bukwoq/btsBRSDPSJv/rckuxPlJeQTg8e8Sy2t6P1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bukwoq/btsBRSDPSJv/rckuxPlJeQTg8e8Sy2t6P1/img.webp&quot; data-alt=&quot;천장 내 각목 위치(집마다 다를 수 있음)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bukwoq/btsBRSDPSJv/rckuxPlJeQTg8e8Sy2t6P1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbukwoq%2FbtsBRSDPSJv%2FrckuxPlJeQTg8e8Sy2t6P1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;505&quot; height=&quot;579&quot; data-filename=&quot;23.webp&quot; data-origin-width=&quot;505&quot; data-origin-height=&quot;579&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;천장 내 각목 위치(집마다 다를 수 있음)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;집에 드릴과 드릴비트만 있으면 아래와 같이 임의의 위치에 타공 가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;24.webp&quot; data-origin-width=&quot;645&quot; data-origin-height=&quot;710&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d98IhW/btsBQpB6WOs/sIks9sSp6XnsByZoFSLEV1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d98IhW/btsBQpB6WOs/sIks9sSp6XnsByZoFSLEV1/img.webp&quot; data-alt=&quot;드릴로 나사구멍 타공&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d98IhW/btsBQpB6WOs/sIks9sSp6XnsByZoFSLEV1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd98IhW%2FbtsBQpB6WOs%2FsIks9sSp6XnsByZoFSLEV1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;645&quot; height=&quot;710&quot; data-filename=&quot;24.webp&quot; data-origin-width=&quot;645&quot; data-origin-height=&quot;710&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;드릴로 나사구멍 타공&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 사진과 같이 총 4개의 나사로 고정하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;25.webp&quot; data-origin-width=&quot;629&quot; data-origin-height=&quot;608&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b76jhz/btsBGoENB8i/H2UAfVLqV7RIcQ9cnmwxfk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b76jhz/btsBGoENB8i/H2UAfVLqV7RIcQ9cnmwxfk/img.webp&quot; data-alt=&quot;나사로 고정한 사진&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b76jhz/btsBGoENB8i/H2UAfVLqV7RIcQ9cnmwxfk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb76jhz%2FbtsBGoENB8i%2FH2UAfVLqV7RIcQ9cnmwxfk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;629&quot; height=&quot;608&quot; data-filename=&quot;25.webp&quot; data-origin-width=&quot;629&quot; data-origin-height=&quot;608&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;나사로 고정한 사진&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;26.webp&quot; data-origin-width=&quot;695&quot; data-origin-height=&quot;722&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oESbn/btsBQTpsITf/KT3JsoXj2ylZKoch6dKEq1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oESbn/btsBQTpsITf/KT3JsoXj2ylZKoch6dKEq1/img.webp&quot; data-alt=&quot;모터 모듈 사진&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oESbn/btsBQTpsITf/KT3JsoXj2ylZKoch6dKEq1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoESbn%2FbtsBQTpsITf%2FKT3JsoXj2ylZKoch6dKEq1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;695&quot; height=&quot;722&quot; data-filename=&quot;26.webp&quot; data-origin-width=&quot;695&quot; data-origin-height=&quot;722&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;모터 모듈 사진&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날개를 장착하기 전에 무선 컨트롤러까지 연결 후 정상 동작 여부를 확인해야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;27.webp&quot; data-origin-width=&quot;570&quot; data-origin-height=&quot;611&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7dJtd/btsBKjWOuer/2aBUBRGi64VixMhEwkNjm0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7dJtd/btsBKjWOuer/2aBUBRGi64VixMhEwkNjm0/img.webp&quot; data-alt=&quot;날개 장착 전 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7dJtd/btsBKjWOuer/2aBUBRGi64VixMhEwkNjm0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7dJtd%2FbtsBKjWOuer%2F2aBUBRGi64VixMhEwkNjm0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;570&quot; height=&quot;611&quot; data-filename=&quot;27.webp&quot; data-origin-width=&quot;570&quot; data-origin-height=&quot;611&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;날개 장착 전 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날개를 고정하는 나사의 드라이버 홈이 생각보다 무른 편입니다. 그래서 날개의 홈을 이용하여 드라이버를 돌리기만 하면 나사가 손상되었습니다. 나사를 돌릴 때는 천장 쪽으로 드라이버를 밀면서 조여주면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;28.webp&quot; data-origin-width=&quot;630&quot; data-origin-height=&quot;772&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WT32N/btsBGjDvwfP/ypxPW6yQuNMJeAMx9oUsv1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WT32N/btsBGjDvwfP/ypxPW6yQuNMJeAMx9oUsv1/img.webp&quot; data-alt=&quot;날개 고정 사진&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WT32N/btsBGjDvwfP/ypxPW6yQuNMJeAMx9oUsv1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWT32N%2FbtsBGjDvwfP%2FypxPW6yQuNMJeAMx9oUsv1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;630&quot; height=&quot;772&quot; data-filename=&quot;28.webp&quot; data-origin-width=&quot;630&quot; data-origin-height=&quot;772&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;날개 고정 사진&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;

            &lt;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&gt;
            &lt;/figure&gt;
        
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;29.webp&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;702&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnC4Jb/btsBGXmnSkx/bqurztUdW8GKVQZOV0VH9k/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnC4Jb/btsBGXmnSkx/bqurztUdW8GKVQZOV0VH9k/img.webp&quot; data-alt=&quot;거실에 설치한 루씨에어 실링팬&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnC4Jb/btsBGXmnSkx/bqurztUdW8GKVQZOV0VH9k/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnC4Jb%2FbtsBGXmnSkx%2FbqurztUdW8GKVQZOV0VH9k%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;682&quot; height=&quot;702&quot; data-filename=&quot;29.webp&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;702&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;거실에 설치한 루씨에어 실링팬&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Personal</category>
      <category>SOVE</category>
      <category>루씨에어</category>
      <category>실링팬</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/22</guid>
      <comments>https://noyecube.tistory.com/entry/SOVE-%EC%8B%A4%EB%A7%81%ED%8C%AC-%EC%85%80%ED%94%84-%EC%84%A4%EC%B9%98#entry22comment</comments>
      <pubDate>Mon, 25 Jul 2022 19:05:42 +0900</pubDate>
    </item>
    <item>
      <title>주방후드 전동댐퍼 설치하기</title>
      <link>https://noyecube.tistory.com/entry/%EC%A3%BC%EB%B0%A9%ED%9B%84%EB%93%9C-%EC%A0%84%EB%8F%99%EB%8C%90%ED%8D%BC-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 아파트에 설치된 주방 후드 제조사는 하츠입니다만, 역류방지 역할을 하는 기본 댐퍼가 플라스틱 소재로 기밀성이 좋지 못하여 식사 시간대에 다른 집에서 후드를 가동하면 가동하지 않은 집에 냄새가 역류합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이웃집의 고기 굽는 냄새, 된장찌개냄새, 생선구이냄새 등이 우리 집에 들어와서 우리 집 공기청정기까지 반응을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;30층이상 고층에 살 때는 비바람 몰아치는날 주방후드 배관으로 공기가 역류하여 기본 댐퍼가 바람에 펄럭거리며 마치 빗물이 후드로 떨어지는 듯한 소리가 났었습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 전동댐퍼 소개&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전동댐퍼는 전원을 켜면 댐퍼가 열렸다가 전원을 끄면 댐퍼가 닫히면서 공기의 이동을 차단하는 제품입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;힘펠 전동댐퍼를 많이 사용하는 것 같아서 힘펠 전동댐퍼를 구매하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하츠 후드에 장착되어 있는 기본 댐퍼와 다르게 테두리가 고무로 되어 있어 기밀성이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 아파트는 125파이 배관이라고 하여 100파이와 125파이 제품 중 125파이로 구입하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;15.webp&quot; data-origin-width=&quot;605&quot; data-origin-height=&quot;789&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZHExJ/btsBGjQ2mDA/RKpxGrPQvvlKSwiI5kt7e1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZHExJ/btsBGjQ2mDA/RKpxGrPQvvlKSwiI5kt7e1/img.webp&quot; data-alt=&quot;전동댐퍼 사진&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZHExJ/btsBGjQ2mDA/RKpxGrPQvvlKSwiI5kt7e1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZHExJ%2FbtsBGjQ2mDA%2FRKpxGrPQvvlKSwiI5kt7e1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;605&quot; height=&quot;789&quot; data-filename=&quot;15.webp&quot; data-origin-width=&quot;605&quot; data-origin-height=&quot;789&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;전동댐퍼 사진&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 전동댐퍼 설치 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주방후드 필터를 떼어내고 안쪽을 보면 플라스틱 박스가 보입니다.(컨트롤러 박스 사진 참조)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 PCB 회로 등 컨트롤러가 위치합니다. 박스의 나사를 풉니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;16.webp&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;604&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/De7qa/btsBQs6FFo4/7Mj2SJtGO0pn4kQEV028sK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/De7qa/btsBQs6FFo4/7Mj2SJtGO0pn4kQEV028sK/img.webp&quot; data-alt=&quot;컨트롤러 박스&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/De7qa/btsBQs6FFo4/7Mj2SJtGO0pn4kQEV028sK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDe7qa%2FbtsBQs6FFo4%2F7Mj2SJtGO0pn4kQEV028sK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;632&quot; height=&quot;604&quot; data-filename=&quot;16.webp&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;604&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;컨트롤러 박스&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;박스의 나사를 풀면 여기에 캐패시터가 하나 있는데 거기에 흰색선과 유색선이 각각 1개씩 꽂혀있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기에 전동댐퍼의 흰색선과 유색선을 꽂고 기존에 꽂혀있던 선은 전동댐퍼선에 있는 커넥터에 꽂아주면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;17.webp&quot; data-origin-width=&quot;1057&quot; data-origin-height=&quot;698&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9ziRO/btsBSaj9bc1/XIn8BwtpiL7RxFegouHT50/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9ziRO/btsBSaj9bc1/XIn8BwtpiL7RxFegouHT50/img.webp&quot; data-alt=&quot;컨트롤러 배선 참고용&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9ziRO/btsBSaj9bc1/XIn8BwtpiL7RxFegouHT50/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9ziRO%2FbtsBSaj9bc1%2FXIn8BwtpiL7RxFegouHT50%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1057&quot; height=&quot;698&quot; data-filename=&quot;17.webp&quot; data-origin-width=&quot;1057&quot; data-origin-height=&quot;698&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;컨트롤러 배선 참고용&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파란색 동그라미로 표시된 검정색의 네모난 상자 같은 부품이 캐패시터입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분홍색과 흰색의 기존 선을 뽑았고 전동댐퍼의 흰색, 검정 선을 캐패시터에 꽂고 기존 선은 전동댐퍼선에 있는 커넥터에 연결하면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;18.webp&quot; data-origin-width=&quot;990&quot; data-origin-height=&quot;765&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZEvxM/btsBGEglWrO/VV92eshI3eEKRWAEAsmKzK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZEvxM/btsBGEglWrO/VV92eshI3eEKRWAEAsmKzK/img.webp&quot; data-alt=&quot;배선 연결 사진&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZEvxM/btsBGEglWrO/VV92eshI3eEKRWAEAsmKzK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZEvxM%2FbtsBGEglWrO%2FVV92eshI3eEKRWAEAsmKzK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;990&quot; height=&quot;765&quot; data-filename=&quot;18.webp&quot; data-origin-width=&quot;990&quot; data-origin-height=&quot;765&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;배선 연결 사진&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배선작업 후에는 조립을 하지 말고 댐퍼가 정상적으로 동작하는지 On/Off&amp;nbsp; 테스트를 해봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;후드 위쪽의 공기가 빠져나가는 배관이 플라스틱 배관인데 시계방향으로 조이도록 되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 조금 풀려있어서 단단하게 조였습니다. 아마도 여기에서도 이웃집의 음식 냄새가 누출되었을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;19.webp&quot; data-origin-width=&quot;691&quot; data-origin-height=&quot;641&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZ6IhL/btsBQqueK1E/k16fUa2sBkTafIa5V0qvK0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZ6IhL/btsBQqueK1E/k16fUa2sBkTafIa5V0qvK0/img.webp&quot; data-alt=&quot;후드 배출 배관&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZ6IhL/btsBQqueK1E/k16fUa2sBkTafIa5V0qvK0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZ6IhL%2FbtsBQqueK1E%2Fk16fUa2sBkTafIa5V0qvK0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;691&quot; height=&quot;641&quot; data-filename=&quot;19.webp&quot; data-origin-width=&quot;691&quot; data-origin-height=&quot;641&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;후드 배출 배관&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주름관과 후드 배관의 기밀성 유지를 위해 안쪽 결합부위에 두꺼운 양면테이프를 붙이고 연결하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;20.webp&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;678&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yUXOL/btsBF1JCPF9/bpj6HPcvmcqOKJqIUClkw1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yUXOL/btsBF1JCPF9/bpj6HPcvmcqOKJqIUClkw1/img.webp&quot; data-alt=&quot;양면 테이트 붙이기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yUXOL/btsBF1JCPF9/bpj6HPcvmcqOKJqIUClkw1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyUXOL%2FbtsBF1JCPF9%2Fbpj6HPcvmcqOKJqIUClkw1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;591&quot; height=&quot;678&quot; data-filename=&quot;20.webp&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;678&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;양면 테이트 붙이기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바깥쪽은 은박 테이프로 밀봉하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;21.webp&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;699&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnR12V/btsBJfUoR9f/cKA7CZcnii7q2GEYdkYY3k/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnR12V/btsBJfUoR9f/cKA7CZcnii7q2GEYdkYY3k/img.webp&quot; data-alt=&quot;은박 테이프 밀봉 사진&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnR12V/btsBJfUoR9f/cKA7CZcnii7q2GEYdkYY3k/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnR12V%2FbtsBJfUoR9f%2FcKA7CZcnii7q2GEYdkYY3k%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;610&quot; height=&quot;699&quot; data-filename=&quot;21.webp&quot; data-origin-width=&quot;610&quot; data-origin-height=&quot;699&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;은박 테이프 밀봉 사진&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상으로 주방후드에 전동댐퍼를 장착하여 이웃집 음식 냄새를 차단하는 방법을 알아보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요즘 뉴스기사를 보면 이웃집의 삼겹살 굽는 냄새 때문에 다른 집에서 민원을 넣는다는 내용이 있던데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 소비자의 입장에서 제품을 제대로 만들면 추가적인 비용과 노력을 들이지 않아도 될 텐데 아쉬운 부분입니다.&amp;nbsp;&lt;/p&gt;</description>
      <category>Personal</category>
      <category>dbn-90s</category>
      <category>JMD-S125HLD</category>
      <category>전동댐퍼</category>
      <category>주방후드댐퍼</category>
      <category>하츠후드역류</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/21</guid>
      <comments>https://noyecube.tistory.com/entry/%EC%A3%BC%EB%B0%A9%ED%9B%84%EB%93%9C-%EC%A0%84%EB%8F%99%EB%8C%90%ED%8D%BC-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0#entry21comment</comments>
      <pubDate>Tue, 5 Jul 2022 14:37:47 +0900</pubDate>
    </item>
    <item>
      <title>스마트 조명, 스마트스위치, IoT조명</title>
      <link>https://noyecube.tistory.com/entry/%EC%8A%A4%EB%A7%88%ED%8A%B8-%EC%A1%B0%EB%AA%85-%EC%8A%A4%EB%A7%88%ED%8A%B8%EC%8A%A4%EC%9C%84%EC%B9%98-IoT%EC%A1%B0%EB%AA%85</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 집으로 이사를 하면서 조명과 관련 전기공사는 셀프로 진행하기로 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;향후 집 팔 때를 대비하여 인수인계 목적으로 남기는 포스팅입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 조명 설계 고려사항&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;흰색 빛을 내는 주광색은 배제하고 주방은 주백색 나머지 공간은 전구색&lt;/li&gt;
&lt;li&gt;독서, 작업 등 집중이 필요한 공간은 별도 조명 사용&lt;/li&gt;
&lt;li&gt;다운라이트끼리의 간격은 15cm 또는 전체 벽면 길이 나누기 다운라이트의 수로 결정&lt;/li&gt;
&lt;li&gt;거실, 주방 등 공용공간은 IoT 스위치를 연동하여 스마트폰으로 조작 가능하도록 설정&lt;/li&gt;
&lt;li&gt;플리커프리, 절연컨버터 사양의 제품만 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고려 사항들을 반영하여 아래와 같이 평면도에 조명 위치를 표기하고 전기 기술자를 별도로 섭외하여 작업 요청하였습니다. 인테리어 회사에 턴키로 의뢰하는 것보다 결과물은 만족스럽고 조명 품질도 더 좋은데 비용은 많이 절약되었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.webp&quot; data-origin-width=&quot;1276&quot; data-origin-height=&quot;756&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbWSvi/btsBJip2mo1/u3rlxf0TUldskuKH9s2AVK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbWSvi/btsBJip2mo1/u3rlxf0TUldskuKH9s2AVK/img.webp&quot; data-alt=&quot;평면도 및 조명 위치 표기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbWSvi/btsBJip2mo1/u3rlxf0TUldskuKH9s2AVK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbWSvi%2FbtsBJip2mo1%2Fu3rlxf0TUldskuKH9s2AVK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1276&quot; height=&quot;756&quot; data-filename=&quot;1.webp&quot; data-origin-width=&quot;1276&quot; data-origin-height=&quot;756&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;평면도 및 조명 위치 표기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주방 싱크대 위쪽, 아일랜드 식탁 위쪽의 다운라이트 간격은 15cm로 하였고 벽에서는 25cm 또는 30cm를 띄웠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은방들의 다운라이트는 방문을 여는 쪽이 통로 또는 책상 위치라서 2개씩 샤시 창틀 기준까지 띄워 타공 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노밧 IoT 스마트 컨트롤러는 개당 600W까지 연결 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 집에서 조명 설계를 한다면 커튼박스에 라인조명은 안 할 것 같습니다. 생각보다 전력소모가 높은 대신 잘 안 쓰게 되네요. 우물천장 안쪽에 8개의 다운라이트를 넣었는데 이 조명도 실제 켠 적이 거의 없네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 조명 제품 목록&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조명 제품은 플리커프리, 절연컨버터 적용된 제품으로만 설치하였습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;LED 3&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;인치&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;다운라이트&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;EL 5131&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;움푹 확산형&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;8W&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;플리커프리&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;주백색&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;17&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;개&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;LED 3&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;인치&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;다운라이트&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;EL 5131&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;움푹 확산형&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;8W&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;플리커프리&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;전구색&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;11&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;개&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;LED&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;다운라이트&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;EL-9511 2&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;인치&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4W&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;매립조명 할로겐&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;COB&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;매입등&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;플리커프리&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;주백색&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;개&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;LED 3&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;인치&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;다운라이트&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;EL 5291&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;집중형&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;6W COB&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;매입등&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;플리커프리&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;전구색&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;8&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;개&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;텔트론&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;오토온미니&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;히든센서&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;현관 재실감지기&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;레이더투시&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;센서&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #222222; text-align: start;&quot;&gt;남영전구 LED T5 (광효율 105lm/w)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3. 벽 스위치 및 IoT 컨트롤러&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;노밧 광고는 아니지만 벽 스위치가 르그랑 제품군과 협업하여 출시되어 디자인이 예쁘고 품질이 좋아서 선택하였습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Zigbee 방식이 아니고 WIFI 방식이라 별도의 라우터 설치 없이 집에 설치되어 있는 WIFI를 활용할 수 있는 점도 마음에 들었습니다. 벽 스위치에는 전선을 연결하지 않고 기존 스위치 위치에 장착만 하면 자가발전으로 전기를 생산하여 IoT 컨트롤러와 통신하며 전원 On/Off 동작을 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;어플에 등록된 컨트롤러는 어플에서 다른 스위치 연동 가능합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;어플에 컨트롤러 연동 방법은 2가지, 스위치 연속 5번, 또는 컨트롤러버튼 직접 눌러 AP모드로 등록할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;762&quot; data-origin-height=&quot;445&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dH7ycT/btrGv4nxlsr/jhLeq3kILVmU22Rq368i11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dH7ycT/btrGv4nxlsr/jhLeq3kILVmU22Rq368i11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dH7ycT/btrGv4nxlsr/jhLeq3kILVmU22Rq368i11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdH7ycT%2FbtrGv4nxlsr%2FjhLeq3kILVmU22Rq368i11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;762&quot; height=&quot;445&quot; data-origin-width=&quot;762&quot; data-origin-height=&quot;445&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 컨트롤러 위치 기록&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨트롤러 퓨즈 교체 시, 기타 컨트롤러 유지보수 필요시 위치 파악을 위한 기록입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.1 현관&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;텔트론&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;오토온미니&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;히든센서&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;현관 재실감지기&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;레이더투시&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;센서가 장착되어 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;309&quot; data-origin-height=&quot;638&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PUFJe/btrGuoNUOpm/Lgn8cL5BCN44KLd7XqQkeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PUFJe/btrGuoNUOpm/Lgn8cL5BCN44KLd7XqQkeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PUFJe/btrGuoNUOpm/Lgn8cL5BCN44KLd7XqQkeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPUFJe%2FbtrGuoNUOpm%2FLgn8cL5BCN44KLd7XqQkeK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;309&quot; height=&quot;638&quot; data-origin-width=&quot;309&quot; data-origin-height=&quot;638&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.2 거실&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;월패드벽 좌측에 컨트롤러가 있고 소파 다운라이트 3개를 제어합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우측의 컨트롤러는 커튼박스에 T5 조명을 제어합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커튼박스의 전원선 중 흰색-빨강 배선은 전동커튼용 상시전원입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.webp&quot; data-origin-width=&quot;1110&quot; data-origin-height=&quot;624&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WVl0q/btsBGXGFkSi/CmvAaq8hIjbqpFzWLG7XKk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WVl0q/btsBGXGFkSi/CmvAaq8hIjbqpFzWLG7XKk/img.webp&quot; data-alt=&quot;소파벽&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WVl0q/btsBGXGFkSi/CmvAaq8hIjbqpFzWLG7XKk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWVl0q%2FbtsBGXGFkSi%2FCmvAaq8hIjbqpFzWLG7XKk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1110&quot; height=&quot;624&quot; data-filename=&quot;2.webp&quot; data-origin-width=&quot;1110&quot; data-origin-height=&quot;624&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;소파벽&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복도등 컨트롤러 1번은 우물박스 안쪽 주백색 다운라이트 8w x 8개를 제어합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복도등 컨트롤러 2번은 복도 다운라이트 6w x 4개를 제어합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.webp&quot; data-origin-width=&quot;942&quot; data-origin-height=&quot;765&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6GhJH/btsBQtxNtdR/WzdSMMy4Fk7YZE0own1NC1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6GhJH/btsBQtxNtdR/WzdSMMy4Fk7YZE0own1NC1/img.webp&quot; data-alt=&quot;복도 컨트롤러&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6GhJH/btsBQtxNtdR/WzdSMMy4Fk7YZE0own1NC1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6GhJH%2FbtsBQtxNtdR%2FWzdSMMy4Fk7YZE0own1NC1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;942&quot; height=&quot;765&quot; data-filename=&quot;3.webp&quot; data-origin-width=&quot;942&quot; data-origin-height=&quot;765&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;복도 컨트롤러&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;거실 아트월은 우측 다운라이트에 컨트롤러가 있으며 6W x 2 개입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.webp&quot; data-origin-width=&quot;507&quot; data-origin-height=&quot;322&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5xXcD/btsBJdvArJF/UreduJaf6weJsylFfqX0D0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5xXcD/btsBJdvArJF/UreduJaf6weJsylFfqX0D0/img.webp&quot; data-alt=&quot;아트월&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5xXcD/btsBJdvArJF/UreduJaf6weJsylFfqX0D0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5xXcD%2FbtsBJdvArJF%2FUreduJaf6weJsylFfqX0D0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;507&quot; height=&quot;322&quot; data-filename=&quot;4.webp&quot; data-origin-width=&quot;507&quot; data-origin-height=&quot;322&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;아트월&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;거실우물은 간접조명으로 T5가 설치되어 있습니다. 소비전력은 70w입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목공사장님께 틈을 더 넓게 해달라고 했으나 저렇게 해야 한다고 고집을 부려서 내버려 두었는데 결국 T5 넣을 때 잘 안 들어갔습니다. 나중에 우물천장 전체 다 뜯고 다시 하는 비용 200만 원 언더로 예상합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨트롤러는 사진의 동그라미입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.webp&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;476&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CQIiI/btsBF3AFKQC/SYssozIBDwCWn3iK0c5HL0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CQIiI/btsBF3AFKQC/SYssozIBDwCWn3iK0c5HL0/img.webp&quot; data-alt=&quot;우물 간접 조명&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CQIiI/btsBF3AFKQC/SYssozIBDwCWn3iK0c5HL0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCQIiI%2FbtsBF3AFKQC%2FSYssozIBDwCWn3iK0c5HL0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;670&quot; height=&quot;476&quot; data-filename=&quot;5.webp&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;476&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;우물 간접 조명&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.3 주방&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;식탁등 옆 환기구에 컨트롤러 2개가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨트롤러 1은 식탁등이고 컨트롤러 2는 아일랜드 위에 15cm 간격으로 설치된 2인치 주백색 4W x 3개입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6.webp&quot; data-origin-width=&quot;518&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2kZVM/btsBQl0Lism/HzZNHLYPgFQeF2r7gxcz81/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2kZVM/btsBQl0Lism/HzZNHLYPgFQeF2r7gxcz81/img.webp&quot; data-alt=&quot;주방 컨트롤러&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2kZVM/btsBQl0Lism/HzZNHLYPgFQeF2r7gxcz81/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2kZVM%2FbtsBQl0Lism%2FHzZNHLYPgFQeF2r7gxcz81%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;518&quot; height=&quot;700&quot; data-filename=&quot;6.webp&quot; data-origin-width=&quot;518&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;주방 컨트롤러&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;냉장고장 앞은 우측에 컨트롤러가 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;7.webp&quot; data-origin-width=&quot;974&quot; data-origin-height=&quot;649&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y0CE7/btsBGEUVB2j/KQYf1uzHSiwNgMLKkX24D0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y0CE7/btsBGEUVB2j/KQYf1uzHSiwNgMLKkX24D0/img.webp&quot; data-alt=&quot;냉장고장 컨트롤러&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y0CE7/btsBGEUVB2j/KQYf1uzHSiwNgMLKkX24D0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy0CE7%2FbtsBGEUVB2j%2FKQYf1uzHSiwNgMLKkX24D0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;974&quot; height=&quot;649&quot; data-filename=&quot;7.webp&quot; data-origin-width=&quot;974&quot; data-origin-height=&quot;649&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;냉장고장 컨트롤러&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싱크대는 아래 사진의 동그라미 안에 컨트롤러가 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;8.webp&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;454&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cPq8sq/btsBQTJHFY0/xrQsagmprd7XVCOw1bS8kK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cPq8sq/btsBQTJHFY0/xrQsagmprd7XVCOw1bS8kK/img.webp&quot; data-alt=&quot;씽크대 컨트롤러&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cPq8sq/btsBQTJHFY0/xrQsagmprd7XVCOw1bS8kK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcPq8sq%2FbtsBQTJHFY0%2FxrQsagmprd7XVCOw1bS8kK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;454&quot; data-filename=&quot;8.webp&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;454&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;씽크대 컨트롤러&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 조명 사진 모음&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 구역별 조명 사진입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.webp&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;590&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w1R8R/btsBQWzHBwf/m44tKsMkIoddfOs0cwDli0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w1R8R/btsBQWzHBwf/m44tKsMkIoddfOs0cwDli0/img.webp&quot; data-alt=&quot;주방&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w1R8R/btsBQWzHBwf/m44tKsMkIoddfOs0cwDli0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw1R8R%2FbtsBQWzHBwf%2Fm44tKsMkIoddfOs0cwDli0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;840&quot; height=&quot;590&quot; data-filename=&quot;9.webp&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;590&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;주방&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;10.webp&quot; data-origin-width=&quot;574&quot; data-origin-height=&quot;579&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dbZBsL/btsBHq2WYsN/bsPaUYVrT76OnTUcoF2yjK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dbZBsL/btsBHq2WYsN/bsPaUYVrT76OnTUcoF2yjK/img.webp&quot; data-alt=&quot;거실 우물&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dbZBsL/btsBHq2WYsN/bsPaUYVrT76OnTUcoF2yjK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdbZBsL%2FbtsBHq2WYsN%2FbsPaUYVrT76OnTUcoF2yjK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;574&quot; height=&quot;579&quot; data-filename=&quot;10.webp&quot; data-origin-width=&quot;574&quot; data-origin-height=&quot;579&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;거실 우물&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;11.webp&quot; data-origin-width=&quot;593&quot; data-origin-height=&quot;546&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NMgN5/btsBGGSJvaF/CRT6zHSvgaKqTzCRDKpIRk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NMgN5/btsBGGSJvaF/CRT6zHSvgaKqTzCRDKpIRk/img.webp&quot; data-alt=&quot;거실 소파&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NMgN5/btsBGGSJvaF/CRT6zHSvgaKqTzCRDKpIRk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNMgN5%2FbtsBGGSJvaF%2FCRT6zHSvgaKqTzCRDKpIRk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;593&quot; height=&quot;546&quot; data-filename=&quot;11.webp&quot; data-origin-width=&quot;593&quot; data-origin-height=&quot;546&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;거실 소파&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;12.webp&quot; data-origin-width=&quot;609&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RLbLZ/btsBFLG4oG1/vK0a5jOyo0Wk4sek4KrW3k/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RLbLZ/btsBFLG4oG1/vK0a5jOyo0Wk4sek4KrW3k/img.webp&quot; data-alt=&quot;거실 아트월&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RLbLZ/btsBFLG4oG1/vK0a5jOyo0Wk4sek4KrW3k/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRLbLZ%2FbtsBFLG4oG1%2FvK0a5jOyo0Wk4sek4KrW3k%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;609&quot; height=&quot;540&quot; data-filename=&quot;12.webp&quot; data-origin-width=&quot;609&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;거실 아트월&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;13.webp&quot; data-origin-width=&quot;505&quot; data-origin-height=&quot;617&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnZlRE/btsBRP1onMC/OdObRJaBGLqNtjKCMtCQ7k/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnZlRE/btsBRP1onMC/OdObRJaBGLqNtjKCMtCQ7k/img.webp&quot; data-alt=&quot;거실 우물 간접&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnZlRE/btsBRP1onMC/OdObRJaBGLqNtjKCMtCQ7k/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnZlRE%2FbtsBRP1onMC%2FOdObRJaBGLqNtjKCMtCQ7k%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;505&quot; height=&quot;617&quot; data-filename=&quot;13.webp&quot; data-origin-width=&quot;505&quot; data-origin-height=&quot;617&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;거실 우물 간접&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;14.webp&quot; data-origin-width=&quot;1338&quot; data-origin-height=&quot;741&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bf38iZ/btsBOipLiET/TIjyj4QbqPcQfBw1xSbRJ1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bf38iZ/btsBOipLiET/TIjyj4QbqPcQfBw1xSbRJ1/img.webp&quot; data-alt=&quot;공사 안내문 참고용&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bf38iZ/btsBOipLiET/TIjyj4QbqPcQfBw1xSbRJ1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbf38iZ%2FbtsBOipLiET%2FTIjyj4QbqPcQfBw1xSbRJ1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1338&quot; height=&quot;741&quot; data-filename=&quot;14.webp&quot; data-origin-width=&quot;1338&quot; data-origin-height=&quot;741&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;공사 안내문 참고용&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Personal</category>
      <category>IOT조명</category>
      <category>공사안내문</category>
      <category>스마트스위치</category>
      <category>스마트조명</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/20</guid>
      <comments>https://noyecube.tistory.com/entry/%EC%8A%A4%EB%A7%88%ED%8A%B8-%EC%A1%B0%EB%AA%85-%EC%8A%A4%EB%A7%88%ED%8A%B8%EC%8A%A4%EC%9C%84%EC%B9%98-IoT%EC%A1%B0%EB%AA%85#entry20comment</comments>
      <pubDate>Tue, 5 Jul 2022 14:02:44 +0900</pubDate>
    </item>
    <item>
      <title>Ubuntu Server 20.04.4 C/C++ 개발환경</title>
      <link>https://noyecube.tistory.com/entry/Ubuntu-20044-CC-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. putty 다운로드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- &lt;a href=&quot;https://puttytray.goeswhere.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://puttytray.goeswhere.com/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1647177854492&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;PuTTYTray&quot; data-og-description=&quot;Minimizing to the system tray (on CTRL + minimize, always or directly on startup) Icons are customisable Blinks tray icon when a bell signal is received Configurable window transparency URL hyperlinking Portability: optionally stores session configuration &quot; data-og-host=&quot;puttytray.goeswhere.com&quot; data-og-source-url=&quot;https://puttytray.goeswhere.com/&quot; data-og-url=&quot;https://puttytray.goeswhere.com/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://puttytray.goeswhere.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://puttytray.goeswhere.com/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;PuTTYTray&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Minimizing to the system tray (on CTRL + minimize, always or directly on startup) Icons are customisable Blinks tray icon when a bell signal is received Configurable window transparency URL hyperlinking Portability: optionally stores session configuration&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;puttytray.goeswhere.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 빌드 패키지 설치&lt;/h3&gt;
&lt;pre id=&quot;code_1647178234535&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt install gcc build-essential linux-headers-$(uname -r) git make cmake gnupg gperf zip curl exuberant-ctags graphviz cscope dos2unix bc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. vim profile 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.vimrc 수정&lt;/p&gt;
&lt;pre id=&quot;code_1647178433164&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;set tagbsearch
set cindent
set autoindent
set smartindent
set enc=euc-kr
set incsearch
syntax on
filetype on
set hlsearch
set showmatch
set bs=2
set tabstop=4
colo delek
set nu
set shiftwidth=4
set visualbell
set ignorecase&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/usr/share/vim/vim&amp;lt;version&amp;gt;/colors/delek.vim 수정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; Search의 ctermfg=NONE 을 ctermfg=Black 으로 변경 후 저장&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. Host Disk Mount&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(물리서버)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; Samba 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(VMware)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; VM-&amp;gt;Settings-&amp;gt;Options-&amp;gt;Shared Folders&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; 마운트위치 /mnt/hgfs/&amp;lt;Shard Folder&amp;gt; 를 /home/&amp;lt;계정명&amp;gt;/ 하위에 Symbolic Link 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; ln -s /mnt/hgfs/&amp;lt;Shared Folder&amp;gt; /home/&amp;lt;계정명&amp;gt;/&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; open-vm-tools로 재부팅 이후에도 공유 설정 유지하려면 systemd 등록 필요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;/etc/systemd/system/mnt-hgfs.mount 파일 생성 후 아래 내용 입력&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1647324737951&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[Unit]
Description=VMware mount for hgfs
DefaultDependencies=no
Before=umount.target
ConditionVirtualization=vmware
After=sys-fs-fuse-connections.mount

[Mount]
What=vmhgfs-fuse
Where=/mnt/hgfs
Type=fuse
Options=default_permissions,allow_other

[Install]
WantedBy=multi-user.target&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt;&amp;nbsp;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;/etc/modules-load.d/open-vm-tools.conf 파일 생성 후 아래 내용 입력(이미 존재할 경우 마지막 줄 추가&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1647324771520&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fuse&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; 시스템 서비스에 등록 : &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;sudo systemctl enable mnt-hgfs.mount&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; &quot;fuse&quot; 모듈 로드 : &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;sudo modprobe -v fuse&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;sudo systemctl start mnt-hgfs.mount 또는 재부팅&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(VirtualBox)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; 설정-&amp;gt;공유폴더&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; $home에 &amp;lt;폴더명A&amp;gt; 생성 후&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; sudo mount -t vboxsf &amp;lt;HOST공유폴더명&amp;gt; &amp;lt;폴더명A&amp;gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. SSH Port 변경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; /etc/ssh/sshd_config 내 &quot;#Port 22&quot;를 &quot;Port &amp;lt;변경포트&amp;gt;&quot;로 변경 저장 후 &quot;service ssh restart&quot; 명령&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. Time Zone 확인/변경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; timedatectl&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime&lt;/p&gt;</description>
      <category>Windows | Linux</category>
      <category>delek</category>
      <category>ubuntu설치</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/16</guid>
      <comments>https://noyecube.tistory.com/entry/Ubuntu-20044-CC-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD#entry16comment</comments>
      <pubDate>Sun, 13 Mar 2022 22:42:03 +0900</pubDate>
    </item>
    <item>
      <title>[xingAPI][차트 데이터 수집기 만들기](6) 주식마스터 조회</title>
      <link>https://noyecube.tistory.com/entry/xingAPI%EC%B0%A8%ED%8A%B8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%88%98%EC%A7%91%EA%B8%B0-%EB%A7%8C%EB%93%A4%EA%B8%B06-%EC%A3%BC%EC%8B%9D%EB%A7%88%EC%8A%A4%ED%84%B0-%EC%A1%B0%ED%9A%8C</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;증권사 API로 차트 데이터를 수집하려면 종목코드를 알아야 하는데 전 종목에 대한 종목코드는 주식마스터조회 API를 호출하여 얻을 수 있습니다. 이베스트투자증권의 xingAPI의 TR 에는 t8430(주식종목조회), t8436(주식종목조회 API용), t9945(주식마스터조회 API용)가 있는데 그중 t8430(주식종목조회)를 이용하여 전체 주식종목(주식마스터)을 조회해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 주식마스터 API 사용방법 알아보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 DevCenter를 통해 t8430의 속성을 확인합니다. 초당 2회 조회할 수 있고 Block Mode로 응답이 온다고 되어 있네요. Request는 t8430Inblock에 속성을 설정하여 전송하고 응답은 t8430OutBlock에 담겨서 옵니다. 1개 종목의 정보가 t8430OutBlock 1개의 메모리 공간에 담겨 오고, 종목이 3200개면 T8430OutBlock이 3200개만큼 할당되어 옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Block Mode이면 이 OutBlock이 메모리공간에 연속적으로 할당되어 하나의 Block으로 응답이 오니까 수신 패킷의 Data사이즈를 sizeof(t8430OutBlock)으로 나누면 몇 개의 종목정보를 수신했는지 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGhyyc/btsBQx80T2b/8JQirVeKisZkhRIPkhH8Jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGhyyc/btsBQx80T2b/8JQirVeKisZkhRIPkhH8Jk/img.png&quot; data-alt=&quot;DevCenter의 t8430 속성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGhyyc/btsBQx80T2b/8JQirVeKisZkhRIPkhH8Jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGhyyc%2FbtsBQx80T2b%2F8JQirVeKisZkhRIPkhH8Jk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1022&quot; height=&quot;520&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1022&quot; data-origin-height=&quot;520&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;DevCenter의 t8430 속성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 실제 API 호출에 필요한 헤더파일 t8430.h의 내용입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1188&quot; data-origin-height=&quot;763&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/x4aHF/btsBRTjqWoo/aslgjy89qOHLcDtXQOZhD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/x4aHF/btsBRTjqWoo/aslgjy89qOHLcDtXQOZhD0/img.png&quot; data-alt=&quot;t8430.h&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/x4aHF/btsBRTjqWoo/aslgjy89qOHLcDtXQOZhD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fx4aHF%2FbtsBRTjqWoo%2Faslgjy89qOHLcDtXQOZhD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1188&quot; height=&quot;763&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1188&quot; data-origin-height=&quot;763&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;t8430.h&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 글에서 xingAPI 응답메시지 수신을 담당하는 &lt;span style=&quot;color: #555555;&quot;&gt;CDlgXingMsg 스레드를 생성하고 MainLoop 역할을 할 CThreadLooper 스레드를 생성하였습니다. CThreadLooper에서 Request, CDlgXingMsg에서 Response를 담당하는 기본 구조는 완성되었지만 수신데이터의 원활한 처리를 위하여 CThreadDequeue라는 이름의 스레드를 추가 생성하겠습니다. CDlgXingMsg에서 수신패킷을 바로 처리하는 것이 아니라 CDlgXingMsg는 받은 패킷을 메모리 복사를 통해 다른 버퍼에 저장하고 Queue에 Push 한 후 CThreadDequeue에 패킷 수신을 알리는 이벤트를 발생시킵니다. 그럼 CThreadDequeue는 EVENT_WAIT 상태에서 깨어나 Queue의 데이터를 Pop 하여 처리하게 됩니다. 일종의 Producer-Consumer 구조가 되는 것이죠.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;그리고 2종의 Data Structure를 추가로 정의합니다. 각 TR의 Property와 Method를 관리할 클래스 CDataStore와 비동기 데이터 처리를 관리하는 클래스 CAsyncControl입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. CDataStore 자료 구조 정의&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;t8430.h&quot;헤더파일의 t8430OutBlock을 그대로 TST__DS_T8430_OUT_OBJ로 정의하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 주식 전체 종목이 3200개 정도 되는 것을 파악했으니 #define _COUNT_OF_TST_DS_T8430_OUT_OBJ (3300)으로 설정하고 TST_DS_STOCK_MASTER를 정의하였습니다. 추가로 Hash Map 구조체를 정의하였습니다. 3000개의 종목 중 3000번째 배열에 저장되어 있는 종목코드를 찾기 위해 3000번 비교하지 않도록 Map에 &amp;lt;종목코드, 배열인덱스&amp;gt;로 저장해 두고 종목코드만 넣으면 해당 종목의 인덱스를 바로 반환받을 수 있게 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;971&quot; data-origin-height=&quot;923&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2Jywh/btsBRRsn37l/Mky7btpg1hLRokzF12JGSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2Jywh/btsBRRsn37l/Mky7btpg1hLRokzF12JGSk/img.png&quot; data-alt=&quot;CDataStore 코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2Jywh/btsBRRsn37l/Mky7btpg1hLRokzF12JGSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2Jywh%2FbtsBRRsn37l%2FMky7btpg1hLRokzF12JGSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;971&quot; height=&quot;923&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;971&quot; data-origin-height=&quot;923&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CDataStore 코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. CAsyncControl 정의&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TR을 Request 함수를 호출하면 Request 함수는 Request ID를 반환하고 종료됩니다. 서버 응답은 일정시간 경과 후 해당하는 MSG_ID를 통해 Callback 함수의 파라미터로 넘어옵니다. Request와 Response가 sync가 아닌 async로 처리되고 있기 때문에 아래와 같이 관련 자료구조를 정의하였습니다. 위 CDataStore의 TST_DS_STOCK_MASTER의 TEN_DataState는 데이터가 요청 중인지, 저장완료상태인지, 비어있는지 등을 나타냅니다. 이 변수를 활용하면 굳이 비동기처리를 하지 않아도 Polling 방식으로 해당 메모리를 주기적으로 감시하며 데이터 수신여부를 알 수 있기도 합니다. Polling 방식과 Callback Interrupt 방식 중 어떤 방식으로 응답을 확인할 것인지는 해당 TR을 Request 하는 Caller의 선택입니다. Callback Function Pointer를 설정하지 않으면 Caller가 Polling을 하고 있으면 되고, 아니라면 Callback Function이 호출될 때까지 대기하고 있으면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;992&quot; data-origin-height=&quot;1375&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bG3OmO/btsBSaZvq1A/4CigpCEqAmhQKOTiikHlek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bG3OmO/btsBSaZvq1A/4CigpCEqAmhQKOTiikHlek/img.png&quot; data-alt=&quot;관련 자료 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bG3OmO/btsBSaZvq1A/4CigpCEqAmhQKOTiikHlek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbG3OmO%2FbtsBSaZvq1A%2F4CigpCEqAmhQKOTiikHlek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;992&quot; height=&quot;1375&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;992&quot; data-origin-height=&quot;1375&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;관련 자료 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TST_XING_MSG_ITEM은 MSG_ID(ex.MSG_ID_XM_RECEIVE_DATA)와 WPARAM, LPARAM을 담는 기본 메시지 구조체입니다.(afx_msg LRESULT CDlgXingMsg::OnMsgIdXmReceiveData(WPARAM wParam, LPARAM lParam))&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. t8430 조회 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;t8430 TR 조회를 예로 설명하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CTreadLooper의 MainState가 REQUEST_MASTER 면 t8430 TR 조회를 합니다. 이를 위해 int iRequest_T8430(DATASTORE::TEN_DS_MARKET_CAT enMkCat, ASYNCCONTROL::PTST_TR_CALLBACK pstCb)이라는 별도의 Helper Function을 작성하였는데 첫 번째 파라미터로 코스피, 코스닥 구분값을 넣고 두 번째 파라미터로 콜백구조체 포인터를 넘겨줍니다. TST_TR_CALLBACK의 pvCbFunc는 수신데이터를 처리할 함수포인터, pvPreCbFunc는 수신데이터처리 함수 호출 전 호출하는 함수포인터, pvPostCBFunc는 수신데이터 처리 함수 호출 후 호출하는 함수포인터입니다. pvPreCbFunc와 pvPostCbFunc를 통해 비동기 처리를 원활하게 할 수 있습니다. stParams의 Opaque 변수에는 Caller와 Callee(수신데이터 처리 함수) 간 약속한 필요한 값을 넘겨주면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;1091&quot; data-origin-height=&quot;917&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8klqD/btsBQsM95qR/N9p3hjrp901E6qxiK9PEfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8klqD/btsBQsM95qR/N9p3hjrp901E6qxiK9PEfK/img.png&quot; data-alt=&quot;조회 코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8klqD/btsBQsM95qR/N9p3hjrp901E6qxiK9PEfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8klqD%2FbtsBQsM95qR%2FN9p3hjrp901E6qxiK9PEfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1091&quot; height=&quot;917&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;1091&quot; data-origin-height=&quot;917&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;조회 코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;xingAPI Request() 호출이 성공하면 0 &amp;lt; RequestID &amp;lt; 256 의 값을 반환하는데 이 값을 인덱스로 하여 CAsyncControl의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TST_TR_REQUEST_STATUS[256];에 Request에 사용한 TST_TR_REQUEST_ITEM을 임시 저장하고 있다가 Callback에 넘겨주는 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;1040&quot; data-origin-height=&quot;1089&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eM30aY/btsBGUDZMYo/Zn4ddlI6LilVpPtB3Tg0o0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eM30aY/btsBGUDZMYo/Zn4ddlI6LilVpPtB3Tg0o0/img.png&quot; data-alt=&quot;t8430 API 호출 코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eM30aY/btsBGUDZMYo/Zn4ddlI6LilVpPtB3Tg0o0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeM30aY%2FbtsBGUDZMYo%2FZn4ddlI6LilVpPtB3Tg0o0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1040&quot; height=&quot;1089&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;1040&quot; data-origin-height=&quot;1089&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;t8430 API 호출 코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Request 호출이 정상적으로 처리되면 CDlgXingMsg의 OnMsgIdXmReceiveData에 수신데이터가 넘어옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이 수신데이터가 어떤 Request의 응답인지 알아야겠죠. LPARAM이 RECEVE_PACKET구조체 타입이기 때문에 RECEVE_PACKET::nRqID값을 통해 Request ID를 알 수 있고 이 Request ID에 해당하는 Request ITEM은 CAsyncControl의 TR_REQUEST_STATUS ARRAY에 저장해 뒀으므로 거기서 REQUEST ITEM을 꺼낸 뒤 수신한 WPARAM과 LPARAM을 REQUEST_ITEM::pXingMsg에 붙여서 Queue에 PUSH 하면 됩니다. Push 후에 SetEvent(m_hEvent_Wakeup_ThreadDequeue) 호출을 하면 Dequeue 스레드가 깨어나 Queue에 뭐가 들어왔나 확인 후 처리함수를 호출하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;921&quot; data-origin-height=&quot;756&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmFPBN/btsBQsfiCzZ/WYTzKCDZtlegXBL4Iy13a1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmFPBN/btsBQsfiCzZ/WYTzKCDZtlegXBL4Iy13a1/img.png&quot; data-alt=&quot;응답 수신부 코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmFPBN/btsBQsfiCzZ/WYTzKCDZtlegXBL4Iy13a1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmFPBN%2FbtsBQsfiCzZ%2FWYTzKCDZtlegXBL4Iy13a1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;921&quot; height=&quot;756&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;921&quot; data-origin-height=&quot;756&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;응답 수신부 코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ThreadDequeue에 이벤트가 발생하면 Queue를 뒤적여서 수신데이터를 꺼내고 어떤 MSG_ID를 가진 패킷인지 확인 후 ProcessMsgxxxx()를 호출합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;1122&quot; data-origin-height=&quot;797&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qdF6G/btsBUbX85hI/PkEKh7VN7Uejiiw6lkGnP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qdF6G/btsBUbX85hI/PkEKh7VN7Uejiiw6lkGnP1/img.png&quot; data-alt=&quot;데이터 큐 코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qdF6G/btsBUbX85hI/PkEKh7VN7Uejiiw6lkGnP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqdF6G%2FbtsBUbX85hI%2FPkEKh7VN7Uejiiw6lkGnP1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1122&quot; height=&quot;797&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;1122&quot; data-origin-height=&quot;797&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;데이터 큐 코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아까 CThreadLooper에서 iRequest_t8430() 호출할 때 Callback parameter로 vTRCb_T8430_UpdateMaster를 설정했죠. 그럼 pvPreCbFunc, vTRCb_T8430_UpdateMaster, pvPostCbFunc 순으로 호출됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;922&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmncyx/btsBTXy7kOk/E7yYwjeDkj3XDL4boXgOFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmncyx/btsBTXy7kOk/E7yYwjeDkj3XDL4boXgOFK/img.png&quot; data-alt=&quot;수신데이터 처리 콜백 호출&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmncyx/btsBTXy7kOk/E7yYwjeDkj3XDL4boXgOFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbmncyx%2FbtsBTXy7kOk%2FE7yYwjeDkj3XDL4boXgOFK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;916&quot; height=&quot;922&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;922&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;수신데이터 처리 콜백 호출&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vTRCb_T8430_UpdateMaster()에서는 t8430OutBlock의 수만큼 for() 구문을 돌면서 주식마스터 구조체에 데이터를 저장합니다. 주식마스터에 저장을 끝내면 Hash Map에도 &amp;lt;종목코드, 주식마스터테이블 인덱스&amp;gt;를 저장해 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1637163158253&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/*
 * @fn vTRCb_T8430_UpdateMaster
 * @ASYNCCONTROL::PTST_PARAMS_TR_CALLBACK::pOpaque1: PTST_DS_STOCK_MASTER
 * @ASYNCCONTROL::PTST_PARAMS_TR_CALLBACK::pOpaque2: PTST_DS_STOCK_MASTER_INDEX_MAP
*/
void vTRCb_T8430_UpdateMaster(ASYNCCONTROL::PTST_TR_REQUEST_ITEM pRq, ASYNCCONTROL::PTST_PARAMS_TR_CALLBACK pParams)
{
	PTST_DS_STOCK_MASTER pstMaster;
	PTST_DS_STOCK_MASTER_INDEX_MAP pstIndex;
	PTST__DS_T8430_OUT_OBJ p;
	LPRECV_PACKET pRcv;
	LPt8430OutBlock pOut;
	int iCount;	// number of outblocks received
	TCHAR tchBuf[16 * sizeof(TCHAR)];
	const int ciSLOTS = sizeof(TST_DS_STOCK_MASTER::astStockMaster) / sizeof(TST__DS_T8430_OUT_OBJ);	// number of slots(TST__DS_T8430_OUT_OBJ) in TST_DS_STOCK_MASTER

	DPRINTF(&quot;ENTER\n&quot;);

	if (NULL == pRq) { return; }

	if (NULL == (pRq-&amp;gt;pXingMsg)) { return; }
	
	if (NULL == pParams) { return; }

	if ((NULL == pParams-&amp;gt;pOpaque1) || (NULL == pParams-&amp;gt;pOpaque2)) { return; }

	pstMaster = (PTST_DS_STOCK_MASTER)(pParams-&amp;gt;pOpaque1);
	pstIndex = (PTST_DS_STOCK_MASTER_INDEX_MAP)(pParams-&amp;gt;pOpaque2);

	pRcv = (LPRECV_PACKET)(pRq-&amp;gt;pXingMsg-&amp;gt;lParam);
	if (_tcsncmp(pRcv-&amp;gt;szBlockName, NAME_t8430OutBlock, _countof(NAME_t8430OutBlock)) != 0) {
		return;
	}

	/* TODO: check datastate , if not empty, clear memory and map, ??
	*/
	/* Empty HashMap */
	AcquireSRWLockExclusive(&amp;amp;(pstIndex-&amp;gt;srwlock));
	pstIndex-&amp;gt;mapIndexSH.RemoveAll();
	pstIndex-&amp;gt;mapIndexEXP.RemoveAll();
	ReleaseSRWLockExclusive(&amp;amp;(pstIndex-&amp;gt;srwlock));

	/* Init Memory */
	AcquireSRWLockExclusive(&amp;amp;(pstMaster-&amp;gt;srwlock));
	pstMaster-&amp;gt;enState = TEN_DataState::enState_SAVING;
	GetLocalTime(&amp;amp;(pstMaster-&amp;gt;stTimeLastUpdate));
	pstMaster-&amp;gt;wCountTotal = 0;
	pstMaster-&amp;gt;wCountKOSPI = 0;
	pstMaster-&amp;gt;wCountKOSDAQ = 0;
	p = pstMaster-&amp;gt;astStockMaster;
	for (int i = 0; i &amp;lt; ciSLOTS; i++) {
		AcquireSRWLockExclusive(&amp;amp;(p[i].srwlock));
		ZeroMemory(p[i].tchHname, sizeof(TST__DS_T8430_OUT_OBJ::tchHname));
		ZeroMemory(p[i].tchShcode, sizeof(TST__DS_T8430_OUT_OBJ::tchShcode));
		ZeroMemory(p[i].tchExpcode, sizeof(TST__DS_T8430_OUT_OBJ::tchExpcode));
		p[i].enStockCat = TEN_DS_STOCK_CAT::enST_UNKNOWN;
		p[i].lUplmtprice = 0;
		p[i].lDnlmtprice = 0;
		p[i].lJnilclose = 0;
		p[i].lMemedan = 0;
		p[i].lRecprice = 0;
		p[i].enMarketCat = TEN_DS_MARKET_CAT::enMK_UNKNOWN;
		ReleaseSRWLockExclusive(&amp;amp;(p[i].srwlock));
	}
	ReleaseSRWLockExclusive(&amp;amp;(pstMaster-&amp;gt;srwlock));

	iCount = pRcv-&amp;gt;nDataLength / sizeof(t8430OutBlock);
	pOut = (LPt8430OutBlock)pRcv-&amp;gt;lpData;

	AcquireSRWLockExclusive(&amp;amp;(pstMaster-&amp;gt;srwlock));
	for (int i = 0; i &amp;lt; iCount; i++) {
		if (i &amp;gt;= ciSLOTS) {
			DPRINTF(&quot;WARN] Out of memory space. Mem:[%d], Data:[%d]\n&quot;, ciSLOTS, iCount);
			break;
		}
		AcquireSRWLockExclusive(&amp;amp;(p[i].srwlock));
		_tcsncpy_s(p[i].tchHname, _countof(TST__DS_T8430_OUT_OBJ::tchHname), pOut[i].hname, _countof(t8430OutBlock::hname));	/* string 20 */
		_tcsncpy_s(p[i].tchShcode, _countof(TST__DS_T8430_OUT_OBJ::tchShcode), pOut[i].shcode, _countof(t8430OutBlock::shcode));	/* string 6 */
		_tcsncpy_s(p[i].tchExpcode, _countof(TST__DS_T8430_OUT_OBJ::tchExpcode), pOut[i].expcode, _countof(t8430OutBlock::expcode));	/* string 12 */
		tchrTrim_s(p[i].tchHname, sizeof(TST__DS_T8430_OUT_OBJ::tchHname));
		tchrTrim_s(p[i].tchShcode, sizeof(TST__DS_T8430_OUT_OBJ::tchShcode));
		tchrTrim_s(p[i].tchExpcode, sizeof(TST__DS_T8430_OUT_OBJ::tchExpcode));

		if ('0' == pOut[i].etfgubun[0])	/* string 1 */
			p[i].enStockCat = TEN_DS_STOCK_CAT::enST_NORMAL;
		else if ('1' == pOut[i].etfgubun[0])
			p[i].enStockCat = TEN_DS_STOCK_CAT::enST_ETF;
		else if ('2' == pOut[i].etfgubun[0])
			p[i].enStockCat = TEN_DS_STOCK_CAT::enST_ETN;
		else {
			; /* should never reach here */
		}

		if ('1' == pOut[i].gubun[0])	/* string 1 */
			p[i].enMarketCat = TEN_DS_MARKET_CAT::enMK_KOSPI;
		else if ('2' == pOut[i].gubun[0])
			p[i].enMarketCat = TEN_DS_MARKET_CAT::enMK_KOSDAQ;
		else {
			;	/* should never reach here */
		}

		ZeroMemory(tchBuf, sizeof(tchBuf));	/* long 8 */
		_tcsncpy_s(tchBuf, _countof(tchBuf), pOut[i].uplmtprice, _countof(t8430OutBlock::uplmtprice));
		p[i].lUplmtprice = _ttoi(tchBuf);

		ZeroMemory(tchBuf, sizeof(tchBuf));	/* long 8 */
		_tcsncpy_s(tchBuf, _countof(tchBuf), pOut[i].dnlmtprice, _countof(t8430OutBlock::dnlmtprice));
		p[i].lDnlmtprice = _ttoi(tchBuf);

		ZeroMemory(tchBuf, sizeof(tchBuf));	/* long 8 */
		_tcsncpy_s(tchBuf, _countof(tchBuf), pOut[i].jnilclose, _countof(t8430OutBlock::jnilclose));
		p[i].lJnilclose = _ttoi(tchBuf);

		ZeroMemory(tchBuf, sizeof(tchBuf));	/* long 8 */
		_tcsncpy_s(tchBuf, _countof(tchBuf), pOut[i].recprice, _countof(t8430OutBlock::recprice));
		p[i].lRecprice = _ttoi(tchBuf);

		ZeroMemory(tchBuf, sizeof(tchBuf));	/* string 5 */
		_tcsncpy_s(tchBuf, _countof(tchBuf), pOut[i].memedan, _countof(t8430OutBlock::memedan));
		tchrTrim_s(tchBuf, sizeof(tchBuf));
		p[i].lMemedan = _ttoi(tchBuf);

		ReleaseSRWLockExclusive(&amp;amp;(p[i].srwlock));

		if (TEN_DS_MARKET_CAT::enMK_KOSPI == p[i].enMarketCat) {
			pstMaster-&amp;gt;wCountKOSPI++;
		}
		else if (TEN_DS_MARKET_CAT::enMK_KOSDAQ == p[i].enMarketCat) {
			pstMaster-&amp;gt;wCountKOSDAQ++;
		}

		pstMaster-&amp;gt;wCountTotal++;
	}

	pstMaster-&amp;gt;enState = TEN_DataState::enState_VALID;
	ReleaseSRWLockExclusive(&amp;amp;(pstMaster-&amp;gt;srwlock));

	AcquireSRWLockExclusive(&amp;amp;(pstIndex-&amp;gt;srwlock));
	AcquireSRWLockExclusive(&amp;amp;(pstMaster-&amp;gt;srwlock));
	for (int i = 0; i &amp;lt; pstMaster-&amp;gt;wCountTotal; i++) {
		pstIndex-&amp;gt;mapIndexSH.SetAt(p[i].tchShcode, i);
		pstIndex-&amp;gt;mapIndexEXP.SetAt(p[i].tchExpcode, i);
	}
	ReleaseSRWLockExclusive(&amp;amp;(pstMaster-&amp;gt;srwlock));
	ReleaseSRWLockExclusive(&amp;amp;(pstIndex-&amp;gt;srwlock));

	DPRINTF(&quot;LEAVE\n&quot;);
	return;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수신 패킷 처리가 끝나면 동적으로 할당한 메모리를 아래 함수 호출을 통해 메모리 해제 해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;842&quot; data-origin-height=&quot;751&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZHg1q/btsBQXl1vDq/H6hisYd3Z3tFqhKvV0ZFek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZHg1q/btsBQXl1vDq/H6hisYd3Z3tFqhKvV0ZFek/img.png&quot; data-alt=&quot;메모리 해제 코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZHg1q/btsBQXl1vDq/H6hisYd3Z3tFqhKvV0ZFek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZHg1q%2FbtsBQXl1vDq%2FH6hisYd3Z3tFqhKvV0ZFek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;842&quot; height=&quot;751&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;842&quot; data-origin-height=&quot;751&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;메모리 해제 코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리 데이터를 파일로 저장하는 덤프함수를 아래와 같이 작성하여, t8430 수신 완료 후 호출해 보았습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;1075&quot; data-origin-height=&quot;1452&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u0j1R/btsBJcjO85W/KnLOKnDo4apqkeMTgKyMD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u0j1R/btsBJcjO85W/KnLOKnDo4apqkeMTgKyMD0/img.png&quot; data-alt=&quot;수신 데이터 덤프 코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u0j1R/btsBJcjO85W/KnLOKnDo4apqkeMTgKyMD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu0j1R%2FbtsBJcjO85W%2FKnLOKnDo4apqkeMTgKyMD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1075&quot; height=&quot;1452&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;1075&quot; data-origin-height=&quot;1452&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;수신 데이터 덤프 코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;1421&quot; data-origin-height=&quot;941&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vrrPs/btsBQwCfkOY/WRE9DK4IRmYuS30bMzknyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vrrPs/btsBQwCfkOY/WRE9DK4IRmYuS30bMzknyK/img.png&quot; data-alt=&quot;실제 실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vrrPs/btsBQwCfkOY/WRE9DK4IRmYuS30bMzknyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvrrPs%2FbtsBQwCfkOY%2FWRE9DK4IRmYuS30bMzknyK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1421&quot; height=&quot;941&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;1421&quot; data-origin-height=&quot;941&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실제 실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인 완료 후 &quot;수집 시작&quot;을 누르니 디스크에 덤프파일이 생겼네요. t8430 주식마스터조회 성공입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>트레이딩/시스템트레이딩</category>
      <category>ebestsec</category>
      <category>OpenAPI</category>
      <category>t8411</category>
      <category>t8412</category>
      <category>t8430</category>
      <category>t8436</category>
      <category>t9945</category>
      <category>XINGAPI</category>
      <category>이베스트투자증권</category>
      <category>자동매매</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/14</guid>
      <comments>https://noyecube.tistory.com/entry/xingAPI%EC%B0%A8%ED%8A%B8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%88%98%EC%A7%91%EA%B8%B0-%EB%A7%8C%EB%93%A4%EA%B8%B06-%EC%A3%BC%EC%8B%9D%EB%A7%88%EC%8A%A4%ED%84%B0-%EC%A1%B0%ED%9A%8C#entry14comment</comments>
      <pubDate>Thu, 18 Nov 2021 00:45:01 +0900</pubDate>
    </item>
    <item>
      <title>[xingAPI][차트 데이터 수집기 만들기](5) 작업 스레드 생성</title>
      <link>https://noyecube.tistory.com/entry/xingAPI%EC%B0%A8%ED%8A%B8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%88%98%EC%A7%91%EA%B8%B0-%EB%A7%8C%EB%93%A4%EA%B8%B05-%EC%9E%91%EC%97%85%EC%8A%A4%EB%A0%88%EB%93%9C%EC%83%9D%EC%84%B1</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전 글에서 차트 데이터 수집기 개발을 위한 다이얼로그 화면 구성, 설정값 내부 변수 저장, 로그인 처리를 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는&amp;nbsp;수집상태 화면을 완성하기 전에 내부에 작업 스레드를 생성하고 주식마스터 테이블을 생성 후 주식종목조회 TR을 이용하여 전체 주식종목을 메모리에 로딩해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 작업 스레드 생성하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메인 다이얼로그에서 수집작업을 수행하면 코드가 실행되는 동안 GUI가 Freezing 상태가 되기 때문에 별도의 Worker Thread를 생성하도록 하겠습니다. MFC에서는 UI Thread와 Worker Thread를 생성할 수 있습니다. MFC에서의 스레드에 대한 정보는 아래 포스팅을 참고하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blog.noyecube.com/entry/MFC-MFC-%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%98%88%EC%A0%9C&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2023.12.10 - [프로그래밍/C | C++] - [MFC] MFC 스레드 예제&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1702380153931&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[MFC] MFC 스레드 예제&quot; data-og-description=&quot;이번 포스팅에서는 윈도 MFC 환경에서 사용가능한 두 가지 타입의 스레드에 대해 알아보고 생성/동기화/종료 방법을 예제를 통해 알아보겠습니다. 리눅스 환경에서 thread를 사용해 보신 분들은 pt&quot; data-og-host=&quot;blog.noyecube.com&quot; data-og-source-url=&quot;https://blog.noyecube.com/entry/MFC-MFC-%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%98%88%EC%A0%9C&quot; data-og-url=&quot;https://blog.noyecube.com/entry/MFC-MFC-%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%98%88%EC%A0%9C&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/RNOHt/hyUL4Mtk4B/4BKpRjTmKrr9YtGOm5WkzK/img.jpg?width=800&amp;amp;height=416&amp;amp;face=0_0_800_416,https://scrap.kakaocdn.net/dn/bJk2ry/hyUL2A8E6S/WZ513krwqaXFL7LQwaVxsk/img.jpg?width=800&amp;amp;height=416&amp;amp;face=0_0_800_416&quot;&gt;&lt;a href=&quot;https://blog.noyecube.com/entry/MFC-MFC-%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%98%88%EC%A0%9C&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://blog.noyecube.com/entry/MFC-MFC-%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%98%88%EC%A0%9C&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/RNOHt/hyUL4Mtk4B/4BKpRjTmKrr9YtGOm5WkzK/img.jpg?width=800&amp;amp;height=416&amp;amp;face=0_0_800_416,https://scrap.kakaocdn.net/dn/bJk2ry/hyUL2A8E6S/WZ513krwqaXFL7LQwaVxsk/img.jpg?width=800&amp;amp;height=416&amp;amp;face=0_0_800_416');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[MFC] MFC 스레드 예제&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 윈도 MFC 환경에서 사용가능한 두 가지 타입의 스레드에 대해 알아보고 생성/동기화/종료 방법을 예제를 통해 알아보겠습니다. 리눅스 환경에서 thread를 사용해 보신 분들은 pt&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;blog.noyecube.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스명을 CThreadLooper라고 이름 짓고 아래와 같이 기본 뼈대 코드를 작성 후 App의 Init에서 Thread를 생성해 주면 UINT ThreadLooper()가 메인 App의 스레드에서 분리되어 별도 Thread로 실행됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Thread 생성 파라미터로 theApp 의 포인터를 넘겨주고, 작업 동기화를 위한 이벤트 핸들을 하나 생성하여 초기화를 해주면 됩니다. 이 이벤트 핸들의 조작을 통하여 ThreadLooper()를 실행/정지시킬 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 그림과 같이 기본 코드 작성 후 AfxBeginThread로 실제 스레드를 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스레드가 생성되면 Loop 진입 후 WaitForSingleObject()에서 SUSPENDED 상태로 대기하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;421&quot; data-origin-height=&quot;263&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWRQvC/btsBHpqe6Uq/smOJZ8k3AiDzG2LfqGkn10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWRQvC/btsBHpqe6Uq/smOJZ8k3AiDzG2LfqGkn10/img.png&quot; data-alt=&quot;스레드 클래스 헤더&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWRQvC/btsBHpqe6Uq/smOJZ8k3AiDzG2LfqGkn10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWRQvC%2FbtsBHpqe6Uq%2FsmOJZ8k3AiDzG2LfqGkn10%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;421&quot; height=&quot;263&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;421&quot; data-origin-height=&quot;263&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;스레드 클래스 헤더&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;970&quot; data-origin-height=&quot;476&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mZcnK/btsBQT4Nv9q/11gmQWwsJToeJ3HyM4ldxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mZcnK/btsBQT4Nv9q/11gmQWwsJToeJ3HyM4ldxk/img.png&quot; data-alt=&quot;스레드 클래스 구현부&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mZcnK/btsBQT4Nv9q/11gmQWwsJToeJ3HyM4ldxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmZcnK%2FbtsBQT4Nv9q%2F11gmQWwsJToeJ3HyM4ldxk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;970&quot; height=&quot;476&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;970&quot; data-origin-height=&quot;476&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;스레드 클래스 구현부&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스레드 생성 코드는 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1634204234418&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// datacollector.h 에 아래 구문 추가
#include &quot;CThreadLooper.h&quot;

CThreadLooper m_ThreadLooper;
CWinThread* m_pThreadLooper;	// 생성자에서 m_pThreadLooper = NULL 로 초기화해주세요.&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1634204736025&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// datacollector.cpp 의 InitDialog()에 아래 구문 추가

m_pThreadLooper = AfxBeginThread(m_ThreadLooper.ThreadLooper, (void*)&amp;amp;theApp);
if (NULL == m_pThreadLooper) {
	AfxMessageBox(_T(&quot;AfxBeginThread ThreadLooper Error.\nQuit Program.&quot;));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 생성한 작업스레드 내부에서 수집작업을 어떤 단계로 진행할 것인지에 대하여 구조를 잡을 차례입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큰 맥락에서 각 상태를 정의하고 그에 맞는 뼈대코드를 작성하여 틀을 잡습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업 스레드가 생성되면 이벤트 대기 상태에 머무르게 됩니다. 이 때 데이터 수집 명령을 받으면 관련 메모리 초기화 후 주식마스터테이블을 받습니다. 그다음에 데이터 수집 단계를 시작하여 주식마스터테이블에서 수집 대상 종목을 가져와서 수집시작일-수집종료일에 해당하는 데이터 수집 작업을 수행합니다. 이때 사용자가 일시정지/작업중단을 명령하거나 수집 완료가 되면 다시 이벤트 대기상태로 빠집니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;1326&quot; data-origin-height=&quot;659&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0RmJH/btsBQpbOTtR/gGezDcFJdb0tDGK1W5v3o0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0RmJH/btsBQpbOTtR/gGezDcFJdb0tDGK1W5v3o0/img.png&quot; data-alt=&quot;state diagram&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0RmJH/btsBQpbOTtR/gGezDcFJdb0tDGK1W5v3o0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0RmJH%2FbtsBQpbOTtR%2FgGezDcFJdb0tDGK1W5v3o0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1326&quot; height=&quot;659&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;1326&quot; data-origin-height=&quot;659&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;state diagram&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 상태도를 반영하여 추가 코드를 작성하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;헤더 파일에 메인스테이트, 서브스테이트를 정의하고 STATE 관리를 위한 TST_STATE 구조체 정의, 그리고 Thread 상태를 관리할 Thread Control Block 구조체를 정의하였습니다. 큰 틀에서 생각나는 대로 적어둔 것이고 코드 작성중에 얼마든지 변경될 수 있는 상태입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;534&quot; data-origin-height=&quot;740&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/45XDO/btsBOhZEnKo/V5VVxYQyCPI2ssKLD5PGKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/45XDO/btsBOhZEnKo/V5VVxYQyCPI2ssKLD5PGKK/img.png&quot; data-alt=&quot;스테이트 정의&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/45XDO/btsBOhZEnKo/V5VVxYQyCPI2ssKLD5PGKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F45XDO%2FbtsBOhZEnKo%2FV5VVxYQyCPI2ssKLD5PGKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;534&quot; height=&quot;740&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;534&quot; data-origin-height=&quot;740&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;스테이트 정의&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lRetSleepTime 변수는 thread sleep 타임을 결정하거나 다음 state로의 전이를 결정할 때 사용할 변수입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;증권사 서버로의 데이터 요청/수신이 비동기적으로 이루어지고 각 TR마다 초당/10분당 요청할 수 있는 횟수 제한이 있기 때문에 Request이후에 어느정도 대기가 필요한 경우에는 이 변수값을 조정하며 Thread의 Sleep Time을 결정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 상태는 아무 이벤트가 없는 상태, EVENT_WAIT상태이기 때문에 이벤트 핸들을 초기화해주고, STATE변수를 EVENT_WAIT으로 설정해 줍니다. 그다음에 Thread Loop가 시작되면 WaitForSingleObject에서 이벤트를 대기하는 상태로 스레드 대기 상태가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EVENT_WAIT상태에서는 이전 작업 계속 또는 첫 작업 시작에 의한 INIT 상태로의 전이가 가능하기 때문에 EVENT_WAIT에서 다음 상태로 넘어갈 때는 Old State값을 체크하여 INIT으로 갈지, 이전 상태로 돌아갈지를 결정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;INIT 상태에서는 메모리초기화와 주식마스터테이블 수신을 하기로 하였는데 이를 위하여 SUB State를 정의해 두었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;QUIT은 데이터 수집작업이 완료되면 넘어오는 일시적인 상태인데 여기서 STATE를 Thread가 처음 생성될 때 기준으로 초기화를 해주고 이벤트 역시 Reset 해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;776&quot; data-origin-height=&quot;1058&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/24Lky/btsBUMw2dJu/YFu5JclzB3kzlKwQAbxCVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/24Lky/btsBUMw2dJu/YFu5JclzB3kzlKwQAbxCVK/img.png&quot; data-alt=&quot;스테이트 머신 구현부&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/24Lky/btsBUMw2dJu/YFu5JclzB3kzlKwQAbxCVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F24Lky%2FbtsBUMw2dJu%2FYFu5JclzB3kzlKwQAbxCVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;776&quot; height=&quot;1058&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;776&quot; data-origin-height=&quot;1058&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;스테이트 머신 구현부&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상으로 차트데이터 수집을 위하여 별도의 작업 스레드를 생성하고 스테이트머신 방식으로 코드를 작성하는 예제를 알아보았습니다.&lt;/p&gt;</description>
      <category>트레이딩/시스템트레이딩</category>
      <category>AfxBeginThread</category>
      <category>ebestsec</category>
      <category>OpenAPI</category>
      <category>t8430</category>
      <category>t8436</category>
      <category>t9945</category>
      <category>XINGAPI</category>
      <category>이베스트투자증권</category>
      <category>주식마스터</category>
      <category>주식종목조회</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/13</guid>
      <comments>https://noyecube.tistory.com/entry/xingAPI%EC%B0%A8%ED%8A%B8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%88%98%EC%A7%91%EA%B8%B0-%EB%A7%8C%EB%93%A4%EA%B8%B05-%EC%9E%91%EC%97%85%EC%8A%A4%EB%A0%88%EB%93%9C%EC%83%9D%EC%84%B1#entry13comment</comments>
      <pubDate>Thu, 14 Oct 2021 17:55:12 +0900</pubDate>
    </item>
    <item>
      <title>[xingAPI][차트 데이터 수집기 만들기](4) 데이터 수집 설정 기능</title>
      <link>https://noyecube.tistory.com/entry/xingAPI%EC%B0%A8%ED%8A%B8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%88%98%EC%A7%91%EA%B8%B0-%EB%A7%8C%EB%93%A4%EA%B8%B04-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%88%98%EC%A7%91-%EC%84%A4%EC%A0%95-%EA%B8%B0%EB%8A%A5</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 차트데이터 수집에 필요한 옵션 설정 기능을 만들어 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 수집 옵션 설정화면&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주식마스터 조회 API 호출 시 코스피, 코스닥을 선택하여 조회할 수 있게 되어 있어서 전종목, 코스피, 코스닥, 사용자지정 개별 종목코드를 선택할 수 있도록 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 종류는 틱 데이터, 1분봉 데이터, 일봉 데이터를 설정할 수 있도록 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 수집 기간과 저장 경로를 설정할 수 있도록 하였습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;833&quot; data-origin-height=&quot;505&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKfhzR/btsBJhFrQI9/o24K4gCdfhVlgIBCc3CPiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKfhzR/btsBJhFrQI9/o24K4gCdfhVlgIBCc3CPiK/img.png&quot; data-alt=&quot;수집 옵션 설정 화면 부분&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKfhzR/btsBJhFrQI9/o24K4gCdfhVlgIBCc3CPiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKfhzR%2FbtsBJhFrQI9%2Fo24K4gCdfhVlgIBCc3CPiK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;642&quot; height=&quot;389&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;833&quot; data-origin-height=&quot;505&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;수집 옵션 설정 화면 부분&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 화면 컨트롤 속성 설정 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수집 종목 선택에 라디오 버튼을 사용했는데 라디오 버튼을 그룹으로 묶으려면 탭 순서를 연속된 번호로 지정해 주어야 합니다. 리소스 뷰어에서 메인 대화창을 열고 ctrl+D 버튼을 누르면 아래와 같이 탭 순서가 표기됩니다. 아래 그림에서는 연속된 번호로 지정된 것을 확인할 수 있습니다. 연속 번호로 지정되어 있지 않으면 마우스를 순서대로 클릭하여 다시 연속된 순번의 탭 번호를 가질 수 있도록 해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1262&quot; data-origin-height=&quot;544&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCcWEB/btsBR79xrP9/KaPUvGVYWLU0YuZ6fzN4J1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCcWEB/btsBR79xrP9/KaPUvGVYWLU0YuZ6fzN4J1/img.png&quot; data-alt=&quot;탭스탑 순서 설정 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCcWEB/btsBR79xrP9/KaPUvGVYWLU0YuZ6fzN4J1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCcWEB%2FbtsBR79xrP9%2FKaPUvGVYWLU0YuZ6fzN4J1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1262&quot; height=&quot;544&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1262&quot; data-origin-height=&quot;544&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;탭스탑 순서 설정 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;컨트롤의 리소스 ID, 속성, 변수는 아래 표를 참고하여 설정합니다.&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 200px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;Label&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;Control&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;ID&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;Property&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;Variable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;전체&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;RadioButton&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;IDC_CFG_RADIO_ALL&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;Group::True&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;CButton::m_ctrRadioAll, int m_iRadioSelect&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;코스피&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;RadioButton&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;IDC_CFG_RADIO_KOSPI&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;CButton::m_ctrRadioKospi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;코스닥&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;RadioButton&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;IDC_CFG_RADIO_KOSDAQ&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;CButton::m_ctrRadioKosdaq&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&lt;span style=&quot;background-color: #efefef;&quot;&gt;사용자지정 종목코드&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;RadioButton&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;IDC_CFG_RADIO_USER&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;CButton::m_ctrRadioUser&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;사용자지정 종목코드&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;EditBox&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;IDC_CFG_EDIT_USER&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;CEdit::m_ctrEditUserInput&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;틱&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;CheckBox&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;IDC_CFG_CHECK_TICK&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;CButton::m_ctrChkTick&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;분봉&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;CheckBox&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;IDC_CFG_CHECK_MIN&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;CButton::m_ctrChkMin1&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;일봉&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;CheckBox&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;IDC_CFG_CHECK_DAY&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;CButton::m_ctrChkDay&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;수집기간시작&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;Date-Time&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;IDC_DATETIMEPICKER_START&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;CDateTimeCtrl::m_ctrDateStart&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;수집기간종료&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;Date-Time&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;IDC_DATETIMEPICKER_END&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 20px;&quot;&gt;CDateTimeCtrl::m_ctrDateEnd&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;수집경로&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;EditBox&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;IDC_CFG_EDIT_DIR&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;CEdit::m_ctrEditDir&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;경로변경&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;Button&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;IDC_CFG_BTN_CHANGE&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;CButton::m_ctrBtnDirChange&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;폴더열기&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;Button&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;IDC_CFG_BTN_OPEN&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;CButton::m_ctrBtnOpenFolder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;수집시작&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;Button&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;IDC_BTN_START&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;CButton::m_ctrBtnStart&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;일시정지&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;Button&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;IDC_BTN_PAUSE&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;CButton::m_ctrBtnPause&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;수집종료&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;Button&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;IDC_BTN_QUIT&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;CButton::m_ctrBtnQuit&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정 내용 관리를 위한 구조체를 아래와 같이 정의하였습니다. &quot;수집시작&quot; 버튼을 누를 때 설정창 컨트롤의 내용을 아래 구조체에 저장합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;602&quot; data-origin-height=&quot;418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/udVm5/btsBJa0CMNA/kZP4kJUJ8b6MC4jgC9zSHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/udVm5/btsBJa0CMNA/kZP4kJUJ8b6MC4jgC9zSHK/img.png&quot; data-alt=&quot;TST_CONFIG_INFO 구조체&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/udVm5/btsBJa0CMNA/kZP4kJUJ8b6MC4jgC9zSHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FudVm5%2FbtsBJa0CMNA%2FkZP4kJUJ8b6MC4jgC9zSHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;418&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;602&quot; data-origin-height=&quot;418&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;TST_CONFIG_INFO 구조체&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 구조체 변수에 설정값을 저장합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1082&quot; data-origin-height=&quot;758&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dkdU5y/btsBRSEKVGA/J1Uy5VX3XcIwnWyHHHOVdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dkdU5y/btsBRSEKVGA/J1Uy5VX3XcIwnWyHHHOVdk/img.png&quot; data-alt=&quot;구조체에 설정값 저장 코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dkdU5y/btsBRSEKVGA/J1Uy5VX3XcIwnWyHHHOVdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdkdU5y%2FbtsBRSEKVGA%2FJ1Uy5VX3XcIwnWyHHHOVdk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1082&quot; height=&quot;758&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1082&quot; data-origin-height=&quot;758&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;구조체에 설정값 저장 코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 화면처럼 설정 후 로그인 시 사용자가 선택한 옵션이 구조체 변수에 잘 저장된 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;833&quot; data-origin-height=&quot;263&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brZxCk/btsBQYFg87J/CksDpuTRSn0B4MVFespuKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brZxCk/btsBQYFg87J/CksDpuTRSn0B4MVFespuKk/img.png&quot; data-alt=&quot;메모리 값 출력 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brZxCk/btsBQYFg87J/CksDpuTRSn0B4MVFespuKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrZxCk%2FbtsBQYFg87J%2FCksDpuTRSn0B4MVFespuKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;833&quot; height=&quot;263&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;833&quot; data-origin-height=&quot;263&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;메모리 값 출력 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수집한 차트 데이터의 기본 저장 경로는 프로그램 실행경로 하위에 &quot;repo&quot; 폴더로 하겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;827&quot; data-origin-height=&quot;780&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpX0iC/btsBUB3l1kG/DG4A1LZWvg4lkVhrrSimK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpX0iC/btsBUB3l1kG/DG4A1LZWvg4lkVhrrSimK0/img.png&quot; data-alt=&quot;저장경로 코드와 실제 생성된 폴더&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpX0iC/btsBUB3l1kG/DG4A1LZWvg4lkVhrrSimK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpX0iC%2FbtsBUB3l1kG%2FDG4A1LZWvg4lkVhrrSimK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;827&quot; height=&quot;780&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;827&quot; data-origin-height=&quot;780&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;저장경로 코드와 실제 생성된 폴더&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;경로변경&quot;, &quot;폴더열기&quot; 버튼의 이벤트 핸들러에 CFolderPickerDialog와 ShellExecute를 사용하여 코드를 입력하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;375&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dxLtP3/btsBTYxYdE0/AXgHAYSPqybN0hDmqByiKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dxLtP3/btsBTYxYdE0/AXgHAYSPqybN0hDmqByiKK/img.png&quot; data-alt=&quot;코드에서 윈도우 탐색기를 실행하는 방법&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dxLtP3/btsBTYxYdE0/AXgHAYSPqybN0hDmqByiKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdxLtP3%2FbtsBTYxYdE0%2FAXgHAYSPqybN0hDmqByiKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;568&quot; height=&quot;375&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;375&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;코드에서 윈도우 탐색기를 실행하는 방법&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 데이터 수집에 필요한 설정 사항 모두가 TST_CONFIG_INFO 구조체 변수에 저장되도록 마무리되었습니다.&lt;/p&gt;</description>
      <category>트레이딩/시스템트레이딩</category>
      <category>ebestsec</category>
      <category>OpenAPI</category>
      <category>RadioButton</category>
      <category>XINGAPI</category>
      <category>이베스트투자증권</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/12</guid>
      <comments>https://noyecube.tistory.com/entry/xingAPI%EC%B0%A8%ED%8A%B8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%88%98%EC%A7%91%EA%B8%B0-%EB%A7%8C%EB%93%A4%EA%B8%B04-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%88%98%EC%A7%91-%EC%84%A4%EC%A0%95-%EA%B8%B0%EB%8A%A5#entry12comment</comments>
      <pubDate>Tue, 12 Oct 2021 23:48:54 +0900</pubDate>
    </item>
    <item>
      <title>[xingAPI][차트 데이터 수집기 만들기](3) 로그인/로그아웃</title>
      <link>https://noyecube.tistory.com/entry/xingAPI%EC%B0%A8%ED%8A%B8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%88%98%EC%A7%91%EA%B8%B0-%EB%A7%8C%EB%93%A4%EA%B8%B03-%EB%A1%9C%EA%B7%B8%EC%9D%B8%EB%A1%9C%EA%B7%B8%EC%95%84%EC%9B%83</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;MFC기반의 xingAPI 프로그램에서 로그인과 로그아웃 하는 방법을 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 포스팅에서 그려둔 화면을 이용하여 로그인 코드를 작성해 보겠습니다. 화면을 만드는 방법은 아래의 이전 포스팅을 참고해 주세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blog.noyecube.com/entry/xingAPI%EC%B0%A8%ED%8A%B8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%88%98%EC%A7%91%EA%B8%B0-%EB%A7%8C%EB%93%A4%EA%B8%B01-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1-%EB%B0%8F-%ED%99%94%EB%A9%B4-%EB%A7%8C%EB%93%A4%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2021.10.01 - [트레이딩/시스템트레이딩] - [xingAPI][차트 데이터 수집기 만들기](1) 프로젝트 생성 및 화면 만들기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. MFC 컨트롤 설정하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 사진에서 빨간색 박스에 해당하는 부분을 설정하는 방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인에 필요한 아이디, 비밀번호, 공인인증번호, 접속서버(실서버, 모의서버) 정보를 입력받아 로그인을 하거나 로그아웃을 할 수 있는 인터페이스입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;833&quot; data-origin-height=&quot;508&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cncAil/btsBQpCQ8F9/vFrQub96lOTlyoxwaqqiAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cncAil/btsBQpCQ8F9/vFrQub96lOTlyoxwaqqiAK/img.png&quot; data-alt=&quot;Login - eBEST&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cncAil/btsBQpCQ8F9/vFrQub96lOTlyoxwaqqiAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcncAil%2FbtsBQpCQ8F9%2FvFrQub96lOTlyoxwaqqiAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;635&quot; height=&quot;387&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;833&quot; data-origin-height=&quot;508&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Login - eBEST&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨트롤의 리소스 ID, 속성, 변수는 아래 표를 참고하여 설정합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 99.8837%; height: 462px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 15%; text-align: center; height: 40px;&quot;&gt;Label&lt;/td&gt;
&lt;td style=&quot;width: 15%; text-align: center; height: 40px;&quot;&gt;Control&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 40px;&quot;&gt;ID&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 40px;&quot;&gt;Property&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 40px;&quot;&gt;Variable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 15%; text-align: center; height: 40px;&quot;&gt;ID&lt;/td&gt;
&lt;td style=&quot;width: 15%; text-align: center; height: 40px;&quot;&gt;EditBox&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 40px;&quot;&gt;IDC_EDIT_LOGIN_ID&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 40px;&quot;&gt;password:True&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 40px;&quot;&gt;CEditBox::m_ctrEditLoginId&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 71px;&quot;&gt;
&lt;td style=&quot;width: 15%; text-align: center; height: 71px;&quot;&gt;비밀번호&lt;/td&gt;
&lt;td style=&quot;width: 15%; text-align: center; height: 71px;&quot;&gt;EditBox&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 71px;&quot;&gt;IDC_EDIT_LOGIN_PASSWD&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 71px;&quot;&gt;password:True&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 71px;&quot;&gt;CEditBox::&lt;br /&gt;m_ctrEditLoginPasswd&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 15%; text-align: center; height: 40px;&quot;&gt;공인인증&lt;/td&gt;
&lt;td style=&quot;width: 15%; text-align: center; height: 40px;&quot;&gt;EditBox&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 40px;&quot;&gt;IDC_EDIT_LOGIN_CERT&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 40px;&quot;&gt;password:True&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 40px;&quot;&gt;CEditBox::m_ctrEditLoginCert&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 71px;&quot;&gt;
&lt;td style=&quot;width: 15%; text-align: center; height: 71px;&quot;&gt;접속서버&lt;/td&gt;
&lt;td style=&quot;width: 15%; text-align: center; height: 71px;&quot;&gt;ComboBox&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 71px;&quot;&gt;IDC_COMBO_LOGIN_SERVER&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 71px;&quot;&gt;type:DropList, Sort:False&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 71px;&quot;&gt;CComboBox::&lt;br /&gt;m_ctrComboServer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 15%; text-align: center; height: 40px;&quot;&gt;Login&lt;/td&gt;
&lt;td style=&quot;width: 15%; text-align: center; height: 40px;&quot;&gt;Button&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 40px;&quot;&gt;IDC_BUTTON_LOGIN&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 40px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 40px;&quot;&gt;CButton::m_ctrBtnLogin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 15%; text-align: center; height: 40px;&quot;&gt;Logout&lt;/td&gt;
&lt;td style=&quot;width: 15%; text-align: center; height: 40px;&quot;&gt;Button&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 40px;&quot;&gt;IDC_BUTTON_LOGOUT&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 40px;&quot;&gt;-&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 40px;&quot;&gt;CButton::m_ctrBtnLogout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 15%; text-align: center; height: 40px;&quot;&gt;STAT_0&lt;/td&gt;
&lt;td style=&quot;width: 15%; text-align: center; height: 40px;&quot;&gt;StatusBar&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 40px;&quot;&gt;IDC_STATUSBAR_0&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 40px;&quot;&gt;SBPS_NORMAL&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 40px;&quot;&gt;CStatusBar::m_statusBar[0]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 15%; text-align: center; height: 40px;&quot;&gt;STAT_1&lt;/td&gt;
&lt;td style=&quot;width: 15%; text-align: center; height: 40px;&quot;&gt;StatusBar&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 40px;&quot;&gt;IDC_STATUSBAR_1&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 40px;&quot;&gt;SBPS_NORMAL&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 40px;&quot;&gt;CStatusBar::m_statusBar[1]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 15%; text-align: center; height: 40px;&quot;&gt;STAT_2&lt;/td&gt;
&lt;td style=&quot;width: 15%; text-align: center; height: 40px;&quot;&gt;StatusBar&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 40px;&quot;&gt;IDC_STATUSBAR_2&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 40px;&quot;&gt;SBPS_NORMAL&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 40px;&quot;&gt;CStatusBar::m_statusBar[2]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Login, Logout 버튼에 BN_CLICKED 이벤트핸들러를 등록해 줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;412&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2tzsP/btsBMSSSRW1/3wYYrGgMCLWk5ilXBBT8FK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2tzsP/btsBMSSSRW1/3wYYrGgMCLWk5ilXBBT8FK/img.png&quot; data-alt=&quot;Login 버튼 이벤트 핸들러 등록&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2tzsP/btsBMSSSRW1/3wYYrGgMCLWk5ilXBBT8FK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2tzsP%2FbtsBMSSSRW1%2F3wYYrGgMCLWk5ilXBBT8FK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;435&quot; height=&quot;341&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;412&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Login 버튼 이벤트 핸들러 등록&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버튼을 클릭하면 아래의 이벤트핸들러가 호출됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;581&quot; data-origin-height=&quot;186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bf6zr8/btsBHr2zQtP/BdwnprBeIUaxOyfw62nKy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bf6zr8/btsBHr2zQtP/BdwnprBeIUaxOyfw62nKy1/img.png&quot; data-alt=&quot;이벤트 핸들러 코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bf6zr8/btsBHr2zQtP/BdwnprBeIUaxOyfw62nKy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbf6zr8%2FbtsBHr2zQtP%2FBdwnprBeIUaxOyfw62nKy1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;581&quot; height=&quot;186&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;581&quot; data-origin-height=&quot;186&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이벤트 핸들러 코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Combobox는 CdatacollectorDlg::OnInitDialog()에 아래와 같이 초기화 코드를 작성합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;519&quot; data-origin-height=&quot;72&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bttO6l/btsBR7BHjK3/KZICDCjO8KK3gIjNMdu1e1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bttO6l/btsBR7BHjK3/KZICDCjO8KK3gIjNMdu1e1/img.png&quot; data-alt=&quot;콤보박스 설정 코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bttO6l/btsBR7BHjK3/KZICDCjO8KK3gIjNMdu1e1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbttO6l%2FbtsBR7BHjK3%2FKZICDCjO8KK3gIjNMdu1e1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;519&quot; height=&quot;72&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;519&quot; data-origin-height=&quot;72&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;콤보박스 설정 코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램 실행 시 Combobox에서 실서버, 모의서버를 선택할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;117&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PYzpH/btsBR8ABOjP/qLLGsZ3nJWGQpfI3vHcqn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PYzpH/btsBR8ABOjP/qLLGsZ3nJWGQpfI3vHcqn1/img.png&quot; data-alt=&quot;콤보박스 동작 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PYzpH/btsBR8ABOjP/qLLGsZ3nJWGQpfI3vHcqn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPYzpH%2FbtsBR8ABOjP%2FqLLGsZ3nJWGQpfI3vHcqn1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;828&quot; height=&quot;117&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;117&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;콤보박스 동작 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 로그인 처리하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인 상태 관리를 위해 로그인시간, 실서버/데모서버 여부를 저장해 둘 구조체를 하나 정의합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1634048605568&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;namespace DATACOLLECTOR
{
	typedef struct _USER_LOGIN_INFO {
		SRWLOCK srwlock;
		BOOL boISLogin;
		BOOL boIsDemoServer;
		SYSTEMTIME stSysTimeTag;
		TCHAR tchId[64];
		TCHAR tchPasswd[64];
		TCHAR tchCert[64];
		_USER_LOGIN_INFO() {
			ZeroMemory(this, sizeof(struct _USER_LOGIN_INFO));
			InitializeSRWLock(&amp;amp;srwlock);
		}
	} TST_USER_LOGIN_INFO, * PTST_USER_LOGIN_INFO;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인은 아래와 같이 Connect() 함수 호출 후 Login() 함수를 호출합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;1258&quot; data-origin-height=&quot;130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A33TQ/btsBQnE07sL/tGnIAVDR1iAYL84mHAIKw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A33TQ/btsBQnE07sL/tGnIAVDR1iAYL84mHAIKw0/img.png&quot; data-alt=&quot;모의거래, 실거래 구분하여 연결함수 호출&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A33TQ/btsBQnE07sL/tGnIAVDR1iAYL84mHAIKw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA33TQ%2FbtsBQnE07sL%2FtGnIAVDR1iAYL84mHAIKw0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1258&quot; height=&quot;130&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;1258&quot; data-origin-height=&quot;130&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;모의거래, 실거래 구분하여 연결함수 호출&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1634048422184&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;	m_ctrEditLoginId.GetWindowTextA(cstrId);
	m_ctrEditLoginPasswd.GetWindowTextA(cstrPasswd);
	m_ctrEditLoginCert.GetWindowTextA(cstrCert);
	cstrId.Trim();
	cstrPasswd.Trim();
	cstrCert.Trim();

	boRet = theApp.m_xing.Login(theApp.m_pDlgXingMsg-&amp;gt;GetSafeHwnd(), cstrId, cstrPasswd, cstrCert, 0, FALSE);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공/실패 여부는 afx_msg&amp;nbsp;LRESULT&amp;nbsp;CDlgXingMsg::OnMsgIdXmLogin(WPARAM&amp;nbsp;wParam,&amp;nbsp;LPARAM&amp;nbsp;lParam)에서 확인하여 처리합니다. wParam 값이 0이면 로그인 성공, 이외의 값이면 로그인 실패입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 로그인이 되면 관련 컨트롤을 잠그고 상태 바에 로그인 여부와 접속시간을 표기하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;967&quot; data-origin-height=&quot;623&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ejPnBP/btsBUCBaJGJ/aKNbsWxZnaGCTl23f37c4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ejPnBP/btsBUCBaJGJ/aKNbsWxZnaGCTl23f37c4K/img.png&quot; data-alt=&quot;모의서버 로그인에 성공한 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ejPnBP/btsBUCBaJGJ/aKNbsWxZnaGCTl23f37c4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FejPnBP%2FbtsBUCBaJGJ%2FaKNbsWxZnaGCTl23f37c4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;598&quot; height=&quot;385&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;967&quot; data-origin-height=&quot;623&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;모의서버 로그인에 성공한 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인이 성공하면 TR, Real API를 호출하여 사용할 수 있습니다.&lt;/p&gt;</description>
      <category>트레이딩/시스템트레이딩</category>
      <category>mfc</category>
      <category>OpenAPI</category>
      <category>XINGAPI</category>
      <category>xingapi 로그인</category>
      <category>이베스트</category>
      <category>차트데이터</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/11</guid>
      <comments>https://noyecube.tistory.com/entry/xingAPI%EC%B0%A8%ED%8A%B8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%88%98%EC%A7%91%EA%B8%B0-%EB%A7%8C%EB%93%A4%EA%B8%B03-%EB%A1%9C%EA%B7%B8%EC%9D%B8%EB%A1%9C%EA%B7%B8%EC%95%84%EC%9B%83#entry11comment</comments>
      <pubDate>Mon, 4 Oct 2021 22:56:53 +0900</pubDate>
    </item>
    <item>
      <title>[xingAPI][차트 데이터 수집기 만들기](2) 프로젝트 셋업</title>
      <link>https://noyecube.tistory.com/entry/xingAPI%EC%B0%A8%ED%8A%B8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%88%98%EC%A7%91%EA%B8%B0-%EB%A7%8C%EB%93%A4%EA%B8%B02-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%85%8B%EC%97%85</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전 포스팅에서 MFC 프로젝트 생성과 Dialog 화면 설계를 마쳤습니다. 이번 포스팅에서는 MFC 프로젝트에서 xingAPI를 사용할 수 있도록 xingAPI를 셋업 하는 방법을 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 글 링크:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blog.noyecube.com/entry/xingAPI%EC%B0%A8%ED%8A%B8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%88%98%EC%A7%91%EA%B8%B0-%EB%A7%8C%EB%93%A4%EA%B8%B01-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1-%EB%B0%8F-%ED%99%94%EB%A9%B4-%EB%A7%8C%EB%93%A4%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2021.10.01 - [트레이딩/시스템트레이딩] - [xingAPI][차트 데이터 수집기 만들기](1) 프로젝트 생성 및 화면 만들기&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. xingAPI 프로젝트 셋업&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 작성하는 코드에서 xingAPI를 사용하기 위해서는 xingAPI에서 제공하는 DLL을 내 프로젝트에 로딩하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;xingAPI 헤더파일들을 내 프로젝트에 포함시켜야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;xingAPI의 DLL은 Debug, Release 두 가지 버전으로 구분되어 제공됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각 다운로드하여서 비교해 보면 아래와 같이 DEBUG버전에는 파일 뒤에 d 가 붙어있고 xingAPI.dll 용량도 더 큽니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1497&quot; data-origin-height=&quot;342&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cHsjiw/btsBGVv0qJ0/Y3nWVJp8UylyxbqYGfFkp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cHsjiw/btsBGVv0qJ0/Y3nWVJp8UylyxbqYGfFkp1/img.png&quot; data-alt=&quot;Debug-Release DLL 차이점&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cHsjiw/btsBGVv0qJ0/Y3nWVJp8UylyxbqYGfFkp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcHsjiw%2FbtsBGVv0qJ0%2FY3nWVJp8UylyxbqYGfFkp1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1497&quot; height=&quot;342&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1497&quot; data-origin-height=&quot;342&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Debug-Release DLL 차이점&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비주얼스튜디오에서 Debug 모드로 빌드하면 Debug 버전 DLL, Release 모드로 빌드하면 Release 버전 DLL이 링크될 수 있도록 합니다. 아래와 같이 프로젝트의 Debug 폴더에 &quot;xingdlldbg&quot; 폴더 생성 후 Debug 버전 DLL 파일들을 복사, Release 폴더에 &quot;xingdllrel&quot; 폴더 생성 후 Release 버전 DLL 파일들을 복사합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝터 경로에 &quot;xinghdr&quot; 폴더 생성 후 주식차트와 관련된 TR의 헤더파일들을 복사합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 헤더파일들을 일괄적으로 include 할 수 있는 &quot;allxingheaders.h&quot; 파일을 생성하여 그 파일에 xinghdr에 복사한 헤더파일들을 include 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 xingAPI 폴더의 예제소스코드를 뒤져보면 Helper.h, Helper.cpp가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 파일들도 프로젝트에 복사합니다. IXingAPI.h 파일도 복사합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 추가한 파일들을 비주얼스튜디오의 솔루션 익스플로러에 추가해 줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;695&quot; data-origin-height=&quot;781&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zAsBB/btsBQs7lj4N/XIoLyuKCF1s7te2UVLISk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zAsBB/btsBQs7lj4N/XIoLyuKCF1s7te2UVLISk1/img.png&quot; data-alt=&quot;윈도우 탐색기 폴더 구조와 솔루션 탐색기 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zAsBB/btsBQs7lj4N/XIoLyuKCF1s7te2UVLISk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzAsBB%2FbtsBQs7lj4N%2FXIoLyuKCF1s7te2UVLISk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;695&quot; height=&quot;781&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;695&quot; data-origin-height=&quot;781&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;윈도우 탐색기 폴더 구조와 솔루션 탐색기 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;811&quot; data-origin-height=&quot;308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSG0xB/btsBJgzAJgg/wRq43wu0YcrDxMMmCcpDfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSG0xB/btsBJgzAJgg/wRq43wu0YcrDxMMmCcpDfK/img.png&quot; data-alt=&quot;allxingheaders.h&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSG0xB/btsBJgzAJgg/wRq43wu0YcrDxMMmCcpDfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSG0xB%2FbtsBJgzAJgg%2FwRq43wu0YcrDxMMmCcpDfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;811&quot; height=&quot;308&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;811&quot; data-origin-height=&quot;308&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;allxingheaders.h&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. xingAPI 빌드에러 수정하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본으로 복사한 파일을 빌드하면 문자열 관련 함수에서 threadsafe 에러가 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;threadsafe 경고를 무시하는 방법도 있지만&amp;nbsp;그 함수들을 _s 가 붙은 threadsafe 함수로 변경해 줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1563&quot; data-origin-height=&quot;466&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZUE0Z/btsBG4UaKRm/Nz3fPxpT0N55tEckJdYlAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZUE0Z/btsBG4UaKRm/Nz3fPxpT0N55tEckJdYlAK/img.png&quot; data-alt=&quot;IxingAPI.h 수정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZUE0Z/btsBG4UaKRm/Nz3fPxpT0N55tEckJdYlAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZUE0Z%2FbtsBG4UaKRm%2FNz3fPxpT0N55tEckJdYlAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1563&quot; height=&quot;466&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1563&quot; data-origin-height=&quot;466&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;IxingAPI.h 수정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Debug, Release 모드에 따른 DLL 분기 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Helper.cpp 파일의 GetModuleDir() 함수에 Debug, Release DLL 분기 코드를 작성해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일 단계에서 Debug 모드이면 xingdlldbg, Release 모드이면 xingdllrel 하위의 DLL를 로드하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;1856&quot; data-origin-height=&quot;445&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cM8p3F/btsBRSdvtbV/VRGFKarrpjdTlkylk5A8Q0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cM8p3F/btsBRSdvtbV/VRGFKarrpjdTlkylk5A8Q0/img.png&quot; data-alt=&quot;Helper.cpp 수정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cM8p3F/btsBRSdvtbV/VRGFKarrpjdTlkylk5A8Q0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcM8p3F%2FbtsBRSdvtbV%2FVRGFKarrpjdTlkylk5A8Q0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1856&quot; height=&quot;445&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;1856&quot; data-origin-height=&quot;445&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Helper.cpp 수정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 프로젝트 속성 수정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 속성 수정입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DLL이 유니코드가 아닌 멀티바이트로 빌드되어있어서 이 부분을 바꿔줘야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Project-&amp;gt;Configuration Properties-&amp;gt;Advanced-&amp;gt;Character Set-&amp;gt;Use Unicode Character Set을 Use Multi-Byte Character Set으로 변경합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;458&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btuEr8/btsBUduIcT2/5g9jyYPxIGu2P5X3ynGuu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btuEr8/btsBUduIcT2/5g9jyYPxIGu2P5X3ynGuu1/img.png&quot; data-alt=&quot;멀티바이트 캐릭터로 변경&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btuEr8/btsBUduIcT2/5g9jyYPxIGu2P5X3ynGuu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtuEr8%2FbtsBUduIcT2%2F5g9jyYPxIGu2P5X3ynGuu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;979&quot; height=&quot;458&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;458&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;멀티바이트 캐릭터로 변경&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 윈도 메시지 및 핸들러 등록&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 윈도우 메시지 및 핸들러 등록입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;xingAPI DLL 함수 Reference를 보면 API가 사용하는 8개의 Window User Message가 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;821&quot; data-origin-height=&quot;609&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRJ97T/btsBQuRJZeH/31Bx3CzNNj5RRWyCk3m4v1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRJ97T/btsBQuRJZeH/31Bx3CzNNj5RRWyCk3m4v1/img.png&quot; data-alt=&quot;xingAPI 메시지 사양서&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRJ97T/btsBQuRJZeH/31Bx3CzNNj5RRWyCk3m4v1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRJ97T%2FbtsBQuRJZeH%2F31Bx3CzNNj5RRWyCk3m4v1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;821&quot; height=&quot;609&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;821&quot; data-origin-height=&quot;609&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;xingAPI 메시지 사양서&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Window User Message는 User Interface Thread로 생성된 Dialog로 처리할 수 있습니다. 기본 Dialog에서 처리해도 되지만 메시지 처리를 전담하는 별도의 Dialog Class를 생성하겠습니다. Class Wizard에서 Add MFC Class 창을 띄워서 base class는 CDialogEx로 하고 Class name은 &quot;CDlgXingMsg&quot;로 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;809&quot; data-origin-height=&quot;634&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d7YMXF/btsBQmsveHg/f1WQw9ek2z95bVxSt2qvH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d7YMXF/btsBQmsveHg/f1WQw9ek2z95bVxSt2qvH1/img.png&quot; data-alt=&quot;다이얼로그를 상속받는 클래스 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d7YMXF/btsBQmsveHg/f1WQw9ek2z95bVxSt2qvH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd7YMXF%2FbtsBQmsveHg%2Ff1WQw9ek2z95bVxSt2qvH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;809&quot; height=&quot;634&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;809&quot; data-origin-height=&quot;634&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;다이얼로그를 상속받는 클래스 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 App 헤더파일에 아래와 같이 &quot;IxingAPI.h&quot;와 &quot;CDlgXingMsg.h&quot;를 include 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTS서버 주소와 DEMO서버의 URL도 기입해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커스텀 메시지는 &quot;WM_USER&quot;부터 사용가능합니다. 아래와 같이 xingAPI가 사용하는 8개의 메시지를 정의해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 WM_USER가 아닌 다른 값을 BASE로 정하면 일부 TR 응답이 오지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 App 클래스에 멤버변수로 IXingAPI m_xing, CDlgXingMsg* m_pDlgXingMsg를 선언해 줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;624&quot; data-origin-height=&quot;561&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciFFhf/btsBTY5De53/nrFHFEpvSf6p0KIpc23qSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciFFhf/btsBTY5De53/nrFHFEpvSf6p0KIpc23qSK/img.png&quot; data-alt=&quot;WM_USER 값부터 메시지 등록&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciFFhf/btsBTY5De53/nrFHFEpvSf6p0KIpc23qSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FciFFhf%2FbtsBTY5De53%2FnrFHFEpvSf6p0KIpc23qSK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;624&quot; height=&quot;561&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;624&quot; data-origin-height=&quot;561&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;WM_USER 값부터 메시지 등록&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CDlgXingMsg Class에 메시지맵을 등록하고 메시지 핸들러를 생성할 차례입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Class Wizard에서 Class name을 CDlgXingMsg로 선택한 상태에서 Add Custom Message 버튼을 눌러 8개의 메시지를 등록해 줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;803&quot; data-origin-height=&quot;619&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbrQ4Q/btsBG310LCY/E5WwBwalAFkzNOQnEGWQk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbrQ4Q/btsBG310LCY/E5WwBwalAFkzNOQnEGWQk1/img.png&quot; data-alt=&quot;메시지 핸들러 등록&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbrQ4Q/btsBG310LCY/E5WwBwalAFkzNOQnEGWQk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbrQ4Q%2FbtsBG310LCY%2FE5WwBwalAFkzNOQnEGWQk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;803&quot; height=&quot;619&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;803&quot; data-origin-height=&quot;619&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;메시지 핸들러 등록&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등록을 완료하고 CDlgXingMsg에 가보면 아래와 같이 메시지 맵과 메시지 핸들러가 생성된 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;542&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mH8Ju/btsBR8He7bk/bx210FauXCHvfUO78RMxx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mH8Ju/btsBR8He7bk/bx210FauXCHvfUO78RMxx1/img.png&quot; data-alt=&quot;메시지 핸들러 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mH8Ju/btsBR8He7bk/bx210FauXCHvfUO78RMxx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmH8Ju%2FbtsBR8He7bk%2Fbx210FauXCHvfUO78RMxx1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;774&quot; height=&quot;542&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;542&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;메시지 핸들러 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CDlgXingMsg.h에 가서 핸들러 프로토타입이 protected로 되어 있으면 public으로 바꿔주세요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;685&quot; data-origin-height=&quot;246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRH3tV/btsBKgs6lU0/F0QE2dHLOHL5UGQVilMY41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRH3tV/btsBKgs6lU0/F0QE2dHLOHL5UGQVilMY41/img.png&quot; data-alt=&quot;호출될 수 있도록 public으로 변경&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRH3tV/btsBKgs6lU0/F0QE2dHLOHL5UGQVilMY41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRH3tV%2FbtsBKgs6lU0%2FF0QE2dHLOHL5UGQVilMY41%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;685&quot; height=&quot;246&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;685&quot; data-origin-height=&quot;246&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;호출될 수 있도록 public으로 변경&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;App.cpp의 InitInstance()의 DoModal() 호출구문 앞에&amp;nbsp;XingAPI DLL 초기화 코드를 넣어줍니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 CDlgXingMsg의 Create 코드를 작성해 주고 ExitInstance()에 Delete 코드를 작성해 줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;304&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PAsuZ/btsBT4xOw7d/qcMXjiKjkopgrOIhlMP3e1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PAsuZ/btsBT4xOw7d/qcMXjiKjkopgrOIhlMP3e1/img.png&quot; data-alt=&quot;초기화 코드 삽입&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PAsuZ/btsBT4xOw7d/qcMXjiKjkopgrOIhlMP3e1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPAsuZ%2FbtsBT4xOw7d%2FqcMXjiKjkopgrOIhlMP3e1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;673&quot; height=&quot;304&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;304&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;초기화 코드 삽입&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;14.png&quot; data-origin-width=&quot;603&quot; data-origin-height=&quot;160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/df16An/btsBT36J3yQ/PMGBnmLFlRKRlFGY5SWUt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/df16An/btsBT36J3yQ/PMGBnmLFlRKRlFGY5SWUt1/img.png&quot; data-alt=&quot;종료 코드 삽입&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/df16An/btsBT36J3yQ/PMGBnmLFlRKRlFGY5SWUt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdf16An%2FbtsBT36J3yQ%2FPMGBnmLFlRKRlFGY5SWUt1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;603&quot; height=&quot;160&quot; data-filename=&quot;14.png&quot; data-origin-width=&quot;603&quot; data-origin-height=&quot;160&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;종료 코드 삽입&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 하고 ctrl+F5로 빌드하면 에러 없이 실행되어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 xingAPI를 사용할 수 있습니다.&lt;/p&gt;</description>
      <category>트레이딩/시스템트레이딩</category>
      <category>getmoduledir</category>
      <category>mfc</category>
      <category>OpenAPI</category>
      <category>XINGAPI</category>
      <category>xingapi t1857</category>
      <category>자동매매</category>
      <category>차트데이터</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/10</guid>
      <comments>https://noyecube.tistory.com/entry/xingAPI%EC%B0%A8%ED%8A%B8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%88%98%EC%A7%91%EA%B8%B0-%EB%A7%8C%EB%93%A4%EA%B8%B02-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%85%8B%EC%97%85#entry10comment</comments>
      <pubDate>Mon, 4 Oct 2021 02:11:01 +0900</pubDate>
    </item>
    <item>
      <title>[xingAPI][차트 데이터 수집기 만들기](1) 프로젝트 생성 및 화면 만들기</title>
      <link>https://noyecube.tistory.com/entry/xingAPI%EC%B0%A8%ED%8A%B8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%88%98%EC%A7%91%EA%B8%B0-%EB%A7%8C%EB%93%A4%EA%B8%B01-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1-%EB%B0%8F-%ED%99%94%EB%A9%B4-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;증권사 API를 활용하여 일봉, 주봉 등 차트 데이터를 수집하는 프로그램을 처음부터 만들어 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;xingAPI 기반으로 만들어 봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수집한 데이터를 디스크에 파일로 저장하는 것이 목표입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차트 데이터는 과거 데이터이고 수정주가 반영이 필요할 때 외에는 고정된 값이기 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복잡한 데이터 가공이 필요하지 않으면 데이터베이스 사용 대신 파일로 관리해도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;DB로 관리할까 파일로 관리할까&quot;에 대한 여러 의견은 아래 링크에서 추가적으로 읽어보세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://quant.stackexchange.com/questions/29572/building-financial-data-time-series-database-from-scratch&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://quant.stackexchange.com/questions/29572/building-financial-data-time-series-database-from-scratch&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. MFC 프로젝트 생성하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MFC App 프로젝트를 생성합니다. MFC App 프로젝트 템플릿이 안 보이면 Visual Studio Installer를 실행하여 MFC 구성요소 추가 설치를 진행해 주세요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.webp&quot; data-origin-width=&quot;958&quot; data-origin-height=&quot;378&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhdAfY/btsBRQUjuBd/8ZtHeo09xEf8n93UQeRVmK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhdAfY/btsBRQUjuBd/8ZtHeo09xEf8n93UQeRVmK/img.webp&quot; data-alt=&quot;MFC App 선택&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhdAfY/btsBRQUjuBd/8ZtHeo09xEf8n93UQeRVmK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhdAfY%2FbtsBRQUjuBd%2F8ZtHeo09xEf8n93UQeRVmK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;958&quot; height=&quot;378&quot; data-filename=&quot;1.webp&quot; data-origin-width=&quot;958&quot; data-origin-height=&quot;378&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;MFC App 선택&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application type은 &quot;Dialog based&quot;, Use of MFC는 &quot;Use MFC in a static library&quot;로 선택합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;static library 속성으로 하지 않으면 Visual Studio가 설치되지 않은 타 PC에서는 실행이 어렵습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;858&quot; data-origin-height=&quot;499&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bf8QER/btsBJcKISqQ/fPpKmRwKL04mjWTzTY2xaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bf8QER/btsBJcKISqQ/fPpKmRwKL04mjWTzTY2xaK/img.png&quot; data-alt=&quot;타입과 MFC 라이브러리 링크 방식 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bf8QER/btsBJcKISqQ/fPpKmRwKL04mjWTzTY2xaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbf8QER%2FbtsBJcKISqQ%2FfPpKmRwKL04mjWTzTY2xaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;858&quot; height=&quot;499&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;858&quot; data-origin-height=&quot;499&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;타입과 MFC 라이브러리 링크 방식 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 생성 후 &quot;Ctrl+F5&quot;를 눌렀을 때 아래와 같이 기본 프로그램이 실행되면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.webp&quot; data-origin-width=&quot;955&quot; data-origin-height=&quot;752&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbbbf3/btsBTXlkvbh/gyKQk7KZHUG36YjN52EeSk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbbbf3/btsBTXlkvbh/gyKQk7KZHUG36YjN52EeSk/img.webp&quot; data-alt=&quot;빈 프로젝트 실행시 기본 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbbbf3/btsBTXlkvbh/gyKQk7KZHUG36YjN52EeSk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbbbbf3%2FbtsBTXlkvbh%2FgyKQk7KZHUG36YjN52EeSk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;955&quot; height=&quot;752&quot; data-filename=&quot;3.webp&quot; data-origin-width=&quot;955&quot; data-origin-height=&quot;752&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;빈 프로젝트 실행시 기본 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Dialog 화면 설계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Resource View 탭 선택 후 기본 Dialog를 선택하면 필요한 컨트롤들을 배치하여 화면을 구성할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;947&quot; data-origin-height=&quot;784&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tZDiD/btsBKlOBq7N/F8wKo40QfqU8vVLCmEV2Z1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tZDiD/btsBKlOBq7N/F8wKo40QfqU8vVLCmEV2Z1/img.png&quot; data-alt=&quot;리소스 매니저 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tZDiD/btsBKlOBq7N/F8wKo40QfqU8vVLCmEV2Z1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtZDiD%2FbtsBKlOBq7N%2FF8wKo40QfqU8vVLCmEV2Z1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;947&quot; height=&quot;784&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;947&quot; data-origin-height=&quot;784&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;리소스 매니저 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면 하단에 상태 바(StatusBar) 추가를 위하여 Resource Symbols 창을 열고 아래와 같이 ID생성을 해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;853&quot; data-origin-height=&quot;570&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tDQYt/btsBRSYQyA1/9d1QXrslnzfJ6VoCfGbEs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tDQYt/btsBRSYQyA1/9d1QXrslnzfJ6VoCfGbEs1/img.png&quot; data-alt=&quot;리소스 심볼 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tDQYt/btsBRSYQyA1/9d1QXrslnzfJ6VoCfGbEs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtDQYt%2FbtsBRSYQyA1%2F9d1QXrslnzfJ6VoCfGbEs1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;853&quot; height=&quot;570&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;853&quot; data-origin-height=&quot;570&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;리소스 심볼 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 추가한 리소스 ID를 String Table에 추가해 줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;1156&quot; data-origin-height=&quot;834&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mm3kl/btsBMXl9Z4n/YhYl5YklxYGOux3wYaeTnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mm3kl/btsBMXl9Z4n/YhYl5YklxYGOux3wYaeTnK/img.png&quot; data-alt=&quot;String Table 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mm3kl/btsBMXl9Z4n/YhYl5YklxYGOux3wYaeTnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmm3kl%2FbtsBMXl9Z4n%2FYhYl5YklxYGOux3wYaeTnK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1156&quot; height=&quot;834&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;1156&quot; data-origin-height=&quot;834&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;String Table 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MSDN에서 참조한 아래의 status bar 생성방법 확인 후 코드를 작성합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;544&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvlN0u/btsBQUoUEU8/3BF1FWhYYinBVFfQt38nVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvlN0u/btsBQUoUEU8/3BF1FWhYYinBVFfQt38nVk/img.png&quot; data-alt=&quot;MSDN 가이드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvlN0u/btsBQUoUEU8/3BF1FWhYYinBVFfQt38nVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvlN0u%2FbtsBQUoUEU8%2F3BF1FWhYYinBVFfQt38nVk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;544&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;544&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;MSDN 가이드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태 바에 3개의 상태칸을 만들고 싶어서 OnInitDialog()에 아래와 같이 작성하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;1180&quot; data-origin-height=&quot;1114&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Z6TxU/btsBQtd0U1S/J21FVhrwnVMfEG0TddNi7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Z6TxU/btsBQtd0U1S/J21FVhrwnVMfEG0TddNi7k/img.png&quot; data-alt=&quot;StatusBar 추가 코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Z6TxU/btsBQtd0U1S/J21FVhrwnVMfEG0TddNi7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZ6TxU%2FbtsBQtd0U1S%2FJ21FVhrwnVMfEG0TddNi7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1180&quot; height=&quot;1114&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;1180&quot; data-origin-height=&quot;1114&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;StatusBar 추가 코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 로그인 정보 입력을 위한 컨트롤 배치를 완료 후 &quot;ctrl+F5&quot;로 확인하면 아래와 같이 상태 바 생성이 완료된 것을 확인할 수 있습니다. 로그인 컨트롤 배치 시 EDIT 컨트롤은 password 속성을 True, 접속서버 선택을 위한 Combobox 컨트롤의 type 속성은 Drop List로, Sort 속성은 False로 설정하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;564&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cHk6S7/btsBQs7jENF/DjxLDxuKaNZcXmk1eWlH51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cHk6S7/btsBQs7jENF/DjxLDxuKaNZcXmk1eWlH51/img.png&quot; data-alt=&quot;컨트롤 배치 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cHk6S7/btsBQs7jENF/DjxLDxuKaNZcXmk1eWlH51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcHk6S7%2FbtsBQs7jENF%2FDjxLDxuKaNZcXmk1eWlH51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;980&quot; height=&quot;564&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;564&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;컨트롤 배치 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 완성된 화면입니다. 로그인 후 수집할 종목, 수집할 데이터 타입, 수집 기간, 저장 경로를 설정할 수 있도록 하였습니다. 상태 창에서는 현재 수집 중인 종목정보와 전체 진행률, 잔여시간을 확인할 수 있도록 컨트롤을 배치하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;1005&quot; data-origin-height=&quot;1293&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAh4Jg/btsBUcJiXhX/PeC8cZ9JPnBEyUumDKdoHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAh4Jg/btsBUcJiXhX/PeC8cZ9JPnBEyUumDKdoHK/img.png&quot; data-alt=&quot;최종 완성 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAh4Jg/btsBUcJiXhX/PeC8cZ9JPnBEyUumDKdoHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAh4Jg%2FbtsBUcJiXhX%2FPeC8cZ9JPnBEyUumDKdoHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1005&quot; height=&quot;1293&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;1005&quot; data-origin-height=&quot;1293&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;최종 완성 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 xingAPI사용을 위한 프로젝트 셋업을 다룹니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음글 링크는 아래와 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blog.noyecube.com/entry/xingAPI%EC%B0%A8%ED%8A%B8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%88%98%EC%A7%91%EA%B8%B0-%EB%A7%8C%EB%93%A4%EA%B8%B02-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%85%8B%EC%97%85&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2021.10.04 - [트레이딩/시스템트레이딩] - [xingAPI][차트 데이터 수집기 만들기](2) 프로젝트 셋업&lt;/a&gt;&lt;/p&gt;</description>
      <category>트레이딩/시스템트레이딩</category>
      <category>mfc</category>
      <category>OpenAPI</category>
      <category>XINGAPI</category>
      <category>자동매매</category>
      <category>차트데이터</category>
      <author>noyecube</author>
      <guid isPermaLink="true">https://noyecube.tistory.com/9</guid>
      <comments>https://noyecube.tistory.com/entry/xingAPI%EC%B0%A8%ED%8A%B8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%88%98%EC%A7%91%EA%B8%B0-%EB%A7%8C%EB%93%A4%EA%B8%B01-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1-%EB%B0%8F-%ED%99%94%EB%A9%B4-%EB%A7%8C%EB%93%A4%EA%B8%B0#entry9comment</comments>
      <pubDate>Fri, 1 Oct 2021 23:10:03 +0900</pubDate>
    </item>
  </channel>
</rss>