<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>velog</title>
        <link>https://velog.io/</link>
        <description>개발자들을 위한 블로그 서비스. 어디서 글 쓸지 고민하지 말고 벨로그에서 시작하세요.</description>
        <lastBuildDate>Fri, 16 Oct 2026 03:46:32 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>velog</title>
            <url>https://images.velog.io/velog.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. Velog. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[0.4kW 0.5Hp 단상 기어드 모터의 기술 사양 및 실제 적용 사례를 평가합니다.]]></title>
            <link>https://velog.io/@thanhthaimotor/0.4kW-0.5Hp-%EB%8B%A8%EC%83%81-%EA%B8%B0%EC%96%B4%EB%93%9C-%EB%AA%A8%ED%84%B0%EC%9D%98-%EA%B8%B0%EC%88%A0-%EC%82%AC%EC%96%91-%EB%B0%8F-%EC%8B%A4%EC%A0%9C-%EC%A0%81%EC%9A%A9-%EC%82%AC%EB%A1%80%EB%A5%BC-%ED%8F%89%EA%B0%80%ED%95%A9%EB%8B%88%EB%8B%A4</link>
            <guid>https://velog.io/@thanhthaimotor/0.4kW-0.5Hp-%EB%8B%A8%EC%83%81-%EA%B8%B0%EC%96%B4%EB%93%9C-%EB%AA%A8%ED%84%B0%EC%9D%98-%EA%B8%B0%EC%88%A0-%EC%82%AC%EC%96%91-%EB%B0%8F-%EC%8B%A4%EC%A0%9C-%EC%A0%81%EC%9A%A9-%EC%82%AC%EB%A1%80%EB%A5%BC-%ED%8F%89%EA%B0%80%ED%95%A9%EB%8B%88%EB%8B%A4</guid>
            <pubDate>Fri, 16 Oct 2026 03:46:32 GMT</pubDate>
            <description><![CDATA[<p>0.4kW(0.5마력) 단상 기어드 모터는 220V 전원을 사용하는 중소형 장비에 널리 사용되는 구동 장치입니다. 주요 기술 사양은 다음과 같습니다.
⚡ 정격 전기 사양 및 기동 능력: 정격 전류는 2.5A~3A이며, 강력한 기동 토크와 단상 전원망에서의 안정적인 작동을 위해 런인 콘덴서와 기동 콘덴서가 장착되어 있습니다.</p>
<p>⚙️ 기계 구조 및 축 방향: 100% 동심 전기 모터와 웜 기어 감속기를 결합하여 수직 출력축을 구현함으로써 소형 장비의 설치 공간을 최적화했습니다.</p>
<p>🏭 생산 및 일상생활에서의 실제 적용 분야: 미니 컨베이어, 식품 원료 혼합기, 자동 회전 오븐, 대형 롤러 도어 리프팅 시스템, 경부하 자동 생산 라인 등에 적합합니다.</p>
<p>🔍 작동 및 유지 보수 관련 기술 참고 사항: 기어박스의 윤활유 레벨을 정기적으로 점검하십시오. 모터의 정격 전류 이상으로 지속적으로 작동시키지 마십시오. 고정자 권선이 과열되는 것을 방지할 수 있습니다.</p>
<p>👉 자세한 내용은 여기를 참조하세요: <a href="https://thanhthaimotor.com/video/dong-co-giam-toc/video-review-motor-giam-toc-1-pha-0-4kw-0-5hp">https://thanhthaimotor.com/video/dong-co-giam-toc/video-review-motor-giam-toc-1-pha-0-4kw-0-5hp</a></p>
<hr>
<p>📞 지금 바로 연락주시면 최고의 상담과 가격을 제공해 드립니다!</p>
<p>🌐 웹사이트: thanhthaimotor.com
☎️ 핫라인: 0909.539.175 - 0908.993.489 - 0902.530.445 - 0978.352.024
#thanhthaimotor #motorigiamtoc #docothanhthai
<img src="https://velog.velcdn.com/images/thanhthaimotor/post/28e472c0-8484-4b65-ba89-e79e60faf75f/image.jpg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[코드를 한 줄도 쓰지 않고 해커톤에 참가하고 있다]]></title>
            <link>https://velog.io/@volvolisa/%EC%BD%94%EB%93%9C%EB%A5%BC-%ED%95%9C-%EC%A4%84%EB%8F%84-%EC%93%B0%EC%A7%80-%EC%95%8A%EA%B3%A0-%ED%95%B4%EC%BB%A4%ED%86%A4%EC%97%90-%EC%B0%B8%EA%B0%80%ED%95%98%EA%B3%A0-%EC%9E%88%EB%8B%A4</link>
            <guid>https://velog.io/@volvolisa/%EC%BD%94%EB%93%9C%EB%A5%BC-%ED%95%9C-%EC%A4%84%EB%8F%84-%EC%93%B0%EC%A7%80-%EC%95%8A%EA%B3%A0-%ED%95%B4%EC%BB%A4%ED%86%A4%EC%97%90-%EC%B0%B8%EA%B0%80%ED%95%98%EA%B3%A0-%EC%9E%88%EB%8B%A4</guid>
            <pubDate>Fri, 16 Oct 2026 03:42:20 GMT</pubDate>
            <description><![CDATA[<p>공공조달 데이터에서 시중가보다 비싸게 산 물품을 찾는 해커톤에 참가 중이다. 10월 25일 마감이고, 지금까지 15건을 등록했다.</p>
<p>먼저 밝혀둘 게 있다. <strong>이 저장소의 코드는 내가 쓰지 않았다.</strong> 전부 Claude가 썼다. 나는 한 줄도 타이핑하지 않았다.</p>
<p>그래서 이 글은 &quot;이렇게 구현했다&quot;가 아니라 &quot;AI에게 이 일을 어떻게 시켰나&quot;에 대한 기록이다.</p>
<h2 id="내가-한-것과-ai가-한-것">내가 한 것과 AI가 한 것</h2>
<p>처음엔 웹사이트에서 직접 찾아볼 생각이었다. 그런데 데이터셋을 보니 물품이 98만 개였다. 사람이 훑을 양이 아니다.</p>
<p>그래서 Claude에게 도구를 만들게 했다. 데이터를 받아서 조건으로 거르고, 등록 가능한지 확인하고, 근거를 붙여 제출하는 일련의 스크립트다. 파이썬, DuckDB, 대회 API를 쓴다는 건 결과물을 보고 알았지 내가 고른 게 아니다.</p>
<p><strong>내가 한 건 판단이었다.</strong></p>
<ul>
<li>어떤 분야를 볼 것인가 (건설 자재는 시중 판매 페이지가 없으니 제외)</li>
<li>가설이 틀렸을 때 어디로 틀 것인가 (유명 브랜드 완제품 → 전문몰 제품)</li>
<li>어떤 근거를 쓸 것인가 (더 싼 페이지가 있어도 서버가 못 읽으면 소용없다)</li>
<li>등록할 것인가 말 것인가 (preview 결과를 보고 매번 직접 결정)</li>
</ul>
<p>가장 중요한 판단은 방향 전환이었다. 처음 가설은 &quot;같은 물품인데 업체마다 가격이 다르면 비싼 쪽이 의심스럽다&quot;였는데, 걸러진 게 전부 석재 블록과 단열재였다. 가격 차이는 보이지만 시중가를 증명할 수가 없다.</p>
<p>두 번째 가설은 &quot;삼성·LG 같은 유명 브랜드면 시중가를 쉽게 확인할 수 있다&quot;였다. 1,500건을 조회해서 416건을 추리고 342건의 시중가를 확인했는데, 기준을 넘긴 건 1건이었다. 조달가가 시중가보다 <strong>싼</strong> 경우가 대부분이었다.</p>
<p>이 두 번의 실패를 겪고 나서야 방향이 잡혔다. 전문몰에서 같은 규격으로 팔리는 제품, 그중에서도 재제조 토너처럼 &quot;같은 물건&quot;의 정의가 명확한 분야.</p>
<p>AI는 내가 시킨 걸 빠르게 해줬지만, <strong>어디를 팔지는 알려주지 않았다.</strong> 두 번 헛삽질한 뒤에야 알게 된 것이다.</p>
<h2 id="규칙을-먼저-정했다">규칙을 먼저 정했다</h2>
<p>AI에게 일을 맡기면서 가장 신경 쓴 건 속도가 아니라 경계였다. 대회 참가 토큰이 있고, 되돌릴 수 없는 제출 행위가 있고, 남의 데이터가 있기 때문이다.</p>
<p>그래서 작업 지침에 이런 것들을 못박아뒀다.</p>
<pre><code>1. 소스 코드를 수정하지 않는다. 파일 생성은 claims/ 와 output/ 아래만 허용.
2. 참가 토큰(.env)을 출력·복사·커밋하지 않는다. .env 파일을 열어 보지도 않는다.
3. 등록은 내가 preview 결과를 보고 &quot;등록해&quot;라고 말한 물품만 실행한다.
   한 번의 승인은 그 물품 하나에만 유효하다.
4. 기관·업체가 부정을 저질렀다고 단정하는 표현을 쓰지 않는다.
5. 차단된 사이트를 VPN·프록시로 뚫지 않는다.
6. 근거의 인용은 그 판매 페이지에서 직접 본 내용만 옮긴다. 추측을 쓰지 않는다.</code></pre><p>3번이 특히 중요했다. 등록은 횟수 제한이 있고(하루 50건, 전체 100건) 철회해도 한도가 돌아오지 않는다. 그래서 &quot;알아서 좋은 거 찾아서 올려줘&quot;는 절대 하지 않았다. 매번 preview 결과를 보고 내가 승인했다.</p>
<p>4번은 성격이 다르다. 이건 안전장치가 아니라 태도의 문제다. 내가 찾은 건 &quot;확인해볼 만한 가격 차이&quot;지 누군가의 잘못이 아니다. AI가 생성한 문장이 그 선을 넘지 않도록 처음부터 못박아둬야 했다.</p>
<h2 id="gemini를-같이-돌리려다-실패했다">Gemini를 같이 돌리려다 실패했다</h2>
<p>경쟁이 빠른 대회다. 괜찮아 보이는 물품을 조회해보면 이미 다른 참가자가 가져간 경우가 절반 가까이 됐다.</p>
<p>그래서 생각한 게 <strong>두 대를 동시에 돌리는 것</strong>이었다. Claude가 쓴 스크립트를 Gemini에게도 실행시키고, 서로 다른 분야를 맡게 하면 커버리지가 두 배가 된다.</p>
<p>작업 분담 프롬프트까지 만들었다.</p>
<pre><code>## 역할 분담 (Claude와 겹치지 않게)
- Claude 담당: 재제조토너·잉크, LED 조명, 가구, 광통신·네트워크 랙
- 너의 담당: market 모드 후보 중 위 분야가 아닌 물품
           (보안 카메라·영상감시, 음향 장비, 교육 기자재, 안내전광판 등)
- 등록 직전에는 항상 lookup 으로 available 인지 다시 확인한다
  (Claude나 다른 참가자가 먼저 가져갔을 수 있음)</code></pre><p>같은 저장소, 같은 스크립트, 같은 규칙. 분야만 나눠서 병렬로 돌리는 그림이었다.</p>
<p><strong>그런데 실행이 안 됐다.</strong></p>
<p>웹 Gemini는 내 로컬 파일을 읽거나 스크립트를 실행하지 못했다. 무료 사용이라 그런 건지, 원래 그런 건지는 모르겠다. 어쨌든 &quot;이 폴더에서 이 스크립트를 돌려줘&quot;가 성립하지 않았다.</p>
<p>그래서 Gemini CLI를 설치했다. 이건 로컬에서 돌아가니까 될 거라고 봤는데, <strong>로그인 인증에서 막혔다.</strong> 몇 번 시도하다가 그만뒀다.</p>
<p>답답해서 그냥 Claude 하나로 계속했다.</p>
<h2 id="결과적으로는">결과적으로는</h2>
<p>병렬화는 실패했지만 진행은 됐다. 15건을 등록했고 예상 금액은 약 1,235만 원이다.</p>
<p>돌아보면 두 대를 돌렸어도 크게 빨라지진 않았을 것 같다. 이 대회의 병목은 후보를 찾는 속도가 아니라 <strong>같은 모델의 시중가를 증명하는 것</strong>이었기 때문이다. 그 판단은 결국 사람이 하나씩 해야 했다.</p>
<p>실제로 가장 오래 걸린 작업은 코드가 아니라 이런 것들이었다.</p>
<ul>
<li>이 제품과 저 제품이 정말 &quot;같은 물건&quot;인가 (토너 카트리지 코드, 프로젝터 렌즈 포함 여부)</li>
<li>이 가격이 대표성 있는 시세인가, 아니면 일시적인 반값 행사가인가</li>
<li>이 페이지를 대회 서버가 읽을 수 있는가</li>
</ul>
<p>AI가 빠르게 해준 건 수집과 정리였고, 느리게 남은 건 판단이었다.</p>
<h2 id="정직하게-말하면">정직하게 말하면</h2>
<p>이 저장소를 내 포트폴리오라고 부를 수 있을까. 코드는 내가 안 썼는데.</p>
<p>그래도 적어둘 가치는 있다고 생각한다. 무엇을 만들지 정하고, 틀린 가설을 두 번 버리고, 경계를 미리 긋고, 되돌릴 수 없는 행동마다 승인을 끼워 넣은 건 나니까. 그게 앞으로 이런 도구를 쓰는 방식에 가깝지 않을까 싶다.</p>
<p>대회는 10월 25일까지다. 끝나면 결과를 정리해서 한 번 더 쓸 생각이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[PostgreSQL] 이름만 부르면 순서대로 처리해주는 레시피 카드 — Stored Procedure 실습으로 이해하기]]></title>
            <link>https://velog.io/@yonghoon_kim/PostgreSQL-%EC%9D%B4%EB%A6%84%EB%A7%8C-%EB%B6%80%EB%A5%B4%EB%A9%B4-%EC%88%9C%EC%84%9C%EB%8C%80%EB%A1%9C-%EC%B2%98%EB%A6%AC%ED%95%B4%EC%A3%BC%EB%8A%94-%EB%A0%88%EC%8B%9C%ED%94%BC-%EC%B9%B4%EB%93%9C-Stored-Procedure-%EC%8B%A4%EC%8A%B5%EC%9C%BC%EB%A1%9C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@yonghoon_kim/PostgreSQL-%EC%9D%B4%EB%A6%84%EB%A7%8C-%EB%B6%80%EB%A5%B4%EB%A9%B4-%EC%88%9C%EC%84%9C%EB%8C%80%EB%A1%9C-%EC%B2%98%EB%A6%AC%ED%95%B4%EC%A3%BC%EB%8A%94-%EB%A0%88%EC%8B%9C%ED%94%BC-%EC%B9%B4%EB%93%9C-Stored-Procedure-%EC%8B%A4%EC%8A%B5%EC%9C%BC%EB%A1%9C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 16 Oct 2026 03:39:49 GMT</pubDate>
            <description><![CDATA[<p>지난 포스트에서는 데이터가 바뀌면 <strong>자동으로</strong> 실행되는 트리거를 다뤘습니다. 이번에는 짝꿍처럼 함께 언급되는 <strong>스토어드 프로시저(Stored Procedure)</strong> 를 직접 만들어보면서 정리해보려고 합니다.</p>
<p>트리거 때와 같은 방식으로, <strong>작은 예제 하나를 처음부터 끝까지 따라 해보는 구성</strong>입니다. 테이블 두 개와 프로시저 하나만 있으면 충분합니다.</p>
<hr>
<h2 id="1-먼저-비유로-한-번에-잡고-가기">1. 먼저 비유로 한 번에 잡고 가기</h2>
<p>요리할 때 쓰는 <strong>레시피 카드</strong>를 떠올려보겠습니다.</p>
<ul>
<li>카드에는 &quot;재료 손질 → 볶기 → 간 맞추기&quot; 같은 <strong>순서가 미리 적혀</strong> 있습니다.</li>
<li>필요할 때 <strong>내가 카드를 꺼내서</strong> 재료(입력값)만 넣고 시작하면 됩니다.</li>
<li>매번 순서를 처음부터 생각할 필요가 없습니다.</li>
</ul>
<p>프로시저가 바로 이렇습니다. 여러 SQL을 <strong>하나의 이름으로 묶어서 저장</strong>해두고, 필요할 때 이름을 불러 실행합니다.</p>
<p>지난 포스트의 트리거와 비교하면 차이가 선명해집니다.</p>
<table>
<thead>
<tr>
<th>구분</th>
<th>비유</th>
<th>실행 방식</th>
</tr>
</thead>
<tbody><tr>
<td>트리거 (Trigger)</td>
<td>사람이 다가오면 저절로 켜지는 <strong>센서등</strong></td>
<td>데이터가 바뀌면 <strong>자동</strong> 실행</td>
</tr>
<tr>
<td>프로시저 (Procedure)</td>
<td>내가 꺼내 쓰는 <strong>레시피 카드</strong></td>
<td>내가 <code>CALL</code>로 <strong>직접</strong> 실행</td>
</tr>
</tbody></table>
<blockquote>
<p><strong>프로시저 = 데이터베이스 안에 미리 저장해둔 SQL 묶음. 필요할 때 <code>CALL</code>로 호출해서 실행한다.</strong></p>
</blockquote>
<hr>
<h2 id="2-프로시저를-쓰면-뭐가-좋을까">2. 프로시저를 쓰면 뭐가 좋을까?</h2>
<h3 id="①-로직을-재사용할-수-있습니다">① 로직을 재사용할 수 있습니다</h3>
<p>복잡한 쿼리 조합이나 반복되는 비즈니스 로직을 데이터베이스에 저장해두면, 자바나 파이썬 같은 애플리케이션에서 <strong>이름만 불러서</strong> 사용할 수 있습니다. 같은 로직을 여러 곳에 복사해 둘 필요가 없습니다.</p>
<h3 id="②-네트워크-통신이-줄어듭니다">② 네트워크 통신이 줄어듭니다</h3>
<p>쿼리 세 개를 실행하려면 클라이언트와 서버가 세 번 오가야 합니다. 프로시저로 묶으면 <strong>한 번의 호출</strong>로 서버 안에서 일괄 처리됩니다.</p>
<h3 id="③-보안을-강화할-수-있습니다">③ 보안을 강화할 수 있습니다</h3>
<p>테이블에 직접 접근할 권한은 주지 않고, <strong>프로시저를 통해서만</strong> 데이터를 조작하도록 권한을 제어할 수 있습니다.</p>
<h3 id="④-트랜잭션을-직접-제어할-수-있습니다">④ 트랜잭션을 직접 제어할 수 있습니다</h3>
<p>PostgreSQL의 일반 함수(<code>FUNCTION</code>)는 내부에서 <code>COMMIT</code>/<code>ROLLBACK</code>을 하기 어렵지만, 프로시저는 <strong>내부에서 트랜잭션을 명시적으로 관리</strong>할 수 있습니다. (프로시저는 PostgreSQL 11부터 지원됩니다.)</p>
<hr>
<h2 id="3-이번-실습-시나리오">3. 이번 실습 시나리오</h2>
<blockquote>
<p><strong>accounts 테이블의 잔액을 충전하고, 그 내역을 charge_logs 테이블에 함께 기록하는 프로시저를 만든다.</strong></p>
</blockquote>
<p>&quot;잔액 증가&quot;와 &quot;이력 기록&quot;은 <strong>반드시 함께 이루어져야 하는 작업</strong>입니다. 잔액만 늘고 이력이 없거나, 이력만 있고 잔액이 그대로라면 데이터가 어긋나기 때문입니다. 이런 작업을 하나로 묶기에 프로시저는 좋은 예제입니다.</p>
<p>실습 환경은 지난 포스트와 같습니다.</p>
<ul>
<li>DB: PostgreSQL</li>
<li>도구: DBeaver</li>
</ul>
<hr>
<h2 id="4-실습-시작">4. 실습 시작</h2>
<h3 id="step-1-테이블-만들고-데이터-넣기">Step 1. 테이블 만들고 데이터 넣기</h3>
<pre><code class="language-sql">-- 1. 사용자 계좌 테이블
CREATE TABLE accounts (
    user_id SERIAL PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    balance INT DEFAULT 0
);

-- 2. 충전 이력 테이블
CREATE TABLE charge_logs (
    log_id SERIAL PRIMARY KEY,
    user_id INT,
    amount INT,
    charged_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 3. 테스트용 데이터 삽입
INSERT INTO accounts (username, balance) VALUES (&#39;bob&#39;, 10000);</code></pre>
<p><code>accounts</code>에는 잔액 10,000원인 bob이 들어 있고, <code>charge_logs</code>는 비어 있는 상태입니다.</p>
<h3 id="step-2-프로시저-만들기">Step 2. 프로시저 만들기</h3>
<p><code>CREATE PROCEDURE</code>로 만듭니다. 트리거 함수와 달리 <strong>입력값(파라미터)</strong> 을 받을 수 있다는 점이 눈에 띕니다.</p>
<pre><code class="language-sql">CREATE OR REPLACE PROCEDURE sp_charge_balance(
    p_user_id INT,
    p_amount INT
)
LANGUAGE plpgsql
AS $$
BEGIN
    -- 1. 유효성 검사 (충전 금액이 0보다 커야 함)
    IF p_amount &lt;= 0 THEN
        RAISE EXCEPTION &#39;충전 금액은 0보다 커야 합니다.&#39;;
    END IF;

    -- 2. 사용자 잔액 업데이트
    UPDATE accounts
    SET balance = balance + p_amount
    WHERE user_id = p_user_id;

    -- 3. 충전 이력 로그 남기기
    INSERT INTO charge_logs (user_id, amount)
    VALUES (p_user_id, p_amount);

    -- 4. 성공 시 트랜잭션 확정
    COMMIT;

    RAISE NOTICE &#39;사용자(ID: %) 포인트 %원 충전 완료!&#39;, p_user_id, p_amount;
END;
$$;</code></pre>
<p>한 줄씩 풀어보겠습니다.</p>
<table>
<thead>
<tr>
<th>코드</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td><code>CREATE OR REPLACE PROCEDURE sp_charge_balance(...)</code></td>
<td><code>sp_charge_balance</code>라는 이름의 프로시저를 만듭니다. 이미 있으면 덮어씁니다. (<code>sp_</code>는 stored procedure의 약자로 흔히 붙이는 접두어입니다.)</td>
</tr>
<tr>
<td><code>p_user_id INT, p_amount INT</code></td>
<td>호출할 때 전달받을 <strong>입력값</strong>입니다. (<code>p_</code>는 parameter의 약자입니다.)</td>
</tr>
<tr>
<td><code>LANGUAGE plpgsql</code></td>
<td>PostgreSQL의 프로그래밍 언어인 PL/pgSQL로 작성했다는 뜻입니다.</td>
</tr>
<tr>
<td><code>AS $$ ... $$</code></td>
<td>본문을 <code>$$</code>로 감쌉니다. 안에 <code>;</code>가 많아도 하나의 덩어리로 다루기 위한 표기입니다.</td>
</tr>
<tr>
<td><code>IF p_amount &lt;= 0 THEN RAISE EXCEPTION ...</code></td>
<td>충전 금액이 0 이하면 <strong>에러를 일으키고 작업을 중단</strong>합니다.</td>
</tr>
<tr>
<td><code>UPDATE accounts SET balance = balance + p_amount ...</code></td>
<td>해당 사용자의 잔액을 충전 금액만큼 늘립니다.</td>
</tr>
<tr>
<td><code>INSERT INTO charge_logs ...</code></td>
<td>충전 이력을 한 줄 기록합니다.</td>
</tr>
<tr>
<td><code>COMMIT</code></td>
<td>지금까지의 작업을 <strong>확정</strong>합니다.</td>
</tr>
<tr>
<td><code>RAISE NOTICE &#39;... % ...&#39;, 값</code></td>
<td>사용자에게 안내 메시지를 출력합니다. <code>%</code>는 뒤에 오는 값이 순서대로 들어갈 자리입니다.</td>
</tr>
</tbody></table>
<blockquote>
<p>💡 <strong>트리거 함수와 한 가지 다른 점</strong>
트리거 함수는 <code>RETURNS TRIGGER</code>로 선언하고 <code>RETURN NEW</code>처럼 값을 반환해야 했지만, 프로시저는 <strong>반환 선언이 없고</strong> <code>RETURN NEW</code> 같은 코드도 필요 없습니다.</p>
</blockquote>
<blockquote>
<p>💡 <strong><code>COMMIT</code>을 꼭 써야 할까?</strong>
이 예제는 한 번의 <code>CALL</code>로 끝나기 때문에, DBeaver처럼 자동 커밋(auto-commit) 모드에서는 <code>COMMIT</code>을 쓰지 않아도 결과가 저장됩니다. 여기서는 &quot;프로시저 안에서 트랜잭션을 직접 제어할 수 있다&quot;는 점을 보여주기 위해 넣었습니다.</p>
</blockquote>
<h3 id="step-3-프로시저-호출하기">Step 3. 프로시저 호출하기</h3>
<p>함수는 <code>SELECT</code>로 호출하지만, 프로시저는 <strong><code>CALL</code></strong> 키워드로 실행합니다.</p>
<pre><code class="language-sql">-- bob(user_id = 1)에게 5000원 충전하기
CALL sp_charge_balance(1, 5000);</code></pre>
<p>한 줄이면 끝입니다. 잔액 증가와 이력 기록이라는 두 가지 작업이 <strong>한 번의 호출</strong>로 처리됩니다.</p>
<hr>
<h2 id="5-정말-동작하는지-테스트해보기">5. 정말 동작하는지 테스트해보기</h2>
<h3 id="①-잔액-확인">① 잔액 확인</h3>
<pre><code class="language-sql">SELECT * FROM accounts;</code></pre>
<table>
<thead>
<tr>
<th>user_id</th>
<th>username</th>
<th>balance</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>bob</td>
<td>15000</td>
</tr>
</tbody></table>
<p>10,000원이던 잔액이 5,000원이 늘어 <strong>15,000원</strong>이 되었습니다.</p>
<h3 id="②-충전-이력-확인">② 충전 이력 확인</h3>
<pre><code class="language-sql">SELECT * FROM charge_logs;</code></pre>
<table>
<thead>
<tr>
<th>log_id</th>
<th>user_id</th>
<th>amount</th>
<th>charged_at</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>1</td>
<td>5000</td>
<td>(실행한 시각)</td>
</tr>
</tbody></table>
<p>우리가 <code>charge_logs</code>에는 직접 <code>INSERT</code>하지 않았는데도 5,000원 충전 이력이 기록되어 있습니다.</p>
<h3 id="③-안내-메시지는-어디에">③ 안내 메시지는 어디에?</h3>
<p><code>RAISE NOTICE</code>로 출력한 메시지(<code>사용자(ID: 1) 포인트 5000원 충전 완료!</code>)는 쿼리 결과 표가 아니라 <strong>서버 메시지(출력) 창</strong>에 표시됩니다. 도구마다 보이는 위치가 다르니, 메시지가 안 보인다면 출력 창이나 메시지 탭을 확인해보시면 됩니다.</p>
<hr>
<h2 id="6-조금-더-만져보기">6. 조금 더 만져보기</h2>
<h3 id="실험-①-잘못된-금액을-넣으면">실험 ① 잘못된 금액을 넣으면?</h3>
<pre><code class="language-sql">CALL sp_charge_balance(1, -100);</code></pre>
<p>프로시저 안의 유효성 검사에 걸려서 아래와 같은 에러가 발생합니다.</p>
<pre><code>ERROR: 충전 금액은 0보다 커야 합니다.</code></pre><p>이때 <code>UPDATE</code>와 <code>INSERT</code>까지 도달하지 못하고 중단되므로, <code>accounts</code>와 <code>charge_logs</code> 모두 <strong>아무것도 바뀌지 않습니다.</strong> 잘못된 입력을 안쪽에서 막아주는 역할입니다.</p>
<h3 id="실험-②-존재하지-않는-사용자에게-충전하면">실험 ② 존재하지 않는 사용자에게 충전하면?</h3>
<pre><code class="language-sql">CALL sp_charge_balance(999, 3000);</code></pre>
<p>이 예제는 조금 아쉬운 점이 있습니다. <code>user_id = 999</code>인 계좌는 없으므로 <code>UPDATE</code>가 영향을 주는 행은 <strong>0개</strong>인데, PostgreSQL은 이를 에러로 보지 않습니다. 그래서 그대로 다음 줄로 넘어가 <strong><code>charge_logs</code>에는 존재하지 않는 사용자의 충전 이력이 쌓일 수 있습니다.</strong> (<code>charge_logs</code>에 외래 키 제약을 걸어두지 않았기 때문입니다.)</p>
<p>이럴 때는 <code>UPDATE</code> 직후에 <code>FOUND</code>로 확인하는 코드를 추가하면 됩니다.</p>
<pre><code class="language-sql">CREATE OR REPLACE PROCEDURE sp_charge_balance(
    p_user_id INT,
    p_amount INT
)
LANGUAGE plpgsql
AS $$
BEGIN
    IF p_amount &lt;= 0 THEN
        RAISE EXCEPTION &#39;충전 금액은 0보다 커야 합니다.&#39;;
    END IF;

    UPDATE accounts
    SET balance = balance + p_amount
    WHERE user_id = p_user_id;

    -- 수정된 행이 없으면 존재하지 않는 사용자
    IF NOT FOUND THEN
        RAISE EXCEPTION &#39;존재하지 않는 사용자입니다. (ID: %)&#39;, p_user_id;
    END IF;

    INSERT INTO charge_logs (user_id, amount)
    VALUES (p_user_id, p_amount);

    RAISE NOTICE &#39;사용자(ID: %) 포인트 %원 충전 완료!&#39;, p_user_id, p_amount;
END;
$$;</code></pre>
<p><code>FOUND</code>는 직전에 실행한 <code>UPDATE</code>가 한 행이라도 수정했으면 <code>TRUE</code>, 아니면 <code>FALSE</code>가 되는 PL/pgSQL의 특수 변수입니다. 예제를 한 번 직접 돌려보니 <strong>&quot;에러가 안 난다고 정상은 아니다&quot;</strong> 라는 점을 확인할 수 있었습니다.</p>
<h3 id="실험-③-내가-만든-프로시저-확인하고-지우기">실험 ③ 내가 만든 프로시저 확인하고 지우기</h3>
<pre><code class="language-sql">-- 만들어둔 프로시저 목록 확인
SELECT proname
FROM pg_proc
WHERE prokind = &#39;p&#39;;

-- 프로시저 삭제 (파라미터 타입까지 적어줍니다)
DROP PROCEDURE IF EXISTS sp_charge_balance(INT, INT);</code></pre>
<p>프로시저를 삭제할 때는 이름만 쓰지 않고 <strong>파라미터 타입</strong>까지 함께 적어야 합니다. 같은 이름에 파라미터가 다른 프로시저를 만들 수 있기 때문입니다.</p>
<p>처음부터 다시 해보고 싶다면 아래처럼 정리하고 시작하면 편합니다.</p>
<pre><code class="language-sql">DROP PROCEDURE IF EXISTS sp_charge_balance(INT, INT);
DROP TABLE IF EXISTS charge_logs;
DROP TABLE IF EXISTS accounts;</code></pre>
<hr>
<h2 id="7-알아두면-좋은-점--commit은-아무-데서나-되는-게-아닙니다">7. 알아두면 좋은 점 — <code>COMMIT</code>은 아무 데서나 되는 게 아닙니다</h2>
<p>프로시저 안에서 <code>COMMIT</code>을 쓸 수 있다는 점이 장점이라고 했지만, <strong>이미 열려 있는 트랜잭션 안에서 <code>CALL</code>하면</strong> <code>COMMIT</code>이 막힐 수 있습니다.</p>
<pre><code class="language-sql">BEGIN;                                -- 트랜잭션 직접 시작
CALL sp_charge_balance(1, 5000);      -- 프로시저 안의 COMMIT에서 에러 가능</code></pre>
<p>이런 경우 <code>invalid transaction termination</code> 같은 에러가 발생할 수 있습니다. 프로시저 안에서 트랜잭션을 끝내려면, 그 프로시저가 <strong>바깥 트랜잭션에 갇혀 있지 않은 상태에서 호출</strong>되어야 하기 때문입니다.</p>
<p>그래서 아래 기준으로 생각하면 편합니다.</p>
<ul>
<li>프로시저 하나로 모든 처리가 끝나는 경우 → 자동 커밋 상태에서 <code>CALL</code>해도 충분하므로 <code>COMMIT</code>은 생략 가능</li>
<li>대량 작업을 <strong>중간중간 나눠서 확정</strong>해야 하는 경우 → 프로시저 안의 <code>COMMIT</code>이 진가를 발휘</li>
</ul>
<hr>
<h2 id="8-트리거-vs-프로시저-vs-함수">8. 트리거 vs 프로시저 vs 함수</h2>
<p>지난 포스트의 트리거까지 포함해 한 번에 비교해보겠습니다.</p>
<table>
<thead>
<tr>
<th>구분</th>
<th>트리거 (Trigger)</th>
<th>프로시저 (Procedure)</th>
<th>함수 (Function)</th>
</tr>
</thead>
<tbody><tr>
<td>실행 방식</td>
<td>데이터 변경 시 <strong>자동</strong></td>
<td><code>CALL</code>로 <strong>직접</strong></td>
<td><code>SELECT</code> 등으로 <strong>직접</strong></td>
</tr>
<tr>
<td>주요 용도</td>
<td>무결성 검증, 자동 로그 기록, 값 자동 보정</td>
<td>복잡한 비즈니스 로직 일괄 처리, 배치 작업</td>
<td>값을 계산해서 돌려줄 때</td>
</tr>
<tr>
<td>트랜잭션 제어</td>
<td>불가 (부모 트랜잭션에 종속)</td>
<td><strong>가능</strong> (<code>COMMIT</code>, <code>ROLLBACK</code>)</td>
<td>어려움</td>
</tr>
</tbody></table>
<p>한 줄로 정리하자면 이렇습니다.</p>
<blockquote>
<p><strong>&quot;알아서 실행돼야 하면 트리거, 내가 부를 땐 프로시저, 값을 돌려받고 싶다면 함수.&quot;</strong></p>
</blockquote>
<hr>
<h2 id="9-프로시저-사용-시-주의할-점">9. 프로시저 사용 시 주의할 점</h2>
<ul>
<li><strong>로직이 데이터베이스에 숨어 있습니다.</strong> 애플리케이션 코드만 보면 내부에서 무슨 일이 벌어지는지 알 수 없어서, 프로시저의 내용을 별도로 문서화하고 버전 관리하는 것이 좋습니다.</li>
<li><strong>디버깅과 테스트가 상대적으로 불편합니다.</strong> 애플리케이션 코드처럼 중단점을 걸고 확인하기 어려워서, <code>RAISE NOTICE</code>로 값을 출력해가며 확인하는 경우가 많습니다.</li>
<li><strong>비즈니스 로직이 너무 많이 들어가면 곤란합니다.</strong> 데이터베이스에 로직이 쏠리면 변경과 배포가 어려워질 수 있어서, &quot;데이터와 가까이 있어야 효율적인 작업&quot;에 한정하는 편이 좋습니다.</li>
</ul>
<hr>
<h2 id="마무리">마무리</h2>
<p>정리하고 나니 프로시저의 구조가 트리거보다도 단순하게 느껴졌습니다.</p>
<ul>
<li>프로시저는 <strong>&quot;만들기(<code>CREATE PROCEDURE</code>) → 호출하기(<code>CALL</code>)&quot;</strong> 두 단계로 구성됩니다.</li>
<li>트리거와 달리 <strong>입력값을 받을 수 있고</strong>, 내가 원할 때 직접 실행합니다.</li>
<li>여러 SQL을 <strong>하나로 묶어서</strong> 한 번의 호출로 처리할 수 있습니다.</li>
<li>함수와 달리 프로시저 안에서 <strong><code>COMMIT</code>/<code>ROLLBACK</code>을 직접 제어</strong>할 수 있습니다.</li>
</ul>
<p>특히 <code>user_id = 999</code>처럼 존재하지 않는 사용자에게 충전했을 때 <strong>에러 없이 이력만 쌓일 수 있다</strong>는 점이 가장 기억에 남았습니다. 프로시저는 &quot;돌아가기만 하면 된다&quot;가 아니라 <strong>예외 상황까지 안쪽에서 막아줘야</strong> 비로소 믿을 수 있다는 생각이 들었습니다.</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[2차 프로젝트 보완] 빈자리 알림 (2) - Kafka 개념 정리: 메시지 큐부터 파티션, 오프셋, 컨슈머 그룹까지]]></title>
            <link>https://velog.io/@anton061311/2%EC%B0%A8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%B3%B4%EC%99%84-%EB%B9%88%EC%9E%90%EB%A6%AC-%EC%95%8C%EB%A6%BC-2-Kafka-%ED%95%B5%EC%8B%AC-%EC%9A%A9%EC%96%B4</link>
            <guid>https://velog.io/@anton061311/2%EC%B0%A8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%B3%B4%EC%99%84-%EB%B9%88%EC%9E%90%EB%A6%AC-%EC%95%8C%EB%A6%BC-2-Kafka-%ED%95%B5%EC%8B%AC-%EC%9A%A9%EC%96%B4</guid>
            <pubDate>Fri, 16 Oct 2026 03:39:35 GMT</pubDate>
            <description><![CDATA[<h1 id="2차-프로젝트-보완-빈자리-알림-2---kafka-핵심-개념">[2차 프로젝트 보완] 빈자리 알림 (2) - Kafka 핵심 개념</h1>
<p>지난 편에서는 이벤트 기반 아키텍처가 무엇이고 왜 쓰는지를 정리했다. 이번 편에서는 그 구조에서 Broker 역할을 맡는 Kafka를 다룬다. Slot Key의 빈자리 알림은 Kafka의 <code>slot-released</code> 토픽을 통해 흐르는데, 이 구조를 설명하려면 메시지 큐부터 파티션, 오프셋, 컨슈머 그룹까지 용어를 먼저 잡아야 한다. 각 단계마다 Slot Key에서 실제로 쓴 값을 예시로 붙였다.</p>
<h2 id="💡-한-줄-요약">💡 한 줄 요약</h2>
<blockquote>
<p>Kafka는 이벤트를 토픽이라는 이름의 로그에 순서대로 쌓아 두고, 컨슈머가 자기 속도에 맞춰 읽어 가게 하는 분산 이벤트 스트리밍 플랫폼이다.</p>
</blockquote>
<h2 id="🚧-기존-방식의-한계">🚧 기존 방식의 한계</h2>
<h3 id="🕸️-서비스가-직접-연결되면-연결선이-곱으로-늘어난다">🕸️ 서비스가 직접 연결되면 연결선이 곱으로 늘어난다</h3>
<p>데이터를 주고받는 시스템이 늘어나면 보내는 쪽과 받는 쪽을 일대일로 이어 붙이게 된다. 이를 end-to-end 연결이라고 부른다. 이 구조에서는 다음 문제가 생긴다.</p>
<ul>
<li><strong>파이프라인이 파편화된다.</strong> 연결마다 각자 파이프라인이 생겨서 전체 데이터 흐름을 한눈에 볼 수 없다.</li>
<li><strong>복잡도가 폭발한다.</strong> 보내는 쪽이 N개, 받는 쪽이 M개이면 연결은 N × M개가 된다.</li>
<li><strong>관리가 어렵다.</strong> 연결마다 프로토콜, 포맷, 장애 처리 방식이 다르다.</li>
<li><strong>데이터 불일치가 생긴다.</strong> 같은 사건을 여러 곳에 따로 전달하다 보니 어떤 곳은 반영되고 어떤 곳은 빠질 수 있다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/anton061311/post/409b2cc1-31d9-426e-8f71-47300b90bca9/image.png" alt=""></p>
<p>Slot Key의 빈자리 알림으로 보면 이렇다. 자리가 비는 계기는 예약 취소, 강제 취소, 홀드 만료로 3가지다. 이 사건을 받아서 처리할 곳이 알림 저장, 감사 로그, 통계라면 직접 연결은 3 × 3 = 9개가 된다. 감사 로그와 통계는 확장을 가정한 예시이고, 현재 구현된 것은 알림 저장뿐이다.</p>
<h3 id="🔗-동기-호출은-본업에-후속-처리를-얹는다">🔗 동기 호출은 본업에 후속 처리를 얹는다</h3>
<p><img src="https://velog.velcdn.com/images/anton061311/post/7cc435a4-d53c-454b-915b-41330e2b99a9/image.png" alt=""></p>
<p>더 현실적인 문제는 코드 안에서 드러난다. 예약 취소 API 안에서 대기자를 조회하고 알림을 저장하고 메일과 SSE까지 직접 호출하면, 취소라는 본업 트랜잭션에 후속 처리가 전부 묶인다. 알림 처리가 느려지거나 실패하면 취소 자체도 영향을 받는다. 후속 기능이 하나 늘 때마다 취소 코드를 고쳐야 하는 점도 같은 뿌리의 문제다.</p>
<!-- 그림 삽입: fig2-sync-call.png (취소 API 하나에 본업과 덧붙은 후속 처리가 한 트랜잭션으로 묶인 모습) -->

<h2 id="🤔-왜-필요한가">🤔 왜 필요한가</h2>
<h3 id="📬-메시지-큐mq">📬 메시지 큐(MQ)</h3>
<p>이 문제를 푸는 가장 기본적인 방법이 메시지 큐다. 보내는 쪽과 받는 쪽 사이에 큐를 두고, 둘이 서로 직접 통신하지 않게 한다.</p>
<p><img src="https://velog.velcdn.com/images/anton061311/post/911f7642-9696-4295-883f-3db135f01f73/image.png" alt=""></p>
<p>용어를 정리하면 다음과 같다.</p>
<table>
<thead>
<tr>
<th>용어</th>
<th>의미</th>
<th>Slot Key 예시</th>
</tr>
</thead>
<tbody><tr>
<td>📤 Producer</td>
<td>메시지를 보내는 쪽</td>
<td>예약 서비스 (취소, 강제 취소, 홀드 만료 때 발행)</td>
</tr>
<tr>
<td>📨 Message / Event</td>
<td>전달하려는 정보</td>
<td><code>SlotReleased</code> 이벤트</td>
</tr>
<tr>
<td>📮 Broker</td>
<td>메시지를 받아 저장하고 전달하는 중간자</td>
<td>Kafka</td>
</tr>
<tr>
<td>📥 Consumer</td>
<td>메시지를 가져다 처리하는 쪽</td>
<td>알림 컨슈머</td>
</tr>
</tbody></table>
<p>MQ를 두면 얻는 이점은 다음과 같다.</p>
<ul>
<li>✅ <strong>비동기 처리.</strong> Producer는 보내고 바로 다음 일을 한다. 예약 취소는 알림이 끝나길 기다리지 않는다.</li>
<li>✅ <strong>결합도 감소.</strong> Producer는 누가 받는지 모른다. 알림 로직이 바뀌어도 예약 쪽은 영향이 없다.</li>
<li>✅ <strong>장애 격리.</strong> Consumer가 잠시 죽어도 메시지는 Broker에 남아 있고, 복구된 뒤에 처리할 수 있다.</li>
<li>✅ <strong>부하 완충.</strong> 요청이 몰려도 Broker가 쌓아 두고 Consumer가 처리 가능한 속도로 가져간다.</li>
</ul>
<p>다만 MQ가 메시지 전달을 절대 보장하는 것은 아니다. Producer가 보낸 메시지가 사라지지 않게 하려면 설정(Kafka의 <code>acks</code> 등)과 재시도 정책이 따로 필요하다. 이 부분은 아래 주의할 점에서 다시 다룬다.</p>
<h3 id="📢-pubsub-모델">📢 Pub/Sub 모델</h3>
<p>1편에서 본 Pub/Sub 모델로 보면 이점이 더 분명하다. 게시판(Topic)을 가운데 두면 연결선은 보내는 쪽 3개 + 받는 쪽 3개 = 6개가 된다. 곱셈이 덧셈으로 바뀐 것이다. 받는 곳이 하나 늘어도 보내는 쪽 코드는 그대로다.</p>
<p><img src="https://velog.velcdn.com/images/anton061311/post/835c760e-db7f-43e4-9b5a-311e866f0b7c/image.png" alt=""></p>
<h3 id="🆚-메시지-브로커와-이벤트-브로커">🆚 메시지 브로커와 이벤트 브로커</h3>
<p>둘은 비슷해 보이지만 메시지를 다루는 방식이 다르다.</p>
<table>
<thead>
<tr>
<th>구분</th>
<th>메시지 브로커</th>
<th>이벤트 브로커</th>
</tr>
</thead>
<tbody><tr>
<td>처리 후</td>
<td>소비되면 큐에서 사라지는 모델이 일반적이다</td>
<td>보존 기간 동안 남겨 둔다</td>
</tr>
<tr>
<td>다시 읽기</td>
<td>어렵다</td>
<td>원하는 지점부터 다시 소비할 수 있다</td>
</tr>
<tr>
<td>처리량</td>
<td>상대적으로 작은 규모에 적합하다</td>
<td>대용량 이벤트 처리에 적합하다</td>
</tr>
<tr>
<td>예시</td>
<td>RabbitMQ 등</td>
<td>Kafka, Kinesis</td>
</tr>
</tbody></table>
<p>메시지 브로커가 &quot;읽으면 사라지는 우편함&quot;이라면, 이벤트 브로커는 &quot;읽어도 남아 있는 기록 장부&quot;에 가깝다. 소비되면 삭제되는 것은 일반적인 모델을 단순화한 설명이고, 제품과 설정에 따라 다르다. Kafka가 기록 장부형에 속한다.</p>
<h2 id="⚙️-동작-방식-및-특징">⚙️ 동작 방식 및 특징</h2>
<h3 id="✨-특징">✨ 특징</h3>
<p>Kafka를 소개할 때 자주 나오는 특징은 네 가지다. 각각이 무엇을 뜻하는지 풀어 쓰면 다음과 같다.</p>
<ul>
<li>⭐ <strong>분산.</strong> 여러 Broker가 클러스터를 이루고, 토픽을 파티션으로 쪼개 여러 Broker에 나눠 저장한다. 서버 한 대의 한계에 갇히지 않고, 한 대가 죽어도 나머지가 이어받는다.</li>
<li>⭐ <strong>실시간.</strong> 이벤트가 들어오는 즉시 컨슈머가 읽어 갈 수 있다. 배치로 모아서 처리하지 않아도 되어 지연이 짧다.</li>
<li>⭐ <strong>내구성.</strong> 이벤트를 디스크에 순차적으로 기록하고, 복제본을 다른 Broker에 둔다. 메모리에만 있다가 사라지는 구조가 아니다.</li>
<li>⭐ <strong>높은 처리량.</strong> 디스크에 덧붙이기만 하는 순차 쓰기와 파티션 단위 병렬 처리로 대량의 이벤트를 다룬다.</li>
</ul>
<p>Slot Key에서는 이 중 일부만 쓰고 있다. 로컬 개발 환경은 Docker의 <code>apache/kafka:3.8.0</code> KRaft 단일 노드 한 대라서, 분산과 복제의 이점은 실제로 검증하지 못했다. 빈자리 알림은 이벤트 양이 크지 않아서 처리량보다는 &quot;알림 로직을 예약 취소에서 분리하고, 컨슈머가 잠시 죽어도 이벤트가 남는다&quot;는 점이 선택의 이유였다. 이 한계는 뒤에서 다시 적는다.</p>
<h3 id="🧩-구성-요소">🧩 구성 요소</h3>
<p>Kafka를 이해하려면 먼저 구성 요소와 데이터의 흐름을 알아야 한다. Kafka는 기본적으로 프로듀서, 컨슈머, 브로커, 토픽, 파티션으로 이루어진다.</p>
<ul>
<li><strong>프로듀서(Producer).</strong> 데이터를 토픽에 전송한다.</li>
<li><strong>컨슈머(Consumer).</strong> 토픽에서 데이터를 읽는다.</li>
<li><strong>토픽(Topic).</strong> 데이터를 구분하는 채널이다.</li>
<li><strong>파티션(Partition).</strong> 토픽을 여러 조각으로 나눠 저장하는 단위다.</li>
<li><strong>브로커(Broker).</strong> Kafka 클러스터의 서버로, 데이터를 저장하고 관리한다.</li>
</ul>
<p>여기에 오프셋, 키, 컨슈머 그룹을 더하면 Slot Key에 대응시킬 수 있다.</p>
<table>
<thead>
<tr>
<th>구성 요소</th>
<th>설명</th>
<th>Slot Key 예시</th>
</tr>
</thead>
<tbody><tr>
<td>🗂️ Topic</td>
<td>이벤트를 종류별로 모아 두는 이름 붙은 로그</td>
<td><code>slot-released</code></td>
</tr>
<tr>
<td>📤 Producer</td>
<td>토픽에 이벤트를 발행한다</td>
<td>예약 서비스 (Outbox relay가 발행)</td>
</tr>
<tr>
<td>📥 Consumer</td>
<td>토픽에서 이벤트를 읽어 처리한다</td>
<td>알림 컨슈머</td>
</tr>
<tr>
<td>👥 Consumer Group</td>
<td>같은 목적으로 함께 읽는 컨슈머 묶음. 그룹마다 독립적으로 읽는다</td>
<td><code>slotkey-waitlist-notifier</code></td>
</tr>
<tr>
<td>🧱 Partition</td>
<td>토픽을 나눈 조각. 각 조각 안에서는 순서가 유지된다</td>
<td>같은 spaceId의 이벤트가 같은 파티션에 모인다</td>
</tr>
<tr>
<td>🔖 Offset</td>
<td>파티션 안에서 이벤트의 위치 번호</td>
<td>컨슈머가 어디까지 읽었는지 기록하는 기준</td>
</tr>
<tr>
<td>🔑 Key</td>
<td>어느 파티션에 넣을지 정하는 값</td>
<td><code>spaceId</code></td>
</tr>
<tr>
<td>📮 Broker</td>
<td>이벤트를 저장하고 전달하는 Kafka 서버</td>
<td>로컬 Docker 1대</td>
</tr>
</tbody></table>
<p>Slot Key 이벤트 하나는 이렇게 생각하면 된다. 어떤 공간(spaceId)의 슬롯이 비면 <code>SlotReleased</code> 이벤트가 <code>slot-released</code> 토픽에 key=spaceId로 발행된다. 같은 공간에서 일어난 취소와 만료는 같은 파티션에 순서대로 쌓이고, 알림 컨슈머 그룹이 그 순서대로 읽어 대기자에게 보낼 알림을 저장한다.</p>
<h3 id="🔄-동작-원리">🔄 동작 원리</h3>
<p>Kafka를 도입한 뒤의 전체 구조는 아래와 같다. 예약 서비스 안의 세 가지 계기가 모두 같은 토픽으로 발행되고, 알림 컨슈머가 구독한다. 점선의 감사, 통계 컨슈머는 새 그룹만 붙이면 되는 확장 예시다.</p>
<p><img src="https://velog.velcdn.com/images/anton061311/post/ab7149d4-a848-468f-98f7-e3b696ad382e/image.png" alt=""></p>
<p>Kafka의 동작은 크게 프로듀서 → 브로커 → 컨슈머의 흐름이다. 이를 4단계로 나눠서 본다.</p>
<p><img src="https://velog.velcdn.com/images/anton061311/post/2346eb6a-3202-4a95-9a85-84dbb684c4df/image.png" alt=""></p>
<h4 id="1단계-메시지-전송-프로듀서-→-토픽">1단계. 메시지 전송 (프로듀서 → 토픽)</h4>
<p>프로듀서는 특정 토픽에 메시지를 보낸다. 이때 메시지에는 key를 붙일 수 있다. 어느 파티션에 들어갈지는 이 key가 정한다.</p>
<p>Slot Key에서는 예약 서비스가 <code>slot-released</code> 토픽에 <code>SlotReleased</code> 이벤트를 보낸다. key는 <code>spaceId</code>이고, <code>acks: all</code>로 설정해서 복제본에 기록된 것까지 확인한 뒤에야 전송이 성공했다고 본다. 예약 서비스가 Kafka로 바로 보내는 것이 아니라 Outbox relay가 대신 발행하는데, 그 이유는 3편에서 다룬다.</p>
<h4 id="2단계-파티션에-메시지-저장">2단계. 파티션에 메시지 저장</h4>
<p>Kafka는 메시지를 어떤 파티션에 보낼지 결정하고, 그 파티션에 순차적으로 저장한다. 분배 규칙은 key 유무에 따라 다르다.</p>
<table>
<thead>
<tr>
<th>경우</th>
<th>파티션 결정 방식</th>
<th>특징</th>
</tr>
</thead>
<tbody><tr>
<td>🔑 key가 있을 때</td>
<td>key를 해시해서 파티션 번호를 정한다</td>
<td>같은 key는 파티션 수가 바뀌지 않는 동안 항상 같은 파티션에 들어간다</td>
</tr>
<tr>
<td>🎲 key가 없을 때</td>
<td>라운드 로빈이나 스티키 방식으로 고르게 분배한다 (Kafka 버전에 따라 다르다)</td>
<td>순서가 필요 없는 이벤트를 고르게 나눌 때 쓴다</td>
</tr>
</tbody></table>
<p>메시지는 로그 형태로 저장된다. 파티션은 기록을 끝에 계속 덧붙이는(append-only) 방식이라, 한 번 쓴 메시지를 수정하지 않는다. 읽어도 삭제하지 않고 남겨 두며, 보존 기간(retention)이 지나면 오래된 것부터 정리한다. &quot;계속 보존한다&quot;가 아니라 설정한 기간 동안 보존한다는 점을 기억해야 한다.</p>
<p>Slot Key에서는 key가 <code>spaceId</code>라서 같은 공간에서 일어난 취소와 만료가 같은 파티션에 offset 순서대로 쌓인다. 예를 들어 spaceId=1에서 취소 → 홀드 만료 → 취소가 연달아 일어나면 파티션에 0, 1, 2번 offset으로 쌓이고, 컨슈머는 이 순서대로 읽는다. 다른 공간(spaceId=2)의 이벤트는 다른 파티션에 있을 수 있어서 공간끼리의 순서는 보장하지 않는다. 공간 안의 순서만 필요하기 때문에 key를 <code>spaceId</code>로 한 것이다. 그림의 파티션 2개는 설명을 위해 가정한 값이고, 실제 파티션 수는 문서에 따로 정해 두지 않았다.</p>
<h4 id="3단계-브로커에-파티션-저장">3단계. 브로커에 파티션 저장</h4>
<p>각 파티션은 브로커에 저장되고 관리된다. 운영 환경에서는 파티션마다 복제본을 만들어 다른 브로커에 둔다. 원본 역할을 하는 leader가 읽기와 쓰기를 받고, 복제본인 follower가 leader를 따라가며 같은 내용을 복사한다. leader가 있는 브로커가 죽으면 follower 중 하나가 새 leader가 되어 이어간다. 이것이 Kafka의 내구성과 가용성의 바탕이다.</p>
<p>Producer의 <code>acks</code> 설정이 이 복제와 연결된다. <code>acks=0</code>은 확인 없이 보내고, <code>acks=1</code>은 leader가 기록하면 성공으로 보고, <code>acks=all</code>은 복제본까지 기록돼야 성공으로 본다. Slot Key는 <code>acks: all</code>을 썼다.</p>
<p>다만 Slot Key의 로컬 개발 환경은 Docker의 KRaft 단일 노드 Broker 1대라서 복제본이 없다. 복제와 leader 전환은 개념으로 이해한 것이고 직접 검증하지는 못했다.</p>
<h4 id="4단계-컨슈머-처리">4단계. 컨슈머 처리</h4>
<p>컨슈머는 토픽을 구독하고 해당 파티션의 데이터를 소비한다. 컨슈머는 offset, 곧 파티션 안에서 각 메시지의 위치를 기준으로 메시지를 읽는다. 메시지를 읽고 처리한 뒤에는 그 offset을 기록해 두어서, 재시작해도 어디까지 읽었는지 알고 이어서 읽는다. 흔히 &quot;offset으로 중복 읽기를 방지한다&quot;고 설명하지만, 정확히는 읽은 위치를 기억하는 것이다. 커밋 시점에 따라 같은 이벤트를 다시 읽을 수도 있어서, 중복 방지는 별도 설계가 필요하다(아래 주의할 점).</p>
<p>컨슈머 그룹에 대해 한 가지 더 알아야 한다. 같은 그룹 안에서는 한 파티션을 한 컨슈머만 읽는다. 그래서 컨슈머를 파티션 수보다 많이 늘려도 놀게 되는 컨슈머가 생긴다. 컨슈머가 추가되거나 빠지면 파티션을 다시 나눠 맡는 리밸런싱이 일어난다. 반대로 서로 다른 그룹은 같은 토픽을 각자 독립적으로 끝까지 읽는다. 알림 그룹과 감사 그룹이 같은 이벤트를 각각 받을 수 있는 이유다.</p>
<p>Slot Key의 알림 컨슈머는 <code>slotkey-waitlist-notifier</code> 그룹이고, <code>auto-offset-reset: earliest</code>라서 기록된 offset이 없으면 처음부터 읽는다. 이벤트를 읽으면 대기자에게 보낼 알림을 저장하고, <code>processed_event</code>에 처리 기록을 남겨 같은 이벤트가 다시 와도 중복 처리하지 않는다.</p>
<h4 id="🧭-한-건이-흘러가는-전체-시나리오">🧭 한 건이 흘러가는 전체 시나리오</h4>
<p>위 단계를 Slot Key의 실제 상황 하나로 이어 보면 다음과 같다. 숫자는 이해를 돕기 위한 가정이다.</p>
<table>
<thead>
<tr>
<th>순서</th>
<th>일어나는 일</th>
<th>단계</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>예약자가 예약을 취소하고, 같은 트랜잭션에서 이벤트가 Outbox에 기록된다</td>
<td>(3편)</td>
</tr>
<tr>
<td>2</td>
<td>Outbox relay가 <code>SlotReleased</code>를 key=spaceId로 <code>slot-released</code>에 발행한다</td>
<td>1단계</td>
</tr>
<tr>
<td>3</td>
<td>해당 spaceId의 파티션 끝에 offset N으로 쌓인다</td>
<td>2단계</td>
</tr>
<tr>
<td>4</td>
<td>브로커가 저장하고, 운영 환경이면 복제본에 복사한다</td>
<td>3단계</td>
</tr>
<tr>
<td>5</td>
<td>알림 컨슈머가 offset N을 읽고, 대기자 전원에게 보낼 알림을 저장한다</td>
<td>4단계</td>
</tr>
<tr>
<td>6</td>
<td>처리 기록을 남기고 offset을 기록한다</td>
<td>4단계</td>
</tr>
</tbody></table>
<h3 id="👍-장점">👍 장점</h3>
<ul>
<li>✅ <strong>순서 유지.</strong> 같은 key의 이벤트는 같은 파티션에서 순서가 유지된다. 단, 순서는 파티션 안에서만 보장되고 토픽 전체에서는 보장되지 않는다. Slot Key에서는 같은 공간의 취소와 만료가 순서대로 처리된다.</li>
<li>✅ <strong>재처리 가능.</strong> 이벤트가 보존 기간 동안 남아 있어서 offset을 되돌려 다시 읽을 수 있다. 영구 보관을 뜻하지는 않는다. 알림 로직에 버그가 있었다면 고친 뒤 과거 이벤트를 다시 읽어 처리하는 식으로 쓸 수 있다.</li>
<li>✅ <strong>독립적인 소비.</strong> 컨슈머 그룹마다 자기 offset을 가져서, 한 그룹의 처리가 다른 그룹에 영향을 주지 않는다. 감사 로그 컨슈머를 새 그룹으로 추가해도 알림 컨슈머는 그대로다.</li>
<li>✅ <strong>수평 확장.</strong> 파티션을 늘리고 같은 그룹의 컨슈머를 늘려 처리량을 높일 수 있다. 다만 한 그룹 안에서 한 파티션은 한 컨슈머만 읽으므로, 컨슈머 수의 상한은 파티션 수다.</li>
<li>✅ <strong>장애 격리.</strong> 컨슈머가 죽어도 이벤트는 브로커에 남아 있고, 복구 후 기록된 offset부터 이어서 읽는다. 예약 취소는 알림 컨슈머의 상태와 상관없이 성공한다.</li>
</ul>
<h2 id="⏱️-언제-사용하는가">⏱️ 언제 사용하는가</h2>
<ul>
<li>✅ <strong>하나의 사건에 여러 후속 처리가 붙을 때.</strong> 슬롯이 비면 알림 외에도 감사, 통계가 붙을 수 있다.</li>
<li>✅ <strong>후속 처리가 늦게 반영돼도 괜찮을 때.</strong> 빈자리 알림은 몇 초 늦어도 큰 문제가 없다.</li>
<li>✅ <strong>트래픽이 몰릴 수 있을 때.</strong> Broker가 완충 역할을 한다.</li>
<li>✅ <strong>이벤트를 다시 읽을 일이 있을 때.</strong> 장애 복구나 새 컨슈머의 초기 처리에 쓸 수 있다.</li>
<li>✅ <strong>순서가 중요한 단위가 있을 때.</strong> key로 같은 단위의 이벤트를 한 파티션에 모을 수 있다.</li>
</ul>
<h2 id="⚠️-주의할-점">⚠️ 주의할 점</h2>
<p>1️⃣ <strong>전달 보장 수준을 알아야 한다.</strong> 메시지 전달에는 at-most-once, at-least-once, exactly-once가 있다. 보통 at-least-once로 운영하고, 이 경우 같은 이벤트가 두 번 올 수 있다. Slot Key에서도 타임아웃 설정 때문에 중복 이벤트가 실제로 관찰됐다. 그래서 컨슈머를 멱등하게 만들었다. <code>processed_event</code>에 UNIQUE(consumer, event_id)를 두고, 알림 테이블에는 UNIQUE(event_id, waitlist_entry_id)를 둬서 같은 이벤트를 두 번 처리해도 알림이 중복 저장되지 않게 했다.</p>
<p>2️⃣ <strong>offset 커밋 시점에 따라 결과가 달라진다.</strong> 처리 전에 커밋하면 실패 시 이벤트를 잃고, 처리 후에 커밋하면 실패 시 다시 읽어 중복된다. offset이 중복을 막아 주는 것이 아니라 읽은 위치를 기록하는 것일 뿐이다.</p>
<p>3️⃣ <strong>순서는 파티션 안에서만 보장된다.</strong> 파티션이 2개 이상일 때 순서가 어떻게 되는지는 Slot Key에서 검증하지 못했다. 또 파티션 수를 바꾸면 같은 key가 다른 파티션으로 갈 수 있다.</p>
<p>4️⃣ <strong>실패한 이벤트를 어디에 둘지 정해야 한다.</strong> Slot Key는 FixedBackOff 1000ms 간격으로 2번 재시도한 뒤 건너뛰고, Dead Letter Topic(DLT)은 두지 않았다. 건너뛴 이벤트는 따로 복구할 수단이 없다는 뜻이라 한계로 남겼다.</p>
<p>5️⃣ <strong>Kafka의 트랜잭션과 Transactional Outbox는 다르다.</strong> Kafka 트랜잭션은 Kafka 안에서의 원자성이고, DB 저장과 이벤트 발행을 묶어 주지는 않는다. 이 문제를 푸는 것이 3편의 Outbox다.</p>
<h2 id="⚖️-사용할-때와-사용하지-않을-때">⚖️ 사용할 때와 사용하지 않을 때</h2>
<h3 id="🥊-kafka와-redis-비교">🥊 Kafka와 Redis 비교</h3>
<p>실시간 이벤트 처리에는 Redis도 자주 쓰인다. 두 기술을 같은 기준으로 비교하면 다음과 같다. Redis는 Pub/Sub 기능을 기준으로 정리했다.</p>
<table>
<thead>
<tr>
<th>특징</th>
<th>Kafka</th>
<th>Redis (Pub/Sub)</th>
</tr>
</thead>
<tbody><tr>
<td>메시지 보관</td>
<td>디스크에 보존 기간 동안 저장한다</td>
<td>구독자가 없으면 메시지는 사라진다</td>
</tr>
<tr>
<td>다시 읽기</td>
<td>offset으로 원하는 지점부터 가능하다</td>
<td>지난 메시지를 다시 받을 수 없다</td>
</tr>
<tr>
<td>컨슈머가 죽었을 때</td>
<td>복구 후 이어서 읽는다</td>
<td>그 사이 메시지를 놓친다</td>
</tr>
<tr>
<td>순서</td>
<td>파티션 안에서 보장한다</td>
<td>단일 채널 기준으로 발행 순서대로 전달된다</td>
</tr>
<tr>
<td>처리량과 확장</td>
<td>파티션 단위로 확장한다</td>
<td>단일 노드 기준으로 매우 빠르지만 확장 방식이 다르다</td>
</tr>
<tr>
<td>운영 부담</td>
<td>높다</td>
<td>낮다</td>
</tr>
</tbody></table>
<p>Redis에는 Pub/Sub 외에 Redis Streams가 있고, 이쪽은 메시지를 보관하고 컨슈머 그룹도 지원한다. 그래서 위 표의 차이는 Pub/Sub 기준이라는 점을 감안해야 한다.</p>
<table>
<thead>
<tr>
<th>용도</th>
<th>더 어울리는 쪽</th>
</tr>
</thead>
<tbody><tr>
<td>🛡️ 이벤트를 잃으면 안 되고 재처리가 필요한 경우</td>
<td>Kafka</td>
</tr>
<tr>
<td>📚 이벤트 로그를 쌓아 여러 소비자가 읽는 경우</td>
<td>Kafka</td>
</tr>
<tr>
<td>🚛 대용량 데이터 파이프라인</td>
<td>Kafka</td>
</tr>
<tr>
<td>⚡ 캐시, 세션 저장</td>
<td>Redis</td>
</tr>
<tr>
<td>🏃 가볍고 빠른 실시간 알림, 잃어도 되는 메시지</td>
<td>Redis</td>
</tr>
</tbody></table>
<h3 id="🚫-사용하지-않는-편이-나은-경우">🚫 사용하지 않는 편이 나은 경우</h3>
<ul>
<li>❌ 이벤트 양이 적고 후속 처리도 한두 개뿐일 때. 운영 부담이 이점보다 크다.</li>
<li>❌ 요청에 대한 즉시 응답이 필요한 동기 처리일 때.</li>
<li>❌ 강한 일관성이 필요해서 결과적 일관성을 받아들일 수 없을 때.</li>
<li>❌ 팀이 Broker를 운영할 여력이 없을 때.</li>
</ul>
<h2 id="🔔-내-프로젝트-적용">🔔 내 프로젝트 적용</h2>
<p>Slot Key에서는 방식 B(대기자 전원에게 알림을 보내고 선착순으로 예약하는 방식)를 택했고, 빈자리 이벤트를 Kafka로 전달한다. 현재 구현된 내용은 다음과 같다.</p>
<table>
<thead>
<tr>
<th>항목</th>
<th>값</th>
</tr>
</thead>
<tbody><tr>
<td>이벤트</td>
<td><code>SlotReleased</code></td>
</tr>
<tr>
<td>토픽</td>
<td><code>slot-released</code></td>
</tr>
<tr>
<td>메시지 key</td>
<td><code>spaceId</code></td>
</tr>
<tr>
<td>컨슈머 그룹</td>
<td><code>slotkey-waitlist-notifier</code></td>
</tr>
<tr>
<td>발행 방식</td>
<td>Transactional Outbox + polling relay (3편)</td>
</tr>
<tr>
<td>acks</td>
<td><code>all</code></td>
</tr>
<tr>
<td>auto-offset-reset</td>
<td><code>earliest</code></td>
</tr>
<tr>
<td>실패 처리</td>
<td>FixedBackOff 1000ms × 재시도 2회 후 건너뜀, DLT 없음</td>
</tr>
<tr>
<td>중복 방지</td>
<td><code>processed_event</code> UNIQUE(consumer, event_id), notification UNIQUE(event_id, waitlist_entry_id)</td>
</tr>
<tr>
<td>환경</td>
<td>로컬 Docker <code>apache/kafka:3.8.0</code> KRaft 단일 노드, 기본값은 Kafka 비활성</td>
</tr>
</tbody></table>
<p>이렇게 구성한 이유는 앞에서 정리한 기준과 맞는다. 예약 취소라는 본업과 알림이라는 후속 처리를 분리하고, 알림은 잠시 늦어도 괜찮으며, 이벤트가 중복되어도 멱등하게 처리할 수 있기 때문이다.</p>
<p>한계도 분명히 적어 둔다. 로컬 단일 노드라서 복제와 장애 전환은 검증하지 못했고, 파티션이 2개 이상일 때의 순서 보장도 확인하지 않았다. DLT가 없어서 건너뛴 이벤트는 복구할 수 없다. 팀의 EC2 배포 환경에는 Kafka를 올리지 않았다. 이 부분은 4편에서 다시 정리한다.</p>
<h2 id="🎤-면접-대비-한-줄-정리">🎤 면접 대비 한 줄 정리</h2>
<table>
<thead>
<tr>
<th>질문</th>
<th>답변</th>
</tr>
</thead>
<tbody><tr>
<td>Kafka를 왜 썼나?</td>
<td>예약 취소와 알림 처리를 분리하고, 알림이 늦거나 실패해도 취소에 영향이 없게 하기 위해서다.</td>
</tr>
<tr>
<td>Kafka와 일반 MQ의 차이는?</td>
<td>Kafka는 이벤트를 보존 기간 동안 남겨 두고 offset으로 다시 읽을 수 있다.</td>
</tr>
<tr>
<td>key를 spaceId로 한 이유는?</td>
<td>같은 공간의 이벤트를 같은 파티션에 모아 순서를 유지하기 위해서다. 단, 파티션 수가 바뀌지 않는다는 전제가 있다.</td>
</tr>
<tr>
<td>이벤트가 중복되면?</td>
<td>at-least-once라 중복이 가능해서, <code>processed_event</code>와 UNIQUE 제약으로 멱등하게 처리한다.</td>
</tr>
<tr>
<td>Redis Pub/Sub 대신 Kafka를 쓴 이유는?</td>
<td>Redis Pub/Sub은 구독자가 없을 때 메시지가 사라진다. 빈자리 알림은 컨슈머가 잠시 죽어도 복구 후 이어서 처리해야 해서 Kafka가 맞다.</td>
</tr>
</tbody></table>
<h2 id="📝-정리">📝 정리</h2>
<ul>
<li>📌 서비스를 직접 연결하면 연결선이 곱으로 늘고, 본업에 후속 처리가 묶인다. 브로커를 두면 이를 풀 수 있다.</li>
<li>📌 Kafka는 이벤트를 토픽의 파티션에 순서대로 쌓고, 컨슈머 그룹이 offset을 기록하며 읽는 이벤트 브로커다.</li>
<li>📌 순서는 파티션 안에서만, 보존은 보존 기간 안에서만 성립한다. 전달 보장과 중복 처리는 별도로 설계해야 한다.</li>
</ul>
<h2 id="➡️-다음-편-예고">➡️ 다음 편 예고</h2>
<p>3편에서는 DB 저장과 이벤트 발행이 어긋나는 문제를 푸는 Transactional Outbox 패턴을 다룬다. 예약 취소는 저장됐는데 이벤트 발행이 실패하는 상황을 어떻게 막는지, Slot Key의 polling relay 방식과 함께 정리할 예정이다.</p>
<h4 id="참조">참조</h4>
<ul>
<li><a href="https://velog.io/@holicme7">메시지 큐와 이벤트 브로커 정리 - holicme7</a></li>
<li><a href="https://this-circle-jeong.tistory.com/245">Kafka 개념 정리 - this-circle-jeong</a></li>
<li><a href="https://aws.amazon.com/what-is/apache-kafka/">What is Apache Kafka - AWS</a></li>
<li><a href="https://www.ibm.com/topics/apache-kafka">What is Apache Kafka - IBM</a></li>
<li><a href="https://kafka.apache.org/documentation/">Apache Kafka Documentation</a></li>
<li>Slot Key 내부 문서: docs/decisions/outbox-kafka.md</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[풀스택 개발자 과정 104일차]]></title>
            <link>https://velog.io/@chaen0830/%ED%92%80%EC%8A%A4%ED%83%9D-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EA%B3%BC%EC%A0%95-104%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@chaen0830/%ED%92%80%EC%8A%A4%ED%83%9D-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EA%B3%BC%EC%A0%95-104%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Fri, 16 Oct 2026 03:39:01 GMT</pubDate>
            <description><![CDATA[<p>노션 정리, 깃 정리, 이력서 수정, 피그마로 포트폴리오 작성</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[풀스택 개발자 과정 103일차]]></title>
            <link>https://velog.io/@chaen0830/%ED%92%80%EC%8A%A4%ED%83%9D-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EA%B3%BC%EC%A0%95-103%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@chaen0830/%ED%92%80%EC%8A%A4%ED%83%9D-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EA%B3%BC%EC%A0%95-103%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Fri, 16 Oct 2026 03:38:23 GMT</pubDate>
            <description><![CDATA[<p>이력서 수정 및 개인프로젝트 마지막 마무리(배포 전단계)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Do it! 쉽게 배우는 R 데이터분석 CH.13-15]]></title>
            <link>https://velog.io/@jaes2/Do-it-%EC%89%BD%EA%B2%8C-%EB%B0%B0%EC%9A%B0%EB%8A%94-R-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B6%84%EC%84%9D-CH.13-15</link>
            <guid>https://velog.io/@jaes2/Do-it-%EC%89%BD%EA%B2%8C-%EB%B0%B0%EC%9A%B0%EB%8A%94-R-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B6%84%EC%84%9D-CH.13-15</guid>
            <pubDate>Fri, 16 Oct 2026 03:34:40 GMT</pubDate>
            <description><![CDATA[<h2 id="🧪-13-통계-분석-기법을-이용한-가설-검정">🧪 13. 통계 분석 기법을 이용한 가설 검정</h2>
<hr>
<h3 id="❓-13-1-통계적-가설-검정이란">❓ 13-1. 통계적 가설 검정이란?</h3>
<h4 id="기술-통계와-추론-통계">기술 통계와 추론 통계</h4>
<blockquote>
<p><strong>기술 통계(Descriptive Statistics)</strong>
: 데이터를 요약해 설명하는 통계 기법
-&gt; 예: 사람들이 받는 월급을 집계해 전체 월급 평균 구하기</p>
</blockquote>
<hr>
<p><strong>추론 통계(Inferential Statistics)</strong>
: 단순히 숫자를 요약하는 것을 넘어, 어떤 값이 발생할 확률을 계산하는 통계 기법
-&gt; 예: 성별에 따른 월급 차이가 <strong>우연히</strong> 발생했을 확률 계산</p>
<ul>
<li>우연히 나타날 확률이 작다 -&gt; <strong>통계적으로 유의하다</strong>(statistically significant)</li>
<li>우연히 나타날 확률이 크다 -&gt; <strong>통계적으로 유의하지 않다</strong>(not statistically significant)</li>
</ul>
<blockquote>
<p>※ 기술 통계에서 차이가 있는 것으로 나타나더라도 우연에 의한 차이일 수 있음
-&gt; 신뢰할 수 있는 결론을 내리려면 유의확률을 계산하는 <strong>통계적 가설 검정</strong> 절차를 거쳐야 함</p>
</blockquote>
<hr>
<h4 id="통계적-가설-검정">통계적 가설 검정</h4>
<blockquote>
<p><strong>통계적 가설 검정(Statistical Hypothesis Test)</strong>
: 유의확률을 이용해 가설을 검정하는 방법</p>
</blockquote>
<hr>
<p><strong>유의확률(Significance Probability, p-value)</strong>
: 실제로는 집단 간 차이가 없는데 우연히 차이가 있는 데이터가 추출될 확률</p>
<table>
<thead>
<tr>
<th>유의확률</th>
<th>해석</th>
</tr>
</thead>
<tbody><tr>
<td>크다</td>
<td>집단 간 차이가 통계적으로 <strong>유의하지 않다</strong> (우연에 의해 관찰될 가능성이 큼)</td>
</tr>
<tr>
<td>작다</td>
<td>집단 간 차이가 통계적으로 <strong>유의하다</strong> (우연이라고 보기 힘듦)</td>
</tr>
</tbody></table>
<blockquote>
<p>※ 일반적으로 유의확률 <strong>5%(0.05)</strong>를 판단 기준으로 삼음</p>
</blockquote>
<hr>
<h3 id="⚖️-13-2-t-검정---두-집단의-평균-비교">⚖️ 13-2. t 검정 - 두 집단의 평균 비교</h3>
<blockquote>
<p><strong>t 검정(t-test)</strong>
: 두 집단의 평균에 통계적으로 유의한 차이가 있는지 알아볼 때 사용하는 통계 분석 기법
-&gt; R에 내장된 <code>t.test()</code> 이용</p>
</blockquote>
<h4 id="compact-자동차와-suv-자동차의-도시-연비-t-검정">compact 자동차와 suv 자동차의 도시 연비 t 검정</h4>
<h5 id="1-데이터-준비하기">1. 데이터 준비하기</h5>
<pre><code class="language-R">mpg &lt;- as.data.frame(ggplot2::mpg)

library(dplyr)
mpg_diff &lt;- mpg %&gt;%
  select(class, cty) %&gt;%
  filter(class %in% c(&quot;compact&quot;, &quot;suv&quot;))

table(mpg_diff$class)

## compact     suv
##      47      62</code></pre>
<hr>
<h5 id="2-t-검정-하기">2. t 검정 하기</h5>
<pre><code class="language-R"># 비교할 값 ~ 비교할 집단, 집단 간 분산이 같다고 가정
t.test(data = mpg_diff, cty ~ class, var.equal = T)

##  Two Sample t-test
##
## data:  cty by class
## t = 11.917, df = 107, p-value &lt; 2.2e-16
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  5.525180 7.730139
## sample estimates:
## mean in group compact     mean in group suv
##              20.12766              13.50000</code></pre>
<blockquote>
<p>※ <code>~</code> 기호 앞에는 <strong>비교할 값</strong>(cty), 뒤에는 <strong>비교할 집단</strong>(class)
※ <code>var.equal = T</code>: 집단 간 분산(값이 퍼져 있는 정도)이 같다고 가정</p>
</blockquote>
<hr>
<h5 id="3-결과-해석하기">3. 결과 해석하기</h5>
<blockquote>
<p><strong>p-value &lt; 2.2e-16</strong>
: 2.2 앞에 0이 16개 있는 값(2.2 × 10⁻¹⁶)보다 작다
-&gt; 0.05보다 작으므로 compact와 suv 간 평균 도시 연비 차이가 <strong>통계적으로 유의함</strong></p>
</blockquote>
<hr>
<p><strong>sample estimates</strong>
: 각 집단의 cty 평균
-&gt; compact 20, suv 13 -&gt; compact의 도시 연비가 더 높음</p>
<hr>
<h4 id="일반-휘발유와-고급-휘발유의-도시-연비-t-검정">일반 휘발유와 고급 휘발유의 도시 연비 t 검정</h4>
<pre><code class="language-R">mpg_diff2 &lt;- mpg %&gt;%
  select(fl, cty) %&gt;%
  filter(fl %in% c(&quot;r&quot;, &quot;p&quot;))  # r: regular, p: premium

t.test(data = mpg_diff2, cty ~ fl, var.equal = T)

## t = 1.0662, df = 218, p-value = 0.2875
## sample estimates:
## mean in group p mean in group r
##        17.36538        16.73810</code></pre>
<blockquote>
<p>p-value 0.2875 -&gt; 0.05보다 큼
-&gt; 실제로는 차이가 없는데 우연에 의해 이런 차이가 관찰될 확률이 28.75%
-&gt; 일반 휘발유와 고급 휘발유 자동차 간 도시 연비 차이가 <strong>통계적으로 유의하지 않음</strong>
※ 평균은 고급 휘발유가 0.6 정도 높지만, 우연히 발생했을 가능성이 큼</p>
</blockquote>
<hr>
<h3 id="🔗-13-3-상관분석---두-변수의-관계성-분석">🔗 13-3. 상관분석 - 두 변수의 관계성 분석</h3>
<blockquote>
<p><strong>상관분석(Correlation Analysis)</strong>
: 두 연속 변수가 서로 관련이 있는지 검정하는 통계 분석 기법</p>
</blockquote>
<hr>
<p><strong>상관계수(Correlation Coefficient)</strong>
: 두 변수가 얼마나 관련되어 있는지, 관련성의 정도를 나타내는 값</p>
<ul>
<li>0~1 사이의 값, 1에 가까울수록 관련성이 큼</li>
<li>양수면 <strong>정비례</strong>, 음수면 <strong>반비례</strong> 관계</li>
</ul>
<h4 id="실업자-수와-개인-소비-지출의-상관관계">실업자 수와 개인 소비 지출의 상관관계</h4>
<pre><code class="language-R">economics &lt;- as.data.frame(ggplot2::economics)
cor.test(economics$unemploy, economics$pce)

##  Pearson&#39;s product-moment correlation
##
## t = 18.605, df = 572, p-value &lt; 2.2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.5603164 0.6625460
## sample estimates:
##       cor
## 0.6139997</code></pre>
<blockquote>
<ul>
<li>p-value &lt; 0.05 -&gt; 실업자 수와 개인 소비 지출의 상관이 <strong>통계적으로 유의함</strong></li>
</ul>
</blockquote>
<ul>
<li>cor(상관계수) 0.61 -&gt; 양수 -&gt; 한 변수가 증가하면 다른 변수도 증가하는 <strong>정비례</strong> 관계</li>
</ul>
<hr>
<h4 id="상관행렬-히트맵-만들기">상관행렬 히트맵 만들기</h4>
<blockquote>
<p><strong>상관행렬(Correlation Matrix)</strong>
: 여러 변수의 관련성을 한 번에 알아보고자 할 경우, 모든 변수의 상관 관계를 나타낸 행렬
-&gt; 어떤 변수끼리 관련이 크고 적은지 파악 가능</p>
</blockquote>
<h5 id="1-상관행렬-만들기">1. 상관행렬 만들기</h5>
<p>: <code>mtcars</code> - 자동차 32종의 11개 속성 정보 (R 내장 데이터)</p>
<pre><code class="language-R">car_cor &lt;- cor(mtcars)   # 상관행렬 생성
round(car_cor, 2)        # 소수점 셋째 자리에서 반올림해 출력

##        mpg   cyl  disp    hp  drat    wt
## mpg   1.00 -0.85 -0.85 -0.78  0.68 -0.87
## cyl  -0.85  1.00  0.90  0.83 -0.70  0.78</code></pre>
<blockquote>
<ul>
<li>mpg(연비)와 cyl(실린더 수): <strong>-0.85</strong> -&gt; 실린더 수가 많을수록 연비가 낮음</li>
</ul>
</blockquote>
<ul>
<li>cyl(실린더 수)와 wt(무게): <strong>0.78</strong> -&gt; 실린더 수가 많을수록 무게가 무거움</li>
</ul>
<hr>
<h5 id="2-히트맵-만들기">2. 히트맵 만들기</h5>
<blockquote>
<p><strong>히트맵(Heat Map)</strong>
: 값의 크기를 색깔로 표현한 그래프
-&gt; 숫자로 된 상관행렬보다 변수들의 관계를 쉽게 파악할 수 있음</p>
</blockquote>
<pre><code class="language-R">install.packages(&quot;corrplot&quot;)
library(corrplot)

corrplot(car_cor)</code></pre>
<blockquote>
<ul>
<li>상관계수가 클수록 원의 크기가 크고 색깔이 진함</li>
</ul>
</blockquote>
<ul>
<li>양수면 파란색, 음수면 빨간색 계열</li>
</ul>
<hr>
<h5 id="3-상관계수-숫자로-표현하기">3. 상관계수 숫자로 표현하기</h5>
<pre><code class="language-R">corrplot(car_cor, method = &quot;number&quot;)</code></pre>
<hr>
<h5 id="4-다양한-파라미터-지정하기">4. 다양한 파라미터 지정하기</h5>
<pre><code class="language-R"># 색상 코드 목록을 생성하는 col() 함수 만들기
col &lt;- colorRampPalette(c(&quot;#BB4444&quot;, &quot;#EE9988&quot;, &quot;#FFFFFF&quot;, &quot;#77AADD&quot;, &quot;#4477AA&quot;))

corrplot(car_cor,
         method = &quot;color&quot;,        # 색깔로 표현
         col = col(200),          # 색상 200개 선정
         type = &quot;lower&quot;,          # 왼쪽 아래 행렬만 표시
         order = &quot;hclust&quot;,        # 유사한 상관계수끼리 군집화
         addCoef.col = &quot;black&quot;,   # 상관계수 색깔
         tl.col = &quot;black&quot;,        # 변수명 색깔
         tl.srt = 45,             # 변수명 45도 기울임
         diag = F)                # 대각 행렬 제외</code></pre>
<hr>
<h2 id="📝-14-r-markdown으로-데이터-분석-보고서-만들기">📝 14. R Markdown으로 데이터 분석 보고서 만들기</h2>
<hr>
<h3 id="📑-14-1-신뢰할-수-있는-데이터-분석-보고서-만들기">📑 14-1. 신뢰할 수 있는 데이터 분석 보고서 만들기</h3>
<blockquote>
<p><strong>데이터 분석 보고서</strong>
: 코드와 결과물이 설명 글과 함께 어우러진 문서
-&gt; 독자가 분석 과정을 명확히 이해하고, 직접 코드를 실행하며 동일한 결과가 나오는지 확인 가능</p>
</blockquote>
<hr>
<p><strong>재현성(Reproducibility)</strong>
: 동일한 분석 과정을 거치면 동일한 분석 결과가 반복되어 나오는 성질
-&gt; 신뢰할 수 있는 보고서의 조건</p>
<h4 id="r-마크다운r-markdown">R 마크다운(R Markdown)</h4>
<ul>
<li>데이터 분석의 전 과정을 담은 보고서를 쉽게 만들 수 있음</li>
<li>HTML, 워드, PDF 등 다양한 포맷으로 저장 가능</li>
<li>별도의 문서 작성 소프트웨어 없이도 보고서 작성 가능</li>
<li>분석 과정과 결과를 자세하게 기술할 수 있어 <strong>재현성</strong>을 갖춘 보고서를 만들 수 있음</li>
</ul>
<hr>
<h3 id="🖋️-14-2-r-마크다운-문서-만들기">🖋️ 14-2. R 마크다운 문서 만들기</h3>
<h4 id="r-마크다운으로-데이터-분석-보고서-만들기">R 마크다운으로 데이터 분석 보고서 만들기</h4>
<h5 id="1-문서-생성-창-열기">1. 문서 생성 창 열기</h5>
<p>: [File → New File → R Markdown] 클릭
-&gt; HTML, PDF, Word 중 저장할 문서 포맷을 정하고 [OK]</p>
<blockquote>
<p>※ 처음 만들면 <code>rmarkdown</code> 패키지 설치 창이 나타남 -&gt; [YES]</p>
</blockquote>
<hr>
<h5 id="2-문서-생성하기">2. 문서 생성하기</h5>
<p>: 마크다운 창 메뉴의 뜨개실 모양 <strong>[Knit]</strong> 버튼 클릭
-&gt; R 마크다운 문서 파일(<strong>.Rmd</strong>)을 저장하면 HTML 포맷으로 변환되어 새 창이 열림
-&gt; Rmd 파일과 변환된 HTML 파일은 프로젝트 폴더에 저장</p>
<hr>
<h5 id="3-다른-포맷으로-변환하기">3. 다른 포맷으로 변환하기</h5>
<p>: [Knit] 버튼 오른쪽 화살표 클릭
-&gt; [Knit to HTML], [Knit to PDF], [Knit to Word] 중 선택</p>
<blockquote>
<p><strong>PDF 파일로 저장하기</strong></p>
</blockquote>
<ol>
<li>TeX 소프트웨어 설치 (윈도우: MiKTeX, 맥: MacTeX)</li>
<li>R 스튜디오 재실행 후 문서 상단의 output 부분 수정<pre><code></code></pre></li>
</ol>
<hr>
<p>output:
  pdf_document:
    latex_engine: xelatex
mainfont: MalgunGothic</p>
<hr>
<pre><code>3. [Knit to PDF] 클릭

---
#### 마크다운 문법
&gt; **마크다운 문법**
: 특수문자를 활용해 문서 양식을 결정하는 형태로 구성됨

##### 양식 적용하기</code></pre><p>문자 앞뒤에 <em>특수문자</em>를 넣으면 기울임체가 됩니다.
문자 앞뒤에 <strong>특수문자</strong>를 넣으면 강조체가 됩니다.
문자 앞뒤에 <del>특수문자</del>를 넣으면 취소선을 만듭니다.
문자 앞뒤에 <a href="http://www.google.com">특수문자</a>를 넣으면 하이퍼링크를 만듭니다.</p>
<h1 id="1단계-제목">1단계 제목</h1>
<h2 id="2단계-제목">2단계 제목</h2>
<h3 id="3단계-제목">3단계 제목</h3>
<h4 id="4단계-제목">4단계 제목</h4>
<p>데이터 앞부분 일부를 출력하려면 <code>head()</code>를 이용하면 됩니다.</p>
<pre><code>
| 문법 | 결과 |
|---|---|
| `*문자*` | 기울임체 |
| `**문자**` | 강조체 |
| `~~문자~~` | 취소선 |
| `[문자](주소)` | 하이퍼링크 |
| `#` | 제목 (# 개수에 따라 레벨 결정) |
| `` `문자` `` | 코드 음영 처리 |

&gt; ※ backtick 기호(`)는 `Esc` 키 아래에 있는 키

---
#### 코드와 실행 결과
&gt; **코드 청크(Code Chunk)**
: 코드를 입력하면 코드와 실행 결과를 함께 출력하는 영역
-&gt; `ctrl` + `alt` + `I`로 삽입
</code></pre><pre><code class="language-{r}">summary(cars)

library(ggplot2)
qplot(data = mpg, x = drv, fill = drv)</code></pre>
<p>````</p>
<blockquote>
<p>별도로 이미지 파일을 붙여 넣는 작업을 하지 않아도 코드 아래에 그래프가 삽입됨</p>
</blockquote>
<h4 id="r-마크다운-매뉴얼">R 마크다운 매뉴얼</h4>
<ul>
<li>R 마크다운 치트시트: [Help → Cheat Sheets → R Markdown Cheat Sheet]</li>
<li>R Markdown 가이드: [Help → Cheat Sheets → R Markdown Reference Guide]</li>
<li>R 마크다운 공식 사이트: <a href="http://rmarkdown.rstudio.com">http://rmarkdown.rstudio.com</a></li>
</ul>
<hr>
<h2 id="🧱-15-r-내장-함수-변수-타입과-데이터-구조">🧱 15. R 내장 함수, 변수 타입과 데이터 구조</h2>
<hr>
<h3 id="🔍-15-1-r-내장-함수로-데이터-추출하기">🔍 15-1. R 내장 함수로 데이터 추출하기</h3>
<blockquote>
<p><strong>R 내장 함수</strong>
: dplyr 패키지를 사용하지 않고 R에 기본적으로 내장되어 있는 함수</p>
</blockquote>
<ul>
<li>dplyr이 더 쉽고 처리 속도도 빠름</li>
<li>하지만 다른 사람의 코드를 이해하고 활용하려면 내장 함수 사용법도 알아야 함</li>
<li>다른 함수들과 조합해 활용할 수 있음</li>
</ul>
<h4 id="기본-형태">기본 형태</h4>
<blockquote>
<p><strong>데이터프레임명[행 조건, 열 조건]</strong>
: 대괄호 안 쉼표를 기준으로 <strong>왼쪽은 행</strong>, <strong>오른쪽은 열(변수)</strong>에 대한 조건</p>
</blockquote>
<pre><code class="language-R">exam &lt;- read.csv(&quot;csv_exam.csv&quot;)
exam[]  # 조건 없이 전체 데이터 출력</code></pre>
<hr>
<h4 id="행-번호로-행-추출하기">행 번호로 행 추출하기</h4>
<pre><code class="language-R">exam[1,]  # 1행 추출

##   id class math english science
## 1  1     1   50      98      50

exam[2,]  # 2행 추출</code></pre>
<blockquote>
<p><strong>인덱스(Index)</strong>
: 데이터의 위치 또는 순서를 의미하는 값
<strong>인덱싱(Indexing)</strong>
: 인덱스를 이용해 데이터를 추출하는 작업</p>
</blockquote>
<hr>
<h4 id="조건을-충족하는-행-추출하기">조건을 충족하는 행 추출하기</h4>
<pre><code class="language-R">exam[exam$class == 1,]     # class가 1인 행 추출
exam[exam$math &gt;= 80,]     # 수학 점수가 80점 이상인 행 추출

# 1반 이면서 수학 점수가 50점 이상
exam[exam$class == 1 &amp; exam$math &gt;= 50,]

# 영어 점수가 90점 미만이거나 과학 점수가 50점 미만
exam[exam$english &lt; 90 | exam$science &lt; 50,]</code></pre>
<blockquote>
<p>※ dplyr과 달리 내장 함수에서는 조건을 입력할 때 변수명 앞에 <strong>데이터 프레임 이름을 반복</strong>해 써야 함</p>
</blockquote>
<hr>
<h4 id="열-번호로-변수-추출하기">열 번호로 변수 추출하기</h4>
<pre><code class="language-R">exam[,1]  # 첫 번째 열 추출

## [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20

exam[,2]  # 두 번째 열 추출
exam[,3]  # 세 번째 열 추출</code></pre>
<blockquote>
<p>※ 변수를 하나만 추출하면 데이터 프레임이 아니라 <strong>연속 값(벡터)</strong>이 출력됨
※ 반면 dplyr의 <code>select()</code>는 변수가 하나만 있어도 데이터 프레임 형태로 추출</p>
</blockquote>
<hr>
<h4 id="변수명으로-변수-추출하기">변수명으로 변수 추출하기</h4>
<p>: 변수가 많으면 인덱스를 알아내기 힘듦 -&gt; 변수명 활용이 편리</p>
<pre><code class="language-R">exam[, &quot;class&quot;]  # class 변수 추출
exam[, &quot;math&quot;]   # math 변수 추출

exam[, c(&quot;class&quot;, &quot;math&quot;, &quot;english&quot;)]  # 여러 변수 동시 추출</code></pre>
<hr>
<h4 id="행-변수-동시-추출하기">행, 변수 동시 추출하기</h4>
<pre><code class="language-R">exam[1,3]                                     # 행, 변수 모두 인덱스
exam[5, &quot;english&quot;]                            # 행 인덱스, 열 변수명
exam[exam$math &gt;= 50, &quot;english&quot;]              # 행 부등호 조건, 열 변수명
exam[exam$math &gt;= 50, c(&quot;english&quot;, &quot;science&quot;)]</code></pre>
<hr>
<h4 id="dplyr과-내장-함수의-차이">dplyr과 내장 함수의 차이</h4>
<blockquote>
<p><strong>문제)</strong> 수학 점수 50 이상, 영어 점수 80 이상인 학생들을 대상으로 각 반의 전 과목 총평균을 구하라.</p>
</blockquote>
<h5 id="내장-함수-코드">내장 함수 코드</h5>
<pre><code class="language-R">exam$tot &lt;- (exam$math + exam$english + exam$science)/3
aggregate(data = exam[exam$math &gt;= 50 &amp; exam$english &gt;= 80,], tot~class, mean)</code></pre>
<blockquote>
<p>※ <code>aggregate()</code>: 범주별 요약 통계량을 구하는 R 내장 함수</p>
</blockquote>
<hr>
<h5 id="dplyr-코드">dplyr 코드</h5>
<pre><code class="language-R">exam %&gt;%
  filter(math &gt;= 50 &amp; english &gt;= 80) %&gt;%
  mutate(tot = (math + english + science)/3) %&gt;%
  group_by(class) %&gt;%
  summarise(mean = mean(tot))</code></pre>
<blockquote>
<p>dplyr 코드가 논리의 흐름대로 구조화되어 있어 <strong>가독성이 높고 이해하기 쉬움</strong></p>
</blockquote>
<hr>
<h3 id="🏷️-15-2-변수-타입">🏷️ 15-2. 변수 타입</h3>
<blockquote>
<p>변수에는 여러 가지 타입(Type, 속성)이 있음
-&gt; 함수에 따라 적용할 수 있는 변수 타입이 다르기 때문에 분석 전에 변수 타입 확인 필요
-&gt; 함수 실행 시 오류가 발생하거나 예상과 다른 결과가 나오면 변수 타입 확인</p>
</blockquote>
<h4 id="변수의-종류">변수의 종류</h4>
<table>
<thead>
<tr>
<th>변수</th>
<th>Data Type</th>
<th>특징</th>
<th>예</th>
</tr>
</thead>
<tbody><tr>
<td>연속 변수</td>
<td>Numeric</td>
<td>크기를 의미하는 값, 산술 가능 (양적 변수)</td>
<td>키, 몸무게</td>
</tr>
<tr>
<td>범주 변수</td>
<td>Factor</td>
<td>대상을 분류하는 값, 산술 불가 (명목 변수)</td>
<td>성별(1, 2), 지역(1, 2, 3, 4)</td>
</tr>
</tbody></table>
<hr>
<h4 id="변수-타입-간-차이-알아보기">변수 타입 간 차이 알아보기</h4>
<h5 id="1-numeric-변수와-factor-변수-만들기">1. numeric 변수와 factor 변수 만들기</h5>
<pre><code class="language-R">var1 &lt;- c(1,2,3,1,2)          # numeric 변수 생성
var2 &lt;- factor(c(1,2,3,1,2))  # factor 변수 생성

var1

## [1] 1 2 3 1 2

var2

## [1] 1 2 3 1 2
## Levels: 1 2 3</code></pre>
<blockquote>
<p>※ factor 변수는 값과 함께 범주를 의미하는 <strong>Levels</strong> 정보를 지님</p>
</blockquote>
<hr>
<h5 id="2-factor-변수는-연산이-안-된다">2. factor 변수는 연산이 안 된다</h5>
<pre><code class="language-R">var1+2  # numeric 변수로 연산

## [1] 3 4 5 3 4

var2+2  # factor 변수로 연산

## Warning in Ops.factor(var2, 2): &#39;+&#39; not meaningful for factors
## [1] NA NA NA NA NA</code></pre>
<blockquote>
<p>factor 변수는 숫자로 되어 있지만 크기가 아니라 <strong>범주</strong>를 의미하기 때문에 연산 불가</p>
</blockquote>
<hr>
<h5 id="3-변수-타입과-범주-확인하기">3. 변수 타입과 범주 확인하기</h5>
<pre><code class="language-R">class(var1)   # 변수 타입 확인

## [1] &quot;numeric&quot;

class(var2)

## [1] &quot;factor&quot;

levels(var1)  # factor 변수의 구성 범주 확인

## NULL

levels(var2)

## [1] &quot;1&quot; &quot;2&quot; &quot;3&quot;</code></pre>
<blockquote>
<p>※ factor가 아닌 변수에 <code>levels()</code>를 적용하면 값이 없다는 의미의 <strong>NULL</strong> 출력</p>
</blockquote>
<hr>
<h5 id="4-문자로-구성된-factor-변수">4. 문자로 구성된 factor 변수</h5>
<pre><code class="language-R">var3 &lt;- c(&quot;a&quot;, &quot;b&quot;, &quot;b&quot;, &quot;c&quot;)          # 문자 변수 생성
var4 &lt;- factor(c(&quot;a&quot;, &quot;b&quot;, &quot;b&quot;, &quot;c&quot;))  # 문자로 된 factor 변수 생성

class(var3)

## [1] &quot;character&quot;

class(var4)

## [1] &quot;factor&quot;</code></pre>
<hr>
<h5 id="5-함수마다-적용-가능한-변수-타입이-다르다">5. 함수마다 적용 가능한 변수 타입이 다르다</h5>
<pre><code class="language-R">mean(var1)

## [1] 1.8

mean(var2)

## Warning in mean.default(var2): argument is not numeric or logical: returning NA
## [1] NA</code></pre>
<blockquote>
<p><code>mean()</code>은 numeric 변수에만 적용 가능</p>
</blockquote>
<hr>
<h4 id="변수-타입-바꾸기">변수 타입 바꾸기</h4>
<pre><code class="language-R">var2 &lt;- as.numeric(var2)  # numeric 타입으로 변환
mean(var2)                # 함수 재적용

## [1] 1.8

class(var2)

## [1] &quot;numeric&quot;

levels(var2)

## NULL</code></pre>
<blockquote>
<p><strong>변환 함수(Coercion Function)</strong>
: <code>as.</code>로 시작하는 함수들, 변수의 타입을 바꾸는 기능</p>
</blockquote>
<table>
<thead>
<tr>
<th>함수</th>
<th>기능</th>
</tr>
</thead>
<tbody><tr>
<td><code>as.numeric()</code></td>
<td>numeric으로 변환</td>
</tr>
<tr>
<td><code>as.factor()</code></td>
<td>factor로 변환</td>
</tr>
<tr>
<td><code>as.character()</code></td>
<td>character로 변환</td>
</tr>
<tr>
<td><code>as.Date()</code></td>
<td>Date로 변환</td>
</tr>
<tr>
<td><code>as.data.frame()</code></td>
<td>Data Frame으로 변환</td>
</tr>
</tbody></table>
<hr>
<h4 id="다양한-변수-타입들">다양한 변수 타입들</h4>
<table>
<thead>
<tr>
<th>Data Type</th>
<th>의미</th>
<th>값</th>
</tr>
</thead>
<tbody><tr>
<td>numeric</td>
<td>실수</td>
<td>1, 12.3</td>
</tr>
<tr>
<td>integer</td>
<td>정수</td>
<td>1L, 23L</td>
</tr>
<tr>
<td>complex</td>
<td>복소수</td>
<td>3 + 2i</td>
</tr>
<tr>
<td>character</td>
<td>문자</td>
<td>&quot;male&quot;, &quot;%FEMALE#&quot;, &quot;123&quot;</td>
</tr>
<tr>
<td>logical</td>
<td>논리</td>
<td>TRUE, FALSE, T, F</td>
</tr>
<tr>
<td>factor</td>
<td>범주</td>
<td>1, 2, a, b</td>
</tr>
<tr>
<td>Date</td>
<td>날짜</td>
<td>&quot;2014-04-16&quot;, &quot;16/04/14&quot;</td>
</tr>
</tbody></table>
<hr>
<h3 id="🗃️-15-3-데이터-구조">🗃️ 15-3. 데이터 구조</h3>
<blockquote>
<p><strong>데이터 구조(Data Structures)</strong>
: 데이터 프레임 외에도 다양한 데이터 구조가 있으며, 구조에 따라 활용 방법에 차이가 있음</p>
</blockquote>
<table>
<thead>
<tr>
<th>데이터 구조</th>
<th>차원</th>
<th>특징</th>
</tr>
</thead>
<tbody><tr>
<td>벡터(Vector)</td>
<td>1차원</td>
<td>한 가지 변수 타입으로 구성</td>
</tr>
<tr>
<td>데이터 프레임(Data Frame)</td>
<td>2차원</td>
<td>다양한 변수 타입으로 구성</td>
</tr>
<tr>
<td>매트릭스(Matrix)</td>
<td>2차원</td>
<td>한 가지 변수 타입으로 구성</td>
</tr>
<tr>
<td>어레이(Array)</td>
<td>다차원</td>
<td>2차원 이상의 매트릭스</td>
</tr>
<tr>
<td>리스트(List)</td>
<td>다차원</td>
<td>서로 다른 데이터 구조 포함</td>
</tr>
</tbody></table>
<hr>
<h4 id="데이터-구조-비교하기">데이터 구조 비교하기</h4>
<h5 id="1-벡터">1. 벡터</h5>
<p>: 하나의 값 또는 여러 개의 값으로 구성, 여러 변수 타입을 섞을 수 없음</p>
<pre><code class="language-R">a &lt;- 1
b &lt;- &quot;hello&quot;

class(a)

## [1] &quot;numeric&quot;

class(b)

## [1] &quot;character&quot;</code></pre>
<hr>
<h5 id="2-데이터-프레임">2. 데이터 프레임</h5>
<p>: 행과 열로 구성된 2차원 데이터 구조, 다양한 변수 타입으로 구성 가능</p>
<pre><code class="language-R">x1 &lt;- data.frame(var1 = c(1,2,3),
                 var2 = c(&quot;a&quot;,&quot;b&quot;,&quot;c&quot;))
class(x1)

## [1] &quot;data.frame&quot;</code></pre>
<hr>
<h5 id="3-매트릭스">3. 매트릭스</h5>
<p>: 데이터 프레임처럼 2차원이지만, 한 가지 변수 타입으로만 구성 가능</p>
<pre><code class="language-R">x2 &lt;- matrix(c(1:12), ncol = 2)  # 1~12로 2열
class(x2)

## [1] &quot;matrix&quot;</code></pre>
<hr>
<h5 id="4-어레이">4. 어레이</h5>
<p>: 2차원 이상으로 구성된 매트릭스, 한 가지 변수 타입으로만 구성 가능</p>
<pre><code class="language-R">x3 &lt;- array(1:20, dim = c(2, 5, 2))  # 1~20으로 2행 x 5열 x 2차원
class(x3)

## [1] &quot;array&quot;</code></pre>
<hr>
<h5 id="5-리스트">5. 리스트</h5>
<p>: 모든 데이터 구조를 포함하는 데이터 구조
-&gt; 여러 데이터 구조를 합해 하나의 리스트로 만들 수 있음</p>
<pre><code class="language-R">x4 &lt;- list(f1 = a,   # 벡터
           f2 = x1,  # 데이터 프레임
           f3 = x2,  # 매트릭스
           f4 = x3)  # 어레이
class(x4)

## [1] &quot;list&quot;</code></pre>
<h4 id="리스트-활용하기">리스트 활용하기</h4>
<p>: 함수의 결과물이 리스트 형태로 반환되는 경우가 많음
-&gt; 리스트를 다루는 문법을 이용하면 원하는 값을 추출할 수 있음</p>
<pre><code class="language-R">mpg &lt;- ggplot2::mpg
x &lt;- boxplot(mpg$cty)  # 매트릭스 stats, 벡터 out 등을 담은 리스트

x$stats[,1]     # 요약 통계량 추출

## [1]  9 14 17 19 26

x$stats[,1][3]  # 중앙값 추출

## [1] 17

x$stats[,1][2]  # 1사분위수 추출

## [1] 14</code></pre>
<blockquote>
<p>※ <code>$</code> 기호와 <code>[]</code> 기호를 이용하면 리스트 내부의 값을 추출할 수 있음</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[문제풀이] 9로 나눈 나머지]]></title>
            <link>https://velog.io/@koikoilove99/181914-remainder-divided-by-9</link>
            <guid>https://velog.io/@koikoilove99/181914-remainder-divided-by-9</guid>
            <pubDate>Fri, 16 Oct 2026 03:33:31 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<blockquote>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/181914">9로 나눈 나머지</a></p>
</blockquote>
<h2 id="span-stylecolor-a78bfa문제-설명span"><span style="color: #A78BFA">문제 설명</span></h2>
<p>음이 아닌 정수를 $9$로 나눈 나머지는 그 정수의 각 자리 숫자의 합을 $9$로 나눈 나머지와 같은 것이 알려져 있습니다.
이 사실을 이용하여 음이 아닌 정수가 문자열 <code>number</code>로 주어질 때, 이 정수를 $9$로 나눈 나머지를 return 하는 <code>solution</code> 함수를 작성해주세요.</p>
<hr>
<h2 id="span-stylecolor-a78bfa제한사항span"><span style="color: #A78BFA">제한사항</span></h2>
<ul>
<li>$1 \leq$ <code>number</code>의 길이 $\leq 100,000$</li>
<li><code>number</code>의 원소는 숫자로만 이루어져 있습니다.</li>
<li><code>number</code>는 정수 0이 아니라면 숫자 &#39;0&#39;으로 시작하지 않습니다.</li>
</ul>
<hr>
<h2 id="span-stylecolor-a78bfa입출력-예시span"><span style="color: #A78BFA">입출력 예시</span></h2>
<table>
<thead>
<tr>
<th>number</th>
<th>result</th>
</tr>
</thead>
<tbody><tr>
<td>&quot;123&quot;</td>
<td>6</td>
</tr>
<tr>
<td>&quot;78720646226947352489&quot;</td>
<td>2</td>
</tr>
</tbody></table>
<hr>
<h2 id="span-stylecolor-a78bfa문제-풀이span"><span style="color: #A78BFA">문제 풀이</span></h2>
<p>처음에는 그냥 <code>int</code>로 형변화해서 % 9 반환했는데 런타임 에러가 발생해서 문제 설명과 같이 각 자리 수를 다 더한 후 % 9 반환함.</p>
<pre><code class="language-python">def solution(number):
    return sum((map(int, number))) % 9</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[투자자 due diligence에서 레퍼런스 콜을 받기 전에, 90초 고객 성과 영상을 먼저 보내는 법]]></title>
            <link>https://velog.io/@axiomlab/%ED%88%AC%EC%9E%90%EC%9E%90-due-diligence%EC%97%90%EC%84%9C-%EB%A0%88%ED%8D%BC%EB%9F%B0%EC%8A%A4-%EC%BD%9C%EC%9D%84-%EB%B0%9B%EA%B8%B0-%EC%A0%84%EC%97%90-90%EC%B4%88-%EA%B3%A0%EA%B0%9D-%EC%84%B1%EA%B3%BC-%EC%98%81%EC%83%81%EC%9D%84-%EB%A8%BC%EC%A0%80-%EB%B3%B4%EB%82%B4%EB%8A%94-%EB%B2%95</link>
            <guid>https://velog.io/@axiomlab/%ED%88%AC%EC%9E%90%EC%9E%90-due-diligence%EC%97%90%EC%84%9C-%EB%A0%88%ED%8D%BC%EB%9F%B0%EC%8A%A4-%EC%BD%9C%EC%9D%84-%EB%B0%9B%EA%B8%B0-%EC%A0%84%EC%97%90-90%EC%B4%88-%EA%B3%A0%EA%B0%9D-%EC%84%B1%EA%B3%BC-%EC%98%81%EC%83%81%EC%9D%84-%EB%A8%BC%EC%A0%80-%EB%B3%B4%EB%82%B4%EB%8A%94-%EB%B2%95</guid>
            <pubDate>Fri, 16 Oct 2026 03:31:11 GMT</pubDate>
            <description><![CDATA[<p>투자 라운드가 진지해지면 거의 예외 없이 이런 요청이 옵니다. &quot;고객 레퍼런스 한두 명과 통화하고 싶습니다.&quot; 많은 창업자는 곧바로 고객에게 소개 메일을 보내고, 투자자와 고객을 캘린더에 올립니다. 그런데 그 콜이 시작되면 투자자는 처음부터 다시 묻습니다. 무엇을 샀는지, 왜 샀는지, 결과가 무엇인지. 고객은 갑작스러운 통화를 준비하지 못한 채 대답하고, 창업자는 옆에서 초조하게 듣습니다.</p>
<p>레퍼런스 콜 자체는 필요합니다. 문제는 콜 <em>전에</em> 공통 맥락이 없다는 점입니다. 이 글에서는 고객 허락을 받은 90초 안팎의 &quot;성과 영상&quot;을 먼저 보내고, 그다음에 통화를 잡는 방식을 정리합니다. 목표는 콜을 없애는 것이 아니라, 콜이 확인과 심화 질문에 쓰이게 만드는 것입니다.</p>
<h2 id="1-왜-영상인가-메일이-아닌-이유">1. 왜 영상인가 (메일이 아닌 이유)</h2>
<p>레퍼런스 요청에 대한 흔한 대응은 긴 텍스트입니다. 고객 로고, 인용문, 케이스 스터디 PDF. 투자자는 읽지 않거나, 읽더라도 자기 머릿속에 있는 질문과 매칭되지 않습니다. 짧은 영상은 세 가지를 동시에 줍니다.</p>
<ul>
<li><strong>장면</strong>: 제품이 실제로 쓰이는 화면, 또는 고객이 직접 말하는 얼굴</li>
<li><strong>인과</strong>: &quot;이전 → 이후&quot;가 한 호흡으로 이어짐</li>
<li><strong>신뢰 신호</strong>: 고객이 카메라 앞에 섰다는 사실 자체</li>
</ul>
<p>물론 영상은 고객 동의 없이는 만들 수 없습니다. 동의 범위(어디에 쓰는지, 공개인지 비공개 링크인지, 얼굴 노출 여부)를 문서나 메일로 남겨 두세요. 동의 없는 녹화·인용은 레퍼런스가 아니라 리스크입니다.</p>
<h2 id="2-90초-구성-문제-→-선택-→-결과-→-한-줄-요청">2. 90초 구성: 문제 → 선택 → 결과 → 한 줄 요청</h2>
<p>레퍼런스용 영상은 피치 영상과 다릅니다. 시장 이야기나 팀 소개는 빼세요. 투자자가 듣고 싶은 건 &quot;이 고객이 왜 돈을 냈고, 무엇이 달라졌는가&quot;입니다.</p>
<p><strong>0~15초: 고객이 누구인지, 어떤 상황에서 만났는지</strong>
&quot;○○ 업종의 □□팀에서 △△를 담당하고 있습니다. 작년 말에 주간 보고에 반나절이 걸리던 문제가 있었습니다.&quot; 회사명·로고만 보여주는 인트로는 약합니다. 역할과 통증이 한 문장에 들어가야 합니다.</p>
<p><strong>15~35초: 왜 이 제품을 골랐는지 (대안과 비교)</strong>
&quot;직접 만들까, 기존 도구를 쓸까 고민했고, 최종적으로는 ○○ 때문에 선택했습니다.&quot; 투자자는 &#39;유일한 선택&#39;보다 &#39;비교 후 선택&#39;을 더 믿습니다. 과장된 &quot;최고의 제품&quot; 표현은 빼세요.</p>
<p><strong>35~70초: 결과 — 수치 하나 + 장면 하나</strong>
숫자는 실제 수치만, 기간과 기준과 함께. &quot;효율이 좋아졌다&quot;보다 &quot;주간 보고 준비 시간이 N시간에서 M시간으로 줄었다&quot;가 낫습니다. 가능하면 대시보드나 산출물 화면을 잠깐 보여 주세요. 수치가 아직 약하면, 프로세스 변화(승인 단계 축소, 핸드오프 횟수 감소)처럼 검증 가능한 사실을 쓰세요.</p>
<p><strong>70~90초: 레퍼런스 콜을 위한 안내</strong>
&quot;투자자분과 20분 통화가 가능합니다. 미리 이 영상을 보시면 맥락이 맞습니다.&quot; 고객이 직접 말해도 되고, 창업자가 클로징 자막으로 넣어도 됩니다. 마지막 화면에 연락 채널과 &#39;비공개 링크&#39;임을 명시하세요.</p>
<h2 id="3-투자자에게-보내는-메일-영상이-주인공">3. 투자자에게 보내는 메일 (영상이 주인공)</h2>
<p>레퍼런스 영상 링크를 메일 본문 한가운데에 두고, 텍스트는 짧게 유지하세요.</p>
<blockquote>
<p>제목: 고객 레퍼런스 — ○○ (성과 90초 영상 + 통화 가능 여부)</p>
<p>○○님, 안녕하세요.
due diligence 중 요청하신 고객 레퍼런스입니다. 먼저 고객 동의하에 만든 90초 성과 영상을 공유드립니다: [링크]
영상 시청 후 통화가 필요하시면, 고객 ○○○님과 20분 일정 후보를 잡아 드리겠습니다.
덱/계약 요약이 필요하면 바로 보내 드리겠습니다.</p>
</blockquote>
<p>순서가 중요합니다. <strong>영상 → (필요 시) 콜</strong>. 콜을 먼저 잡고 영상은 첨부만 하면, 투자자는 영상을 건너뛰고 콜에 들어옵니다. 그러면 다시 기초 설명부터 시작됩니다.</p>
<h2 id="4-하지-말아야-할-것">4. 하지 말아야 할 것</h2>
<ul>
<li><strong>고객을 영업 스크립트로 쓰기.</strong> &quot;투자하세요&quot;를 말하게 하면 레퍼런스가 아니라 광고가 됩니다. 고객의 역할은 본인 경험과 결과입니다.</li>
<li><strong>동의 범위를 넘는 공개.</strong> 내부용 비공개 링크를 블로그나 SNS에 올리지 마세요.</li>
<li><strong>모든 고객을 한 영상에 넣기.</strong> 한 고객, 한 결과. 두 번째 고객은 별도 클립으로.</li>
<li><strong>피치 영상을 재탕하기.</strong> 회사 소개 1분이 앞에 있으면 투자자는 레퍼런스라고 느끼지 않습니다.</li>
</ul>
<h2 id="5-미리-준비해-두면-라운드가-빨라진다">5. 미리 준비해 두면 라운드가 빨라진다</h2>
<p>레퍼런스 요청은 라운드 후반에 갑자기 옵니다. 그때 고객에게 처음 동의를 구하면 일정이 밀립니다. 가능하면 성공 고객 온보딩이 끝난 뒤, &quot;나중에 투자자 due diligence용 90초 클립을 비공개로 쓸 수 있을지&quot;를 미리 물어 두세요. 거절도 정중히 받아들이고, 동의한 고객만 목록으로 관리하세요.</p>
<p>촬영·편집을 매번 직접 하기 어렵다면 도구를 쓰는 것도 방법입니다. 예를 들어 <a href="https://axiomlab.online">Axiom Lab</a>은 이미 가진 덱, PDF, 이미지, 영상 자료를 바탕으로 투자자·고객·파트너용 짧은 프로젝트 영상을 만들어 주는 서비스입니다. 현재는 초대제 베타이며, 자료 분석은 먼저 무료로 받아 본 뒤 제작은 견적으로 안내된 금액을 선불 잔액에서 차감하는 방식이라 시작 전에 비용을 알 수 있습니다. 어떤 도구를 쓰든 핵심은 같습니다. 레퍼런스 콜 전에, 고객이 직접 말할 수 있는 &quot;이전 → 이후&quot;를 90초에 담아 두는 것입니다.</p>
<h2 id="마무리">마무리</h2>
<p>이번 주에 할 수 있는 일 하나만 고르세요. 가장 결과가 분명한 고객 한 명을 떠올리고, 동의 범위부터 메일로 확인한 뒤, 문제·선택·결과·다음 단계가 담긴 90초 초안을 만드세요. 투자자가 &quot;레퍼런스 통화할까요?&quot;라고 묻는 순간, 이미 맥락을 본 상태에서 일정을 잡게 되면 due diligence는 훨씬 짧아집니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Online Accommodation Market Size and Global-to-Regional Growth Outlook]]></title>
            <link>https://velog.io/@devashishw/Online-Accommodation-Market-Size-and-Global-to-Regional-Growth-Outlook</link>
            <guid>https://velog.io/@devashishw/Online-Accommodation-Market-Size-and-Global-to-Regional-Growth-Outlook</guid>
            <pubDate>Fri, 16 Oct 2026 03:29:53 GMT</pubDate>
            <description><![CDATA[<p>📅 Release Date: 16 Oct 2026 | ⏳ Forecast Period: 2026–2033<img src="blob:https://velog.io/26f6418a-3227-403a-b485-e3cadeca364b" alt="업로드중.."></p>
<p>According to Transpire Insight, the Online Accommodation Market size is estimated at USD 391.9 billion in 2026 and is anticipated to reach USD 637.8 million by 2033, growing at a CAGR of 7.21% from 2026 to 2033. The growth will be fueled by increasing online travel bookings, rising domestic and international tourism, and growing consumer preference for convenient digital reservations.</p>
<p>The market is expected to benefit from the expansion of vacation rentals, increasing smartphone adoption, and growing demand for personalized travel experiences. Advancements in AI-powered accommodation recommendations, mobile booking platforms, secure digital payments, and dynamic pricing solutions are also creating new opportunities for online travel agencies and accommodation providers.</p>
<p>Online Accommodation Market Size &amp; Revenue Landscape
The Online Accommodation Market includes digital platforms and services that allow travelers to search for, compare, and reserve accommodation. These platforms connect consumers with hotels, resorts, hostels, serviced apartments, vacation rentals, and other lodging providers. Revenue models commonly include booking commissions, service fees, advertising, and accommodation partnerships.</p>
<p>Market demand is influenced by tourism activity, smartphone usage, consumer preference for convenient booking processes, and the availability of detailed property information online. Platforms are increasingly focusing on user-friendly interfaces, transparent pricing, customer reviews, and flexible reservation policies to improve the booking experience.</p>
<p>Online Accommodation Market Key Drivers &amp; Demand Catalysts
One of the primary growth drivers is the increasing adoption of digital travel services. Consumers can compare accommodation prices, amenities, locations, and reviews across multiple properties without visiting individual providers. This convenience supports online reservations for business trips, family holidays, and leisure travel.</p>
<p>The expansion of tourism and international travel also contributes to market development. Travelers increasingly rely on digital platforms to arrange accommodation in familiar and unfamiliar destinations. The growth of alternative lodging, including vacation homes and serviced apartments, is broadening consumer choice and creating opportunities for accommodation marketplaces.</p>
<p>Technological innovation is another important catalyst. Artificial intelligence, recommendation engines, chatbots, and data analytics help platforms personalize search results, improve customer support, and present relevant accommodation options. Secure digital payments and mobile applications further simplify the booking process.</p>
<p>Strategic partnerships between online travel agencies, hotels, airlines, and local property operators are also strengthening market opportunities. However, price competition, changing travel demand, platform fees, and concerns about booking transparency remain important challenges for industry participants.</p>
<p>Read more: <a href="https://www.transpireinsight.com/report/online-accommodation-market">https://www.transpireinsight.com/report/online-accommodation-market</a> </p>
<p>Online Accommodation Market Regional Insights &amp; Growth Outlook
United States Online Accommodation Market: Regional Forecast (2026–2033)
The U.S. market benefits from established online travel platforms, domestic tourism, and strong digital booking adoption. Demand for hotels, vacation rentals, and flexible accommodation options creates opportunities for online booking providers.</p>
<p>United Kingdom Online Accommodation Market: Regional Forecast (2026–2033)
The UK market is supported by domestic and international tourism, widespread internet access, and demand for convenient reservations. Personalized offers and alternative accommodation options contribute to market opportunities.</p>
<p>South Korea Online Accommodation Market: Regional Forecast (2026–2033)
South Korea&#39;s market is influenced by digital connectivity, domestic travel, and demand for mobile-friendly booking services. Technology-driven recommendations and streamlined payment experiences can support platform adoption.</p>
<p>Japan Online Accommodation Market: Regional Forecast (2026–2033)
Japan&#39;s market benefits from domestic tourism and international visitor demand. Online platforms help travelers compare accommodation options, explore destinations, and arrange reservations efficiently.</p>
<p>Asia-Pacific Online Accommodation Market: Regional Forecast (2026–2033)
Asia-Pacific offers opportunities through expanding tourism, rising digital adoption, and growing demand for diverse accommodation options. Markets such as India, China, Japan, and Southeast Asia provide opportunities for online booking platforms and lodging providers.</p>
<p>Frequently Asked Questions (FAQs)
Q1. What drives the Online Accommodation Market?
Digital booking adoption, tourism growth, mobile applications, and convenient accommodation comparisons are key drivers.</p>
<p>Q2. Which accommodation types are booked online?
Hotels, resorts, hostels, vacation rentals, serviced apartments, and guesthouses are commonly booked through online platforms.</p>
<p>Q3. How is AI influencing the market?
AI can support personalized recommendations, automated customer service, and more relevant search results.</p>
<p>Q4. What challenges affect online accommodation platforms?
Price competition, cancellations, customer trust, service fees, and changing travel demand can affect performance.</p>
<p>Q5. What are the future growth opportunities?
Alternative accommodation, mobile booking, personalized travel experiences, and expansion into emerging destinations offer potential opportunities.</p>
<p>Leading Companies in the Online Accommodation Market
The competitive landscape includes online travel agencies, accommodation marketplaces, and digital booking platforms.</p>
<p>Major Market Participants:</p>
<p>Booking Holdings</p>
<p>Expedia Group</p>
<p>Airbnb</p>
<p>Trip.com Group</p>
<p>Agoda</p>
<p>MakeMyTrip</p>
<p>Hotels.com</p>
<p>Tripadvisor</p>
<p>Traveloka</p>
<p>trivago</p>
<p>Comprehensive Market Segmentation
The Online Accommodation Market can be segmented by accommodation type, booking platform, and end-user category.</p>
<p>Type
Hotels and Resorts</p>
<p>Vacation Rentals</p>
<p>Hostels and Guesthouses</p>
<p>Serviced Apartments</p>
<p>Alternative Accommodation</p>
<p>Application
Leisure Travel</p>
<p>Business Travel</p>
<p>Family and Group Travel</p>
<p>Domestic Tourism</p>
<p>International Tourism</p>
<p>Latest Reports:</p>
<p><a href="https://medium.com/@devashish09wankhade/ground-coffee-beverage-market-size-share-growth-trends-forecast-2f12299b1224">https://medium.com/@devashish09wankhade/ground-coffee-beverage-market-size-share-growth-trends-forecast-2f12299b1224</a></p>
<p><a href="https://in.pinterest.com/pin/1083819466609697524/">https://in.pinterest.com/pin/1083819466609697524/</a></p>
<p><a href="https://medium.com/@devashish09wankhade/pickled-cucumber-market-size-share-growth-trends-forecast-f1d08bd15cdc">https://medium.com/@devashish09wankhade/pickled-cucumber-market-size-share-growth-trends-forecast-f1d08bd15cdc</a></p>
<p><a href="https://in.pinterest.com/pin/1083819466609625210/">https://in.pinterest.com/pin/1083819466609625210/</a></p>
<p><a href="https://in.pinterest.com/pin/1083819466609621288/">https://in.pinterest.com/pin/1083819466609621288/</a></p>
<p><a href="https://in.pinterest.com/pin/1083819466609619202/">https://in.pinterest.com/pin/1083819466609619202/</a></p>
<p><a href="https://www.quora.com/What-types-of-ground-coffee-are-typically-used-to-make-espresso-cappuccino-and-latte-How-do-they-differ-from-the-ground-coffee-used-for-a-regular-cup-of-black-coffee/answer/Devashish-217">https://www.quora.com/What-types-of-ground-coffee-are-typically-used-to-make-espresso-cappuccino-and-latte-How-do-they-differ-from-the-ground-coffee-used-for-a-regular-cup-of-black-coffee/answer/Devashish-217</a></p>
<p><a href="https://www.quora.com/How-accurate-is-robotic-technology-in-knee-replacement-surgery/answer/Devashish-217">https://www.quora.com/How-accurate-is-robotic-technology-in-knee-replacement-surgery/answer/Devashish-217</a></p>
<p><a href="https://www.quora.com/What-role-does-automation-play-in-modern-rail-systems/answer/Devashish-217">https://www.quora.com/What-role-does-automation-play-in-modern-rail-systems/answer/Devashish-217</a></p>
<p><a href="https://www.quora.com/What-is-a-printed-circuit-board-PCB-What-are-the-benefits-of-using-a-PCB/answer/Devashish-217">https://www.quora.com/What-is-a-printed-circuit-board-PCB-What-are-the-benefits-of-using-a-PCB/answer/Devashish-217</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Stable Diffusion을 이해하기 위한 U-Net·VAE·CLIP]]></title>
            <link>https://velog.io/@pink-frog/Stable-Diffusion%EC%9D%84-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%9C-U-NetVAECLIP</link>
            <guid>https://velog.io/@pink-frog/Stable-Diffusion%EC%9D%84-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%9C-U-NetVAECLIP</guid>
            <pubDate>Fri, 16 Oct 2026 03:28:41 GMT</pubDate>
            <description><![CDATA[<h1 id="til-stable-diffusion을-이해하기-위한-u-net·vae·clip">[TIL] Stable Diffusion을 이해하기 위한 U-Net·VAE·CLIP</h1>
<p>📅 2026.10.16</p>
<p>오늘은 Stable Diffusion을 구성하는 모델들을 배웠다. 처음에는 U-Net, VAE, CLIP이 각각 무슨 일을 하는지 구분하기 어려웠다. 각 모델의 입력과 출력을 따라가니 전체 흐름이 보였다.</p>
<p>이 글은 SD 1.x처럼 U-Net과 CLIP을 사용하는 구조를 기준으로 정리했다.</p>
<h2 id="1-diffusion-ldm-stable-diffusion의-관계">1. Diffusion, LDM, Stable Diffusion의 관계</h2>
<p><img src="https://velog.velcdn.com/images/pink-frog/post/07975742-bbaa-43a3-9e8d-21886ca022f2/image.png" alt="Diffusion·LDM·Stable Diffusion 비교"></p>
<p>Diffusion은 <strong>노이즈에서 출발해 단계적으로 데이터를 생성하는 방식</strong>이다. 학습할 때는 원본에 노이즈를 섞고, 모델이 그 노이즈를 예측하도록 한다. 생성할 때는 새로운 무작위 노이즈에서 시작한다.</p>
<p>LDM은 이 과정을 <strong>압축된 잠재 공간에서 수행하는 Diffusion 모델</strong>이다. 큰 이미지의 픽셀 전체를 매번 처리하는 대신 작은 잠재 표현을 갱신하고, 마지막에 디코더로 이미지로 바꾼다.</p>
<table>
<thead>
<tr>
<th>구분</th>
<th>픽셀 기반 Diffusion</th>
<th>LDM</th>
</tr>
</thead>
<tbody><tr>
<td>반복 계산 대상</td>
<td>이미지 픽셀</td>
<td>압축된 잠재 표현</td>
</tr>
<tr>
<td>생성 결과</td>
<td>이미지</td>
<td>잠재 표현을 디코딩한 이미지</td>
</tr>
<tr>
<td>특징</td>
<td>큰 공간에서 계산</td>
<td>반복 계산의 시간·메모리 부담을 줄임</td>
</tr>
</tbody></table>
<p>Stable Diffusion은 LDM을 활용한 대표적인 이미지 생성 모델이다. 텍스트 조건을 더하면 “우주복을 입은 고양이”처럼 원하는 내용을 지정할 수 있다. <strong>LDM은 Diffusion의 한 종류이고, Stable Diffusion은 LDM을 기반으로 한다.</strong> <a href="https://arxiv.org/abs/2112.10752">LDM 논문</a></p>
<h2 id="2-u-net-특징을-추출하고-세부-정보를-연결한다">2. U-Net: 특징을 추출하고 세부 정보를 연결한다</h2>
<p><img src="https://velog.velcdn.com/images/pink-frog/post/505257fd-d12f-4496-980b-293712773288/image.png" alt="U-Net과 Convolution·Max Pooling·Skip Connection"></p>
<p>U-Net은 공간 크기를 줄이는 인코더와 다시 키우는 디코더로 구성된다. 원래는 이미지의 각 픽셀이 어느 클래스인지 예측하는 분할 모델로 제안됐다.</p>
<p>고양이 사진을 넣었을 때 “이 픽셀은 고양이, 저 픽셀은 배경”을 예측하는 식이다. 다만 <strong>U-Net은 구조 이름</strong>이다. Stable Diffusion에서는 같은 구조를 확장해 노이즈를 예측한다.</p>
<h3 id="conv와-pooling은-역할이-다르다">Conv와 Pooling은 역할이 다르다</h3>
<p><strong>Convolution은 학습 가능한 필터로 특징을 계산하는 연산</strong>이다. 작은 영역의 값과 필터를 곱해 더하고, 같은 필터를 이동시키며 특징 지도(Feature Map)를 만든다. 여러 필터를 사용하면 여러 채널이 생긴다.</p>
<p><strong>Max Pooling은 영역의 최댓값을 선택해 공간 크기를 줄인다.</strong> 일반적인 Max Pooling에는 학습 가중치가 없으며, 채널 수는 유지한다.</p>
<pre><code class="language-text">256×256×64
  ↓ 2×2 Max Pooling, stride=2
128×128×64</code></pre>
<p>위 표기는 높이×너비×채널이다. 합성곱이 채널을 늘리는 것과 풀링이 공간을 줄이는 것을 구분해야 한다.</p>
<h3 id="skip-connection이-필요한-이유">Skip Connection이 필요한 이유</h3>
<p>공간을 줄이면 넓은 영역의 정보를 처리하기 쉬워지지만, 정확한 경계와 위치 정보는 일부 사라진다. 크기를 다시 키운다고 그 정보가 저절로 돌아오지는 않는다.</p>
<p>U-Net은 인코더의 중간 특징을 같은 공간 크기의 디코더에 직접 전달한다. 이것이 <strong>Skip Connection</strong>이다. 원래 U-Net에서는 두 특징을 채널 방향으로 이어 붙이는 <strong>Concatenation</strong>을 사용한다.</p>
<pre><code class="language-text">인코더 특징 64×64×256 ─┐
                       ├─ Concat → 64×64×512
디코더 특징 64×64×256 ─┘</code></pre>
<p>깊은 경로에서 얻은 전체 맥락에 인코더의 세밀한 정보를 더해 위치별 예측을 만든다. <a href="https://arxiv.org/abs/1505.04597">U-Net 논문</a></p>
<h2 id="3-ae와-vae-이미지와-잠재-표현-사이를-변환한다">3. AE와 VAE: 이미지와 잠재 표현 사이를 변환한다</h2>
<p><img src="https://velog.velcdn.com/images/pink-frog/post/1cec31dd-344e-430f-853b-dc5e20628d87/image.png" alt="AE와 VAE 비교"></p>
<p>AE는 입력을 작은 잠재 표현으로 압축한 뒤 원본과 비슷하게 복원하는 모델이다.</p>
<pre><code class="language-text">이미지 → Encoder → 잠재 표현 z → Decoder → 복원 이미지</code></pre>
<p>VAE는 인코더가 잠재값 하나 대신 <strong>잠재 분포의 평균과 분산</strong>을 출력한다. 이 분포에서 z를 샘플링해 디코더에 전달한다.</p>
<p>$$
z=\mu+\sigma\odot\epsilon,\qquad \epsilon\sim\mathcal N(0,I)
$$</p>
<p>이를 재매개변수화 기법이라고 한다. 무작위성은 ε에서 얻고, 평균과 표준편차에는 미분 가능한 계산을 적용해 인코더까지 학습 신호를 전달한다.</p>
<p>VAE는 두 목표를 함께 학습한다.</p>
<ul>
<li><strong>재구성 손실:</strong> 원본과 비슷하게 복원한다.</li>
<li><strong>KL 발산:</strong> 입력별 잠재 분포가 기준 사전분포에서 지나치게 벗어나지 않게 한다.</li>
</ul>
<p>이 둘을 포함한 ELBO를 최대화하며, 손실로 구현할 때는 부호를 뒤집어 최소화한다. <a href="https://arxiv.org/abs/1312.6114">VAE 논문</a></p>
<h3 id="z는-벡터-하나로만-표현되지-않는다">z는 벡터 하나로만 표현되지 않는다</h3>
<p>SD 1.x의 VAE는 공간 구조가 남은 잠재 텐서를 사용한다.</p>
<pre><code class="language-text">입력 이미지: [3, 512, 512]
잠재 표현 z: [4, 64, 64]</code></pre>
<p>입력의 3은 RGB 채널이다. 잠재 표현의 4는 모델이 사용하도록 설계된 특징 채널 수다. 각 채널이 ‘형태·색·위치·질감’처럼 정해진 역할을 맡는 것은 아니다.</p>
<p>이미지 한 장의 z는 [C,H,W], 배치를 포함하면 [B,C,H,W]다. 샘플링 전후에도 평균·표준편차·ε·z의 모양은 같다.</p>
<h2 id="4-clip-이미지와-문장의-관련성을-학습한다">4. CLIP: 이미지와 문장의 관련성을 학습한다</h2>
<p><img src="https://velog.velcdn.com/images/pink-frog/post/2f81cf7f-6b3e-46d4-9c6f-155a6f489e54/image.png" alt="CLIP의 구조와 대조학습"></p>
<p>CLIP은 이미지와 문장을 각각 벡터로 바꿔 관련성을 비교한다.</p>
<pre><code class="language-text">이미지 → Image Encoder → 이미지 벡터 ─┐
                                      ├─ 유사도 비교
문장   → Text Encoder  → 텍스트 벡터 ─┘</code></pre>
<p>이미지 인코더에는 CNN 계열인 ResNet 또는 ViT를 사용한다. CNN은 작은 영역의 특징을 층을 거치며 결합한다. ViT는 이미지를 패치로 나누고, 패치 사이의 관계를 Self-Attention으로 계산한다.</p>
<h3 id="원래-짝의-점수가-높아지도록-학습한다">원래 짝의 점수가 높아지도록 학습한다</h3>
<p>이미지·문장 N쌍이 있으면 모든 조합인 N×N개의 유사도를 계산한다. 고양이 사진은 고양이 설명과, 강아지 사진은 강아지 설명과 높은 점수를 받도록 학습한다.</p>
<p>여기에 사용하는 InfoNCE는 <strong>후보들 사이에서 정답 쌍에 부여한 비중이 클수록 손실이 작아지는 방식</strong>이다. CLIP은 이미지에서 문장을 찾는 방향과 문장에서 이미지를 찾는 방향을 모두 학습한다.</p>
<p>학습 후에는 사진과 “고양이 사진”, “강아지 사진” 같은 후보 문장을 비교해 분류할 수 있다. 이것이 Zero-shot 분류다. 해당 분류 작업에 맞춘 추가 학습이 없다는 뜻이며, CLIP 자체의 사전학습까지 없었다는 뜻은 아니다. <a href="https://github.com/openai/CLIP">CLIP 공식 구현</a></p>
<p>Stable Diffusion에서는 CLIP의 텍스트 인코더가 프롬프트를 조건 정보로 바꾼다. 이때는 문장 대표 벡터 하나보다 <strong>토큰별 표현의 시퀀스</strong>를 사용한다.</p>
<h2 id="5-stable-diffusion에서-이미지가-만들어지는-과정">5. Stable Diffusion에서 이미지가 만들어지는 과정</h2>
<p>각 구성 요소의 역할을 나누면 다음과 같다.</p>
<table>
<thead>
<tr>
<th>구성 요소</th>
<th>역할</th>
</tr>
</thead>
<tbody><tr>
<td>VAE Encoder</td>
<td>입력 이미지를 잠재 표현으로 변환</td>
</tr>
<tr>
<td>CLIP Text Encoder</td>
<td>프롬프트를 조건 정보로 변환</td>
</tr>
<tr>
<td>U-Net</td>
<td>현재 잠재 표현의 노이즈 예측</td>
</tr>
<tr>
<td>Scheduler</td>
<td>예측을 이용해 다음 잠재 상태 계산</td>
</tr>
<tr>
<td>VAE Decoder</td>
<td>완성된 잠재 표현을 이미지로 변환</td>
</tr>
</tbody></table>
<p>Text-to-Image 생성은 무작위 잠재 노이즈에서 시작한다.</p>
<pre><code class="language-text">프롬프트 → 텍스트 인코더 → 조건 정보
                              ↓
잠재 노이즈 → U-Net → Scheduler → 다음 잠재 표현
                ↑                    │
                └────── 반복 ────────┘
                              ↓
                        VAE Decoder
                              ↓
                          최종 이미지</code></pre>
<p><strong>U-Net이 노이즈를 예측하고, 스케줄러가 실제 상태를 갱신한다.</strong> 새 이미지를 만드는 경우 원본 이미지가 없으므로 VAE Encoder는 필요하지 않다. <a href="https://huggingface.co/docs/diffusers/api/pipelines/stable_diffusion/text2img">Stable Diffusion 구성</a></p>
<h3 id="텍스트-조건은-cross-attention으로-반영한다">텍스트 조건은 Cross-Attention으로 반영한다</h3>
<p>U-Net의 중간 특징은 공간 축을 펼쳐 위치별 벡터로 만든다.</p>
<pre><code class="language-text">[B, C, H, W] → [B, H×W, C]</code></pre>
<p>전체를 벡터 하나로 합치는 것이 아니다. 각 위치의 벡터에서 Q를 만들고, 텍스트 토큰 특징에서 K와 V를 만든다. 이미지의 각 위치가 어떤 텍스트 정보를 얼마나 참고할지 계산한 뒤 다시 공간 형태로 바꿔 다음 연산에 사용한다.</p>
<h3 id="cfg는-조건의-영향을-강조한다">CFG는 조건의 영향을 강조한다</h3>
<p>CFG는 조건부 예측과 조건 없는 예측의 차이를 이용한다.</p>
<p>$$
\hat\epsilon
=\epsilon_{\mathrm{uncond}}
+s(\epsilon_{\mathrm{cond}}-\epsilon_{\mathrm{uncond}})
$$</p>
<p>Guidance Scale인 s가 클수록 프롬프트가 만드는 방향을 강조한다. 다만 너무 크면 결과가 부자연스러워질 수 있다. 조건을 강하게 적용하는 것과 이미지 품질이 좋아지는 것은 항상 같지 않다. <a href="https://arxiv.org/abs/2207.12598">CFG 논문</a></p>
<h2 id="6-학습하면서-가장-헷갈렸던-부분">6. 학습하면서 가장 헷갈렸던 부분</h2>
<p>VAE의 샘플링과 Diffusion의 Noising에서 모두 ε가 등장해 두 과정을 섞어 생각했다.</p>
<p>VAE의 ε는 평균과 표준편차로부터 잠재 표현을 뽑는 데 사용한다. Diffusion의 ε는 깨끗한 잠재 표현에 섞는 노이즈다. <strong>Noising에서는 ε를 직접 뽑고, Denoising 학습에서는 모델이 그 ε를 예측한다.</strong></p>
<p>또 하나는 U-Net이 이미지를 바로 출력한다고 생각했던 점이다. Stable Diffusion의 U-Net은 노이즈를 예측한다. 잠재 상태를 바꾸는 것은 스케줄러이고, 최종 이미지를 만드는 것은 VAE Decoder다.</p>
<p>이미지 변환이나 Inpainting도 이 흐름을 활용한다. Image-to-Image는 원본을 압축한 뒤 노이즈를 섞은 상태에서 시작하고, Inpainting은 마스크로 수정할 영역을 지정한다. 각 단계의 입력과 출력을 구분하고 나니 모델들이 왜 함께 필요한지 이해할 수 있었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[DONJOY, HIGH-QUALITY SANITARY EXHAUST VALVE SUPPLIER]]></title>
            <link>https://velog.io/@donjoyvalve/DONJOY-HIGH-QUALITY-SANITARY-EXHAUST-VALVE-SUPPLIER</link>
            <guid>https://velog.io/@donjoyvalve/DONJOY-HIGH-QUALITY-SANITARY-EXHAUST-VALVE-SUPPLIER</guid>
            <pubDate>Fri, 16 Oct 2026 03:27:42 GMT</pubDate>
            <description><![CDATA[<p>It is mainly used in equipment such as piping systems, radiators, hydraulic systems, or water pumps. Its core function is to automatically vent air from pipes or containers, while preventing external personnel from entering, thus ensuring the normal operation, efficiency, and safety of the system.</p>
<p>There are many <a href="https://www.donjoy-valve.com/products/sanitary-exhaust-valve/">exhaust valve suppliers</a>, but we are the best choice for you.</p>
<p>If you are looking for a reliable <a href="https://www.donjoy-valve.com/products/sanitary-exhaust-valve/">exhaust valve factory</a>, please contact us.</p>
<p><img src="https://velog.velcdn.com/images/donjoyvalve/post/b30c3121-546c-40fd-bdaa-9eb88df87df1/image.jpg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[DONJOY OFFERS PREMIUM SANITARY DIVERTER VALVES]]></title>
            <link>https://velog.io/@donjoyvalve/DONJOY-OFFERS-PREMIUM-SANITARY-DIVERTER-VALVES</link>
            <guid>https://velog.io/@donjoyvalve/DONJOY-OFFERS-PREMIUM-SANITARY-DIVERTER-VALVES</guid>
            <pubDate>Fri, 16 Oct 2026 03:27:04 GMT</pubDate>
            <description><![CDATA[<p>Introducing Donjoy&#39;s Sanitary Diverter Valve, designed for high-purity applications with exceptional cleanliness and reliability. This valve features synchronized three-way operation, ensuring low torque, long service life, and zero dead zones for contamination-free flow. Certified with 3-A, FDA, and CE standards, it is ideal for the biopharmaceutical, food, and beverage industries. Enhance your process efficiency and safety – contact Donjoy now for a customized solution</p>
<p>WHAT IS THE INDUSTRY SOLUTIONS OF SANITARY DIVERTER VALVE?
DONJOY&#39;s sanitary diverter valves, are engineered for critical flow direction applications in biopharmaceuticals, food &amp; beverage, and dairy industries. They enable seamless product diverting, CIP/SIP processes, and batching with zero cross-contamination, ensuring full compliance with 3-A and FDA standards for hygienic processing.</p>
<p>WHAT IS THE ADVANTAGE OF SANITARY DIVERTER VALVE?</p>
<p>The key advantages include zero dead legs, smooth internal surfaces, and flexible L/T-type flow patterns that prevent media retention. These valves offer durable, leak-proof performance with easy maintenance, supporting automation integration for enhanced operational efficiency and reduced total cost of ownership.</p>
<p>For more information about <a href="https://www.donjoy-valve.com/products/sanitary-diverter-valve/">diverter valve manual</a>, please feel free to contact us!</p>
<p>There are many <a href="https://www.donjoy-valve.com/products/sanitary-diverter-valve/">diverter valve manufacturers</a>, but we are the best choice for you.</p>
<p>If you are looking for a reliable <a href="https://www.donjoy-valve.com/products/sanitary-diverter-valve/">diverter valve factory</a>, please contact us.</p>
<p><img src="https://velog.velcdn.com/images/donjoyvalve/post/a147b722-1b87-4c12-b980-54404b3f2453/image.jpg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[DONJOY HIGH-QUALITY SANITARY DIAPHRAGM VALVES]]></title>
            <link>https://velog.io/@donjoyvalve/DONJOY-HIGH-QUALITY-SANITARY-DIAPHRAGM-VALVES</link>
            <guid>https://velog.io/@donjoyvalve/DONJOY-HIGH-QUALITY-SANITARY-DIAPHRAGM-VALVES</guid>
            <pubDate>Fri, 16 Oct 2026 03:26:18 GMT</pubDate>
            <description><![CDATA[<p>Donjoy&#39;s Sanitary Diaphragm Valves provide a high-purity solution with over 20 years of expertise. Available in 2-way, 3-way, and multi-port designs, it supports manual, pneumatic, or electric operation. Certified to 3-A, and compliant with FDA regulations, it ensures hygiene for biopharmaceutical, food, and beverage applications. Choose reliability—contact us for a quote today.</p>
<p>ADVANTAGE OF DONJOY SANITARY DIAPHRAGM VALVE
01
Zero Deadleg Design: Ensuring True Aseptic Processing
DONJOY&#39;s diaphragm valves feature a smooth, crevice-free flow path where the flexible diaphragm isolates the media, guaranteeing zero deadlegs. This prevents contamination, supports full drainage, and ensures true aseptic processing for critical applications like biopharma. It meets 3-A and FDA standards for uncompromised hygiene.</p>
<p>02
Multi-Port and Block Valve Configurations for Complex Manifolds
Our diaphragm valves offer multi-port (such as T-type and U-type) and block-and-bleed configurations, enabling versatile flow diversion and isolation in complex manifolds. This simplifies piping, reduces connections, and enhances reliability in multi-line systems for pharmaceuticals and food processing.</p>
<p>03
CIP/SIP Reliability and Sterilization Performance
Engineered for robust CIP/SIP cycles, DONJOY&#39;s diaphragm valves provide reliable sterilization with materials like EPDM,  or PTFE+EPDM seals. They withstand high temperatures and pressures, ensuring consistent performance and compliance with strict hygiene protocols in demanding environments.</p>
<p>There are many <a href="https://www.donjoy-valve.com/products/sanitary-diaphragm-valve/">diaphragm valve manufacturers</a>, but we are the best choice for you.</p>
<p>If you are looking for a reliable <a href="https://www.donjoy-valve.com/products/sanitary-diaphragm-valve/">diaphragm valve factory</a>, please contact us.</p>
<p><img src="https://velog.velcdn.com/images/donjoyvalve/post/c0a75634-8840-46ac-96e2-ae5ede08f452/image.jpg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ROS 2] A* 경로 주행에 라이다 비상정지와 수동 해제 조건 연결하기]]></title>
            <link>https://velog.io/@waterhyunsu/ros2-turtlebot3-astar-lidar-emergency-stop</link>
            <guid>https://velog.io/@waterhyunsu/ros2-turtlebot3-astar-lidar-emergency-stop</guid>
            <pubDate>Fri, 16 Oct 2026 03:26:01 GMT</pubDate>
            <description><![CDATA[<p>지도에서 목적지까지 경로를 찾는 것과, 로봇이 멈춰야 할 때 멈추게 하는 것은 별개의 문제였다. 이번 실습에서는 ROS 2 Humble과 TurtleBot3 Gazebo 환경에서 A* 경로 계획·주행을 구현하고, 수동 비상정지와 전방 라이다를 이용한 정지 조건을 연결했다.</p>
<p>정지 후 장애물이 사라졌다고 곧바로 출발하지 않도록 만드는 데도 신경을 썼다. 정지 상태를 유지하고, 전방 여유를 확인한 뒤 수동으로 해제하며, 새 목표를 받아야 다시 주행하는 구조로 정리했다.</p>
<blockquote>
<p>확인 범위: Gazebo 주행·수동 비상정지는 직접 확인했다. 라이다 정지 로직은 합성 LaserScan을 사용하는 테스트 5개로 확인했다. Gazebo에 장애물을 배치해 자동 정지를 시연하는 과정과 실물 로봇 검증은 아직 수행하지 않았다.</p>
</blockquote>
<h2 id="1-실행-환경과-데이터-흐름">1. 실행 환경과 데이터 흐름</h2>
<ul>
<li>Ubuntu 22.04 / WSL</li>
<li>ROS 2 Humble, Python</li>
<li>TurtleBot3 Waffle Pi, Gazebo, RViz</li>
<li>지도 서버와 AMCL을 이용한 위치 추정</li>
</ul>
<p>주행 노드는 <code>/map</code>의 점유격자와 TF <code>map → base_footprint</code>를 이용한다. RViz에서 <code>/goal_pose</code>로 목표를 보내면 경로를 계산하고, <code>/astar_path</code>로 경로를 표시하며 <code>/cmd_vel</code>로 속도 명령을 발행한다.</p>
<p>라이다 입력은 <code>/scan</code>으로 받고, 수동 정지·해제는 <code>/emergency_stop</code>의 <code>SetBool</code> 서비스로 처리한다.</p>
<p><img src="https://velog.velcdn.com/images/waterhyunsu/post/0c41c5be-23d8-49c0-be4b-5ea3375c3612/image.png" alt=""></p>
<p>RViz와 Gazebo를 나란히 놓은 실행 화면이다. 목표 지정 후 <code>A* path found</code> 로그를 확인했고, 로봇이 경로를 따라 이동하는 동작을 확인했다. 정지 화면 한 장만으로 주행 전체를 입증하는 것은 아니므로, 실행 캡처와 직접 확인한 동작을 구분해 기록했다.</p>
<h2 id="2-점유격자에서-경로를-계산하고-따라가기">2. 점유격자에서 경로를 계산하고 따라가기</h2>
<p>A*는 지도의 셀을 노드로 보고 상하좌우 4방향으로 탐색한다. 이동 비용과 맨해튼 거리 휴리스틱을 합친 값을 우선순위 큐에 넣어 다음 탐색 위치를 선택한다.</p>
<p>현재 코드에서는 알려진 빈 공간 중 점유값이 65보다 작은 셀을 이동 가능한 공간으로 취급한다. 지도 밖의 시작점·목표점이나, 미확인·점유 셀에 놓인 시작점·목표점은 경로 계획에서 거부한다.</p>
<p>계산한 격자 경로는 셀 중심의 월드 좌표로 변환한다. 주행 중에는 TF에서 현재 위치와 방향을 받아 다음 경로점까지의 거리와 방향 오차를 계산한다. 방향 오차가 크면 회전하고, 작아지면 전진하도록 속도 명령을 만든다.</p>
<p>현재 구현은 학습용 경로 탐색·추종 구조다. 로봇의 외곽 크기를 반영한 장애물 팽창과 국소 회피까지 구현한 것은 아니다. 점유격자상의 경로가 존재한다는 사실과 실제 로봇이 충돌 없이 통과할 수 있다는 조건은 구분해야 한다.</p>
<h2 id="3-수동-비상정지-속도뿐-아니라-경로도-비우기">3. 수동 비상정지: 속도뿐 아니라 경로도 비우기</h2>
<p>정지 명령에서 0 속도만 한 번 발행하면, 다음 제어 주기에 기존 경로를 따라가는 명령이 다시 나갈 수 있다. 그래서 정지 시 다음 상태를 함께 정리했다.</p>
<ul>
<li>정지 상태를 유지한다.</li>
<li>기존 경로와 목표를 삭제한다.</li>
<li>RViz에 발행하는 경로도 비운다.</li>
<li>제어 주기마다 0 속도를 반복 발행한다.</li>
<li>정지 중에는 새 목표를 거부한다.</li>
</ul>
<pre><code class="language-bash">ros2 service call /emergency_stop std_srvs/srv/SetBool &quot;{data: true}&quot;</code></pre>
<p><img src="https://velog.velcdn.com/images/waterhyunsu/post/125d1c80-ab23-42a1-b5c4-6b5741c4df60/image.png" alt=""></p>
<p>새 목표로 주행하는 도중 정지를 요청했다. 서비스의 <code>success=True</code>, <code>Emergency stop engaged</code> 응답과 주행 노드의 <code>Manual emergency stop engaged</code> 로그를 확인했고, 목적지에 도착하기 전에 로봇이 멈추는 동작을 확인했다.</p>
<p>이 기능은 해당 노드의 속도 출력을 제어하는 소프트웨어 정지다. 다른 노드가 <code>/cmd_vel</code>을 동시에 발행하는 상황이나 하드웨어 비상정지 장치를 대체하는 기능으로 보지는 않는다. 노드를 재시작하면 정지 상태도 초기화된다.</p>
<h2 id="4-전방-라이다-정지와-해제-조건">4. 전방 라이다 정지와 해제 조건</h2>
<p>라이다 프레임의 +X가 로봇 전방이라는 전제로, 전방 ±20° 영역에서 사용할 수 있는 거리값 중 최솟값을 확인한다.</p>
<table>
<thead>
<tr>
<th>항목</th>
<th>기본값</th>
<th>동작</th>
</tr>
</thead>
<tbody><tr>
<td>검사 영역</td>
<td>전방 ±20°</td>
<td>옆·뒤 장애물은 이 기능의 검사 범위에서 제외</td>
</tr>
<tr>
<td>정지 거리</td>
<td>0.4m 이하</td>
<td>정지 상태로 전환하고 경로 삭제</td>
</tr>
<tr>
<td>해제 거리</td>
<td>0.5m 이상</td>
<td>최신 유효 스캔이 있을 때 수동 해제 허용</td>
</tr>
<tr>
<td>스캔 유효 시간</td>
<td>1.0초</td>
<td>주행 중 수신 중단 시 정지, 해제 요청 시 오래된 데이터 거부</td>
</tr>
</tbody></table>
<p>거리는 로봇 외곽이 아니라 라이다 센서 기준이다. 장애물 종류를 인식하는 객체 검출 기능도 아니다.</p>
<p>NaN, 음의 무한대, 0, 유한 측정 범위 밖의 값은 제외한다. 양의 무한대는 측정 범위 안에 반사체가 없는 것으로 취급한다. 전방 데이터가 모두 무효라면 해제를 허용하지 않는다.</p>
<p>정지 거리와 해제 거리를 다르게 두었다. 0.4m에서 정지한 뒤 전방 거리가 0.45m가 됐다고 바로 해제되지 않도록, 해제에는 0.5m 이상의 여유를 요구한다. 장애물이 사라져도 자동으로 정지를 풀지는 않는다.</p>
<pre><code class="language-bash">ros2 service call /emergency_stop std_srvs/srv/SetBool &quot;{data: false}&quot;</code></pre>
<p>해제에 성공해도 기존 경로는 복원하지 않는다. RViz에서 새 목표를 지정해야 다시 움직인다.</p>
<h2 id="5-실행-중-만난-상황">5. 실행 중 만난 상황</h2>
<h3 id="목표를-줬는데-움직이지-않음">목표를 줬는데 움직이지 않음</h3>
<p>로그에 <code>Laser emergency stop: 0.12 m ahead</code>가 나온 뒤, 새 목표에 <code>Goal rejected: emergency stop engaged</code>가 표시됐다. 시작 위치에서 가까운 전방 측정값으로 정지가 걸린 상태였던 것이다.</p>
<p>Gazebo에서 로봇을 빈 공간으로 옮긴 뒤 수동 해제를 요청했다. RViz의 <code>2D Pose Estimate</code>는 위치 추정에 초기값을 주는 기능이므로, Gazebo 로봇의 물리 위치를 직접 옮기는 것과 구분해서 처리했다.</p>
<h3 id="정지-명령을-입력했는데-계속-이동함">정지 명령을 입력했는데 계속 이동함</h3>
<p>처음 정지 요청은 <code>waiting for service to become available...</code>에서 대기했다. 이 상태는 아직 서비스 응답을 받지 못한 상태다. 같은 터미널에서 ROS 환경과 워크스페이스를 불러오고 <code>ROS_DOMAIN_ID=0</code>을 맞춘 뒤, 실제 요청·성공 응답까지 확인했다. 이전 터미널 설정값을 기록하지 않았기 때문에 원인을 특정 환경 변수 하나로 단정하지는 않았다.</p>
<pre><code class="language-bash">source /opt/ros/humble/setup.bash
source ~/turtlebot3_ws/install/setup.bash
export ROS_DOMAIN_ID=0</code></pre>
<p>명령을 입력했다는 사실과 서비스가 요청을 받았다는 사실을 구분해야 했다. 이후 주행 중 정지 요청과 실제 정지 동작을 다시 확인했다.</p>
<h3 id="해제-요청이-거부됨">해제 요청이 거부됨</h3>
<p>다음 응답도 확인했다.</p>
<pre><code class="language-text">success=False
Release denied: front clearance must be at least 0.50 m</code></pre>
<p>전방 여유 조건을 만족하지 못하면 해제가 거부되도록 만든 동작이다. 빈 공간으로 옮긴 뒤 해제 성공을 확인하고, 초기 위치 추정과 새 목표를 다시 지정했다.</p>
<h2 id="6-합성-laserscan-테스트-5개">6. 합성 LaserScan 테스트 5개</h2>
<p>시뮬레이션 실행 도메인과 분리된 ROS 도메인 87에서 테스트를 실행했다. 테스트가 실제 주행 노드의 토픽과 섞이지 않도록 구분했다.</p>
<pre><code class="language-bash">source /opt/ros/humble/setup.bash
source ~/turtlebot3_ws/install/setup.bash
cd ~/turtlebot3_ws/src/astar_navigation
ROS_DOMAIN_ID=87 python3 -m unittest discover -s test -p test_lidar_safety.py -v</code></pre>
<p><img src="https://velog.velcdn.com/images/waterhyunsu/post/6452837a-9c86-4c9c-a321-964fd0b267b2/image.png" alt=""></p>
<p>캡처에서 <code>Ran 5 tests in 2.735s</code>와 <code>OK</code>를 확인했다. 2.735초는 이 실행의 테스트 소요 시간이며 제어 응답 시간을 뜻하지 않는다.</p>
<p>테스트 항목은 다음과 같다.</p>
<ol>
<li>전방 장애물 감지 시 정지·경로 삭제·0 속도 발행, 여유 거리 확보 전 해제 거부, 수동 해제 후 경로가 비어 있는 상태 확인</li>
<li>후방 장애물 제외와 수동 정지·해제</li>
<li>오래된 스캔에 대한 해제 거부와 주행 중 정지</li>
<li>무효 스캔에 대한 해제 거부</li>
<li>각도 경계·NaN·무한대·잘못된 스캔 설정 처리</li>
</ol>
<p>이 테스트는 합성 메시지를 이용한 로직과 ROS 통신 확인이다. Gazebo에서 상자를 배치해 자동 정지하는 시각적 시연이나 실물 환경 검증을 완료했다는 의미로 확대하지 않았다.</p>
<h2 id="7-이번-실습에서-정리한-점">7. 이번 실습에서 정리한 점</h2>
<p>경로 계획을 구현한 뒤에는 정지와 해제 조건도 상태 관리로 다뤄야 했다. 특히 정지 후 기존 목표를 남길지, 해제 후 자동 출발을 허용할지처럼 작은 선택이 실제 동작에 영향을 줬다.</p>
<p>앞으로는 지도·위치 추정 오차, 로봇 크기를 고려한 경로 여유, 실제 센서 환경에서의 정지 동작을 더 확인할 필요가 있다. 현재까지 확인한 결과와 아직 확인하지 않은 범위를 나누어 기록하고 다음 실습으로 이어가려 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[The Era of Reading Reviews Before Purchasing | User Ratings and Consumer Psychology for Adult Products]]></title>
            <link>https://velog.io/@askfjkasf/The-Era-of-Reading-Reviews-Before-Purchasing-User-Ratings-and-Consumer-Psychology-for-Adult-Products</link>
            <guid>https://velog.io/@askfjkasf/The-Era-of-Reading-Reviews-Before-Purchasing-User-Ratings-and-Consumer-Psychology-for-Adult-Products</guid>
            <pubDate>Fri, 16 Oct 2026 03:26:00 GMT</pubDate>
            <description><![CDATA[<h2 id="一、日本の消費者が口コミに依存する背景">一、日本の消費者が口コミに依存する背景</h2>
<h3 id="11オンラインショッピングの普及と情報取得の敷居の低下">1.1オンラインショッピングの普及と情報取得の敷居の低下</h3>
<p>日本の電子商取引プラットフォーム、ソーシャルシェアリングサイトが発展しており、消費者は他のバイヤーのリアルな評価を検索しやすい。店員の売り込みよりも、見知らぬ人の使い心地の方が信頼を得やすい。</p>
<h3 id="12大人向け商品特有の心理的障壁">1.2大人向け商品特有の心理的障壁</h3>
<p>大人は<a href="https://www.beyourlover.co.jp/vibe/">バイブ</a>に対して一定のプライバシーを持っており、オフラインでの問い合わせは店員に詳しく質問するのが恥ずかしいことが多く、面と向かって詳細を聞きたくない人が多い。口コミは重要な参考ルートになります。
<img src="https://velog.velcdn.com/images/askfjkasf/post/7d8279a3-2e6a-4e91-a824-64b93760e0cc/image.jpg" alt=""></p>
<h2 id="二、成人商品評価における消費者が重点的に注目する情報">二、成人商品評価における消費者が重点的に注目する情報</h2>
<h3 id="21使用感、騒音、サイズなど商品ページから読み取りにくい詳細">2.1使用感、騒音、サイズなど商品ページから読み取りにくい詳細</h3>
<p>商品紹介はスペックだけを書くことが多いですが、評価では実際の感触、運転音量、収納の難易度が取り上げられており、一人暮らしの人が非常に気になるポイントです。</p>
<h3 id="22材質安全性とアフターサービス状況">2.2材質安全性とアフターサービス状況</h3>
<p>多くのバイヤーは材質、洗いやすさ、故障対応、返品ルールに関するコメントを重点的にめくり、雷を踏むリスクを下げる。</p>
<h2 id="三、口コミの背後にある消費者心理">三、口コミの背後にある消費者心理</h2>
<h3 id="31不安を回避し、試行錯誤コストを削減">3.1不安を回避し、試行錯誤コストを削減</h3>
<p>価格、体験は人それぞれで、直接注文するのは心理的負担がかかりやすい。大量の評価を見ると、消費者が不安を緩和し、穏当な選択をするための方法である。</p>
<h3 id="32「自分と同じ人」を探す">3.2「自分と同じ人」を探す</h3>
<p>消費者は好評そのものよりも、立場の近い利用者（一人暮らしや夜間利用の人）を見つけ、他人の経験を通じて自分の選択が合理的かどうかを確認したいと考えている。</p>
<h2 id="四、商店への啓示">四、商店への啓示</h2>
<h3 id="41意図的に美化せず、真実の評価の方が説得力がある">4.1意図的に美化せず、真実の評価の方が説得力がある</h3>
<p>過度に修飾された好評はかえって疑問を引き起こしやすい。日本の消費者は、装飾を加えずに長所と短所を書く客観的なメッセージを重視している。
<img src="https://velog.velcdn.com/images/askfjkasf/post/1e7d88ab-59f2-4eaa-8547-e8184f8d9342/image.jpg" alt=""></p>
<h3 id="42マイナス評価を直視し、疑問に積極的に対応する">4.2マイナス評価を直視し、疑問に積極的に対応する</h3>
<p>評価内の疑問に丁寧に返信することで、プライバシー<a href="https://www.beyourlover.co.jp/dildo/">ディルド</a>特有の不信感を和らげ、ユーザーの好感を高めることができます。</p>
<h2 id="結語">結語</h2>
<p>注文前に口コミを調べておく時代、大人向け商品の評価はもはや参考情報だけではありません。恥ずかしがり屋で慎重な日本の一人暮らし消費者を助け、私的消費の中で不確かさを解消し、安心して自分に合った商品を選ぶことができます。口コミの流行は、消費者が自主的に、穏当に自分のニーズを満たしたいことの表れである。</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[PS] 0. C++로 다시 시작하는 알고리즘 공부]]></title>
            <link>https://velog.io/@kwondo1017/PS-0.-C%EB%A1%9C-%EB%8B%A4%EC%8B%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EB%8A%94-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EA%B3%B5%EB%B6%80</link>
            <guid>https://velog.io/@kwondo1017/PS-0.-C%EB%A1%9C-%EB%8B%A4%EC%8B%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EB%8A%94-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EA%B3%B5%EB%B6%80</guid>
            <pubDate>Fri, 16 Oct 2026 03:25:56 GMT</pubDate>
            <description><![CDATA[<h2 id="알고리즘-공부를-다시-시작하며">알고리즘 공부를 다시 시작하며</h2>
<p>오랜만에 알고리즘 공부를 다시 시작한다.
올해 1<del>2월, Java/Spring을 공부하면서 알고리즘 문제 풀이(PS)도 Java로 시작했다. 당시에는 백준 실버 2</del>1 수준의 문제까지 도전했지만, 다른 공부와 병행하다 보니 알고리즘 자체에 집중하거나 체계적인 학습을 이어가지는 못했다.
3학년 2학기에 접어든 지금은 필수 전공 과목들을 미리 이수해 둔 덕분에 학업 부담에서 상당히 자유로워졌다. 이번 기회에 알고리즘을 기초부터 체계적으로 공부하기로 했고, 학습 언어도 C++로 변경하면서 그 과정을 Velog 시리즈로 기록하고자 한다.</p>
<h2 id="왜-c인가">왜 C++인가?</h2>
<p>기존에 사용하던 Java에서 C++로 언어를 변경한 데에는 몇 가지 이유가 있다.
우선 진로에 대한 관심이 기존의 백엔드 엔지니어링에서 시스템 소프트웨어와 인프라 영역으로 확장되었다. C++은 이러한 분야에서도 폭넓게 활용되는 언어인 만큼, 알고리즘 학습을 통해 언어에 익숙해지는 것 자체가 의미 있다고 판단했다.
또한 Java는 IDE 없이 알고리즘 문제를 풀 때 다소 번거로운 부분이 있다. 입출력 처리나 자료구조 활용에서 작성해야 하는 코드가 상대적으로 많기 때문에, 알고리즘 자체에 집중하기에는 아쉬움이 있었다. 그래서 알고리즘 학습에 널리 사용되는 C++과 Python을 두고 고민했다.</p>
<p>최종적으로 C++을 선택한 이유는 시스템 엔지니어링과의 연관성뿐만 아니라 STL(Standard Template Library)의 강력한 기능과 개인적으로 선호하는 코드 가독성 때문이다. C++은 문법이 복잡한 편이지만, 익숙해진다면 간결하면서도 명시적인 코드를 작성할 수 있다는 점이 매력적이었다. 반면 Python은 간결하다는 장점에도 불구하고, 개인적으로는 복잡한 로직에서의 가독성이 썩 마음에 들지 않았다.</p>
<h2 id="학습-계획">학습 계획</h2>
<p>이번 학습은 바킹독의 실전 알고리즘 강의를 중심으로 진행하며, LeetCode와 백준 문제 풀이를 병행할 예정이다.
이 시리즈는 단순히 문제의 정답 코드나 알고리즘 개념을 정리하는 공간으로 사용하지 않으려 한다. 어떤 문제를 접했고, 처음에는 어떻게 접근했으며, 어떤 시행착오를 거쳐 해결했는지를 중심으로 기록할 생각이다.</p>
<h2 id="기록-방식">기록 방식</h2>
<p>주로 다음과 같은 내용을 다룰 예정이다.</p>
<ul>
<li>문제 해결 과정: 최초 접근법, 반례와 시행착오, 최종 알고리즘을 선택한 이유</li>
<li>알고리즘 분석: 사용한 자료구조와 알고리즘의 시간·공간 복잡도</li>
<li>C++ 학습: 문제를 구현하면서 새롭게 익힌 문법, STL의 활용법과 내부 동작</li>
<li>학습 회고: 문제를 통해 배운 점과 추가로 공부해야 할 내용</li>
</ul>
<h2 id="학습-기록-위치">학습 기록 위치</h2>
<p>모든 문제를 글로 남기기보다는 학습 과정에서 의미 있는 경험이 있었던 문제들을 선별해 작성하려 한다. 실제 풀이 코드는 GitHub의 <a href="https://github.com/kwondo1017/problem-solving">problem-solving 저장소</a>에서 관리할 예정이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HIGH-QUALITY DONJOY SANITARY CHECK VALVES]]></title>
            <link>https://velog.io/@donjoyvalve/HIGH-QUALITY-DONJOY-SANITARY-CHECK-VALVES</link>
            <guid>https://velog.io/@donjoyvalve/HIGH-QUALITY-DONJOY-SANITARY-CHECK-VALVES</guid>
            <pubDate>Fri, 16 Oct 2026 03:25:31 GMT</pubDate>
            <description><![CDATA[<p>Donjoy&#39;s Sanitary Check Valve, engineered for high-cleanliness applications in the biopharmaceutical, food, and beverage industries. It features a full-bore design with minimal flow resistance, zero leakage, and no dead zones, ensuring hygienic operation. Built with 316L stainless steel and certified to FDA and 3-A standards, it offers reliability and easy maintenance. Enhance your fluid system efficiency and safety—choose Donjoy for superior performance.</p>
<p>WORKING PRINCIPLE OF CHECK VALVE
DONJOY&#39;s <a href="https://www.donjoy-valve.com/products/sanitary-check-valve/">check valve in china</a> ensure unidirectional flow, preventing backflow with minimal resistance and zero leakage. Their simple design allows smooth operation in high-purity systems, enhancing safety and efficiency for industries like biopharma and food processing.</p>
<p>SELECTING THE IDEAL CONNECTION TYPE: CLAMP, THREADED, OR FLANGED</p>
<p>Correct check valve selection depends on the process connection, installation space, cleaning method, pressure and maintenance requirements. Buyers should evaluate these factors together with the application conditions to confirm the most suitable sanitary check valve configuration. Choose clamp connections for quick removal and cleaning, threaded versions for compact piping and flanged models for robust mechanical installation.</p>
<p>There are many <a href="https://www.donjoy-valve.com/products/sanitary-check-valve/">check valve manufacturers</a>, but we are the best choice for you.</p>
<p>If you are looking for a reliable <a href="https://www.donjoy-valve.com/products/sanitary-check-valve/">check valve factory</a>, please contact us.</p>
<p><img src="https://velog.velcdn.com/images/donjoyvalve/post/8ec14fdd-e3b1-4e81-9647-ecc624d415bf/image.jpg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Integration Platform as a Service (iPaaS) 市場の規模、競争環境、および予測（2034年）]]></title>
            <link>https://velog.io/@ritikpandey-3011/Integration-Platform-as-a-Service-iPaaS-%E5%B8%82%E5%A0%B4%E3%81%AE%E8%A6%8F%E6%A8%A1%E3%80%81%E7%AB%B6%E4%BA%89%E7%92%B0%E5%A2%83%E3%80%81%E3%81%8A%E3%82%88%E3%81%B3%E4%BA%88%E6%B8%AC%EF%BC%882034%E5%B9%B4%EF%BC%89</link>
            <guid>https://velog.io/@ritikpandey-3011/Integration-Platform-as-a-Service-iPaaS-%E5%B8%82%E5%A0%B4%E3%81%AE%E8%A6%8F%E6%A8%A1%E3%80%81%E7%AB%B6%E4%BA%89%E7%92%B0%E5%A2%83%E3%80%81%E3%81%8A%E3%82%88%E3%81%B3%E4%BA%88%E6%B8%AC%EF%BC%882034%E5%B9%B4%EF%BC%89</guid>
            <pubDate>Fri, 16 Oct 2026 03:22:35 GMT</pubDate>
            <description><![CDATA[<div><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">クラウドコンピューティング、自動化、および接続されたデジタルシステムの導入が進むにつれ、グローバルな統合プラットフォーム・アズ・ア・サービス（iPaaS）市場は拡大しています。企業はiPaaSソリューションを利用して、アプリケーション、データベース、クラウド環境、およびエンタープライズソフトウェアを集中管理プラットフォームを通じて統合します。これらのソリューションは、データ交換の効率化、手作業の削減、およびさまざまな業務機能における運用効率の向上に役立ちます。</span></span></div>
<div>
<div>
<p><strong><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">Fortune Business Insightsによると</span></span></strong><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">、世界の</span></span><a href="https://www.fortunebusinessinsights.com/integration-platform-as-a-service-ipaas-market-109835"><strong><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">統合プラットフォーム・アズ・ア・サービス（iPaaS）市場</span></span></strong></a><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">規模は2025年に156億3000万米ドルと評価されました。同市場は2026年の191億5000万米ドルから2034年には1087億6000万米ドルに成長すると予測されており、予測期間中の年平均成長率（CAGR）は24.20%です。2025年には北米が世界市場を牽引し、24.20%のシェアを占めました。クラウド導入の拡大、デジタルトランスフォーメーションへの取り組み、シームレスなアプリケーション接続への需要の高まりが、この成長を支えると予想されます。</span></span></p>
<h2><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">クラウドベースの統合ソリューションに対する需要の高まり</span></span></h2>
<p><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">クラウドベースのビジネスアプリケーションの普及拡大は、統合プラットフォーム・アズ・ア・サービス（iPaaS）市場を牽引する主要因の一つです。企業は顧客関係管理、企業資源計画、人事、財務、サプライチェーン管理など、複数のアプリケーションを多用することが多く、これらのシステムを連携させることで、効率的な情報交換と部門横断的なデータ整合性の維持が可能になります。</span></span></p>
<p><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">従来の統合手法では、膨大な開発リソース、専門的な技術知識、そして継続的なメンテナンスが必要となる場合があります。iPaaSプラットフォームは、クラウドベースのツール、事前構築済みのコネクタ、アプリケーションプログラミングインターフェース（API）、ワークフロー自動化機能を提供することで、これらのプロセスを簡素化します。これらの機能により、組織は変化する運用要件に適応しながら、より効率的にシステムを統合することができます。</span></span></p>
<h2><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">自動化とデジタルトランスフォーメーションの導入拡大</span></span></h2>
<p><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">企業は生産性の向上、意思決定の迅速化、顧客体験の向上を目指し、デジタル変革に投資しています。iPaaS市場はこのトレンドの恩恵を受けており、デジタルアプリケーションの連携やビジネスプロセスの自動化には統合が不可欠です。</span></span></p>
<p><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">例えば、企業は統合プラットフォームを利用して、顧客記録の同期、注文処理の自動化、決済システムの接続、運用アプリケーション間の情報転送などを行うことができます。これらの機能により、反復作業を削減し、データの可視性を向上させることが可能です。企業がより多くのデジタルツールを採用するにつれて、拡張性の高い統合インフラストラクチャへの需要が高まることが予想されます。</span></span></p>
<p><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">詳細な市場洞察については:&nbsp; </span></span><a href="https://www.fortunebusinessinsights.com/jp/サービスとしての統合プラットフォーム-(ipaas)-市場-109835"><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">https://www.fortunebusinessinsights.com/jp/サービスとしての統合プラットフォーム-(ipaas)-市場-109835</span></span></a>&nbsp;</p>
<h2><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">人工知能とAPI統合がイノベーションを推進</span></span></h2>
<p><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">人工知能（AI）は、統合プラットフォーム・アズ・ア・サービス（iPaaS）市場に新たな機会をもたらしています。AIを活用した統合ツールは、ワークフロー開発の自動化、データ不整合の特定、複雑な統合環境の管理支援に役立ちます。こうした機能により、アプリケーション間の接続構築と維持に必要な技術的な労力を軽減できる可能性があります。</span></span></p>
<p><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">API統合は、開発におけるもう一つの重要な分野です。企業は、クラウドアプリケーション、モバイルサービス、パートナーシステム、社内ソフトウェアを接続するために、APIへの依存度を高めています。iPaaSプラットフォームは、これらの接続を管理し、分散したテクノロジー環境全体でのデータ交換をサポートするツールを提供します。</span></span></p>
<p><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">しかし、企業はこれらのソリューションを導入する際に、データセキュリティ、アクセス制御、ガバナンス、互換性といった課題に対処する必要がある。</span></span></p>
<h2><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">展開と組織規模の分析</span></span></h2>
<p><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">iPaaS市場は、さまざまな技術要件と運用規模を持つ組織に対応しています。クラウドベースの導入により、企業は基盤となるインフラストラクチャを自社で維持管理することなく、統合機能を利用できるようになります。</span></span></p>
<p><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">大企業は、複雑なアプリケーション環境を接続し、グローバルな事業運営をサポートし、大量のデータを管理するために、iPaaSソリューションを利用しています。これらの組織は、高度なセキュリティ制御、ガバナンス機能、および既存システムとの統合を必要とすることがよくあります。</span></span></p>
<p><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">中小企業も、生産性の向上と個別のアプリケーション管理に伴う技術的負担の軽減を目的として、クラウドベースの統合ツールを採用し始めています。柔軟な価格設定、簡素化された設定、ローコードまたはノーコード機能により、これらのプラットフォームはITリソースが限られている企業にとってより利用しやすくなっています。</span></span></p>
<h2><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">アプリケーション分析</span></span></h2>
<p><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">統合プラットフォーム・アズ・ア・サービス（iPaaS）市場は、複数のビジネスアプリケーションをサポートしています。</span></span></p>
<p><strong><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">顧客関係管理：</span></span></strong><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">統合プラットフォームは、営業、マーケティング、サービスアプリケーション間で顧客データを連携させ、組織が一貫性のある顧客記録を維持できるよう支援します。</span></span></p>
<p><strong><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">企業資源計画（ERP）：</span></span></strong><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;"> iPaaSソリューションは、財務、在庫、調達、および運用システムを連携させ、協調的なビジネスプロセスをサポートします。</span></span></p>
<p><strong><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">データ統合と管理：</span></span></strong><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">これらのプラットフォームは、アプリケーションとクラウド環境間のデータ移動を容易にし、レポート作成、分析、情報共有をサポートします。</span></span></p>
<p><strong><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">ワークフローの自動化：</span></span></strong><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">企業は、通知、承認プロセス、注文管理、データ同期など、反復的な作業を自動化できます。</span></span></p>
<p><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">その他の用途としては、サプライチェーン統合、人事管理、eコマース業務、パートナー企業との連携などが挙げられる。</span></span></p>
<h2><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">地域分析</span></span></h2>
<p><strong><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">北米は、</span></span></strong><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;"> 2025年の世界統合プラットフォーム・アズ・ア・サービス（iPaaS）市場において24.20%のシェアを占める見込みです。この地域は、クラウドの普及、確立されたエンタープライズソフトウェアインフラ、そしてデジタル変革への継続的な投資といった恩恵を受けています。アプリケーション接続とワークフロー自動化への需要が、北米の市場における地位を支えています。</span></span></p>
<p><strong><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">欧州で</span></span></strong><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">は、企業がエンタープライズシステムを近代化し、クラウドベースのテクノロジーを採用するにつれて、新たなビジネスチャンスが生まれると予想されます。データガバナンス、規制遵守、安全な情報交換は、この地域の企業にとって依然として重要な課題です。</span></span></p>
<p><strong><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">アジア太平洋地域は</span></span></strong><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">、デジタル経済の拡大、クラウド導入の増加、企業ITインフラの近代化を通じて、成長の可能性を秘めている。発展途上国と先進国を問わず、多くの企業がコネクテッドアプリケーションと自動化プロセスへの投資を進めている。</span></span></p>
<p><strong><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">南米では、</span></span></strong><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">企業がデジタル能力を向上させ、業務を近代化するにつれて、クラウドサービスの導入がさらに進む可能性がある。拡張性の高いクラウドサービスの利用可能性は、さまざまな業界での導入を支援するだろう。</span></span></p>
<p><strong><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">中東・アフリカ地域では、</span></span></strong><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">企業がデジタルインフラ、クラウド技術、そして企業近代化に投資するにつれ、新たなビジネスチャンスが生まれています。統合によるメリットへの認識が高まることで、市場は徐々に拡大していくでしょう。</span></span></p>
<h2><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">競争環境</span></span></h2>
<p><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">iPaaS（Integration Platform as a Service）市場における競争は、拡張性、使いやすさ、セキュリティ、自動化、接続性、および既存のエンタープライズシステムとの互換性を中心に展開されています。プロバイダーは、コネクタライブラリの拡張、ハイブリッド環境のサポート、および統合開発の簡素化によって、プラットフォームの機能向上を目指しています。</span></span></p>
<p><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">ローコードツール、AIを活用したワークフロー、API管理、リアルタイムデータ同期は、製品開発において重要な分野です。柔軟な導入オプションと信頼性の高い統合機能を提供するプロバイダーは、複雑な技術要件を持つ組織にとって、より有利な立場にあると言えるでしょう。</span></span></p>
<h2><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">最近の業界動向</span></span></h2>
<p><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">近年の業界動向としては、AIを活用した統合、ローコードおよびノーコード開発ツール、API主導の接続性、自動化されたワークフロー管理の利用拡大が挙げられます。これらの機能は、統合作業を簡素化し、変化するビジネス要件への迅速な対応を支援することを目的としています。</span></span></p>
<p><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">ハイブリッドクラウドおよびマルチクラウド環境の拡大も、プラットフォーム開発に影響を与えています。企業は、パブリッククラウド、プライベートインフラストラクチャ、オンプレミスシステムに分散してホストされているアプリケーションを接続する必要性がますます高まっています。統合プラットフォームは、セキュリティ、ガバナンス、運用上の信頼性を重視しながら、これらの要件に対応するために進化しています。</span></span></p>
<p><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">無料サンプル PDF を入手:&nbsp; </span></span><a href="https://www.fortunebusinessinsights.com/jp/問い合わせ/リクエスト-サンプル-pdf/サービスとしての統合プラットフォーム-(ipaas)-市場-109835"><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">https://www.fortunebusinessinsights.com/jp/問い合わせ/リクエスト-サンプル-pdf/サービスとしての統合プラットフォーム-(ipaas)-市場-109835</span></span></a>&nbsp;</p>
<h2><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">今後の見通し</span></span></h2>
<p><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">世界の統合プラットフォーム・アズ・ア・サービス（iPaaS）市場は、2034年まで大幅な成長が見込まれています。Fortune Business Insightsによると、同市場は2026年の191億5000万米ドルから2034年には1087億6000万米ドルに拡大し、予測期間中の年平均成長率（CAGR）は24.20%になると予測されています。</span></span></p>
<p><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">クラウド導入の継続、エンタープライズオートメーション、API統合、AIを活用したワークフローは、さらなるビジネスチャンスを生み出すと予想されます。同時に、企業はサイバーセキュリティリスク、データガバナンス要件、統合の複雑さへの対応も必要となります。柔軟なアーキテクチャ、信頼性の高いパフォーマンス、そして使いやすい開発ツールを兼ね備えたプロバイダーは、企業がデジタルオペレーションの連携をますます重視するようになるにつれて、大きなメリットを得られるでしょう。</span></span></p>
<p><strong><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">関連レポート</span></span></strong></p>
<p><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">日本市場を牽引する要因とは？最新情報 - </span></span><a href="https://www.fortunebusinessinsights.com/jp/サービスとしての統合プラットフォーム-(ipaas)-市場-109835"><strong><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">iPaaS（Integration Platform as a Service）市場</span></span></strong></a><span dir="auto" style="vertical-align: inherit;"><span dir="auto" style="vertical-align: inherit;">- インサイト</span></span></p>
</div>
</div>]]></description>
        </item>
        <item>
            <title><![CDATA[[문제풀이] 글자 이어 붙여 문자열 만들기]]></title>
            <link>https://velog.io/@koikoilove99/181915-create-string-by-concatenating-letters</link>
            <guid>https://velog.io/@koikoilove99/181915-create-string-by-concatenating-letters</guid>
            <pubDate>Fri, 16 Oct 2026 03:21:25 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<blockquote>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/181915">글자 이어 붙여 문자열 만들기</a></p>
</blockquote>
<h2 id="span-stylecolor-a78bfa문제-설명span"><span style="color: #A78BFA">문제 설명</span></h2>
<p>문자열 <code>my_string</code>과 정수 배열 <code>index_list</code>가 매개변수로 주어집니다. <code>my_string</code>의 <code>index_list</code>의 원소들에 해당하는 인덱스의 글자들을 순서대로 이어 붙인 문자열을 return 하는 <code>solution</code> 함수를 작성해 주세요.</p>
<hr>
<h2 id="span-stylecolor-a78bfa제한사항span"><span style="color: #A78BFA">제한사항</span></h2>
<ul>
<li>$1 \leq$ <code>my_string</code>의 길이 $\leq 1,000$</li>
<li><code>my_string</code>의 원소는 영소문자로 이루어져 있습니다.</li>
<li>$1 \leq$ <code>index_list</code>의 길이 $\leq 1,000$</li>
<li>$0 \leq$ <code>index_list</code>의 원소 $&lt;$ <code>my_string</code>의 길이</li>
</ul>
<hr>
<h2 id="span-stylecolor-a78bfa입출력-예시span"><span style="color: #A78BFA">입출력 예시</span></h2>
<table>
<thead>
<tr>
<th>my_string</th>
<th>index_list</th>
<th>result</th>
</tr>
</thead>
<tbody><tr>
<td>&quot;cvsgiorszzzmrpaqpe&quot;</td>
<td>[16, 6, 5, 3, 12, 14, 11, 11, 17, 12, 7]</td>
<td>&quot;programmers&quot;</td>
</tr>
<tr>
<td>&quot;zpiaz&quot;</td>
<td>[1, 2, 0, 0, 3]</td>
<td>&quot;pizza&quot;</td>
</tr>
</tbody></table>
<hr>
<h2 id="span-stylecolor-a78bfa문제-풀이span"><span style="color: #A78BFA">문제 풀이</span></h2>
<table>
<thead>
<tr>
<th>0</th>
<th>1</th>
<th>2</th>
<th>3</th>
<th>4</th>
<th>5</th>
<th>6</th>
<th>7</th>
<th>8</th>
<th>9</th>
<th>10</th>
<th>11</th>
<th>12</th>
<th>13</th>
<th>14</th>
<th>15</th>
<th>16</th>
<th>17</th>
</tr>
</thead>
<tbody><tr>
<td>c</td>
<td>v</td>
<td>s</td>
<td>g</td>
<td>i</td>
<td>o</td>
<td>r</td>
<td>s</td>
<td>z</td>
<td>z</td>
<td>z</td>
<td>m</td>
<td>r</td>
<td>p</td>
<td>a</td>
<td>q</td>
<td>p</td>
<td>e</td>
</tr>
</tbody></table>
<p>16: p
6: r
5: o
3: g
12: r
14: a
11: m
11: m
17: e
12: r
7: s</p>
<p>마지막에서는 <code>join()</code> 활용해서 반환</p>
<pre><code class="language-python">def solution(my_string, index_list):
    return &quot;&quot;.join([my_string[idx] for idx in index_list])</code></pre>
]]></description>
        </item>
    </channel>
</rss>