<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>코드한입</title>
    <link>https://subling.tistory.com/</link>
    <description>리눅스, C#, WPF 등 개발에 관련된 기초와 실무에서 발생하는 문제 해결을 제시하는 블로그입니다.</description>
    <language>ko</language>
    <pubDate>Fri, 10 Apr 2026 10:38:47 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>코드한입</managingEditor>
    <image>
      <title>코드한입</title>
      <url>https://tistory1.daumcdn.net/tistory/2217226/attach/6b7478d208af4e0bb246ba83d2aca7d1</url>
      <link>https://subling.tistory.com</link>
    </image>
    <item>
      <title>[리눅스 프로그래밍 시리즈 28편]epoll 기반 채팅 서버 만들기: 멀티 클라이언트 브로드캐스트 구현</title>
      <link>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-28%ED%8E%B8epoll-%EA%B8%B0%EB%B0%98-%EC%B1%84%ED%8C%85-%EC%84%9C%EB%B2%84-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EB%A9%80%ED%8B%B0-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%EB%B8%8C%EB%A1%9C%EB%93%9C%EC%BA%90%EC%8A%A4%ED%8A%B8-%EA%B5%AC%ED%98%84</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;목표: epoll을 활용해 여러 클라이언트가 동시에 대화하는 서버를 구현한다&lt;br /&gt;&amp;nbsp;결과: 클라이언트가 보낸 메시지를 모든 접속자에게 전달(브로드캐스트)할 수 있다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 프로젝트 목표&lt;/h1&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;모든 클라이언트에게 전달 (broadcast)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Client A &amp;rarr; 서버 &amp;rarr; Client B, C, D
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 핵심 구조&lt;/h1&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;epoll
  &amp;darr;
이벤트 발생 (read)
  &amp;darr;
메시지 읽기
  &amp;darr;
모든 클라이언트에게 write
&lt;/code&gt;&lt;/pre&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-4141655932477481&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 서버 코드 (epoll 기반 채팅)&lt;/h1&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;arpa/inet.h&amp;gt;
#include &amp;lt;sys/epoll.h&amp;gt;

#define MAX_EVENTS 10
#define PORT 8080

int main() {

    int server_fd = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(PORT);

    bind(server_fd, (struct sockaddr*)&amp;amp;addr, sizeof(addr));
    listen(server_fd, 5);

    int epfd = epoll_create1(0);

    struct epoll_event ev, events[MAX_EVENTS];

    ev.events = EPOLLIN;
    ev.data.fd = server_fd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, server_fd, &amp;amp;ev);

    printf(&quot;Chat server started on %d...\n&quot;, PORT);

    while(1) {

        int n = epoll_wait(epfd, events, MAX_EVENTS, -1);

        for(int i = 0; i &amp;lt; n; i++) {

            int fd = events[i].data.fd;

            if(fd == server_fd) {

                int client_fd = accept(server_fd, NULL, NULL);

                ev.events = EPOLLIN;
                ev.data.fd = client_fd;
                epoll_ctl(epfd, EPOLL_CTL_ADD, client_fd, &amp;amp;ev);

                printf(&quot;Client connected: %d\n&quot;, client_fd);

            } else {

                char buf[1024];
                int len = read(fd, buf, sizeof(buf));

                if(len &amp;lt;= 0) {
                    close(fd);
                    printf(&quot;Client disconnected: %d\n&quot;, fd);
                } else {

                    // broadcast
                    for(int j = 0; j &amp;lt; MAX_EVENTS; j++) {
                        int target = events[j].data.fd;
                        if(target != fd &amp;amp;&amp;amp; target != server_fd) {
                            write(target, buf, len);
                        }
                    }
                }
            }
        }
    }

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 테스트 방법 (netcat)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 실행:&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;gcc chat_server.c -o chat_server
./chat_server
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트 접속 (여러 터미널):&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;nc 127.0.0.1 8080
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 클라이언트에서 입력하면 다른 클라이언트에 출력된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 개선 포인트 (실무 관점)&lt;/h1&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;메시지 포맷 (JSON 등)&lt;/li&gt;
&lt;li&gt;non-blocking 소켓&lt;/li&gt;
&lt;li&gt;EPOLLET 적용&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 브로드캐스트 구조 개선 팁&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재는 events 배열 기반이라 문제가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;실제로는 별도의 &lt;b&gt;client list&lt;/b&gt;를 유지해야 한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;int clients[1024];
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 자주 하는 실수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이벤트 배열을 클라이언트 리스트로 착각&lt;br /&gt;&amp;nbsp;non-blocking 설정 안함&lt;br /&gt;&amp;nbsp;disconnect 처리 누락&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 28편 요약&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;epoll로 멀티 클라이언트 처리&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메인 키워드&lt;/h2&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;epoll 채팅 서버 C&lt;/li&gt;
&lt;li&gt;Linux chat server example&lt;/li&gt;
&lt;li&gt;TCP broadcast server&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서브 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;epoll multi client chat&lt;/li&gt;
&lt;li&gt;리눅스 네트워크 프로젝트&lt;/li&gt;
&lt;li&gt;C socket chat server&lt;/li&gt;
&lt;li&gt;서버 브로드캐스트 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Linux 완전 정복/Linux 실무 운영</category>
      <category>epoll</category>
      <category>LinuxProgramming</category>
      <category>네트워크프로그래밍</category>
      <category>실무프로젝트</category>
      <category>채팅서버</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/240</guid>
      <comments>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-28%ED%8E%B8epoll-%EA%B8%B0%EB%B0%98-%EC%B1%84%ED%8C%85-%EC%84%9C%EB%B2%84-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EB%A9%80%ED%8B%B0-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%EB%B8%8C%EB%A1%9C%EB%93%9C%EC%BA%90%EC%8A%A4%ED%8A%B8-%EA%B5%AC%ED%98%84#entry240comment</comments>
      <pubDate>Wed, 8 Apr 2026 16:52:19 +0900</pubDate>
    </item>
    <item>
      <title>[WPF 실무 34편] WPF MVVM Navigation 고급 패턴 (View 완전 분리 구조)</title>
      <link>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-34%ED%8E%B8-WPF-MVVM-Navigation-%EA%B3%A0%EA%B8%89-%ED%8C%A8%ED%84%B4-View-%EC%99%84%EC%A0%84-%EB%B6%84%EB%A6%AC-%EA%B5%AC%EC%A1%B0</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;규모가 커질수록 Navigation 로직이 복잡해집니다.&lt;br /&gt;View에서 직접 화면을 바꾸는 구조는 유지보수가 어려워집니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 실무에서는&lt;br /&gt;&lt;b&gt;Navigation을 ViewModel에서 완전히 분리하는 구조&lt;/b&gt;를 사용합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 기존 Navigation 문제&lt;/h1&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;MainFrame.Navigate(new HomePage());
&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;✔ View에 의존&lt;br /&gt;✔ 테스트 어려움&lt;br /&gt;✔ 구조 확장 불가&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 목표 구조&lt;/h1&gt;
&lt;pre class=&quot;cos&quot;&gt;&lt;code&gt;View &amp;rarr; ViewModel &amp;rarr; NavigationService &amp;rarr; ViewModel
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ View 제거&lt;br /&gt;✔ ViewModel 기반 전환&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. NavigationService 설계&lt;/h1&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;public interface INavigationService
{
    void Navigate&amp;lt;TViewModel&amp;gt;();
}
&lt;/code&gt;&lt;/pre&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-4141655932477481&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 구현&lt;/h1&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public class NavigationService : INavigationService
{
    private readonly Func&amp;lt;Type, object&amp;gt; _viewModelFactory;
    private readonly MainViewModel _mainViewModel;

    public NavigationService(Func&amp;lt;Type, object&amp;gt; factory, MainViewModel main)
    {
        _viewModelFactory = factory;
        _mainViewModel = main;
    }

    public void Navigate&amp;lt;TViewModel&amp;gt;()
    {
        var vm = (TViewModel)_viewModelFactory(typeof(TViewModel));
        _mainViewModel.CurrentViewModel = vm;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. MainViewModel&lt;/h1&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public class MainViewModel : ObservableObject
{
    private object _currentViewModel;

    public object CurrentViewModel
    {
        get =&amp;gt; _currentViewModel;
        set =&amp;gt; SetProperty(ref _currentViewModel, value);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. View 연결 (DataTemplate)&lt;/h1&gt;
&lt;pre class=&quot;dust&quot;&gt;&lt;code&gt;&amp;lt;DataTemplate DataType=&quot;{x:Type vm:HomeViewModel}&quot;&amp;gt;
    &amp;lt;views:HomeView/&amp;gt;
&amp;lt;/DataTemplate&amp;gt;

&amp;lt;DataTemplate DataType=&quot;{x:Type vm:SettingsViewModel}&quot;&amp;gt;
    &amp;lt;views:SettingsView/&amp;gt;
&amp;lt;/DataTemplate&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;dust&quot;&gt;&lt;code&gt;&amp;lt;ContentControl Content=&quot;{Binding CurrentViewModel}&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ View 자동 매핑&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. ViewModel에서 Navigation 호출&lt;/h1&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;_navigation.Navigate&amp;lt;SettingsViewModel&amp;gt;();
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ View 몰라도 됨&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8.&amp;nbsp; 실무에서 자주 하는 실수 1 &amp;mdash; ViewModel에서 View 생성&lt;/h1&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;new SettingsView()
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;절대 금지&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9.&amp;nbsp; 실무에서 자주 하는 실수 2 &amp;mdash; NavigationService 없이 직접 변경&lt;/h1&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;CurrentViewModel = new ViewModel();
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 확장성 없음&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 실무 확장&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ DI 컨테이너 연결&lt;br /&gt;✔ Navigation 히스토리 관리&lt;br /&gt;✔ Parameter 전달&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 한 줄 핵심 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Navigation은 View가 아니라 ViewModel에서 제어해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF MVVM Navigation&lt;br /&gt;WPF NavigationService&lt;br /&gt;WPF ViewModel 화면 전환&lt;br /&gt;WPF DataTemplate Navigation&lt;br /&gt;WPF MVVM 구조&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발이야기/WPF 어플리케이션 구조</category>
      <category>csharp wpf</category>
      <category>WPF</category>
      <category>wpf architecture</category>
      <category>WPF MVVM</category>
      <category>wpf navigation</category>
      <category>닷넷 개발</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/239</guid>
      <comments>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-34%ED%8E%B8-WPF-MVVM-Navigation-%EA%B3%A0%EA%B8%89-%ED%8C%A8%ED%84%B4-View-%EC%99%84%EC%A0%84-%EB%B6%84%EB%A6%AC-%EA%B5%AC%EC%A1%B0#entry239comment</comments>
      <pubDate>Wed, 8 Apr 2026 16:04:39 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스 프로그래밍 시리즈 27편]epoll 완전 이해: 고성능 이벤트 기반 서버의 표준 (edge/level trigger)</title>
      <link>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-27%ED%8E%B8epoll-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%84%9C%EB%B2%84%EC%9D%98-%ED%91%9C%EC%A4%80-edgelevel-trigger</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;목표: epoll의 동작 원리와 select와의 차이를 이해한다&lt;br /&gt;&amp;nbsp;결과: 대규모 동시 접속을 처리하는 고성능 서버 구조를 설계할 수 있다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 왜 epoll이 필요한가?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;select는 다음 한계가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;fd 수 제한(보통 1024)&lt;/li&gt;
&lt;li&gt;매 호출마다 fd_set 복사&lt;/li&gt;
&lt;li&gt;O(n) 스캔&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;epoll은 이 문제를 해결하기 위해 등장했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;O(1)에 가까운 이벤트 처리&lt;/li&gt;
&lt;li&gt;대규모 연결 처리에 적합&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. epoll 개념&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;epoll은 &lt;b&gt;관심 있는 fd를 커널에 등록&lt;/b&gt;해두고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이벤트가 발생한 fd만 반환받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 불필요한 스캔이 없다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. epoll 핵심 API&lt;/h1&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;#include &amp;lt;sys/epoll.h&amp;gt;

int epoll_create1(int flags);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
&lt;/code&gt;&lt;/pre&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-4141655932477481&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. epoll_create1&lt;/h1&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;int epfd = epoll_create1(0);
&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;epoll 인스턴스 생성&lt;/li&gt;
&lt;li&gt;epfd = epoll 전용 fd&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. epoll_ctl (등록/수정/삭제)&lt;/h1&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = sock;

epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &amp;amp;ev);
&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;EPOLL_CTL_ADD &amp;rarr; 등록&lt;/li&gt;
&lt;li&gt;EPOLL_CTL_MOD &amp;rarr; 수정&lt;/li&gt;
&lt;li&gt;EPOLL_CTL_DEL &amp;rarr; 삭제&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. epoll_wait (이벤트 대기)&lt;/h1&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;struct epoll_event events[10];

int n = epoll_wait(epfd, events, 10, -1);
&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;이벤트 발생한 fd 개수 반환&lt;/li&gt;
&lt;li&gt;events 배열에 결과 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 기본 epoll 서버 예제&lt;/h1&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;arpa/inet.h&amp;gt;
#include &amp;lt;sys/epoll.h&amp;gt;

int main() {

    int server_fd = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(8080);

    bind(server_fd, (struct sockaddr*)&amp;amp;addr, sizeof(addr));
    listen(server_fd, 5);

    int epfd = epoll_create1(0);

    struct epoll_event ev, events[10];

    ev.events = EPOLLIN;
    ev.data.fd = server_fd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, server_fd, &amp;amp;ev);

    while(1) {

        int n = epoll_wait(epfd, events, 10, -1);

        for(int i = 0; i &amp;lt; n; i++) {

            if(events[i].data.fd == server_fd) {

                int client_fd = accept(server_fd, NULL, NULL);

                ev.events = EPOLLIN;
                ev.data.fd = client_fd;
                epoll_ctl(epfd, EPOLL_CTL_ADD, client_fd, &amp;amp;ev);

            } else {

                int fd = events[i].data.fd;
                char buf[100];

                int len = read(fd, buf, 100);

                if(len &amp;lt;= 0) {
                    close(fd);
                } else {
                    write(fd, buf, len);
                }
            }
        }
    }

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. Level Trigger vs Edge Trigger&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Level Trigger (기본)&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;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Edge Trigger (EPOLLET)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상태 변화 시 1번만 이벤트 발생&lt;/li&gt;
&lt;li&gt;성능 좋지만 구현 난이도 높음&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. epoll 장점&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특징설명&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&gt;고성능&lt;/td&gt;
&lt;td&gt;O(1) 이벤트 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;대규모 연결&lt;/td&gt;
&lt;td&gt;수만 개 연결 처리 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;커널 관리&lt;/td&gt;
&lt;td&gt;효율적 이벤트 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 실무에서 사용하는 구조&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;epoll + non-blocking socket&lt;/li&gt;
&lt;li&gt;worker thread&lt;/li&gt;
&lt;li&gt;event loop&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;nginx&lt;/li&gt;
&lt;li&gt;redis&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 자주 하는 실수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;non-blocking 설정 안함&lt;br /&gt;&amp;nbsp;ET 모드에서 반복 read 안함&lt;br /&gt;&amp;nbsp;epoll_ctl 관리 실수&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. 27편 요약&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;epoll은 고성능 이벤트 기반 I/O 핵심&lt;/li&gt;
&lt;li&gt;select의 한계를 해결&lt;/li&gt;
&lt;li&gt;ET/LT 모드 이해 중요&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메인 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;epoll 사용법&lt;/li&gt;
&lt;li&gt;리눅스 epoll 서버&lt;/li&gt;
&lt;li&gt;Linux epoll example&lt;/li&gt;
&lt;li&gt;epoll vs select&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서브 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;epoll edge trigger&lt;/li&gt;
&lt;li&gt;epoll level trigger&lt;/li&gt;
&lt;li&gt;고성능 서버 구조&lt;/li&gt;
&lt;li&gt;Linux event loop&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Linux 완전 정복/Linux 시스템 프로그래밍</category>
      <category>epoll</category>
      <category>LinuxProgramming</category>
      <category>고성능서버</category>
      <category>네트워크프로그래밍</category>
      <category>이벤트루프</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/238</guid>
      <comments>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-27%ED%8E%B8epoll-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-%EC%84%9C%EB%B2%84%EC%9D%98-%ED%91%9C%EC%A4%80-edgelevel-trigger#entry238comment</comments>
      <pubDate>Tue, 7 Apr 2026 13:27:17 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스 프로그래밍 시리즈 26편]select 완전 이해: 여러 소켓을 동시에 처리하는 이벤트 기반 I/O</title>
      <link>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-26%ED%8E%B8select-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4-%EC%97%AC%EB%9F%AC-%EC%86%8C%EC%BC%93%EC%9D%84-%EB%8F%99%EC%8B%9C%EC%97%90-%EC%B2%98%EB%A6%AC%ED%95%98%EB%8A%94-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-IO</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;목표: select를 이용해 여러 파일 디스크립터를 동시에 감시하는 방법을 이해한다&lt;br /&gt;&amp;nbsp;결과: 단일 프로세스로 다수의 클라이언트를 처리하는 서버를 구현할 수 있다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 왜 select가 필요한가?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;25편의 fork 서버는 단순하지만 비용이 크다.&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;&amp;nbsp;해결: &lt;b&gt;이벤트 기반 I/O&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 프로세스가 여러 소켓을 동시에 처리한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. select 개념&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;select는 여러 파일 디스크립터(fd)를 감시하다가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;읽기/쓰기 가능한 fd가 생기면 알려준다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. select 함수&lt;/h1&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;#include &amp;lt;sys/select.h&amp;gt;

int select(int nfds, fd_set *readfds, fd_set *writefds,
           fd_set *exceptfds, struct timeval *timeout);
&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;readfds &amp;rarr; 읽기 가능 감시&lt;/li&gt;
&lt;li&gt;writefds &amp;rarr; 쓰기 가능 감시&lt;/li&gt;
&lt;li&gt;nfds &amp;rarr; 가장 큰 fd + 1&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. fd_set 사용법&lt;/h1&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;fd_set set;

FD_ZERO(&amp;amp;set);        // 초기화
FD_SET(fd, &amp;amp;set);     // fd 추가
FD_CLR(fd, &amp;amp;set);     // 제거
FD_ISSET(fd, &amp;amp;set);   // 이벤트 확인
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 기본 흐름&lt;/h1&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. fd_set 구성
2. select 호출
3. 이벤트 발생 fd 확인
4. read/write 처리
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 간단한 select 서버 예제&lt;/h1&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;arpa/inet.h&amp;gt;
#include &amp;lt;sys/select.h&amp;gt;

int main() {

    int server_fd = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(8080);

    bind(server_fd, (struct sockaddr*)&amp;amp;addr, sizeof(addr));
    listen(server_fd, 5);

    fd_set master, readfds;

    FD_ZERO(&amp;amp;master);
    FD_SET(server_fd, &amp;amp;master);

    int maxfd = server_fd;

    while(1) {

        readfds = master;

        select(maxfd + 1, &amp;amp;readfds, NULL, NULL, NULL);

        for(int i = 0; i &amp;lt;= maxfd; i++) {

            if(FD_ISSET(i, &amp;amp;readfds)) {

                if(i == server_fd) {

                    int client_fd = accept(server_fd, NULL, NULL);
                    FD_SET(client_fd, &amp;amp;master);

                    if(client_fd &amp;gt; maxfd) maxfd = client_fd;

                } else {

                    char buf[100];
                    int n = read(i, buf, 100);

                    if(n &amp;lt;= 0) {
                        close(i);
                        FD_CLR(i, &amp;amp;master);
                    } else {
                        write(i, buf, n);
                    }
                }
            }
        }
    }

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 동작 구조&lt;/h1&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;select()
  &amp;darr;
이벤트 발생 fd 확인
  &amp;darr;
각 fd 처리
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;하나의 프로세스로 다수 클라이언트 처리&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. select의 한계&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제설명&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&gt;fd 개수 제한&lt;/td&gt;
&lt;td&gt;보통 1024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;매번 fd_set 복사&lt;/td&gt;
&lt;td&gt;성능 저하&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;O(n) 탐색&lt;/td&gt;
&lt;td&gt;비효율&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그래서 등장한 것이 epoll&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 언제 select를 쓰나?&lt;/h1&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 자주 하는 실수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;readfds 재설정 안함&lt;br /&gt;&amp;nbsp;maxfd 갱신 안함&lt;br /&gt;&amp;nbsp;FD_CLR 누락&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 26편 요약&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;select는 &lt;b&gt;멀티 I/O 처리 도구&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;fd_set으로 여러 fd 관리&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메인 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;select 사용법&lt;/li&gt;
&lt;li&gt;리눅스 멀티 I/O&lt;/li&gt;
&lt;li&gt;Linux select example&lt;/li&gt;
&lt;li&gt;select 서버 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서브 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;fd_set 사용법&lt;/li&gt;
&lt;li&gt;리눅스 이벤트 기반 서버&lt;/li&gt;
&lt;li&gt;select socket server&lt;/li&gt;
&lt;li&gt;Linux multiplexing&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;태그&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#select&lt;br /&gt;#멀티IO&lt;br /&gt;#이벤트기반&lt;br /&gt;#LinuxProgramming&lt;br /&gt;#네트워크프로그래밍&lt;/p&gt;</description>
      <category>Linux 완전 정복/Linux 시스템 프로그래밍</category>
      <category>LinuxProgramming</category>
      <category>select</category>
      <category>네트워크프로그래밍</category>
      <category>멀티IO</category>
      <category>이벤트기반</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/237</guid>
      <comments>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-26%ED%8E%B8select-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4-%EC%97%AC%EB%9F%AC-%EC%86%8C%EC%BC%93%EC%9D%84-%EB%8F%99%EC%8B%9C%EC%97%90-%EC%B2%98%EB%A6%AC%ED%95%98%EB%8A%94-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%EB%B0%98-IO#entry237comment</comments>
      <pubDate>Mon, 6 Apr 2026 10:04:54 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스 프로그래밍 시리즈 24편] 소켓 프로그래밍 기초: TCP 서버/클라이언트 구현 (connect, bind, listen, accept)</title>
      <link>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-24%ED%8E%B8-%EC%86%8C%EC%BC%93-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EA%B8%B0%EC%B4%88-TCP-%EC%84%9C%EB%B2%84%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%EA%B5%AC%ED%98%84-connect-bind-listen-accept</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;목표: TCP 소켓의 기본 흐름을 이해한다&lt;br /&gt;&amp;nbsp;결과: 간단한 서버와 클라이언트를 직접 구현할 수 있다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 소켓이란?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소켓(Socket)은 &lt;b&gt;프로세스 간 네트워크 통신의 끝점(endpoint)&lt;/b&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;채팅 프로그램&lt;/li&gt;
&lt;li&gt;API 서버&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. TCP vs UDP&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로토콜특징&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&gt;TCP&lt;/td&gt;
&lt;td&gt;연결 지향, 신뢰성 높음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UDP&lt;/td&gt;
&lt;td&gt;비연결, 빠름&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 편에서는 &lt;b&gt;TCP&lt;/b&gt;를 사용한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 서버 측 기본 흐름&lt;/h1&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;socket()
 &amp;darr;
bind()
 &amp;darr;
listen()
 &amp;darr;
accept()
 &amp;darr;
read()/write()
&lt;/code&gt;&lt;/pre&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-4141655932477481&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 클라이언트 측 기본 흐름&lt;/h1&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;socket()
 &amp;darr;
connect()
 &amp;darr;
read()/write()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 서버 예제 코드&lt;/h1&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;arpa/inet.h&amp;gt;

int main() {

    int server_fd = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(8080);

    bind(server_fd, (struct sockaddr*)&amp;amp;addr, sizeof(addr));

    listen(server_fd, 5);

    printf(&quot;Server listening on 8080...\n&quot;);

    int client_fd = accept(server_fd, NULL, NULL);

    char buffer[100];
    read(client_fd, buffer, 100);

    printf(&quot;Received: %s\n&quot;, buffer);

    write(client_fd, &quot;Hello Client&quot;, 12);

    close(client_fd);
    close(server_fd);

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 클라이언트 예제 코드&lt;/h1&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;arpa/inet.h&amp;gt;

int main() {

    int sock = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(8080);
    inet_pton(AF_INET, &quot;127.0.0.1&quot;, &amp;amp;server.sin_addr);

    connect(sock, (struct sockaddr*)&amp;amp;server, sizeof(server));

    write(sock, &quot;Hello Server&quot;, 12);

    char buffer[100];
    read(sock, buffer, 100);

    printf(&quot;Server reply: %s\n&quot;, buffer);

    close(sock);

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 실행 방법&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 실행:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;gcc server.c -o server
./server
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트 실행:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;gcc client.c -o client
./client
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 핵심 함수 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수역할&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&gt;socket&lt;/td&gt;
&lt;td&gt;소켓 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bind&lt;/td&gt;
&lt;td&gt;주소/포트 할당&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;listen&lt;/td&gt;
&lt;td&gt;연결 대기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;accept&lt;/td&gt;
&lt;td&gt;클라이언트 연결 수락&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;connect&lt;/td&gt;
&lt;td&gt;서버 연결&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;read/write&lt;/td&gt;
&lt;td&gt;데이터 송수신&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 실무에서 중요한 개념&lt;/h1&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;논블로킹 I/O&lt;/li&gt;
&lt;li&gt;select / epoll&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 내용은 다음 편에서 이어진다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 자주 하는 실수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;bind 포트 충돌&lt;br /&gt;&amp;nbsp;htons / inet_pton 누락&lt;br /&gt;&amp;nbsp;read/write 버퍼 처리 오류&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 24편 요약&lt;/h1&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;TCP는 연결 기반 통신&lt;/li&gt;
&lt;li&gt;서버: socket &amp;rarr; bind &amp;rarr; listen &amp;rarr; accept&lt;/li&gt;
&lt;li&gt;클라이언트: socket &amp;rarr; connect&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스 네트워크 프로그래밍의 시작점이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메인 키워드&lt;/h2&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;TCP 서버 클라이언트 C&lt;/li&gt;
&lt;li&gt;Linux socket example&lt;/li&gt;
&lt;li&gt;socket bind listen accept&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서브 키워드&lt;/h2&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;리눅스 네트워크 프로그래밍&lt;/li&gt;
&lt;li&gt;TCP socket tutorial C&lt;/li&gt;
&lt;li&gt;서버 클라이언트 통신 예제&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Linux 완전 정복/Linux 시스템 프로그래밍</category>
      <category>LinuxProgramming</category>
      <category>SOCKET</category>
      <category>TCP</category>
      <category>네트워크프로그래밍</category>
      <category>시스템프로그래밍</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/236</guid>
      <comments>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-24%ED%8E%B8-%EC%86%8C%EC%BC%93-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EA%B8%B0%EC%B4%88-TCP-%EC%84%9C%EB%B2%84%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%EA%B5%AC%ED%98%84-connect-bind-listen-accept#entry236comment</comments>
      <pubDate>Thu, 2 Apr 2026 11:10:46 +0900</pubDate>
    </item>
    <item>
      <title>[WPF 실무 32편] WPF Messenger 패턴 (ViewModel 간 통신 완전 정리)</title>
      <link>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-32%ED%8E%B8-WPF-Messenger-%ED%8C%A8%ED%84%B4-ViewModel-%EA%B0%84-%ED%86%B5%EC%8B%A0-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC-1</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVVM 구조에서 가장 많이 막히는 부분 중 하나는&lt;br /&gt;&amp;nbsp;ViewModel 간 데이터 전달입니다.&lt;/p&gt;
&lt;/blockquote&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;A 화면 &amp;rarr; B 화면 데이터 전달&lt;/li&gt;
&lt;li&gt;Dialog 결과 전달&lt;/li&gt;
&lt;li&gt;전역 이벤트 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 때 사용하는 것이 바로&lt;br /&gt;&lt;b&gt;Messenger 패턴 (CommunityToolkit.Mvvm)&lt;/b&gt; 입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 왜 Messenger가 필요한가?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVVM에서는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;ViewModel끼리 직접 참조하면 안 됨&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;&amp;nbsp;그래서 중간 전달자 필요&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. Messenger 개념&lt;/h1&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;Sender &amp;rarr; Message &amp;rarr; Receiver
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 메시지를 보내고&lt;br /&gt;✔ 필요한 ViewModel만 받는다&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. Toolkit Messenger 사용 준비&lt;/h1&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;using CommunityToolkit.Mvvm.Messaging;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 메시지 클래스 정의&lt;/h1&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class UserSelectedMessage
{
    public string UserName { get; }

    public UserSelectedMessage(string userName)
    {
        UserName = userName;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&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-4141655932477481&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;h1&gt;5. 메시지 보내기 (Sender)&lt;/h1&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;WeakReferenceMessenger.Default.Send(
    new UserSelectedMessage(&quot;홍길동&quot;));
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 어디서든 전송 가능&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 메시지 받기 (Receiver)&lt;/h1&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;WeakReferenceMessenger.Default.Register&amp;lt;UserSelectedMessage&amp;gt;(
    this,
    (r, m) =&amp;gt;
    {
        SelectedUser = m.UserName;
    });
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 필요한 ViewModel만 수신&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 자동 해제 (중요)&lt;/h1&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;WeakReferenceMessenger.Default.UnregisterAll(this);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 메모리 누수 방지&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8.&amp;nbsp; 실무에서 자주 하는 실수 1 &amp;mdash; StrongReferenceMessenger 사용&lt;/h1&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;StrongReferenceMessenger
&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;✔ GC 안됨 &amp;rarr; 메모리 누수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;반드시 WeakReferenceMessenger 사용&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9.&amp;nbsp; 실무에서 자주 하는 실수 2 &amp;mdash; Unregister 안 함&lt;/h1&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 실무 활용 예&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 화면 간 데이터 전달&lt;br /&gt;✔ Dialog 결과 전달&lt;br /&gt;✔ 글로벌 이벤트 (로그인 상태 등)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. Messenger vs Event 비교&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구분EventMessenger&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&gt;결합도&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;td&gt;낮음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;구조&lt;/td&gt;
&lt;td&gt;직접 연결&lt;/td&gt;
&lt;td&gt;중간 전달&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MVVM 적합&lt;/td&gt;
&lt;td&gt;낮음&lt;/td&gt;
&lt;td&gt;매우 높음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. 한 줄 핵심 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Messenger는 ViewModel 간 결합 없이 데이터를 전달하는 구조다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF Messenger 패턴&lt;br /&gt;CommunityToolkit Messenger&lt;br /&gt;WPF ViewModel 통신&lt;br /&gt;WPF MVVM 메시지 전달&lt;br /&gt;WeakReferenceMessenger&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발이야기/WPF MVVM</category>
      <category>communitytoolkit</category>
      <category>csharp wpf</category>
      <category>messenger</category>
      <category>MVVM</category>
      <category>WPF</category>
      <category>닷넷 개발</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/235</guid>
      <comments>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-32%ED%8E%B8-WPF-Messenger-%ED%8C%A8%ED%84%B4-ViewModel-%EA%B0%84-%ED%86%B5%EC%8B%A0-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC-1#entry235comment</comments>
      <pubDate>Thu, 2 Apr 2026 10:57:40 +0900</pubDate>
    </item>
    <item>
      <title>[WPF 실무 32편] WPF Messenger 패턴 (ViewModel 간 통신 완전 정리)</title>
      <link>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-32%ED%8E%B8-WPF-Messenger-%ED%8C%A8%ED%84%B4-ViewModel-%EA%B0%84-%ED%86%B5%EC%8B%A0-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVVM 구조에서 가장 많이 막히는 부분 중 하나는&lt;br /&gt;&amp;nbsp;ViewModel 간 데이터 전달입니다.&lt;/p&gt;
&lt;/blockquote&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;A 화면 &amp;rarr; B 화면 데이터 전달&lt;/li&gt;
&lt;li&gt;Dialog 결과 전달&lt;/li&gt;
&lt;li&gt;전역 이벤트 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 때 사용하는 것이 바로&lt;br /&gt;&lt;b&gt;Messenger 패턴 (CommunityToolkit.Mvvm)&lt;/b&gt; 입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 왜 Messenger가 필요한가?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVVM에서는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;ViewModel끼리 직접 참조하면 안 됨&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;&amp;nbsp;그래서 중간 전달자 필요&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. Messenger 개념&lt;/h1&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;Sender &amp;rarr; Message &amp;rarr; Receiver
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 메시지를 보내고&lt;br /&gt;✔ 필요한 ViewModel만 받는다&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. Toolkit Messenger 사용 준비&lt;/h1&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;using CommunityToolkit.Mvvm.Messaging;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 메시지 클래스 정의&lt;/h1&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class UserSelectedMessage
{
    public string UserName { get; }

    public UserSelectedMessage(string userName)
    {
        UserName = userName;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 메시지 보내기 (Sender)&lt;/h1&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;WeakReferenceMessenger.Default.Send(
    new UserSelectedMessage(&quot;홍길동&quot;));
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 어디서든 전송 가능&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 메시지 받기 (Receiver)&lt;/h1&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;WeakReferenceMessenger.Default.Register&amp;lt;UserSelectedMessage&amp;gt;(
    this,
    (r, m) =&amp;gt;
    {
        SelectedUser = m.UserName;
    });
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 필요한 ViewModel만 수신&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 자동 해제 (중요)&lt;/h1&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;WeakReferenceMessenger.Default.UnregisterAll(this);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 메모리 누수 방지&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-4141655932477481&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8.&amp;nbsp; 실무에서 자주 하는 실수 1 &amp;mdash; StrongReferenceMessenger 사용&lt;/h1&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;StrongReferenceMessenger
&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;✔ GC 안됨 &amp;rarr; 메모리 누수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;반드시 WeakReferenceMessenger 사용&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9.&amp;nbsp; 실무에서 자주 하는 실수 2 &amp;mdash; Unregister 안 함&lt;/h1&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 실무 활용 예&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 화면 간 데이터 전달&lt;br /&gt;✔ Dialog 결과 전달&lt;br /&gt;✔ 글로벌 이벤트 (로그인 상태 등)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. Messenger vs Event 비교&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구분EventMessenger&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&gt;결합도&lt;/td&gt;
&lt;td&gt;높음&lt;/td&gt;
&lt;td&gt;낮음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;구조&lt;/td&gt;
&lt;td&gt;직접 연결&lt;/td&gt;
&lt;td&gt;중간 전달&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MVVM 적합&lt;/td&gt;
&lt;td&gt;낮음&lt;/td&gt;
&lt;td&gt;매우 높음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. 한 줄 핵심 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Messenger는 ViewModel 간 결합 없이 데이터를 전달하는 구조다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF Messenger 패턴&lt;br /&gt;CommunityToolkit Messenger&lt;br /&gt;WPF ViewModel 통신&lt;br /&gt;WPF MVVM 메시지 전달&lt;br /&gt;WeakReferenceMessenger&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발이야기/WPF MVVM</category>
      <category>communitytoolkit</category>
      <category>csharp wpf</category>
      <category>messenger</category>
      <category>MVVM</category>
      <category>WPF</category>
      <category>닷넷 개발</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/234</guid>
      <comments>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-32%ED%8E%B8-WPF-Messenger-%ED%8C%A8%ED%84%B4-ViewModel-%EA%B0%84-%ED%86%B5%EC%8B%A0-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC#entry234comment</comments>
      <pubDate>Tue, 24 Mar 2026 15:02:24 +0900</pubDate>
    </item>
    <item>
      <title>[WPF 실무 31편] ObservableProperty / RelayCommand 자동화 심화 (Toolkit 고급 기능 완전 정리)</title>
      <link>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-31%ED%8E%B8-ObservableProperty-RelayCommand-%EC%9E%90%EB%8F%99%ED%99%94-%EC%8B%AC%ED%99%94-Toolkit-%EA%B3%A0%EA%B8%89-%EA%B8%B0%EB%8A%A5-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC</link>
      <description>&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;30편에서 CommunityToolkit.Mvvm의 기본 사용법을 배웠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 편에서는&amp;nbsp; 실무에서 자주 쓰는 &lt;b&gt;고급 기능&lt;/b&gt;을 다룹니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ ObservableProperty 연동 처리 ✔ NotifyPropertyChangedFor 사용법 ✔ NotifyCanExecuteChangedFor 사용법 ✔ RelayCommand 파라미터 전달 ✔ 비동기 Command (AsyncRelayCommand) 를 정리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. ObservableProperty 복습&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;30편에서 배운 기본 구조입니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;public partial class MainViewModel : ObservableObject
{
    [ObservableProperty]
    private string name;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ Name 프로퍼티 자동 생성 ✔ PropertyChanged 자동 호출&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. NotifyPropertyChangedFor &amp;mdash; 연관 프로퍼티 알림&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로퍼티가 변경될 때&amp;nbsp; 다른 프로퍼티도 함께 갱신해야 하는 경우가 많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예: FirstName이 바뀌면 FullName도 갱신&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public partial class MainViewModel : ObservableObject
{
    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(FullName))]
    private string firstName;

    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(FullName))]
    private string lastName;

    public string FullName =&amp;gt; $&quot;{FirstName} {LastName}&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ FirstName 변경 &amp;rarr; FullName도 PropertyChanged 발생 ✔ lastName 변경 &amp;rarr; FullName도 PropertyChanged 발생&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;기존 방식이었다면 setter마다 수동 호출 필요&amp;nbsp; Toolkit은 어트리뷰트 한 줄이면 끝&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. NotifyCanExecuteChangedFor &amp;mdash; Command 자동 갱신&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로퍼티가 변경될 때&amp;nbsp; Command의 CanExecute도 갱신해야 할 때 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public partial class MainViewModel : ObservableObject
{
    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(SaveCommand))]
    private string name;

    [RelayCommand(CanExecute = nameof(CanSave))]
    private void Save()
    {
        // 저장 로직
    }

    private bool CanSave()
    {
        return !string.IsNullOrEmpty(Name);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ Name이 변경되면 SaveCommand의 CanExecute 자동 재평가 ✔ 버튼 활성화/비활성화가 자동으로 처리됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;30편에서 수동으로 NotifyCanExecuteChanged() 호출하던 것을&amp;nbsp; 어트리뷰트 하나로 해결&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. RelayCommand에 파라미터 전달&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Command에 값을 전달해야 할 때가 있습니다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;[RelayCommand]
private void Delete(int id)
{
    // id로 삭제
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XAML&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;&amp;lt;Button Content=&quot;삭제&quot;
        Command=&quot;{Binding DeleteCommand}&quot;
        CommandParameter=&quot;3&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ CommandParameter 값이 Delete(int id)로 전달됨&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 비동기 Command (AsyncRelayCommand)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서는 비동기 작업이 필수입니다.&amp;nbsp; API 호출, DB 저장 등&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;[RelayCommand]
private async Task LoadDataAsync()
{
    IsLoading = true;
    var data = await _service.GetDataAsync();
    Items = new ObservableCollection&amp;lt;string&amp;gt;(data);
    IsLoading = false;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ async Task 메서드에 [RelayCommand] 붙이면 ✔ AsyncRelayCommand 자동 생성됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XAML&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;&amp;lt;Button Content=&quot;불러오기&quot; Command=&quot;{Binding LoadDataCommand}&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;메서드명 LoadDataAsync &amp;rarr; Command명 LoadDataCommand&amp;nbsp; Async 접미사는 자동으로 제거됨&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. AsyncRelayCommand의 IsRunning 활용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 Command는 실행 중 상태를 확인할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;&amp;lt;Button Content=&quot;불러오기&quot;
        Command=&quot;{Binding LoadDataCommand}&quot;/&amp;gt;

&amp;lt;ProgressBar IsIndeterminate=&quot;True&quot;
             Visibility=&quot;{Binding LoadDataCommand.IsRunning,
                          Converter={StaticResource BoolToVisibility}}&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ IsRunning &amp;rarr; 실행 중이면 true ✔ 로딩 표시를 별도 프로퍼티 없이 처리 가능&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. OnPropertyChanged 부분 커스텀&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값 변경 시 추가 로직이 필요하면&amp;nbsp; partial 메서드를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;[ObservableProperty]
private string name;

partial void OnNameChanged(string value)
{
    // Name이 변경된 후 실행되는 로직
    Debug.WriteLine($&quot;Name 변경됨: {value}&quot;);
}

partial void OnNameChanging(string value)
{
    // Name이 변경되기 직전 실행되는 로직
    Debug.WriteLine($&quot;Name 변경 예정: {value}&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ OnXxxChanged &amp;rarr; 변경 후 콜백 ✔ OnXxxChanging &amp;rarr; 변경 전 콜백&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;setter를 직접 작성할 필요 없음&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8.&amp;nbsp; 실무에서 자주 하는 실수&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실수 1 &amp;mdash; NotifyPropertyChangedFor에 필드명 사용&lt;/h3&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;[NotifyPropertyChangedFor(nameof(fullName))]  //&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[NotifyPropertyChangedFor(nameof(FullName))]  // ✔ 프로퍼티명 사용
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실수 2 &amp;mdash; async void 사용&lt;/h3&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;[RelayCommand]
private async void LoadData()  //  예외 처리 불가&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;[RelayCommand]
private async Task LoadDataAsync()  // ✔ Task 반환
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실수 3 &amp;mdash; partial 클래스 누락 (30편 복습)&lt;/h3&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;public class MainViewModel  //&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public partial class MainViewModel  // ✔
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. 30편 기본 vs 31편 고급 비교&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능 30편 (기본) 31편 (고급)&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&gt;프로퍼티&lt;/td&gt;
&lt;td&gt;[ObservableProperty]&lt;/td&gt;
&lt;td&gt;+ NotifyPropertyChangedFor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Command&lt;/td&gt;
&lt;td&gt;[RelayCommand]&lt;/td&gt;
&lt;td&gt;+ CanExecute 자동 갱신&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;파라미터&lt;/td&gt;
&lt;td&gt;없음&lt;/td&gt;
&lt;td&gt;CommandParameter 전달&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;비동기&lt;/td&gt;
&lt;td&gt;없음&lt;/td&gt;
&lt;td&gt;AsyncRelayCommand&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;콜백&lt;/td&gt;
&lt;td&gt;없음&lt;/td&gt;
&lt;td&gt;OnXxxChanged / OnXxxChanging&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10. 한 줄 핵심 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Toolkit의 어트리뷰트를 조합하면 ViewModel 코드의 90%를 자동 생성할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF ObservableProperty 심화 WPF NotifyPropertyChangedFor WPF AsyncRelayCommand WPF MVVM Toolkit 고급 WPF 비동기 Command&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발이야기/WPF MVVM</category>
      <category>csharp</category>
      <category>MVVM</category>
      <category>ObservableProperty</category>
      <category>relaycommand</category>
      <category>Toolkit</category>
      <category>WPF</category>
      <category>닷넷 개발</category>
      <category>비동기</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/233</guid>
      <comments>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-31%ED%8E%B8-ObservableProperty-RelayCommand-%EC%9E%90%EB%8F%99%ED%99%94-%EC%8B%AC%ED%99%94-Toolkit-%EA%B3%A0%EA%B8%89-%EA%B8%B0%EB%8A%A5-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC#entry233comment</comments>
      <pubDate>Mon, 23 Mar 2026 17:02:14 +0900</pubDate>
    </item>
    <item>
      <title>[WPF 실무 30편] WPF MVVM Toolkit 사용법 (CommunityToolkit.Mvvm으로 코드 줄이기)</title>
      <link>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-30%ED%8E%B8-WPF-MVVM-Toolkit-%EC%82%AC%EC%9A%A9%EB%B2%95-CommunityToolkitMvvm%EC%9C%BC%EB%A1%9C-%EC%BD%94%EB%93%9C-%EC%A4%84%EC%9D%B4%EA%B8%B0</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVVM 패턴을 적용하다 보면 반복되는 코드가 많습니다.&lt;br /&gt;INotifyPropertyChanged, RelayCommand, PropertyChanged 호출...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 전부 자동으로 처리해주는 것이 바로&lt;br /&gt;&lt;b&gt;CommunityToolkit.Mvvm (MVVM Toolkit)&lt;/b&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;✔ MVVM Toolkit 개념&lt;br /&gt;✔ 설치 방법&lt;br /&gt;✔ ObservableProperty 사용법&lt;br /&gt;✔ RelayCommand 자동 생성&lt;br /&gt;✔ 기존 MVVM 코드와 차이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 실무 기준으로 정리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. MVVM Toolkit이란?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVVM Toolkit은 Microsoft에서 제공하는 라이브러리로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;MVVM 코드를 자동으로 생성해주는 도구입니다.&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;br /&gt;✔ 유지보수 쉬움&lt;br /&gt;✔ 실수 감소&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 설치 방법&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NuGet에서 설치&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;CommunityToolkit.Mvvm
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치 후 바로 사용 가능&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 기존 MVVM 코드 문제&lt;/h1&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public class MainViewModel : INotifyPropertyChanged
{
    private string _name;

    public string Name
    {
        get =&amp;gt; _name;
        set
        {
            _name = value;
            OnPropertyChanged(nameof(Name));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string name)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
}
&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;br /&gt;✔ 반복 구조&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-4141655932477481&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. ObservableObject 사용&lt;/h1&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;using CommunityToolkit.Mvvm.ComponentModel;

public partial class MainViewModel : ObservableObject
{
    [ObservableProperty]
    private string name;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 자동으로 Property 생성됨&lt;br /&gt;✔ PropertyChanged 자동 처리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;코드 80% 감소&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. RelayCommand 자동 생성&lt;/h1&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;using CommunityToolkit.Mvvm.Input;

public partial class MainViewModel : ObservableObject
{
    [RelayCommand]
    private void Save()
    {
        // 저장 로직
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XAML&lt;/p&gt;
&lt;pre class=&quot;dust&quot;&gt;&lt;code&gt;&amp;lt;Button Command=&quot;{Binding SaveCommand}&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ Command 자동 생성됨&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. CanExecute 자동 처리&lt;/h1&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;[RelayCommand(CanExecute = nameof(CanSave))]
private void Save()
{
}

private bool CanSave()
{
    return IsValid;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 버튼 활성화 자동 제어&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7.&amp;nbsp; 실무에서 자주 하는 실수 1 &amp;mdash; partial 누락&lt;/h1&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;public class MainViewModel
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;동작 안 함&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;public partial class MainViewModel
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필수&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8.&amp;nbsp; 실무에서 자주 하는 실수 2 &amp;mdash; 필드 이름 규칙&lt;/h1&gt;
&lt;pre class=&quot;delphi&quot;&gt;&lt;code&gt;private string Name;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;생성 안 됨&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;pre class=&quot;delphi&quot;&gt;&lt;code&gt;private string name;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;camelCase 필드 필요&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 기존 방식 vs Toolkit 비교&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구분기존 MVVMToolkit&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&gt;코드량&lt;/td&gt;
&lt;td&gt;많음&lt;/td&gt;
&lt;td&gt;적음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PropertyChanged&lt;/td&gt;
&lt;td&gt;수동&lt;/td&gt;
&lt;td&gt;자동&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Command&lt;/td&gt;
&lt;td&gt;직접 구현&lt;/td&gt;
&lt;td&gt;자동 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;생산성&lt;/td&gt;
&lt;td&gt;낮음&lt;/td&gt;
&lt;td&gt;매우 높음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 실무 추천&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 신규 프로젝트 &amp;rarr; Toolkit 사용&lt;br /&gt;✔ 기존 프로젝트 &amp;rarr; 점진적 적용&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 한 줄 핵심 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVVM Toolkit은 MVVM 코드를 자동으로 만들어주는 도구다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF MVVM Toolkit&lt;br /&gt;CommunityToolkit.Mvvm 사용법&lt;br /&gt;WPF ObservableProperty&lt;br /&gt;WPF RelayCommand 자동 생성&lt;br /&gt;WPF MVVM 최신 방법&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발이야기/WPF MVVM</category>
      <category>communitytoolkit</category>
      <category>csharp wpf</category>
      <category>mvvm toolkit</category>
      <category>WPF</category>
      <category>WPF MVVM</category>
      <category>닷넷 개발</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/232</guid>
      <comments>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-30%ED%8E%B8-WPF-MVVM-Toolkit-%EC%82%AC%EC%9A%A9%EB%B2%95-CommunityToolkitMvvm%EC%9C%BC%EB%A1%9C-%EC%BD%94%EB%93%9C-%EC%A4%84%EC%9D%B4%EA%B8%B0#entry232comment</comments>
      <pubDate>Mon, 23 Mar 2026 17:00:12 +0900</pubDate>
    </item>
    <item>
      <title>[WPF 실무 29편] WPF Validation (입력 검증 + ValidationRule 사용법 완전 정리)</title>
      <link>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-29%ED%8E%B8-WPF-Validation-%EC%9E%85%EB%A0%A5-%EA%B2%80%EC%A6%9D-ValidationRule-%EC%82%AC%EC%9A%A9%EB%B2%95-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 잘못된 값을 입력했을 때&lt;br /&gt;UI에서 바로 피드백을 주는 것은 매우 중요한 UX 요소입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF에서는 &lt;b&gt;Validation 시스템&lt;/b&gt;을 통해&lt;br /&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;✔ Validation 구조&lt;br /&gt;✔ ValidationRule 사용법&lt;br /&gt;✔ ErrorTemplate 적용&lt;br /&gt;✔ MVVM 방식 검증&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 실무 기준으로 정리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. WPF Validation 구조&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF Validation은 Binding 과정에서 동작합니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;입력 &amp;rarr; Binding &amp;rarr; Validation &amp;rarr; ViewModel
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 잘못된 값이면 Binding 중단&lt;br /&gt;✔ UI에 오류 표시&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. ValidationRule 기본 구현&lt;/h1&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public class NumberValidationRule : ValidationRule
{
    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        if (int.TryParse(value?.ToString(), out int result))
        {
            if (result &amp;gt;= 0 &amp;amp;&amp;amp; result &amp;lt;= 100)
                return ValidationResult.ValidResult;
        }

        return new ValidationResult(false, &quot;0~100 사이 값 입력&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. XAML에서 Validation 적용&lt;/h1&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;TextBox&amp;gt;
    &amp;lt;TextBox.Text&amp;gt;
        &amp;lt;Binding Path=&quot;Score&quot; UpdateSourceTrigger=&quot;PropertyChanged&quot;&amp;gt;
            &amp;lt;Binding.ValidationRules&amp;gt;
                &amp;lt;local:NumberValidationRule/&amp;gt;
            &amp;lt;/Binding.ValidationRules&amp;gt;
        &amp;lt;/Binding&amp;gt;
    &amp;lt;/TextBox.Text&amp;gt;
&amp;lt;/TextBox&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 입력 시 실시간 검증&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&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-4141655932477481&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;h1&gt;4. ErrorTemplate 적용 (UI 표시)&lt;/h1&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;Style TargetType=&quot;TextBox&quot;&amp;gt;
    &amp;lt;Setter Property=&quot;Validation.ErrorTemplate&quot;&amp;gt;
        &amp;lt;Setter.Value&amp;gt;
            &amp;lt;ControlTemplate&amp;gt;
                &amp;lt;DockPanel&amp;gt;
                    &amp;lt;TextBlock Foreground=&quot;Red&quot;
                               Text=&quot;⚠&quot;
                               Margin=&quot;5&quot;/&amp;gt;
                    &amp;lt;AdornedElementPlaceholder/&amp;gt;
                &amp;lt;/DockPanel&amp;gt;
            &amp;lt;/ControlTemplate&amp;gt;
        &amp;lt;/Setter.Value&amp;gt;
    &amp;lt;/Setter&amp;gt;
&amp;lt;/Style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 오류 발생 시 아이콘 표시&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 오류 메시지 표시&lt;/h1&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;&amp;lt;TextBlock Foreground=&quot;Red&quot;
           Text=&quot;{Binding (Validation.Errors)[0].ErrorContent,
                  RelativeSource={RelativeSource Self}}&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 사용자에게 구체적인 오류 메시지 제공&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6.&amp;nbsp; 실무에서 자주 하는 실수 1 &amp;mdash; UpdateSourceTrigger 미설정&lt;/h1&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;&amp;lt;Binding Path=&quot;Score&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본값은 LostFocus&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;증상&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 입력 중에는 검증 안 됨&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;UpdateSourceTrigger=&quot;PropertyChanged&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7.&amp;nbsp; 실무에서 자주 하는 실수 2 &amp;mdash; 예외 처리 안 함&lt;/h1&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;int.Parse(value.ToString())
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘못된 입력 시 예외 발생&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ TryParse 사용&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. MVVM 방식 Validation&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서는 ValidationRule 대신&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ IDataErrorInfo&lt;br /&gt;✔ INotifyDataErrorInfo&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용하기도 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예 (IDataErrorInfo)&lt;/h3&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;public string this[string columnName]
{
    get
    {
        if(columnName == &quot;Score&quot; &amp;amp;&amp;amp; Score &amp;lt; 0)
            return &quot;0 이상 입력&quot;;

        return null;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ ViewModel에서 검증 처리&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 실무 추천 방식&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 간단 검증 &amp;rarr; ValidationRule&lt;br /&gt;✔ 복잡한 비즈니스 검증 &amp;rarr; ViewModel&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 방식 혼합 사용이 일반적입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 한 줄 핵심 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Validation은 UI가 아니라 Binding 단계에서 처리된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF Validation&lt;br /&gt;WPF ValidationRule 사용법&lt;br /&gt;WPF 입력 검증&lt;br /&gt;WPF TextBox Validation&lt;br /&gt;WPF IDataErrorInfo&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발이야기/WPF 데이터 바인딩</category>
      <category>csharp wpf</category>
      <category>WPF</category>
      <category>wpf binding</category>
      <category>wpf textbox</category>
      <category>wpf validation</category>
      <category>닷넷 개발</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/231</guid>
      <comments>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-29%ED%8E%B8-WPF-Validation-%EC%9E%85%EB%A0%A5-%EA%B2%80%EC%A6%9D-ValidationRule-%EC%82%AC%EC%9A%A9%EB%B2%95-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC#entry231comment</comments>
      <pubDate>Mon, 23 Mar 2026 16:50:21 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스 프로그래밍 시리즈 23편] 공유 메모리 &amp;amp; 메시지 큐: 고급 IPC(프로세스 간 통신) 완전 이해</title>
      <link>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-23%ED%8E%B8-%EA%B3%B5%EC%9C%A0-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%A9%94%EC%8B%9C%EC%A7%80-%ED%81%90-%EA%B3%A0%EA%B8%89-IPC%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EA%B0%84-%ED%86%B5%EC%8B%A0-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;목표: 공유 메모리와 메시지 큐의 개념과 사용법을 이해한다&lt;br /&gt;&amp;nbsp;결과: 프로세스 간 빠르고 안전하게 데이터를 주고받을 수 있다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. IPC의 진짜 핵심 단계&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;18편에서 pipe를 배웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 pipe는 한계가 있다.&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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 더 강력한 IPC가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;공유 메모리 (Shared Memory)&lt;/b&gt;&lt;br /&gt;&lt;b&gt;메시지 큐 (Message Queue)&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 공유 메모리란?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 프로세스가 &lt;b&gt;같은 메모리 영역을 공유&lt;/b&gt;하는 방식.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조:&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;프로세스 A
      │
      ├── 공유 메모리 ──┤
      │
프로세스 B
&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;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 공유 메모리 생성 (shmget)&lt;/h1&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;#include &amp;lt;sys/ipc.h&amp;gt;
#include &amp;lt;sys/shm.h&amp;gt;

int shmid = shmget(IPC_PRIVATE, 1024, IPC_CREAT | 0666);
&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;1024 바이트 메모리 생성&lt;/li&gt;
&lt;li&gt;접근 권한 0666&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 메모리 연결 (shmat)&lt;/h1&gt;
&lt;pre class=&quot;haskell&quot;&gt;&lt;code&gt;char *data = (char *)shmat(shmid, NULL, 0);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 일반 변수처럼 사용 가능.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;strcpy(data, &quot;Hello Shared Memory&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 메모리 해제&lt;/h1&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;shmdt(data);
shmctl(shmid, IPC_RMID, NULL);
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 공유 메모리 예제&lt;/h1&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;sys/ipc.h&amp;gt;
#include &amp;lt;sys/shm.h&amp;gt;
#include &amp;lt;string.h&amp;gt;

int main() {

    int shmid = shmget(IPC_PRIVATE, 1024, IPC_CREAT | 0666);

    char *data = (char *)shmat(shmid, NULL, 0);

    strcpy(data, &quot;Hello IPC&quot;);

    printf(&quot;Data: %s\n&quot;, data);

    shmdt(data);
    shmctl(shmid, IPC_RMID, NULL);

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 메시지 큐란?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메시지 큐는 &lt;b&gt;데이터를 메시지 형태로 주고받는 방식&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조:&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;프로세스 A &amp;rarr; 메시지 큐 &amp;rarr; 프로세스 B
&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;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 메시지 큐 생성&lt;/h1&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;#include &amp;lt;sys/ipc.h&amp;gt;
#include &amp;lt;sys/msg.h&amp;gt;

int msgid = msgget(IPC_PRIVATE, IPC_CREAT | 0666);
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 메시지 구조 정의&lt;/h1&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;struct msgbuf {
    long mtype;
    char mtext[100];
};
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 메시지 보내기&lt;/h1&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;struct msgbuf msg;
msg.mtype = 1;
strcpy(msg.mtext, &quot;Hello Queue&quot;);

msgsnd(msgid, &amp;amp;msg, sizeof(msg.mtext), 0);
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 메시지 받기&lt;/h1&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;msgrcv(msgid, &amp;amp;msg, sizeof(msg.mtext), 1, 0);

printf(&quot;Received: %s\n&quot;, msg.mtext);
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. 공유 메모리 vs 메시지 큐&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방식특징&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&gt;공유 메모리&lt;/td&gt;
&lt;td&gt;빠름, 동기화 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;메시지 큐&lt;/td&gt;
&lt;td&gt;안전함, 구조화됨&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;13. 언제 어떤 걸 써야 할까?&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대용량 데이터 &amp;rarr; 공유 메모리&lt;/li&gt;
&lt;li&gt;안정적 통신 &amp;rarr; 메시지 큐&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서는 &lt;b&gt;세마포어(semaphore)&lt;/b&gt; 와 함께 사용한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;14. 초보자가 자주 하는 실수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;공유 메모리 동기화 안함&lt;br /&gt;&amp;nbsp;shmctl로 제거 안함&lt;br /&gt;&amp;nbsp;메시지 타입(mtype) 무시&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;15. 23편 요약&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공유 메모리 &amp;rarr; 빠른 IPC&lt;/li&gt;
&lt;li&gt;메시지 큐 &amp;rarr; 안전한 IPC&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;리눅스 고급 IPC의 핵심이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메인 키워드&lt;/h2&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;메시지 큐 IPC&lt;/li&gt;
&lt;li&gt;Linux shared memory&lt;/li&gt;
&lt;li&gt;Linux message queue&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서브 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;shmget shmat 사용법&lt;/li&gt;
&lt;li&gt;msgsnd msgrcv example&lt;/li&gt;
&lt;li&gt;리눅스 IPC 종류&lt;/li&gt;
&lt;li&gt;시스템 프로그래밍 IPC&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Linux 완전 정복/Linux 시스템 프로그래밍</category>
      <category>IPC</category>
      <category>LinuxProgramming</category>
      <category>공유메모리</category>
      <category>메시지큐</category>
      <category>시스템프로그래밍</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/230</guid>
      <comments>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-23%ED%8E%B8-%EA%B3%B5%EC%9C%A0-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%A9%94%EC%8B%9C%EC%A7%80-%ED%81%90-%EA%B3%A0%EA%B8%89-IPC%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EA%B0%84-%ED%86%B5%EC%8B%A0-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4#entry230comment</comments>
      <pubDate>Mon, 23 Mar 2026 16:37:32 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스 프로그래밍 시리즈 22편] signal 처리 완전 이해: SIGINT, SIGTERM, SIGCHLD로 프로세스 제어하기</title>
      <link>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-22%ED%8E%B8-signal-%EC%B2%98%EB%A6%AC-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4-SIGINT-SIGTERM-SIGCHLD%EB%A1%9C-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EC%A0%9C%EC%96%B4%ED%95%98%EA%B8%B0</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;목표: 리눅스 시그널의 개념과 처리 방법을 이해한다&lt;br /&gt;&amp;nbsp;결과: 프로그램에서 인터럽트/종료/자식 종료를 안전하게 처리할 수 있다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 시그널(signal)이란?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시그널은 &lt;b&gt;프로세스에게 보내는 비동기 이벤트 알림&lt;/b&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;Ctrl + C &amp;rarr; SIGINT&lt;/li&gt;
&lt;li&gt;kill 명령 &amp;rarr; SIGTERM / SIGKILL&lt;/li&gt;
&lt;li&gt;자식 종료 &amp;rarr; SIGCHLD&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 OS가 프로세스에게 &quot;이런 일이 발생했다&quot;고 알려주는 방식이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 주요 시그널 종류&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시그널의미&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&gt;SIGINT&lt;/td&gt;
&lt;td&gt;인터럽트 (Ctrl+C)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SIGTERM&lt;/td&gt;
&lt;td&gt;정상 종료 요청&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SIGKILL&lt;/td&gt;
&lt;td&gt;강제 종료 (무시 불가)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SIGCHLD&lt;/td&gt;
&lt;td&gt;자식 프로세스 종료&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SIGSEGV&lt;/td&gt;
&lt;td&gt;잘못된 메모리 접근&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 기본 동작(Default Action)&lt;/h1&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;SIGINT &amp;rarr; 프로그램 종료&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-4141655932477481&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. signal 함수로 처리하기&lt;/h1&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;signal.h&amp;gt;

void handler(int sig) {
    printf(&quot;Signal received: %d\n&quot;, sig);
}

int main() {

    signal(SIGINT, handler);

    while(1);

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ctrl+C를 눌러도 종료되지 않고 handler가 실행된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. sigaction (권장 방식)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;signal보다 더 안정적인 방식.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;signal.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;

void handler(int sig) {
    printf(&quot;Caught signal %d\n&quot;, sig);
}

int main() {

    struct sigaction sa;
    sa.sa_handler = handler;
    sigemptyset(&amp;amp;sa.sa_mask);
    sa.sa_flags = 0;

    sigaction(SIGINT, &amp;amp;sa, NULL);

    while(1);

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. SIGCHLD 처리 (중요)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자식 프로세스 종료 시 발생.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 처리하지 않으면 &amp;rarr; 좀비 프로세스 발생&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;void handler(int sig) {
    wait(NULL);
}

int main() {

    signal(SIGCHLD, handler);

    fork();

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. kill 명령으로 시그널 보내기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널에서:&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;kill -SIGTERM PID
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;kill -9 PID
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-9 = SIGKILL&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. pause로 시그널 대기&lt;/h1&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;#include &amp;lt;unistd.h&amp;gt;

pause();
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시그널이 올 때까지 대기한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 안전한 시그널 처리 주의점&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시그널 핸들러에서는 다음을 주의해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;printf 남용&lt;br /&gt;&amp;nbsp;복잡한 로직&lt;br /&gt;&amp;nbsp;malloc/free 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;간단하게 처리해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 실무 활용 예&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버 종료 처리 (SIGTERM)&lt;/li&gt;
&lt;li&gt;Ctrl+C graceful shutdown&lt;/li&gt;
&lt;li&gt;자식 프로세스 관리 (SIGCHLD)&lt;/li&gt;
&lt;li&gt;타이머 이벤트&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 자주 하는 실수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;SIGKILL 잡으려 함 (불가능)&lt;br /&gt;&amp;nbsp;signal vs sigaction 차이 모름&lt;br /&gt;&amp;nbsp;wait 처리 안함&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. 22편 요약&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시그널은 &lt;b&gt;프로세스 이벤트 알림 시스템&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;signal / sigaction으로 처리 가능&lt;/li&gt;
&lt;li&gt;SIGCHLD로 좀비 방지&lt;/li&gt;
&lt;li&gt;kill로 시그널 전송 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스 프로세스 제어의 핵심 메커니즘이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메인 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리눅스 signal 처리&lt;/li&gt;
&lt;li&gt;SIGINT SIGTERM SIGCHLD&lt;/li&gt;
&lt;li&gt;Linux signal handler&lt;/li&gt;
&lt;li&gt;sigaction 사용법&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서브 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;signal vs sigaction&lt;/li&gt;
&lt;li&gt;리눅스 시그널 처리 방법&lt;/li&gt;
&lt;li&gt;Linux kill command signal&lt;/li&gt;
&lt;li&gt;프로세스 시그널 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Linux 완전 정복/Linux 시스템 프로그래밍</category>
      <category>LinuxProgramming</category>
      <category>SIGINT</category>
      <category>signal</category>
      <category>sigterm</category>
      <category>시스템프로그래밍</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/229</guid>
      <comments>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-22%ED%8E%B8-signal-%EC%B2%98%EB%A6%AC-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4-SIGINT-SIGTERM-SIGCHLD%EB%A1%9C-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EC%A0%9C%EC%96%B4%ED%95%98%EA%B8%B0#entry229comment</comments>
      <pubDate>Fri, 20 Mar 2026 15:04:47 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스 프로그래밍 시리즈 21편] exec 시스템 콜 완전 이해: 현재 프로세스를 다른 프로그램으로 바꾸는 방법</title>
      <link>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-21%ED%8E%B8-exec-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%BD%9C-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4-%ED%98%84%EC%9E%AC-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EB%A5%BC-%EB%8B%A4%EB%A5%B8-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8%EC%9C%BC%EB%A1%9C-%EB%B0%94%EA%BE%B8%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;목표: exec 계열 함수의 동작 원리를 이해한다&lt;br /&gt;&amp;nbsp;결과: fork와 exec를 조합해 새로운 프로그램을 실행할 수 있다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. exec는 무엇인가?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fork()가 &lt;b&gt;프로세스를 복제&lt;/b&gt;한다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;exec()는 &lt;b&gt;현재 프로세스의 내용을 다른 프로그램으로 교체&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fork()  &amp;rarr; 새 프로세스 생성
exec()  &amp;rarr; 프로세스 내용 교체
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;exec 호출 이후에는 &lt;b&gt;기존 코드로 돌아오지 않는다&lt;/b&gt;.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. exec 계열 함수 종류&lt;/h1&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;execl&lt;/li&gt;
&lt;li&gt;execlp&lt;/li&gt;
&lt;li&gt;execv&lt;/li&gt;
&lt;li&gt;execvp&lt;/li&gt;
&lt;li&gt;execve (기본)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 많이 쓰는 것은 &lt;b&gt;execvp&lt;/b&gt;다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. execvp 기본 사용법&lt;/h1&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;unistd.h&amp;gt;

int execvp(const char *file, char *const argv[]);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;char *args[] = {&quot;ls&quot;, &quot;-l&quot;, NULL};
execvp(&quot;ls&quot;, args);
&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;&quot;ls&quot; 프로그램 실행&lt;/li&gt;
&lt;li&gt;&quot;-l&quot; 옵션 전달&lt;/li&gt;
&lt;/ul&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-4141655932477481&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. exec 동작 특징&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;exec가 호출되면&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;PID는 그대로 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 &lt;b&gt;프로세스는 그대로, 내용만 바뀐다&lt;/b&gt;.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. fork + exec 구조 (핵심)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스에서 프로그램 실행의 표준 구조:&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fork()
 &amp;darr;
(자식)
exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부모는 유지되고&lt;br /&gt;자식이 새로운 프로그램을 실행한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. fork + exec 예제&lt;/h1&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;

int main() {

    pid_t pid = fork();

    if(pid == 0) {

        char *args[] = {&quot;ls&quot;, &quot;-l&quot;, NULL};
        execvp(&quot;ls&quot;, args);

        // exec 실패 시만 실행
        perror(&quot;exec failed&quot;);

    } else {
        printf(&quot;Parent Process\n&quot;);
    }

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. exec 실패 처리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;exec는 성공하면 &lt;b&gt;절대 돌아오지 않는다&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음 코드는 실패 시에만 실행된다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;execvp(&quot;ls&quot;, args);
perror(&quot;exec failed&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. PATH 환경변수와 execvp&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;execvp는 PATH를 자동으로 검색한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 다음이 가능하다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;execvp(&quot;ls&quot;, args);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 execv는 전체 경로 필요.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;execv(&quot;/bin/ls&quot;, args);
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 쉘이 동작하는 원리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉘은 내부적으로 다음 구조를 사용한다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;사용자 입력
 &amp;darr;
fork()
 &amp;darr;
exec()
 &amp;darr;
프로그램 실행
&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;fork + exec 구조로 실행된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. dup2 + exec 조합 (중요)&lt;/h1&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 class=&quot;lisp&quot;&gt;&lt;code&gt;int fd = open(&quot;out.txt&quot;, O_WRONLY | O_CREAT, 0644);
dup2(fd, 1);
execvp(&quot;ls&quot;, args);
&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;ls 출력이 파일로 저장된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 자주 하는 실수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;exec 이후 코드 실행된다고 착각&lt;br /&gt;&amp;nbsp;NULL 종료 안함&lt;br /&gt;&amp;nbsp;exec 실패 처리 안함&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. 21편 요약&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;exec는 &lt;b&gt;프로세스 내용을 교체&lt;/b&gt;하는 시스템 콜&lt;/li&gt;
&lt;li&gt;fork + exec 구조가 프로그램 실행의 핵심&lt;/li&gt;
&lt;li&gt;exec 성공 시 기존 코드로 돌아오지 않음&lt;/li&gt;
&lt;li&gt;execvp는 PATH 기반 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스 프로세스 실행 구조의 핵심이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메인 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;exec 시스템 콜&lt;/li&gt;
&lt;li&gt;fork exec 구조&lt;/li&gt;
&lt;li&gt;Linux execvp example&lt;/li&gt;
&lt;li&gt;리눅스 프로그램 실행 원리&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서브 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;execv execvp 차이&lt;/li&gt;
&lt;li&gt;리눅스 프로세스 실행&lt;/li&gt;
&lt;li&gt;fork exec 관계&lt;/li&gt;
&lt;li&gt;Linux process exec&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Linux 완전 정복/Linux 시스템 프로그래밍</category>
      <category>exec</category>
      <category>forkexec</category>
      <category>LinuxProgramming</category>
      <category>리눅스프로세스</category>
      <category>시스템프로그래밍</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/228</guid>
      <comments>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-21%ED%8E%B8-exec-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%BD%9C-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4-%ED%98%84%EC%9E%AC-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EB%A5%BC-%EB%8B%A4%EB%A5%B8-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8%EC%9C%BC%EB%A1%9C-%EB%B0%94%EA%BE%B8%EB%8A%94-%EB%B0%A9%EB%B2%95#entry228comment</comments>
      <pubDate>Thu, 19 Mar 2026 11:32:02 +0900</pubDate>
    </item>
    <item>
      <title>[WPF 실무 28편] WPF Binding 심화 (MultiBinding / PriorityBinding 완전 정리)</title>
      <link>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-28%ED%8E%B8-WPF-Binding-%EC%8B%AC%ED%99%94-MultiBinding-PriorityBinding-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF의 가장 강력한 기능 중 하나는 바로 &lt;b&gt;데이터 바인딩(Data Binding)&lt;/b&gt; 입니다.&lt;br /&gt;대부분의 UI 로직은 Binding으로 해결할 수 있지만,&lt;br /&gt;실무에서는 단순 Binding만으로 해결되지 않는 경우도 많습니다.&lt;/p&gt;
&lt;/blockquote&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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 때 사용하는 것이 바로 &lt;b&gt;MultiBinding&lt;/b&gt;과 &lt;b&gt;PriorityBinding&lt;/b&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;✔ MultiBinding 개념&lt;br /&gt;✔ IMultiValueConverter 사용법&lt;br /&gt;✔ PriorityBinding 동작 방식&lt;br /&gt;✔ 실무 활용 패턴&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 정리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. MultiBinding이란 무엇인가&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MultiBinding은 &lt;b&gt;여러 Binding 값을 하나의 결과로 합치는 방식&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;FirstName = &quot;John&quot;
LastName = &quot;Doe&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 값을 합쳐서&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;John Doe
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처럼 표시할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. MultiBinding 기본 사용 예&lt;/h1&gt;
&lt;pre class=&quot;dust&quot;&gt;&lt;code&gt;&amp;lt;TextBlock&amp;gt;
    &amp;lt;TextBlock.Text&amp;gt;
        &amp;lt;MultiBinding Converter=&quot;{StaticResource NameConverter}&quot;&amp;gt;
            &amp;lt;Binding Path=&quot;FirstName&quot;/&amp;gt;
            &amp;lt;Binding Path=&quot;LastName&quot;/&amp;gt;
        &amp;lt;/MultiBinding&amp;gt;
    &amp;lt;/TextBlock.Text&amp;gt;
&amp;lt;/TextBlock&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. IMultiValueConverter 구현&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MultiBinding은 반드시 &lt;b&gt;IMultiValueConverter&lt;/b&gt;를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public class NameConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType,
                          object parameter, CultureInfo culture)
    {
        string first = values[0]?.ToString();
        string last = values[1]?.ToString();

        return $&quot;{first} {last}&quot;;
    }

    public object[] ConvertBack(object value, Type[] targetTypes,
                                object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 여러 값을 배열로 전달받음&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-4141655932477481&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. MultiBinding 실무 활용 예&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 경우 자주 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 이름 + 성 결합&lt;br /&gt;✔ 값 조합 계산&lt;br /&gt;✔ 상태 메시지 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;온도: 23&amp;deg;C
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;Value + Unit
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. PriorityBinding이란 무엇인가&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PriorityBinding은 &lt;b&gt;여러 Binding 중 먼저 성공한 값을 사용하는 방식&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Binding1 실패 &amp;rarr; Binding2 사용
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. PriorityBinding 예제&lt;/h1&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;TextBlock&amp;gt;
    &amp;lt;TextBlock.Text&amp;gt;
        &amp;lt;PriorityBinding&amp;gt;
            &amp;lt;Binding Path=&quot;DisplayName&quot;/&amp;gt;
            &amp;lt;Binding Path=&quot;UserName&quot;/&amp;gt;
            &amp;lt;Binding Path=&quot;Id&quot;/&amp;gt;
        &amp;lt;/PriorityBinding&amp;gt;
    &amp;lt;/TextBlock.Text&amp;gt;
&amp;lt;/TextBlock&amp;gt;
&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;1️⃣ DisplayName 있으면 사용&lt;br /&gt;2️⃣ 없으면 UserName&lt;br /&gt;3️⃣ 그것도 없으면 Id&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. ❌ 실무에서 자주 하는 실수 1 &amp;mdash; MultiBinding Converter 누락&lt;/h1&gt;
&lt;pre class=&quot;apache&quot;&gt;&lt;code&gt;&amp;lt;MultiBinding&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Converter가 없으면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 값을 결합할 방법이 없습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. ❌ 실무에서 자주 하는 실수 2 &amp;mdash; 값 null 처리 안 함&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MultiBinding Converter에서&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;values[0]
&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;rarr; NullReferenceException 발생&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항상 null 체크 필요&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. MultiBinding vs PriorityBinding 차이&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구분MultiBindingPriorityBinding&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&gt;목적&lt;/td&gt;
&lt;td&gt;값 결합&lt;/td&gt;
&lt;td&gt;값 선택&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Converter 필요&lt;/td&gt;
&lt;td&gt;필요&lt;/td&gt;
&lt;td&gt;필요 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;사용 예&lt;/td&gt;
&lt;td&gt;이름 합치기&lt;/td&gt;
&lt;td&gt;대체 값 표시&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 한 줄 핵심 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MultiBinding은 &quot;값을 합치고&quot;&lt;br /&gt;PriorityBinding은 &quot;값을 선택한다&quot;.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF MultiBinding&lt;br /&gt;WPF PriorityBinding&lt;br /&gt;WPF IMultiValueConverter&lt;br /&gt;WPF 여러 값 바인딩&lt;br /&gt;WPF Binding 심화&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발이야기/WPF 데이터 바인딩</category>
      <category>csharp wpf</category>
      <category>WPF</category>
      <category>wpf binding</category>
      <category>WPF Converter</category>
      <category>wpf multibinding</category>
      <category>닷넷 개발</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/227</guid>
      <comments>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-28%ED%8E%B8-WPF-Binding-%EC%8B%AC%ED%99%94-MultiBinding-PriorityBinding-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC#entry227comment</comments>
      <pubDate>Tue, 17 Mar 2026 13:45:16 +0900</pubDate>
    </item>
    <item>
      <title>[WPF 실무 27편] WPF CustomControl vs UserControl 차이 (언제 무엇을 써야 할까?)</title>
      <link>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-27%ED%8E%B8-WPF-CustomControl-vs-UserControl-%EC%B0%A8%EC%9D%B4-%EC%96%B8%EC%A0%9C-%EB%AC%B4%EC%97%87%EC%9D%84-%EC%8D%A8%EC%95%BC-%ED%95%A0%EA%B9%8C</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF에서 UI 컴포넌트를 만들다 보면 다음 질문이 반드시 나옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;UserControl을 써야 할까? CustomControl을 만들어야 할까?&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 방식 모두 커스텀 UI를 만들 수 있지만 &lt;b&gt;목적과 사용 방식이 완전히 다릅니다.&lt;/b&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;✔ UserControl 구조&lt;br /&gt;✔ CustomControl 구조&lt;br /&gt;✔ 두 방식의 차이&lt;br /&gt;✔ 실무에서 언제 무엇을 선택해야 하는지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 정리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. UserControl이란 무엇인가&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UserControl은 &lt;b&gt;여러 컨트롤을 묶어서 재사용하는 UI 컴포넌트&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 UI를 하나의 컴포넌트로 만들 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;[ 아이콘 ]  사용자 이름
           상태 메시지
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예시&lt;/h3&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;UserControl&amp;gt;

    &amp;lt;Grid&amp;gt;
        &amp;lt;StackPanel&amp;gt;
            &amp;lt;TextBlock Text=&quot;User Name&quot; FontSize=&quot;16&quot;/&amp;gt;
            &amp;lt;TextBlock Text=&quot;Online&quot;/&amp;gt;
        &amp;lt;/StackPanel&amp;gt;
    &amp;lt;/Grid&amp;gt;

&amp;lt;/UserControl&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 여러 컨트롤을 묶어서 하나의 UI로 재사용&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. UserControl 특징&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 기존 컨트롤 조합&lt;br /&gt;✔ 구현이 매우 쉬움&lt;br /&gt;✔ 빠른 UI 제작&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;br /&gt;&amp;nbsp;템플릿 교체 어려움&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. CustomControl이란 무엇인가&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CustomControl은 &lt;b&gt;완전히 새로운 컨트롤을 만드는 방식&lt;/b&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;b&gt;ControlTemplate 기반으로 UI를 정의합니다.&lt;/b&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;커스텀 슬라이더&lt;/li&gt;
&lt;li&gt;특수 UI 컴포넌트&lt;/li&gt;
&lt;/ul&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-4141655932477481&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. CustomControl 구조&lt;/h1&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public class MyButton : Control
{
    static MyButton()
    {
        DefaultStyleKeyProperty.OverrideMetadata(
            typeof(MyButton),
            new FrameworkPropertyMetadata(typeof(MyButton)));
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 스타일은&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Themes/Generic.xaml
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에서 정의합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. CustomControl 특징&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 스타일 완전 변경 가능&lt;br /&gt;✔ ControlTemplate 지원&lt;br /&gt;✔ WPF 기본 컨트롤과 동일한 구조&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;br /&gt;&amp;nbsp;구조 이해 필요&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. UserControl vs CustomControl 비교&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구분UserControlCustomControl&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&gt;목적&lt;/td&gt;
&lt;td&gt;UI 조합&lt;/td&gt;
&lt;td&gt;새로운 컨트롤 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;구현 난이도&lt;/td&gt;
&lt;td&gt;쉬움&lt;/td&gt;
&lt;td&gt;어려움&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;스타일 변경&lt;/td&gt;
&lt;td&gt;제한적&lt;/td&gt;
&lt;td&gt;완전 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ControlTemplate&lt;/td&gt;
&lt;td&gt;제한&lt;/td&gt;
&lt;td&gt;완전 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 언제 UserControl을 써야 할까&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 경우에는 &lt;b&gt;UserControl이 가장 적합합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 화면 일부를 재사용할 때&lt;br /&gt;✔ 복잡한 UI 묶음&lt;br /&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;리스트 아이템 UI&lt;/li&gt;
&lt;li&gt;설정 패널&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 언제 CustomControl을 써야 할까&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 경우에는 &lt;b&gt;CustomControl을 사용해야 합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 완전히 새로운 컨트롤&lt;br /&gt;✔ 스타일 완전 커스터마이징&lt;br /&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;특수 그래프 컨트롤&lt;/li&gt;
&lt;li&gt;UI 프레임워크 컴포넌트&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9.&amp;nbsp; 실무에서 자주 하는 실수 1 &amp;mdash; 모든 것을 CustomControl로 만듦&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CustomControl은 강력하지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;대부분 UI는 &lt;b&gt;UserControl로 충분&lt;/b&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불필요하게 복잡한 구조가 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10.&amp;nbsp; 실무에서 자주 하는 실수 2 &amp;mdash; UserControl에 DependencyProperty 안 씀&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재사용 가능한 UserControl이라면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ DependencyProperty&lt;br /&gt;✔ Command&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 실무 추천 규칙&lt;/h1&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;UI 묶기 &amp;rarr; UserControl
새 컨트롤 제작 &amp;rarr; CustomControl
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 규칙이면 대부분 올바른 선택을 할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. 한 줄 핵심 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UserControl은 &quot;조합&quot;이고 CustomControl은 &quot;새로운 컨트롤&quot;이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF CustomControl&lt;br /&gt;WPF UserControl 차이&lt;br /&gt;WPF CustomControl 예제&lt;br /&gt;WPF UserControl 사용법&lt;br /&gt;WPF ControlTemplate 컨트롤&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발이야기/WPF 내부 구조</category>
      <category>csharp wpf</category>
      <category>WPF</category>
      <category>wpf controltemplate</category>
      <category>wpf customcontrol</category>
      <category>wpf usercontrol</category>
      <category>닷넷 개발</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/226</guid>
      <comments>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-27%ED%8E%B8-WPF-CustomControl-vs-UserControl-%EC%B0%A8%EC%9D%B4-%EC%96%B8%EC%A0%9C-%EB%AC%B4%EC%97%87%EC%9D%84-%EC%8D%A8%EC%95%BC-%ED%95%A0%EA%B9%8C#entry226comment</comments>
      <pubDate>Mon, 16 Mar 2026 10:37:06 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스 프로그래밍 시리즈 20편] wait / waitpid 완전 이해: 좀비 프로세스(Zombie Process)와 프로세스 종료 관리</title>
      <link>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-20%ED%8E%B8-wait-waitpid-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4-%EC%A2%80%EB%B9%84-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4Zombie-Process%EC%99%80-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EC%A2%85%EB%A3%8C-%EA%B4%80%EB%A6%AC</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;목표: fork로 생성된 자식 프로세스를 올바르게 종료 처리하는 방법을 이해한다&lt;br /&gt;&amp;nbsp;결과: 좀비 프로세스가 왜 발생하는지 알고 wait / waitpid로 해결할 수 있다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. fork 이후 반드시 필요한 작업&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;19편에서 fork()를 이용해 &lt;b&gt;자식 프로세스를 생성&lt;/b&gt;하는 방법을 배웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 fork만 사용하면 문제가 하나 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;좀비 프로세스(Zombie Process)&lt;/b&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;wait()&lt;/li&gt;
&lt;li&gt;waitpid()&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 좀비 프로세스란?&lt;/h1&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;종료 상태(exit status)&lt;/li&gt;
&lt;li&gt;CPU 사용 정보&lt;/li&gt;
&lt;li&gt;프로세스 ID&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정보를 &lt;b&gt;부모 프로세스가 회수(wait)&lt;/b&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;b&gt;좀비 프로세스&lt;/b&gt;가 된다.&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-4141655932477481&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 좀비 프로세스 구조&lt;/h1&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;Parent Process
      │
     fork
      │
   Child Process
      │
   exit()
      │
   Zombie
      │
   wait()
      │
   완전 제거
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 부모가 wait를 호출해야 프로세스가 완전히 정리된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. wait 시스템 콜&lt;/h1&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;sys/wait.h&amp;gt;

pid_t wait(int *status);
&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;종료 상태를 status에 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 기본 wait 예제&lt;/h1&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;sys/wait.h&amp;gt;

int main() {

    pid_t pid = fork();

    if(pid == 0) {
        printf(&quot;Child Process\n&quot;);
        return 0;
    }

    wait(NULL);

    printf(&quot;Parent Process End\n&quot;);

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동작:&lt;/p&gt;
&lt;pre class=&quot;powershell&quot;&gt;&lt;code&gt;Child Process
Parent Process End
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부모는 자식 종료 후 실행된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. waitpid 시스템 콜&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 자식 프로세스를 기다릴 때 사용한다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;pid_t waitpid(pid_t pid, int *status, int options);
&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;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&gt;pid&lt;/td&gt;
&lt;td&gt;기다릴 프로세스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;status&lt;/td&gt;
&lt;td&gt;종료 상태&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;options&lt;/td&gt;
&lt;td&gt;동작 옵션&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. waitpid 예제&lt;/h1&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;sys/wait.h&amp;gt;

int main() {

    pid_t pid = fork();

    if(pid == 0) {
        printf(&quot;Child Running\n&quot;);
        return 0;
    }

    waitpid(pid, NULL, 0);

    printf(&quot;Child Finished\n&quot;);

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 종료 상태 확인&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자식 프로세스의 종료 상태를 확인할 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;int status;

wait(&amp;amp;status);

if(WIFEXITED(status)) {
    printf(&quot;exit code: %d\n&quot;, WEXITSTATUS(status));
}
&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;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&gt;WIFEXITED&lt;/td&gt;
&lt;td&gt;정상 종료 여부&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WEXITSTATUS&lt;/td&gt;
&lt;td&gt;종료 코드&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 좀비 프로세스 확인 방법&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스에서 좀비 프로세스는 다음 명령으로 확인 가능하다.&lt;/p&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;ps aux
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력에서&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;Z
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태가 보이면 좀비 프로세스다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 서버 프로그램에서 중요한 이유&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 서버나 네트워크 서버는 다음 구조를 사용한다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;fork()
 &amp;darr;
client 처리
 &amp;darr;
exit()
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 wait 처리가 없으면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;서버에 &lt;b&gt;좀비 프로세스가 계속 쌓인다&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 서버 프로그램에서는 반드시&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;wait
waitpid
signal(SIGCHLD)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처리가 필요하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 초보자가 자주 하는 실수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;wait 호출 안함&lt;br /&gt;&amp;nbsp;fork 후 부모 종료&lt;br /&gt;&amp;nbsp;waitpid 사용법 혼동&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. 20편 요약&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;fork로 생성된 자식 프로세스는 반드시 정리해야 한다&lt;/li&gt;
&lt;li&gt;wait &amp;rarr; 자식 종료 대기&lt;/li&gt;
&lt;li&gt;waitpid &amp;rarr; 특정 자식 대기&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메인 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;wait 시스템 콜&lt;/li&gt;
&lt;li&gt;waitpid 사용법&lt;/li&gt;
&lt;li&gt;좀비 프로세스 zombie process&lt;/li&gt;
&lt;li&gt;리눅스 프로세스 종료 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서브 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;fork wait 관계&lt;/li&gt;
&lt;li&gt;Linux wait example&lt;/li&gt;
&lt;li&gt;zombie process 해결&lt;/li&gt;
&lt;li&gt;리눅스 프로세스 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Linux 완전 정복/Linux 시스템 프로그래밍</category>
      <category>LinuxProgramming</category>
      <category>Wait</category>
      <category>waitpid</category>
      <category>시스템프로그래밍</category>
      <category>좀비프로세스</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/225</guid>
      <comments>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-20%ED%8E%B8-wait-waitpid-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4-%EC%A2%80%EB%B9%84-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4Zombie-Process%EC%99%80-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EC%A2%85%EB%A3%8C-%EA%B4%80%EB%A6%AC#entry225comment</comments>
      <pubDate>Mon, 16 Mar 2026 10:25:14 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스 프로그래밍 시리즈 19편] fork 시스템 콜 완전 이해: 리눅스에서 새로운 프로세스가 만들어지는 원리</title>
      <link>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-19%ED%8E%B8-fork-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%BD%9C-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4-%EB%A6%AC%EB%88%85%EC%8A%A4%EC%97%90%EC%84%9C-%EC%83%88%EB%A1%9C%EC%9A%B4-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EA%B0%80-%EB%A7%8C%EB%93%A4%EC%96%B4%EC%A7%80%EB%8A%94-%EC%9B%90%EB%A6%AC</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;목표: fork 시스템 콜의 동작 원리를 이해한다&lt;br /&gt;&amp;nbsp;결과: 부모 프로세스와 자식 프로세스의 실행 흐름을 이해하고 활용할 수 있다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 리눅스에서 프로세스는 어떻게 생성될까?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스에서 새로운 프로그램이 실행될 때 가장 중요한 시스템 콜이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;fork()&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fork는 현재 실행 중인 프로세스를 &lt;b&gt;복사하여 새로운 프로세스(자식 프로세스)&lt;/b&gt; 를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조:&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;부모 프로세스
      │
     fork()
      │
 ┌─────────────┐
 │             │
부모          자식
프로세스      프로세스
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fork 이후에는 &lt;b&gt;두 개의 프로세스가 동시에 실행&lt;/b&gt;된다.&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-4141655932477481&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. fork 함수&lt;/h1&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;unistd.h&amp;gt;

pid_t fork(void);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fork는 호출 후 &lt;b&gt;두 번 반환&lt;/b&gt;된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반환값의미&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&gt;&amp;gt;0&lt;/td&gt;
&lt;td&gt;부모 프로세스 (자식 PID 반환)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;자식 프로세스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;오류&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 가장 기본적인 fork 예제&lt;/h1&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

int main() {

    fork();

    printf(&quot;Hello Fork\n&quot;);

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Hello Fork
Hello Fork
&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;fork 이후 &lt;b&gt;부모와 자식이 같은 코드를 실행&lt;/b&gt;하기 때문이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 부모와 자식 구분하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fork의 반환값을 이용해 구분한다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

int main() {

    pid_t pid = fork();

    if(pid == 0) {
        printf(&quot;Child Process\n&quot;);
    } else {
        printf(&quot;Parent Process\n&quot;);
    }

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력:&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;Parent Process
Child Process
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 프로세스 ID 확인&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스에서는 각 프로세스가 &lt;b&gt;PID(Process ID)&lt;/b&gt; 를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련 함수:&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;getpid()
getppid()
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제:&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

int main() {

    pid_t pid = fork();

    if(pid == 0) {
        printf(&quot;Child PID: %d\n&quot;, getpid());
        printf(&quot;Parent PID: %d\n&quot;, getppid());
    } else {
        printf(&quot;Parent PID: %d\n&quot;, getpid());
    }

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. fork 이후 메모리 구조&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fork는 부모 프로세스의 메모리를 그대로 복사한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;int x = 10;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fork 이후&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;부모 x = 10
자식 x = 10
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이후 변경은 서로 영향을 주지 않는다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;자식 x = 20
부모 x = 10
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 메모리는 &lt;b&gt;복사(copy)&lt;/b&gt; 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. fork + exec 구조&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 리눅스 프로그램 실행은 보통 이렇게 이루어진다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fork()
 &amp;darr;
exec()
&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;fork &amp;rarr; 새로운 프로세스 생성&lt;/li&gt;
&lt;li&gt;exec &amp;rarr; 새로운 프로그램 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉘이 프로그램을 실행할 때 사용하는 구조다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. fork가 많이 사용되는 곳&lt;/h1&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;li&gt;데몬 프로세스&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 &lt;b&gt;서버 프로그램&lt;/b&gt;에서 많이 사용된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. fork 사용 시 주의점&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fork는 프로세스를 복사하기 때문에 비용이 있다.&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;너무 많은 fork 호출&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 초보자가 자주 하는 실수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;fork 이후 코드 흐름 이해 못함&lt;br /&gt;&amp;nbsp;부모/자식 구분 안함&lt;br /&gt;&amp;nbsp;프로세스 종료 관리 안함&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 19편 요약&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;fork는 &lt;b&gt;프로세스를 복사하는 시스템 콜&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;부모와 자식 프로세스가 동시에 실행&lt;/li&gt;
&lt;li&gt;반환값으로 부모/자식 구분&lt;/li&gt;
&lt;li&gt;exec와 함께 프로그램 실행 구조 형성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스 프로세스 구조를 이해하는 핵심 시스템 콜이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메인 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;fork 시스템 콜&lt;/li&gt;
&lt;li&gt;리눅스 프로세스 생성&lt;/li&gt;
&lt;li&gt;Linux fork example&lt;/li&gt;
&lt;li&gt;fork 프로세스 구조&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서브 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;fork 부모 자식 프로세스&lt;/li&gt;
&lt;li&gt;fork exec 구조&lt;/li&gt;
&lt;li&gt;리눅스 프로세스 생성 원리&lt;/li&gt;
&lt;li&gt;Linux process fork&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Linux 완전 정복/Linux 시스템 프로그래밍</category>
      <category>fork</category>
      <category>IPC</category>
      <category>LinuxProgramming</category>
      <category>리눅스프로세스</category>
      <category>시스템프로그래밍</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/224</guid>
      <comments>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-19%ED%8E%B8-fork-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%BD%9C-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4-%EB%A6%AC%EB%88%85%EC%8A%A4%EC%97%90%EC%84%9C-%EC%83%88%EB%A1%9C%EC%9A%B4-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EA%B0%80-%EB%A7%8C%EB%93%A4%EC%96%B4%EC%A7%80%EB%8A%94-%EC%9B%90%EB%A6%AC#entry224comment</comments>
      <pubDate>Fri, 13 Mar 2026 10:28:00 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스 프로그래밍 시리즈 18편] pipe 시스템 콜: 리눅스 프로세스 간 통신(IPC)의 가장 기본 구조</title>
      <link>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-18%ED%8E%B8-pipe-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%BD%9C-%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EA%B0%84-%ED%86%B5%EC%8B%A0IPC%EC%9D%98-%EA%B0%80%EC%9E%A5-%EA%B8%B0%EB%B3%B8-%EA%B5%AC%EC%A1%B0</link>
      <description>&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;목표: pipe 시스템 콜을 이용한 프로세스 간 통신을 이해한다&lt;br /&gt;&amp;nbsp;결과: 한 프로세스의 출력이 다른 프로세스의 입력으로 전달되는 구조를 구현할 수 있다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 프로세스는 기본적으로 서로 독립적이다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스에서 실행되는 프로세스는 서로 &lt;b&gt;독립적인 메모리 공간&lt;/b&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;메모리 공유 불가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제 프로그램에서는 프로세스 간 &lt;b&gt;데이터 전달&lt;/b&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;쉘 파이프 (ls | grep txt)&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;b&gt;IPC (Inter Process Communication)&lt;/b&gt; 라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 가장 기본적인 IPC가 바로 &lt;b&gt;pipe&lt;/b&gt;다.&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-4141655932477481&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. pipe의 기본 개념&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pipe는 &lt;b&gt;한 방향 데이터 통로&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조:&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;프로세스 A  &amp;rarr;  pipe  &amp;rarr;  프로세스 B
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A는 데이터를 쓰고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B는 데이터를 읽는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. pipe 시스템 콜&lt;/h1&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;unistd.h&amp;gt;

int pipe(int fd[2]);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pipe를 생성하면 두 개의 파일 디스크립터가 생성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fd 번호의미&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&gt;fd[0]&lt;/td&gt;
&lt;td&gt;읽기(read)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fd[1]&lt;/td&gt;
&lt;td&gt;쓰기(write)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. pipe 동작 구조&lt;/h1&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;        write(fd[1])
프로세스 A ---------&amp;gt; pipe ---------&amp;gt; 프로세스 B
                       read(fd[0])
&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;fd[1] &amp;rarr; 데이터 입력&lt;/li&gt;
&lt;li&gt;fd[0] &amp;rarr; 데이터 읽기&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 기본 pipe 예제&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 예제는 pipe로 데이터를 전달하는 프로그램이다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

int main() {

    int fd[2];

    pipe(fd);

    write(fd[1], &quot;Hello Pipe&quot;, 10);

    char buf[20];

    read(fd[0], buf, 10);

    buf[10] = '\0';

    printf(&quot;pipe data: %s\n&quot;, buf);

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력:&lt;/p&gt;
&lt;pre class=&quot;haskell&quot;&gt;&lt;code&gt;pipe data: Hello Pipe
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. fork와 함께 사용하는 이유&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pipe는 보통 &lt;b&gt;fork()와 함께 사용&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조:&lt;/p&gt;
&lt;pre class=&quot;perl&quot;&gt;&lt;code&gt;부모 프로세스
      │
     fork
      │
 ┌─────────────┐
 │             │
부모          자식
write         read
&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. fork + pipe 예제&lt;/h1&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;

int main() {

    int fd[2];
    pipe(fd);

    pid_t pid = fork();

    if(pid &amp;gt; 0) {

        write(fd[1], &quot;Hello Child&quot;, 11);

    } else {

        char buf[20];

        read(fd[0], buf, 11);

        buf[11] = '\0';

        printf(&quot;Child received: %s\n&quot;, buf);

    }

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. pipe 사용 시 중요한 점&lt;/h1&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 class=&quot;applescript&quot;&gt;&lt;code&gt;부모 &amp;rarr; read 필요 없음
자식 &amp;rarr; write 필요 없음
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 보통 이렇게 작성한다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;close(fd[0]);
close(fd[1]);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요 없는 쪽을 닫는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 쉘 파이프 구조 이해&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 명령을 보자.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;ls | grep txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부적으로는 이렇게 동작한다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;ls stdout &amp;rarr; pipe &amp;rarr; grep stdin
&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;pipe()&lt;/li&gt;
&lt;li&gt;fork()&lt;/li&gt;
&lt;li&gt;dup2()&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 구현된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. pipe의 특징&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특징설명&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&gt;단방향&lt;/td&gt;
&lt;td&gt;한 방향 통신&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;커널 버퍼 사용&lt;/td&gt;
&lt;td&gt;데이터 임시 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;부모/자식 프로세스에서 많이 사용&lt;/td&gt;
&lt;td&gt;fork와 함께 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 초보자가 자주 하는 실수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;read / write 방향 혼동&lt;br /&gt;&amp;nbsp;필요 없는 fd 닫지 않음&lt;br /&gt;&amp;nbsp;fork 없이 pipe 사용&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. 18편 요약&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;pipe는 &lt;b&gt;프로세스 간 통신(IPC)&lt;/b&gt; 의 기본 구조&lt;/li&gt;
&lt;li&gt;fd[0] &amp;rarr; read&lt;/li&gt;
&lt;li&gt;fd[1] &amp;rarr; write&lt;/li&gt;
&lt;li&gt;fork와 함께 사용&lt;/li&gt;
&lt;li&gt;쉘 파이프(|)의 내부 구조&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pipe는 이후 배울 &lt;b&gt;프로세스 통신의 기초&lt;/b&gt;가 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메인 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리눅스 pipe 사용법&lt;/li&gt;
&lt;li&gt;Linux pipe system call&lt;/li&gt;
&lt;li&gt;프로세스 간 통신 IPC&lt;/li&gt;
&lt;li&gt;pipe fork example&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서브 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;pipe read write&lt;/li&gt;
&lt;li&gt;Linux IPC pipe&lt;/li&gt;
&lt;li&gt;리눅스 프로세스 통신&lt;/li&gt;
&lt;li&gt;시스템 프로그래밍 pipe&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Linux 완전 정복/Linux 시스템 프로그래밍</category>
      <category>IPC</category>
      <category>LinuxProgramming</category>
      <category>Pipe</category>
      <category>리눅스프로세스통신</category>
      <category>시스템프로그래밍</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/223</guid>
      <comments>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-18%ED%8E%B8-pipe-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%BD%9C-%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EA%B0%84-%ED%86%B5%EC%8B%A0IPC%EC%9D%98-%EA%B0%80%EC%9E%A5-%EA%B8%B0%EB%B3%B8-%EA%B5%AC%EC%A1%B0#entry223comment</comments>
      <pubDate>Fri, 13 Mar 2026 09:26:03 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스 프로그래밍 시리즈 17편] 파일 디스크립터와 리다이렉션: dup, dup2 완전 이해</title>
      <link>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-17%ED%8E%B8-%ED%8C%8C%EC%9D%BC-%EB%94%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%84%B0%EC%99%80-%EB%A6%AC%EB%8B%A4%EC%9D%B4%EB%A0%89%EC%85%98-dup-dup2-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4</link>
      <description>&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-4141655932477481&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;목표: 파일 디스크립터 복사와 입출력 리다이렉션 구조를 이해한다&lt;br /&gt;&amp;nbsp;결과: 프로그램의 출력(stdout)을 파일이나 다른 곳으로 변경할 수 있다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 파일 디스크립터 복습&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;15편에서 배운 것처럼 리눅스에서는 모든 입출력이 &lt;b&gt;파일 디스크립터(File Descriptor)&lt;/b&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;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&gt;0&lt;/td&gt;
&lt;td&gt;stdin (표준 입력)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;stdout (표준 출력)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;stderr (에러 출력)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 printf()는 사실상 &lt;b&gt;stdout(1)&lt;/b&gt; 으로 데이터를 보내는 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 리다이렉션이 가능한 이유&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널에서 다음 명령을 자주 사용한다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;ls &amp;gt; output.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 명령은&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;stdout &amp;rarr; 파일
&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;b&gt;파일 디스크립터 변경&lt;/b&gt; 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 작업을 프로그래밍에서 수행하는 함수가 &lt;b&gt;dup / dup2&lt;/b&gt; 다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. dup 함수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 디스크립터를 복사한다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;unistd.h&amp;gt;

int dup(int oldfd);
&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;기존 fd를 복사&lt;/li&gt;
&lt;li&gt;사용 가능한 &lt;b&gt;가장 작은 번호&lt;/b&gt;의 fd 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;old fd = 3
new fd = 4
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. dup2 함수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 번호의 파일 디스크립터로 복사한다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;int dup2(int oldfd, int newfd);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;dup2(fd, 1);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의미:&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;stdout &amp;rarr; fd 파일
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 &lt;b&gt;printf 출력이 파일로 이동한다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 출력 리다이렉션 예제&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 프로그램을 만들어 보자.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;

int main() {

    int fd = open(&quot;output.txt&quot;, O_WRONLY | O_CREAT | O_TRUNC, 0644);

    dup2(fd, 1);

    printf(&quot;Hello Linux Redirect!\n&quot;);

    close(fd);

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일:&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;gcc redirect.c -o redirect
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 후 output.txt 확인.&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;Hello Linux Redirect!
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널이 아니라 파일에 출력된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. stderr 리다이렉션&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;stderr도 동일하게 변경할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;dup2(fd, 2);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 &lt;b&gt;에러 메시지&lt;/b&gt;도 파일로 저장된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 파이프와 함께 사용되는 이유&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스 쉘에서 다음 명령을 보자.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;ls | grep txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조는 내부적으로 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;stdout(ls) &amp;rarr; pipe &amp;rarr; stdin(grep)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 연결도 결국 &lt;b&gt;파일 디스크립터 복사&lt;/b&gt;로 구현된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 dup/dup2는&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 파일 디스크립터 흐름&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 stdout을 파일로 변경하면 구조는 이렇게 된다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;printf()
  &amp;darr;
stdout (fd=1)
  &amp;darr;
output.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램은 그대로인데 &lt;b&gt;출력 대상만 바뀐다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 자주 하는 실수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;dup 후 기존 fd 닫지 않음&lt;br /&gt;&amp;nbsp;dup2 순서 혼동&lt;br /&gt;&amp;nbsp;stdout / stderr 구분 안함&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 17편 요약&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리눅스 입출력은 &lt;b&gt;파일 디스크립터 기반&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;dup &amp;rarr; fd 복사&lt;/li&gt;
&lt;li&gt;dup2 &amp;rarr; 특정 fd로 복사&lt;/li&gt;
&lt;li&gt;stdout / stderr 리다이렉션 가능&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메인 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dup dup2 사용법&lt;/li&gt;
&lt;li&gt;리눅스 리다이렉션 프로그래밍&lt;/li&gt;
&lt;li&gt;Linux file descriptor redirect&lt;/li&gt;
&lt;li&gt;stdout redirect C&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서브 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dup2 stdout redirect&lt;/li&gt;
&lt;li&gt;리눅스 파일 디스크립터 복사&lt;/li&gt;
&lt;li&gt;Linux dup2 example&lt;/li&gt;
&lt;li&gt;시스템 프로그래밍 redirect&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Linux 완전 정복/Linux 시스템 프로그래밍</category>
      <category>dup</category>
      <category>DUP2</category>
      <category>LinuxProgram</category>
      <category>리눅스리다이렉션</category>
      <category>파일디스크립터</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/222</guid>
      <comments>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-17%ED%8E%B8-%ED%8C%8C%EC%9D%BC-%EB%94%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%84%B0%EC%99%80-%EB%A6%AC%EB%8B%A4%EC%9D%B4%EB%A0%89%EC%85%98-dup-dup2-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4#entry222comment</comments>
      <pubDate>Thu, 12 Mar 2026 19:22:28 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스 프로그래밍 시리즈 16편] lseek 완전 이해: 파일 읽기 위치를 자유롭게 이동하는 방법</title>
      <link>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-16%ED%8E%B8-lseek-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4-%ED%8C%8C%EC%9D%BC-%EC%9D%BD%EA%B8%B0-%EC%9C%84%EC%B9%98%EB%A5%BC-%EC%9E%90%EC%9C%A0%EB%A1%AD%EA%B2%8C-%EC%9D%B4%EB%8F%99%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
      <description>&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-4141655932477481&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;목표: 파일 포인터 위치를 이동하는 방법을 이해한다&lt;br /&gt;&amp;nbsp;결과: 파일을 원하는 위치에서 읽거나 쓸 수 있는 프로그램을 만들 수 있다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 파일은 항상 '위치'가 있다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 글(15편)에서 우리는 다음 시스템 콜을 배웠다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;open()&lt;/li&gt;
&lt;li&gt;read()&lt;/li&gt;
&lt;li&gt;write()&lt;/li&gt;
&lt;li&gt;close()&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;b&gt;파일에는 현재 읽기/쓰기 위치가 존재한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;Hello Linux
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 read()를 실행하면&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;H e l l o
&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;다음 read()는&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;(이어서) Linux
&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;b&gt;현재 위치(offset)&lt;/b&gt; 가 존재한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. lseek란 무엇인가?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lseek()은 파일의 현재 위치를 이동시키는 시스템 콜이다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;unistd.h&amp;gt;

off_t lseek(int fd, off_t offset, int whence);
&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;fd &amp;rarr; 파일 디스크립터&lt;/li&gt;
&lt;li&gt;offset &amp;rarr; 이동할 위치&lt;/li&gt;
&lt;li&gt;whence &amp;rarr; 기준 위치&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. whence 옵션&lt;/h1&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;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&gt;SEEK_SET&lt;/td&gt;
&lt;td&gt;파일 시작 기준&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SEEK_CUR&lt;/td&gt;
&lt;td&gt;현재 위치 기준&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SEEK_END&lt;/td&gt;
&lt;td&gt;파일 끝 기준&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 파일 처음으로 이동&lt;/h1&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;lseek(fd, 0, SEEK_SET);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 포인터가 &lt;b&gt;파일 시작 위치&lt;/b&gt;로 이동한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 현재 위치에서 이동&lt;/h1&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;lseek(fd, 10, SEEK_CUR);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 위치에서 &lt;b&gt;10바이트 앞으로 이동&lt;/b&gt;.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 파일 끝으로 이동&lt;/h1&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;lseek(fd, 0, SEEK_END);
&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 전체 예제 코드&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일을 읽고 다시 처음부터 읽는 예제.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;

int main() {

    int fd = open(&quot;test.txt&quot;, O_RDONLY);

    char buf[6];

    read(fd, buf, 5);
    buf[5] = '\0';

    printf(&quot;첫 읽기: %s\n&quot;, buf);

    lseek(fd, 0, SEEK_SET);

    read(fd, buf, 5);
    buf[5] = '\0';

    printf(&quot;두번째 읽기: %s\n&quot;, buf);

    close(fd);

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;gcc seek_example.c -o seek_example
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 파일 크기 구하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lseek는 파일 크기를 확인하는 데도 사용된다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;off_t size = lseek(fd, 0, SEEK_END);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 값이 &lt;b&gt;파일 전체 크기&lt;/b&gt;가 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. Sparse File (희소 파일)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lseek는 특별한 파일도 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;lseek(fd, 1000, SEEK_SET);
write(fd, &quot;A&quot;, 1);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 &lt;b&gt;중간이 비어있는 파일&lt;/b&gt;이 생성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것을 &lt;b&gt;Sparse File&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대용량 파일 처리에서 사용된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 자주 하는 실수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;lseek 반환값 확인 안함&lt;br /&gt;&amp;nbsp;SEEK_SET / SEEK_CUR 혼동&lt;br /&gt;&amp;nbsp;read 이후 위치 변경 고려 안함&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 16편 요약&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파일에는 항상 현재 위치(offset)가 존재&lt;/li&gt;
&lt;li&gt;lseek로 위치 이동 가능&lt;/li&gt;
&lt;li&gt;SEEK_SET / SEEK_CUR / SEEK_END 이해 중요&lt;/li&gt;
&lt;li&gt;파일 크기 확인에도 사용 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 I/O 프로그래밍에서 매우 중요한 시스템 콜이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메인 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리눅스 lseek 사용법&lt;/li&gt;
&lt;li&gt;파일 포인터 이동&lt;/li&gt;
&lt;li&gt;Linux lseek example&lt;/li&gt;
&lt;li&gt;파일 offset 개념&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서브 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SEEK_SET SEEK_CUR SEEK_END&lt;/li&gt;
&lt;li&gt;리눅스 파일 위치 이동&lt;/li&gt;
&lt;li&gt;Linux file offset&lt;/li&gt;
&lt;li&gt;시스템 프로그래밍 lseek&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Linux 완전 정복/Linux 시스템 프로그래밍</category>
      <category>LinuxIO</category>
      <category>LinuxProgramming</category>
      <category>Seek</category>
      <category>리눅스시스템프로그래밍</category>
      <category>파일포인터</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/221</guid>
      <comments>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-16%ED%8E%B8-lseek-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4-%ED%8C%8C%EC%9D%BC-%EC%9D%BD%EA%B8%B0-%EC%9C%84%EC%B9%98%EB%A5%BC-%EC%9E%90%EC%9C%A0%EB%A1%AD%EA%B2%8C-%EC%9D%B4%EB%8F%99%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95#entry221comment</comments>
      <pubDate>Thu, 12 Mar 2026 18:38:10 +0900</pubDate>
    </item>
    <item>
      <title>[WPF 실무 26편] WPF AttachedProperty 완전 이해 (Grid.Row 같은 속성은 어떻게 만들어질까?)</title>
      <link>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-26%ED%8E%B8-WPF-AttachedProperty-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4-GridRow-%EA%B0%99%EC%9D%80-%EC%86%8D%EC%84%B1%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%A7%8C%EB%93%A4%EC%96%B4%EC%A7%88%EA%B9%8C</link>
      <description>&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-4141655932477481&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF를 사용하다 보면 다음과 같은 속성을 자주 보게 됩니다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;&amp;lt;Button Grid.Row=&quot;1&quot; Grid.Column=&quot;2&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Button에는 Row나 Column 속성이 없는데도 사용이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 바로 &lt;b&gt;AttachedProperty&lt;/b&gt; 때문입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ AttachedProperty 개념&lt;br /&gt;✔ DependencyProperty와 차이&lt;br /&gt;✔ AttachedProperty 생성 방법&lt;br /&gt;✔ 실무 활용 사례&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 정리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. AttachedProperty란 무엇인가&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AttachedProperty는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;다른 컨트롤에 속성을 &quot;붙여서&quot; 사용하는 구조&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Grid.Row
Grid.Column
Canvas.Left
Canvas.Top
DockPanel.Dock
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 속성들은 실제로는 &lt;b&gt;Grid / Canvas / DockPanel&lt;/b&gt;이 정의한 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 다른 컨트롤(Button, TextBox 등)에 붙여서 사용할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 왜 AttachedProperty가 필요한가&lt;/h1&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 class=&quot;mathematica&quot;&gt;&lt;code&gt;Grid
 ├ Button
 ├ TextBox
 └ Label
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Grid는 각 자식이 &lt;b&gt;어느 Row / Column에 위치하는지&lt;/b&gt; 알아야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Button 자체 속성이 아니라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Grid가 속성을 &quot;붙이는&quot; 구조를 사용합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. DependencyProperty와 차이&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구분DependencyPropertyAttachedProperty&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&gt;위치&lt;/td&gt;
&lt;td&gt;자기 자신 컨트롤&lt;/td&gt;
&lt;td&gt;다른 컨트롤에 적용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;사용 예&lt;/td&gt;
&lt;td&gt;Button.Background&lt;/td&gt;
&lt;td&gt;Grid.Row&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;목적&lt;/td&gt;
&lt;td&gt;속성 확장&lt;/td&gt;
&lt;td&gt;레이아웃/행동 제어&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;AttachedProperty = 확장 속성&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. AttachedProperty 선언 방법&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예: Highlight 속성 만들기&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public class HighlightHelper
{
    public static readonly DependencyProperty IsHighlightProperty =
        DependencyProperty.RegisterAttached(
            &quot;IsHighlight&quot;,
            typeof(bool),
            typeof(HighlightHelper),
            new PropertyMetadata(false));

    public static void SetIsHighlight(UIElement element, bool value)
    {
        element.SetValue(IsHighlightProperty, value);
    }

    public static bool GetIsHighlight(UIElement element)
    {
        return (bool)element.GetValue(IsHighlightProperty);
    }
}
&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;✔ RegisterAttached 사용&lt;br /&gt;✔ Get / Set 메서드 필요&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. XAML에서 사용&lt;/h1&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;&amp;lt;Button Content=&quot;Test&quot;
        local:HighlightHelper.IsHighlight=&quot;True&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 다른 컨트롤에 속성 추가 가능&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. PropertyChanged 활용&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AttachedProperty는 값 변경 시 로직을 실행할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;new PropertyMetadata(false, OnHighlightChanged);
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;private static void OnHighlightChanged(
    DependencyObject d,
    DependencyPropertyChangedEventArgs e)
{
    var element = d as UIElement;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 실무 활용 예&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AttachedProperty는 다음 상황에서 자주 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ Behavior 구현&lt;br /&gt;✔ 공통 UI 기능 추가&lt;br /&gt;✔ Validation 표시&lt;br /&gt;✔ Drag &amp;amp; Drop 기능 확장&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 기존 컨트롤을 수정하지 않고 기능을 확장할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8.&amp;nbsp; 실무에서 자주 하는 실수 1 &amp;mdash; Get/Set 메서드 생략&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AttachedProperty는 반드시&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;GetProperty()
SetProperty()
&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;없으면 XAML에서 사용할 수 없습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9.&amp;nbsp; 실무에서 자주 하는 실수 2 &amp;mdash; Register 사용&lt;/h1&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;DependencyProperty.Register()
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 일반 DependencyProperty가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AttachedProperty는 반드시&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;RegisterAttached()
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용해야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 한 줄 핵심 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AttachedProperty는 컨트롤에 기능을 &quot;붙여서&quot; 확장하는 구조다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF AttachedProperty&lt;br /&gt;WPF Grid.Row 구조&lt;br /&gt;WPF RegisterAttached 예제&lt;br /&gt;WPF Attached Property 사용법&lt;br /&gt;WPF DependencyProperty 확장&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발이야기/WPF 내부 구조</category>
      <category>csharp wpf</category>
      <category>WPF</category>
      <category>wpf attachedproperty</category>
      <category>wpf dependencyproperty</category>
      <category>wpf property</category>
      <category>닷넷 개발</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/220</guid>
      <comments>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-26%ED%8E%B8-WPF-AttachedProperty-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4-GridRow-%EA%B0%99%EC%9D%80-%EC%86%8D%EC%84%B1%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%A7%8C%EB%93%A4%EC%96%B4%EC%A7%88%EA%B9%8C#entry220comment</comments>
      <pubDate>Thu, 12 Mar 2026 18:34:24 +0900</pubDate>
    </item>
    <item>
      <title>[WPF 실무 25편] WPF DependencyProperty 완전 이해 (커스텀 컨트롤 핵심 구조)</title>
      <link>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-25%ED%8E%B8-WPF-DependencyProperty-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4-%EC%BB%A4%EC%8A%A4%ED%85%80-%EC%BB%A8%ED%8A%B8%EB%A1%A4-%ED%95%B5%EC%8B%AC-%EA%B5%AC%EC%A1%B0</link>
      <description>&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-4141655932477481&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF를 오래 사용하다 보면 반드시 만나게 되는 개념이 바로 &lt;b&gt;DependencyProperty&lt;/b&gt;입니다.&lt;br /&gt;일반 C# Property와 비슷해 보이지만 내부 동작 방식은 완전히 다릅니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF의 &lt;b&gt;데이터 바인딩, 스타일, 애니메이션, 트리거&lt;/b&gt; 대부분이 DependencyProperty 기반으로 동작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ DependencyProperty 개념&lt;br /&gt;✔ 일반 Property와 차이&lt;br /&gt;✔ DependencyProperty 생성 방법&lt;br /&gt;✔ 실무에서 사용하는 패턴&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 정리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. DependencyProperty란 무엇인가&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DependencyProperty는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;WPF Property System에 등록되는 속성&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 C# 속성&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;class User
{
    public string Name { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF 속성&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;TextBox.Text
Button.Background
Grid.Row
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 속성들은 대부분 &lt;b&gt;DependencyProperty&lt;/b&gt;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 일반 Property와 차이&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구분일반 PropertyDependencyProperty&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&gt;데이터 바인딩&lt;/td&gt;
&lt;td&gt;제한적&lt;/td&gt;
&lt;td&gt;완전 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;스타일 적용&lt;/td&gt;
&lt;td&gt;불가능&lt;/td&gt;
&lt;td&gt;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;애니메이션&lt;/td&gt;
&lt;td&gt;불가능&lt;/td&gt;
&lt;td&gt;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;값 우선순위&lt;/td&gt;
&lt;td&gt;없음&lt;/td&gt;
&lt;td&gt;존재&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 WPF UI 속성 대부분은 DependencyProperty입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. DependencyProperty가 필요한 이유&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF는 다음 기능을 위해 Property System을 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 데이터 바인딩&lt;br /&gt;✔ 스타일&lt;br /&gt;✔ 트리거&lt;br /&gt;✔ 애니메이션&lt;br /&gt;✔ 값 상속&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 기능을 일반 Property로 구현하기 어렵기 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;DependencyProperty 구조를 사용합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. DependencyProperty 선언 방법&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예: CustomControl에 Title 속성 추가&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public class MyControl : Control
{
    public static readonly DependencyProperty TitleProperty =
        DependencyProperty.Register(
            &quot;Title&quot;,
            typeof(string),
            typeof(MyControl),
            new PropertyMetadata(&quot;&quot;));

    public string Title
    {
        get { return (string)GetValue(TitleProperty); }
        set { SetValue(TitleProperty, value); }
    }
}
&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;✔ PropertyKey&lt;br /&gt;✔ GetValue / SetValue&lt;br /&gt;✔ PropertyMetadata&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. XAML에서 사용&lt;/h1&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;&amp;lt;local:MyControl Title=&quot;Hello&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 바인딩&lt;/p&gt;
&lt;pre class=&quot;dust&quot;&gt;&lt;code&gt;&amp;lt;local:MyControl Title=&quot;{Binding UserName}&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. PropertyMetadata&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DependencyProperty는 메타데이터를 가질 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;new PropertyMetadata(
    &quot;Default&quot;,
    OnTitleChanged);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값 변경 시 호출&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;private static void OnTitleChanged(
    DependencyObject d,
    DependencyPropertyChangedEventArgs e)
{
    var control = (MyControl)d;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7.&amp;nbsp; 실무에서 자주 하는 실수 1 &amp;mdash; 일반 Property 사용&lt;/h1&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public string Title { get; set; }
&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;br /&gt;✔ 스타일 적용 안 됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CustomControl에서는 반드시 DependencyProperty 사용해야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8.&amp;nbsp; 실무에서 자주 하는 실수 2 &amp;mdash; static 누락&lt;/h1&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public readonly DependencyProperty TitleProperty
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DependencyProperty는 반드시&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ static&lt;br /&gt;✔ readonly&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 값 우선순위 구조&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DependencyProperty 값은 다음 순서로 결정됩니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Animation
Local Value
Style
Template
Default
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 애니메이션이 항상 가장 우선합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 실무 사용 예&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DependencyProperty는 주로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ CustomControl&lt;br /&gt;✔ UserControl&lt;br /&gt;✔ AttachedProperty&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에서 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 커스텀 UI 컴포넌트 만들 때 필수입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 한 줄 핵심 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DependencyProperty는 WPF Property System의 핵심이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF DependencyProperty&lt;br /&gt;WPF DependencyProperty 예제&lt;br /&gt;WPF Property System&lt;br /&gt;WPF Custom Control Property&lt;br /&gt;WPF DependencyProperty Register&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발이야기/WPF 내부 구조</category>
      <category>csharp wpf</category>
      <category>WPF</category>
      <category>wpf customcontrol</category>
      <category>wpf dependencyproperty</category>
      <category>wpf property</category>
      <category>닷넷 개발</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/218</guid>
      <comments>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-25%ED%8E%B8-WPF-DependencyProperty-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4-%EC%BB%A4%EC%8A%A4%ED%85%80-%EC%BB%A8%ED%8A%B8%EB%A1%A4-%ED%95%B5%EC%8B%AC-%EA%B5%AC%EC%A1%B0#entry218comment</comments>
      <pubDate>Wed, 11 Mar 2026 17:44:50 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스 프로그래밍 시리즈 15편] 리눅스 파일 I/O 프로그래밍: open, read, write 시스템 콜 완전 이해</title>
      <link>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-15%ED%8E%B8-%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%8C%8C%EC%9D%BC-IO-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-open-read-write-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%BD%9C-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4</link>
      <description>&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-4141655932477481&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;목표: 리눅스에서 파일을 직접 읽고 쓰는 &lt;b&gt;시스템 프로그래밍 방식&lt;/b&gt;을 이해한다&lt;br /&gt;&amp;nbsp;결과: C 프로그램에서 파일을 열고 데이터를 읽고 쓰는 코드를 작성할 수 있다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 이제부터 '진짜' 리눅스 프로그래밍&lt;/h1&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;하지만 리눅스 시스템 프로그래밍에서는 &lt;b&gt;운영체제와 직접 상호작용&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 방법이 바로 &lt;b&gt;시스템 콜(System Call)&lt;/b&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;파일 열기 &amp;rarr; open()&lt;/li&gt;
&lt;li&gt;파일 읽기 &amp;rarr; read()&lt;/li&gt;
&lt;li&gt;파일 쓰기 &amp;rarr; write()&lt;/li&gt;
&lt;li&gt;파일 닫기 &amp;rarr; close()&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 함수들은 리눅스 커널과 직접 연결된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 파일 I/O의 기본 흐름&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스 파일 작업은 항상 다음 순서를 따른다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;open()
 &amp;darr;
read() / write()
 &amp;darr;
close()
&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;1) 파일 열기&lt;br /&gt;2) 데이터 읽기/쓰기&lt;br /&gt;3) 파일 닫기&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. open() 함수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일을 열 때 사용하는 시스템 콜.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;fcntl.h&amp;gt;

int open(const char *pathname, int flags);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;int fd = open(&quot;test.txt&quot;, O_RDONLY);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 fd는 &lt;b&gt;파일 디스크립터(File Descriptor)&lt;/b&gt; 다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 파일 디스크립터란?&lt;/h1&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;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&gt;0&lt;/td&gt;
&lt;td&gt;stdin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;stdout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;stderr&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 파일을 열면 보통 &lt;b&gt;3번부터&lt;/b&gt; 할당된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. read() 함수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일에서 데이터를 읽는다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;#include &amp;lt;unistd.h&amp;gt;

ssize_t read(int fd, void *buf, size_t count);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;char buffer[100];
read(fd, buffer, 100);
&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;fd &amp;rarr; 파일 디스크립터&lt;/li&gt;
&lt;li&gt;buffer &amp;rarr; 데이터를 저장할 공간&lt;/li&gt;
&lt;li&gt;100 &amp;rarr; 읽을 바이트 수&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. write() 함수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일에 데이터를 쓴다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;ssize_t write(int fd, const void *buf, size_t count);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;write(fd, &quot;Hello Linux\n&quot;, 12);
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. close() 함수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 작업이 끝나면 반드시 닫아야 한다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;close(fd);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일을 닫지 않으면 &lt;b&gt;파일 디스크립터 누수&lt;/b&gt;가 발생할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 전체 예제 코드&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일에 문자열을 쓰는 프로그램.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

int main() {

    int fd = open(&quot;test.txt&quot;, O_WRONLY | O_CREAT, 0644);

    write(fd, &quot;Hello Linux!\n&quot;, 13);

    close(fd);

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;gcc file_write.c -o file_write
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 후 test.txt 파일이 생성된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. open 옵션 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵션의미&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&gt;O_RDONLY&lt;/td&gt;
&lt;td&gt;읽기 전용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;O_WRONLY&lt;/td&gt;
&lt;td&gt;쓰기 전용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;O_RDWR&lt;/td&gt;
&lt;td&gt;읽기/쓰기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;O_CREAT&lt;/td&gt;
&lt;td&gt;파일 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;O_APPEND&lt;/td&gt;
&lt;td&gt;파일 끝에 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;open(&quot;log.txt&quot;, O_WRONLY | O_CREAT | O_APPEND, 0644);
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 파일 권한 (mode)&lt;/h1&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 class=&quot;angelscript&quot;&gt;&lt;code&gt;0644
&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;owner &amp;rarr; read/write&lt;/li&gt;
&lt;li&gt;group &amp;rarr; read&lt;/li&gt;
&lt;li&gt;others &amp;rarr; read&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 초보자가 자주 하는 실수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;open 실패 체크 안함&lt;br /&gt;&amp;nbsp;close 호출 안함&lt;br /&gt;&amp;nbsp;read 반환값 확인 안함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서는 반드시 &lt;b&gt;에러 체크&lt;/b&gt;를 해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. 15편 요약&lt;/h1&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;open &amp;rarr; read/write &amp;rarr; close 구조&lt;/li&gt;
&lt;li&gt;파일 디스크립터 개념 중요&lt;/li&gt;
&lt;li&gt;O_CREAT, O_APPEND 옵션 자주 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 리눅스 시스템 프로그래밍의 핵심이 시작된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리눅스 파일 I/O&lt;/li&gt;
&lt;li&gt;open read write 사용법&lt;/li&gt;
&lt;li&gt;리눅스 시스템 콜&lt;/li&gt;
&lt;li&gt;Linux file descriptor&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서브 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C open 함수&lt;/li&gt;
&lt;li&gt;Linux read write example&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>Linux 완전 정복/Linux 시스템 프로그래밍</category>
      <category>c프로그래밍</category>
      <category>LinuxIO</category>
      <category>LinuxProgramming</category>
      <category>리눅스시스템프로그래밍</category>
      <category>파일디스크립터</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/217</guid>
      <comments>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-15%ED%8E%B8-%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%8C%8C%EC%9D%BC-IO-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-open-read-write-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%BD%9C-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4#entry217comment</comments>
      <pubDate>Wed, 11 Mar 2026 15:14:21 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스 프로그래밍 시리즈 14편] Bash 쉘 스크립트 실전 자동화: 로그 분석과 서버 상태 체크 스크립트 만들기</title>
      <link>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-14%ED%8E%B8-Bash-%EC%89%98-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%8B%A4%EC%A0%84-%EC%9E%90%EB%8F%99%ED%99%94-%EB%A1%9C%EA%B7%B8-%EB%B6%84%EC%84%9D%EA%B3%BC-%EC%84%9C%EB%B2%84-%EC%83%81%ED%83%9C-%EC%B2%B4%ED%81%AC-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
      <description>&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-4141655932477481&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;목표: 실무에서 바로 사용할 수 있는 자동화 스크립트를 만든다&lt;br /&gt;&amp;nbsp;결과: 로그 분석, 서버 상태 확인을 자동으로 수행하는 Bash 스크립트를 작성할 수 있다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 왜 쉘 스크립트 자동화가 중요한가?&lt;/h1&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;로그에서 ERROR 개수 확인&lt;/li&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;그래서 실무에서는 &lt;b&gt;쉘 스크립트로 자동화&lt;/b&gt;한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 로그 분석 자동화 스크립트&lt;/h1&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 class=&quot;css&quot;&gt;&lt;code&gt;nano log_check.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용 작성:&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;#!/bin/bash

LOGFILE=&quot;server.log&quot;

ERROR_COUNT=$(grep &quot;ERROR&quot; $LOGFILE | wc -l)

 echo &quot;총 ERROR 개수: $ERROR_COUNT&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 권한 부여:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;chmod +x log_check.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행:&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;./log_check.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 예:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;총 ERROR 개수: 5
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 디스크 사용량 체크 스크립트&lt;/h1&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 class=&quot;css&quot;&gt;&lt;code&gt;nano disk_check.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용:&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;#!/bin/bash

 echo &quot;디스크 사용량 확인&quot;
 df -h
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행:&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;chmod +x disk_check.sh
./disk_check.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 특정 프로세스 실행 여부 확인&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에서 특정 서비스가 실행 중인지 확인하는 스크립트.&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;#!/bin/bash

PROCESS=&quot;nginx&quot;

if pgrep $PROCESS &amp;gt; /dev/null
then
    echo &quot;$PROCESS 실행 중&quot;
else
    echo &quot;$PROCESS 실행 안됨&quot;
fi
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pgrep은 프로세스 존재 여부를 확인하는 명령어다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 여러 작업을 하나로 묶기&lt;/h1&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 class=&quot;bash&quot;&gt;&lt;code&gt;#!/bin/bash

 echo &quot;===== 시스템 체크 =====&quot;

 echo &quot;현재 사용자:&quot;
 whoami

 echo &quot;디스크 사용량&quot;
 df -h

 echo &quot;메모리 상태&quot;
 free -h

 echo &quot;현재 실행 프로세스&quot;
 ps aux | head
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 서버 상태를 빠르게 확인할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. cron으로 자동 실행&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉘 스크립트는 &lt;b&gt;cron&lt;/b&gt;을 이용해 자동 실행할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;crontab -e
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시 설정:&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;*/5 * * * * /home/user/log_check.sh
&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;5분마다 스크립트 실행&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 실무에서 자주 만드는 스크립트&lt;/h1&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;li&gt;배포 스크립트&lt;/li&gt;
&lt;li&gt;빌드 자동화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉘 스크립트는 DevOps 환경에서 매우 중요한 기술이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 초보자가 자주 하는 실수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;실행 권한 없이 실행&lt;br /&gt;&amp;nbsp;경로를 절대경로로 작성하지 않음&lt;br /&gt;&amp;nbsp;로그 파일 위치 오류&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 14편 요약&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Bash 스크립트로 반복 작업 자동화 가능&lt;/li&gt;
&lt;li&gt;로그 분석, 서버 상태 체크에 많이 사용&lt;/li&gt;
&lt;li&gt;여러 명령어를 하나의 스크립트로 묶을 수 있음&lt;/li&gt;
&lt;li&gt;cron과 함께 사용하면 자동 운영 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉘 스크립트는 리눅스 자동화의 핵심 도구다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메인 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;bash 자동화 스크립트&lt;/li&gt;
&lt;li&gt;리눅스 쉘 자동화&lt;/li&gt;
&lt;li&gt;bash cron 자동 실행&lt;/li&gt;
&lt;li&gt;리눅스 서버 체크 스크립트&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서브 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;bash 로그 분석 스크립트&lt;/li&gt;
&lt;li&gt;linux cron 사용법&lt;/li&gt;
&lt;li&gt;bash 서버 모니터링&lt;/li&gt;
&lt;li&gt;리눅스 자동화 스크립트&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Linux 완전 정복/Linux  개발도구</category>
      <category>Bash</category>
      <category>cron</category>
      <category>LinuxProgramming</category>
      <category>리눅스자동화</category>
      <category>쉘스크립트</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/216</guid>
      <comments>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-14%ED%8E%B8-Bash-%EC%89%98-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%8B%A4%EC%A0%84-%EC%9E%90%EB%8F%99%ED%99%94-%EB%A1%9C%EA%B7%B8-%EB%B6%84%EC%84%9D%EA%B3%BC-%EC%84%9C%EB%B2%84-%EC%83%81%ED%83%9C-%EC%B2%B4%ED%81%AC-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0#entry216comment</comments>
      <pubDate>Wed, 11 Mar 2026 15:04:03 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스 프로그래밍 시리즈 13편] Bash 쉘 스크립트 입문: 리눅스 작업을 자동화하는 첫 번째 단계</title>
      <link>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-13%ED%8E%B8-Bash-%EC%89%98-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9E%85%EB%AC%B8-%EB%A6%AC%EB%88%85%EC%8A%A4-%EC%9E%91%EC%97%85%EC%9D%84-%EC%9E%90%EB%8F%99%ED%99%94%ED%95%98%EB%8A%94-%EC%B2%AB-%EB%B2%88%EC%A7%B8-%EB%8B%A8%EA%B3%84</link>
      <description>&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-4141655932477481&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;목표: Bash 스크립트를 작성하고 실행할 수 있다&lt;br /&gt;&amp;nbsp;결과: 반복 작업을 자동화하는 기본 스크립트를 만들 수 있다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 쉘 스크립트란 무엇인가?&lt;/h1&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;서버 상태 확인&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;b&gt;매번 명령어로 입력하는 대신 하나의 파일로 만들어 실행&lt;/b&gt;할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일을 &lt;b&gt;쉘 스크립트(Shell Script)&lt;/b&gt; 라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 많이 사용하는 쉘은 &lt;b&gt;Bash&lt;/b&gt; 이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 첫 번째 Bash 스크립트 만들기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 생성:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;nano hello.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용 작성:&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;#!/bin/bash

echo &quot;Hello Linux Script&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 줄 의미:&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;#!/bin/bash
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 스크립트를 &lt;b&gt;bash로 실행하라&lt;/b&gt;는 의미다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 실행 권한 부여&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트를 실행하려면 실행 권한이 필요하다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;chmod +x hello.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 스크립트 실행&lt;/h1&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;./hello.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Hello Linux Script
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 변수 사용&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bash에서도 변수를 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;#!/bin/bash

name=&quot;Linux&quot;

echo &quot;Hello $name&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;Hello Linux
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의:&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;name = &quot;Linux&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;&amp;nbsp;Bash 변수는 &lt;b&gt;공백 없이&lt;/b&gt; 선언한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 사용자 입력 받기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;read 명령어 사용.&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;#!/bin/bash

echo &quot;이름을 입력하세요:&quot;
read name

echo &quot;Hello $name&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 조건문 (if)&lt;/h1&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;#!/bin/bash

num=10

if [ $num -gt 5 ]
then
    echo &quot;num은 5보다 큽니다&quot;
fi
&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;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&gt;-eq&lt;/td&gt;
&lt;td&gt;같다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-ne&lt;/td&gt;
&lt;td&gt;같지 않다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-gt&lt;/td&gt;
&lt;td&gt;크다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-lt&lt;/td&gt;
&lt;td&gt;작다&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 반복문 (for)&lt;/h1&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;#!/bin/bash

for i in 1 2 3 4 5
 do
    echo &quot;Number: $i&quot;
 done
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력:&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;Number: 1
Number: 2
Number: 3
Number: 4
Number: 5
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 파일 존재 여부 확인&lt;/h1&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;#!/bin/bash

FILE=&quot;test.txt&quot;

if [ -f $FILE ]
then
    echo &quot;파일 존재&quot;
else
    echo &quot;파일 없음&quot;
fi
&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;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&gt;-f&lt;/td&gt;
&lt;td&gt;파일 존재&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-d&lt;/td&gt;
&lt;td&gt;디렉토리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-e&lt;/td&gt;
&lt;td&gt;파일/디렉토리 존재&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 실습: 간단한 시스템 정보 스크립트&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 생성:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;nano sysinfo.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용:&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;#!/bin/bash

echo &quot;현재 사용자:&quot;
whoami

echo &quot;현재 디렉토리:&quot;
pwd

echo &quot;시스템 정보:&quot;
uname -a
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행:&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;chmod +x sysinfo.sh
./sysinfo.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 초보자가 자주 하는 실수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;#!/bin/bash 누락&lt;br /&gt;&amp;nbsp;실행 권한 없이 실행&lt;br /&gt;&amp;nbsp;변수 선언 시 공백 사용&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. 13편 요약&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Bash 스크립트는 &lt;b&gt;리눅스 자동화 도구&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;#!/bin/bash 로 시작&lt;/li&gt;
&lt;li&gt;chmod +x 로 실행 권한 필요&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메인 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;bash 스크립트 기초&lt;/li&gt;
&lt;li&gt;리눅스 쉘 스크립트&lt;/li&gt;
&lt;li&gt;bash script tutorial&lt;/li&gt;
&lt;li&gt;리눅스 자동화 스크립트&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서브 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;bash 변수 사용법&lt;/li&gt;
&lt;li&gt;bash if 문&lt;/li&gt;
&lt;li&gt;bash for 문&lt;/li&gt;
&lt;li&gt;리눅스 쉘 자동화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Linux 완전 정복/Linux  개발도구</category>
      <category>Bash</category>
      <category>LinuxProgramming</category>
      <category>linuxscript</category>
      <category>리눅스자동화</category>
      <category>쉘스크립트</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/215</guid>
      <comments>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-13%ED%8E%B8-Bash-%EC%89%98-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9E%85%EB%AC%B8-%EB%A6%AC%EB%88%85%EC%8A%A4-%EC%9E%91%EC%97%85%EC%9D%84-%EC%9E%90%EB%8F%99%ED%99%94%ED%95%98%EB%8A%94-%EC%B2%AB-%EB%B2%88%EC%A7%B8-%EB%8B%A8%EA%B3%84#entry215comment</comments>
      <pubDate>Tue, 10 Mar 2026 12:35:32 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스 프로그래밍 시리즈 12편] ltrace 사용법: 리눅스 프로그램이 어떤 라이브러리 함수를 호출하는지 추적하기</title>
      <link>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-12%ED%8E%B8-ltrace-%EC%82%AC%EC%9A%A9%EB%B2%95-%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8%EC%9D%B4-%EC%96%B4%EB%96%A4-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%ED%95%A8%EC%88%98%EB%A5%BC-%ED%98%B8%EC%B6%9C%ED%95%98%EB%8A%94%EC%A7%80-%EC%B6%94%EC%A0%81%ED%95%98%EA%B8%B0</link>
      <description>&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-4141655932477481&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;목표: ltrace를 이용해 프로그램이 어떤 &lt;b&gt;라이브러리 함수&lt;/b&gt;를 호출하는지 확인한다&lt;br /&gt;&amp;nbsp;결과: 라이브러리 관련 문제와 함수 호출 흐름을 분석할 수 있다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. ltrace는 무엇인가?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;11편에서 우리는 &lt;b&gt;strace&lt;/b&gt;를 배웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;strace는 프로그램이 호출하는 &lt;b&gt;시스템 콜(System Call)&lt;/b&gt; 을 추적한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 &lt;b&gt;ltrace&lt;/b&gt;는 다음을 추적한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;라이브러리 함수 호출 (Library Call)&lt;/b&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;printf&lt;/li&gt;
&lt;li&gt;malloc&lt;/li&gt;
&lt;li&gt;free&lt;/li&gt;
&lt;li&gt;fopen&lt;/li&gt;
&lt;li&gt;strcmp&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 프로그램이 어떤 &lt;b&gt;라이브러리 함수를 호출하는지&lt;/b&gt; 확인할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. strace vs ltrace 차이&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도구추적 대상&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&gt;strace&lt;/td&gt;
&lt;td&gt;시스템 콜&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ltrace&lt;/td&gt;
&lt;td&gt;라이브러리 함수&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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 class=&quot;stylus&quot;&gt;&lt;code&gt;fopen()  &amp;rarr; 라이브러리 함수
open()   &amp;rarr; 시스템 콜
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ltrace는 &lt;b&gt;fopen&lt;/b&gt;을 보고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;strace는 &lt;b&gt;open&lt;/b&gt;을 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘을 함께 사용하면 프로그램 동작을 매우 깊게 분석할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. ltrace 기본 사용법&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램 실행과 동시에 추적:&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;ltrace ./hello
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 예:&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;printf(&quot;Hello Linux!\n&quot;) = 13
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 의미는 프로그램이 printf 함수를 호출했다는 뜻이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 특정 함수만 추적&lt;/h1&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 class=&quot;cpp&quot;&gt;&lt;code&gt;ltrace -e printf ./hello
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;printf 호출만 표시된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 실행 중인 프로세스 추적&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 실행 중인 프로세스도 추적할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 PID 확인:&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;ps aux | grep program
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 attach:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;ltrace -p PID
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 중인 프로그램의 라이브러리 호출을 실시간으로 확인할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 출력 로그 저장&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ltrace 출력도 파일로 저장할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;ltrace -o trace.log ./hello
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 분석하기 쉽다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 메모리 함수 추적 예시&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 프로그램을 가정해 보자.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;

int main() {

    int *p = malloc(sizeof(int));

    *p = 10;

    printf(&quot;%d\n&quot;, *p);

    free(p);

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일:&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;gcc -g test.c -o test
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추적:&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;ltrace ./test
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 예:&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;malloc(4) = 0x55...
printf(&quot;%d\n&quot;, 10) = 3
free(0x55...)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램이 호출한 라이브러리 함수 흐름을 확인할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. ltrace가 유용한 상황&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;메모리 관련 문제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;malloc / free 호출 흐름 분석&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;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. strace + ltrace 함께 사용하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램 분석은 보통 다음 순서로 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) gdb &amp;rarr; 코드 디버깅&lt;br /&gt;2) strace &amp;rarr; 시스템 콜 분석&lt;br /&gt;3) ltrace &amp;rarr; 라이브러리 호출 분석&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 세 도구는 리눅스 개발자의 기본 디버깅 도구다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 초보자가 자주 하는 실수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;strace와 ltrace 차이 모름&lt;br /&gt;&amp;nbsp;출력이 많아 분석 포기&lt;br /&gt;&amp;nbsp;특정 함수 필터링 없이 사용&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 12편 요약&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ltrace는 &lt;b&gt;라이브러리 함수 호출을 추적하는 도구&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;strace와 함께 사용하면 프로그램 내부 동작을 깊게 분석 가능&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;&lt;b&gt;gdb + strace + ltrace&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 조합이 가장 강력하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메인 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ltrace 사용법&lt;/li&gt;
&lt;li&gt;리눅스 라이브러리 추적&lt;/li&gt;
&lt;li&gt;리눅스 디버깅 도구&lt;/li&gt;
&lt;li&gt;Linux ltrace tutorial&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서브 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ltrace printf 추적&lt;/li&gt;
&lt;li&gt;strace ltrace 차이&lt;/li&gt;
&lt;li&gt;리눅스 함수 호출 추적&lt;/li&gt;
&lt;li&gt;Linux debugging tools&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Linux 완전 정복/Linux  개발도구</category>
      <category>LinuxProgramming</category>
      <category>ltrace</category>
      <category>리눅스개발</category>
      <category>리눅스디버깅</category>
      <category>시스템분석</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/214</guid>
      <comments>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-12%ED%8E%B8-ltrace-%EC%82%AC%EC%9A%A9%EB%B2%95-%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8%EC%9D%B4-%EC%96%B4%EB%96%A4-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%ED%95%A8%EC%88%98%EB%A5%BC-%ED%98%B8%EC%B6%9C%ED%95%98%EB%8A%94%EC%A7%80-%EC%B6%94%EC%A0%81%ED%95%98%EA%B8%B0#entry214comment</comments>
      <pubDate>Mon, 9 Mar 2026 17:32:35 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스 프로그래밍 시리즈 11편] strace 완전 입문: 리눅스 프로그램이 내부에서 무슨 일을 하는지 추적하는 방법</title>
      <link>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-11%ED%8E%B8-strace-%EC%99%84%EC%A0%84-%EC%9E%85%EB%AC%B8-%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8%EC%9D%B4-%EB%82%B4%EB%B6%80%EC%97%90%EC%84%9C-%EB%AC%B4%EC%8A%A8-%EC%9D%BC%EC%9D%84-%ED%95%98%EB%8A%94%EC%A7%80-%EC%B6%94%EC%A0%81%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
      <description>&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-4141655932477481&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;목표: strace를 이용해 프로그램이 어떤 시스템 콜을 호출하는지 확인한다&lt;br /&gt;&amp;nbsp;결과: 실행 오류, 파일 접근 문제, 권한 문제의 원인을 분석할 수 있다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. strace는 무엇인가?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스 프로그램은 운영체제와 직접 대화하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 &lt;b&gt;시스템 콜(System Call)&lt;/b&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;네트워크 연결&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;b&gt;시스템 콜&lt;/b&gt;을 통해 수행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;strace는 바로 이 시스템 콜을 추적하는 도구다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 프로그램이 내부적으로 무엇을 하는지 그대로 볼 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. strace가 필요한 상황&lt;/h1&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;li&gt;라이브러리 로딩 실패&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 strace를 사용하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;프로그램이 어떤 파일을 열려고 했는지&lt;br /&gt;&amp;nbsp;어떤 시스템 콜에서 실패했는지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확히 확인할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. strace 기본 사용법&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램을 strace로 실행하는 방법:&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;strace ./hello
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 예시:&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;execve(&quot;./hello&quot;, [&quot;./hello&quot;], 0x7ffd...) = 0
openat(AT_FDCWD, &quot;/etc/ld.so.cache&quot;, O_RDONLY) = 3
read(3, ... ) = 832
write(1, &quot;Hello Linux!&quot;, 12) = 12
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 출력은 프로그램이 실행되는 동안 발생한 &lt;b&gt;시스템 콜 목록&lt;/b&gt;이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 특정 시스템 콜만 보기&lt;/h1&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 class=&quot;routeros&quot;&gt;&lt;code&gt;strace -e trace=open ./hello
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는&lt;/p&gt;
&lt;pre class=&quot;mel&quot;&gt;&lt;code&gt;strace -e trace=file ./hello
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 접근 관련 시스템 콜만 출력된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 실행 중인 프로세스 추적&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 실행 중인 프로그램도 추적할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 PID 확인:&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;ps aux | grep program
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 attach:&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;strace -p PID
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 실행 중인 프로그램의 시스템 콜을 실시간으로 확인할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 출력 파일로 저장&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;strace 출력은 매우 길기 때문에 보통 파일로 저장한다.&lt;/p&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;strace -o trace.log ./hello
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 결과가 trace.log 파일에 저장된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 파일 접근 문제 분석 예시&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 프로그램이 특정 파일을 열지 못하는 경우.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;strace ./program
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 예:&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;open(&quot;config.txt&quot;, O_RDONLY) = -1 ENOENT
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의미:&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;ENOENT = No such file or directory
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 프로그램이 config.txt 파일을 찾지 못했다는 의미다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 문제 원인을 바로 확인할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 네트워크 시스템 콜 추적&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 관련 동작도 확인할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;strace -e trace=network ./program
&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;socket&lt;/li&gt;
&lt;li&gt;connect&lt;/li&gt;
&lt;li&gt;send&lt;/li&gt;
&lt;li&gt;recv&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 디버깅에 매우 유용하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. strace + grep 조합&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력이 많기 때문에 grep과 함께 사용하는 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;strace ./program 2&amp;gt;&amp;amp;1 | grep open
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 open 시스템 콜만 확인 가능하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 초보자가 자주 하는 실수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;strace 출력이 너무 많아 이해 못함&lt;br /&gt;&amp;nbsp;시스템 콜 의미를 모름&lt;br /&gt;&amp;nbsp;로그 저장 없이 터미널로만 분석&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. strace 핵심 정리&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그램의 &lt;b&gt;시스템 콜을 추적하는 도구&lt;/b&gt;&lt;/li&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;b&gt;gdb와 strace는 핵심 도구&lt;/b&gt;다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메인 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;strace 사용법&lt;/li&gt;
&lt;li&gt;리눅스 시스템콜 추적&lt;/li&gt;
&lt;li&gt;리눅스 디버깅 도구&lt;/li&gt;
&lt;li&gt;strace 파일 접근 분석&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서브 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;strace open 추적&lt;/li&gt;
&lt;li&gt;strace network 추적&lt;/li&gt;
&lt;li&gt;리눅스 프로그램 분석&lt;/li&gt;
&lt;li&gt;Linux strace tutorial&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Linux 완전 정복/Linux  개발도구</category>
      <category>LinuxDebug</category>
      <category>LinuxProgramming</category>
      <category>strace</category>
      <category>리눅스디버깅</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/213</guid>
      <comments>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-11%ED%8E%B8-strace-%EC%99%84%EC%A0%84-%EC%9E%85%EB%AC%B8-%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8%EC%9D%B4-%EB%82%B4%EB%B6%80%EC%97%90%EC%84%9C-%EB%AC%B4%EC%8A%A8-%EC%9D%BC%EC%9D%84-%ED%95%98%EB%8A%94%EC%A7%80-%EC%B6%94%EC%A0%81%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95#entry213comment</comments>
      <pubDate>Mon, 9 Mar 2026 14:31:24 +0900</pubDate>
    </item>
    <item>
      <title>[WPF 실무 24편] WPF Command 패턴 (MVVM에서 버튼 이벤트 처리 완전 정리)</title>
      <link>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-24%ED%8E%B8-WPF-Command-%ED%8C%A8%ED%84%B4-MVVM%EC%97%90%EC%84%9C-%EB%B2%84%ED%8A%BC-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%B2%98%EB%A6%AC-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC</link>
      <description>&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-4141655932477481&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVVM 구조에서 가장 처음 부딪히는 문제 중 하나가 바로 버튼 이벤트 처리입니다.&lt;br /&gt;CodeBehind에서 Click 이벤트를 처리하면 MVVM 구조가 깨지기 때문입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 해결하는 핵심이 바로 &lt;b&gt;Command 패턴&lt;/b&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;✔ Command 패턴 개념&lt;br /&gt;✔ ICommand 구조&lt;br /&gt;✔ RelayCommand 구현&lt;br /&gt;✔ MVVM에서 버튼 이벤트 처리 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 실무 기준으로 정리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. MVVM에서 Click 이벤트를 쓰면 안 되는 이유&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;일반적인 WPF 방식&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;&amp;lt;Button Content=&quot;저장&quot; Click=&quot;Save_Click&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;private void Save_Click(object sender, RoutedEventArgs e)
{
    SaveData();
}
&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;✔ View &amp;rarr; CodeBehind &amp;rarr; 로직 연결&lt;br /&gt;✔ ViewModel 분리 불가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;MVVM 구조 깨짐&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. Command 패턴 구조&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Command 패턴은 다음 구조로 동작합니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Button
  &amp;darr; Command
ViewModel
  &amp;darr; Execute
Business Logic
&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;✔ View &amp;rarr; Command 호출&lt;br /&gt;✔ 실제 로직 &amp;rarr; ViewModel&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. ICommand 인터페이스&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF Command는 다음 인터페이스를 구현합니다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public interface ICommand
{
    event EventHandler CanExecuteChanged;

    bool CanExecute(object parameter);

    void Execute(object parameter);
}
&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;✔ Execute &amp;rarr; 실제 실행 로직&lt;br /&gt;✔ CanExecute &amp;rarr; 버튼 활성화 여부&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. RelayCommand 구현&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서는 ICommand를 직접 구현하지 않고&lt;br /&gt;&lt;b&gt;RelayCommand&lt;/b&gt;를 만들어 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public class RelayCommand : ICommand
{
    private readonly Action _execute;
    private readonly Func&amp;lt;bool&amp;gt; _canExecute;

    public RelayCommand(Action execute, Func&amp;lt;bool&amp;gt; canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute == null || _canExecute();
    }

    public void Execute(object parameter)
    {
        _execute();
    }

    public event EventHandler CanExecuteChanged;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. ViewModel에서 Command 생성&lt;/h1&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public ICommand SaveCommand { get; }

public MainViewModel()
{
    SaveCommand = new RelayCommand(SaveData);
}

private void SaveData()
{
    // 저장 로직
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. XAML에서 Command 연결&lt;/h1&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;&amp;lt;Button Content=&quot;저장&quot;
        Command=&quot;{Binding SaveCommand}&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 버튼 클릭 &amp;rarr; Command 실행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CodeBehind 필요 없음&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. CanExecute로 버튼 활성화 제어&lt;/h1&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public ICommand SaveCommand { get; }

public MainViewModel()
{
    SaveCommand = new RelayCommand(SaveData, CanSave);
}

private bool CanSave()
{
    return IsDataValid;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 조건에 따라 버튼 활성화&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8.&amp;nbsp; 실무에서 자주 하는 실수 1 &amp;mdash; Command를 View에서 생성&lt;/h1&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;Button.Command = new RelayCommand(...)
&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;✔ View &amp;rarr; 로직 연결&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVVM 구조 깨짐&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9.&amp;nbsp; 실무에서 자주 하는 실수 2 &amp;mdash; CanExecuteChanged 갱신 안 함&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버튼 활성화 상태를 갱신하려면&lt;/p&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;CommandManager.InvalidateRequerySuggested();
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출 필요&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. CommandParameter 사용&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버튼에서 데이터를 전달할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;&amp;lt;Button Content=&quot;삭제&quot;
        Command=&quot;{Binding DeleteCommand}&quot;
        CommandParameter=&quot;{Binding}&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;public RelayCommand DeleteCommand =&amp;gt; new RelayCommand(obj =&amp;gt; DeleteItem(obj));
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 한 줄 핵심 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVVM에서 버튼 이벤트는 Click이 아니라 Command로 처리한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF ICommand 사용법&lt;br /&gt;WPF RelayCommand 예제&lt;br /&gt;WPF MVVM Command 패턴&lt;br /&gt;WPF Button Command&lt;br /&gt;WPF CanExecute&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발이야기/WPF MVVM</category>
      <category>csharp wpf</category>
      <category>WPF</category>
      <category>wpf command</category>
      <category>wpf icommand</category>
      <category>WPF MVVM</category>
      <category>닷넷 개발</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/212</guid>
      <comments>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-24%ED%8E%B8-WPF-Command-%ED%8C%A8%ED%84%B4-MVVM%EC%97%90%EC%84%9C-%EB%B2%84%ED%8A%BC-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%B2%98%EB%A6%AC-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC#entry212comment</comments>
      <pubDate>Mon, 9 Mar 2026 09:38:19 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스 프로그래밍 시리즈 10편] gdb 디버깅 입문: 리눅스에서 프로그램 오류를 찾는 가장 강력한 방법</title>
      <link>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-10%ED%8E%B8-gdb-%EB%94%94%EB%B2%84%EA%B9%85-%EC%9E%85%EB%AC%B8-%EB%A6%AC%EB%88%85%EC%8A%A4%EC%97%90%EC%84%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EC%98%A4%EB%A5%98%EB%A5%BC-%EC%B0%BE%EB%8A%94-%EA%B0%80%EC%9E%A5-%EA%B0%95%EB%A0%A5%ED%95%9C-%EB%B0%A9%EB%B2%95</link>
      <description>&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-4141655932477481&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;목표: gdb를 사용해 프로그램 내부 상태를 확인하고 버그를 찾을 수 있다&lt;br /&gt;&amp;nbsp;결과: 실행 중인 프로그램을 멈추고 변수, 스택, 흐름을 분석할 수 있다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 왜 gdb를 배워야 할까?&lt;/h1&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;segmentation fault 발생&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;printf로 디버깅할 수도 있지만 한계가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 사용하는 도구가 &lt;b&gt;gdb (GNU Debugger)&lt;/b&gt; 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;gdb는 실행 중인 프로그램을 멈추고 내부 상태를 확인할 수 있는 도구다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. gdb 사용을 위한 컴파일 옵션&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디버깅을 위해서는 프로그램을 다음 옵션으로 컴파일해야 한다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;gcc -g hello.c -o hello
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-g 옵션은 &lt;b&gt;디버깅 정보를 포함&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 옵션이 없으면 gdb 분석이 어려워진다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. gdb 실행&lt;/h1&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;gdb ./hello
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행하면 다음과 같은 프롬프트가 나타난다.&lt;/p&gt;
&lt;pre class=&quot;clojure&quot;&gt;&lt;code&gt;(gdb)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 다양한 디버깅 명령을 사용할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 프로그램 실행&lt;/h1&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;run
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;r
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램이 실행된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 브레이크포인트 설정&lt;/h1&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 class=&quot;kotlin&quot;&gt;&lt;code&gt;break main
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;b 10
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10번째 줄에서 프로그램이 멈춘다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 코드 한 줄씩 실행&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램이 멈춘 상태에서 다음 명령을 사용할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;next&lt;/h3&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;next
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 줄 실행&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;step&lt;/h3&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;step
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 내부로 들어가며 실행&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 변수 값 확인&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 변수 값 확인:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;print 변수명
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;print count
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$1 = 5
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 호출 스택 확인&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램이 어디서 호출되었는지 확인할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;backtrace
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;bt
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 segmentation fault 분석 시 매우 중요하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 프로그램 계속 실행&lt;/h1&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;continue
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 브레이크포인트까지 실행된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. gdb 주요 명령 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령의미&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&gt;run&lt;/td&gt;
&lt;td&gt;프로그램 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;break&lt;/td&gt;
&lt;td&gt;브레이크포인트 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;next&lt;/td&gt;
&lt;td&gt;다음 줄 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;step&lt;/td&gt;
&lt;td&gt;함수 내부 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;print&lt;/td&gt;
&lt;td&gt;변수 값 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;backtrace&lt;/td&gt;
&lt;td&gt;호출 스택 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;continue&lt;/td&gt;
&lt;td&gt;실행 계속&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 실습 예제&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 프로그램을 만들어 보자.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;

int main() {

    int a = 10;
    int b = 0;

    int c = a / b;

    printf(&quot;%d\n&quot;, c);

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일:&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;gcc -g test.c -o test
&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;이때 gdb로 실행한다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;gdb ./test
run
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;bt
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어디서 문제가 발생했는지 확인할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. 초보자가 자주 하는 실수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;-g 옵션 없이 컴파일&lt;br /&gt;&amp;nbsp;브레이크포인트 없이 실행&lt;br /&gt;&amp;nbsp;print 대신 printf 디버깅만 사용&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;13. 10편 요약&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;gdb는 리눅스 디버깅 핵심 도구&lt;/li&gt;
&lt;li&gt;-g 옵션으로 컴파일 필요&lt;/li&gt;
&lt;li&gt;break / next / step / print 필수&lt;/li&gt;
&lt;li&gt;backtrace는 오류 분석 핵심&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 리눅스 개발 환경에서 gdb는 기본 도구다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메인 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;gdb 사용법&lt;/li&gt;
&lt;li&gt;리눅스 디버깅&lt;/li&gt;
&lt;li&gt;C 프로그램 디버깅&lt;/li&gt;
&lt;li&gt;gdb 브레이크포인트&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서브 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;gdb next step&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Linux 완전 정복/Linux  개발도구</category>
      <category>C 프로그램 디버깅</category>
      <category>gcc</category>
      <category>GDB</category>
      <category>gdb 브레이크포인트</category>
      <category>gdb 사용법</category>
      <category>리눅스 디버깅</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/211</guid>
      <comments>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-10%ED%8E%B8-gdb-%EB%94%94%EB%B2%84%EA%B9%85-%EC%9E%85%EB%AC%B8-%EB%A6%AC%EB%88%85%EC%8A%A4%EC%97%90%EC%84%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EC%98%A4%EB%A5%98%EB%A5%BC-%EC%B0%BE%EB%8A%94-%EA%B0%80%EC%9E%A5-%EA%B0%95%EB%A0%A5%ED%95%9C-%EB%B0%A9%EB%B2%95#entry211comment</comments>
      <pubDate>Mon, 9 Mar 2026 09:33:34 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스 프로그래밍 시리즈 9편] Makefile 입문: 리눅스 빌드 자동화와 의존성 관리 제대로 이해하기</title>
      <link>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-9%ED%8E%B8-Makefile-%EC%9E%85%EB%AC%B8-%EB%A6%AC%EB%88%85%EC%8A%A4-%EB%B9%8C%EB%93%9C-%EC%9E%90%EB%8F%99%ED%99%94%EC%99%80-%EC%9D%98%EC%A1%B4%EC%84%B1-%EA%B4%80%EB%A6%AC-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
      <description>&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-4141655932477481&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;목표: 여러 개의 C 파일을 효율적으로 빌드하는 방법을 이해한다&lt;br /&gt;&amp;nbsp;결과: Makefile을 사용해 자동으로 프로젝트를 빌드할 수 있다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 왜 Makefile이 필요한가?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트가 커지면 파일 구조가 보통 이렇게 늘어난다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;main.c
math.c
math.h
util.c
util.h
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자는 보통 이렇게 컴파일한다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;gcc main.c math.c util.c -o program
&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;하지만 파일이 20개, 50개가 되면 문제가 생긴다.&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;b&gt;make + Makefile&lt;/b&gt;이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. make의 핵심 개념: 의존성 (Dependency)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;make는 &lt;b&gt;변경된 파일만 다시 컴파일&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 프로그램 구조가 이렇게 있다고 가정하자.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;program
 ├─ main.o
 ├─ math.o
 └─ util.o
&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;main.c 수정 &amp;rarr; main.o 다시 컴파일&lt;/li&gt;
&lt;li&gt;math.c 수정 &amp;rarr; math.o 다시 컴파일&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 &lt;b&gt;변경된 파일과 관련된 부분만 빌드&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 make의 가장 중요한 특징이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 가장 기본적인 Makefile 구조&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Makefile 파일 이름은 반드시 다음 중 하나여야 한다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;Makefile
makefile
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;program: main.o math.o
	gcc main.o math.o -o program

main.o: main.c
	gcc -c main.c

math.o: math.c
	gcc -c math.c
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조 설명:&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;타겟: 의존성
	실행명령
&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;b&gt;명령어 앞에는 반드시 TAB이 들어가야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스페이스를 사용하면 오류가 발생한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. make 실행&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Makefile이 있는 폴더에서 다음 명령어를 실행한다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;make
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;make는 Makefile을 읽고 필요한 컴파일 작업을 자동으로 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시 출력:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;gcc -c main.c
gcc -c math.c
gcc main.o math.o -o program
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 변경된 파일만 다시 컴파일되는 과정&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 math.c만 수정했다고 가정해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 실행:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;make
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;gcc -c math.c
gcc main.o math.o -o program
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;main.c는 다시 컴파일하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이것이 &lt;b&gt;빌드 시간 단축의 핵심 원리&lt;/b&gt;다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 변수 사용 (실무에서 필수)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Makefile에서는 변수를 사용해 코드를 깔끔하게 만들 수 있다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;CC=gcc
CFLAGS=-Wall

program: main.o math.o
	$(CC) main.o math.o -o program

main.o: main.c
	$(CC) $(CFLAGS) -c main.c

math.o: math.c
	$(CC) $(CFLAGS) -c math.c
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 컴파일러나 옵션을 쉽게 변경할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. clean 타겟 만들기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드 파일을 삭제하는 용도로 많이 사용한다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;clean:
	rm -f *.o program
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;make clean
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 프로젝트에서 반드시 포함되는 타겟이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 자동 변수 (중요)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Make에는 자동 변수들이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수의미&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&gt;$@&lt;/td&gt;
&lt;td&gt;현재 타겟&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$&amp;lt;&lt;/td&gt;
&lt;td&gt;첫 번째 의존성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$^&lt;/td&gt;
&lt;td&gt;모든 의존성&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;mel&quot;&gt;&lt;code&gt;%.o: %.c
	gcc -c $&amp;lt; -o $@
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 큰 프로젝트에서 많이 사용된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 실무 프로젝트 구조 예시&lt;/h1&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;project/
 ├─ src/
 │   ├─ main.c
 │   ├─ math.c
 │
 ├─ include/
 │   ├─ math.h
 │
 ├─ build/
 ├─ Makefile
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Makefile이 프로젝트 전체 빌드를 관리한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 초보자가 자주 하는 실수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;TAB 대신 스페이스 사용&lt;br /&gt;&amp;nbsp;의존성 누락&lt;br /&gt;&amp;nbsp;clean 타겟 없음&lt;br /&gt;&amp;nbsp;모든 파일을 한 번에 컴파일&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 9편 요약&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Makefile은 &lt;b&gt;빌드 자동화 도구&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;변경된 파일만 다시 컴파일&lt;/li&gt;
&lt;li&gt;변수와 자동 변수 활용 중요&lt;/li&gt;
&lt;li&gt;clean 타겟은 필수&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 규모가 커질수록 Makefile의 중요성은 더 커진다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메인 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Makefile 사용법&lt;/li&gt;
&lt;li&gt;리눅스 make 명령어&lt;/li&gt;
&lt;li&gt;C 프로젝트 빌드&lt;/li&gt;
&lt;li&gt;리눅스 빌드 자동화&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서브 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Makefile dependency&lt;/li&gt;
&lt;li&gt;make clean 의미&lt;/li&gt;
&lt;li&gt;리눅스 Makefile 예제&lt;/li&gt;
&lt;li&gt;gcc make 빌드&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Linux 완전 정복/Linux  개발도구</category>
      <category>c프로그래밍</category>
      <category>LinuxProgramming</category>
      <category>Make</category>
      <category>makefile</category>
      <category>리눅스빌드</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/210</guid>
      <comments>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-9%ED%8E%B8-Makefile-%EC%9E%85%EB%AC%B8-%EB%A6%AC%EB%88%85%EC%8A%A4-%EB%B9%8C%EB%93%9C-%EC%9E%90%EB%8F%99%ED%99%94%EC%99%80-%EC%9D%98%EC%A1%B4%EC%84%B1-%EA%B4%80%EB%A6%AC-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0#entry210comment</comments>
      <pubDate>Fri, 6 Mar 2026 15:12:48 +0900</pubDate>
    </item>
    <item>
      <title>[WPF 실무 23편] WPF 비동기 UI 처리 (async/await + Dispatcher 완전 정리)</title>
      <link>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-23%ED%8E%B8-WPF-%EB%B9%84%EB%8F%99%EA%B8%B0-UI-%EC%B2%98%EB%A6%AC-asyncawait-Dispatcher-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC</link>
      <description>&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-4141655932477481&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버튼을 눌렀는데 프로그램이 멈춘 것처럼 보인다.&lt;br /&gt;데이터 로딩 중 화면이 프리징 된다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF에서 가장 흔한 문제 중 하나가 바로 &lt;b&gt;UI Thread 블로킹&lt;/b&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;✔ WPF UI Thread 구조&lt;br /&gt;✔ async / await 사용법&lt;br /&gt;✔ Dispatcher 사용 이유&lt;br /&gt;✔ UI 멈춤 문제 해결 패턴&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 실무 기준으로 정리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. WPF UI Thread 구조&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF는 기본적으로 &lt;b&gt;Single UI Thread 모델&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;UI 업데이트
버튼 클릭 처리
렌더링
이벤트 처리
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모두 &lt;b&gt;같은 Thread에서 실행&lt;/b&gt;됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 오래 걸리는 작업을 UI Thread에서 실행하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;화면이 멈춥니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. UI 멈춤이 발생하는 코드&lt;/h1&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;private void LoadButton_Click(object sender, RoutedEventArgs e)
{
    LoadBigData();
}

private void LoadBigData()
{
    Thread.Sleep(5000);
}
&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;✔ 버튼 클릭 후 UI 멈춤&lt;br /&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;UI Thread가 작업에 묶여 있음&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. async / await 사용&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 간단한 해결 방법은 &lt;b&gt;비동기 처리&lt;/b&gt;입니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;private async void LoadButton_Click(object sender, RoutedEventArgs e)
{
    await Task.Run(() =&amp;gt; LoadBigData());
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ UI Thread 차단 없음&lt;br /&gt;✔ 작업은 백그라운드 Thread에서 실행&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. UI 업데이트 문제&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백그라운드 작업에서는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;UI 요소를 직접 변경할 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘못된 코드&lt;/p&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;Task.Run(() =&amp;gt;
{
    StatusText.Text = &quot;완료&quot;;
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예외 발생&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;The calling thread cannot access this object
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. Dispatcher 사용&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UI 업데이트는 반드시 UI Thread에서 실행해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;Dispatcher.Invoke(() =&amp;gt;
{
    StatusText.Text = &quot;완료&quot;;
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는&lt;/p&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;Application.Current.Dispatcher.Invoke(() =&amp;gt;
{
    StatusText.Text = &quot;완료&quot;;
});
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 실무 패턴 (추천 구조)&lt;/h1&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;private async void LoadButton_Click(object sender, RoutedEventArgs e)
{
    StatusText.Text = &quot;Loading...&quot;;

    var result = await Task.Run(() =&amp;gt; LoadData());

    StatusText.Text = &quot;Done&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 데이터 처리 &amp;rarr; 백그라운드&lt;br /&gt;✔ UI 업데이트 &amp;rarr; UI Thread&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7.&amp;nbsp; 실무에서 자주 하는 실수 1 &amp;mdash; async void 남용&lt;/h1&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public async void LoadData()
&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;br /&gt;✔ 디버깅 어려움&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추천&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public async Task LoadData()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8.&amp;nbsp; 실무에서 자주 하는 실수 2 &amp;mdash; Task.Result 사용&lt;/h1&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;var result = Task.Run(() =&amp;gt; LoadData()).Result;
&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;UI Thread 다시 블로킹&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. Progress UI 업데이트&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장시간 작업 시 진행 상태 표시&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;var progress = new Progress&amp;lt;int&amp;gt;(value =&amp;gt;
{
    ProgressBar.Value = value;
});

await Task.Run(() =&amp;gt; LongWork(progress));
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;void LongWork(IProgress&amp;lt;int&amp;gt; progress)
{
    for(int i=0;i&amp;lt;=100;i++)
    {
        Thread.Sleep(50);
        progress.Report(i);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ UI 안전 업데이트&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 한 줄 핵심 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF UI는 한 개의 Thread만 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;무거운 작업은 반드시 백그라운드로 보내라.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF async await&lt;br /&gt;WPF UI 멈춤 해결&lt;br /&gt;WPF Dispatcher 사용법&lt;br /&gt;WPF Task.Run&lt;br /&gt;WPF 비동기 처리&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;  추천 태그&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;wpf&lt;br /&gt;wpf async&lt;br /&gt;wpf dispatcher&lt;br /&gt;wpf threading&lt;br /&gt;닷넷 개발&lt;br /&gt;csharp wpf&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발이야기/WPF 성능 및 안정성</category>
      <category>csharp wpf</category>
      <category>WPF</category>
      <category>wpf async</category>
      <category>wpf dispatcher</category>
      <category>wpf threading</category>
      <category>닷넷 개발</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/209</guid>
      <comments>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-23%ED%8E%B8-WPF-%EB%B9%84%EB%8F%99%EA%B8%B0-UI-%EC%B2%98%EB%A6%AC-asyncawait-Dispatcher-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC#entry209comment</comments>
      <pubDate>Fri, 6 Mar 2026 15:04:44 +0900</pubDate>
    </item>
    <item>
      <title>[WPF 실무 22편] WPF Dialog / Modal 창 구조 (MVVM 친화적인 팝업 구현)</title>
      <link>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-22%ED%8E%B8-WPF-Dialog-Modal-%EC%B0%BD-%EA%B5%AC%EC%A1%B0-MVVM-%EC%B9%9C%ED%99%94%EC%A0%81%EC%9D%B8-%ED%8C%9D%EC%97%85-%EA%B5%AC%ED%98%84</link>
      <description>&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-4141655932477481&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램을 만들다 보면 확인창, 설정창, 경고 메시지 같은 팝업 UI가 필요합니다.&lt;br /&gt;단순히 Window를 새로 띄우는 것처럼 보이지만,&lt;br /&gt;&lt;b&gt;MVVM 구조에서는 Dialog 처리 방식이 따로 존재합니다.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ Modal / Modeless 차이&lt;br /&gt;✔ Dialog 창 구현 방법&lt;br /&gt;✔ MVVM에서 Dialog 처리 패턴&lt;br /&gt;✔ 실무에서 자주 하는 실수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 정리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. Modal vs Modeless 차이&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Modal Dialog&lt;/h2&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;var dialog = new SettingsWindow();
dialog.ShowDialog();
&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;br /&gt;✔ 사용자가 Dialog 닫을 때까지 대기&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Modeless Window&lt;/h2&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;var win = new LogWindow();
win.Show();
&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;br /&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;모니터링 화면&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 기본 Dialog 구현&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Dialog Window 생성&lt;/h3&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;Add &amp;rarr; Window &amp;rarr; ConfirmDialog.xaml
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;Window Height=&quot;200&quot; Width=&quot;350&quot;
        WindowStartupLocation=&quot;CenterOwner&quot;
        ResizeMode=&quot;NoResize&quot;&amp;gt;

    &amp;lt;Grid Margin=&quot;20&quot;&amp;gt;

        &amp;lt;TextBlock Text=&quot;삭제하시겠습니까?&quot;
                   FontSize=&quot;16&quot;
                   VerticalAlignment=&quot;Center&quot;/&amp;gt;

        &amp;lt;StackPanel Orientation=&quot;Horizontal&quot;
                    HorizontalAlignment=&quot;Right&quot;
                    VerticalAlignment=&quot;Bottom&quot;&amp;gt;

            &amp;lt;Button Content=&quot;확인&quot; Width=&quot;70&quot; Margin=&quot;5&quot; Click=&quot;Ok_Click&quot;/&amp;gt;
            &amp;lt;Button Content=&quot;취소&quot; Width=&quot;70&quot; Margin=&quot;5&quot; Click=&quot;Cancel_Click&quot;/&amp;gt;

        &amp;lt;/StackPanel&amp;gt;

    &amp;lt;/Grid&amp;gt;

&amp;lt;/Window&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. Dialog 결과 반환&lt;/h1&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;private void Ok_Click(object sender, RoutedEventArgs e)
{
    DialogResult = true;
}

private void Cancel_Click(object sender, RoutedEventArgs e)
{
    DialogResult = false;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용:&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;var dialog = new ConfirmDialog();

if(dialog.ShowDialog() == true)
{
    DeleteItem();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ Dialog 결과 기반 처리&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. Owner 설정 (실무 중요)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dialog는 항상 Owner를 지정하는 것이 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;var dialog = new ConfirmDialog();
dialog.Owner = Application.Current.MainWindow;

dialog.ShowDialog();
&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;br /&gt;✔ 작업 표시줄 중복 방지&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5.&amp;nbsp; 실무에서 자주 하는 실수 1 &amp;mdash; DialogResult 직접 Close()&lt;/h1&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;Close();
&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;Dialog 결과를 받을 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 반드시 DialogResult 설정&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6.&amp;nbsp; 실무에서 자주 하는 실수 2 &amp;mdash; ViewModel에서 Window 직접 생성&lt;/h1&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;var dialog = new ConfirmDialog();
dialog.ShowDialog();
&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;ViewModel &amp;rarr; View 참조&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVVM 구조 깨짐&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. MVVM Dialog Service 패턴&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서는 DialogService를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Services/
 └ DialogService.cs
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public class DialogService
{
    public bool ShowConfirm(string message)
    {
        var dialog = new ConfirmDialog();
        dialog.MessageText.Text = message;

        return dialog.ShowDialog() == true;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ViewModel&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;if(_dialogService.ShowConfirm(&quot;삭제하시겠습니까?&quot;))
{
    DeleteItem();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ MVVM 구조 유지&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 실무 추천 Dialog 구조&lt;/h1&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Views/
 ├ Dialogs/
 │   ├ ConfirmDialog.xaml
 │   └ MessageDialog.xaml

Services/
 └ DialogService.cs
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 팝업 관리 중앙화&lt;br /&gt;✔ 코드 재사용&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 한 줄 핵심 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dialog는 Window가 아니라 서비스로 관리하라.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF Dialog 구현&lt;br /&gt;WPF ShowDialog 사용법&lt;br /&gt;WPF Modal Window&lt;br /&gt;WPF MVVM DialogService&lt;br /&gt;WPF 확인 팝업&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발이야기/WPF 어플리케이션 구조</category>
      <category>csharp wpf</category>
      <category>WPF</category>
      <category>wpf dialog</category>
      <category>wpf modal</category>
      <category>WPF MVVM</category>
      <category>닷넷 개발</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/208</guid>
      <comments>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-22%ED%8E%B8-WPF-Dialog-Modal-%EC%B0%BD-%EA%B5%AC%EC%A1%B0-MVVM-%EC%B9%9C%ED%99%94%EC%A0%81%EC%9D%B8-%ED%8C%9D%EC%97%85-%EA%B5%AC%ED%98%84#entry208comment</comments>
      <pubDate>Fri, 6 Mar 2026 14:54:47 +0900</pubDate>
    </item>
    <item>
      <title>[WPF 실무 21편] WPF 화면 전환 구조 (Frame / Page / Navigation 패턴 완전 정리)</title>
      <link>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-21%ED%8E%B8-WPF-%ED%99%94%EB%A9%B4-%EC%A0%84%ED%99%98-%EA%B5%AC%EC%A1%B0-Frame-Page-Navigation-%ED%8C%A8%ED%84%B4-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC</link>
      <description>&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-4141655932477481&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램 규모가 커지면 하나의 Window 안에서 여러 화면을 전환해야 하는 경우가 많습니다.&lt;br /&gt;메뉴 클릭 &amp;rarr; 다른 화면 표시&lt;br /&gt;설정 페이지 이동&lt;br /&gt;검사 결과 화면 전환&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 사용하는 것이 바로 &lt;b&gt;WPF Navigation 구조 (Frame + Page)&lt;/b&gt; 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는&lt;br /&gt;✔ Frame / Page 기본 개념&lt;br /&gt;✔ 화면 전환 구현 방법&lt;br /&gt;✔ MVVM 환경에서 사용하는 네비게이션 패턴&lt;br /&gt;✔ 실무에서 자주 하는 실수&lt;br /&gt;를 정리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. WPF 화면 전환 기본 구조&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF의 기본 화면 전환 구조는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Window
 └ Frame
     └ Page (화면)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 Window는 그대로 유지하고&lt;br /&gt;Frame 안의 Page만 교체합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 메모리 효율적&lt;br /&gt;✔ UI 전환 자연스러움&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. Frame 생성&lt;/h1&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;Window x:Class=&quot;MainWindow&quot;&amp;gt;

    &amp;lt;Grid&amp;gt;
        &amp;lt;Frame x:Name=&quot;MainFrame&quot;/&amp;gt;
    
    &amp;lt;/Grid&amp;gt;

&amp;lt;/Window&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Frame은 화면을 표시하는 컨테이너입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. Page 생성&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio에서&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Add &amp;rarr; New Item &amp;rarr; Page
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예: HomePage.xaml&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;Page&amp;gt;

    &amp;lt;Grid&amp;gt;
        &amp;lt;TextBlock Text=&quot;Home Screen&quot;
                   FontSize=&quot;30&quot;
                   HorizontalAlignment=&quot;Center&quot;
                   VerticalAlignment=&quot;Center&quot;/&amp;gt;
    &amp;lt;/Grid&amp;gt;

&amp;lt;/Page&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 코드에서 화면 전환&lt;/h1&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;MainFrame.Navigate(new HomePage());
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;MainFrame.Navigate(new SettingsPage());
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ Frame 내부 Page 교체&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. XAML에서 초기 화면 설정&lt;/h1&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;&amp;lt;Frame Source=&quot;Pages/HomePage.xaml&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱 시작 시 해당 페이지 자동 로드됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6.&amp;nbsp; 실무에서 자주 하는 실수 1 &amp;mdash; Window를 계속 새로 생성&lt;/h1&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;var win = new SettingsWindow();
win.Show();
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ Window 계속 증가&lt;br /&gt;✔ 메모리 사용 증가&lt;br /&gt;✔ UX 불편&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일 Window + Frame 구조 사용&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7.&amp;nbsp; 실무에서 자주 하는 실수 2 &amp;mdash; Page에서 Window 직접 접근&lt;/h1&gt;
&lt;pre class=&quot;clojure&quot;&gt;&lt;code&gt;((MainWindow)Application.Current.MainWindow)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVVM 구조 깨짐&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NavigationService 사용&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. NavigationService 사용&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Page 내부에서는 NavigationService를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;NavigationService.Navigate(new SettingsPage());
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ Window 참조 필요 없음&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. MVVM 네비게이션 패턴&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서는 NavigationService를 래핑해서 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Services/
 └ NavigationService.cs
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;public class NavigationService
{
    private Frame _frame;

    public NavigationService(Frame frame)
    {
        _frame = frame;
    }

    public void Navigate(Page page)
    {
        _frame.Navigate(page);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ViewModel에서 호출&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;_navigation.Navigate(new HomePage());
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 뒤로가기 기능&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Frame은 기본적으로 히스토리를 관리합니다.&lt;/p&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;MainFrame.GoBack();
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 브라우저처럼 이전 화면 이동 가능&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 한 줄 핵심 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF 화면 전환은 Window가 아니라 Frame을 교체하는 구조다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF 화면 전환&lt;br /&gt;WPF Frame Page 사용법&lt;br /&gt;WPF NavigationService&lt;br /&gt;WPF 페이지 전환&lt;br /&gt;WPF MVVM 네비게이션&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발이야기/WPF 어플리케이션 구조</category>
      <category>csharp wpf</category>
      <category>WPF</category>
      <category>wpf frame</category>
      <category>wpf navigation</category>
      <category>wpf page</category>
      <category>닷넷 개발</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/207</guid>
      <comments>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-21%ED%8E%B8-WPF-%ED%99%94%EB%A9%B4-%EC%A0%84%ED%99%98-%EA%B5%AC%EC%A1%B0-Frame-Page-Navigation-%ED%8C%A8%ED%84%B4-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC#entry207comment</comments>
      <pubDate>Thu, 5 Mar 2026 13:22:41 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스 프로그래밍 시리즈 8편] gcc 컴파일 구조 완전 이해: C 프로그램이 실행 파일이 되는 과정</title>
      <link>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-8%ED%8E%B8-gcc-%EC%BB%B4%ED%8C%8C%EC%9D%BC-%EA%B5%AC%EC%A1%B0-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4-C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8%EC%9D%B4-%EC%8B%A4%ED%96%89-%ED%8C%8C%EC%9D%BC%EC%9D%B4-%EB%90%98%EB%8A%94-%EA%B3%BC%EC%A0%95</link>
      <description>&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-4141655932477481&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;목표: 리눅스에서 C 프로그램이 어떻게 컴파일되는지 이해한다&lt;br /&gt;&amp;nbsp;결과: gcc 컴파일 과정과 기본 빌드 구조를 이해한다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 왜 gcc 컴파일 구조를 알아야 할까?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스에서 개발을 시작하면 가장 먼저 만나게 되는 도구가 &lt;b&gt;gcc&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 초보자는 단순히 이렇게만 사용한다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;gcc hello.c -o hello
&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;1) 전처리 (Preprocessing)&lt;br /&gt;2) 컴파일 (Compilation)&lt;br /&gt;3) 어셈블리 (Assembly)&lt;br /&gt;4) 링크 (Linking)&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 예제 프로그램 준비&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 간단한 C 프로그램을 만든다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;nano hello.c
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용 작성:&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;

int main() {
    printf(&quot;Hello Linux!\n&quot;);
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일:&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;gcc hello.c -o hello
./hello
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력:&lt;/p&gt;
&lt;pre class=&quot;erlang-repl&quot;&gt;&lt;code&gt;Hello Linux!
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 전처리 단계 (Preprocessing)&lt;/h1&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;#include 처리&lt;/li&gt;
&lt;li&gt;#define 매크로 확장&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;pre class=&quot;css&quot;&gt;&lt;code&gt;gcc -E hello.c
&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;stdio.h 내용까지 모두 포함되기 때문.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 컴파일 단계 (Compilation)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C 코드를 &lt;b&gt;어셈블리 코드&lt;/b&gt;로 변환한다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;gcc -S hello.c
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성 파일:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;hello.s
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일은 CPU가 이해할 수 있는 어셈블리 코드다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 어셈블리 단계 (Assembly)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어셈블리 코드를 &lt;b&gt;오브젝트 파일&lt;/b&gt;로 변환한다.&lt;/p&gt;
&lt;pre class=&quot;llvm&quot;&gt;&lt;code&gt;gcc -c hello.c
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성 파일:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;hello.o
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일은 아직 실행 파일이 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 링크 단계 (Linking)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오브젝트 파일과 라이브러리를 결합하여 실행 파일을 만든다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;gcc hello.o -o hello
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 printf 같은 함수는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;표준 라이브러리(libc)&lt;/b&gt; 와 연결된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 컴파일 과정 한눈에 보기&lt;/h1&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;hello.c
   │
   ▼
전처리
   │
   ▼
컴파일
   │
   ▼
어셈블리
   │
   ▼
hello.o
   │
   ▼
링크
   │
   ▼
hello (실행파일)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 자주 사용하는 gcc 옵션&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;디버그 정보 포함&lt;/h3&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;gcc -g hello.c
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; gdb 디버깅 가능&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;경고 표시&lt;/h3&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;gcc -Wall hello.c
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 문제를 미리 발견 가능.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;최적화&lt;/h3&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;gcc -O2 hello.c
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 성능 개선.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 여러 파일 컴파일&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;main.c
math.c
math.h
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;gcc main.c math.c -o program
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무 프로젝트는 대부분 여러 파일로 구성된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 실무에서 중요한 포인트&lt;/h1&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;&lt;b&gt;Makefile&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 편에서 자세히 다룬다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 8편 요약&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;gcc는 단순 컴파일러가 아니다&lt;/li&gt;
&lt;li&gt;전처리 &amp;rarr; 컴파일 &amp;rarr; 어셈블리 &amp;rarr; 링크 단계 존재&lt;/li&gt;
&lt;li&gt;hello.c &amp;rarr; hello.o &amp;rarr; 실행 파일 생성&lt;/li&gt;
&lt;li&gt;디버그(-g)와 경고(-Wall)는 필수 옵션&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메인 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;gcc 사용법&lt;/li&gt;
&lt;li&gt;리눅스 C 컴파일&lt;/li&gt;
&lt;li&gt;gcc 컴파일 과정&lt;/li&gt;
&lt;li&gt;리눅스 gcc 옵션&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서브 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;gcc -Wall 의미&lt;/li&gt;
&lt;li&gt;gcc -g 디버그&lt;/li&gt;
&lt;li&gt;gcc 링크 과정&lt;/li&gt;
&lt;li&gt;리눅스 C 빌드 구조&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Linux 완전 정복/Linux  개발도구</category>
      <category>c컴파일</category>
      <category>gcc</category>
      <category>LinuxProgramming</category>
      <category>리눅스개발</category>
      <category>시스템프로그래밍</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/206</guid>
      <comments>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-8%ED%8E%B8-gcc-%EC%BB%B4%ED%8C%8C%EC%9D%BC-%EA%B5%AC%EC%A1%B0-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4-C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8%EC%9D%B4-%EC%8B%A4%ED%96%89-%ED%8C%8C%EC%9D%BC%EC%9D%B4-%EB%90%98%EB%8A%94-%EA%B3%BC%EC%A0%95#entry206comment</comments>
      <pubDate>Thu, 5 Mar 2026 13:20:23 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스 프로그래밍 시리즈 7편]sed와 awk 실전 정복: 로그를 '가공'하는 진짜 리눅스 실무 기술</title>
      <link>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-7%ED%8E%B8sed%EC%99%80-awk-%EC%8B%A4%EC%A0%84-%EC%A0%95%EB%B3%B5-%EB%A1%9C%EA%B7%B8%EB%A5%BC-%EA%B0%80%EA%B3%B5%ED%95%98%EB%8A%94-%EC%A7%84%EC%A7%9C-%EB%A6%AC%EB%88%85%EC%8A%A4-%EC%8B%A4%EB%AC%B4-%EA%B8%B0%EC%88%A0</link>
      <description>&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-4141655932477481&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;목표: grep을 넘어서 로그를 수정&amp;middot;추출&amp;middot;가공할 수 있다&lt;br /&gt;&amp;nbsp;결과: 반복되는 로그 분석을 자동화할 수 있다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 왜 sed와 awk를 배워야 할까?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6편에서 우리는 grep으로 &quot;찾기&quot;를 배웠다.&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;li&gt;로그 형식 재구성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 가능하게 하는 도구가 바로 &lt;b&gt;sed와 awk&lt;/b&gt;다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. sed 기본 개념 (Stream Editor)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sed는 텍스트를 &quot;치환&quot;하는 데 특화된 도구다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기본 형태&lt;/h2&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;sed 's/원본/변경/' 파일명
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;sed 's/ERROR/WARNING/' server.log
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ERROR를 WARNING으로 변경하여 출력.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ 원본 파일은 수정되지 않음.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전체 치환 (g 옵션)&lt;/h2&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;sed 's/ERROR/WARNING/g' server.log
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;파일 직접 수정 (-i 옵션)&lt;/h2&gt;
&lt;pre class=&quot;matlab&quot;&gt;&lt;code&gt;sed -i 's/ERROR/WARNING/g' server.log
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⚠ 실무에서는 백업 후 사용 권장&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. awk 기본 개념 (컬럼 처리 도구)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;awk는 &quot;공백 기준 컬럼 처리&quot;에 강하다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;예제 로그&lt;/h2&gt;
&lt;pre class=&quot;openscad&quot;&gt;&lt;code&gt;echo &quot;2026-03-01 ERROR DatabaseFail&quot; &amp;gt;&amp;gt; server.log
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;특정 컬럼만 출력&lt;/h2&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;awk '{print $2}' server.log
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$1 &amp;rarr; 첫 번째 컬럼&lt;br /&gt;$2 &amp;rarr; 두 번째 컬럼&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. grep + awk 조합 (실무 핵심)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러 발생 시간만 추출:&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;grep &quot;ERROR&quot; server.log | awk '{print $1}'
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;날짜만 출력 가능&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 특정 조건 필터링&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 컬럼이 ERROR인 줄만 출력:&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;awk '$2 == &quot;ERROR&quot; {print $0}' server.log
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 로그 통계 계산&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러 횟수 세기:&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;awk '$2 == &quot;ERROR&quot; {count++} END {print count}' server.log
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;grep 없이도 가능&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 실습: 로그 자동 분석 스크립트 만들기&lt;/h1&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;nano analyze.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용 작성:&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;#!/bin/bash

LOG=server.log

echo &quot;총 에러 수:&quot;
awk '$2 == &quot;ERROR&quot; {count++} END {print count}' $LOG
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 권한 부여:&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;chmod +x analyze.sh
./analyze.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. sed vs awk 차이 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구분sedawk&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&gt;목적&lt;/td&gt;
&lt;td&gt;문자열 치환&lt;/td&gt;
&lt;td&gt;컬럼/조건 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;사용 난이도&lt;/td&gt;
&lt;td&gt;쉬움&lt;/td&gt;
&lt;td&gt;중간&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;실무 활용&lt;/td&gt;
&lt;td&gt;로그 치환&lt;/td&gt;
&lt;td&gt;로그 분석/통계&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 자주 하는 실수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;sed -i 사용 후 원본 날림&lt;br /&gt;&amp;nbsp;awk에서 따옴표 오류&lt;br /&gt;&amp;nbsp;컬럼 구분자가 공백이 아닐 때 실수&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 7편 요약&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;sed &amp;rarr; 문자열 치환 도구&lt;/li&gt;
&lt;li&gt;awk &amp;rarr; 컬럼 분석 도구&lt;/li&gt;
&lt;li&gt;grep + awk 조합은 실무 핵심&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메인 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리눅스 sed 사용법&lt;/li&gt;
&lt;li&gt;리눅스 awk 사용법&lt;/li&gt;
&lt;li&gt;리눅스 로그 분석 자동화&lt;/li&gt;
&lt;li&gt;awk 컬럼 추출&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서브 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;sed 문자열 치환&lt;/li&gt;
&lt;li&gt;awk 조건 필터링&lt;/li&gt;
&lt;li&gt;grep awk 조합&lt;/li&gt;
&lt;li&gt;리눅스 로그 통계&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Linux 완전 정복/Linux 기초</category>
      <category>awk</category>
      <category>linuxcommand</category>
      <category>sed</category>
      <category>리눅스기초</category>
      <category>리눅스로그분석</category>
      <category>텍스트처리</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/205</guid>
      <comments>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-7%ED%8E%B8sed%EC%99%80-awk-%EC%8B%A4%EC%A0%84-%EC%A0%95%EB%B3%B5-%EB%A1%9C%EA%B7%B8%EB%A5%BC-%EA%B0%80%EA%B3%B5%ED%95%98%EB%8A%94-%EC%A7%84%EC%A7%9C-%EB%A6%AC%EB%88%85%EC%8A%A4-%EC%8B%A4%EB%AC%B4-%EA%B8%B0%EC%88%A0#entry205comment</comments>
      <pubDate>Tue, 3 Mar 2026 12:14:54 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스 프로그래밍 시리즈 6편]grep과 파이프(|) 완전 정복: 리눅스 로그 분석을 지배하는 핵심 기술</title>
      <link>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-6%ED%8E%B8grep%EA%B3%BC-%ED%8C%8C%EC%9D%B4%ED%94%84-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5-%EB%A6%AC%EB%88%85%EC%8A%A4-%EB%A1%9C%EA%B7%B8-%EB%B6%84%EC%84%9D%EC%9D%84-%EC%A7%80%EB%B0%B0%ED%95%98%EB%8A%94-%ED%95%B5%EC%8B%AC-%EA%B8%B0%EC%88%A0</link>
      <description>&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-4141655932477481&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;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;목표: grep과 파이프(|)를 이용해 원하는 데이터를 추출할 수 있다&lt;br /&gt;&amp;nbsp;결과: 서버 로그 분석을 혼자서 수행할 수 있다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 왜 grep과 파이프가 중요한가?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서 리눅스를 쓰는 이유 중 하나는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&quot;로그 분석&quot; 때문이다.&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;li&gt;시간대별 분석&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 모든 작업의 중심이 바로 &lt;b&gt;grep + 파이프(|)&lt;/b&gt; 다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. grep 기본 사용법&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기본 형태&lt;/h2&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;grep &quot;검색어&quot; 파일명
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;grep &quot;ERROR&quot; server.log
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;server.log 파일에서 ERROR가 포함된 줄만 출력.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;자주 쓰는 옵션&lt;/h2&gt;
&lt;pre class=&quot;matlab&quot;&gt;&lt;code&gt;grep -i &quot;error&quot; server.log
&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;i &amp;rarr; 대소문자 무시&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;grep -n &quot;ERROR&quot; server.log
&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;n &amp;rarr; 줄 번호 표시&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;grep -r &quot;ERROR&quot; .
&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;r &amp;rarr; 하위 디렉토리까지 검색&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 파이프(|)란 무엇인가?&lt;/h1&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;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;명령어1 | 명령어2
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. grep + wc 조합 (실무에서 매우 많이 사용)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러 개수 세기:&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;grep &quot;ERROR&quot; server.log | wc -l
&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;wc -l &amp;rarr; 줄 수 계산&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;서버 에러 발생 횟수 확인 가능&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 여러 조건 필터링&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;grep &quot;ERROR&quot; server.log | grep &quot;2026-03-01&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 날짜의 에러만 필터링.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. tail과 함께 실시간 로그 보기&lt;/h1&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;tail -f server.log
&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;pre class=&quot;1c&quot;&gt;&lt;code&gt;tail -f server.log | grep &quot;ERROR&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;실시간 에러 모니터링 가능&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 정규표현식 활용 (기초)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자 포함 줄 검색:&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;grep &quot;[0-9]&quot; file.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IP 주소 포함 줄 검색:&lt;/p&gt;
&lt;pre class=&quot;tex&quot;&gt;&lt;code&gt;grep -E &quot;[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+&quot; access.log
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 실습: 가짜 로그 만들고 분석하기&lt;/h1&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;cd ~/linux-lab
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그 파일 생성:&lt;/p&gt;
&lt;pre class=&quot;openscad&quot;&gt;&lt;code&gt;echo &quot;INFO Start&quot; &amp;gt; server.log
echo &quot;ERROR Database failed&quot; &amp;gt;&amp;gt; server.log
echo &quot;INFO Retry&quot; &amp;gt;&amp;gt; server.log
echo &quot;ERROR Timeout&quot; &amp;gt;&amp;gt; server.log
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러 개수 확인:&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;grep &quot;ERROR&quot; server.log | wc -l
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 실무 시나리오 예시&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;① 웹 서버 500 에러 분석&lt;/h2&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;grep &quot;500&quot; access.log | wc -l
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;② 특정 사용자 요청 추적&lt;/h2&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;grep &quot;192.168.0.10&quot; access.log
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;③ 특정 시간대 필터링&lt;/h2&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;grep &quot;14:3&quot; access.log
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 자주 하는 실수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;로그를 직접 열어서 스크롤로 찾기&lt;br /&gt;&amp;nbsp;대소문자 구분 때문에 검색 실패&lt;br /&gt;&amp;nbsp;파이프 대신 &amp;amp;&amp;amp; 사용&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. grep + 파이프 핵심 정리&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;grep &amp;rarr; 텍스트 필터&lt;/li&gt;
&lt;li&gt;| &amp;rarr; 출력 연결&lt;/li&gt;
&lt;li&gt;wc -l &amp;rarr; 개수 계산&lt;/li&gt;
&lt;li&gt;tail -f &amp;rarr; 실시간 모니터링&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 네 가지 조합은 실무에서 매일 쓰인다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메인 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리눅스 grep 사용법&lt;/li&gt;
&lt;li&gt;리눅스 파이프 사용법&lt;/li&gt;
&lt;li&gt;리눅스 로그 분석&lt;/li&gt;
&lt;li&gt;tail -f 사용법&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서브 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;grep wc 조합&lt;/li&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>Linux 완전 정복/Linux 기초</category>
      <category>grep</category>
      <category>grep wc 조합</category>
      <category>Linux</category>
      <category>TAIL</category>
      <category>리눅스 grep 사용법</category>
      <category>리눅스로그분석</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/204</guid>
      <comments>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-6%ED%8E%B8grep%EA%B3%BC-%ED%8C%8C%EC%9D%B4%ED%94%84-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5-%EB%A6%AC%EB%88%85%EC%8A%A4-%EB%A1%9C%EA%B7%B8-%EB%B6%84%EC%84%9D%EC%9D%84-%EC%A7%80%EB%B0%B0%ED%95%98%EB%8A%94-%ED%95%B5%EC%8B%AC-%EA%B8%B0%EC%88%A0#entry204comment</comments>
      <pubDate>Tue, 3 Mar 2026 12:11:32 +0900</pubDate>
    </item>
    <item>
      <title>[WPF 실무 20편] WPF 애니메이션 기본 (Storyboard 실무 사용 패턴 완전 정리)</title>
      <link>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-20%ED%8E%B8-WPF-%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98-%EA%B8%B0%EB%B3%B8-Storyboard-%EC%8B%A4%EB%AC%B4-%EC%82%AC%EC%9A%A9-%ED%8C%A8%ED%84%B4-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC</link>
      <description>&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-4141655932477481&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버튼이 자연스럽게 나타나고,&lt;br /&gt;패널이 부드럽게 슬라이드되고,&lt;br /&gt;화면 전환이 끊기지 않는 UI.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 모든 것은 WPF의 &lt;b&gt;Storyboard 애니메이션&lt;/b&gt;으로 구현됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는&lt;br /&gt;✔ 기본 애니메이션 구조&lt;br /&gt;✔ Storyboard 사용법&lt;br /&gt;✔ 실무에서 많이 쓰는 패턴&lt;br /&gt;✔ 성능 고려사항&lt;br /&gt;을 정리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. WPF 애니메이션 기본 개념&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF 애니메이션은 다음 구조로 동작합니다.&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;Animation &amp;rarr; Storyboard &amp;rarr; Target 속성 변경
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ DoubleAnimation &amp;rarr; 숫자 변경&lt;br /&gt;✔ ColorAnimation &amp;rarr; 색상 변경&lt;br /&gt;✔ ThicknessAnimation &amp;rarr; Margin 변경&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Storyboard는 여러 애니메이션을 묶는 컨테이너입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 가장 기본적인 예제 (Opacity 페이드인)&lt;/h1&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;Grid&amp;gt;
    &amp;lt;Grid.Triggers&amp;gt;
        &amp;lt;EventTrigger RoutedEvent=&quot;Grid.Loaded&quot;&amp;gt;
            &amp;lt;BeginStoryboard&amp;gt;
                &amp;lt;Storyboard&amp;gt;
                    &amp;lt;DoubleAnimation
                        Storyboard.TargetProperty=&quot;Opacity&quot;
                        From=&quot;0&quot; To=&quot;1&quot;
                        Duration=&quot;0:0:0.5&quot;/&amp;gt;
                &amp;lt;/Storyboard&amp;gt;
            &amp;lt;/BeginStoryboard&amp;gt;
        &amp;lt;/EventTrigger&amp;gt;
    &amp;lt;/Grid.Triggers&amp;gt;
&amp;lt;/Grid&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 화면 로딩 시 0 &amp;rarr; 1로 부드럽게 변경&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 버튼 클릭 시 애니메이션 실행&lt;/h1&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;Button Content=&quot;Click&quot;&amp;gt;
    &amp;lt;Button.Triggers&amp;gt;
        &amp;lt;EventTrigger RoutedEvent=&quot;Button.Click&quot;&amp;gt;
            &amp;lt;BeginStoryboard&amp;gt;
                &amp;lt;Storyboard&amp;gt;
                    &amp;lt;DoubleAnimation
                        Storyboard.TargetProperty=&quot;Width&quot;
                        To=&quot;200&quot;
                        Duration=&quot;0:0:0.3&quot;/&amp;gt;
                &amp;lt;/Storyboard&amp;gt;
            &amp;lt;/BeginStoryboard&amp;gt;
        &amp;lt;/EventTrigger&amp;gt;
    &amp;lt;/Button.Triggers&amp;gt;
&amp;lt;/Button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 클릭 시 Width 변경&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 코드에서 Storyboard 실행하기&lt;/h1&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;Window.Resources&amp;gt;
    &amp;lt;Storyboard x:Key=&quot;FadeOut&quot;&amp;gt;
        &amp;lt;DoubleAnimation
            Storyboard.TargetProperty=&quot;Opacity&quot;
            To=&quot;0&quot;
            Duration=&quot;0:0:0.5&quot;/&amp;gt;
    &amp;lt;/Storyboard&amp;gt;
&amp;lt;/Window.Resources&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;var sb = (Storyboard)FindResource(&quot;FadeOut&quot;);
sb.Begin(MyGrid);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ MVVM 구조에서도 활용 가능&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 실무에서 많이 쓰는 패턴 1 &amp;mdash; 슬라이드 패널&lt;/h1&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;&amp;lt;TranslateTransform x:Key=&quot;SlideTransform&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;&amp;lt;DoubleAnimation
    Storyboard.TargetProperty=&quot;(UIElement.RenderTransform).(TranslateTransform.X)&quot;
    From=&quot;-300&quot; To=&quot;0&quot;
    Duration=&quot;0:0:0.3&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 왼쪽에서 슬라이드 인&lt;br /&gt;✔ 메뉴 패널 구현에 자주 사용&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 실무에서 많이 쓰는 패턴 2 &amp;mdash; Hover 확대 효과&lt;/h1&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;&amp;lt;Style TargetType=&quot;Button&quot;&amp;gt;
    &amp;lt;Setter Property=&quot;RenderTransformOrigin&quot; Value=&quot;0.5,0.5&quot;/&amp;gt;
    &amp;lt;Setter Property=&quot;RenderTransform&quot;&amp;gt;
        &amp;lt;Setter.Value&amp;gt;
            &amp;lt;ScaleTransform/&amp;gt;
        &amp;lt;/Setter.Value&amp;gt;
    &amp;lt;/Setter&amp;gt;

    &amp;lt;Style.Triggers&amp;gt;
        &amp;lt;Trigger Property=&quot;IsMouseOver&quot; Value=&quot;True&quot;&amp;gt;
            &amp;lt;Trigger.EnterActions&amp;gt;
                &amp;lt;BeginStoryboard&amp;gt;
                    &amp;lt;Storyboard&amp;gt;
                        &amp;lt;DoubleAnimation
                            Storyboard.TargetProperty=&quot;(UIElement.RenderTransform).(ScaleTransform.ScaleX)&quot;
                            To=&quot;1.1&quot; Duration=&quot;0:0:0.2&quot;/&amp;gt;
                        &amp;lt;DoubleAnimation
                            Storyboard.TargetProperty=&quot;(UIElement.RenderTransform).(ScaleTransform.ScaleY)&quot;
                            To=&quot;1.1&quot; Duration=&quot;0:0:0.2&quot;/&amp;gt;
                    &amp;lt;/Storyboard&amp;gt;
                &amp;lt;/BeginStoryboard&amp;gt;
            &amp;lt;/Trigger.EnterActions&amp;gt;
        &amp;lt;/Trigger&amp;gt;
    &amp;lt;/Style.Triggers&amp;gt;
&amp;lt;/Style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 버튼 Hover 시 자연스러운 확대 효과&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7.&amp;nbsp; 실무에서 자주 하는 실수 1 &amp;mdash; Layout 속성 애니메이션 남발&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Width / Height / Margin 변경은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 레이아웃 재계산 발생&lt;br /&gt;&amp;rarr; 성능 저하&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실무 권장&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ RenderTransform 사용&lt;br /&gt;✔ Opacity 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레이아웃 변경 대신 렌더링 레벨 애니메이션 사용&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8.&amp;nbsp; 실무에서 자주 하는 실수 2 &amp;mdash; Storyboard 재사용 안 함&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매번 코드에서 새로 생성하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 성능 저하&lt;br /&gt;&amp;rarr; GC 부담 증가&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ Resource에 등록 후 재사용&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 애니메이션 성능 팁&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ Duration 0.2~0.3초가 가장 자연스러움&lt;br /&gt;✔ EasingFunction 사용하면 부드러움 증가&lt;br /&gt;✔ 너무 많은 동시 애니메이션 금지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;DoubleAnimation.EasingFunction&amp;gt;
    &amp;lt;CubicEase EasingMode=&quot;EaseOut&quot;/&amp;gt;
&amp;lt;/DoubleAnimation.EasingFunction&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 한 줄 핵심 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF 애니메이션은 레이아웃이 아니라 RenderTransform을 움직여라.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF Storyboard 사용법&lt;br /&gt;WPF 애니메이션 예제&lt;br /&gt;WPF DoubleAnimation&lt;br /&gt;WPF RenderTransform 애니메이션&lt;br /&gt;WPF 버튼 Hover 효과&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발이야기/WPF UI 커스터마이징</category>
      <category>csharp wpf</category>
      <category>WPF</category>
      <category>wpf animation</category>
      <category>wpf storyboard</category>
      <category>wpf ui</category>
      <category>닷넷 개발</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/203</guid>
      <comments>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-20%ED%8E%B8-WPF-%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98-%EA%B8%B0%EB%B3%B8-Storyboard-%EC%8B%A4%EB%AC%B4-%EC%82%AC%EC%9A%A9-%ED%8C%A8%ED%84%B4-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC#entry203comment</comments>
      <pubDate>Tue, 3 Mar 2026 11:36:33 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스 프로그래밍 시리즈 5편] 리눅스 프로세스 완전 정복: ps, top, 백그라운드 실행, kill까지 실무 정리</title>
      <link>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-5%ED%8E%B8-%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5-ps-top-%EB%B0%B1%EA%B7%B8%EB%9D%BC%EC%9A%B4%EB%93%9C-%EC%8B%A4%ED%96%89-kill%EA%B9%8C%EC%A7%80-%EC%8B%A4%EB%AC%B4-%EC%A0%95%EB%A6%AC</link>
      <description>&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-4141655932477481&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;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;목표: 리눅스에서 실행되는 프로그램(프로세스)의 구조를 이해한다&lt;br /&gt;&amp;nbsp;결과: 프로세스 문제를 스스로 분석하고 제어할 수 있다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 프로세스란 무엇인가?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스에서 실행 중인 모든 프로그램은 &lt;b&gt;프로세스(Process)&lt;/b&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;li&gt;내가 실행한 C 프로그램&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;&amp;nbsp;리눅스는 결국 &quot;프로세스를 관리하는 OS&quot;라고 봐도 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 현재 실행 중인 프로세스 확인하기&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;① ps 명령어&lt;/h2&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;ps
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 현재 터미널에서 실행 중인 프로세스만 표시된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;② 전체 프로세스 보기 (실무에서 자주 사용)&lt;/h2&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;ps aux
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 예:&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;USER   PID  %CPU %MEM  VSZ   RSS TTY  STAT START  TIME COMMAND
&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;USER &amp;rarr; 실행 사용자&lt;/li&gt;
&lt;li&gt;PID &amp;rarr; 프로세스 ID&lt;/li&gt;
&lt;li&gt;%CPU &amp;rarr; CPU 사용률&lt;/li&gt;
&lt;li&gt;%MEM &amp;rarr; 메모리 사용률&lt;/li&gt;
&lt;li&gt;COMMAND &amp;rarr; 실행 명령&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;PID는 반드시 기억해야 한다. (kill 할 때 사용)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 실시간 모니터링: top&lt;/h1&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;top
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실시간으로 CPU, 메모리 사용량을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료: q&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;htop (추천)&lt;/h2&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;sudo apt install htop
htop
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 직관적인 프로세스 관리 도구.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 포그라운드 vs 백그라운드 실행&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;① 포그라운드 실행&lt;/h2&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;./hello
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행이 끝날 때까지 터미널 점유.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;② 백그라운드 실행 (&amp;amp; 사용)&lt;/h2&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;./hello &amp;amp;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 예:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;[1] 2345
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2345 = PID&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;③ 실행 중인 작업 확인&lt;/h2&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;jobs
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;④ 다시 포그라운드로 가져오기&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;fg %1
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 프로세스 종료하기&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;① 정상 종료 요청&lt;/h2&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;kill PID
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 SIGTERM(15) 신호 전송.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;② 강제 종료&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;kill -9 PID
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-9 = SIGKILL&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⚠ 주의: 강제 종료는 마지막 수단&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 실습: 프로세스 직접 다뤄보기&lt;/h1&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;sleep 100
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 터미널에서:&lt;/p&gt;
&lt;pre class=&quot;perl&quot;&gt;&lt;code&gt;ps aux | grep sleep
kill PID
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 프로세스 상태 이해 (STAT 컬럼)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ps aux 출력에서 STAT 값 예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;R &amp;rarr; Running&lt;/li&gt;
&lt;li&gt;S &amp;rarr; Sleeping&lt;/li&gt;
&lt;li&gt;Z &amp;rarr; Zombie&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Zombie 프로세스란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부모 프로세스가 자식 종료 상태를 회수(wait)하지 않은 상태.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;시스템 프로그래밍에서 fork/exec 배울 때 등장.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. nice와 우선순위 조정&lt;/h1&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;nice -n 10 ./program
&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;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;renice 5 -p PID
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 실무에서 자주 겪는 상황&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;① 서버 CPU 100% 사용&lt;/h3&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;top
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원인 프로세스 확인 &amp;rarr; 필요 시 kill&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;② 포트가 이미 사용 중&lt;/h3&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;sudo netstat -tulpn
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PID 확인 후 종료&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;③ 백그라운드 작업이 멈춘 것처럼 보임&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;jobs 확인 후 fg&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 자주 하는 실수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;무조건 kill -9 사용&lt;br /&gt;&amp;nbsp;root 권한으로 모든 프로세스 종료&lt;br /&gt;&amp;nbsp;PID 잘못 확인하고 다른 프로세스 종료&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 프로세스 관리 핵심 정리&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ps aux &amp;rarr; 전체 프로세스 확인&lt;/li&gt;
&lt;li&gt;top &amp;rarr; 실시간 모니터링&lt;/li&gt;
&lt;li&gt;kill PID &amp;rarr; 종료 요청&lt;/li&gt;
&lt;li&gt;kill -9 PID &amp;rarr; 강제 종료&lt;/li&gt;
&lt;li&gt;jobs / fg / &amp;amp; &amp;rarr; 작업 제어&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스 운영에서 프로세스 제어는 기본기다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메인 키워드&lt;/h2&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;ps aux 사용법&lt;/li&gt;
&lt;li&gt;리눅스 kill 명령어&lt;/li&gt;
&lt;li&gt;리눅스 top 사용법&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서브 키워드&lt;/h2&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;리눅스 jobs 명령어&lt;/li&gt;
&lt;li&gt;리눅스 zombie 프로세스&lt;/li&gt;
&lt;li&gt;리눅스 nice renice&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Linux 완전 정복/Linux 기초</category>
      <category>kill명령어</category>
      <category>ps명령어</category>
      <category>top사용법</category>
      <category>리눅스기초</category>
      <category>리눅스프로세스</category>
      <category>시스템프로그래밍</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/202</guid>
      <comments>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-5%ED%8E%B8-%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5-ps-top-%EB%B0%B1%EA%B7%B8%EB%9D%BC%EC%9A%B4%EB%93%9C-%EC%8B%A4%ED%96%89-kill%EA%B9%8C%EC%A7%80-%EC%8B%A4%EB%AC%B4-%EC%A0%95%EB%A6%AC#entry202comment</comments>
      <pubDate>Sun, 1 Mar 2026 11:55:54 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스 프로그래밍 시리즈 4편] 리눅스 권한 완전 정복: chmod, chown, sudo, 실무에서 진짜 쓰는 개념 정리</title>
      <link>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-4%ED%8E%B8-%EB%A6%AC%EB%88%85%EC%8A%A4-%EA%B6%8C%ED%95%9C-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5-chmod-chown-sudo-%EC%8B%A4%EB%AC%B4%EC%97%90%EC%84%9C-%EC%A7%84%EC%A7%9C-%EC%93%B0%EB%8A%94-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC</link>
      <description>&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-4141655932477481&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;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;목표: 리눅스 권한 구조를 완전히 이해한다&lt;br /&gt;&amp;nbsp;결과: 권한 문제를 스스로 해결할 수 있다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 리눅스에서 가장 많이 막히는 지점 = 권한&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스를 쓰다 보면 반드시 보게 되는 메시지:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;Permission denied
&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;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;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;&amp;nbsp;권한은 선택이 아니라 필수다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 리눅스 권한 구조의 핵심 개념&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스 파일에는 세 가지 주체가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) Owner (소유자)&lt;br /&gt;2) Group (그룹)&lt;br /&gt;3) Others (그 외 사용자)&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;r (read) : 읽기&lt;/li&gt;
&lt;li&gt;w (write) : 쓰기&lt;/li&gt;
&lt;li&gt;x (execute) : 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. ls -l로 권한 읽는 법&lt;/h1&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;ls -l
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시 출력:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;-rwxr-xr-- 1 sujin sujin  1200 Jun 10 10:00 test.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조 분석:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;- rwx r-x r--
  │   │   │
  │   │   └─ Others
  │   └───── Group
  └──────── Owner
&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;첫 글자: 파일 타입&lt;/li&gt;
&lt;li&gt;rwx: 소유자 권한&lt;/li&gt;
&lt;li&gt;r-x: 그룹 권한&lt;/li&gt;
&lt;li&gt;r--: 기타 사용자 권한&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. chmod 사용법 (권한 변경)&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;방법 ① 기호 방식&lt;/h2&gt;
&lt;pre class=&quot;sas&quot;&gt;&lt;code&gt;chmod u+x file.sh
&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;u = owner&lt;/li&gt;
&lt;li&gt;g = group&lt;/li&gt;
&lt;li&gt;o = others&lt;/li&gt;
&lt;li&gt;a = all&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;chmod go-w test.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;방법 ② 숫자 방식 (실무에서 더 많이 사용)&lt;/h2&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;r = 4&lt;/li&gt;
&lt;li&gt;w = 2&lt;/li&gt;
&lt;li&gt;x = 1&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;chmod 755 script.sh
&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;7 = 4+2+1 (rwx)&lt;/li&gt;
&lt;li&gt;5 = 4+1 (r-x)&lt;/li&gt;
&lt;li&gt;5 = 4+1 (r-x)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. chown (소유자 변경)&lt;/h1&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;sudo chown user:group file.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;sudo chown sujin:sujin test.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 디렉토리 권한의 특별한 점&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디렉토리에서 x 권한은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&quot;그 안으로 들어갈 수 있는 권한&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;chmod 700 private_folder
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소유자만 접근 가능.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. sudo의 의미&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sudo는 관리자 권한으로 명령 실행.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;sudo apt update
&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;습관적으로 sudo 남발 금지&lt;br /&gt;&amp;nbsp;모든 파일을 777로 만드는 행위 금지&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 절대 하면 안 되는 실수&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;chmod 777&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;chmod 777 file.txt
&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 실습: 권한 직접 테스트&lt;/h1&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;cd ~/linux-lab

echo &quot;secret&quot; &amp;gt; secret.txt
chmod 600 secret.txt
ls -l secret.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;600 의미:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소유자: rw&lt;/li&gt;
&lt;li&gt;그룹: 없음&lt;/li&gt;
&lt;li&gt;기타: 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 실무에서 자주 보는 상황&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;① 웹 서버가 파일을 못 읽는 경우&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 웹 서버 실행 계정 확인&lt;br /&gt;&amp;rarr; 그룹 권한 확인&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;② SSH 키 권한 오류&lt;/h3&gt;
&lt;pre class=&quot;inform7&quot;&gt;&lt;code&gt;Permissions are too open
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;chmod 600 ~/.ssh/id_rsa
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 권한 문제 해결 순서&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) ls -l 로 현재 권한 확인&lt;br /&gt;2) 소유자 확인&lt;br /&gt;3) 그룹 확인&lt;br /&gt;4) 필요한 최소 권한만 부여&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. 4편 요약&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;권한은 Owner / Group / Others 구조&lt;/li&gt;
&lt;li&gt;chmod 숫자 방식 필수 숙지&lt;/li&gt;
&lt;li&gt;777은 금지&lt;/li&gt;
&lt;li&gt;디렉토리 x 권한의 의미 이해&lt;/li&gt;
&lt;li&gt;sudo 남발 금지&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메인 키워드&lt;/h2&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;chmod 사용법&lt;/li&gt;
&lt;li&gt;chown 사용법&lt;/li&gt;
&lt;li&gt;리눅스 permission denied 해결&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서브 키워드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리눅스 755 의미&lt;/li&gt;
&lt;li&gt;리눅스 777 위험성&lt;/li&gt;
&lt;li&gt;리눅스 파일 권한 구조&lt;/li&gt;
&lt;li&gt;리눅스 sudo 사용법&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Linux 완전 정복/Linux 기초</category>
      <category>chmod</category>
      <category>chown</category>
      <category>permissionDenied</category>
      <category>리눅스권한</category>
      <category>리눅스보안</category>
      <category>시스템프로그래밍</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/201</guid>
      <comments>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-4%ED%8E%B8-%EB%A6%AC%EB%88%85%EC%8A%A4-%EA%B6%8C%ED%95%9C-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5-chmod-chown-sudo-%EC%8B%A4%EB%AC%B4%EC%97%90%EC%84%9C-%EC%A7%84%EC%A7%9C-%EC%93%B0%EB%8A%94-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC#entry201comment</comments>
      <pubDate>Sun, 1 Mar 2026 11:19:18 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스 프로그래밍 시리즈 3편] 리눅스 파일 시스템 완전 정복: 경로, 구조, 링크, 실무 개념까지 한 번에</title>
      <link>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-3%ED%8E%B8</link>
      <description>&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-4141655932477481&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;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;목표: 리눅스 파일 시스템의 구조를 정확히 이해한다&lt;br /&gt;&amp;nbsp;결과: 경로/권한/링크 개념을 실무 수준으로 이해&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 리눅스 파일 시스템이 중요한 이유&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스를 공부하다 막히는 가장 큰 이유는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&quot;경로가 헷갈려서&quot;&lt;br /&gt;&amp;nbsp;&quot;파일이 어디 있는지 몰라서&quot;&lt;br /&gt;&amp;nbsp;&quot;권한 때문에 접근이 안 돼서&quot;&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;장치(device)&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;br /&gt;리눅스의 절반을 이해한 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 리눅스 디렉토리 구조 한눈에 보기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;루트 디렉토리부터 시작한다.&lt;/p&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;/
├── bin
├── boot
├── dev
├── etc
├── home
├── lib
├── proc
├── root
├── tmp
├── usr
└── var
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조는 대부분의 배포판에서 거의 동일하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 핵심 디렉토리 설명 (실무 기준)&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;/ (루트)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 파일의 시작점&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;/home&lt;/h2&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 class=&quot;arduino&quot;&gt;&lt;code&gt;/home/sujin
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;우리가 작업하는 공간&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;/etc&lt;/h2&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 class=&quot;dts&quot;&gt;&lt;code&gt;/etc/passwd
/etc/ssh/sshd_config
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;서버 문제 생기면 자주 보게 됨&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;/var&lt;/h2&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 class=&quot;excel&quot;&gt;&lt;code&gt;/var/log
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;로그 분석할 때 반드시 사용&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;/usr&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램 설치 위치&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;/tmp&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임시 파일&lt;br /&gt;재부팅 시 삭제될 수 있음&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 절대 경로 vs 상대 경로&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;절대 경로&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;루트(/)부터 시작&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;/home/user/linux-lab
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;상대 경로&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 위치 기준&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;../
./file.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 경로 실습&lt;/h1&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;pwd
ls /
cd /home
ls
cd ~
pwd
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;~ 는 현재 사용자 홈 디렉토리&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 파일 확인 기본 명령어&lt;/h1&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;ls
ls -l
ls -al
&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;l &amp;rarr; 상세 정보&lt;/li&gt;
&lt;li&gt;a &amp;rarr; 숨김 파일 포함&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 숨김 파일이란?&lt;/h1&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 class=&quot;asciidoc&quot;&gt;&lt;code&gt;.bashrc
.gitconfig
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정 파일이 대부분 숨김 처리됨&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 하드 링크 vs 심볼릭 링크&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;심볼릭 링크 (바로가기 개념)&lt;/h2&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;ln -s original.txt link.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;하드 링크&lt;/h2&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;ln original.txt hardlink.txt
&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;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&gt;원본 삭제 시&lt;/td&gt;
&lt;td&gt;같이 깨짐&lt;/td&gt;
&lt;td&gt;유지됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;다른 파티션&lt;/td&gt;
&lt;td&gt;가능&lt;/td&gt;
&lt;td&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 파일 타입 이해&lt;/h1&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;ls -l
&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;: 일반 파일&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;d : 디렉토리&lt;/li&gt;
&lt;li&gt;l : 링크&lt;/li&gt;
&lt;li&gt;c : 문자 장치&lt;/li&gt;
&lt;li&gt;b : 블록 장치&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스는 장치도 파일로 관리한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 실무 미션&lt;/h1&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;~/linux-lab 이동&lt;/li&gt;
&lt;li&gt;test.txt 생성&lt;/li&gt;
&lt;li&gt;심볼릭 링크 생성&lt;/li&gt;
&lt;li&gt;원본 삭제 후 링크 확인&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;cd ~/linux-lab
echo &quot;hello&quot; &amp;gt; test.txt
ln -s test.txt link.txt
rm test.txt
cat link.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 에러가 나는지 생각해보기.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 자주 하는 실수&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;절대 경로와 상대 경로 혼동&lt;br /&gt;&amp;nbsp;sudo 없이 /etc 수정 시도&lt;br /&gt;&amp;nbsp;/tmp 파일 영구 저장 착각&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;12. 3편 요약&lt;/h1&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;/home, /etc, /var 구조 중요&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>Linux 완전 정복/Linux 기초</category>
      <category>file command</category>
      <category>file type</category>
      <category>Linux</category>
      <category>linux path</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/200</guid>
      <comments>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-3%ED%8E%B8#entry200comment</comments>
      <pubDate>Fri, 27 Feb 2026 10:30:35 +0900</pubDate>
    </item>
    <item>
      <title>[WPF 실무 19편] WPF 다크모드 구현 (런타임 테마 전환 구조 완전 정리)</title>
      <link>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-19%ED%8E%B8-WPF-%EB%8B%A4%ED%81%AC%EB%AA%A8%EB%93%9C-%EA%B5%AC%ED%98%84-%EB%9F%B0%ED%83%80%EC%9E%84-%ED%85%8C%EB%A7%88-%EC%A0%84%ED%99%98-%EA%B5%AC%EC%A1%B0-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC</link>
      <description>&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-4141655932477481&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요즘 대부분의 프로그램은 다크모드를 지원합니다.&lt;br /&gt;단순히 배경색만 바꾸는 것이 아니라,&lt;br /&gt;&amp;nbsp;전체 테마 구조를 전환할 수 있어야 진짜 실무 구현입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 WPF에서&lt;br /&gt;✔ 라이트 / 다크 모드 전환&lt;br /&gt;✔ 런타임 테마 변경&lt;br /&gt;✔ ResourceDictionary 기반 테마 구조&lt;br /&gt;를 실무 기준으로 정리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 다크모드를 단순 색 변경으로 구현하면 안 되는 이유&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;잘못된 방식&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;Background=&quot;Black&quot;
Foreground=&quot;White&quot;
&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;&amp;nbsp;실무에서는 반드시 테마 구조로 관리해야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 테마 파일 분리하기&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) LightTheme.xaml&lt;/h2&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;
    #FFFFFF
    #222222

    

    


&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) DarkTheme.xaml&lt;/h2&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;
    #1E1E1E
    #F0F0F0

    

    


&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. App.xaml 기본 테마 적용&lt;/h1&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;Application.Resources&amp;gt;
    &amp;lt;ResourceDictionary&amp;gt;
        &amp;lt;ResourceDictionary.MergedDictionaries&amp;gt;
            &amp;lt;ResourceDictionary Source=&quot;Themes/LightTheme.xaml&quot;/&amp;gt;
        &amp;lt;/ResourceDictionary.MergedDictionaries&amp;gt;
    &amp;lt;/ResourceDictionary&amp;gt;
&amp;lt;/Application.Resources&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 런타임 테마 변경 구현&lt;/h1&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public static class ThemeManager
{
    public static void ApplyTheme(string themeName)
    {
        var dict = new ResourceDictionary();
        dict.Source = new Uri($&quot;Themes/{themeName}.xaml&quot;, UriKind.Relative);

        Application.Current.Resources.MergedDictionaries.Clear();
        Application.Current.Resources.MergedDictionaries.Add(dict);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용:&lt;/p&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;ThemeManager.ApplyTheme(&quot;DarkTheme.xaml&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 즉시 전체 UI 변경&lt;br /&gt;✔ 앱 재시작 불필요&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. XAML에서 브러시 사용 방법&lt;/h1&gt;
&lt;pre class=&quot;dust&quot;&gt;&lt;code&gt;&amp;lt;Grid Background=&quot;{DynamicResource BackgroundBrush}&quot;&amp;gt;
    &amp;lt;TextBlock Text=&quot;Hello&quot;
               Foreground=&quot;{DynamicResource ForegroundBrush}&quot;/&amp;gt;
&amp;lt;/Grid&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⚠ 반드시 &lt;b&gt;DynamicResource&lt;/b&gt; 사용해야 런타임 변경 반영됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6.&amp;nbsp; 실무에서 자주 하는 실수 1 &amp;mdash; StaticResource 사용&lt;/h1&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;Background=&quot;{StaticResource BackgroundBrush}&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;증상&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테마 바꿔도 UI 안 바뀜&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;Background=&quot;{DynamicResource BackgroundBrush}&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7.&amp;nbsp; 실무에서 자주 하는 실수 2 &amp;mdash; 리소스 전체 Clear()&lt;/h1&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;Application.Current.Resources.MergedDictionaries.Clear();
&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;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실무 추천 구조&lt;/h2&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Resources/
 ├ BaseStyles.xaml
 ├ LightTheme.xaml
 └ DarkTheme.xaml
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 BaseStyles는 유지한 채&lt;br /&gt;테마만 교체하도록 구성하는 것이 안전합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 실무 추천 테마 구조&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 색상만 테마 파일에 정의&lt;br /&gt;✔ 스타일은 BaseStyles에서 정의&lt;br /&gt;✔ 브러시 키는 동일하게 유지&lt;br /&gt;✔ UI는 DynamicResource로 참조&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조가 확장성과 유지보수에 가장 좋습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 고급 팁 &amp;mdash; 사용자 설정 저장&lt;/h1&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;Settings 파일&lt;/li&gt;
&lt;li&gt;Registry&lt;/li&gt;
&lt;li&gt;JSON 파일&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에 저장하고&lt;br /&gt;앱 시작 시 자동 적용하면 완성도 &amp;uarr;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 한 줄 핵심 정리&lt;/h1&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF 다크모드 구현&lt;br /&gt;WPF 테마 전환&lt;br /&gt;WPF 런타임 테마 변경&lt;br /&gt;WPF DynamicResource 사용법&lt;br /&gt;WPF ResourceDictionary 테마&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발이야기/WPF UI 커스터마이징</category>
      <category>csharp wpf</category>
      <category>WPF</category>
      <category>wpf resourcedictionary</category>
      <category>wpf 다크모드</category>
      <category>wpf 테마</category>
      <category>닷넷 개발</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/199</guid>
      <comments>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-19%ED%8E%B8-WPF-%EB%8B%A4%ED%81%AC%EB%AA%A8%EB%93%9C-%EA%B5%AC%ED%98%84-%EB%9F%B0%ED%83%80%EC%9E%84-%ED%85%8C%EB%A7%88-%EC%A0%84%ED%99%98-%EA%B5%AC%EC%A1%B0-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC#entry199comment</comments>
      <pubDate>Fri, 27 Feb 2026 10:27:30 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스 프로그래밍 시리즈 2편] 리눅스 실습 환경 만들기 완벽 가이드 (WSL2 설치부터 개발 세팅까지)</title>
      <link>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-2%ED%8E%B8</link>
      <description>&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-4141655932477481&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;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;대상: Windows 사용자 기준&lt;br /&gt;&amp;nbsp;목표: 리눅스 프로그래밍을 위한 안정적인 실습 환경을 구축한다&lt;br /&gt;&amp;nbsp;결과: C 컴파일 및 개발 준비 완료 상태&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 왜 실습 환경이 중요한가?&lt;/h1&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;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;따라서 이번 편에서는 &lt;b&gt;가장 현실적이고 안정적인 개발 환경 구성 방법&lt;/b&gt;을 다룬다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 리눅스를 설치하는 방법 3가지 비교&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;① WSL2 (가장 추천 )&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows 안에서 리눅스를 실행하는 방식&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&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;VSCode 연동 쉬움&lt;/li&gt;
&lt;li&gt;Windows 파일 접근 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;대부분의 개발 학습에는 충분하며 가장 추천&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;② 가상머신 (VirtualBox / VMware)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&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;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;서버 운영 실습까지 할 경우 추천&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;③ 듀얼부팅&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&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;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;초보자 비추천&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. WSL2 설치 방법 (Windows 10/11)&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1단계: PowerShell 관리자 실행&lt;/h2&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;wsl --install
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치 후 재부팅.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2단계: 설치 확인&lt;/h2&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;wsl -l -v
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ubuntu가 있고 Version이 2인지 확인.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3단계: 리눅스 접속&lt;/h2&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;wsl
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 시작 메뉴 &amp;rarr; Ubuntu 실행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 실행 시 사용자 계정 생성.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 기본 패키지 세팅 (필수)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스 접속 후 실행:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sudo apt update
sudo apt upgrade -y
sudo apt install build-essential git curl vim net-tools -y
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;각 패키지 설명&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;build-essential &amp;rarr; gcc, make 포함&lt;/li&gt;
&lt;li&gt;git &amp;rarr; 소스 관리&lt;/li&gt;
&lt;li&gt;curl &amp;rarr; 네트워크 테스트&lt;/li&gt;
&lt;li&gt;vim &amp;rarr; 텍스트 편집&lt;/li&gt;
&lt;li&gt;net-tools &amp;rarr; 네트워크 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 개발자 기본 환경 설정&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;① 실습 폴더 생성&lt;/h2&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;mkdir -p ~/linux-lab
cd ~/linux-lab
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;② Git 사용자 정보 설정&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;git config --global user.name &quot;YourName&quot;
git config --global user.email &quot;you@example.com&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;③ alias 설정 (생산성 향상)&lt;/h2&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;nano ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 추가:&lt;/p&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;alias ll='ls -alF'
alias gs='git status'
alias ..='cd ..'
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적용:&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;source ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. VSCode 연동 (강력 추천)&lt;/h1&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;VSCode 설치&lt;/li&gt;
&lt;li&gt;Remote - WSL 확장 설치&lt;/li&gt;
&lt;li&gt;WSL 터미널에서 실행&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;code .
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 리눅스 환경에서 바로 코드 작성 가능.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. C 컴파일 테스트 (환경 검증)&lt;/h1&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;nano hello.c
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용 입력:&lt;/p&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;

int main() {
    printf(&quot;Hello Linux!\n&quot;);
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일 및 실행:&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;gcc hello.c -o hello
./hello
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상 출력되면 환경 구축 성공.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 자주 발생하는 오류 해결&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;sudo 권한 문제&lt;/h2&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;groups
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sudo 그룹 포함 여부 확인.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;apt update 실패&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 상태 확인:&lt;/p&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;ping google.com
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;gcc 명령어 없음&lt;/h2&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;sudo apt install build-essential -y
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. WSL2 vs VM 선택 가이드 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목적추천&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&gt;리눅스 기초 공부&lt;/td&gt;
&lt;td&gt;WSL2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C 시스템 프로그래밍&lt;/td&gt;
&lt;td&gt;WSL2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;네트워크 서버 실습&lt;/td&gt;
&lt;td&gt;VM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;실제 서버 운영 연습&lt;/td&gt;
&lt;td&gt;VM&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 2편 요약&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가장 현실적인 선택은 WSL2&lt;/li&gt;
&lt;li&gt;필수 패키지: build-essential&lt;/li&gt;
&lt;li&gt;hello.c 컴파일로 환경 검증&lt;/li&gt;
&lt;li&gt;alias 설정으로 생산성 향상&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;&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;&lt;span&gt;리눅스 설치 방법&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;WSL2 설치 방법&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;리눅스 개발 환경 구축&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;리눅스 프로그래밍 환경 세팅&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Ubuntu WSL 설치&lt;/span&gt;&lt;/li&gt;
&lt;/ul&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;&lt;span&gt;Windows에서 리눅스 설치&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;WSL2 Ubuntu 설치 가이드&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;리눅스 C 컴파일 환경 만들기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;build-essential 설치&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;리눅스 gcc 설치 방법&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;리눅스 개발자 세팅&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;VSCode WSL 연동 방법&lt;/span&gt;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Linux 완전 정복/Linux 환경 준비</category>
      <category>C언어</category>
      <category>gcc</category>
      <category>Linux</category>
      <category>Linux install</category>
      <category>ubntu</category>
      <category>wsl</category>
      <category>WSL2</category>
      <category>리눅스 설치</category>
      <category>리눅스개발환경</category>
      <category>리눅스프로그래밍</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/198</guid>
      <comments>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-2%ED%8E%B8#entry198comment</comments>
      <pubDate>Thu, 26 Feb 2026 17:23:44 +0900</pubDate>
    </item>
    <item>
      <title>[WPF 실무 18편] WPF 창 테두리 제거 + 커스텀 타이틀바 구현 (드래그 이동까지 완전 정리)</title>
      <link>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-18%ED%8E%B8-WPF-%EC%B0%BD-%ED%85%8C%EB%91%90%EB%A6%AC-%EC%A0%9C%EA%B1%B0-%EC%BB%A4%EC%8A%A4%ED%85%80-%ED%83%80%EC%9D%B4%ED%8B%80%EB%B0%94-%EA%B5%AC%ED%98%84-%EB%93%9C%EB%9E%98%EA%B7%B8-%EC%9D%B4%EB%8F%99%EA%B9%8C%EC%A7%80-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC</link>
      <description>&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-4141655932477481&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 Window 테두리가 마음에 들지 않거나,&lt;br /&gt;장비 UI / 기업용 프로그램처럼 완전한 커스텀 디자인이 필요할 때가 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 때 사용하는 것이 바로&lt;br /&gt;&lt;b&gt;WindowStyle 제거 + 커스텀 타이틀바 구현&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 창 테두리 제거, 드래그 이동, 최소화/최대화 버튼 구현까지 실무 기준으로 정리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 기본 창 테두리 제거하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 Window 기본 스타일을 제거합니다.&lt;/p&gt;
&lt;pre class=&quot;protobuf&quot;&gt;&lt;code&gt;&amp;lt;Window
    WindowStyle=&quot;None&quot;
    ResizeMode=&quot;CanResize&quot;
    Background=&quot;White&quot;&amp;gt;
&amp;lt;/Window&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ WindowStyle=None &amp;rarr; 기본 타이틀바 제거&lt;br /&gt;✔ ResizeMode=CanResize &amp;rarr; 크기 조절 유지 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 완전한 빈 창이 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 커스텀 타이틀바 만들기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상단에 Grid 또는 Border를 만들어 직접 타이틀 영역을 구성합니다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;Grid&amp;gt;
    &amp;lt;Border Background=&quot;#2C3E50&quot;
            Height=&quot;40&quot;
            MouseLeftButtonDown=&quot;TitleBar_MouseDown&quot;&amp;gt;
        &amp;lt;Grid&amp;gt;
            &amp;lt;TextBlock Text=&quot;My Application&quot;
                       VerticalAlignment=&quot;Center&quot;
                       Margin=&quot;15,0&quot;
                       Foreground=&quot;White&quot;/&amp;gt;
        &amp;lt;/Grid&amp;gt;
    &amp;lt;/Border&amp;gt;

    &amp;lt;Grid Margin=&quot;0,40,0,0&quot;&amp;gt;
        &amp;lt;!-- 실제 콘텐츠 영역 --&amp;gt;
    &amp;lt;/Grid&amp;gt;
&amp;lt;/Grid&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 창 드래그 이동 구현&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타이틀바 영역에서 DragMove()를 호출합니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;private void TitleBar_MouseDown(object sender, MouseButtonEventArgs e)
{
    if (e.LeftButton == MouseButtonState.Pressed)
        this.DragMove();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 기본 타이틀바와 동일한 이동 기능 구현&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 최소화 / 최대화 / 닫기 버튼 구현&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;XAML 버튼 추가&lt;/h3&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;StackPanel Orientation=&quot;Horizontal&quot;
            HorizontalAlignment=&quot;Right&quot;&amp;gt;
    &amp;lt;Button Content=&quot;_&quot; Click=&quot;Minimize_Click&quot;/&amp;gt;
    &amp;lt;Button Content=&quot;□&quot; Click=&quot;Maximize_Click&quot;/&amp;gt;
    &amp;lt;Button Content=&quot;X&quot; Click=&quot;Close_Click&quot;/&amp;gt;
&amp;lt;/StackPanel&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CodeBehind&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;private void Minimize_Click(object sender, RoutedEventArgs e)
{
    WindowState = WindowState.Minimized;
}

private void Maximize_Click(object sender, RoutedEventArgs e)
{
    WindowState = WindowState == WindowState.Maximized
        ? WindowState.Normal
        : WindowState.Maximized;
}

private void Close_Click(object sender, RoutedEventArgs e)
{
    Close();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5.&amp;nbsp; 실무에서 자주 하는 실수 1 &amp;mdash; ResizeMode=None 설정&lt;/h1&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;ResizeMode=&quot;NoResize&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;증상&lt;/h2&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;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;ResizeMode=&quot;CanResize&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6.&amp;nbsp; 실무에서 자주 하는 실수 2 &amp;mdash; 최대화 시 화면 잘림&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WindowStyle=None 상태에서 최대화하면&lt;br /&gt;작업 표시줄을 침범하는 경우가 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결 방법&lt;/h2&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;WindowStyle=&quot;None&quot;
AllowsTransparency=&quot;False&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;this.MaxHeight = SystemParameters.WorkArea.Height;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업 표시줄 영역 고려 필요&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7.&amp;nbsp; 실무에서 자주 하는 실수 3 &amp;mdash; 드래그 이동 안 됨&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DragMove()는 반드시&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 마우스 왼쪽 버튼 상태에서&lt;br /&gt;✔ MouseDown 이벤트에서 호출해야 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건이 맞지 않으면 예외 발생&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 실무 추천 구조&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ WindowStyle=None&lt;br /&gt;✔ 상단 Border를 타이틀바로 사용&lt;br /&gt;✔ DragMove 구현&lt;br /&gt;✔ 버튼 직접 제어&lt;br /&gt;✔ ResizeMode=CanResize 유지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조가 가장 안정적입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 한 줄 핵심 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 타이틀바를 제거하면&lt;br /&gt;모든 창 동작을 직접 구현해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF 창 테두리 제거&lt;br /&gt;WPF WindowStyle None&lt;br /&gt;WPF 커스텀 타이틀바&lt;br /&gt;WPF DragMove 사용법&lt;br /&gt;WPF 창 이동 구현&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발이야기/WPF UI 커스터마이징</category>
      <category>csharp wpf</category>
      <category>WPF</category>
      <category>wpf dragmove</category>
      <category>wpf windowstyle</category>
      <category>wpf 커스텀ui</category>
      <category>닷넷 개발</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/197</guid>
      <comments>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-18%ED%8E%B8-WPF-%EC%B0%BD-%ED%85%8C%EB%91%90%EB%A6%AC-%EC%A0%9C%EA%B1%B0-%EC%BB%A4%EC%8A%A4%ED%85%80-%ED%83%80%EC%9D%B4%ED%8B%80%EB%B0%94-%EA%B5%AC%ED%98%84-%EB%93%9C%EB%9E%98%EA%B7%B8-%EC%9D%B4%EB%8F%99%EA%B9%8C%EC%A7%80-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC#entry197comment</comments>
      <pubDate>Thu, 26 Feb 2026 14:55:34 +0900</pubDate>
    </item>
    <item>
      <title>[리눅스 프로그래밍 시리즈 1편] 리눅스 프로그래밍 입문 가이드: 기초부터 실무까지 제대로 배우는 방법 (로드맵 총정리)</title>
      <link>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-1%ED%8E%B8</link>
      <description>&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-4141655932477481&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;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;대상: 리눅스를 처음 배우는 개발자 / 서버 개발 준비생 / 시스템 엔지니어 지망생&lt;br /&gt;&amp;nbsp;목표: 리눅스 기초부터 시스템 프로그래밍, 실무 운영까지 이어지는 &lt;b&gt;현실적인 학습 로드맵&lt;/b&gt; 제시&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 리눅스 프로그래밍, 왜 배워야 할까?&lt;/h1&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;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;리눅스가 사용되는 영역&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;클라우드 서버 (AWS, GCP, Azure 대부분 Linux)&lt;/li&gt;
&lt;li&gt;Docker / Kubernetes&lt;/li&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버 로그가 이상하다 &amp;rarr; SSH 접속해서 직접 분석&lt;/li&gt;
&lt;li&gt;프로세스가 계속 죽는다 &amp;rarr; top / ps로 상태 확인&lt;/li&gt;
&lt;li&gt;메모리 누수 의심 &amp;rarr; 시스템 콜 추적&lt;/li&gt;
&lt;li&gt;서비스 자동 재시작 필요 &amp;rarr; systemd 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 리눅스는 단순한 OS가 아니라&lt;br /&gt;**&amp;ldquo;개발자가 시스템을 이해하게 만드는 도구&amp;rdquo;**다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 리눅스를 배우면 생기는 실력 차이&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;① 시스템 구조를 이해하는 능력&lt;/h2&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;li&gt;프로그램이 OS와 어떻게 통신하는지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 단순 문법 공부와 차원이 다르다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;② 문제 해결 능력 상승&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스는 GUI보다 CLI 중심이다.&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;pre class=&quot;1c&quot;&gt;&lt;code&gt;grep &quot;ERROR&quot; server.log | wc -l
&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;로그에서 ERROR만 찾고&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;③ 자동화 능력&lt;/h2&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 class=&quot;bash&quot;&gt;&lt;code&gt;for file in *.log
do
  grep &quot;ERROR&quot; &quot;$file&quot;
done
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복 작업 &amp;rarr; 자동화 &amp;rarr; 생산성 향상&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 이 시리즈의 최종 목표 (완주 시 도달 수준)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 시리즈를 끝내면 다음이 가능해야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✔ 기본기&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;프로세스 제어 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✔ 개발 능력&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;gcc로 C 코드 컴파일&lt;/li&gt;
&lt;li&gt;make로 빌드 관리&lt;/li&gt;
&lt;li&gt;gdb로 디버깅&lt;/li&gt;
&lt;li&gt;쉘 스크립트 작성&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✔ 시스템 프로그래밍&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;open/read/write 이해&lt;/li&gt;
&lt;li&gt;fork/exec 구조 이해&lt;/li&gt;
&lt;li&gt;pthread 멀티스레드 작성&lt;/li&gt;
&lt;li&gt;TCP 소켓 서버 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✔ 실무 운영&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;systemd 서비스 등록&lt;/li&gt;
&lt;li&gt;로그 분석&lt;/li&gt;
&lt;li&gt;SSH 키 설정&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;&lt;b&gt;리눅스 기반 개발자가 되는 과정&lt;/b&gt;이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 리눅스 학습 순서 (실패하지 않는 로드맵)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많이 하는 실수:&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;소켓부터 배워야지!&quot;&lt;/p&gt;
&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;기초 체력이 없으면 시스템 프로그래밍에서 반드시 막힌다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;Part 1. 리눅스 기초 체력&lt;/h2&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;권한 (chmod, chown)&lt;/li&gt;
&lt;li&gt;프로세스 (ps, top)&lt;/li&gt;
&lt;li&gt;텍스트 처리 (grep, sed, awk)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;여기서 50% 이상이 결정된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;Part 2. 개발 도구 체인&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;gcc 컴파일&lt;/li&gt;
&lt;li&gt;라이브러리 링크&lt;/li&gt;
&lt;li&gt;make 빌드 자동화&lt;/li&gt;
&lt;li&gt;gdb 디버깅&lt;/li&gt;
&lt;li&gt;쉘 스크립트&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;개발자다운 리눅스 사용 단계&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;Part 3. 시스템 프로그래밍&lt;/h2&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;fork / exec&lt;/li&gt;
&lt;li&gt;pthread&lt;/li&gt;
&lt;li&gt;TCP 소켓&lt;/li&gt;
&lt;li&gt;epoll&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 단계부터 &amp;ldquo;리눅스 프로그래머&amp;rdquo;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;Part 4. 실무 운영&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;systemd&lt;/li&gt;
&lt;li&gt;로그 관리&lt;/li&gt;
&lt;li&gt;SSH 보안&lt;/li&gt;
&lt;li&gt;배포 기본&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;여기까지 오면 실무 투입 가능&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 오늘 바로 시작하는 실습 (환경 없어도 가능)&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;① 실험용 폴더 만들기&lt;/h2&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;mkdir -p ~/linux-lab
cd ~/linux-lab
pwd
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-p 옵션은 상위 폴더가 없어도 생성한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;② 현재 시스템 정보 확인&lt;/h2&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;uname -a
whoami
pwd
&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;uname : 커널 정보&lt;/li&gt;
&lt;li&gt;whoami : 현재 사용자&lt;/li&gt;
&lt;li&gt;pwd : 현재 경로&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;③ 도움말 보는 습관 만들기&lt;/h2&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;man ls
ls --help
&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;b&gt;찾는 능력이 곧 실력이다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 초보자가 반드시 버려야 할 생각&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&quot;명령어를 다 외워야 하나요?&quot;&lt;br /&gt;&amp;rarr; 아니다. 필요한 걸 찾는 능력이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&quot;GUI가 더 편한데요?&quot;&lt;br /&gt;&amp;rarr; 서버에는 GUI가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&quot;리눅스는 어려워 보이는데요&quot;&lt;br /&gt;&amp;rarr; 처음엔 낯설 뿐이다. 구조를 이해하면 오히려 단순하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 오늘의 미션 (기록 습관 만들기)&lt;/h1&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;~/linux-lab 생성&lt;/li&gt;
&lt;li&gt;notes.txt 파일 생성&lt;/li&gt;
&lt;li&gt;오늘 실행한 명령어 5개 기록&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;echo &quot;uname, whoami, pwd, man, mkdir&quot; &amp;gt; notes.txt
cat notes.txt
&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 1편 요약&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리눅스는 개발&amp;middot;서버 환경의 표준&lt;/li&gt;
&lt;li&gt;단순 명령어 학습이 아니라 시스템 이해가 핵심&lt;/li&gt;
&lt;li&gt;학습 순서가 매우 중요&lt;/li&gt;
&lt;li&gt;오늘부터 실험 폴더 + 기록 습관 시작&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&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;리눅스 프로그래밍 입문&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;/li&gt;
&lt;li&gt;리눅스 실무 준비&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Linux 완전 정복/Linux 환경 준비</category>
      <category>Linux</category>
      <category>LINUX 기초</category>
      <category>linux 시스템</category>
      <category>linux 프로그래밍</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/196</guid>
      <comments>https://subling.tistory.com/entry/%EB%A6%AC%EB%88%85%EC%8A%A4-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%9C%EB%A6%AC%EC%A6%88-1%ED%8E%B8#entry196comment</comments>
      <pubDate>Thu, 26 Feb 2026 13:36:39 +0900</pubDate>
    </item>
    <item>
      <title>[WPF 실무 17편] WPF 메모리 누수 원인과 해결 방법 (이벤트, 바인딩, 가상화 이슈 완전 정리)</title>
      <link>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-17%ED%8E%B8-WPF-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%88%84%EC%88%98-%EC%9B%90%EC%9D%B8%EA%B3%BC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%B0%94%EC%9D%B8%EB%94%A9-%EA%B0%80%EC%83%81%ED%99%94-%EC%9D%B4%EC%8A%88-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC</link>
      <description>&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-4141655932477481&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF 프로젝트를 오래 실행하면 메모리가 계속 증가하는 현상을 경험한 적이 있을 겁니다.&lt;br /&gt;창을 닫았는데도 메모리가 내려가지 않는다면, 거의 확실하게 메모리 누수입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 WPF에서 자주 발생하는 메모리 누수 원인과 실무에서 바로 적용 가능한 해결 방법을 정리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. WPF에서 메모리 누수가 발생하는 이유&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF는 이벤트, 바인딩, 시각적 트리(Visual Tree)가 복잡하게 연결된 구조입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 경우 객체가 GC 대상이 되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 이벤트 구독 해제 안 함&lt;br /&gt;✔ 정적 이벤트 사용&lt;br /&gt;✔ CollectionView 참조 유지&lt;br /&gt;✔ 가상화 꺼진 상태에서 대량 UI 유지&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2.&amp;nbsp; 실무에서 가장 흔한 원인 1 &amp;mdash; 이벤트 해제 안 함&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;잘못된 코드&lt;/h2&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;public MainWindow()
{
    InitializeComponent();
    SomeService.DataChanged += OnDataChanged;
}
&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;rarr; SomeService가 MainWindow를 계속 참조&lt;br /&gt;&amp;rarr; GC 수거 안 됨&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결 방법&lt;/h2&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;protected override void OnClosed(EventArgs e)
{
    SomeService.DataChanged -= OnDataChanged;
    base.OnClosed(e);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트는 반드시 해제해야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3.&amp;nbsp; 원인 2 &amp;mdash; 정적(static) 이벤트 사용&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;잘못된 코드&lt;/h2&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public static event EventHandler GlobalEvent;
&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;rarr; 구독 객체 절대 해제 안 됨&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 가능하면 static 이벤트 사용 금지&lt;br /&gt;✔ WeakEvent 패턴 사용 고려&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4.&amp;nbsp; 원인 3 &amp;mdash; CollectionView 참조 유지&lt;/h1&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;private ICollectionView _view;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;View가 닫혀도 ViewModel이 살아있으면&lt;br /&gt;CollectionView가 Items를 계속 참조&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 메모리 해제 안 됨&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;_view.Filter = null;
_view = null;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 ViewModel 정리 시 참조 제거&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5.&amp;nbsp; 원인 4 &amp;mdash; ScrollViewer로 가상화 끄기&lt;/h1&gt;
&lt;pre class=&quot;dust&quot;&gt;&lt;code&gt;&amp;lt;ScrollViewer&amp;gt;
    &amp;lt;DataGrid ItemsSource=&quot;{Binding Items}&quot;/&amp;gt;
&amp;lt;/ScrollViewer&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상화 꺼짐 &amp;rarr; 모든 Row 생성&lt;br /&gt;&amp;rarr; 메모리 급증&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 ScrollViewer 제거&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6.&amp;nbsp; 원인 5 &amp;mdash; 대용량 이미지 바인딩&lt;/h1&gt;
&lt;pre class=&quot;dust&quot;&gt;&lt;code&gt;&amp;lt;Image Source=&quot;{Binding ImageData}&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BitmapImage를 캐시 안 하면&lt;br /&gt;메모리 해제 지연&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.Freeze();
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Freeze() 호출 필수&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7.&amp;nbsp; 원인 6 &amp;mdash; DispatcherTimer 미정리&lt;/h1&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;DispatcherTimer timer = new DispatcherTimer();
timer.Tick += OnTick;
timer.Start();
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Timer가 Window를 계속 참조&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;pre class=&quot;vbscript&quot;&gt;&lt;code&gt;timer.Stop();
timer.Tick -= OnTick;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 메모리 누수 확인 방법&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ Visual Studio Diagnostic Tools&lt;br /&gt;✔ dotMemory&lt;br /&gt;✔ GC.Collect() 테스트 (임시 확인용)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;창 닫은 뒤에도 객체가 남아있으면 누수입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 실무 체크리스트&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 이벤트 해제&lt;br /&gt;✔ static 이벤트 최소화&lt;br /&gt;✔ Timer 정리&lt;br /&gt;✔ 가상화 유지&lt;br /&gt;✔ CollectionView 참조 해제&lt;br /&gt;✔ 이미지 Freeze 처리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 6가지 점검하면 대부분 해결됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 한 줄 핵심 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF 메모리 누수의 90%는 이벤트 참조 문제다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF 메모리 누수&lt;br /&gt;WPF GC 안됨&lt;br /&gt;WPF 이벤트 해제&lt;br /&gt;WPF static 이벤트 문제&lt;br /&gt;WPF DataGrid 메모리 증가&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발이야기/WPF 실무 문제 해결</category>
      <category>csharp wpf</category>
      <category>WPF</category>
      <category>wpf gc</category>
      <category>wpf 메모리누수</category>
      <category>wpf 성능</category>
      <category>닷넷 개발</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/195</guid>
      <comments>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-17%ED%8E%B8-WPF-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%88%84%EC%88%98-%EC%9B%90%EC%9D%B8%EA%B3%BC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%B0%94%EC%9D%B8%EB%94%A9-%EA%B0%80%EC%83%81%ED%99%94-%EC%9D%B4%EC%8A%88-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC#entry195comment</comments>
      <pubDate>Thu, 26 Feb 2026 13:25:18 +0900</pubDate>
    </item>
    <item>
      <title>[WPF 실무 16편] WPF 프로젝트 구조 추천 (실무 폴더 설계 완전 정리)</title>
      <link>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-16%ED%8E%B8-WPF-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B5%AC%EC%A1%B0-%EC%B6%94%EC%B2%9C-%EC%8B%A4%EB%AC%B4-%ED%8F%B4%EB%8D%94-%EC%84%A4%EA%B3%84-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC</link>
      <description>&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-4141655932477481&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 초반에 구조를 제대로 잡지 않으면&lt;br /&gt;기능이 늘어날수록 코드가 엉키고 유지보수가 어려워집니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF는 특히 MVVM 기반이기 때문에&lt;br /&gt;&lt;b&gt;초기 폴더 구조 설계가 매우 중요&lt;/b&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 실무에서 가장 많이 사용하는 WPF 프로젝트 구조와&lt;br /&gt;대형 프로젝트 기준 폴더 설계 방법을 정리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 왜 프로젝트 구조가 중요한가?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 기능 추가 시 충돌 방지&lt;br /&gt;✔ 코드 위치 예측 가능&lt;br /&gt;✔ 협업 시 생산성 향상&lt;br /&gt;✔ 테스트 구조 분리 용이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 MVVM 구조에서는&lt;br /&gt;View / ViewModel / Model 분리가 핵심입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 기본 MVVM 구조 (소형 프로젝트)&lt;/h1&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;MyProject/
 ├ Views/
 ├ ViewModels/
 ├ Models/
 ├ Resources/
 └ App.xaml
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;각 폴더 역할&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Views &amp;rarr; XAML 화면&lt;/li&gt;
&lt;li&gt;ViewModels &amp;rarr; 로직 처리&lt;/li&gt;
&lt;li&gt;Models &amp;rarr; 데이터 구조&lt;/li&gt;
&lt;li&gt;Resources &amp;rarr; 스타일, 템플릿, Converter&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소형 프로젝트에서는 이 정도면 충분합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 실무 표준 구조 (중형 프로젝트)&lt;/h1&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;MyProject/
 ├ Core/
 │   ├ BaseViewModel.cs
 │   ├ RelayCommand.cs
 │   └ Services/
 │
 ├ Models/
 │
 ├ ViewModels/
 │   ├ MainViewModel.cs
 │   └ DataGridViewModel.cs
 │
 ├ Views/
 │   ├ MainView.xaml
 │   └ DataGridView.xaml
 │
 ├ Resources/
 │   ├ Styles.xaml
 │   ├ DataGridStyles.xaml
 │   └ Converters.xaml
 │
 └ App.xaml
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;핵심 포인트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ Core &amp;rarr; 공통 로직&lt;br /&gt;✔ Services &amp;rarr; 파일/DB/메시지 처리&lt;br /&gt;✔ Resources &amp;rarr; 스타일 전역 관리&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 대형 프로젝트 구조 (실무 장비 UI 기준)&lt;/h1&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;MyProject/
 ├ Modules/
 │   ├ Inspection/
 │   │   ├ Views/
 │   │   ├ ViewModels/
 │   │   └ Models/
 │   ├ Monitoring/
 │   └ Settings/
 │
 ├ Core/
 ├ Infrastructure/
 ├ Resources/
 └ App.xaml
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 기능 단위 모듈화&lt;br /&gt;✔ 독립 개발 가능&lt;br /&gt;✔ 유지보수 용이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장비 UI / 기업용 프로그램에서 자주 사용됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5.&amp;nbsp; 실무에서 자주 하는 실수 1 &amp;mdash; View와 ViewModel 한 폴더에 섞음&lt;/h1&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;MainWindow.xaml
MainWindowViewModel.cs
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 위치에 두는 방식&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제&lt;/h2&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;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반드시 View / ViewModel 분리&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6.&amp;nbsp; 실무에서 자주 하는 실수 2 &amp;mdash; 공통 로직 중복 작성&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RelayCommand, BaseViewModel을&lt;br /&gt;각 ViewModel에 복붙&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제&lt;/h2&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;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Core 폴더에 공통 클래스 분리&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7.&amp;nbsp; 실무에서 자주 하는 실수 3 &amp;mdash; Resources 정리 안 함&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스타일을 각 View에 작성&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제&lt;/h2&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;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ResourceDictionary 전역 관리&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 실무 추천 규칙&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ View는 로직 작성 금지&lt;br /&gt;✔ ViewModel은 View 참조 금지&lt;br /&gt;✔ 공통 클래스는 Core에&lt;br /&gt;✔ UI 스타일은 Resources에&lt;br /&gt;✔ 기능별 모듈 분리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 5가지만 지켜도 프로젝트 품질이 크게 달라집니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 폴더 구조 vs 네임스페이스&lt;/h1&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 class=&quot;css&quot;&gt;&lt;code&gt;MyProject.ViewModels
MyProject.Views
MyProject.Core
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조가 명확하면 협업이 쉬워집니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 한 줄 핵심 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF는 구조가 곧 품질이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 설계한 구조가 프로젝트 수명을 결정한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;SEO 키워드&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF 프로젝트 구조&lt;br /&gt;WPF MVVM 폴더 구조&lt;br /&gt;WPF 실무 구조 설계&lt;br /&gt;WPF 대형 프로젝트 구조&lt;br /&gt;WPF MVVM 구조 예제&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발이야기/WPF 프로젝트 구조 &amp;amp; 설계</category>
      <category>csharp wpf</category>
      <category>WPF</category>
      <category>WPF MVVM</category>
      <category>wpf 설계</category>
      <category>wpf 프로젝트구조</category>
      <category>닷넷 개발</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/194</guid>
      <comments>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-16%ED%8E%B8-WPF-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B5%AC%EC%A1%B0-%EC%B6%94%EC%B2%9C-%EC%8B%A4%EB%AC%B4-%ED%8F%B4%EB%8D%94-%EC%84%A4%EA%B3%84-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC#entry194comment</comments>
      <pubDate>Thu, 26 Feb 2026 10:43:56 +0900</pubDate>
    </item>
    <item>
      <title>[WPF 실무 15편] WPF ResourceDictionary 완전 정리 (스타일 전역 관리 실무 구조)</title>
      <link>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-15%ED%8E%B8-WPF-ResourceDictionary-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC-%EC%8A%A4%ED%83%80%EC%9D%BC-%EC%A0%84%EC%97%AD-%EA%B4%80%EB%A6%AC-%EC%8B%A4%EB%AC%B4-%EA%B5%AC%EC%A1%B0</link>
      <description>&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-4141655932477481&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스타일을 여기저기 Window 안에 작성하다 보면&lt;br /&gt;프로젝트가 커질수록 관리가 어려워집니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서는 스타일을 한 파일에 모아 관리합니다.&lt;br /&gt;그 핵심이 바로 &lt;b&gt;ResourceDictionary&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 WPF ResourceDictionary의 개념, 분리 방법, 병합 방법, 그리고 실무에서 자주 하는 실수까지 정리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. ResourceDictionary란 무엇인가?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ResourceDictionary는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 스타일&lt;br /&gt;✔ 템플릿&lt;br /&gt;✔ 브러시&lt;br /&gt;✔ Converter&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;&amp;ldquo;스타일 저장소&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 생각하면 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 기본 사용 방법&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Window 내부에 리소스 정의&lt;/h2&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;Window.Resources&amp;gt;
    &amp;lt;Style TargetType=&quot;Button&quot;&amp;gt;
        &amp;lt;Setter Property=&quot;Background&quot; Value=&quot;LightBlue&quot;/&amp;gt;
    &amp;lt;/Style&amp;gt;
&amp;lt;/Window.Resources&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 해당 Window에서만 적용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 방식은 규모가 커지면 관리가 어렵습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. ResourceDictionary 파일 분리하기&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1) 새 ResourceDictionary 파일 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예: Styles.xaml&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2) App.xaml에 병합&lt;/h3&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;Application.Resources&amp;gt;
    &amp;lt;ResourceDictionary&amp;gt;
        &amp;lt;ResourceDictionary.MergedDictionaries&amp;gt;
            &amp;lt;ResourceDictionary Source=&quot;Styles.xaml&quot;/&amp;gt;
        &amp;lt;/ResourceDictionary.MergedDictionaries&amp;gt;
    &amp;lt;/ResourceDictionary&amp;gt;
&amp;lt;/Application.Resources&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 이제 프로젝트 전체에서 사용 가능&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 스타일 사용 방법&lt;/h1&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;&amp;lt;Button Content=&quot;확인&quot;
        Style=&quot;{StaticResource PrimaryButtonStyle}&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 전역 스타일 적용 완료&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5.&amp;nbsp; 실무에서 자주 하는 실수 1 &amp;mdash; Build Action 설정 안 함&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ResourceDictionary 파일의 Build Action이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;None&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이면 로드되지 않습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ Build Action &amp;rarr; Page&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6.&amp;nbsp; 실무에서 자주 하는 실수 2 &amp;mdash; StaticResource vs DynamicResource 혼동&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;StaticResource&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 로드 시점에 한 번만 바인딩&lt;br /&gt;✔ 성능 좋음&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;DynamicResource&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 런타임 변경 가능&lt;br /&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;일반 스타일은 &amp;rarr; StaticResource&lt;br /&gt;테마 변경 가능 UI는 &amp;rarr; DynamicResource&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7.&amp;nbsp; 실무에서 자주 하는 실수 3 &amp;mdash; 리소스 병합 순서 문제&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MergedDictionaries는 아래에 있는 것이 우선 적용됩니다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;ResourceDictionary.MergedDictionaries&amp;gt;
    &amp;lt;ResourceDictionary Source=&quot;BaseStyles.xaml&quot;/&amp;gt;
    &amp;lt;ResourceDictionary Source=&quot;OverrideStyles.xaml&quot;/&amp;gt;
&amp;lt;/ResourceDictionary.MergedDictionaries&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ OverrideStyles가 우선&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순서 잘못되면 스타일 덮어쓰기 안 됨&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 실무 추천 폴더 구조&lt;/h1&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Resources/
 ├ Colors.xaml
 ├ Styles.xaml
 ├ DataGridStyles.xaml
 ├ ButtonStyles.xaml
 └ Converters.xaml
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 기능별 분리&lt;br /&gt;✔ 유지보수 쉬움&lt;br /&gt;✔ 대형 프로젝트에 적합&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. DataGrid 전용 리소스 분리 예시&lt;/h1&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;ResourceDictionary&amp;gt;
    &amp;lt;Style TargetType=&quot;DataGridRow&quot;&amp;gt;
        &amp;lt;Setter Property=&quot;Height&quot; Value=&quot;30&quot;/&amp;gt;
    &amp;lt;/Style&amp;gt;

    &amp;lt;Style TargetType=&quot;DataGridColumnHeader&quot;&amp;gt;
        &amp;lt;Setter Property=&quot;FontWeight&quot; Value=&quot;Bold&quot;/&amp;gt;
    &amp;lt;/Style&amp;gt;
&amp;lt;/ResourceDictionary&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DataGrid 관련 스타일은 별도 파일로 분리하는 것이 좋습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 실무 결론&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 스타일은 Window에 직접 작성하지 말 것&lt;br /&gt;✔ ResourceDictionary로 분리할 것&lt;br /&gt;✔ App.xaml에서 병합 관리&lt;br /&gt;✔ 파일 기능별 분리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조가 장기 유지보수에 가장 좋습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 한 줄 핵심 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스타일이 많아지면 &amp;rarr; ResourceDictionary로 분리하라.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF ResourceDictionary 사용법&lt;br /&gt;WPF 스타일 전역 관리&lt;br /&gt;WPF MergedDictionaries&lt;br /&gt;WPF StaticResource DynamicResource 차이&lt;br /&gt;WPF 스타일 분리 방법&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발이야기/WPF 스타일 &amp;amp; 템플릿</category>
      <category>csharp wpf</category>
      <category>WPF</category>
      <category>wpf resourcedictionary</category>
      <category>wpf 스타일</category>
      <category>wpf 템플릿</category>
      <category>닷넷 개발</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/193</guid>
      <comments>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-15%ED%8E%B8-WPF-ResourceDictionary-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC-%EC%8A%A4%ED%83%80%EC%9D%BC-%EC%A0%84%EC%97%AD-%EA%B4%80%EB%A6%AC-%EC%8B%A4%EB%AC%B4-%EA%B5%AC%EC%A1%B0#entry193comment</comments>
      <pubDate>Thu, 26 Feb 2026 09:13:22 +0900</pubDate>
    </item>
    <item>
      <title>[WPF 실무 14편] WPF ControlTemplate 완전 이해 (스타일이 안 먹는 진짜 이유)</title>
      <link>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-14%ED%8E%B8-WPF-ControlTemplate-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4-%EC%8A%A4%ED%83%80%EC%9D%BC%EC%9D%B4-%EC%95%88-%EB%A8%B9%EB%8A%94-%EC%A7%84%EC%A7%9C-%EC%9D%B4%EC%9C%A0</link>
      <description>&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-4141655932477481&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Setter를 줬는데 적용이 안 된다.&lt;br /&gt;Background를 바꿨는데 색이 안 바뀐다.&lt;br /&gt;정렬을 바꿨는데 그대로다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 문제의 80%는 &lt;b&gt;ControlTemplate 때문&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 WPF 스타일 구조의 핵심인 ControlTemplate을 실무 기준으로 정리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. WPF 스타일 구조 이해하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF 컨트롤 렌더링 구조는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;Style &amp;rarr; ControlTemplate &amp;rarr; Visual Tree &amp;rarr; 실제 UI 표시
&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;✔ Style은 속성 값을 설정하는 단계&lt;br /&gt;✔ ControlTemplate은 실제 모양을 정의하는 단계&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Template이 다르면 Style이 무시될 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. ControlTemplate이란 무엇인가?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ControlTemplate은 컨트롤의 &amp;ldquo;외형 구조&amp;rdquo;를 정의합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Button은 내부적으로 다음과 같은 구조를 가집니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Border&lt;/li&gt;
&lt;li&gt;ContentPresenter&lt;/li&gt;
&lt;li&gt;Trigger&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Template을 재정의하면 버튼 모양 자체를 바꿀 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 간단한 ControlTemplate 예제&lt;/h1&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;Style TargetType=&quot;Button&quot;&amp;gt;
    &amp;lt;Setter Property=&quot;Template&quot;&amp;gt;
        &amp;lt;Setter.Value&amp;gt;
            &amp;lt;ControlTemplate TargetType=&quot;Button&quot;&amp;gt;
                &amp;lt;Border Background=&quot;LightGray&quot;
                        CornerRadius=&quot;10&quot;
                        Padding=&quot;10&quot;&amp;gt;
                    &amp;lt;ContentPresenter HorizontalAlignment=&quot;Center&quot;
                                      VerticalAlignment=&quot;Center&quot;/&amp;gt;
                &amp;lt;/Border&amp;gt;
            &amp;lt;/ControlTemplate&amp;gt;
        &amp;lt;/Setter.Value&amp;gt;
    &amp;lt;/Setter&amp;gt;
&amp;lt;/Style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 기본 버튼 구조 완전 교체&lt;br /&gt;✔ 디자인 완전 자유화 가능&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 왜 스타일이 안 먹는가?&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;예시&lt;/h2&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;&amp;lt;Setter Property=&quot;Background&quot; Value=&quot;Red&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Template 안에&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;&amp;lt;Border Background=&quot;White&quot;/&amp;gt;
&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;rarr; Template이 Style 값을 덮어씀&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. 올바른 Template 설계 방식&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Template 내부에서 TemplateBinding을 사용해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;&amp;lt;Border Background=&quot;{TemplateBinding Background}&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 해야 Style에서 준 Background가 적용됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6.&amp;nbsp; 실무에서 자주 하는 실수 1 &amp;mdash; TemplateBinding 안 씀&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;잘못된 코드&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;&amp;lt;Border Background=&quot;White&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;증상&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Style에서 Background 바꿔도 적용 안 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;&amp;lt;Border Background=&quot;{TemplateBinding Background}&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7.&amp;nbsp; 실무에서 자주 하는 실수 2 &amp;mdash; ContentPresenter 누락&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Template 재정의했는데 텍스트가 안 보이는 경우&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;원인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ContentPresenter를 안 넣음&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;pre class=&quot;apache&quot;&gt;&lt;code&gt;&amp;lt;ContentPresenter/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반드시 포함해야 함&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8.&amp;nbsp; 실무에서 자주 하는 실수 3 &amp;mdash; Trigger 위치 오류&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Trigger는 반드시 Template 안에 정의하거나&lt;br /&gt;Style.Triggers에 올바르게 작성해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조 잘못 잡으면 아예 동작 안 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. Template vs Style 차이 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구분StyleControlTemplate&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&gt;역할&lt;/td&gt;
&lt;td&gt;속성 설정&lt;/td&gt;
&lt;td&gt;외형 정의&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;모양 변경&lt;/td&gt;
&lt;td&gt;제한적&lt;/td&gt;
&lt;td&gt;완전 교체 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;정렬/색상 문제&lt;/td&gt;
&lt;td&gt;일부 해결&lt;/td&gt;
&lt;td&gt;근본 해결&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 언제 Template을 수정해야 할까?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ Style이 적용되지 않을 때&lt;br /&gt;✔ 기본 디자인을 완전히 바꿔야 할 때&lt;br /&gt;✔ 내부 구조를 수정해야 할 때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Setter로 안 되면 거의 Template 문제입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 한 줄 핵심 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Style이 안 먹는다 = Template을 의심하라.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF ControlTemplate 설명&lt;br /&gt;WPF 스타일 안 먹음&lt;br /&gt;WPF TemplateBinding 사용법&lt;br /&gt;WPF ControlTemplate 예제&lt;br /&gt;WPF 스타일 문제 해결&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발이야기/WPF 스타일 &amp;amp; 템플릿</category>
      <category>csharp wpf</category>
      <category>WPF</category>
      <category>wpf controltemplate</category>
      <category>wpf 스타일</category>
      <category>wpf 템플릿</category>
      <category>닷넷 개발</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/192</guid>
      <comments>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-14%ED%8E%B8-WPF-ControlTemplate-%EC%99%84%EC%A0%84-%EC%9D%B4%ED%95%B4-%EC%8A%A4%ED%83%80%EC%9D%BC%EC%9D%B4-%EC%95%88-%EB%A8%B9%EB%8A%94-%EC%A7%84%EC%A7%9C-%EC%9D%B4%EC%9C%A0#entry192comment</comments>
      <pubDate>Wed, 25 Feb 2026 10:48:50 +0900</pubDate>
    </item>
    <item>
      <title>[WPF 실무 13편] WPF DataGrid 정렬 / 필터 구현 완전 정리 (CollectionView 제대로 쓰는 법)</title>
      <link>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-13%ED%8E%B8-WPF-DataGrid-%EC%A0%95%EB%A0%AC-%ED%95%84%ED%84%B0-%EA%B5%AC%ED%98%84-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC-CollectionView-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EC%93%B0%EB%8A%94-%EB%B2%95</link>
      <description>&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-4141655932477481&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DataGrid에서 정렬은 되는데 필터는 안 되거나,&lt;br /&gt;필터를 구현했더니 원본 데이터가 망가지는 경험을 해본 적 있을 겁니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF에서 정렬과 필터는 직접 리스트를 조작하는 방식이 아니라&lt;br /&gt;&lt;b&gt;CollectionView를 통해 처리하는 것이 정석&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 DataGrid 정렬과 필터를 실무에서 안전하게 구현하는 방법을 정리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 왜 List를 직접 건드리면 안 될까?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;잘못된 방식&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Items = Items.Where(x =&amp;gt; x.Age &amp;gt; 30).ToList();
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제점&lt;/h2&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;li&gt;UI 갱신 문제&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;실무에서는 절대 이렇게 하지 않습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 정석 방법 &amp;mdash; CollectionView 사용&lt;/h1&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;using System.ComponentModel;

private ICollectionView _view;

public MainViewModel()
{
    _view = CollectionViewSource.GetDefaultView(Items);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CollectionView는 원본 컬렉션을 유지한 채&lt;br /&gt;정렬/필터/그룹화를 지원합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3. 정렬 구현 방법&lt;/h1&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;_view.SortDescriptions.Clear();
_view.SortDescriptions.Add(
    new SortDescription(&quot;Age&quot;, ListSortDirection.Ascending));
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 원본 데이터 유지&lt;br /&gt;✔ 여러 컬럼 정렬 가능&lt;br /&gt;✔ 동적 정렬 가능&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4. 필터 구현 방법&lt;/h1&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;_view.Filter = item =&amp;gt;
{
    var user = item as User;
    return user.Age &amp;gt; 30;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 조건식 자유롭게 작성 가능&lt;br /&gt;✔ 필터 해제 쉬움&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필터 해제:&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;_view.Filter = null;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5. XAML에서 DataGrid 연결&lt;/h1&gt;
&lt;pre class=&quot;dust&quot;&gt;&lt;code&gt;&amp;lt;DataGrid ItemsSource=&quot;{Binding Items}&quot;/&amp;gt;
&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;✔ Items는 그대로 ObservableCollection 유지&lt;br /&gt;✔ 정렬/필터는 View에서 처리됨&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6.&amp;nbsp; 실무에서 자주 하는 실수 1 &amp;mdash; CollectionView를 새로 만듦&lt;/h1&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;_view = new ListCollectionView(Items);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제&lt;/h2&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;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항상&lt;/p&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;CollectionViewSource.GetDefaultView(Items);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7.&amp;nbsp; 실무에서 자주 하는 실수 2 &amp;mdash; Filter 안에서 예외 발생&lt;/h1&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;_view.Filter = item =&amp;gt;
{
    return ((User)item).Name.Contains(searchText);
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;searchText가 null이면 예외 발생&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;안전한 코드&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;_view.Filter = item =&amp;gt;
{
    var user = item as User;
    return string.IsNullOrEmpty(searchText)
        || user.Name?.Contains(searchText) == true;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8.&amp;nbsp; 실무에서 자주 하는 실수 3 &amp;mdash; Filter 후 Refresh 안 함&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필터 조건이 변경되었는데 UI가 갱신 안 될 때&lt;/p&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;_view.Refresh();
&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 실무 추천 구조&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ Items &amp;rarr; ObservableCollection 유지&lt;br /&gt;✔ CollectionView &amp;rarr; 정렬/필터 전용&lt;br /&gt;✔ Filter 변경 시 Refresh() 호출&lt;br /&gt;✔ 원본 데이터 절대 직접 수정 금지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조가 가장 안전합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;10. 고급 팁 &amp;mdash; 다중 정렬&lt;/h1&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;_view.SortDescriptions.Add(
    new SortDescription(&quot;Age&quot;, ListSortDirection.Ascending));

_view.SortDescriptions.Add(
    new SortDescription(&quot;Name&quot;, ListSortDirection.Descending));
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 1차 정렬 &amp;rarr; Age&lt;br /&gt;✔ 2차 정렬 &amp;rarr; Name&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;11. 한 줄 핵심 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DataGrid 정렬/필터는 리스트를 바꾸는 게 아니라&lt;br /&gt;&amp;nbsp;View를 바꾸는 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF DataGrid 정렬 구현&lt;br /&gt;WPF DataGrid 필터 구현&lt;br /&gt;WPF CollectionView 사용법&lt;br /&gt;WPF SortDescription 예제&lt;br /&gt;WPF DataGrid Filter&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발이야기/WPF DataGrid</category>
      <category>csharp wpf</category>
      <category>WPF</category>
      <category>wpf collectionview</category>
      <category>wpf datagrid</category>
      <category>wpf 정렬</category>
      <category>wpf 필터</category>
      <category>닷넷 개발</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/191</guid>
      <comments>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-13%ED%8E%B8-WPF-DataGrid-%EC%A0%95%EB%A0%AC-%ED%95%84%ED%84%B0-%EA%B5%AC%ED%98%84-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC-CollectionView-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EC%93%B0%EB%8A%94-%EB%B2%95#entry191comment</comments>
      <pubDate>Tue, 24 Feb 2026 16:11:32 +0900</pubDate>
    </item>
    <item>
      <title>[WPF 실무 12편] WPF DataGrid 성능 최적화 완전 정리 (대용량 데이터 느려짐 해결)</title>
      <link>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-12%ED%8E%B8-WPF-DataGrid-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC-%EB%8C%80%EC%9A%A9%EB%9F%89-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%8A%90%EB%A0%A4%EC%A7%90-%ED%95%B4%EA%B2%B0</link>
      <description>&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-4141655932477481&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DataGrid에 데이터가 1,000건 이상만 들어가도 갑자기 느려지는 경험을 해본 적 있을 겁니다.&lt;br /&gt;스크롤이 버벅이고, 클릭 반응이 늦고, 심하면 UI가 멈춰 보이기도 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 WPF DataGrid가 느려지는 이유와 실무에서 반드시 적용해야 하는 성능 최적화 방법을 정리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;1. 왜 DataGrid는 느려질까?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF DataGrid는 기본적으로 모든 Row와 Cell을 UI 요소로 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 10,000건 = 10,000개의 Row 객체 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 컬럼이 20개면?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 200,000개의 Cell UI 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연히 무거워질 수밖에 없습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;2. 가장 중요한 옵션 &amp;mdash; 가상화(Virtualization)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상화는 화면에 보이는 Row만 생성하는 기능입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;반드시 확인해야 할 설정&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;&amp;lt;DataGrid
    EnableRowVirtualization=&quot;True&quot;
    EnableColumnVirtualization=&quot;True&quot;
    VirtualizingPanel.IsVirtualizing=&quot;True&quot;
    VirtualizingPanel.VirtualizationMode=&quot;Recycling&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ EnableRowVirtualization = 보이는 Row만 생성&lt;br /&gt;✔ Recycling = 기존 Row 재사용 (성능 &amp;uarr;)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;3.&amp;nbsp; 실무에서 자주 하는 실수 1 &amp;mdash; ScrollViewer 감싸기&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;잘못된 코드&lt;/h2&gt;
&lt;pre class=&quot;dust&quot;&gt;&lt;code&gt;&amp;lt;ScrollViewer&amp;gt;
    &amp;lt;DataGrid ItemsSource=&quot;{Binding Items}&quot;/&amp;gt;
&amp;lt;/ScrollViewer&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상화 완전히 꺼짐&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 전체 Row 생성됨&lt;br /&gt;&amp;rarr; 성능 급격히 저하&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;해결&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DataGrid는 자체 ScrollViewer를 가지고 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;절대 외부 ScrollViewer로 감싸지 말 것&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;4.&amp;nbsp; 실무에서 자주 하는 실수 2 &amp;mdash; RowHeight Auto 사용&lt;/h1&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;&amp;lt;DataGrid RowHeight=&quot;Auto&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Row 높이를 매번 계산&lt;br /&gt;&amp;rarr; 성능 저하&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고정 높이 사용&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;&amp;lt;DataGrid RowHeight=&quot;30&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;5.&amp;nbsp; 실무에서 자주 하는 실수 3 &amp;mdash; 복잡한 CellTemplate 남발&lt;/h1&gt;
&lt;pre class=&quot;apache&quot;&gt;&lt;code&gt;&amp;lt;DataGridTemplateColumn&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TemplateColumn은 유연하지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ 시각적 트리 깊어짐&lt;br /&gt;✔ UI 객체 증가&lt;br /&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ DataGridTextColumn 사용&lt;br /&gt;✔ 필요할 때만 TemplateColumn&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;6. 대용량 데이터 처리 팁&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) UI Thread에서 대량 Add 금지&lt;/h2&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;foreach (var item in bigList)
{
    Items.Add(item);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; UI 멈춤&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결&lt;/h3&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;await Task.Run(() =&amp;gt; LoadData());
&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;Range 추가&lt;/li&gt;
&lt;li&gt;Bulk Update 구조 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2) CollectionView 사용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필터/정렬을 위해 직접 리스트 조작하지 말고&lt;/p&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;CollectionViewSource.GetDefaultView(Items);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용 권장&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;7. 성능 체크리스트 (실무용)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ EnableRowVirtualization=True&lt;br /&gt;✔ VirtualizationMode=Recycling&lt;br /&gt;✔ 외부 ScrollViewer 제거&lt;br /&gt;✔ RowHeight 고정&lt;br /&gt;✔ TemplateColumn 최소화&lt;br /&gt;✔ 대량 데이터는 비동기 로딩&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 6가지만 지켜도 체감 성능 크게 개선됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;8. 실무 결론&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DataGrid 느려짐의 80% 원인은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;가상화 꺼짐&lt;br /&gt;&amp;nbsp;불필요한 UI 요소 생성&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;9. 한 줄 핵심 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DataGrid 성능 최적화 = 가상화 유지 + UI 요소 최소화&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WPF DataGrid 성능&lt;br /&gt;WPF DataGrid 느려짐&lt;br /&gt;WPF DataGrid 가상화&lt;br /&gt;WPF EnableRowVirtualization&lt;br /&gt;WPF DataGrid 최적화&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발이야기/WPF DataGrid</category>
      <category>csharp wpf</category>
      <category>WPF</category>
      <category>wpf datagrid</category>
      <category>wpf 가상화</category>
      <category>wpf 성능</category>
      <category>닷넷 개발</category>
      <author>코드한입</author>
      <guid isPermaLink="true">https://subling.tistory.com/190</guid>
      <comments>https://subling.tistory.com/entry/WPF-%EC%8B%A4%EB%AC%B4-12%ED%8E%B8-WPF-DataGrid-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94-%EC%99%84%EC%A0%84-%EC%A0%95%EB%A6%AC-%EB%8C%80%EC%9A%A9%EB%9F%89-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%8A%90%EB%A0%A4%EC%A7%90-%ED%95%B4%EA%B2%B0#entry190comment</comments>
      <pubDate>Tue, 24 Feb 2026 13:54:04 +0900</pubDate>
    </item>
  </channel>
</rss>