<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>hwi_min.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Thu, 29 Jan 2026 08:04:47 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>hwi_min.log</title>
            <url>https://velog.velcdn.com/images/hwi_min/profile/5ee43725-a204-4775-afe6-a823552af473/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. hwi_min.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/hwi_min" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Nginx] 현대 웹의 관문 Nginx란?]]></title>
            <link>https://velog.io/@hwi_min/Nginx-%ED%98%84%EB%8C%80-%EC%9B%B9%EC%9D%98-%EA%B4%80%EB%AC%B8-Nginx%EB%9E%80</link>
            <guid>https://velog.io/@hwi_min/Nginx-%ED%98%84%EB%8C%80-%EC%9B%B9%EC%9D%98-%EA%B4%80%EB%AC%B8-Nginx%EB%9E%80</guid>
            <pubDate>Thu, 29 Jan 2026 08:04:47 GMT</pubDate>
            <description><![CDATA[<p>최근 프로젝트에서 <strong>Nginx</strong>를 통해 포트 제한 문제를 해결하고 HTTPS 환경을 구축했습니다. 설치했다에서 끝나는 것이 아닌 &#39;왜 Nginx가 현대 인프라의 표준이 되었는지&#39; 공식 문서와 실무 관점에서 정리했습니다.</p>
<hr>
<h2 id="1-nginx는-왜-특별한가-vs-apache">1. Nginx는 왜 특별한가? (vs Apache)</h2>
<p>전통적인 웹 서버인 Apache가 요청당 하나의 프로세스/스레드를 사용하는 <strong>Process-driven</strong> 방식이라면 Nginx는 <strong>Event-driven(비동기 이벤트 루프)</strong> 구조를 채택하고 있습니다.</p>
<table>
<thead>
<tr>
<th>비교 항목</th>
<th>Process-driven (Apache 방식)</th>
<th>Event-driven (Nginx 방식)</th>
</tr>
</thead>
<tbody><tr>
<td><strong>핵심 비유</strong></td>
<td>손님마다 전담 웨이터가 1명씩 붙음</td>
<td>웨이터 1명이 여러 테이블을 돌며 주문만 받음</td>
</tr>
<tr>
<td><strong>자원 사용</strong></td>
<td>요청 수에 비례하여 급증</td>
<td>요청이 많아도 낮고 일정하게 유지</td>
</tr>
<tr>
<td><strong>동시 접속</strong></td>
<td>수천 명 이상 시 서버 부하 급증</td>
<td>수만 명 이상의 동시 접속도 거뜬히 처리</td>
</tr>
<tr>
<td><strong>주요 용도</strong></td>
<td>복잡한 비즈니스 로직 처리</td>
<td>대규모 트래픽 처리, 리버스 프록시</td>
</tr>
</tbody></table>
<h4 id="nginx가-apache보다-빠른-이유는-context-switching의-늪에-빠지지-않기-때문">Nginx가 Apache보다 빠른 이유는 Context Switching의 늪에 빠지지 않기 때문!</h4>
<ul>
<li><p><strong>Context Switching이란</strong>, CPU가 한 프로세스(또는 스레드)에서 다른 프로세스로 실행을 넘길 때 현재까지의 작업 상태(Context)를 저장하고 다음 작업의 상태를 불러오는 과정</p>
</li>
<li><p><strong>Context(문맥/상태)</strong>: 프로그램이 어디까지 실행되었는지(프로그램 카운터), 어떤 값을 계산 중이었는지(레지스터 상태) 등의 정보</p>
</li>
<li><p><strong>교환 과정</strong>: A 작업을 멈추고 현재 상태를 메모리에 기록 → B 작업의 이전 상태를 메모리에서 불러옴 → B 작업 실행.</p>
</li>
</ul>
<h2 id="2-nginx의-4대-핵심-기능">2. Nginx의 4대 핵심 기능</h2>
<h4 id="①-리버스-프록시-reverse-proxy--보안">① 리버스 프록시 (Reverse Proxy) &amp; 보안</h4>
<p>클라이언트와 백엔드 서버(Docker 컨테이너) 사이의 중개자입니다.</p>
<ul>
<li><strong>보안 강화:</strong> 백엔드 서버의 실제 IP와 포트(예: 8080, 3000)를 외부에 노출하지 않습니다. 외부에서는 오직 Nginx의 80/443 포트만 보입니다.</li>
<li><strong>버퍼링:</strong> Nginx가 클라이언트의 요청을 끝까지 다 받은 후 백엔드에 전달하므로 느린 네트워크 환경의 클라이언트로부터 백엔드 서버를 보호합니다.</li>
</ul>
<blockquote>
<p><a href="https://www.notion.so/Infra-Nginx-2f71b7d769d980989858e33281526734?source=copy_link">프로젝트 실제 활용 사례 보기</a></p>
</blockquote>
<h4 id="②-로드-밸런싱-load-balancing">② 로드 밸런싱 (Load Balancing)</h4>
<p>여러 대의 서버에 트래픽을 분산합니다.</p>
<ul>
<li><strong>Round Robin:</strong> 순차적으로 분산</li>
<li><strong>Least Connections:</strong> 연결이 가장 적은 서버로 전달</li>
<li><strong>IP Hash:</strong> 특정 사용자(IP)를 특정 서버에 고정 (세션 유지가 필요할 때 사용)</li>
</ul>
<h4 id="③-정적-파일-서빙-static-content-serving">③ 정적 파일 서빙 (Static Content Serving)</h4>
<p>이미지, HTML, JS 파일 같은 정적 자원을 어플리케이션(Spring/Node.js) 단까지 보내지 않고 Nginx가 메모리 캐시를 활용해 즉시 응답합니다. 이는 전체 시스템의 응답 속도를 비약적으로 향상시킵니다.</p>
<h4 id="④-ssltls-종단점-ssl-termination">④ SSL/TLS 종단점 (SSL Termination)</h4>
<p>HTTPS 암호화 해제 작업을 Nginx가 전담합니다. 백엔드 서버는 복잡한 암호화 계산을 할 필요 없이 비즈니스 로직에만 집중할 수 있게 됩니다.</p>
<blockquote>
<p><a href="https://www.notion.so/Infra-Nginx-SSL-Termination-2f71b7d769d980dd8852e12900cc245a?source=copy_link">프로젝트 실제 활용 사례 보기</a></p>
</blockquote>
<h2 id="3-실전-설정-파헤치기-configuration">3. 실전 설정 파헤치기 (Configuration)</h2>
<p>Nginx 설정 (<code>/etc/nginx/sites-available/default</code>)은 <code>context</code> 구조로 이루어져 있습니다. 이번 프로젝트에서 사용한 <code>location</code> 블록의 의미는 다음과 같습니다.</p>
<pre><code class="language-nginx">server {
    listen 80;
    server_name [도메인IP];

    # 프론트엔드: &quot;/&quot; 경로로 들어오면 nnnn번 컨테이너로!
    location / {
        proxy_pass http://127.0.0.1:nnnn;
        proxy_set_header Host $host; # 원본 호스트 정보 유지
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 실제 클라이언트 IP 전달
    }

    # 백엔드: &quot;/api/&quot; 경로로 시작하면 xxxx번 컨테이너로!
    location /api/ {
        proxy_pass http://127.0.0.1:xxxx;
    }
}
</code></pre>
<h2 id="4-인프라-담당자가-주의해야-할-점">4. 인프라 담당자가 주의해야 할 점</h2>
<ol>
<li><strong>502 Bad Gateway:</strong> Nginx는 살아있지만, 뒤에 연결된 Docker 컨테이너가 죽었거나 포트가 다를 때 발생합니다. <code>docker ps</code>로 상태를 확인합니다.</li>
<li><strong>권한 설정:</strong> Nginx 워커 프로세스는 보통 <code>www-data</code> 계정으로 실행됩니다. 정적 파일을 서빙할 때 해당 폴더에 접근 권한이 있는지 확인해야 합니다.</li>
<li><strong>방화벽(UFW):</strong> 외부 포트(80, 443)는 열고, 내부 포트(8080, 3000 등)는 닫아서 보안을 유지하는 것이 핵심입니다.</li>
</ol>
<hr>
<h4 id="🔗-참고-문헌-및-출처">🔗 참고 문헌 및 출처</h4>
<ul>
<li><a href="https://nginx.org/en/docs/">Nginx Official Documentation</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Jenkins] Jenkins + Docker + EC2 배포 파이프라인]]></title>
            <link>https://velog.io/@hwi_min/Jenkins-Jenkins-Docker-EC2-%EB%B0%B0%ED%8F%AC-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8</link>
            <guid>https://velog.io/@hwi_min/Jenkins-Jenkins-Docker-EC2-%EB%B0%B0%ED%8F%AC-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8</guid>
            <pubDate>Wed, 28 Jan 2026 05:43:22 GMT</pubDate>
            <description><![CDATA[<p>우리 프로젝트는 쿠버네티스(K8s)의 복잡함 대신 <strong>Docker 컨테이너를 활용해 EC2에 직접 배포</strong>하는 효율적인 방식을 선택했습니다. K8s 단계가 빠지는 대신 Jenkins가 EC2에 접속해 컨테이너를 교체하는 과정이 추가됩니다.</p>
<blockquote>
<p><a href="https://velog.io/@hwi_min/Jenkins-CICD-%EB%8F%84%EA%B5%AC-%EC%A0%A0%ED%82%A8%EC%8A%A4%EB%9E%80">➡️ (K8s포함된) Jekins 이해하기 _ [Jenkins] CI/CD 도구 젠킨스란?</a></p>
</blockquote>
<hr>
<h3 id="1-수정된-전체-흐름-cicd-workflow">1. 수정된 전체 흐름 (CI/CD Workflow)</h3>
<p><img src="https://velog.velcdn.com/images/hwi_min/post/7a942010-9fd9-490d-a05b-e32618849713/image.png" alt=""></p>
<ol>
<li><strong>CI (지속적 통합)</strong>: 코드를 커밋하면 Jenkins가 빌드하고 Docker 이미지를 생성합니다.</li>
<li><strong>Docker Registry Push</strong>: 생성된 이미지를 Docker Hub(또는 AWS ECR)에 업로드합니다.</li>
<li><strong>CD (지속적 배포)</strong>: Jenkins가 EC2 서버에 SSH로 접속하여, 기존 컨테이너를 내리고 새 이미지를 받아 실행합니다.</li>
</ol>
<h3 id="2-단계별-상세-프로세스">2. 단계별 상세 프로세스</h3>
<h4 id="1️⃣-이미지-빌드-및-푸시-build--push">1️⃣ 이미지 빌드 및 푸시 (Build &amp; Push)</h4>
<ul>
<li>Jenkins 내부에서 <code>docker build</code> 명령어로 애플리케이션 이미지를 만듭니다.</li>
<li>빌드된 이미지는 <strong>Docker Hub</strong> 같은 저장소에 저장합니다. (EC2에서 이 이미지를 당겨와야 하기 때문입니다.)</li>
</ul>
<h4 id="2️⃣-ec2-접속-ssh-agent">2️⃣ EC2 접속 (SSH Agent)</h4>
<ul>
<li>Jenkins의 <strong>SSH Agent</strong> 플러그인을 사용하여 우리 프로젝트의 EC2 서버에 원격 접속합니다.</li>
</ul>
<h4 id="3️⃣-배포-스크립트-실행-deploy">3️⃣ 배포 스크립트 실행 (Deploy)</h4>
<ul>
<li>K8s 배포 대신, EC2 서버에서 직접 다음 명령어를 수행하도록 자동화합니다.</li>
<li><code>docker pull [우리_이미지]</code>: 저장소에서 새 버전 가져오기</li>
<li><code>docker stop [기존_컨테이너]</code>: 실행 중인 구버전 중지</li>
<li><code>docker rm [기존_컨테이너]</code>: 구버전 삭제</li>
<li><code>docker run -d [새_이미지]</code>: 새 버전으로 서버 실행</li>
</ul>
<hr>
<h3 id="3-k8s-환경과의-차이점">3. K8s 환경과의 차이점</h3>
<table>
<thead>
<tr>
<th>구분</th>
<th>K8s 환경</th>
<th>EC2 직접 배포</th>
</tr>
</thead>
<tbody><tr>
<td><strong>배포 대상</strong></td>
<td>K8s 클러스터 (Pod)</td>
<td>AWS EC2 인스턴스</td>
</tr>
<tr>
<td><strong>관리 도구</strong></td>
<td>kubectl, YAML 설정</td>
<td>Docker CLI, Shell Script</td>
</tr>
<tr>
<td><strong>장점</strong></td>
<td>고가용성, 오토스케일링</td>
<td><strong>설정이 간편함, 리소스 절약, 직관적임</strong></td>
</tr>
</tbody></table>
<hr>
<h3 id="💡-insight">💡 Insight</h3>
<p>쿠버네티스가 없다고 해서 자동화 수준이 낮은 것은 아닙니다. 오히려 프로젝트 규모에 따라 <strong>Jenkins와 Docker를 이용한 EC2 자동 배포</strong>가 훨씬 빠르고 안정적인 운영 환경을 제공할 수 있습니다. 이제 &quot;코드 한 줄 커밋&quot;으로 실제 서버까지 테스트할 수 있는 환경을 구축할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Jenkins] CI/CD 도구 젠킨스란?]]></title>
            <link>https://velog.io/@hwi_min/Jenkins-CICD-%EB%8F%84%EA%B5%AC-%EC%A0%A0%ED%82%A8%EC%8A%A4%EB%9E%80</link>
            <guid>https://velog.io/@hwi_min/Jenkins-CICD-%EB%8F%84%EA%B5%AC-%EC%A0%A0%ED%82%A8%EC%8A%A4%EB%9E%80</guid>
            <pubDate>Wed, 28 Jan 2026 01:43:49 GMT</pubDate>
            <description><![CDATA[<h2 id="📌cicd란">📌CI/CD란?</h2>
<p>CI/CD: 지속적 통합/지속적 배포</p>
<ul>
<li>많은 기업들이 빠른 개발 주기와 안정적 배포를 위해 CI/CD도구를 사용하고 있음</li>
<li>그 중 가장 널리 사용되는 도구가 <strong>Jenkins</strong></li>
</ul>
<hr>
<h2 id="📌jenkins란">📌Jenkins란?</h2>
<p><img src="https://velog.velcdn.com/images/hwi_min/post/bc633aad-2ba9-45bf-b496-156239261e99/image.png" alt="Introduction to Jenkins"></p>
<ul>
<li><em>자세한 작동 방식은 아래에 추가 설명 참고</em></li>
<li>오픈 소스 자동화 서버로 소프트웨어 개발 과정에서 <strong>코드 변경 사항을 자동으로 통합하고 배포</strong></li>
<li>코드를 빌드 -&gt; 테스트하며 개발자들이 변경 사항의 품질을 확인하고 문제를 확인할 수 있도록 도움</li>
<li>방대한 플러그인 생태계와 큰 커뮤니티로 유연성과 안정성을 제공</li>
<li>초보자를 위한 간편한 설정 기능 &amp; 고급 사용자를 위한 커스터마이징 옵션 제공</li>
</ul>
<hr>
<h2 id="📌-jenkins는-어떻게-작동할까-cicd-파이프라인-이해하기">📌 Jenkins는 어떻게 작동할까? (CI/CD 파이프라인 이해하기)</h2>
<p>앞서 언급했듯이 Jenkins는 지속적 통합(CI)과 지속적 배포(CD)를 자동화해주는 가장 대표적인 오픈소스 도구입니다. 아래 이미지를 통해 Jenkins가 개발자의 코드를 어떻게 관리하고 배포하는지 그 흐름을 정리해 보겠습니다.</p>
<p><img src="https://velog.velcdn.com/images/hwi_min/post/bc633aad-2ba9-45bf-b496-156239261e99/image.png" alt="Introduction to Jenkins"></p>
<h3 id="1-전체적인-흐름-workflow">1. 전체적인 흐름 (Workflow)</h3>
<p>이미지는 크게 <strong>빌드 실패(Error)</strong> 케이스와 <strong>빌드 성공(Success)</strong> 케이스 두 가지 시나리오를 보여줍니다. 공통적인 시작점은 개발자의 &#39;Commit&#39;입니다.</p>
<ol>
<li><strong>Commit &amp; Push</strong>: 개발자가 코드를 수정하고 Git과 같은 버전 관리 시스템에 소스코드를 커밋합니다.</li>
<li><strong>Jenkins Trigger</strong>: Jenkins는 Git의 변경 사항을 감지하여 자동으로 미리 설정된 파이프라인(Job)을 실행합니다.</li>
</ol>
<h3 id="2-단계별-상세-과정">2. 단계별 상세 과정</h3>
<h4 id="📂-step-1-clone-repo-코드-가져오기">📂 Step 1: Clone Repo (코드 가져오기)</h4>
<p><img src="https://velog.velcdn.com/images/hwi_min/post/c462b4d4-5097-423a-9156-79862fd384e7/image.png" alt="step1"></p>
<p>Jenkins 서버로 원격 저장소(Git)에 있는 최신 코드를 복제(Clone)해옵니다. 이 과정이 완료되어야 이후 빌드 작업을 수행할 수 있습니다.</p>
<h4 id="⚙️-step-2-build--compile-컴파일-및-빌드">⚙️ Step 2: Build / Compile (컴파일 및 빌드)</h4>
<p><img src="https://velog.velcdn.com/images/hwi_min/post/ed9eb544-281d-4544-954e-08e7c9425356/image.png" alt="step2"></p>
<p>가져온 소스코드를 실행 가능한 형태로 컴파일하고 빌드합니다.</p>
<ul>
<li><strong>만약 여기서 오류가 발생한다면?</strong> (이미지 상단 흐름) 곧바로 개발자에게 <strong>Error 알림(Notified)</strong>을 보내며 프로세스가 중단됩니다. 빠른 피드백을 통해 즉각적인 수정이 가능합니다.</li>
</ul>
<h4 id="✅-step-3-unit-test-단위-테스트">✅ Step 3: Unit Test (단위 테스트)</h4>
<p><img src="https://velog.velcdn.com/images/hwi_min/post/fae8c0b2-022b-4411-b1dc-55883f8e1026/image.png" alt="step3"></p>
<p>빌드가 성공하면 작성된 테스트 코드를 실행하여 기능상의 문제가 없는지 검증합니다. 코드의 품질을 보장하는 핵심 단계입니다.</p>
<h4 id="🐳-step-4-docker-이미지-빌드-및-푸시">🐳 Step 4: Docker (이미지 빌드 및 푸시)</h4>
<p><img src="https://velog.velcdn.com/images/hwi_min/post/1d5c4a6b-9fe5-4fed-bd59-6cad8a8d4093/image.png" alt="step4"></p>
<p>테스트를 통과한 애플리케이션을 Docker 이미지로 빌드합니다. 이는 어디서나 동일한 환경으로 실행될 수 있도록 컨테이너화하는 과정입니다.</p>
<h4 id="☸️-step-5-k8s-dev-deploy-쿠버네티스-배포">☸️ Step 5: K8S Dev Deploy (쿠버네티스 배포)</h4>
<p><img src="https://velog.velcdn.com/images/hwi_min/post/da1affa2-1baf-48a3-9621-73e0502474a9/image.png" alt="step5"></p>
<p>생성된 Docker 이미지를 기반으로 Kubernetes(K8s) 개발 환경에 자동으로 배포합니다. 실제 서비스 환경에 적용되기 전 마지막 확인 단계라고 볼 수 있습니다.</p>
<hr>
<h3 id="3-결과-및-피드백-notification">3. 결과 및 피드백 (Notification)</h3>
<ul>
<li><strong>Success</strong>: 모든 파이프라인 단계가 성공적으로 완료되면 최종적으로 <strong>Success</strong> 상태가 되며, 개발자는 성공 알림을 받습니다.</li>
<li><strong>Continuous Feedback</strong>: Jenkins의 핵심은 <strong>결과를 개발자에게 즉시 알리는 것</strong>입니다. 성공하든 실패하든 개발자는 자신의 코드가 시스템에 어떻게 반영되었는지 실시간으로 파악할 수 있습니다.</li>
</ul>
<hr>
<h3 id="💡-요약">💡 요약</h3>
<p>Jenkins를 사용하면 <strong>코드 커밋 → 빌드 → 테스트 → 이미지화 → 배포</strong>로 이어지는 복잡한 과정을 자동화할 수 있습니다. 이를 통해 개발자는 비즈니스 로직 구현에만 집중할 수 있고, 배포 주기(Release Cycle)를 획기적으로 단축할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Airflow] Airflow 기초 개념 및 구성요소]]></title>
            <link>https://velog.io/@hwi_min/Airflow-Airflow-%EA%B8%B0%EC%B4%88-%EA%B0%9C%EB%85%90%EB%B0%8F-%EA%B5%AC%EC%84%B1%EC%9A%94%EC%86%8C</link>
            <guid>https://velog.io/@hwi_min/Airflow-Airflow-%EA%B8%B0%EC%B4%88-%EA%B0%9C%EB%85%90%EB%B0%8F-%EA%B5%AC%EC%84%B1%EC%9A%94%EC%86%8C</guid>
            <pubDate>Fri, 01 Aug 2025 07:00:18 GMT</pubDate>
            <description><![CDATA[<h2 id="airflow란">Airflow란</h2>
<blockquote>
<ul>
<li>Apache Airflow is a platform created by the community to programmatically author, schedule and monitor workflows. - Apache Airflow official</li>
</ul>
</blockquote>
<ul>
<li>복잡한 작업 흐름을 코드로 작성하고 스케줄링하고 모니터링할 수 있게 해주는 플랫폼</li>
<li>ETL/데이터 파이프라인 작업을 자동화하고 시각화할 수 있는 워크플로우 오케스트레이션 도구</li>
<li>ELT(Data Extract, Load, Transform), 머신러닝 등의 업무 자동화에 있어 유용한 도구</li>
</ul>
<h2 id="airflow-개요">Airflow 개요</h2>
<ul>
<li>Python 기반으로 워크플로우 설정 가능<ul>
<li>Airflow를 통해 딱 하나의 .py 파일로 워크플로우 작성 가능</li>
</ul>
</li>
<li><strong>DAG(Directed Acyclic Graph: 방향성 비순환 그래프)</strong> 구조로 작업 흐름을 정의<ul>
<li>이미지와 같이 방향성이 있으면서 순환하지 않는 구조를 DAG구조라고 한다.
<img src="https://velog.velcdn.com/images/hwi_min/post/8420dc76-1dca-467b-872e-03aceaed8f81/image.png" alt="DAG_image"> <em>출처: 위키피디아</em></li>
</ul>
</li>
<li>워크플로우를 설정해두면 Web UI를 통한 모니터링 및 실행이 가능</li>
</ul>
<h2 id="airflow-개념">Airflow 개념</h2>
<ul>
<li>다양한 컴포넌트와 용어로 구성되어있음
<img src="https://velog.velcdn.com/images/hwi_min/post/e2c16d6c-1c50-42c5-8113-945f1ffecc46/image.png" alt="Core_component"></li>
</ul>
<p><img src="https://velog.velcdn.com/images/hwi_min/post/7a80b8c5-e123-4e49-88c0-38823476283b/image.png" alt="airflow_concept_map"></p>
<p><strong>1. Metadata DB</strong></p>
<ul>
<li><p>작업 및 파이프라인의 메타데이터 저장소</p>
</li>
<li><p>task status(ex. queued, scheduled, running, success, failed, etc)가 저장됨</p>
</li>
<li><p>Airflow 첫 다운로드시 SQLite가 설치되는데, 이를 사용하기 위해서는 mySQL or Postgres를 연결해야함</p>
</li>
</ul>
<p><strong>2. Web Server</strong></p>
<ul>
<li>Web UI 제공 + 실행 중인 작업을 한 눈에 볼 수 있는 View 기능 제공
<img src="https://velog.velcdn.com/images/hwi_min/post/a379778f-b05d-474f-856a-9fc3b1ac56e2/image.png" alt="web_sever_view"> - 기본 뷰 구성
<img src="https://velog.velcdn.com/images/hwi_min/post/124e4ede-37f2-42a4-9af8-66fb74a17e3f/image.png" alt="web_tree_view"> - 트리 뷰
<img src="https://velog.velcdn.com/images/hwi_min/post/41f2966b-4824-4e3b-b7cc-e2f093935f89/image.png" alt="web_graph_view"> - 그래프 뷰</li>
</ul>
<p><strong>3. Scheduler</strong></p>
<ul>
<li><p>Airflow 구성요소의 핵심</p>
</li>
<li><p>일 다했어? 그럼 이거 시작해! 하는 역할 (DAG 파일을 모니터링 하고, 실행 시점이 되면 실행 큐(Executor)로 보내는 역할</p>
<ul>
<li><strong>Executor</strong>: Scheduler와 함께 동작하는 구성요소. status가 queued인 task를 확인하며 실제 어떤 리소스가 투입되어 실행될 것인지 결정. 흔히 Local Executor, Celery Executor, Kubernetes Executor등이 있음</li>
</ul>
</li>
<li><p>모든 작업과 DAG를 모니터링하다가 Metadatabase 내 모든 작업의 status를 모니터링</p>
</li>
<li><p><strong>특정 작업의 dependency가 만족되면 이를 실행시킬 뿐만 아니라, 모든 작업의 실행 순서 또한 결정</strong></p>
<ul>
<li>즉,  쉽게 풀어쓰면? Airflow는 작업간의 의존 관계를 <strong>DAG</strong>로 정의한다. </li>
<li>즉, Task A가 끝나야 Task B를 실행할 수 있다는 관계 설정 가능. <code>task_a &gt;&gt; tast_b</code>로 정의한다면 Airflow는 이 의존성(dependency)이 충족되었는지 확인하고 다음을 실행 </li>
</ul>
</li>
</ul>
<p><strong>4. Worker</strong></p>
<ul>
<li><p>Scheduler와 짝을 이루는 실제 task를 수행하는 구성요소</p>
</li>
<li><p>Scheduler가 task를 실행하라고 지시하면 Worker가 실제로 파이썬 코드, Spark Job, SQL등을 실행
<code>[Scheduler] -&gt; (실행 명령) -&gt; [Worker] -&gt; (작업 처리: Python, SQL, Spark 등)</code></p>
</li>
<li><p>필요에 따라 scale-out 되어 병렬 작업이나 동시에 여러 task를 진행할 수 있음</p>
</li>
<li><p>Executor 및 airflow.cfg에 의해 작업 환경 구성이 완성됨</p>
</li>
</ul>
<p><strong>5. Airflow.cfg</strong></p>
<ul>
<li>전반적인 Airflow configuration을 담당하는 파일</li>
<li>대표적으로는 DAG 파일을 어느 위치에 놓을 건지, log는 어디 저장할 건지, 어떤 Executor를 쓸지, DB 연결을 어떻게 할지, 로그는 어디에 저장할지 등의 설정을 할 수 있음</li>
</ul>
<p><strong>6. DAGs</strong></p>
<ul>
<li>&#39;이런 일을 해줘&#39;하고 컴퓨터에 내리는 명령 모음 python 파일<pre><code class="language-python"># datetime 모듈에서 timedelta 클래스를 import하여 시간 간격을 설정하는 데 사용
from datetime import timedelta
</code></pre>
</li>
</ul>
<h1 id="airflow의-dag-클래스를-import-dag-directed-acyclic-graph">Airflow의 DAG 클래스를 import (DAG: Directed Acyclic Graph)</h1>
<p>from airflow import DAG</p>
<h1 id="bashoperator-bash-명령어를-실행하는-airflow-operator">BashOperator: Bash 명령어를 실행하는 Airflow Operator</h1>
<p>from airflow.operators.bash import BashOperator</p>
<h1 id="days_ago-함수는-dag의-시작일을-현재-날짜로부터-며칠-전으로-설정할-때-사용">days_ago 함수는 DAG의 시작일을 현재 날짜로부터 며칠 전으로 설정할 때 사용</h1>
<p>from airflow.utils.dates import days_ago</p>
<h1 id="기본-인자-설정-dag-내-모든-task에-공통-적용될-기본값">기본 인자 설정: DAG 내 모든 Task에 공통 적용될 기본값</h1>
<p>default_args = {
    &#39;owner&#39;: &#39;airflow&#39;,                       # DAG 소유자 이름
    &#39;retries&#39;: 1,                             # Task 실패 시 재시도 횟수
    &#39;retry_delay&#39;: timedelta(minutes=5),     # 재시도 사이의 대기 시간
}</p>
<h1 id="dag-정의-tutorial이라는-id를-가진-dag-인스턴스-생성">DAG 정의: &#39;tutorial&#39;이라는 ID를 가진 DAG 인스턴스 생성</h1>
<p>with DAG(
    &#39;tutorial&#39;,                               # DAG의 고유 ID
    default_args=default_args,               # 위에서 정의한 기본 인자 적용
    description=&#39;A simple tutorial DAG&#39;,     # DAG 설명
    schedule_interval=timedelta(days=1),     # DAG 실행 주기: 하루마다 실행
    start_date=days_ago(2),                  # DAG의 시작일: 현재로부터 2일 전
    tags=[&#39;example&#39;],                        # DAG를 분류하기 위한 태그
) as dag:                                     # with 문을 통해 dag 변수에 DAG 인스턴스를 바인딩</p>
<pre><code># 첫 번째 Task 정의: 현재 날짜를 출력하는 Bash 명령 실행
t1 = BashOperator(
    task_id=&#39;print_date&#39;,                # Task의 고유 ID
    bash_command=&#39;date&#39;,                # 실행할 Bash 명령어
)

# 두 번째 Task 정의: 5초 동안 대기하는 Bash 명령 실행
t2 = BashOperator(
    task_id=&#39;sleep&#39;,                     # Task의 고유 ID
    depends_on_past=False,              # 이전 실행 결과에 의존하지 않음
    bash_command=&#39;sleep 5&#39;,             # 실행할 Bash 명령어 (5초 대기)
    retries=3,                          # Task 실패 시 최대 3회 재시도
)

# Task 간 의존성 정의: t1 → t2 순서로 실행됨
t1 &gt;&gt; t2</code></pre><h2 id="출처-kt-cloud-t-story">출처: KT cloud T-story</h2>
<pre><code>
+ Trigger: 비동기 Operator를 지원하는 프로세스

## Airflow 핵심 용어

**1. DAG  **
   - Directed Acyclic Graph의 약자  
   - 여러 Task의 흐름을 정의하는 워크플로우 단위  
   - 하나의 스케줄 단위로도 사용됨  

**2. Task  **
   - DAG 내에 포함된 각각의 작업 노드  
   - 예: 데이터 추출, 변환, 적재 등 하나하나의 작업 단위  

**3. Upstream / Downstream**  
   - 작업 순서 관계 표현  
   - A → B 구조에서 A는 B의 Upstream, B는 A의 Downstream  

**4. start_date  **
   - DAG 실행 시작 기준 날짜  
   - 과거 날짜일 경우 backfill(과거 데이터 자동 처리) 발생  
   - 원치 않으면 `catchup=False` 설정  

**5. end_date  **
   - DAG 실행 종료 기준 날짜  

**6. schedule_interval**  
   - DAG가 주기적으로 실행되는 간격 설정  
   - 예: `@daily`, `timedelta(days=1)`, `crontab(&#39;0 8 * * *&#39;)`  

**7. Trigger DAG  **
   - 스케줄과 상관없이 즉시 DAG 실행 명령  

**8. Clear ** 
   - 특정 Task와 그 downstream 작업들을 초기화(clear) 후 재실행  
   - 실패 작업 재실행 시 유용  

**9. Retry ** 
   - 실패한 Task를 설정한 횟수와 간격으로 자동 재시도  
   - 모두 실패하면 최종 fail 처리  


---
출처: [KT Cloud T-story](https://tech.ktcloud.com/entry/%EC%9C%A0%EC%9D%80%EC%9D%B4-Airflow-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC), [Benheo Github](https://benheo.github.io/2021/08/14/airflow_tutorial1.html)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[GitHub] 팀프로젝트 소스코드 내 GitHub Repository로 옮기기]]></title>
            <link>https://velog.io/@hwi_min/GitHub-%ED%8C%80%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%86%8C%EC%8A%A4%EC%BD%94%EB%93%9C-%EB%82%B4-GitHub-Repository%EB%A1%9C-%EC%98%AE%EA%B8%B0%EA%B8%B0</link>
            <guid>https://velog.io/@hwi_min/GitHub-%ED%8C%80%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%86%8C%EC%8A%A4%EC%BD%94%EB%93%9C-%EB%82%B4-GitHub-Repository%EB%A1%9C-%EC%98%AE%EA%B8%B0%EA%B8%B0</guid>
            <pubDate>Mon, 01 Apr 2024 05:35:50 GMT</pubDate>
            <description><![CDATA[<h2 id="1-가져오고-싶은-소스코드가-담긴-repository를-clone">1. 가져오고 싶은 소스코드가 담긴 Repository를 clone</h2>
<ul>
<li><p>소스코드를 일시적으로 저장할 pc의 로컬 폴더 생성</p>
</li>
<li><p>(Mac기준) Terminal창을 열고 가져오고 싶은 소스코드가 담긴 Repository의 소스코드를 clone</p>
<ul>
<li><p>팀 프로젝트의 깃허브 주소가 <a href="https://github.com/team_prj">https://github.com/team_prj</a>,</p>
</li>
<li><p>소스코드를 일시적으로 저장할 pc의 포컬 폴더의 이름을 localFile이라고 했을 때</p>
<p>git clone --mirror &lt;소스코드가 담긴 repository주소&gt; &lt;생성한 pc 로컬 폴더&gt;
  git clone -- mirror <a href="https://github.com/team_prj">https://github.com/team_prj</a> localFile</p>
</li>
</ul>
</li>
</ul>
<h2 id="2-내-github에-새로운-repository-생성">2. 내 GitHub에 새로운 Repository 생성</h2>
<ul>
<li><p>소스코드를 옮겨 담고 싶은 새로운 Repository를 생성</p>
<ul>
<li>push시 충돌을 피하기 위해 완전히 비어있는 repository로 생성하는 것을 추천한다. (Readme 등의 파일도 없게)</li>
<li>data_team_prj라는 이름으로 생성할 경우 https//github.com/깃허브ID/data_team_prj 로 주소가 생성이 됨</li>
</ul>
</li>
<li><p>(Mac기준) Terminal창을 열고 소스코드를 일시 저장해 둔 로컬 폴더(localFile)로 이동한다.</p>
<pre><code># 로컬 파일의 위치에 따라 해당 멍령어는 달라질 수 있으나, 
# cd 혹은 ..cd를 사용해 해당 폴더로 이동하면 됨

cd localFile</code></pre><p>  git remote set-url origin &lt;내 깃허브 주소&gt;.git
  git remote set-url origin <a href="https://github.com/%EA%B9%83%ED%97%88%EB%B8%8CID/data_team_prj.git">https://github.com/깃허브ID/data_team_prj.git</a>
  git push</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[SELECT / 조건에 맞는 회원수 구하기]]></title>
            <link>https://velog.io/@hwi_min/SELECT-%EC%A1%B0%EA%B1%B4%EC%97%90-%EB%A7%9E%EB%8A%94-%ED%9A%8C%EC%9B%90%EC%88%98-%EA%B5%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@hwi_min/SELECT-%EC%A1%B0%EA%B1%B4%EC%97%90-%EB%A7%9E%EB%8A%94-%ED%9A%8C%EC%9B%90%EC%88%98-%EA%B5%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 04 Mar 2024 15:22:55 GMT</pubDate>
            <description><![CDATA[<p>프로그래머스 SQL 고득점 Kit SELECT문 연습 문제 입니다.</p>
<h2 id="📌-문제-설명">📌 문제 설명</h2>
<blockquote>
<p>상세 설명: <a href="https://school.programmers.co.kr/learn/courses/30/lessons/131535">https://school.programmers.co.kr/learn/courses/30/lessons/131535</a></p>
</blockquote>
<ul>
<li>USER_INFO는 의류 쇼핑물에 가입한 회원정보를 담은 테이블이다.</li>
<li>2021년에 가입한 회원 중 나이가 20세이상 29세 이하인 회원이 몇 명인지 출력해야 한다.
<img src="https://velog.velcdn.com/images/hwi_min/post/c12491d5-04b7-4316-9dc9-1ce18e2a30a6/image.png" alt=""><br>

</li>
</ul>
<h2 id="📌-문제-풀이">📌 문제 풀이</h2>
<pre><code class="language-sql">SELECT count(*) as USERS
FROM USER_INFO
WHERE (age BETWEEN 20 AND 29) AND date_format(JOINED, &quot;%Y-%m-%d&quot;) like &#39;2021%&#39;</code></pre>
<ol>
<li><p><strong>FROM</strong>
USER_INFO에서 정보를 가져온다.</p>
</li>
<li><p><strong>WHERE</strong>
나이가 20세에서 29세 사이인 데이터만 가져오기 때문에 BETWEEN 20 AND 29로 작성한다. 그리고 동시에 가입일이 2021년이여야하기 때문에 date_format으로 패턴을 맞춰주고 그것이 &#39;2021&#39;으로 시작하면 데이터를 가져오기로 한다.</p>
</li>
<li><p><strong>SELECT</strong>
이때 추출된 데이터들의 개수를 세기 위해 count(*)를 사용하고 이때 *는 모든 로우를 말한다. 이때 그대로 가져오면 컬럼이 &#39;count(*)&#39;로 표시되므로 USERS로 표시될 수 있도록 as USERS를 추가해준다.</p>
</li>
</ol>
<ul>
<li>출력 결과
<img src="https://velog.velcdn.com/images/hwi_min/post/6f2a3ad9-9874-4bd2-a2c2-8d57b9822aa8/image.png" alt=""></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[SELECT / 서울에 위치한 식당 목록 출력하기]]></title>
            <link>https://velog.io/@hwi_min/SELECT-%EC%84%9C%EC%9A%B8%EC%97%90-%EC%9C%84%EC%B9%98%ED%95%9C-%EC%8B%9D%EB%8B%B9-%EB%AA%A9%EB%A1%9D-%EC%B6%9C%EB%A0%A5%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@hwi_min/SELECT-%EC%84%9C%EC%9A%B8%EC%97%90-%EC%9C%84%EC%B9%98%ED%95%9C-%EC%8B%9D%EB%8B%B9-%EB%AA%A9%EB%A1%9D-%EC%B6%9C%EB%A0%A5%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 04 Mar 2024 14:40:36 GMT</pubDate>
            <description><![CDATA[<p>프로그래머스 SQL 고득점 Kit SELECT문 연습 문제입니다</p>
<h2 id="📌-문제-설명">📌 문제 설명</h2>
<blockquote>
<p>상세 설명: <a href="https://school.programmers.co.kr/learn/courses/30/lessons/131118">https://school.programmers.co.kr/learn/courses/30/lessons/131118</a></p>
</blockquote>
<ul>
<li>REST_INFO는 식당의 정보를 담은 테이블, REST_REVIEW는 식당의 리뷰 정보를 담은 테이블이다.</li>
<li>REST_INFO와 REST_REVIEW 테이블에서 서울에 위치한 식당들의 식당 ID, 식당 이름, 음식 종류, 즐겨찾기수, 주소, 리뷰 평균 점수를 조회하는 쿼리를 작성해야한다.</li>
<li>리뷰 평균 점수는 소수점 세번째 자리에서 반올림해야한다.</li>
<li>결과는 평균점수를 기준으로 내림차순 정렬, 2순위는 즐겨찾기수를 기준으로 정렬해야 한다.
<img src="https://velog.velcdn.com/images/hwi_min/post/40a20db3-42bf-4373-82b0-f64d00c2e8aa/image.png" alt=""><br>

</li>
</ul>
<h2 id="📌-문제-풀이">📌 문제 풀이</h2>
<pre><code class="language-sql">SELECT i.REST_ID, REST_NAME, FOOD_TYPE, FAVORITES, ADDRESS, ROUND(AVG(REVIEW_SCORE),2) as SCORE
FROM REST_INFO as i, REST_REVIEW as r
WHERE i.REST_ID = r.REST_ID AND ADDRESS like &quot;서울%&quot;
GROUP BY REST_ID
ORDER BY SCORE desc, FAVORITES desc</code></pre>
<ol>
<li><p><strong>FROM</strong>
REST_INFO를 i로, REST_REVIEW를 r로 지칭하기로 하고, 이 두 테이블에서 데이터를 가져온다.</p>
</li>
<li><p><strong>WHERE</strong>
REST_INFO의 REST_ID가 REST_REVEIW의 REST_ID가 같고, 주소가 서울로 시작하는 데이터만 추출해 온다.</p>
</li>
<li><p><strong>GROUP BY</strong>
그 데이터들을 REST_ID를 기준으로 그룹을 만든다.</p>
</li>
<li><p><strong>SELECT</strong>
레스토랑의 ID, 이름, 음식 종류, 즐겨찾기 수, 주소를 가져오고, 리뷰 점수의 평균을 내주기 위해 avg 사용후 round와 ,2를 사용해 소수점 둘째자리까지만 표현할 수 있도록 조정해서 SCORE이라고 표시한다.</p>
</li>
<li><p><strong>ORDER BY</strong>
1순위인 SCORE의 내림차순을 위해 SCORE desc, 2순위인 FAVORITES 기준 내림차순을 위해 FAVORITES desc로 작성한다. </p>
</li>
</ol>
<ul>
<li>출력 결과
<img src="https://velog.velcdn.com/images/hwi_min/post/10c6e973-26c0-47ac-885d-55e70fd9b104/image.png" alt=""></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[SELECT / 오프라인/온라인 판매 데이터 통합하기]]></title>
            <link>https://velog.io/@hwi_min/SELECT-%EC%98%A4%ED%94%84%EB%9D%BC%EC%9D%B8%EC%98%A8%EB%9D%BC%EC%9D%B8-%ED%8C%90%EB%A7%A4-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%86%B5%ED%95%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@hwi_min/SELECT-%EC%98%A4%ED%94%84%EB%9D%BC%EC%9D%B8%EC%98%A8%EB%9D%BC%EC%9D%B8-%ED%8C%90%EB%A7%A4-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%86%B5%ED%95%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 03 Mar 2024 03:51:32 GMT</pubDate>
            <description><![CDATA[<p>프로그래머스 SQL 고득점 Kit SELECT문 연습 문제입니다.</p>
<h2 id="📌-문제-설명">📌 문제 설명</h2>
<blockquote>
<p>상세 설명: <a href="https://school.programmers.co.kr/learn/courses/30/lessons/131537">https://school.programmers.co.kr/learn/courses/30/lessons/131537</a></p>
</blockquote>
<ul>
<li>ONLINE_SALE은 의류 쇼핑몰의 온라인 상품 판매 정보, OFFLINE_SALE은 의류 쇼핑몰의 오프라인 상품 판매 정보를 담은 테이블이다.</li>
<li>두 테이블은 모두 동일한 날짜, (회원ID), 상품ID 조합에 대해서는 하나의 판매 데이터만 존재한다.</li>
<li>두 테이블에서 2022년 3월의 오프라인/온라인 상품 판매 데이터의 판매 날짜, 상품ID, 유저ID, 판매량을 출력하는 쿼리를 작성해야 한다. </li>
<li>oFFLINE_SALE 테이블의 판매 데이터의 USER_ID값은 NULL로 표시해야하며, 결과는 판매일 기준 오름차순, 2순위 판매일, 3순위 상품ID, 4순위 유저ID 오름차순으로 정렬해야 한다.
<img src="https://velog.velcdn.com/images/hwi_min/post/8f26d5ae-2089-485e-adf0-522baed13e55/image.png" alt=""></li>
</ul>
<h2 id="📌-문제-풀이">📌 문제 풀이</h2>
<pre><code class="language-sql"># online_sale 테이블
SELECT date_format(SALES_DATE, &quot;%Y-%m-%d&quot;) as SALES_DATE, PRODUCT_ID, USER_ID, SALES_AMOUNT
FROM ONLINE_SALE
WHERE date_format(SALES_DATE, &quot;%Y-%m-%d&quot;) like &#39;2022-03%&#39;
UNION
# offline_sale 테이블
SELECT date_format(SALES_DATE, &quot;%Y-%m-%d&quot;) as SALES_DATE, PRODUCT_ID, null as USER_ID, SALES_AMOUNT
FROM OFFLINE_SALE
WHERE date_format(SALES_DATE, &quot;%Y-%m-%d&quot;) like &#39;2022-03%&#39;

ORDER BY SALES_DATE, PRODUCT_ID, USER_ID;</code></pre>
<ol>
<li>online_sale 테이블</li>
</ol>
<ul>
<li>online_sale테이블에서(FROM) sales_date가 2022-03으로 시작하는 데이터만 추출하여(WHERE) 그것의 sales_date, product_id, user_id, sales_amount를 추출(SELECT)한다.</li>
</ul>
<ol start="2">
<li>offline_sale 테이블</li>
</ol>
<ul>
<li>offline_sale테이블에서(FROM) sales_date가 2022-03으로 시작하는 데이터만 추출하여(WHERE) 그것의 sales_date, product_id, sales_amount를 불러오고, 이 테이블에 없는 user_id의 값들은 null값으로 채워 그것을 user_id로 사용(SELECT)한다.</li>
</ul>
<ol start="3">
<li><p>UNION
오프라인과 온라인 상품 판매 내역에서 원하는 데이터만 각자 가져왔으니, 이 데이터들을 합치기 위해 UNION을 사용한다. </p>
</li>
<li><p>ORDER BY
1순위 판매일 2순위 상품 아이디, 3순위 사용자 아이디이고 모두 오름차순이기 떄문에 디폴트값인 ASC는 위와같이 생략해도 되고, 명시해도 된다. 
<img src="https://velog.velcdn.com/images/hwi_min/post/49031d6a-7bab-44cf-9f6c-80bf60db287f/image.png" alt=""></p>
</li>
</ol>
<br>

<h3 id="💡-union--union-all">💡 UNION &amp; UNION ALL</h3>
<p><strong>UNION</strong></p>
<ul>
<li>여러 쿼리문을 합쳐서 하나의 쿼리로 만들어준다.</li>
<li>중복된 값이 있으면, 중복된 값을 제거한 뒤 보여준다.</li>
<li>중복된 값을 제거하는 연산이 필요하기 때문에 UNION ALL과 비교했을 때 비교적 속도가 느리다.</li>
</ul>
<p><strong>UNION ALL</strong></p>
<ul>
<li>UNION과 마찬가지로 여러 쿼리문을 합쳐서 하나의 쿼리로 만들어준다.</li>
<li>중복된 값을 제거하는 연산 없이 모두 보여준다.</li>
<li>중복된 값을 제거하는 연산이 없어 UNION보다 비교적 빠르다.</li>
</ul>
<p><strong>유의사항</strong></p>
<ul>
<li>컬럼명 동일</li>
<li>컬럼별 데이터 타입 동일</li>
<li>출력한 컬럼의 개수 동일</li>
</ul>
<p><strong>사용 예시 비교</strong>
다음과 같은 테이블 name_woman이 있다고 가정하자.</p>
<ul>
<li><table>
<thead>
<tr>
<th align="center">ID</th>
<th align="left">NAME</th>
</tr>
</thead>
<tbody><tr>
<td align="center">1</td>
<td align="left">김가영</td>
</tr>
<tr>
<td align="center">2</td>
<td align="left">김다인</td>
</tr>
<tr>
<td align="center">3</td>
<td align="left">김지민</td>
</tr>
<tr>
<td align="center">4</td>
<td align="left">박은정</td>
</tr>
</tbody></table>
</li>
</ul>
<p>name_man이 있다고 가정하자.</p>
<ul>
<li><table>
<thead>
<tr>
<th align="center">ID</th>
<th align="left">NAME</th>
</tr>
</thead>
<tbody><tr>
<td align="center">1</td>
<td align="left">김지민</td>
</tr>
<tr>
<td align="center">2</td>
<td align="left">김민석</td>
</tr>
<tr>
<td align="center">3</td>
<td align="left">박태정</td>
</tr>
<tr>
<td align="center">4</td>
<td align="left">최현석</td>
</tr>
</tbody></table>
</li>
</ul>
<p><strong>남성과 여성의 이름을 중복값은 빼고 추출한다면</strong></p>
<pre><code class="language-sql">SELECT name FROM name_woman
UNION
SELECT name FROM name_man</code></pre>
<p><img src="https://velog.velcdn.com/images/hwi_min/post/84bbb442-3192-46c2-bc8c-467c14df0359/image.png" alt=""></p>
<ul>
<li>중복값인 &#39;김지민&#39;은 한 번만 출력됨<Br>

</li>
</ul>
<p><strong>남성과 여성의 이름은 중복값 포함하여 모두 추출한다면</strong></p>
<pre><code class="language-sql">SELECT name FROM name_woman
UNION ALL 
SELECT name FROM name_man</code></pre>
<p><img src="https://velog.velcdn.com/images/hwi_min/post/933c3cc9-570a-46b1-8805-fbee51f4807e/image.png" alt=""></p>
<ul>
<li>중복값인 &#39;김지민&#39;이 두 번 다 출력됨</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[SELECT / 조건에 부합하는 중고거래 댓글 조회하기]]></title>
            <link>https://velog.io/@hwi_min/SELECT-%EC%A1%B0%EA%B1%B4%EC%97%90-%EB%B6%80%ED%95%A9%ED%95%98%EB%8A%94-%EC%A4%91%EA%B3%A0%EA%B1%B0%EB%9E%98-%EB%8C%93%EA%B8%80-%EC%A1%B0%ED%9A%8C%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@hwi_min/SELECT-%EC%A1%B0%EA%B1%B4%EC%97%90-%EB%B6%80%ED%95%A9%ED%95%98%EB%8A%94-%EC%A4%91%EA%B3%A0%EA%B1%B0%EB%9E%98-%EB%8C%93%EA%B8%80-%EC%A1%B0%ED%9A%8C%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 02 Mar 2024 11:12:05 GMT</pubDate>
            <description><![CDATA[<p>프로그래머스 SQL 고득점 Kit SELECT문 연습 문제입니다.</p>
<h2 id="📌-문제-설명">📌 문제 설명</h2>
<blockquote>
<p>상세 설명: <a href="https://school.programmers.co.kr/learn/courses/30/lessons/164673">https://school.programmers.co.kr/learn/courses/30/lessons/164673</a></p>
</blockquote>
<ul>
<li>USED_GOODS_BOARD는 중고거래 게시판의 정보, USED_GOODS_REPLY는 게시판내 첨부파일의 정보를 담은 테이블이다.</li>
<li>2022년 10월에 작성된 게시글의 제목, 게시글 ID, 댓글 ID, 댓글 작성자 ID, 댓글 내용, 댓글 작성일을 조회하는 SQL문을 조회하는 쿼리를 작성해야 한다.</li>
<li>이때, 결과는 댓글 작성일 기준으로 오름차순으로 정렬하고 만약 댓글 작성일이 같다면 게시글 제목을 기준으로 오름차순 한다.<br>


</li>
</ul>
<h2 id="📌-문제-풀이">📌 문제 풀이</h2>
<pre><code class="language-sql">SELECT b.title as title, b.board_id as board_id, reply_id, r.writer_id as writer_id, r.contents as contents, date_format(r.created_date, &quot;%Y-%m-%d&quot;) as created_date
FROM used_goods_board as b, used_goods_reply as r
WHERE b.board_id = r.board_id AND b.created_date BETWEEN &#39;2022-10-01&#39; AND &#39;2022-10-31&#39;
ORDER BY r.created_date, b.title;</code></pre>
<ol>
<li><p><strong>FROM</strong>
USED_GOODS_BOARD의 이름이 너무 길기때문에 alias를 사용해 b로 지칭한다. USED_GOODS_REPLY역시 r로 지칭한다.</p>
</li>
<li><p><strong>WHERE</strong></p>
</li>
</ol>
<p><strong>USED_GOODS_BOARD의 게시글 아이디와, USED_GOODS_REPLY의 게시글 아이디가 같은 경우</strong>에 데이터를 가져온다. 그리고 동시에 게시글이 생성된 날짜가 2022-10-01부터 2022-10-31일 사이인 게시글을 추출해야하기 때문에 <strong>BETWEEN</strong>을 활용하여 그 기간 내에 생성된 데이터만 추출하는 조건을 작성한다.</p>
<ol start="3">
<li><p><strong>SELECT</strong>
조건에 명시된대로 컬럼을 가져올건데, 두 테이블에 중복되는 컬럼명이 있을시에는 <strong>어떤 테이블의 컬럼인지 정확히 명시</strong>해 주어야하기때문에 r.created_date 같이 <strong>alias.column name</strong>의 형식으로 작성한다. 이때 결과 컬럼 역시 내가 불러온대로 r.created_date로 나오기 때문에 원하는 결과 &#39;created_date&#39;로만 나오게 하기 위해서는 <strong>r.created_date as created_date</strong>로 작성해주어야 한다. 이렇게 작성한 뒤 결과를 출력했더니 결과 값이 &#39;2022-10-02&#39;가 아닌 &#39;2022-10-02 00:00:00&#39;으로 출력되어 오답이 떴다. 그래서 date_format을 사용하여 &#39;2022-10-02&#39;만 출력값이 될 수 있도록 수정했다.</p>
</li>
<li><p><strong>ORDER BY</strong>
가져온 데이터들을 <strong>답글이 생성된 날짜를 기준으로 오름차순 정렬</strong>, 그리고 2순위로 <strong>게시글의 타이틀 기준으로 오름차순 정렬</strong>이기 때문에 r.created_date, b.title; 로 작성하여 준다. 이때, 오름차순은 디폴트 값이므로 생략해도 되고, ASC를 뒤에 명시해 주어도 된다. </p>
<br></li>
</ol>
<ul>
<li>출력 결과
<img src="https://velog.velcdn.com/images/hwi_min/post/2d3cc67c-64c1-453a-90e2-64014f12fae4/image.png" alt=""></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[다리 놓기]]></title>
            <link>https://velog.io/@hwi_min/%EB%8B%A4%EB%A6%AC-%EB%86%93%EA%B8%B0</link>
            <guid>https://velog.io/@hwi_min/%EB%8B%A4%EB%A6%AC-%EB%86%93%EA%B8%B0</guid>
            <pubDate>Tue, 06 Feb 2024 03:56:40 GMT</pubDate>
            <description><![CDATA[<p>백준 1010번 문제입니다.</p>
<h3 id="📌-문제-설명">📌 문제 설명</h3>
<blockquote>
<p>상세 설명: <a href="https://www.acmicpc.net/problem/1010">https://www.acmicpc.net/problem/1010</a></p>
</blockquote>
<ul>
<li>강 주변에 다리를 지으려고 한다. 서쪽에는 N개의 사이트, 동쪽에서 M개의 사이트가 있다.</li>
<li>서쪽의 사이트와 동쪽의 사이트를 다리로 연결한다.</li>
<li>서쪽의 사이트 개수만큼(N개) 다리를 짓는다. 단, 다리끼리는 서로 겹쳐질 수 없다.</li>
<li>이때 다리를 지을 수 있는 경우의 수를 구하는 프로그램을 작성해야 한다.
<img src="https://velog.velcdn.com/images/hwi_min/post/465a31f4-795e-4666-b47b-bde959023ae0/image.png" alt=""></li>
<li>입력 첫 줄에는 테스트 케이스의 개수 T</li>
<li>그 다음 줄부터는 각각의 테스트케이스에 대해 강의 서쪽과 동쪽에 있는 사이트 개수가 주어진다. (N, M / 0 &lt; N &lt;= M &lt; 30)</li>
<li>출력은 각 테스트 케이스에 대한 조건하에 다리를 지을 수 있는 경우의 수를 출력한다.</li>
</ul>
<h3 id="📌-문제-풀이">📌 문제 풀이</h3>
<p>두 가지로 문제를 풀 수 있다. 일단, 문제를 읽자마자 조합의 경우를 생각해 볼 수 있었다. 
$$
_nC_r = \dfrac{n!}{(n-r)!r!}
$$</p>
<pre><code class="language-python"># factorial 정의
def factorial(N):
    if N &gt; 1:
        return (N * factorial(N-1))
    else:
        return 1

# T 입력 받기
T = int(input())
for _ in range(T):
    N, M = map(int, input().split())
    print(factorial(M) // (factorial(M-N) * factorial(N)))</code></pre>
<ul>
<li>위 코드는 factorial을 정의한 후, 이것을 활용하여 풀이 한 것이다.</li>
<li>마지막 줄의 print(factorial(M) // (factorial(M-N) * factorial(N)))은 조합 공식을 활용한 것이다.<br>

</li>
</ul>
<p>다른 방법으로도 해결할 수 있는데, 이 방법은 하단의 그림을 바탕으로 작성된 코드이다.</p>
<pre><code class="language-python">T = int(input())
dp = [[0] * 30 for _ in range(30)] #N, M / 0 &lt; N &lt;= M &lt; 30 조건에 의거
for i in range(30):
    for j in range(30):
        if i == 1:
            dp[i][j] = j #위 그림에서 볼 수 있듯, N==1이면 M의 개수가 경우의 수를 결정
        else:
            if i == j:
                dp[i][j] = 1 #N=M이면 경우의 수는 하나뿐
            elif i &lt; j:
                dp[i][j] = dp[i-1][j-1] + dp[i][j-1]

for _ in range(T):
    N, M = map(int, input().split())
    print (dp[N][M])</code></pre>
<p><img src="https://velog.velcdn.com/images/hwi_min/post/7f143e4c-fec4-4cde-ace2-d2b03dcdb9ca/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SELECT / 재구매가 일어난 상품과 회원 리스트 구하기]]></title>
            <link>https://velog.io/@hwi_min/SELECT-%EC%9E%AC%EA%B5%AC%EB%A7%A4%EA%B0%80-%EC%9D%BC%EC%96%B4%EB%82%9C-%EC%83%81%ED%92%88%EA%B3%BC-%ED%9A%8C%EC%9B%90-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EA%B5%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@hwi_min/SELECT-%EC%9E%AC%EA%B5%AC%EB%A7%A4%EA%B0%80-%EC%9D%BC%EC%96%B4%EB%82%9C-%EC%83%81%ED%92%88%EA%B3%BC-%ED%9A%8C%EC%9B%90-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EA%B5%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 05 Feb 2024 12:04:48 GMT</pubDate>
            <description><![CDATA[<p>프로그래머스 SQL 고득점 Kit SELECT문 연습 문제 입니다.</p>
<h2 id="📌-문제-설명">📌 문제 설명</h2>
<blockquote>
<p>상세 설명: <a href="https://school.programmers.co.kr/learn/courses/30/lessons/131536">https://school.programmers.co.kr/learn/courses/30/lessons/131536</a></p>
</blockquote>
<ul>
<li>테이블 ONLINE_SALE은 의류 쇼핑몰의 온라인 상품 판매 정보를 담은 테이블이다.</li>
<li>ONLINE_SALE_ID(온라인 상품 판매 ID), USER_ID(회원 ID), PRODUCT_ID(상품 ID), SALES_AMOUNT(판매량), SALES_DATE(판매일)의 칼럼으로 구성되어 있다.</li>
<li>동일한 날짜, 회원 ID, 상품 ID조합에 대해서는 하나의 판매 데이터만 존재한다.</li>
<li>회원이 동일한 상품을 재구매한 데이터를 구하여, 재구매한 회원 ID와 재구매한 상품 ID를 출력해야한다.</li>
<li>결과는 회원 ID를 기준으로 오름차순 정렬하여야하고, 회원 ID가 같으면 상품 ID를 기준으로 내림차순 정렬해야 한다.<br>
## 📌 문제 풀이
```sql
SELECT USER_ID, PRODUCT_ID
FROM ONLINE_SALE
GROUP BY USER_ID, PRODUCT_ID
HAVING count(*) >= 2
ORDER BY USER_ID, PRODUCT_ID DESC;
```</li>
</ul>
<ol>
<li><strong>FROM ONLINE_SALE</strong> : ONLINE_SALE 테이블에서 데이터를 추출한다.</li>
<li><strong>GROUP BY 절</strong>
회원 ID(USER_ID)와 상품 ID(PRODUCT_ID)를 기준으로 그룹을 묶는다. 그래야 재구매한 회원에 대한 데이터 추출이 가능하기 때문이다.</li>
<li><strong>HAVING 절</strong>
재구매한 회원을 찾기 위해서 그룹화되어 있는 데이터들 사이에서 having절을 이용해 중복된 열의 개수를 센다. 이때 count 집계함수를 사용한다. 이때 <strong>count로 행의 개수를 구했을 때 2개 이상인 경우에는 재구매</strong>를 했다고 판단할 수 있다.</li>
<li><strong>SELECT 절</strong>
우리는 최종적으로 회원 ID(USER_ID)와 상품 ID(PRODUCT_ID)를 추출하고 싶기 때문에, USER_ID와 PRODUCT_ID를 SELECT절에 작성해 준다.</li>
<li><strong>ORDER BY 절</strong>
ORDER BY를 통해 오름차순과 내림차순으로 정렬 할 수 있는데, 우선 순위를 생각해 보아야 한다. <strong>1순위는 USER_ID의 오름차순</strong>이다. default가 ASCending(오름차순)이므로 ASC는 생략해도 된다. <strong>2순위는 PRODUCT_ID의 내림차순</strong>이다. 내림차순은 생략해서는 안되므로 PRODUCT_ID DESC이라고 작성을 해준다. 따라서 <strong>&#39;USER_ID, PRODUCT_ID DESC&#39;</strong>으로 작성하면 된다. </li>
</ol>
<br>

<h3 id="💡-sql-구문-순서">💡 SQL 구문 순서</h3>
<ul>
<li>SQL구문의 대표적은 순서는 다음과 같다.</li>
</ul>
<ol>
<li>SELECT 컬럼명 ------ <strong>(5): 추출된 데이터들을 조회한다.</strong></li>
<li>FROM 테이블명 ------ <strong>(1): 테이블을 가장 먼저 확인한다.</strong></li>
<li>WHERE 테이블 조건 -- <strong>(2): 테이블에서 조건에 맞는 데이터를 추출한다.</strong></li>
<li>GROUP BY 컬럼명 --- <strong>(3): 공통적인 데이터들끼리 묶어 그룹을 만든다.</strong></li>
<li>HAVING 그룹 조건 --- <strong>(4): 묶인 그룹 중, 주어진 조건에 맞는 그룹을 추출한다.</strong></li>
<li>ORDER BY 컬럼명 --- <strong>(6): 추출된 데이터들을 정렬한다.</strong></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[SELECT / 조건에 맞는 도서 리스트 출력하기]]></title>
            <link>https://velog.io/@hwi_min/SELECT-%EC%A1%B0%EA%B1%B4%EC%97%90-%EB%A7%9E%EB%8A%94-%EB%8F%84%EC%84%9C-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EC%B6%9C%EB%A0%A5%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@hwi_min/SELECT-%EC%A1%B0%EA%B1%B4%EC%97%90-%EB%A7%9E%EB%8A%94-%EB%8F%84%EC%84%9C-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EC%B6%9C%EB%A0%A5%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 05 Feb 2024 11:09:55 GMT</pubDate>
            <description><![CDATA[<p>프로그래머스 SQL 고득점 Kit SELECT문 연습 문제 입니다.</p>
<h2 id="📌-문제-설명">📌 문제 설명</h2>
<blockquote>
<p>상세 설명: <a href="https://school.programmers.co.kr/learn/courses/30/lessons/144853">https://school.programmers.co.kr/learn/courses/30/lessons/144853</a></p>
</blockquote>
<ul>
<li>BOOK테이블은 한 서점에서 판매중인 도서들의 도서 정보를 담은 테이블이다.</li>
<li>BOOK테이블에서 CATEGORY가 &#39;인문&#39;이고 PUBLISHED_DATE가 2021년인 도서 리스트를 찾아야 한다.</li>
<li>해당하는 책의 BOOK_ID, PUBLISHED_DATE를 출력해야 한다.</li>
<li>결과는 출판일을 기준으로 오름차순 정렬한다.<br>

</li>
</ul>
<h2 id="📌-문제-풀이">📌 문제 풀이</h2>
<p>기본 SELECT &gt; FROM &gt; WHERE에 ORDER BY가 추가된 문제이다. 일반적으로 SQL은 대소문자를 구분하지 않기 때문에 모두 대문자로, 모두 소문자로 작성하여도 되나,집계함수 및 연산자(like) 등을 소문자로 처리하였다.</p>
<pre><code class="language-sql">SELECT BOOK_ID, date_format(PUBLISHED_DATE, &quot;%Y-%m-%d&quot;) AS PUBLISHED_DATE
FROM BOOK
WHERE PUBLISHED_DATE like &#39;2021%&#39; AND CATEGORY like &#39;인문&#39;
ORDER BY PUBLISHED_DATE ASC;</code></pre>
<ol>
<li><strong>FROM BOOK</strong>: 말 그대로 BOOK 테이블에서 데이터를 추출한다.</li>
<li><strong>WHERE 문</strong>
CATEGORY는 모든 요소들이 &#39;인문&#39;, &#39;경제&#39; ... 등으로 작성되어 있기 때문에 &#39;인문&#39;과 문자열이 일치하는 경우에 추출하겠다는 의미로 GATEGORY like &#39;인문&#39;이라고 작성하면 된다. 하지만 PUBLISHED_DATE는 &#39;2021-01-01&#39;과 같이 작성이 되어 있기 때문에, 2021이 가장 앞에 작성되어 있는 것을 2021년에 출판된 도서로 판단하고 이것을 추출해야한다. 이때 사용되는 것이 like 연산자와, %, _ 이다.</li>
</ol>
<ul>
<li>포스팅 뒷부분 💡 like 연산자 에서 추가적으로 설명한다.</li>
</ul>
<ol start="3">
<li><strong>ORDER BY 문</strong>
출판일을 기준으로 오름차순 정렬해야 하기 때문에 ORDER BY문에 PUBLISHED_DATE를 기준으로 오름차순 정리한다는 의미의 ASC를 작성해 준다.</li>
<li>SELECT 문
BOOK_ID, PUBLISHED_DATE를 출력해야하므로 BOOK_ID를 가져오고, PUBLISHED_DATE를 &#39;년-월-일&#39;의 형식으로 추출해야 하므로 date_format을 사용하여 PUBLISHED_DATE라는 이름으로 추출한다는 의미로 위와 같이 작성해야한다.</li>
</ol>
<ul>
<li>포스팅 뒤부분 💡 date_format 에서 추가적으로 설명한다. <br>

</li>
</ul>
<h3 id="💡-like-연산자">💡 like 연산자</h3>
<p>like 연산자는 문자열의 패턴을 검색하는 데 사용된다.
기본적인 공식은 다음과 같다.</p>
<pre><code class="language-sql">SELECT * 
FROM TABLE
WHERE COLUMN like &#39;PATTERN&#39;;</code></pre>
<p>이때, 패턴에서 활용되는 것이 <strong>%</strong>와 <strong>_</strong>이다. %는 모든 문자라는 의미이고, _는 한 글자라는 의미이다. 예시로 다음 패턴들을 활용해 보겠다.</p>
<pre><code class="language-sql">SELECT * 
FROM TABLE
WHERE COLUMN like &#39;%2021%&#39;;</code></pre>
<ul>
<li>다음 코드에서는 시작이 2021의 앞 뒤에 어떤 문자가 와도 상관이 없고, 단지 2021이라는 문자열이 전체 문자열중에 포함되어 있으면 된다. 따라서 0002021abb도 추출되고, year2021-도 추출되는 것이다.<pre><code class="language-sql">SELECT *
FROM TABLE
WHERE COLUMN like &#39;_2021_&#39;;</code></pre>
</li>
<li>다음 코드에서는 2021의 각 앞과 뒤에 한 글자가 존재한다는 의미이다. 따라서 120213, y20211도 추출되는 것이다.<pre><code class="language-sql">SELECT *
FROM TABLE
WHERE COLUMN like &#39;_2021%&#39;;</code></pre>
</li>
<li>당연히 두가지를 혼용하여 사용해도 된다. 다음 코드는 앞에 한글자, 뒤에는 상관 없으므로 y2021-01-20, 22021year22 와 같은 문자열이 추출될 것이다.<br>

</li>
</ul>
<p><strong>우리는 시작이 2021이기만 하면 되기 때문에 where문을 다음과 같이 코드를 작성할 수 있다. 그리고 category는 &#39;인문&#39;, &#39;과학&#39; 처럼 정확히 문자열과 일치해야 하므로 다음과 같이 패턴에 사용되는 %, _없이 표현할 수 있다. AND는 앞 뒤의 모든 조건을 만족해야 한다는 의미이다.</strong></p>
<pre><code class="language-sql">WHERE COLUMN like &#39;2021%&#39; AND CATEGORY like &#39;인문&#39;;</code></pre>
<br>

<h3 id="💡-date_format">💡 date_format</h3>
<p>date_format(날짜, 형식)은 날짜를 지정한 형식으로 출력하게 한다. date_format에 사용되는 구분 기호를 다음과 같다.</p>
<ul>
<li>%Y : 4자리 년도</li>
<li>%y : 2자리 년도</li>
<li>%M : 영문, 긴 월 (ex.January)</li>
<li>%b : 영문, 짧은 월 (ex.Jan)</li>
<li>%W : 영문, 긴 요일 이름 (ex.Monday)</li>
<li>%a : 영문, 짧은 요일 이름 (ex.Mon)</li>
<li>%i : 분</li>
<li>%T : hh:mm:SS</li>
<li>%m : 숫자 월 (두 자리)</li>
<li>%c : 숫자 월 (한 자리는 한 자리)</li>
<li>%d : 일자 (두 자리)</li>
<li>%e : 일자 (한 자리는 한자리)</li>
<li>%I : 시간 (12시간)</li>
<li>%H : 시간 (24시간)</li>
<li>%r : hh:mm:SS AM, PM</li>
<li>%S : 초</li>
</ul>
<p>우리는 원래 형태인 &#39;2021-01-01&#39;과 같인 4자리 년도-두자리 숫자 월-두자리 일로 표현하는 것이 목표이므로 &#39;%Y-%m-%d&#39;로 패턴을 지정해주면 된다. 따라서 다음과 같이 코드를 작성할 수 있다.</p>
<pre><code class="language-sql">date_format(PUBLISHED_DATE, &quot;%Y-%m-%d&quot;)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[소수 구하기]]></title>
            <link>https://velog.io/@hwi_min/%EC%86%8C%EC%88%98-%EA%B5%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@hwi_min/%EC%86%8C%EC%88%98-%EA%B5%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 05 Feb 2024 07:16:05 GMT</pubDate>
            <description><![CDATA[<p>백준 1929번 문제입니다.</p>
<h3 id="📌-문제-설명">📌 문제 설명</h3>
<blockquote>
<p>상셰 설명: <a href="https://www.acmicpc.net/problem/1929">https://www.acmicpc.net/problem/1929</a></p>
</blockquote>
<ul>
<li>M N의 형태로 주어지는 입력을 받아 M이상 N이하의 소수를 모두 출력한다.</li>
<li>출력은 한 줄에 하나씩, 증가하는 순서대로 소수를 출력한다.</li>
</ul>
<h3 id="📌-문제-풀이">📌 문제 풀이</h3>
<pre><code class="language-python">m,n = map(int, input().split())

for i in range(m,n+1):
    if i==1:
        continue
    for j in range(2,int(i**0.5)+1):
        if i%j==0: #약수가 존재함 -&gt; 소수가 아님
            break #더 이상 볼 필요 없음
    else:
        print(i)</code></pre>
<p>이 문제는 문제만 읽었을 때는 간단하다. 주어진 범위 내의 수를 모두 돌면서 1을 제외하고 나누어 떨어지는 수가 있는지 없는지 확인하면 된다. 하지만 시간초과로 정답을 맞출 수 없다. 그래서 사용하는 방법이 <strong>에라토스테네스의 체(소수 구하기)</strong>이다.</p>
<h4 id="💡-에라토스테네서의-체">💡 에라토스테네서의 체</h4>
<ul>
<li>임의의 자연수 n에 대해 그 이하의 소수를 모두 찾는 가장 간단하고 빠른 방법</li>
<li>만약, 1~ 20 사이의 소수를 찾는다고 가정해 보자. 
<img src="https://velog.velcdn.com/images/hwi_min/post/b5c8552e-7af1-4dad-bfdf-4dbb9bf9ed36/image.png" alt=""></li>
<li>가장 먼저 소수도 합성수도 아닌 유일한 자연수 1을 제거한다.
<img src="https://velog.velcdn.com/images/hwi_min/post/fb60f1e0-6db1-4ea3-aeb4-12ffe45b95c6/image.png" alt=""></li>
<li>2를 제외한 2의 배수를 제거한다. (2는 소수가 되지만, 2의 배수는 2로 나누어 떨어지기 때문에 소수가 될 수 없다.)
<img src="https://velog.velcdn.com/images/hwi_min/post/b37e7e08-5a2b-4ed6-aecf-e6f7d9e2b0f2/image.png" alt=""></li>
<li>3을 제외한 3의 배수를 제거한다. (3은 소수가 되지만, 3의 배수는 3으로 나누어 떨어지기 때문에 소수가 될 수 없다.)
<img src="https://velog.velcdn.com/images/hwi_min/post/075509a4-9d77-4722-95cf-7141fc6d104d/image.png" alt=""></li>
<li>4의 배수는 이미 지워졌기 때문에 (2의 배수에서) 2, 3 다음 남아있는 가장 작은 소수, 즉 5를 제외한 5의 배수를 제거한다.
<img src="https://velog.velcdn.com/images/hwi_min/post/11f9fa0c-b0b7-480c-ab2d-e68ee43e5245/image.png" alt=""></li>
<li>마지막으로 7을 제외한 7의 배수를 제거한다.
<img src="https://velog.velcdn.com/images/hwi_min/post/112b8252-284c-4177-9284-568982257d41/image.png" alt=""></li>
<li>8의 배수, 9의 배수, 10의 배수 등은 지울 필요 없다. (이미 2, 3 배수에서 지워졌기 때문이다.)</li>
<li>11 이상의 소수들의 배수부터는 지우지 않아도 된다. (100이하 자연수 중에서 11의 배수는 1<del>9사이의 값을 곱한 것인데 모두 1</del>9사이의 배수이다.)</li>
<li>위의 순서처럼 남은 것들의 2배수, 3배수, n배수를 지우다보면 소수만 남는다.
📂 <a href="https://namu.wiki/w/%EC%97%90%EB%9D%BC%ED%86%A0%EC%8A%A4%ED%85%8C%EB%84%A4%EC%8A%A4%EC%9D%98%20%EC%B2%B4">출처: 에라토스테네스의 체</a></li>
</ul>
<h4 id="코드풀이"><strong>&lt;코드풀이&gt;</strong></h4>
<ol>
<li><p><strong>if i ==1: continue</strong>
1은 소수가 될 수 없으므로 제외한다.</p>
</li>
<li><p><strong>for j in range(2 ,int(i**0.5)+1)</strong>
: 2~제곱근 사이의 수로 나누어 떨어진다면 종료 -&gt; 다음 수 확인
범위 내의 수의 제곱근을 구해, <strong>그 제곱근과 같거나 작은 수로 나누어 떨어지면 제거</strong>할 수 있다. 특정 수의 약수들은 대칭하여 곱을 이루기 때문이다. 
예를 들어 18의 약수는 1, 2, 3, 6, 9, 18이고 1*18, 2*9, 3*6으로 서로 대칭을 이룬다. 따라서 이런 경우에는 3까지만 확인하면 된다. (18의 제곱근은 18 ** 0.5 = 4.xx ... 이고, 이 제곱근보다 같거나 작은 수까지만 나눠보고 나누어 떨어지는게 있는지 없는지 확인하면 된다.) </p>
</li>
<li><p><strong>else</strong>
나누어 떨어지지 않거나, for문이 끝난다면 else문에 해당 값 출력한다.</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[단어 정렬]]></title>
            <link>https://velog.io/@hwi_min/%EB%8B%A8%EC%96%B4-%EC%A0%95%EB%A0%AC</link>
            <guid>https://velog.io/@hwi_min/%EB%8B%A8%EC%96%B4-%EC%A0%95%EB%A0%AC</guid>
            <pubDate>Fri, 26 Jan 2024 03:13:02 GMT</pubDate>
            <description><![CDATA[<h2 id="📌-문제-설명">📌 문제 설명</h2>
<blockquote>
<p>상세 설명: <a href="https://www.acmicpc.net/problem/1181">https://www.acmicpc.net/problem/1181</a></p>
</blockquote>
<ul>
<li>알파벳 소문자로 이루어진 N개의 단어가 들어오면 다음과 같이 정렬한다.<ul>
<li>길이가 짧은 것부터</li>
<li>길이가 같으면 사전 순으로</li>
<li>중복된 단어는 하나만 남기고 모두 제거</li>
</ul>
</li>
<li>입력은 첫째 줄에 단어의 개수 N이 주어진다. (1 &lt;= N &lt;= 20,000)</li>
<li>둘째 줄부터 N개의 줄에 걸쳐 알파벳 소문자로 이루어진 단어가 한 줄에 하나 주어진다. (문자열의 길이는 50을 넘지 않음)</li>
</ul>
<h2 id="📌-문제-풀이">📌 문제 풀이</h2>
<pre><code class="language-python">import sys
n = int(sys.stdin.readline())
words = [sys.stdin.readline().strip() for i in range(n)]

words = sorted(list(set(w)))
words.sort(key=len)

for word in words:
    print(word)</code></pre>
<p>제일 먼저 input()을 당연하게 사용했는데, input()은 실행시간이 너무 오래 걸렸다🥲 그래서 sys.stdin.readline()을 사용했다.</p>
<ol>
<li><p><strong>sys모듈을 활용한 정수 입력</strong>
여기서 n은 입력 받은 단어의 개수를 의미한다. 간단하게 n = int(input())으로 받아도 무관하다. </p>
</li>
<li><p><strong>words 리스트</strong>
n의 단어를 입력받아 리스트로 저장하는 과정으로, 단어를 입력받은 후 strip()함수를 통해 양쪽 공백을 제거하여 담아 준다. sys.stdin.readline()은 \n을 포함하는 입력이기 때문에 strip()의 과정이 필요하다. </p>
</li>
<li><p><strong>sorted &amp; sort</strong>
입력받은 단어들 중 중복은 하나만 남기고 삭제해주어야 한다. 따라서 중복을 허용하지 않는 set을 이용해 중복을 없애주고 다시 list에 담아준다. 그 후 sorted()를 통해 사전순(오름차순)으로 먼저 리스트를 정렬해 준다. 이때, sorted()는 반드시 변수에 담아주어야 정렬이 적용되기 때문에 유의하여야 한다. 마지막으로 sort()를 key=len으로 지정하여 단어의 길이 순으로 정렬해준다. 다음과 같은 순서로 적용해주면, 단어의 길이를 우선순위로 그리고 길이가 같을 경우 사전순으로 나열된 리스트를 만들 수 있다.</p>
</li>
<li><p><strong>for문</strong>
리스트 안에 정렬된 후 담겨있는 단어들을 차례대로 print()해준다.</p>
</li>
</ol>
<h3 id="💡-sysstdinreadline">💡 sys.stdin.readline()</h3>
<p>반복문으로 여러줄을 입력받는 상황에서는 시간초과가 발생하지 않게 하기 위해 sys.stdin.readline()을 사용한다. 이 문제의 경우 문자열 n줄을 입력받아 리스트로 저장해주어야 하기 떄문에 다음과 같이 작성한다.</p>
<pre><code class="language-python">import sys
n = int(sys.stdin.readline())
words = [sys.stdin.readline().strip() for i in range(n)]</code></pre>
<ul>
<li>strip(): 문자열 맨 앞과 맨 끝의 공백문자 제거</li>
</ul>
<h3 id="💡-key-point">💡 key point</h3>
<ul>
<li>sys.stdin.readline()의 사용: 시간 초과 방지</li>
<li>단어길이 &gt;&gt; 사전순: 사전순으로 정렬 후 -&gt; 단어 길이로 정렬</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[알람 시계]]></title>
            <link>https://velog.io/@hwi_min/%EC%95%8C%EB%9E%8C-%EC%8B%9C%EA%B3%84</link>
            <guid>https://velog.io/@hwi_min/%EC%95%8C%EB%9E%8C-%EC%8B%9C%EA%B3%84</guid>
            <pubDate>Thu, 25 Jan 2024 12:49:26 GMT</pubDate>
            <description><![CDATA[<p>백준 2884번 문제 입니다.</p>
<h3 id="📌-문제-설명">📌 문제 설명</h3>
<blockquote>
<p>상세 설명: <a href="https://www.acmicpc.net/problem/2884">https://www.acmicpc.net/problem/2884</a></p>
</blockquote>
<ul>
<li>학교에 지각하지 않기 위해 기존에 설정해둔 알람시간을 45분 앞선 시간으로 바꾸려 한다.</li>
<li>첫째 줄에 두 정수 H와 M이 주어진다. (0 &lt;= H &lt;= 23,0 &lt;= M &lt;= 59)</li>
<li>입력 시간은 24시간 표현을 사용하며, 시간을 나타낼 때, 불필요한 0은 사용하지 않는다.</li>
</ul>
<h3 id="📌-문제-풀이">📌 문제 풀이</h3>
<pre><code class="language-python">origTime = input()
hour = int(origTime.split(&#39; &#39;)[0])
min = int(origTime.split(&#39; &#39;)[1])

if min - 45 &gt;= 0:
    min = min - 45
    print(hour, min)
elif min - 45 &lt; 0:
    min += 15
    if hour == 0:
        hour = 23
        print(hour, min)
    else:
        hour -= 1
        print(hour, min)</code></pre>
<ol>
<li><strong>분(min)의 2가지 케이스</strong></li>
</ol>
<ul>
<li>첫째, 45분을 뺐을 때 남은 분(min)의 수가 <strong>0 이상</strong>인 경우</li>
<li>둘째, 45분을 뺐을 때 <strong>0보다 작은 숫자</strong>가 남는 경우</li>
</ul>
<p>첫째 케이스는 가장 쉽게 풀 수 있다. 첫번째 if문에서 볼 수 있듯이 hour(시)에는 손대지 않고 분만 빼기 연산 해준다. 두번째 케이스는 또 두가지 경우로 나뉜다.</p>
<ol start="2">
<li><strong>시(hour)의 2가지 케이스</strong>
분(min)에서 45분을 뺐을 때, 0보다 작은 수가 남는다는 것은 시간(hour)에서 1을 빼주고 분(min) 역시 수정해주어야 한다. 이러한 경우 다음과 같은 두 가지 케이스로 다시 나뉜다.</li>
</ol>
<ul>
<li>첫째, <strong>주어진 시(hour)가 0</strong>인 경우</li>
<li>둘째, <strong>주어진 시(hour)가 0이 아닌</strong> 경우</li>
</ul>
<p>우선, 시간 계산을 할 때 분이 0보다 작을때 45분을 뺀 뒤 분(min)이 같는 공통적인 특징을 확인할 수 있다.
<em>(ex. 35분 -&gt; 50분, 20분 -&gt; 35분, 10분 -&gt; 25분)</em></p>
<p>그러므로 주어진 분(min)에서 15를 더해주면 된다. 따라서 <strong>min += 15</strong>이다. 이제 시(hour)를 계산해야하는데 첫번째의 경우 0시 이전은 23시이므로 hour을 23으로 확정지을 수 있다. 그렇지 않은 경우에는 주어진 수에서 -1 연산을 해주면 된다. </p>
<h4 id="생각해-볼-것">생각해 볼 것</h4>
<ul>
<li>팀스터디를 통해 팀원과 풀이를 공유하는 과정에서 다음 코드를 더 짧고 효율적으로 수정할 수 있었다. 조금 더 효율적인 코드를 작성할 수 있는 방향으로 고민을 해 봐야 할 것 같다.<pre><code class="language-python"># 수정 전
origTime = input()
hour = int(origTime.split(&#39; &#39;)[0])
min = int(origTime.split(&#39; &#39;)[1])</code></pre>
<pre><code class="language-python"># 수정 후
hour, min = map(int, input().split())</code></pre>
<img src="https://velog.velcdn.com/images/hwi_min/post/3639ed62-a831-4a27-9378-05433d0d0d8b/image.png" alt="">
내가 작성했던 기존 코드보다 약 4초의 시간이 줄어들었다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[SELECT / 평균 일일 대여 요금 구하기]]></title>
            <link>https://velog.io/@hwi_min/SELECT-%ED%8F%89%EA%B7%A0-%EC%9D%BC%EC%9D%BC-%EB%8C%80%EC%97%AC-%EC%9A%94%EA%B8%88-%EA%B5%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@hwi_min/SELECT-%ED%8F%89%EA%B7%A0-%EC%9D%BC%EC%9D%BC-%EB%8C%80%EC%97%AC-%EC%9A%94%EA%B8%88-%EA%B5%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 22 Jan 2024 08:55:20 GMT</pubDate>
            <description><![CDATA[<p>프로그래머스 SQL 고득점 Kit SELECT문 연습 문제 입니다.</p>
<h3 id="📌-문제-설명">📌 문제 설명</h3>
<blockquote>
<p>상세 설명: <a href="https://school.programmers.co.kr/learn/courses/30/lessons/151136">https://school.programmers.co.kr/learn/courses/30/lessons/151136</a></p>
</blockquote>
<ul>
<li>자동차 대여 회사에서 대여중인 자동차의 정보를 담은 테이블 CAR_RENTAL_COMPANY_CAR</li>
<li>CAR_RENTAL_COMPANY_CAR는 CAR_ID, CAR_TYPE, DAILY_FEE, OPTIONS로 구성되어 있다.</li>
<li>자동차 종류(CAR_TYPE)는 &#39;SUV&#39;, &#39;세단&#39;, &#39;승합차&#39;, &#39;트럭&#39;, &#39;리무진&#39;으로 구성되어 있다.</li>
<li>이때, 자동차의 종류가 &#39;SUV&#39;인 자동차의 평균 일일 대여 요금을 소수 첫 번째 자리에서 반올림 후 AVERAGE_FEE로 지정하여 출력하는 SQL문을 작성해야한다.</li>
</ul>
<h3 id="📌-문제-풀이">📌 문제 풀이</h3>
<p>SELECT문 중에서도 가장 난이도가 낮고 기본적인 문제이기 때문에 큰 문제는 없었다.
일반적으로 SQL은 대소문자를 구분하지 않기 때문에 모두 대문자로, 모두 소문자로 작성하여도 되나, 나는 집계 함수 부분을 소문자로 처리하였다. </p>
<pre><code class="language-sql">SELECT round(avg(DAILY_FEE)) as AVERAGE_FEE
FROM CAR_RENTAL_COMPANY_CAR
WHERE CAR_TYPE = &quot;SUV&quot;;</code></pre>
<ol>
<li><p>SQL의 가장 기본적인 코드 작성 순서는 <strong>SELECT &gt; FROM &gt; WHERE</strong> 순이다. 나중에 여러 조건을 추가시키면, HAVING, GROUP BY 등 추가적으로 등장하지만, 지금과 같이 단순한 코드에서는 SELECT, FROM, WHERE만으로도 충분히 풀이할 수 있다.</p>
</li>
<li><p><strong>FROM 문</strong>
FROM은 영어 뜻 그대로 ~에서 라는 의미정도로 보면 될 것 같다. CAR_RENTAL_COMPANY_CAR 테이블에서 정보를 가져올 것이기 때문에 위와 같이 작성한다. </p>
</li>
<li><p><strong>WHERE 문</strong>
WHERE에서는 문제가 CAR_TYPE이 &#39;SUV&#39;인 데이터에 대한 정보를 추출하고 싶어하기 때문에 CAR_TYPE = &#39;SUV&#39;로 지정해 주었다. 여기서 주의해야하는 것은 <strong>SQL에서 비교 연산자는 우리가 흔히 아는 &#39;==&#39; 대신에 &#39;=&#39;를 사용해야 한다는 것</strong>이다. 덧붙여 SQL에서의 &#39;;&#39;는 쿼리의 끝을 나타낸다. </p>
</li>
<li><p>*<em>SELECT문 *</em>
우리는 1.(CAR_TYPE이 &#39;SUV&#39;인) DAILY_FEE의 평균을 구해 2. 소수 첫 번째 자리에서 반올림 하여 3. AVERAGE_FEE로 선언할 것이다. 따라서 평균을 계산해주는 SQL의 집계함수 avg를 이용해 해당되는 DAILY_FEE의 평균을 구한 뒤, 소수 첫 번째 자리에서 반올림하는 함수 round를 사용한 뒤 as를 이용해 AVERAGE_FEE로 정의해 준다.</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[옹알이(1)]]></title>
            <link>https://velog.io/@hwi_min/%EC%98%B9%EC%95%8C%EC%9D%B41</link>
            <guid>https://velog.io/@hwi_min/%EC%98%B9%EC%95%8C%EC%9D%B41</guid>
            <pubDate>Mon, 22 Jan 2024 08:16:58 GMT</pubDate>
            <description><![CDATA[<p>프로그래머스 코딩테스트 입문 마지막 문제입니다.</p>
<h3 id="📌-문제-설명">📌 문제 설명</h3>
<blockquote>
<p>상세 설명 : <a href="https://school.programmers.co.kr/learn/courses/30/lessons/120956">https://school.programmers.co.kr/learn/courses/30/lessons/120956</a></p>
</blockquote>
<ul>
<li>6개월된 조카가 할 수 있는 발음은 <strong>&quot;aya&quot;, &quot;ye&quot;, &quot;woo&quot;, &quot;ma&quot;</strong> 네 가지이다.</li>
<li>이 네 가지 발음을 최대 한 번씩 사용해 조합한 발음만 할 수 있다.</li>
<li>문자열 배열 babbling이 매개변수로 주어질 때, 조카가 발음할 수 있는 단어의 개수를 return하는 solution함수를 생성해야 한다.</li>
</ul>
<h3 id="📌-문제-풀이">📌 문제 풀이</h3>
<p>우선, 문제 이해의 핵심은 <strong>발음할 수 있는 단어들간의 조합만</strong> 조카가 발음할 수 있다는 것이다. <em>ex. ayaye -&gt; O / mayaya -&gt; X</em></p>
<p>최근에 NLP 졸업시험을 봤더니 문자열 처리라는 키워드를 떠올리니 제일 먼저 정규표현식이 생각나서, 우선 정규 표현식을 활용하여 문제를 풀어봤다.</p>
<pre><code class="language-python">import re

def solution(babbling):
    regex = re.compile(&#39;^(aya|ye|woo|ma)+$&#39;)
    answer = 0
    for word in babbling:
        if regex.match(word):
            answer += 1
    return answer</code></pre>
<ol>
<li><p><strong>regex : ()</strong>
정규 표현식에서 <strong>()는 추출한 패턴을 지정</strong>하는 역할을 한다. 조카가 발음 가능한 단어들끼리의 조합만을 발음할 수 있기 때문에 &quot;aya&quot; &quot;ye&quot;, &quot;woo&quot;,&quot;ma&quot;만 추출해야 한다.</p>
</li>
<li><p><strong>regex : |</strong>
정규 표현식에서 <strong>|는 or</strong>을 뜻한다. 위에서 언급한 발음 가능한 단어들 중 하나만 족해도 추출해야하므로 모든 단어들 사이를 |로 연결해 준 것이다.</p>
</li>
<li><p><strong>regex : ^ , $</strong></p>
</li>
</ol>
<p><strong>^는 문자열의 시작</strong> 또는 부정(not)을 의미하고 <strong>$는 문자열의 끝</strong>을 의미한다. 여기서는 문자열의 시작의 역할로 사용되었다. 발음가능한 단어를 제외한 다른 단어가 앞 뒤에 들어가면 안되고 ()안의 요소들끼리로만 구성되어야 하므로 ^ 와 $를 사용했다.</p>
<ol start="4">
<li><strong>regex : match</strong>
문자열에서 <strong>정규표현식에 매칭되는 항목들을 배열로 반환</strong>한다. <strong>match가 될 때는 match의 객체를 돌려주고, match되지 않을 때는 None을 반환</strong>한다. 정규식 메소드 중 match를 선택한 이유는 <strong>match 메서드는 문자열의 처음부터 정규식과 match되지 않으면 None값</strong>을 내놓기 때문이다. 따라서 문자열 전체를 탐색하는 search보다 처음부터 검색하는 match를 선택했다. 사실 정규 표현식을 위와 같이 선언해주었기 때문에, search를 사용해도 테스트 결과가 틀리지는 않는다. </li>
</ol>
<p>정규 표현식을 사용하지 않고 푸는 사례도 있길래 정규표현식을 사용하지 않고 문제를 풀어보았다.</p>
<pre><code class="language-python">def solution(babbling):
    answer = 0
    ableWord = [&#39;aya&#39;, &#39;ye&#39;, &#39;woo&#39;, &#39;ma&#39;]
    for saidWord in babbling:
        for word in ableWord:
            if word in saidWord:
                saidWord = saidWord.replace(word, &#39; &#39;)
        if len(saidWord.strip()) == 0:
                answer += 1
    return answer</code></pre>
<p><strong>1. ableWord 정의</strong>
조카가 발음할 수 있는 단어들을 담은 리스트를 정의하여, 발음할 수 있는 단어의 변화가 있을 때도 재활용 할 수 있도록 하였다.</p>
<p><strong>2. 바깥 for문</strong>
입력 받은 babbling을 순차적으로 돌면서 전체 babbling에 대한 처리를 해줄 수 있도록 for문을 정의하였다.</p>
<p><strong>3. 내부 for문 &amp; 내부 if문</strong>
babbling의 하나의 문자열(saidWord) 내부에 발음할 수 있는 단어(word)가 포함되어있는 경우 발음할 수 있는 단어(word)를 space(공백)로 바꾸어주도록 하였다.
<em>ex. &#39;ayaee&#39;인 경우 &#39;aya&#39;를 발음할 수 있기 때문에 해당 문자열은 &#39; ee&#39;가 됨</em></p>
<p><strong>4. 외부 if문</strong>
모든 과정을 마친 후, <strong>saidWord의 양쪽 공백을 없앤 문자열의 길이가 0인 경우는 해당 단어 내의 모든 발음이 가능하여 모두 공백으로 바뀐 뒤 공백을 제거하여 0이 되었다는 뜻</strong>이므로 최종적으로 조카가 발음할 수 있다. 따라서 answer에 1을 더하여 준다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Python] 기초 수학 with Python]]></title>
            <link>https://velog.io/@hwi_min/Python-%EA%B8%B0%EC%B4%88-%EC%88%98%ED%95%99-with-Python</link>
            <guid>https://velog.io/@hwi_min/Python-%EA%B8%B0%EC%B4%88-%EC%88%98%ED%95%99-with-Python</guid>
            <pubDate>Thu, 18 Jan 2024 05:23:49 GMT</pubDate>
            <description><![CDATA[<h3 id="📌-약수와-소수">📌 약수와 소수</h3>
<h4 id="정의">정의</h4>
<p>약수 : 어떤 수를 나누어떨어지게 하는 수
소수 : 1과 자신만을 약수로 가지는 수(단, 1은 제외)
합성수 : 소수의 반대 개념. 1과 자신 외의 다른 약수를 가지는 수</p>
<h4 id="파이썬으로-구현하기">파이썬으로 구현하기</h4>
<ol>
<li><p>파이썬을 이용하여 사용자의 입력을 받아 숫자의 약수를 구해보자.</p>
<pre><code class="language-python">inputNumber = int(input(&quot;0보다 큰 정수 입력: &quot;))

for number in range(1, (inputNumber+1)):
   if inputNumber % number == 0:
       print(&#39;{}의 약수: {}&#39;.format(inputNumber, number))</code></pre>
</li>
<li><p>파이썬을 이용하여 사용자가 입력한 숫자까지의 소수를 구해보자.</p>
<pre><code class="language-python">inputNumber = int(input(&quot;0보다 큰 정수 입력: &quot;))

for number in range(2, (inputNumber+1)):
   flag = True
   for n in range(2, number):
       if number % n == 0:
           flag = False
           break

   if (flag):
       print(&#39;{} : 소수&#39;.format(number))
   else:
       print(&#39;{} : 합성수&#39;.format(number))</code></pre>
</li>
</ol>
<h3 id="📌-소인수와-소인수분해">📌 소인수와 소인수분해</h3>
<h4 id="정의-1">정의</h4>
<p>소인수 : 소수와 인수(약수)를 합친 말로, 약수(인수) 중에서 소수인 숫자
소인수분해 : 1보다 큰 정수를 소인수의 곱으로 나타낸 것. 소인수분해를 통해 약수를 찾을 수 있음.</p>
<h4 id="파이썬으로-구현하기-1">파이썬으로 구현하기</h4>
<ol>
<li><p>파이썬을 이용해서 사용자가 입력한 수를 소인수분해 해보자.</p>
<pre><code class="language-python">inputNumber = int(input(&#39;1보다 큰 정수 입력: &#39;))

n = 2
while n &lt;= inputNumber:
   if inputNumber % n == 0:
       print(&#39;소인수 : {}&#39;.format(n))
       inputNumber /= n
   else:
       n += 1</code></pre>
<ol start="2">
<li>72에 x를 곱하면 y의 제곱이 된다고 할 때, x에 해당하는 가장 작은 정수를 구해보자.<pre><code class="language-python">inputNumber = int(input(&#39;1보다 큰 정수 입력: &#39;))
</code></pre>
</li>
</ol>
<p>n = 2
searchNumbers = []</p>
<p>while n &lt;= inputNumber:
   if inputNumber % n == 0:</p>
<pre><code>   print(&#39;소인수 : {}&#39;.format(n))
   if searchNumbers.count(n): #n의 개수 확인
       searchNumbers.append(n)

   elif searchNumbers.count(n) == 1:
       searchNumbers.remove(n) #이미 제곱형태이므로, 무시하기 위해 remove

   inputNumber /= n</code></pre><p>   else:</p>
<pre><code>   n += 1</code></pre><p>print(&#39;searchNumbers: {}&#39;.format(searchNumbers))</p>
<pre><code></code></pre></li>
</ol>
<h3 id="📌-공약수와-최대공약수">📌 공약수와 최대공약수</h3>
<h4 id="정의-2">정의</h4>
<p>공약수 : 두 개 이상의 수에서 공통된 약수
최대공약수 : 공약수 중 가장 큰 수. 소수로 나눗셈하여 편리하게 최대공약수를 구할 수 있음.</p>
<h4 id="파이썬으로-구현하기-2">파이썬으로 구현하기</h4>
<ol>
<li><p>두개의 수를 입력하면 공약수와 최대공약수를 출력하는 코드를 작성해보자.
 단, 두 번째 입력하는 숫자가 첫 번째 입력하는 숫자보다 크다고 가정한다.</p>
<pre><code class="language-python">num1 = int(input(&#39;1보다 큰 정수 입력: &#39;))
num2 = int(input(&#39;1보다 큰 정수 입력: &#39;))
maxNum = 0

for i in range(1, (num1+1)):
   if num1 % i == 0 and num2 % i ==0:
     print(&#39;공약수: {}&#39;.format(i))
     maxNum = i
print(&#39;최대공약수 : {}&#39;.foramt(maxNum))
</code></pre>
</li>
<li><p>세 개의 수를 입력하면 공약수와 최대공약수를 출력하는 코드를 작성해보자.</p>
<pre><code class="language-python">num1 = int(input(&#39;1보다 큰 정수 입력: &#39;))
num2 = int(input(&#39;1보다 큰 정수 입력: &#39;))
num3  = int(input(&#39;1보다 큰 정수 입력: &#39;))
maxNum = 0

for i in range(1, (num1+1)):
   if num1 % i == 0 and num2 % i ==0 and num3 % i ==0:
       print(&#39;공약수: {}&#39;.format(i))
       maxNum = i

print(&#39;최대공약수 : {}&#39;.format(maxNum))</code></pre>
</li>
</ol>
<h3 id="📌-공배수와-최소공배수">📌 공배수와 최소공배수</h3>
<h4 id="정의-3">정의</h4>
<p>공배수 : 두 개 이상의 수에서 공통된 배수
최소공배수 : 공배수 중 가장 작은 수</p>
<h4 id="파이썬으로-구현하기-3">파이썬으로 구현하기</h4>
<ol>
<li><p>두 개의 수를 입력하면 최소공배수를 출력하는 코드를 작성해보자.</p>
<pre><code class="language-python">num1 = int(input(&#39;1보다 큰 정수 입력: &#39;))
num2 = int(input(&#39;1보다 큰 정수 입력: &#39;))
maxNum = 0

for i in range(1, (num1+1)):
if num1 % i == 0 and num2 % i ==0:
   print(&#39;공약수: {}&#39;.format(i))
   maxNum = i

print(&#39;최대공약수 : {}&#39;.format(maxNum))

minNum = (num1 * num2) // maxNum
print(&#39;최소공배수 : {}&#39;.format(minNum))</code></pre>
<ol start="2">
<li>세 개의 수를 입력하면 최소공배수를 출력하는 코드를 작성해보자.<pre><code class="language-python"># 1. 두 개의 수에 대한 최소공배수를 구한다
num1 = int(input(&#39;1보다 큰 정수 입력: &#39;))
num2 = int(input(&#39;1보다 큰 정수 입력: &#39;))
num3  = int(input(&#39;1보다 큰 정수 입력: &#39;))
maxNum = 0
</code></pre>
</li>
</ol>
<p>for i in range(1, (num1+1)):
   if num1 % i == 0 and num2 % i ==0:</p>
<pre><code> print(&#39;공약수: {}&#39;.format(i))
 maxNum = i</code></pre><p>print(&#39;최대공약수 : {}&#39;.format(maxNum))</p>
<p>minNum = (num1 * num2) // maxNum
print(&#39;{}, {}의 최소공배수 : {}&#39;.format(num1, num2, minNum))</p>
<h1 id="2-세-번째-수의-공배수를-구한다">2. 세 번째 수의 공배수를 구한다</h1>
<p>newNum = minNum
for i in range(1, (newNum+1)):
   if newNum % i == 0 and num3 % i == :</p>
<pre><code> maxNum = i</code></pre><p>print(&#39;최대공약수 : {}.format(maxNum))</p>
<p>minNum = (newNum * num3) // maxNum
print(&#39;{}, {}, {}의 최소공배수 : {}&#39;.format(num1, num2, num3, minNum))</p>
<pre><code></code></pre></li>
<li><p>섬마을에 과일, 생선, 야채를 판매하는 배가 다음 주기로 입항한다고 할 때, 모든 배가 입항하는 날짜를 계산해 보자.</p>
<blockquote>
<p>과일 선박: 3일 주기 / 생선 선박: 4일 주기 / 야채 선박: 5일 주기</p>
</blockquote>
<pre><code class="language-python"> ship1 = 3; ship2 = 4; ship3 = 5;
 maxDay = 0

 for i in range(1, ship1+1)):
     if ship1 % i == 0 and ship2 % i == 0:
         maxDay = i

 print(&#39;최대공약수 : {}.format(maxDay))

 minDay = (ship1 * ship2) // maxDay
 print(&#39;{}, {}의 최소공배수 : {}&#39;.format(ship1, ship2, minDay))

 newDay = minDay

 for i in range(1, (newDay+1)):
     if newDay %  i == 0 and ship3 % i == 0:
         maxDay = i

 print(&#39;최대공약수 : {}.format(maxDay))

 minDay = (newDay * ship3) // maxDay
 print(&#39;{}, {}, {}의 최소공배수 : {}&#39;.format(ship1, ship2, ship3, minDay))</code></pre>
</li>
</ol>
]]></description>
        </item>
    </channel>
</rss>