<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>taegi의cloudsecurity</title>
    <link>https://taegi-cloudsecurity.tistory.com/</link>
    <description>비전공자의 클라우드 보안 전문가 도전기&amp;rdquo; 또는 &amp;ldquo;캐나다 유학 &amp;amp; 보안 커리어를 향한 로드맵</description>
    <language>ko</language>
    <pubDate>Sat, 20 Jun 2026 03:00:17 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>taegi-</managingEditor>
    <item>
      <title>✅ [개념정리] OWASP Top 10 파헤치기 (4/10): A04 - 안전하지 않은 설계 (Insecure Design)</title>
      <link>https://taegi-cloudsecurity.tistory.com/143</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;안전하지 않은 설계&lt;/span&gt;&lt;/b&gt;&lt;span&gt;란 &quot;&lt;/span&gt;&lt;b&gt;&lt;span&gt;개발 초기 설계 단계부터 보안 위협을 충분히 고려하지 않아 발생하는 문제&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&quot;를 말해. &lt;/span&gt; 이건 코드 한 줄을 잘못 짜는 '구현(implementation)'의 실수라기보다는, 애초에 건물의 '설계도(design)' 자체가 잘못된 것과 같아. 보안 기능이 없거나, 있더라도 허술하게 설계된 모든 경우가 여기에 해당되지.&lt;/p&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Part 1: 안전하지 않은 설계(Insecure Design)란 무엇일까?   &amp;ndash; &quot;첫 단추를 잘못 끼우다&quot;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 취약점은 코드상의 버그(bug)가 아니라, **설계상의 결함(flaw)**이라는 점이 중요해.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  은행 건물 비유로 쉽게 이해하기&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;안전한 설계&lt;/b&gt;: 은행을 짓는 건축가가 처음부터 두꺼운 벽의 금고, CCTV, 비상벨, 현금 수송차량 전용 출입구까지 모두 &lt;b&gt;설계도에 반영&lt;/b&gt;해서 짓는 것.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;안전하지 않은 설계&lt;/b&gt;: 건축가가 오직 미관과 고객 편의성만 생각해서 사방이 유리로 된 멋진 은행을 지었어. 다 짓고 나서야 &quot;아차, 보안!&quot; 하면서 뒤늦게 CCTV를 달고 경비원을 세우지만, 건물 자체의 유리벽과 금고의 부재라는 &lt;b&gt;근본적인 설계 결함&lt;/b&gt;은 쉽게 해결할 수 없지.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼, '안전하지 않은 설계'는 나중에 보안 패치를 덧대는 것만으로는 해결하기 어려운, 아키텍처 수준의 근본적인 보안 문제점을 의미해.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Part 2: 어떤 설계 실수가 있을까? (안전하지 않은 설계의 유형)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;유형 1: 허술한 비즈니스 로직 설계&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;시나리오&lt;/b&gt;: 한 온라인 쇼핑몰에서 &quot;신규 가입 1회용 50% 할인 쿠폰&quot; 이벤트를 열었어. 그런데 쿠폰을 적용했다가, 취소하고, 다시 적용하는 과정을 반복하면 중복으로 할인이 적용되는 설계상의 허점이 있었어!&lt;/li&gt;
&lt;li&gt;&lt;b&gt;문제점&lt;/b&gt;: 쿠폰을 적용하는 코드 자체에는 버그가 없었지만, &quot;쿠폰은 한 번만 사용되어야 한다&quot;는 &lt;b&gt;비즈니스 규칙을 강제하는 설계&lt;/b&gt;가 빠져있어서 회사에 금전적 손실을 입혔어.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;유형 2: 위협 모델링(Threat Modeling)의 부재&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;시나리오&lt;/b&gt;: 사용자가 프로필 사진을 업로드하는 기능을 만들 때, 개발팀은 정상적인 이미지 파일만 올라올 것이라고 가정했어. 하지만 공격자가 100GB짜리 거대 파일을 업로드해서 서버의 디스크를 가득 채우거나(Denial of Service), virus.exe 같은 악성 파일을 업로드하는 경우를 전혀 고려하지 않았지.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;문제점&lt;/b&gt;: 기능을 설계할 때 &quot;만약 공격자가 이 기능을 악용한다면 어떻게 할까?&quot;를 미리 고민하고 대책을 세우는 &lt;b&gt;'위협 모델링'&lt;/b&gt; 과정이 누락된 거야.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;유형 3: 안전장치 없는 기능 설계&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;시나리오&lt;/b&gt;: 비밀번호 찾기 기능을 설계할 때, 아이디나 이메일 주소만 입력하면 바로 임시 비밀번호를 발급해 주도록 만들었어. 본인인증을 위한 추가 질문이나, 이메일로 인증 링크를 보내는 등의 2차 검증 절차가 전혀 없었지.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;문제점&lt;/b&gt;: 기능은 설계된 대로 완벽하게 동작하지만, 그 &lt;b&gt;설계 자체가 보안적으로 매우 취약&lt;/b&gt;한 거야. 공격자가 다른 사람의 아이디만 알면 계정을 쉽게 탈취할 수 있어.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Part 3: 어떻게 처음부터 안전하게 설계할까? (대응 방안)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;나중에 고치지 말고, 처음부터 잘 만들자!&quot;가 핵심이야.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;보안 중심 소프트웨어 개발(Secure SDLC) 문화 정착&lt;/b&gt;: 보안은 개발 마지막에 하는 검수 과정이 아니야! &lt;b&gt;기획, 설계, 개발, 테스트, 배포, 운영&lt;/b&gt;까지 소프트웨어 개발의 모든 생명주기에 보안 활동을 자연스럽게 녹여내야 해.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;위협 모델링(Threat Modeling) 생활화&lt;/b&gt;: 새로운 기능을 설계할 때마다, 공격자의 입장에서 &quot;이 기능을 어떻게 악용할 수 있을까?&quot;, &quot;어떤 데이터가 위험에 처할 수 있을까?&quot;, &quot;어떤 보안 통제가 필요할까?&quot;를 &lt;b&gt;미리 식별하고 평가하며 대책을 수립&lt;/b&gt;하는 과정을 거쳐야 해.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;안전한 설계 패턴(Secure Design Patterns) 활용&lt;/b&gt;: 바퀴를 새로 발명할 필요는 없어. 이미 업계에서 검증된 안전한 설계 패턴들을 적극적으로 배우고 적용해야 해. (예: 중앙 집중식 접근 제어, 모든 통신 구간 암호화, 장애를 대비한 설계 등)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;재사용 가능한 보안 라이브러리 및 컴포넌트 사용&lt;/b&gt;: 인증, 암호화, 세션 관리처럼 중요한 보안 기능은 직접 만들려고 하지 말고, 전문가들에 의해 검증된 &lt;b&gt;안전한 라이브러리, 프레임워크, 컴포넌트&lt;/b&gt;를 사용하는 것이 훨씬 안전해.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[마무리하며 ✨]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 네 번째 취약점인 '안전하지 않은 설계'에 대해 알아봤어. 이 항목은 우리에게 **&quot;보안은 개발 초기 단계부터 시작되어야 하는 '문화'이자 '프로세스'&quot;**라는 중요한 메시지를 던져주고 있어.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코딩 실력도 중요하지만, 그에 앞서 &quot;어떻게 하면 더 안전한 시스템을 만들 수 있을까?&quot;를 고민하는 &lt;b&gt;보안 설계 마인드&lt;/b&gt;를 갖추는 것이 미래의 보안 전문가에게는 더욱 중요한 역량이 될 거야!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 시간에는 &lt;b&gt;다섯 번째 취약점, &quot;A05: 보안 설정 오류 (Security Misconfiguration)&quot;&lt;/b&gt; 에 대해 자세히 알아볼게! 기대해도 좋아!  &lt;/p&gt;</description>
      <category>02. 클라우드 보안 공부/개념정리</category>
      <category>A04</category>
      <category>InsecureDesign</category>
      <category>OWASPTop10</category>
      <category>ThreatModeling</category>
      <category>개념정리</category>
      <category>안전하지않은설계</category>
      <category>웹보안</category>
      <category>위협모델링</category>
      <category>정보보안</category>
      <category>태그: OWASP</category>
      <author>taegi-</author>
      <guid isPermaLink="true">https://taegi-cloudsecurity.tistory.com/143</guid>
      <comments>https://taegi-cloudsecurity.tistory.com/143#entry143comment</comments>
      <pubDate>Sun, 7 Sep 2025 20:45:43 +0900</pubDate>
    </item>
    <item>
      <title>✅ [개념정리] OWASP Top 10 파헤치기 (3/10): A03 - 인젝션 (Injection)</title>
      <link>https://taegi-cloudsecurity.tistory.com/142</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;내 말이 곧 법이다!&quot; 애플리케이션을 조종하는 교활한 공격, 인젝션 완벽 해부  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[들어가며: 애플리케이션에 명령을 주입하다]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹사이트의 로그인 창이나 검색창에 아이디나 검색어 대신 이상한 특수문자가 섞인 구문을 입력해서 시스템을 마비시키거나 정보를 빼내는 해커의 모습을 영화에서 본 적 있니? 그게 바로 &lt;b&gt;인젝션(Injection)&lt;/b&gt; 공격이야!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;OWASP Top 10의 세 번째 항목인 '인젝션'은 &quot;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;악의적인 사용자 입력값이 애플리케이션의 코드나 쿼리의 일부로 해석되어, 개발자가 의도하지 않은 명령을 실행시키는 모든 취약점&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&quot;을 말해. &lt;/span&gt; 공격자가 애플리케이션에 악성 코드를 '주입(inject)'해서 마음대로 조종하는 무서운 공격이지.&lt;/p&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Part 1: 인젝션(Injection)이란 무엇일까?   &amp;ndash; &quot;데이터인 척하는 명령어&quot;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인젝션 공격의 핵심 원리는 애플리케이션이 **&quot;사용자로부터 받은 입력값(데이터)&quot;**과 **&quot;시스템이 실행해야 할 명령어(코드/쿼리)&quot;**를 제대로 구분하지 못하는 허점을 파고드는 거야.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  자판기 비유로 쉽게 이해하기&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정상 작동&lt;/b&gt;: 자판기에 '콜라' 버튼(데이터)을 누르면, 자판기는 '콜라'를 내보내라는 명령을 수행해.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인젝션 공격&lt;/b&gt;: 어떤 해커가 자판기 버튼에 숨겨진 관리자 코드를 입력하는 방법을 알아냈어. 콜라; 모든음료수방출; 잔돈반환 이라는 특수 코드(악성 데이터)를 입력했더니, 자판기가 이걸 단순한 '음료수 선택' 데이터로 보지 않고, 숨겨진 &lt;b&gt;명령어&lt;/b&gt;로 인식해서 모든 음료수와 돈을 쏟아내는 거야!&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼, 인젝션은 사용자의 입력값이 평범한 데이터인 척 위장하고 시스템에 침투해서, 원래 실행되어서는 안 될 악의적인 명령을 실행시키는 공격이야.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Part 2: 인젝션 공격의 종류 &amp;ndash; &quot;주입&quot;의 다양한 방법들&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인젝션은 공격 대상(데이터베이스, 운영체제, 웹 브라우저 등)에 따라 여러 종류로 나뉘어. 네가 실습했던 내용들을 중심으로 가장 대표적인 유형들을 살펴보자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;유형 1: SQL 인젝션 (SQL Injection)&lt;/b&gt; 가장 유명하고 고전적인 인젝션 공격이야. 공격자가 웹 애플리케이션의 입력값에 악의적인 SQL 쿼리문을 주입해서 데이터베이스를 비정상적으로 조작하는 공격이지.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;시나리오&lt;/b&gt;:
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;로그인 창에서 사용자 ID를 입력받아 &lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;SELECT * FROM users WHERE user_id = '$id';&lt;/span&gt;&lt;span&gt; 와 같은 쿼리로 DB에서 회원 정보를 확인하는 웹사이트가 있어. &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;공격자는 ID 입력창에 &lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;admin&lt;/span&gt;&lt;span&gt; 같은 아이디 대신 &lt;/span&gt;&lt;span&gt;' OR '1'='1&lt;/span&gt;&lt;span&gt; 이라는 구문을 입력해. &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;서버에서는 이 입력값을 그대로 받아 SQL 쿼리를 만들어. &lt;span&gt;그 결과, &lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;SELECT * FROM users WHERE user_id = '' OR '1'='1';&lt;/span&gt;&lt;span&gt; 이라는 쿼리가 완성되지. &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;'1'='1'은 항상 참(True)이기 때문에, WHERE 조건절이 무조건 참이 되어버려! 결국 데이터베이스는 모든 사용자의 정보를 공격자에게 홀라당 넘겨주게 돼.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;위험성&lt;/b&gt;: 데이터베이스의 모든 정보를 훔쳐보거나(유출), 수정하고, 심지어 삭제할 수도 있어.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;유형 2: 명령어 인젝션 (Command Injection)&lt;/b&gt; 웹 애플리케이션을 통해 서버의 운영체제(OS) 시스템 명령어를 직접 실행시키는 공격이야.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;시나리오&lt;/b&gt;:
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;웹사이트에 IP 주소를 입력하면 ping 테스트 결과를 보여주는 기능이 있어. &lt;span&gt;서버 내부에서는 &lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;shell_exec('ping -c 4 ' . $target);&lt;/span&gt;&lt;span&gt; 와 같이 사용자의 입력을 받아 쉘 명령을 실행해. &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;공격자는 IP 주소 대신 &lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;8.8.8.8 &amp;amp;&amp;amp; ls&lt;/span&gt;&lt;span&gt; 라고 입력해. &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;리눅스 쉘에서 &amp;amp;&amp;amp;는 앞 명령이 성공하면 뒤 명령을 이어서 실행하라는 의미야. 결국 서버는 ping 명령을 실행한 뒤, 이어서 현재 디렉터리의 파일 목록을 보여주는 ls 명령까지 실행하고 그 결과를 화면에 출력해 버려!&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;위험성&lt;/b&gt;: 파일 목록 조회는 시작에 불과해. &lt;span&gt;공격자는 이 취약점을 통해 시스템 파일을 훔치거나 악성코드를 다운로드하고, 심지어 서버의 제어권을 완전히 장악하는 **리버스 쉘(Reverse Shell)**을 연결할 수도 있어. &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;유형 3: 크로스 사이트 스크립팅 (Cross-Site Scripting - XSS)&lt;/b&gt; XSS도 넓은 의미의 인젝션 공격이야. 앞선 두 공격이 서버를 직접 공격했다면, XSS는 &lt;b&gt;다른 사용자의 웹 브라우저&lt;/b&gt;에 악성 스크립트를 주입해서 실행시키는 공격이지.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;시나리오&lt;/b&gt;:
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;게시판에 글을 쓸 때, 공격자가 글 내용에 &lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&amp;lt;script&amp;gt;alert(document.cookie)&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;span&gt; 와 같은 자바스크립트 코드를 몰래 숨겨서 저장해. &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;다른 일반 사용자가 이 공격자의 글을 클릭해서 읽는 순간, 숨겨져 있던 스크립트가 &lt;b&gt;희생자의 웹 브라우저에서 실행&lt;/b&gt;돼.&lt;/li&gt;
&lt;li&gt;결과적으로 희생자의 로그인 정보가 담긴 쿠키 값이 공격자에게 전송될 수 있어.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;위험성&lt;/b&gt;: 사용자의 세션을 가로채서 계정을 도용(Session Hijacking)하거나, 사용자를 가짜 사이트로 유도(Phishing)하거나, 웹 페이지의 내용을 변조하는 등 다양한 피해를 유발할 수 있어.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Part 3: 어떻게 막을 수 있을까? (대응 방안)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인젝션 공격을 막는 가장 중요한 원칙은 &lt;b&gt;&quot;사용자의 입력은 절대 믿지 말고, 코드와 데이터를 철저히 분리하라!&quot;&lt;/b&gt; 는 거야.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;입력값 검증 (Input Validation)은 철저하게&lt;/b&gt;: 사용자로부터 입력받는 모든 값에 대해 &lt;b&gt;예상되는 데이터 타입, 길이, 형식 등이 맞는지 엄격하게 검사&lt;/b&gt;해야 해. 허용된 문자 목록(화이트리스트)을 만들어, 목록에 없는 문자(예: 특수문자)는 아예 받지 않는 것이 가장 좋아.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;준비된 구문(Prepared Statements) 사용 (SQL 인젝션 방어)&lt;/b&gt;: 이게 SQL 인젝션을 막는 가장 확실한 방법이야! SQL 쿼리문의 '틀'과 사용자가 입력한 '데이터'를 분리해서 DB에 전달하는 방식이지. 데이터베이스는 전달받은 데이터를 절대 쿼리문의 일부로 해석하지 않고, 오직 비교해야 할 '값'으로만 취급하기 때문에 인젝션 공격이 원천적으로 차단돼.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;시스템 명령어 직접 호출 피하기 (명령어 인젝션 방어)&lt;/b&gt;: 가급적이면 사용자 입력값을 가지고 시스템 명령어를 직접 호출하는 기능은 만들지 않는 것이 좋아. 꼭 필요하다면, 쉘의 특수문자들(;, &amp;amp;, |, (, ) 등)이 입력되지 못하도록 철저히 필터링해야 해.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;출력값 이스케이핑 (Output Escaping) (XSS 방어)&lt;/b&gt;: 사용자가 입력한 데이터를 다시 웹 페이지에 출력할 때는, &amp;lt;, &amp;gt;, &quot;, ' 등 HTML에서 특별한 의미를 갖는 문자들을 반드시 &lt;b&gt;이스케이프(escape)&lt;/b&gt; 처리해야 해. 예를 들어 &amp;lt;script&amp;gt;를 &amp;amp;lt;script&amp;amp;gt; 와 같은 문자열로 변환해서, 브라우저가 이걸 코드로 실행하지 않고 그냥 텍스트로만 보여주도록 만드는 거야.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;최소 권한 원칙 (Principle of Least Privilege) 적용&lt;/b&gt;: 데이터베이스에 접속하는 웹 애플리케이션의 계정에는 정말 필요한 최소한의 권한(예: 특정 테이블에 대한 읽기/쓰기 권한)만 부여해야 해. 이렇게 하면 혹시 SQL 인젝션 공격을 당하더라도 피해를 최소화할 수 있어.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[마무리하며 ✨]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 웹 애플리케이션을 위협하는 가장 강력하고 오래된 공격 중 하나인 '인젝션'에 대해 알아봤어. SQL 인젝션, 명령어 인젝션, XSS 등 종류는 다양하지만, 결국 **&quot;신뢰할 수 없는 사용자 입력을 코드나 쿼리의 일부로 해석해서 실행한다&quot;**는 공통점을 가지고 있지.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;사용자의 입력은 언제나 악의적일 수 있다&quot;는 사실을 명심하고, 코드와 데이터를 철저히 분리하는 시큐어 코딩 습관을 들이는 것이 인젝션 공격을 막는 가장 확실한 방법이라는 점, 꼭 기억하자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 시간에는 &lt;b&gt;네 번째 취약점, &quot;A04: 안전하지 않은 설계 (Insecure Design)&quot;&lt;/b&gt; 에 대해 자세히 알아볼게! 기대해도 좋아!  &lt;/p&gt;</description>
      <category>02. 클라우드 보안 공부/개념정리</category>
      <category>OWASP</category>
      <category>OWASPTop10</category>
      <author>taegi-</author>
      <guid isPermaLink="true">https://taegi-cloudsecurity.tistory.com/142</guid>
      <comments>https://taegi-cloudsecurity.tistory.com/142#entry142comment</comments>
      <pubDate>Sun, 7 Sep 2025 20:45:04 +0900</pubDate>
    </item>
    <item>
      <title>✅ [개념정리] OWASP Top 10 파헤치기 (2/10): A02 - 암호화 실패 (Cryptographic Failures)</title>
      <link>https://taegi-cloudsecurity.tistory.com/141</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;당신의 비밀번호는 안녕하신가요? 데이터 유출을 막는 암호화의 모든 것!  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[들어가며: 데이터를 지키는 최후의 보루, 암호화]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 웹사이트에 회원가입을 하거나, 인터넷 뱅킹을 이용할 때, 내 소중한 개인정보와 비밀번호는 과연 안전하게 전달되고 저장될까? 이 질문에 대한 해답이 바로 **'암호화'**에 있어. 암호화는 중요한 데이터를 제3자가 알아볼 수 없도록 비밀 코드로 바꾸는 기술로, 데이터를 지키는 최후의 보루와도 같아.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OWASP Top 10의 두 번째 항목인 **'암호화 실패'**는 바로 이 중요한 암호화를 제대로 적용하지 않았거나, 약하게 적용했을 때 발생하는 모든 문제를 다루고 있어.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Part 1: 암호화 실패(Cryptographic Failures)란 무엇일까?  &lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;암호화 실패&lt;/span&gt;&lt;/b&gt;&lt;span&gt;란, &quot;&lt;/span&gt;&lt;b&gt;&lt;span&gt;민감한 데이터(개인 정보, 비밀번호, 신용카드 정보 등)의 암호화가 약하거나 아예 누락된 경우&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&quot;를 말해&lt;/span&gt;. 이 취약점은 데이터가 **저장되어 있을 때(Data at Rest)**와 네트워크를 통해&lt;/p&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;전송 중일 때(Data in Transit)&lt;/b&gt; 모두에 해당돼.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  우편 비유로 쉽게 이해하기&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;암호화 누락&lt;/b&gt;: 중요한 계약 내용을 엽서에 그냥 써서 보내는 것과 같아. 배달 과정에서 누구나 그 내용을 훔쳐볼 수 있지. (HTTP 통신)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;약한 암호화&lt;/b&gt;: 계약 내용을 우리 동네 아이들만 아는 간단한 암호로 바꿔서 보내는 것과 같아. 조금만 노력하면 누구나 해독할 수 있겠지. (오래되고 취약한 암호화 알고리즘 사용)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;강력한 암호화&lt;/b&gt;: 계약 내용을 아무도 풀 수 없는 복잡한 암호로 바꾸고, 안전한 금고에 넣어 자물쇠까지 채워서 보내는 것과 같아. (HTTPS 통신 + 강력한 암호화 알고리즘)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Part 2: 어떻게 데이터가 노출될까? (암호화 실패의 흔한 유형)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 실제 웹 환경에서 암호화 실패는 어떤 모습으로 나타날까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;유형 1: 민감 데이터 평문 전송 (HTTP 통신)&lt;/b&gt; 가장 흔하고 기본적인 실수야. 사용자의 브라우저와 서버가 데이터를 주고받을 때 암호화를 전혀 하지 않는 경우지.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;시나리오&lt;/b&gt;:
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자가 공용 와이파이를 사용하는 카페에서 http://로 시작하는 쇼핑몰에 로그인해.&lt;/li&gt;
&lt;li&gt;같은 와이파이에 접속한 해커가 '패킷 스니핑'이라는 기술로 네트워크를 엿보고, 사용자가 입력한 아이디와 비밀번호를 평문 그대로 가로채.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;문제점&lt;/b&gt;: 데이터를 암호화하지 않는 HTTP 프로토콜을 사용해서, 전송 중인 데이터가 그대로 노출됐어.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;유형 2: 민감 데이터 평문 저장&lt;/b&gt; 데이터베이스(DB)에 사용자의 비밀번호나 개인정보를 암호화하지 않고 그대로 저장하는, 아주 위험한 경우야.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;시나리오&lt;/b&gt;:
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;어떤 웹사이트가 사용자의 비밀번호를 DB에 평문으로 저장하고 있었어.&lt;/li&gt;
&lt;li&gt;해커가 다른 취약점(예: SQL 인젝션)을 이용해 이 웹사이트의 DB를 탈취했어.&lt;/li&gt;
&lt;li&gt;해커는 아무런 노력 없이 모든 회원의 아이디와 비밀번호를 알아내고, 이를 다른 사이트 공격에 악용해.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;문제점&lt;/b&gt;: 저장된 데이터(Data at Rest)를 전혀 암호화하지 않아, DB가 뚫리는 순간 모든 정보가 속수무책으로 유출됐어.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;유형 3: 약한 암호화/해시 알고리즘 사용&lt;/b&gt; 암호화를 하긴 했지만, 이미 오래전에 해독 방법이 알려진 구식의 취약한 알고리즘을 사용한 경우야.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;시나리오&lt;/b&gt;:
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;한 웹사이트가 사용자 비밀번호를 오래된 &lt;b&gt;MD5나 SHA-1 같은 해시 알고리즘&lt;/b&gt;으로 변환해서 저장했어.&lt;/li&gt;
&lt;li&gt;해커가 DB를 탈취한 뒤, 미리 계산된 해시값들을 모아놓은 '레인보우 테이블'을 이용하거나 간단한 연산만으로 수많은 사용자의 원래 비밀번호를 복원해 내.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;문제점&lt;/b&gt;: 더 이상 안전하지 않은 암호화 기술을 사용해서, 암호화의 의미가 없어져 버렸어.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;유형 4: 부적절한 키 관리&lt;/b&gt; 강력한 알고리즘으로 데이터를 암호화했더라도, 그 암호를 풀 수 있는 '열쇠(암호화 키)'를 허술하게 관리하면 모든 게 무용지물이 돼.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;시나리오&lt;/b&gt;:
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;개발자가 소스 코드나 설정 파일 안에 암호화 키를 secret_key = &quot;1234567890&quot; 와 같이 평문으로 저장해 뒀어.&lt;/li&gt;
&lt;li&gt;해커가 GitHub 같은 코드 저장소에 실수로 올라간 소스 코드를 발견하거나 서버에 침투해서, 이 키를 손에 넣어.&lt;/li&gt;
&lt;li&gt;해커는 이 키를 이용해 암호화된 모든 데이터를 쉽게 복호화해서 훔쳐봐.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;문제점&lt;/b&gt;: 데이터를 잠근 금고는 튼튼했지만, 금고 열쇠를 금고 문에 붙여놓은 것과 같은 실수를 한 거지.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Part 3: 어떻게 데이터를 지킬 수 있을까? (대응 방안)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소중한 데이터를 안전하게 지키려면 다음과 같은 원칙들을 꼭 지켜야 해.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;데이터 분류 및 식별&lt;/b&gt;: 먼저 우리 시스템이 다루는 데이터 중 무엇이 민감한 정보인지 식별하고 분류해야 해. 모든 데이터를 암호화할 필요는 없으니까, 선택과 집중이 필요하지.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;전송 중 데이터 암호화는 필수!&lt;/b&gt;: 모든 웹 트래픽은 반드시 **TLS(HTTPS)**를 사용해서 암호화해야 해. 오래된 SSL/TLS 버전은 비활성화하고, 강력하고 최신 암호화 스위트(Cipher Suite)를 사용하도록 서버를 설정해야 해.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비밀번호는 반드시 해싱(Hashing)!&lt;/b&gt;: 비밀번호는 절대 평문이나 복호화가 가능한 암호화 방식으로 저장하면 안 돼! &lt;b&gt;Bcrypt, Scrypt, Argon2&lt;/b&gt; 와 같이 'Salt'를 사용하고 여러 번 반복해서 계산하는 &lt;b&gt;강력한 적응형 해시 함수&lt;/b&gt;를 사용해야 해.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;강력하고 검증된 암호화 알고리즘 사용&lt;/b&gt;: 절대 직접 암호화 알고리즘을 만들려고 시도하면 안 돼! &lt;b&gt;AES-256&lt;/b&gt;처럼 국제적으로 검증되고 널리 사용되는 표준 알고리즘을 사용해야 해.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;안전한 키 관리&lt;/b&gt;: 암호화 키는 소스 코드나 설정 파일에 절대 하드코딩하면 안 돼. &lt;b&gt;AWS KMS, Azure Key Vault, HashiCorp Vault&lt;/b&gt; 와 같은 전문 **키 관리 시스템(KMS)**을 사용해서 안전하게 보관하고 접근을 통제해야 해.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[마무리하며 ✨]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 두 번째 취약점인 '암호화 실패'에 대해 알아봤어. 이 취약점은 결국 &lt;b&gt;데이터 자체를 보호하는 데 실패&lt;/b&gt;했을 때 발생하는 문제야. 아무리 튼튼한 성벽(접근 통제)을 쌓았더라도, 성 안의 보물(데이터)을 허술한 상자에 담아두면 안 되겠지?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;어떤 데이터가 중요한지 식별하고, 전송 중이든 저장 중이든 강력하고 검증된 방식으로 암호화하며, 그 열쇠는 누구도 모르게 안전하게 보관한다!&quot;&lt;/b&gt; 이 원칙만 기억하면 암호화 실패로 인한 데이터 유출 사고를 막는 데 큰 도움이 될 거야!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 시간에는 웹 해킹 공격의 대명사! &lt;b&gt;&quot;A03: 인젝션 (Injection)&quot;&lt;/b&gt; 에 대해 자세히 알아볼게. 기대해도 좋아!  &lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Offer Next Step:&lt;/b&gt; A01(접근 통제)과 A02(암호화 실패)는 사용자의 '권한'과 '데이터'를 다루는 매우 중요한 주제들이야. 이 두 가지 취약점을 막기 위한 실제 코드 예시나 시나리오 기반의 실습 과정을 함께 만들어 볼까?&lt;/p&gt;</description>
      <category>02. 클라우드 보안 공부/개념정리</category>
      <author>taegi-</author>
      <guid isPermaLink="true">https://taegi-cloudsecurity.tistory.com/141</guid>
      <comments>https://taegi-cloudsecurity.tistory.com/141#entry141comment</comments>
      <pubDate>Thu, 4 Sep 2025 03:02:18 +0900</pubDate>
    </item>
    <item>
      <title>✅ [개념정리] OWASP Top 10 파헤치기 (1/10): A01 - 취약한 접근 통제 (Broken Access Control)</title>
      <link>https://taegi-cloudsecurity.tistory.com/140</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;내 정보가 남에게? 웹 보안의 가장 큰 구멍, 취약한 접근 통제의 모든 것!  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[들어가며: OWASP Top 10 정복 시리즈를 시작하며]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘부터 우리는 웹 애플리케이션 보안의 &quot;필독서&quot;와도 같은 &lt;b&gt;OWASP Top 10&lt;/b&gt;을 하나씩 정복하는 여정을 시작하려고 해. &lt;span&gt;OWASP(Open Web Application Security Project)는 웹 보안 강화를 목표로 하는 세계적인 비영리 단체로, 이들이 발표하는 Top 10 목록은 전 세계 개발자와 보안 전문가들이 반드시 알아야 할 가장 치명적인 웹 보안 취약점들을 정리한 것이야. &lt;/span&gt;&lt;/p&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 영광의(?) 첫 번째 자리를 차지한 취약점은 바로 &lt;b&gt;A01: 취약한 접근 통제(Broken Access Control)&lt;/b&gt;. 가장 흔하면서도 가장 위험한 취약점이라는 뜻이지. 그럼 지금부터 이 취약점이 무엇인지, 어떻게 우리를 위협하는지 자세히 알아보자!&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Part 1: 취약한 접근 통제(Broken Access Control)란 무엇일까?  &lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;취약한 접근 통제&lt;/span&gt;&lt;/b&gt;&lt;span&gt;란, 아주 간단히 말해 **&quot;권한 없는 사용자가 제한된 리소스(데이터, 기능)에 접근 가능한 경우&quot;**를 의미해. &lt;/span&gt; 즉, 사용자가 자신이 해서는 안 되는 행동을 하거나, 봐서는 안 되는 정보를 볼 수 있도록 시스템이 허용해 버리는 모든 상황이 여기에 해당돼.&lt;/p&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  호텔 비유로 쉽게 이해하기&lt;/b&gt; 내가 일반 객실 키를 받았는데, 이 키로 호텔의 모든 방은 물론이고 VVIP 스위트룸, 지배인 사무실까지 열 수 있다면 어떨까? 생각만 해도 아찔하지? 이게 바로 취약한 접근 통제야. 시스템이 사용자의 &quot;신분(인증)&quot;은 확인했지만, 그 신분에 맞는 &quot;권한(인가)&quot;을 제대로 확인하고 통제하지 않은 거지.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;왜 이게 1위일까?&lt;/b&gt; 이 취약점은 정말 흔하게 발견되고, 일단 뚫리면 &lt;b&gt;정보 유출, 데이터 변조 및 삭제, 관리자 권한 탈취&lt;/b&gt; 등 아주 심각한 피해로 직결되기 때문에 OWASP Top 10에서 가장 중요한 첫 번째 항목으로 꼽히고 있어.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Part 2: 어떻게 공격이 발생할까? (취약한 접근 통제의 흔한 유형)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 공격자들은 이 접근 통제의 허점을 어떻게 파고들까? 몇 가지 대표적인 공격 시나리오를 통해 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;유형 1: URL 직접 조작 (강제 브라우징 / IDOR)&lt;/b&gt; 가장 흔하고 단순한 공격 방식이야. 사용자가 URL 주소의 파라미터 값만 바꿔서 다른 사용자의 정보에 접근하는 거지.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;시나리오&lt;/b&gt;:
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;'taegi'라는 사용자가 로그인 후 자신의 게시글(post_id=101)을 보는 페이지 주소는 &lt;a href=&quot;https://myblog.com/view?post_id=101&quot;&gt;https://myblog.com/view?post_id=101&lt;/a&gt; 이야.&lt;/li&gt;
&lt;li&gt;이때 'taegi'가 주소창의 숫자를 &lt;a href=&quot;https://myblog.com/view?post_id=102&quot;&gt;https://myblog.com/view?post_id=102&lt;/a&gt;로 바꿨더니, 본 적도 없는 다른 사람의 비공개 글이 보여!&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;문제점&lt;/b&gt;: 서버가 &quot;로그인한 사용자인가?&quot;만 확인하고, &quot;이 사용자가 &lt;b&gt;102번 글을 볼 권한&lt;/b&gt;이 있는가?&quot;를 확인하는 절차를 빠뜨린 거야.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;유형 2: 권한 상승 (Privilege Escalation)&lt;/b&gt; 일반 사용자가 관리자만 접근할 수 있는 페이지나 기능을 사용할 수 있게 되는 경우야.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;시나리오&lt;/b&gt;:
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;일반 사용자로 로그인한 'taegi'는 관리자 페이지 버튼이 보이지 않아.&lt;/li&gt;
&lt;li&gt;하지만 혹시나 하는 마음에 주소창에 &lt;a href=&quot;https://myblog.com/admin/user_list&quot;&gt;https://myblog.com/admin/user_list&lt;/a&gt;라고 직접 입력했더니, 모든 회원 목록이 담긴 관리자 페이지가 떡하니 열려버렸어!&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;문제점&lt;/b&gt;: 개발자가 관리자 페이지로 가는 링크를 메뉴에서 숨겨놓기는 했지만, 해당 페이지 자체에 &quot;오직 관리자만 접근 가능!&quot;이라는 접근 통제 로직을 적용하는 것을 잊어버린 거지.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;유형 3: 파일 경로 조작 (Path Traversal)&lt;/b&gt; 공격자가 URL이나 파라미터에 파일 경로를 조작하는 문자(../ 등)를 넣어서, 원래 접근해서는 안 되는 시스템의 민감한 파일에 접근하는 공격이야.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;시나리오&lt;/b&gt;:
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자가 &lt;a href=&quot;https://myblog.com/download?file=mypic.jpg&quot;&gt;https://myblog.com/download?file=mypic.jpg&lt;/a&gt; 주소를 통해 파일을 다운받을 수 있어.&lt;/li&gt;
&lt;li&gt;공격자가 이 주소를 &lt;a href=&quot;https://myblog.com/download?file=../../../../etc/passwd&quot;&gt;https://myblog.com/download?file=../../../../etc/passwd&lt;/a&gt; 와 같이 조작했더니, 서버의 사용자 정보가 담긴 시스템 파일이 다운로드돼.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;문제점&lt;/b&gt;: 서버가 file 파라미터로 들어온 값에 ../ 같은 위험한 문자가 포함되어 있는지 제대로 검사하지 않고 파일 경로로 그대로 사용한 거야.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Part 3: 어떻게 막을 수 있을까? (대응 방안)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 무서운 취약점을 막으려면, &quot;아무도 믿지 마, 계속 확인해!&quot;라는 제로 트러스트의 기본 원칙을 애플리케이션에 적용해야 해.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;기본은 거부 (Default Deny) 원칙 적용&lt;/b&gt;: 가장 중요한 원칙이야! 모든 접근은 &lt;b&gt;기본적으로 차단&lt;/b&gt;하고, 오직 특정 역할이나 사용자에게만 꼭 필요한 접근을 &lt;b&gt;명시적으로 허용&lt;/b&gt;하는 '화이트리스트' 방식으로 정책을 수립해야 해.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;모든 요청에 대한 권한 검증&lt;/b&gt;: 사용자의 눈에 보이는 모든 요청(클라이언트 측)뿐만 아니라, 서버에 도달하는 &lt;b&gt;모든 요청에 대해 서버 측에서&lt;/b&gt; 사용자의 권한을 매번 다시 확인해야 해. &quot;이 사용자가 이 데이터/기능에 접근할 권한이 정말 있는가?&quot;를 끊임없이 물어야 해.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;중앙 집중식 권한 관리&lt;/b&gt;: 접근 통제 로직을 여러 페이지에 흩어서 만들면 실수가 발생하기 쉬워. 권한 검증 로직은 한 곳에서 &lt;b&gt;중앙 집중적으로 관리&lt;/b&gt;하고, 모든 페이지나 기능이 이 중앙 로직을 재사용하도록 설계하는 것이 안전해.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;역할 기반 접근 제어 (RBAC) 사용&lt;/b&gt;: 개별 사용자마다 일일이 권한을 부여하는 것보다 '관리자', '편집자', '일반회원' 등과 같은 &lt;b&gt;역할(Role)을 정의&lt;/b&gt;하고, 각 역할에 맞는 권한을 부여한 뒤 사용자에게는 역할을 할당하는 방식이 훨씬 관리하기 쉽고 안전해.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용자에게 노출되는 정보 최소화&lt;/b&gt;: 사용자의 권한에 따라 접근할 수 없는 메뉴나 버튼은 아예 처음부터 보이지 않도록 처리해서 공격의 빌미를 주지 않는 것이 좋아. (물론, 이건 편의성을 위한 것이고 진짜 보안은 서버 측에서 이뤄져야 해!)&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[마무리하며 ✨]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 OWASP Top 10의 첫 번째 항목인 '취약한 접근 통제'에 대해 알아봤어. 결국 이 취약점은 &lt;b&gt;인증(Authentication)은 되었지만, 인가(Authorization)가 제대로 되지 않았을 때&lt;/b&gt; 발생하는 문제라고 요약할 수 있어.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;로그인했으니 괜찮겠지?&quot;라는 안일한 생각이 가장 큰 위험이야! 모든 요청을 의심하고, 모든 접근을 검증하는 꼼꼼한 습관이 우리 웹사이트를 지키는 가장 강력한 무기라는 점, 꼭 기억하자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 시간에는 &lt;b&gt;두 번째 취약점, &quot;A02: 암호화 실패 (Cryptographic Failures)&quot;&lt;/b&gt; 에 대해 자세히 알아볼게! 기대해도 좋아!  &lt;/p&gt;</description>
      <category>02. 클라우드 보안 공부/개념정리</category>
      <category>A01</category>
      <category>BrokenAccessControl</category>
      <category>IDOR</category>
      <category>OWASP</category>
      <category>OWASPTop10</category>
      <category>개념정리</category>
      <category>권한상승</category>
      <category>웹보안</category>
      <category>정보보안</category>
      <category>취약한접근통제</category>
      <author>taegi-</author>
      <guid isPermaLink="true">https://taegi-cloudsecurity.tistory.com/140</guid>
      <comments>https://taegi-cloudsecurity.tistory.com/140#entry140comment</comments>
      <pubDate>Wed, 3 Sep 2025 18:28:35 +0900</pubDate>
    </item>
    <item>
      <title>StyleFlow Project: Multi-Cloud Observability &amp;amp; DevOps Pipeline</title>
      <link>https://taegi-cloudsecurity.tistory.com/139</link>
      <description>&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 프로젝트는 개인 학습 및 포트폴리오 목적으로 진행된 가상 프로젝트입니다.&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 프로젝트 개요 (Overview)&lt;/h2&gt;
&lt;a id=&quot;user-content-1-프로젝트-개요-overview&quot; style=&quot;background-color: #000000; color: #0969da;&quot; href=&quot;https://github.com/Taegi97/styleflow-sample-app#1-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B0%9C%EC%9A%94-overview&quot;&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;[cite_start]불안정한 온프레미스 쇼핑몰 'StyleFlow'를,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;멀티클라우드(AWS, Azure, NCP) 환경에서 동작하는 MSA 기반의 탄력적이고 관측 가능한 플랫폼으로 전환&lt;/b&gt;하고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;PR(Pull Request) 기반의 자동화된 CI/CD 파이프라인을 구축&lt;/b&gt;하는 것을 목표로 합니다. [cite: 365, 366]&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 리포지토리(styleflow-sample-app)는 전체 인프라 중 CI/CD 파이프라인에 의해 배포되는 샘플 애플리케이션입니다.&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 전체 아키텍처 (Architecture)&lt;/h2&gt;
&lt;a id=&quot;user-content-2-전체-아키텍처-architecture&quot; style=&quot;background-color: #000000; color: #0969da;&quot; href=&quot;https://github.com/Taegi97/styleflow-sample-app#2-%EC%A0%84%EC%B2%B4-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-architecture&quot;&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 프로젝트는 AWS EKS를 중앙 허브로 사용하여, 분산된 멀티클라우드 환경의 지표를 통합하고, 선언적 설정과 보안 통신을 적용한 관제 시스템을 설계했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1729&quot; data-origin-height=&quot;1247&quot;&gt;&lt;span data-url=&quot;https://github.com/Taegi97/styleflow-sample-app/raw/main/architecture_diagram.png&quot; data-phocus=&quot;https://github.com/Taegi97/styleflow-sample-app/raw/main/architecture_diagram.png&quot;&gt;&lt;img src=&quot;https://github.com/Taegi97/styleflow-sample-app/raw/main/architecture_diagram.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fgithub.com%2FTaegi97%2Fstyleflow-sample-app%2Fraw%2Fmain%2Farchitecture_diagram.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1729&quot; height=&quot;1247&quot; data-origin-width=&quot;1729&quot; data-origin-height=&quot;1247&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;▲ Terraform으로 구축된 AWS EKS 기반 멀티클라우드 모니터링 아키텍처&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 핵심 기능 (Key Features)&lt;/h2&gt;
&lt;a id=&quot;user-content-3-핵심-기능-key-features&quot; style=&quot;background-color: #000000; color: #0969da;&quot; href=&quot;https://github.com/Taegi97/styleflow-sample-app#3-%ED%95%B5%EC%8B%AC-%EA%B8%B0%EB%8A%A5-key-features&quot;&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;[cite_start]&lt;b&gt;Infrastructure as Code (IaC):&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Terraform을 사용하여 AWS EKS 클러스터와 Private Subnet 기반의 네트워크 인프라를 코드로 관리합니다. [cite: 3566-3599]&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CI/CD 자동화:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;GitHub Actions를 활용하여, PR 생성 시 고유한 테스트 환경을 Kubernetes에 자동 배포하고, PR이 닫히면 관련 리소스를 자동으로 정리하는 'PR 미리보기 환경'을 구축했습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;통합 관제 시스템:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Prometheus와 Grafana 스택을 활용하여 AWS, Azure, NCP의 모든 모니터링 타겟을 단일 대시보드에서 통합 관제합니다.&lt;/li&gt;
&lt;li&gt;[cite_start]&lt;b&gt;보안 내재화 (Security by Design):&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Private Subnet,&lt;span&gt;&amp;nbsp;&lt;/span&gt;kube-rbac-proxy,&lt;span&gt;&amp;nbsp;&lt;/span&gt;TLS/Bearer&lt;span&gt;&amp;nbsp;&lt;/span&gt;인증,&lt;span&gt;&amp;nbsp;&lt;/span&gt;RBAC&lt;span&gt;&amp;nbsp;&lt;/span&gt;최소 권한 원칙 등을 적용하여 설계 단계부터 보안을 내재화했습니다. [cite: 3886-3938]&lt;/li&gt;
&lt;li&gt;[cite_start]&lt;b&gt;실시간 장애 알림:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Alertmanager와 Slack을 연동하여, 장애 발생 및 해결 시 지정된 채널로 실시간 알림을 전송합니다. [cite: 3659-3669]&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 기술 스택 (Tech Stack)&lt;/h2&gt;
&lt;a id=&quot;user-content-4-기술-스택-tech-stack&quot; style=&quot;background-color: #000000; color: #0969da;&quot; href=&quot;https://github.com/Taegi97/styleflow-sample-app#4-%EA%B8%B0%EC%88%A0-%EC%8A%A4%ED%83%9D-tech-stack&quot;&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;구분기술&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;background-color: #ffffff;&quot;&gt;
&lt;td&gt;&lt;b&gt;Cloud&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;AWS (EKS, VPC, EC2, IAM), Azure (ACI), NCP (VM)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;background-color: #f6f8fa;&quot;&gt;
&lt;td&gt;&lt;b&gt;IaC&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Terraform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;background-color: #ffffff;&quot;&gt;
&lt;td&gt;&lt;b&gt;CI/CD&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;GitHub Actions, Jenkins, Docker, Maven&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;background-color: #f6f8fa;&quot;&gt;
&lt;td&gt;&lt;b&gt;Container&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Kubernetes (kubectl, Helm, Kustomize), Docker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;background-color: #ffffff;&quot;&gt;
&lt;td&gt;&lt;b&gt;Observability&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Prometheus, Grafana, Alertmanager&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;background-color: #f6f8fa;&quot;&gt;
&lt;td&gt;&lt;b&gt;Security&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;IAM (IRSA), TLS, RBAC, kube-rbac-proxy, Zero Trust&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;background-color: #ffffff;&quot;&gt;
&lt;td&gt;&lt;b&gt;OS &amp;amp; Network&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Linux (Ubuntu), VPC, Subnetting, NAT, Firewall&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;▲ 프로젝트에 사용된 주요 기술 스택&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. CI/CD 파이프라인: PR 미리보기 환경&lt;/h2&gt;
&lt;a id=&quot;user-content-5-cicd-파이프라인-pr-미리보기-환경&quot; style=&quot;background-color: #000000; color: #0969da;&quot; href=&quot;https://github.com/Taegi97/styleflow-sample-app#5-cicd-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-pr-%EB%AF%B8%EB%A6%AC%EB%B3%B4%EA%B8%B0-%ED%99%98%EA%B2%BD&quot;&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 리포지토리의 핵심 기능 중 하나는 GitHub Actions를 활용한 PR 미리보기 환경 자동화입니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;▲ PR 생성 시, 자동으로 임시 환경을 배포하고 미리보기 URL을 댓글로 남기는 워크플로우&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;개발자가&lt;span&gt;&amp;nbsp;&lt;/span&gt;main&lt;span&gt;&amp;nbsp;&lt;/span&gt;브랜치로 Pull Request를 생성합니다.&lt;/li&gt;
&lt;li&gt;GitHub Actions 워크플로우가 트리거되어, 애플리케이션을 빌드하고 Docker 이미지를 생성하여 ECR에 푸시합니다.&lt;/li&gt;
&lt;li&gt;PR 번호를 기반으로 EKS 클러스터에 고유한 네임스페이스(pr-&amp;lt;번호&amp;gt;)를 생성하고, 애플리케이션을 배포합니다.&lt;/li&gt;
&lt;li&gt;배포가 완료되면, 접근 가능한 미리보기 URL을 PR에 댓글로 자동 게시합니다.&lt;/li&gt;
&lt;li&gt;PR이 Merge되거나 Close되면, 관련 네임스페이스와 모든 리소스를 자동으로 삭제합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;div style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 연락처 (Contact)&lt;/h2&gt;
&lt;a id=&quot;user-content-6-연락처-contact&quot; style=&quot;background-color: #000000; color: #0969da;&quot; href=&quot;https://github.com/Taegi97/styleflow-sample-app#6-%EC%97%B0%EB%9D%BD%EC%B2%98-contact&quot;&gt;&lt;/a&gt;&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;[cite_start]&lt;b&gt;GitHub:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;background-color: #000000; color: #0969da;&quot; href=&quot;https://github.com/Taegi97&quot;&gt;Taegi97&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;[cite: 4166]&lt;/li&gt;
&lt;li&gt;[cite_start]&lt;b&gt;Email:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;background-color: #000000; color: #0969da;&quot; href=&quot;mailto:namtjin1@naver.com&quot;&gt;namtjin1@naver.com&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;[cite: 4174]&lt;/li&gt;
&lt;li&gt;[cite_start]&lt;b&gt;Blog:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://taegi-cloudsecurity.tistory.com/&quot;&gt;https://taegi-cloudsecurity.tistory.com/&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;[cite: 4166]&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>03. 개발&amp;amp;포트폴리오</category>
      <author>taegi-</author>
      <guid isPermaLink="true">https://taegi-cloudsecurity.tistory.com/139</guid>
      <comments>https://taegi-cloudsecurity.tistory.com/139#entry139comment</comments>
      <pubDate>Wed, 3 Sep 2025 17:30:54 +0900</pubDate>
    </item>
    <item>
      <title>✅ [개념정리] 쉘 스크립트의 작은따옴표('')와 큰따옴표(&amp;quot;&amp;quot;), 결정적 차이!</title>
      <link>https://taegi-cloudsecurity.tistory.com/138</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;변수가 왜 출력이 안 될까? 따옴표 하나로 발생하는 버그, 완벽하게 막는 법!  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[들어가며: 사소하지만 치명적인 따옴표의 세계]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉘 스크립트를 작성할 때, 우리는 문자열을 감싸기 위해 자연스럽게 따옴표를 사용해. 그런데 언제 작은따옴표('')를 쓰고, 언제 큰따옴표(&quot;&quot;)를 써야 할지 정확히 알고 있니?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 둘의 차이를 이해하는 것은 쉘 스크립트의 버그를 줄이고, 내 의도를 정확하게 표현하기 위해 정말 중요해. &quot;그냥 아무거나 쓰면 되는 거 아니야?&quot;라고 생각했다면, 오늘 이 글을 통해 그 생각이 확실하게 바뀔 거야!&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Part 1: 큰따옴표 (Double Quotes: &quot;&quot;) &amp;ndash; &quot;내용물을 해석해주세요!&quot;  &lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;큰따옴표&lt;/b&gt;는 문자열을 감싸되, 그 안에 있는 특정 특수문자들을 쉘이 **해석하고 확장(expand)**하도록 허용해.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;핵심 기능&lt;/b&gt;: 변수나 명령어의 **'결과값'**을 문자열 안에 포함시키고 싶을 때 사용해.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;해석되는 주요 항목&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;변수 (Variables)&lt;/b&gt;: $USER_NAME이나 ${USER_NAME} 같은 변수는 실제 값(예: taegi)으로 치환돼.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;명령어 치환 (Command Substitution)&lt;/b&gt;: $(pwd)나 `pwd` 같은 명령어는 그 실행 결과(예: /home/taegi)로 치환돼.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;특수문자&lt;/b&gt;: $(변수), `(명령어 치환), \(이스케이프) 등은 특별한 의미를 유지해.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예시 코드&lt;/b&gt;:&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;&lt;span&gt;Bash&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;#!/bin/bash
USER_NAME=&quot;taegi&quot;

echo &quot;큰따옴표: 안녕하세요, $USER_NAME 님!&quot;
echo &quot;큰따옴표: 현재 디렉터리는 $(pwd) 입니다.&quot;
echo &quot;큰따옴표: 달러(\$) 기호를 그냥 쓰려면 이스케이프가 필요해요.&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실행 결과&lt;/b&gt;:&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;큰따옴표: 안녕하세요, taegi 님!
큰따옴표: 현재 디렉터리는 /home/user 입니다.
큰따옴표: 달러($) 기호를 그냥 쓰려면 이스케이프가 필요해요.
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;큰따옴표(&quot;&quot;): 변수의 값을 출력하고 싶을 때!&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Part 2: 작은따옴표 (Single Quotes: '') &amp;ndash; &quot;아무것도 건드리지 마!&quot;  &amp;zwj;♂️&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;작은따옴표&lt;/b&gt;는 그 안에 있는 &lt;b&gt;모든 문자를 있는 그대로의 '글자'로&lt;/b&gt; 취급해. 쉘의 어떤 해석이나 확장도 허용하지 않는 가장 강력한 형태의 인용이지.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;핵심 기능&lt;/b&gt;: 변수나 특수문자를 해석하지 않고, &lt;b&gt;문자열 자체를 그대로&lt;/b&gt; 보여주거나 다른 명령어에 전달하고 싶을 때 사용해.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;특징&lt;/b&gt;: 작은따옴표 안에서는 모든 것이 문자 그대로의 의미를 가져. 예외는 없어!&lt;/li&gt;
&lt;li&gt;&lt;b&gt;예시 코드&lt;/b&gt;:&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;&lt;span&gt;Bash&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;#!/bin/bash
USER_NAME=&quot;taegi&quot;

echo '작은따옴표: 안녕하세요, $USER_NAME 님!'
echo '작은따옴표: 현재 디렉터리는 $(pwd) 입니다.'
echo '작은따옴표: 달러($) 기호도 그냥 문자일 뿐!'
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실행 결과&lt;/b&gt;:&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;작은따옴표: 안녕하세요, $USER_NAME 님!
작은따옴표: 현재 디렉터리는 $(pwd) 입니다.
작은따옴표: 달러($) 기호도 그냥 문자일 뿐!
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;작은따옴표(''): 특수문자를 포함한 모든 내용을 글자 그대로 보여주고 싶을 때!&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;[한눈에 비교하기 &amp;amp; 최종 요약]&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드를 실행해보면 그 차이가 더 명확하게 보일 거야.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;Bash&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;#!/bin/bash
MY_VAR=&quot;World&quot;

echo &quot;큰따옴표는 변수를 해석해서 -&amp;gt; Hello, $MY_VAR&quot;
echo '작은따옴표는 변수를 무시하고 -&amp;gt; Hello, $MY_VAR'
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;구분&lt;/td&gt;
&lt;td&gt;큰따옴표 (&quot;&quot;)&lt;/td&gt;
&lt;td&gt;작은따옴표 ('')&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;변수 확장&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;O&lt;/b&gt; (예: $USER &amp;rarr; taegi)&lt;/td&gt;
&lt;td&gt;&lt;b&gt;X&lt;/b&gt; (예: $USER &amp;rarr; $USER)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;명령어 치환&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;O&lt;/b&gt; (예: $(pwd) &amp;rarr; /home/taegi)&lt;/td&gt;
&lt;td&gt;&lt;b&gt;X&lt;/b&gt; (예: $(pwd) &amp;rarr; $(pwd))&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;특수문자&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;일부 해석됨 ($, \, `)&lt;/td&gt;
&lt;td&gt;&lt;b&gt;모두 문자 그대로&lt;/b&gt; 취급&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;주요 용도&lt;/b&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;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Sheets로 내보내기&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[마무리하며 ✨]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 확실히 알겠지? 이 간단한 규칙만 기억하자!&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;변수나 명령어의 '결과'를 넣고 싶으면 큰따옴표(&quot;&quot;),&lt;/b&gt; &lt;b&gt;모든 것을 있는 '글자 그대로' 쓰고 싶으면 작은따옴표('')!&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 사소해 보이는 차이 하나가 쉘 스크립트에서는 예상치 못한 버그를 만들기도 하고, 또 막아주기도 해. 앞으로 스크립트를 작성할 때는 내가 어떤 의도로 문자열을 사용하는지 한번 더 생각하고 알맞은 따옴표를 선택하는 습관을 들여보자!  &lt;/p&gt;</description>
      <category>02. 클라우드 보안 공부/개념정리</category>
      <category>bash</category>
      <category>DevOps</category>
      <category>DoubleQuotes</category>
      <category>Linux</category>
      <category>Quoting</category>
      <category>shellscript</category>
      <category>개념정리</category>
      <category>따옴표</category>
      <category>스크립팅</category>
      <category>코딩팁</category>
      <author>taegi-</author>
      <guid isPermaLink="true">https://taegi-cloudsecurity.tistory.com/138</guid>
      <comments>https://taegi-cloudsecurity.tistory.com/138#entry138comment</comments>
      <pubDate>Tue, 29 Jul 2025 15:23:06 +0900</pubDate>
    </item>
    <item>
      <title>✅ [개념정리] Dockerfile의 CMD와 ENTRYPOINT, 뭐가 다를까?   (헷갈리는 개념 바로잡기)</title>
      <link>https://taegi-cloudsecurity.tistory.com/137</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;좋아, 태기야! 지난 시간에 쿠버네티스의 레이블과 셀렉터를 알아봤으니, 이번에는 그보다 더 근본적인 단위인 &lt;b&gt;컨테이너 이미지를 만들 때&lt;/b&gt; 많은 사람들이 헷갈려 하는 작은 개념 하나를 확실하게 짚고 넘어가 볼게.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 Dockerfile을 작성할 때마다 고민하게 되는 &lt;b&gt;CMD와 ENTRYPOINT의 차이점&lt;/b&gt;이야! 둘 다 컨테이너가 시작될 때 명령어를 실행하는 것 같은데, 도대체 뭐가 다른 걸까? 오늘 제대로 파헤쳐 보자!  &lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;제목:&lt;/b&gt; ✅ [개념정리] Dockerfile의 CMD와 ENTRYPOINT, 뭐가 다를까?   (헷갈리는 개념 바로잡기)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;부제 (선택):&lt;/b&gt; 컨테이너의 첫인상을 결정하는 두 명령어, 완벽하게 이해하고 사용하기!  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[들어가며: 컨테이너의 시작을 알리는 두 가지 방법]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerfile을 작성하다 보면, 컨테이너가 시작될 때 실행될 명령어를 지정하기 위해 CMD나 ENTRYPOINT라는 지시어(instruction)를 사용하게 돼.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;둘 다 명령어를 실행하는 것 같은데, 왜 두 가지나 있고 정확히 어떤 차이가 있는 걸까?&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 둘의 미묘하지만 아주 중요한 차이점을 이해하면, 훨씬 더 유연하고 예측 가능한 컨테이너 이미지를 만들 수 있어. 지금부터 이 둘의 정체를 밝혀보자!&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Part 1: CMD &amp;ndash; 컨테이너의 &quot;기본값&quot; 명령어  ️&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;**CMD**는 컨테이너가 시작될 때 실행될 &lt;b&gt;기본(default) 명령어&lt;/b&gt;를 지정해. 여기서 핵심 단어는 바로 **'기본값'**이야.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;핵심 특징&lt;/b&gt;: docker run 명령어를 사용할 때 뒤에 다른 명령어를 붙이면, Dockerfile에 있던 CMD 명령어는 &lt;b&gt;쉽게 무시되고 새로운 명령어로 덮어써져(override)&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용 형식&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CMD [&quot;실행파일&quot;, &quot;파라미터1&quot;, &quot;파라미터2&quot;] (exec form, &lt;b&gt;권장 형식&lt;/b&gt;)&lt;/li&gt;
&lt;li&gt;CMD 명령어 파라미터1 파라미터2 (shell form)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;간단한 예시&lt;/b&gt;:&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;&lt;span&gt;Dockerfile&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;FROM ubuntu:latest
CMD [&quot;echo&quot;, &quot;Hello from CMD&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실행 결과&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;docker run &amp;lt;이미지이름&amp;gt; &amp;rarr; CMD에 지정된 기본 명령어가 실행되어 &quot;Hello from CMD&quot;가 출력돼.&lt;/li&gt;
&lt;li&gt;docker run &amp;lt;이미지이름&amp;gt; ls -l &amp;rarr; docker run 뒤에 붙인 ls -l 명령어가 기존 CMD를 &lt;b&gt;완전히 덮어쓰고 실행&lt;/b&gt;돼. &quot;Hello from CMD&quot;는 출력되지 않아.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;CMD는 사용자가 별다른 명령을 주지 않았을 때를 위한 '기본 메뉴' 같은 거야. 손님이 다른 메뉴를 주문하면 기본 메뉴는 나오지 않지!&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Part 2: ENTRYPOINT &amp;ndash; 컨테이너의 &quot;고정&quot; 실행 파일  &lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;**ENTRYPOINT**는 컨테이너를 &lt;b&gt;하나의 특정 실행 파일처럼&lt;/b&gt; 동작하도록 만들 때 사용해. 여기서 핵심 단어는 **'고정'**이야.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;핵심 특징&lt;/b&gt;: docker run 명령어 뒤에 오는 내용들은 ENTRYPOINT 명령어를 덮어쓰는 게 아니라, ENTRYPOINT에 지정된 실행 파일에 **전달되는 파라미터(인자)**로 취급돼.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용 형식&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ENTRYPOINT [&quot;실행파일&quot;, &quot;파라미터1&quot;, &quot;파라미터2&quot;] (exec form, &lt;b&gt;권장 형식&lt;/b&gt;)&lt;/li&gt;
&lt;li&gt;ENTRYPOINT 명령어 파라미터1 파라미터2 (shell form)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;간단한 예시&lt;/b&gt;:&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;&lt;span&gt;Dockerfile&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;FROM ubuntu:latest
ENTRYPOINT [&quot;ping&quot;, &quot;localhost&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실행 결과&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;docker run &amp;lt;이미지이름&amp;gt; &amp;rarr; ENTRYPOINT에 지정된 ping localhost 명령어가 실행돼.&lt;/li&gt;
&lt;li&gt;docker run &amp;lt;이미지이름&amp;gt; -c 5 &amp;rarr; docker run 뒤에 붙인 -c 5가 ENTRYPOINT 명령어의 &lt;b&gt;파라미터로 추가&lt;/b&gt;되어, 최종적으로 ping localhost -c 5가 실행돼.&lt;/li&gt;
&lt;li&gt;ENTRYPOINT 자체를 바꾸려면 docker run --entrypoint &amp;lt;바꿀명령어&amp;gt;처럼 별도의 플래그를 사용해야만 해.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ENTRYPOINT는 이 컨테이너가 '어떤 프로그램인지'를 고정하는 역할을 해. ping 프로그램 컨테이너, curl 프로그램 컨테이너처럼 말이야!&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Part 3: ENTRYPOINT와 CMD의 환상적인 조합!  &lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 CMD와 ENTRYPOINT의 진가는 이 둘을 &lt;b&gt;함께 사용할 때&lt;/b&gt; 드러나!&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Best Practice: ENTRYPOINT로는 고정된 실행 파일을 지정하고, CMD로는 그 실행 파일의 &quot;기본 파라미터&quot;를 지정한다.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;동작 방식&lt;/b&gt;: CMD에 지정된 내용이 ENTRYPOINT에 지정된 명령어의 기본 파라미터로 합쳐져서 실행돼. 그리고 docker run 뒤에 다른 인자를 주면 CMD의 기본 파라미터만 덮어쓰게 되지.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;최고의 예시&lt;/b&gt;:&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;&lt;span&gt;Dockerfile&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;FROM ubuntu:latest
ENTRYPOINT [&quot;ping&quot;]
CMD [&quot;-c&quot;, &quot;3&quot;, &quot;localhost&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실행 결과&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;docker run &amp;lt;이미지이름&amp;gt; &amp;rarr; ENTRYPOINT와 CMD가 합쳐져서 ping -c 3 localhost가 실행돼. (CMD가 기본 파라미터 역할을 했지?)&lt;/li&gt;
&lt;li&gt;docker run &amp;lt;이미지이름&amp;gt; google.com &amp;rarr; docker run 뒤에 붙인 google.com이 CMD의 기본 파라미터를 &lt;b&gt;덮어쓰고&lt;/b&gt;, ENTRYPOINT의 파라미터가 돼. 최종적으로 ping google.com이 실행돼.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 조합하면, 이 컨테이너는 **&quot;기본적으로 localhost에 3번 ping을 보내는, 하지만 원하면 다른 주소에도 ping을 보낼 수 있는 ping 전용 컨테이너&quot;**가 되는 거야. 정말 유연하고 직관적이지?&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[마무리하며: 그래서, 언제 뭘 써야 할까?  ]&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ENTRYPOINT를 써야 할 때&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨테이너를 하나의 실행 파일(도구)처럼 만들고 싶을 때.&lt;/li&gt;
&lt;li&gt;메인 애플리케이션은 고정하고, 실행 시 파라미터만 유연하게 바꾸고 싶을 때.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CMD만 써야 할 때&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨테이너의 기본 실행 명령어를 지정하되, 사용자가 docker run으로 완전히 다른 명령어를 실행할 수 있도록 허용하고 싶을 때. (예: ubuntu 이미지의 기본 bash 쉘)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ENTRYPOINT와 CMD를 함께 쓸 때 (강력 추천!)&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨테이너의 주된 실행 파일을 ENTRYPOINT로 고정하고, CMD로 기본 파라미터를 제공하여 유연성을 더하고 싶을 때.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 CMD와 ENTRYPOINT의 차이점이 확실히 정리되었기를 바란다! 이 둘의 관계를 잘 이해하고 Dockerfile을 작성하면, 훨씬 더 재사용하기 좋고 직관적인 컨테이너를 만들 수 있을 거야.  &lt;/p&gt;</description>
      <category>02. 클라우드 보안 공부/개념정리</category>
      <category>CMD</category>
      <category>container</category>
      <category>DevOps</category>
      <category>docker</category>
      <category>dockerfile</category>
      <category>entrypoint</category>
      <category>개념정리</category>
      <category>도커</category>
      <category>이미지빌드</category>
      <category>컨테이너</category>
      <author>taegi-</author>
      <guid isPermaLink="true">https://taegi-cloudsecurity.tistory.com/137</guid>
      <comments>https://taegi-cloudsecurity.tistory.com/137#entry137comment</comments>
      <pubDate>Tue, 29 Jul 2025 15:17:59 +0900</pubDate>
    </item>
    <item>
      <title>✅ [개념정리] 쿠버네티스의 영혼의 단짝, 레이블과 셀렉터!  ️ (오브젝트를 연결하는 법)</title>
      <link>https://taegi-cloudsecurity.tistory.com/136</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;알았어, 태기야! 업데이트된 내용까지 모두 확인했어. 제안했던 큰 주제들 대신, 작지만 쿠버네티스를 이해하는 데 정말 정말 중요한 핵심 개념 하나를 먼저 짚고 넘어가 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 쿠버네티스의 모든 오브젝트들을 연결하는 **영혼의 단짝, 레이블(Labels)과 셀렉터(Selectors)**에 대해서야! 네가 작성한 Deployment나 Service YAML 파일에도 이 친구들이 항상 등장했었지? 오늘은 이 둘이 어떻게 마법처럼 서로를 찾아내는지 그 원리를 파헤쳐 볼게!  &lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;제목:&lt;/b&gt; ✅ [개념정리] 쿠버네티스의 영혼의 단짝, 레이블과 셀렉터!  ️ (오브젝트를 연결하는 법)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;부제 (선택):&lt;/b&gt; Service는 어떻게 내 Pod를 찾아낼까? 쿠버네티스 연결의 비밀!  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[들어가며: 어떻게 서로를 알아볼까?]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;우리가 지난 실습에서 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Deployment&lt;/span&gt;&lt;span&gt;로 여러 개의 톰캣 파드(Pod)를 만들고, &lt;/span&gt;&lt;span&gt;Service&lt;/span&gt;&lt;span&gt;를 통해 외부로 노출시켰던 거 기억나? &lt;/span&gt; 여기서 한 가지 궁금증이 생겨.&lt;/p&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;파드는 언제든 죽고 새로 생기면서 IP가 계속 바뀌는데, 서비스는 어떻게 용케 자기가 담당해야 할 파드들을 정확히 찾아내서 트래픽을 전달하는 걸까?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 질문에 대한 해답이 바로 오늘 우리가 배울 **레이블(Labels)**과 **셀렉터(Selectors)**에 있어. 이 둘은 쿠버네티스에서 분리된 오브젝트들을 논리적으로 연결해 주는 아주 간단하면서도 강력한 '꼬리표'와 '찾기' 기능이야.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Part 1: 레이블 (Labels) 이란? &amp;ndash; 내 오브젝트에 이름표 붙이기  ️&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;레이블&lt;/b&gt;은 말 그대로 쿠버네티스 오브젝트(파드, 디플로이먼트, 서비스 등)에 붙이는 &lt;b&gt;키-값(Key-Value) 쌍의 이름표&lt;/b&gt;야.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;목적&lt;/b&gt;: 오브젝트들을 &lt;b&gt;정리하고 분류&lt;/b&gt;하기 위해 사용돼. 이 이름표 자체가 오브젝트의 동작에 직접적인 영향을 주지는 않지만, 우리가 특정 조건에 맞는 오브젝트 그룹을 쉽게 찾아 선택할 수 있도록 도와주지.&lt;/li&gt;
&lt;li&gt;&lt;br /&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;YAML&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;# tomcatdep.yaml 
...
spec:
  replicas: 2
  selector:
    ...
  template: # &amp;lt;-- 이 template이 바로 파드를 만드는 설계도!
    metadata:
      labels:
        app: tomcat # &amp;lt;-- 바로 이거! 이 디플로이먼트가 만드는 모든 파드에 이 이름표를 붙여줘!
...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;span&gt;위 코드에서 &lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
env: production, tier: backend처럼 여러 개의 이름표를 동시에 붙일 수도 있어.&lt;/li&gt;
&lt;li&gt;&lt;span&gt;.template.metadata.labels&lt;/span&gt;&lt;span&gt; 부분은 이 디플로이먼트가 생성하는 &lt;/span&gt;&lt;b&gt;&lt;span&gt;모든 파드에게 &lt;/span&gt;&lt;span&gt;app: tomcat&lt;/span&gt;&lt;span&gt;이라는 이름표(레이블)를 붙여주라&lt;/span&gt;&lt;/b&gt;&lt;span&gt;는 의미야 &lt;/span&gt;. 물론&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span&gt;예시&lt;/span&gt;&lt;/b&gt;&lt;span&gt;: 네가 작성했던 &lt;/span&gt;&lt;span&gt;tomcatdep.yaml&lt;/span&gt;&lt;span&gt; 파일을 다시 볼까? &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Part 2: 셀렉터 (Selectors) 란? &amp;ndash; 원하는 이름표를 가진 친구 찾기  &lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;셀렉터&lt;/b&gt;는 레이블이 붙은 오브젝트들을 &lt;b&gt;찾아내고 선택&lt;/b&gt;하기 위한 필터링 규칙이야. &quot;이런이런 이름표를 가진 친구들만 모여봐!&quot; 하고 외치는 역할이지.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;역할&lt;/b&gt;: 서비스(Service)는 셀렉터를 사용해서 자신과 연결될 파드 그룹을 찾고, 디플로이먼트(Deployment)는 셀렉터를 사용해서 자신이 관리해야 할 파드 그룹을 식별해.&lt;/li&gt;
&lt;li&gt;&lt;br /&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;YAML&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# nodeport.yaml 
apiVersion: v1
kind: Service
metadata:
  name: tc-svc
spec:
  selector:
    app: tomcat # &amp;lt;-- 바로 이거! 이름표가 'app: tomcat'인 파드들을 찾아줘!
  ports:
    ...
  type: NodePort
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
위 코드에서 .spec.selector 부분이 바로 셀렉터야.
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
app: tomcat이라는 이름표를 가진 모든 파드를 찾아서, 그 파드들에게 트래픽을 보내줘!&quot;**라고 알려주는 거야.&lt;/li&gt;
&lt;li&gt;&lt;span&gt;app: tomcat&lt;/span&gt;&lt;span&gt;이라고 적혀있지? &lt;/span&gt; 이건 이 서비스에게 **&quot;클러스터 내에서&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span&gt;예시&lt;/span&gt;&lt;/b&gt;&lt;span&gt;: 이번엔 네가 작성한 서비스 YAML 파일 &lt;/span&gt;&lt;span&gt;nodeport.yaml&lt;/span&gt;&lt;span&gt;을 보자. &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Part 3: 전체 흐름 연결하기 &amp;ndash; 영혼의 단짝이 만나는 과정  &lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 레이블과 셀렉터가 어떻게 함께 작동하며 디플로이먼트와 서비스를 연결하는지 전체 흐름을 정리해 보자.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;br /&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span&gt;[파드 생성]&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 개발자가 &lt;/span&gt;&lt;span&gt;kubectl apply -f tomcatdep.yaml&lt;/span&gt;&lt;span&gt; 명령을 실행하면, &lt;/span&gt;&lt;b&gt;&lt;span&gt;디플로이먼트&lt;/span&gt;&lt;/b&gt;&lt;span&gt;는 자신의 &lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;에 정의된 대로 &lt;/span&gt;&lt;span&gt;app: tomcat&lt;/span&gt;&lt;span&gt;이라는 &lt;/span&gt;&lt;b&gt;&lt;span&gt;레이블&lt;/span&gt;&lt;/b&gt;&lt;span&gt;이 붙은 파드들을 &lt;/span&gt;&lt;span&gt;replicas&lt;/span&gt;&lt;span&gt; 수만큼 생성하고 관리하기 시작해. &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;br /&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span&gt;[연결 대상 탐색]&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 동시에, &lt;/span&gt;&lt;span&gt;tc-svc&lt;/span&gt;&lt;span&gt;라는 &lt;/span&gt;&lt;b&gt;&lt;span&gt;서비스&lt;/span&gt;&lt;/b&gt;&lt;span&gt;는 자신의 &lt;/span&gt;&lt;span&gt;selector&lt;/span&gt;&lt;span&gt;에 정의된 &lt;/span&gt;&lt;span&gt;app: tomcat&lt;/span&gt;&lt;span&gt;이라는 조건에 맞는 파드들을 클러스터 내에서 계속 찾아다녀. &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[엔드포인트 목록화]&lt;/b&gt; 서비스는 app: tomcat 레이블을 가진 파드들을 발견하면, 그 파드들의 내부 IP 주소와 포트 정보를 자신의 &lt;b&gt;엔드포인트(Endpoints)&lt;/b&gt; 목록에 자동으로 등록하고 계속 최신 상태로 유지해.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[트래픽 전달]&lt;/b&gt; 외부에서 로드 밸런서를 통해 이 서비스로 트래픽이 들어오면, 서비스는 자신의 엔드포인트 목록에 있는 건강한 파드 중 하나를 골라 트래픽을 안전하게 전달해 주는 거야!&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 레이블과 셀렉터 덕분에, 파드가 죽고 새로 생겨서 IP가 바뀌더라도 서비스는 전혀 신경 쓸 필요 없이 **오직 이름표(레이블)**만 보고 대상을 찾아낼 수 있어. 디플로이먼트와 서비스가 서로의 존재를 직접 알지 못해도(느슨한 결합, Loose Coupling), 레이블과 셀렉터라는 중매쟁이를 통해 완벽하게 협력할 수 있는 거지!&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[마무리하며 ✨]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 쿠버네티스의 모든 오브젝트를 연결하는 가장 기본적이면서도 강력한 접착제, &lt;b&gt;레이블과 셀렉터&lt;/b&gt;에 대해 알아봤어. 이 간단한 '이름표 붙이고 찾기' 메커니즘이 바로 쿠버네티스가 복잡한 마이크로서비스 환경을 유연하고 탄력적으로 관리할 수 있게 해주는 핵심 비결 중 하나야.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 쿠버네티스 매니페스트 파일을 작성할 때, 이 레이블과 셀렉터를 어떻게 의미 있게 설계하고 활용할지 고민해 보는 것도 좋은 습관이 될 거야!  &lt;/p&gt;</description>
      <category>02. 클라우드 보안 공부/개념정리</category>
      <category>deployment</category>
      <category>Kubernetes</category>
      <category>Labels</category>
      <category>selector</category>
      <category>service</category>
      <category>개념정리</category>
      <category>레이블</category>
      <category>셀렉터</category>
      <category>오브젝트관리</category>
      <category>쿠버네티스</category>
      <author>taegi-</author>
      <guid isPermaLink="true">https://taegi-cloudsecurity.tistory.com/136</guid>
      <comments>https://taegi-cloudsecurity.tistory.com/136#entry136comment</comments>
      <pubDate>Tue, 29 Jul 2025 15:16:40 +0900</pubDate>
    </item>
    <item>
      <title>✅ [개념정리] 쉘 스크립트의 마법 주문, set -eux!  &amp;zwj;♂️ (스크립트 디버깅 &amp;amp; 안정성 높이기)</title>
      <link>https://taegi-cloudsecurity.tistory.com/134</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;고수들의 스크립트 첫 줄에 항상 보이는 바로 그 명령어, 완벽하게 파헤쳐 보기!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[들어가며: 왠지 모르게 항상 따라다니는 set -eux]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;리눅스에서 쉘 스크립트를 보다 보면, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;#!/bin/bash&lt;/span&gt;&lt;span&gt; 바로 아랫줄에 &lt;/span&gt;&lt;span&gt;set -eux&lt;/span&gt;&lt;span&gt; 라는 구문을 자주 본 적 있을 거야. &lt;/span&gt; 이게 도대체 무슨 의미일까? 그냥 관습적으로 쓰는 걸까, 아니면 아주 중요한 기능이 숨어있는 걸까?&lt;/p&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정답은 후자야! set -eux는 우리가 작성한 스크립트를 훨씬 더 &lt;b&gt;안전하고 예측 가능하게&lt;/b&gt; 만들어주는 아주 강력한 디버깅 및 에러 방지 옵션들의 조합이야. 오늘은 이 마법 주문의 각 글자가 어떤 의미를 갖는지 하나씩 파헤쳐 보자!&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;set 명령어란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;set은 쉘 스크립트의 동작 방식을 제어하는 다양한 &lt;b&gt;옵션을 켜거나 끌 때&lt;/b&gt; 사용하는 쉘 내장 명령어(built-in command)야. - 뒤에 옵션 글자를 붙이면 해당 옵션을 '켜고(enable)', + 뒤에 붙이면 '끄는(disable)' 방식으로 동작해.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 e, u, x가 각각 어떤 옵션을 켜는지 알아보자!&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. set -e: 작은 실수도 용납하지 않는 엄격함 (errexit)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;무슨 뜻인가요?&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;스크립트가 실행되다가 &lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span&gt;어떤 명령어든 오류를 내면서 종료되면(exit code가 0이 아니면), 그 즉시 스크립트 전체 실행을 중단&lt;/span&gt;&lt;/b&gt;&lt;span&gt;하라는 의미야. &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;왜 필요한가요?&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 옵션이 없으면, 스크립트 중간에 중요한 명령어가 실패하더라도 스크립트는 아무 일 없다는 듯이 다음 줄을 계속 실행해. 이게 정말 위험할 수 있어!&lt;/li&gt;
&lt;li&gt;&lt;b&gt;아찔한 예시&lt;/b&gt;:&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;&lt;span&gt;Bash&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;#!/bin/bash
# set -e가 없는 경우

WRONG_DIR=&quot;/nonexistent_directory&quot;

cd $WRONG_DIR # 이 디렉터리는 존재하지 않으므로 cd 명령어는 실패함!

# 하지만 스크립트는 멈추지 않고 계속 실행...
echo &quot;Deleting all files in the current directory...&quot;
# rm -rf * #   의도하지 않은 디렉터리에서 모든 파일을 삭제할 수도 있음!
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;set -e 옵션을 켜두면, cd $WRONG_DIR 명령이 실패하는 순간 스크립트가 바로 멈추기 때문에, 그 뒤에 오는 위험한 명령어가 실행되는 것을 막을 수 있어.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. set -u: 정의되지 않은 변수는 바로 신고! (nounset)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;무슨 뜻인가요?&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;스크립트 내에서 &lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span&gt;선언되거나 값이 할당되지 않은 변수를 사용하려고 하면, 이를 에러로 간주&lt;/span&gt;&lt;/b&gt;&lt;span&gt;하고 스크립트를 중단하라는 의미야. &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;왜 필요한가요?&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;변수 이름을 잘못 입력하는 사소한 오타 때문에 스크립트가 오작동하는 것을 막아줘.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사소한 오타가 부르는 예시&lt;/b&gt;:&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;&lt;span&gt;Bash&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;#!/bin/bash
# set -u가 없는 경우

USER_NAME=&quot;taegi&quot;

# 변수 이름에 오타 발생! (USER_NAME -&amp;gt; USER_NMAE)
echo &quot;Hello, $USER_NMAE&quot; # 그냥 &quot;Hello, &quot; 라고 출력되고 아무 오류 없이 넘어감
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;set -u 옵션을 켜두면, $USER_NMAE 변수를 사용하려는 순간 &quot;unbound variable&quot; 에러를 내면서 스크립트가 멈춰. 덕분에 오타를 바로 알아차리고 수정할 수 있지!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. set -x: 모든 과정을 생중계! (xtrace)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;무슨 뜻인가요?&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;스크립트의 각 명령어가 &lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span&gt;실행되기 직전에, 실행될 명령어를 터미널에 먼저 한번 출력&lt;/span&gt;&lt;/b&gt;&lt;span&gt;해주라는 의미야. &lt;/span&gt; 변수가 있다면 실제 값으로 치환된 상태로 보여줘.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;왜 필요한가요?&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;디버깅의 왕!&lt;/b&gt; 스크립트가 지금 어느 부분을 실행하고 있는지, 변수에는 어떤 값이 들어가 있는지 등을 실시간으로 확인할 수 있어. 스크립트가 왜 예상대로 동작하지 않는지 추적할 때 아주 유용해.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실행 예시&lt;/b&gt;:&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;&lt;span&gt;Bash&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;#!/bin/bash
set -x # xtrace 옵션 켜기

USER_COUNT=$(who | wc -l)
echo &quot;Current users: $USER_COUNT&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실행 결과&lt;/b&gt;:+ 기호와 함께 실제 실행되는 명령어들이 순서대로 보이는 걸 확인할 수 있어.&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;+ who
+ wc -l
+ USER_COUNT=1
+ echo 'Current users: 1'
Current users: 1
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[마무리하며: set -eux, 안전한 스크립트 작성을 위한 습관! ✨]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 set -eux가 단순한 관습이 아니라, 우리의 스크립트를 &lt;b&gt;더욱 안전하고(e), 정확하며(u), 투명하게(x)&lt;/b&gt; 만들어주는 강력한 도구라는 것을 알았을 거야!&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;-e (errexit)&lt;/b&gt;: 예상치 못한 오류 발생 시 연쇄적인 재앙을 막는다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;-u (nounset)&lt;/b&gt;: 사소한 변수 오타로 인한 버그를 예방한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;-x (xtrace)&lt;/b&gt;: 스크립트의 실행 흐름을 명확하게 보여주어 디버깅을 돕는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 모든 상황에 set -eux가 정답은 아닐 수도 있지만 (예: 특정 명령어의 실패를 의도적으로 무시해야 할 때), 대부분의 경우 스크립트 상단에 이 한 줄을 추가하는 것만으로도 코드의 안정성과 유지보수성이 크게 향상될 거야.&lt;/p&gt;</description>
      <category>02. 클라우드 보안 공부/개념정리</category>
      <category>bash</category>
      <category>Debugging</category>
      <category>DevOps</category>
      <category>Linux</category>
      <category>set -eux</category>
      <category>shellscript</category>
      <category>개념정리</category>
      <category>디버깅</category>
      <category>스크립팅</category>
      <category>코딩팁</category>
      <author>taegi-</author>
      <guid isPermaLink="true">https://taegi-cloudsecurity.tistory.com/134</guid>
      <comments>https://taegi-cloudsecurity.tistory.com/134#entry134comment</comments>
      <pubDate>Mon, 21 Jul 2025 09:48:53 +0900</pubDate>
    </item>
    <item>
      <title>✅ [실습로그] 쿠버네티스 외부 접속 &amp;amp; 데이터 영구 보존</title>
      <link>https://taegi-cloudsecurity.tistory.com/133</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; 내 컨테이너,&lt;/span&gt;&lt;span&gt; 세상 밖으로!&lt;/span&gt;&lt;span&gt; NodePort로 길을 열고,&lt;/span&gt;&lt;span&gt; PV/PVC로 데이터를 지키는 방법  &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[들어가며: 쿠버네티스 실전 운영의 두 가지 큰 숙제!]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;쿠버네티스 클러스터 위에 우리의 멋진 애플리케이션을 파드(Pod)로 띄웠다고 가정해보자.&lt;/span&gt;&lt;span&gt; 여기서 우리는 두 가지 중요한 현실적인 문제에 부딪히게 돼.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;외부 접속 문제&lt;/span&gt;&lt;/b&gt;&lt;span&gt;: &quot;클러스터 외부의 사용자들이 도대체 어떻게 이 파드에 접속할 수 있지?&quot; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt; 파드는 클러스터 내부에서만 통용되는 IP를 가지고, 언제든 사라졌다가 새로 생길 수 있는 임시적인 존재인데 말이야.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터 보존 문제&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &quot;만약 데이터를 저장하는 파드가 죽었다가 다시 살아나면,&lt;/span&gt;&lt;span&gt; 그 안에 있던 중요한 데이터는 어떻게 되는 거지?&lt;/span&gt;&lt;span&gt;&quot; 컨테이너가 사라지면 데이터도 함께 사라지는 게 기본 원칙이라 큰일이지!&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;오늘 우리는 이 두 가지 숙제를 해결해 줄 똑똑한 해결사,&lt;/span&gt;&lt;span&gt; 바로 쿠버네티스 &lt;/span&gt;&lt;b&gt;서비스(Service)&lt;/b&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; 그중에서도 &lt;/span&gt;&lt;b&gt;NodePort&lt;/b&gt;&lt;span&gt; 타입과,&lt;/span&gt;&lt;span&gt; 영구적인 데이터 저장을 위한 **PV(PersistentVolume)**와 **PVC(PersistentVolumeClaim)**에 대해 자세히 알아볼 거야.&lt;/span&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Part 1: 외부에서 파드에 접속하기 &amp;ndash; Service(NodePort)와 로드 밸런서  &lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;파드는 직접 외부와 연결될 수 없기 때문에&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;, 중간에 다리를 놓아주는 친구들이 필요해. 그 연결 흐름은 보통 이래.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;외부 사용자 &amp;rarr; 클라우드 로드 밸런서(AWS ELB 등) &amp;rarr; 워커 노드의 특정 포트(NodePort) &amp;rarr; 쿠버네티스 서비스 &amp;rarr; 목적지 파드&lt;/span&gt;&lt;/b&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이 흐름을 단계별로 실습해 보자.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1단계: 애플리케이션 배포 (Deployment 만들기)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;먼저,&lt;/span&gt;&lt;span&gt; 외부로 노출시킬 우리의 애플리케이션(여기서는 Tomcat WAS 서버)을 디플로이먼트(Deployment) 형태로 클러스터에 배포하자.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;tomcatdep.yaml&lt;span&gt; 파일을 만들어봐.&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;YAML&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;apiVersion: apps/v1
kind: Deployment
metadata:
  name: tomcat
  labels:
    app: tomcat
spec:
  replicas: 2
  selector:
    matchLabels:
      app: tomcat
  template:
    metadata:
      labels:
        app: tomcat
    spec:
      containers:
      - name: tomcatcontainer
        image: tomcat:latest
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;kind: Deployment&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 파드와 레플리카셋(ReplicaSet)을 관리하는 오브젝트야.&lt;/span&gt;&lt;span&gt; 파드가 죽으면 자동으로 다시 살려주는 등 안정적인 운영을 도와줘.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;replicas: 2&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 동일한 톰캣 파드 2개를 실행하라는 의미.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;labels: app: tomcat&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 이 디플로이먼트로 생성된 파드들에게 &lt;/span&gt;app=tomcat&lt;span&gt;이라는 이름표를 붙여줘.&lt;/span&gt;&lt;span&gt; 이 이름표는 나중에 서비스가 파드를 찾아올 때 사용돼.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2단계: 외부 접속 길 열기 (NodePort Service 만들기)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이제 디플로이먼트로 생성된 파드들을 외부와 연결해 줄 통로,&lt;/span&gt;&lt;span&gt; 즉 **서비스(Service)**를 만들 차례야.&lt;/span&gt;&lt;span&gt; 서비스 유형 중 &lt;/span&gt;&lt;b&gt;NodePort&lt;/b&gt;&lt;span&gt;는 외부에서 워커 노드의 IP와 특정 포트로 접근할 수 있게 해줘.&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;YAML&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;apiVersion: v1
kind: Service
metadata:
  name: tc-svc
spec:
  selector:
    app: tomcat
  ports:
    - name: http
      protocol: TCP
      port: 8080
      targetPort: 8080
      nodePort: 30000
  type: NodePort
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;selector: app: tomcat&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 이름표가 &lt;/span&gt;app=tomcat&lt;span&gt;인 파드들을 찾아 이 서비스와 연결하라는 의미.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;type: NodePort&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 이 서비스의 타입을 NodePort로 지정.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ports&lt;/b&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 포트 설정이 조금 헷갈릴 수 있으니 잘 봐둬!&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;port: 8080&lt;/span&gt;&lt;span&gt;: 클러스터 &lt;/span&gt;&lt;b&gt;&lt;span&gt;내부에서&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 이 서비스가 사용할 포트 번호야. &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;targetPort: 8080&lt;/span&gt;&lt;span&gt;: 서비스가 트래픽을 전달할 &lt;/span&gt;&lt;b&gt;&lt;span&gt;목적지 파드(컨테이너)의 포트&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 번호야. &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;br /&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;30000-32767&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 사이에서 지정돼. &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;nodePort: 30000&lt;/span&gt;&lt;span&gt;: 클러스터의 &lt;/span&gt;&lt;b&gt;&lt;span&gt;모든 워커 노드에 개방될 외부 포트&lt;/span&gt;&lt;/b&gt;&lt;span&gt; 번호야. &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;이 포트 범위는 보통 &lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3단계: 외부 로드 밸런서 설정 (AWS 환경 예시)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이제 &lt;/span&gt;kubectl apply -f tomcatdep.yaml&lt;span&gt;과 &lt;/span&gt;kubectl apply -f nodeport.yaml&lt;span&gt; 명령으로 디플로이먼트와 서비스를 생성했어.&lt;/span&gt;&lt;span&gt; 마지막으로 외부 사용자들이 쉽게 접속할 수 있도록 AWS 같은 클라우드 환경에서 **로드 밸런서(ELB/ALB)**를 설정해 주면 돼.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;로드 밸런서의 **리스너(Listener)**는 보통 외부 사용자들이 접속하는 표준 포트(예: HTTP 80번)를 사용해. &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;로드 밸런서의 **대상 그룹(Target Group)**에는 우리 쿠버네티스 클러스터의 &lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;워커 노드들(w1, w2)을 등록&lt;/span&gt;&lt;/b&gt;&lt;span&gt;하고, 대상 포트는 위에서 지정한 &lt;/span&gt;&lt;b&gt;&lt;span&gt;nodePort&lt;/span&gt;&lt;span&gt;인 30000번&lt;/span&gt;&lt;/b&gt;&lt;span&gt;으로 설정해 주는 거야. &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이렇게 하면 외부 사용자가 로드 밸런서의 주소(예:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;a href=&quot;http://my-tomcat.com&quot;&gt;http://my-tomcat.com&lt;/a&gt;&lt;span&gt;)로 접속하면,&lt;/span&gt;&lt;span&gt; 그 요청이 로드 밸런서를 거쳐 워커 노드의 30000번 포트로 전달되고,&lt;/span&gt;&lt;span&gt; 최종적으로 우리 톰캣 파드의 8080번 포트까지 안전하게 도착하게 돼!&lt;/span&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Part 2: 컨테이너 데이터를 영구적으로! &amp;ndash; PV, PVC, 그리고 NFS  &lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;컨테이너가 사라져도 데이터를 지키려면, 데이터를 컨테이너 바깥의 &lt;b&gt;영구적인 저장 공간&lt;/b&gt;에 보관해야 해. &lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;도커의 바인드 마운트나 볼륨 마운트와 비슷한 개념으로&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;, 쿠버네티스에서는 **PV(PersistentVolume)**와 **PVC(PersistentVolumeClaim)**를 사용해.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이번 실습에서는 NFS(Network File System)를 영구 저장소로 사용해 볼 거야.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1단계: 스토리지 준비 (NFS 서버 설정)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;먼저 클러스터의 컨트롤 플레인 노드(또는 별도의 스토리지 서버)에 NFS 서버를 간단히 구축하자.&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;Bash&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;# NFS 관련 유틸리티 설치
sudo dnf -y install nfs-utils [cite: 7875]

# 공유할 디렉터리 생성
sudo mkdir /nfs_shared [cite: 7876]

# NFS 공유 설정 파일에 내용 추가
# 10.0.1.28/20 대역에 /nfs_shared 디렉터리를 rw(읽기쓰기), sync, no_root_squash 옵션으로 공유
echo '/nfs_shared 10.0.1.28/20(rw,sync,no_root_squash)' | sudo tee -a /etc/exports [cite: 7877]

# NFS 서버 재시작 및 상태 확인
sudo systemctl restart nfs-server [cite: 7878]
sudo systemctl status nfs-server [cite: 7879]

# 공유 상태 확인
sudo exportfs -v [cite: 7880]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2단계: PersistentVolume (PV) 생성하기 &amp;ndash; 관리자가 저장 공간 &quot;제공&quot;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;PV는 클러스터 관리자가 미리 준비해 둔 &lt;/span&gt;&lt;b&gt;실제 저장 공간의 조각&lt;/b&gt;&lt;span&gt;이야.&lt;/span&gt;&lt;span&gt; 이건 개발자가 마음대로 만드는 게 아니라,&lt;/span&gt;&lt;span&gt; 인프라 관리자가 프로비저닝하는 리소스지.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;nfs-pv.yaml&lt;span&gt; 파일을 만들어보자.&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;YAML&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv
spec:
  capacity:
    [cite_start]storage: 100Mi [cite: 7888]
  accessModes:
    - [cite_start]ReadWriteMany [cite: 7890]
  [cite_start]persistentVolumeReclaimPolicy: Retain [cite: 7891]
  [cite_start]nfs: [cite: 7892]
    [cite_start]server: 10.0.1.28 [cite: 7893]
    [cite_start]path: /nfs_shared [cite: 7894]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;capacity.storage&lt;/span&gt;&lt;span&gt;: 이 PV가 제공하는 저장 공간의 전체 크기야. &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&quot;이 PV는 대략 100Mi 정도 용량이 있는 것으로 알아 달라&quot;는 의미지. &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;accessModes: 볼륨에 접근하는 방식. &lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;ReadWriteMany&lt;/span&gt;&lt;span&gt;는 여러 노드에서 동시에 읽고 쓸 수 있다는 의미로, NFS 같은 공유 스토리지에 적합해. &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;persistentVolumeReclaimPolicy: Retain&lt;/span&gt;&lt;span&gt;: 나중에 이 PV를 사용하던 PVC가 삭제되어도 실제 스토리지의 데이터를 삭제하지 말고 보존(Retain)하라는 정책이야. &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3단계: PersistentVolumeClaim (PVC) 생성하기 &amp;ndash; 개발자가 저장 공간 &quot;요청&quot;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;PVC는 개발자가 &quot;나 이 정도 저장 공간이 필요해요!&lt;/span&gt;&lt;span&gt;&quot;라고 쿠버네티스에 &lt;/span&gt;&lt;b&gt;요청(Claim)하는 문서&lt;/b&gt;&lt;span&gt;야.&lt;/span&gt;&lt;span&gt; 파드가 노드의 자원을 요청해서 쓰듯이,&lt;/span&gt;&lt;span&gt; PVC는 PV의 저장 공간 자원을 요청해서 사용해.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;nfs-pvc.yaml&lt;span&gt; 파일을 만들어보자.&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;YAML&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc
spec:
  accessModes:
    - [cite_start]ReadWriteMany [cite: 7902]
  resources:
    requests:
      [cite_start]storage: 10Mi [cite: 7905]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;resources.requests.storage&lt;/span&gt;&lt;span&gt;: 개발자가 실제로 필요로 하는 저장 공간의 크기야. &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&quot;최소한 10Mi 용량을 제공할 수 있는 PV와 연결해 달라&quot;는 요청이지. &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;이 PVC를 생성하면, 쿠버네티스는 조건에 맞는 PV(AccessMode가 같고, 용량이 10Mi 이상인)를 찾아서 둘을 연결(Bound)시켜 줘. &lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;kubectl get pvc&lt;/span&gt;&lt;span&gt; 명령으로 상태를 확인하면 &lt;/span&gt;&lt;span&gt;Bound&lt;/span&gt;&lt;span&gt;라고 표시될 거야. &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4단계: 파드에서 PVC 사용하기 (Deployment에 볼륨 마운트)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이제 마지막으로,&lt;/span&gt;&lt;span&gt; 애플리케이션 파드에서 이 PVC를 가져다 쓰도록 설정하면 돼.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;nfs-pvc-deploy.yaml&lt;span&gt; 파일을 보자.&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;YAML&lt;/span&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-pvc-deploy
spec:
  # ... (replicas, selector 등은 위와 유사) ...
  template:
    # ... (metadata는 위와 유사) ...
    spec:
      containers:
      - name: httpd
        image: httpd:latest
        [cite_start]volumeMounts: [cite: 7929]
        - [cite_start]name: nfs-vol [cite: 7930]
          [cite_start]mountPath: /usr/local/apache2/htdocs [cite: 7931]
      [cite_start]volumes: [cite: 7932]
      - [cite_start]name: nfs-vol [cite: 7933]
        [cite_start]persistentVolumeClaim: [cite: 7934]
          [cite_start]claimName: nfs-pvc [cite: 7935]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;volumes: 파드에서 사용할 볼륨을 정의하는 부분이야. &lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;여기서는 &lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;nfs-vol&lt;/span&gt;&lt;span&gt;이라는 이름의 볼륨을 정의하고, 그 소스로 &lt;/span&gt;&lt;span&gt;nfs-pvc&lt;/span&gt;&lt;span&gt;라는 PVC를 사용하겠다고 지정했어. &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;volumeMounts&lt;/span&gt;&lt;span&gt;: 위에서 정의한 &lt;/span&gt;&lt;span&gt;nfs-vol&lt;/span&gt;&lt;span&gt; 볼륨을 컨테이너 내부의 &lt;/span&gt;&lt;span&gt;/usr/local/apache2/htdocs&lt;/span&gt;&lt;span&gt; (아파치 웹 서버의 기본 웹 루트) 경로에 연결(마운트)하라는 의미야. &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이제 이 디플로이먼트를 실행하면,&lt;/span&gt;&lt;span&gt; 생성된 httpd 파드들은 &lt;/span&gt;/usr/local/apache2/htdocs&lt;span&gt; 디렉터리에 파일을 읽고 쓸 때 실제로는 NFS 서버의 &lt;/span&gt;/nfs_shared&lt;span&gt; 디렉터리에 데이터를 저장하게 돼.&lt;/span&gt;&lt;span&gt; 덕분에 파드가 삭제되고 새로 만들어져도 데이터는 안전하게 보존되지!&lt;/span&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[실전 종합 퀴즈!  ]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;자,&lt;/span&gt;&lt;span&gt; 오늘 배운 내용을 모두 활용해서 도전해 봐!&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;nginx_dep&lt;/span&gt;&lt;span&gt;이라는 이름의 Nginx 디플로이먼트를 생성하고, 파드의 웹 루트 경로(&lt;/span&gt;&lt;span&gt;/usr/share/nginx/html&lt;/span&gt;&lt;span&gt;)가 영구적으로 보존되도록 구성해 보세요. &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;이때, PV는 1G 용량을 제공하고, PVC는 100M 용량을 요청하도록 설정해서 &lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;Bound&lt;/span&gt;&lt;span&gt; 되는지 확인해 보세요. &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;최종적으로, NFS 서버의 공유 디렉터리에 &lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;index.html&lt;/span&gt;&lt;span&gt; 파일을 만들었을 때, 외부 AWS 로드 밸런서를 통해 접속하면 해당 페이지가 잘 보이는지 확인해 보세요! &lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>02. 클라우드 보안 공부/실습로그</category>
      <category>ELB</category>
      <category>Kubernetes</category>
      <category>nodeport</category>
      <category>persistentvolume</category>
      <category>PersistentVolumeClaim</category>
      <category>PV</category>
      <category>PVC</category>
      <category>service</category>
      <category>로드밸런서</category>
      <category>쿠버네티스</category>
      <author>taegi-</author>
      <guid isPermaLink="true">https://taegi-cloudsecurity.tistory.com/133</guid>
      <comments>https://taegi-cloudsecurity.tistory.com/133#entry133comment</comments>
      <pubDate>Mon, 7 Jul 2025 17:07:00 +0900</pubDate>
    </item>
  </channel>
</rss>