<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>vanillajs &amp;mdash; Hyun1008.log</title>
    <link>https://blog.daydream.ink/jyhyun1008/tag:vanillajs</link>
    <description>재연의 개발블로그</description>
    <pubDate>Tue, 28 Apr 2026 02:26:39 +0900</pubDate>
    <item>
      <title>JS 자바스크립트 Array.prototype.map() 에 대해서</title>
      <link>https://blog.daydream.ink/jyhyun1008/js-jabaseukeuribteu-array-prototype-map-e-daehaeseo</link>
      <description>&lt;![CDATA[#스터디 #VanillaJS&#xA;&#xA;MDN Web Docs의 소개는 다음과 같습니다.&#xA;&#xA;  map() 메서드는 배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 (반복하여) 새로운 배열을 반환합니다.&#xA;&#xA;const array1 = [1, 4, 9, 16];&#xA;&#xA;// Pass a function to map&#xA;const map1 = array1.map((x) =  x  2);&#xA;&#xA;console.log(map1);&#xA;// Expected output: Array [2, 8, 18, 32]&#xA;&#xA;또는 return을 사용해서 반환할 수도 있습니다.&#xA;&#xA;var numbers = [1, 4, 9];&#xA;var doubles = numbers.map(function (num) {&#xA;  return num  2;&#xA;});&#xA;// doubles는 [2, 8, 18]&#xA;&#xA;map 안의 function에서는 파라미터를 두 개까지 쓸 수 있는데, 두번째 파라미터는 배열의 index를 나타냅니다.&#xA;&#xA;예를 들면&#xA;&#xA;var array = [0, 0, 0]&#xA;array.map(function(a, i){&#xA;   console.log(i)&#xA;})&#xA;// 0&#xA;// 1&#xA;// 2&#xA;// 반환되는 새로운 배열은 undefined 로 채워짐&#xA;`]]&gt;</description>
      <content:encoded><![CDATA[<p><a href="/jyhyun1008/tag:%EC%8A%A4%ED%84%B0%EB%94%94" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">스터디</span></a> <a href="/jyhyun1008/tag:VanillaJS" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">VanillaJS</span></a></p>

<p><a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/map" rel="nofollow">MDN Web Docs</a>의 소개는 다음과 같습니다.</p>

<blockquote><p><code>map()</code> 메서드는 배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 (반복하여) 새로운 배열을 반환합니다.</p></blockquote>

<pre><code>const array1 = [1, 4, 9, 16];

// Pass a function to map
const map1 = array1.map((x) =&gt; x * 2);

console.log(map1);
// Expected output: Array [2, 8, 18, 32]
</code></pre>

<p>또는 return을 사용해서 반환할 수도 있습니다.</p>

<pre><code>var numbers = [1, 4, 9];
var doubles = numbers.map(function (num) {
  return num * 2;
});
// doubles는 [2, 8, 18]
</code></pre>

<p>map 안의 function에서는 파라미터를 두 개까지 쓸 수 있는데, 두번째 파라미터는 배열의 index를 나타냅니다.</p>

<p>예를 들면</p>

<pre><code>var array = [0, 0, 0]
array.map(function(a, i){
   console.log(i)
})
// 0
// 1
// 2
// 반환되는 새로운 배열은 undefined 로 채워짐
</code></pre>
]]></content:encoded>
      <guid>https://blog.daydream.ink/jyhyun1008/js-jabaseukeuribteu-array-prototype-map-e-daehaeseo</guid>
      <pubDate>Mon, 23 Sep 2024 15:00:49 +0900</pubDate>
    </item>
    <item>
      <title>JS 가로스크롤 만들기</title>
      <link>https://blog.daydream.ink/jyhyun1008/js-garoseukeurol-mandeulgi</link>
      <description>&lt;![CDATA[#개발과정 #VanillaJS&#xA;&#xA;포트폴리오 사이트를 만드는 중 프로젝트를 보여줄 때는 가로스크롤을 하는 게 좋을 것 같아서 만들어 보았습니다.&#xA;&#xA;우선 이렇게 구조를 짰습니다.&#xA;&#xA;        div id=&#34;content1&#34;&#xA;            div id=&#34;content-scroll&#34;&#xA;                div class=&#34;width-fix&#34;&#xA;                    h1Items./h1&#xA;                    div class=&#34;overflow-width&#34;&#xA;                    /div&#xA;                /div&#xA;            /div&#xA;        /div&#xA;&#xA;CSS에서는 .overflow-width 에만 position: relative 를 적용해 주었습니다. 그리고 자바스크립트에서는 다음과 같은 코드를 적용했어요.&#xA;&#xA;    document.querySelector(&#39;.overflow-width&#39;).style.left=0;&#xA;    // 이게 없으면 후에 style.left 를 불러오지 못합니다.&#xA;    document.querySelector(&#39;#content-scroll&#39;).addEventListener(&#39;wheel&#39;,function(e){&#xA;        if(e.deltaY   0){&#xA;        // 스크롤을 내리고 있으면,&#xA;            if (parseInt(document.querySelector(&#39;.overflow-width&#39;).style.left)   -100) {&#xA;                // 가로스크롤이 충분히 진행되지 않았으면(=스크롤이 필요하면)&#xA;                e.preventDefault();&#xA;                e.stopPropagation();&#xA;                // 기존 세로 스크롤을 막습니다.&#xA;                document.querySelector(&#39;.overflow-width&#39;).style.top=0%;&#xA;                document.querySelector(&#39;.overflow-width&#39;).style.left = ${parseInt(document.querySelector(&#39;.overflow-width&#39;).style.left) - 1}%;&#xA;                // 그리고 스크롤 양에 따라 요소의 위치를 옮겨줍니다.&#xA;            }&#xA;        } else if (e.deltaY &lt; 0) {&#xA;        // 스크롤을 올리고 있으면,&#xA;            if (parseInt(document.querySelector(&#39;.overflow-width&#39;).style.left) &lt; 0) {&#xA;                // 가로스크롤이 충분히 돌아오지 않았으면(=스크롤이 필요하면)&#xA;                // 아래 내용은 동일합니다.&#xA;                e.preventDefault();&#xA;                e.stopPropagation();&#xA;                document.querySelector(&#39;.overflow-width&#39;).style.top=0%;&#xA;                document.querySelector(&#39;.overflow-width&#39;).style.left = ${parseInt(document.querySelector(&#39;.overflow-width&#39;).style.left) + 1}%;&#xA;            }&#xA;        }&#xA;    });&#xA;&#xA;남의코드 복붙해야지 히히 하고 들어갔다가 저한테 맞는 방법, 제가 당장 필요한 방법은 따로 있다는 사실을 깨달았습니다.&#xA;&#xA;물론 제가 당장 필요한 방법을 구현해 놓은 포스트가 있었다면 완전 럭키비키겠지만요.&#xA;&#xA;그래도 최대한 제가 만들어보고 남의 손을 빌리는 게 맞는 것 같습니다.]]&gt;</description>
      <content:encoded><![CDATA[<p><a href="/jyhyun1008/tag:%EA%B0%9C%EB%B0%9C%EA%B3%BC%EC%A0%95" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">개발과정</span></a> <a href="/jyhyun1008/tag:VanillaJS" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">VanillaJS</span></a></p>

<p>포트폴리오 사이트를 만드는 중 프로젝트를 보여줄 때는 가로스크롤을 하는 게 좋을 것 같아서 만들어 보았습니다.</p>

<p>우선 이렇게 구조를 짰습니다.</p>

<pre><code>        &lt;div id=&#34;content1&#34;&gt;
            &lt;div id=&#34;content-scroll&#34;&gt;
                &lt;div class=&#34;width-fix&#34;&gt;
                    &lt;h1&gt;Items.&lt;/h1&gt;
                    &lt;div class=&#34;overflow-width&#34;&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
</code></pre>

<p>CSS에서는 <code>.overflow-width</code> 에만 <code>position: relative</code> 를 적용해 주었습니다. 그리고 자바스크립트에서는 다음과 같은 코드를 적용했어요.</p>

<pre><code>    document.querySelector(&#39;.overflow-width&#39;).style.left=`0`;
    // 이게 없으면 후에 style.left 를 불러오지 못합니다.
    document.querySelector(&#39;#content-scroll&#39;).addEventListener(&#39;wheel&#39;,function(e){
        if(e.deltaY &gt; 0){
        // 스크롤을 내리고 있으면,
            if (parseInt(document.querySelector(&#39;.overflow-width&#39;).style.left) &gt; -100) {
                // 가로스크롤이 충분히 진행되지 않았으면(=스크롤이 필요하면)
                e.preventDefault();
                e.stopPropagation();
                // 기존 세로 스크롤을 막습니다.
                document.querySelector(&#39;.overflow-width&#39;).style.top=`0%`;
                document.querySelector(&#39;.overflow-width&#39;).style.left = `${parseInt(document.querySelector(&#39;.overflow-width&#39;).style.left) - 1}%`;
                // 그리고 스크롤 양에 따라 요소의 위치를 옮겨줍니다.
            }
        } else if (e.deltaY &lt; 0) {
        // 스크롤을 올리고 있으면,
            if (parseInt(document.querySelector(&#39;.overflow-width&#39;).style.left) &lt; 0) {
                // 가로스크롤이 충분히 돌아오지 않았으면(=스크롤이 필요하면)
                // 아래 내용은 동일합니다.
                e.preventDefault();
                e.stopPropagation();
                document.querySelector(&#39;.overflow-width&#39;).style.top=`0%`;
                document.querySelector(&#39;.overflow-width&#39;).style.left = `${parseInt(document.querySelector(&#39;.overflow-width&#39;).style.left) + 1}%`;
            }
        }
    });
</code></pre>

<p><img src="https://peachtart2.s3.amazonaws.com/tart/79f53cb4-952d-4064-974e-63db759a50ca.webp" alt=""></p>

<p>남의코드 복붙해야지 히히 하고 들어갔다가 저한테 맞는 방법, 제가 당장 필요한 방법은 따로 있다는 사실을 깨달았습니다.</p>

<p>물론 제가 당장 필요한 방법을 구현해 놓은 포스트가 있었다면 완전 럭키비키겠지만요.</p>

<p>그래도 최대한 제가 만들어보고 남의 손을 빌리는 게 맞는 것 같습니다.</p>
]]></content:encoded>
      <guid>https://blog.daydream.ink/jyhyun1008/js-garoseukeurol-mandeulgi</guid>
      <pubDate>Mon, 23 Sep 2024 07:24:54 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript의 if문에서 false로 판정하는 값들</title>
      <link>https://blog.daydream.ink/jyhyun1008/javascriptyi-ifmuneseo-falsero-panjeonghaneun-gabsdeul</link>
      <description>&lt;![CDATA[#스터디 #VanillaJS&#xA;&#xA;모르는 내용은 아니지만, 매번 코드를 작성할 때 잊게 되는 것 같아 정리 겸 확실히 하기 위해 적습니다.&#xA;&#xA;false: Boolean&#xA;0: 정수 0&#xA;-0: 정수 -0&#xA;0n: BigInt&#xA;&#34;&#34;: 빈 문자열&#xA;null&#xA;undefined&#xA;NaN&#xA;&#xA;0n: BigInt&#xA;&#xA;정수 리터럴의 뒤에 n을 붙이거나(10n) 함수 BigInt()를 호출해 BigInt를 생성할 수 있습니다. 일반적으로 Int 에서 가장 큰 정수는 9007199254740991 이지만, 그 뒤에 n을 붙이거나 BigInt() 로 감싸면 그 이상의 숫자 9007199254740992n 도 담을 수 있습니다.&#xA;&#xA;undefined에 대한 판정&#xA;&#xA;예를 들어, 어떤 변수에 값이 정의되어 있는지 아닌지에 따라 if 문을 써야 한다면, 절대로 &#xA;&#xA;if ( 변수이름 ) {&#xA;} else {&#xA;}&#xA;&#xA;이렇게 판정하면 안 되고, === 라든가 !== 라든가 typeof 같은 걸 사용하는 것이 맞습니다.&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p><a href="/jyhyun1008/tag:%EC%8A%A4%ED%84%B0%EB%94%94" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">스터디</span></a> <a href="/jyhyun1008/tag:VanillaJS" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">VanillaJS</span></a></p>

<p>모르는 내용은 아니지만, 매번 코드를 작성할 때 잊게 되는 것 같아 정리 겸 확실히 하기 위해 적습니다.</p>
<ol><li><code>false</code>: Boolean</li>
<li><code>0</code>: 정수 0</li>
<li><code>-0</code>: 정수 -0</li>
<li><code>0n</code>: BigInt</li>
<li><code>&#34;&#34;</code>: 빈 문자열</li>
<li><code>null</code></li>
<li><code>undefined</code></li>
<li><code>NaN</code></li></ol>

<h2 id="0n-bigint" id="0n-bigint">0n: BigInt</h2>

<p>정수 리터럴의 뒤에 <code>n</code>을 붙이거나(<code>10n</code>) 함수 <code>BigInt()</code>를 호출해 BigInt를 생성할 수 있습니다. 일반적으로 Int 에서 가장 큰 정수는 <code>9007199254740991</code> 이지만, 그 뒤에 <code>n</code>을 붙이거나 <code>BigInt()</code> 로 감싸면 그 이상의 숫자 <code>9007199254740992n</code> 도 담을 수 있습니다.</p>

<h2 id="undefined에-대한-판정" id="undefined에-대한-판정">undefined에 대한 판정</h2>

<p>예를 들어, 어떤 변수에 값이 정의되어 있는지 아닌지에 따라 <code>if</code> 문을 써야 한다면, 절대로</p>

<pre><code>if ( 변수이름 ) {
} else {
}
</code></pre>

<p>이렇게 판정하면 안 되고, <code>===</code> 라든가 <code>!==</code> 라든가 <code>typeof</code> 같은 걸 <a href="https://witch.work/posts/javascript-check-undefined" rel="nofollow">사용</a>하는 것이 맞습니다.</p>
]]></content:encoded>
      <guid>https://blog.daydream.ink/jyhyun1008/javascriptyi-ifmuneseo-falsero-panjeonghaneun-gabsdeul</guid>
      <pubDate>Wed, 11 Sep 2024 20:54:43 +0900</pubDate>
    </item>
    <item>
      <title>Google API를 활용한 정적 위키엔진, 초코스프레드</title>
      <link>https://blog.daydream.ink/jyhyun1008/cokoseupeuredeu-wiki</link>
      <description>&lt;![CDATA[#프로젝트 #VanillaJS #블로그&#xA;&#xA;이미지&#xA;&#xA;링크&#xA;&#xA;개발 시작: 2024.08.23. ~ 2024.08.24.&#xA;개발 기간: 이틀&#xA;&#xA;구글 스프레드시트와 깃허브 페이지를 사용하여 제작한 위키입니다.&#xA;&#xA;구글스프레드시트의 &#39;시트 하나&#39; 가 문서 하나가 됩니다. 따라서... 만약 이걸로 수백 개의 문서를 가진 대형 위키를 만들려고 하면 좀 문제가 커집니다. 소형 위키에 사용하고자 합니다.&#xA;문서를 편집하기 위해서는 그냥 원본 스프레드시트를 편집하는 방법이 있고(이 경우 기록이 남지 않으므로 추천하지 않아요), 자체 편집 기능을 이용하는 방법이 있습니다.&#xA;&#xA;주의사항&#xA;&#xA;API 제한 설정: 구글에서  API키를 받을 때 범위를 특정 웹사이트, 스프레드시트로만 설정합니다.&#xA;연결할 스프레드시트의 제목은 어떤 것이어도 괜찮습니다. ID만 제대로 따면 됩니다.&#xA;링크가 있는 모든 사람이 볼 수 있음 으로 하고, 링크를 복사한 뒤 d/와 /edit 사이에 있는 문자열 복사&#xA;&#xA;왜 만들었나요?&#xA;&#xA;위키를 벌쳐에서 빼낼 수 있는 방법... &#xA;그러나 팬덤 위키나 기존 위키 서비스를 쓰지 않는 방법&#xA;php 기반 무료호스팅도 안 쓸수있는 방법(이건 도메인 연결도 못함...) &#xA;&#xA;을 찾다가 만들었습니다.&#xA;&#xA;어떻게 만들었나요?&#xA;&#xA;구글 스프레드시트 API 문서 중에 자바스크립트 빠른 시작 이라는 게 있습니다. 이걸 기본으로 해서 제작했습니다.&#xA;불러오고 편집하는 것은 셀 값 읽기 및 쓰기에 대략적인 내용이 있습니다.&#xA;마크다운 파싱은 Marked.js를 사용했습니다.&#xA;&#xA;가능한 것&#xA;&#xA;문서 읽기&#xA;구글 로그인, 로그아웃&#xA;이미 존재하는 문서의 편집&#xA;&#xA;해야하는 것&#xA;&#xA;없던 문서의 생성&#xA;문서를 이전 버전으로 되돌리기&#xA;자잘한 속성 (몇 번째 버전인지, 언제 마지막으로 수정되었는지)의 표시]]&gt;</description>
      <content:encoded><![CDATA[<p><a href="/jyhyun1008/tag:%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">프로젝트</span></a> <a href="/jyhyun1008/tag:VanillaJS" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">VanillaJS</span></a> <a href="/jyhyun1008/tag:%EB%B8%94%EB%A1%9C%EA%B7%B8" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">블로그</span></a></p>

<p><img src="https://for.stella.place/D1/webpublic-aa523af4-026d-47e0-a577-fa84690cfd60.png" alt="이미지"></p>

<p><a href="https://wiki.rongo.moe" rel="nofollow">링크</a></p>
<ul><li>개발 시작: 2024.08.23. ~ 2024.08.24.</li>
<li>개발 기간: 이틀</li></ul>

<p>구글 스프레드시트와 깃허브 페이지를 사용하여 제작한 위키입니다.</p>
<ul><li>구글스프레드시트의 &#39;시트 하나&#39; 가 문서 하나가 됩니다. 따라서... 만약 이걸로 수백 개의 문서를 가진 대형 위키를 만들려고 하면 좀 문제가 커집니다. 소형 위키에 사용하고자 합니다.</li>
<li>문서를 편집하기 위해서는 그냥 <strong>원본 스프레드시트</strong>를 편집하는 방법이 있고(이 경우 기록이 남지 않으므로 추천하지 않아요), <strong>자체 편집 기능</strong>을 이용하는 방법이 있습니다.</li></ul>

<h2 id="주의사항">주의사항</h2>
<ul><li>API 제한 설정: 구글에서  API키를 받을 때 범위를 특정 웹사이트, 스프레드시트로만 설정합니다.</li>
<li>연결할 스프레드시트의 제목은 어떤 것이어도 괜찮습니다. ID만 제대로 따면 됩니다.</li>
<li>링크가 있는 모든 사람이 <strong>볼 수 있음</strong> 으로 하고, 링크를 복사한 뒤 d/와 /edit 사이에 있는 문자열 복사</li></ul>

<h2 id="왜-만들었나요" id="왜-만들었나요">왜 만들었나요?</h2>
<ul><li>위키를 벌쳐에서 빼낼 수 있는 방법...</li>
<li>그러나 팬덤 위키나 기존 위키 서비스를 쓰지 않는 방법</li>
<li>php 기반 무료호스팅도 안 쓸수있는 방법(이건 도메인 연결도 못함...)</li></ul>

<p>을 찾다가 만들었습니다.</p>

<h2 id="어떻게-만들었나요" id="어떻게-만들었나요">어떻게 만들었나요?</h2>
<ul><li>구글 스프레드시트 API 문서 중에 <a href="https://developers.google.com/sheets/api/quickstart/js?hl=ko" rel="nofollow">자바스크립트 빠른 시작</a> 이라는 게 있습니다. 이걸 기본으로 해서 제작했습니다.</li>
<li>불러오고 편집하는 것은 <a href="https://developers.google.com/sheets/api/guides/values?hl=ko" rel="nofollow">셀 값 읽기 및 쓰기</a>에 대략적인 내용이 있습니다.</li>
<li>마크다운 파싱은 <a href="https://marked.js.org/" rel="nofollow">Marked.js</a>를 사용했습니다.</li></ul>

<h2 id="가능한-것" id="가능한-것">가능한 것</h2>
<ul><li>문서 읽기</li>
<li>구글 로그인, 로그아웃</li>
<li>이미 존재하는 문서의 편집</li></ul>

<h2 id="해야하는-것" id="해야하는-것">해야하는 것</h2>
<ul><li>없던 문서의 생성</li>
<li>문서를 이전 버전으로 되돌리기</li>
<li>자잘한 속성 (몇 번째 버전인지, 언제 마지막으로 수정되었는지)의 표시</li></ul>
]]></content:encoded>
      <guid>https://blog.daydream.ink/jyhyun1008/cokoseupeuredeu-wiki</guid>
      <pubDate>Sat, 24 Aug 2024 00:10:34 +0900</pubDate>
    </item>
    <item>
      <title>Open AI를 활용한 다기능 Misskey 챗봇, 파이(Pi)</title>
      <link>https://blog.daydream.ink/jyhyun1008/open-ai-dagineung-caesbos-mandeulgi</link>
      <description>&lt;![CDATA[#프로젝트 #VanillaJS #연합우주&#xA;&#xA;img src=&#34;https://for.stella.place/D1/a3174c40-acfb-4c6a-aa4c-f31e364508b2.webp&#34; class=&#34;thumbnail&#34;&#xA;&#xA;  OpenAI의 API를 이용해서 다양한 기능을 지원하는 Misskey 챗봇을 제작했습니다.&#xA;&#xA;이미지&#xA;&#xA;최근 &#39;파이&#39;를 다시 만들고 있습니다. 거의 처음부터 다시 만들었다고 보시면 됩니다.&#xA;&#xA;&#39;파이&#39;는 이전 피치타르트 시절 웹브라우저에서 구동했었던 챗봇입니다. 파이를 구동하기 위해서는 운영체제 상관 없이 24/7 꺼지지 않는 컴퓨터와 브라우저가 있으면 됩니다. 미스키 계정으로의 액세스 토큰과 챗GPT 토큰은 GET 방식으로 받습니다.&#xA;&#xA;파이 리포지토리: 링크&#xA;  위 폴더 중 js/main.js 가 중요합니다!&#xA;&#xA;구현한 기능&#xA;&#xA;0. 자동 포스트&#xA;&#xA;정해진 시간마다 한번씩 자동으로 게시글을 남깁니다. 이 때, 매주 매 시간별로 일정을 셋팅하고, 저녁 메뉴와 대화 주제의 경우에는 랜덤 선택지를 두어 다양한 주제의 게시글을 작성하도록 했습니다.&#xA;&#xA;1. 단순 채팅&#xA;&#xA;유저의 멘션에 반응해서 답글을 남깁니다. 이번 버전의 파이는 자신있는 주제가 있고 자신없는 주제가 있어요. 코딩, 수학, 의학 등과 같은 hallucination에 민감한 소재는 피하도록 했습니다.&#xA;&#xA;2. 하루 채팅 제한&#xA;&#xA;하루 20회의 채팅 횟수 제한이 있습니다.&#xA;&#xA;3. 호감도&#xA;&#xA;일반 채팅 포함, 대화 내용이 챗봇의 입장에서 긍정적이었는지 부정적이었는지 평가하여 호감도를 증감 합니다. &#xA;&#xA;호감도가 일정 수준 이상이면 대화의 퀄리티가 증가하게 되며, 특히 멘션을 줄 때마다 자신의 감정에 따른 이미지를 첨부해 줍니다.&#xA;&#xA;4. 맞팔로우&#xA;&#xA;유저로부터 맞팔로우 요청으로 보이는 언급이 있으면 맞팔로우 해 줍니다. 단, 이미 파이를 팔로우한 유저에 한해 맞팔로우 합니다.&#xA;&#xA;5. 리마인더&#xA;&#xA;img src=&#34;https://for.stella.place/D1/39b05ba8-0bf5-40c8-a838-894fef922992.png&#34; width=&#34;400px&#34;&#xA;&#xA;유저로부터 시각 언급이 있으면(몇 시, 몇시간 후 둘 다 가능) 자동으로 그 시각을 저장해 두었다가 리마인드 해 줍니다.&#xA;&#xA;코드의 흐름&#xA;&#xA;유저가 파이에게 질문을 합니다.&#xA;브라우저의 페이지는 주기적으로 멘션창을 확인하고, 유저의 질문을 감지합니다.&#xA;로컬스토리지에서 질문한 사람의 호감도와, 오늘의 잔여 질문 횟수를 읽습니다.&#xA;질문 횟수가 남아있으면 gpt-4o 에게 메인 프롬프트, 호감도에 따른 프롬프트, 질문한 사람의 이름을 전달합니다.&#xA;프롬프트와 챗봇의 답변을 gpt-4o-mini 에게 전달하여, 대화가 긍정적이었는지 부정적이었는지 / 리마인더 언급이 있었는지 / 맞팔 요청이 있었는지 / 드라이브의 이미지 중 뭘 쓰는 게 좋은지 간단한 json 형식으로 반환합니다.&#xA;대화가 긍정적이었는지 부정적이었는지 여부에 따라 호감도를 재설정하고, 잔여 질문 횟수도 조정합니다.&#xA;리마인더 언급이 있을 경우 해당 데이터를 로컬스토리지에 저장합니다.&#xA;반환된 드라이브의 이미지를 사용해서 유저에게 답변합니다.&#xA;맞팔 요청이 있었을 경우 맞팔합니다. 그러나 맞팔로우 이슈가 있을 경우(파이를 팔로우하지 않음, 이미 팔로우한 유저 등) gpt-4o-mini에게 오류 전달 후 유저에게 다시 답변합니다.&#xA;&#xA;이와 같이, 파이에게 멘션 하나를 날리면 이전과 다르게 gpt로의 요청이 최소 두 번, 최대 세 번까지 이루어집니다.&#xA;&#xA;TODO&#xA;다른 misskey 서버에서 비슷한 챗봇을 구현할 수 있게, 커스터마이징이 쉽도록 settings.js에 옵션을 추가할 계획입니다.&#xA;README.md 를 영어로, 친절하게 수정할 계획입니다.&#xA;코드 내의 주석 언어를 영어로 수정할 계획입니다.&#xA;채팅 제한이 있는 노트에도 멘션을 달아야 합니다. (답해야 할 노트를 멘션 갯수로 판정하기 때문입니다.)]]&gt;</description>
      <content:encoded><![CDATA[<p><a href="/jyhyun1008/tag:%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">프로젝트</span></a> <a href="/jyhyun1008/tag:VanillaJS" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">VanillaJS</span></a> <a href="/jyhyun1008/tag:%EC%97%B0%ED%95%A9%EC%9A%B0%EC%A3%BC" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">연합우주</span></a></p>

<p><img src="https://for.stella.place/D1/a3174c40-acfb-4c6a-aa4c-f31e364508b2.webp" class="thumbnail"></p>

<blockquote><p>OpenAI의 API를 이용해서 다양한 기능을 지원하는 Misskey 챗봇을 제작했습니다.</p></blockquote>

<p><img src="https://for.stella.place/D1/a3174c40-acfb-4c6a-aa4c-f31e364508b2.webp" alt="이미지"></p>

<p>최근 &#39;파이&#39;를 다시 만들고 있습니다. 거의 처음부터 다시 만들었다고 보시면 됩니다.</p>

<p>&#39;파이&#39;는 이전 피치타르트 시절 <strong>웹브라우저에서</strong> 구동했었던 챗봇입니다. 파이를 구동하기 위해서는 운영체제 상관 없이 24/7 꺼지지 않는 컴퓨터와 브라우저가 있으면 됩니다. 미스키 계정으로의 액세스 토큰과 챗GPT 토큰은 GET 방식으로 받습니다.</p>
<ul><li>파이 리포지토리: <a href="https://github.com/jyhyun1008/pichan" rel="nofollow">링크</a>
<ul><li>위 폴더 중 js/main.js 가 중요합니다!</li></ul></li></ul>

<h2 id="구현한-기능" id="구현한-기능">구현한 기능</h2>

<h3 id="0-자동-포스트" id="0-자동-포스트">0. 자동 포스트</h3>

<p><img src="https://for.stella.place/D1/0950e00d-0876-47e5-b9a0-08b2fd56d2ac.png" alt=""></p>

<p>정해진 시간마다 한번씩 자동으로 게시글을 남깁니다. 이 때, 매주 매 시간별로 일정을 셋팅하고, 저녁 메뉴와 대화 주제의 경우에는 랜덤 선택지를 두어 다양한 주제의 게시글을 작성하도록 했습니다.</p>

<h3 id="1-단순-채팅" id="1-단순-채팅">1. 단순 채팅</h3>

<p><img src="https://for.stella.place/D1/d3024157-ccfc-405a-85da-5cfdb17cff0b.webp" alt=""></p>

<p>유저의 멘션에 반응해서 답글을 남깁니다. 이번 버전의 파이는 자신있는 주제가 있고 자신없는 주제가 있어요. 코딩, 수학, 의학 등과 같은 hallucination에 민감한 소재는 피하도록 했습니다.</p>

<h3 id="2-하루-채팅-제한" id="2-하루-채팅-제한">2. 하루 채팅 제한</h3>

<p>하루 20회의 채팅 횟수 제한이 있습니다.</p>

<h3 id="3-호감도" id="3-호감도">3. 호감도</h3>

<p><img src="https://for.stella.place/D1/b1fca2b7-d18f-46b5-99ce-d0a1977ee7e0.webp" alt=""></p>

<p>일반 채팅 포함, 대화 내용이 챗봇의 입장에서 긍정적이었는지 부정적이었는지 평가하여 호감도를 증감 합니다.</p>

<p>호감도가 일정 수준 이상이면 대화의 퀄리티가 증가하게 되며, 특히 멘션을 줄 때마다 자신의 감정에 따른 이미지를 첨부해 줍니다.</p>

<h3 id="4-맞팔로우" id="4-맞팔로우">4. 맞팔로우</h3>

<p><img src="https://for.stella.place/D1/a6cb0022-c12c-4e23-99ad-06ce226748e1.webp" alt=""></p>

<p>유저로부터 맞팔로우 요청으로 보이는 언급이 있으면 맞팔로우 해 줍니다. 단, 이미 파이를 팔로우한 유저에 한해 맞팔로우 합니다.</p>

<h3 id="5-리마인더" id="5-리마인더">5. 리마인더</h3>

<p><img src="https://for.stella.place/D1/39b05ba8-0bf5-40c8-a838-894fef922992.png"></p>

<p>유저로부터 시각 언급이 있으면(몇 시, 몇시간 후 둘 다 가능) 자동으로 그 시각을 저장해 두었다가 리마인드 해 줍니다.</p>

<h2 id="코드의-흐름" id="코드의-흐름">코드의 흐름</h2>
<ul><li>유저가 파이에게 질문을 합니다.</li>
<li>브라우저의 페이지는 주기적으로 멘션창을 확인하고, 유저의 질문을 감지합니다.</li>
<li>로컬스토리지에서 질문한 사람의 호감도와, 오늘의 잔여 질문 횟수를 읽습니다.</li>
<li>질문 횟수가 남아있으면 gpt-4o 에게 메인 프롬프트, 호감도에 따른 프롬프트, 질문한 사람의 이름을 전달합니다.</li>
<li>프롬프트와 챗봇의 답변을 gpt-4o-mini 에게 전달하여, 대화가 긍정적이었는지 부정적이었는지 / 리마인더 언급이 있었는지 / 맞팔 요청이 있었는지 / 드라이브의 이미지 중 뭘 쓰는 게 좋은지 간단한 json 형식으로 반환합니다.</li>
<li>대화가 긍정적이었는지 부정적이었는지 여부에 따라 호감도를 재설정하고, 잔여 질문 횟수도 조정합니다.</li>
<li>리마인더 언급이 있을 경우 해당 데이터를 로컬스토리지에 저장합니다.</li>
<li>반환된 드라이브의 이미지를 사용해서 유저에게 답변합니다.</li>
<li>맞팔 요청이 있었을 경우 맞팔합니다. 그러나 맞팔로우 이슈가 있을 경우(파이를 팔로우하지 않음, 이미 팔로우한 유저 등) gpt-4o-mini에게 오류 전달 후 유저에게 다시 답변합니다.</li></ul>

<p>이와 같이, 파이에게 멘션 하나를 날리면 이전과 다르게 gpt로의 요청이 최소 두 번, 최대 세 번까지 이루어집니다.</p>

<h2 id="todo" id="todo">TODO</h2>
<ul><li>다른 misskey 서버에서 비슷한 챗봇을 구현할 수 있게, 커스터마이징이 쉽도록 <code>settings.js</code>에 옵션을 추가할 계획입니다.</li>
<li><code>README.md</code> 를 영어로, 친절하게 수정할 계획입니다.</li>
<li>코드 내의 주석 언어를 영어로 수정할 계획입니다.</li>
<li>채팅 제한이 있는 노트에도 멘션을 달아야 합니다. (답해야 할 노트를 멘션 갯수로 판정하기 때문입니다.)</li></ul>
]]></content:encoded>
      <guid>https://blog.daydream.ink/jyhyun1008/open-ai-dagineung-caesbos-mandeulgi</guid>
      <pubDate>Thu, 22 Aug 2024 10:23:31 +0900</pubDate>
    </item>
  </channel>
</rss>