<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Cookie on Chanyeol Dev</title>
    <link>https://chanyeols.com/tags/cookie/</link>
    <description>Recent content in Cookie on Chanyeol Dev</description>
    <generator>Hugo</generator>
    <language>ko-kr</language>
    <lastBuildDate>Mon, 06 Apr 2026 11:00:00 +0900</lastBuildDate>
    <atom:link href="https://chanyeols.com/tags/cookie/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>사내 Slack 봇 만들기 - SSO 세션 처리 &#43; 내부 API 연동 (2편)</title>
      <link>https://chanyeols.com/posts/slack-02-sso/</link>
      <pubDate>Mon, 06 Apr 2026 11:00:00 +0900</pubDate>
      <guid>https://chanyeols.com/posts/slack-02-sso/</guid>
      <description>사내 그룹웨어 SSO 로그인을 axios로 처리하는 방법과 쿠키 기반 세션 유지 패턴을 정리합니다. 도메인별 세션 분리, 수동 리다이렉트 처리 등 실제 겪은 함정들을 다룹니다.</description>
      <content:encoded><![CDATA[<h2 id="문제-그룹웨어-api를-어떻게-호출하나">문제: 그룹웨어 API를 어떻게 호출하나</h2>
<p>사내 그룹웨어는 브라우저에서 로그인한 세션 쿠키로 API를 호출하는 구조다. 공개 API키 같은 게 없고, 그냥 브라우저처럼 로그인해서 쿠키를 들고 API를 쳐야 한다.</p>
<p>Node.js에서 이걸 하려면 axios로 로그인 과정을 그대로 흉내내야 한다.</p>
<hr>
<h2 id="sso-로그인-흐름">SSO 로그인 흐름</h2>
<p>대부분의 사내 SSO는 아래 패턴을 따른다.</p>
<pre tabindex="0"><code>1단계: 로그인 페이지 GET → 초기 세션 쿠키 획득
2단계: 로그인 form POST → 인증 처리
3단계: SSO 토큰 발급 페이지 GET → 리다이렉트로 토큰 획득
4단계: 대상 도메인에 토큰으로 접근 → 해당 도메인 세션 쿠키 획득
</code></pre><p>핵심은 <strong>각 단계에서 받은 쿠키를 다음 요청에 그대로 넘겨줘야</strong> 한다는 것이다.</p>
<hr>
<h2 id="axios로-sso-구현하기">axios로 SSO 구현하기</h2>
<h3 id="쿠키-파싱-헬퍼">쿠키 파싱 헬퍼</h3>
<p>axios는 브라우저와 달리 쿠키를 자동으로 관리해주지 않는다. 응답 헤더에서 직접 파싱해야 한다.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#ff7b72">function</span> parseCookies(setCookieHeader) {
</span></span><span style="display:flex;"><span>  <span style="color:#ff7b72">if</span> (<span style="color:#ff7b72;font-weight:bold">!</span>setCookieHeader) <span style="color:#ff7b72">return</span> <span style="color:#a5d6ff">&#39;&#39;</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#ff7b72">const</span> cookies <span style="color:#ff7b72;font-weight:bold">=</span> Array.isArray(setCookieHeader)
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72;font-weight:bold">?</span> setCookieHeader
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72;font-weight:bold">:</span> [setCookieHeader];
</span></span><span style="display:flex;"><span>  <span style="color:#ff7b72">return</span> cookies
</span></span><span style="display:flex;"><span>    .map(c =&gt; c.split(<span style="color:#a5d6ff">&#39;;&#39;</span>)[<span style="color:#a5d6ff">0</span>])  <span style="color:#8b949e;font-style:italic">// &#39;name=value; Path=/&#39; → &#39;name=value&#39;
</span></span></span><span style="display:flex;"><span>    .join(<span style="color:#a5d6ff">&#39;; &#39;</span>);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="1단계-초기-세션-쿠키-획득">1단계: 초기 세션 쿠키 획득</h3>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#ff7b72">const</span> initRes <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#ff7b72">await</span> axios.get(<span style="color:#a5d6ff">&#39;https://portal.company.com/login&#39;</span>, {
</span></span><span style="display:flex;"><span>  maxRedirects<span style="color:#ff7b72;font-weight:bold">:</span> <span style="color:#a5d6ff">0</span>,
</span></span><span style="display:flex;"><span>  validateStatus<span style="color:#ff7b72;font-weight:bold">:</span> s =&gt; s <span style="color:#ff7b72;font-weight:bold">&lt;</span> <span style="color:#a5d6ff">400</span>,
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">let</span> cookie <span style="color:#ff7b72;font-weight:bold">=</span> parseCookies(initRes.headers[<span style="color:#a5d6ff">&#39;set-cookie&#39;</span>]);
</span></span></code></pre></div><h3 id="2단계-로그인">2단계: 로그인</h3>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#ff7b72">const</span> loginRes <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#ff7b72">await</span> axios.post(
</span></span><span style="display:flex;"><span>  <span style="color:#a5d6ff">&#39;https://portal.company.com/login/check&#39;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#ff7b72">new</span> URLSearchParams({
</span></span><span style="display:flex;"><span>    id<span style="color:#ff7b72;font-weight:bold">:</span> process.env.LOGIN_ID,
</span></span><span style="display:flex;"><span>    password<span style="color:#ff7b72;font-weight:bold">:</span> process.env.LOGIN_PW,
</span></span><span style="display:flex;"><span>  }),
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>    headers<span style="color:#ff7b72;font-weight:bold">:</span> {
</span></span><span style="display:flex;"><span>      Cookie<span style="color:#ff7b72;font-weight:bold">:</span> cookie,
</span></span><span style="display:flex;"><span>      <span style="color:#a5d6ff">&#39;Content-Type&#39;</span><span style="color:#ff7b72;font-weight:bold">:</span> <span style="color:#a5d6ff">&#39;application/x-www-form-urlencoded&#39;</span>,
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    maxRedirects<span style="color:#ff7b72;font-weight:bold">:</span> <span style="color:#a5d6ff">0</span>,
</span></span><span style="display:flex;"><span>    validateStatus<span style="color:#ff7b72;font-weight:bold">:</span> s =&gt; s <span style="color:#ff7b72;font-weight:bold">&lt;</span> <span style="color:#a5d6ff">400</span>,
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>);
</span></span><span style="display:flex;"><span>cookie <span style="color:#ff7b72;font-weight:bold">=</span> parseCookies(loginRes.headers[<span style="color:#a5d6ff">&#39;set-cookie&#39;</span>]) <span style="color:#ff7b72;font-weight:bold">||</span> cookie;
</span></span></code></pre></div><p>로그인 요청은 form-data(<code>application/x-www-form-urlencoded</code>) 방식으로 보내야 한다. <code>axios.post</code>에 객체를 그냥 넘기면 JSON으로 보내져서 로그인이 안 된다. <code>URLSearchParams</code>로 감싸줘야 한다.</p>
<h3 id="3단계-sso-토큰-발급">3단계: SSO 토큰 발급</h3>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#ff7b72">const</span> ssoRes <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#ff7b72">await</span> axios.get(<span style="color:#a5d6ff">&#39;https://portal.company.com/sso-redirect&#39;</span>, {
</span></span><span style="display:flex;"><span>  headers<span style="color:#ff7b72;font-weight:bold">:</span> { Cookie<span style="color:#ff7b72;font-weight:bold">:</span> cookie },
</span></span><span style="display:flex;"><span>  maxRedirects<span style="color:#ff7b72;font-weight:bold">:</span> <span style="color:#a5d6ff">0</span>,  <span style="color:#8b949e;font-style:italic">// 리다이렉트 수동으로 따라가야 함
</span></span></span><span style="display:flex;"><span>  validateStatus<span style="color:#ff7b72;font-weight:bold">:</span> s =&gt; s <span style="color:#ff7b72;font-weight:bold">&lt;</span> <span style="color:#a5d6ff">400</span>,
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">// 리다이렉트 URL에서 토큰 추출
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">const</span> ssoUrl <span style="color:#ff7b72;font-weight:bold">=</span> ssoRes.headers[<span style="color:#a5d6ff">&#39;location&#39;</span>];
</span></span></code></pre></div><p><code>maxRedirects: 0</code>이 중요하다. axios가 자동으로 리다이렉트를 따라가면 쿠키가 유실된다. 리다이렉트를 수동으로 처리해야 각 단계의 쿠키를 직접 챙길 수 있다.</p>
<h3 id="4단계-대상-도메인-세션-획득">4단계: 대상 도메인 세션 획득</h3>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#ff7b72">const</span> domainRes <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#ff7b72">await</span> axios.get(ssoUrl, {
</span></span><span style="display:flex;"><span>  maxRedirects<span style="color:#ff7b72;font-weight:bold">:</span> <span style="color:#a5d6ff">5</span>,  <span style="color:#8b949e;font-style:italic">// 여기서는 따라가도 됨
</span></span></span><span style="display:flex;"><span>  validateStatus<span style="color:#ff7b72;font-weight:bold">:</span> s =&gt; s <span style="color:#ff7b72;font-weight:bold">&lt;</span> <span style="color:#a5d6ff">400</span>,
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">const</span> domainCookie <span style="color:#ff7b72;font-weight:bold">=</span> parseCookies(domainRes.headers[<span style="color:#a5d6ff">&#39;set-cookie&#39;</span>]);
</span></span></code></pre></div><p>이 <code>domainCookie</code>가 실제 API 호출에 쓰는 세션이다.</p>
<hr>
<h2 id="도메인별-세션-분리">도메인별 세션 분리</h2>
<p>우리 그룹웨어는 기능별로 서브도메인이 달랐다. 예를 들면 휴가는 <code>leave.company.com</code>, 회의실은 <code>gw.company.com</code> 식이다.</p>
<p>문제는 <strong>도메인이 다르면 쿠키가 공유되지 않는다</strong>는 거다. 각 도메인에 별도로 SSO 로그인을 해야 한다.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">// 서버 시작 시 두 도메인 모두 로그인
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">let</span> avsCookie <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#ff7b72">await</span> login(<span style="color:#a5d6ff">&#39;leave&#39;</span>);  <span style="color:#8b949e;font-style:italic">// 휴가 도메인
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">let</span> gwCookie  <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#ff7b72">await</span> login(<span style="color:#a5d6ff">&#39;gw&#39;</span>);     <span style="color:#8b949e;font-style:italic">// 회의실 도메인
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">// API 호출 시 해당 도메인 쿠키 사용
</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">async</span> <span style="color:#ff7b72">function</span> getVacation(date) {
</span></span><span style="display:flex;"><span>  <span style="color:#ff7b72">return</span> axios.post(VACATION_API_URL, payload, {
</span></span><span style="display:flex;"><span>    headers<span style="color:#ff7b72;font-weight:bold">:</span> { Cookie<span style="color:#ff7b72;font-weight:bold">:</span> avsCookie, ...customHeaders },
</span></span><span style="display:flex;"><span>  });
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">async</span> <span style="color:#ff7b72">function</span> getMeetingRooms(date) {
</span></span><span style="display:flex;"><span>  <span style="color:#ff7b72">return</span> axios.post(ROOM_API_URL, payload, {
</span></span><span style="display:flex;"><span>    headers<span style="color:#ff7b72;font-weight:bold">:</span> { Cookie<span style="color:#ff7b72;font-weight:bold">:</span> gwCookie, ...customHeaders },
</span></span><span style="display:flex;"><span>  });
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><hr>
<h2 id="커스텀-헤더-문제">커스텀 헤더 문제</h2>
<p>API를 호출했는데 404나 의미 없는 오류 코드가 계속 반환됐다. 로그인도 됐고 쿠키도 맞는데 왜 안 되나 한참 삽질했다.</p>
<p>브라우저 Network 탭을 열어서 실제 요청을 비교해보니 커스텀 헤더들이 빠져있었다.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#ff7b72">const</span> customHeaders <span style="color:#ff7b72;font-weight:bold">=</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#a5d6ff">&#39;__service_id__&#39;</span><span style="color:#ff7b72;font-weight:bold">:</span> <span style="color:#a5d6ff">&#39;SERVICE_NAME&#39;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a5d6ff">&#39;__view_id__&#39;</span><span style="color:#ff7b72;font-weight:bold">:</span> <span style="color:#a5d6ff">&#39;view-identifier&#39;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a5d6ff">&#39;__menu_id__&#39;</span><span style="color:#ff7b72;font-weight:bold">:</span> <span style="color:#a5d6ff">&#39;MENU_CODE&#39;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a5d6ff">&#39;ajax&#39;</span><span style="color:#ff7b72;font-weight:bold">:</span> <span style="color:#a5d6ff">&#39;true&#39;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a5d6ff">&#39;x-requested-with&#39;</span><span style="color:#ff7b72;font-weight:bold">:</span> <span style="color:#a5d6ff">&#39;XMLHttpRequest&#39;</span>,
</span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p>그룹웨어 API는 이런 커스텀 헤더로 어떤 서비스/메뉴에서 요청이 왔는지 검증한다. 빠지면 요청이 거부된다.</p>
<p><strong>해결법:</strong> 브라우저 Network 탭에서 실제 API 요청을 찾아 Request Headers를 전부 복사해서 동일하게 맞춰줬다.</p>
<hr>
<h2 id="세션-유지-30분마다-keepalive">세션 유지 (30분마다 keepalive)</h2>
<p>SSO 세션은 일정 시간 요청이 없으면 만료된다. 봇이 새벽에 아무것도 안 하다가 아침에 알림을 보내려 하면 세션이 끊겨있는 상황이 생긴다.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span>cron.schedule(<span style="color:#a5d6ff">&#39;*/30 * * * *&#39;</span>, <span style="color:#ff7b72">async</span> () =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#ff7b72">try</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic">// 세션 페이지에 GET 요청으로 keepalive
</span></span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">await</span> axios.get(SESSION_KEEP_URL, {
</span></span><span style="display:flex;"><span>      headers<span style="color:#ff7b72;font-weight:bold">:</span> { Cookie<span style="color:#ff7b72;font-weight:bold">:</span> avsCookie },
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">await</span> axios.get(SESSION_KEEP_URL_GW, {
</span></span><span style="display:flex;"><span>      headers<span style="color:#ff7b72;font-weight:bold">:</span> { Cookie<span style="color:#ff7b72;font-weight:bold">:</span> gwCookie },
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>  } <span style="color:#ff7b72">catch</span> (err) {
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic">// 실패 시 재로그인
</span></span></span><span style="display:flex;"><span>    avsCookie <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#ff7b72">await</span> login(<span style="color:#a5d6ff">&#39;leave&#39;</span>);
</span></span><span style="display:flex;"><span>    gwCookie  <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#ff7b72">await</span> login(<span style="color:#a5d6ff">&#39;gw&#39;</span>);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p>keepalive 실패 시 재로그인하도록 해두면 세션이 끊겨도 자동 복구된다.</p>
<hr>
<h2 id="메모리-캐시로-api-호출-줄이기">메모리 캐시로 API 호출 줄이기</h2>
<p>같은 날짜 데이터를 10분마다 계속 API에서 가져오면 서버에 부담이 된다. 메모리 캐시로 불필요한 호출을 줄였다.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#ff7b72">const</span> cache <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#ff7b72">new</span> Map();
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">const</span> CACHE_TTL <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">15</span> <span style="color:#ff7b72;font-weight:bold">*</span> <span style="color:#a5d6ff">60</span> <span style="color:#ff7b72;font-weight:bold">*</span> <span style="color:#a5d6ff">1000</span>; <span style="color:#8b949e;font-style:italic">// 15분
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">async</span> <span style="color:#ff7b72">function</span> fetchWithCache(key, fetchFn) {
</span></span><span style="display:flex;"><span>  <span style="color:#ff7b72">const</span> cached <span style="color:#ff7b72;font-weight:bold">=</span> cache.get(key);
</span></span><span style="display:flex;"><span>  <span style="color:#ff7b72">if</span> (cached <span style="color:#ff7b72;font-weight:bold">&amp;&amp;</span> Date.now() <span style="color:#ff7b72;font-weight:bold">-</span> cached.time <span style="color:#ff7b72;font-weight:bold">&lt;</span> CACHE_TTL) {
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">return</span> cached.data;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#ff7b72">const</span> data <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#ff7b72">await</span> fetchFn();
</span></span><span style="display:flex;"><span>  cache.set(key, { data, time<span style="color:#ff7b72;font-weight:bold">:</span> Date.now() });
</span></span><span style="display:flex;"><span>  <span style="color:#ff7b72">return</span> data;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><hr>
<h2 id="트러블슈팅-정리">트러블슈팅 정리</h2>
<table>
  <thead>
      <tr>
          <th>증상</th>
          <th>원인</th>
          <th>해결</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>로그인 후 API 호출 시 인증 오류</td>
          <td>axios가 리다이렉트 자동 처리 중 쿠키 유실</td>
          <td><code>maxRedirects: 0</code>으로 수동 처리</td>
      </tr>
      <tr>
          <td>API 404 / 의미 없는 오류 코드</td>
          <td>커스텀 헤더 누락</td>
          <td>브라우저 Network 탭에서 헤더 전부 확인 후 동일하게 설정</td>
      </tr>
      <tr>
          <td>아침 알림 시 세션 만료</td>
          <td>SSO 세션 TTL 초과</td>
          <td>30분마다 keepalive + 실패 시 재로그인</td>
      </tr>
      <tr>
          <td>같은 도메인인데 API마다 세션 다름</td>
          <td>서브도메인별 쿠키 분리</td>
          <td>도메인별 별도 로그인 세션 관리</td>
      </tr>
      <tr>
          <td>form 로그인 안 됨</td>
          <td>axios POST에 JSON으로 전송됨</td>
          <td><code>URLSearchParams</code>로 감싸서 form-data 형식으로 전송</td>
      </tr>
  </tbody>
</table>
<hr>
<p>다음 편에서는 수집한 데이터를 스냅샷으로 관리하고, 이전 상태와 비교해서 변동을 감지하는 로직과 Slack 알림 발송 구현을 다룬다.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
