<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>goo-gy.log</title>
        <link>https://velog.io/</link>
        <description>재밌는 걸 만들고 싶어요</description>
        <lastBuildDate>Sat, 18 Jul 2026 09:52:07 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>goo-gy.log</title>
            <url>https://velog.velcdn.com/images/goo-gy/profile/8fdfb9ca-9d16-4cc0-a11c-924417ad5a53/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. goo-gy.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/goo-gy" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[하네스 엔지니어링 (Harness Engineering)]]></title>
            <link>https://velog.io/@goo-gy/%ED%95%98%EB%84%A4%EC%8A%A4-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4%EB%A7%81-Harness-Engineering</link>
            <guid>https://velog.io/@goo-gy/%ED%95%98%EB%84%A4%EC%8A%A4-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4%EB%A7%81-Harness-Engineering</guid>
            <pubDate>Sat, 18 Jul 2026 09:52:07 GMT</pubDate>
            <description><![CDATA[<h1 id="harness-란">Harness 란?</h1>
<p>코드 에이전트가 장시간 오랫동안 목적을 지속적으로 수행하도록 하기 위한 작업이다. 적절한 가이드를 제공함으로써 개발 과정의 효율을 높일 수도 있고 돌발 상황에 대한 안전장치가 될 수도 있다.</p>
<h1 id="코드-에이전트-기술">코드 에이전트 기술</h1>
<p>Harness를 다루기 전에 코드 에이전트와 관련된 여러 가지 용어와 기술에 대해서 알아보자.</p>
<ul>
<li>AGENTS.md</li>
<li>Skill</li>
<li>MCP (Model Context Protocol)</li>
<li>멀티 에이전트</li>
</ul>
<h2 id="agentsmd">AGENTS.md</h2>
<p>코드 에이전트에서 우선적으로 사용되는 규칙, 지침 파일이다.</p>
<blockquote>
<p>코드 에이전트별로 다르며 Claude code에서는 CLAUDE.md 파일을 사용한다.</p>
</blockquote>
<h3 id="작동-방식">작동 방식</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/0b38b7f8-cb2f-4c54-ae2f-2237e4f4a6c2/image.png" alt=""></p>
<ul>
<li>Context Window에 사용자 입력과 AGENTS.md의 내용이 같이 들어간다.</li>
</ul>
<blockquote>
<p>context window:
모델이 한 번에 받아들이는 입력 내용의 영역으로 크기가 제한되어 있다. </p>
</blockquote>
<h3 id="agentsmd-작성-가이드">AGENTS.md 작성 가이드</h3>
<ul>
<li>프로젝트 구성요소에 대한 설명 명시한다.</li>
<li>프로젝트의 행동 규칙(Rule) 명시한다.</li>
<li>최대한 변하지 않을 내용으로 작성한다.<ul>
<li>자주 변경되면 Agent도 혼란을 겪을 수 있다.</li>
</ul>
</li>
<li>간략하게 작성한다.<ul>
<li>디테일할수록 context를 많이 차지하고, 토큰 소모량 증가하며, 변경될 확률이 증가한다.</li>
</ul>
</li>
<li>디테일한 내용은 분리된 md 파일로 작성하여 필요할 때 읽도록 명시한다.</li>
</ul>
<h2 id="skill">Skill</h2>
<p>에이전트에게 특정 작업을 수행하는 방법을 정의하여, 재사용 가능한 지식, 능력 패키지이다. AGENTS.md와 달리, <strong>on-demand</strong> 방식으로 필요할 때만 동적으로 불러온다.</p>
<h3 id="skill-metadata">Skill metadata</h3>
<p>각 Skill은 SKILL.md 파일을 가지는데 여기에는 Skill의 <strong>metadata (name, description)</strong> 정보가 함께 들어있다. 코드 에이전트는 스킬 목록에서 metadata를 불러와서 스킬을 로드할지 판단한다.</p>
<h3 id="use-case">use case</h3>
<ul>
<li>자주 반복되는 프롬프트를 하나의 명령(skill)으로 생성</li>
<li>파일 편집 / 명령어 실행 / 인터넷 검색 같은 Tool을 사용하는 명령(skill)으로 생성</li>
</ul>
<h2 id="mcp-model-context-protocol">MCP (Model Context Protocol)</h2>
<p>코드 에이전트에서 수많은 외부 서비스를 통일된 방식으로 연결하기 위한 표준 프로토콜이다.</p>
<h2 id="멀티-에이전트">멀티 에이전트</h2>
<p>단일 에이전트에서 복잡한 업무를 처리할 때 Context Window에 입력된 많은 정보로 성능이 떨어질 수 있다. 멀티 에이전트는 복잡한 업무를 여러 단계로 나누어 각각의 <strong>전문가(서브 에이전트)</strong>에게 업무를 분배하고 다음과 같은 특성을 이용하여 효율적으로 처리한다.</p>
<ul>
<li><strong>격리(isolation)</strong> — 각 에이전트는 자신만의 독립된 Context를 가진다.  </li>
<li><strong>병렬성(parallelism)</strong> — 각 에이전트는 동시에 병렬로 작업할 수 있다.  </li>
</ul>
<blockquote>
<p><strong>궁금한 점</strong><br>모델이 100k의 컨텍스트를 지원한다고 할 때, 멀티 에이전트로 구성할 경우 각 서브 에이전트는 100k의 컨텍스트를 가질까? =&gt; Yes</p>
</blockquote>
<blockquote>
<p><strong>멀티 에이전트의 단점</strong>
토큰 소비량이 많아진다. 각 에이전트 별로 독립된 Context를 가지기 때문에, 초기화 비용이 중복으로 발생한다.  </p>
</blockquote>
<h1 id="harness-적용하여-개발하기">Harness 적용하여 개발하기</h1>
<ul>
<li>Harness 구성</li>
<li>요구사항 문서 작성</li>
<li>Phase 수립 및 개발</li>
</ul>
<h2 id="harness-구성">Harness 구성</h2>
<ul>
<li>AGENTS.md 파일 생성 (claude의 경우 CLAUDE.md)</li>
<li>프로젝트 내용 작성</li>
<li>하네스 구성<ul>
<li>주의사항  명시</li>
<li>프레임워크, 도구 명시</li>
</ul>
</li>
</ul>
<h3 id="andrej-karpathy-skill">Andrej-karpathy skill</h3>
<p><a href="https://github.com/multica-ai/andrej-karpathy-skills">https://github.com/multica-ai/andrej-karpathy-skills</a>
<a href="https://github.com/multica-ai/andrej-karpathy-skills/blob/main/CLAUDE.md">https://github.com/multica-ai/andrej-karpathy-skills/blob/main/CLAUDE.md</a></p>
<p>AI 코딩 에이전트의 흔한 실수를 줄이기 위해 사용되는 유명한 지침 (2026-07 기준 Github Stars 수 약 200k)</p>
<blockquote>
<p>이런 기본적인 하네스를 구성하는 것도 좋지만, 이는 LLM, 코드 에이전트가 발전할수록 필요성이 떨어질 것이다.<br>나의 프로젝트에 맞게 최적화된 Harness를 구성하는 것이 더 중요하다.</p>
</blockquote>
<h2 id="요구사항-문서-작성">요구사항 문서 작성</h2>
<ul>
<li>PRD (Product Requirements Document) 작성</li>
<li>Specification 문서 작성</li>
</ul>
<h3 id="prd-product-requirements-document-작성">PRD (Product Requirements Document) 작성</h3>
<ul>
<li>고객 입장에서 어떤 프로그램을 원하는지 작성한다.</li>
<li>개발적인 지식이 들어가면 안 된다.</li>
</ul>
<h3 id="specification-문서-작성">Specification 문서 작성</h3>
<ul>
<li>요구사항 문서보다 디테일하게 기준을 정리하는 문서</li>
</ul>
<blockquote>
<p> Superpowers Skill의 /brainstorming을 사용하면 질의응답을 통해, 모도한 요구사항을 더 명확하게 만들 수 있다.</p>
</blockquote>
<h2 id="phase-수립-및-개발-수행">Phase 수립 및 개발 수행</h2>
<ul>
<li>Phase 작성</li>
<li>서브 에이전트 구성</li>
<li>Workflow 구성</li>
<li>Feedback loop 구성</li>
</ul>
<h3 id="phase-작성">Phase 작성</h3>
<p>단계별로 어떤 내용을 개발할지 계획
각 Phase 별로 동작되어야 하며 사람이 직접 테스트 가능해야 한다.</p>
<ul>
<li>ex) 로그분석 차트 개발<ul>
<li>Phase 1: UI 구성</li>
<li>Phase 2: 파일 URL </li>
<li>Phase 3: 데이터 파싱</li>
<li>Phase 4: API 별 Latency 측정 통계 계산</li>
<li>Phase 5: 차트 출력</li>
</ul>
</li>
</ul>
<h3 id="서브-에이전트-구성">서브 에이전트 구성</h3>
<p>코드 품질을 높이기 위해 각 Phase 마다, 4단계 작업을 구성하고 각 에이전트가 이를 담당하게 구성한다.  </p>
<ul>
<li>coding</li>
<li>test</li>
<li>review</li>
<li>verify</li>
</ul>
<blockquote>
<p><strong>ex) claude</strong>
  .claude/
  ├── agents/
  │   ├── coder.md        # 구현 (Coding)
  │   ├── tester.md       # 테스트 작성·실행 (Test)
  │   ├── reviewer.md     # 정확성/보안/품질 리뷰 (Review)
  │   └── verifier.md     # end-to-end 동작 검증 (Verify)</p>
</blockquote>
<h3 id="workflow-구성">Workflow 구성</h3>
<ul>
<li>각 서브 에이전트를 순서대로 실행하도록 Workflow를 구성한다.  </li>
</ul>
<blockquote>
<p><strong>Claude Code 전용 기능</strong><br>서브 에이전트들의 Workflow를 구성하여 실행해 주는 JavaScript 코드를 만들어 준다.</p>
</blockquote>
<h3 id="feedback-loop-구성">Feedback loop 구성</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/afc87b98-36aa-4be8-a223-229a9f5ac066/image.png" alt=""></p>
<p>일부 작업이 실패하더라도 개발 수행이 중단되지 않도록 하는 <strong>안전장치(Harness)</strong>가 필요하다.  각 에이전트 수행 중 실패 시 코드 수정 후, 다시 test 단계로 되돌아가는 피드백 루프를 추가한다.</p>
<h1 id="more">More</h1>
<p>Harness Engineering 잘하기 위해서 많이 사용되는 AI Harness를 적용해 보면 좋을 것 같다.</p>
<ul>
<li>Superpowers</li>
<li>G-Stack</li>
<li>Oh My Opencode (Oh My Claudecode)</li>
<li>Hands off</li>
<li>...</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Agile 하게 서비스 기획하기!]]></title>
            <link>https://velog.io/@goo-gy/Agile-%ED%95%98%EA%B2%8C-%EC%84%9C%EB%B9%84%EC%8A%A4-%EA%B8%B0%ED%9A%8D%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@goo-gy/Agile-%ED%95%98%EA%B2%8C-%EC%84%9C%EB%B9%84%EC%8A%A4-%EA%B8%B0%ED%9A%8D%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 02 Sep 2024 12:54:50 GMT</pubDate>
            <description><![CDATA[<p>회사에서 Agile 교육을 받으면서, 나도 Agile 하게!!!!! 개발하고 싶다!!! 그랬는데.. 마침 토이 프로젝트를 시작하면서 배운 것을 써먹어 볼 좋은 기회라고 생각했다. Agile 교육에서 배운 프로세스들을 팀원들과 공유하면서 적용하면 좋겠다, 재밌겠다! 하는 것들을 도입하여 진행해 봤다.</p>
<h2 id="agile-이란">Agile 이란?</h2>
<p>Agile 교육도 받고 이 단어를 참 많이 들었지만, 아직도 나는 Agile이 무엇인지 모른다. 아마 정확하게 설명할 수 있는 사람도 없을 거라 생각한다. 다만 내 기억 속에서 느낀 대로 설명하자면, Agile에서는 다음 세 가지를 강조하는 듯하다.</p>
<ul>
<li>고객 중심</li>
<li>피드백</li>
<li>민첩성 (작은 단위)</li>
</ul>
<p><strong>고객의 입장에서 생각하면서 개발하고, 작은 스프린트로 빠르게 피드백을 받아 더 나은 서비스를 만들자.</strong> 정도로 해석해 보았다.</p>
<h2 id="서비스-기획-프로세스">서비스 기획 프로세스</h2>
<p>아래는 Agile 교육에서 기억에 남고 유용하면서도 재미있었던 과정들이라 우리 팀에 가져와서 적용해 보았다.</p>
<ul>
<li>Persona</li>
<li>Customer Journey Map</li>
<li>Feature Identification</li>
<li>Feature Estimation</li>
<li>Feature Pyramid</li>
</ul>
<p>우리는 <strong>꿈 일기 공유 플랫폼</strong>이라는 주제를 가지고 시작했기 때문에, 주제를 선정하기 위한 단계는 생략하고 페르소나부터 진행하였다.</p>
<h3 id="persona">Persona</h3>
<p><strong>Persona(페르소나)</strong>는 가상의 고객을 가정한다. 고객의 입장이 되어, 우리 서비스를 찾는 과정, 서비스를 이용하면서 얻으려는 것이 무엇인지 예상해 보는 것이다. Confluence에서 괜찮은 <strong><a href="https://www.atlassian.com/software/confluence/templates/persona">Persona Template</a></strong>을 제공하여 이를 변형하여 사용하였다.   </p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/7f346dfd-2a82-4730-8cda-9a674cbcd4aa/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/4fd146bc-268c-4fc0-b726-5c448261262e/image.png" alt=""></p>
<p>꿈 일기 공유 플랫폼을 사용할 법한 10대 ~ 30대로 범위를 정해두고 여러 페르소나를 만들어 보았다. 다양한 가상의 인물을 만들어 보니 다양한 니즈를 예상해 볼 수 있었다.</p>
<h3 id="customer-journey-map">Customer Journey Map</h3>
<p><strong>Customer Journey Map</strong>에서는 앞서 만들어둔 페르소나(고객)가 우리 서비스를 이용하는 여정을 여러 단계의 시나리오로 만들어 본다. 단계별로 고객의 감정 변화를 시각화하고 문제점을 예상해 보면서 더 나은 솔루션을 만들어보는 과정이다. 이번에도 Confluence에서 <strong><a href="https://www.atlassian.com/software/confluence/templates/customer-journey-mapping">Customer journey mapping Template</a></strong> 을 제공하여 이를 사용했다.</p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/265d430e-5588-4ab7-8283-bc48d8491c0d/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/66f34857-b7b5-4a4e-a0c9-f46fcb971da0/image.png" alt=""></p>
<p>Customer Journey Map을 진행할 때는 각 단계에서의 감정이 어떨지 예상하고, 문제점을 만들어 보는 것이 쉽지 않았다. 그래도 이런 시나리오를 만들고 이입하여 실행해 보니 재밌는 아이디어들을 많이 찾아낼 수 있어서 굉장히 만족스러운 단계였다.</p>
<h3 id="feature-identification">Feature Identification</h3>
<p><strong>Feature Identification</strong>은 Customer Journey Map에서 도출한 문제점, 솔루션들을 토대로 Feature(기능)를 정리해 보는 과정이다. </p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/f6333345-b3a5-46ca-bd2e-8bbeab15b5ed/image.png" alt=""></p>
<p>Customer Journey Map에서 나온 솔루션들이 명확해서 기능을 도출하는 데 큰 어려움은 없었다. 미리 생각하고 있던 기본적인 기능들부터 Persona, Customer Journey Map을 통해 얻은 새로운 아이디어까지 세분화하니 정말 많은 기능들이 나왔다. </p>
<blockquote>
<p>점수를 부여하여 기능의 사이즈를 정하는 <strong>Feature Estimation</strong> 단계도 있지만, 너무 많은 공수가 들면 팀이 지칠 것 같아 생략하였다.</p>
</blockquote>
<h3 id="feature-pyramid">Feature Pyramid</h3>
<p>짧은 시간 내에 모든 기능들을 다 구현할 수는 없다. 따라서 <strong>Feature Pyramid</strong>에서는 기능의 가치, 선행 관계 등을 고려하여 우선순위를 정해서 피라미드 형태로 표현하고 하위에 배치된 일부 기능들은 보류하거나 걷어낸다.</p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/4ad47a7a-58c8-4619-9bc1-7bfa8ad1aac1/image.png" alt=""></p>
<p>우리는 먼저 Slack에서 <strong>Polly</strong>라는 기능을 사용하여 팀에서 각 Feature의 중요도를 조사하였다. 팀원들이 각 기능마다 1점~5점으로 중요도(가치)를 투표하고, 평균을 내어 점수를 내었다. (아쉽게도 Polly 무료 플랜에서는 45일이 지난 투표 결과를 보여주지 않는다...)</p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/37d43c32-656a-4369-a364-32e0b2ef2cf3/image.png" alt=""></p>
<p>투표 결과 점수로 선 정렬한 후 토론을 통해서 순서를 재조정하였다. 애플리케이션 형태를 위해 기본적인 기능들이 선순위 작업으로 올라왔다. 중요도가 높지만 유사한 가치를 제공하는 기능이나 사이즈가 너무 큰 기능은 아래로 밀려났다. (겪어 보니 Feature Estimation을 통해 사이즈를 예상해 보면 좋았겠다는 생각도 들었다.)</p>
<h2 id="next">Next</h2>
<p>지금까지 4가지 단계를 거쳐서 기능을 도출하고, 개발 순서도 정해보았다. 처음에는 이 과정들을 우리가 잘 적용할 수 있을까 하는 걱정이 있었는데, 결과도 아주 만족스러웠고 팀원들의 반응도 긍정적이었다. 다음으로는 우리 팀이 협업을 위해 사용하는 도구들과, 협업/개발 과정을 하나씩 소개해 보겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Python의 Namespace와 단위 테스트 (with Mock)]]></title>
            <link>https://velog.io/@goo-gy/Python%EC%9D%98-Namespace%EC%99%80-%EB%8B%A8%EC%9C%84-%ED%85%8C%EC%8A%A4%ED%8A%B8-with-Mock</link>
            <guid>https://velog.io/@goo-gy/Python%EC%9D%98-Namespace%EC%99%80-%EB%8B%A8%EC%9C%84-%ED%85%8C%EC%8A%A4%ED%8A%B8-with-Mock</guid>
            <pubDate>Tue, 25 Jun 2024 10:29:39 GMT</pubDate>
            <description><![CDATA[<h1 id="단위-테스트와-테스트-더블">단위 테스트와 테스트 더블</h1>
<p>테스트 더블을 사용하면 테스트를 단순화하고 외부 환경과 독립된 코드를 테스트할 수 있다는 장점이 있다. 테스트 더블에는 다양한 종류가 있지만 여기서는 Stub과 Mock을 통칭하여 Mock이라고 부르겠다.  </p>
<h2 id="mocking---의존성-주입">Mocking - 의존성 주입</h2>
<ul>
<li>client.py</li>
</ul>
<pre><code class="language-python">class Client:
    def list_items(self):
        raise Exception(&quot;Network Exception!&quot;)</code></pre>
<blockquote>
<p>Mocking 여부를 쉽게 파악할 수 있도록, list_items가 에러를 발생시키도록 구현하였다.</p>
</blockquote>
<ul>
<li>service.py</li>
</ul>
<pre><code class="language-python">import client


class Service:
    def __init__(self, _client: client.Client):
        self.client = _client

    def do_list(self):
        return self.client.list_items()</code></pre>
<ul>
<li>test_service.py</li>
</ul>
<pre><code class="language-python">from unittest import TestCase
from unittest.mock import Mock

from service import Service


class TestService(TestCase):
    def test_do_list(self):
        # given
        mock_client = Mock()
        mock_client.list_items.return_value = [&#39;item1&#39;, &#39;item2&#39;, &#39;item3&#39;]
        target_service = Service(mock_client)
        # when
        result = target_service.do_list()
        # then
        self.assertEqual(result, [&#39;item1&#39;, &#39;item2&#39;, &#39;item3&#39;])</code></pre>
<p>일반적인 언어에서는 의존성 주입 형태로 구현해 두어야, 대상 서비스를 생성할 때 Mock 객체를 주입하여 Mock 테스트를 구현할 수 있다.  </p>
<h2 id="python에서의-mocking">Python에서의 Mocking</h2>
<p>Python에서는 위처럼 의존성 주입 형태로 구현하지 않아도 <code>unittest.mock.patch</code>를 이용하여 Mocking을 수행할 수 있다.</p>
<ul>
<li>client.py</li>
</ul>
<pre><code class="language-python">def list_items():
    raise Exception(&quot;Network Exception!&quot;)</code></pre>
<ul>
<li>service.py</li>
</ul>
<pre><code class="language-python">import client


def do_list():
    return client.list_items()</code></pre>
<ul>
<li>test_service.py</li>
</ul>
<pre><code class="language-python">from unittest import TestCase
from unittest.mock import patch

import service


class TestService(TestCase):
    @patch(&#39;client.list_items&#39;, return_value=[&#39;items1&#39;, &#39;items2&#39;, &#39;items3&#39;])
    def test_do_list(self, mock_list_items):
        result = service.do_list()
        self.assertEqual(result, [&#39;items1&#39;, &#39;items2&#39;, &#39;items3&#39;])
</code></pre>
<p>patch는 Namespace에 등록된 이름이 생성한 Mock 객체를 가리키도록 한다.</p>
<h2 id="python-mock-주의사항">Python mock 주의사항</h2>
<p>Python unittest에서는 의존성 주입 형태로 구현하지 않더라도 Mock을 주입할 수 있는 강력한 기능을 제공한다. 하지만 위 코드에서 <code>import client</code> 부분을 <code>from client import list_items</code>로 변경한다면 Mocking이 제대로 이루어지지 않고 테스트는 실패한다. 원인을 제대로 이해하기 위해 Python의 Namespace에 대해서 자세히 알아보자.</p>
<blockquote>
<p><a href="https://docs.python.org/ko/3/library/unittest.mock.html#id6">https://docs.python.org/ko/3/library/unittest.mock.html#id6</a></p>
</blockquote>
<h1 id="python-namespace">Python namespace</h1>
<p>Python에서 Namespace는 Object 정보를 가리키는 이름을 매핑해 둔 공간으로 Dictionary(Key:Value) 형태로 저장된다.  </p>
<ul>
<li>Key: Name</li>
<li>Value: Object address</li>
</ul>
<h2 id="종류">종류</h2>
<p>Namespace에는 다음과 같이 4가지 종류가 있다.  </p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/214b3512-bcaf-4e72-9ad8-e17c1bc39e45/image.png" alt=""></p>
<ul>
<li>Function namespace (Local namespace)</li>
<li>Enclosing namespace</li>
<li>Module namespace (Global namespace)</li>
<li>Built-in namespace</li>
</ul>
<p>위 순서와 같이 좁은 범위에서 넓은 범위로 탐색한다.  </p>
<blockquote>
<p>Python에서 패키지는 디렉터리 단위, 모듈은 파일 단위로 볼 수 있다.</p>
</blockquote>
<h2 id="python-import">Python import</h2>
<p>Python에서는 일반적으로 2가지 방식의 import 구문을 가지고 있다.</p>
<ul>
<li>모듈을 Import 
<code>from &lt;package&gt; import &lt;module&gt;</code></li>
<li>모듈 내 함수를 Import 
<code>from &lt;module&gt; import &lt;function&gt;</code></li>
</ul>
<p>이렇게 다른 import 문이 Namespace에서 어떤 차이가 있는지 예제를 통해서 살펴보자.  </p>
<h2 id="모듈을-import-하는-경우">모듈을 Import 하는 경우</h2>
<ul>
<li>client.py</li>
</ul>
<pre><code class="language-python">def list_items():
    raise Exception(&quot;Network Exception!&quot;)</code></pre>
<ul>
<li>service.py</li>
</ul>
<pre><code class="language-python">import client


def do_list():
    return client.list_items()</code></pre>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/f9937606-df4e-4da1-bb5c-c3ca5bf37a4b/image.png" alt=""></p>
<p><code>service.py</code> 모듈의 Namespace에 client라는 이름이 등록되고, 이는 <code>client.py</code> 모듈의 경로를 가리킨다.</p>
<h3 id="테스트">테스트</h3>
<pre><code class="language-python">from unittest import TestCase
from unittest.mock import patch

import service


class TestService(TestCase):
    @patch(&#39;client.list_items&#39;, return_value=[&#39;items1&#39;, &#39;items2&#39;, &#39;items3&#39;])
    def test_do_list(self, mock_list_items):
        result = service.do_list()
        self.assertEqual(result, [&#39;items1&#39;, &#39;items2&#39;, &#39;items3&#39;])</code></pre>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/c9622aad-46ba-4a53-9a00-bf565962bda8/image.png" alt=""></p>
<p><code>service.py</code> 모듈의 client는 실제 <code>client.py</code>를 가리키기 때문에 <code>client.list_items</code>를 패치하면 do_list가 Mock list_items를 호출하게 된다.</p>
<h2 id="모듈-내-함수를-import-하는-경우">모듈 내 함수를 Import 하는 경우</h2>
<ul>
<li>client.py</li>
</ul>
<pre><code class="language-python">def list_items():
    raise Exception(&quot;Network Exception!&quot;)</code></pre>
<ul>
<li>service.py</li>
</ul>
<pre><code class="language-python">from client import list_items


def do_list():
    return list_items()</code></pre>
<p>앞 예제에서 <code>service.py</code> 모듈에서 기능은 변경하지 않고 import 구문만 (함수를 직접 import 하도록) 수정하였다.  </p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/5f0ad942-a6d1-4aa7-a857-cdd1aa717629/image.png" alt=""></p>
<p>위처럼 함수를 직접 import 할 경우, <code>service.py</code> 모듈의 Namespace에 list_items라는 이름이 등록되고, 이는 실제 list_items 함수를 가리킨다.  </p>
<h3 id="테스트-기존과-동일">테스트 (기존과 동일)</h3>
<pre><code class="language-python">from unittest import TestCase
from unittest.mock import patch

import service


class TestService(TestCase):
    @patch(&#39;client.list_items&#39;, return_value=[&#39;items1&#39;, &#39;items2&#39;, &#39;items3&#39;])
    def test_do_list(self, mock_list_items):
        result = service.do_list()
        self.assertEqual(result, [&#39;items1&#39;, &#39;items2&#39;, &#39;items3&#39;])</code></pre>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/91db9bdc-7c2f-409d-96ac-49b4684b15f3/image.png" alt=""></p>
<p>테스트 코드를 변경하지 않았다면, <code>client.py</code> 모듈의 list_items는 패치되어 Mock 객체를 가리키지만, do_list가 호출하는 <code>service.py</code> 모듈의 list_items는 여전히 실제 list_items 함수를 가리키고 있다.  </p>
<h3 id="테스트-patch-구문-수정">테스트 (patch 구문 수정)</h3>
<pre><code class="language-python">from unittest import TestCase
from unittest.mock import patch

import service


class TestService(TestCase):
    @patch(&#39;service.list_items&#39;, return_value=[&#39;items1&#39;, &#39;items2&#39;, &#39;items3&#39;])
    def test_do_list(self, mock_list_items):
        result = service.do_list()
        self.assertEqual(result, [&#39;items1&#39;, &#39;items2&#39;, &#39;items3&#39;])</code></pre>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/25581ccc-3db1-48a3-b97d-afbb5b6ba742/image.png" alt=""></p>
<p><code>client.py</code> 모듈의 list_items가 아닌 <code>service.py</code> 모듈의 list_items를 패치하면, 테스트 대상인 do_list 함수에서 호출하는 list_items가 정상적으로 Mocking되어 테스트가 성공한다.  </p>
<h1 id="결론">결론</h1>
<p>import 구문을 다양한 방식으로 사용할 경우 단위 테스트의 Mock 처리에서 혼란을 일으킬 수 있다. 따라서 import 하는 방법을 하나로 통일하여 컨벤션으로 정해두는 것을 추천한다. 개인적으로는 모듈을 import 하는 방법을 선호한다.</p>
<blockquote>
<p>또 다른 방법으로는 Mocking 대상을 다른 언어와 같이 (Dependency Injection으로 구현하지 않더라도) 클래스로 구현하는 방법이 있다. 원리는 다음 글에서 설명하겠다.</p>
</blockquote>
<h1 id="참고-자료">참고 자료</h1>
<blockquote>
</blockquote>
<p><a href="https://docs.python.org/ko/3/library/unittest.mock.html">https://docs.python.org/ko/3/library/unittest.mock.html</a>
<a href="https://realpython.com/python-namespaces-scope/">https://realpython.com/python-namespaces-scope/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CloudWatch에서 Lambda 로그 확인하기]]></title>
            <link>https://velog.io/@goo-gy/CloudWatch%EC%97%90%EC%84%9C-Lambda-%EB%A1%9C%EA%B7%B8-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@goo-gy/CloudWatch%EC%97%90%EC%84%9C-Lambda-%EB%A1%9C%EA%B7%B8-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 05 Mar 2024 14:09:27 GMT</pubDate>
            <description><![CDATA[<p><a href="https://velog.io/@goo-gy/AWS-Lambda">[ AWS Lambda ]</a> 에서 람다 함수에 대해서 알아보았다. 이번에는 Lambda의 로그를 확인하는 방법을 알아보자.</p>
<h2 id="logging-코드-python">Logging 코드 (Python)</h2>
<pre><code class="language-python">import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

stream_handler = logging.StreamHandler()
logger.addHandler(stream_handler)

def main():
    logger.debug(&quot;Debug[Level: 10]&quot;)
    logger.info(&quot;Info[Level: 20]&quot;)
    logger.warning(&quot;Warning[Level: 30]&quot;)
    logger.error(&quot;Error[Level: 40]&quot;)
    logger.critical(&quot;Critical[Level: 50]&quot;)

if __name__ == &#39;__main__&#39;:
    main()</code></pre>
<pre><code>Info[Level: 20]
Warning[Level: 30]
Error[Level: 40]
Critical[Level: 50]</code></pre><p>Python에서 로그를 출력하는 간단한 예제를 가져왔다. 로그는 설정한 Level 이상의 로그만 출력된다. 위 코드에서는 INFO level 이상의 로그만 출력된다.</p>
<h3 id="logging-level">Logging level</h3>
<table>
<thead>
<tr>
<th>함수</th>
<th>Level</th>
<th>용도</th>
</tr>
</thead>
<tbody><tr>
<td>debug</td>
<td>10</td>
<td>디버깅 내용 출력</td>
</tr>
<tr>
<td>info</td>
<td>20</td>
<td>작업 내용 확인</td>
</tr>
<tr>
<td>warning</td>
<td>30</td>
<td>경고 표시</td>
</tr>
<tr>
<td>error</td>
<td>40</td>
<td>에러 출력</td>
</tr>
<tr>
<td>critical</td>
<td>50</td>
<td>시스템의 심각한 문제 알림</td>
</tr>
</tbody></table>
<blockquote>
<p><a href="https://docs.python.org/3/library/logging.html">https://docs.python.org/3/library/logging.html</a></p>
</blockquote>
<h2 id="aws-lambda-log">AWS Lambda log</h2>
<p>AWS Lambda에서 출력한 내용은 CloudWatch에서 확인할 수 있다. </p>
<h3 id="권한">권한</h3>
<p>AWS Lambda 함수를 생성할 때 역할을 생성하도록 하면, CloudWatch 로그 그룹을 생성하고 해당 로그 그룹에 로그를 작성할 수 있는 권한이 생성된다. </p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/a81a12eb-72f0-4ba5-889c-81de0dded97a/image.png" alt=""></p>
<h3 id="example">Example</h3>
<pre><code class="language-python">import json
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
    logger.debug(&quot;Debug: %s&quot; %event)
    logger.info(&quot;Info: %s&quot; %event)
    logger.warning(&quot;Warning: %s&quot; %event)
    logger.error(&quot;Error: %s&quot; %event)
    logger.critical(&quot;Critical: %s&quot; %event)
    return {
        &#39;statusCode&#39;: 200,
        &#39;body&#39;: json.dumps(&#39;Hello from Lambda!&#39;)
    }</code></pre>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/f4a90629-00ba-4c99-b2ad-f604d5b23947/image.png" alt=""></p>
<p>예제를 Test로 실행하고 CloudWatch에서 확인해 보자. </p>
<h2 id="amazon-cloudwatch">Amazon CloudWatch</h2>
<h3 id="aws-lambda-로그">AWS Lambda 로그</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/b8d02302-7d62-4114-9d06-2e346bc0a723/image.png" alt=""></p>
<p>로그 스트림에서 함수 실행 전후로 START, END, REPORT 정보를 <strong>Request ID</strong>와 함께 출력해 주는 것을 확인할 수 있다. Logging 함수로 출력한 로그에도 <strong>Request ID</strong>가 Prefix로 들어가 있다. </p>
<h3 id="live-tail">Live Tail</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/a1342a27-c1d5-416d-9bfa-b03447a339ee/image.png" alt=""></p>
<p>Live tail은 CloudWatch의 로그를 실시간으로 확인할 수 있는 기능이다. 로그 그룹을 선택해 시작한 후, Lambda에서 테스트를 실행하면, 로그를 실시간으로 확인할 수 있을 것이다.</p>
<h3 id="log-insights">Log Insights</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/e8ff6012-d1a1-44ee-a1b0-f72e29105c1e/image.png" alt=""></p>
<p>Log Insights에서는 쿼리를 통해서 원하는 로그를 검색할 수 있다. 위처럼 <strong>like</strong> 키워드를 이용해서 특정 단어가 포함된 로그를 검색할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/6aa650cd-d8fe-4eec-a406-448c8cc26645/image.png" alt=""></p>
<p>특정 로그를 처음부터 끝까지 보고 싶다면 <strong>@requestId</strong> 필드를 조건으로 검색해 보자.</p>
<blockquote>
<p><a href="https://docs.aws.amazon.com/ko_kr/AmazonCloudWatch/latest/logs/CWL_QuerySyntax.html">CloudWatch 로그 인사이트 쿼리 구문</a> 에서 자세한 쿼리 문법을 확인할 수 있다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS Lambda]]></title>
            <link>https://velog.io/@goo-gy/AWS-Lambda</link>
            <guid>https://velog.io/@goo-gy/AWS-Lambda</guid>
            <pubDate>Thu, 11 Jan 2024 15:10:06 GMT</pubDate>
            <description><![CDATA[<p>AWS Lambda는 대표적인 Serverless 서비스이다. 코드를 실행하려면 일반적으로 서버가 필요하고, 이 서버를 운영/관리해야 한다. Serverless는 서버의 운영/관리를 클라우드에서 해준다는 것이다. 우리는 우리의 코드만 신경 쓰면 된다. </p>
<h2 id="aws-lambda">AWS Lambda</h2>
<p>언제 AWS Lambda를 사용해야 할까? 라는 질문에는 여러 가지 고려 사항이 있겠지만, AWS Lambda의 요금 정책이 어느 정도 답안을 줄 수 있다. Lambda의 요금은 CPU, 메모리 사양과 요청 수, 실행한 시간에 따라 결정된다. 따라서 많이 호출될수록, 오래 실행될수록 많은 요금이 많이 발생한다. 대신 드물게 요청되면서 짧은 시간 안에 처리되는 로직이라면 직접 서버를 프로비저닝하여 애플리케이션을 운영하는 것보다 노력과 비용을 절감할 수 있다. </p>
<blockquote>
<p><a href="https://aws.amazon.com/ko/lambda/pricing/">AWS Lambda 요금</a></p>
</blockquote>
<h2 id="예제">예제</h2>
<p>AWS Lambda의 기능, 사용 방법을 더 잘 이해하기 위해 <code>네트워크 비용 절감을 위해 업로드된 프로필 사진의 썸네일 제작</code>이라는 예제를 통해 살펴보자.</p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/fc7abe82-31b5-4ff7-99fe-ee27e9707a96/image.png" alt=""></p>
<blockquote>
<p><strong>주의 사항</strong>
트리거로 사용한 버킷과 Lambda에서 생성하는 버킷을 다르게 설정하자. 동일하게 설정한다면, 트리거가 무한으로 작동하여 Lambda 요금이 정말 많이 나올 수 있다.</p>
</blockquote>
<h2 id="lambda-트리거">Lambda 트리거</h2>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/26112000-c8c5-408d-af35-4782fd21d2c3/image.png" alt=""></p>
<p>Lambda에서는 API Gateway, S3 Event, SQS 등 다양한 트리거를 설정할 수 있다.</p>
<h3 id="트리거-추가">트리거 추가</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/5fd8c4ad-440f-4c9e-a8ca-57e51ea5fcd9/image.png" alt=""></p>
<p><code>gook-app-image</code> 버킷의 모든 파일 생성 이벤트를 트리거로 등록하였다. 
(아래에는 트리거를 발생시킬 S3 버킷과 코드에서 대상으로 하는 S3 버킷을 동일하게 설정하면 재귀적인 호출로 Lambda 사용량이 높아질 수 있다는 경고를 보여주고 있다.)</p>
<h2 id="코드">코드</h2>
<pre><code class="language-python">import boto3
import json
import os
from PIL import Image
from io import BytesIO


def lambda_handler(event, context):
    print(json.dumps(event))
    for record in event[&#39;Records&#39;]:
        source_bucket = record[&#39;s3&#39;][&#39;bucket&#39;][&#39;name&#39;]
        target_bucket = os.environ.get(&#39;THUMBNAIL_BUCKET&#39;)
        key = record[&#39;s3&#39;][&#39;object&#39;][&#39;key&#39;]
        file_extension = key.split(&#39;.&#39;)[-1].lower()
        if file_extension in [&#39;jpg&#39;, &#39;jpeg&#39;, &#39;png&#39;, &#39;gif&#39;]:
            generate_thumbnail(source_bucket, target_bucket, key)
        else:
            print(&quot;Not supported image file&quot;)


def generate_thumbnail(source_bucket, target_bucket, path):
    s3_client = boto3.client(&#39;s3&#39;)
    response = s3_client.get_object(Bucket=source_bucket, Key=path)
    image_stream = BytesIO(response[&#39;Body&#39;].read())
    thumbnail_stream = extract_thumbnail(image_stream)
    s3_client.put_object(Body=thumbnail_stream, Bucket=target_bucket, Key=path)
    print(f&quot;Thumbnail generated and saved to {target_bucket}/{path}&quot;)


def extract_thumbnail(image_stream):
    image = Image.open(image_stream)
    image.thumbnail((100, 100))
    thumbnail_bytes = BytesIO()
    image.save(thumbnail_bytes, format=&#39;jpeg&#39;)
    thumbnail_bytes.seek(0)
    return thumbnail_bytes
</code></pre>
<h3 id="환경-변수">환경 변수</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/30a4af56-b9ce-4f67-82fa-6b1b79fa7597/image.png" alt=""></p>
<p>예제 코드에서 대상 버킷에 대한 이름은 환경 변수를 통해 입력받는다. 환경 변수는 Lambda 함수 <code>구성 &gt; 환경 변수</code>에서 설정할 수 있다.</p>
<h2 id="라이브러리-설치">라이브러리 설치</h2>
<p>우리는 개발 환경이나 서버에서 Python 코드를 실행할 때 PIP를 이용해 필요한 라이브러리를 설치한다. Lambda에서 외부 라이브러리를 설치하는 방법을 알아보자.</p>
<blockquote>
<p><strong>boto3</strong> 라이브러리는 AWS Lambda에서 내장하고 있어 별도로 설치하지 않아도 된다.</p>
</blockquote>
<h3 id="라이브러리-zip-생성">라이브러리 zip 생성</h3>
<pre><code class="language-shell">pip install pillow -t ./python</code></pre>
<p>예제 코드에서는 boto3를 제외하고 <strong>pillow</strong>라는 외부 라이브러리가 필요하다. Lambda에 추가하기 위해, 동일한 리눅스 환경에서 위 명령어를 통해 python 폴더 아래에 필요한 라이브러리를 설치하자.</p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/859a9535-e485-454a-9044-02441216ddcb/image.png" alt=""></p>
<p>그리고 필요한 라이브러리들이 설치된 이 python 폴더를 압축한다.  </p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/02c4c66c-1e01-4d1e-98b3-2c35428a2b60/image.png" alt=""></p>
<blockquote>
<p><strong>AWS Cloudshell</strong>
바로 접속할 수 있는 리눅스 서버가 없다면, AWS의 Cloudshell을 활용할 수 있다. Cloudshell은 무료로 사용할 수 있지만, AWS 네트워크 비용은 발생할 수 있다.
<a href="https://docs.aws.amazon.com/ko_kr/cloudshell/latest/userguide/shell-pricing.html">https://docs.aws.amazon.com/ko_kr/cloudshell/latest/userguide/shell-pricing.html</a></p>
</blockquote>
<h3 id="계층layer-생성">계층(Layer) 생성</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/e84a5fc3-fef2-4d06-a788-950a0f130a26/image.png" alt=""></p>
<p>앞에서 압축한 zip 파일을 등록하여 Lambda 계층을 생성한다. 런타임을 라이브러리를 설치한 Python 버전과 맞춰주자. </p>
<h3 id="계층-등록">계층 등록</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/c6e0ebf9-03a0-46be-9c40-e531f8e40e10/image.PNG" alt=""></p>
<p>Lambda 함수에서 <strong>Layers</strong>를 선택하고 [Add a layer]를 통해 앞에서 생성한 계층을 등록할 수 있다. <code>사용자 지정 계층</code>에서 선택하거나 ARN을 직접 지정하면 된다.</p>
<h2 id="권한-구성">권한 구성</h2>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/1fea6835-4a5a-4b4e-a164-cb7842ea5cb5/image.png" alt=""></p>
<p><code>gook-app-image</code> 버킷에 이미지를 업로드하여, 예제 코드를 실행하면, S3 버킷에서 파일을 읽는 권한이 없어 <strong>Access Denied</strong> 에러가 발생한 것을 확인할 수 있다. S3 버킷에 있는 파일을 읽고, 쓰기 위한 권한이 필요하다.</p>
<h3 id="lambda에-연결된-역할-확인">Lambda에 연결된 역할 확인</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/2a9d8024-1a80-493c-a50d-4e745e9aca02/image.png" alt=""></p>
<p>Lambda 함수의 <code>구성 &gt; 권한</code>으로 들어가면 할당된 역할을 확인할 수 있다.  </p>
<blockquote>
<p>Lambda 함수를 생성할 때 <code>기본 Lambda 권한을 가진 새 역할 생성</code> 옵션을 사용하였다면, <code>${함수명}-xxx</code>라는 이름으로 역할이 생성되며, 기본적으로 함수의 로깅을 위한 권한 정책이 할당된다.  </p>
</blockquote>
<h3 id="역할에-권한-정책-추가">역할에 권한 정책 추가</h3>
<p>우리는 <code>gook-app-image</code> 버킷에서 파일을 가져오는 권한과 <code>gook-app-thumbnail</code> 버킷에 파일을 업로드하는 권한이 필요하므로 역할에 다음과 같은 정책을 추가해 주자.</p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/a5597175-4625-45a7-b413-f652bf6b0cad/image.png" alt=""></p>
<pre><code class="language-json">{
    &quot;Version&quot;: &quot;2012-10-17&quot;,
    &quot;Statement&quot;: [
        {
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: &quot;s3:GetObject&quot;,
            &quot;Resource&quot;: &quot;arn:aws:s3:::gook-app-image/**&quot;
        },
        {
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: &quot;s3:PutObject&quot;,
            &quot;Resource&quot;: &quot;arn:aws:s3:::gook-app-thumbnail/**&quot;
        }
    ]
}</code></pre>
<p>각 Statement는 제한된 <strong>리소스(Resource)</strong> 범위에 대해서 특정 <strong>작업(Action)</strong>을 <strong>허용/거부(Effect)</strong> 할지 나타낸다.</p>
<h2 id="결과-확인">결과 확인</h2>
<h3 id="로그">로그</h3>
<p>이제 권한을 추가했으니 <code>gook-app-image</code> 버킷에 이미지를 업로드하여 Lambda를 실행해 보자.</p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/fac68bcc-e656-4fdb-ab42-898c0675f5a1/image.png" alt=""></p>
<p>Lambda 함수가 에러 없이 잘 실행되었다.</p>
<h3 id="썸네일">썸네일</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/f165fe3b-633c-43e7-b277-ff2d672e39cb/image.png" alt=""></p>
<p><code>gook-app-thumbnail</code> 버킷에 이미지가 잘 생성된 것을 확인할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/77912152-376a-4cb6-b6e1-ef6bc74bf87d/image.png" alt=""></p>
<p>200 x 200 크기의 이미지에서 100 x 100 크기의 썸네일이 잘 생성되었다.</p>
<h2 id="next">Next</h2>
<p>AWS Lambda가 무엇이고, 언제 어떻게 사용하는지 간단하게 알아보았다. 다음에는 트리거 유형의 차이와 Lambda 로그 확인 방법에 대해서 알아보자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[S3 보안 - 버킷 정책]]></title>
            <link>https://velog.io/@goo-gy/S3-%EB%B3%B4%EC%95%88-%EB%B2%84%ED%82%B7-%EC%A0%95%EC%B1%85</link>
            <guid>https://velog.io/@goo-gy/S3-%EB%B3%B4%EC%95%88-%EB%B2%84%ED%82%B7-%EC%A0%95%EC%B1%85</guid>
            <pubDate>Fri, 17 Nov 2023 16:58:22 GMT</pubDate>
            <description><![CDATA[<h2 id="s3-접근-권한">S3 접근 권한</h2>
<ul>
<li>자격 증명 기반 정책<ul>
<li>사용자</li>
<li>그룹</li>
<li>역할</li>
</ul>
</li>
<li>리소스 기반 정책<ul>
<li><strong>버킷 정책(Bucket policy)</strong></li>
<li>ACL (Access Control List)</li>
</ul>
</li>
</ul>
<blockquote>
<p><a href="https://velog.io/@goo-gy/IAM-Identity-and-Access-Management">AWS IAM (Identity and Access Management)</a></p>
</blockquote>
<p>자격 증명(사용자, 그룹, 역할) 기반 정책은 이미 위 포스트에서 다루었다. 이번에는 주로 S3에서 많이 사용하는 리소스 기반 정책(특히 버킷 정책)에 대해서 알아보자.</p>
<h2 id="bucket-policy">Bucket policy</h2>
<h3 id="권한-없는-사용자">권한 없는 사용자</h3>
<p>어떤 권한도 주지 않은 <strong>tester</strong>라는 사용자로, 콘솔에서 (URL을 통해) <strong>gook-policy-test</strong> 버킷에 접속해 보았다.</p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/6890ab7e-86b9-4c4a-abd5-9f113c9ea9ea/image.png" alt=""></p>
<p>tester는 S3에 대한 권한이 없기 때문에, gook-policy-test라는 버킷 내의 어떤 파일도 확인할 수 없다.</p>
<h3 id="s3에-버킷-정책-적용">S3에 버킷 정책 적용</h3>
<p>tester라는 사용자에게 권한을 부여하는 대신 S3에 버킷 정책을 부여하여 tester에게 권한을 줄 수 있다.</p>
<pre><code class="language-json">{
    &quot;Version&quot;: &quot;2012-10-17&quot;,
    &quot;Statement&quot;: [
        {
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Principal&quot;: {
                &quot;AWS&quot;: &quot;arn:aws:iam::{계정명}:user/{사용자명}&quot;
            },
            &quot;Action&quot;: [
                &quot;s3:ListBucket&quot;,
                &quot;s3:GetObject&quot;
            ],
            &quot;Resource&quot;: [
                &quot;arn:aws:s3:::{bucket 명}&quot;,
                &quot;arn:aws:s3:::{bucket 명}/*&quot;
            ]
        }
    ]
}</code></pre>
<p>리소스 기반 정책에서는 <strong>Principal</strong>을 통해 권한을 적용할 <strong>주체(사용자, 그룹, 역할 등)</strong>을 지정할 수 있다. 내 계정의 tester를 지정해 주었다. Action을 통해 어떤 권한을 줄 것인지 지정하고, Resource에는 버킷과 버킷 내부의 모든 파일로 지정해 주었다.</p>
<blockquote>
<p>공식 문서 <a href="https://docs.aws.amazon.com/ko_kr/IAM/latest/UserGuide/reference_policies_elements.html">IAM JSON 정책 요소 참조</a>에서 자세히 확인할 수 있다.</p>
</blockquote>
<h3 id="권한-확인">권한 확인</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/40a10c5d-0310-43e3-a1f3-90290c4cb291/image.png" alt=""></p>
<p>버킷 정책을 통해 <strong>tester</strong>라는 사용자에게 <strong>gook-policy-test</strong>라는 버킷에 대해 <code>s3:GetObject</code> 권한을 부여하였다. 이제 tester는 gook-policy-test 버킷의 파일들을 확인할 수 있다.</p>
<h3 id="다른-계정-간-권한-부여">다른 계정 간 권한 부여</h3>
<p>앞에서 살펴 본 <strong>리소스 기반 정책 (버킷 정책)</strong>의 핵심은 자격 증명 주체(사용자, 그룹, 역할)가 아니라 리소스에게 정책을 할당하여 권한을 부여한다는 것이다.</p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/337184c7-5a32-48ea-8e14-7b41d8451991/image.png" alt=""></p>
<p>자격 증명 기반 정책으로는 다른 계정에 있는 리소스에 대한 권한을 줄 수 없다. 하지만 리소스 기반 정책을 사용하면 위와 같이 다른 계정에 있는 사용자에게도 리소스에 대한 권한을 부여할 수 있다.</p>
<p>B 마음대로 A 계정에 있는 버킷에 접근 권한을 설정할 수는 없겠지만, A 계정이 본인의 S3 버킷에 대해 B 계정의 주체(사용자, 그룹, 역할)에게 권한을 줄 수 있는 것이다.</p>
<h2 id="acl-access-control-list">ACL (Access Control List)</h2>
<p> ACL은 버킷 내의 파일 별로 접근 제어를 수행할 수 있지만 일반적으로 사용하는 방식은 아니다. AWS에서도 <code>&quot;Amazon S3의 최신 사용 사례 대부분은 더 이상 ACL을 사용할 필요가 없습니다. 각 객체에 대해 액세스를 개별적으로 제어할 필요가 있는 드문 상황을 제외하고는 ACL을 비활성화한 채로 두는 것이 좋습니다.&quot;</code>라고 한다. </p>
<blockquote>
<p>그래도 궁금하다면 <a href="https://docs.aws.amazon.com/ko_kr/AmazonS3/latest/userguide/acl-overview.html">ACL(액세스 제어 목록) 개요</a>를 참고하자.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Amazon S3]]></title>
            <link>https://velog.io/@goo-gy/Amazon-S3</link>
            <guid>https://velog.io/@goo-gy/Amazon-S3</guid>
            <pubDate>Fri, 03 Nov 2023 15:20:42 GMT</pubDate>
            <description><![CDATA[<h2 id="amazon-s3">Amazon S3</h2>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/c167bd09-55bf-4f55-b0a5-d69360c2f3fb/image.png" alt=""></p>
<p>S3(Simple Storage Service)는 객체 스토리지 서비스로 버킷(Bucket)으로 구분된 단위에 파일(Object)를 저장할 수 있는 서비스이다.</p>
<h2 id="s3-버킷-bucket">S3 버킷 (Bucket)</h2>
<p>S3는 파일(Object)을 버킷(Bucket)이라는 곳에 모아서 저장한다. </p>
<ul>
<li>버킷은 리전 별로 생성된다.</li>
<li>버킷 이름은 AWS 전체에서 고유하게 설정해야 한다. </li>
<li>버킷 이름은 <code>소문자, 숫자, &#39;-&#39;</code>로 3-63 글자로 설정할 수 있다.</li>
</ul>
<h2 id="s3-오브젝트-object">S3 오브젝트 (Object)</h2>
<p>S3에 저장되는 파일을 의미한다. Object는 정확한 파일을 가리킬 수 있도록 Key를 가진다. 이 Key는 경로(Prefix)와 파일 이름(Object name)으로 구성된다. </p>
<blockquote>
<p>ex) s3://buekct-name/path1/path2/object.txt</p>
</blockquote>
<h3 id="제한">제한</h3>
<ul>
<li>Object의 최대 크기는 5TB이다.</li>
<li>5GB가 넘는 파일을 업로드할 때는 Multi-part로 나누어서 업로드한다.</li>
</ul>
<h2 id="s3-storage-class">S3 storage class</h2>
<p>S3는 목적에 맞게 사용할 수 있도록, 다음과 같이 성능, 검색 비용, 저장 비용 등이 구분된 Storage class를 가지고 있으며, 파일(Object) 별로 적용된다.</p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/254651e8-96f5-432d-a8ad-661731887c62/image.png" alt=""></p>
<ul>
<li><strong>Amazon S3 standard</strong><ul>
<li>General Purpose</li>
<li>Infrequent Access (IA)</li>
</ul>
</li>
<li><strong>Amazon S3 Glacier</strong><ul>
<li>Instant Retrieval</li>
<li>Flexible Retrieval</li>
<li>Deep Archive</li>
</ul>
</li>
<li><strong>Amazon S3 one zone</strong><ul>
<li>Infrequent Access</li>
</ul>
</li>
</ul>
<h3 id="amazon-s3-standard">Amazon S3 Standard</h3>
<p><strong>S3 Standard(General Purpose)</strong>는 검색(조회) 비용이 저렴하여 자주 접근되는 파일을 저장할 때 용이하다. <strong>S3 standard-IA</strong>는 General Purpose와 성능은 동일하다. 대신 저장 비용이 더 저렴하고, 검색 비용은 더 비싸기 때문에 자주 접근되지 않는 데이터를 저장하는데 적합하다.</p>
<h3 id="amazon-s3-glacier">Amazon S3 Glacier</h3>
<p><strong>S3 Glacier</strong>는 저장 비용이 저렴하고, 검색 비용이 비싸기 때문에 거의 액세스 되지 않는 파일에 적합하다. 이 중에 <strong>S3 Glacier Instant Retrieval</strong>은 <strong>S3 Standard</strong>와 동일한 성능을 제공하지만,
<strong>S3 Glacier Flexible Retrieval</strong>, <strong>S3 Glacier Deep Archive</strong>는 바로 접근할 수 없다. 접근하려면 복원 작업(클래스 변경)이 필요하다.</p>
<h3 id="amazon-s3-one-zone">Amazon S3 One Zone</h3>
<p><strong>S3 One Zone</strong>은 3개의 가용 영역을 사용하는 다른 클래스와 달리 하나의 가용 영역만 사용하기 때문에 내구성/가용성이 떨어진다. 대신 저장 비용이 더 저렴하다.  </p>
<blockquote>
<p>자세한 성능 및 가격은 <a href="https://aws.amazon.com/ko/s3/storage-classes/">Amazon S3 스토리지 클래스</a>을 참고하자</p>
</blockquote>
<h3 id="s3-lifecycle-configuration">S3 Lifecycle Configuration</h3>
<p><strong>S3 Lifecycle Configuration</strong>을 활용하면 시간의 흐름에 따라 파일(Object)의 Storage class를 변경할 수 있다. 서버의 로그를 S3에 저장한다고 하자. 로그가 처음 한 달 동안만 자주 접근되고, 6개월 정도 지나면 거의 접근되지 않는다고 한다면 다음과 같이 설정할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/708b581a-5b75-42b5-a0e5-64d36f00ea0b/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ Amazon RDS]]></title>
            <link>https://velog.io/@goo-gy/Amazon-RDS</link>
            <guid>https://velog.io/@goo-gy/Amazon-RDS</guid>
            <pubDate>Fri, 22 Sep 2023 15:05:36 GMT</pubDate>
            <description><![CDATA[<p><strong>Amazon RDS (Relational database service)</strong>는 말 그대로 AWS에서 제공하는 관계형 데이터베이스 서비스로 <strong>완전 관리형</strong>으로 제공된다. 실제로 데이터베이스를 사용하기 위해서는 실행될 서버, 스토리지 구성뿐만 아니라를 DB를 설치하고, 운영하기 위한 업데이트도 필요하다. Amazon RDS는 이를 모두 내부적으로 처리하여 완전 관리형으로 제공한다. 그만큼 가격도 비싼데, 그 값을 하는지 한 번 알아보자.</p>
<h2 id="rds-기능">RDS 기능</h2>
<p>RDS는 다음과 같은 기능들을 제공하고 있다.</p>
<ul>
<li><strong>Postgres, MySQL, MairaDB, Oracle, Mirosoft SQL Server, Aurora 지원</strong></li>
<li><strong>완전 관리형 서비스</strong><ul>
<li>서버 및 데이터베이스 프로비저닝 자동화</li>
<li>OS 패치 자동화</li>
</ul>
</li>
<li><strong>지속적인 백업 생성</strong><ul>
<li>자동으로 백업하여 특정 시점으로 복원할 수 있다.</li>
<li>최대 35일 전까지</li>
</ul>
</li>
<li><strong>대시보드 모니터링</strong></li>
<li><strong>Auto scaling</strong></li>
<li><strong>읽기 복제본 (Read replicas) 생성 가능</strong></li>
<li><strong>Multi-AZ</strong></li>
</ul>
<h2 id="rds-구조">RDS 구조</h2>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/582fb4bd-e46d-4c01-ba45-b5e110040c5b/image.png" alt=""></p>
<p>Amazon RDS는 EC2 + EBS (Storage)로 구성되어 데이터베이스를 형태로 제공되는 PaaS 서비스이다. EC2의 컴퓨팅으로 운영체제와 데이터베이스를 실행하고, 데이터 저장을 위한 스토리지로 EBS를 사용한다. 때문에 우리는 연결된 스토리지의 용량을 원하는 대로 늘릴 수 있고, Auto scaling을 활성화하여 자동으로 확장하도록 구성할 수 있다.</p>
<h2 id="read-replicas">Read Replicas</h2>
<p>일반적으로 데이터베이스는 쓰기 작업보다 읽기 작업이 훨씬 많다. 거기다 애플리케이션 동작 외에도 데이터 분석 등을 위해 DB 읽기 작업이 추가될 수 있다. 많은 읽기 작업으로 부하가 커질 수 있는데, RDS는 <strong>읽기 복제본(Read replicas)을 제공</strong>하여 작업 부하를 줄일 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/f90b06cc-b9a4-4259-95d5-1ac2d64a4c10/image.png" alt=""></p>
<ul>
<li><p>데이터가 비동기식으로 복제 </p>
</li>
<li><p>다른 가용 영역(AZ), 다른 리전(Region)에도 생성 가능</p>
</li>
<li><p>Read replicas를 DB로 전환 가능</p>
</li>
<li><p>최대 5개 생성 가능</p>
</li>
</ul>
<blockquote>
<p>Read replica가 다른 AZ에 있어도 데이터 전송에 따른 네트워크 비용이 발생하지 않는다.
하지만 다른 리전에 있다면 데이터 전송 비용이 발생한다.</p>
</blockquote>
<h2 id="multi-az">Multi-AZ</h2>
<p>Multi-AZ는 데이터가 동기식으로 복제되며, 주로 재해복구를 위한 대비책으로 사용한다. 단일 DB 인스턴스도 언제든 무중단으로 Multi-AZ 전환이 가능하다.</p>
<h2 id="rds-생성하기">RDS 생성하기</h2>
<h3 id="배포-옵션">배포 옵션</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/9d519e34-4762-4277-9bbb-1485ea516507/image.png" alt=""></p>
<p>단일 DB 인스턴스로 구성하고 Read replica를 직접 추가해 보자.</p>
<h3 id="자격증명-설정">자격증명 설정</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/a7a3f142-6c8d-47d2-a15e-8213aae5ef5a/image.png" alt=""></p>
<h3 id="사양-설정">사양 설정</h3>
<ul>
<li>인스턴스 (EC2)</li>
</ul>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/8ed8c930-0c86-41c2-a38b-88e9100ec3b9/image.png" alt=""></p>
<ul>
<li>스토리지 (EBS)</li>
</ul>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/2a6e6ea3-f3c2-4e47-9699-6f411a4ce731/image.png" alt=""></p>
<p>원하는 사양과 스토리지 Auto scaling을 사용 여부를 선택하면 된다.</p>
<h3 id="네트워크-구성">네트워크 구성</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/b6580447-a700-4c58-adee-91ff012e2fe5/image.png" alt=""></p>
<p>RDS를 생성하려면 VPC와 DB 서브넷 그룹을 선택해야 한다.  </p>
<blockquote>
<p>VPC, 서브넷에 대해 잘 모르겠다면 <a href="https://velog.io/@goo-gy/AWS-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-VPC-Subnet-">AWS 네트워크 (VPC, Subnet, ...)</a>를 참고하자</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/367c9983-2134-49b6-830d-2b86236b63f7/image.png" alt=""></p>
<p>RDS를 생성하려면 DB 서브넷 그룹은 최소 2개 이상의 AZ를 포함해야 한다. (언제든지 Multi-AZ로 전환할 수 있도록 구성하기 위한 조건으로 보인다.)</p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/d19ea61a-43c2-437f-9bfc-aa6b46c23f2d/image.png" alt=""></p>
<p>DB 인스턴스가 생성될 AZ도 선택해준다.</p>
<h3 id="보안-그룹">보안 그룹</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/ed80621d-0f94-4948-9966-c4005d181fc3/image.png" alt=""></p>
<p>보안 그룹을 새로 생성하고 이후에 필요한 허용 규칙을 추가해 주자.</p>
<h3 id="데이터베이스-인증">데이터베이스 인증</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/0ad9883e-3998-4fdf-b0a3-b0b05b4fbfd1/image.png" alt=""></p>
<p>RDS는 암호만을 사용한 인증뿐만 아니라 IAM 인증을 함께 사용할 수 있다. </p>
<h3 id="백업--암호화">백업 &amp; 암호화</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/9f699e9b-6466-4f5d-8fc6-cf625e61413e/image.png" alt=""></p>
<h2 id="확인">확인</h2>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/1a322ad2-cc98-47af-8c29-3061fcc830da/image.png" alt=""></p>
<p>DB 생성이 완료되면 엔드 포인트가 나타난다. 이 주소를 통해 RDS DB에 접속할 수 있다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS 방화벽 (Security group, NACL)]]></title>
            <link>https://velog.io/@goo-gy/AWS-%EB%B0%A9%ED%99%94%EB%B2%BD-Security-group-NACL</link>
            <guid>https://velog.io/@goo-gy/AWS-%EB%B0%A9%ED%99%94%EB%B2%BD-Security-group-NACL</guid>
            <pubDate>Sun, 16 Jul 2023 15:26:12 GMT</pubDate>
            <description><![CDATA[<p>VPC와 NACL은 VPC 내부에서 적용할 수 있는 방화벽이다. 이 두 가지 방화벽에 대해서 알아보고 어떤 차이점이 있는지 비교해 보자.</p>
<h2 id="보안-그룹-security-group">보안 그룹 (Security group)</h2>
<p>보안 그룹은 AWS 리소스 (EC2 인스턴스) 단위로 적용할 수 있는 방화벽이다. 인바운드/아웃바운드로 구분하여 방화벽 규칙을 지정할 수 있다. 보안 그룹에는 허용(Allow) 규칙만 존재한다. 기본적으로 모든 트래픽을 차단하고 있고, 허용된 트래픽만 이동할 수 있다. </p>
<blockquote>
<p><a href="https://docs.aws.amazon.com/ko_kr/vpc/latest/userguide/security-groups.html">https://docs.aws.amazon.com/ko_kr/vpc/latest/userguide/security-groups.html</a></p>
</blockquote>
<h3 id="인바운드-규칙-inbound-rule">인바운드 규칙 (Inbound rule)</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/fd89ff85-53fb-4714-a70f-abe8a3a3415a/image.png" alt=""></p>
<p>인바운드 규칙은 기본적으로 아무것도 등록되어 있지 않다. 따라서 EC2 인스턴스에 SSH로 접속하려면 TCP 22번 포트를 열어 줘야 한다. 소스는 0.0.0.0/0과 같이 모든 주소 (또는 특정 CIDR IP 범위)로 허용할 수도 있고 다른 보안 그룹을 지정할 수도 있다.</p>
<h3 id="아웃바운드-규칙-outbound-rule">아웃바운드 규칙 (Outbound rule)</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/ccfb30fe-802a-48f2-b4e1-9fdfbd0f4260/image.png" alt=""></p>
<p>아웃바운드 규칙 역시 같은 방식으로 규칙을 추가하여 나가는 트래픽을 허용할 수 있다. 아웃바운드 규칙에는 기본적으로 모든 트래픽을 모든 대상으로 허용하는 규칙이 추가되어 있다.</p>
<h3 id="stateful">Stateful</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/448997f5-b7fa-4aa6-aee7-fdc3de3299ec/image.png" alt=""></p>
<p>보안 그룹은 Stateful이라는 특성을 가진다. 트래픽이 오고 가는 상태를 기억하고 처리해 준다는 것이다. 
예를 들어 인바운드 규칙으로 아무것도 등록되어 있지 않다면 어떤 트래픽도 리소스로 접근할 수 없다. 하지만 만약 아웃바운드 규칙으로 허용되어 나간 트래픽(Request)에 대한 응답(Response)은 들어올 수 있도록 허용된다.</p>
<h2 id="nacl-network-">NACL (Network ...)</h2>
<p>NACL은 <strong>서브넷 단위</strong>로 적용할 수 있는 방화벽이다. NACL에는 <strong>허용 규칙과 거부 규칙이 모두 존재</strong>한다. 그리고 보안 그룹과 달리 <strong>Stateless</strong>하다. 즉 NACL 인바운드에서 허용되어 들어왔다 하더라도, 아웃바운드 규칙에서 거부되어 통신이 안될 수 있다.  </p>
<h3 id="nacl-규칙">NACL 규칙</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/ddd4cefd-9d1d-4d44-86f9-5ee06a5a1d21/image.png" alt=""></p>
<ul>
<li>NACL 규칙에는 Allow/Deny가 모두 존재한다.</li>
<li>각 규칙의 번호(1 ~ 32766)가 낮을수록 우선순위가 높다.</li>
<li>모든 NACL은 <strong>*번</strong>으로 모든 트래픽을 거부하는 규칙이 있다.</li>
<li>Default NACL에는 Inbound/Outbound의 100번 규칙으로 모든 트래픽을 허용하는 규칙이 있다.</li>
</ul>
<blockquote>
<p>*번은 마지막 규칙으로 우선순위가 가장 낮다.</p>
</blockquote>
<h3 id="nacl-주의사항-stateless">NACL 주의사항: Stateless</h3>
<ul>
<li>임시 포트 (Ephemeral port)</li>
</ul>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/ff4632e1-6742-4031-8044-c0f22fa58959/image.png" alt=""></p>
<p>일반적으로 서버의 포트는 고정되어 있지만 클라이언트는 임시 포트 범위 중에서 랜덤으로 사용한다. 따라서 Stateless한 NACL에서는 임시 포트를 신경 써서 관리해야 한다. 예를 들어 서버 서브넷에는 아웃바운드로 클라이언트의 임시 포트 범위를 열어 주어야 한다.  </p>
<h2 id="보안-그룹-vs-nacl">보안 그룹 vs NACL</h2>
<p>보안 그룹과 NACL 모두 논리적인 개념으로 하나를 가지고 여러 리소스에 연결하여 적용할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/04c6051c-0a89-43b7-b821-7ce4f18eddbd/image.png" alt=""></p>
<table>
<thead>
<tr>
<th></th>
<th>보안 그룹</th>
<th>NACL</th>
</tr>
</thead>
<tbody><tr>
<td>범위</td>
<td>리소스 (인스턴스)</td>
<td>서브넷</td>
</tr>
<tr>
<td>규칙</td>
<td>Allow Only</td>
<td>Allow / Deny</td>
</tr>
<tr>
<td>상태 저장</td>
<td>Stateful</td>
<td>Stateless</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS 네트워크 (VPC, Subnet, ...)]]></title>
            <link>https://velog.io/@goo-gy/AWS-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-VPC-Subnet-</link>
            <guid>https://velog.io/@goo-gy/AWS-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-VPC-Subnet-</guid>
            <pubDate>Sun, 02 Jul 2023 09:14:10 GMT</pubDate>
            <description><![CDATA[<p><a href="https://velog.io/@goo-gy/Amazon-EC2">Amazon EC2</a> 에서 EC2를 생성해 보았다. EC2와 같이 직접 프로비저닝하고 관리하는 자원은 보통 VPC (Virtual Private Cloud)에 생성된다. 실습을 위해서 Default VPC에서 생성해 보았지만, 실제로 배포할 서비스라면 AWS 네트워크에 대해서 잘 알아두고 적절하게 구성할 필요가 있다. </p>
<h2 id="vpc">VPC</h2>
<p>먼저 가장 큰 단위인 Amazon VPC(Virtual Private Cloud)에 대해서 알아보자. VPC는 특정 리전 내에서 AWS 자원을 배치할 수 있는 가상의 네트워크이다. 내부 통신을 위해 <strong>사설 IP (Private IP)</strong> 대역을 사용하며, Subnet mask를 활용하는 <strong>CIDR</strong> 형태의 주소를 사용한다.</p>
<blockquote>
<p><strong>CIDR (Classless Inter-Domain Routing)</strong>
CIDR은 IP를 사용할 때 클래스로 네트워크 주소를 구분하지 않고 Subnet mask를 사용한다. 클래스 기반 방식보다 더 유연하게 네트워크 크기를 조절할 수 있다. 
<a href="https://aws.amazon.com/ko/what-is/cidr/">https://aws.amazon.com/ko/what-is/cidr/</a></p>
</blockquote>
<blockquote>
<p><strong>Private IP</strong>
외부 통신과 별개로, 특정 네트워크 내에서만 통신하기 위한 IP 주소. 따라서 분리된 네트워크에서는 Private IP가 중복되어도 문제없다. 아래 대역을 Private IP로 사용할 수 있다.
<a href="https://en.wikipedia.org/wiki/Private_network">https://en.wikipedia.org/wiki/Private_network</a></p>
</blockquote>
<ul>
<li>10.0.0.0/8
10.0.0.0 – 10.255.255.255</li>
<li>172.16.0.0/12
172.16.0.0 – 172.31.255.255</li>
<li>192.168.0.0/16
192.168.0.0 – 192.168.255.255</li>
</ul>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/8932d313-c1f8-42e5-aa4d-4a912c0b507f/image.png" alt=""></p>
<p>한 리전에 여러 개의 VPC를 만들 수 있다. (최대 5개지만 추가 신청할 수 있음) VPC의 CIDR은 내부에서 통신하기 위한 가상 주소이기 때문에 중복되어도 상관없다. 하지만 VPC 간에 연결할 계획이 있다면 다르게 설정해야 한다.</p>
<h2 id="서브넷-subnet">서브넷 (Subnet)</h2>
<p>AWS는 VPC 내에서 IP 주소 범위로 분리하여 관리할 수 있도록 서브넷(Subnet)을 제공한다. 하나의 서브넷은 특정 가용 영역 내에서 관리된다. </p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/d00a851e-0345-46f2-a935-42f5dcab19bc/image.png" alt=""></p>
<p><a href="https://velog.io/@goo-gy/AWS-Region-Available-Zone">AWS 리전(Region)과 가용 영역 (Availability Zone)</a> 에서 다룬 것처럼 고가용성을 위해 동일 목적의 서브넷을 하나 이상의 AZ에 생성해 두는 것이 좋다.</p>
<h3 id="예약된-ip">예약된 IP</h3>
<p>각 서브넷에서 5개 (처음 4개, 마지막 1개)의 IP는 자동으로 예약된다.
ex) 10.0.0.0 / 24</p>
<ul>
<li>네트워크 주소 (10.0.0.0)</li>
<li>VPC 라우터 (10.0.0.1)</li>
<li>Amazon-provided DNS (10.0.0.2)</li>
<li>미래를 위해 예약된 주소 (10.0.0.3)</li>
<li>Broadcast (10.0.0.255)</li>
</ul>
<h2 id="네트워크-리소스">네트워크 리소스</h2>
<p>VPC 내부에 우리가 사용하는 EC2, RDS(DB) 등의 리소스를 생성했다고 하자. 그러면 VPC 내부의 리소스들은 서로 통신할 수 있지만 외부와는 통신할 수 없을 것이다. VPC 내의 리소스들이 외부와 통신하기 위해 필요한 자원들을 알아보자.</p>
<ul>
<li>인터넷 게이트웨이 (Internet gateway)</li>
<li>NAT 게이트웨이 (NAT gateway)</li>
<li>라우팅 테이블 (Routing table)</li>
</ul>
<h3 id="인터넷-게이트웨이-internet-gateway">인터넷 게이트웨이 (Internet Gateway)</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/1fa37ee3-8d27-459c-9035-3d5be33c4c01/image.png" alt=""></p>
<p>VPC 내부의 리소스와 외부 인터넷 간에 통신할 수 있게 해준다. Public 서브넷에서 외부로 접근할 수도 있고, 리소스에 Public IP가 있다면 외부에서 해당 리소스에 접근할 수도 있다.</p>
<blockquote>
<p><a href="https://docs.aws.amazon.com/ko_kr/vpc/latest/userguide/VPC_Internet_Gateway.html">https://docs.aws.amazon.com/ko_kr/vpc/latest/userguide/VPC_Internet_Gateway.html</a></p>
</blockquote>
<h3 id="nat-게이트웨이-nat-gateway">NAT 게이트웨이 (NAT Gateway)</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/fa4a05ec-b7b5-4f92-8958-f6d847e7a872/image.png" alt=""></p>
<p>Private 서브넷의 리소스는 외부에서 접근할 수 없도록 막아야 하지만, 내부에서 외부 인터넷으로의 접근은 필요할 수 있다. 이때 NAT 게이트웨이를 사용할 수 있다.</p>
<blockquote>
<p><a href="https://docs.aws.amazon.com/ko_kr/vpc/latest/userguide/vpc-nat-gateway.html">https://docs.aws.amazon.com/ko_kr/vpc/latest/userguide/vpc-nat-gateway.html</a></p>
</blockquote>
<h3 id="라우팅-테이블-route-table">라우팅 테이블 (Route Table)</h3>
<p>인터넷 게이트웨이, NAT 게이트웨이를 생성했다고 해서 내부 리소스에서 외부 인터넷과 통신할 수 있는 것은 아니다. 라우팅 테이블 설정에서 게이트웨이를 사용하도록 설정해 주어야 한다. </p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/f7fc1e86-36de-404b-a2ae-ec002b5ee44d/image.png" alt=""></p>
<p>위 라우팅 테이블을 보면 10.1.0.0/16 에 포함되는 주소는 패킷을 local로 보내고 나머지(0.0.0.0/0)는 인터넷 게이트웨이로 보내도록 설정되었다.</p>
<blockquote>
<p><strong>라우팅 테이블의 우선순위 (Logest Match Rule)</strong>
대상 주소를 포함하는 규칙 중 네트워크 주소가 가장 긴 규칙에 해당하는 대상으로 보낸다. 
ex) 10.1.0.100 이라는 주소는 10.1.0.0/16, 0.0.0.0/0 모두 포함되지만 네트워크 주소가 더 긴 10.1.0.0/16의 우선순위가 더 높다.</p>
</blockquote>
<h2 id="실습-vpc-구성하기">실습: VPC 구성하기</h2>
<p><code>VPC &gt; VPC 생성</code>으로 진입해서 <code>VPC 등</code> 옵션을 위에서 다룬 리소스들까지 한 번에 생성할 수 있다. VPC CIDR만 지정해 주면 서브넷의 IP는 자동으로 할당해 주고, AZ(가용 영역)의 수도 선택할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/c0875e22-e1ec-4873-97e9-70ec8d05082e/image.png" alt=""></p>
<p>그림처럼 리소스 맵도 확인할 수 있다. 그림을 보면 각 서브넷에 라우팅 테이블이 연결되고 해당 라우팅 테이블에서 연결된 자원도 확인할 수 있다.</p>
<h2 id="next">Next</h2>
<p>지금까지 AWS의 네트워크 대해서 공부하고 직접 구성해 보았다. 하지만 아직 아주 중요한 부분 보안이 남아있다. 다음에는 방화벽 역할을 하는 <a href="https://velog.io/@goo-gy/AWS-%EB%B0%A9%ED%99%94%EB%B2%BD-Security-group-NACL">보안 그룹 (Security group)과 NACL(Network access control list)</a>에 대해서 알아보자. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS IAM (Identity and Access Management)]]></title>
            <link>https://velog.io/@goo-gy/IAM-Identity-and-Access-Management</link>
            <guid>https://velog.io/@goo-gy/IAM-Identity-and-Access-Management</guid>
            <pubDate>Sun, 25 Jun 2023 02:58:26 GMT</pubDate>
            <description><![CDATA[<p>IAM (Identity and Access Management)은 AWS 서비스 및 리소스에 대한 접근 권한을 관리할 수 있는 서비스이다.</p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/3c76df12-19fb-4915-bc9c-66d6f13fd2ca/image.png" alt=""></p>
<p>IAM 서비스에 접속하면 리전이 Global인 것을 확인할 수 있다. IAM은 리전 별로 구분하지 않고 글로벌로 통합하여 관리하는 서비스이다.</p>
<h2 id="iam-resources">IAM Resources</h2>
<ul>
<li>사용자 (User)</li>
<li>그룹 (Group)</li>
<li>정책 (Policy)</li>
<li>역할 (Role)</li>
</ul>
<p>IAM을 이용해 적절한 접근 제어를 적용하려면 위 개념들을 잘 이해하고 넘어가야 한다.</p>
<h2 id="사용자user">사용자(User)</h2>
<p>AWS에서는 일반적으로 하나의 계정(Account)를 여러 사람이 공유하여 사용하게 된다. 같이 작업할 사람이 추가되었다면 이 사용자(User)를 새로 생성하여 신규 작업자에게 제공할 수 있다.</p>
<h3 id="사용자-권한">사용자 권한</h3>
<p>사용자(User)에게 권한을 주는 방법은 다음과 같다.</p>
<ul>
<li>권한 정책(Policy) 연결</li>
<li>그룹(Group)에 포함</li>
<li>역할(Role) 부여</li>
</ul>
<h3 id="사용자-생성">사용자 생성</h3>
<p>별로 실감 나지 않으니 직접 생성하고 사용해 보자.<br><code>사용자 &gt; 사용자 추가</code></p>
<p><strong>Step 1) 사용자 세부 정보 지정</strong>  </p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/183e298b-0855-4b31-8448-029c6365a81b/image.png" alt=""></p>
<p><strong>Step 2) 권한 설정</strong>  </p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/37800d8e-ad08-44b6-8127-8a61aaf6eae9/image.png" alt=""></p>
<p>권한을 줄 수 있는 여러 가지 방법을 확인할 수 있다. 직접 정책 연결을 선택하면 AWS에서 미리 만들어놓은 여러 권한 정책을 확인할 수 있다. 지금은 특별히 권한을 주지 않고 생성해 보자.</p>
<p><strong>Step 3) 검토 및 생성</strong>  </p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/22877fe5-81e8-4ded-b574-fd8aef0af783/image.png" alt=""></p>
<p>앞에서 설정한 내용들을 확인해 볼 수 있다. 생성된 User 계정에 자동으로 암호를 부여해 주고, 로그인 시 재설정하도록 설정된 것을 볼 수 있다. 특별히 권한을 추가하지 않았지만 사용자가 비밀번호를 변경하여 사용할 수 있도록 <code>IAMUserChangePassword</code> 권한이 있는 것도 확인할 수 있다.  </p>
<p><strong>Step 4) 암호 검색</strong>  </p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/53020ef1-c712-4ff7-83e3-4b1ec949cb95/image.png" alt=""></p>
<p>사용자(User) 생성을 완료하면 생성된 사용자의 로그인 정보를 확인할 수 있다. 위 정보를 csv 파일로 다운로드 받아서 사용자 계정을 전달해 주면 된다.</p>
<h3 id="사용자-로그인">사용자 로그인</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/2af9b365-1642-4ce9-8682-a28ffc53d4b3/image.png" alt=""></p>
<p>앞에서 본 콘솔 로그인 URL로 접속하면 &quot;IAM 사용자로 로그인&quot; 페이지로 이동하고, 자동으로 계정 ID가 입력된다. 계정 ID (또는 계정 Alias)를 외우고 있다면 AWS 로그인 페이지로 와서 직접 입력해도 된다. 그리고 전달받은 사용자 이름과 암호를 입력하면 사용자(User)로 로그인할 수 있다.</p>
<blockquote>
<p>매번 링크로 접속하기는 귀찮고, 계정 ID (12자리)를 외우기도 힘들다면, 루트 사용자가 계정 ID 대신 외우기 쉬운 계정 별칭(Alias)를 만들어 줄 수 있다.</p>
</blockquote>
<h3 id="사용자를-생성해야-하는-이유">사용자를 생성해야 하는 이유</h3>
<ul>
<li>사용자에 따라서 권한을 다르게 부여할 수 있다.</li>
<li>AWS 계정에서 어떤 사용자가 어떤 작업을 했는지 확인할 수 있다. (CloudTrail)</li>
</ul>
<h2 id="그룹group">그룹(Group)</h2>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/6ecbba86-0b36-49f9-87c2-f94f4ac66665/image.png" alt=""></p>
<p>그룹(Group)을 이용하면 여러 사용자를 포함시키고 권한 정책(Policy)를 연결하여, 여러 사용자에게 동일한 권한을 제공할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/753e4f39-97e5-4fc3-a701-322903c214ca/image.png" alt=""></p>
<p>한 사용자는 여러 그룹에도 들어갈 수 있다.</p>
<h2 id="정책policy">정책(Policy)</h2>
<p>실제 서비스나 리소스에 대한 권한이 명시되어 있는 리소스이다. 사용자, 그룹, 역할 등에 연결하여 권한을 부여한다.</p>
<h3 id="정책-유형">정책 유형</h3>
<ul>
<li><strong>자격 증명 기반 정책 (Identity-based policies)</strong></li>
<li>리소스 기반 정책 (Resource-based policies)</li>
<li>IAM 권한 경계 (Permissions boundaries)</li>
<li>서비스 제어 정책 (Organizations SCPs)</li>
<li>액세스 제어 목록 (Access control lists (ACLs))</li>
<li>세션 정책 (Session policies)</li>
</ul>
<p>위와 같이 다양한 유형의 정책이 있지만, 여기서는 자격 증명 기반 정책에 대해서만 다룰 것이다. 자격 증명 기반 정책은 <strong>자격 증명(사용자, 그룹, 역할 등)</strong>이 무슨 <strong>작업(Action)</strong>을 어느 <strong>리소스(Resource)</strong>에서 어떤 <strong>조건(Condition)</strong>에서 수행할 수 있는지를 정의하여 제어할 수 있다.</p>
<blockquote>
<p><a href="https://docs.aws.amazon.com/ko_kr/IAM/latest/UserGuide/access_policies.html#policies_id-based">https://docs.aws.amazon.com/ko_kr/IAM/latest/UserGuide/access_policies.html#policies_id-based</a></p>
</blockquote>
<h3 id="사용자에-정책-연결">사용자에 정책 연결</h3>
<p>앞에서 만들었던 John이라는 사용자로 로그인하여 EC2 서비스에 접속해 보자. </p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/e4978671-ba5e-4325-bcde-c5389aa010bb/image.png" alt=""></p>
<p>현재는 아무 권한도 없기 때문에 EC2와 관련된 어떤 것도 조회할 수 없다. </p>
<hr>
<p>그럼 다시 관리자로 로그인하여 John에게 권한을 추가해 주자.
<code>IAM &gt; 사용자 &gt; John &gt; 권한 추가</code></p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/333b27f5-7624-405d-b025-01866b24bd23/image.png" alt=""></p>
<p><code>AmazonEC2FullAccess</code> 권한을 추가하자.</p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/4edc14e9-5335-4cd6-b9c8-66939b13d462/image.png" alt=""></p>
<p>사용자 John의 권한에 AmazonEC2FullAccess가 추가된 것을 볼 수 있다. [+]를 누르면 세부적으로 어떻게 권한이 적용되었는지 Json으로 확인할 수 있다. 이제 다시 John으로 로그인하면 EC2 서비스를 정상적으로 이용할 수 있다. </p>
<h3 id="정책-json-구조">정책 Json 구조</h3>
<p>ex) IAMReadOnlyAccess</p>
<pre><code class="language-json">{
    &quot;Version&quot;: &quot;2012-10-17&quot;,
    &quot;Statement&quot;: [
        {
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: [
                &quot;iam:GenerateCredentialReport&quot;,
                &quot;iam:GenerateServiceLastAccessedDetails&quot;,
                &quot;iam:Get*&quot;,
                &quot;iam:List*&quot;,
                &quot;iam:SimulateCustomPolicy&quot;,
                &quot;iam:SimulatePrincipalPolicy&quot;
            ],
            &quot;Resource&quot;: &quot;*&quot;
        }
    ]
}</code></pre>
<p>Statement 내에 값을 중점적으로 알아보자.</p>
<ul>
<li>Effect
Allow / Deny 지정</li>
<li>Action
어떤 작업인지 지정</li>
<li>Resource
특정 리소스를 지정</li>
<li>Condition
정책이 적용될 조건을 지정
ex) <code>&quot;NotIpAddress&quot;: { &quot;aws:SourceIp&quot;: [&quot;192.168.1.0&quot;, &quot;192.168.1.1&quot;] }</code></li>
</ul>
<blockquote>
<p><a href="https://docs.aws.amazon.com/ko_kr/IAM/latest/UserGuide/reference_policies_elements.html">https://docs.aws.amazon.com/ko_kr/IAM/latest/UserGuide/reference_policies_elements.html</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Amazon EC2]]></title>
            <link>https://velog.io/@goo-gy/Amazon-EC2</link>
            <guid>https://velog.io/@goo-gy/Amazon-EC2</guid>
            <pubDate>Tue, 20 Jun 2023 14:04:56 GMT</pubDate>
            <description><![CDATA[<p>AWS를 사용할 때 대부분의 사람이 가장 먼저 접하는 EC2 (Elastic Compute Cloud)에 대해서 알아보자. EC2는 IaaS (Infrastructure as a Service) 형태로 가상의 컴퓨터를 빌려주는 클라우드 서비스이다. </p>
<h2 id="ec2-keyword">EC2 Keyword</h2>
<ul>
<li>AMI</li>
<li>Instance type</li>
<li>Key pair</li>
<li>Network<ul>
<li>VPC  </li>
<li>Subnet</li>
<li>Security group</li>
</ul>
</li>
<li>Storage<ul>
<li>EBS</li>
</ul>
</li>
<li>More<ul>
<li>구매 옵션</li>
<li>User data</li>
</ul>
</li>
</ul>
<p>위 키워드에 대해서 미리 알아 둔다면 EC2를 생성하고 사용하는 데 어려움이 없을 것이다. 나중에 하나씩 알아보면 되니 당장 모두 이해할 필요는 없다.</p>
<h2 id="ec2-생성하기">EC2 생성하기</h2>
<p>이전 포스팅 <a href="https://velog.io/@goo-gy/AWS-Region-Available-Zone">AWS 리전(Region)과 가용 영역 (Availability Zone)</a>에서 Region에 대해서 배웠다. </p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/a13bc3e6-a4b8-4f63-bb76-cc6a76830b74/image.png" alt=""></p>
<p>EC2를 생성할 리전을 선택하고 EC2 &gt; Instances에 접속하면 Launch instance를 통해 EC를 생성할 수 있다. 직접 EC2를 생성해 보면서 키워드를 하나씩 알아보자.  </p>
<h3 id="ami-amazon-machine-image">AMI (Amazon Machine Image)</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/785b3eb8-3508-4d9d-9393-3ac704d3a5cd/image.png" alt=""></p>
<p>AMI는 Vmware에서 iso를 불러오는 것처럼 OS의 이미지라고 볼 수 있다. Amazon Linux에서 프리티어 사용 가능한 것으로 선택하자.</p>
<blockquote>
<p>AWS 프리티어는 1년 동안 일부 AWS 서비스를 매월 특정 사용량까지 무료로 사용해 볼 수 있는 기능으로, 신규 AWS 계정에서 자동으로 활성화된다.  <a href="https://repost.aws/ko/knowledge-center/what-is-free-tier">https://repost.aws/ko/knowledge-center/what-is-free-tier</a></p>
</blockquote>
<h3 id="instance-type">Instance type</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/a3968d44-189c-4cc6-b7ee-d03717e52c75/image.png" alt=""></p>
<p>Instance type은 CPU, Memory 성능을 결정한다. 타입을 선택하면 CPU 개수, Memory 용량, 비용을 확인할 수 있다. 마찬가지로 프리티어 사용 가능한 <code>t2.micro</code>를 선택하자</p>
<h3 id="key-pair">Key pair</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/5d6cd4f7-5ece-4200-882b-3d2275797f7d/image.png" alt=""></p>
<p>Key pair는 보안을 위해 생성한 EC2에 SSH로 접속할 때 제공할 Key 파일을 설정하는 것이다. 처음 접속하면 Key pair가 없을 테니 생성하자. </p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/5011671b-7d22-4720-ba26-5861b13ce73b/image.png" alt=""></p>
<p>Key pair를 생성하면 EC2에 접속할 때 필요한 Private key 파일이 선택한 타입(.pem)으로 다운로드 된다. 이 파일은 키 페어 생성 시 한 번만 다운로드할 수 있으므로 잃어버리지 말고 잘 보관하고 있자.</p>
<h3 id="network">Network</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/1a6bf42b-9b01-4e93-9456-ac520e9aa1ff/image.png" alt=""></p>
<p>다음으로는 EC2가 생성될 <strong>VPC (Virtual Private Network)</strong>, <strong>Subnet</strong> 등의 네트워크를 구성하고, EC2의 방화벽 역할을 하는 <strong>보안 그룹(Security group)</strong>을 설정해야 한다. 자세한 내용은 이후에 Network를 정리하면서 다룰 것이다. Public IP를 할당하는지 확인하고 넘어가자.  </p>
<h3 id="storage">Storage</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/6c51f51c-4597-4d89-87ec-6c1aa417f185/image.png" alt=""></p>
<p>EC2를 생성하면 기본적으로 EBS Root 볼륨이 생성된다. EBS (Elastic Block Store)는 EC2에 연결하여 사용할 수 있는 Network Drive이다. Root 볼륨 외에도 EBS 볼륨을 추가할 수 있다. EBS에 대해서는 나중에 Storage를 공부할 때 자세히 알아보자. </p>
<h3 id="ec2-확인">EC2 확인</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/6dd05fd8-519e-4e6a-971e-1d2f19347a43/image.png" alt=""></p>
<p>EC2를 생성하면 실행하고, 상태를 확인하는 데 몇 분 정도의 시간이 좀 걸릴 수 있다. EC2를 선택하면 그림과 같이 Public/Private IP, VPC, Status 등 EC2 인스턴스에 대한 여러 가지 정보를 확인할 수 있다.  </p>
<h2 id="ec2-접속하기">EC2 접속하기</h2>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/70fc5879-c18e-4b85-9209-1f6ae33d152f/image.png" alt=""></p>
<p>EC2를 선택하고 연결(Connect) 하면 EC2에 접속하는 방법들을 확인할 수 있다. 4가지 옵션이 있지만 가장 기본적인 SSH client를 사용해 보자.  </p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/fa2527c7-a3db-4e64-91d9-a133a7c0b58c/image.png" alt=""></p>
<p>Example 명령어를 복사해서 아까 다운로드 받은 .pem key가 있는 곳에서 실행하면 EC2 인스턴스에 접속할 수 있다.</p>
<h3 id="ec2-삭제">EC2 삭제</h3>
<p>실습을 해봤다면 이제 추가적인 요금이 발생하지 않도록 EC2 인스턴스를 삭제하자. 한국어로는 중지(Stop), 종료(Terminate)로 표기되어 있는데 <strong>종료(Terminate)가 EC2 인스턴스 삭제</strong>를 의미한다. 헷갈려서 실수로 삭제하는 일이 없도록 하자.    </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS 리전(Region)과 가용 영역 (Availability Zone)]]></title>
            <link>https://velog.io/@goo-gy/AWS-Region-Available-Zone</link>
            <guid>https://velog.io/@goo-gy/AWS-Region-Available-Zone</guid>
            <pubDate>Sat, 17 Jun 2023 23:10:01 GMT</pubDate>
            <description><![CDATA[<p>물리적인 컴퓨팅 자원을 클라우드로 서비스로 제공하는 AWS를 효율적이고 안정적으로 운영하기 위해 리전(Region)과, 가용 영역(Availability zone)에 대해서 알아보자.</p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/aff6b10d-4da2-478b-ab60-8efdcb90052b/image.png" alt=""></p>
<p>AWS는 전 세계적으로 클라우드 서비스를 제공하기 위해 리전(지역)으로 구분하여 서비스를 제공한다.<br><a href="https://aws.amazon.com/about-aws/global-infrastructure/">https://aws.amazon.com/about-aws/global-infrastructure/</a>에 접속하면 전 세계에서 AWS가 어떤 리전에서 지원하고 있는지 한눈에 볼 수 있다.</p>
<h2 id="리전-region">리전 (Region)</h2>
<p>AWS에서 리전은 물리적인 데이터 센터 그룹을 클러스터링 하는 지역을 말한다. 리전을 선택하는 데 다양한 원인이 있을 수 있지만, 대표적으로 <strong>지연 시간</strong>과 <strong>법률 준수(Compliance)</strong> 등이 있다.</p>
<h3 id="리전-선택하기---지연-시간">리전 선택하기 - 지연 시간</h3>
<p>우리가 EC2(가상의 컴퓨터) 서비스를 사용해 우리나라를 대상으로 한 웹 서비스를 배포한다고 하자. 그러면 일반적으로 사용자와 물리적으로 가까운 서울 리전에서 배포하는 것이 좋을 것이다. 우리나라 사람들이 사용하는데 서버가 미국에 있다고 하면 물리적인 거리로 인해 큰 지연 시간을 겪을 수 있다.</p>
<h3 id="리전-선택하기---compliance">리전 선택하기 - Compliance</h3>
<p>우리가 정부 관련 또는 다른 나라에 서비스를 제공하려고 할 때, 데이터 센터를 반드시 국내에 두어야 한다 등의 법이 있을 수 있기 때문에 서비스를 위한 리전을 선택할 때 법적인 부분도 파악해야 한다.</p>
<h3 id="리전-별-서비스">리전 별 서비스</h3>
<p>각 리전 별로 지원되는 서비스가 다를 수 있다. 각 리전 별로 사용 가능한 서비스는 <a href="https://aws.amazon.com/ko/about-aws/global-infrastructure/regional-product-services/">리전 별 AWS 서비스</a>에서 확인할 수 있다.  </p>
<h3 id="리전-확인-aws-console">리전 확인 (AWS Console)</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/8f356b34-621f-4d8b-af1f-0a94a300ddcb/image.png" alt=""></p>
<p>AWS Console 상단 바에서 리전을 선택할 수 있다. </p>
<h2 id="가용-영역availability-zone--az">가용 영역(Availability Zone / AZ)</h2>
<p>가용 영역(Availability zone)은 한 리전 내의 데이터 센터 그룹을 의미한다. AZ(줄여서 AZ라는 표현을 많이 사용한다)는 서비스의 가용성을 높이기 위해 설계되었다.</p>
<h3 id="multi-az">Multi-AZ</h3>
<p>내가 운영하는 서비스를 하나의 AZ에만 배포했다면, 해당 AZ에 재해가 생겼을 때 서비스는 멈추게 된다.</p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/1e1d4557-3ca7-4618-8c84-691b465802f9/image.png" alt=""></p>
<p>하지만 서비스를 그림과 같이 Multi-AZ로 배포한다면 재해로 인해 특정 AZ에 배포한 서비스가 마비되었다 하더라도 다른 AZ의 서비스를 계속 이용할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS SAA-C03 후기]]></title>
            <link>https://velog.io/@goo-gy/AWS-SAA-C03-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@goo-gy/AWS-SAA-C03-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Thu, 08 Jun 2023 14:36:31 GMT</pubDate>
            <description><![CDATA[<p>최근에 AWS를 사용할 일이 많아서 <a href="https://aws.amazon.com/certification/certified-solutions-architect-associate/">AWS Solution Architect</a> 시험에 응시했다. 시험 범위가 넓다 보니 전혀 몰랐던 유용한 서비스들을 많이 알게 되었고, 여러 옵션을 더 잘 이해하고 쓸 수 있어서 좋았다. 물론, 정말 시험 통과를 위해서만 공부한다면 다를 수도 있다.</p>
<h2 id="공부-방법">공부 방법</h2>
<ul>
<li>Udemy 강의 - <a href="https://www.udemy.com/course/best-aws-certified-solutions-architect-associate/">【한글자막】 AWS Certified Solutions Architect Associate 시험합격!</a></li>
<li><a href="https://www.examtopics.com/">Exam Topics</a> Dump 문제 풀이</li>
<li>오답 노트</li>
</ul>
<h3 id="강의">강의</h3>
<p>공부는 주로 Udemy에 <a href="https://www.udemy.com/course/best-aws-certified-solutions-architect-associate/">【한글자막】 AWS Certified Solutions Architect Associate 시험합격!</a> 이라는 강의를 들었고, 유료이긴 하지만 강의가 정말 잘 구성되어 있다. 시험 범위가 넓은 만큼 총 강의 시간은 27시간이다.. 그래도 강의 하나에 평균 5분 정도로 짧게 끊어놔서 편하게 들을 수 있었다. 처음에는 시간 날 때마다 내가 궁금했던 내용을 위주로 들었는데 강의 수가 너무 많다 보니 진도율이 너무 느리게 올랐다.</p>
<p>평소에는 정말 조금씩 실습 하나하나 해가면서 한 달 정도 공부했고, 일주일쯤 남았을 때는 강의를 내용을 위주로 빠르게 들었다. 강의는 총 400개 중 평소에 천천히 100개, 일주일 정도 동안 빠르게 100개 정도 해서 반 정도 들었다. 이미 알고 있는 내용이나 특정 서비스에 너무 심화적인 내용의 강의를 제외하고 들었다. (제외했다기보다는 미루다가 못 들었다.) 하나하나 들을 때마다 확실히 도움 되는 내용인 것 같아서 시간이 충분하다면 다 들었을 것 같다. 들을 거다. 아마도.</p>
<h3 id="dump-문제-풀이">Dump 문제 풀이</h3>
<p>강의를 듣다가 오! 나 이제 문제 잘 풀 수 있을 것 같아! 하는 순간 <a href="https://www.examtopics.com/">Exam Topics</a> 문제를 봤는데, 범위가 넓다 보니 방금 공부한 내용이 좀처럼 안 나와서 재미는 못 봤다. 그래도 강의 들을 때 문제에서 봤던 내용이 나오면 집중력이 좋아지는 것 같다. 문제를 먼저 보고 나서 관련된 부분의 강의를 찾아보는 것도 괜찮다.</p>
<p>평소에 시간 날 때 한두 문제씩 100번 정도까지 풀었다. 시험 3일 전에는 Exam topics 한 번에 1번 ~ 100번을 다시 풀고, 이틀 전에 시험 치듯이 101 ~ 150까지 풀어봤다. 방금 찾아보니 Exam topics에서 270번까지는 무료로 볼 수 있는 것 같다. </p>
<h3 id="오답-노트">오답 노트</h3>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/a8b03195-15d5-49db-9ce7-6ce2585a79d5/image.png" alt=""></p>
<p>시간 날 때 틈틈이 푸는 것도 문제 유형을 파악하는 데는 괜찮았는데, 시험 치듯이 문제를 쭉 풀고 오답 노트를 만드는 게 훨씬 좋았다. Google sheet로 작성했고 틀린 문제에서 풀이를 이해하고 나면 옆에 Keyword와 Solution을 매칭 시켰다.</p>
<h2 id="결과">결과</h2>
<p>시험은 아침에 봤는데 의외로 다음 날에 바로 결과가 나왔다. 메일로 합격 축하를 보내주고 <a href="https://www.credly.com/badges/249787aa-33ea-4440-b733-093fa314dea0/public_url">Badge</a>도 준다. 이런 건 참 잘 만들었다. 기분 좋다.  </p>
<p><img src="https://velog.velcdn.com/images/goo-gy/post/30eb1746-7264-4be3-952e-6277912adbf9/image.png" alt=""></p>
<p>시험 사이트에 들어가면 [취득한 자격증]에서 인증서를 받을 수 있고 [시험 내역]에서 PDF로 점수도 확인할 수 있다. </p>
]]></description>
        </item>
    </channel>
</rss>