<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>hyun_ji.log</title>
        <link>https://velog.io/</link>
        <description>헤맨만큼 내 땅이다</description>
        <lastBuildDate>Sat, 18 Apr 2026 11:35:51 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>hyun_ji.log</title>
            <url>https://velog.velcdn.com/images/hyun_ji/profile/18e807d3-e73d-4e90-abf2-4822f211bc69/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. hyun_ji.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/hyun_ji" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[정보처리기사(정처기) 공부 기록 ]]></title>
            <link>https://velog.io/@hyun_ji/%EC%A0%95%EB%B3%B4%EC%B2%98%EB%A6%AC%EA%B8%B0%EC%82%AC%EC%A0%95%EC%B2%98%EA%B8%B0-%EA%B3%B5%EB%B6%80-%EA%B8%B0%EB%A1%9D</link>
            <guid>https://velog.io/@hyun_ji/%EC%A0%95%EB%B3%B4%EC%B2%98%EB%A6%AC%EA%B8%B0%EC%82%AC%EC%A0%95%EC%B2%98%EA%B8%B0-%EA%B3%B5%EB%B6%80-%EA%B8%B0%EB%A1%9D</guid>
            <pubDate>Sat, 18 Apr 2026 11:35:51 GMT</pubDate>
            <description><![CDATA[<h1 id="프로그래밍-언어">&lt;&lt;프로그래밍 언어&gt;&gt;</h1>
<h2 id="java">[JAVA]</h2>
<ol>
<li><p>부모 클래스에서 부모,자식 둘 다 있는 이름의 클래스를 호출할땐, 해당 객체의 실제 객체 타입의 함수를 호출하는거. this.draw();가 되어 현재 this객체가 가리키는 걸 가야 하기 때문</p>
</li>
<li><p>생성자로 객체 생성할때, 부모 클래스의 생성자 먼저 호출되고, 그 다음 자식객체 생성자가 호출 됨.</p>
</li>
<li><p>자식 생성자에서 super(10)같이 매개변수 있는 생성자를 호출하면, 부모 생성자에 그게 무조건 구현되어 잇어야함(이건 알지?)</p>
</li>
<li><p>그리고 자식 생성자에서 따로 super호출 안 하면 super()가 맨 처음 자동으로 붙는 것도</p>
</li>
<li><p>추상클래스를 상속받으면 추상 메서드는 반드시 구현해야함</p>
</li>
</ol>
<p>** 비트 연산자**
 순서 : &lt;&lt; 먼저, | &amp;^같은것들은 그 뒤에</p>
<p> &amp;    AND    둘 다 1이면 1
 |    OR    하나라도 1이면 1
 ^    XOR    다르면 1
 ~    NOT    0↔1 서로 바꾸기
 &#39;&lt;&lt;&#39;    왼쪽 이동    ×2ⁿ
 &#39;&gt;&gt;&#39;    오른쪽 이동    ÷2ⁿ (양수 기준)</p>
<p> &amp;&amp;    논리 AND    둘 다 참이어야 참
 !    논리 NOT    참↔거짓 반전</p>
<h3 id="자바-오버라이딩">자바 오버라이딩</h3>
<p>Parent ref = new Child();(업캐스팅)에서 자식객체의 새로 정의된 메서드는 사용 불가능해지는 이유(어차피 실행 시점에 메서드 뭐 쓸지 결정한다면 왜?)
-&gt;프로그램 실행 전 컴파일러가 먼저 돌면서 &quot;변수의 타입(Parent)&quot;를 검사하는데 이게 어긋나기 때문. 이후 JVM이 변수에 담긴 알맹이를 확인하고, 알맹이가 자식 객체이면 오버라이딩된 자식의 메서드가 실행되는것.
쉽게 말하면 컴파일러는 대참사만 막느라 아주 얕게 훑어서 알맹이까지 검사할 시간따윈 없음. </p>
<ul>
<li>static 붙은 메소드는 무조건 변수의 타입 클래스(엄마꺼)를 따라가면 됨 -&gt; 원인</li>
<li>일반 메소드는 실행 시점(Runtime)에 객체를 확인하는 &#39;동적 바인딩&#39;을 사용하지만, static 메소드는 컴파일 시점(Compile-time)에 호출 대상이 결정</li>
<li>메소드가 저장되는 위치 자체가 일반 메소드와 완전히 다름
  일반 메소드: 객체마다 생성되는 <strong>가상 메소드 테이블(VTable)</strong>을 통해 실제 구현체 주소를 찾아가.
  static 메소드: 객체와 무관하게 메소드 영역(Method Area) 내의 해당 클래스 정보에 귀속돼.</li>
<li>변수는 오버라이딩 안 되는거 알지? 이름 같아도 걍 아예 다른 애임</li>
</ul>
<p>자식 객체를 만들면 부모 생성자가 먼저 실행되고 그 다음 자식 생성자가 실행됩니다. (super()가 생략되어 있다는 걸 잊지 마세요!) 
-&gt; 항상 부모가 먼저, 자식이 나중!
이유는 자식 객체 안에는 부모의 부분도 포함되어 있어서(상속받았으니까), 부모부터 제대로 만들어져야 자식도 완성될 수 있기 때문이야.</p>
<h2 id="c">[C]</h2>
<ul>
<li><p>→ 값(내용)을 가져온다.
&amp; → 주소를 가져온다.</p>
</li>
<li><p>구조체 포인터 연산에서, 구조체 배열 인덱스 [0]을 가리키던 포인터에 +1을 하면 [1]번 구조체를 가리킴. p는 &quot;구조체를 가리키는 포인터&quot;이기 때문에 p+1은 구조체 전체의 크기만큼 이동해 &quot;다음 구조체&quot;를 가리키기 때문</p>
</li>
</ul>
<h2 id="python">[PYTHON]</h2>
<ul>
<li>슬라이싱 [::2]는 무조건 첫 번째 놈부터 시작해서 2칸씩 뛰는거임 ex.0,2,4...</li>
<li>print함수 end속성은 기본이 &quot;\n&quot;이고 뭐가 붙으면 그거 붙이고 줄바꿈은 안 함</li>
<li>소수 뒤에 Ea 는 앞에서 x 10^a, E-a는 x 1/10^a</li>
<li>%.3f 4번째 자리에서 반올림해서 소수 세 번째 자리까지만 </li>
</ul>
<h2 id="몫-나머지-계산-기호">몫, 나머지 계산 기호</h2>
<table>
<thead>
<tr>
<th>기능</th>
<th>Python</th>
<th>Java</th>
<th>C</th>
</tr>
</thead>
<tbody><tr>
<td><strong>몫(정수 나눗셈)</strong></td>
<td><code>//</code></td>
<td><code>/</code> (정수끼리)</td>
<td><code>/</code> (정수끼리)</td>
</tr>
<tr>
<td><strong>나머지</strong></td>
<td><code>%</code></td>
<td><code>%</code></td>
<td><code>%</code></td>
</tr>
<tr>
<td><strong>소수점 포함 나눗셈</strong></td>
<td><code>/</code></td>
<td><code>/</code> (실수 포함)</td>
<td><code>/</code> (실수 포함)</td>
</tr>
</tbody></table>
<pre><code class="language-python"># python

7 // 3   # 2
7 % 3    # 1
7 / 3    # 2.333333...</code></pre>
<pre><code class="language-c"># java, c

7 / 3        // 2
7 % 3        // 1
7.0 / 3      // 2.333333...</code></pre>
<h2 id="8진수-16진수">8진수, 16진수</h2>
<p>8,16진수 -&gt; 10진수 변환
<img src="https://velog.velcdn.com/images/hyun_ji/post/3ce9bb07-e947-4740-9f85-2e438147aee7/image.png" alt="">
<img src="https://velog.velcdn.com/images/hyun_ji/post/bf29b13f-61f7-47a0-9927-74080b29c5e3/image.png" alt=""></p>
<p>10진수 -&gt; 8,16진수 
<img src="https://velog.velcdn.com/images/hyun_ji/post/8761c3a6-23fe-4a60-9720-531b2c00daae/image.png" alt="">
<img src="https://velog.velcdn.com/images/hyun_ji/post/44e6c5a6-44f1-4647-ba53-a94fec0dffee/image.png" alt=""></p>
<h1 id="운영체제">&lt;&lt;운영체제&gt;&gt;</h1>
<h2 id="운영체제-스케줄링-알고리즘">운영체제 스케줄링 알고리즘</h2>
<p>비선점 스케줄링 - 뺏기 불가능
선점 스케줄링 - 뺏기 가능</p>
<p>Round Robin - 모든 프로세스 조금씩 동일하게 실행. 끝나고 남으면 맨 뒤로 감.
FCFS(First Come First Served) - 걍 줄서기 큐. 당연히 비선점
SJF(Shortest Jop First) - 선점+비선점 스케줄링. 짧은 놈부터 처리. 그리서 긴 놈은 영원히 기다리기만 하는 기아 상태 발생 가능
SRT(Shortest Remaining Time) - 선점 스케줄링. 짧은 놈부터 처리. 더 짧은 놈 들어오면 걔로 바뀜
HRN(Highest Response Ratio Next) - 대기 중인 프로세스 중 현재 Response Ratio((대기시간 + 서비스시간) / 서비스시간)가 가장 높은 것을 선택. 대기시간이 긴 프로세스일 경우 우선순위가 높아져 불평등 해소. 비선점
PS(Priority Scheduling) - 우선순위로 스케줄링 순서 정함. 선점, 비선점 둘 다 가능</p>
<h2 id="운영체제-페이지-교체-알고리즘">운영체제 페이지 교체 알고리즘</h2>
<p>FIFO - 걍 줄서기 처리
LRU - 가장 오랫동안 사용되지 않은 페이지 교체
LFU - 참조 횟수가 가장 적은 페이지 제거
MFU - 참조 횟수가 가장 많은 페이지 교체
NUR - 최근에 사용하지 않은 페이지 교체
OPT - 앞으로 가장 오래 사용 안 할 페이지 제거(불가능)</p>
<h1 id="네트워크">&lt;&lt;네트워크&gt;&gt;</h1>
<h2 id="osi-7계층">OSI 7계층</h2>
<p>국제표준화기구(International Standard Organization, ISO)에서 발표한 네트워크 표준 모델로써 네트워크 통신의 각 과정을 계층별로 개념화한 모델이기에 각 계층의 구간별로 데이터의 움직임을 알수 있고, 장치간의 데이터 통신을 설명하는데 유용하게 활용됨 </p>
<p>[1계층] 물리 계층 : 전선, 전파, 광섬유, 동축케이블, 도피관, PSTN, DSU, CSU, Modem</p>
<p>[2계층] 데이터 링크 계층 : Ethernet, ToKen Ring, PPP, HDLC, ISDN, ATM</p>
<p>[3계층] 네트워크 계층 : IP, <strong>ARP(IP → MAC주소), RARP(MAC → IP)</strong>, ICMP, IGMP, 라우팅 프로토콜</p>
<p>[4계층] 전송 계층 : <strong>TCP, UDP</strong>, RTP, SCTP, SPX</p>
<p>[5계층] 세션 계층 : TLS, SSH, ISO 8327 / CCITTX225, RPC, NetBIOS</p>
<p>[6계층] 표현 계층 : JPEG, MPEG, XDR, ASN1, SMB, AFP</p>
<p>[7계층] 응용 계층 : <strong>HTTP, SMTP(메일), FTP(파일 전송), Telnet(원격 접속, 암호화 X), SSH(원격 접속, 암호화 O),</strong>, SNMP, NFS, RTSP</p>
<p><strong>WSDL</strong>(Web Services Description Language의 약자)은 웹 서비스 기술언어 또는 기술된 정의 파일의 총칭으로 XML로 기술됩니다. 웹 서비스의 구체적 내용이 기술되어 있으며 서비스 제공 장소, 서비스 메시지 포맷, 프로토콜 등이 기술됩니다.</p>
<h2 id="라우팅-프로토콜">라우팅 프로토콜</h2>
<p>라우터들이 최적의 경로를 선택하기 위해 서로 정보를 교환하는 규칙(걍 길 찾는 방식)</p>
<p>RIP    - 홉(Hop) 수가 적은 경로 선택. 거리 벡터, 홉 수 사용(최대 15홉)
OSPF(Open Shortest Path First) - 링크 상태를 이용해 최단 경로 계산. 빠르고 대규모 네트워크에 적합
BGP - 인터넷처럼 서로 다른 조직(AS) 간 경로 설정. 인터넷에서 사용하는 외부 라우팅 프로토콜</p>
<h2 id="icmp">ICMP</h2>
<p>TCP/IP에서 오류가 발생하면 ICMP 메세지를 보내서 오류가 발생했음을 알린다. </p>
<p>ICMP(Internet Control Message Protocol)
: IP 패킷을 처리할 때 발생하는 문제를 알려주는 프로토콜로, 메세지 형식은 8바이트의 헤더와 가변 길이의 데이터 영역으로 분리되어 있다.</p>
<h2 id="ipc">IPC</h2>
<p>IPC(Inter-Process Communication) = 프로세스 간 통신. 즉, 서로 다른 프로세스가 데이터를 주고받는 방법입니다.</p>
<p><strong>Shared Memory</strong></p>
<ul>
<li>가장 빠름</li>
<li>메모리를 공유</li>
<li>동기화 필요</li>
</ul>
<p><strong>Pipe</strong></p>
<ul>
<li>단방향 통신</li>
<li>부모-자식 프로세스 간 사용</li>
</ul>
<p><strong>Message Queue</strong></p>
<ul>
<li>메시지를 큐에 저장</li>
<li>비동기 통신 가능</li>
</ul>
<p><strong>Semaphore</strong></p>
<ul>
<li>데이터 전달이 목적이 아니라 동기화가 목적</li>
<li>공유 자원에 동시에 접근하지 못하게 함</li>
</ul>
<p><strong>Socket</strong></p>
<ul>
<li>네트워크 통신</li>
<li>서로 다른 컴퓨터 간 통신 가능</li>
</ul>
<h2 id="병행제어-기법">병행제어 기법</h2>
<p>다중 프로그램이 이점을 활용하여 동시에 여러 개의 트랜잭션을 병행수행할 때, 동시에 실행되는 트랜잭션들이 데이터베이스의 일관성을 파괴하지 않도록 트랜잭션 간의 상호작용을 제어하는 것</p>
<table>
<thead>
<tr>
<th>병행제어 기법</th>
<th>특징</th>
</tr>
</thead>
<tbody><tr>
<td><strong>로킹(Locking)</strong></td>
<td>데이터를 잠가 다른 트랜잭션의 접근을 제어</td>
</tr>
<tr>
<td><strong>타임스탬프(Timestamp Ordering)</strong></td>
<td>트랜잭션에 시간을 부여하여 순서대로 처리</td>
</tr>
<tr>
<td><strong>낙관적 검증(Optimistic Validation)</strong></td>
<td>충돌이 거의 없다고 가정하고 마지막에 검증</td>
</tr>
<tr>
<td><strong>다중 버전(MVCC, Multi-Version Concurrency Control)</strong></td>
<td>여러 버전의 데이터를 유지하여 읽기 성능 향상</td>
</tr>
</tbody></table>
<h2 id="무선-네트워크">무선 네트워크</h2>
<p>무선 네트워크 구성 방식 :</p>
<p>애드혹 네트워크 - 센서망, 재난망에 사용되며 구성이 빠르고 모든 단말이 동등한 자격으로 망을 구성</p>
<p>Infrastructure 네트워크 - 이동통신망,Wi-Fi에 사용되며 중앙집중형 방식</p>
<p>메쉬 네트워크 - 무선백본망에 사용되며 신뢰성이 우수하고, 계층적인 구조를 가지고 있음</p>
<h1 id="db">&lt;&lt; DB &gt;&gt;</h1>
<h2 id="관계-릴레이션-용어">관계 릴레이션 용어</h2>
<ul>
<li>&quot;속성(attribute)&quot;의 개수는 &quot;차수(degree)&quot;</li>
<li>&quot;튜플/레코드&quot;의 개수는 &quot;카디널리티&quot;</li>
<li>&quot;도메인&quot;은 속성들이 가질 수 있는 값들의 집합
<img src="https://velog.velcdn.com/images/hyun_ji/post/e68b87f8-9d94-4ea0-9158-f68170aab4fd/image.png" alt=""></li>
</ul>
<h2 id="db-설계-절차">DB 설계 절차</h2>
<p>요구조건 분석 → 개념적 설계 → 논리적 설계 → 물리적 설계 → 구현</p>
<p>개념적 설계 (ERD 만드는 단계) - 현실 세계를 데이터 모델로 표현합니다.
논리적 설계 - ERD를 테이블(릴레이션)로 변환합니다.(여기까지가 설계.)
물리적 설계 - 실제로 DBMS에 저장할 방법을 결정합니다. (<strong>저장 구조로 변환</strong> -&gt; 키워드)</p>
<h2 id="관계-대수">관계 대수</h2>
<p>순수관계연산자는 셀렉트(σ) / 프로젝트(π) / 조인(⋈) / 디비전(÷) 이 있습니다.</p>
<table>
<thead>
<tr>
<th>연산</th>
<th>기호</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td>선택</td>
<td>σ (시그마)</td>
<td>행 선택 (<code>WHERE</code>)</td>
</tr>
<tr>
<td>사영</td>
<td>π (파이)</td>
<td>열 선택 (<code>SELECT 컬럼</code>)</td>
</tr>
<tr>
<td>합집합</td>
<td>∪</td>
<td>UNION</td>
</tr>
<tr>
<td>차집합</td>
<td>−</td>
<td>A에는 있고 B에는 없는 것</td>
</tr>
<tr>
<td>카티션 곱</td>
<td>×</td>
<td>모든 조합 생성</td>
</tr>
<tr>
<td>이름 변경</td>
<td>ρ (로)</td>
<td>테이블이나 속성 이름 변경</td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th>연산</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td>조인(Join, ⋈)</td>
<td>두 테이블 연결</td>
</tr>
<tr>
<td>나누기(÷)</td>
<td>&quot;모든&quot; 조건을 만족하는 데이터 찾기</td>
</tr>
</tbody></table>
<h2 id="무결성-제약조건">무결성 제약조건</h2>
<ol>
<li>개체 무결성<ul>
<li>기본키 속성 중 NULL/중복값 없음</li>
</ul>
</li>
<li>참조 무결성<ul>
<li>외래키 값 NULL 아니고 참조중인 기본키 값이랑 동일</li>
</ul>
</li>
<li>도메인 무결성<ul>
<li>각 속성들의 값이 도메인 안의 범위에 속한 값</li>
</ul>
</li>
</ol>
<h2 id="트랜잭션의-특징">트랜잭션의 특징</h2>
<p>원자성 : 트랜잭션이 데이터베이스에 모두 반영되던가, 전혀 반영되지 않아야 한다는 점</p>
<p>일관성 : 트랜잭션의 작업 처리 결과가 항상 일관성이 있어야 한다는 점</p>
<p>독립성 : 둘 이상의 트랜잭션이 동시에 실행되고 있을 경우, 어떤 하나의 트랜잭션이라도 다른 트랜잭션의 연산에 끼어들 수 없다는 점</p>
<p>지속성 : 트랜잭션이 성공적으로 완료되었을 경우, 결과는 영구적으로 반영되어야 한다는 점</p>
<h2 id="정규화">정규화</h2>
<p>정규형 순서
1NF → 2NF → 3NF → BCNF → 4NF → 5NF</p>
<table>
<thead>
<tr>
<th>정규형</th>
<th>제거하는 것</th>
</tr>
</thead>
<tbody><tr>
<td><strong>1NF</strong></td>
<td>원자값. 컬럼 하나에 값은 하나만 있어야 함.</td>
</tr>
<tr>
<td><strong>2NF</strong></td>
<td><strong>부분 함수 종속 제거</strong>. 기본키가 여러 개인 경우(복합키), 기본키 일부에만 종속되는 컬럼 제거. 즉, 키본키 하나로만 모든 컬럼이 결정되어야함.</td>
</tr>
<tr>
<td><strong>3NF</strong></td>
<td><strong>이행 함수 종속 제거</strong>. 복합키가 아니더라도, 다른 컬럼에 종속되는 컬럼도 없어야함</td>
</tr>
<tr>
<td><strong>BCNF</strong></td>
<td><strong>결정자가 후보키가 아니면 분해</strong></td>
</tr>
<tr>
<td><strong>4NF</strong></td>
<td>다치 종속</td>
</tr>
<tr>
<td><strong>5NF</strong></td>
<td>조인 종속</td>
</tr>
</tbody></table>
<h2 id="and-or">AND, OR</h2>
<p>AND가 OR보다 우선 순위 높음. 
a AND b OR c이면 -&gt; (a AND b) OR c </p>
<h2 id="like">LIKE</h2>
<table>
<thead>
<tr>
<th>조건</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td><code>LIKE &#39;A%&#39;</code></td>
<td>A로 시작</td>
</tr>
<tr>
<td><code>LIKE &#39;%A&#39;</code></td>
<td>A로 끝남</td>
</tr>
<tr>
<td><code>LIKE &#39;%A%&#39;</code></td>
<td>A 포함</td>
</tr>
<tr>
<td><code>LIKE &#39;_A%&#39;</code></td>
<td>두 번째 글자가 A</td>
</tr>
<tr>
<td><code>LIKE &#39;A__&#39;</code></td>
<td>A로 시작하고 총 3글자</td>
</tr>
</tbody></table>
<h2 id="sql">SQL</h2>
<p><strong>① DDL (Data Definition Language)</strong>
테이블 구조를 만드는 명령어</p>
<p>CREATE : 테이블 생성
ALTER : 테이블 구조 변경
DROP : 테이블 삭제
TRUNCATE : 테이블의 모든 데이터 삭제(구조는 유지)</p>
<p><strong>② DML (Data Manipulation Language)</strong>
테이블 안의 데이터를 다루는 명령어</p>
<p>SELECT : 조회
INSERT : 추가
UPDATE : 수정
DELETE : 삭제</p>
<p><strong>③ DCL (Data Control Language)</strong>
사용자 권한을 관리하는 명령어</p>
<p>GRANT : 권한 부여
REVOKE : 권한 회수</p>
<p><strong>④ TCL (Transaction Control Language)</strong>
트랜잭션을 관리하는 명령어</p>
<p>COMMIT : 변경사항 저장
ROLLBACK : 이전 상태로 되돌리기
SAVEPOINT : 중간 저장 지점 생성</p>
<h1 id="프로그램-설계">&lt;&lt;프로그램 설계&gt;&gt;</h1>
<h2 id="디자인-패턴">디자인 패턴</h2>
<ol>
<li>생성 패턴<ul>
<li>Singleton -&gt; 전역 변수 하나만</li>
<li>Factory Method -&gt; 서브 클래스에 생성 위임(책임 분리)</li>
<li>Abstract Factory -&gt; 객체들을 묶어서 생성</li>
<li>Builder -&gt; 객체를 단계적으로 생성</li>
</ul>
</li>
<li>구조 패턴(객체의 구조/연결 방식)<ul>
<li>Adapter -&gt; 호환 안 되는 인터페이스 연결(변환기)</li>
<li>Bridge -&gt; 추상과 구현을 분리(구조를 분리 설계)</li>
<li>Decorator -&gt; 객체 기능을 동적으로 추가(기능 확장)</li>
<li>Proxy -&gt; 대리 객체로 접근 제어</li>
<li>Facade -&gt; 복잡한 시스템을 단순 인터페이스로 제공</li>
</ul>
</li>
<li>행위 패턴(객체 간 상호작용)<ul>
<li>Observer -&gt; 상태 변화 시 자동 알림(1:N, publish-subscribe)</li>
<li>strategy -&gt; 알고리즘을 변경 가능하게(다양한 결제방식)</li>
<li>command -&gt; 요청 자체를 객체로 캡슐화(버튼)</li>
<li>State -&gt; 상태에 따라 행동 &quot;&quot;자동&quot;&quot; 변경(strategy는 개발자가 선택)</li>
<li>Template Method -&gt; 알고리즘 구조 고정하고 틀 제공</li>
</ul>
</li>
</ol>
<p>“객체를 하나만 생성” → Singleton
“상태 변화 시 통지” → Observer
“알고리즘 교체 가능” → Strategy
“호환 안되는 인터페이스 연결” → Adapter
“객체 생성 책임을 서브클래스에 위임” → Factory Method
“요청을 객체로 캡슐화” → Command</p>
<h2 id="결합도">결합도</h2>
<p>결합도와 품질(낮은 품질에서 좋은 품질 순) :
내용결합도 &gt; 공통 결합도 &gt; 외부 결합도 &gt; 제어 결합도 &gt; 스탬프 결합도 &gt; 자료 결합도</p>
<p><strong>내용 결합도</strong> : 다른 모듈 내부에 있는 변수나 기능을 다른 모듈에서 사용하는 경우의 결합도</p>
<p>*<em>공통 결합도 *</em>: 파라미터가 아닌 모듈 밖에 선언된 전역 변수를 참조하고 전역 변수를 갱신하는 식으로 상호작용하는 경우의 결합도</p>
<p>*<em>외부 결합도 *</em>: 어떤 모듈에서 반환한 값을 다른 모듈에서 참조해서 사용하는 경우</p>
<p>*<em>제어 결합도 *</em>: 단순히 처리를 해야할 대상인 값만 전달되는게 아니라 어떻게 처리를 해야 한다는 제어요소가 전달</p>
<p><strong>스탬프 결합도</strong>: 모듈 간의 인터페이스로 배열이나 객체, 구조 등이 전달되는 경우의 결합도</p>
<p>*<em>자료 결합도 *</em>: 모듈간의 인터페이스 전달되는 파라미터를 통해서만 모듈간의 상호 작용이 일어나는 경우</p>
<h2 id="응집도">응집도</h2>
<p>응집도 순서 (낮은것부터 높은 순서)
우연적 &gt; 논리적 &gt; 시간적 &gt; 절차적 &gt; 교환적 &gt; 순차적 &gt; 기능적</p>
<p>*<em>우연적 응집도 *</em>: 모듈 내부의 각 구성요소들이 연관이 없을 경우</p>
<p>*<em>논리적 응집도 *</em>: 유사한 성격을 갖거나 특정 형태로 분류되는 처리 요소들이 한 모듈에서 처리되는 경우</p>
<p><strong>시간적 응집도</strong> : 연관된 기능이라기보단 특정 시간에 처리되어야 하는 활동들을 한 모듈에서 처리할 경우</p>
<p>*<em>절차적 응집도 *</em>: 입출력 간 연관성은 없으나, 순서에 따라 수행되는 것</p>
<p><strong>교환적 응집도</strong> : 동일한 입력과 출력 사용</p>
<p><strong>순차적 응집도</strong> : 모듈 내에서 한 활동으로부터 나온 출력값을 다른 활동이 사용할 경우</p>
<p>*<em>기능적 응집도 *</em>: 하나의 기능에 모두 기어하고 밀접하게 연관되어 있는 것 (그룹화)</p>
<h2 id="solid-원칙">SOLID 원칙</h2>
<table>
<thead>
<tr>
<th>약자</th>
<th>원칙</th>
<th>한 줄 암기</th>
</tr>
</thead>
<tbody><tr>
<td><strong>S</strong></td>
<td><strong>SRP (단일 책임 원칙)</strong></td>
<td>클래스는 <strong>하나의 책임만</strong> 가져야 한다.</td>
</tr>
<tr>
<td><strong>O</strong></td>
<td><strong>OCP (개방-폐쇄 원칙)</strong></td>
<td><strong>확장에는 열려 있고, 수정에는 닫혀 있어야 한다.</strong></td>
</tr>
<tr>
<td><strong>L</strong></td>
<td><strong>LSP (리스코프 치환 원칙)</strong></td>
<td><strong>자식은 부모를 대신할 수 있어야 한다.</strong></td>
</tr>
<tr>
<td><strong>I</strong></td>
<td><strong>ISP (인터페이스 분리 원칙)</strong></td>
<td><strong>불필요한 인터페이스를 강요하지 않는다.</strong></td>
</tr>
<tr>
<td><strong>D</strong></td>
<td><strong>DIP (의존 역전 원칙)</strong></td>
<td><strong>구체 클래스가 아니라 추상화(인터페이스)에 의존한다.</strong></td>
</tr>
</tbody></table>
<h2 id="uml-다이어그램-종류">UML 다이어그램 종류</h2>
<p><strong>구조 다이어그램 (Static, 정적) - 클객컴배패복</strong>
클래스(Class) : 클래스와 관계
객체(Object) : 객체 간 관계
컴포넌트(Component) : 소프트웨어 구성 요소
배치(Deployment) : 하드웨어에 소프트웨어 배치
패키지(Package) : 패키지 구조
복합체(Composite Structure) : 클래스 내부 구조</p>
<p>👉 암기: 클객컴배패복</p>
<p><strong>행위 다이어그램 (Dynamic, 동적) - 유시활상통상타</strong>
유스케이스(Use Case) : 사용자 기능
시퀀스(Sequence) : 시간 순서대로 메시지
활동(Activity) : 업무 흐름
상태(State) : 객체 상태 변화
통신(Communication) : 객체 간 메시지
상호작용 개요(Interaction Overview)
타이밍(Timing) : 시간 변화</p>
<p>👉 암기: 유시활상통상타</p>
<h2 id="uml클래스의-관계">UML클래스의 관계</h2>
<ol>
<li>일반화  ⇾ 상속(동물 - 호랑이)</li>
<li>실체화(의존) ---⇾ 인터페이스 구현</li>
<li>의존 ---&gt;하나의 클래스가 다른 클래스 참조(메서드 호출)</li>
<li>연관 ㅡ A가 B를 사용한다(학생 - 수업)</li>
</ol>
<h1 id="테스트">&lt;&lt;테스트&gt;&gt;</h1>
<h2 id="블랙박스-테스트">블랙박스 테스트</h2>
<p>프로그램 내부 코드는 보지 않고, 입력과 출력만 확인하는 테스트. 즉, 사용자 입장에서 테스트하는 것</p>
<p>특징</p>
<ul>
<li>코드를 몰라도 가능</li>
<li>기능이 제대로 동작하는지 확인</li>
<li>요구사항을 만족하는지 검사</li>
</ul>
<p><strong>동치 분할 검사 :</strong></p>
<p>입력 자료에 초점을 맞춰 테스트 케이스를 만들고 검사하는 방법 (= 동등 분할 기법)</p>
<p><strong>경계값 분석 :</strong></p>
<p>입력 자료에만 치중한 동치 분할 기법을 보완한 기법으로, 입력 조건의 중간값보다 경계값에서 오류가 발생될 확률이 높다는 점을 이용하여 입력 조건의 경계값을 테스트 케이스로 선정하여 검사하는 기법</p>
<p> *<em>원인-효과 그래프 검사 :
*</em>
입력 데이터 간의 관계와 출력에 영향을 미치는 상황을 체계적으로 분석한 다음 효용성이 높은 테스트 케이스를 선정하여 검사하는 기법</p>
<p> <strong>오류 예측 검사 :</strong></p>
<p>과거의 경험이나 확인자의 감각으로 테스트하는 기법으로, 다른 블랙박스 테스트 기법으로는 찾아낼 수 없는 오류를 찾아내는 일력의 보충적 검사 기법 (= 데이터 확인 검사)</p>
<p> <strong>비교 검사 :</strong></p>
<p>여러 버전의 프로그램에 동일한 테스트 자료를 제공하여 동일한 결과가 출력되는지 테스트하는 기법</p>
<h2 id="화이트박스-테스트">화이트박스 테스트</h2>
<p>프로그램 내부 코드까지 보면서 테스트하는 것</p>
<p>특징</p>
<ul>
<li>코드를 알아야 함</li>
<li>개발자가 주로 수행</li>
<li>코드의 모든 경로를 검사</li>
</ul>
<p> <strong>기초 경로 검사 :</strong></p>
<p>대표적 화이트박스 테스트 기법. 설계자가 논리적 복잡성을 측정할 수 있게 해주는 테스트 기법
-&gt; 걍 모든 길을 가보는거(소스의 모든 문장을 실행해 보는것)</p>
<p> <strong>제어 구조 검사 :</strong></p>
<p>조건 검사 : 모듈 내에 있는 논리적 조건을 테스트하는 기법
루프 검사 : 프로그램의 반복 구조에 초점을 맞춰 테스트하는 설계 기법(단순 루프, 중첩 루프)
데이터 흐름 검사 :프로그램의 변수 정의와 변수 사용 위치에 초점을 맞춰 테스트하는 기법</p>
<h2 id="소프트웨어-통합-테스트">소프트웨어 통합 테스트</h2>
<ol>
<li>빅뱅(Big Bang) 접근법 : 모든 구성 요소들을 한꺼번에 통합된 후에 테스트 수행</li>
</ol>
<ol start="2">
<li>점증적(incremental) 접근법 : 논리적으로 연관된 두개 또는 그 이상의 모듈을 조합시켜서 수행</li>
</ol>
<p>(1) 하향식(Top Down) 접근법 :</p>
<p>소프트웨어 시스템의 제어 흐름이 위에서 아래로 발생</p>
<p>(테스팅을 위해 스텁의 도움이 필요)</p>
<p>(2) 상향식(Bottom Up) 접근법 : </p>
<p>모든 모듈들이 테스트될 때까지 더 낮은 레벨에 있는 각 모듈은 더 위에 있는 모듈과 함께 테스트 수행</p>
<p>(테스팅을 위한 드라이버의 도움이 필요)</p>
<p>(3) 샌드위치 접근법 : 하향식과 상향식 접근법의 조합</p>
<h2 id="v-모델">V-모델</h2>
<p>개발 단계마다 대응되는 테스트 단계를 연결한 개발 모델</p>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/72b8b1d7-54d0-42a5-aebc-ab018bf09b29/image.png" alt=""></p>
<h1 id="보안">&lt;&lt;보안&gt;&gt;</h1>
<h2 id="네트워크-공격-유형">네트워크 공격 유형</h2>
<p><strong>서비스 거부 공격 (DOS - Denial of Service) :</strong></p>
<p>표적이 되는 서비스 자원을 고갈시킬 목적으로 다수의 공격자 또는 시스템에서 대량의 데이터를 한 곳의 서버에 집중적으로 전송함으로써 표적이 되는 서버의 정상적인 기능을 방해하는 공격 방법으로, DDoS와의 차이점은 Attacker가 직접 공격을 수행</p>
<p><strong>분산 서비스 거부 (DDoS -Distributed Denial of Service) :</strong></p>
<p>Attacker가 여러 대의 컴퓨터를 감염시켜 동시에 한 타깃 시스템을 집중적으로 공격하는 방법으로써, 짧은 시간 안에 서버를 마비시킬 수 있으며 Dos보다 치명적이고 Dos와의 차이점은 실질적인 Attacker가 아닌 Attacker가 감염시킨 좀비 PC가 공격을 수행한다는 점</p>
<p>**
Ping of Death :**</p>
<p>ping 명령을 전송할 때 패킷의 크기를 인터넷 프로토콜 허용 범위 이상으로 전송하여 네트워크를 마비시키는 공격 방법</p>
<p>*<em>Smurfing :
*</em>IP또는 ICMP의 특성을 악용하여 엄청난 양의 데이터를 한 사이트에 집중적으로 보내냄으로써 네트워크 불능 상태로 만드는 공격</p>
<p><strong>SYN Flooding :</strong> 공격자가 가상의 클라이언트로 위장하여 3-way-handshake 과정을 의도적으로 중단 시킴으로써 공격 대상자인 서버가 대기 상태에 놓여 정상적으로 서비스를 수행하지 못하게 하는 공격 방법</p>
<p><strong>TearDrop :</strong>
데이터의 송.수신 과정에서 패킷의 크기가 여러 개로 분할되어 전송할때 분할 순서를 변경시켜 수신측에서 패킷을 재조립할 때 오류로 인한 과부하를 발생시킴으로써 시스템이 다운되도록 하는 공격 방법Smurfing :IP또는 ICMP의 특성을 악용하여 엄청난 양의 데이터를 한 사이트에 집중적으로 보내냄으로써 네트워크 불능 상태로 만드는 공격 </p>
<p><strong>Land Attack :</strong>
공격자가 패킷의 출발지 주소나 포트를 임의로 변경해 출발지와 목적지 주소를 동일하게 함으로써 공격 대상 컴퓨터의 실행속도를 느리게 하거나 동작을 마비시켜 서비스 거부 상태에 빠지도록 하는 공격 방법</p>
<h2 id="암호화-알고리즘">암호화 알고리즘</h2>
<table>
<thead>
<tr>
<th>알고리즘</th>
<th>핵심</th>
</tr>
</thead>
<tbody><tr>
<td><strong>DES</strong></td>
<td>대칭키, 56비트</td>
</tr>
<tr>
<td><strong>AES</strong></td>
<td>대칭키, DES보다 안전 ⭐⭐⭐⭐⭐</td>
</tr>
<tr>
<td><strong>RSA</strong></td>
<td>공개키(비대칭키) ⭐⭐⭐⭐⭐</td>
</tr>
<tr>
<td><strong>SEED</strong></td>
<td>국내 표준 대칭키 ⭐⭐⭐</td>
</tr>
<tr>
<td><strong>ARIA</strong></td>
<td>국내 개발 대칭키 ⭐⭐</td>
</tr>
</tbody></table>
<h2 id="접근제어-방식">접근제어 방식</h2>
<table>
<thead>
<tr>
<th>방식</th>
<th>특징</th>
<th>암기 키워드</th>
</tr>
</thead>
<tbody><tr>
<td><strong>DAC</strong> (Discretionary Access Control, 임의 접근제어)</td>
<td><strong>소유자</strong>가 권한을 부여</td>
<td><strong>소유자</strong></td>
</tr>
<tr>
<td><strong>MAC</strong> (Mandatory Access Control, 강제 접근제어)</td>
<td><strong>관리자</strong>가 보안등급에 따라 권한 부여</td>
<td><strong>등급</strong></td>
</tr>
<tr>
<td><strong>RBAC</strong> (Role-Based Access Control, 역할 기반 접근제어)</td>
<td><strong>역할(Role)</strong>에 따라 권한 부여</td>
<td><strong>역할</strong></td>
</tr>
<tr>
<td><strong>ABAC</strong> (Attribute-Based Access Control, 속성 기반 접근제어)</td>
<td>사용자·자원·환경 등의 <strong>속성(Attribute)</strong>을 기준으로 권한 결정</td>
<td><strong>속성</strong></td>
</tr>
</tbody></table>
<p>임의적 접근 통제(DAC - Discretionary Access Control) : 시스템 객체에 대한 접근을 사용자나 그룹의 신분을 기준으로 제한하는 방법</p>
<p>강제적 접근 통제(MAC - Mandatory Access Control) : 미리 정해진 정책과 보안 등급에 의거하여 주체에게 허용된 접근 권한과 객체에게 부여된 허용 등급을 비교하여 접근을 통제하는 모델</p>
<p>역할기반 접근 통제(RBAC - Role Based Access Control) : DAC와 MAC의 단점을 보완한 방식으로 멀티 프로그래밍 환경에서의 보완 처리를 위해 제안되었으며 사용자에게 할당된 역할에 기반하여 접근을 통제하며 중앙에서 집중적으로 관리</p>
<h2 id="aes">AES</h2>
<p>AES(Advanced Encryption Standard)는 미국 국립 표준 기술연구소 (NIST), DES를 대체하며, 128 비트 블록 크기와 128,192,256비트 키 크기의 대칭 키 암호화 방식</p>
<p> 대칭키 알고리즘 :</p>
<p>동일한 키를 사용하여 암호화와 복호화를 하는 것을 의미하며 가장많이 사용하는 알고리즘이 AES 입니다.</p>
<h2 id="isms">ISMS</h2>
<p>ISMS = Information Security Management System(정보보호 관리체계)
조직의 정보자산을 안전하게 보호하기 위한 관리체계를 수립·운영하는 제도</p>
<p>기밀성(Confidentiality)
허가된 사람만 접근 가능
무결성(Integrity)
정보가 임의로 변경되지 않음
가용성(Availability)
필요할 때 사용 가능</p>
<p>👉 CIA (Confidentiality, Integrity, Availability)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Lambda Cold Start 해결]]></title>
            <link>https://velog.io/@hyun_ji/Lambda-Cold-Start-%ED%95%B4%EA%B2%B0</link>
            <guid>https://velog.io/@hyun_ji/Lambda-Cold-Start-%ED%95%B4%EA%B2%B0</guid>
            <pubDate>Fri, 09 Jan 2026 08:20:43 GMT</pubDate>
            <description><![CDATA[<p>이전 글에서 람다 함수 첫 실행시간이 말도 안되게 길게 나와서(30초 이상) 여러 개선 사항들을 시도해보았다. 람다 함수는 실행시간에 비례하여 요금이 측정되기 때문에 사용자 이용 측면과 비용 측면 둘 다에서 좋지 않다. </p>
<h2 id="aws-lambda-power-tuning">AWS Lambda Power Tuning</h2>
<p>우선 AWS Lambda는 메모리 설정값에 비례해서 CPU 자원을 할당하기 때문에 메모리 크기를 올리면 CPU 성능도 함께 좋아져 실행 속도가 향상된다. </p>
<p>AWS Lambda Power Tuning이라는 툴을 사용하면 시간과 비용적으로 최적의 메모리 값을 찾아낼 수 있다. 
AWS Lambda Power Tuning은 여러 가지 메모리 값을 적용해서 해당 람다 함수를 실행한다음,  Lambda 함수의 비용 및 성능을 최적화하는 데 도움을 준다.
<a href="https://serverlessrepo.aws.amazon.com/applications/arn:aws:serverlessrepo:us-east-1:451282441545:applications~aws-lambda-power-tuning">AWS Lambda Power Tuning 공식 문서</a></p>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/01c6b3ef-7135-4be7-bbd8-01ad6a1a2fd6/image.png" alt=""></p>
<p>위 결과 그래프를 보면 <strong>메모리 크기를 증가시켜도 성능 곡선(분홍색 선)은 유의미한 변화가 없으며, 비용 곡선(파란색 선)만 완만하게 상승하고 있음을</strong> 확인할 수 있다. </p>
<p>이는 단순히 메모리(CPU 자원)를 늘리는 것만으로는 응답 속도를 개선할 수 없다는 의미이다. 즉, 현재 발생하고 있는 심각한 응답 지연의 근본 원인이 <strong>연산 자원 부족</strong>이 아닌 다른 곳에 있음을 알 수 있다.</p>
<h2 id="lambda의-cold-start">Lambda의 Cold Start</h2>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/fcf27fa5-30a7-4ce5-a8ec-fff691847da3/image.png" alt="">
람다함수 코드는 위 그림과 같은 순서로 실행되는데, 저 과정들 중 코드를 실행하기 위한 환경을 준비하는 파란 부분을 <a href="https://docs.aws.amazon.com/ko_kr/lambda/latest/dg/lambda-runtime-environment.html#cold-start-latency">콜드 스타트(Cold Start)</a>라고 한다. 
이와 반대로 이미 실행 환경이 갖추어져 있다면 Cold Start부분은 스킵하고 바로 노란색 부분부터 실행이 가능한데 이것을 Warm Start라고 한다. 
람다는 실행시간에 비례하여 요금이 청구되므로 Warm Start가 훨씬 실행시간도 단축되고 비용도 적게 든다. </p>
<h2 id="로그-분석">로그 분석</h2>
<h3 id="cold-start">Cold Start</h3>
<p>우선 실행 환경이 하나도 갖추어지지 않은채 처음부터 준비해야 하는 Cold Start를 해보자. </p>
<pre><code>Duration: 31949.48 ms    Billed Duration: 31950 ms    Memory Size: 2048 MB    Max Memory Used: 645 MB    </code></pre><p>첫 번째 실행은 CloudWatch를 이용해 모델 분석 API요청 로그를 보면 무려 <strong>31.9초</strong>나 걸린것을 볼 수 있다. </p>
<p>로그를 자세히 살펴보니, 실제 모델 추론시간보다 모델을 메모리에 적재하는 초기화 시간이 훨씬 더 오래 걸리고 있었다. </p>
<h3 id="warm-start">Warm Start</h3>
<blockquote>
<p>Lambda는 효율성을 위해 일정 시간 동안 실행 환경(컨테이너)을 파괴하지 않고 유지한다. 이 환경이 사라지기 전에 함수를 다시 호출하면 이미 메모리에 모델이 적재된 상태인 Warm Start가 가능하다.</p>
</blockquote>
<pre><code>Duration: 214.72 ms    Billed Duration: 215 ms    Memory Size: 2048 MB    Max Memory Used: 653 MB    </code></pre><p>이에 반해 두 번째 실행 (Warm Start)에서는 첫 번째와는 다른 사진을 분석시켰음에도 <strong>0.2초</strong>로 실행시간이 확 줄어든 것을 볼 수 있다. </p>
<p>이는 YOLO모델 로드 코드를 전역 변수 영역에 배치하였기 때문이다. Warm start로 재실행시, 이미 메모리에 적재된 모델 객체를 즉시 참조하게 되면서 무거운 초기화 과정이 생략되었기 때문이다. </p>
<h2 id="provisioned-concurrency를-통한-warm-start">Provisioned Concurrency를 통한 Warm Start</h2>
<p><strong>그러나 Warm start는 영구적이지 않다.</strong>
람다는 일정시간동안 요청이 없으면 리소스를 회수하기 위해 실행 환경(컨테이너)를 삭제한다. 이 경우 다시 Cold Start가 발생한다는 한계가 있다. 이용자가 많은 서비스라면 요청이 잦아 대부분의 요청에서 Warm Start상태를 유지할 수 있겠지만, 개인 프로젝트처럼 사용자가 거의 없는 경우에는 요청이 잦아봐야 몇시간에 한 번일테니 매번 Cold Start가 되어 사진 하나를 분석하는데 30초를 넘게 기다려야 할 것이다. </p>
<p>이를 해결하기 위한 방법으로는 2가지가 있는데</p>
<p>첫번째는 <strong><a href="https://docs.aws.amazon.com/ko_kr/lambda/latest/dg/lambda-runtime-environment.html#cold-starts-pc">프로비저닝 동시성</a>(Provisioned Concurrency)</strong>이다. 
이 기능은 EC2처럼 계-속 람다 함수를 켜두는 것이다. 
프로비저닝 동시성 기능을 활성화하면, 설정된 개수만큼의 인스턴스를 초기화하여 대기시켜 둔다. 그러면 Cold Start가 발생하지 않고 Warm Start로 즉시 실행되게 된다.</p>
<p>프로비저닝 동시성 기능을 활성시킨후, 람다가 컨테이너를 삭제할만한 시간동안 호출을 하지 않았다. </p>
<p>다시 앱에서 람다 함수를 호출해보았더니 훨씬 단축된 실행시간에, 로그에 Init duration(초기화 시간)도 찍히지 않는 것을 볼 수 있었다. 즉, 성공적으로 Warm Start가 적용된 것이다. </p>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/4f99c3cd-8c93-4cf5-9742-979ae5533373/image.png" alt=""></p>
<p><em>[실행시간이 0.27초로 단축되었다]</em></p>
<p>다만 이는 EC2처럼 계속 실행시켜 두는 것이나 다름없다보니 비용이 꽤 있는편이다. 그래서 좀 더 심화된 최적화 방법으로, 프로비저닝 오토스케일을 사용하기도 한다고 한다.</p>
<p>프로비저닝 오토스케일링은 트래픽이 높은 시간대에 맞춰 프로비저닝을 오토스케일링하여 가격을 최적화하는 방법이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS  VPC, EC2]]></title>
            <link>https://velog.io/@hyun_ji/AWS-EC2-VPC</link>
            <guid>https://velog.io/@hyun_ji/AWS-EC2-VPC</guid>
            <pubDate>Wed, 31 Dec 2025 08:10:32 GMT</pubDate>
            <description><![CDATA[<p>AWS를 이용해서 서버를 띄우기 전에 기본적인 지식을 공부하였다.
강의는 <a href="https://www.youtube.com/@AWSClassroom">https://www.youtube.com/@AWSClassroom</a> 에서 들었는데 여타 유료 강의 못지않게 설명을 잘 해주신다.</p>
<h2 id="기본-지식">기본 지식</h2>
<blockquote>
<p><strong>[들어가기전 기본 용어 정리]</strong>
<code>EC2</code> :  AWS에서 제공하는 가상 서버 대여 <strong>서비스</strong>의 이름이다. 
<code>인스턴스</code> : <code>EC2</code>서비스를 사용해서 만들어진 <strong>실제 가상 서버</strong> 한 대 단위를 이르는 말</p>
</blockquote>
<p>스케일링 - 자원을 늘리는 것
vertical scale 수직적 스케일링 - 하나의 자원 자체의 &#39;성능&#39;을 높이는것 
horizontal scale  - 자원의 규모, 수를 늘리는것
    - 대게 이걸 씀 (탄력적으로 쪼개기 쉬움)
    - 대신 아키텍처 복잡</p>
<p>-&gt; Auto Scaling - horizontal scaling 자동화
    EC2 Auto Scaling - 정확한 수의 EC2인스턴스를 보유하도록 보장
        - 최소, 최대 숫자 인스턴스 설정 가능
        - CPU 부하에 따라 인스턴스 크기 조정
        - 가용 영역에 인스턴스가 골고루 분산되도록 인스턴스 분배</p>
<p>NAT(Network Address Translation) - 사설 IP를 공용 IP로 변환해주는 방법
    - Dynamic NAT - NAT Pool에서 현재 사용가능한 공용 IP하나 가져다 씀
    - Static NAT - 이미 사설과 공용 IP가 1:1 매칭이 되어 있음(수가 같음)
    - PAT(Port Address Translation) 
        - 가장 많이 쓰이는 형식
        - 하나의 공용 IP가 사설망 전체를 대표함
        - 포트별로 PC를 다르게 구분함 
        - 포트 포워딩(Port Forwarding) - PAT 원리를 수동으로 설정하여 특정 포트에 특정 기기를 연결시키는 설정작업
        - 보통 공유기가 이 역할을 함</p>
<p>CIDR는 그냥 IP주소 표기법. 클래스 단위로 빌리면 너무 비싸기 때문에 정교하게 수를 조정가능한 CIDR표기법이 생긴것. 할당받은 공용 IP 개수에 x 사설 IP주소로 해서 인터넷 연결 가능한 기기를 많이 만들 수 있음.</p>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/3cc79de1-c838-4417-8d27-4915d34e5395/image.png" alt="">
출처 : <a href="https://www.youtube.com/watch?v=IJgAIbxxJCE">https://www.youtube.com/watch?v=IJgAIbxxJCE</a></p>
<p>보통 EC2 인스턴스로 서버를 띄운다고 한다면, 그 기본 틀의 구조는 위와 비슷하다. 각 구성요소를 하나씩 알아보자</p>
<h2 id="vpcvirtual-private-cloud">VPC(Virtual Private Cloud)</h2>
<ul>
<li>외부와 격리된 가상 네트워크 단위. 쉽게 말해 사설망</li>
<li>원칙상으론 퍼블릭 인터넷에서 접근불가,(하지만 인터넷 게이트웨이를 통해 접근 가능) AWS내부에서도 외부를 통해 접근해야함</li>
<li>다양한 서브넷 구성 가능     </li>
<li>인터넷에 노출되지 않는 EC2구성 등 보안 설정 가능</li>
<li>하나의 VPC는 하나의 리전밖에 커버하지 못하기 때문에 리전과 리전 사이를 하나의 VPC가 커버할 수는 없음. 리전의 수만큼 VPC가 존재해야 함.</li>
<li><strong>EC2를 띄우려면 필수적으로 VPC도 설계해야함</strong> 애초에 계정을 만들면 디폴트 VPC가 자동으로 생성된다.</li>
</ul>
<blockquote>
<p><strong>왜 VPC가 필요할까?</strong>
서버를 냅다 아무 환경에나 띡하고 띄울 순 없다. 일단 네트워크 환경을 만들어주어야 한다. VPC는 쉽게 말하면 거대한 공용 클라우드(AWS)에서 나만의 울타리를 치는거다. 이 울타리가 없으면 밖에서 내 사설 IP주소만 알게 되면 그냥 바로 침범이 가능해진다. 그러나 VPC라는 울타리를 치면 내가 허락한 문(인터넷 게이트웨이)과 규칙(보안 그룹)등을 통과해야지만 들어올수 있기 때문에 보안을 위해서는 필수적이다. 또한 사설 IP대역(내 정원의 크기)도 마음대로 지정이 가능해진다. 그리고 내가 소유한 땅이나 다름없으니 거기 안에서 역할에 따른 구역(서브넷)을 자유롭게 나눌수도 있다. 그렇기 때문에 VPC는 EC2뿐만 아니라 RDS, Lambda등 대부분의 서비스에서도 필수적인 요소이다. </p>
</blockquote>
<h3 id="구성-요소들">&lt;구성 요소들&gt;</h3>
<h3 id="1-서브넷">1. 서브넷</h3>
<ul>
<li>VPC의 하위 단위. VPC에 할당된 IP를 작은 단위로 분할한 개념. 서브 네트워크</li>
<li><strong>하나의 서브넷은 하나의 가용영역 안에 위치</strong></li>
<li>사용 가능한 IP개수는 -5개</li>
<li>외부에서 접근이 가능한 퍼블릭 서브넷은 웹서버 등의 용도로, 외부와 아예 차단된 프라이빗 서브넷은 데이터베이스, 로직 서버등의 용도로 사용됨.(라우트 테이블에 밖으로 나갈 수 있는 IP자체가 없음)</li>
<li>가장 작은 서브넷 단위는 CIDR단위 /24(11개, 16-5)</li>
</ul>
<h3 id="2-인터넷-게이트웨이">2. 인터넷 게이트웨이</h3>
<ul>
<li>기본적으로 외부와 통신이 안 되는 VPC가 외부의 인터넷과 통신할 수 있도록 경로를 뚫어주는 리소스(쉽게 말해 가상환경에 현실세계로 향하는 포탈 뚫어주는 역할)</li>
<li>공유기처럼 사설IP와 공용IP간의 변환 역할을 수행함. 다만 공유기와 IGW의 차이라면 공유기는 자신의 고유 IP로 변환해서 내보내고 IGW는 해당 리소스에 할당된 공용 IP와 매핑해서 보냄</li>
<li>이름에서 알 수 있듯이 폐쇠적으로 인터넷 필요없이 운영할 서비스면 굳이 필요없기도 함</li>
<li>무료임</li>
</ul>
<blockquote>
<p><strong>IGW(인터넷 게이트웨이)도 공유기처럼 그냥 IGW자체가 공용 IP소유하고 있으면 편하지 않나?</strong>
공유기는 대게 안에서 밖으로 나가는 요청이 먼저 시작되므로 라우트 테이블에 먼저 어떤 사설IP가 어떤 포트로 나갔는지 기록이 가능함. 그러나 IGW로 연결된 서비스는 대게 웹 서버와 같은 외부에 노출되어야 하는 서비스다. 그런데 만약 인바운드 요청이 아웃바운드 요청보다 먼저 들어오게 된다면, 아직 라우트 테이블에는 아무 기록이 없으니 어디로 매핑을 해줘야 할지 모르게 된다. 그러므로 외부에서 내부로 들어오는 요청이 원활하게 이루어져야 하기 때문에 그냥 리소스 자체에 공용 IP를 박아서 바로 연결이 되도록 한 것이다.</p>
</blockquote>
<h3 id="3-보안그룹">3. 보안그룹</h3>
<ul>
<li>방화벽의 역할을 하는 서비스</li>
<li>기본적으로 모든 포트 비활성화</li>
<li>특정 포트 허용만 가능, Deny는 불가능</li>
<li>인스턴스에 붙어있음. 하나의 인스턴스에 여러개의 보안그룹 설정 가능</li>
<li>보통 8080번 포트와 3000번 포트 (TCP)를 열어두는데 우리는 웹 애플리케이션 및 API 서버를 주로 만들기 때문에 이와 관련된 통신을 허용하기 위함임</li>
<li>보안그룹은 인바운드 들어왔던걸 기억해서 아웃바운드가 자유롭지만(StateFul), NACL은 인바운드가 통과되었던 것이라도 아웃바운드 할 때 또 체크해야함(Stateless)</li>
</ul>
<h3 id="4-nacl">4. NACL</h3>
<ul>
<li>서브넷 단위(서브넷 들어가기전에 NACL을 거쳐야함)</li>
<li>포트 및 IP 직접 Deny가능(특정 IP 블락 가능)</li>
<li>규칙번호 100번 단위, 작은 수부터 규칙 적용</li>
</ul>
<h3 id="5-라우트-테이블">5. 라우트 테이블</h3>
<ul>
<li>트래픽이 어디로 가야 할지 알려주는 이정표</li>
<li>0.0.0.0/0(외부 아이피 대역)이 목적지일 때, 인터넷 게이트웨이로 나가도록 설정된 라우팅 테이블을 서브넷에 붙이면, 해당 서브넷은 퍼블릭 서브넷이 되는것임</li>
</ul>
<h3 id="6-nat-gateway">6. NAT Gateway</h3>
<ul>
<li>VPC의 프라이빗 서브넷에 있는 인스턴스에서 외부의 인터넷과 통신하기 위한 통로</li>
<li>서브넷 단위(퍼블릭 서브넷에 있어야함)</li>
<li>NAT Gateway는 퍼블릭 서브넷에 위치해서 프라이빗 서브넷에 위치한 인스턴스 대신에 인터넷에 연결해 줌(업데이트나 다운로드 같은 외부 인터넷 연결이 필요할 때)</li>
<li>보안을 위해 아웃바운드(안에서 밖으로 일방통행)만 가능</li>
</ul>
<h3 id="7-bastion-host">7. Bastion Host</h3>
<ul>
<li>Private Subnet의 자원에 접속이 되도록 도와주는 호스트</li>
<li>이건 NAT Gateway랑 반대로 외부에서 프라이빗 서브넷에 접근하기 위한 EC2 인스턴스</li>
<li>퍼블릭 서브넷에 위치해야함(프라이빗 서브넷 중개 역할이니까)</li>
<li>private서비스에 접근할땐 Session Manager를 사용하기도 </li>
</ul>
<h3 id="8-elastic-load-balancer">8. Elastic Load Balancer</h3>
<ul>
<li>다수의 트래픽을 분산 시켜주는 서비스</li>
<li>Health Check를 통해 트래픽을 발생시켜 Instance가 살아있는지 체크</li>
<li>지속적으로 IP주소가 바뀌어 고정 불가능하기 때문에 항상 도메인 기반으로 사용</li>
<li>서버가 죽으면 로드밸런서가 새로운 복제 서버를 만들어(AMI로) 요청을 거기로 우회시키고 기존 죽은 서버는 삭제시킴(고가용성 설계)</li>
<li>얘도 Bastion Host랑 비슷하게 프라이빗 서브넷에 인바운드 요청 전용 길인건 맞지만, 로드밸런서는 외부 요청의 메시지만 서버에 전달하고 답을 받아다주고 외부와 직접 연결되는 것은 막음(reverse proxy). 다시 말해 외부 손님을 위한 길이나 마찬가지라 외부 요청인 HTTP/HTTPS만 알아듣고 80/443번 포트만 열려있음. 
이에 반해 Bastion Host는 관리자가 <strong>직접</strong> 서버 유지보수나 설정을 만지기 위해 길을 뚫어놓는 것에 가까움.(프라이빗 서브넷은 관리자도 함부로 접속 불가능하기 때문에) 그래서 보통 관리용 포트인 22(SSH)번 포트만 열어두고 특정 IP만 들어오게 해 둠.</li>
</ul>
<blockquote>
<p><strong>왜 ELB의 IP는 유동적인가?</strong>
ELB의 IP는 계속해서 바뀌기 때문에 도메인 주소를 기반으로 작동함.
이유는 ALB도 결국 AWS가 관리하는 인스턴스(서버)인데 모든 트래픽이 ALB를 거쳐가기 때문에, 갑자기 트래픽이 늘어나면 AWS는 로드밸런서를 5대, 10대로 늘이고 이렇게 로드밸런서 서버를 늘릴 때마다 새로운 IP를 계속 추가함. 그리고 이 여러 개의 IP 주소를 하나의 DNS 이름(도메인) 아래에 묶어버림. 만약 IP가 하나뿐이라면 모든 트래픽이 거기로 몰려 병목현상이 일어날것임.</p>
</blockquote>
<h2 id="ec2">EC2</h2>
<p>EC2(Elastic Compute Cloud) 구성요소</p>
<h3 id="1-ebs">1. EBS</h3>
<ul>
<li>하드디스크 역할</li>
<li>종료 시 삭제 옵션(인스턴스가 종료될때 같이 삭제할 건지 여부)</li>
<li>암호화(하드디스크 암호화해서 데이터 저장)</li>
</ul>
<h3 id="2-스냅샷">2. 스냅샷</h3>
<ul>
<li>특정 시간의 EBS상태의 저장본(S3에 증분식 저장)</li>
</ul>
<h3 id="3-ami">3. AMI</h3>
<ul>
<li>EC2인스턴스를 실행하기 위해 필요한 정보를 모은 단위</li>
<li>인스턴스 복제할때 커스텀 AMI를 생성해서 복제가능</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Codility] Lesson 7 Brackets 파이썬]]></title>
            <link>https://velog.io/@hyun_ji/Codility-Lesson-7-Brackets-%ED%8C%8C%EC%9D%B4%EC%8D%AC</link>
            <guid>https://velog.io/@hyun_ji/Codility-Lesson-7-Brackets-%ED%8C%8C%EC%9D%B4%EC%8D%AC</guid>
            <pubDate>Wed, 17 Dec 2025 22:33:20 GMT</pubDate>
            <description><![CDATA[<p>단순한 괄호식 스택 문제</p>
<pre><code class="language-python"># you can write to stdout for debugging purposes, e.g.
# print(&quot;this is a debug message&quot;)

def solution(S):
    stack = []
    mapping = {&#39;)&#39;:&#39;(&#39;, &#39;}&#39;:&#39;{&#39;, &#39;]&#39;:&#39;[&#39;}

    for element in S:
        if element in &#39;{[(&#39;: # 여는 괄호 나올때때
            stack.append(element)
        else: # 닫는 괄호 나올때
            if not stack: # 스택 비어있으면 중첩 문자열 아님
                return 0 

            tmp = stack.pop()
            if tmp != mapping[element]:
                return 0 
            element

    if stack: # 스택에 남아있음
        return 0
    else: 
        return 1</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Codility] Lesson 6 BinaryGap 파이썬]]></title>
            <link>https://velog.io/@hyun_ji/Codility-Lesson-6-BinaryGap-%ED%8C%8C%EC%9D%B4%EC%8D%AC</link>
            <guid>https://velog.io/@hyun_ji/Codility-Lesson-6-BinaryGap-%ED%8C%8C%EC%9D%B4%EC%8D%AC</guid>
            <pubDate>Wed, 17 Dec 2025 21:46:15 GMT</pubDate>
            <description><![CDATA[<h3 id="문제">문제</h3>
<p>배열의 A의 원소 3가지 중 
A[P] + A[Q] &gt; A[R],
A[Q] + A[R] &gt; A[P],
A[R] + A[P] &gt; A[Q].
를 만족하는 숫자 3개 찾기</p>
<p>가장 큰 수가 다른 숫자 2개보다 작아야함
삼각형을 만들 수 있는 3 수가 존재하면 1, 아니면 0
시간복잡도 nlogn 이하, 요소 범위 -2,147,483,648..2,147,483,647(음수 싹 다 제외해야함)</p>
<h3 id="코드">코드</h3>
<p>정렬시켜서 0이상인 수부터 3개씩 묶어서 검사하면 될 듯
시간복잡도는 2N</p>
<pre><code class="language-python"># you can write to stdout for debugging purposes, e.g.
# print(&quot;this is a debug message&quot;)

def solution(A):
    A.sort(reverse = True)

    for idx, num in enumerate(A):
        if idx+2 &gt; len(A)-1 or A[idx+2] &lt; 0: # 음수가 오면 안됨
            return 0
        if A[idx] &lt; A[idx+1] + A[idx+2]: # 삼각형 조건 만족하는지 검사
            return 1

    return 0</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Codility] Lesson 6 MaxProductOfThree 파이썬]]></title>
            <link>https://velog.io/@hyun_ji/Codility-Lesson-6-MaxProductOfThree-%ED%8C%8C%EC%9D%B4%EC%8D%AC</link>
            <guid>https://velog.io/@hyun_ji/Codility-Lesson-6-MaxProductOfThree-%ED%8C%8C%EC%9D%B4%EC%8D%AC</guid>
            <pubDate>Wed, 17 Dec 2025 12:16:15 GMT</pubDate>
            <description><![CDATA[<h3 id="문제풀이">문제풀이</h3>
<p>배열 A의 원소 3개의 곱 중 가장 큰 것을 찾는 문제
A의 크기는 10만 이하
배열 A의 요소의 크기는 -1000 ~ 1000</p>
<p>A의 범위가 음수를 포함시키니 아마 가장 큰 수의 조합은 양수x양수x양수 이거나 음수x음수x양수가 될것이다.</p>
<p>정렬시켜서 2가지 버전을 만든 다음에 더 큰수를 반환하면 될 것 같다.</p>
<h3 id="최종-제출-코드">최종 제출 코드</h3>
<pre><code class="language-python"># you can write to stdout for debugging purposes, e.g.
# print(&quot;this is a debug message&quot;)

def solution(A):

    A.sort()
    length = len(A)

    return max(A[-1] * A[-2] * A[-3], A[0] * A[1] * A[-1])</code></pre>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/66e96980-d4a7-43ee-9064-c1c09361db57/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Codility] Lesson 6 Distinct 파이썬]]></title>
            <link>https://velog.io/@hyun_ji/Codility-Lesson-6-Distinct-%ED%8C%8C%EC%9D%B4%EC%8D%AC</link>
            <guid>https://velog.io/@hyun_ji/Codility-Lesson-6-Distinct-%ED%8C%8C%EC%9D%B4%EC%8D%AC</guid>
            <pubDate>Wed, 17 Dec 2025 11:58:52 GMT</pubDate>
            <description><![CDATA[<h3 id="문제풀이">문제풀이</h3>
<p>배열에서 나오는 숫자의 종류 가짓수를 계산하는 문제이다. 
N의 범위는 10만, 배열 A의 요소의 크기는 -100만 ~ 100만.</p>
<h3 id="최종-제출-코드">최종 제출 코드</h3>
<pre><code class="language-python">def solution(A):
    x = set()

    for i in A:
        x.add(i)

    return len(x)</code></pre>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/9822742e-8914-4de8-a360-4f4cf02afc0a/image.png" alt=""></p>
<p>set사용법 익히라고 낸 문제인가 풀려서 좀 당황함.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Codility] Lesson 5 PassingCars 파이썬]]></title>
            <link>https://velog.io/@hyun_ji/Codility-Lesson-5-PassingCars-%ED%8C%8C%EC%9D%B4%EC%8D%AC</link>
            <guid>https://velog.io/@hyun_ji/Codility-Lesson-5-PassingCars-%ED%8C%8C%EC%9D%B4%EC%8D%AC</guid>
            <pubDate>Wed, 17 Dec 2025 11:44:22 GMT</pubDate>
            <description><![CDATA[<h3 id="문제풀이">문제풀이</h3>
<p>솔직히 문제 이해하는게 너무 어려웠다. 예시로 주어진 예제도 이해를 못 해서 계속 ???상태로 문제만 한 10번 읽었다. </p>
<p>문제를 정리해보자면 가로지르는 차의 수를 구하는게 목표이다. 여기서 <code>passing</code>을 내가 이상하게 해석해서 문제 이해가 안 됐던것... 
0은 오른쪽, 1은 왼쪽으로 가는 차이고 차량끼리 서로 <strong>교차</strong>하게 되는 수를 구한다고 생각하면 된다. 
가로지르는 차의 수가 10억개를 넘으면 -1반환하고 수 범위 10만이다.</p>
<h3 id="고민-포인트">고민 포인트</h3>
<p>역시 시간이 포인트인 문제인듯. 이중포문써서 단순 더하기로 세면 쉽게 풀리겠지만 그러면 무조건 타임아웃 뜰 듯. </p>
<pre><code class="language-python"># you can write to stdout for debugging purposes, e.g.
# print(&quot;this is a debug message&quot;)

def solution(A):
    count = 0

    for idx, num in enumerate(A):
        if num == 0:
            for num2 in A[idx+1:]:
                if num2 == 1:
                    count += 1

    return count</code></pre>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/e69ee417-e8b0-4526-8d3c-33b765a7202a/image.png" alt="">
<del>역시나</del></p>
<h3 id="트러블-슈팅">트러블 슈팅</h3>
<p>핵심은 0이 나온 뒤의 수들 중, 1의 출현수를 구하는 것이다. 그래서 배열의 원소들의 종류가 0과 1뿐인걸 이용해서 sum()을 이용해 전체합(=1의 전체 개수)을 구했다. 그 다음 for문으로 A를 순환하며 현재까지 발견한 1의 수를 카운트해서 전체수에서 빼 주는 방식으로 간단하게 이후에 나오는 1의 수를 계산할 수 있었다. </p>
<h3 id="최종-제출-코드">최종 제출 코드</h3>
<pre><code class="language-python">def solution(A):
    total_num = sum(A) # 배열의 모든 1을 더한 수
    count_1 = 0 # 여태나온 1의 수
    result = 0 # 반환할 결과

    for num in A:
        if num == 0:
            result += (total_num - count_1)
            if result &gt; 1000000000:
                return -1
        else:
            count_1 += 1

    return result</code></pre>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/35d957c7-78f6-426d-be75-4afb12cf316c/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Codility] Lesson 4 PermCheck 파이썬]]></title>
            <link>https://velog.io/@hyun_ji/Codility-Lesson-1-BinaryGap-%ED%8C%8C%EC%9D%B4%EC%8D%AC-t7xcxhzm</link>
            <guid>https://velog.io/@hyun_ji/Codility-Lesson-1-BinaryGap-%ED%8C%8C%EC%9D%B4%EC%8D%AC-t7xcxhzm</guid>
            <pubDate>Wed, 17 Dec 2025 09:18:52 GMT</pubDate>
            <description><![CDATA[<h3 id="문제풀이">문제풀이</h3>
<p>A가 1~N까지의 수가 단 한 번씩만 나오는 순열이 맞으면 1반환, 아니면 0을 반환하는 문제이다. </p>
<blockquote>
<p>N is an integer within the range [1..100,000];
each element of array A is an integer within the range [1..1,000,000,000].</p>
</blockquote>
<p>라는 조건으로 보아 시간복잡도는 O(nlogn)안에 해결해야 할 것 같고,
A의 요소의 범위가 10억인걸 보아 메모리를 신경써야 하는 문제같다.</p>
<p>보통의 코딩문제 메모리 제한이 <code>128MB ~ 512MB 정도</code>이니 리스트는 크기 500만 이내, Set이나 딕셔너리는 크기 100만 이내로 제한해야 한다. </p>
<h3 id="고민-포인트">고민 포인트</h3>
<p>A가 순열인지는 무조건 배열을 다 돌아야지 알 수 있음 -&gt; 순열의 수의 수가 A의 크기이기 때문</p>
<p>그렇다면 순열이 맞는지 확인하는 것보다 순열이 아닌 조건에서 바로 return 0으로 쳐낸 다음, 반복문을 문제없이 다 돌았다면 순열이 맞다는 방식으로 판단하는게 좋을 것 같다.</p>
<p>순열은 1부터 시작해서 N의 최대 크기가 100000이니, 만약 100000이상인 수가 나오면 볼 것도 없이 바로 순열이 아니다. 그리고 중복된 수도 나올 수가 없으니 이 두 조건을 불충족하면 바로 순열 탈락이다.</p>
<p>2가지 방법을 생각해봤다.</p>
<blockquote>
<p><strong>set 방법</strong>
Set생성하고 반복문으로 A순환하며 처음 발견한 수 Set에 add하기
중복된 수 나오면 바로 0반환
100000초과인 수 나오면 바로 0반환
무사히 반복문 끝났다? -&gt; 순열 확정! </p>
</blockquote>
<blockquote>
</blockquote>
<p><strong>체크 배열 방법</strong>
A의 크기만큼(10만 크기 이하)의 False배열 생성
반복문 돌며 해당하는 수의 인덱스 True로 변환
중복된 수가 나오거나 10만 초과인 수 나오면 바로 return 0
반복문 무사히 끝나면 return 1</p>
<p>Set이 해시테이블을 사용하기 때문에 배열보다 메모리를 아주 조금 더 사용한다는 말에 이번엔 Set말고 배열을 이용해 보기로 하였다.</p>
<h3 id="최종-제출-코드">최종 제출 코드</h3>
<p><strong>체크 배열 방법</strong></p>
<pre><code class="language-python"># you can write to stdout for debugging purposes, e.g.
# print(&quot;this is a debug message&quot;)

def solution(A):
    check_arr = [False] * (len(A) + 1) # 0 인덱스는 안 쓰니까 +1

    for num in A:
        if num &gt; len(A):
            return 0
        elif check_arr[num]: # 중복인 수 발견
            return 0
        else:  # 해당 수가 미발견 상태이면
            check_arr[num] = True

    return 1

# A가 순열이면 1 반환, 아니면 0반환

# 체크 배열 방법
# A의 크기만큼(10만 크기 이하)의 False배열 생성
# 반복문 돌며 해당하는 수의 인덱스 True로 변환
# 중복된 수가 나오거나 10만 초과인 수 나오면 바로 return 0
# 반복문 무사히 끝나면 return 1</code></pre>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/75243e79-ccd1-4a37-b5ae-1da5f23756e9/image.png" alt=""></p>
<p>Set으로 하면 훨씬 간결하게 할 수 있다해서 그냥 Set도 해봤다. 리스트를 Set으로 변환하고 변환된 Set의 크기가 기존 배열 크기와 같고 요소들의 크기가 모두 A의 길이 이하이면 된다.</p>
<p><strong>set 방법</strong></p>
<pre><code class="language-python"># you can write to stdout for debugging purposes, e.g.
# print(&quot;this is a debug message&quot;)

def solution(A):
    N = len(A)
    visited = set(A) # 중복 제거된 A

    # 길이가 N과 같고 최대값이 N과 같으면 순열
    if (len(visited) == N) and (max(visited) == N):
        return 1

    return 0
</code></pre>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/76151203-6b0f-403f-a053-cc002561fbb0/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Codility] Lesson 4 Counting Elements 파이썬, 시간복잡도
]]></title>
            <link>https://velog.io/@hyun_ji/Codility-Lesson-4-Counting-Elements-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%8B%9C%EA%B0%84%EB%B3%B5%EC%9E%A1%EB%8F%84</link>
            <guid>https://velog.io/@hyun_ji/Codility-Lesson-4-Counting-Elements-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%8B%9C%EA%B0%84%EB%B3%B5%EC%9E%A1%EB%8F%84</guid>
            <pubDate>Wed, 17 Dec 2025 09:17:03 GMT</pubDate>
            <description><![CDATA[<h2 id="문제풀이">문제풀이</h2>
<p>0에 위치한 개구리가 X+1로 가기 위한 최단 시간을 구하는 문제이다. 배열 A에 주어진 숫자들은 각 초(인덱스)에 떨어진 나뭇잎의 위치를 가리킨다. 그리고 X+1로 가기 위한 길이 완성된 최단 시간을 구하면 된다. 
 ex.배열 A가 [1,3,1,4,2,3,5,4]라면 0초엔 1번 위치, 1초엔 3번 위치에 떨어진다는 뜻. 최단 시간은 2번 위치에 나뭇잎이 떨어진 다음 초인 5초.</p>
<h2 id="고민-포인트">고민 포인트</h2>
<p><strong>문제 주요 조건</strong>들을 정리해보면
<em><del>1. 1</del>X까지가 매워져야함 
2. 이동 불가능이라면 -1반환
3. 시간 복잡도 효율성 생각하기<del>~</del></em></p>
<p><strong>어떻게 할 지 고민되었던 지점</strong>
이미 나온 위치인지 아닌지 어케 구분하지? 
-&gt; 얘만 구분되면 개수를 세든 해서 길이 완성되었는지 여부 판단은 쉬울듯</p>
<p> 일단 1차원적으로 X크기의 배열을 만든 다음에 전부 -1로 초기화해서, A배열에서 해당하는 위치의 숫자를 발견하면 그 배열의 해당 인덱스를 양수로 바꾸는 방법을 생각해보았다. 그런데 이 방법은 배열A를 X번 순회하는 것도 시간복잡도가 큰데, X크기의 배열이 전부 양수가 되었는지 또한 계속해서 검토를 해주어야 하기 때문에 무조건 시간 초과할 것 같아서 기각. </p>
<p>그래서 1<del>X까지 빠짐없이 매워야 한다는 특징을 이용했다. 즉, 랜덤한 숫자를 찾는게 아니므로 1</del>X까지의 정돈된 수를 차례대로 찾아간다는 느낌.
두 방식의 차이라고 한다면 두 번째 방법은 수를 차례대로 찾기 때문에 첫 번째처럼 이미 메워진 위치인지를 검사할 필요가 없이 마지막 칸(X)를 찾으면 바로 다 찾았음을 알 수 있다.</p>
<ol>
<li>배열에서 1~X까지 하나하나 원소를 찾는데 해당 원소를 찾은 인덱스 위치가 큰 것으로 점점 교체해나가기</li>
<li>만약 중간에 발견되지 않으면 즉시 중단하고 -1반환<pre><code class="language-python"># you can write to stdout for debugging purposes, e.g.
# print(&quot;this is a debug message&quot;)
</code></pre>
</li>
</ol>
<p>def solution(X, A):
    result_time = 0</p>
<pre><code>for i in range(1, X+1):
    for idx, num in enumerate(A):
        if i == num:
            if result_time &lt; idx:
                result_time = idx
            break # 남은 배열 원소들은 확인할 필요 X

        # 여기까지 왔다는 것은 배열에 해당 요소가 없다는 뜻
        if idx &gt;= (len(A)-1):
            return -1

return result_time</code></pre><pre><code>
## 트러블 슈팅
![](https://velog.velcdn.com/images/hyun_ji/post/d21e6d54-17d2-4158-a4cc-528f63f20d2e/image.png)
정확성 테스트는 모두 통과했는데 성능 테스트는 모조리 타임아웃 에러가 났다.

지금 코드의 시간복잡도는 O(n^2)이니 여기서 더 줄여야한다. 입력 범위가 $N, X \le 100,000$이니, 최악의 경우를 가정했을 때 $O(N)$ 또는 $O(N \log N)$ 복잡도 내에 해결해야한다.

### 데이터 크기에 따른 시간 복잡도
보통 코딩문제 제한시간이 1~6초이고 파이썬의 초당 연산 횟수를 대략 20000000회라고 가정. 
&gt; $N \le 20$    |  $O(2^N)$ 또는 $O(N!)$    | 재귀, 백트래킹, 완전 탐색 (브루트 포스)
$N \le 500$    |     $O(N^3)$    | 플로이드-워셜, 단순 3중 for문
$N \le 2,000$        | $O(N^2)$    | 이중 for문, 삽입 정렬, 거품 정렬
$N \le 100,000$ |    $O(N \log N)$    | 정렬, 이진 탐색, 우선순위 큐(Heap)
$N \le 1,000,000$    | $O(N)$    | 단일 for문, 스택, 큐, 해시(Set/Dict)
$N \ge 10^8$ |    $O(\log N)$    | 유클리드 호제법, 고속 거듭제곱

위의 표는 N의 범위별 적용하기 적절한 시간복잡도이다.  
예를들어 N의 범위가 1,000,000이하라면 시간복잡도가 N이거나 logN인 방법으로 풀 수있다. 이것보다 큰 것들(ex.N^2)을 사용하면 시간초과.

원래 코드는 O(N^2)의 시간복잡도라 100000 x 100000 = 10000000000(100억) 번의 연산이 수행된다. 즉, 100억 % 2천만 = 500(초)가 걸린다는 소리. 

중요한건 N의 입력가능 범위! 가능하면 N의 최악의 연산 횟수가 1000만 단위를 넘지 않게 하는게 좋다. (파이썬의 연산 횟수를 고려했을때)

그래도 이참에 시간복잡도 계산법은 확실히 알고가게 된 듯...

배열을 정렬 후 이진탐색하는것을 생각해봤는데 이건 잎이 떨어진 순서가 섞여버려서 안된다. 

도저히 아이디어가 생각이 안 나서 AI에게 힌트를 달라했더니 Set이나 체크 배열을 활용하여 전체를 단 한 번만 훑어보되 그때그때 새로운 것을 발견하면 찾은 개수를 증가시키라 조언을 주었다. 이러면 언제 다 찾았는지도 쉽게 알 수 있다. 

내가 기존에 짠 코드는 1찾으려고 전체 함 훑고, 2찾으려고 전체 함 훑고 이런 식인데 저 방식은 한 번 쫙 훑을 때 어 3이네? 어 1이네? 하고 새로운 것들 그때그때 찾아내고 한 번 본건 2번 확인하지 않는다. 종료조건 쉽게 알아내는 법에 꽃혀서 효율적인 탐색 방법은 생각 못 한게 패착의 원인인듯. 


## 최종 제출 코드
**Set방식**
```python
# you can write to stdout for debugging purposes, e.g.
# print(&quot;this is a debug message&quot;)

def solution(X, A):
    visited = set()

    for idx, num in enumerate(A):
        if num &lt;= X:
            visited.add(num) 

        if len(visited) == X:
            return idx # 마지막으로 찾은 수의 인덱스가 젤 클테니까 

    return -1 # for문에서 끝나지 않았다면 해당 위치가 존재하지 않는다는 뜻뜻


# A를 한 번 순회
# 처음 찾는 위치면 set에 추가(X보다 작은수만 추가가능)
# set의 크기가 X와 같을때까지 반복</code></pre><p>set은 탐색할때 Hash알고리즘을 사용하기 때문에 값을 추가할때 이미 중복된 값인지 검사할때의 시간복잡도가 O(1)이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Codility] Lesson 3 FrogJmp 파이썬
]]></title>
            <link>https://velog.io/@hyun_ji/Codility-Lesson-2-BinaryGap-%ED%8C%8C%EC%9D%B4%EC%8D%AC</link>
            <guid>https://velog.io/@hyun_ji/Codility-Lesson-2-BinaryGap-%ED%8C%8C%EC%9D%B4%EC%8D%AC</guid>
            <pubDate>Tue, 16 Dec 2025 11:05:57 GMT</pubDate>
            <description><![CDATA[<h3 id="문제풀이">문제풀이</h3>
<p>정수X, D, Y가 주어졌을 때, X에 위치한 한 번에 D만큼 점프하는 개구리가 Y와 같거나 큰 거리만큼을 가는데 필요한 최소 점프수를 구하는 문제</p>
<p>단순히 X값이 Y보다 커질때까지 D를 더해주면 되나? 라고 생각하고 코드를 짰는데</p>
<pre><code class="language-python">def solution(X, Y, D):
    jump_count = 0

    while X &lt; Y: # 현재 위치가 목표 지점에 닿을때까지 반복
        X = X + D
        jump_count += 1

    return jump_count</code></pre>
<p>뭔가 분명 이렇게 쉬울리가 없다는 생각에 예외를 찾아보려고 생각해봤는데... X, Y, D 전부 양의 정수이고 Y값이 X값 보다 크다는 조건까지 전부 잘 명시되어 있어서 일단 제출해보았다. </p>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/11a98127-cd44-4962-b82a-9fea44b997b5/image.png" alt="">
그럼 그렇지... 생각해보니 성능보다 정확성을 요구했던 이전 문제들과는 달리 이 문제의 테마는 <strong>시간 복잡도</strong>다. </p>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/5e718a4e-bd42-4922-88a6-f659258387b8/image.png" alt="">
통과 못 한 케이스들 전부 시간 초과. </p>
<h3 id="트러블-슈팅">트러블 슈팅</h3>
<p><strong>O(n)</strong>에서는 시간 초과가 나는 것 같으니 시간복잡도를 <strong>O(log n)</strong>이나 <strong>O(1)</strong>으로 줄여야 한다. </p>
<h3 id="최종-제출-코드">최종 제출 코드</h3>
<pre><code class="language-python"># you can write to stdout for debugging purposes, e.g.
# print(&quot;this is a debug message&quot;)
import math

def solution(X, Y, D):

    jump_count = (Y - X) / D
    result = math.ceil(jump_count) # 결과값 올림

    return result</code></pre>
<p><code>(최종위치 - 현재위치) / 이동거리</code> 의 결과를 올림하면 Y에 도달하기 위한 최종 이동 횟수가 나온다. 시간복잡도는 O(1).
<img src="https://velog.velcdn.com/images/hyun_ji/post/2165d0bd-6dcb-4638-b185-27d2f905df40/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Codility] Lesson 2 CyclicRotation 파이썬]]></title>
            <link>https://velog.io/@hyun_ji/Codility-Lesson-2-CyclicRotation-%ED%8C%8C%EC%9D%B4%EC%8D%AC</link>
            <guid>https://velog.io/@hyun_ji/Codility-Lesson-2-CyclicRotation-%ED%8C%8C%EC%9D%B4%EC%8D%AC</guid>
            <pubDate>Tue, 16 Dec 2025 10:36:52 GMT</pubDate>
            <description><![CDATA[<h3 id="문제풀이">문제풀이</h3>
<p>배열 A와 정수 K가 주어지면, 배열 A의 원소들을 K번 오른쪽으로 옮기는 문제이다. </p>
<p>조건이 복잡하지 않고 단순히 모든 원소를 다음칸으로 한 칸씩 회전시킨다는 조건에 처음엔 인덱스를 이용해서 [현재 인덱스 + 1]로 자리를 재지정하면 될려나 라고 생각했다. 근데 간단하게 생각하면 결론적으로 <code>회전한 듯한 모양새</code>만 만들면 되는 것이니 마지막 원소를 떼서 앞에다 붙이면 되는거 아닌가?라는 생각이 들었다.</p>
<pre><code class="language-python">def solution(A, K):

    for i in range(K):
        last_num = A.pop() # 맨 뒤의 원소 떼서
        A.insert(0, last_num) # 맨 앞에 붙여넣기

    return A</code></pre>
<p>그래서 이렇게 초단순하게 코드를 짰는데 테스트 케이스 3개가 다 통과하는것이 아닌가. 그래서 설마 하는 마음으로 제출해봤는데.</p>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/259f5924-6211-4a3c-9436-0624f837f43b/image.png" alt=""></p>
<p>역시 최종 테스트 코드를 100% 통과하진 않았다.</p>
<h3 id="트러블-슈팅">트러블 슈팅</h3>
<p>통과하지 못한 테스트 케이스를 살펴보니 </p>
<pre><code> File &quot;/tmp/exec_user_s3cqt9mm/solution.py&quot;, line 7, in solution
    last_num = A.pop()
               ^^^^^^^
IndexError: pop from empty list</code></pre><p>라는 에러 메시지로, 빈 배열이 주어졌을때 pop()을 해서 생긴 문제 같았다.  </p>
<h3 id="최종-제출-코드">최종 제출 코드</h3>
<p>주어진 배열이 빈 배열일 경우 예외처리하는 코드를 추가하였다. </p>
<pre><code class="language-python">def solution(A, K):

    if len(A) == 0:
        return A

    for i in range(K):
        last_num = A.pop()
        A.insert(0, last_num)

    return A</code></pre>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/f5b43bb2-e632-48e4-a489-74f3727ba335/image.png" alt=""></p>
<p>잘 풀었다고 생각했는데 생각지도 못한 예외였다... 앞으로 좀 더 꼼꼼히 예외 케이스를 생각해보아야 할 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Codility] Lesson 1 BinaryGap 파이썬]]></title>
            <link>https://velog.io/@hyun_ji/Codility-Lesson-1-BinaryGap-%ED%8C%8C%EC%9D%B4%EC%8D%AC</link>
            <guid>https://velog.io/@hyun_ji/Codility-Lesson-1-BinaryGap-%ED%8C%8C%EC%9D%B4%EC%8D%AC</guid>
            <pubDate>Tue, 16 Dec 2025 10:14:25 GMT</pubDate>
            <description><![CDATA[<h3 id="문제풀이">문제풀이</h3>
<p>10진수의 수 N이 주어지면, 이 수의 이진수에서 1로 둘러쌓인 최대 연속된 0의 길이를 구하는 문제이다. </p>
<pre><code class="language-python">def solution(N):

    binary_num = bin(N)[2:]
    max_count_0 = 0 
    count_0 = 0 

    for char in binary_num:
        if char == 1 and count_0 &gt; 0: # 앞에 0을 낀 닫히는 1
            if max_count_0 &lt; count_0:
                max_count_0 = count_0
                count_0 = 0
        elif char == 0:
            count_0 += 1

    return max_count_0 </code></pre>
<h3 id="고민-포인트">고민 포인트</h3>
<ol>
<li>0을 감싸는 시작&#39;1&#39;과 끝나는 &#39;1&#39;의 차이를 무슨 수로 구분하지</li>
<li>이진수 변환 어케 시키지</li>
</ol>
<p>2번은 파이썬에 <code>bin()</code>이라는 편리한 이진수 변환 함수를 이용하였다. 2진수임을 나타내는 접두사 0b가 붙어서 슬라이싱으로 제거해줬다.
1번은 &#39;1&#39;일때 <code>count_0 &gt; 0</code>이라는 조건을 추가해줬는데, 첫 번째 인덱스의 &#39;1&#39;과 &#39;1&#39;이 연속되어서 오는 경우에는 <code>count_0</code>변수가 증가되지 않아 0일 거라는 생각에서였다. </p>
<h3 id="트러블-슈팅">트러블 슈팅</h3>
<p>저 상태의 코드로 제출했더니 테스트 케이스 3개중 2개만 자꾸 통과했다. 결과적으로 놓친 부분이</p>
<ol>
<li><code>char == 1</code> 과 <code>char == 0</code> 에서 정수가 아닌 문자열와 비교하는 거라서 작은 따옴표를 붙였어야했다.</li>
<li><code>if max_count_0 &lt; count_0:</code>의 if문 안의 <code>count_0 = 0</code>는 if문 안에 들어있지 말고 밖에 나와야 함. 0의 개수가 <code>max_count_0</code>보다 작을지라도 <code>count_0</code>를 초기화해줘야 하기 때문.</li>
<li><code>if char == 1 and count_0 &gt; 0:</code>에서 <code>count_0 &gt; 0</code>조건은 없어도 됨. 어차피 <code>count_0</code>변수가 0이라면 <code>max_count_0</code>보다 클리가 없으니까.</li>
</ol>
<h3 id="최종-제출-코드">최종 제출 코드</h3>
<p>오류 사항 수정하고 조금 더 가독성 좋게 바꾼 코드 </p>
<pre><code class="language-python">def solution(N):

    binary_num = bin(N)[2:]
    max_count_0 = 0 
    count_0 = 0 

    for char in binary_num:
        if char == `0`:
            count_0 += 1

        elif char == `1`: 
            if max_count_0 &lt; count_0:
                max_count_0 = count_0
            count_0 = 0

    return max_count_0 </code></pre>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/23960458-976a-42bd-aef9-567285344d40/image.png" alt="">
테스트 케이스를 전부 통과했다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS Lambda에 FastAPI YOLO 모델 배포하기]]></title>
            <link>https://velog.io/@hyun_ji/AWS-Lambda%EC%97%90-FastAPI-YOLO-%EB%AA%A8%EB%8D%B8-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@hyun_ji/AWS-Lambda%EC%97%90-FastAPI-YOLO-%EB%AA%A8%EB%8D%B8-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 24 Nov 2025 09:05:24 GMT</pubDate>
            <description><![CDATA[<h1 id="프로젝트-개요">프로젝트 개요</h1>
<p>본 프로젝트는 간단한 물고기 종 분류 YOLO 모델을 활용하여,
백엔드 서비스를 혼자서 설계·배포까지 완주해보는 것을 목표로 한 개인 프로젝트이다.</p>
<h3 id="동기">동기</h3>
<p>데이터 사이언스를 전공하며 모델을 구현하는 경험은 많았지만, 해당 모델이 실제 서비스 환경에서 어떻게 배포되고 운영되는지는 직접 경험해보지 못한 영역이었다.</p>
<p>특히 이전 팀 프로젝트에서는 주로 기능 구현에 집중했기에, 인프라 설계부터 배포까지의 전 과정을 직접 책임지는 경험에 갈증이 있었다.
이에 본 프로젝트에서는 <strong>모델의 성능 고도화보다는 모델을 포함한 백엔드 서비스를 설계부터 배포까지 혼자서 완주하는 경험</strong>을 목표로 삼았다.</p>
<h3 id="서비스-구성-및-구현-범위">서비스 구성 및 구현 범위</h3>
<p>기존에 제작해두었던 물고기 종 분류 YOLO 모델을 기반으로, 해당 모델을 호출할 수 있는 백엔드 API를 <strong>FastAPI</strong>로 구성하고, 모바일 클라이언트에서는 <strong>Kotlin(Android)</strong>을 사용해 서비스를 구현하였다.</p>
<p>또한, 단순히 추론 기능만 있는게 아니라 엄연히 서비스다운 모습을 갖추기 위해 수집 요소를 추가하였다. 포켓몬고의 도감 시스템처럼, 사진 분석 결과로 등장한 종(예: 상어, 해파리)을 사용자가 사전에 등록·확인할 수 있는 기능을 추가하였다.  </p>
<p>서비스 운영에 필요한 데이터와 사용자 수집 정보는 <strong>DynamoDB</strong>에 저장하였으며,클라이언트에서 생성한 랜덤 UUID를 사용자 식별자로 사용하여, 로그인 기능을 따로 구현하지 않고도 사용자 식별이 가능하도록 하였다.</p>
<h3 id="lambda-선택-배경">Lambda 선택 배경</h3>
<p>원래 서버를 EC2로 띄우려고 했는데 프리티어 기간이 만료가 되었다(...) </p>
<p>개인 프로젝트라는 특성상 서버 운영과 비용 관리에 많은 리소스를 쓰고 싶지 않았기 때문에 프리티어를 넉넉하게 제공해주고 운영 부담도 적은 <strong>AWS Lambda</strong>를 서버 실행 환경으로 선택하였다.</p>
<p>AWS Lambda 서비스는 DynamoDB와 같은 ServerLess서비스이기 때문에, DynamoDB와 연계되어 서버 관리가 거의 필요 없는 완전한 서버리스(Serverless) 아키텍처를 구성할 수 있다는 장점도 있었다.</p>
<p>서버리스라는 표현은 서버가 존재하지 않는다는 의미가 아니라, 개발자가 서버의 운영·확장·장애 대응을 직접 관리하지 않아도 된다는 의미이다.
AWS에서 알아서 운영, 수요 예측 등을 다 해주기 때문에 서버 안정성이나 스케일링에 대한 고민없이 개발자는 오직 서비스의 개발에만 집중할 수 있다. </p>
<p>게다가 EC2는 서버가 24시간 내내 돌아가기 때문에 서버를 실행하는 동안 비용이 지속적으로 발생하는 반면, Lambda는 이벤트가 발생할 때만 실행되는 온디맨드 방식으로, 월별 <code>320만초 or 40만 GB-Second</code> 까지 무료라 소규모 개인 프로젝트에서는 거의 무료로 평생 사용할 수 있다. </p>
<p>다만 AWS에서 자체적으로 여러 서비스들을 결합해 자동으로 관리해주는 거라 아키텍처가 복잡하게 얽혀있다는 단점과, 한 번 서버리스 방식으로 서버를 구축하면 다른 방식으로 마이그레이션이 쉽지 않다는 단점이 있다. (한마디로 AWS 람다 서비스 종료되면 내 서비스도 영원히 종료된다는것... 근데 람다 서비스가 끝날 가능성도 거의 0%이긴하다.) 그리고 Stateless한 서비스이기 때문에 EC2처럼 EBS같은 메모리에 데이터 저장이 불가능하다. 람다 자체에 저장 기능이 없기 때문에, 기억해야 할 데이터가 있다면 반드시 S3나 DB같은 외부 저장소가 필요하다. </p>
<blockquote>
<p>물론 람다에게도 아주 작은 로컬 디스크 공간인 /tmp 폴더(최소 512MB 제공)가 있긴 하다.
그러나 이 역시 함수가 종료되고 컨테이너가 내려가면 삭제된다. 주로 실행 중에 잠깐 압축을 풀거나, 큰 파일을 임시로 내려받아 처리하는 용도로만 사용된다.</p>
</blockquote>
<h1 id="lambda-이용하여-배포하기">Lambda 이용하여 배포하기</h1>
<p>일반적으로 람다에 서버를 배포하려면 그냥 라이브러리와 코드를 zip파일로 묶어서 올리면 된다. 이 방식은 환경설정에 문제만 없다면(EC2랑은 다르게 OS환경이 리눅스로 고정되어있어 다른 OS를 선택할수 없다.) 매우 간단하지만 파일이 <code>50MB</code> 이하여야 한다.</p>
<blockquote>
<p><strong>하지만 내가 만든 서비스는</strong></p>
</blockquote>
<ol>
<li>모델 파일과 무거운 라이브러리등이 포함되어 있기 때문에 용량이 <code>50MB</code>을 한참 넘어가고</li>
<li>PyTorch, OpenCV, NumPy 같은 라이브러리는 빠른 연산을 위해 내부에 OS별로 컴파일된 바이너리 파일을 포함하고 있다. 그런데 내 컴퓨터(윈도우)에서 설치한 라이브러리에는 윈도우용 바이너리가 들어있어서, 이를 그대로 압축해 리눅스 환경인 람다에 올리면 해당 바이너리를 실행할 수 없어 충돌이 발생한다.</li>
</ol>
<p>그리하여 이런 용량과 버전충돌 문제를 해결하기 위해 도커를 이용하여 배포하기로 하였다.(도커는 10GB까지 가능하다!)</p>
<h2 id="mangum-라이브러리-설정">Mangum 라이브러리 설정</h2>
<p>먼저 Mangum라이브러리를 설치해주어야 하는데 이게 왜 필요하냐면...</p>
<p>AWS Lambda는 HTTP 서버가 아닌 <strong>이벤트 기반 실행 환경</strong>이기 때문에,
클라이언트의 HTTP 요청을 직접 처리할 수 없다. 대신 API Gateway가 앞단에서 요청을 받아 이를 Lambda Event(JSON) 형식으로 변환해 Lambda에 전달한다.</p>
<p>이때 람다 안의 FastAPI에게 Lambda Event(JSON)객체를 그대로 전해주면 FastAPI가 무슨 말인지 못 알아먹기 때문에 다시 FastAPI가 이해할 수 있는 <strong>ASGI객체</strong>로 변환해주어야 한다.(FastAPI는 ASGI 기반의 웹 애플리케이션 프레임워크이기 때문)</p>
<p>따라서 API Gateway를 통해 전달된 Lambda Event(JSON)를 FastAPI가 이해할 수 있는 ASGI 요청 객체로 변환하는 어댑터 계층이 필요하며, 이 역할을 <code>Mangum</code>이 해줄것이다.</p>
<blockquote>
<ul>
<li>로컬 환경에서는 주로 Uvicorn이라는 웹 서버가 API 게이트웨이와 Mangum의 역할인 HTTP 소켓 연결과 ASGI 변환을 혼자 수행해준다.</li>
</ul>
</blockquote>
<p>Mangum공식 깃허브에 들어가보면 Fastapi를 사용한 Mangum호출법이 나와있다.
<a href="https://github.com/Kludex/mangum">[Mangum 공식 깃허브]</a></p>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/b5a3286b-433a-4a1f-8c88-3bb467f2fb37/image.png" alt=""></p>
<p>간단하게 Handler변수를 기존 코드 밑에 추가해주면 되는데, Handler는 람다가 신호를 받았을때 처음 실행시키는 진입 함수이다. </p>
<p>만들어둔 Fastapi의 app객체를 Mangum에 전달하고 이걸 handler변수에 저장한다. 그럼 람다가 실행되면 자동으로 이 handler 변수(app객체가 전달된 Mangum 객체)를 찾아서 실행하게 된다. 
<a href="https://docs.aws.amazon.com/ko_kr/lambda/latest/dg/python-handler.html">[파이썬 람다 핸들러 함수 정의]</a></p>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/b1286e57-2687-455d-bc97-79528d77c5ca/image.png" alt=""></p>
<blockquote>
<p>클라이언트(https 소켓) -&gt;  API 게이트웨이(JSON 변환) -&gt; Lambda -&gt; mangum(ASGI 변환) -&gt; FastAPI 코드 돌아감</p>
</blockquote>
<p>대략적으로 이런 구조를 예상했다.</p>
<h2 id="dockerfile-이미지-만들기">Dockerfile 이미지 만들기</h2>
<p>이제 handler까지 추가해준 코드파일을 도커 이미지로 구워보자. 우선 도커 설정이 들어있는 <code>Dockerfile</code>을 작성해주어야 한다. </p>
<p>작성한 내용은 다음과 같다</p>
<pre><code class="language-docker">FROM public.ecr.aws/lambda/python:3.10

RUN yum install -y mesa-libGL

COPY requirements.txt ${LAMBDA_TASK_ROOT}

RUN pip install --no-cache-dir -r requirements.txt

COPY . ${LAMBDA_TASK_ROOT}

CMD [ &quot;main.handler&quot; ]</code></pre>
<p><code>yum install -y mesa-libGL</code>는 소스 코드에서 사용된 OpenCV와 YOLO가 이미지를 행렬로 계산할 때 사용하는 그래픽 관련 라이브러리인데 람다용 리눅스에는 이게 빠져있기 때문에 별도로 설치해준다. </p>
<p>그리고 <code>requirements.txt</code>의 라이브러리 목록을 설치할때는 용량 확보를 위해 <code>--no-cache-dir</code>옵션을 넣어준다.(보통 pip은 재설치를 대비해 설치 파일을 임시로 저장(Cache)해 두기 때문에.)</p>
<p>라이브러리 설치는 매우 오래 걸리기 때문에 이후 <code>main.py</code> 소스파일 수정이 있을시, 또 다시 처음부터 설치될일이 없도록 Dockerfile 상단에서 먼저 라이브러리를 설치한다. 
Docker는 위쪽 레이어에 변경이 발생하면 그 아래 모든 레이어의 캐시를 무효화하고 다시 실행하기 때문에, 변경 가능성이 낮은 라이브러리 설치 단계를 앞부분에 배치하고 이후에 소스 코드와 모델 파일을 복사하도록 구성한 것이다.</p>
<p><code>.dockerignore</code>에 쓸데없는 파일들까지 같이 빌드되지 않도록 파일을 추가해준다. 가상환경에서 테스트로 여러 라이브러리들을 설치하고 실행해보았가 때문에, 무조건 제외 목록에 추가해주어야 쓸데없는 용량이 소모되지 않고 빌드 시간이 길어지지 않는다.</p>
<pre><code class="language-dockerignore">__pycache__/
venv/
.venv/
.git/
.env</code></pre>
<p>또한 프로젝트에 사용된 라이브러리 목록을 기재한 <code>requirements.txt</code>도 작성해준다. </p>
<pre><code class="language-python"># FastAPI 및 람다 핸들러
fastapi
mangum
python-multipart

# AWS 서비스 연동
boto3

# AI 모델 및 수치 계산 (CPU 전용 버전으로 용량 최적화)
--index-url https://download.pytorch.org/whl/cpu
torch
torchvision
ultralytics

# 이미지 처리 (람다 에러 방지용 화면 띄우는 GUI기능이 없는 headless 버전)
opencv-python-headless
Pillow
numpy</code></pre>
<p><code>--index-url https://download.pytorch.org/whl/cpu</code>는 torch와 torchvision을 보다 가벼운 CPU버전으로 설치하라는 명령어이다. 그냥 설치하면 도커가 무거운 GPU용 파일(수 GB)까지 다 받아오려고 하는데, 이걸 적어주면 람다에 딱 맞는 가벼운 CPU 전용 파일만 찾아낸다. 
<em>애초 람다는 GPU가 없기 때문에 GPU용 파일을 깔아봤자 쓰지도 못하고 실행 속도만 느려진다...</em></p>
<p>이렇게 준비가 되었으면 <code>docker build -t fish-app .</code> 명령어로 도커를 빌드해준다. </p>
<h3 id="🛠️-troubleshooting-docker-pytorch-라이브러리-버전-오류">🛠️ Troubleshooting: Docker pytorch 라이브러리 버전 오류</h3>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/12c52cdd-3744-4f1a-a3ca-c7de55f36e1e/image.png" alt="">
도커 이미지 빌드 중 에러가 뜨며 빌드가 중단되었다.</p>
<p><strong>원인 :</strong>
뜬 에러 메시지는 다음과 같다.
<code>Looking in indexes: https://download.pytorch.org/whl/cpu ERROR: Could not find a version that satisfies the requirement fastapi</code> </p>
<p>라이브러리들을 설치하는 과정에서 에러가 생겼는데, cpu용 pytorch설치하라고 입력해준 주소에서 쌩뚱맞게 fastapi를 찾아서 다운로드 하려다가 없으니까 뜬 에러 메시지이다. 아무래도 <code>--index-url https://download.pytorch.org/whl/cpu</code>명령이 다른 라이브러리들 설치될때도 적용이 되었던 모양이다. </p>
<p><strong>1차 시도 :</strong></p>
<pre><code class="language-python">fastapi
mangum
python-multipart
boto3
opencv-python-headless
Pillow
numpy
ultralytics

# torch 종류만 아래 url에서 다운
--extra-index-url https://download.pytorch.org/whl/cpu
torch
torchvision</code></pre>
<p><code>--extra-index-url</code>명령어로 변경해준다. 해당 명령어는 일단 <code>pip</code>가 <code>PyPI</code>에서 해당 라이브러리를 찾아본다음 없으면 그때 해당 url에서 다운받는 명령어이다.</p>
<p>이제 다시 빌드를 해준다.</p>
<pre><code>=&gt; [4/5] RUN pip install --no-cache-dir -r requirements.txt                                                                                                450.3s
 =&gt; =&gt; # Installing collected packages: triton, nvidia-cusparselt-cu12, mpmath, urllib3, typing-extensions, sympy, six, pyyaml, python-multipart, pyparsing, psuti 
 =&gt; =&gt; # l, polars-runtime-32, Pillow, packaging, nvidia-nvtx-cu12, nvidia-nvjitlink-cu12, nvidia-nccl-cu12, nvidia-curand-cu12, nvidia-cufft-cu12, nvidia-cuda-ru
 =&gt; =&gt; # ntime-cu12, nvidia-cuda-nvrtc-cu12, nvidia-cuda-cupti-cu12, nvidia-cublas-cu12, numpy, networkx, MarkupSafe, kiwisolver, jmespath, idna, fsspec, fonttool 
 =&gt; =&gt; # s, filelock, cycler, charset_normalizer, certifi, annotated-types, annotated-doc, typing-inspection, scipy, requests, python-dateutil, pydantic-core, pol
 =&gt; =&gt; # ars, opencv-python-headless, opencv-python, nvidia-cusparse-cu12, nvidia-cudnn-cu12, mangum, jinja2, exceptiongroup, contourpy, pydantic, nvidia-cusolver
 =&gt; =&gt; # -cu12, matplotlib, botocore, anyio, torch, starlette, s3transfer, ultralytics-thop, torchvision, fastapi, boto3, ultralytics  </code></pre><p>그런데 몇십분이 지나도 빌드가 끝나지 않고 딱 봐도 뭔가 웅장한 라이브러리들이 우후죽순 설치되고 있다는 로그가 뜨더니...</p>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/a658f2af-6607-40c4-a2fa-329f34678ce0/image.png" alt=""></p>
<p>결국 WSL(윈도우용 리눅스 가상 엔진) 용량 부족 엔딩.</p>
<p><strong>원인 :</strong>
<code>--extra-index-url</code> 명령어는 <strong>먼저 <code>PyPI</code>를 살펴보고 <code>PyPI</code>에 존재하지 않으면</strong> <code>https://download.pytorch.org/whl/cpu</code>에서 cpu버전을 다운받는다고 하지 않았는가? </p>
<p>그런데 <strong><code>PyPI</code>에 pytorch GPU버전이 이미 존재하기 때문에</strong> pip는 응? <code>https://download.pytorch.org/whl/cpu</code> 갈 필요 없겠는데? 하고 그냥 <code>PyPI</code>에서 GPU버전 다운받아 버린 거다.</p>
<p>위의 여러 라이브러리들 중 nvidia-로 시작하는 라이브러리들은 GPU 연산용 부품들인데(람다에서 돌리지도 못하는), 이것들이 다 합쳐지면 이미지 용량이 어마어마하니 연약한 내 컴퓨터가 버틸리가 없다.</p>
<p><strong>2차 시도 :</strong>
일단 <code>wsl --shutdown</code>명령어로 wsl을 강제종료 시키고, <code>docker system prune -a --volumes</code> 명령어로 도커 이미지, 컨테이너, 캐시 등등 전부 다 지워 초기화 시켜 용량을 정상화시킨다. (14GB가 지워졌다ㄷㄷ)</p>
<pre><code class="language-python">--extra-index-url https://download.pytorch.org/whl/cpu
torch==2.0.1+cpu 
torchvision==0.15.2+cpu
ultralytics
fastapi
mangum
python-multipart
boto3
opencv-python-headless
Pillow
numpy</code></pre>
<p>이번엔 GPU용 라이브러리 설치를 막기 위해, <code>torch</code>와 <code>torchvision</code> 뒤에 cpu버전까지 확실하게 명시해주었다. </p>
<p>그리고 빌드....</p>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/d09b01e4-f721-4ee0-b78a-91ad86a37c1f/image.png" alt=""></p>
<pre><code>C:\Users\khsta\OneDrive\바탕 화면\Python\fish_application&gt;docker build -t fish-app .
[+] Building 685.7s (10/10) FINISHED                                                                                                          docker:desktop-linux
</code></pre><p><code>FINISHED</code>문구와 함께 빌드가 성공했다.</p>
<p>*<em>결과 : *</em></p>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/d306cbcc-289b-4789-a289-5835e0461671/image.png" alt=""></p>
<p>도커 데스크탑에서도 만들어진 이미지를 볼 수 있다.</p>
<p>빌드가 완료되었으니 <code>docker run -p 8080:8080 fish-app</code>명령어로 람다 환경을 모방한 서버를 내 컴퓨터의 8080포트로 띄운다.</p>
<p>그리고 <code>docker run -p 8080:8080 -e AWS_DEFAULT_REGION=ap-northeast-2 fish-app</code> 도커 실행 명령어를 실행하면(<code>-e AWS_DEFAULT_REGION=ap-northeast-2</code>는 리전 환경 변수 정해주는 것)</p>
<pre><code>C:\Users\khsta\OneDrive\바탕 화면\Python\fish_application&gt;curl -XPOST &quot;http://localhost:8080/2015-03-31/functions/function/invocations&quot; -d &#39;{}&#39;
{&quot;errorMessage&quot;: &quot;Unable to unmarshal input: Expecting value: line 1 column 1 (char 0)&quot;, &quot;errorType&quot;: &quot;Runtime.UnmarshalError&quot;, &quot;requestId&quot;: &quot;57fac8c5-7236-4834-a764-ef6f3dd447b1&quot;, &quot;stackTrace&quot;: []}</code></pre><p>다음과 같은 결과가 나온다. 에러메시지이긴 한데 <code>Runtime.UnmarshalError</code>은 분석할 이미지가 함께 오지 않았다는 뜻이라서 일단 서버가 돌아가고 있다는 뜻이다.</p>
<h2 id="ecr-설치">ECR 설치</h2>
<p><code>ECR</code>은 도커 컨테이너 전용 저장소라고 생각하면 된다. 람다에 도커로 배포하려면 무조건 <code>ECR</code>을 거쳐야 한다.</p>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/5c13ea23-6a26-410a-a947-d929fffc8beb/image.png" alt="">
aws 콘솔에서 ECR 리포지토리를 하나 생성해준다.</p>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/09d08c25-0f78-44e4-8bf2-a0e82e6f2851/image.png" alt=""></p>
<p>AWS CLI로 IAM 사용자 로그인을 해 주고</p>
<p>IAM 권한에 <code>EC2ContainerRegistryFullAccess</code>를 추가해주면 된다.(내 IAM계정은 <code>AdministratorAccess</code>가 부여되어 있어 필요없음)</p>
<p>그리고 ECR콘솔창에 있는 <code>푸시 명령 보기</code>버튼을 누르면 
<img src="https://velog.velcdn.com/images/hyun_ji/post/d480932b-a96e-4fe4-9a49-95ad0059937b/image.png" alt="">
다음과 같은 명령어들을 안내해주는데 그냥 복붙하면 된다.(2번은 이미 빌드했으니 패스)</p>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/eab4a83f-1c1f-47ca-b0b8-83d0c991fff5/image.png" alt="">
이미지가 ECR에 잘 올라갔다.</p>
<h2 id="람다-함수-생성">람다 함수 생성</h2>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/2b8aad77-5de2-4f67-920f-a0e6d5eb9836/image.png" alt="">
이제 람다 함수를 생성해보자. 컨테이너 이미지 옵션을 선택하고 함수 이름을 입력해준뒤 방금 리포에 올린 이미지를 선택해주면 된다. </p>
<h3 id="🛠️-troubleshooting-lambda의-not-supported-이미지-에러-해결">🛠️ Troubleshooting: Lambda의 &#39;Not Supported&#39; 이미지 에러 해결</h3>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/bde39d2b-3635-43c2-a78f-6d0eed19597e/image.png" alt="">
콘솔에서 람다 함수를 생성하려는데 에러 창이 떴다</p>
<p><strong>원인</strong> : 
<a href="https://docs.aws.amazon.com/ko_kr/lambda/latest/dg/images-create.html#images-reqs">AWS 람다 공식문서 요구사항</a> 탭을 보면 </p>
<blockquote>
<p>Lambda는 다중 아키텍처 기본 이미지를 제공합니다. 하지만 함수에 대해 빌드하는 이미지는 아키텍처 중 하나만 대상으로 해야 합니다. <strong>Lambda는 다중 아키텍처 컨테이너 이미지를 사용하는 함수를 지원하지 않습니다.</strong></p>
</blockquote>
<p>라고 기재되어 있다. 
도커의 최신 빌드 엔진인 <code>buildx</code>의 <code>v0.10</code> 이상 버전에서는 여러 CPU 환경을 지원하기 위해 기본적으로 <code>OCI image index</code> 형식을 사용해 <strong>멀티 플랫폼 이미지를 빌드하는데</strong>, 위 문서 내용처럼 람다는 다중 아키텍처 컨테이너 이미지(=멀티 플랫폼 이미지)를 지원하지 않아 <strong>manifest not supported</strong> 에러가 발생한것이다.</p>
<p>최신 <code>Docker Buildx</code> 환경에서는 기본적으로 <code>OCI Image Index</code> 형식을 사용하며, 빌드 메타데이터인 Provenance를 포함시킨다. 하지만 AWS Lambda는 단일 플랫폼의 Docker V2 Manifest 포맷만 지원하기 때문에, manifest not supported 에러가 발생한것이다. </p>
<p><strong>해결 방법</strong> : 
간단히 메타데이터인 Provenance를 제외시키는 <code>--provenance=false</code> 옵션을 붙여 형식을 강제 변환시킨 후 다시 빌드해주면 단일 이미지 포멧으로 다시 빌드된다. </p>
<pre><code>docker build --platform linux/amd64 --provenance=false -t fish-app:latest .
</code></pre><p>위 명령어로 다시 이미지를 구워준다.
<img src="https://velog.velcdn.com/images/hyun_ji/post/dbd2d4ea-5d2d-407e-9ccd-b0d33fe22a5d/image.png" alt="">
<strong>결과</strong>: 
<img src="https://velog.velcdn.com/images/hyun_ji/post/7f4b605f-3c80-433c-8347-9a711f093393/image.png" alt="">
굿. 람다함수가 잘 생성되었다.</p>
<p><strong>람다 함수 설정 변경:</strong></p>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/e1c83001-608c-43e8-bcb5-12608753c0dd/image.png" alt=""></p>
<p>함수가 생성되었으니 설정을 조금 바꿔준다. 무거운 모델이 돌아가야 하기 때문에 메모리와 제한 시간을 2048MB, 1분으로 늘려주었다.(기본설정은 128MB에 3초)</p>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/ab578536-f192-42d0-bb41-ca408f154f18/image.png" alt=""></p>
<p>또한, AWS Lambda 콘솔 → [구성] → [권한(Permissions)] → 역할 이름(Role name) → [권한 추가] → [정책 연결]에서 <code>AmazonDynamoDBFullAccess_v2</code> 권한을 검색하여** dynamoDB에 대한 권한을 추가해 준다.**</p>
<h2 id="api-게이트웨이">API 게이트웨이</h2>
<p><del>인터넷 게이트웨이랑 다른거다.</del>
람다는 정확히 말하면 일반 서버처럼 데이터를 stream형식으로 받는게 아니라 이벤트에 의해 실행되는 <strong>함수</strong>이기 때문에 <strong>클라이언트와 TCP연결을 맺지 않는다.</strong> 
이때 람다의 앞에서 <code>API 게이트웨이</code>가 대신 클라이언트와 TCP연결을 맺고, 들어온 HTTP 요청을 JSON 이벤트 객체로 변환하여 람다에 넘겨준다.</p>
<blockquote>
<p><strong>왜 JSON으로 변환해줘야 함?</strong>
람다(Lambda) 함수가 &#39;입력값&#39;으로 받을 수 있는 유일한 표준 규격이 JSON 형식의 이벤트 객체이기 때문에.</p>
</blockquote>
<p>_물론 2022년에 <strong>람다 함수 URL(Function URL)</strong>이라는 람다 내장 기능이 추가되어, API 게이트웨이 없이 람다만으로도 고유한 URL 엔드포인트를 생성하고 JSON 규격(Payload Format 2.0)으로 변환하는게 가능해졌지만, API 게이트웨이는 JSON 이벤트 형식 변환 외에도 요청 속도 제한(Throttling), 캐싱, 그리고 보안 및 인증 등 여러 기능을 수행해주기 때문에 앵간하면 람다랑 같이 쓰는 게 좋다. _</p>
<p><a href="https://docs.aws.amazon.com/ko_kr/apigateway/latest/developerguide/api-gateway-basic-concept.html">[API Gateway 개념 설명] </a></p>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/6e0f4b47-67a1-4b2c-9dca-a77b7b81e78c/image.png" alt="">
람다 함수의 트리거 추가를 눌러 API 게이트웨이를 생성해준다.</p>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/7bd0318d-2b99-4af9-84b4-c67d33f0a188/image.png" alt=""></p>
<h3 id="🛠️-troubleshooting-api-게이트웨이-스테이지-경로-주소-오류">🛠️ Troubleshooting: API 게이트웨이 스테이지 경로 주소 오류</h3>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/99467519-2901-4be3-b702-b35097e00b70/image.png" alt=""></p>
<p>생성된 주소로 Postman을 이용해서 형식을 맞춰 요청을 보내봤지만 계속해서 
<code>404 Not Found</code> 응답이 뜨며 2ms대의 짧은 실행 이후 끊겼다.</p>
<p><strong>원인 :</strong>
Gemini에게 물어보니 <code>&quot;detail&quot;: &quot;Not Found&quot;</code> 메시지는 FastAPI 프레임워크가 공식적으로 내보내는 404 에러 양식이기 때문에, 람다 함수쪽이 문제가 아니라 FastAPI 쪽의 문제일 확률이 높고, 이 경우엔 <strong>서버(FastAPI)가 요청을 받자마자 담당경로가 아니기 때문에 바로 거절을 한 것</strong> 같다고 했다.</p>
<p>API 엔드포인트 주소를 확인해보면 <code>https://아이디.execute-api.리전.amazonaws.com/default/{proxy+}</code> 라고 되어있다. 호스트주소 바로 뒤에 <code>default</code>라는 경로가 붙은 것이 보인다. (<code>{proxy+}</code>는 FastAPI의 api엔드포인트가 오는 곳)</p>
<p>문제는 FastAPI의 라우팅 방식이다. FastAPI는 호스트 주소 이후의 전체 경로인 <code>/default/{proxy+}</code>를 자신의 라우팅 테이블과 대조한다. 하지만 내 서버 코드는 <code>/analyze</code>처럼 루트 경로를 기준으로 설계되어 있다 보니, 앞에 붙은 /default라는 불청객 때문에 경로를 찾지 못하고 404 Not Found를 뱉으며 즉시 종료되었던 것이다.</p>
<blockquote>
<p>그렇다면 저 <code>default</code>의 정체는 무엇인가?</p>
</blockquote>
<p>API Gateway는 개발용(dev), 테스트용(test), 실제 배포용(prod)등의 <strong>버전(Stage)</strong>을 관리하기 위해 <code>https://아이디.execute-api.리전.amazonaws.com/스테이지명/~~</code> 같이 스테이지 이름을 주소에 강제로 포함한다.</p>
<p>현재 나의 코드 같은 경우 <strong>기본(default)</strong> 스테이지를 사용 중이라 주소 중간에 <code>/default</code>가 자동으로 박혀버린 것이다.</p>
<p><a href="https://docs.aws.amazon.com/ko_kr/apigateway/latest/developerguide/http-api-stages.html">[API 게이트웨이의 스테이지]</a></p>
<p><strong>해결 방법1:</strong>
요청 코드 주소 자체를 바꾸어주는 방법이다. </p>
<p>API 게이트웨이의 주소에 맞추어, FastAPI의 루트 주소에 <code>/default</code>를 포함해준다. 
기존의 <code>app = FastAPI()</code>를 다음과 같이 바꾸어준다. </p>
<pre><code class="language-python">app = FastAPI(root_path=&quot;/default&quot;)</code></pre>
<p>이러면 모든 주소 앞에 <code>/default</code>가 자동으로 붙게되어 <code>/default/{proxy+}</code>형식을 만족시킨다.</p>
<p>*<em>해결 방법2: *</em>
람다에 접속하는 URL을 아예 다른 주소로 사용하는 방법이다.</p>
<p>위에서 명시했듯이 <strong>람다 함수 URL</strong>을 사용하면, API 게이트웨이 없이 람다만으로도 고유한 URL 엔드포인트를 생성하고 자체적으로 JSON으로 변환하는것이 가능하다.</p>
<p>람다 함수의 [구성] 탭에서 쉽게 함수 URL을 생성할 수 있다. </p>
<p>두 방법으로 만들어진 주소들을 모두 테스트 해봤더니 <code>200 OK</code> 결과와 함께 정상적으로 분석 결과가 나온것을 볼 수 있었다.
<img src="https://velog.velcdn.com/images/hyun_ji/post/15d4a8e1-d99e-4d64-a08b-5644d13c5350/image.png" alt=""></p>
<p>최종적으로는 API 게이트웨이 대신 함수 URL을 선택했는데, 그 이유로는</p>
<ol>
<li><p>API 게이트웨이의 스테이지 경로 문제가 해결되어 FastAPI의 라우팅이 꼬이는 문제가 다시 생길 걱정이 없고</p>
</li>
<li><p>API Gateway는 요청 횟수에 따라 별도의 요금이 발생하지만, 함수 URL은 완전히 무료이기 때문에 비용 걱정이 없으며</p>
</li>
<li><p>API Gateway는 최대 타임아웃이 29초로 제한되어 있어, 무거운 인공지능 모델(YOLO 등)을 돌릴 때 시간이 부족할 수 있는데, 반면 함수 URL은 람다 자체의 최대 타임아웃인 15분까지 그대로 사용할 수 있어 타임아웃 제한이 완화된다. </p>
</li>
</ol>
<p>물론 API Gateway를 통해 인가(Authorizer)나 유량 제어(Throttling) 등 정교한 기능을 활용할 수 있으나, 나의 프로젝트 같은 경우 <strong>1. 서비스의 복잡도가 낮고 2. 별도의 사용자 인증 및 인가를 요구하지 않는 서비스</strong>이기 때문에 현재 프로젝트의 규모와 빠른 프로토타이핑이라는 목적을 고려했을 때, 불필요한 복잡성을 줄이고 직관적인 아키텍처를 유지할 수 있는 함수 URL이 더 적합하다고 판단하여 최종 선택하게 되었다.</p>
<h2 id="최종-프로젝트-아키텍처">최종 프로젝트 아키텍처</h2>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/158f0fbe-99e5-4de1-8342-374811b52cf1/image.png" alt=""></p>
<blockquote>
<p>추가적인 성능 개선은 해당 포스팅에서 -&gt; <a href="https://velog.io/@hyun_ji/Lambda-Cold-Start-%ED%95%B4%EA%B2%B0">[Lambda Cold Start 해결]</a> </p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[막학기 이공계 국가우수장학금 수령]]></title>
            <link>https://velog.io/@hyun_ji/%EB%A7%89%ED%95%99%EA%B8%B0-%EC%9D%B4%EA%B3%B5%EA%B3%84-%EA%B5%AD%EA%B0%80%EC%9A%B0%EC%88%98%EC%9E%A5%ED%95%99%EA%B8%88-%EC%88%98%EB%A0%B9</link>
            <guid>https://velog.io/@hyun_ji/%EB%A7%89%ED%95%99%EA%B8%B0-%EC%9D%B4%EA%B3%B5%EA%B3%84-%EA%B5%AD%EA%B0%80%EC%9A%B0%EC%88%98%EC%9E%A5%ED%95%99%EA%B8%88-%EC%88%98%EB%A0%B9</guid>
            <pubDate>Tue, 07 Oct 2025 08:46:12 GMT</pubDate>
            <description><![CDATA[<p>4학년 2학기 막학기가 되고 마지막 이공계 국가우수장학금을 수령하였다. 
<img src="https://velog.velcdn.com/images/hyun_ji/post/0764e77d-3faf-4538-bae5-b80aec9c8e35/image.png" alt=""></p>
<p>나는 2년 지원 유형이라 3학년 1학기 부터 장학금을 받았다. 장학금 합격 발표가 꽤나 늦게 났던지라 3학년 1학기 장학금은 선감면 대신 추후에 계좌로 입금되었다. 계절학기를 제외한 이후의 정규학기들은 전부 전액면제로 다닐 수 있었다. </p>
<p>이로서 대학교 8학기중 1-1학기 성적장학금을 포함하여 3학년,4학년까지 총 5번의 장학금을 수혜받았다. (감사합니다 열심히 살게요)  </p>
<p>사실 당시에는 전혀 붙을 기대를 하지 않았는데 붙었다는 소식을 듣고, 부모님에게 부담을 덜어드릴 수 있어서 굉장히 기뻤던 기억이 있다. 
<del>소식 듣고 나보다 좋아하심</del></p>
<p>근데 졸업하고 일정기간안에 관련분야로 취업해서 종사하지 않으면 반환해야 된다고 말씀드리니, 취업 할 수 있겠냐고 걱정하시더라 아놔.... (사실 나도 조금 걱정됐음)</p>
<p>근데 최근에 그 의무사항도 폐지되었다고 들었다. 그래도 규정이 언제 또 바뀌었을지 모르니 본인에게 해당되는 요건을 꼼꼼히 잘 살펴보고 확인하자.</p>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/4f79dd19-2d4e-48f4-b1d4-b651444e4c28/image.png" alt=""></p>
<p>(상장이랑 메달도 준다.)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[네트워크 OSI 7Layer 총정리 ]]></title>
            <link>https://velog.io/@hyun_ji/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-OSI-7Layer-%EC%B4%9D%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@hyun_ji/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-OSI-7Layer-%EC%B4%9D%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Tue, 07 Oct 2025 07:28:45 GMT</pubDate>
            <description><![CDATA[<h2 id="osi-7layer-종류-및-특징">OSI 7Layer 종류 및 특징</h2>
<p><strong>Physical Layer</strong></p>
<ul>
<li>통신 주체끼리 연결이 수립되면 연결이 기하급수적으로 많아짐</li>
<li>Hub로 다수의 기기를 연결 </li>
<li>Hub : 받은 신호를 묶여있는 다른애들한테 뿌림(확성기 같이)<ul>
<li>1:1 통신 불가. 오로지 브로드캐스팅만...</li>
<li>충돌 제어 불가</li>
</ul>
</li>
</ul>
<p><strong>Data Link Layer</strong></p>
<ul>
<li>Frame 단위</li>
<li>CSMA/CD(충돌 방지) </li>
<li>Mac 주소라는게 생김<ul>
<li>고유값, 변동 X, 기계에 새겨진 번호같은거(IP는 변동)</li>
<li>유니캐스트(1:1 통신) 가능해짐</li>
</ul>
</li>
<li>Switch를 사용해서 특정 네트워크 내에서 구간을 따로 관리<ul>
<li>포트별로 디바이스(MAC주소)가 매핑된 테이블이 있음 </li>
</ul>
</li>
</ul>
<p><strong>Nerwork Layer</strong></p>
<ul>
<li>Packet 단위</li>
<li>로컬 네트워크간의 연결을 지원 </li>
<li>IP : 인터넷(네트워크와 네트워크 간)에서의 주소라고 생각하면 됨<ul>
<li>MAC주소는 로컬 네트워크 안에서의 주소</li>
<li>수시로 IP 주소 변동됨</li>
</ul>
</li>
<li>CIDR : 네트워크 영역을 나누기 위해 IP주소를 묶는 방식<ul>
<li>ex) 172.0.1.0/24 b -&gt; 네트워크 주소 + 호스트 주소 / 네트워크 주소 bit수</li>
</ul>
</li>
<li>Router : 네트워크간 패깃을 주고받는 장치<ul>
<li>IP 대역별 최적 경로 수집 및 관리<ul>
<li>로컬 네트워크 주소 아니면 라우터로 전달(외부로 보낸다는 뜻)</li>
</ul>
</li>
</ul>
</li>
<li>but. 한 번에 하나의 애플리케이션만 통신 가능, 인터넷상 패킷 손실 가능성 있음</li>
</ul>
<p><strong>Transport Layer</strong></p>
<ul>
<li><p>Segment 단위</p>
</li>
<li><p>통신 주체끼리의 데이터 신뢰성 확보하는 층(패킷 순서, 에러 처리 등)</p>
</li>
<li><p>Port : IP 프로토콜에서 패킷을 올바른 프로세스로 라우팅 하기 위한 논리적 단위</p>
<ul>
<li>목적지 기기에 도착하더라도 여러 프로세스들이 돌아가고 있음</li>
<li>포트 번호를 통해 알맞은 프로세스를 찾을 수 있음</li>
<li>1~1023번은 보통 잘 알려진 서비스(well-known ports)를 위해 예약되어 있음 -&gt; 사내망이나 서버 테스트용 임의 포트 지정할거면 저거 이외에 다른거 써라(ex.8080, 3000, 9000)</li>
<li>다른 외부 서버에 요청을 보낼 때 사용하는 포트는 보통 임시로 할당받는 동적 포트를 사용 </li>
</ul>
</li>
<li><p>TCP : 신뢰성 있는 전송 보장</p>
<ul>
<li>3 way handShake를 통해 패킷의 순서나 전달여부등을 보장</li>
<li>HTTP/HTTPS/FTP/SSH/DNS 등에 사용</li>
</ul>
</li>
<li><p>UDP : 빠르고 간단하게 데이터를 주고 받을 수 있는 방법을 정의</p>
<ul>
<li><p>TCP와 달리 데이터의 무결성, 순서, 전달여부 노상관</p>
</li>
<li><p>그래서 신뢰성은 낮지만 속도가 빠르고 세그먼트 용량이 작음</p>
</li>
<li><p>DNS/DHCP/VoIP(음성통화) 등에 사용</p>
<h3 id="내-노트북에서-사용중인-port-살펴보기">내 노트북에서 사용중인 Port 살펴보기</h3>
<p>windows는 터미널 창에 <code>netstat -ano</code>라고 입력하면 현재 사용중인 포트들을 볼 수 있다. 내 노트북 열린 포트들도 뭐 있나 궁금해서 한 번 살펴보았다.</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/986f1f7b-1802-4c4c-93e5-f3d3471b7de2/image.png" alt=""></p>
<p><del>뭐 이리 많아</del>
밑에도 한참 더 있었는데 너무 길어서 잘랐다.
궁금해서 상위 2개 포트가 어따 쓰이나를 검색해봤는데 135번 포트는 주로 Windows 운영체제에서 RPC(원격 프로시저 호출) 서비스에 사용되고, 445번 포트는 SMB(서버 메시지 블록) 프로토콜에 사용되며, 네트워크 상에서 파일 및 프린터 공유를 가능하게 하는 Microsoft-DS 서비스에 사용된다고 한다. </p>
</li>
</ul>
<p><strong>Session Layer</strong></p>
<ul>
<li>통신 주체끼리 연결을 유지할 수 있는 방법을 정의</li>
<li>HTTP Cookie 같은것 </li>
</ul>
<p><strong>Presentation Layer</strong></p>
<ul>
<li>Application layer에서 사용할 수 있도록 데이터를 파싱, 압축 해제 함</li>
</ul>
<p><strong>Application Layer</strong></p>
<ul>
<li>실제 받은 데이터를 어떻게 처리할 것인지를 정의 </li>
<li>Http의 경우 Method(ex.Get, Post, Put), Header등을 처리</li>
</ul>
<h2 id="라우터를-통한-네트워크간-통신-과정">라우터를 통한 네트워크간 통신 과정</h2>
<ol>
<li><p>사용자가 <a href="http://www.naver.com%EC%9D%84">www.naver.com을</a> 입력함</p>
</li>
<li><p>DNS서버를 통해 해당 도메인과 매핑된 IP주소를 반환</p>
</li>
<li><p>목적지 주소를 알았으니 IP패킷 생성</p>
</li>
<li><p>목적지 IP가 같은 네트워크인지 확인</p>
</li>
<li><p>다른 로컬 네트워크라면 라우터(게이트웨이)의 IP로 ARP패킷 브로드캐스트</p>
</li>
<li><p>라우터가 자신의 MAC주소를 알려줌. 해당 주소로 패킷 전송</p>
<ul>
<li>이 때 전송되는 패킷의 목적지 IP : 최종 목적지 IP, 목적지 MAC : 라우터의 MAC</li>
</ul>
</li>
<li><p>라우터에 패킷이 도착하고, 라우터가 목적지 IP(최종 목적지) 확인</p>
</li>
<li><p>라우터가 라우팅 테이블을 통해 최적 경로 탐색</p>
</li>
<li><p>NAT로 출발지의 사설 IP 주소를 라우터 자신의 공인 IP주소로 변경. (다시 돌아올때 이거 보고 돌아옴)</p>
</li>
<li><p>라우터가 출발지, 목적지 MAC주소 변경해서 전달. (라우터의 MAC주소, 다음 라우터의 MAC주소로)</p>
</li>
<li><p>다음 라우터는 목적지 IP와 자신의 네트워크 IP 주소 대조. </p>
</li>
<li><p>도착할때까지 10번 반복(ARP 브로드캐스팅 통해서 계속 MAC주소 물어봄)</p>
</li>
<li><p>해당 로컬 네트워크 라우터에 패킷이 도착하면 라우터는 ARP 브로드캐스팅을 통해 최종 목적지의 MAC주소를 얻고 마지막 전송을 함.</p>
</li>
</ol>
<ul>
<li>패킷에 새겨진 MAC주소는 한 번 이동될때마다 계속 바뀌고 최종 IP 목적지주소는 절대 바뀌지 않음 </li>
</ul>
<h2 id="왜-ip주소-개념이-필요한가">왜 IP주소 개념이 필요한가?</h2>
<p>공부하면서 이런 의문이 들었는데 MAC주소 자체가 완전 고유한 번호인데 얘만을 이용해서도 경로를 찾을 수 있지 않나? 굳이 IP주소를 도입해서 왜 주소를 2개로 만들지? 라는 생각이 들었다.</p>
<p>하지만 MAC주소만을 이용해 통신을 하게 되면 전 세계적 통신이 불가능했을 것이다.</p>
<p>우선, MAC주소는 특정 네트워크에 속해 있지 않아서, 하나하나가 인터넷이라는 방대한 공간의 노드가 되어 버린다. 이렇게 되면 효율적 경로를 통해 찾아가는 것이 불가능해지고 하나하나 대조를 해봐야 한다. 전 세계의 모든 기기에....
그리고 브로드캐스트 한 번 했다간 전 세계 사람의 컴퓨터에 요청이 간다...</p>
<p>IP주소는 이러한 단점을 극복하기 위해 만들어진 논리적 주소라고 할 수 있으며 기기들끼리 묶어 로컬 네트워크를 구성할 수 있게 해준다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹서버? WAS? 톰캣? 아파치? ]]></title>
            <link>https://velog.io/@hyun_ji/%EC%9B%B9%EC%84%9C%EB%B2%84-WAS-%ED%86%B0%EC%BA%A3-%EC%95%84%ED%8C%8C%EC%B9%98</link>
            <guid>https://velog.io/@hyun_ji/%EC%9B%B9%EC%84%9C%EB%B2%84-WAS-%ED%86%B0%EC%BA%A3-%EC%95%84%ED%8C%8C%EC%B9%98</guid>
            <pubDate>Wed, 24 Sep 2025 05:03:14 GMT</pubDate>
            <description><![CDATA[<h2 id="웹서버가-뭘까">웹서버가 뭘까?</h2>
<p>위키피디아에 명시된 정의를 그대로 가져오자면</p>
<pre><code>웹 서버(영어: web server)는 웹 콘텐츠(영어판) 배포를 위해 만든 프로토콜 
즉, HTTP 또는 HTTPS를 통해 클라이언트의 요청을 수행하는 서비스 프로그램 및 기반 하드웨어다.</code></pre><p>라고 적혀있다.</p>
<p>당연히 이해가 가지 않으니 조금씩 해석해보자. 
<code>웹 콘텐츠 배포를 위해 만든 프로토콜</code> 
-&gt; 이건 우리가 평소 인터넷에서 보는 보는 사이트나 서비스페이지같은 콘텐츠들을, 불특정 다수가 이용할 수 있도록(배포) 만든 어떠한 통신 약속(프로토콜)이라는 뜻이다.</p>
<p><code>HTTP 또는 HTTPS를 통해 클라이언트의 요청을 수행하는 서비스 프로그램 및 기반 하드웨어</code> 
-&gt; 이 말은 <code>HTTP</code>와 <code>HTTPS</code>라는 어떠한 통신 규칙(프로토콜)이 있는데 이것을 사용해 통신 요청을 보내는 쪽(클라이언트)의 요청을 수행하는 프로그램이나 컴퓨터 기계(하드웨어)라는 뜻이다.</p>
<p>좀 풀어서 설명하긴 했는데 아직도 뭔가 모호하다.</p>
<p>정말 쉽게 요약하자면, 우리가 어디 사이트에 접속할때, 그 사이트의 콘텐츠를 그려내기 위해 html 파일 같은것들이 필요하다. 이러한 사이트의 파일들을 제공해주는 역할을 하는것이 웹서버이다. </p>
<p>웹서버의 종류에는 pache(아파치), Nginx(엔진엑스)등이 있다. </p>
<h2 id="was가-웹서버랑-다른-건가">WAS가 웹서버랑 다른 건가?</h2>
<p>다르다.</p>
<p>고대의 인터넷에는 웹서버만이 존재했다... 모든 사용자에게 똑같은 화면만을 보여주는 <code>정적 웹(static web)</code>만이 있었다. </p>
<p>하지만 인터넷이 널리 보급되고 사용자 수가 폭발적으로 늘며 웹 서버 만으로는 다양한 서비스를 제공할 수 없었다. 예를 들어 사용자마다 다른 정보를 표시하는 일, 실시간 데이터를 띄워서 보여주는 일. 모두 고정된 정적 문서를 제공하는 웹 서버에서는 불가능한 일이었다. </p>
<p>이때 등장한 것이 웹 애플리케이션 서버 WAS(Web Application Server)이다. Web Server는 기존이랑 똑같은데 그 사이에 Application이라는 단어가 들어갔다. 애플리케이션이 무엇인가? 애플리케이션은 특정 작업을 실행되도록 프로그래밍된 소프트웨어이다. 다시말해, 기존의 딱딱한 웹 서버에 소프트웨어 기능이 붙은 서버이다.</p>
<p>WAS는 <code>정적 웹</code> 페이지와 반대되는 <code>동적 웹</code>페이지를 생성할 수 있다. 
<code>동적 웹</code>은 말 그대로 실시간으로 html문서를 만들어 낼 수 있었다.</p>
<p>이러한 WAS와 웹 서버는 이후 백엔드에서 상호 보완적인 관계를 이루게 되는데 특정 로직이 없는 페이지는 정적 페이지는 웹서버가, 로직이 필요한 동적 페이지는 웹서버가 WAS로 요청을 전달해 WAS가 대신 처리할 수 있도록 한다. </p>
<p>우리가 많이 들어본 Apache Tomcat (아파치 톰캣)또한 WAS의 한 종류이다. </p>
<h2 id="웹서버-만들어보기">웹서버 만들어보기</h2>
<p>이제 FastAPI를 이용해 간단하게 서버를 만들어보자. </p>
<pre><code class="language-python">from fastapi import FastAPI

app = FastAPI()

@app.get(&quot;/&quot;)
async def read_root():
    return {&quot;message&quot;: &quot;Server is connected!&quot;}</code></pre>
<p>이 5줄의 코드를 통해 현재 웹서버가 만들어졌다!
FastAPI는 Uvicorn이라는 WAS 역할을 주로 수행하는 웹서버와 함께 쓰인다.</p>
<p>이제 <code>필자의컴퓨터IP주소:포트번호</code> 를 브라우저 주소창에 입력하면</p>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/9c99b283-b4be-432c-9b7b-c8fe9a30d7cb/image.png" alt=""></p>
<p>다음과 같이 서버로부터 응답을 받을 수 있다!</p>
<p>이제 <code>동일한 네트워크</code>에서 해당 주소를 입력하면 어떤 기기에서든 동일한 응답값을 받을 수 있다!</p>
<hr>
<p>잠깐, <code>동일한 네트워크</code>라는 게 무슨 뜻일까?</p>
<p>네트워크를 공부한 사람을 알겠지만, 현재 내 IP주소는 공유기가 할당해준 고유한 번호, 말하자면 정확한 <strong>도로명 주소가 아닌 철수네 집, 영희네 집 같은 임시 주소번호</strong>이다. </p>
<p>당연히 우리 집 근처에 사는 사람들은 영희네 집, 철수네 집(IP 내부 주소)라고만 해도 어디인지 알겠지만, 저 멀리 미국 텍사스에 사는 제임스한테 영희네 집이라고 한다면 당연히 어딘지 모른다. 영희라는 이름을 가진 사람이 한 두 명도 아니고...(IP 내부 주소는 서로 다른 네트워크에서 중복이 가능하기 때문)</p>
<p>그래서 같은 공유기에 연결된 기기에서는 방금 만든 서버에 접속이 잘 되지만, 
그 외의 외부 기기들에서는 <code>필자의IP주소:포트번호</code>를 입력해봤자, IP주소가 영희네 집 같은 알 수 없는 주소이기 때문에 입력해도 접속이 안 된다. </p>
<h3 id="그럼-어떡해">그럼 어떡해!!</h3>
<p>나는 전세계적으로 서비스를 하는 초대형 글로벌 서비스를 만들고 싶은데 이러면 구멍가게 밖에 못 만들지 않나!! 싶지만 우리가 많이 들어본 AWS 같은 클라우드 서버를 사용하여 도로명주소같은 공인 IP주소를 할당받을 수 있다. </p>
<p>공인 IP주소는 <code>대한민국 00시 00동</code>같이 전 세계 어디에서나 인터넷을 통해 접근할 수 있는 공식적인 주소이다. 그래서 AWS 서버를 이용한다면 다른 컴퓨터나 스마트폰이 어떤 네트워크에 있든 상관없이 항상 접속할 수 있다.</p>
<p>물론 <code>포트 포워딩</code>등을 사용해 클라우드 서버를 사용하지 않고도 내 컴퓨터 자체를 외부에서 바로 접속이 가능하게 만드는 방법도 있긴 하다. 다만 이 방법은 보안도 취약하고 설정도 좀 만져야해서 꽤나 번거롭다고...</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTTP, TCP/UDP/IP, Socket]]></title>
            <link>https://velog.io/@hyun_ji/HTTP-TCPUDPIP-Socket</link>
            <guid>https://velog.io/@hyun_ji/HTTP-TCPUDPIP-Socket</guid>
            <pubDate>Wed, 24 Sep 2025 05:02:50 GMT</pubDate>
            <description><![CDATA[<h2 id="http-request와-response의-구조">HTTP Request와 Response의 구조</h2>
<p>HTTP는 HyperText Transfer Protocol의 약자로,
웹에서 클라이언트(브라우저 등)와 서버 간에 데이터를 주고받기 위한 통신 규약(Protocol)이다.</p>
<p>쉽게 말하면 웹에서 문서, 이미지, 영상 등을 요청하고 응답받기 위한 약속된 통신 방법.</p>
<p>HTTP는 요청-응답을 기반으로 하기 때문에 HTTP의 메시지 형태도 요청/응답 2가지 이다.</p>
<p>클라이언트가 HTTP request를 서버에 보내면 서버는 HTTP response를 보내는 구조</p>
<p><strong>HTTP Request Message</strong></p>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/3000c8ce-2ec7-4fb0-b4e6-cd12afaad310/image.png" alt=""></p>
<p><strong>HTTP Response Message</strong></p>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/802ea6ef-76de-46e3-a0a8-85f57e6a849e/image.png" alt=""></p>
<p><a href="https://developer.mozilla.org/ko/docs/Web/HTTP/Guides/Messages">https://developer.mozilla.org/ko/docs/Web/HTTP/Guides/Messages</a></p>
<h2 id="tcpudpip">TCP/UDP/IP</h2>
<h3 id="ip">IP</h3>
<p>IP는 컴퓨터(호스트)들 사이에서 데이터 패킷을 목적지까지 전달하는 역할을 하는 인터넷 계층의 핵심 프로토콜이다.</p>
<p><strong>IP의 핵심 역할</strong></p>
<ol>
<li>주소 지정 (Addressing)</li>
</ol>
<ul>
<li>각 컴퓨터(=노드)는 고유한 IP 주소를 갖는다.</li>
<li>예) 192.168.1.12, 8.8.8.8, 2001:db8::1</li>
</ul>
<ol start="2">
<li>패킷 전달 (Routing)</li>
</ol>
<ul>
<li><p>IP는 데이터를 여러 네트워크를 거쳐 목적지까지 전달함.</p>
</li>
<li><p>중간에 <strong>라우터(router)</strong>가 경로를 결정하고, 패킷을 넘겨줌.</p>
</li>
</ul>
<ol start="3">
<li>비신뢰성 (Unreliable)</li>
</ol>
<ul>
<li><p>IP는 전달만 해줄 뿐, 순서를 보장하지 않음.</p>
</li>
<li><p>중간에 패킷이 유실될 수도 있음</p>
</li>
<li><p>중복되거나 깨져도 복구 안 해줌</p>
</li>
<li><p>이건 TCP가 처리해줘야 하는 영역임!</p>
</li>
</ul>
<p><strong>IP 주소 체계</strong></p>
<ol>
<li><p>IPv4
32비트 주소 (총 2³² = 약 43억 개)
형식: 192.168.0.1</p>
</li>
<li><p>IPv6
128비트 주소 (엄청 많음… 340 언쩌구 x 10³⁶ 개)
형식: 2001:0db8:85a3:0000:0000:8a2e:0370:7334</p>
</li>
</ol>
<h3 id="tcp">TCP</h3>
<p>신뢰성 있는 데이터 전송을 보장하는 전송 계층(Transport Layer)의 대표적인 프로토콜. 데이터를 정확하고 순서대로 전송하는 데 최적화되어 있다.</p>
<p><strong>TCP의 핵심 특징</strong></p>
<ol>
<li><p>연결 지향(Connection-Oriented)
데이터를 보내기 전, 3-way handshake로 연결을 먼저 설정함</p>
</li>
<li><p>신뢰성 보장(Reliability)
데이터 유실 시 재전송, 순서 보장, 중복 제거</p>
</li>
<li><p>흐름 제어(Flow Control)
수신 측의 수용 능력을 고려해 전송량 조절</p>
</li>
<li><p>혼잡 제어(Congestion Control)
네트워크 상태에 따라 전송 속도 조절</p>
</li>
</ol>
<h3 id="udp">UDP</h3>
<p>빠르게 데이터를 전송하고 싶은 상황에서 사용하는 전송 계층(Transport Layer)의 대표적인 프로토콜</p>
<p>3-way handshake를 통해 연결 지향적인 통신을 하는 TCP와 달리 비연결성이라는 특징을 띈다. 클라이언트에서 서버로 마구 데이터를 보내는 것이다(받았다는 응답이 없어도).</p>
<p>이런 특성때문에 속도가 중요하고 약간의 데이터 손실은 크게 상관이 없는 통화나 스트리밍 서비스에 많이 사용되는 프로토콜이다.</p>
<h2 id="socket">Socket</h2>
<p>네트워크에서 통신하는 두 개의 프로그램 간의 연결을 설정하고 데이터를 전송하기 위한 인터페이스.</p>
<p>IP주소와 포트 번호를 사용하여 네트워크 상의 특정 컴퓨터와 특정 서비스에 연결된다</p>
<p><strong>소켓 통신</strong></p>
<p>소켓을 이용하여 두 프로그램(서버-클라이언트) 간에 데이터를 주고 받는 과정. TCP와 UDP같은 네트워크 프로토콜을 사용</p>
<p>소켓은 프로토콜,IP주소,포트 넘버로 정의된다.</p>
<p><img src="https://velog.velcdn.com/images/hyun_ji/post/e16d3d4b-223e-44a7-9446-7c70a15079c6/image.png" alt=""></p>
<h2 id="echo-server">Echo Server</h2>
<p>클라이언트가 전송해주는 데이터를 그대로 되돌려 전송해 주는 기능을 가진 서버를 말한다.</p>
<p>데이터를 그대로 되돌려 받기 때문에 클라이언트와 서버가 제대로 연결됐는지 테스트 용도로 많이 쓰인다.</p>
<h2 id="client와-server">Client와 Server</h2>
<p>통신이 일어날 때 항상 요청을 시작하는 쪽은 클라이언트, 응답을 처리하는 쪽은 서버</p>
<h2 id="session과-connection">Session과 Connection</h2>
<p>언듯 비슷한 개념 같지만 그 역할과 범위가 다르다.</p>
<h3 id="connection">Connection</h3>
<p>클라이언트와 서버 사이에 데이터를 주고받기 위한 통신 통로가 만들어진 것.</p>
<p>ex) TCP 3-way handshake를 통한 연결</p>
<h3 id="session">Session</h3>
<ul>
<li>클라이언트와 서버 간에 연속된 상호작용을 하나의 논리적인 단위로 묶은 것.</li>
<li>연결과는 다르게 &quot;이 사용자가 누구고, 어떤 상태인가&quot;를 추적할 수 있어야 함.</li>
<li>즉, 연결 위에서 유지되는 사용자 상태 추적 단위.</li>
<li>연결 위에서 유지되기 때문에 세션은 연결에 의존함.</li>
</ul>
<p>ex) 로그인 세션, 채팅 세션</p>
<h2 id="unicast--broadcast--multicast">Unicast / Broadcast / Multicast</h2>
<p><strong>Unicast</strong></p>
<ul>
<li>유니캐스트는 가장 일반적인 형태의 데이터 전송 방식으로, 한 개의 송신자가 한 개의 수신자에게 데이터를 전송하는 방식.</li>
<li>이 방식은 특정 대상과 1:1로 통신할 때 사용되며, 인터넷에서 흔히 볼 수 있는 데이터 전송 방식.</li>
<li>신자와 수신자 각각의 고유한 IP 주소를 사용</li>
<li>예) 웹 서버에서 클라이언트 컴퓨터로 웹 페이지를 보내는 경우</li>
</ul>
<p><strong>Broadcast</strong></p>
<ul>
<li>브로드캐스트는 한 개의 송신자가 네트워크 내 모든 장치에게 데이터를 동시에 전송하는 방식입니다.</li>
<li>같은 로컬 네트워크(LAN) 내의 모든 장치에게 신호나 메시지를 보낼 때 사용됨.</li>
<li>브로드캐스트에서는 네트워크 내 모든 장치에 데이터를 전송하기 위해 특정한 브로드캐스트 주소를 사용하는데, 반적으로 이 주소는 IP 네트워크의 마지막 주소로 설정됨</li>
<li>예) 네트워크 상의 모든 장치에게 서비스나 알림을 보내야 할 때</li>
</ul>
<p><strong>Multicast</strong></p>
<ul>
<li>멀티캐스트는 한 개의 송신자가 특정 그룹의 수신자들에게만 데이터를 전송하는 방식</li>
<li>데이터 스트리밍이나 실시간 데이터 전송에 효과적</li>
<li>멀티캐스트에서는 특정 멀티캐스트 그룹의 주소를 사용하고 이 주소는 224.0.0.0에서 239.255.255.255 범위에 속한다.</li>
<li>각 멀티캐스트 그룹은 고유한 IP 주소를 갖고 있으며, 그룹의 멤버만이 해당 주소로 보내진 데이터를 수신한다.</li>
<li>예) 라이브 비디오 방송이나 원격 회의 시스템</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[벡터 데이터베이스 ]]></title>
            <link>https://velog.io/@hyun_ji/%EB%B2%A1%ED%84%B0-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4</link>
            <guid>https://velog.io/@hyun_ji/%EB%B2%A1%ED%84%B0-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4</guid>
            <pubDate>Wed, 24 Sep 2025 05:01:14 GMT</pubDate>
            <description><![CDATA[<h2 id="벡터-데이터베이스란">벡터 데이터베이스란?</h2>
<p>텍스트, 이미지, 오디오 등의 데이터를 고차원 벡터로 표현해 저장하는 데이터베이스입니다. 오늘날 대부분의 데이터는 미리 정의된 구조나 형식이 없는 텍스트, 이미지, 오디오 등의 <strong>비정형 데이터</strong>이다. 이러한 형태가 일관적이지 않은 데이터들을 RDBMS에 넣기란 어려운 일이다.</p>
<p>이미지를 예로 들자면 이미지는 픽셀값이 저장되기 때문에 특정 이미지를 찾기 위해선 태그나 속성(ex. 고양이, 강아지)을 수동으로 입력해야 한다.</p>
<p>하지만 벡터 데이터베이스는 벡터 임베딩을 통해 인덱싱하고 저장하여 빠른 검색 및 유사성 검색이 가능하다. 다시말해
데이터의 의미 기반 유사성 검색이 가능해진다는 말이다.</p>
<p>그렇다면 벡터 임베딩은 무엇일까?</p>
<h2 id="벡터-임베딩">벡터 임베딩</h2>
<p>임베딩은 텍스트나 이미지 같은 데이터를 숫자 리스트, 즉 벡터로 바꾸는 방식이다. 이 벡터들은 데이터의 의미나 특성을 담고 있다.</p>
<p>의미가 비슷한 항목은 벡터 공간에서도 가까운 위치에 놓이게 되기 때문에 코사인 유사도나 유클리드 거리 등을 통해 서로 간의 유사도를 계산하여, 유사한 항목을 쉽게 찾을 수 있다.</p>
<p>벡터 데이터베이스는 이러한 임베딩들을 효율적으로 저장하고 검색하기 위해 만들어졌다. 기존 데이터베이스처럼 정확히 일치하는 값을 찾는 게 아니라, 유사한 것을 찾는 방식이다.</p>
<h2 id="word2vec">word2vec</h2>
<p>대표적인 단어 임베딩 신경망 모델이다. Word2Vec에는 CBOW와 Skip-gram이라는 두 가지 대표적인 구조가 있다.</p>
<p><strong>CBOW</strong></p>
<p>CBOW의 기본 아이디어는 주변 단어를 통해 주어진 단어를 예측하는 것이다.
총 단어가 c개라고 할 때 앞뒤로 c/2개의 단어를 통해 주어진 단어를 예측한다.</p>
<p><strong>Skip-gram 구조 (중심 → 주변 예측)</strong></p>
<p>Skip - gram은 CBOW와는 반대로 하나의 단어에서 여러 단어를 예측하는 방법이다. 즉 중심단어에서 주변단어를 예측하는 방식인데 CBOW보다 성능이 좋아 더 많이 쓰인다.</p>
<h2 id="벡터-인덱싱">벡터 인덱싱</h2>
<p>벡터 임베딩이 비정형 데이터를 숫자 벡터로 바꾸는 것이라면, 인덱싱은 벡터 공간에 위치한 이미 만들어진 벡터들끼리 비슷한 걸 빠르게 찾도록 구조화하는 작업이라고 할 수 있다.</p>
<p>벡터 DB에서 데이터를 검색할 때 KNN(기준 벡터와 가장 가까운 K개의 벡터를 찾아주는 방법)이 사용되는데, 벡터 인덱싱은 그 작업을 더 빠르게 하기 위해 사용된다.</p>
<p>벡터 공간을 쪼개거나, 비슷한 벡터끼리 그래프처럼 연결하거나, 트리 구조로 나누거나 이런 다양한 방식으로 비슷한 애들끼리 미리 묶어놓고 검색 범위를 줄인다.</p>
<p><strong>vector Indexing 기법</strong></p>
<ol>
<li><p><strong>Flat Index</strong></p>
<p>별도의 인덱싱 기법 없이 벡터를 저장하는 방법이다.
모든 벡터들과 유사도를 계산해 가장 높은 유사도를 지닌 벡터를 찾는다. 일반적으로 10000~50000개 정도이 벡터에서 적당한 성능과 높은 정확성을 얻을 수 있다.
하지만 실제로 이렇게 사용한다면 벡터 데이터가 더 많아 탐색 속도가 느려질 것이다.</p>
</li>
<li><p><strong>Random Projection</strong></p>
<p>랜덤한 벡터를 만들어서 원래 벡터와의 내적을 시켜서 차원을 줄
이는 방법이다.
원래 벡터와의 유사성도 유지할수 있고 고차원의 원래 벡터보다
탐색속도가 빠를수 있다고 한다.
하지만 Approxiamtion이기 때문에 정확도는 다소 떨어질수 있다.</p>
</li>
<li><p><strong>PQ (Product Quantization)</strong></p>
<p>원래 벡터를 균등하게 몇개의 서브벡터로 쪼개고, 각 서브벡터들
을 Quantization하여 크기를 줄이는 방법이다.
빠르고 정확도도 좋으며 큰 데이터셋에서 사용하기 좋은 기법이라
고 한다. Quantiztion으로 값을 표현하는데 사용하는 bit수를 줄
이기 때문에 메모리 사용량도 적고, 탐색 속도도 빠르다.
하지만 근본적으로 표현하는 숫자의 범위를 제한하는 방법이기 때
문에 정확도는 다소 떨어질수 있다.</p>
</li>
<li><p><strong>LSH (Locality-Sensitive Hashing)</strong></p>
<p>원래 벡터를 hashing 함수에 한번 돌려서 bucket에 매핑하고,
bucket에서 비교하여 찾는 방법이다.
유사도 검색을 할때도 쿼리문에 대한 벡터를 동일한 해싱함수를
사용해서 같은 버킷에 있는 벡터들하고만 비교하여 찾는다.
전체 데이터가 아니라 버킷에 있는 데이터안에서만 찾기 때문에
탐색속도가 빠르다.
하지만 버킷이 많을수록 정확도가 높아지겟지만, 더 많은 메모리
가필요하게 된다.</p>
</li>
<li><p><strong>HNSW (Hierarchical Navigable Small World
grahp)</strong></p>
<p>원래 벡터를 가지고 N개의 레이어를 가진 그래프를 생성하고, 그
래프를 기반으로 가장 가까운(유사도가 높은)결과를 찾아내는 방
법이다.</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[VCS, GIT 내부 동작 원리, SHA1알고리즘, Fork, Clone]]></title>
            <link>https://velog.io/@hyun_ji/VCS-GIT-%EB%82%B4%EB%B6%80-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC</link>
            <guid>https://velog.io/@hyun_ji/VCS-GIT-%EB%82%B4%EB%B6%80-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC</guid>
            <pubDate>Wed, 24 Sep 2025 05:00:45 GMT</pubDate>
            <description><![CDATA[<h2 id="vcs">VCS</h2>
<p>VCS는 Version Control System의 약자로 버전관리 시스템을 이르는 말이다. 이름 그대로 파일의 변화를 버전에 따라 기록하고 관리하기 때문에, 파일을 이전 상태로 쉽게 복구할 수 있다.</p>
<h2 id="git-동작-원리">git 동작 원리</h2>
<p>버전 관리 시스템의 한 종류인 git을 사용하기 위해선 처음에 <code>git init</code>이란 명령어를 실행해야한다. 새 디렉터리나 기존 디렉터리에서 <code>git init</code>을 실행하면 Git은 <code>.git</code>디렉터리를 생성하는데, 이 디렉터리에 Git이 저장하고 조작하는 파일들, 트리 구조, 커밋 등 모든 것이 저장된다.</p>
<p>git 공식 문서를 보면 config, description, hooks/등 여러 파일과 디렉토리가 생성되지만 그 중 가장 중요한 것은 역시 objects 디렉토리이다.(이 단계에서 index파일은 아직 생성되지 않는다.) objects디렉토리는 데이터베이스의 모든 콘텐츠와 버전 정보들을 객체 형태로 저장하는데 이 객체들의 종류는 다음과 같다.</p>
<ul>
<li>blob</li>
<li>tree</li>
<li>commit</li>
</ul>
<p>그럼 이제 git add 명령어와 git commit 명령어를 입력하면 각 객체들이 object파일에 어떻게 저장되는지 흐름을 따라가며 알아보자.</p>
<ol>
<li>git add</li>
</ol>
<ul>
<li>파일1이라는 제목의 파일을 작성한다.</li>
<li><code>git add 파일1</code>이라는 명령어를 실행할시, 파일1의 내용은 <strong>SHA1 해시알고리즘</strong>을 통해 일정한 길이의 문자열로 변환된다.</li>
<li>해시된 값의 앞에서 2글자는 object밑의 디렉토리명이 되고, 나머지 해시값은 해당 디렉토리 밑에 파일명이 된다. 이 파일이 <strong>blob객체</strong>다! 그리고 그 blob객체 파일의 내용에 파일1의 원본 내용이 들어있게 된다.</li>
<li>그러면 blob객체에는 실질적으로 파일의 내용만 담기게 되는데 파일제목같은건 어디에 저장될까? 바로 <strong>index</strong>파일에 저장된다.</li>
<li>index파일은 첫 add 실행시 생성되고, 여기에 Git의 스테이징 영역 정보를 저장한다. (스테이징 영역은 커밋 될 준비가 된 파일들이 위치한 영역을 뜻한다.)</li>
<li>index파일에는 해당 파일의 파일이름, 해시값등이 담기게 된다. 나중에 이 정보들을 보고 커밋을 할 수 있다.</li>
<li>간단하게 말하면 파일의 내용은 objects디렉토리 밑에, 파일의 이름은 index파일에 저장되게 된다.</li>
</ul>
<ol start="2">
<li>git commit</li>
</ol>
<ul>
<li>스테이징 영역에 담긴 파일을 커밋하면, 하위트리와 blob으로 구성된 tree 정보, 커밋한 사용자 정보, 커밋 메시지 등을 담은 commit객체가 만들어진다.</li>
<li>tree는 index정보를 바탕으로 만들어진다.</li>
<li>여기서 만약에 파일을 다시 수정하고 add 한 후에(여기서 index파일의 해시값도 당연히 업데이트 된다.) 또 commit을 하면 새로운 tree가 생성되는데, 만약 커밋한 파일들 중 내용이 변경되지 않은 파일이 있다면 그 파일은 이전 커밋에서의 해시값과 동일한 값을 가진다. 재활용하는 것이다. 그리고 새로 생성된 commit오브젝트는 첫 commit오브젝트를 parent로 가진다.</li>
</ul>
<p><a href="https://git-scm.com/book/en/v2/Git-Internals-Git-Objects">https://git-scm.com/book/en/v2/Git-Internals-Git-Objects</a>
공식문서에 git의 동작흐름이 이해하기 쉽게 그림과 예시와 함께 자세하게 잘 나와있다.</p>
<h2 id="sha1-zlib">SHA1? zlib?</h2>
<p>blob객체를 생성할때 SHA1알고리즘을 사용한다고 하는데 이게 뭘까?
SHA-1 해시 값은 40자리 고정 문자열로, 내용이 같으면 무조건 같은 해시 값이 나온다. 이걸 통해서 git은 해시 값을 통해 파일이 변경되었는지, 그대로인지 판단해서 변경된 파일(해시값이 바뀐 파일)만 새로 저장한다.</p>
<p>sha256은 SHA-1보다 더 강력한 해시 알고리즘이다. SHA-1은 160비트 40자리 해시값이고, SHA-256은 256비트 64자리 해시값이다. 이 덕분에 sha256는 SHA-1보다 보안에 훨씬 더 강력하다. 또한 같은 해시값이 만들어질 가능성 또한 매우 적다.</p>
<p>zlib는 또 무엇인가. 간단히 말하자면 압축 알고리즘이다. Git은 저장 효율을 높이기 위해, 저장하는 객체를 zlib 압축해서 .git/objects/에 저장한다.</p>
<p>node.js에는 SHA와 zlib 두 개의 알고리즘 모듈이 모두 내장되어 있다!</p>
<h3 id="파일-상태-용어">파일 상태 용어</h3>
<p><strong>&lt;Tracking 단계&gt;</strong>
git init 직후에는 모든 파일이 <strong>Untracked</strong> (추적 안됨) 상태, git add &lt;파일&gt;을 하면 → <strong>Tracked</strong> (추적됨) 상태로 전환됨</p>
<p>Git이 &quot;이 파일은 나중에 커밋할지 말지 확인해야겠군!&quot; 하고 계속 지켜보는 게 Tracking</p>
<p><strong>&lt;Modified 단계&gt;</strong>
추적 중인(Tracked) 파일을 수정해서 저장소와 내용이 다른 상태</p>
<p><strong>&lt;Staged 단계&gt;</strong>
커밋할 준비가 완료된 상태. Git이 “얘는 커밋할 거야” 하고 인덱스(index)에 올려둔 파일</p>
<h3 id="fork-vs-clone">Fork vs Clone</h3>
<p>이건 예전부터 계속 헷갈렸던 개념이었는데 둘 다 대충 남의 코드를 복사해온다는 느낌으로 알고 있어서 차이점을 그리 자세하게 알진 못했다.</p>
<p>좀 더 정확하게 비교를 하자면 <code>Fork</code>는 남의 원격 저장소 -&gt; 내 원격 저장소로 옮기는 작업이다. 이건 당연히 Git의 기능이 아니라 GitHub같은 원격 저장소 서비스에서만 존재하는 기능이다. 보통 외부 프로젝트에 PR을 보내고 싶을때 Upstream저장소(원본 저장소)에 push할 권한을 가지고 있지 않기 때문에 본인의 원격 저장소로 작업물을 복사해오는 걸 뜻한다. 이렇게 되면 원본과 내 저장소 사이에 연결이 생겨 PR요청을 보내는 것이 가능해진다. </p>
<p><code>Clone</code>은 원격 저장소 -&gt; 로컬 저장소로 복사하는 Git의 명령어이다. 복사할 저장소는 내꺼든 남의거든 상관없이 복사해 올 수 있지만 외부 프로젝트에 연결은 안 되어 있기 때문에 나중에 내 원격저장소에 코드를 올려도 PR이 불가능하다.</p>
<p>보통 오픈 소스 프로젝트에 기여할때는 해당 저장소의 push 권한이 없기 때문에 <code>Fork</code> -&gt; <code>Clone</code>의 순서를 따른다.</p>
<p>권한이 부여된 경우 굳이 번거롭게 외부 저장소를 만들 필요 없이 해당 원본 프로젝트 내에서 직접적으로 브랜치를 통해 작업하기 때문에 <code>Clone</code>만으로 프로젝트에 참여하는 경우가 많다고 한다. </p>
<p>결론적으로 둘 다 저장소를 복사하는건 맞는데 그 용도나 복사되는 장소, 목적등이 다르다.</p>
]]></description>
        </item>
    </channel>
</rss>