<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Aiden Lee.log</title>
        <link>https://velog.io/</link>
        <description>파인애플 좋아하세요?</description>
        <lastBuildDate>Fri, 07 Aug 2026 07:46:56 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. Aiden Lee.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/aiden_lee" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Web Security] HTTP와 HTTPS의 동작 원리 및 보안 메커니즘]]></title>
            <link>https://velog.io/@aiden_lee/Web-Security-HTTP%EC%99%80-HTTPS%EC%9D%98-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC-%EB%B0%8F-%EB%B3%B4%EC%95%88-%EB%A9%94%EC%BB%A4%EB%8B%88%EC%A6%98</link>
            <guid>https://velog.io/@aiden_lee/Web-Security-HTTP%EC%99%80-HTTPS%EC%9D%98-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC-%EB%B0%8F-%EB%B3%B4%EC%95%88-%EB%A9%94%EC%BB%A4%EB%8B%88%EC%A6%98</guid>
            <pubDate>Fri, 07 Aug 2026 07:46:56 GMT</pubDate>
            <description><![CDATA[<h2 id="1-http-hypertext-transfer-protocol-란">1. HTTP (HyperText Transfer Protocol) 란?</h2>
<p>HTTP는 인터넷상에서 클라이언트(브라우저)와 서버가 <strong>문서(HTML), 이미지, 데이터 등을 주고받기 위해 사용하는 표준 통신 규약</strong>이다.</p>
<h3 id="⚠️-http의-치명적인-한계-평문plaintext-통신">⚠️ HTTP의 치명적인 한계: &quot;평문(Plaintext) 통신&quot;</h3>
<p>HTTP는 데이터를 <strong>아무런 암호화 없이 평문 그대로 전송</strong>한다.</p>
<pre><code class="language-text">[클라이언트]  --- &quot;ID: admin / PW: 1234&quot; (평문) ---&gt;  [서버]
                      ▲
                [네트워크 패킷 스니핑 공격자]
                (중간에서 데이터 그대로 열람 가능)
</code></pre>
<ul>
<li><strong>도청(Sniffing) 위험:</strong> Wireshark 같은 패킷 분석 도구를 사용하면 네트워크 중간에서 로그인 정보, 개인정보, 쿠키/세션 ID 등을 쉽게 탈취할 수 있음.</li>
<li><strong>변조(Tampering) 위험:</strong> 클라이언트와 서버 사이에서 악의적인 공격자가 데이터 내용을 임의로 수정하여 전달할 수 있음 (Man-in-the-Middle, MITM 공격).</li>
<li><strong>위장(Spoofing) 위험:</strong> 접속하려는 서버가 진짜 서버인지 검증할 수 없어 피싱 사이트에 노출되기 쉬움.</li>
</ul>
<hr>
<h2 id="2-https-http-secure-란">2. HTTPS (HTTP Secure) 란?</h2>
<p>HTTPS는 기존 HTTP 프로토콜에 <strong>SSL/TLS(Transport Layer Security) 암호화 프로토콜을 얹어 데이터를 보호하는 보안 통신 규약</strong>이다. 기본 포트로 HTTP는 <code>80</code>, HTTPS는 <code>443</code>을 사용한다.</p>
<pre><code class="language-text">[클라이언트]  --- &quot;a8f!9#z1$kL...&quot; (암호화) ---&gt;  [서버]
                      ▲
                [공격자 (내용 해독 불가)]
</code></pre>
<h3 id="🛡️-https가-제공하는-3가지-핵심-보안-가치">🛡️ HTTPS가 제공하는 3가지 핵심 보안 가치</h3>
<ol>
<li><strong>기밀성 (Confidentiality):</strong> 모든 데이터를 암호화하여 제3자가 도청해도 내용을 알 수 없음.</li>
<li><strong>무결성 (Integrity):</strong> 전송 중 데이터가 위변조되었는지 검증 가능함.</li>
<li><strong>인증 (Authentication):</strong> 전자 서명된 CA 인증서를 통해 접속한 서버가 진짜 신뢰할 수 있는 서버인지 확인.</li>
</ol>
<hr>
<h2 id="3-https의-핵심-암호화-방식-대칭키--공개키">3. HTTPS의 핵심 암호화 방식 (대칭키 + 공개키)</h2>
<p>HTTPS는 효율적인 암호화를 위해 <strong>대칭키 암호화</strong>와 <strong>공개키(비대칭키) 암호화</strong> 방식을 혼합하여 사용한다.</p>
<table>
<thead>
<tr>
<th>구분</th>
<th>대칭키(Symmetric Key) 암호화</th>
<th>공개키(Public Key / Asymmetric) 암호화</th>
</tr>
</thead>
<tbody><tr>
<td><strong>원리</strong></td>
<td>암호화와 복호화에 <strong>동일한 키</strong> 사용</td>
<td><strong>공개키</strong>(암호화용)와 <strong>개인키</strong>(복호화용) 한 쌍 사용</td>
</tr>
<tr>
<td><strong>장점</strong></td>
<td>연산 속도가 매우 빠름</td>
<td>키 전달 시 유출 위험이 없음 (안전한 키 교환)</td>
</tr>
<tr>
<td><strong>단점</strong></td>
<td>키를 상대방에게 전달할 때 유출 위험 존재</td>
<td>연산 속도가 느려 대용량 데이터 전송에 부적합</td>
</tr>
</tbody></table>
<blockquote>
<p>💡 <strong>HTTPS의 하이브리드 메커니즘:</strong>
속도가 느린 <strong>공개키 암호화</strong>는 최초 접속 시 <strong>&#39;데이터를 암호화할 대칭키(세션키)&#39;를 안전하게 공유하는 용도</strong>로만 사용하고, 실제로 데이터를 주고받을 때는 속도가 빠른 <strong>대칭키 암호화</strong>를 이용함.</p>
</blockquote>
<hr>
<h2 id="4-ssltls-핸드셰이크-handshake-동작-흐름">4. SSL/TLS 핸드셰이크 (Handshake) 동작 흐름</h2>
<p>클라이언트와 서버가 HTTPS 통신을 시작하기 전, 안전하게 암호화 키를 교환하고 인증서를 검증하는 과정을 <strong>Handshake</strong>라고 함.</p>
<pre><code class="language-text">[Client]                                              [Server]
   |                                                      |
   | -------- (1) Client Hello (지원 cipher suite) -----&gt; |
   | &lt;------- (2) Server Hello + CA 인증서 -------------- |
   |                                                      |
   | [3. 인증서 검증 (CA 공개키로 서명 확인)]             |
   | [4. Pre-Master Secret 생성 및 서버 공개키로 암호화]  |
   |                                                      |
   | -------- (5) 암호화된 Pre-Master Secret 전송 ------&gt; |
   |                                                      |
   | [6. 양쪽 모두 세션키(대칭키) 생성 완료]              |
   |                                                      |
   | &lt;====== (7) 대칭키 기반의 안전한 암호화 통신 ======&gt; |
</code></pre>
<ol>
<li><strong>Client Hello:</strong> 클라이언트가 서버에게 지원 가능한 암호화 방식(Cipher Suite)과 난수 값을 전달함.</li>
<li><strong>Server Hello &amp; Certificate:</strong> 서버가 사용할 암호화 방식을 선택하고, 발급받은 <strong>SSL/TLS CA 인증서</strong>를 클라이언트에 전달함.</li>
<li><strong>인증서 검증:</strong> 클라이언트는 브라우저에 미리 내장된 <strong>CA(인증 기관)의 공개키</strong>로 서버 인증서의 서명을 검증함 (신뢰할 수 있는 서버인지 확인).</li>
<li><strong>Pre-Master Secret 전달:</strong> 클라이언트는 새로운 난수(Pre-Master Secret)를 생성한 뒤, <strong>인증서에서 추출한 서버의 공개키로 암호화</strong>하여 서버로 전송함.</li>
<li><strong>세션키 생성:</strong> 서버는 자신의 <strong>개인키</strong>로 이를 복호화함. 이제 클라이언트와 서버 양쪽 모두 동일한 대칭키(세션키)를 보유하게 됨.</li>
<li><strong>암호화 통신 개시:</strong> 이후 실제 데이터 요청/응답은 이 세션키(대칭키)를 이용해 빠르게 암호화/복호화하여 주고받음.</li>
</ol>
<hr>
<h2 id="💡-개발자보안-관점">💡 개발자/보안 관점</h2>
<ul>
<li><strong>혼합 콘텐츠 (Mixed Content) 주의:</strong> HTTPS 페이지 내에서 HTTP로 이미지나 API 요청(AJAX)을 보낼 경우 브라우저가 이를 블로킹함. 전 리소스 HTTPS 전환 필수.</li>
<li><strong>HSTS (HTTP Strict Transport Security):</strong> 최초 접근 시 HTTP 접속 시도를 서버 측에서 강제로 HTTPS로만 리다이렉트 및 고정하도록 브라우저에 지시하는 보안 헤더 설정.</li>
<li><strong>쿠키 보안 플래그 연동:</strong> HTTPS 환경에서는 인증 관련 쿠키에 반드시 <code>Secure</code> 및 <code>HttpOnly</code> 플래그를 설정하여 네트워크 스니핑 및 XSS로 인한 탈취 방지.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Web Security] 로그인 인증의 작동 원리

]]></title>
            <link>https://velog.io/@aiden_lee/Web-Security-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%9D%B8%EC%A6%9D%EC%9D%98-%EC%9E%91%EB%8F%99-%EC%9B%90%EB%A6%AC</link>
            <guid>https://velog.io/@aiden_lee/Web-Security-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%9D%B8%EC%A6%9D%EC%9D%98-%EC%9E%91%EB%8F%99-%EC%9B%90%EB%A6%AC</guid>
            <pubDate>Fri, 07 Aug 2026 01:42:48 GMT</pubDate>
            <description><![CDATA[<h2 id="1-쿠키cookie와-세션session의-등장-배경">1. 쿠키(Cookie)와 세션(Session)의 등장 배경</h2>
<p>HTTP 프로토콜은 두 가지 핵심 특성을 가진다.</p>
<ul>
<li><strong>Stateless (무상태성):</strong> 서버는 클라이언트의 이전 상태를 기억하지 않음. 각 요청은 완전히 독립적임.</li>
<li><strong>Connectionless (비연결성):</strong> 클라이언트가 요청을 보내고 서버가 응답을 마치면 연결을 끊음.</li>
</ul>
<p>이로 인해 &quot;로그인한 사용자가 페이지를 이동할 때 로그인 상태를 어떻게 유지할 것인가?&quot;라는 문제가 발생한다. 매번 아이디/비밀번호를 요청에 실어 보낼 수는 없으므로, 이를 해결하기 위해 쿠키(Cookie)와 <strong>세션(Session)</strong> 개념이 도입되었다.</p>
<hr>
<h2 id="2-쿠키-cookie">2. 쿠키 (Cookie)</h2>
<h3 id="🍪-개념-및-작동-방식">🍪 개념 및 작동 방식</h3>
<p>쿠키는 서버가 클라이언트(브라우저)에 저장하는 작은 데이터 조각(Key-Value 형태)이다.</p>
<pre><code class="language-text">[클라이언트]  ---&gt;  (1) 로그인 요청 (ID/PW)  ---&gt;  [서버]
[클라이언트]  &lt;---  (2) 응답 + Set-Cookie  &lt;---  [서버]
[클라이언트]  ---&gt;  (3) 요청 + Cookie Header ---&gt;  [서버] (상태 유지)
</code></pre>
<ol>
<li>클라이언트가 로그인에 성공하면, 서버는 응답 헤더에 <code>Set-Cookie</code>를 실어 보냄.</li>
<li>브라우저는 전달받은 쿠키를 저장소에 보관함.</li>
<li>이후 동일한 도메인으로 요청을 보낼 때마다 브라우저가 <strong>자동으로 <code>Cookie</code> 헤더에 담아 전송</strong>함.</li>
</ol>
<h3 id="⚠️-쿠키의-보안-취약점과-설정-옵션">⚠️ 쿠키의 보안 취약점과 설정 옵션</h3>
<p>쿠키는 브라우저에 저장되므로 클라이언트 측에서 손쉽게 조회 및 변조가 가능하다. 이를 방지하기 위한 핵심 플래그 설정이 필수적이다.</p>
<ul>
<li><strong><code>HttpOnly</code>:</strong> JavaScript(<code>document.cookie</code>)를 통한 쿠키 접근을 차단함. <strong>XSS(Cross-Site Scripting) 공격으로 인한 쿠키 탈취 방지</strong>.</li>
<li><strong><code>Secure</code>:</strong> HTTPS 통신 환경에서만 쿠키를 전송하도록 제한함. 네트워크 스니핑 방지.</li>
<li><strong><code>SameSite</code>:</strong> Cross-Site 요청 시 쿠키 전송 여부를 제어해 <strong>CSRF(Cross-Site Request Forgery) 공격을 방어</strong>함.</li>
<li><code>Strict</code>: 타 사이트에서 발생하는 모든 요청에 쿠키 미전송.</li>
<li><code>Lax</code>: 기본값. 링크 클릭 등 일부 Safe 요청에만 쿠키 전송.</li>
<li><code>None</code>: 모든 Cross-Site 요청에 쿠키 전송 (<code>Secure</code> 옵션 필수).</li>
</ul>
<hr>
<h2 id="3-세션-session">3. 세션 (Session)</h2>
<h3 id="🔑-개념-및-작동-방식">🔑 개념 및 작동 방식</h3>
<p>쿠키에 민감한 정보(예: 사용자 ID, 권한 등)를 직접 담으면 보안상 매우 위험함. 따라서 <strong>민감한 정보는 서버 메모리나 DB에 저장하고, 클라이언트에게는 난수화된 &#39;세션 ID&#39;만 발급</strong>하는 방식이 세션 기반 인증임.</p>
<pre><code class="language-text">[클라이언트]  ---&gt;  (1) 로그인 요청           ---&gt;  [서버] (인증 성공 후 Session DB에 저장)
[클라이언트]  &lt;---  (2) 응답 + Set-Cookie:    &lt;---  [서버] (JSESSIONID=a1b2c3d4...)
                    JSESSIONID
[클라이언트]  ---&gt;  (3) 요청 + Cookie Header ---&gt;  [서버] (Session ID 조회 후 사용자 식별)
</code></pre>
<ol>
<li>클라이언트 로그인 성공 시, 서버는 메모리/DB에 세션 공간을 생성하고 난수 형태의 <code>Session ID</code>를 생성함.</li>
<li>서버는 <code>Set-Cookie</code>를 통해 <code>Session ID</code>만 클라이언트에 전달함.</li>
<li>클라이언트는 요청마다 <code>Session ID</code>가 담긴 쿠키를 전송하고, 서버는 세션 저장소에서 해당 ID를 조회해 사용자를 식별함.</li>
</ol>
<h3 id="📌-세션의-특징-및-단점">📌 세션의 특징 및 단점</h3>
<ul>
<li><strong>장점:</strong> 데이터 실체가 서버에 있으므로 쿠키 대비 보안성이 우수함.</li>
<li><strong>단점:</strong></li>
<li><strong>서버 자원 소모:</strong> 접속자가 늘어날수록 서버 메모리/DB 부하가 증가함.</li>
<li><strong>Scalability(확장성) 이슈:</strong> 서버를 여러 대 두는 분산 환경(Load Balancing)에서 세션 공유 문제 발생.</li>
<li><em>(해결책: <strong>Redis</strong> 같은 In-Memory DB를 활용하여 중앙 세션 서버 구축)</em>.</li>
</ul>
<hr>
<h2 id="4-토큰-기반-인증-jwt-json-web-token">4. 토큰 기반 인증 (JWT: JSON Web Token)</h2>
<p>세션의 서버 저장소 부담을 줄이고 Stateless한 구조를 유지하기 위해 자주 사용되는 방식이다.</p>
<h3 id="📄-jwt-구조">📄 JWT 구조</h3>
<p>JWT는 <code>.</code>을 구분자로 세 부분으로 구성된 문자열이다.</p>
<pre><code class="language-text">Header.Payload.Signature
</code></pre>
<ol>
<li><strong>Header:</strong> 토큰 타입(JWT) 및 사용된 암호화 알고리즘 정보.</li>
<li><strong>Payload:</strong> 클라이언트/사용자에 대한 정보 (Claim 세트: ID, 만료시간 등). <strong>Base64로 인코딩되어 있으므로 누구나 복호화 가능 (민감 정보 저장 금지)</strong>.</li>
<li><strong>Signature:</strong> Header + Payload + <strong>서버의 Secret Key</strong>를 조합하여 위변조 여부를 검증하는 서명값.</li>
</ol>
<h3 id="🔄-jwt-로그인-흐름">🔄 JWT 로그인 흐름</h3>
<ol>
<li>로그인 성공 시 서버는 Secret Key로 서명된 JWT를 생성하여 클라이언트에 전달.</li>
<li>클라이언트는 토큰을 보관 (<code>LocalStorage</code> 또는 <code>HttpOnly Cookie</code>).</li>
<li>요청 시 <code>Authorization: Bearer &lt;JWT_TOKEN&gt;</code> 헤더에 담아 전송.</li>
<li>서버는 별도의 DB 조회 없이 <strong>Secret Key를 이용해 Signature만 검증</strong>하면 인증 완료.</li>
</ol>
<h3 id="⚖️-세션-vs-jwt-비교-summary">⚖️ 세션 vs JWT 비교 Summary</h3>
<table>
<thead>
<tr>
<th>구분</th>
<th>세션 (Session)</th>
<th>토큰 (JWT)</th>
</tr>
</thead>
<tbody><tr>
<td><strong>상태 저장 여부</strong></td>
<td><strong>Stateful</strong> (서버에 상태 저장)</td>
<td><strong>Stateless</strong> (서버에 상태 저장 안 함)</td>
</tr>
<tr>
<td><strong>정확한 제어</strong></td>
<td>서버에서 즉시 세션 강제 종료/만료 가능</td>
<td>발급된 토큰은 만료 전까지 강제 정지 어려움</td>
</tr>
<tr>
<td><strong>확장성</strong></td>
<td>다중 서버 환경 시 Redis 등 추가 세션 서버 필요</td>
<td>서버 확장(Scale-Out) 시 별도 저장소 없이 검증 가능</td>
</tr>
<tr>
<td><strong>데이터 크기</strong></td>
<td>쿠키에 ID만 들어가므로 작음</td>
<td>Payload 정보에 따라 토큰 크기가 커질 수 있음</td>
</tr>
</tbody></table>
<hr>
<h2 id="🛡️-백엔드보안-관점에서의-로그인-구현-체크리스트">🛡️ 백엔드/보안 관점에서의 로그인 구현 체크리스트</h2>
<ol>
<li><strong>비밀번호 저장:</strong></li>
</ol>
<ul>
<li>단방향 해시 함수(SHA-256 등) 단순 사용 금지.</li>
<li><strong>Bcrypt, PBKDF2, Argon2</strong> 등 Salt(소금)가 적용된 Key Derivation Function 활용 필수.</li>
</ul>
<ol start="2">
<li><strong>세션 고정(Session Fixation) 공격 방어:</strong></li>
</ol>
<ul>
<li>로그인 성공 시 기존 세션 ID를 파기하고 <strong>새로운 세션 ID를 재발급</strong>하여 전달.</li>
</ul>
<ol start="3">
<li><strong>인증과 인가 분리:</strong></li>
</ol>
<ul>
<li>로그인 성공(인증) 후에도, 특정 API 호출 시 해당 사용자 권한(Role)이 맞는지 백엔드 인터셉터/미들웨어 단에서 <strong>인가(Authorization) 검증 필수</strong>.</li>
</ul>
<ol start="4">
<li><strong>쿠키 보안 설정:</strong></li>
</ol>
<ul>
<li>인증 쿠키에는 반드시 <code>HttpOnly</code>, <code>Secure</code>, <code>SameSite=Lax/Strict</code> 옵션 적용.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[백엔드 핵심 개념 및 데이터베이스·인프라 구조 정리]]></title>
            <link>https://velog.io/@aiden_lee/%EB%B0%B1%EC%97%94%EB%93%9C-%ED%95%B5%EC%8B%AC-%EA%B0%9C%EB%85%90-%EB%B0%8F-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EC%9D%B8%ED%94%84%EB%9D%BC-%EA%B5%AC%EC%A1%B0-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@aiden_lee/%EB%B0%B1%EC%97%94%EB%93%9C-%ED%95%B5%EC%8B%AC-%EA%B0%9C%EB%85%90-%EB%B0%8F-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%EC%9D%B8%ED%94%84%EB%9D%BC-%EA%B5%AC%EC%A1%B0-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Thu, 06 Aug 2026 08:44:14 GMT</pubDate>
            <description><![CDATA[<h2 id="1-백엔드-및-웹-프레임워크-핵심">1. 백엔드 및 웹 프레임워크 핵심</h2>
<h3 id="🔐-인증authentication과-인가authorization">🔐 인증(Authentication)과 인가(Authorization)</h3>
<ul>
<li><strong>인증 (Authentication):</strong> &quot;이 사용자가 누구인가?&quot;를 검증하는 과정 (예: 로그인, JWT 토큰 검증).</li>
<li><strong>인가 (Authorization):</strong> &quot;로그인한 사용자가 특정 자원/기능에 접근할 권한이 있는가?&quot;를 검증하는 과정 (예: 일반 사용자의 관리자 페이지 접근 차단).</li>
<li><strong>RBAC (Role-Based Access Control):</strong></li>
<li><code>Admin</code>, <code>Manager</code>, <code>User</code> 등 역할(Role)을 부여하고 권한 테이블을 조회해 접근을 통제하는 방식.</li>
<li>프론트엔드에서 버튼이나 메뉴를 숨기는 것만으로는 부족하며, <strong>백엔드 API 컨트롤러/미들웨어 단에서 세션 및 토큰의 권한 정보를 매번 재검증</strong>해야 함.</li>
</ul>
<ul>
<li><strong>보안 위협 대응:</strong></li>
<li><strong>IDOR (Insecure Direct Object Reference):</strong> 타 계정의 식별자(ID)를 직접 참조해 정보에 접근하려는 시도를 서버 측 권한 검증으로 차단.</li>
<li><strong>RESTful API:</strong> 상태 코드를 올바르게 활용하고, 클라이언트 전달 파라미터를 그대로 신뢰하지 않는 방어적 코딩(Defensive Coding)이 필수적임.</li>
</ul>
<h3 id="📄-대용량-데이터엑셀-일괄-등록-등-비동기-처리">📄 대용량 데이터(엑셀 일괄 등록 등) 비동기 처리</h3>
<ul>
<li><strong>문제점:</strong> 수만 건의 데이터를 단일 HTTP 요청으로 동기(Synchronous) 처리할 경우 DB 락, 메모리 초과, 타임아웃(Timeout) 발생.</li>
<li><strong>해결 구조:</strong></li>
</ul>
<ol>
<li><strong>비동기 이관:</strong> 파일 업로드 요청 접수 즉시 클라이언트에 응답을 반환하고, 실제 작업은 비동기 스케줄러나 메시지 큐(Message Queue)로 이관.</li>
<li><strong>청크(Chunk) 단위 처리:</strong> 전체 데이터를 한 번에 메모리에 올리지 않고 1,000건 단위로 쪼개어 읽은 뒤 <code>bulk insert</code> 수행.</li>
</ol>
<hr>
<h2 id="2-데이터베이스-및-쿼리-최적화">2. 데이터베이스 및 쿼리 최적화</h2>
<h3 id="🔍-인덱스index-및-b-tree-원리">🔍 인덱스(Index) 및 B-Tree 원리</h3>
<ul>
<li><strong>개념:</strong> 데이터 탐색 속도를 $O(N)$(풀 스캔)에서 $O(\log N)$으로 줄여주는 색인 구조.</li>
<li><strong>B-Tree 트리 구조 및 동작 방식:</strong><pre><code class="language-text">     [Root Node]
      /        \
 [Branch]     [Branch]
 /      \     /      \
[Leaf]  [Leaf] [Leaf]  [Leaf]  &lt;-- 실제 데이터 주소(Pointer) 보유
</code></pre>
</li>
</ul>
<p>```</p>
<ul>
<li><strong>루트-브랜치-리프 노드</strong> 형태의 균형 잡힌 트리(Balanced Tree) 구조를 가짐.</li>
<li>루트 노드부터 시작해 조건에 맞는 자식 노드를 추적하며 내려가므로 $O(\log N)$의 시간에 원하는 데이터를 탐색할 수 있음.</li>
<li>트리의 최하단 리프 노드(Leaf Node)가 실제 데이터 레코드의 주소(Pointer)를 가지고 있음.</li>
</ul>
<ul>
<li><strong>복합 인덱스(Composite Index) 주의점:</strong></li>
<li><code>(A, B)</code> 순으로 인덱스 생성 시 <code>WHERE</code> 절에 <strong><code>A</code> 조건이 반드시 포함되어야</strong> 인덱스가 적용됨.</li>
<li><code>B</code> 조건만 단독 사용 시 인덱스를 타지 못하고 풀 스캔 발생.</li>
</ul>
<h3 id="📊-풀-스캔full-table-scan과-explain">📊 풀 스캔(Full Table Scan)과 <code>EXPLAIN</code></h3>
<ul>
<li><strong>풀 스캔:</strong> 인덱스를 활용하지 못하고 테이블의 첫 번째 행부터 끝까지 전부 읽는 현상 (데이터 축적 시 서버 부하 급증).</li>
<li><strong><code>EXPLAIN</code> 실행 계획 분석:</strong></li>
<li>쿼리 앞에 <code>EXPLAIN</code>을 붙여 실행 계획 확인.</li>
<li><code>type: ALL</code> $\rightarrow$ <strong>풀 스캔 발생</strong> (인덱스 추가 필요).</li>
<li><code>type: ref</code> 또는 <code>range</code> $\rightarrow$ <strong>인덱스 정상 활용 중</strong>.</li>
</ul>
<h3 id="🔄-트랜잭션과-동시성-제어-locking">🔄 트랜잭션과 동시성 제어 (Locking)</h3>
<ul>
<li><strong>ACID 원칙:</strong> 원자성(Atomicity), 일관성(Consistency), 격리성(Isolation), 영속성(Durability).</li>
<li><strong>낙관적 락 (Optimistic Lock):</strong></li>
<li>충돌 빈도가 낮을 것으로 가정.</li>
<li>DB 락을 걸지 않고 <code>version</code> 컬럼을 이용해 수정 시점에 버전 일치 여부 체크 (성능상 유리).</li>
</ul>
<ul>
<li><strong>비관적 락 (Pessimistic Lock):</strong></li>
<li>충돌 빈도가 높을 것으로 가정.</li>
<li><code>SELECT ... FOR UPDATE</code> 구문 등으로 조회 순간 데이터에 락을 걸어 타 트랜잭션 접근 차단 (재고 차감, 회계 마감 등에 활용).</li>
</ul>
<hr>
<h2 id="3-캐싱-및-인프라-devops-basics">3. 캐싱 및 인프라 (DevOps Basics)</h2>
<h3 id="⚡-redis-활용-및-캐싱-전략">⚡ Redis 활용 및 캐싱 전략</h3>
<ul>
<li><strong>개념:</strong> 메모리 기반(In-Memory) Key-Value 저장소로 RDBMS 대비 압도적으로 빠른 읽기/쓰기 속도 제공.</li>
<li><strong>Cache-Aside (Look-Aside) 패턴:</strong></li>
</ul>
<ol>
<li>애플리케이션(App)이 먼저 Redis에서 데이터를 조회함.</li>
<li><strong>Cache Hit:</strong> Redis에 데이터가 존재하면 즉시 반환.</li>
<li><strong>Cache Miss:</strong> Redis에 없으면 DB를 조회한 후, 해당 데이터를 Redis에 저장하고 반환.</li>
</ol>
<ul>
<li><strong>유효 기간 (TTL - Time To Live):</strong> 메모리 고갈 방지 및 DB 데이터와의 불일치 최소화를 위해 캐시 데이터에 만료 시간을 반드시 설정.</li>
<li><strong>Message Queue 연동:</strong> 대규모 배치 작업(단가 업데이트, 결제 연동 등) 처리 시 RabbitMQ나 Redis Pub/Sub을 활용해 시스템 간 결합도를 낮추고 부하 분산.</li>
</ul>
<h3 id="📦-docker--docker-compose-핵심-개념">📦 Docker / Docker Compose 핵심 개념</h3>
<ul>
<li><strong>컨테이너(Container):</strong> 애플리케이션과 실행 환경(라이브러리, 패키지 등)을 하나로 묶어 어디서나 동일하게 실행하도록 만드는 가상화 기술.</li>
<li><strong>Docker Compose:</strong> 애플리케이션, DB, Redis 등 여러 컨테이너 환경을 <code>docker-compose.yml</code> 파일 하나로 정의하고 관리하는 도구.</li>
<li><strong>실행 모드 (<code>-d</code> 옵션):</strong></li>
<li><code>docker compose up -d</code></li>
<li><code>-d</code>는 Detached Mode(백그라운드 실행)를 의미함.</li>
<li>터미널을 점유하지 않고 백그라운드에서 데몬 형태로 컨테이너를 실행시켜 주며, 터미널을 닫아도 프로세스가 유지됨.</li>
<li>실시간 로그 확인 시 <code>docker compose logs -f</code> 명령어 사용.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[보안·해킹 공부를 위한 문제풀이 사이트 추천 (2026년 기준)]]></title>
            <link>https://velog.io/@aiden_lee/%EB%B3%B4%EC%95%88%ED%95%B4%ED%82%B9-%EA%B3%B5%EB%B6%80%EB%A5%BC-%EC%9C%84%ED%95%9C-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-%EC%82%AC%EC%9D%B4%ED%8A%B8-%EC%B6%94%EC%B2%9C-2026%EB%85%84-%EA%B8%B0%EC%A4%80</link>
            <guid>https://velog.io/@aiden_lee/%EB%B3%B4%EC%95%88%ED%95%B4%ED%82%B9-%EA%B3%B5%EB%B6%80%EB%A5%BC-%EC%9C%84%ED%95%9C-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-%EC%82%AC%EC%9D%B4%ED%8A%B8-%EC%B6%94%EC%B2%9C-2026%EB%85%84-%EA%B8%B0%EC%A4%80</guid>
            <pubDate>Tue, 14 Jul 2026 00:43:16 GMT</pubDate>
            <description><![CDATA[<p>보안을 공부하다 보면 이론을 적용해보고 문제를 풀어 봐야 할 필요성을 느끼게 된다.</p>
<p>알고리즘 문제와 달리 보안은 분야가 워낙 넓어서 목적에 따라 플랫폼도 달라진다.</p>
<p>이번 글에서는 다음 기준으로 선별해보았다.</p>
<ul>
<li>난이도별 학습 가능 여부</li>
<li>체계적인 커리큘럼</li>
<li>문제의 품질</li>
<li>문제 수</li>
<li>프로필 및 기록 관리</li>
<li>국내외 인지도</li>
<li>실제 실무와의 연관성</li>
</ul>
<hr>
<h1 id="1-hack-the-box-htb">1. Hack The Box (HTB)</h1>
<p><strong>추천도 : ⭐⭐⭐⭐⭐</strong></p>
<p>현재 가장 유명한 해킹 실습 플랫폼이다.</p>
<p>취업 준비생부터 현직 보안 전문가까지 모두 사용하는 사이트라고 봐도 될 정도이다.</p>
<h3 id="주요-분야">주요 분야</h3>
<ul>
<li>Linux</li>
<li>Windows</li>
<li>Active Directory</li>
<li>Web</li>
<li>Network</li>
<li>Reverse Engineering</li>
<li>Forensics</li>
<li>Privilege Escalation</li>
<li>Cloud</li>
<li>Red Team</li>
</ul>
<h3 id="장점">장점</h3>
<ul>
<li>실습 환경이 매우 현실적</li>
<li>Academy라는 체계적인 교육 과정 제공</li>
<li>난이도별 머신 제공</li>
<li>인증 시험(CPTS 등)</li>
<li>글로벌 기업에서도 인정받는 플랫폼</li>
</ul>
<h3 id="프로필">프로필</h3>
<p>매우 잘 되어있다.</p>
<ul>
<li>Rank</li>
<li>Points</li>
<li>Badge</li>
<li>Completed Labs</li>
<li>Certificates</li>
<li>Team 기능</li>
</ul>
<p>GitHub처럼 자신의 활동을 보여줄 수 있다.</p>
<h3 id="단점">단점</h3>
<ul>
<li>초보자에게 어려울 수 있다.</li>
<li>Academy 일부는 유료이다.</li>
</ul>
<h3 id="추천-대상">추천 대상</h3>
<ul>
<li>실무형 침투테스트</li>
<li>Red Team</li>
<li>취업 준비</li>
</ul>
<hr>
<h1 id="2-tryhackme">2. TryHackMe</h1>
<p><strong>추천도 : ⭐⭐⭐⭐⭐</strong></p>
<p>입문자에게 가장 추천되는 사이트이다.</p>
<p>Hack The Box보다 훨씬 쉽고 설명도 친절하다.</p>
<h3 id="장점-1">장점</h3>
<ul>
<li>단계별 학습</li>
<li>Room 방식</li>
<li>Windows</li>
<li>Linux</li>
<li>Web</li>
<li>Network</li>
<li>Active Directory</li>
<li>Malware</li>
</ul>
<p>모든 분야를 입문부터 배울 수 있다.</p>
<h3 id="learning-path">Learning Path</h3>
<p>특히 Learning Path가 매우 잘 되어 있다.</p>
<p>예를 들면</p>
<pre><code>Pre Security

↓

Complete Beginner

↓

Jr Penetration Tester

↓

SOC Level 1</code></pre><p>이런 식으로 자연스럽게 올라갈 수 있다.</p>
<h3 id="프로필-1">프로필</h3>
<p>잘 구성되어 있다.</p>
<ul>
<li>Badges</li>
<li>Rank</li>
<li>Completed Rooms</li>
<li>Streak</li>
<li>Certificates</li>
</ul>
<h3 id="단점-1">단점</h3>
<ul>
<li>심화 과정이 HTB보다 부족하다.</li>
</ul>
<h3 id="추천-대상-1">추천 대상</h3>
<p>보안을 처음 시작하는 사람</p>
<hr>
<h1 id="3-portswigger-web-security-academy">3. PortSwigger Web Security Academy</h1>
<p><strong>추천도 : ⭐⭐⭐⭐⭐</strong></p>
<p>웹해킹 공부에서 강하다.</p>
<p>무료입니다.</p>
<h3 id="분야">분야</h3>
<ul>
<li>SQL Injection</li>
<li>XSS</li>
<li>CSRF</li>
<li>SSRF</li>
<li>XXE</li>
<li>JWT</li>
<li>OAuth</li>
<li>SSTI</li>
<li>Deserialization</li>
</ul>
<p>등 거의 모든 웹 취약점을 다룬다.</p>
<h3 id="장점-2">장점</h3>
<ul>
<li>문제 품질 최고</li>
<li>설명 최고</li>
<li>실습 환경 최고</li>
</ul>
<h3 id="단점-2">단점</h3>
<p>웹해킹만 가능하다.</p>
<h3 id="프로필-2">프로필</h3>
<p>약간 아쉬울 수 있는 부분이다.</p>
<p>기록은 남지만 HTB처럼 화려하지는 않다.</p>
<h3 id="추천-대상-2">추천 대상</h3>
<p>웹해킹 공부</p>
<hr>
<h1 id="4-picoctf">4. PicoCTF</h1>
<p><strong>추천도 : ⭐⭐⭐⭐☆</strong></p>
<p>미국 Carnegie Mellon University에서 운영하는 CTF 플랫폼이다.</p>
<p>교육용으로 매우 유명하다.</p>
<h3 id="분야-1">분야</h3>
<ul>
<li>Crypto</li>
<li>Web</li>
<li>Reverse</li>
<li>Binary</li>
<li>Forensics</li>
<li>Network</li>
</ul>
<h3 id="장점-3">장점</h3>
<ul>
<li>초보자 친화적</li>
<li>문제 품질 좋음</li>
<li>무료</li>
</ul>
<h3 id="단점-3">단점</h3>
<p>실무형보다는 교육용이다.</p>
<hr>
<h1 id="5-overthewire">5. OverTheWire</h1>
<p><strong>추천도 : ⭐⭐⭐⭐☆</strong></p>
<p>리눅스와 시스템 공부의 고전이다.</p>
<h3 id="특징">특징</h3>
<p>게임처럼 진행된다.</p>
<pre><code>Bandit

↓

Natas

↓

Leviathan

↓

Krypton</code></pre><p>순서대로 해결하면 된다.</p>
<h3 id="장점-4">장점</h3>
<ul>
<li>Linux 공부 최고</li>
<li>쉘 사용 능력 향상</li>
</ul>
<h3 id="단점-4">단점</h3>
<p>UI가 오래되었다.</p>
<p>프로필이 거의 없다.</p>
<hr>
<h1 id="6-root-me">6. Root Me</h1>
<p><strong>추천도 : ⭐⭐⭐⭐☆</strong></p>
<p>유럽에서 매우 유명한 플랫폼이다.</p>
<h3 id="특징-1">특징</h3>
<p>문제 수가 엄청 많다.</p>
<ul>
<li>Web</li>
<li>Crypto</li>
<li>Reverse</li>
<li>Steganography</li>
<li>Network</li>
<li>Exploit</li>
</ul>
<h3 id="장점-5">장점</h3>
<p>문제 종류가 다양하다.</p>
<h3 id="단점-5">단점</h3>
<p>UI가 오래되었다.</p>
<hr>
<h1 id="7-dreamhack">7. Dreamhack</h1>
<p><strong>추천도 : ⭐⭐⭐⭐☆</strong></p>
<p>국내에서 가장 유명한 보안 플랫폼이다.</p>
<h3 id="특징-2">특징</h3>
<ul>
<li>워게임</li>
<li>CTF</li>
<li>강의</li>
<li>문제풀이</li>
</ul>
<p>모두 제공한다.</p>
<h3 id="장점-6">장점</h3>
<ul>
<li>한국어</li>
<li>국내 커뮤니티</li>
<li>체계적인 강의</li>
</ul>
<h3 id="단점-6">단점</h3>
<p>글로벌 인지도는 거의 없다.</p>
<hr>
<h1 id="8-pwncollege">8. pwn.college</h1>
<p><strong>추천도 : ⭐⭐⭐⭐⭐</strong></p>
<p>최근 가장 평가가 좋은 교육 플랫폼 중 하나이다.</p>
<p>Arizona State University에서 운영한다.</p>
<h3 id="특징-3">특징</h3>
<p>특히</p>
<ul>
<li>Pwn</li>
<li>Linux</li>
<li>Exploit</li>
<li>Binary</li>
</ul>
<p>교육이 엄청 체계적이다.</p>
<h3 id="장점-7">장점</h3>
<ul>
<li>무료</li>
<li>난이도별 진행</li>
<li>대학 강의 수준</li>
</ul>
<h3 id="단점-7">단점</h3>
<p>웹해킹은 많지 않다.</p>
<hr>
<h1 id="사이트-비교">사이트 비교</h1>
<table>
<thead>
<tr>
<th>사이트</th>
<th align="center">입문</th>
<th align="center">심화</th>
<th align="center">문제 수</th>
<th align="center">프로필</th>
<th align="center">인지도</th>
</tr>
</thead>
<tbody><tr>
<td>TryHackMe</td>
<td align="center">⭐⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐⭐</td>
</tr>
<tr>
<td>Hack The Box</td>
<td align="center">⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐⭐</td>
</tr>
<tr>
<td>PortSwigger Academy</td>
<td align="center">⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐⭐</td>
</tr>
<tr>
<td>Dreamhack</td>
<td align="center">⭐⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐</td>
</tr>
<tr>
<td>PicoCTF</td>
<td align="center">⭐⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐</td>
<td align="center">⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐</td>
</tr>
<tr>
<td>pwn.college</td>
<td align="center">⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐</td>
</tr>
<tr>
<td>Root Me</td>
<td align="center">⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐</td>
</tr>
<tr>
<td>OverTheWire</td>
<td align="center">⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐</td>
<td align="center">⭐</td>
<td align="center">⭐⭐⭐⭐</td>
</tr>
</tbody></table>
<hr>
<h1 id="목적별-추천">목적별 추천</h1>
<h3 id="보안을-처음-시작한다면">보안을 처음 시작한다면</h3>
<p>👉 <strong>TryHackMe</strong></p>
<p>가장 친절하다.</p>
<hr>
<h3 id="웹해킹을-공부한다면">웹해킹을 공부한다면</h3>
<p>👉 <strong>PortSwigger Academy</strong></p>
<p>사실상 필수이다.</p>
<hr>
<h3 id="실무-침투테스트를-배우고-싶다면">실무 침투테스트를 배우고 싶다면</h3>
<p>👉 <strong>Hack The Box</strong></p>
<p>업계에서 가장 많이 사용한다고 한다.</p>
<hr>
<h3 id="리눅스-실력을-키우고-싶다면">리눅스 실력을 키우고 싶다면</h3>
<p>👉 <strong>OverTheWire</strong></p>
<p>고전이지만 입문 과정으로 좋다.</p>
<hr>
<h3 id="국내-자료를-원한다면">국내 자료를 원한다면</h3>
<p>👉 <strong>Dreamhack</strong></p>
<p>한국어로 학습하기 좋다.</p>
<hr>
<h3 id="바이너리-해킹·pwn을-깊게-공부한다면">바이너리 해킹·Pwn을 깊게 공부한다면</h3>
<p>👉 <strong>pwn.college</strong></p>
<p>최근 교육 품질이 매우 높다는 평가를 받고 있다.</p>
<hr>
<h1 id="추천-순위">추천 순위</h1>
<h3 id="1위-hack-the-box-⭐⭐⭐⭐⭐">1위. Hack The Box ⭐⭐⭐⭐⭐</h3>
<p>실무성, 인지도, 프로필, 문제 품질을 모두 고려하면 가장 뛰어난 플랫폼이다.</p>
<h3 id="2위-tryhackme-⭐⭐⭐⭐⭐">2위. TryHackMe ⭐⭐⭐⭐⭐</h3>
<p>입문자에게는 최고의 선택이다. 로드맵과 학습 경험이 특히 뛰어나다.</p>
<h3 id="3위-portswigger-web-security-academy-⭐⭐⭐⭐⭐">3위. PortSwigger Web Security Academy ⭐⭐⭐⭐⭐</h3>
<p>웹 보안 분야에서는 사실상 표준 교재이자 실습 플랫폼이다.</p>
<h3 id="4위-pwncollege-⭐⭐⭐⭐⭐">4위. pwn.college ⭐⭐⭐⭐⭐</h3>
<p>시스템·바이너리 해킹을 깊이 있게 배우고 싶다면 가장 추천할 만한 교육 플랫폼이다.</p>
<h3 id="5위-dreamhack-⭐⭐⭐⭐☆">5위. Dreamhack ⭐⭐⭐⭐☆</h3>
<p>한국어 환경에서 학습하고 CTF를 경험하기에 매우 좋다.</p>
<hr>
<h1 id="마무리">마무리</h1>
<p>보안은 알고리즘처럼 하나의 사이트만으로 공부하기 어려운 분야이다. 각 플랫폼이 강점을 가진 영역이 다르기 때문이다.</p>
<p>추천하는 학습 순서는 다음과 같다.</p>
<ol>
<li><strong>TryHackMe</strong>로 네트워크, 리눅스, 웹 기초를 익힌다.</li>
<li><strong>PortSwigger Web Security Academy</strong>로 웹 취약점을 체계적으로 학습한다.</li>
<li><strong>Hack The Box</strong>에서 실무형 침투 테스트와 Active Directory, Windows, Linux 환경을 경험한다.</li>
<li>관심 분야에 따라 <strong>Dreamhack</strong>(국내 CTF), <strong>pwn.college</strong>(바이너리), <strong>OverTheWire</strong>(리눅스), <strong>Root Me</strong>(다양한 워게임)를 병행한다.</li>
</ol>
<p>이러한 조합이라면 입문부터 실무 수준까지 자연스럽게 이어지는 학습 경로를 만들 수 있으며, 프로필과 풀이 기록도 함께 관리해 학습 성과를 꾸준히 쌓아갈 수 있을 것이다.</p>
<table>
<thead>
<tr>
<th>사이트</th>
<th>공식 링크</th>
<th>특징</th>
</tr>
</thead>
<tbody><tr>
<td>Hack The Box</td>
<td><a href="https://www.hackthebox.com">https://www.hackthebox.com</a></td>
<td>실무형 침투 테스트, 업계 표준 플랫폼</td>
</tr>
<tr>
<td>HTB Academy</td>
<td><a href="https://academy.hackthebox.com">https://academy.hackthebox.com</a></td>
<td>Hack The Box의 체계적인 교육 과정</td>
</tr>
<tr>
<td>TryHackMe</td>
<td><a href="https://tryhackme.com">https://tryhackme.com</a></td>
<td>입문자에게 가장 추천하는 학습 플랫폼</td>
</tr>
<tr>
<td>PortSwigger Web Security Academy</td>
<td><a href="https://portswigger.net/web-security">https://portswigger.net/web-security</a></td>
<td>웹해킹 학습의 표준</td>
</tr>
<tr>
<td>Dreamhack</td>
<td><a href="https://dreamhack.io">https://dreamhack.io</a></td>
<td>국내 대표 워게임·CTF 플랫폼</td>
</tr>
<tr>
<td>PicoCTF</td>
<td><a href="https://picoctf.org">https://picoctf.org</a></td>
<td>Carnegie Mellon University 교육용 CTF</td>
</tr>
<tr>
<td>Root Me</td>
<td><a href="https://www.root-me.org">https://www.root-me.org</a></td>
<td>다양한 분야의 워게임 문제</td>
</tr>
<tr>
<td>OverTheWire</td>
<td><a href="https://overthewire.org">https://overthewire.org</a></td>
<td>Linux·시스템 해킹 입문</td>
</tr>
<tr>
<td>pwn.college</td>
<td><a href="https://pwn.college">https://pwn.college</a></td>
<td>바이너리 해킹·시스템 해킹 교육</td>
</tr>
<tr>
<td>CryptoHack <em>(추가 추천)</em></td>
<td><a href="https://cryptohack.org">https://cryptohack.org</a></td>
<td>암호학(Cryptography) 전문 학습 플랫폼</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준을 대체할 알고리즘 공부·문제풀이 사이트 추천 (2026년 기준)]]></title>
            <link>https://velog.io/@aiden_lee/%EB%B0%B1%EC%A4%80%EC%9D%84-%EB%8C%80%EC%B2%B4%ED%95%A0-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EA%B3%B5%EB%B6%80%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-%EC%82%AC%EC%9D%B4%ED%8A%B8-%EC%B6%94%EC%B2%9C-2026%EB%85%84-%EA%B8%B0%EC%A4%80</link>
            <guid>https://velog.io/@aiden_lee/%EB%B0%B1%EC%A4%80%EC%9D%84-%EB%8C%80%EC%B2%B4%ED%95%A0-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EA%B3%B5%EB%B6%80%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-%EC%82%AC%EC%9D%B4%ED%8A%B8-%EC%B6%94%EC%B2%9C-2026%EB%85%84-%EA%B8%B0%EC%A4%80</guid>
            <pubDate>Tue, 14 Jul 2026 00:29:40 GMT</pubDate>
            <description><![CDATA[<p>백준(BOJ)은 국내에서 가장 많이 사용되는 알고리즘 문제 풀이 사이트였지만, 서비스 종료 이후 어떤 사이트를 선택하는 것이 좋을까?</p>
<p>단순히 문제 수만 많은 사이트보다는 다음 기준을 중심으로 비교했다.</p>
<ul>
<li>체계적인 커리큘럼이 있는가</li>
<li>단계별로 학습할 수 있는가</li>
<li>문제 수가 충분한가</li>
<li>문제의 품질이 좋은가</li>
<li>풀이 기록과 프로필 관리가 잘 되는가</li>
<li>국내외 인지도가 높은가</li>
</ul>
<p>이 기준으로 추천할 수 있는 사이트는 다음 네 곳이다.</p>
<hr>
<h1 id="1-leetcode-가장-추천">1. LeetCode (가장 추천)</h1>
<p><strong>추천도 : ⭐⭐⭐⭐⭐</strong></p>
<p>LeetCode는 현재 전 세계에서 가장 영향력이 큰 알고리즘 플랫폼이다.</p>
<p>특히 Google, Meta, Amazon, Microsoft 같은 글로벌 기업의 코딩 인터뷰 준비 사이트로 사실상 표준처럼 사용된다.</p>
<h3 id="장점">장점</h3>
<ul>
<li>Easy → Medium → Hard 난이도 체계</li>
<li>Arrays, Graph, DP 등 태그별 학습</li>
<li>공식 Study Plan 제공</li>
<li>문제 3,800개 이상</li>
<li>문제 품질 매우 우수</li>
<li>깔끔한 UI</li>
<li>제출 기록, 통계, 연속 풀이(Streak) 관리</li>
<li>전 세계 최대 규모 커뮤니티</li>
</ul>
<h3 id="단점">단점</h3>
<ul>
<li>국내 기업 코딩테스트와는 약간 스타일이 다르다.</li>
<li>인터뷰 중심 문제가 많다.</li>
</ul>
<h3 id="추천-대상">추천 대상</h3>
<ul>
<li>취업 준비</li>
<li>알고리즘 실력 향상</li>
<li>해외 기업 준비</li>
</ul>
<p>백준을 대체할 사이트 하나만 고른다면 가장 추천하고 싶은 곳이다.</p>
<hr>
<h1 id="2-codetree-학습용-최고">2. CodeTree (학습용 최고)</h1>
<p><strong>추천도 : ⭐⭐⭐⭐⭐</strong></p>
<p>CodeTree는 국내에서 가장 잘 만들어진 <strong>학습 플랫폼</strong> 중 하나다.</p>
<p>백준처럼 문제를 무작정 많이 제공하는 방식이 아니라,
커리큘럼을 따라 공부하도록 설계되어 있다.</p>
<h3 id="장점-1">장점</h3>
<ul>
<li>매우 체계적인 로드맵</li>
<li>구현</li>
<li>DFS/BFS</li>
<li>백트래킹</li>
<li>DP</li>
<li>자료구조</li>
<li>시뮬레이션</li>
</ul>
<p>등을 순서대로 배울 수 있다.</p>
<p>특히 삼성 SW 역량테스트 준비용으로 좋다고 한다.</p>
<h3 id="장점-2">장점</h3>
<ul>
<li>UI가 매우 좋다.</li>
<li>진행률 관리</li>
<li>학습 로드맵</li>
<li>문제 추천</li>
<li>풀이 기록</li>
</ul>
<p>등이 잘 되어 있다.</p>
<h3 id="단점-1">단점</h3>
<ul>
<li>문제 수는 백준보다 적다.</li>
<li>해외 인지도는 거의 없다.</li>
<li>알고리즘 대회 플랫폼은 아니다.</li>
</ul>
<h3 id="추천-대상-1">추천 대상</h3>
<ul>
<li>알고리즘 입문</li>
<li>삼성 코딩테스트 준비</li>
<li>체계적으로 공부하고 싶은 사람</li>
</ul>
<hr>
<h1 id="3-codeforces-실력-향상-최고">3. Codeforces (실력 향상 최고)</h1>
<p><strong>추천도 : ⭐⭐⭐⭐☆</strong></p>
<p>Codeforces는 세계 최고 수준의 알고리즘 대회 플랫폼이다.</p>
<h3 id="장점-3">장점</h3>
<ul>
<li>문제 품질 최고</li>
<li>문제 수도 매우 많음</li>
<li>정기 Contest</li>
<li>Rating 시스템</li>
<li>세계적인 커뮤니티</li>
</ul>
<h3 id="단점-2">단점</h3>
<ul>
<li>초보자가 접근하기 어렵다.</li>
<li>UI가 오래된 편이다.</li>
<li>체계적인 학습 과정은 부족하다.</li>
</ul>
<h3 id="추천-대상-2">추천 대상</h3>
<ul>
<li>알고리즘 대회</li>
<li>고난도 문제 풀이</li>
<li>실력 향상</li>
</ul>
<hr>
<h1 id="4-프로그래머스">4. 프로그래머스</h1>
<p><strong>추천도 : ⭐⭐⭐⭐☆</strong></p>
<p>국내 기업 코딩테스트를 준비한다면 가장 익숙한 플랫폼이다.</p>
<h3 id="장점-4">장점</h3>
<ul>
<li>국내 기업 스타일</li>
<li>단계별 문제</li>
<li>SQL</li>
<li>PCCP</li>
<li>깔끔한 UI</li>
<li>기업 채용 연계</li>
</ul>
<h3 id="단점-3">단점</h3>
<ul>
<li>문제 수는 백준보다 적다.</li>
<li>최고난도 알고리즘 문제는 적은 편이다.</li>
</ul>
<h3 id="추천-대상-3">추천 대상</h3>
<ul>
<li>국내 취업</li>
<li>코딩테스트 준비</li>
</ul>
<hr>
<h1 id="사이트-비교">사이트 비교</h1>
<table>
<thead>
<tr>
<th>항목</th>
<th align="center">LeetCode</th>
<th align="center">CodeTree</th>
<th align="center">Codeforces</th>
<th align="center">프로그래머스</th>
</tr>
</thead>
<tbody><tr>
<td>체계적인 학습</td>
<td align="center">⭐⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐⭐</td>
<td align="center">⭐⭐</td>
<td align="center">⭐⭐⭐⭐</td>
</tr>
<tr>
<td>문제 수</td>
<td align="center">⭐⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐</td>
</tr>
<tr>
<td>문제 품질</td>
<td align="center">⭐⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐</td>
</tr>
<tr>
<td>UI</td>
<td align="center">⭐⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐⭐</td>
<td align="center">⭐⭐</td>
<td align="center">⭐⭐⭐⭐⭐</td>
</tr>
<tr>
<td>풀이 기록</td>
<td align="center">⭐⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐</td>
</tr>
<tr>
<td>단계별 학습</td>
<td align="center">⭐⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐⭐</td>
<td align="center">⭐⭐</td>
<td align="center">⭐⭐⭐⭐</td>
</tr>
<tr>
<td>국내 인지도</td>
<td align="center">⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐</td>
<td align="center">⭐⭐⭐⭐⭐</td>
</tr>
<tr>
<td>해외 인지도</td>
<td align="center">⭐⭐⭐⭐⭐</td>
<td align="center">⭐</td>
<td align="center">⭐⭐⭐⭐⭐</td>
<td align="center">⭐⭐</td>
</tr>
</tbody></table>
<hr>
<h1 id="목적별-추천">목적별 추천</h1>
<h3 id="알고리즘을-처음부터-배우고-싶다면">알고리즘을 처음부터 배우고 싶다면</h3>
<p>👉 <strong>CodeTree</strong></p>
<p>가장 체계적인 학습 경험을 제공한다.</p>
<hr>
<h3 id="다양한-문제를-오래-풀고-싶다면">다양한 문제를 오래 풀고 싶다면</h3>
<p>👉 <strong>LeetCode</strong></p>
<p>문제 수도 많고 품질도 뛰어나다.</p>
<hr>
<h3 id="국내-코딩테스트가-목표라면">국내 코딩테스트가 목표라면</h3>
<p>👉 <strong>프로그래머스 + CodeTree</strong></p>
<p>국내 기업 스타일에 가장 가깝다.</p>
<hr>
<h3 id="알고리즘-대회를-준비한다면">알고리즘 대회를 준비한다면</h3>
<p>👉 <strong>Codeforces</strong></p>
<p>레이팅 시스템과 대회 문화가 매우 활발하다.</p>
<hr>
<h3 id="해외-빅테크-취업을-목표로-한다면">해외 빅테크 취업을 목표로 한다면</h3>
<p>👉 <strong>LeetCode</strong></p>
<p>사실상 필수 플랫폼이다.</p>
<hr>
<h1 id="추천-순위">추천 순위</h1>
<h3 id="1위-leetcode-⭐⭐⭐⭐⭐">1위. LeetCode ⭐⭐⭐⭐⭐</h3>
<p>가장 균형이 뛰어난 플랫폼이다. 문제 수, 품질, 학습 자료, 프로필 관리, 글로벌 인지도까지 모두 우수하다.</p>
<h3 id="2위-codetree-⭐⭐⭐⭐⭐">2위. CodeTree ⭐⭐⭐⭐⭐</h3>
<p>문제를 &#39;많이 푸는 사이트&#39;라기보다 <strong>가장 잘 가르치는 사이트</strong>에 가깝다. 특히 알고리즘을 처음 배우거나 삼성 SW 역량테스트를 준비하는 사람에게 추천한다.</p>
<h3 id="3위-codeforces-⭐⭐⭐⭐☆">3위. Codeforces ⭐⭐⭐⭐☆</h3>
<p>실력 향상과 알고리즘 대회 준비에는 최고의 플랫폼이다. 다만 초보자에게는 진입장벽이 있는 편이다.</p>
<h3 id="4위-프로그래머스-⭐⭐⭐⭐☆">4위. 프로그래머스 ⭐⭐⭐⭐☆</h3>
<p>국내 취업을 목표로 한다면 여전히 매우 좋은 선택이다. 다만 문제의 양과 난이도 면에서는 LeetCode나 Codeforces보다 범위가 좁다.</p>
<hr>
<h1 id="마무리">마무리</h1>
<p>각 사이트마다 특성을 가지고 있기 때문에, 한가지만 사용하기보다 목적에 따라 조합해서 사용하는 것이 가장 효과적일 것이다.</p>
<ul>
<li><strong>기초를 탄탄히 배우고 싶다면:</strong> CodeTree</li>
<li><strong>문제를 많이 풀며 실력을 넓히고 싶다면:</strong> LeetCode</li>
<li><strong>고난도 알고리즘과 대회를 준비한다면:</strong> Codeforces</li>
<li><strong>국내 기업 코딩테스트를 준비한다면:</strong> 프로그래머스</li>
</ul>
<p>이 네 곳을 목적에 맞게 활용하면 백준이 제공하던 학습 경험을 상당 부분 대체할 수 있으리라 생각한다.</p>
<table>
<thead>
<tr>
<th>사이트</th>
<th>공식 링크</th>
<th>특징</th>
</tr>
</thead>
<tbody><tr>
<td>LeetCode</td>
<td><a href="https://leetcode.com">https://leetcode.com</a></td>
<td>전 세계 1위 코딩 인터뷰·알고리즘 플랫폼</td>
</tr>
<tr>
<td>CodeTree</td>
<td><a href="https://www.codetree.ai">https://www.codetree.ai</a></td>
<td>체계적인 커리큘럼, 삼성 SW 대비</td>
</tr>
<tr>
<td>Codeforces</td>
<td><a href="https://codeforces.com">https://codeforces.com</a></td>
<td>알고리즘 대회, 레이팅 시스템</td>
</tr>
<tr>
<td>프로그래머스</td>
<td><a href="https://school.programmers.co.kr">https://school.programmers.co.kr</a></td>
<td>국내 코딩테스트 준비</td>
</tr>
<tr>
<td>CSES Problem Set <em>(추가 추천)</em></td>
<td><a href="https://cses.fi/problemset">https://cses.fi/problemset</a></td>
<td>알고리즘 기초를 단계별로 학습하기 좋은 무료 문제집</td>
</tr>
<tr>
<td>AtCoder <em>(추가 추천)</em></td>
<td><a href="https://atcoder.jp">https://atcoder.jp</a></td>
<td>일본 최대 알고리즘 대회 플랫폼</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[Rebase 기반 혼자 쓰는 브랜치 workflow]]></title>
            <link>https://velog.io/@aiden_lee/Rebase-%EA%B8%B0%EB%B0%98-%ED%98%BC%EC%9E%90-%EC%93%B0%EB%8A%94-%EB%B8%8C%EB%9E%9C%EC%B9%98-workflow</link>
            <guid>https://velog.io/@aiden_lee/Rebase-%EA%B8%B0%EB%B0%98-%ED%98%BC%EC%9E%90-%EC%93%B0%EB%8A%94-%EB%B8%8C%EB%9E%9C%EC%B9%98-workflow</guid>
            <pubDate>Mon, 06 Jul 2026 06:16:44 GMT</pubDate>
            <description><![CDATA[<h3 id="1단계-출근-후-작업-시작-새-브랜치를-만들-때만">1단계: 출근 후 작업 시작 (새 브랜치를 만들 때만)</h3>
<p>만약 새로운 기능을 만들기 위해 <code>main</code>에서 브랜치를 새로 파야 하는 상황이라면 이때 딱 한 번 <code>checkout</code>(또는 <code>switch</code>)이 필요</p>
<pre><code class="language-bash"># 1. 메인 브랜치로 이동해서 최신 상태로 만들기
git checkout main
git pull origin main

# 2. 최신 main을 기준으로 내 새 작업 브랜치 만들기 (-b 옵션)
git checkout -b [이름]
</code></pre>
<p><em>(※ 이미 만들어서 쓰던 브랜치가 있다면 이 단계는 건너뛰고 바로 2단계로)</em></p>
<hr>
<h3 id="2단계-평소-작업-중-main-최신-코드-동기화하기">2단계: 평소 작업 중 <code>main</code> 최신 코드 동기화하기</h3>
<p>동료들이 메인 올린 코드를 합쳐야할 때 실행하는 루틴</p>
<pre><code class="language-bash"># 1. 현재 작업 중인 파일들을 안전하게 커밋해두기
git add .
git commit -m &quot;[커밋 메세지]&quot;

# 2. 내 브랜치에 앉은 채로 main의 최신 코드 가져와 내 밑에 깔기
git pull --rebase origin main
</code></pre>
<blockquote>
<p>⚠️ <strong>만약 이때 충돌(Conflict)이 난다면?</strong>
Rebase 중 충돌이 나면 당황하지 말고 충돌 코드 수정 후 아래 딱 두 줄만 입력 (머지 커밋을 안 만들기 때문에 일반 커밋이 아니라 <code>--continue</code>를 사용)</p>
<pre><code class="language-bash">git add .
git rebase --continue
</code></pre>
</blockquote>
<hr>
<h3 id="3단계-작업-완료-후-github에-올리기-퇴근-혹은-pr-직전">3단계: 작업 완료 후 GitHub에 올리기 (퇴근 혹은 PR 직전)</h3>
<p>깃허브에 백업하거나 PR을 날릴 때</p>
<pre><code class="language-bash"># 1. 깃허브에 내 히스토리를 예쁘게 정렬해서 덮어쓰기
git push origin [브랜치명] --force-with-lease
</code></pre>
<hr>
<ul>
<li>이미 내 브랜치에서 계속 작업 중일 때는 <strong><code>checkout</code>을 사용하지 않아도 됨</strong></li>
<li><strong><code>add</code> ➡️ <code>commit</code> ➡️ <code>pull --rebase</code> ➡️ `push --force-with-lease</strong>` 이 루틴만 무한 반복해도 됨</li>
</ul>
<p>만약 하다가 도저히 꼬여서 리베이스 하기 전으로 되돌리고 싶다면</p>
<pre><code>git rebase --abort</code></pre><p>충돌 나기 전으로 안전하게 되돌아감.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[입문자를 위한 RAG, LLM, 벡터DB, 청킹, 임베딩 이해하기]]></title>
            <link>https://velog.io/@aiden_lee/%EC%9E%85%EB%AC%B8%EC%9E%90%EB%A5%BC-%EC%9C%84%ED%95%9C-RAG-LLM-%EB%B2%A1%ED%84%B0DB-%EC%B2%AD%ED%82%B9-%EC%9E%84%EB%B2%A0%EB%94%A9-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@aiden_lee/%EC%9E%85%EB%AC%B8%EC%9E%90%EB%A5%BC-%EC%9C%84%ED%95%9C-RAG-LLM-%EB%B2%A1%ED%84%B0DB-%EC%B2%AD%ED%82%B9-%EC%9E%84%EB%B2%A0%EB%94%A9-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 01 Jul 2026 00:37:25 GMT</pubDate>
            <description><![CDATA[<p>AI 서비스를 개발하려고 하면 RAG, LLM, 벡터DB, 임베딩, 청킹 같은 용어를 자주 접하게 된다. 이 용어들은 각각 따로 존재하는 개념처럼 보이지만, 실제 서비스에서는 하나의 파이프라인 안에서 함께 동작하는 경우가 많다.</p>
<p>예를 들어 “회사 내부 문서를 기반으로 답변하는 챗봇”을 만든다고 하자. 이때 LLM만 사용하면 회사 내부 문서를 모르는 상태에서 답변할 수 있다. 반면 RAG 구조를 사용하면 사용자의 질문과 관련된 문서를 먼저 검색한 뒤, 그 문서를 LLM에게 전달해 답변을 생성하게 만들 수 있다.</p>
<p>이 글에서는 개발자 입문자를 대상으로 RAG 시스템을 구성하는 주요 개념을 기술적인 관점에서 정리한다.</p>
<h2 id="1-llm이란-무엇인가">1. LLM이란 무엇인가</h2>
<p>LLM은 Large Language Model의 약자다. 한국어로는 대규모 언어 모델이라고 한다. ChatGPT, Claude, Gemini, Llama 계열 모델 등이 대표적인 LLM이다.</p>
<p>LLM은 대량의 텍스트 데이터를 학습해, 입력된 문맥을 바탕으로 다음에 올 토큰을 예측하는 방식으로 동작한다. 사용자가 질문을 입력하면 모델은 그 질문의 의미와 문맥을 바탕으로 답변을 생성한다.</p>
<p>개발 관점에서 LLM은 보통 API 형태로 사용한다. 애플리케이션 서버는 사용자의 입력을 받아 LLM API에 요청을 보내고, 응답으로 생성된 텍스트를 받아 사용자에게 보여준다.</p>
<p>기본적인 흐름은 다음과 같다.</p>
<pre><code class="language-text">사용자 입력
  → 애플리케이션 서버
  → LLM API 호출
  → LLM 응답
  → 사용자에게 출력</code></pre>
<p>LLM은 다음과 같은 작업에 활용할 수 있다.</p>
<ul>
<li>질문 답변</li>
<li>문서 요약</li>
<li>문장 분류</li>
<li>번역</li>
<li>코드 생성</li>
<li>코드 설명</li>
<li>이메일, 보고서, 블로그 글 작성</li>
<li>대화형 인터페이스 구성</li>
</ul>
<p>하지만 LLM에는 중요한 한계가 있다.</p>
<p>첫째, 학습 데이터에 없는 정보는 정확히 알지 못한다.
둘째, 최신 정보나 내부 문서에 접근하지 못한다.
셋째, 사실이 아닌 내용을 그럴듯하게 생성할 수 있다.
넷째, 긴 문서 전체를 한 번에 처리하는 데 제한이 있다.</p>
<p>이런 한계를 보완하기 위해 RAG 구조가 자주 사용된다.</p>
<h2 id="2-rag란-무엇인가">2. RAG란 무엇인가</h2>
<p>RAG는 Retrieval-Augmented Generation의 약자다. 검색 증강 생성이라고도 한다.</p>
<p>RAG는 LLM이 답변을 생성하기 전에 외부 데이터에서 관련 정보를 검색하고, 검색된 정보를 함께 참고해 답변하도록 만드는 구조다.</p>
<p>일반 LLM 호출은 다음과 같다.</p>
<pre><code class="language-text">질문
  → LLM
  → 답변</code></pre>
<p>RAG 구조는 다음과 같다.</p>
<pre><code class="language-text">질문
  → 관련 문서 검색
  → 검색된 문서를 프롬프트에 포함
  → LLM
  → 문서 기반 답변</code></pre>
<p>즉, RAG는 LLM 자체를 새로 학습시키는 방식이 아니라, 답변 시점에 필요한 정보를 찾아서 LLM에게 제공하는 방식이다.</p>
<p>예를 들어 사용자가 “우리 회사의 재택근무 규정은 어떻게 되나”라고 질문한다고 하자. LLM은 회사 내부 규정을 모를 수 있다. RAG 시스템은 먼저 사내 규정 문서에서 관련 내용을 검색하고, 검색된 문단을 LLM에게 전달한다. LLM은 그 문단을 바탕으로 답변을 생성한다.</p>
<p>RAG는 다음과 같은 서비스에 적합하다.</p>
<ul>
<li>사내 문서 기반 챗봇</li>
<li>고객센터 FAQ 챗봇</li>
<li>제품 매뉴얼 검색 서비스</li>
<li>법률 문서 검색 및 요약 서비스</li>
<li>논문 검색 기반 질의응답 서비스</li>
<li>사용자가 업로드한 PDF 기반 챗봇</li>
<li>개발 문서 기반 코딩 어시스턴트</li>
</ul>
<p>RAG의 핵심은 LLM이 답변을 만들기 전에 외부 지식을 검색한다는 점이다.</p>
<h2 id="3-rag-시스템의-기본-아키텍처">3. RAG 시스템의 기본 아키텍처</h2>
<p>RAG 시스템은 크게 두 가지 단계로 나눌 수 있다.</p>
<p>첫 번째는 인덱싱 단계다. 문서를 미리 처리해서 검색 가능한 형태로 저장하는 과정이다.</p>
<p>두 번째는 질의 단계다. 사용자의 질문이 들어왔을 때 관련 문서를 검색하고 답변을 생성하는 과정이다.</p>
<h2 id="4-인덱싱-단계">4. 인덱싱 단계</h2>
<p>인덱싱 단계는 문서를 벡터DB에 저장하기 위한 준비 과정이다.</p>
<p>전체 흐름은 다음과 같다.</p>
<pre><code class="language-text">원본 문서 수집
  → 텍스트 추출
  → 전처리
  → 청킹
  → 임베딩 생성
  → 벡터DB 저장</code></pre>
<p>각 단계는 다음과 같다.</p>
<h3 id="41-문서-수집">4.1 문서 수집</h3>
<p>먼저 검색 대상으로 사용할 문서를 모은다.</p>
<p>예를 들면 다음과 같은 데이터가 될 수 있다.</p>
<ul>
<li>PDF</li>
<li>Word 문서</li>
<li>Notion 페이지</li>
<li>Google Docs</li>
<li>HTML 문서</li>
<li>Markdown 문서</li>
<li>데이터베이스에 저장된 게시글</li>
<li>고객센터 FAQ</li>
<li>Slack, Jira, Confluence 데이터</li>
</ul>
<p>서비스 목적에 따라 어떤 데이터를 검색 대상으로 삼을지 결정해야 한다.</p>
<h3 id="42-텍스트-추출">4.2 텍스트 추출</h3>
<p>문서에서 실제 텍스트를 추출한다. PDF, DOCX, HTML처럼 파일 형식이 다르면 텍스트를 추출하는 방식도 달라진다.</p>
<p>이 단계에서는 표, 제목, 목록, 코드 블록 같은 구조를 최대한 보존하는 것이 좋다. 단순히 텍스트만 긁어오면 문맥이 깨질 수 있다.</p>
<p>예를 들어 다음 정보는 이후 검색 품질에 영향을 준다.</p>
<ul>
<li>문서 제목</li>
<li>섹션 제목</li>
<li>문단 구조</li>
<li>표의 행과 열</li>
<li>코드 블록</li>
<li>링크</li>
<li>작성일</li>
<li>작성자</li>
<li>문서 카테고리</li>
</ul>
<p>문서 구조를 잘 보존하면 검색 결과의 품질과 답변의 정확도가 좋아질 수 있다.</p>
<h3 id="43-전처리">4.3 전처리</h3>
<p>전처리는 문서를 검색하기 좋은 형태로 정리하는 작업이다.</p>
<p>예를 들어 다음과 같은 작업을 할 수 있다.</p>
<ul>
<li>불필요한 공백 제거</li>
<li>깨진 문자 제거</li>
<li>중복 문서 제거</li>
<li>너무 짧은 문단 제거</li>
<li>개인정보 마스킹</li>
<li>HTML 태그 정리</li>
<li>표를 텍스트 형태로 변환</li>
<li>문서의 메타데이터 정리</li>
</ul>
<p>전처리를 과하게 하면 중요한 정보가 사라질 수 있다. 반대로 전처리가 부족하면 검색 품질이 떨어질 수 있다.</p>
<h3 id="44-청킹">4.4 청킹</h3>
<p>청킹은 긴 문서를 작은 단위로 나누는 작업이다. 나누어진 조각을 청크라고 한다.</p>
<p>LLM과 벡터 검색은 보통 문서 전체보다 적절한 크기의 문서 조각을 다룰 때 더 잘 동작한다. 긴 문서를 그대로 임베딩하면 하나의 벡터 안에 너무 많은 주제가 섞일 수 있다. 그러면 특정 질문과 정확히 관련된 부분을 찾기 어려워진다.</p>
<p>예를 들어 30페이지짜리 인사 규정 문서를 하나의 벡터로 저장하는 것보다, “연차”, “병가”, “재택근무”, “퇴직” 같은 섹션 단위로 나누어 저장하는 편이 검색에 유리하다.</p>
<p>청킹 방식에는 여러 가지가 있다.</p>
<h3 id="고정-길이-청킹">고정 길이 청킹</h3>
<p>문서를 일정한 글자 수 또는 토큰 수로 자르는 방식이다.</p>
<pre><code class="language-text">문서 전체
  → 500토큰 단위로 분할</code></pre>
<p>구현은 쉽지만 문맥이 중간에 끊길 수 있다.</p>
<h3 id="문단-기준-청킹">문단 기준 청킹</h3>
<p>문단 단위로 문서를 나누는 방식이다. 문서 구조를 어느 정도 유지할 수 있다.</p>
<pre><code class="language-text">문단 1
문단 2
문단 3</code></pre>
<p>각 문단이 너무 짧거나 너무 길 경우에는 추가 조정이 필요하다.</p>
<h3 id="제목-기준-청킹">제목 기준 청킹</h3>
<p>제목과 소제목을 기준으로 문서를 나누는 방식이다. 기술 문서나 정책 문서에 적합하다.</p>
<pre><code class="language-text">1. 연차 규정
2. 병가 규정
3. 재택근무 규정</code></pre>
<p>사용자의 질문이 특정 섹션과 잘 연결될 가능성이 높다.</p>
<h3 id="오버랩">오버랩</h3>
<p>청크를 나눌 때 앞뒤 내용을 일부 겹치게 하는 방식이다.</p>
<p>예를 들어 500토큰 단위로 자르되, 이전 청크의 마지막 50토큰을 다음 청크에 포함할 수 있다.</p>
<pre><code class="language-text">청크 1: 1~500토큰
청크 2: 451~950토큰
청크 3: 901~1400토큰</code></pre>
<p>오버랩은 문맥이 끊기는 문제를 줄이는 데 도움을 준다. 다만 오버랩이 너무 크면 저장 용량과 검색 비용이 증가한다.</p>
<h2 id="5-임베딩이란-무엇인가">5. 임베딩이란 무엇인가</h2>
<p>임베딩은 텍스트를 숫자 벡터로 변환하는 과정이다.</p>
<p>LLM이나 검색 시스템이 문장의 의미를 계산하려면 텍스트를 숫자 형태로 다룰 수 있어야 한다. 임베딩 모델은 문장, 문단, 문서 조각을 입력받아 고차원 숫자 배열을 출력한다.</p>
<p>예를 들어 다음과 같은 문장이 있다고 하자.</p>
<pre><code class="language-text">비밀번호를 변경하는 방법
계정 암호를 바꾸는 절차</code></pre>
<p>두 문장은 사용하는 단어가 다르지만 의미가 비슷하다. 임베딩 모델은 두 문장을 서로 가까운 벡터로 변환한다. 이후 벡터DB는 두 벡터 간의 거리를 계산해 유사도를 판단한다.</p>
<p>개발 관점에서 임베딩은 보통 다음과 같이 사용된다.</p>
<pre><code class="language-text">텍스트 입력
  → 임베딩 모델 호출
  → 숫자 배열 반환</code></pre>
<p>예시 형태는 다음과 같다.</p>
<pre><code class="language-json">{
  &quot;text&quot;: &quot;비밀번호를 변경하는 방법&quot;,
  &quot;embedding&quot;: [0.012, -0.221, 0.437, ...]
}</code></pre>
<p>실제 임베딩 벡터는 수백 차원에서 수천 차원일 수 있다.</p>
<h2 id="6-벡터db란-무엇인가">6. 벡터DB란 무엇인가</h2>
<p>벡터DB는 임베딩 벡터를 저장하고, 유사한 벡터를 빠르게 검색하기 위한 데이터베이스다.</p>
<p>일반적인 관계형 데이터베이스는 다음과 같은 검색에 강하다.</p>
<pre><code class="language-sql">SELECT * FROM users WHERE id = 10;
SELECT * FROM products WHERE category = &#39;book&#39;;</code></pre>
<p>이런 검색은 정확한 조건을 기준으로 한다.</p>
<p>반면 벡터DB는 “의미적으로 가까운 데이터”를 찾는 데 사용된다.</p>
<p>사용자가 다음과 같이 질문할 수 있다.</p>
<pre><code class="language-text">퇴사할 때 남은 휴가는 어떻게 되나?</code></pre>
<p>문서에는 다음과 같이 적혀 있을 수 있다.</p>
<pre><code class="language-text">퇴직 시 미사용 연차는 회사 규정에 따라 정산한다.</code></pre>
<p>두 문장은 단어가 완전히 같지 않지만 의미가 가깝다. 벡터DB는 질문 벡터와 문서 청크 벡터 간의 유사도를 계산해 관련 문서를 찾는다.</p>
<p>벡터DB에 저장하는 데이터는 보통 다음과 같은 형태다.</p>
<pre><code class="language-json">{
  &quot;id&quot;: &quot;chunk_001&quot;,
  &quot;text&quot;: &quot;퇴직 시 미사용 연차는 회사 규정에 따라 정산한다.&quot;,
  &quot;embedding&quot;: [0.032, -0.114, 0.827, ...],
  &quot;metadata&quot;: {
    &quot;document_id&quot;: &quot;hr_policy_2025&quot;,
    &quot;title&quot;: &quot;인사 규정&quot;,
    &quot;section&quot;: &quot;퇴직&quot;,
    &quot;created_at&quot;: &quot;2025-01-01&quot;
  }
}</code></pre>
<p>벡터DB는 단순히 벡터만 저장하는 것이 아니라, 원문 텍스트와 메타데이터도 함께 저장하는 경우가 많다.</p>
<p>대표적인 벡터DB 또는 벡터 검색 도구는 다음과 같다.</p>
<ul>
<li>Pinecone</li>
<li>Weaviate</li>
<li>Milvus</li>
<li>Chroma</li>
<li>FAISS</li>
<li>Qdrant</li>
<li>Elasticsearch vector search</li>
<li>PostgreSQL pgvector</li>
</ul>
<p>서비스 규모가 작거나 프로토타입 단계라면 Chroma, FAISS, pgvector 같은 선택지가 자주 쓰인다. 대규모 운영 환경에서는 성능, 확장성, 운영 편의성, 권한 관리, 필터링 기능 등을 함께 고려해야 한다.</p>
<h2 id="7-유사도-검색">7. 유사도 검색</h2>
<p>벡터DB는 사용자의 질문 벡터와 문서 청크 벡터를 비교해 가까운 벡터를 찾는다. 이를 유사도 검색이라고 한다.</p>
<p>자주 사용되는 유사도 계산 방식은 다음과 같다.</p>
<ul>
<li>cosine similarity</li>
<li>dot product</li>
<li>Euclidean distance</li>
</ul>
<p>cosine similarity는 두 벡터의 방향이 얼마나 비슷한지를 계산한다. RAG에서는 cosine similarity가 자주 사용된다.</p>
<p>질의 단계에서는 보통 top-k 검색을 한다.</p>
<pre><code class="language-text">사용자 질문을 임베딩
  → 벡터DB에서 가장 가까운 청크 k개 검색
  → 상위 k개 문서를 LLM에 전달</code></pre>
<p>예를 들어 <code>top_k = 5</code>라면 질문과 가장 관련성이 높은 청크 5개를 가져온다.</p>
<p>하지만 top-k 값을 크게 한다고 항상 좋은 것은 아니다. 관련 없는 문서가 함께 들어가면 LLM이 혼란스러운 답변을 만들 수 있다. 반대로 top-k 값이 너무 작으면 답변에 필요한 정보가 누락될 수 있다.</p>
<h2 id="8-질의-단계">8. 질의 단계</h2>
<p>질의 단계는 사용자의 질문을 받아 실제 답변을 생성하는 과정이다.</p>
<p>전체 흐름은 다음과 같다.</p>
<pre><code class="language-text">사용자 질문
  → 질문 임베딩 생성
  → 벡터DB 검색
  → 관련 청크 가져오기
  → 프롬프트 구성
  → LLM 호출
  → 답변 반환</code></pre>
<p>예를 들어 사용자가 다음과 같이 질문한다고 하자.</p>
<pre><code class="language-text">퇴사할 때 남은 연차는 어떻게 처리되나?</code></pre>
<p>시스템은 먼저 이 질문을 임베딩 모델에 넣어 질문 벡터를 만든다. 그다음 벡터DB에서 이 질문 벡터와 가까운 문서 청크를 찾는다.</p>
<p>검색 결과로 다음 청크가 나올 수 있다.</p>
<pre><code class="language-text">퇴직 시 미사용 연차는 근로기준법 및 회사 내부 규정에 따라 정산한다.</code></pre>
<p>이제 시스템은 사용자 질문과 검색된 문서를 함께 프롬프트로 구성한다.</p>
<pre><code class="language-text">아래 참고 문서를 바탕으로 사용자의 질문에 답하라.
문서에 없는 내용은 추측하지 말고 모른다고 답하라.

[참고 문서]
퇴직 시 미사용 연차는 근로기준법 및 회사 내부 규정에 따라 정산한다.

[사용자 질문]
퇴사할 때 남은 연차는 어떻게 처리되나?</code></pre>
<p>LLM은 이 프롬프트를 입력받아 문서 기반 답변을 생성한다.</p>
<h2 id="9-프롬프트-구성">9. 프롬프트 구성</h2>
<p>RAG에서 프롬프트는 매우 중요하다. 검색된 문서를 LLM에게 전달하더라도, LLM이 그 문서를 어떤 방식으로 사용해야 하는지 명확히 알려줘야 한다.</p>
<p>기본적인 RAG 프롬프트 구조는 다음과 같다.</p>
<pre><code class="language-text">너는 문서 기반 질의응답 assistant다.
아래 참고 문서를 바탕으로 질문에 답하라.
참고 문서에 없는 내용은 추측하지 말고 모른다고 답하라.
답변에는 가능한 한 근거를 포함하라.

[참고 문서]
{retrieved_context}

[질문]
{user_question}

[답변]</code></pre>
<p>프롬프트에서 중요한 점은 다음과 같다.</p>
<p>첫째, 참고 문서를 우선 사용하도록 지시해야 한다.
둘째, 문서에 없는 내용은 추측하지 않도록 해야 한다.
셋째, 답변 형식을 명확히 정해야 한다.
넷째, 필요한 경우 출처를 포함하도록 해야 한다.
다섯째, 여러 문서가 서로 충돌할 때 어떻게 처리할지 정해야 한다.</p>
<p>실제 서비스에서는 프롬프트 하나만으로 답변 품질을 보장하기 어렵다. 검색 품질, 청킹 방식, 임베딩 모델, 문서 품질이 함께 맞아야 한다.</p>
<h2 id="10-토큰과-컨텍스트-윈도우">10. 토큰과 컨텍스트 윈도우</h2>
<p>토큰은 LLM이 텍스트를 처리하는 기본 단위다. 하나의 단어가 하나의 토큰이 될 수도 있고, 단어의 일부가 하나의 토큰이 될 수도 있다. 한국어는 형태소나 글자 단위에 가깝게 나뉘는 경우도 있다.</p>
<p>컨텍스트 윈도우는 LLM이 한 번에 처리할 수 있는 최대 토큰 수다. 여기에는 사용자의 질문, 시스템 프롬프트, 대화 히스토리, 검색된 문서, 답변 생성에 필요한 공간이 모두 포함된다.</p>
<p>RAG에서는 검색된 문서를 너무 많이 넣으면 컨텍스트 윈도우를 초과할 수 있다. 따라서 검색된 모든 문서를 넣는 것이 아니라, 관련성이 높은 문서를 선별해 넣어야 한다.</p>
<p>예를 들어 컨텍스트 윈도우가 16,000토큰이라고 해도 전부 참고 문서로 사용할 수는 없다.</p>
<pre><code class="language-text">시스템 지시문
+ 사용자 질문
+ 대화 히스토리
+ 검색된 문서
+ 모델이 생성할 답변 공간
≤ 컨텍스트 윈도우</code></pre>
<p>이 제한 때문에 청킹, top-k 설정, reranking, 요약 등의 전략이 필요하다.</p>
<h2 id="11-metadata와-필터링">11. Metadata와 필터링</h2>
<p>Metadata는 문서나 청크에 붙는 추가 정보다.</p>
<p>예를 들어 사내 문서 검색 시스템에서는 다음과 같은 metadata를 사용할 수 있다.</p>
<pre><code class="language-json">{
  &quot;document_id&quot;: &quot;hr_policy_2025&quot;,
  &quot;title&quot;: &quot;인사 규정&quot;,
  &quot;department&quot;: &quot;HR&quot;,
  &quot;access_level&quot;: &quot;employee&quot;,
  &quot;created_at&quot;: &quot;2025-01-01&quot;,
  &quot;updated_at&quot;: &quot;2025-06-01&quot;,
  &quot;section&quot;: &quot;연차&quot;
}</code></pre>
<p>Metadata는 검색 품질과 권한 관리에 중요하다.</p>
<p>예를 들어 사용자가 인사 문서만 검색하도록 제한할 수 있다.</p>
<pre><code class="language-text">department = HR</code></pre>
<p>또는 사용자가 접근 권한이 있는 문서만 검색하도록 만들 수 있다.</p>
<pre><code class="language-text">access_level &lt;= user.access_level</code></pre>
<p>RAG 시스템에서 권한 관리는 매우 중요하다. 사용자가 볼 수 없는 문서가 검색되어 LLM 프롬프트에 들어가면, LLM이 그 내용을 답변으로 노출할 수 있다. 따라서 벡터 검색 전에 권한 필터링을 적용하거나, 검색 시 metadata 조건을 반드시 함께 사용해야 한다.</p>
<h2 id="12-hybrid-search">12. Hybrid Search</h2>
<p>벡터 검색은 의미적으로 비슷한 문서를 찾는 데 강하다. 하지만 항상 완벽하지는 않다.</p>
<p>특히 다음과 같은 경우에는 키워드 검색이 더 유리할 수 있다.</p>
<ul>
<li>제품 코드</li>
<li>에러 코드</li>
<li>주문 번호</li>
<li>고유명사</li>
<li>API 이름</li>
<li>함수명</li>
<li>법 조항 번호</li>
<li>특정 버전명</li>
</ul>
<p>예를 들어 사용자가 “ERR-5042 해결 방법”을 검색한다면 벡터 검색보다 키워드 검색이 더 정확할 수 있다.</p>
<p>Hybrid search는 벡터 검색과 키워드 검색을 함께 사용하는 방식이다.</p>
<pre><code class="language-text">벡터 검색 결과
+ 키워드 검색 결과
→ 점수 결합
→ 최종 검색 결과</code></pre>
<p>이 방식은 의미 기반 검색과 정확한 단어 검색의 장점을 함께 사용할 수 있다.</p>
<h2 id="13-reranking">13. Reranking</h2>
<p>Reranking은 1차 검색 결과를 다시 정렬하는 과정이다.</p>
<p>벡터DB에서 top-k로 문서를 가져오면, 그 결과가 항상 최적이라고 보장할 수는 없다. 이때 reranker 모델을 사용해 질문과 각 문서의 관련성을 더 정교하게 평가할 수 있다.</p>
<p>흐름은 다음과 같다.</p>
<pre><code class="language-text">질문
  → 벡터DB에서 top-20 검색
  → reranker로 관련도 재평가
  → 상위 5개만 LLM에 전달</code></pre>
<p>Reranking은 검색 품질을 높이는 데 효과적이지만, 추가 모델 호출이 필요하므로 latency와 비용이 증가할 수 있다.</p>
<h2 id="14-hallucination과-grounding">14. Hallucination과 Grounding</h2>
<p>Hallucination은 LLM이 사실이 아닌 내용을 그럴듯하게 생성하는 현상이다.</p>
<p>RAG는 hallucination을 줄이기 위해 사용된다. LLM이 답변할 때 외부 문서를 근거로 삼게 만들기 때문이다. 하지만 RAG를 사용한다고 해서 hallucination이 완전히 사라지는 것은 아니다.</p>
<p>예를 들어 검색 결과가 부정확하거나, 문서가 부족하거나, 프롬프트가 애매하면 LLM은 여전히 추측할 수 있다.</p>
<p>Grounding은 LLM의 답변이 특정 근거에 기반하도록 만드는 것을 의미한다. RAG는 LLM의 답변을 외부 문서에 grounding하는 대표적인 방식이다.</p>
<p>실제 서비스에서는 다음과 같은 방식으로 hallucination을 줄일 수 있다.</p>
<ul>
<li>문서에 없는 내용은 모른다고 답하게 한다.</li>
<li>답변에 출처를 포함한다.</li>
<li>검색 결과의 유사도 점수가 낮으면 답변하지 않는다.</li>
<li>여러 문서가 충돌하면 충돌 사실을 표시한다.</li>
<li>중요한 답변은 사람이 검토하도록 한다.</li>
<li>로그를 저장하고 실패 사례를 분석한다.</li>
</ul>
<h2 id="15-rag와-파인튜닝의-차이">15. RAG와 파인튜닝의 차이</h2>
<p>RAG와 파인튜닝은 자주 비교된다.</p>
<p>RAG는 외부 문서를 검색해 LLM에게 제공하는 방식이다. 모델 자체를 다시 학습시키는 것이 아니다.</p>
<p>파인튜닝은 기존 모델을 특정 데이터로 추가 학습시키는 방식이다. 모델의 동작 방식, 답변 스타일, 특정 작업 수행 능력을 바꾸는 데 사용된다.</p>
<p>둘의 차이는 다음과 같이 정리할 수 있다.</p>
<pre><code class="language-text">RAG
- 외부 지식을 검색해 사용한다.
- 문서가 자주 바뀌는 경우에 유리하다.
- 출처 기반 답변을 만들기 쉽다.
- 내부 문서 QA에 적합하다.

파인튜닝
- 모델을 추가 학습시킨다.
- 특정 형식이나 스타일을 학습시키는 데 유리하다.
- 반복적인 태스크 성능 개선에 적합하다.
- 데이터 준비와 학습 비용이 필요하다.</code></pre>
<p>사내 문서 기반 챗봇을 만든다면 보통 RAG를 먼저 고려한다. 회사 문서는 계속 바뀔 수 있기 때문이다. 문서가 바뀔 때마다 모델을 다시 학습시키는 것은 비효율적이다.</p>
<p>반면 특정 형식으로 답변하게 하거나, 특정 분류 작업을 잘하게 만들고 싶다면 파인튜닝을 고려할 수 있다.</p>
<h2 id="16-간단한-rag-구현-흐름">16. 간단한 RAG 구현 흐름</h2>
<p>실제 코드 수준에서는 대략 다음과 같은 흐름으로 구현할 수 있다.</p>
<h3 id="문서-저장-단계">문서 저장 단계</h3>
<pre><code class="language-python">documents = load_documents(&quot;./docs&quot;)

chunks = []
for doc in documents:
    chunks.extend(split_text(doc.text, chunk_size=500, overlap=50))

for chunk in chunks:
    embedding = embedding_model.embed(chunk.text)
    vector_db.insert(
        id=chunk.id,
        vector=embedding,
        text=chunk.text,
        metadata=chunk.metadata
    )</code></pre>
<p>이 단계에서는 문서를 읽고, 청킹하고, 임베딩한 뒤, 벡터DB에 저장한다.</p>
<h3 id="질문-처리-단계">질문 처리 단계</h3>
<pre><code class="language-python">user_question = &quot;퇴사할 때 남은 연차는 어떻게 처리되나?&quot;

question_embedding = embedding_model.embed(user_question)

results = vector_db.search(
    vector=question_embedding,
    top_k=5
)

context = &quot;\n\n&quot;.join([result.text for result in results])

prompt = f&quot;&quot;&quot;
아래 참고 문서를 바탕으로 질문에 답하라.
문서에 없는 내용은 추측하지 말고 모른다고 답하라.

[참고 문서]
{context}

[질문]
{user_question}
&quot;&quot;&quot;

answer = llm.generate(prompt)</code></pre>
<p>이 코드는 단순화된 예시다. 실제 서비스에서는 에러 처리, 권한 관리, 토큰 제한, 캐싱, 로그 저장, 모니터링, 평가 시스템 등이 추가된다.</p>
<h2 id="17-운영-환경에서-고려할-점">17. 운영 환경에서 고려할 점</h2>
<p>RAG는 개념상 단순해 보이지만 운영 환경에서는 고려할 요소가 많다.</p>
<h3 id="데이터-최신성">데이터 최신성</h3>
<p>문서가 수정되면 벡터DB도 업데이트해야 한다. 원본 문서와 벡터DB 사이에 동기화 전략이 필요하다.</p>
<p>예를 들어 문서가 변경되면 기존 청크를 삭제하고 새로 임베딩할 수 있다. 또는 변경된 부분만 다시 처리할 수도 있다.</p>
<h3 id="권한-관리">권한 관리</h3>
<p>사용자가 접근할 수 없는 문서가 검색되면 안 된다. 검색 전에 사용자 권한을 확인하고, metadata 필터를 적용해야 한다.</p>
<h3 id="비용">비용</h3>
<p>LLM 호출, 임베딩 모델 호출, 벡터DB 운영에는 비용이 든다. 문서를 자주 다시 임베딩하거나 검색 결과를 너무 많이 LLM에 넣으면 비용이 증가한다.</p>
<h3 id="응답-속도">응답 속도</h3>
<p>RAG는 일반 LLM 호출보다 단계가 많다.</p>
<pre><code class="language-text">질문 임베딩
+ 벡터 검색
+ reranking
+ LLM 호출</code></pre>
<p>각 단계가 latency에 영향을 준다. 캐싱, 비동기 처리, 검색 결과 최적화 등을 고려해야 한다.</p>
<h3 id="평가">평가</h3>
<p>RAG 시스템은 답변 품질을 지속적으로 평가해야 한다.</p>
<p>평가할 수 있는 항목은 다음과 같다.</p>
<ul>
<li>질문에 맞는 문서를 검색했는가</li>
<li>답변이 문서에 근거하고 있는가</li>
<li>문서에 없는 내용을 지어내지 않았는가</li>
<li>출처가 정확한가</li>
<li>답변이 사용자의 의도에 맞는가</li>
<li>응답 속도가 적절한가</li>
</ul>
<p>RAG의 품질은 LLM 성능만으로 결정되지 않는다. 검색 품질이 매우 중요하다.</p>
<h2 id="18-전체-개념-연결">18. 전체 개념 연결</h2>
<p>이제 각 개념을 하나의 흐름으로 연결할 수 있다.</p>
<pre><code class="language-text">원본 문서
  → 청킹
  → 임베딩
  → 벡터DB 저장</code></pre>
<p>이것은 문서를 검색 가능한 형태로 준비하는 과정이다.</p>
<pre><code class="language-text">사용자 질문
  → 질문 임베딩
  → 벡터DB 검색
  → 관련 청크 선택
  → 프롬프트 구성
  → LLM 답변 생성</code></pre>
<p>이것은 사용자의 질문에 답하는 과정이다.</p>
<p>각 개념의 역할은 다음과 같다.</p>
<p>LLM은 최종 답변을 생성한다.
RAG는 검색과 생성을 연결하는 구조다.
임베딩은 텍스트를 의미 기반 벡터로 변환한다.
벡터DB는 임베딩 벡터를 저장하고 유사한 문서를 검색한다.
청킹은 긴 문서를 검색 가능한 작은 단위로 나눈다.
프롬프트는 LLM이 검색된 문서를 어떻게 사용할지 지시한다.
Metadata는 검색 필터링과 권한 관리에 사용된다.
Reranking은 검색 결과의 순서를 더 정교하게 조정한다.
Hybrid search는 벡터 검색과 키워드 검색을 함께 사용한다.
Grounding은 답변이 근거 문서에 기반하도록 만드는 개념이다.</p>
<h2 id="19-정리">19. 정리</h2>
<p>개발자 입문자 관점에서 RAG를 이해할 때 가장 중요한 것은 전체 데이터 흐름이다.</p>
<p>먼저 문서를 수집하고, 텍스트를 추출하고, 청킹한다. 그다음 각 청크를 임베딩해 벡터DB에 저장한다. 사용자가 질문하면 질문도 임베딩하고, 벡터DB에서 관련 청크를 검색한다. 검색된 청크를 프롬프트에 넣고 LLM에게 전달하면, LLM이 문서 기반 답변을 생성한다.</p>
<p>RAG는 LLM의 한계를 보완하는 실용적인 구조다. 특히 내부 문서, 최신 정보, 전문 문서처럼 모델이 기본적으로 알기 어려운 정보를 다룰 때 유용하다.</p>
<p>다만 RAG를 도입한다고 자동으로 좋은 답변이 나오는 것은 아니다. 문서 품질, 청킹 전략, 임베딩 모델, 벡터DB 검색 성능, 프롬프트 설계, 권한 관리, 평가 체계가 함께 맞아야 한다.</p>
<p>결국 RAG 시스템은 단순히 LLM API를 호출하는 기능이 아니라, 검색 시스템과 생성 모델을 결합한 애플리케이션 아키텍처다. 개발자는 LLM뿐 아니라 데이터 처리, 검색, 저장, 권한, 평가까지 함께 고려해야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[VS Code 클로드 코드 계정 변경 방법]]></title>
            <link>https://velog.io/@aiden_lee/VS-Code-%ED%81%B4%EB%A1%9C%EB%93%9C-%EC%BD%94%EB%93%9C-%EA%B3%84%EC%A0%95-%EB%B3%80%EA%B2%BD-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@aiden_lee/VS-Code-%ED%81%B4%EB%A1%9C%EB%93%9C-%EC%BD%94%EB%93%9C-%EA%B3%84%EC%A0%95-%EB%B3%80%EA%B2%BD-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Tue, 30 Jun 2026 00:37:27 GMT</pubDate>
            <description><![CDATA[<p>VS Code에서 클로드 코드(Claude Code) 계정을 변경하려면, 기존 세션을 로그아웃하고 새로운 계정으로 다시 인증을 진행해야 한다.</p>
<p>계정 전환 절차는 다음과 같다.</p>
<p><strong>1. 클로드 웹 접속</strong>
웹 브라우저에서 Claude 공식 웹사이트에 접속해 새로 로그인할 계정으로 미리 로그인해둔다.</p>
<p><strong>2. VS Code에서 로그아웃</strong>
VS Code를 열고 Ctrl + Shift + P(Windows/Linux) 또는 Cmd + Shift + P(Mac)를 눌러 명령 팔레트를 연다.
검색창에 Claude Code 또는 Claude를 입력한 뒤 Claude Code: Logout을 클릭해 기존 연결을 해제한다.</p>
<p><strong>3. 새 계정 연결</strong>
코드를 여는 등 클로드 코드를 다시 호출하면, 웹 브라우저 인증 화면이 뜨며 새 계정을 연동할 수 있다.</p>
<p>*터미널을 직접 사용하는 CLI 환경이라면 claude를 실행한 후 채팅창에 직접 /logout 명령어를 입력하고 재로그인할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Git] 깃 커밋 컨벤션 가이드]]></title>
            <link>https://velog.io/@aiden_lee/Git-%EA%B9%83-%EC%BB%A4%EB%B0%8B-%EC%BB%A8%EB%B2%A4%EC%85%98-%EA%B0%80%EC%9D%B4%EB%93%9C</link>
            <guid>https://velog.io/@aiden_lee/Git-%EA%B9%83-%EC%BB%A4%EB%B0%8B-%EC%BB%A8%EB%B2%A4%EC%85%98-%EA%B0%80%EC%9D%B4%EB%93%9C</guid>
            <pubDate>Wed, 24 Jun 2026 06:23:02 GMT</pubDate>
            <description><![CDATA[<h2 id="📌-커밋-메시지의-기본-구조">📌 커밋 메시지의 기본 구조</h2>
<p>좋은 커밋 메시지는 한눈에 변경 내용의 의도와 범위를 파악할 수 있어야 한다. 기본 구조는 제목(Subject), 본문(Body), 바닥글(Footer)의 3단계 구성을 따른다. 각 영역은 빈 줄(New Line)로 구분한다.</p>
<pre><code class="language-text">type(scope): 제목 (Subject)

본문 (Body) - 생략 가능

바닥글 (Footer) - 생략 가능
</code></pre>
<ul>
<li><strong>제목:</strong> 변경의 종류(type)와 핵심 내용을 한 줄로 요약한다.</li>
<li><strong>본문:</strong> &quot;무엇을&quot;, &quot;왜&quot; 변경했는지 상세히 서술한다. (선택 사항)</li>
<li><strong>바닥글:</strong> 이슈 트래커 ID(예: Jira, GitHub Issues)나 Breaking Change(중대 변경)를 명시한다. (선택 사항)</li>
</ul>
<hr>
<h2 id="🛠️-1-제목-subject-작성-규칙">🛠️ 1. 제목 (Subject) 작성 규칙</h2>
<p>제목은 컨벤션의 핵심이다. 현업에서 가장 많이 쓰이는 7가지 대표 <strong>Type</strong>을 숙지하고 일관되게 사용한다.</p>
<h3 id="7가지-핵심-커밋-타입-type">7가지 핵심 커밋 타입 (Type)</h3>
<table>
<thead>
<tr>
<th>타입 (Type)</th>
<th>언제 사용하는가?</th>
</tr>
</thead>
<tbody><tr>
<td><strong><code>feat</code></strong></td>
<td>새로운 기능(Feature)을 추가할 때</td>
</tr>
<tr>
<td><strong><code>fix</code></strong></td>
<td>버그를 수정할 때</td>
</tr>
<tr>
<td><strong><code>docs</code></strong></td>
<td>문서(README, 주석, 위키 등)를 수정할 때</td>
</tr>
<tr>
<td><strong><code>style</code></strong></td>
<td>코드 의미에 영향을 주지 않는 스타일 변경 (포맷팅, 세미콜론 누락 등)</td>
</tr>
<tr>
<td><strong><code>refactor</code></strong></td>
<td>기능 추가나 버그 수정 없이 코드를 리팩토링할 때</td>
</tr>
<tr>
<td><strong><code>test</code></strong></td>
<td>테스트 코드를 추가하거나 수정할 때</td>
</tr>
<tr>
<td><strong><code>chore</code></strong></td>
<td>빌드 업무, 패키지 매니저 설정, .gitignore 등 자잘한 기타 작업</td>
</tr>
</tbody></table>
<blockquote>
<p><strong>💡 Scope(범위) 활용하기 (선택)</strong>
변경 사항이 특정 모듈이나 도메인에 국한된다면 괄호를 이용해 범위를 명시한다. 한눈에 파악하기 매우 용이해진다.</p>
<ul>
<li>예: <code>feat(order): 주문 추가 시 중복 제약 조건 로직 구현</code></li>
<li>예: <code>chore(deps): lodash 라이브러리 버전 업데이트</code></li>
</ul>
</blockquote>
<h3 id="제목-작성-5대-원칙">제목 작성 5대 원칙</h3>
<ol>
<li><strong>타입 뒤에는 콜론과 공백을 둔다:</strong> <code>feat: 내용</code> (O) / <code>feat:내용</code> (X)</li>
<li><strong>첫 글자는 대문자로 시작하지 않는다:</strong> 영문 작성 시 소문자로 시작하는 것이 관례다. (타입과의 통일성)</li>
<li><strong>명령문 형태로 작성한다:</strong> &quot;<del>했음&quot; 보다는 &quot;</del>함&quot;, &quot;<del>추가&quot;, &quot;</del>수정&quot; 형태로 간결하게 작성한다.</li>
<li><strong>끝에 마침표(<code>.</code>)를 찍지 않는다.</strong></li>
<li><strong>글자 수는 50자 내외로 제한한다.</strong></li>
</ol>
<hr>
<h2 id="📝-2-본문-body-작성-규칙-선택-사항">📝 2. 본문 (Body) 작성 규칙 (선택 사항)</h2>
<p>변경의 맥락이 복잡하여 제목 한 줄로 서술이 불가능할 때 작성한다. 단순 버그 수정이나 자잘한 수정 시에는 과감히 생략한다.</p>
<ul>
<li>부연 설명이 필요할 때 사용하며, <strong>최대 72자</strong>마다 줄바꿈을 한다.</li>
<li>&quot;어떻게 변경했는지&quot;보다 <strong>&quot;무엇을&quot;, &quot;왜&quot; 변경했는지</strong>에 집중하여 작성한다.</li>
</ul>
<pre><code class="language-text">fix(auth): JWT 토큰 만료 시 간헐적 튕김 현상 수정

- 기존 익스파이어 타임 계산 로직의 시차 밀리초 계산 오류 확인
- 서버 타임존 기준과 브라우저 타임존을 UTC로 통일하여 오차 범위를 제거함
</code></pre>
<hr>
<h2 id="⚓-3-바닥글-footer-작성-규칙-선택-사항">⚓ 3. 바닥글 (Footer) 작성 규칙 (선택 사항)</h2>
<p>주로 협업 도구와의 연동이나 프로젝트의 중대한 변화를 알릴 때 사용한다.</p>
<h3 id="issue-tracker-연동-github-jira-등">Issue Tracker 연동 (GitHub, Jira 등)</h3>
<p>이슈 번호를 명시하여 해당 커밋이 어떤 태스크와 연결되어 있는지 추적할 수 있게 한다. GitHub의 경우 특정 키워드와 함께 사용하면 푸시 시 이슈가 자동으로 닫힌다.</p>
<ul>
<li>키워드: <code>Fixes</code>, <code>Closes</code>, <code>Resolves</code></li>
<li>예시: <code>규약 키워드: #이슈번호</code> ➡️ <code>Closes: #124</code></li>
</ul>
<h3 id="breaking-change-중대-변경-사항">BREAKING CHANGE (중대 변경 사항)</h3>
<p>이전 버전과의 하위 호환성이 깨지는 대대적인 API 변경이나 구조적 변경이 있을 때 바닥글 맨 앞에 <code>BREAKING CHANGE:</code>를 붙여 동료 개발자들에게 강력한 경고를 전달한다.</p>
<pre><code class="language-text">feat(api): V2 주문 인터페이스 스펙 변경

BREAKING CHANGE: 기존 /api/v1/order API가 폐기됨. 
이제 모든 클라이언트는 /api/v2/order 헤더 기반 인증 방식을 사용해야 함.

Ref: #204
Closes: #205
</code></pre>
<hr>
<h2 id="💡-현업-실용-팁-한-눈에-보는-올바른-예시">💡 현업 실용 팁: 한 눈에 보는 올바른 예시</h2>
<h3 id="올바른-예시-🟢">올바른 예시 🟢</h3>
<pre><code class="language-text">feat(cart): 장바구니 상품 수량 변경 API 연동

- 수량 변경 시 즉각적으로 총 금액이 리프레시되도록 훅 연결
- 최소 수량 1개 미만으로 내려갈 시 하단 경고 토스트 팝업 추가

Closes: #42
</code></pre>
<pre><code class="language-text">fix: 데이터베이스 연결 타임아웃 예외 처리 예외 구간 확장
</code></pre>
<pre><code class="language-text">style: 코드 포맷팅 및 사용하지 않는 임포트 구문 제거
</code></pre>
<h3 id="잘못된-예시-🔴">잘못된 예시 🔴</h3>
<pre><code class="language-text">Fix: 로그인 고쳤음. (타입 뒤 공백 없음, 마침표 사용, 과거형 서술)
</code></pre>
<pre><code class="language-text">수정 (영어 타입 미사용, 일관성 결여)
</code></pre>
<pre><code class="language-text">feat: 어제 짜다 만 장바구니 기능 마저 구현하고 중간 저장함 (지나치게 감정적이거나 사적인 서술)
</code></pre>
<hr>
<h2 id="🥊-요약">🥊 요약</h2>
<p>컨벤션은 절대적인 정답이 아니라 <strong>팀원 간의 약속</strong>이다. 위의 Conventional Commits 스타일을 기본 뼈대로 삼고, 팀의 성격에 맞게 조금씩 변형하여 활용한다면 누구나 읽기 편한 깔끔한 히스토리를 유지할 수 있다. 처음에는 어색할지라도 <code>type</code>을 먼저 정의하고 커밋하는 습관을 들이면 코드의 분리(Atomic Commit)도 자연스럽게 이루어지게 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Github 디렉토리 연결]]></title>
            <link>https://velog.io/@aiden_lee/Github-%EB%94%94%EB%A0%89%ED%86%A0%EB%A6%AC-%EC%97%B0%EA%B2%B0</link>
            <guid>https://velog.io/@aiden_lee/Github-%EB%94%94%EB%A0%89%ED%86%A0%EB%A6%AC-%EC%97%B0%EA%B2%B0</guid>
            <pubDate>Wed, 24 Jun 2026 05:37:33 GMT</pubDate>
            <description><![CDATA[<p>로컬에 있는 프로젝트를 깃허브(GitHub) 리포지토리에 처음 올리는 과정과 계정 연결 상태를 확인하는 방법입니다. 터미널(Terminal)이나 Git Bash를 열고 아래 순서대로 차근차근 따라 해보세요.</p>
<h3 id="🛠️-1단계-깃허브-계정-연결-상태-확인하기">🛠️ 1단계: 깃허브 계정 연결 상태 확인하기</h3>
<p>먼저 현재 내 PC에 깃허브 계정이 잘 연결되어 있는지, 이메일과 이름은 세팅되어 있는지 확인합니다.</p>
<h4 id="1-전역global-설정-확인">1. 전역(Global) 설정 확인</h4>
<p>터미널에 아래 명령어를 각각 입력하여 등록된 이름과 이메일이 내 깃허브 계정과 일치하는지 확인합니다.</p>
<pre><code>git config --global user.name
git config --global user.email</code></pre><p>아무것도 안 뜨거나 바꾸고 싶다면 아래 명령어로 등록합니다:</p>
<pre><code>git config --global user.name &quot;내_깃허브_닉네임&quot;
git config --global user.email &quot;내_깃허브_이메일&quot;</code></pre><h4 id="2-깃허브-인증-상태-확인-가장-확실">2. 깃허브 인증 상태 확인 (가장 확실)</h4>
<p>실제 깃허브 서버와 권한 인증(SSH 또는 인증서)이 연결되어 있는지 확인하려면 아래 명령어를 입력합니다.</p>
<pre><code>ssh -T git@github.com</code></pre><p>성공 메시지: Hi 닉네임! You&#39;ve successfully authenticated... 라는 문구가 뜨면 계정 연동과 권한이 완벽하게 살아있는 상태입니다.</p>
<p>만약 permission denied가 뜨더라도, 요즘은 최초 git push를 할 때 브라우저 로그인 창(입증 창)이 자동으로 뜨기 때문에 바로 다음 단계로 진행하셔도 괜찮습니다.</p>
<h3 id="🚀-2단계-로컬-프로젝트를-깃허브에-올리기-최초-업로드">🚀 2단계: 로컬 프로젝트를 깃허브에 올리기 (최초 업로드)</h3>
<p>깃허브 웹사이트에서 새로운 원격 저장소(New Repository)를 하나 만드신 후, 생성된 저장소 주소(예: <a href="https://github.com/%EB%8B%89%EB%84%A4%EC%9E%84/%EC%A0%80%EC%9E%A5%EC%86%8C%EC%9D%B4%EB%A6%84.git)%EB%A5%BC">https://github.com/닉네임/저장소이름.git)를</a> 복사해 둡니다.</p>
<p>그 후, 내 로컬 프로젝트 디렉토리 안으로 이동하여 아래 명령어를 차례대로 입력합니다.</p>
<h4 id="1-현재-디렉토리를-git-저장소로-초기화">1. 현재 디렉토리를 Git 저장소로 초기화</h4>
<pre><code>git init</code></pre><p>💡 결과: 폴더 내부에 숨겨진 .git 폴더가 생성되며, 이제부터 Git이 파일 변화를 추적합니다.</p>
<h4 id="2-올릴-파일들을-장바구니에-담기-staging">2. 올릴 파일들을 장바구니에 담기 (Staging)</h4>
<pre><code>git add .</code></pre><p>💡 결과: 폴더 내의 모든 파일(.)을 업로드 대기 상태로 만듭니다. (만약 올리지 말아야 할 빌드 파일이나 설정값이 있다면 .gitignore 파일에 먼저 등록해야 합니다.)</p>
<h4 id="3-첫-번째-확정본버전-만들기">3. 첫 번째 확정본(버전) 만들기</h4>
<pre><code>git commit -m &quot;First Commit&quot;</code></pre><p>💡 결과: 현재 대기 상태인 파일들을 &quot;First Commit&quot;이라는 메시지와 함께 로컬 저장소에 최종 기록합니다.</p>
<h4 id="4-기본-브랙치-이름을-main으로-변경-권장">4. 기본 브랙치 이름을 main으로 변경 (권장)</h4>
<pre><code>git branch -M main</code></pre><p>💡 결과: 최근 깃허브 표준에 맞춰 기본 브랜치명을 master에서 main으로 전환합니다.</p>
<h4 id="5-로컬-저장소와-깃허브-원격-저장소-연결">5. 로컬 저장소와 깃허브 원격 저장소 연결</h4>
<pre><code>git remote add origin 아까_복사한_깃허브_리포지토리_주소</code></pre><p>잘 연결되었는지 확인: git remote -v 를 입력했을 때 내 깃허브 주소가 대괄호(fetch, push)와 함께 나란히 출력되면 성공입니다.</p>
<h4 id="6-깃허브로-최종-업로드">6. 깃허브로 최종 업로드</h4>
<pre><code>git push -u origin main</code></pre><p>💡 결과: 로컬의 main 브랜치 내용물이 깃허브 origin 서버로 날아갑니다. 최초에 -u 옵션을 주면, 다음부터는 길게 쓸 필요 없이 git push 두 글자만 쳐도 알아서 이 주소로 올라갑니다.</p>
<p>⚠️ 혹시 Push 도중 에러가 발생한다면?
로그인 창이 뜨는 경우: 당황하지 마시고 브라우저 연동 로그인 버튼을 누르거나, 깃허브에서 발급받은 Personal Access Token(토큰)을 비밀번호 칸에 입력하시면 통과됩니다.</p>
<p>remote origin already exists 에러: 이미 기존에 다른 주소가 연결되어 있다는 뜻입니다. git remote remove origin 명령어로 한 번 지워주신 뒤, 5번 단계의 주소를 다시 등록하시면 해결됩니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Portswigger]SSRF(2)]]></title>
            <link>https://velog.io/@aiden_lee/PortswiggerSSRF2</link>
            <guid>https://velog.io/@aiden_lee/PortswiggerSSRF2</guid>
            <pubDate>Wed, 13 May 2026 05:35:20 GMT</pubDate>
            <description><![CDATA[<p>SSRF Lecture
<a href="https://portswigger.net/web-security/ssrf">https://portswigger.net/web-security/ssrf</a></p>
<p>SSRF Lab
<a href="https://portswigger.net/web-security/all-labs#server-side-request-forgery-ssrf">https://portswigger.net/web-security/all-labs#server-side-request-forgery-ssrf</a></p>
<h2 id="circumventing-common-ssrf-defenses">Circumventing common SSRF defenses</h2>
<h3 id="ssrf-with-blacklist-based-input-filters">SSRF with blacklist-based input filters</h3>
<p>Some applications block input containing hostnames like 127.0.0.1 and localhost, or sensitive URLs like /admin. In this situation, you can often circumvent the filter using the following techniques:</p>
<p>Use an alternative IP representation of 127.0.0.1, such as 2130706433, 017700000001, or 127.1.</p>
<p>→ 다음과 같은 우회 방식을 시도해볼 수 있다.</p>
<ul>
<li>10진수(Decimal): 2130706433 (127.0.0.1을 10진수 정수로 변환한 값)</li>
<li>8진수(Octal): 017700000001 (각 마디를 8진수로 변환)</li>
<li>16진수(Hexadecimal): 0x7f000001</li>
<li>생략: 127.1 (중간의 0을 생략해도 운영체제는 127.0.0.1로 해석)</li>
</ul>
<p>Register your own domain name that resolves to 127.0.0.1. You can use spoofed.burpcollaborator.net for this purpose.</p>
<p>→ 127.0.0.1이라는 숫자 대신, 이 IP를 가리키는 도메인 이름을 사용</p>
<ul>
<li>원리: 서버 필터는 URL에 &quot;127&quot;이나 &quot;localhost&quot;가 있는지 검사한다. 하지만 공격자가 my-loopback.com이라는 도메인을 사고, 이 도메인의 DNS A 레코드를 127.0.0.1로 설정하면 필터는 일반적인 외부 도메인으로 인식하여 통과시킨다.</li>
<li>활용: Burp Suite의 spoofed.burpcollaborator.net처럼 이미 127.0.0.1로 연결되도록 설정된 공개 도메인을 사용할 수도 있다.</li>
</ul>
<p>Obfuscate blocked strings using URL encoding or case variation.</p>
<p>→ 난독화 (Obfuscation): 필터가 차단하는 특정 단어(예: /admin)를 알아보기 힘들게 비트는 방법</p>
<ul>
<li>URL 인코딩: /admin 대신 %61%64%6d%69%6e으로 전달한다. 서버 내부에서 이를 다시 디코딩하여 처리할 경우 필터를 우회하게 된다.</li>
<li>대소문자 변환: 필터가 대소문자를 구분한다면 /ADMIN 또는 /aDmIn으로 시도하여 차단을 피할 수 있다.</li>
</ul>
<p>Provide a URL that you control, which redirects to the target URL. Try using different redirect codes, as well as different protocols for the target URL. For example, switching from an http: to https: URL during the redirect has been shown to bypass some anti-SSRF filters.</p>
<p>→ 리다이렉트 활용 (HTTP Redirect): 서버가 입력받은 URL을 검사할 때와 실제로 접속할 때의 차이를 이용</p>
<ul>
<li>공격자가 제어하는 외부 서버(<a href="http://attacker.com/move)%EB%A5%BC">http://attacker.com/move)를</a> 입력한다.</li>
<li>서버의 필터는 &quot;외부 주소이므로 안전하다&quot;고 판단하고 요청을 허용한다.</li>
<li>공격자의 서버는 이 요청을 받을 때 302 Redirect 응답과 함께 Location: <a href="http://127.0.0.1/admin">http://127.0.0.1/admin</a> 헤더를 보낸다.</li>
<li>대상 서버는 리다이렉트 지시에 따라 내부의 127.0.0.1/admin으로 다시 접속하게 된다.</li>
<li>이 과정에서 프로토콜을 http:에서 https:로 바꾸거나 그 반대로 바꾸는 방식이 필터의 로직을 꼬이게 만들어 우회에 성공하기도 한다.</li>
</ul>
<h3 id="3-lab-ssrf-with-blacklist-based-input-filter">#3 Lab: SSRF with blacklist-based input filter</h3>
<p>This lab has a stock check feature which fetches data from an internal system.</p>
<p>To solve the lab, change the stock check URL to access the admin interface at <a href="http://localhost/admin">http://localhost/admin</a> and delete the user carlos.</p>
<p>The developer has deployed two weak anti-SSRF defenses that you will need to bypass.</p>
<hr>
<p>사용 툴: Burp Suite</p>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/70c73196-ef45-45e2-ac7c-5cab54723a2b/image.png" alt=""></p>
<p><code>http://127.1/</code> 또는 <code>http://2130706433/</code> 우회 시도 차단</p>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/62723f6a-606e-4062-b16c-d829a7b7c877/image.png" alt=""></p>
<p>URL 인코딩 <code>http://127.1/%61%64%6d%69%6e</code> 차단</p>
<p>이중 URL 인코딩 <code>http://127.1/%2561%2564%256d%2569%256e</code> 우회 시도 성공</p>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/b55ab195-7a37-400e-bb39-5cfc393c0a6a/image.png" alt=""></p>
<p><code>http://127.1/%2561%2564%256d%2569%256e/delete?username=carlos</code>로 삭제 성공</p>
<blockquote>
<p>LAB Solved!</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/0637495a-4d06-4270-ac6d-b78f12659fe7/image.png" alt=""></p>
<hr>
<h3 id="ssrf-with-whitelist-based-input-filters">SSRF with whitelist-based input filters</h3>
<p>Some applications only allow inputs that match, a whitelist of permitted values. The filter may look for a match at the beginning of the input, or contained within in it. You may be able to bypass this filter by exploiting inconsistencies in URL parsing.</p>
<p>The URL specification contains a number of features that are likely to be overlooked when URLs implement ad-hoc parsing and validation using this method:</p>
<p>You can embed credentials in a URL before the hostname, using the <code>@</code> character.
For example: <code>https://expected-host:fakepassword@evil-host</code></p>
<p>You can use the <code>#</code> character to indicate a URL fragment.
For example: <code>https://evil-host#expected-host</code></p>
<p>You can leverage the DNS naming hierarchy to place required input into a fully-qualified DNS name that you control.
For example: <code>https://expected-host.evil-host</code></p>
<p>You can URL-encode characters to confuse the URL-parsing code. This is particularly useful if the code that implements the filter handles URL-encoded characters differently than the code that performs the back-end HTTP request. You can also try double-encoding characters; some servers recursively URL-decode the input they receive, which can lead to further discrepancies.
You can use combinations of these techniques together.</p>
<h3 id="4-lab-ssrf-with-whitelist-based-input-filter">#4 Lab: SSRF with whitelist-based input filter</h3>
<p>This lab has a stock check feature which fetches data from an internal system.</p>
<p>To solve the lab, change the stock check URL to access the admin interface at <a href="http://localhost/admin">http://localhost/admin</a> and delete the user carlos.</p>
<p>The developer has deployed an anti-SSRF defense you will need to bypass.</p>
<hr>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/da63c785-696b-4333-acff-acfd678abfef/image.png" alt=""></p>
<p>view details</p>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/ecda205f-06d1-4d2b-94bc-3bbd776a6b8b/image.png" alt=""></p>
<p>Check stock repeater로 보냄</p>
<p><code>stockApi=http://127.0.0.1/</code>로 변경하여 send
요청 차단됨. host는 <code>stock.weliketoshop.net</code>이어야만 함을 확인</p>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/4c55a483-3b33-4ef9-b943-445a5c73043f/image.png" alt=""></p>
<p><code>stockApi=http://username:password@stock.weliketoshop.net/</code>로 바꾸어 전송 시 500 응답을 받는 것 확인</p>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/6c800c97-6bf1-472d-aead-5fc2f78e6c1b/image.png" alt=""></p>
<p><code>stockApi=http://localhost:80%2523@stock.weliketoshop.net/</code></p>
<ul>
<li><p><code>http://localhost:80</code>: 도달하고자 하는 진짜 목적지</p>
</li>
<li><p><code>@</code>: URL에서 호스트명 앞에 사용자 이름이나 비밀번호를 넣을 때 사용하는 구분자</p>
</li>
<li><p><code>#</code> (%2523): 페이지 내부 위치를 나타내는 프래그먼트 구분자. 시스템은 # 이후의 문자열을 무시</p>
</li>
<li><p><code>stock.weliketoshop.net</code>: 서버가 허용한 Whitelist 목적지</p>
</li>
</ul>
<p><code>stockApi=http://localhost:80%2523@stock.weliketoshop.net/admin/delete?username=carlos</code></p>
<blockquote>
<p>LAB Solved!</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/9e96b878-4df9-41e7-adf1-a06a5d42abf2/image.png" alt=""></p>
<h3 id="bypassing-ssrf-filters-via-open-redirection">Bypassing SSRF filters via open redirection</h3>
<p>Bypassing SSRF filters via open redirection
It is sometimes possible to bypass filter-based defenses by exploiting an open redirection vulnerability.</p>
<p>In the previous example, imagine the user-submitted URL is strictly validated to prevent malicious exploitation of the SSRF behavior. However, the application whose URLs are allowed contains an open redirection vulnerability. Provided the API used to make the back-end HTTP request supports redirections, you can construct a URL that satisfies the filter and results in a redirected request to the desired back-end target.</p>
<p>For example, the application contains an open redirection vulnerability in which the following URL:</p>
<pre><code>/product/nextProduct?currentProductId=6&amp;path=http://evil-user.net</code></pre><p>returns a redirection to:</p>
<pre><code>http://evil-user.net</code></pre><p>You can leverage the open redirection vulnerability to bypass the URL filter, and exploit the SSRF vulnerability as follows:</p>
<pre><code>POST /product/stock HTTP/1.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 118

stockApi=http://weliketoshop.net/product/nextProduct?currentProductId=6&amp;path=http://192.168.0.68/admin</code></pre><p>This SSRF exploit works because the application first validates that the supplied stockAPI URL is on an allowed domain, which it is. The application then requests the supplied URL, which triggers the open redirection. It follows the redirection, and makes a request to the internal URL of the attacker&#39;s choosing.</p>
<h3 id="5-lab-ssrf-with-filter-bypass-via-open-redirection-vulnerability">#5 Lab: SSRF with filter bypass via open redirection vulnerability</h3>
<p>This lab has a stock check feature which fetches data from an internal system.</p>
<p>To solve the lab, change the stock check URL to access the admin interface at <a href="http://192.168.0.12:8080/admin">http://192.168.0.12:8080/admin</a> and delete the user carlos.</p>
<p>The stock checker has been restricted to only access the local application, so you will need to find an open redirect affecting the application first.</p>
<hr>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/c7960940-8134-43d8-adcc-e6fc93da0908/image.png" alt=""></p>
<p>View details -&gt; Check stock</p>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/1d1fcfc6-3506-44fd-a7b6-ee23c1305cdf/image.png" alt=""></p>
<p>Next product 선택 시 결과로 받는 <code>HTTP/2 302 Found</code> 응답 메세지를 통해 리다이렉트 응답임을 확인할 수 있음</p>
<p>이 때의 요청메세지에 <code>GET /product/nextProduct?currentProductId=2&amp;path=/product?productId=3</code>가 포함됨</p>
<p><code>GET /product/nextProduct?currentProductId=2&amp;path=http://192.168.0.12:8080/admin</code>에 대한 응답</p>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/b3899297-a851-43e9-b1df-3c1ad9ff27de/image.png" alt=""></p>
<p>리다이렉트 성공</p>
<p><code>stockApi=/product/nextProduct?path=http://192.168.0.12:8080/admin/delete?username=carlos</code>로 바꾸어 전송</p>
<blockquote>
<p>LAB Solved!</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/d8b16ee0-1cc0-4ea9-abf0-52a759caa007/image.png" alt=""></p>
<h3 id="blind-ssrf-vulnerabilities">Blind SSRF vulnerabilities</h3>
<p>Blind SSRF vulnerabilities occur if you can cause an application to issue a back-end HTTP request to a supplied URL, but the response from the back-end request is not returned in the application&#39;s front-end response.</p>
<p>Blind SSRF is harder to exploit but sometimes leads to full remote code execution on the server or other back-end components.</p>
<h3 id="finding-hidden-attack-surface-for-ssrf-vulnerabilities">Finding hidden attack surface for SSRF vulnerabilities</h3>
<p>Many server-side request forgery vulnerabilities are easy to find, because the application&#39;s normal traffic involves request parameters containing full URLs. Other examples of SSRF are harder to locate.</p>
<h4 id="partial-urls-in-requests">Partial URLs in requests</h4>
<p>Sometimes, an application places only a hostname or part of a URL path into request parameters. The value submitted is then incorporated server-side into a full URL that is requested. If the value is readily recognized as a hostname or URL path, the potential attack surface might be obvious. However, exploitability as full SSRF might be limited because you do not control the entire URL that gets requested.</p>
<h4 id="urls-within-data-formats">URLs within data formats</h4>
<p>Some applications transmit data in formats with a specification that allows the inclusion of URLs that might get requested by the data parser for the format. An obvious example of this is the XML data format, which has been widely used in web applications to transmit structured data from the client to the server. When an application accepts data in XML format and parses it, it might be vulnerable to XXE injection. It might also be vulnerable to SSRF via XXE. We&#39;ll cover this in more detail when we look at XXE injection vulnerabilities.</p>
<h4 id="ssrf-via-the-referer-header">SSRF via the Referer header</h4>
<p>Some applications use server-side analytics software to tracks visitors. This software often logs the Referer header in requests, so it can track incoming links. Often the analytics software visits any third-party URLs that appear in the Referer header. This is typically done to analyze the contents of referring sites, including the anchor text that is used in the incoming links. As a result, the Referer header is often a useful attack surface for SSRF vulnerabilities.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTML/CSS]]></title>
            <link>https://velog.io/@aiden_lee/HTMLCSS</link>
            <guid>https://velog.io/@aiden_lee/HTMLCSS</guid>
            <pubDate>Sun, 10 May 2026 09:17:50 GMT</pubDate>
            <description><![CDATA[<ul>
<li>간단한 문법으로 원하는 대로 표현 가능한 시각화 도구</li>
<li>다른 언어들과 잘 섞이므로 활용이 좋음</li>
<li>간편하게 동작함, 접근성이 좋음</li>
</ul>
<h2 id="html">HTML</h2>
<p>대제목 : tag라고 부름
<Tag>content</Tag>
<Tag2><Tag1>content</Tag1></Tag2></p>
<p>대문자, 소문자 가리지 않음</p>
<p></p> : 가장 기본적인 태그
<h1></h1> : 헤딩1. 큰 헤드라인.


<p>문서의 골격</p>
<p>태그 사용 비율
<img src="https://velog.velcdn.com/images/aiden_lee/post/4b324d3b-da6d-4f17-97c6-8af1522f230b/image.png" alt=""></p>
<HTML> </HTML> : 이 문서는 html 문서이다 라는 표현
없어도 동작에 전혀 지장이 없으나 작성하고 있음

<!DOCTYPE html><p> : 문서의 최상단에 위치. 역시 이 문서는 html 문서이다 라는 표현</p>
<p>묶어서 꼭 작성하고 있음. 알아둘 것.</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;/html&gt;</code></pre><p>html 문서는 두 파트로 나뉨</p>
<p>문서에 대한 정보. 작성자. 문서 제목 등</p>
<head></head> : 내용물에 대한 추가정보

<body></body> : 모든 내용물



<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;&lt;/head&gt;
    &lt;body&gt;&lt;/body&gt;
&lt;/html&gt;</code></pre><p>한글 사용하려는 경우</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;/head&gt;
    &lt;body&gt;&lt;/body&gt;
&lt;/html&gt;</code></pre><title></title> : 상단에 나타나는 글자

<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;meta charset=&quot;UTF-8&quot;&gt;
        &lt;title&gt;이력서&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;&lt;/body&gt;
&lt;/html&gt;</code></pre><p>body 태그 부분</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;meta charset=&quot;UTF-8&quot;&gt;
        &lt;title&gt;이력서 페이지&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;h1&gt;Hi&lt;/h1&gt;
        &lt;p&gt;content&lt;/p&gt;
    &lt;/body&gt;
&lt;/html&gt;</code></pre><p>CSS</p>
<p>footer : 화면 최하단의 안내문</p>
<footer>content</footer>

<p>p : paragraph 문단. 일반적으로 쓰는 모든 글을 p</p>
<p>html 파일과 css 파일 분리
-연동하려면</p>
<p>html의 head 태그에 넣음
body에는 화면에 집적적으로 표현되는 컨텐츠만.
컨텐츠 표현을 위한 부수젖ㄱ인 내용은 head에</p>
<link></link>에. head부의 어느 위치에 있던 상관 없음.

<pre><code>  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;title&gt;김멋사의 이력서&lt;/title&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;codelion.css&quot;&gt;
  &lt;/head&gt;</code></pre><p>css 파일에서 꾸미기.</p>
<p>footer 꾸미기
footer{
    text-align: center; //가운데 정렬
    background-color: black;
}
내용물 순서는 상관 없음.</p>
<p>css의 기본 형태</p>
<p>Tag{
    text-align: center;
    color: orange;
}</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[One-Time Pad(OTP)]]></title>
            <link>https://velog.io/@aiden_lee/One-Time-PadOTP</link>
            <guid>https://velog.io/@aiden_lee/One-Time-PadOTP</guid>
            <pubDate>Sun, 10 May 2026 09:10:52 GMT</pubDate>
            <description><![CDATA[<h2 id="one-time-padotp">One-Time Pad(OTP)</h2>
<p>One-Time Pad(OTP, 일회용 패드)는 암호학 역사에서 정보이론적으로 완전한 보안(Perfect Secrecy) 이 증명된 대표적인 암호 체계다.
현대의 대부분의 암호 기술이 “현실적으로 해독이 매우 어려운 수준”의 안전성을 목표로 하는 반면, OTP는 이론적으로 공격자가 무한한 연산 능력을 가지고 있더라도 평문 정보를 알아낼 수 없다는 점에서 특별한 의미를 가진다.</p>
<p>다만 이러한 완벽성은 매우 까다로운 조건 위에서만 성립하며, 실제 운영 환경에서는 치명적인 제약이 존재한다
이 글에서는 OTP의 기본 원리와 완전 보안이 성립하는 이유, 그리고 실무에서 널리 사용되지 못하는 배경을 정리한다.</p>
<h3 id="one-time-pad의-원리">One-Time Pad의 원리</h3>
<p>OTP는 평문(Plaintext)과 동일한 길이의 무작위 키(Key)를 사용하여 암호화를 수행하는 방식이다.
현대 컴퓨터 환경에서는 일반적으로 비트 단위의 XOR(Exclusive OR) 연산을 사용한다.</p>
<h4 id="xor-연산">XOR 연산</h4>
<p>암호화:</p>
<blockquote>
<p>C=P⊕K</p>
</blockquote>
<ul>
<li>P: 평문(Plaintext)</li>
<li>K: 키(Key)</li>
<li>C: 암호문(Ciphertext)</li>
</ul>
<p>복호화:</p>
<blockquote>
<p>P=C⊕K</p>
</blockquote>
<p>XOR 연산의 특징상 같은 키를 다시 XOR하면 원래 데이터가 복원된다.</p>
<p>예를 들어</p>
<table>
<thead>
<tr>
<th>값</th>
<th>비트</th>
</tr>
</thead>
<tbody><tr>
<td>평문(P)</td>
<td>1010</td>
</tr>
<tr>
<td>키(K)</td>
<td>1100</td>
</tr>
<tr>
<td>암호문(C)</td>
<td>0110</td>
</tr>
</tbody></table>
<p>복호화:</p>
<p>0110⊕1100=1010</p>
<p>즉, 동일한 키만 알고 있다면 암호문으로부터 원래 평문을 정확히 복원할 수 있다.</p>
<p>수학자 클로드 샤논(Claude Shannon)은 특정 조건이 모두 충족될 경우 OTP가 Perfect Secrecy를 만족함을 증명했다.</p>
<p>이는 공격자가 암호문만 가지고는 평문에 대한 어떠한 정보도 얻을 수 없음을 의미한다.</p>
<p>OTP의 완전 보안은 다음 조건 위에서 성립한다.</p>
<ol>
<li>키 길이는 평문과 같아야 한다</li>
</ol>
<p>키가 충분히 길어야 모든 평문 비트를 독립적으로 가릴 수 있다.</p>
<p>즉:</p>
<p>∣K∣≥∣P∣</p>
<p>짧은 키를 반복 사용하면 패턴이 발생하며 보안성이 붕괴한다.</p>
<ol start="2">
<li>키는 진정한 무작위(True Random)여야 한다</li>
</ol>
<p>키는 예측 가능해서는 안 된다.</p>
<p>의사난수(PRNG)가 아닌, 통계적 편향이 없는 진정한 난수여야 하며 공격자가 패턴을 추론할 수 없어야 한다.</p>
<ol start="3">
<li>키는 단 한 번만 사용해야 한다</li>
</ol>
<p>“One-Time”이라는 이름 그대로, 동일한 키를 두 번 이상 사용해서는 안 된다.</p>
<p>키 재사용이 발생하면 암호문 간 관계 분석이 가능해지며 OTP의 핵심 보안성이 무너진다.</p>
<p>(4) 키는 완전히 비밀이어야 한다</p>
<p>키를 알고 있는 주체는 송신자와 수신자뿐이어야 한다.</p>
<p>만약 키가 노출된다면 OTP는 일반 평문과 다를 바 없는 상태가 된다.</p>
<h3 id="한계-및-단점">한계 및 단점</h3>
<p>OTP는 이론적으로 완벽하지만, 현실에서는 매우 비실용적이다.</p>
<p>현대 인터넷 보안에서 TLS, AES, RSA 같은 방식이 사용되는 이유도 여기에 있다.</p>
<ol>
<li>키 분배 문제(Key Distribution Problem)</li>
</ol>
<p>가장 큰 문제는 키 전달이다.</p>
<p>OTP는 평문과 동일한 길이의 키를 미리 안전하게 공유해야 한다.
예를 들어 1GB 데이터를 암호화하려면 1GB 길이의 무작위 키가 필요하다.</p>
<p>즉, 대규모 통신 환경에서는 키를 안전하게 생성·전달·동기화하는 비용이 지나치게 커진다.</p>
<ol start="2">
<li>키 저장 비용</li>
</ol>
<p>대용량 데이터를 처리하는 현대 시스템에서는 데이터 크기만큼의 키를 저장해야 한다.</p>
<p>예를 들어:</p>
<p>1TB 데이터 → 1TB 무작위 키 필요
10TB 데이터 → 10TB 키 필요</p>
<p>이는 운영 효율 측면에서 매우 부담이 크다.</p>
<ol start="3">
<li>키 재사용 시 보안 붕괴</li>
</ol>
<p>동일한 키를 두 번 사용하면 다음 관계가 성립한다.</p>
<blockquote>
<p>C1⊕C2=P1⊕P2</p>
</blockquote>
<p>즉, 키가 상쇄되면서 두 평문 간의 관계 정보가 드러난다.</p>
<p>언어 데이터는 통계적 중복성을 가지므로, 공격자는 이를 기반으로 평문 일부를 추론할 수 있다.</p>
<p>실제 역사적으로도 OTP 키 재사용은 여러 정보기관 암호 체계 붕괴의 원인이 되었다.</p>
<h3 id="사용사례">사용사례</h3>
<ol>
<li>냉전 시대 정보기관</li>
</ol>
<p>냉전 시기 스파이 활동에서는 난수가 기록된 종이 패드(Pad)를 사용했다.</p>
<p>암호화 후 사용한 페이지는 즉시 폐기했으며, 여기서 “One-Time Pad”라는 이름이 유래했다.</p>
<ol start="2">
<li>국가 간 통신</li>
</ol>
<p>미국과 소련 간 핫라인 같은 최고 수준 기밀 통신에서도 OTP 기반 방식이 연구·활용되었다.</p>
<ol start="3">
<li>양자 키 분배(QKD)</li>
</ol>
<p>현대에는 양자역학 기반의 QKD(Quantum Key Distribution)를 이용해 OTP용 키를 안전하게 분배하려는 연구가 진행되고 있다.</p>
<p>OTP 자체는 오래된 기술이지만, 안전한 키 분배 문제를 해결하려는 시도는 여전히 현대 암호학의 중요한 연구 주제다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Portswigger]SSRF(1)]]></title>
            <link>https://velog.io/@aiden_lee/PortswiggerSSRF1</link>
            <guid>https://velog.io/@aiden_lee/PortswiggerSSRF1</guid>
            <pubDate>Fri, 24 Apr 2026 05:29:48 GMT</pubDate>
            <description><![CDATA[<h3 id="ssrf-lecture">SSRF Lecture</h3>
<p><a href="https://portswigger.net/web-security/ssrf">https://portswigger.net/web-security/ssrf</a></p>
<h3 id="ssrf-lab">SSRF Lab</h3>
<p><a href="https://portswigger.net/web-security/all-labs#server-side-request-forgery-ssrf">https://portswigger.net/web-security/all-labs#server-side-request-forgery-ssrf</a></p>
<h2 id="ssrf란">SSRF란?</h2>
<p>Server-side request forgery is a web security vulnerability that allows an attacker to cause the server-side application to make requests to an unintended location.</p>
<h3 id="ssrf-공격">SSRF 공격</h3>
<p>A successful SSRF attack can often result in unauthorized actions or access to data within the organization. This can be in the vulnerable application, or on other back-end systems that the application can communicate with. In some situations, the SSRF vulnerability might allow an attacker to perform arbitrary command execution.</p>
<p>An SSRF exploit that causes connections to external third-party systems might result in malicious onward attacks. These can appear to originate from the organization hosting the vulnerable application.</p>
<h3 id="1-lab-basic-ssrf-against-the-local-server">#1 Lab: Basic SSRF against the local server</h3>
<p>This lab has a stock check feature which fetches data from an internal system.</p>
<p>To solve the lab, change the stock check URL to access the admin interface at <a href="http://localhost/admin">http://localhost/admin</a> and <strong>delete the user carlos.</strong></p>
<hr>
<p>사용 툴: Burp Suite</p>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/bf0d5add-b3af-40ad-b495-0647b5573a84/image.png" alt=""></p>
<p>View details 버튼을 눌러 제품 상세 페이지로 이동한다.</p>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/100efa09-4f84-4bb8-b3cf-c195fd960ca4/image.png" alt=""></p>
<p>제품 상세 하단에 Check stock으로 이동 인터셉트</p>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/1f3db64d-3255-4946-bfac-b3128c30b332/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/d854ed95-355e-40cd-8a72-19bbc1c16405/image.png" alt=""></p>
<p><code>stockApi=http://localhost/admin</code>로 request 메시지 조작하여 forward</p>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/c36ef30e-5b1e-43d9-b9c9-03c81823aa8e/image.png" alt=""></p>
<p>carlos 삭제 시도 실패</p>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/12dc6a52-39e2-4e88-a824-29ee83db38d5/image.png" alt=""></p>
<p>이 때의 요청이 <code>https://0ac8004404c2c3d581f9444800900029.web-security-academy.net/admin/delete?username=carlos</code>임을 확인</p>
<p><code>stockApi=http://localhost/admin/delete?username=carlos</code>로 request 메시지 조작하여 다시 Check stock 인터셉트 및 forward</p>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/b79a905a-9110-4d3f-8e26-ecd98668881d/image.png" alt=""></p>
<blockquote>
<p>LAB Solved!</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/ba587ee8-fd9d-4334-8b2b-b715465bbdc9/image.png" alt=""></p>
<hr>
<h3 id="why-do-applications-behave-in-this-way-and-implicitly-trust-requests-that-come-from-the-local-machine">Why do applications behave in this way, and implicitly trust requests that come from the local machine?</h3>
<ul>
<li><p>The access control check might be implemented in a different component that sits in front of the application server. When a connection is made back to the server, the check is bypassed.</p>
</li>
<li><p>For disaster recovery purposes, the application might allow administrative access without logging in, to any user coming from the local machine. This provides a way for an administrator to recover the system if they lose their credentials. This assumes that only a fully trusted user would come directly from the server.</p>
</li>
<li><p>The administrative interface might listen on a different port number to the main application, and might not be reachable directly by users.</p>
</li>
</ul>
<h3 id="2-lab-basic-ssrf-against-another-back-end-system">#2 Lab: Basic SSRF against another back-end system</h3>
<p>This lab has a stock check feature which fetches data from an internal system.</p>
<p>To solve the lab, use the stock check functionality to scan the internal 192.168.0.X range for an admin interface on port 8080, then use it to delete the user carlos.</p>
<hr>
<p>사용 툴: Burp Suite</p>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/228fe252-023b-453c-963a-55e39dbba27a/image.png" alt=""></p>
<p>View details에서, Check stock을 intruder로 보내 자동화 공격 수행</p>
<p><code>stockApi=http://192.168.0.1:8080/</code>에서 1에 대해 Add
Payload를 다음과 같이 설정</p>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/a6e374c6-e7f7-4717-88b2-1fa9bee3a4a5/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/8a1936b6-96f8-4888-a71b-7b372dd8bfd3/image.png" alt=""></p>
<p>공격 수행</p>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/5cb8e366-ff94-4bc8-b883-da9afef45cb1/image.png" alt=""></p>
<p>200 응답 발견</p>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/249d169d-a1e4-4e93-850e-6967fa0db3a1/image.png" alt=""></p>
<p><code>stockApi=http://192.168.0.215:8080/admin</code> 요청 조작</p>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/70a0e2f7-71f3-4be9-b85c-a5926f3f0c4e/image.png" alt=""></p>
<p><code>stockApi=http://192.168.0.215:8080/admin/delete?username=carlos</code> 요청 조작</p>
<blockquote>
<p>LAB Solved!</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/aiden_lee/post/1748d05a-9916-4b16-9ba4-830612aa3125/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[IP:127.0.0.1, 192.168.0.X]]></title>
            <link>https://velog.io/@aiden_lee/IP</link>
            <guid>https://velog.io/@aiden_lee/IP</guid>
            <pubDate>Fri, 24 Apr 2026 05:28:58 GMT</pubDate>
            <description><![CDATA[<h3 id="127001-loopback-address">127.0.0.1 (Loopback Address)</h3>
<p>127.0.0.1은 네트워크 인터페이스를 거치지 않고 시스템 내부에서 통신하기 위해 예약된 루프백(Loopback) 주소다. 보통 &#39;localhost&#39;라는 호스트명과 매핑되어 사용된다.</p>
<ul>
<li><p>자기 참조성: 외부 네트워크망에 연결되어 있지 않더라도 운영체제 내부의 네트워크 스택을 통해 자기 자신에게 데이터를 전송한다.</p>
</li>
<li><p>용도: 로컬 환경에서의 웹 서버 구동 테스트, 애플리케이션 디버깅, 데이터베이스 연결 확인 등 내부 프로세스 간 통신에 사용된다.</p>
</li>
<li><p>IPv6 대응: IPv6 규격에서는 ::1이 동일한 역할을 수행한다.</p>
</li>
</ul>
<h3 id="1921680x-private-ip-address">192.168.0.X (Private IP Address)</h3>
<p>192.168.0.0/24 대역은 RFC 1918 표준에 의해 정의된 사설 IP(Private IP) 주소 공간이다.</p>
<ul>
<li><p>접근 제한: 사설 IP는 인터넷 라우팅이 불가능하도록 설계되어 있다. 외부 인터넷 환경에서 해당 주소로 직접 패킷을 전송하면 ISP의 라우터에서 이를 폐기한다.</p>
</li>
<li><p>할당 배경: 공인 IP 주소(Public IP)의 고갈 문제를 해결하기 위해 도입되었다. NAT(Network Address Translation) 기술을 사용하여 내부망 기기들은 사설 IP를 쓰고, 외부와 통신할 때만 공인 IP를 공유한다.</p>
</li>
<li><p>SSRF와의 관계: 외부에서 접근할 수 없는 내부망 시스템이라도, 내부망에 걸쳐 있는 웹 서버가 공격자의 조작된 요청을 받아 대신 통신을 수행할 경우 보안 경계가 무력화될 수 있다.</p>
</li>
</ul>
<h3 id="ip-스캔-범위와-주소-예약-규정">IP 스캔 범위와 주소 예약 규정</h3>
<p>네트워크 대역 스캔 시 192.168.0.1부터 시작하는 이유는 특정 주소들이 특수 용도로 예약되어 있기 때문이다.</p>
<p>192.168.0.0 (Network Address): 해당 네트워크 대역 자체를 식별하는 주소다. 실제 호스트(기기)에 할당할 수 없으므로 통신 대상이 될 수 없다.</p>
<p>192.168.0.255 (Broadcast Address): 네트워크 내의 모든 호스트에게 데이터를 동시에 전송하기 위한 브로드캐스트 주소다. 개별 서비스나 관리자 인터페이스가 존재할 수 없다.</p>
<p>192.168.0.1 (Gateway): 관례적으로 네트워크의 첫 번째 가용한 주소는 게이트웨이(라우터)에 할당된다. 따라서 관리자 인터페이스나 설정 페이지가 존재할 가능성이 가장 높은 지점이다.</p>
<p>따라서 유효한 호스트가 존재할 수 있는 범위는 네트워크 주소와 브로드캐스트 주소를 제외한 .1부터 .254까지다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2026 주요정보통신기반시설 기술적 취약점 분석·평가 방법 상세가이드 - 웹 서비스]]></title>
            <link>https://velog.io/@aiden_lee/2026-%EC%A3%BC%EC%9A%94%EC%A0%95%EB%B3%B4%ED%86%B5%EC%8B%A0%EA%B8%B0%EB%B0%98%EC%8B%9C%EC%84%A4-%EA%B8%B0%EC%88%A0%EC%A0%81-%EC%B7%A8%EC%95%BD%EC%A0%90-%EB%B6%84%EC%84%9D%ED%8F%89%EA%B0%80-%EB%B0%A9%EB%B2%95-%EC%83%81%EC%84%B8%EA%B0%80%EC%9D%B4%EB%93%9C-%EC%9B%B9-%EC%84%9C%EB%B9%84%EC%8A%A4</link>
            <guid>https://velog.io/@aiden_lee/2026-%EC%A3%BC%EC%9A%94%EC%A0%95%EB%B3%B4%ED%86%B5%EC%8B%A0%EA%B8%B0%EB%B0%98%EC%8B%9C%EC%84%A4-%EA%B8%B0%EC%88%A0%EC%A0%81-%EC%B7%A8%EC%95%BD%EC%A0%90-%EB%B6%84%EC%84%9D%ED%8F%89%EA%B0%80-%EB%B0%A9%EB%B2%95-%EC%83%81%EC%84%B8%EA%B0%80%EC%9D%B4%EB%93%9C-%EC%9B%B9-%EC%84%9C%EB%B9%84%EC%8A%A4</guid>
            <pubDate>Thu, 05 Mar 2026 01:41:56 GMT</pubDate>
            <description><![CDATA[<ol>
<li>계정 관리</li>
<li>서비스 관리</li>
<li>보안 설정</li>
<li>패치 및 로그 관리</li>
</ol>
<ol>
<li>계정 관리</li>
</ol>
<table>
<thead>
<tr>
<th>점검항목</th>
<th>항목 중요도</th>
<th>항목코드</th>
</tr>
</thead>
<tbody><tr>
<td>Default 관리자 계정명 변경</td>
<td>상</td>
<td>WEB-1</td>
</tr>
<tr>
<td>취약한 비밀번호 사용 제한</td>
<td>상</td>
<td>WEB-2</td>
</tr>
<tr>
<td>비밀번호 파일 권한 관리</td>
<td>상</td>
<td>WEB-3</td>
</tr>
</tbody></table>
<ol start="2">
<li>서비스 관리</li>
</ol>
<table>
<thead>
<tr>
<th>점검항목</th>
<th>항목 중요도</th>
<th>항목코드</th>
</tr>
</thead>
<tbody><tr>
<td>웹 서비스 디렉터리 리스팅 방지 설정</td>
<td>상</td>
<td>WEB-4</td>
</tr>
<tr>
<td>지정하지 않은 CGI/ISAPI 실행 제한</td>
<td>상</td>
<td>WEB-5</td>
</tr>
<tr>
<td>웹 서비스 상위 디렉터리 접근 제한 설정</td>
<td>상</td>
<td>WEB-6</td>
</tr>
<tr>
<td>웹 서비스 경로 내 불필요한 파일 제거</td>
<td>중</td>
<td>WEB-7</td>
</tr>
<tr>
<td>웹 서비스 파일 업로드 및 다운로드 용량 제한</td>
<td>하</td>
<td>WEB-8</td>
</tr>
<tr>
<td>웹 서비스 프로세스 권한 제한</td>
<td>상</td>
<td>WEB-9</td>
</tr>
<tr>
<td>불필요한 프록시 설정 제한</td>
<td>상</td>
<td>WEB-10</td>
</tr>
<tr>
<td>웹 서비스 경로 설정</td>
<td>중</td>
<td>WEB-11</td>
</tr>
<tr>
<td>웹 서비스 링크 사용 금지</td>
<td>중</td>
<td>WEB-12</td>
</tr>
<tr>
<td>웹 서비스 설정 파일 노출 제한</td>
<td>상</td>
<td>WEB-13</td>
</tr>
<tr>
<td>웹 서비스 경로 내 파일의 접근 통제</td>
<td>상</td>
<td>WEB-14</td>
</tr>
<tr>
<td>웹 서비스의 불필요한 스크립트 매핑 제거</td>
<td>상</td>
<td>WEB-15</td>
</tr>
<tr>
<td>웹 서비스 헤더 정보 노출 제한</td>
<td>중</td>
<td>WEB-16</td>
</tr>
<tr>
<td>웹 서비스 가상 디렉토리 삭제</td>
<td>중</td>
<td>WEB-17</td>
</tr>
<tr>
<td>웹 서비스 WebDAV 비활성화</td>
<td>상</td>
<td>WEB-18</td>
</tr>
</tbody></table>
<ol start="3">
<li>보안 설정</li>
</ol>
<table>
<thead>
<tr>
<th>점검항목</th>
<th>항목 중요도</th>
<th>항목코드</th>
</tr>
</thead>
<tbody><tr>
<td>웹 서비스 SSI(Server Side Includes) 사용 제한</td>
<td>중</td>
<td>WEB-19</td>
</tr>
<tr>
<td>SSL/TLS 활성화</td>
<td>상</td>
<td>WEB-20</td>
</tr>
<tr>
<td>HTTP 리디렉션</td>
<td>중</td>
<td>WEB-21</td>
</tr>
<tr>
<td>에러 페이지 관리</td>
<td>하</td>
<td>WEB-22</td>
</tr>
<tr>
<td>LDAP 알고리즘 적절하게 구성</td>
<td>중</td>
<td>WEB-23</td>
</tr>
</tbody></table>
<ol start="4">
<li>패치 및 로그 관리</li>
</ol>
<table>
<thead>
<tr>
<th>점검항목</th>
<th>항목 중요도</th>
<th>항목코드</th>
</tr>
</thead>
<tbody><tr>
<td>별도의 업로드 경로 사용 및 권한 설정</td>
<td>중</td>
<td>WEB-24</td>
</tr>
<tr>
<td>주기적 보안 패치 및 벤더 권고사항 적용</td>
<td>상</td>
<td>WEB-25</td>
</tr>
<tr>
<td>로그 디렉터리 및 파일 권한 설정</td>
<td>중</td>
<td>WEB-26</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[2026 주요정보통신기반시설 기술적 취약점 분석·평가 방법 상세가이드 - Web Application(웹)
]]></title>
            <link>https://velog.io/@aiden_lee/2026-%EC%A3%BC%EC%9A%94%EC%A0%95%EB%B3%B4%ED%86%B5%EC%8B%A0%EA%B8%B0%EB%B0%98%EC%8B%9C%EC%84%A4-%EA%B8%B0%EC%88%A0%EC%A0%81-%EC%B7%A8%EC%95%BD%EC%A0%90-%EB%B6%84%EC%84%9D%ED%8F%89%EA%B0%80-%EB%B0%A9%EB%B2%95-%EC%83%81%EC%84%B8%EA%B0%80%EC%9D%B4%EB%93%9C-Web-Application%EC%9B%B9</link>
            <guid>https://velog.io/@aiden_lee/2026-%EC%A3%BC%EC%9A%94%EC%A0%95%EB%B3%B4%ED%86%B5%EC%8B%A0%EA%B8%B0%EB%B0%98%EC%8B%9C%EC%84%A4-%EA%B8%B0%EC%88%A0%EC%A0%81-%EC%B7%A8%EC%95%BD%EC%A0%90-%EB%B6%84%EC%84%9D%ED%8F%89%EA%B0%80-%EB%B0%A9%EB%B2%95-%EC%83%81%EC%84%B8%EA%B0%80%EC%9D%B4%EB%93%9C-Web-Application%EC%9B%B9</guid>
            <pubDate>Wed, 04 Mar 2026 07:52:44 GMT</pubDate>
            <description><![CDATA[<table>
<thead>
<tr>
<th>점검항목</th>
<th>항목 중요도</th>
<th>항목코드</th>
</tr>
</thead>
<tbody><tr>
<td>코드 인젝션 (CodeInjection)</td>
<td>상</td>
<td>CI</td>
</tr>
<tr>
<td>SQL 인젝션 (SQL Injection)</td>
<td>상</td>
<td>SI</td>
</tr>
<tr>
<td>디렉터리 인덱싱</td>
<td>상</td>
<td>DI</td>
</tr>
<tr>
<td>에러 페이지 적용 미흡</td>
<td>상</td>
<td>EP</td>
</tr>
<tr>
<td>정보 누출</td>
<td>상</td>
<td>IL</td>
</tr>
<tr>
<td>크로스사이트 스크립트</td>
<td>상</td>
<td>XS</td>
</tr>
<tr>
<td>크로스사이트 요청 위조(CSRF)</td>
<td>상</td>
<td>CF</td>
</tr>
<tr>
<td>서버사이드 요청 위조(SSRF)</td>
<td>상</td>
<td>SF</td>
</tr>
<tr>
<td>약한 비밀번호 정책</td>
<td>상</td>
<td>BF</td>
</tr>
<tr>
<td>불충분한 인증 절차</td>
<td>상</td>
<td>IA</td>
</tr>
<tr>
<td>불충분한 권한 검증</td>
<td>상</td>
<td>IN</td>
</tr>
<tr>
<td>취약한 비밀번호 복구 절차</td>
<td>상</td>
<td>PR</td>
</tr>
<tr>
<td>프로세스 검증 누락</td>
<td>상</td>
<td>PV</td>
</tr>
<tr>
<td>악성 파일 업로드</td>
<td>상</td>
<td>FU</td>
</tr>
<tr>
<td>파일 다운로드</td>
<td>상</td>
<td>FD</td>
</tr>
<tr>
<td>불충분한 세션 관리</td>
<td>상</td>
<td>IS</td>
</tr>
<tr>
<td>데이터 평문 전송</td>
<td>상</td>
<td>SN</td>
</tr>
<tr>
<td>쿠키 변조</td>
<td>상</td>
<td>CC</td>
</tr>
<tr>
<td>관리자 페이지 노출</td>
<td>상</td>
<td>AE</td>
</tr>
<tr>
<td>자동화 공격</td>
<td>상</td>
<td>AU</td>
</tr>
<tr>
<td>불필요한 Method 악용</td>
<td>상</td>
<td>WM</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로젝트 가이드라인]]></title>
            <link>https://velog.io/@aiden_lee/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B0%80%EC%9D%B4%EB%93%9C%EB%9D%BC%EC%9D%B8</link>
            <guid>https://velog.io/@aiden_lee/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B0%80%EC%9D%B4%EB%93%9C%EB%9D%BC%EC%9D%B8</guid>
            <pubDate>Thu, 19 Feb 2026 08:27:34 GMT</pubDate>
            <description><![CDATA[<h2 id="프로젝트-진행의-4단계">&lt;프로젝트 진행의 4단계&gt;</h2>
<blockquote>
<p>기획 - 설계 - 구현 - 개선</p>
</blockquote>
<h3 id="1-계획설계">1. 계획/설계</h3>
<p>1) 프로덕트 선정
2) 프로젝트 진행 전 점검사항
3) 프로젝트 진행을 위한 세부사항 도출</p>
<h4 id="1-프로덕트-선정">1) 프로덕트 선정</h4>
<p>서비스화보다는 자신의 기본 역량을 보여줄 수 있는 주제
다양한 기술을 활용해 상용화하기 좋은 주제</p>
<p>면접관들은 기술 사용의 넓이보다 경험의 깊이 주목
-&gt; 새로운 기술 사용 어필보다 개발자 기본 역량 먼저</p>
<h4 id="2-요구사항-도출">2) 요구사항 도출</h4>
<ul>
<li>서비스의 주 이용자 고려</li>
<li>기능(각 기능을 위한 화면, 정보, API)</li>
<li>기술(언어, 프레임워크)</li>
<li>개발 방법론, 아키텍처</li>
<li>고려할 예외사항</li>
</ul>
<p>요구사항 도출 후 정리</p>
<ul>
<li><p>요구사항 명세서: 도출한 요구사항을 표(문서)로 정리</p>
</li>
<li><p>이벤트 스토밍: 다이어그램 등
시스템에서 발생하는 이벤트를 중심으로</p>
</li>
<li><p>user story: 서비스에서 구현되는 기능을 사용자 관점에서 작성</p>
</li>
</ul>
<p>-&gt; 기획단계에서 모든 요구사항을 도출하는 것은 불가능
   핵심 요구사항 먼저 도출하여 추가/수정을 통해 발전.</p>
<ul>
<li>요구사항 관련 테스트 케이스를 함께 작성할 것.</li>
</ul>
<h3 id="2-일정-수립">2. 일정 수립</h3>
<p>WBS(Work Breakdown Structure) 작성</p>
<ul>
<li><p>프로젝트 업무를 카테고리로 구분하고 각 카테고리를 세부 작업으로 나누어 일정 및 진행상황 체크.</p>
</li>
<li><p>중간 마일스톤 필요.
중간 목표, 전환점 -&gt; 달성감 부여의 역할.</p>
</li>
<li><p>업무 분담 - 도메인 단위/화면 단위로 분담</p>
</li>
</ul>
<h3 id="3-구현">3. 구현</h3>
<h4 id="1-컨벤션그라운드-룰-합의">1) 컨벤션/그라운드 룰 합의</h4>
<ol>
<li><p>그라운드 룰
팀원 일정 수립. 공통 집중 개발 시간(코어타임) 약속.
회의시간, 수칙 등</p>
</li>
<li><p>협업 컨벤션
실제 협업 시 필요한 규칙</p>
</li>
</ol>
<ul>
<li>협업 방식: 작업물 병합 방식, 도구 ex) git, github-flow, git flow</li>
<li>커밋 메세지 컨벤션</li>
</ul>
<ol start="3">
<li>코딩 컨벤션
변수, 클래스명 등 기본 코딩 포맷 합의. 코드 자체에 대한 협의. DB 테이블명, API 명세 등 포함</li>
</ol>
<h4 id="2-리소스-프로젝트-관리">2) 리소스-프로젝트 관리</h4>
<ul>
<li><p>스탠드업 회의를 통해 리소스 관리</p>
</li>
<li><blockquote>
<p>그 날 해야하는 일, 전날의 문제 상황 등 공유 및 논의</p>
</blockquote>
</li>
<li><p>프로젝트 진행상황 관리
ex) 노션, github, 스프레드 시트 등</p>
</li>
<li><blockquote>
<p>업무 유형, 현 상태, 업무의 포함-계층관계 명시</p>
</blockquote>
</li>
</ul>
<h4 id="3-테스트-코드-작성">3) 테스트 코드 작성</h4>
<h4 id="4-문서-작성">4) 문서 작성</h4>
<h3 id="4-개선">4. 개선</h3>
<h3 id="5-프로젝트-진행-팁">5. 프로젝트 진행 팁</h3>
<ol>
<li>기획/설계는 빠르고 꼼꼼하게</li>
<li>요구사항 도출 시 질문사항 잘 정리할 것</li>
<li>활발한 의사소통</li>
<li>반복업무 최소화</li>
</ol>
<h3 id="테스트-코드-작성">&lt;테스트 코드 작성&gt;</h3>
<ul>
<li><p>스프링부트 프로그램에서 왜 테스트 코드를 작성하는가</p>
</li>
<li><blockquote>
<p>테스트 코드 작성 시 애플리케이션 실행/종료 필요X</p>
</blockquote>
</li>
<li><blockquote>
<p>비용 절감, 명확한 결과 검증</p>
</blockquote>
</li>
<li><blockquote>
<p>계층별 테스트를 통해 문제 확인 가능</p>
</blockquote>
</li>
<li><p>단위 테스트 - 하나의 모듈을 기준으로 독립적 진행</p>
</li>
<li><p>통합 테스트 - 모듈의 통합화 과정에서 모듈간 호환성 테스트</p>
</li>
</ul>
<p>! 디버깅은 필수. 습관 들일것</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[클라우드 기반의 보안 컨설팅 실무 DAY1]]></title>
            <link>https://velog.io/@aiden_lee/%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C-%EA%B8%B0%EB%B0%98%EC%9D%98-%EB%B3%B4%EC%95%88-%EC%BB%A8%EC%84%A4%ED%8C%85-%EC%8B%A4%EB%AC%B4-DAY1</link>
            <guid>https://velog.io/@aiden_lee/%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C-%EA%B8%B0%EB%B0%98%EC%9D%98-%EB%B3%B4%EC%95%88-%EC%BB%A8%EC%84%A4%ED%8C%85-%EC%8B%A4%EB%AC%B4-DAY1</guid>
            <pubDate>Sat, 31 Jan 2026 07:48:53 GMT</pubDate>
            <description><![CDATA[<p>ISMS(정보보호 관리체계)
ISMS-P(정보보호 및 개인정보보호 관리체계)</p>
<h4 id="os의-eos와-eol">OS의 EOS와 EOL</h4>
<ol>
<li><p>EOS(End of Support) - 지원 종료
제품의 공식적인 지원 종료
새로운 취약점 발견 시 제조사는 조치를 취하지 않음.</p>
</li>
<li><p>EOL(End of Life) - 수명 종료
운영체제의 수명 자체가 종료</p>
</li>
</ol>
<p>보안의 세가지 요소</p>
<p>TCP/IP 7layer</p>
<h4 id="정보보안-및-개인정보보호-전문가">정보보안 및 개인정보보호 전문가</h4>
<ol>
<li>관련 자격</li>
</ol>
<p>1.3 개인정보 영향평가
2. 진로 및 준비 방안</p>
<p>tenant
tenacy</p>
<p><a href="https://cloudsecurityalliance.org/artifacts/top-threats-to-cloud-computing-egregious-eleven-korean-translation">CSA 클라우드 보안 위협</a>
<a href="https://www.tatumsecurity.com/articles/csa-2025-%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C-%EB%B3%B4%EC%95%88-%EC%9C%84%ED%98%91-%EB%A6%AC%ED%8F%AC%ED%8A%B8-%EC%8B%A4%EC%A0%9C-%ED%95%B4%ED%82%B9-%EC%82%AC%EB%A1%80%EB%A1%9C-%EB%B0%B0%EC%9A%B0%EB%8A%94-%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C%EC%9D%98-%EC%9C%84%ED%97%98">참고</a></p>
<h4 id="on-promise-vs-cloud-service">On-Promise vs Cloud Service</h4>
<p>클라우드 컴퓨팅의 책임 공유 모델</p>
<ul>
<li><p>CSP(Cloud Service Provider, 클라우드 서비스 제공업체) - AWS, Azure, GCP, 네이버클라우드 등과 같이 인터넷을 통해 컴퓨팅, 스토리지, 데이터베이스, 네트워크 등 클라우드 기반 리소스를 주문형으로 제공하는 제3자 회사</p>
</li>
<li><p>MSP(Managed Service Provider, 매니지드 서비스 프로바이더) - 기업의 클라우드 환경 및 IT 인프라 설계, 구축, 운영, 유지보수를 전담하여 관리하는 전문 파트너사</p>
</li>
<li><p>CSC (Cloud Service Customer, 클라우드 서비스 고객) - 클라우드 서비스를 이용하는 기업이나 개인.</p>
</li>
<li><p>NFV(Network Functions Virtualization, 네트워크 기능 가상화) - 라우터, 방화벽 등 전용 하드웨어 기반의 네트워크 장비를 가상 머신(VM)이나 컨테이너 등 소프트웨어(VNF)로 구현하여 범용 서버에서 실행하는 기술</p>
</li>
<li><p>VPC(Virtual Private Cloud, 가상 프라이빗 클라우드) - 퍼블릭 클라우드 제공업체(AWS, NCP 등) 내에서 논리적으로 격리된 사용자 전용 가상 네트워크 공간</p>
</li>
<li><p>NACL(Network Access Control List) - 클라우드(AWS 등) 및 네트워크 환경에서 서브넷(Subnet) 단위로 들어오고 나가는 트래픽을 제어하는 스테이트리스(Stateless) 방화벽
<a href="https://guide.ncloud-docs.com/docs/vpc-nacl-vpc">참고</a></p>
</li>
</ul>
<h4 id="정보보호관리체계-인증isms-p-vs-클라우드-보안-인증csap">정보보호관리체계 인증(ISMS-P) VS 클라우드 보안 인증(CSAP)</h4>
]]></description>
        </item>
        <item>
            <title><![CDATA[파이썬 크롤링]]></title>
            <link>https://velog.io/@aiden_lee/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%ED%81%AC%EB%A1%A4%EB%A7%81</link>
            <guid>https://velog.io/@aiden_lee/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%ED%81%AC%EB%A1%A4%EB%A7%81</guid>
            <pubDate>Fri, 30 Jan 2026 05:55:55 GMT</pubDate>
            <description><![CDATA[<h2 id="requests-라이브러리">requests 라이브러리</h2>
<pre><code># 라이브러리 설치
!pip install requests

# 파이썬 코드로 http 요청을 보낼 수 있게 해주는 라이브러리
import requests

# http 요청을 보낼 url
url = &quot;https://www.bookspot.store/&quot;</code></pre><h3 id="get">GET</h3>
<p>requests 라이브러리의 get 메소드를 알아보자.</p>
<pre><code># HTTP GET 요청을 보내는 함수(메소드)
requests.get(url)</code></pre><p>-&gt;이 주소(url)에 접속해서 데이터를 주세요&quot;라고 서버에 요청하는 코드</p>
<p>❗<strong>HTTP GET</strong>
웹에서 데이터를 가져올 때 사용하는 요청 방식</p>
<h4 id="구조-설명">구조 설명</h4>
<pre><code>response = requests.get(url)</code></pre><blockquote>
<p>requests - HTTP 요청을 보내는 라이브러리
get - GET 방식으로 요청
url - 접속할 웹 주소
response - 서버가 보내준 응답 객체</p>
</blockquote>
<p><strong>response 객체의 속성</strong></p>
<pre><code>response.text        # HTML 코드
response.status_code # 상태 코드 (200이면 성공)
response.headers     # 응답 헤더
response.content     # 바이너리 데이터</code></pre><h4 id="내부동작">내부동작</h4>
<pre><code>requests.get(url)</code></pre><ol>
<li>서버에 HTTP GET 요청을 보냄</li>
<li>서버가 그 요청을 처리함</li>
<li>서버가 응답(HTML, JSON 등)을 돌려줌</li>
<li>그 응답을 response 객체로 받음</li>
</ol>
<h4 id="파라미터-전달">파라미터 전달</h4>
<pre><code>requests.get(&quot;https://example.com/search&quot;, params={&quot;q&quot;: &quot;python&quot;})</code></pre><p>실제 요청 주소:</p>
<pre><code>https://example.com/search?q=python</code></pre><h2 id="beautifulsoup-라이브러리">beautifulsoup 라이브러리</h2>
<pre><code># pip install requests beautifulsoup4
!pip install requests beautifulsoup4
</code></pre><h3 id="select">.select()</h3>
<p>BeautifulSoup 라이브러리에서 <strong>CSS 선택자(CSS Selector)</strong>를 사용해 HTML 요소를 선택하는 메서드
유사한 것으로는 .select_one(), .find(), .find_all()이라는 것도 있다.</p>
<p>사용법
형식: soup.select(선택자)<br>지정한 태그, 속성, id 등을 찾아서 리스트로 반환한다.</p>
<p>선택자별 예시</p>
<pre><code>    1) tag
    soup.select(&#39;tag&#39;)  
        예시)
        soup.select(&#39;div&#39;) → 모든 &lt;div&gt; 태그를 찾아서 반환

    2) .class
        예시)
        soup.select(&#39;.title&#39;) → class=&quot;title&quot;인 모든 요소를 찾아서 반환

    3) #id
        예시)
        soup.select(&#39;#header&#39;) → id=&quot;header&quot;인 모든 요소를 찾아서 반환

    4) tag.class
        예시)
        soup.select(&#39;p.warning&#39;) → &lt;p class=&quot;warning&quot;&gt;인 모든 요소를 찾아서 반환

    5) 부모태그 &gt; 자식태그
        예시)
        soup.select(&#39;div &gt; span&#39;) → &lt;div&gt; 안에 &lt;span&gt;인 모든 요소를 찾아서 반환

    6)[attr]
        예시)
        soup.select(&#39;[href]&#39;) → href 속성이 있는 모든 요소 찾아서 반환

    7) [attr=&quot;value&quot;]
        예시)
        soup.select(&#39;[type=&quot;text&quot;]&#39;) → 속성 = 값 인 모든 요소를 찾아서 반환

    8) [attr^=&quot;value&quot;]
        예시)
        soup.select(&#39;[class^=&quot;btn&quot;]&#39;) → 속성 값이 ~로 시작하는 모든 요소를 찾아서 반환

    9) [attr$=&quot;value&quot;]
        예시)
        soup.select(&#39;[href$=&quot;.pdf&quot;]&#39;) → 속성 값이 ~로 끝나는 모든 요소를 찾아서 반환

    10) [attr*=&quot;value&quot;]
        예시)
        soup.select(&#39;[class*=&quot;active&quot;]&#39;) → 속성 값에 ~ 포함하는 모든 요소를 찾아서 반환</code></pre><pre><code># HTML을 분석(파싱)하기 위한 라이브러리
from bs4 import BeautifulSoup

# 파일 경로 지정. 현재 폴더 기준(./)
file_path = &#39;./html_css/historyofpython.html&#39;

# HTML 파일 열기
# &#39;r&#39; → 읽기 모드
# encoding=&#39;utf-8&#39; → 한글 깨짐 방지
# f.read() → 파일 전체 내용을 문자열로 읽음
with open(file_path, &#39;r&#39;, encoding=&#39;utf-8&#39;) as f:
    html_doc = f.read()

# HTML 코드 전체가 들어있는 문자열(str)
type(html_doc)
# html_doc

# head 부분 제거
body = html_doc.split(&#39;&lt;/head&gt;&#39;)[1]
response = BeautifulSoup(body, &#39;html.parser&#39;)
response

# BeautifulSoup을 이용해서 파싱하였으므로 bs4.BeautifulSoup 타입
type(response)</code></pre><h2 id="selenium">selenium</h2>
<h3 id="동적-로딩-문제-javascript-rendering-문제">동적 로딩 문제 (JavaScript Rendering 문제)</h3>
<pre><code>import requests
url = &quot;https://finance.naver.com/sise/lastsearch2.naver&quot;
response = requests.get(url)
response

headers = {
    &quot;User-Agent&quot;: &quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36&quot;,
    &quot;Accept-Language&quot;: &quot;ko-KR,ko;q=0.9,en;q=0.8&quot;,
    &quot;Referer&quot;: &quot;https://finance.naver.com/&quot;
}
response = requests.get(url, headers = headers)
response

soup = BeautifulSoup(response.text, &#39;html.parser&#39;)
soup.select(&#39;tbody &gt; tr&#39;)
soup.select(&#39;tbody&#39;)</code></pre><blockquote>
<p>결과로 빈 list 반환됨</p>
</blockquote>
<ol>
<li>정적 초기 로드 (Static Load)</li>
</ol>
<ul>
<li>requests.get()이 가져오는 부분</li>
<li>서버가 처음 보내주는 기본 HTML 문서</li>
<li>기본적인 HTML 구조, CSS 파일 연결 정보, JavaScript 파일을 불러오라는 <script> 태그 포함</li>
</ul>
<p>👉 실제 데이터가 아닌 데이터를 가져올 준비만 된 상태의 뼈대 HTML</p>
<ol start="2">
<li>동적 렌더링 (Dynamic Rendering)</li>
</ol>
<ul>
<li>브라우저에서만 발생</li>
<li>브라우저가 JavaScript 파일을 실행</li>
<li>JavaScript가 서버에 추가 요청(AJAX, fetch 등)을 보냄</li>
<li>서버에서 받은 데이터를 이용해 HTML을 새로 생성하거나 기존 HTML을 수정</li>
<li>화면에 데이터를 동적으로 삽입</li>
</ul>
<p>👉 결과로 우리가 눈으로 보는 최종 화면 얻음</p>
<p>❗ requests.get()은 JavaScript를 실행하지 않고 HTML 파일만 가져오므로 response.text에는 자바스크립트 실행 전의 초기 HTML만 들어 있음
실제 데이터는 브라우저가 JS를 실행해야 생성되기 때문에
requests만으로는 보이지 않는 것.</p>
<h4 id="동적-로딩-페이지-크롤링">동적 로딩 페이지 크롤링</h4>
<ol>
<li>Selenium 사용(브라우저 자동화, JS 실행 가능)</li>
<li>네트워크 탭에서 실제 데이터 API 직접 호출</li>
<li>개발자 도구에서 AJAX 요청 URL 분석 후 직접 requests로 요청</li>
</ol>
<blockquote>
<p>requests는 자바스크립트를 실행하지 않기 때문에, 동적으로 생성되는 데이터는 가져오지 못한다.</p>
</blockquote>
<pre><code># pip install selenium
!pip install selenium

from selenium import webdriver
url = &quot;https://finance.naver.com/sise/lastsearch2.naver&quot;

driver = webdriver.Chrome() # 또는 Firefox, Edge 등
driver.get(url)

# 페이지 로딩 시간을 충분히 줍니다.
driver.implicitly_wait(10) 

# 자바스크립트가 실행된 최종 HTML 소스를 가져옵니다.
final_html = driver.page_source

# BeautifulSoup으로 분석
soup = BeautifulSoup(final_html, &#39;html.parser&#39;)

driver.quit()

# soup.select(&#39;tbody &gt; tr&#39;)
# BeautifulSoup을 이용해 테이블의 tbody 안에 있는 모든 tr(행)을 선택한다.
# 즉, 네이버 금융 인기검색 종목 표의 각 종목 행을 가져오는 코드.</code></pre><h3 id="실습">실습</h3>
<p><a href="https://finance.naver.com/sise/lastsearch2.naver">https://finance.naver.com/sise/lastsearch2.naver</a> 페이지에서</p>
<ol>
<li>데이터를 크롤링하고</li>
<li>데이터프레임으로 만든 후</li>
<li>excel 파일로 저장하시오</li>
</ol>
<pre><code># pip install selenium
!pip install selenium

# pip install requests beautifulsoup4
!pip install requests beautifulsoup4

from selenium import webdriver
from bs4 import BeautifulSoup
import pandas as pd

url = &quot;https://finance.naver.com/sise/lastsearch2.naver&quot;

driver = webdriver.Chrome() # 또는 Firefox, Edge 등
driver.get(url)

# 페이지 로딩 시간을 충분히 줍니다.
driver.implicitly_wait(10) 

# 자바스크립트가 실행된 최종 HTML 소스를 가져옵니다.
final_html = driver.page_source

# BeautifulSoup으로 분석
soup = BeautifulSoup(final_html, &#39;html.parser&#39;)

# BeautifulSoup을 이용해 테이블의 tbody 안에 있는 모든 tr(행)을 선택한다.
# soup.select(&#39;tbody &gt; tr&#39;)

header_row = soup.select_one(&#39;tr.type1&#39;)
headers = [th.get_text(strip=True) for th in header_row.find_all(&#39;th&#39;)]

print(headers)

# driver.quit()

rows = soup.select(&#39;tbody &gt; tr&#39;)

data = []

for row in rows:
    no_cell = row.select_one(&#39;td.no&#39;)

    if no_cell:   # 순위가 있는 행만
        cols = row.find_all(&#39;td&#39;)
        row_data = [col.get_text(strip=True) for col in cols]
        data.append(row_data)

print(data[:2])

# 자동으로 행 인덱스 생성
df = pd.DataFrame(data, columns=headers)

print(df)

df.to_excel(&quot;naver_finance.xlsx&quot;, index=False, engine=&#39;openpyxl&#39;)</code></pre><p>엑셀 저장할 때 행 제거하고 싶은 경우</p>
<p>```
df.to_excel("naver.xlsx", index=False)
``</p>
]]></description>
        </item>
    </channel>
</rss>