<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>hit-that-drum.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Fri, 30 Jan 2026 05:11:29 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>hit-that-drum.log</title>
            <url>https://images.velog.io/images/hit-that-drum/profile/a3445c79-a506-468a-b0b1-24de4bea4c73/85816029.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. hit-that-drum.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/hit-that-drum" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[TWISTERS] Docker로 프로젝트 띄우기]]></title>
            <link>https://velog.io/@hit-that-drum/TWISTERS-Docker%EB%A1%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%9D%84%EC%9A%B0%EA%B8%B0</link>
            <guid>https://velog.io/@hit-that-drum/TWISTERS-Docker%EB%A1%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%9D%84%EC%9A%B0%EA%B8%B0</guid>
            <pubDate>Fri, 30 Jan 2026 05:11:29 GMT</pubDate>
            <description><![CDATA[<ul>
<li>Docker란?
Go언어로 작성된 리눅스 컨테이너 기반으로하는 오픈소스 가상화 플랫폼이다. 이 플랫폼을 이용할 때의 장점은 프로젝트 별로 환경설정이 필요하지 않다는 점이다. 사실 개발하다 보면 초반 환경구축이 너무 힘들어서 포기하고 싶을 때도 많은데 Docker를 사용하면 훨씬 간편해진다. 좀 더 개발이 진행되기 전에 먼저 Docker 컨테이너로 프로젝트를 띄울 수 있게 만들기로 했다.</li>
</ul>
<br>

<p>Docker로 프로젝트를 띄우려면 가장 최상단의 폴더에서  <code>docker-compose.yml</code> 이 있어야 한다. 이걸로 띄울 프로젝트에 연관되어 있는 정보를 주어야 한다. 나의 프로젝트는 프론트엔드-백엔드-데이터베이스 이렇게 세 개가 있어서 이 세가지의 정보를 넣어주었다.</p>
<p>데이터베이스 관련해서는... 만약 같은 컴퓨터를 사용한다면 로컬에 데이터베이스 데이터가 남아있어서 괜찮겠지만 다른 컴퓨터를 사용하면 텅 빈 데이터베이스로 시작하게 된다. 즉, 나중에 배포를 하면 호스팅을 해야 된다는 건데 뭐... 그것도 나중에 찾아보면 되겠지... 일단은 각각의 컴퓨터에서 다른 사용자 정보 등으로 개발하려고 한다. Docker는 데이터에 대한 저장기능은 존재하지 않는다.</p>
<br>


<ul>
<li><p>docker-compose.yml</p>
<pre><code>services:
# Database Service
db:
  image: mysql:8.0
  restart: always
  env_file:
    - .env
  environment:
    MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
    MYSQL_DATABASE: ${DB_NAME}
  ports:
    - &#39;3306:3306&#39;
  volumes:
    - ./data:/var/lib/mysql
    - ./init.sql:/docker-entrypoint-initdb.d/init.sql

# Backend Service
backend:
  build: ./backend
  restart: always
  env_file:
    - .env
  volumes:
    - ./backend:/app
    - /app/node_modules
  ports:
    - &#39;5050:5050&#39;
  environment:
    DB_HOST: ${DB_HOST}
    DB_USER: ${DB_USER}
    DB_PASSWORD: ${DB_PASSWORD}
    DB_NAME: ${DB_NAME}
    PORT: ${PORT}
    DATABASE_URL: mysql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:3306/${DB_NAME}
  depends_on:
    - db

# Frontend Service
frontend:
  build: ./frontend
  env_file:
    - .env
  volumes:
    - ./frontend:/app
    - /app/node_modules
  ports:
    - &#39;5173:5173&#39;
  environment:
    - VITE_API_BASE_URL=${VITE_API_BASE_URL}
    - VITE_GOOGLE_CLIENT_ID=${VITE_GOOGLE_CLIENT_ID}
  depends_on:
    - backend</code></pre></li>
</ul>
<br>

<p>그리고 이제 프론트엔드와 백엔드 각각에 그 프로젝트들을 돌릴 수 있는 정보들을 주어야 한다. </p>
<ul>
<li>frontend/Dockerfile</li>
</ul>
<pre><code># Use Node.js as the base image
FROM node:20-alpine

# Set working directory
WORKDIR /app

# Copy package files and install dependencies
COPY package*.json ./
RUN npm install

# Copy the rest of the code
COPY . .

# Vite&#39;s default dev port
EXPOSE 5173

# Run vite dev
CMD [&quot;npm&quot;, &quot;run&quot;, &quot;dev&quot;, &quot;--&quot;, &quot;--host&quot;]</code></pre><ul>
<li>backend</li>
</ul>
<pre><code># Use Node.js as the base image
FROM node:20-alpine

# Set working directory
WORKDIR /app

# Copy package files and install dependencies
COPY package*.json ./
RUN npm install

# Copy the rest of the code
COPY . .

# Expose the port your backend runs on (usually 5000 or 3000)
EXPOSE 5050

# Start the app using tsx (as seen in your earlier logs)
CMD [&quot;npm&quot;, &quot;run&quot;, &quot;dev&quot;]</code></pre><br>

<p>포스팅 작성할 때는 이렇게 간단하게밖에 쓸 수 없는데 Docker로 띄우는 것만 거의 4-5시간...? 정도는 쓴 것 같다...
일단 처음에는... ㅎㅎ... <code>.env</code> 파일의 존재를 잊음 ㅎㅎ... 
한창 하던 중에 깨달아서 <code>.env</code> 파일 넣고 하니까 그 전에 셋팅해 놨던 모든 것이 리셋된 거나 마찬가지가 되었다ㅋㅋㅋ
그 뒤부터는 다시 셋팅 파일들이나 관련 코드들에서 <code>.env</code> 파일에서 땡겨와서 써야 하는 부분들을 다시 처리해 주어야 했다. </p>
<p><img src="https://velog.velcdn.com/images/hit-that-drum/post/9c39bb59-0973-4495-9d19-b5adfbada699/image.png" alt=""></p>
<p>어쨌든 이제 돌아간다^^! 얏호^^!
프로젝트 켤 때마다 각각의 폴더에 들어가서 <code>npm run dev</code> 이런 거 안 쳐줘도 돼서 좋다!</p>
<br>

<ul>
<li><p>프로젝트 시작 방법
최상단 폴더에서 <code>docker-compose up -d</code> 커맨드 입력하면 끝!</p>
</li>
<li><p>프로젝트 종료  방법
<code>docker-compose down</code> 커맨드 입력하면 끝!</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 2026-01-20 React.ClipboardEvent]]></title>
            <link>https://velog.io/@hit-that-drum/TIL-2026-01-20-React.ClipboardEvent</link>
            <guid>https://velog.io/@hit-that-drum/TIL-2026-01-20-React.ClipboardEvent</guid>
            <pubDate>Tue, 20 Jan 2026 01:09:55 GMT</pubDate>
            <description><![CDATA[<p>상품등록 할 때 이미지 주소를 가져오면 그 이미지를 가지고 오는 로직을 가진 파일 업로드 컴포넌트가 있었다.
이것도 나름의... 개선을 한 거였는데(그 전에는 이미지를 직접 파일로 올렸어야만 했음)
우리한테 들어오는 입점처들이 상품 정보에 관련된 걸 예쁘게 정리해서 주는 곳은 거<del>의 없어서 노가다가 너무 심했다고 한다.
그걸 url로 가지고 올 수 있게 하면서 조금은 편해졌나... 했는데 이런!
어떤 사이트의 이미지들은(정확히 원인은 알 수 없지만 일종의 보안과 관련된 이유라고 추측) 이미지 url을 이용해서 이미지를 가져오면 깨져서 등록되거나 거부되는 경우가 많았다!
그 때에는 어쩔 수 없이 담당자가 그 이미지를 컴퓨터에 다운로드 받아서 다시 파일 업로드를 해야 하는 최초의 방식으로 진행했는데 명절을 맞아 업무가 과부하 되면서 기능 개선의 요청이 들어왔다.
그것은 바로</del>! &quot;이미지 복사&quot;를 해서 바로 붙여넣기가 되게 해달라는 것.
생각해보니까 이 기능 나도 엄청 편리하게 쓰고 있었는데 첨부터 해 줄 생각을 딱히 못했네...^^;;;
심지어 어려울 것 같아서 빨리 될 수 있을지 모르겠다 했는데 매우 쉬웠다.</p>
<p>아래와 같은 함수를 만들고 그 함수를 동작하는 input 태그에 <code>onPaste</code> 로 걸어주니 바로 실행되었다.</p>
<pre><code class="language-javascript">  const handlePaste = async (event: React.ClipboardEvent) =&gt; {
    const items = event.clipboardData.items;
    const files: File[] = [];

    for (let i = 0; i &lt; items.length; i++) {
      if (items[i].type.indexOf(&#39;image&#39;) !== -1) {
        const file = items[i].getAsFile();
        if (file) files.push(file);
      }
    }

    if (files.length &gt; 0) {
      const validatedFiles = await getValidatedFiles(files, requiredConditions);
      await addFiles(validatedFiles);

      event.preventDefault();
    }
  };</code></pre>
  <br>

<blockquote>
<ol>
<li>The Three Main Types
There are three events associated with this type:
onCopy: Fires when the user initiates a &quot;copy&quot; action.
onCut: Fires when the user initiates a &quot;cut&quot; action.
onPaste: Fires when the user initiates a &quot;paste&quot; action.</li>
</ol>
</blockquote>
<blockquote>
<ol start="2">
<li>The clipboardData Property
The most important part of a ClipboardEvent is the clipboardData property. It is a DataTransfer object that acts as a container for the data being moved.
It has several key methods and properties:
getData(format): Retrieves text or data from the clipboard (usually used in onPaste).
setData(format, data): Modifies what gets put onto the clipboard (usually used in onCopy).
items: A list of DataTransferItem objects. This is crucial for handling files or images (as we saw in your previous question).
files: A list of files being pasted.</li>
</ol>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TWISTERS] 홈페이지 만들기]]></title>
            <link>https://velog.io/@hit-that-drum/TWISTERS-%ED%99%88%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@hit-that-drum/TWISTERS-%ED%99%88%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Sun, 11 Jan 2026 13:05:10 GMT</pubDate>
            <description><![CDATA[<p>개인프로젝트 맨날 시작만 하구 끝내지를 못 하네...
근데 마이루틴 복사하는 건... 만들어도 그냥 했다, 정도니까 뭔가 드라이브가 잘 안 걸리고 작년에는 거의 TF 팀이나 마찬가지로 일해서 정신이 없어가지고... 개인프로젝트 버려놓게 되었다^^
그러다가 이제 해가 바꼈고... 승연이한테 커리어 상담 하는데 내가 노션을 DB로 써서 홈페이지 만드는 거에 어떻게 생각하냐고 물어봤다?
그랬더니 굳이 다른 서비스에 의존해야 할 필요가 있냐고 하면서 그냥 내가 만들래.
흠... 근데 것도 맞긴 맞아.
어차피 모임 노션 나 말고 아무도 안 보기 때문에 거기에 있는 데이터를 기반으로 해서 홈페이지를 만들건, 아니면 그냥 홈페이지만 있건 이 모임을 같이 하는 사람들은 솔직히... 알빠쓰레빠라고 생각할 것 같았다.
그래서 그냥 처음부터 끝까지(제발... 좀 다 만들어보자...) 만들어보자, 생각을 하고는 프로젝트를 시작하게 되었다.</p>
<p>몇 주 지나기는 했는데 대충 몇 주 동안은 뭘 했나 기록해보자.</p>
<ul>
<li>프론트엔드 구축: react / typescript</li>
<li>백엔드 구축: node.js</li>
<li>DB 구축: Maria DB</li>
</ul>
<p>일단 이렇게 스택을 잡고는 뚝딱뚝딱 구축했다.</p>
<br>

<p>일단 현재 상황은 회원가입 페이지와 로그인 페이지가 있고,
회원가입을 할 수 있고 비밀번호 변경을 할 수 있다.
그리고 docker를 이용해서 로컬 DB를 사용할 수 있어서 어느 컴퓨터에서도 일단 구성적으로는 똑같이 할 수 있다(데이터는 다를 지라도... 이건 나중에...).
비밀번호 추상화는 bcrypt로 하고 있는데 이건 보안적으로 좋은 방법이 아니라서 다음 목표는 이걸 passportjs를 이용해서 라이브러리를 사용해서 jwt 방식으로 바꾸는 것.
그 다음에는 로그인을 유지할 수 있게 상태관리를 하거나, 또는 로컬스토리지와 세션스토리지를 적절히 활용하는 것(좀 더 찾아보는 과정이 필요하다).</p>
<br>

<p><img src="https://velog.velcdn.com/images/hit-that-drum/post/62af0550-f16b-4501-95bc-84f196dfa019/image.png" alt=""></p>
<p>구글 OAuth랑 카카오 OAuth 하고 싶은데...
일단 그건 본 페이지 다 만든 다음에 가장 마지막에 해야겠다...</p>
<p>지금은 그냥 아무 이메일이나 넣어도 가입할 수 있는데(정규식에서 안 걸리기만 하면) 나중에는 이메일 인증해야지만 가입할 수 있게도 바꾸고 싶다.
꿈은 일단 이것저것 큼...</p>
<p>설날까지 로그인은 할 수 있게 만들어서 배포를 미리 해야지 같이 모임하는 선배들한테 쨘-! 할 수 있는데 사실 잘 모르겠다ㅋㅋㅋㅋㅋ</p>
<p>백엔드 API 만지다보니까 오히려 기본적인 CRUD는 하면 할 수 있을 것 같다. 
물어물어 하면 할 수 있을 것 같은데 OAuth나 디자인적 부분에서 오히려... 조금 고생할 것도 같은...
게시판 예시같은 것도 찾아보는 거 투두리스트에 넣어야겠다.</p>
<p>어쨌든 2026년 나의 프로젝트는 이것을 하면서 지내보도록 해야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 2025-07-18 git stash drop 되살리기]]></title>
            <link>https://velog.io/@hit-that-drum/TIL-2025-07-18-git-stash-drop-%EB%90%98%EC%82%B4%EB%A6%AC%EA%B8%B0</link>
            <guid>https://velog.io/@hit-that-drum/TIL-2025-07-18-git-stash-drop-%EB%90%98%EC%82%B4%EB%A6%AC%EA%B8%B0</guid>
            <pubDate>Fri, 18 Jul 2025 06:45:03 GMT</pubDate>
            <description><![CDATA[<p>ㅎ ㅏ... 
잠깐 버그 고쳐야 해서 하던 거 stash 해 놓고 작업한 다음 돌아와서는 <code>git stash pop</code> 을 쳐야 되는데 <code>git stash drop stash@{0}</code> 을 쳐버려서 날려버렸다... OTL...
아니 진짜... 이거 너무 헷갈리지 않니?! 나만 이런 거니!?
정말 가끔 한 번씩 pop도 나오는 거고 drop도 나오는 거니까 헷갈릴 때가 있어... ㄱ-... 어쨌든...
다시 코드 칠려면 칠 수도 있을 정도의 양이었지만 또 치는 게 간단하지는 않은 약간 귀찮은 작업이라서 얘를 살려보기로 했다.
취업하고 나서 2-3달 정도 됐을 때 그 기간 동안 작업했던 거 지웠어서 ㄴㅇㅁㅇㄱ!!! 했던 거 사수가 복구해 준 적이 있었어가지고 할 수 있는 건 알고 있었다.
물론 그 때의 사수분은 그냥 해 주시기만 하고 어떻게 했는지 자세히는 알려주지 않으셔서 몰라가지고 이번에 알아보자, 하고는 함 해 봤다.
바빠죽겠는데... 일 하기 시러서일까...^^</p>
<p>커서 &amp; 제미나이와 함께하였다.</p>
<br>

<p>먼저 아래와 같이 쳐서 온갖 값의 해시를 얻어낸다.</p>
<pre><code>git fsck --unreachable | grep commit</code></pre><p><code>git fsck</code>는 file system check라고 한다.
verify the integrity and connectivity of the object를 하는 명령어이다.</p>
<p>여기에서 <code>--unreachable</code> 옵션을 넣으면 integrity나 connectivity가 끊어져 있는 해시값들을 찾는 거니까 정상적이지 않은 것, 즉 내가 날려버린 무언가가 들어있을 가능성이 크다.
다만 이 해시들의 순서는 딱히 보장되어 있지 않고 기간은 대충 90-100일 정도인 것 같아서... 나는 이걸 하나하나 다 확인해야만 한다... </p>
<br>

<ul>
<li>대충 아래와 같이 나타난다.</li>
</ul>
<pre><code>Checking object directories: 100% (256/256), done.
Checking objects: 100% (25070/25070), done.
unreachable commit 160005452267ae3cf9e52fd9e3fd770e1fa8288e
unreachable commit 40011e29756d1702be027c4214bb73c336775590
unreachable commit a50182d634cae9f595e0c195129ea45664706c68
unreachable commit 920de0fcc7a9399a5b7e096063b3b1c9a5cfe82e
unreachable commit d014d5a5f8ec8a1cdc43dfef827c10a623f80b77
unreachable commit bc19b4bac5f66da27b32ca434036079f1022f328
unreachable commit 7a202e272062343dc7f3367b5f48ac6ee4ad31d9
unreachable commit 2c240043b943b0a4c591df881f878ec5ba6d1d9c</code></pre><br>

<p>위의 터미널 값을 복사해다가 형식을 <code>git show --stat [hash]</code> 로 해다가 하나하나 확인하였다... </p>
<br>

<p>위의 해시 중에 내가 원하는 브랜치의 데이터 해시는 두 개가 나왔다.
왜 두 개지? 라는 의문과 함께...</p>
<ul>
<li>hash 1</li>
</ul>
<pre><code>git show --stat  7526021b875a641f8f26335f6b2ceb7ad879f55e

commit 7526021b875a641f8f26335f6b2ceb7ad879f55e
Author: -
Date:   Fri Jul 18 10:55:29 2025 +0900

    index on feature/PROD-19237: c40da0e7 [feature/PROD-19237] 식대 부분 취소 모달 개발 진행 중
(END)</code></pre><ul>
<li>hash 2</li>
</ul>
<pre><code>git show --stat  79d5d542966c789485e3f5e05f3ba11424b1c428

commit 79d5d542966c789485e3f5e05f3ba11424b1c428
Merge: c40da0e7 7526021b
Author: -
Date:   Fri Jul 18 10:55:29 2025 +0900

    WIP on feature/PROD-19237: c40da0e7 [feature/PROD-19237] 식대 부분 취소 모달 개발 진행 중

 .../admin/point-history/sikdae-use/sikdaePartialCancelModal.tsx    | 136 +++++++++++++++++++++++-------
 1 file changed, 106 insertions(+), 30 deletions(-)
(END)</code></pre><br>

<p>이 다음에는 이걸 restore 하려고 하는데 cherry-pick을 해보라고 하길래 따라했다.
근데 hash1을 cherry-pick 하려고 하니 아무것도 바뀐 게 없다고 하면서 안 됐다. 뫄???</p>
<p>일단 넘어가서 hash2를 cherry-pick 진행했다.</p>
<p><code>git cherry-pick 79d5d542966c789485e3f5e05f3ba11424b1c428</code></p>
<br>

<p>하지만 되지 않았다.
이게 merge commit이어서 그냥은 할 수 없고 - 옵션을 써야한대서 뫄??? 하지만 어쨌든 해 줘! 하고는 진행했다.</p>
<pre><code>git show --pretty=fuller 79d5d542966c789485e3f5e05f3ba11424b1c428</code></pre><p>이건 무엇을 바꼈는지 예쁘게 보는 명령어라고 한다.
이렇게 확인해서 보니까 확실히 hash2는 hash1과는 다르게 내가 복구시키고 싶었던 작업분이 잘 들어있었다.</p>
<p>근데 커서가 아래와 같이 부모가 두 명이 있다면서 어느 부모에다가 합칠 건지를 골라야 한다고  했다.</p>
<blockquote>
<p>I can see this is a WIP (Work In Progress) merge commit with two parents. For cherry-picking a merge commit, you need to specify which parent to use with the -m option. Let me check what each parent contains:</p>
</blockquote>
<br>

<blockquote>
<p>I can see that this is a WIP stash merge with enhanced changes to the partial cancel modal. The current HEAD is already at the first parent (c40da0e7), so let me try cherry-picking with -m 1 to apply the stash changes:</p>
</blockquote>
<pre><code>git cherry-pick -m 1 79d5d542966c789485e3f5e05f3ba11424b1c428</code></pre><p>이렇게 하니까 내가 원하는대로 잘 살려낼 수 있었다!</p>
<blockquote>
<p>Perfect! The cherry-pick has been resolved successfully. Here&#39;s what happened:
✅ Cherry-pick completed successfully
Issue: The original commit was a merge commit which required the -m option to specify which parent to use
Solution: Used git cherry-pick -m 1 to apply the changes from the WIP stash
Result: The enhanced changes to sikdaePartialCancelModal.tsx have been applied cleanly to your current branch
The new commit 371bfc09 now contains all the WIP improvements including:
Enhanced state management with cancel states
Additional form components and validation
Improved UI with CardDescription and VoneTypography
Better table handling with onCellValueChanged events
Your working tree is clean and ready for continued development. The cherry-pick resolved without any conflicts!</p>
</blockquote>
<br>

<p>근데 살리는 건 살리는 거고, 왜 이렇게 된 건지?는 잘 모르겠어서 더 질문을 해 봤다.
왜 부모가 2명이 됐냐고 하니까 아래와 같이 대답했다.</p>
<blockquote>
<p>The Two-Parent Mystery Explained
The commit 79d5d542 is a merge commit created by git stash. Here&#39;s the breakdown:</p>
</blockquote>
<blockquote>
<p>What happened:</p>
</blockquote>
<ol>
<li>You were working on the feature - making changes to sikdaePartialCancelModal.tsx</li>
<li>You ran git stash - this saved your uncommitted work</li>
<li>Git created a merge commit with two parents:</li>
</ol>
<blockquote>
<p>Parent 1 (c40da0e7):</p>
</blockquote>
<ul>
<li>The commit message: &quot;[feature/PROD-19237] 식대 부분 취소 모달 개발 진행 중&quot;</li>
<li>This was your current HEAD when you stashed</li>
</ul>
<blockquote>
<p>Parent 2 (7526021b):</p>
</blockquote>
<ul>
<li>The commit message: &quot;index on feature/PROD-19237: c40da0e7...&quot;</li>
<li>This contains your uncommitted changes that were stashed</li>
</ul>
<blockquote>
<p>Why Git does this:
When you run git stash, Git doesn&#39;t just save your working directory changes in a simple commit. Instead, it creates a merge commit that preserves:</p>
</blockquote>
<ul>
<li>Your committed work (parent 1)</li>
<li>Your uncommitted changes (parent 2)</li>
<li>The combined state (the merge commit itself)
This allows Git to properly restore both your working directory AND any staged changes when you later git stash pop or git stash apply.</li>
</ul>
<blockquote>
<p>The commit message format:
Git&#39;s standard format for stash commits, where &quot;WIP&quot; means &quot;Work In Progress&quot;.
So you didn&#39;t explicitly create a merge commit - Git automatically created it when you stashed your work! This is why cherry-picking required the -m option to specify which parent&#39;s changes you wanted to apply.</p>
</blockquote>
<br>

<p>맨 처음에는 git stash를 하면 그게 자동적으로 merge commit인 것처럼 말을 했는데 이거저거 찾아보니까 git stash는 merge commit이 아니라고 해서 아니라는데? 이러고 물어봤더니 갑자기 사과하면서ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 
너가 맞다고 merge commit 아니래ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 이 자식이ㅡㅡ</p>
<br>

<p>정수님이랑 이거저거 테스트를 해 본 결과,
git stash를 하면 1번: stash를 한 후의 결과(새 작업분이 사라져 있는 상태), 2번: stash 하기 전의 결과(새 작업분이 포함되어 있는 상태) 이렇게 두 개의 커밋을 남긴다.
평소에는 그냥 이런 상태로 살다가 pop을 하거나 하면 아무런 일 없이 제대로 동작시킬 수 있는데, 이 경우에는 내가 drop을 해 버려서 이 커밋 두 개가 garbage collection으로 넘어갔다.
그런데 정말 신기하게도 1번이 넘어갈 때는 그냥 일반 commit으로 넘어가는데 2번이 넘어갈 때는 얘가 merge commit으로 바뀌어서 넘어간다.
그래서 cherry-pick을 해 올 때는 얘가 merge commit이니까 -m 옵션을 써서 바꿔줘야만 한 것 같다.</p>
<p>휴... 정말 힘드럿따... 
그래도 어케저케 살렸다... 
git은 정말 위대하다...</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 2025-07-18 iterm 한국어 제대로  나오게 하기]]></title>
            <link>https://velog.io/@hit-that-drum/TIL-iterm-%ED%95%9C%EA%B5%AD%EC%96%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%82%98%EC%98%A4%EA%B2%8C-%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@hit-that-drum/TIL-iterm-%ED%95%9C%EA%B5%AD%EC%96%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%82%98%EC%98%A4%EA%B2%8C-%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 18 Jul 2025 06:03:32 GMT</pubDate>
            <description><![CDATA[<p>오랜시간...  별로 안 불편해서 그냥 살았으나... 
오늘 git stash 한 거 pop을 해야 되는 걸 drop해버리는 바람에 각 커밋메세지를 잘 봐야 하는데 도무지 알아먹을 수가 없어서 이제는 알아보았다.</p>
<p>여태까지는 그냥 이렇게 살고 있었다ㅎㅎ</p>
<p><img src="https://velog.velcdn.com/images/hit-that-drum/post/78752a0e-2d15-47ff-8ac1-2e96b9ff08cb/image.png" alt=""></p>
<br>

<p>제미나이와 함께했다.</p>
<pre><code>// 이걸로 확인해서 두 개가 다 utf-8 이 나오면 된다.
// 참고로 이건 잘 나오고 있었다.
git config --get i18n.commitencoding
git config --get i18n.logoutputencoding

// 만약 다른 걸로 설정되어 있다면 아래의 명령어를 친다.
git config --global i18n.commitencoding utf-8
git config --global i18n.logoutputencoding utf-8</code></pre><br>

<p>애초에 잘 되고 있었던 것이었기에 뭐 바꿀 것도 없어서 다음으로 넘어갔다.
다음으로 확인할 것은 Terminal/Shell Encoding (Environment Variables)이라고 한다.</p>
<pre><code>// 아래와 같이 쳤을 때 
// en_US.UTF-8 이나 ko_KR.UTF-8 이 나와야 한다.
echo $LANG
echo $LC_ALL

// 나는 $LANG은 제대로 나오는데 $LC_ALL이 비어있게 나왔다.
// 그래서 아래와 같은 명령어를 쳐줬다.
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8</code></pre><p><img src="https://velog.velcdn.com/images/hit-that-drum/post/f953c6ac-0650-45ef-a421-392d2ad859ab/image.png" alt=""></p>
<p>짠-! 성공-!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TO DO LIST][DAY 06] 2025.06.01  - Heder, Footer 작업]]></title>
            <link>https://velog.io/@hit-that-drum/TO-DO-LISTDAY-06-2025.06.01-Heder-Footer-%EC%9E%91%EC%97%85</link>
            <guid>https://velog.io/@hit-that-drum/TO-DO-LISTDAY-06-2025.06.01-Heder-Footer-%EC%9E%91%EC%97%85</guid>
            <pubDate>Mon, 02 Jun 2025 01:27:17 GMT</pubDate>
            <description><![CDATA[<h3 id="header--footer">Header &amp; Footer</h3>
<ul>
<li>아직 좀 더 해야 될 부분들이 있지만 일단 Header랑 Footer의 모양을 만들어보았다.</li>
<li>여기서 더 해야 할 건 로그인 상태에 따라서 헤더가 바뀌는 거랑(지금은 로그인 후의 헤더 모양), 푸터는 로그인을 하면 사라져서 이걸 display: none 같은 걸 주어야 할 것 같다.</li>
<li>일단 Home을 만들고 나서 로그인 관련 처리를 하고 그 다음에 좀 더 수정할 예정.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hit-that-drum/post/82dcf0c6-d352-4eea-8a42-1f9f9e25d2e9/image.png" alt=""></p>
<br>

<h3 id="global-colortheme">Global ColorTheme</h3>
<ul>
<li>전체 프로젝트 컬러테마를 관리하는 방법을 도입하였다.</li>
</ul>
<pre><code class="language-javascript">export const ColorTheme = {
  // Base colors
  white: &quot;#ffffff&quot;,
  black: &quot;#000000&quot;,

  // Primary colors
  primary: {
    main: &quot;#475B4A&quot;,
    light: &quot;#CCCCCC&quot;,
  },

  // Background colors
  background: {
    default: &quot;#ffffff&quot;,
    paper: &quot;#f5f5f5&quot;,
  },

  // Text colors
  text: {
    primary: &quot;#475B4A&quot;,
    secondary: &quot;#CCCCCC&quot;,
    light: &quot;#919C92&quot;,
  },

  // Border colors
  border: {
    light: &quot;#CCCCCC&quot;,
    main: &quot;#475B4A&quot;,
  },

  // Shadow colors
  shadow: {
    default: &quot;#1A1A1A&quot;,
    light: &quot;#f5f5f5&quot;,
  },
} as const;

export type ColorThemeType = typeof ColorTheme;</code></pre>
<br>

<h3 id="colorhighlight-extension">ColorHighlight extension</h3>
<ul>
<li>커서한테 물어봤더니 얘를 추천해줘서 깔아보았다.
이렇게하니까 색깔을 구분하기가 쉬워지긴했는데 아무래도 ColorTheme 파일 안에서 볼 수는 있는데 <code>${{ColorTheme.text.primary}}</code>이렇게 쓸 때는 색깔이 나오지는 않는다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hit-that-drum/post/03983245-fbc9-429e-8b6c-47c340d66a6d/image.png" alt="">
<img src="https://velog.velcdn.com/images/hit-that-drum/post/c4245c8d-d050-40bd-b07a-56ee64ed2821/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TO DO LIST][DAY 05] 2025.05.28  - 노션 데이터베이스 연결]]></title>
            <link>https://velog.io/@hit-that-drum/TO-DO-LIST-2025.05.28-DAY-05-%EB%85%B8%EC%85%98-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%97%B0%EA%B2%B0</link>
            <guid>https://velog.io/@hit-that-drum/TO-DO-LIST-2025.05.28-DAY-05-%EB%85%B8%EC%85%98-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%97%B0%EA%B2%B0</guid>
            <pubDate>Thu, 29 May 2025 01:19:52 GMT</pubDate>
            <description><![CDATA[<ul>
<li>서버사이드 렌더링이라서 api 쏘는 주소조차도 노출이 안 될 수가 있구나!!! 대박이다!!!</li>
<li><code>axios.post(”/api/notion”)</code> 이 부분이 url을 넣는 게 아니고 저 api 폴더 밑의 notion 이라는 곳의 파일 안을 통째로 읽어오는 것 같다!</li>
</ul>
<pre><code class="language-typescript">// api/notion/route.ts
import { NextResponse } from &quot;next/server&quot;;

const headers = {
  Authorization: `Bearer ${process.env.NOTION_TO_DO_LIST_API_KEY}`,
  &quot;Content-Type&quot;: &quot;application/json&quot;,
  &quot;Notion-Version&quot;: &quot;2022-02-22&quot;,
};

const notionDatabaseUrl = `https://api.notion.com/v1/databases/${process.env.TO_DO_LIST_DATABASE_KEY}/query`;

export async function POST() {
  try {
    const response = await fetch(notionDatabaseUrl, {
      method: &quot;POST&quot;,
      headers,
    });

    if (!response.ok) {
      throw new Error(`Notion API error: ${response.statusText}`);
    }

    const data = await response.json();
    return NextResponse.json(data);
  } catch (error) {
    console.error(&quot;Error fetching from Notion:&quot;, error);
    return NextResponse.json(
      { error: &quot;Failed to fetch data from Notion&quot; },
      { status: 500 }
    );
  }
}</code></pre>
<br>

<p>이렇게 해서 home에서 axios로 불러오니까 데이터가 온다!</p>
<pre><code class="language-typescript">  const [data, setData] = useState&lt;any&gt;(null);
  const [error, setError] = useState&lt;string | null&gt;(null);

  useEffect(() =&gt; {
    const fetchData = async () =&gt; {
      try {
        const response = await axios.post(&quot;/api/notion&quot;);
        setData(response.data);
      } catch (err) {
        setError(&quot;Failed to fetch data&quot;);
        console.error(&quot;Error fetching data:&quot;, err);
      }
    };
    fetchData();
  }, []);</code></pre>
<br>

<p>다음 시간에는 여기에 filter나 sort를 넣어서 보낼 수 있게 수정해봐야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TO DO LIST][DAY 04] 2025.05.25  - 노션 데이터베이스 연결]]></title>
            <link>https://velog.io/@hit-that-drum/TO-DO-LIST-2025.05.28-DAY-04-%EB%85%B8%EC%85%98-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%97%B0%EA%B2%B0</link>
            <guid>https://velog.io/@hit-that-drum/TO-DO-LIST-2025.05.28-DAY-04-%EB%85%B8%EC%85%98-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%97%B0%EA%B2%B0</guid>
            <pubDate>Wed, 28 May 2025 05:58:26 GMT</pubDate>
            <description><![CDATA[<ul>
<li>프로젝트 새로 셋팅<ul>
<li>1년 전에 비해서 너무 편해졌다... LLM들 때문에... 다 물어보면서 했음... ㅎㅎ...</li>
</ul>
</li>
</ul>
<ul>
<li>layout.tsx 수정<ul>
<li>프로젝트 타이틀이랑 favicon 바꿔주고 싶어서 해당 파일에 아이콘파일은 갈아치우고 타이틀이랑 디스크립션은 바꿔주었다.</li>
</ul>
</li>
</ul>
<pre><code class="language-javascript">export const metadata: Metadata = {
  title: &quot;Clone MY ROUTINE&quot;,
  description: &quot;Clone MY ROUTINE&quot;,
  icons: {
    icon: &quot;/icon.svg&quot;,
  },
};</code></pre>
<br>

<ul>
<li>Cursor AI 이용해서 그냥 &quot;make header&quot; &quot;make footer&quot; &quot;make home page&quot; &quot;make sign up page&quot; &quot;make login page&quot; &quot;make dashboard page&quot; &quot;make routines page&quot; &quot;make profile page&quot; 라고 차레차례 한 결과가 아래와 같았다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hit-that-drum/post/cfa7ea7f-9be9-48ad-ad0a-a99d8bcaf2bb/image.png" alt="">
<img src="https://velog.velcdn.com/images/hit-that-drum/post/3dbc84f5-d3b2-40b4-ac2a-b1ea60db2811/image.png" alt="">
<img src="https://velog.velcdn.com/images/hit-that-drum/post/23f2a4f1-1f04-47c0-bc0d-13a94ec5769b/image.png" alt="">
<img src="https://velog.velcdn.com/images/hit-that-drum/post/2120e81e-1862-4a91-9061-e9565e9cc703/image.png" alt="">
<img src="https://velog.velcdn.com/images/hit-that-drum/post/1d8599ad-3c33-48ae-8d99-9cd8c1d49ee9/image.png" alt="">
<img src="https://velog.velcdn.com/images/hit-that-drum/post/c4725826-f069-472d-a569-ffc0ec7ae611/image.png" alt=""></p>
<br>

<ul>
<li>다 하고 나니까... 굳이 내가 먼가를 더 만들 필요가...? ㅜㅜ</li>
<li>하지만 그래도 해야겠죠... 힘 내어 봅시다...</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TO DO LIST][DAY 03] 2025.05.22 - 노션 데이터베이스 연결]]></title>
            <link>https://velog.io/@hit-that-drum/TO-DO-LIST-2025.05.22-DAY-03-%EB%85%B8%EC%85%98-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%97%B0%EA%B2%B0</link>
            <guid>https://velog.io/@hit-that-drum/TO-DO-LIST-2025.05.22-DAY-03-%EB%85%B8%EC%85%98-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%97%B0%EA%B2%B0</guid>
            <pubDate>Wed, 28 May 2025 05:25:29 GMT</pubDate>
            <description><![CDATA[<ul>
<li><code>.env</code> 에서 불러오는데도 안 불러와져서 뭐지 했더니 Client Side Environment에서는 구별을 위해서 <code>REACT_APP</code> 으로 시작해야 되는 거라고 한다. 안 와서 한참을 찍어봄... ㅇ-&lt;-&lt;...</li>
</ul>
<br>

<ul>
<li><p>이러고 처리를 다 해서 Notion API를 불러와봤더니... CORS Error가 났음!!!
알고보니 노션 쪽에서 따로 처리를 해 주지 않기 때문에 이건 알아서 해결을 해야 한다고 하더라고!!! 이럴수가!!!</p>
</li>
<li><p>프록시 관련해서 무료 어쩌구로 되는 것들 있기는 있는데 이거 다 시도는 안 해봤지만 어쨌든 궁극적으로는… 이거 배포까지 하고 싶어서 하는 건데(그렇게까지 할지 안 할지야 모르지만) 배포를 할래도 어차피 저 CORS 에러는 날 거란 말이죠? 그럼 애초에 이게 안 나는 방식을 택해서 개발을 해야 되는 거 아닌가? 싶었다.</p>
</li>
<li><p>인터넷에 여러 예제를 찾아보니까 노션을 데이터베이스로 쓰고싶은 사람들은 애초에 프로젝트를 Next js로 셋팅하는 것 같았다...^^(서버사이드 렌더링이니까). ㅎ ㅏ... 나는 이 프로젝트를 시작했을 때에는 백엔드 관련한 건 생각을 안 하고 나중에 갖다 붙인 거니까 이거까지는 고려가 안 됐던 게 맞기는 해서 고민에 빠졌다.</p>
</li>
<li><p>고민하면서 같은 팀 친구들한테도 조언을 구하고 GPT와 Gemini와 Cursor 한테 node로 서버 띄워서 CORS Error 처리하는 방법을 물어봐서는 이거저거 적용도 해 보았다. 하지만... 나는 애초에 서버리스  상태였기 때문에...(의도한 게 아니고 그냥... 생각 없음으로...ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ) 이제와서 바꾸자니 조금 복잡하기도 하고 그래서... 프로젝트를 새로 파기로 결정.</p>
</li>
</ul>
<br>

<ul>
<li>CORS 에러 해결 예제 참고 블로그
<a href="https://xiubindev.tistory.com/115">나를 너무나 힘들게 했던 CORS 에러 해결하기</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TO DO LIST][DAY 02] 2025.05.21  - 노션 데이터베이스 연결]]></title>
            <link>https://velog.io/@hit-that-drum/TO-DO-LIST-2025.05.21-DAY-02-%EB%85%B8%EC%85%98-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%97%B0%EA%B2%B0</link>
            <guid>https://velog.io/@hit-that-drum/TO-DO-LIST-2025.05.21-DAY-02-%EB%85%B8%EC%85%98-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%97%B0%EA%B2%B0</guid>
            <pubDate>Wed, 28 May 2025 04:59:56 GMT</pubDate>
            <description><![CDATA[<ul>
<li>.gitignore 관련 참고 포스트
<a href="https://adjh54.tistory.com/16">.gitignore 이해 및 구성 방법</a></li>
</ul>
<p>어제에 이어 gitignore를 다시 한 번... 아니 사실 여러번 체크하였다... </p>
<br>

<ul>
<li>Notion API
메뉴 &gt; 설정 &gt; API 통합 에 가면 쉽게 생성할 수 있다.</li>
</ul>
<br>

<ul>
<li>Notion Database Key<pre><code class="language-javascript">// 페이지 전체 공유 말고 해당 데이터베이스의 보기링크 복사 누르면 해당 url이 뜬다.
// 여기서 ?(물음표) 앞에 부분이 해당 데이터베이스의 키
https://www.notion.so/{database key}?v=1f88b8db378c8097a2b3000c869ec2e0&amp;pvs=4</code></pre>
</li>
</ul>
<br>

<ul>
<li>POSTMAN 으로 통신 해보기<pre><code class="language-javascript">METHOD: POST
URL: https://api.notion.com/v1/databases/{database key}/query
AUTHORIZATION: Bearer Token 
HEADER: {
  Content-Type: application/json
  Notion-Version: 2022-02-22
}</code></pre>
</li>
</ul>
<br>

<ul>
<li>통신을 하면 데이터베이스에 있는 8가지의 정보가 이렇게 길<del>~</del>게 온다^.^...</li>
</ul>
<pre><code class="language-javascript">{
    &quot;object&quot;: &quot;list&quot;,
    &quot;results&quot;: [
        {
            &quot;object&quot;: &quot;page&quot;,
            &quot;id&quot;: &quot;-&quot;,
            &quot;created_time&quot;: &quot;2025-05-20T23:47:00.000Z&quot;,
            &quot;last_edited_time&quot;: &quot;2025-05-20T23:49:00.000Z&quot;,
            &quot;created_by&quot;: {
                &quot;object&quot;: &quot;user&quot;,
                &quot;id&quot;: &quot;-&quot;
            },
            &quot;last_edited_by&quot;: {
                &quot;object&quot;: &quot;user&quot;,
                &quot;id&quot;: &quot;-&quot;
            },
            &quot;cover&quot;: null,
            &quot;icon&quot;: null,
            &quot;parent&quot;: {
                &quot;type&quot;: &quot;database_id&quot;,
                &quot;database_id&quot;: &quot;-&quot;
            },
            &quot;archived&quot;: false,
            &quot;in_trash&quot;: false,
            &quot;properties&quot;: {
                &quot;longMemo&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: []
                },
                &quot;timeType&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;multi_select&quot;,
                    &quot;multi_select&quot;: [
                        {
                            &quot;id&quot;: &quot;-&quot;,
                            &quot;name&quot;: &quot;AFTERNOON&quot;,
                            &quot;color&quot;: &quot;pink&quot;
                        }
                    ]
                },
                &quot;shortMemo&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: []
                },
                &quot;emoji&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: [
                        {
                            &quot;type&quot;: &quot;text&quot;,
                            &quot;text&quot;: {
                                &quot;content&quot;: &quot;🌼&quot;,
                                &quot;link&quot;: null
                            },
                            &quot;annotations&quot;: {
                                &quot;bold&quot;: false,
                                &quot;italic&quot;: false,
                                &quot;strikethrough&quot;: false,
                                &quot;underline&quot;: false,
                                &quot;code&quot;: false,
                                &quot;color&quot;: &quot;default&quot;
                            },
                            &quot;plain_text&quot;: &quot;🌼&quot;,
                            &quot;href&quot;: null
                        }
                    ]
                },
                &quot;type&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;select&quot;,
                    &quot;select&quot;: {
                        &quot;id&quot;: &quot;-&quot;,
                        &quot;name&quot;: &quot;TODO&quot;,
                        &quot;color&quot;: &quot;green&quot;
                    }
                },
                &quot;timeMemo&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: [
                        {
                            &quot;type&quot;: &quot;text&quot;,
                            &quot;text&quot;: {
                                &quot;content&quot;: &quot;16:00&quot;,
                                &quot;link&quot;: null
                            },
                            &quot;annotations&quot;: {
                                &quot;bold&quot;: false,
                                &quot;italic&quot;: false,
                                &quot;strikethrough&quot;: false,
                                &quot;underline&quot;: false,
                                &quot;code&quot;: false,
                                &quot;color&quot;: &quot;default&quot;
                            },
                            &quot;plain_text&quot;: &quot;16:00&quot;,
                            &quot;href&quot;: null
                        }
                    ]
                },
                &quot;ID&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;unique_id&quot;,
                    &quot;unique_id&quot;: {
                        &quot;prefix&quot;: null,
                        &quot;number&quot;: 8
                    }
                },
                &quot;status&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;status&quot;,
                    &quot;status&quot;: {
                        &quot;id&quot;: &quot;-&quot;,
                        &quot;name&quot;: &quot;YET&quot;,
                        &quot;color&quot;: &quot;red&quot;
                    }
                },
                &quot;datetime&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;date&quot;,
                    &quot;date&quot;: {
                        &quot;start&quot;: &quot;2025-05-25&quot;,
                        &quot;end&quot;: null,
                        &quot;time_zone&quot;: null
                    }
                },
                &quot;title&quot;: {
                    &quot;id&quot;: &quot;title&quot;,
                    &quot;type&quot;: &quot;title&quot;,
                    &quot;title&quot;: [
                        {
                            &quot;type&quot;: &quot;text&quot;,
                            &quot;text&quot;: {
                                &quot;content&quot;: &quot;The crownless again shall be king&quot;,
                                &quot;link&quot;: null
                            },
                            &quot;annotations&quot;: {
                                &quot;bold&quot;: false,
                                &quot;italic&quot;: false,
                                &quot;strikethrough&quot;: false,
                                &quot;underline&quot;: false,
                                &quot;code&quot;: false,
                                &quot;color&quot;: &quot;default&quot;
                            },
                            &quot;plain_text&quot;: &quot;The crownless again shall be king&quot;,
                            &quot;href&quot;: null
                        }
                    ]
                }
            },
            &quot;url&quot;: &quot;-&quot;,
            &quot;public_url&quot;: null
        },
        {
            &quot;object&quot;: &quot;page&quot;,
            &quot;id&quot;: &quot;1f98b8db-378c-803a-90ca-fa09099cbd2a&quot;,
            &quot;created_time&quot;: &quot;2025-05-20T23:47:00.000Z&quot;,
            &quot;last_edited_time&quot;: &quot;2025-05-20T23:49:00.000Z&quot;,
            &quot;created_by&quot;: {
                &quot;object&quot;: &quot;user&quot;,
                &quot;id&quot;: &quot;-&quot;
            },
            &quot;last_edited_by&quot;: {
                &quot;object&quot;: &quot;user&quot;,
                &quot;id&quot;: &quot;-&quot;
            },
            &quot;cover&quot;: null,
            &quot;icon&quot;: null,
            &quot;parent&quot;: {
                &quot;type&quot;: &quot;database_id&quot;,
                &quot;database_id&quot;: &quot;-&quot;
            },
            &quot;archived&quot;: false,
            &quot;in_trash&quot;: false,
            &quot;properties&quot;: {
                &quot;longMemo&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: []
                },
                &quot;timeType&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;multi_select&quot;,
                    &quot;multi_select&quot;: [
                        {
                            &quot;id&quot;: &quot;-&quot;,
                            &quot;name&quot;: &quot;AFTERNOON&quot;,
                            &quot;color&quot;: &quot;pink&quot;
                        }
                    ]
                },
                &quot;shortMemo&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: []
                },
                &quot;emoji&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: [
                        {
                            &quot;type&quot;: &quot;text&quot;,
                            &quot;text&quot;: {
                                &quot;content&quot;: &quot;🥰&quot;,
                                &quot;link&quot;: null
                            },
                            &quot;annotations&quot;: {
                                &quot;bold&quot;: false,
                                &quot;italic&quot;: false,
                                &quot;strikethrough&quot;: false,
                                &quot;underline&quot;: false,
                                &quot;code&quot;: false,
                                &quot;color&quot;: &quot;default&quot;
                            },
                            &quot;plain_text&quot;: &quot;🥰&quot;,
                            &quot;href&quot;: null
                        }
                    ]
                },
                &quot;type&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;select&quot;,
                    &quot;select&quot;: {
                        &quot;id&quot;: &quot;-&quot;,
                        &quot;name&quot;: &quot;ROUTINE&quot;,
                        &quot;color&quot;: &quot;purple&quot;
                    }
                },
                &quot;timeMemo&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: [
                        {
                            &quot;type&quot;: &quot;text&quot;,
                            &quot;text&quot;: {
                                &quot;content&quot;: &quot;15:00&quot;,
                                &quot;link&quot;: null
                            },
                            &quot;annotations&quot;: {
                                &quot;bold&quot;: false,
                                &quot;italic&quot;: false,
                                &quot;strikethrough&quot;: false,
                                &quot;underline&quot;: false,
                                &quot;code&quot;: false,
                                &quot;color&quot;: &quot;default&quot;
                            },
                            &quot;plain_text&quot;: &quot;15:00&quot;,
                            &quot;href&quot;: null
                        }
                    ]
                },
                &quot;ID&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;unique_id&quot;,
                    &quot;unique_id&quot;: {
                        &quot;prefix&quot;: null,
                        &quot;number&quot;: 7
                    }
                },
                &quot;status&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;status&quot;,
                    &quot;status&quot;: {
                        &quot;id&quot;: &quot;-&quot;,
                        &quot;name&quot;: &quot;YET&quot;,
                        &quot;color&quot;: &quot;red&quot;
                    }
                },
                &quot;datetime&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;date&quot;,
                    &quot;date&quot;: {
                        &quot;start&quot;: &quot;2025-05-24&quot;,
                        &quot;end&quot;: null,
                        &quot;time_zone&quot;: null
                    }
                },
                &quot;title&quot;: {
                    &quot;id&quot;: &quot;title&quot;,
                    &quot;type&quot;: &quot;title&quot;,
                    &quot;title&quot;: [
                        {
                            &quot;type&quot;: &quot;text&quot;,
                            &quot;text&quot;: {
                                &quot;content&quot;: &quot;Renewed shall be blade that was broken&quot;,
                                &quot;link&quot;: null
                            },
                            &quot;annotations&quot;: {
                                &quot;bold&quot;: false,
                                &quot;italic&quot;: false,
                                &quot;strikethrough&quot;: false,
                                &quot;underline&quot;: false,
                                &quot;code&quot;: false,
                                &quot;color&quot;: &quot;default&quot;
                            },
                            &quot;plain_text&quot;: &quot;Renewed shall be blade that was broken&quot;,
                            &quot;href&quot;: null
                        }
                    ]
                }
            },
            &quot;url&quot;: &quot;-&quot;,
            &quot;public_url&quot;: null
        },
        {
            &quot;object&quot;: &quot;page&quot;,
            &quot;id&quot;: &quot;-&quot;,
            &quot;created_time&quot;: &quot;2025-05-20T23:47:00.000Z&quot;,
            &quot;last_edited_time&quot;: &quot;2025-05-20T23:48:00.000Z&quot;,
            &quot;created_by&quot;: {
                &quot;object&quot;: &quot;user&quot;,
                &quot;id&quot;: &quot;-&quot;
            },
            &quot;last_edited_by&quot;: {
                &quot;object&quot;: &quot;user&quot;,
                &quot;id&quot;: &quot;-&quot;
            },
            &quot;cover&quot;: null,
            &quot;icon&quot;: null,
            &quot;parent&quot;: {
                &quot;type&quot;: &quot;database_id&quot;,
                &quot;database_id&quot;: &quot;-&quot;
            },
            &quot;archived&quot;: false,
            &quot;in_trash&quot;: false,
            &quot;properties&quot;: {
                &quot;longMemo&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: []
                },
                &quot;timeType&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;multi_select&quot;,
                    &quot;multi_select&quot;: [
                        {
                            &quot;id&quot;: &quot;-&quot;,
                            &quot;name&quot;: &quot;DAY&quot;,
                            &quot;color&quot;: &quot;green&quot;
                        }
                    ]
                },
                &quot;shortMemo&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: []
                },
                &quot;emoji&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: [
                        {
                            &quot;type&quot;: &quot;text&quot;,
                            &quot;text&quot;: {
                                &quot;content&quot;: &quot;🍒&quot;,
                                &quot;link&quot;: null
                            },
                            &quot;annotations&quot;: {
                                &quot;bold&quot;: false,
                                &quot;italic&quot;: false,
                                &quot;strikethrough&quot;: false,
                                &quot;underline&quot;: false,
                                &quot;code&quot;: false,
                                &quot;color&quot;: &quot;default&quot;
                            },
                            &quot;plain_text&quot;: &quot;🍒&quot;,
                            &quot;href&quot;: null
                        }
                    ]
                },
                &quot;type&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;select&quot;,
                    &quot;select&quot;: {
                        &quot;id&quot;: &quot;-&quot;,
                        &quot;name&quot;: &quot;TODO&quot;,
                        &quot;color&quot;: &quot;green&quot;
                    }
                },
                &quot;timeMemo&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: [
                        {
                            &quot;type&quot;: &quot;text&quot;,
                            &quot;text&quot;: {
                                &quot;content&quot;: &quot;14:00&quot;,
                                &quot;link&quot;: null
                            },
                            &quot;annotations&quot;: {
                                &quot;bold&quot;: false,
                                &quot;italic&quot;: false,
                                &quot;strikethrough&quot;: false,
                                &quot;underline&quot;: false,
                                &quot;code&quot;: false,
                                &quot;color&quot;: &quot;default&quot;
                            },
                            &quot;plain_text&quot;: &quot;14:00&quot;,
                            &quot;href&quot;: null
                        }
                    ]
                },
                &quot;ID&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;unique_id&quot;,
                    &quot;unique_id&quot;: {
                        &quot;prefix&quot;: null,
                        &quot;number&quot;: 6
                    }
                },
                &quot;status&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;status&quot;,
                    &quot;status&quot;: {
                        &quot;id&quot;: &quot;-&quot;,
                        &quot;name&quot;: &quot;YET&quot;,
                        &quot;color&quot;: &quot;red&quot;
                    }
                },
                &quot;datetime&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;date&quot;,
                    &quot;date&quot;: {
                        &quot;start&quot;: &quot;2025-05-23&quot;,
                        &quot;end&quot;: null,
                        &quot;time_zone&quot;: null
                    }
                },
                &quot;title&quot;: {
                    &quot;id&quot;: &quot;title&quot;,
                    &quot;type&quot;: &quot;title&quot;,
                    &quot;title&quot;: [
                        {
                            &quot;type&quot;: &quot;text&quot;,
                            &quot;text&quot;: {
                                &quot;content&quot;: &quot;A light from the shadows shall spring&quot;,
                                &quot;link&quot;: null
                            },
                            &quot;annotations&quot;: {
                                &quot;bold&quot;: false,
                                &quot;italic&quot;: false,
                                &quot;strikethrough&quot;: false,
                                &quot;underline&quot;: false,
                                &quot;code&quot;: false,
                                &quot;color&quot;: &quot;default&quot;
                            },
                            &quot;plain_text&quot;: &quot;A light from the shadows shall spring&quot;,
                            &quot;href&quot;: null
                        }
                    ]
                }
            },
            &quot;url&quot;: &quot;-&quot;,
            &quot;public_url&quot;: null
        },
        {
            &quot;object&quot;: &quot;page&quot;,
            &quot;id&quot;: &quot;&quot;,
            &quot;created_time&quot;: &quot;2025-05-19T23:24:00.000Z&quot;,
            &quot;last_edited_time&quot;: &quot;2025-05-20T23:47:00.000Z&quot;,
            &quot;created_by&quot;: {
                &quot;object&quot;: &quot;user&quot;,
                &quot;id&quot;: &quot;-&quot;
            },
            &quot;last_edited_by&quot;: {
                &quot;object&quot;: &quot;user&quot;,
                &quot;id&quot;: &quot;-&quot;
            },
            &quot;cover&quot;: null,
            &quot;icon&quot;: null,
            &quot;parent&quot;: {
                &quot;type&quot;: &quot;database_id&quot;,
                &quot;database_id&quot;: &quot;-&quot;
            },
            &quot;archived&quot;: false,
            &quot;in_trash&quot;: false,
            &quot;properties&quot;: {
                &quot;longMemo&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: []
                },
                &quot;timeType&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;multi_select&quot;,
                    &quot;multi_select&quot;: [
                        {
                            &quot;id&quot;: &quot;-&quot;,
                            &quot;name&quot;: &quot;NIGHT&quot;,
                            &quot;color&quot;: &quot;yellow&quot;
                        }
                    ]
                },
                &quot;shortMemo&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: []
                },
                &quot;emoji&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: [
                        {
                            &quot;type&quot;: &quot;text&quot;,
                            &quot;text&quot;: {
                                &quot;content&quot;: &quot;🍻&quot;,
                                &quot;link&quot;: null
                            },
                            &quot;annotations&quot;: {
                                &quot;bold&quot;: false,
                                &quot;italic&quot;: false,
                                &quot;strikethrough&quot;: false,
                                &quot;underline&quot;: false,
                                &quot;code&quot;: false,
                                &quot;color&quot;: &quot;default&quot;
                            },
                            &quot;plain_text&quot;: &quot;🍻&quot;,
                            &quot;href&quot;: null
                        }
                    ]
                },
                &quot;type&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;select&quot;,
                    &quot;select&quot;: {
                        &quot;id&quot;: &quot;-&quot;,
                        &quot;name&quot;: &quot;ROUTINE_COUNT&quot;,
                        &quot;color&quot;: &quot;red&quot;
                    }
                },
                &quot;timeMemo&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: [
                        {
                            &quot;type&quot;: &quot;text&quot;,
                            &quot;text&quot;: {
                                &quot;content&quot;: &quot;13:00&quot;,
                                &quot;link&quot;: null
                            },
                            &quot;annotations&quot;: {
                                &quot;bold&quot;: false,
                                &quot;italic&quot;: false,
                                &quot;strikethrough&quot;: false,
                                &quot;underline&quot;: false,
                                &quot;code&quot;: false,
                                &quot;color&quot;: &quot;default&quot;
                            },
                            &quot;plain_text&quot;: &quot;13:00&quot;,
                            &quot;href&quot;: null
                        }
                    ]
                },
                &quot;ID&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;unique_id&quot;,
                    &quot;unique_id&quot;: {
                        &quot;prefix&quot;: null,
                        &quot;number&quot;: 5
                    }
                },
                &quot;status&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;status&quot;,
                    &quot;status&quot;: {
                        &quot;id&quot;: &quot;-&quot;,
                        &quot;name&quot;: &quot;YET&quot;,
                        &quot;color&quot;: &quot;red&quot;
                    }
                },
                &quot;datetime&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;date&quot;,
                    &quot;date&quot;: {
                        &quot;start&quot;: &quot;2025-05-22&quot;,
                        &quot;end&quot;: null,
                        &quot;time_zone&quot;: null
                    }
                },
                &quot;title&quot;: {
                    &quot;id&quot;: &quot;title&quot;,
                    &quot;type&quot;: &quot;title&quot;,
                    &quot;title&quot;: [
                        {
                            &quot;type&quot;: &quot;text&quot;,
                            &quot;text&quot;: {
                                &quot;content&quot;: &quot;From the ashes a fire shall be woken&quot;,
                                &quot;link&quot;: null
                            },
                            &quot;annotations&quot;: {
                                &quot;bold&quot;: false,
                                &quot;italic&quot;: false,
                                &quot;strikethrough&quot;: false,
                                &quot;underline&quot;: false,
                                &quot;code&quot;: false,
                                &quot;color&quot;: &quot;default&quot;
                            },
                            &quot;plain_text&quot;: &quot;From the ashes a fire shall be woken&quot;,
                            &quot;href&quot;: null
                        }
                    ]
                }
            },
            &quot;url&quot;: &quot;-&quot;,
            &quot;public_url&quot;: null
        },
        {
            &quot;object&quot;: &quot;page&quot;,
            &quot;id&quot;: &quot;&quot;,
            &quot;created_time&quot;: &quot;2025-05-19T23:23:00.000Z&quot;,
            &quot;last_edited_time&quot;: &quot;2025-05-20T23:47:00.000Z&quot;,
            &quot;created_by&quot;: {
                &quot;object&quot;: &quot;user&quot;,
                &quot;id&quot;: &quot;-&quot;
            },
            &quot;last_edited_by&quot;: {
                &quot;object&quot;: &quot;user&quot;,
                &quot;id&quot;: &quot;-&quot;
            },
            &quot;cover&quot;: null,
            &quot;icon&quot;: null,
            &quot;parent&quot;: {
                &quot;type&quot;: &quot;database_id&quot;,
                &quot;database_id&quot;: &quot;-&quot;
            },
            &quot;archived&quot;: false,
            &quot;in_trash&quot;: false,
            &quot;properties&quot;: {
                &quot;longMemo&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: []
                },
                &quot;timeType&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;multi_select&quot;,
                    &quot;multi_select&quot;: [
                        {
                            &quot;id&quot;: &quot;-&quot;,
                            &quot;name&quot;: &quot;DAY&quot;,
                            &quot;color&quot;: &quot;green&quot;
                        }
                    ]
                },
                &quot;shortMemo&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: []
                },
                &quot;emoji&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: [
                        {
                            &quot;type&quot;: &quot;text&quot;,
                            &quot;text&quot;: {
                                &quot;content&quot;: &quot;🧶&quot;,
                                &quot;link&quot;: null
                            },
                            &quot;annotations&quot;: {
                                &quot;bold&quot;: false,
                                &quot;italic&quot;: false,
                                &quot;strikethrough&quot;: false,
                                &quot;underline&quot;: false,
                                &quot;code&quot;: false,
                                &quot;color&quot;: &quot;default&quot;
                            },
                            &quot;plain_text&quot;: &quot;🧶&quot;,
                            &quot;href&quot;: null
                        }
                    ]
                },
                &quot;type&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;select&quot;,
                    &quot;select&quot;: {
                        &quot;id&quot;: &quot;-&quot;,
                        &quot;name&quot;: &quot;ROUTINE_MEMO&quot;,
                        &quot;color&quot;: &quot;pink&quot;
                    }
                },
                &quot;timeMemo&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: [
                        {
                            &quot;type&quot;: &quot;text&quot;,
                            &quot;text&quot;: {
                                &quot;content&quot;: &quot;12:00&quot;,
                                &quot;link&quot;: null
                            },
                            &quot;annotations&quot;: {
                                &quot;bold&quot;: false,
                                &quot;italic&quot;: false,
                                &quot;strikethrough&quot;: false,
                                &quot;underline&quot;: false,
                                &quot;code&quot;: false,
                                &quot;color&quot;: &quot;default&quot;
                            },
                            &quot;plain_text&quot;: &quot;12:00&quot;,
                            &quot;href&quot;: null
                        }
                    ]
                },
                &quot;ID&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;unique_id&quot;,
                    &quot;unique_id&quot;: {
                        &quot;prefix&quot;: null,
                        &quot;number&quot;: 4
                    }
                },
                &quot;status&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;status&quot;,
                    &quot;status&quot;: {
                        &quot;id&quot;: &quot;-&quot;,
                        &quot;name&quot;: &quot;YET&quot;,
                        &quot;color&quot;: &quot;red&quot;
                    }
                },
                &quot;datetime&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;date&quot;,
                    &quot;date&quot;: {
                        &quot;start&quot;: &quot;2025-05-21&quot;,
                        &quot;end&quot;: null,
                        &quot;time_zone&quot;: null
                    }
                },
                &quot;title&quot;: {
                    &quot;id&quot;: &quot;title&quot;,
                    &quot;type&quot;: &quot;title&quot;,
                    &quot;title&quot;: [
                        {
                            &quot;type&quot;: &quot;text&quot;,
                            &quot;text&quot;: {
                                &quot;content&quot;: &quot;Deep roots are not reached by the frost&quot;,
                                &quot;link&quot;: null
                            },
                            &quot;annotations&quot;: {
                                &quot;bold&quot;: false,
                                &quot;italic&quot;: false,
                                &quot;strikethrough&quot;: false,
                                &quot;underline&quot;: false,
                                &quot;code&quot;: false,
                                &quot;color&quot;: &quot;default&quot;
                            },
                            &quot;plain_text&quot;: &quot;Deep roots are not reached by the frost&quot;,
                            &quot;href&quot;: null
                        }
                    ]
                }
            },
            &quot;url&quot;: &quot;-&quot;,
            &quot;public_url&quot;: null
        },
        {
            &quot;object&quot;: &quot;page&quot;,
            &quot;id&quot;: &quot;-&quot;,
            &quot;created_time&quot;: &quot;2025-05-19T07:37:00.000Z&quot;,
            &quot;last_edited_time&quot;: &quot;2025-05-20T23:46:00.000Z&quot;,
            &quot;created_by&quot;: {
                &quot;object&quot;: &quot;user&quot;,
                &quot;id&quot;: &quot;-&quot;
            },
            &quot;last_edited_by&quot;: {
                &quot;object&quot;: &quot;user&quot;,
                &quot;id&quot;: &quot;-&quot;
            },
            &quot;cover&quot;: null,
            &quot;icon&quot;: null,
            &quot;parent&quot;: {
                &quot;type&quot;: &quot;database_id&quot;,
                &quot;database_id&quot;: &quot;-&quot;
            },
            &quot;archived&quot;: false,
            &quot;in_trash&quot;: false,
            &quot;properties&quot;: {
                &quot;longMemo&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: []
                },
                &quot;timeType&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;multi_select&quot;,
                    &quot;multi_select&quot;: [
                        {
                            &quot;id&quot;: &quot;-&quot;,
                            &quot;name&quot;: &quot;NIGHT&quot;,
                            &quot;color&quot;: &quot;yellow&quot;
                        },
                        {
                            &quot;id&quot;: &quot;-&quot;,
                            &quot;name&quot;: &quot;DAY&quot;,
                            &quot;color&quot;: &quot;green&quot;
                        },
                        {
                            &quot;id&quot;: &quot;-&quot;,
                            &quot;name&quot;: &quot;AFTERNOON&quot;,
                            &quot;color&quot;: &quot;pink&quot;
                        }
                    ]
                },
                &quot;shortMemo&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: []
                },
                &quot;emoji&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: [
                        {
                            &quot;type&quot;: &quot;text&quot;,
                            &quot;text&quot;: {
                                &quot;content&quot;: &quot;🤣&quot;,
                                &quot;link&quot;: null
                            },
                            &quot;annotations&quot;: {
                                &quot;bold&quot;: false,
                                &quot;italic&quot;: false,
                                &quot;strikethrough&quot;: false,
                                &quot;underline&quot;: false,
                                &quot;code&quot;: false,
                                &quot;color&quot;: &quot;default&quot;
                            },
                            &quot;plain_text&quot;: &quot;🤣&quot;,
                            &quot;href&quot;: null
                        }
                    ]
                },
                &quot;type&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;select&quot;,
                    &quot;select&quot;: {
                        &quot;id&quot;: &quot;-&quot;,
                        &quot;name&quot;: &quot;ROUTINE_TIME&quot;,
                        &quot;color&quot;: &quot;brown&quot;
                    }
                },
                &quot;timeMemo&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: [
                        {
                            &quot;type&quot;: &quot;text&quot;,
                            &quot;text&quot;: {
                                &quot;content&quot;: &quot;11:00&quot;,
                                &quot;link&quot;: null
                            },
                            &quot;annotations&quot;: {
                                &quot;bold&quot;: false,
                                &quot;italic&quot;: false,
                                &quot;strikethrough&quot;: false,
                                &quot;underline&quot;: false,
                                &quot;code&quot;: false,
                                &quot;color&quot;: &quot;default&quot;
                            },
                            &quot;plain_text&quot;: &quot;11:00&quot;,
                            &quot;href&quot;: null
                        }
                    ]
                },
                &quot;ID&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;unique_id&quot;,
                    &quot;unique_id&quot;: {
                        &quot;prefix&quot;: null,
                        &quot;number&quot;: 3
                    }
                },
                &quot;status&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;status&quot;,
                    &quot;status&quot;: {
                        &quot;id&quot;: &quot;btoz&quot;,
                        &quot;name&quot;: &quot;REST&quot;,
                        &quot;color&quot;: &quot;yellow&quot;
                    }
                },
                &quot;datetime&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;date&quot;,
                    &quot;date&quot;: {
                        &quot;start&quot;: &quot;2025-05-20&quot;,
                        &quot;end&quot;: null,
                        &quot;time_zone&quot;: null
                    }
                },
                &quot;title&quot;: {
                    &quot;id&quot;: &quot;title&quot;,
                    &quot;type&quot;: &quot;title&quot;,
                    &quot;title&quot;: [
                        {
                            &quot;type&quot;: &quot;text&quot;,
                            &quot;text&quot;: {
                                &quot;content&quot;: &quot;The old that is strong does not wither&quot;,
                                &quot;link&quot;: null
                            },
                            &quot;annotations&quot;: {
                                &quot;bold&quot;: false,
                                &quot;italic&quot;: false,
                                &quot;strikethrough&quot;: false,
                                &quot;underline&quot;: false,
                                &quot;code&quot;: false,
                                &quot;color&quot;: &quot;default&quot;
                            },
                            &quot;plain_text&quot;: &quot;The old that is strong does not wither&quot;,
                            &quot;href&quot;: null
                        }
                    ]
                }
            },
            &quot;url&quot;: &quot;-&quot;,
            &quot;public_url&quot;: null
        },
        {
            &quot;object&quot;: &quot;page&quot;,
            &quot;id&quot;: &quot;-&quot;,
            &quot;created_time&quot;: &quot;2025-05-19T07:31:00.000Z&quot;,
            &quot;last_edited_time&quot;: &quot;2025-05-20T23:46:00.000Z&quot;,
            &quot;created_by&quot;: {
                &quot;object&quot;: &quot;user&quot;,
                &quot;id&quot;: &quot;-&quot;
            },
            &quot;last_edited_by&quot;: {
                &quot;object&quot;: &quot;user&quot;,
                &quot;id&quot;: &quot;-&quot;
            },
            &quot;cover&quot;: null,
            &quot;icon&quot;: null,
            &quot;parent&quot;: {
                &quot;type&quot;: &quot;database_id&quot;,
                &quot;database_id&quot;: &quot;-&quot;
            },
            &quot;archived&quot;: false,
            &quot;in_trash&quot;: false,
            &quot;properties&quot;: {
                &quot;longMemo&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: []
                },
                &quot;timeType&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;multi_select&quot;,
                    &quot;multi_select&quot;: [
                        {
                            &quot;id&quot;: &quot;-&quot;,
                            &quot;name&quot;: &quot;AFTERNOON&quot;,
                            &quot;color&quot;: &quot;pink&quot;
                        },
                        {
                            &quot;id&quot;: &quot;-&quot;,
                            &quot;name&quot;: &quot;DAY&quot;,
                            &quot;color&quot;: &quot;green&quot;
                        }
                    ]
                },
                &quot;shortMemo&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: []
                },
                &quot;emoji&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: [
                        {
                            &quot;type&quot;: &quot;text&quot;,
                            &quot;text&quot;: {
                                &quot;content&quot;: &quot;😵‍💫&quot;,
                                &quot;link&quot;: null
                            },
                            &quot;annotations&quot;: {
                                &quot;bold&quot;: false,
                                &quot;italic&quot;: false,
                                &quot;strikethrough&quot;: false,
                                &quot;underline&quot;: false,
                                &quot;code&quot;: false,
                                &quot;color&quot;: &quot;default&quot;
                            },
                            &quot;plain_text&quot;: &quot;😵‍💫&quot;,
                            &quot;href&quot;: null
                        }
                    ]
                },
                &quot;type&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;select&quot;,
                    &quot;select&quot;: {
                        &quot;id&quot;: &quot;-&quot;,
                        &quot;name&quot;: &quot;TODO&quot;,
                        &quot;color&quot;: &quot;green&quot;
                    }
                },
                &quot;timeMemo&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: [
                        {
                            &quot;type&quot;: &quot;text&quot;,
                            &quot;text&quot;: {
                                &quot;content&quot;: &quot;10:00&quot;,
                                &quot;link&quot;: null
                            },
                            &quot;annotations&quot;: {
                                &quot;bold&quot;: false,
                                &quot;italic&quot;: false,
                                &quot;strikethrough&quot;: false,
                                &quot;underline&quot;: false,
                                &quot;code&quot;: false,
                                &quot;color&quot;: &quot;default&quot;
                            },
                            &quot;plain_text&quot;: &quot;10:00&quot;,
                            &quot;href&quot;: null
                        }
                    ]
                },
                &quot;ID&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;unique_id&quot;,
                    &quot;unique_id&quot;: {
                        &quot;prefix&quot;: null,
                        &quot;number&quot;: 2
                    }
                },
                &quot;status&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;status&quot;,
                    &quot;status&quot;: {
                        &quot;id&quot;: &quot;-&quot;,
                        &quot;name&quot;: &quot;DONE&quot;,
                        &quot;color&quot;: &quot;green&quot;
                    }
                },
                &quot;datetime&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;date&quot;,
                    &quot;date&quot;: {
                        &quot;start&quot;: &quot;2025-05-20&quot;,
                        &quot;end&quot;: null,
                        &quot;time_zone&quot;: null
                    }
                },
                &quot;title&quot;: {
                    &quot;id&quot;: &quot;title&quot;,
                    &quot;type&quot;: &quot;title&quot;,
                    &quot;title&quot;: [
                        {
                            &quot;type&quot;: &quot;text&quot;,
                            &quot;text&quot;: {
                                &quot;content&quot;: &quot;Not all thoes who wander are lost&quot;,
                                &quot;link&quot;: null
                            },
                            &quot;annotations&quot;: {
                                &quot;bold&quot;: false,
                                &quot;italic&quot;: false,
                                &quot;strikethrough&quot;: false,
                                &quot;underline&quot;: false,
                                &quot;code&quot;: false,
                                &quot;color&quot;: &quot;default&quot;
                            },
                            &quot;plain_text&quot;: &quot;Not all thoes who wander are lost&quot;,
                            &quot;href&quot;: null
                        }
                    ]
                }
            },
            &quot;url&quot;: &quot;-&quot;,
            &quot;public_url&quot;: null
        },
        {
            &quot;object&quot;: &quot;page&quot;,
            &quot;id&quot;: &quot;-&quot;,
            &quot;created_time&quot;: &quot;2025-05-19T07:29:00.000Z&quot;,
            &quot;last_edited_time&quot;: &quot;2025-05-20T23:46:00.000Z&quot;,
            &quot;created_by&quot;: {
                &quot;object&quot;: &quot;user&quot;,
                &quot;id&quot;: &quot;-&quot;
            },
            &quot;last_edited_by&quot;: {
                &quot;object&quot;: &quot;user&quot;,
                &quot;id&quot;: &quot;-&quot;
            },
            &quot;cover&quot;: null,
            &quot;icon&quot;: null,
            &quot;parent&quot;: {
                &quot;type&quot;: &quot;database_id&quot;,
                &quot;database_id&quot;: &quot;-&quot;
            },
            &quot;archived&quot;: false,
            &quot;in_trash&quot;: false,
            &quot;properties&quot;: {
                &quot;longMemo&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: []
                },
                &quot;timeType&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;multi_select&quot;,
                    &quot;multi_select&quot;: [
                        {
                            &quot;id&quot;: &quot;-&quot;,
                            &quot;name&quot;: &quot;DAY&quot;,
                            &quot;color&quot;: &quot;green&quot;
                        }
                    ]
                },
                &quot;shortMemo&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: []
                },
                &quot;emoji&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: [
                        {
                            &quot;type&quot;: &quot;text&quot;,
                            &quot;text&quot;: {
                                &quot;content&quot;: &quot;✅&quot;,
                                &quot;link&quot;: null
                            },
                            &quot;annotations&quot;: {
                                &quot;bold&quot;: false,
                                &quot;italic&quot;: false,
                                &quot;strikethrough&quot;: false,
                                &quot;underline&quot;: false,
                                &quot;code&quot;: false,
                                &quot;color&quot;: &quot;default&quot;
                            },
                            &quot;plain_text&quot;: &quot;✅&quot;,
                            &quot;href&quot;: null
                        }
                    ]
                },
                &quot;type&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;select&quot;,
                    &quot;select&quot;: {
                        &quot;id&quot;: &quot;-&quot;,
                        &quot;name&quot;: &quot;ROUTINE&quot;,
                        &quot;color&quot;: &quot;purple&quot;
                    }
                },
                &quot;timeMemo&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;rich_text&quot;,
                    &quot;rich_text&quot;: [
                        {
                            &quot;type&quot;: &quot;text&quot;,
                            &quot;text&quot;: {
                                &quot;content&quot;: &quot;09:00&quot;,
                                &quot;link&quot;: null
                            },
                            &quot;annotations&quot;: {
                                &quot;bold&quot;: false,
                                &quot;italic&quot;: false,
                                &quot;strikethrough&quot;: false,
                                &quot;underline&quot;: false,
                                &quot;code&quot;: false,
                                &quot;color&quot;: &quot;default&quot;
                            },
                            &quot;plain_text&quot;: &quot;09:00&quot;,
                            &quot;href&quot;: null
                        }
                    ]
                },
                &quot;ID&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;unique_id&quot;,
                    &quot;unique_id&quot;: {
                        &quot;prefix&quot;: null,
                        &quot;number&quot;: 1
                    }
                },
                &quot;status&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;status&quot;,
                    &quot;status&quot;: {
                        &quot;id&quot;: &quot;-&quot;,
                        &quot;name&quot;: &quot;YET&quot;,
                        &quot;color&quot;: &quot;red&quot;
                    }
                },
                &quot;datetime&quot;: {
                    &quot;id&quot;: &quot;-&quot;,
                    &quot;type&quot;: &quot;date&quot;,
                    &quot;date&quot;: {
                        &quot;start&quot;: &quot;2025-05-19&quot;,
                        &quot;end&quot;: null,
                        &quot;time_zone&quot;: null
                    }
                },
                &quot;title&quot;: {
                    &quot;id&quot;: &quot;title&quot;,
                    &quot;type&quot;: &quot;title&quot;,
                    &quot;title&quot;: [
                        {
                            &quot;type&quot;: &quot;text&quot;,
                            &quot;text&quot;: {
                                &quot;content&quot;: &quot;All that is gold does not glitter&quot;,
                                &quot;link&quot;: null
                            },
                            &quot;annotations&quot;: {
                                &quot;bold&quot;: false,
                                &quot;italic&quot;: false,
                                &quot;strikethrough&quot;: false,
                                &quot;underline&quot;: false,
                                &quot;code&quot;: false,
                                &quot;color&quot;: &quot;default&quot;
                            },
                            &quot;plain_text&quot;: &quot;All that is gold does not glitter&quot;,
                            &quot;href&quot;: null
                        }
                    ]
                }
            },
            &quot;url&quot;: &quot;-&quot;,
            &quot;public_url&quot;: null
        }
    ],
    &quot;next_cursor&quot;: null,
    &quot;has_more&quot;: false,
    &quot;type&quot;: &quot;page_or_database&quot;,
    &quot;page_or_database&quot;: {},
    &quot;developer_survey&quot;: &quot;-&quot;,
    &quot;request_id&quot;: &quot;-&quot;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TO DO LIST][DAY 01] 2025.05.20  - 노션 데이터베이스 연결]]></title>
            <link>https://velog.io/@hit-that-drum/TO-DO-LIST-2025.05.20-DAY-01-%EB%85%B8%EC%85%98-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%97%B0%EA%B2%B0</link>
            <guid>https://velog.io/@hit-that-drum/TO-DO-LIST-2025.05.20-DAY-01-%EB%85%B8%EC%85%98-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%97%B0%EA%B2%B0</guid>
            <pubDate>Wed, 28 May 2025 04:24:50 GMT</pubDate>
            <description><![CDATA[<p>개인프로젝트 하면서 겪는 좌출우돌을 조금 올려봐야겠다 싶어서 써보기로 했다.</p>
<p>원래는 1년 전에 마이루틴 클론 코딩을 하던 프로젝트가 있었는데 당시에는 거의 CSS 정도를 많이 했고 기능적으로는 구현이 되어있지 않았다. 
나에게 있어서 너무나도 큰 고난은... 백엔드 연결이었는데 일단 mockdata로 하다가 나중에 DB 연결하고 해야지! 싶어서 눈을 감고... 그리고... 회사 일이 너무 바빠져서 그대로 중지되어버렸다...ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ
이러던 중에 개인적으로 노션가지고 좀 놀면서 API를 쓰는 법을 알게 되었다.
근데 보니까 노션이 테이블 구조여서 데이터베이스로 쓰기도 무리가 없을 것 같다는 생각이 들었다.
페이지 안의 content를 가져오는 게 조금 까다롭고(물론 할 수는 있음) 데이터베이스의 정보를 가져오는 건 database key랑 token만 있으면 돼서 완전 할 수 있을 것 같아서 시작!</p>
<br>

<ul>
<li>데이터베이스 구조 만들기<ul>
<li>데이터베이스 구조는 대충 아래와 같이 짰다. 일단 데이터 넘어오고 나서 만들면서 더 수정할 예정.</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hit-that-drum/post/aeb73a55-00f6-474c-9cf5-6a485df4a189/image.png" alt=""></p>
<br>

<ul>
<li>이번에 알았던 사실이었는데 <code>.gitignore</code> 파일에 <code>.env.local</code>랑  <code>.env.development</code>만 넣어놓고 정작? <code>.env</code> 는 안 넣어놔서? 이게 레포지토리에 올라갔음? ㅎ ㅏ? 어이없어... 퍼블릭이긴했지만 이 안에 포트 번호 이외에는 없었기 때문에 천만 다행으로... 나의 노션이 안전할 수 있었다... ㅎ ㅏ... </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[구글드라이브 rclone 조작]]></title>
            <link>https://velog.io/@hit-that-drum/%EA%B5%AC%EA%B8%80%EB%93%9C%EB%9D%BC%EC%9D%B4%EB%B8%8C-rclone-%EC%A1%B0%EC%9E%91</link>
            <guid>https://velog.io/@hit-that-drum/%EA%B5%AC%EA%B8%80%EB%93%9C%EB%9D%BC%EC%9D%B4%EB%B8%8C-rclone-%EC%A1%B0%EC%9E%91</guid>
            <pubDate>Mon, 05 May 2025 05:00:55 GMT</pubDate>
            <description><![CDATA[<p>조금 이른 휴가를 갔다왔는데 가서 체험한 프로그램에서 사진을 구글드라이브로 공유해주었다.
딱 보니까 한 번에 받을 수 없는 갯수와 사이즈였다.
한 때 구글드라이브 학생 무제한 계정으로 10테라까지ㅋㅋㅋㅋㅋ 써봤는데 
구글드라이브는 한 폴더?나 한 번에 선택하는 파일의 갯수가 많거나 용량이 크면(정확히 기준은 알 수 없었다) 오류가 나면서 잘 안 되거나 돼도 나중에 확인해보면 몇몇개가 무작위로 누락되어 있었다.</p>
<p>이것을 받기위해 오랜만에 rclone을 사용해보았다.
오랜만에 하니까 또 까먹었더라고.
다음번을 위해 기록해 놓아야겠다고 생각함.</p>
<p>저번에 할 때는 내가 사용하는 계정의 구글드라이브 파일을 조작하는 거라서 그냥 등록하면 됐을 것 같은데 이번에는 남이 공유해 준 폴더를 조작하는 거라서 조금 걱정이 되었으나 일단 해 보기로 했다.</p>
<p>일단 <code>rclone config</code> 로 공유받은 폴더에 접근한 계정의 구글 드라이브를 추가해줬다.
타입을 구글드라이브로 선택하고 권한은 drive로 해서 모두 조작 가능할 수 있게 했다.</p>
<p><code>rlcone --help</code> 로 커맨드랑 대충 확인한 다음에 파일에 뭐가 있는지 좀 봤다.</p>
<br>

<ul>
<li>remote 구글드라이브의 파일 확인<pre><code>// yabiji는 remote의 이름
</code></pre></li>
</ul>
<p>rclone ls yabiji:</p>
<pre><code>
근데 이렇게 하니까 파일만 나오고 폴더는 안 나오더라고.
내가 공유받은 폴더만 보는 옵션을 찾아보았다.


&lt;br&gt;

- 공유파일만 보는 옵션
</code></pre><p>rclone --drive-shared-with-me ls yabiji:</p>
<pre><code>이 옵션의 경우 내가 공유한 것과 내가 공유받은 것이 모두 뜨는 것 같았다. 
나는 해당계정에 다른 파일이 별로 없어서 따로 필터는 안 하고 이 파일들을 모두 받았다.
필터링 찾기 귀찮...^^;;;

&lt;br&gt;

- 공유파일을 내 컴퓨터에     복사
</code></pre><p>// yabiji의 공유파일을 현제 디렉토리에 [폴더이름]의 폴더에 저장
// -P 는 progress를 보여주는 옵션이다.
// 안 하면 되고 있는 건지 마는 건지 알 수 없어서 붙여주었다.</p>
<p>rclone copy yabiji: [폴더이름] --drive-shared-with-me -P</p>
<p>```</p>
<br>


<p>복사 끄읕~!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] useMatches 타입캐스팅 관련]]></title>
            <link>https://velog.io/@hit-that-drum/TIL-useMatches-%ED%83%80%EC%9E%85%EC%BA%90%EC%8A%A4%ED%8C%85-%EA%B4%80%EB%A0%A8</link>
            <guid>https://velog.io/@hit-that-drum/TIL-useMatches-%ED%83%80%EC%9E%85%EC%BA%90%EC%8A%A4%ED%8C%85-%EA%B4%80%EB%A0%A8</guid>
            <pubDate>Fri, 24 Jan 2025 06:16:13 GMT</pubDate>
            <description><![CDATA[<p>프로젝트에서 브레드크럼을 작업중인데 <code>/(슬래시)</code>로 split을 해 올래도 서버 쪽의 url이랑 맞추다보면 슬래시를 두 번 사용하게 되는 경우도 존재해서 완벽한 방법이 아니었다.
그러던 중에 react-router-dom의 <code>useMatches()</code>를 사용하면 depth별로 구분할 수 있다는 사실을 알게되었다!</p>
<br>

<p>다만 문제는...
useMaches는 hooks 자체에 아래와 같이 타입캐스팅이 되어있다.</p>
<pre><code class="language-javascript">export declare function useMatches(): UIMatch[];
</code></pre>
<p>제네릭으로 바꿀 수 있는 형식이 아니어서 타입이 계속 맞지 않았다.</p>
<br>

<p>작업하고 있는 프로젝트에서는 아래와 같이 쓰고 있다.
handle 안에 오브젝트를 만들어서 해당 url의 제목을 명시한다.</p>
<pre><code class="language-javascript">const CompanyRoutes = {
  path: &#39;company&#39;,
  handle: { title: &#39;고객사 관리&#39; },
  children: [
    {
      path: &#39;company-manage&#39;,
      handle: { title: &#39;고객사 일반관리&#39; },
      children: [
        {
          path: &#39;&#39;,
          element: &lt;CompanyManage /&gt;,
          handle: { title: &#39;고객사 일반관리&#39; }
        },
        {
          path: &#39;add&#39;,
          element: &lt;CompanyAddPage /&gt;,
          handle: { title: &#39;고객사 생성&#39; }
        },
        {
          path: &#39;:comId&#39;,
          element: &lt;CompanyDetailPage /&gt;,
          handle: { title: &#39;고객사 상세&#39; }
        }
      ]
    }
  ]
};
</code></pre>
<br>

<p>그런데 원래 UIMatches의 타입에서는 Handle이 unknown으로 넘어오다보니 여기에 title은 없다고 계속 빨간 줄이 떠 있었다(나타나기는 함, 타입에만 없을 뿐 실제로는 존재하고 있기 때문에).</p>
<p>근데 이 타입은 제네릭이 아니어서 가져와서 내 마음대로 고치고 이럴 수는 없다고 하더라고. 어쩌지어쩌지 하고 있다가 정수님한테 물어봐서는 해답을 얻었다.</p>
<pre><code class="language-javascript">// react-router-dom.d.ts 파일을 만듦
import &#39;react-router-dom&#39;;

declare module &#39;react-router-dom&#39; {
  export declare function useMatches(): UIMatch&lt;{ Handle: { title?: string } }&gt;[];
}</code></pre>
<br>

<p>이렇게 아예 모듈 자체를 overriding 하는 형식으로 파일을 하나 만들면 전체 프로젝트에서 useMatches의 타입이 바뀐다고!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 2024-11-22 jQuery simplyScroll 관련]]></title>
            <link>https://velog.io/@hit-that-drum/TIL-2024-11-22-jQuery-simplyScroll-%EA%B4%80%EB%A0%A8</link>
            <guid>https://velog.io/@hit-that-drum/TIL-2024-11-22-jQuery-simplyScroll-%EA%B4%80%EB%A0%A8</guid>
            <pubDate>Fri, 22 Nov 2024 05:40:05 GMT</pubDate>
            <description><![CDATA[<p>회사 홈페이지에서 롤링배너 들어가는 페이지가 있어서 작업을 하였다.
사실... 내가 작업한 건 아니구...^^;;; 다른 사람이 작업한 부분 복.붙. 했지만...^^
어쨌든 복붙해왔는데 내가 만든 페이지에서만 이미지가 끊겨서 노출이 되고 있는 것이었다!
어째서!
다시 코드를 비교해보았다.</p>
<p>사실 이 프로젝트를 만든 사람이 한 땀 한 땀 만들었을 줄 알았는데 오늘에서야 찾아보니 simplyScroll 이라는 레포를 가져와서 사용하고 있는 것을 알게되었다.</p>
<p><a href="https://github.com/logicbox/jquery-simplyscroll">jquery-simplyscroll GitHub Page</a></p>
<br>

<p>잘 돌아가는 부분과 안 돌아가는 부분의 코드를 확인해 본 결과 안에 들어가 있는 li 태그의 이미지 갯수에 차이가 있었다.
잘 돌아가는 부분은 6개 정도 있었는데 안 되는 부분은 이미지가 1개 뿐이었기 때문에.
무엇보다 모바일이나 약간 작은 모니터 환경(내꺼)에서는 잘 돌아가는 걸 보니 무언가 width 때문에 일어나는 문제같다는 것을 예감했다.</p>
<br>

<p>콘솔창으로 element 탭 보면서 확인해 본 결과,
simplyScroll은 한 이미지가 돌아가면서 걔가 어느 시점에 기준 위치를 넘으면 그 다음 이미지를 붙여주는 형식이었다. 
지금 우리 프로젝트는 width가 3400px 정도로 잡혀있는 상태.
롤링이미지 하나의 width는 1100px 이었다.</p>
<p>즉, 총 width가 3400px로 있는 상태에서 2개의 이미지만 노출되는 형식이다 보니 2200px만 나타나고 있는 중이고 나머지 1300px은 어느정도 화면에 나타날 때까지 빈 이미지로 보이는 상황이었다.
일정 시점을 넘어야 그 다음 이미지를 붙여주는데 일정 시점이 지나기 전에도 이미 화면에 보이고는 있으니까.
그래서 똑같은 이미지를 3400px 보다 더 넘어갈 수 있도록 아래와 같이 복사를 더 했다.</p>
<p>그리고 잘 되는 것을 확인!</p>
<br>

<pre><code class="language-javascript">&lt;ul class=&quot;pt_brand_in&quot;&gt;
    &lt;li class=&quot;pt_brand_item&quot;&gt;&lt;img src=&quot;/res/img/rolling.png&quot; /&gt;&lt;/li&gt;
    &lt;li class=&quot;pt_brand_item&quot;&gt;&lt;img src=&quot;/res/img/rolling.png&quot; /&gt;&lt;/li&gt;
    &lt;li class=&quot;pt_brand_item&quot;&gt;&lt;img src=&quot;/res/img/rolling.png&quot; /&gt;&lt;/li&gt;
    &lt;li class=&quot;pt_brand_item&quot;&gt;&lt;img src=&quot;/res/img/rolling.png&quot; /&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;script&gt; $(&quot;.pt_brand_in&quot;).simplyScroll({
        speed: 1,
        direction: &#39;forwards&#39;,
        pauseOnHover: false,
        autoMode: &#39;loop&#39;
    });
&lt;/script&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 2024-11-19 useEffect의 async await 사용]]></title>
            <link>https://velog.io/@hit-that-drum/TIL-2024-11-19-useEffect%EC%9D%98-async-await-%EC%82%AC%EC%9A%A9</link>
            <guid>https://velog.io/@hit-that-drum/TIL-2024-11-19-useEffect%EC%9D%98-async-await-%EC%82%AC%EC%9A%A9</guid>
            <pubDate>Tue, 19 Nov 2024 00:10:25 GMT</pubDate>
            <description><![CDATA[<h2 id="로직-설명">로직 설명</h2>
<br>

<ul>
<li>일단 전역상태로 deleteIdx가 존재한다. 이유는 각 그룹을 관리할 때 테이블 내에서 관리되고 있어서 각 row에 있는 &quot;삭제&quot; 버튼을 누르면 그 행의 데이터가 삭제되기 때문에 실질적으로 그 버튼을 누를 때까지는 어느 행이 삭제되어야 하는지 알 수 없기 때문이다.<pre><code class="language-javascript">const [deleteIdx, setDeleteIdx] = useState&lt;string&gt;();</code></pre>
</li>
</ul>
<br>

<ul>
<li>삭제 버튼이 눌릴 때 setState 함수를 써서 삭제할 row의 정보를 전달해준다.</li>
</ul>
<pre><code class="language-javascript">const handleDeleteGroup = async (groupId: number) =&gt; {
    setDeleteIdx(groupId.toString());
  };</code></pre>
<br>

<ul>
<li>useEffect 문으로 deleteIdx가 변경되었을 때 감지하여 삭제 동작을 실행하는 함수를 실행하게 만든다.</li>
</ul>
<pre><code class="language-javascript">  useEffect(() =&gt; {
    if (deleteIdx) {
      deleteRow();
    }
  }, [deleteIdx]);</code></pre>
<br>

<ul>
<li>async/await 함수의 형태로 만들어서 실행시킨다.</li>
</ul>
<pre><code class="language-javascript">  const deleteRow = async () =&gt; {
    showDialog({
      content: (
        &lt;Box&gt;
          &lt;Box&gt;해당 그룹을 삭제하시겠습니까?&lt;/Box&gt;
          &lt;Box&gt;삭제 시 설정 된 고객사 그룹 설정은 삭제되지 않습니다.&lt;/Box&gt;
        &lt;/Box&gt;
      ),
      onConfirm: async () =&gt; {
        await deleteGroup();
        enqueueSnackbar(&#39;그룹이 삭제되었습니다.&#39;, { variant: &#39;success&#39; });
        refreshGroups();
      }
    });
  };</code></pre>
<br>

<h2 id="💡thought">💡thought</h2>
<p>원래는 useEffect 문 자체에 async/await를 거는 방식도 있는 걸로 알았는데 말이죠? 
꿈에서 봤나봅니다...
원래 이런 식으로 useEffect 구문 안에서 함수를 따로 정의해서 돌려야 한다고 했다.
내가 한 방식은 그냥 바로 위에 정의해서 돌려버린 것일 뿐 크게 차이는 없는 듯.
다 같이 모여있는 게 가독성을 위해서는 더 나을 것 같기도 하지만 또 어떤 측면에서는... useEffect 구문이 돌아갈 때마다 사용하지도 않는 함수가 계속 재정의 되어야 할 때도 있으니까 낭비이기도 할지도...
세상에 정답은 없네... 절레절레...</p>
<pre><code class="language-javascript">useEffect(() =&gt; {
  const fetchData = async () =&gt; {
    const data = await getdata();
    setUser(data);
  };
  fetchData();
}, []);</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 2024-10-28 Java/IntelliJ Spring Boot Devtools & LiveReload 사용법]]></title>
            <link>https://velog.io/@hit-that-drum/TIL-JavaIntelliJ-Spring-Boot-Devtools-LiveReload-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@hit-that-drum/TIL-JavaIntelliJ-Spring-Boot-Devtools-LiveReload-%EC%82%AC%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Mon, 28 Oct 2024 02:21:03 GMT</pubDate>
            <description><![CDATA[<p>스프링 강의 조금씩 듣고 있는데 프론트는... 딱히 처리를 안 해도 그냥 웹페이지를 리프레시 하면 당연히 화면 수정사항이 반영이 된단 말이죠? 왜냐면 새로 했으니까? 근데? 백엔드는? 반드시 서버를 한 번 껐다가 다시 켜야 하는 것이었습니다!
와! 백엔드 귀찮다!
김영한 분 강의 중간에 spring devtools 라는 것을 추가하면 서버를 내렸다가 다시 올리지 않아도 바뀐다고 해서 그것을 깔아보려고 노력을 하다가... 몇 번 실패하고 안 됐다.
하루가 지나고 다시 한 번 잘 찾아봐서는 도움이 되는 블로그 발견!</p>
<p>인텔리제이는 정말 좋은 IDE이지만 너무 복잡해서 어디가서 뭘 찾아서 넣어야 하고...
이런 것들을 모르겠는 게 좀 힘든 것 같다.
쨌든 자바/인텔리제이 로 설정하는 자세한 방법은 아래의 블로그에서 찾아볼 수 있었다.</p>
<br>

<p><a href="https://adjh54.tistory.com/62">[Java/IntelliJ] Spring Boot Devtools 이해하고 설정하기
</a></p>
<h2 id="파일-코드">파일 코드</h2>
<ul>
<li><code>hello.helloSpring &gt; Controller &gt; HelloController</code></li>
</ul>
<p>여기에서는 data로 서버에서 내리는 String을 내려주는데 이걸 바꿀 때마다 서버를 내렸다 올렸다 하고 싶지 않았다!</p>
<pre><code class="language-java">package hello.hellospring.controller;

import org.springframework.ui.Model;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HelloController {

    @GetMapping(&quot;hello&quot;)
    public String hello(Model model) {
        model.addAttribute(&quot;data&quot;, &quot;yap yap yap!!!&quot;);
        return &quot;hello&quot;;
    }
}
</code></pre>
<br>

<ul>
<li><code>resources &gt; templates &gt; hello.html</code></li>
</ul>
<pre><code class="language-java">&lt;!DOCTYPE HTML&gt;
&lt;html xmlns:th=&quot;http://www.thymeleaf.org&quot;&gt;
&lt;head&gt;
    &lt;title&gt;HELLOOOO&lt;/title&gt;
    &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=UTF-8&quot; /&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;p th:text=&quot;&#39;안녕하세요 &#39; + ${data}&quot; /&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<br>

<h2 id="설정할-곳">설정할 곳</h2>
<ul>
<li><code>build.gradle</code></li>
</ul>
<pre><code class="language-java">dependencies {
// 이거 이름은 compileOnly로 하는 사람도 있고 developmentOnly인 사람도 있다.
// 아마 이름은 별로 큰 요인은 아닌 듯 함
    compileOnly &#39;org.springframework.boot:spring-boot-devtools&#39;
}</code></pre>
<br>

<ul>
<li><code>application.properties</code></li>
</ul>
<pre><code class="language-java">// 기본으로 있는 프로젝트 전체에 대한 설정
spring.application.name=hello-spring

// 처음에는 위의 두 개만 있었는데 세번째 것도 위의 블로그를 보고 세번째 것도 추가하였다.
spring.devtools.restart.enabled=true
spring.devtools.livereload.enabled=true
spring.devtools.restart.additional-exclude = static/**,public/**</code></pre>
<br>

<ul>
<li>IntelliJ IDEA &gt; Settings<ul>
<li>Advanced Settings &gt; Compiler<ul>
<li>[체크박스 체크] Allow auto-make to start even if developed application is currently running</li>
</ul>
</li>
<li>Build, Execution, Deployment &gt; Compiler<ul>
<li>[체크박스 체크] Build Project automatically</li>
</ul>
</li>
</ul>
</li>
</ul>
<br>

<ul>
<li>Edit Configuration<ul>
<li>돌리고 있는 프로젝트를 선택 후</li>
<li>Modify options &gt; On &#39;Update&#39; action &gt; Update resources</li>
</ul>
</li>
</ul>
<br>

<h2 id="설치할-것">설치할 것</h2>
<p><a href="https://chromewebstore.google.com/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei?hl=ko">LiveReload Chrome Extention</a></p>
<p>무엇보다 이것을 설치하지 않으면 말짱 도루묵이었다.
근데 몰라서 한참을...^^ 안 함...^^
그냥 이거 안 해도 되는 줄...^^;;;</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 2024-09-11 javascript array random 리팩토링]]></title>
            <link>https://velog.io/@hit-that-drum/TIL-2024-09-11-javascript-array-random-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81</link>
            <guid>https://velog.io/@hit-that-drum/TIL-2024-09-11-javascript-array-random-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81</guid>
            <pubDate>Wed, 11 Sep 2024 23:48:05 GMT</pubDate>
            <description><![CDATA[<p>정수님이 예쁘게 리팩토링을 도와주었다!</p>
<p>먼저 이렇게 배열을 랜덤하게 하는 메소드 자체가 자바스크립트가 없고 앞으로도 쓸 일이 있을 수 있으니까 공통 유틸로 따로 분리를 했다.</p>
<pre><code class="language-javascript">// eslint-disable-next-line import/prefer-default-export
export const shuffleArr = &lt;T&gt;(originArr: T[]) =&gt; {
  const randomArr = originArr.slice();
  for (let idx = originArr.length - 1; idx &gt; 0; idx -= 1) {
    const randomIdx = Math.floor(Math.random() * (idx + 1));
    const temp = randomArr[idx];
    randomArr[idx] = randomArr[randomIdx];
    randomArr[randomIdx] = temp;
  }
  return randomArr;
};</code></pre>
<br>

<p>그리고 아래와 같이 리팩토링 해서 훨씬 깔끔해짐!
나는 배열에서 고유인덱스를 뽑아낸 배열을 랜덤 시킨 다음에 그 값으로 다시 원래 배열에서 맞는 고유인덱스의 객체를 갖고 왔었는데 유틸로 분리해버리니까 객체가 배열의 원소인 것도 랜덤시킬 수 있더라고.
원래 안 되던 이유가 반복문을 기반으로 해서 쓰는 함수들은(반복문, 고차함수 등) 받는 배열 자체를 그 당시에 수정할 수는 없으니까 고유인덱스 값을 뽑았던 거였는데 shuffleArr로 분리시켰더니 안에서 randomArr로 배열을 복사하고는 걔를 바꾸는 거여서 동작하기 때문이었다.</p>
<br>

<pre><code class="language-javascript">const revisedThemeGoodsData = useMemo(() =&gt; {
    const originProduct = data?.originBestProductInfo;
    if (originProduct?.length) {
      originProduct[0].menus = shuffleArr&lt;Menu&gt;(originProduct[0]?.menus || []);
    }
    const themeGoods = [...(data?.autoThemeGoods || []), ...(data?.originBestProductInfo || [])];

    themeGoods.sort((current, next) =&gt; {
      const isNeedSwitch =
        themeGoodsTypePosition.indexOf(current.themeGoodsType) &gt; themeGoodsTypePosition.indexOf(next.themeGoodsType);
      return isNeedSwitch ? 1 : -1;
    });

    return themeGoods;
  }, [data]);</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 2024-09-05 javascript 객체 배열 랜덤 정렬]]></title>
            <link>https://velog.io/@hit-that-drum/TIL-2024-09-05-javascript-%EA%B0%9D%EC%B2%B4-%EB%B0%B0%EC%97%B4-%EB%9E%9C%EB%8D%A4-%EC%A0%95%EB%A0%AC</link>
            <guid>https://velog.io/@hit-that-drum/TIL-2024-09-05-javascript-%EA%B0%9D%EC%B2%B4-%EB%B0%B0%EC%97%B4-%EB%9E%9C%EB%8D%A4-%EC%A0%95%EB%A0%AC</guid>
            <pubDate>Thu, 05 Sep 2024 02:07:09 GMT</pubDate>
            <description><![CDATA[<p>아니...? 
자바스크립트에 Array.random()이 없단 것을...? 이제야 알았다...?</p>
<p>백엔드에서 메뉴 정보가 idx 순서로 날아오고 있고 원래는 이걸 순서대로 뿌려주기만 하는데,
좌우스크롤을 하지 않는 사용자에게는 상품이 1-2개 정도밖에는 보이지 않고 하루에 몇 번을 보건 매 번 똑같은 아이템이 보여지니까 이것에 대한 흥미가 들지 않는다는 의견이 있어 이 섹션을 랜덤으로 배열하기로 했다.
백엔드 리소스는 언제나 부족하니까 프론트인 내가 담당하게 되었는데...
난 당연히 뭔가... random()하고 배열 랜덤으로 해버린다음에 다시 재할당~! 하면 끝날 줄 알았더니!!!
아니었다;;;</p>
<p>알고보니... 자바스크립트에는 랜덤배열 메소드가 따로 존재하지 않는다고 한다...
여러가지 방법을 찾아보았는데 대부분 배열의 원소들이 객체가 아닌 숫자로 이루어져 있어서 정확히 복붙해다가 내 상황에 들어맞게 만들 수는 없었다.
그래도 개중에 이 포스팅이 많은 도움을 주었다.</p>
<p><a href="https://7942yongdae.tistory.com/96">Javascript - 배열의 요소를 무작위로 섞는 방법 [array, shuffle]</a></p>
<br>

<p>나는 [{ idx: 123, productName: &#39;strawberry&#39;, price: 3000 }, { idx: 124, productName: &#39;mango&#39;, price: 5000 }, { idx: 125, productName: &#39;melon&#39;, price: 9000 }, { idx: 126, productName: &#39;apple&#39;, price: 1000 }, { idx: 127, productName: &#39;orange&#39;, price: 3000 }] 와 같은 배열을 랜덤배열 시켜야 했다.</p>
<p>최종 코드는 아래와 같다.</p>
<pre><code class="language-javascript">  const revisedThemeGoodsData = useMemo(() =&gt; {
    // 상품 데이터만 따로 변수에 할당(depth가 좀 깊어서)
    const originProduct = data?.originProductInfo;
    // 랜덤 배열할 새로운 배열 생성 및 초기화
    const randomProductArr: Menu[] = [];
    // 상품 데이터 undefined 예외 처리
    if (originProduct) {
      // 상품 데이터에서 메뉴 빼 오기
      const { menus } = originProduct[0];
      // 메뉴 데이터 undefined 예외 처리
      if (menus) {
        // 객체 형태로는 랜덤 배열이 어려워서 idx만 걸러서 새로운 배열 만듦
        const menusIndex = menus.map((el) =&gt; el.idx);
        // 메뉴인덱스 배열에서 뒤에서부터 차례로 랜덤으로 생성한 인덱스와 자리를 바꾸는 반복문
        // 새로운 배열에 넣으면 중복된 값이 계속 들어가서 반드시 원래 배열을 직접 바꿔야 함
        // 원래 배열을 바꿀 경우 중복된 randomIdx가 생성돼도 바뀐 것이 또 바뀔 뿐이지만
        // 새로 생성한 배열에 값을 지정할 경우에는 중복된 값이 또 들어가 버림
        for (let idx = menusIndex.length - 1; idx &gt; 0; idx -= 1) {
          const randomIdx = Math.floor(Math.random() * (idx + 1));
          const temp = menusIndex[idx];
          menusIndex[idx] = menusIndex[randomIdx];
          menusIndex[randomIdx] = temp;
        }
        // 랜덤으로 배열된 menusIndex배열의 값으로 원래 menus 배열에서 해당 객체를 빼서 랜덤으로 상품 정보를 나열하는 랜덤 배열에 넣기
        for (let i = 0; i &lt; menusIndex.length; i += 1) {
          const matchProduct = menus.filter((el) =&gt; el.idx === menusIndex[i]);
          if (matchProduct) {
            randomProductArr.push(matchProduct[0]);
          }
        }
      }
      // 상품 데이터의 menus를 만든 랜덤배열로 바꿈
      originProduct[0].menus = randomProductArr;
    }
    // 여기서는 바뀐 menus의 정보가 랜덤으로 정렬된 배열값으로 들어감
    const themeGoods = [...(data?.autoThemeGoods || []), ...(data?.originProductInfo || [])];

    themeGoods.sort((current, next) =&gt; {
      const isNeedSwitch =
        themeGoodsTypePosition.indexOf(current.themeGoodsType) &gt; themeGoodsTypePosition.indexOf(next.themeGoodsType);
      return isNeedSwitch ? 1 : -1;
    });

    return themeGoods;
  }, [data]);</code></pre>
<p>ㅎ ㅏ... 너무 힘들었다...
애초에 백엔드 개발도 그지같이 되어 있어서 쓸데없이 depth 깊은데 이거 데이터를 또 바꿔치기 할려니까 머리를 엄청 싸맸네...</p>
<p>원래 처음에는 원래 배열을 복사해다가 뭐 거기서 삭제해 나가면서 새로운 랜덤배열을 생성하고 막 이러려고 했는데 이러면 한 스코프 안에서 반복문을 너무 많이 써야 돼서(지금이라고 안 쓰는 것은 아닙니다만...)
아무도 못 알아볼 것 같길래 다 때려치고 그냥 딱 보기에 제일 나은 걸로 했다.
아직 팀원들에게 보여주지 않아서 이대로 배포 나갈 수 있을지 아님 리팩토링이 들어갈지 모르겠군...
리팩토링이 들어간다면 다시 찾아오도록 하겠습니다...</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[모두의데이터] 4주차 팀과제]]></title>
            <link>https://velog.io/@hit-that-drum/%EB%AA%A8%EB%91%90%EC%9D%98%EB%8D%B0%EC%9D%B4%ED%84%B0-4%EC%A3%BC%EC%B0%A8-%ED%8C%80%EA%B3%BC%EC%A0%9C</link>
            <guid>https://velog.io/@hit-that-drum/%EB%AA%A8%EB%91%90%EC%9D%98%EB%8D%B0%EC%9D%B4%ED%84%B0-4%EC%A3%BC%EC%B0%A8-%ED%8C%80%EA%B3%BC%EC%A0%9C</guid>
            <pubDate>Wed, 21 Aug 2024 06:22:54 GMT</pubDate>
            <description><![CDATA[<p>📌 Q1. 시점 컬럼에서 연도와 분기에 대한 파생변수를 생성하고 기술통계를 구해주세요.</p>
<ul>
<li>데이터 컬럼을 단위인 &quot;백만&quot;으로 변경해 주세요.</li>
<li>describe() 로 기술통계를 구했을 때 다음과 같은 결과가 나오도록 합니다. 아래 결과는 예시로 소숫점 자리수가 다르게 표시되더라도 값이 같다면 괜찮습니다.</li>
<li>전처리한 백만, 연도, 분기 컬럼이 수치형 데이터 일 때 아래와 같은 결과가 나오니 데이터가 수치 형태로 되어 있는지도 확인해 주세요!</li>
<li>데이터는 위에서 제공된 데이터를 사용합니다.</li>
<li>해당 기술통계로 알 수 있는 정보가 무엇인지도 함께 작성해 주세요!</li>
</ul>
<pre><code class="language-python"># df 복사 (원본데이터 변형 방지)
df_01 = df.copy()
# &#39;시점&#39; 컬럼 값 확인
df_01[&#39;시점&#39;]
# split 해보기
&#39;2017.1/4&#39;.split(&#39;.&#39;)[0]
&#39;2017.1/4&#39;.split(&#39;.&#39;)[1].split(&#39;/&#39;)[0]

# &#39;연도&#39; 컬럼 생성
df_01[&#39;연도&#39;] = df[&#39;시점&#39;].map(lambda x : int(x.split(&#39;.&#39;)[0]))
# &#39;분기&#39; 컬럼 생성
df_01[&#39;분기&#39;] = df[&#39;시점&#39;].map(lambda x : int(x.split(&#39;.&#39;)[1].split(&#39;/&#39;)[0]))
# &#39;데이터&#39; 컬럼 &#39;백만원&#39; 으로 변경
df_01 = df_01.rename(columns = {&#39;데이터&#39;: &#39;백만원&#39;})
# 기술통계 출력
df_01.describe().round(2)</code></pre>
<br>

<p>📌 Q2. pivot_table을 사용하여 국가(대륙)별 연도별 판매액의 합계를 분석해 주세요.</p>
<ul>
<li>국가(대륙)별 연도별 판매액의 합계를 구해서 어느 지역에 판매액이 많은지를 알아보겠습니다.</li>
<li>pivot_table 을 사용하여 다음의 결과를 구하고 시각화 해주세요!</li>
<li>그래프의 색상, 스타일은 자유롭게 원하는 스타일로 지정해도 됩니다.</li>
</ul>
<pre><code class="language-python"># df 복사 (원본데이터 변형 방지)
df_02 = df_01.copy()
# 데이터 확인
df_02.head(10)

# 피봇테이블 데이터 만들기
pivot_02 = pd.pivot_table(df_02, values=&#39;백만원&#39;, index=[&#39;국가(대륙)별&#39;, &#39;연도&#39;], aggfunc=&#39;sum&#39;)
# 피봇테이블 출력
pivot_02.unstack()
# 라인그래프 출력
sns.lineplot(data=pivot_02, x=&#39;연도&#39;, y=&#39;백만원&#39;, hue=&#39;국가(대륙)별&#39;)
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)</code></pre>
<br>

<p>📌 Q3. groupby를 사용하여 2020년의 온라인 해외판매 상품군을 지역별 합계를 구해 분석해 주세요.</p>
<ul>
<li>2020년은 코로나로 인해 전국민이 어려움을 겪었던 시기입니다. 또, 아마존 등의 해외 온라인 판매가 늘어난 시기이기도 합니다. 이 때, 해외 어느지역에 온라인 판매가 얼마나 되었는지를 groupby() 와 unstack()을 활용해서 합계를 구하고 분석해 주세요! 또 분석한 결과를 보고 어떤 인사이트를 얻었는지도 함께 작성해 주세요!</li>
<li>그래프의 색상, 스타일은 자유롭게 원하는 스타일로 지정해도 됩니다.</li>
</ul>
<pre><code class="language-python"># df 복사 (원본데이터 변형 방지)
df_03 = df_01.copy()
# 2020년을 포함한 이전년도 데이터만 가져옴
df_2020 = df_03[df_03[&#39;연도&#39;] &lt;= 2020]
# groupby 사용
df_2020_group = df_2020.groupby([&#39;국가(대륙)별&#39;, &#39;연도&#39;])[&#39;백만원&#39;].sum().unstack().fillna(0)
# 데이터프레임인지 확인
type(df_2020_group)
# 컬럼값 확인
df_2020_group.columns

# barplot 생성
df_2020_group.plot(kind=&#39;bar&#39;, figsize=(12, 8))
plt.xlabel(&#39;연도&#39;)
plt.ylabel(&#39;값&#39;)
plt.title(&#39;연도별 국가/대륙별 데이터 바 차트&#39;)
plt.legend(title=&#39;국가/대륙&#39;)
plt.grid(True)
plt.show()

# 스택형 바 차트 생성
df_2020_group.plot(kind=&#39;bar&#39;, stacked=True, figsize=(12, 8))
plt.xlabel(&#39;연도&#39;)
plt.ylabel(&#39;값&#39;)
plt.title(&#39;연도별 국가/대륙별 데이터 스택형 바 차트&#39;)
plt.legend(title=&#39;국가/대륙&#39;)
plt.grid(True)
plt.show()</code></pre>
<br>

<p>📌 Q4. 주요 판매 국가와 상품군에 대해 2021년까지의 연도별 온라인 직접 판매액 합계를 시각화 해주세요.</p>
<ul>
<li>온라인 직접 판매액이 높은 지역은 &quot;미국&quot;, &quot;중국&quot;, &quot;일본&quot; 이며, 주요 판매 상품군은 &quot;의류 및 패션 관련상품&quot;, &quot;화장품&quot;, &quot;음반·비디오·악기&quot; 입니다. 2021년까지의 각 지역별 제품별 판매액을 시각화 해주세요!</li>
<li>시각화 결과의 색상이나 스타일이나 스타일은 달라도 괜찮습니다.</li>
</ul>
<pre><code class="language-python"># 데이터베이스 복사
df_03 = df_01.copy()
# 국가가 미국&amp;중국&amp;일본이고 상품군이 의류 및 패션 관련상품&amp;화장품&amp;음반·비디오·악기이고 2021년까지의 데이터로 데이터셋 만듦
df_03_need = df_03[
((df_03[&#39;국가(대륙)별&#39;] == &#39;미국&#39;) 
| (df_03[&#39;국가(대륙)별&#39;] == &#39;중국&#39;) 
| (df_03[&#39;국가(대륙)별&#39;] == &#39;일본&#39;)) 
&amp; ((df_03[&#39;상품군별&#39;] == &#39;의류 및 패션 관련상품&#39;) 
   | (df_03[&#39;상품군별&#39;] == &#39;화장품&#39;) 
   | (df_03[&#39;상품군별&#39;] == &#39;음반·비디오·악기&#39;)) 
&amp; (df_03[&#39;연도&#39;] &lt;= 2021)]
# 출력해서 확인
df_03_need

# 피벗테이블 만들기
pivot_03 = pd.pivot_table(df_03_need, values=&#39;백만원&#39;, index=[&#39;국가(대륙)별&#39;, &#39;상품군별&#39;, &#39;연도&#39;], aggfunc=&#39;sum&#39;)
# 데이터프레임 구조 변경
pivot_03 = pivot_03.unstack()
# 컬럼 확인
pivot_03.columns

# 히트맵으로 시각화
sns.heatmap(pivot_03, cmap=&#39;Blues&#39;, annot=True, fmt=&#39;.0f&#39;)
plt.xticks(ticks=np.arange(5), labels=[&#39;2017년&#39;, &#39;2018년&#39;, &#39;2019년&#39;, &#39;2020년&#39;, &#39;2021년&#39;], ha=&#39;left&#39;)
plt.xlabel(&#39;연도&#39;)</code></pre>
<br>

<h3 id="강의를-마치며">강의를 마치며...</h3>
<p>사실 안 마쳤다네...
문제 5번은 스스로 데이터 정하고 가설 세워서 분석해야 되는데 너무 어려워서 눈물을 흘리고 있음...
그것도 다 하면 여기다 올려야만...</p>
<p>원래 맨 처음에는 화물차의 안전운임제 시행 이전과 이후, 그리고 없어지고 난 다음의 사고통계를 분석해서 안전운임제 이후로 화물차의 교통사고 건수가 줄어들었고... 이런 식으로 분석하려고 했는데...
일단 데이터 자체가 2022년까지밖에 없어서 안전운임제가 사라진 다음의 데이터가 없어서 내 가설의 한 축이 사라졌음.
여기에 KOSIS에 있는 데이터에서 교통사고의 차종을 볼 수 있는 건 맞는데 생각해보면 나 자체가 안전운임제가 어느 차까지 해당되는지 잘 모르니깐... 대충 화물차 사고건수만 살펴볼 수 있어서 좀 빈약한 근거가 되는 것 같았다. 
그래서 때려침^^</p>
<p>지금은 노동시간이 줄어듦에 따라 성인들의 여가시간에서 독서가 차지하는 비율이 늘어나고 실질적으로 독서율이 올라갔다는 것을 가설로 세웠는데...
이것도 별로 안 될 것 같음.
시간이 없어서 이거 하다가 내 가설 검증 안 되면 안 된 채로 실패^^! 이러고 내야될 것 같다.</p>
<p>어쨌든 이런 공짜 기회를 통해 판다스도 써보고 너무 재밌었다.
회사 데이터 가지고 시각화 하는 것도 생각해봐야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[모두의데이터] 3주차 팀과제]]></title>
            <link>https://velog.io/@hit-that-drum/%EB%AA%A8%EB%91%90%EC%9D%98%EB%8D%B0%EC%9D%B4%ED%84%B0-3%EC%A3%BC%EC%B0%A8-%ED%8C%80%EA%B3%BC%EC%A0%9C</link>
            <guid>https://velog.io/@hit-that-drum/%EB%AA%A8%EB%91%90%EC%9D%98%EB%8D%B0%EC%9D%B4%ED%84%B0-3%EC%A3%BC%EC%B0%A8-%ED%8C%80%EA%B3%BC%EC%A0%9C</guid>
            <pubDate>Tue, 13 Aug 2024 07:17:58 GMT</pubDate>
            <description><![CDATA[<p>📌Q1. 연령대별 허리둘레에 대한 기술통계를 구하려고 합니다. 다음 제공되는 딕셔너리를 통해 연령대코드(5세단위)를 &quot;연령대&quot;로 만들고 아래와 같은 기술통계값을 구해주세요!</p>
<pre><code class="language-python">age_code = {1: &#39;0~4세&#39;,
 2: &#39;5~9세&#39;,
 3: &#39;10~14세&#39;,
 4: &#39;15~19세&#39;,
 5: &#39;20~24세&#39;,
 6: &#39;25~29세&#39;,
 7: &#39;30~34세&#39;,
 8: &#39;35~39세&#39;,
 9: &#39;40~44세&#39;,
 10: &#39;45~49세&#39;,
 11: &#39;50~54세&#39;,
 12: &#39;55~59세&#39;,
 13: &#39;60~64세&#39;,
 14: &#39;65~69세&#39;,
 15: &#39;70~74세&#39;,
 16: &#39;75~79세&#39;,
 17: &#39;80~84세&#39;,
 18: &#39;85세+&#39;}

# df 자체를 바꿔버리면 나머지 문제를 푸는 데에 지장이 있어서 copy 해서 사용
df_01 = df.copy()
# 해당 column의 값들을 치환
df_01[&#39;연령대코드(5세단위)&#39;] = df_01[&#39;연령대코드(5세단위)&#39;].map(age_code)

# ANSWER
pd.pivot_table(df_01, index = &#39;연령대코드(5세단위)&#39;, values = &#39;허리둘레&#39;, aggfunc = [&#39;describe&#39; ])</code></pre>
<br>

<p>📌Q2. &quot;음주여부&quot;, &quot;흡연상태&quot;, &quot;연령대코드(5세단위)&quot;, &quot;성별코드&quot;에 대한 상관계수를 구하고 시각화 해주세요.</p>
<pre><code class="language-python"># 필요한 데이터만 가져 옴
columns = [&#39;음주여부&#39;, &#39;흡연상태&#39;, &#39;연령대코드(5세단위)&#39;, &#39;성별코드&#39;]
df_02 = df[columns]

# ANSWER
mask = np.triu(np.ones_like(df_corr, dtype=np))

plt.figure(figsize=(6, 5))
sns.heatmap(df_corr, annot=True, fmt=&#39;2.2f&#39;, cmap=&#39;spring&#39;, mask=mask, vmin=-1, vmax=1)</code></pre>
<br>

<p>📌Q3. 흡연하는 사람과 음주하는 사람들의 수는 얼마나 차이가 있을까요?</p>
<pre><code class="language-python">columns = [&#39;음주여부&#39;, &#39;흡연상태&#39;]
df_03 = df[columns]
# df_03은 df에서 추출한 데이터프레임이고 이걸 그대로 아래에서 가공하려고 하면 데이터프레임의 일부분이라 오류가 발생함
# 그래서 .copy()로 복사본을 떠서 df_03에 재할당함
df_03 = df_03.copy()

# 흡연상태 문자열로 상태값 치환
smoke = {1 : &quot;흡연안함&quot;, 2: &quot;끊음&quot;, 3: &quot;흡연중&quot;}
df_03[&#39;흡연상태&#39;] = df_03[&#39;흡연상태&#39;].replace(smoke)
# 음주여부 문자열로 상태값 치환
drink = {0: &quot;안마심&quot;, 1: &quot;마심&quot;}
df_03[&#39;음주여부&#39;] = df_03[&#39;음주여부&#39;].replace(drink)

# 수업에서는 &#39;가입자일련번호(각 고유번호)&#39;를 넣어서 values에 저걸 넣고 aggfunc=count를 해서 실습한 것 같음
# 찾아보니까 aggfunc=size도 있어서 이걸로 해 봤더니 됨!
# size는 NaN 값을 제외하고 true이거나 false인 값들을 세어준다고 함.

# ANSWER
df_03_pivot = pd.pivot_table(df_03, index=&#39;음주여부&#39;, columns=&#39;흡연상태&#39;, aggfunc=&#39;size&#39;)
df_03_pivot

# ANSWER
sns.countplot(data=df_03, x=&#39;흡연상태&#39;, hue=&#39;음주여부&#39;, palette=&#39;spring&#39;)</code></pre>
<br>

<p>📌 Q4. 체중이 120Kg 이상인 데이터를 찾아 &quot;총콜레스테롤&quot;, &quot;감마지티피&quot; 값을 음주여부에 따라 산점도로 시각화해주세요!</p>
<pre><code class="language-python"># 필요한 데이터만 가지고 옴
columns = [&#39;체중(5Kg 단위)&#39;, &#39;총콜레스테롤&#39;, &#39;감마지티피&#39;, &#39;음주여부&#39;, &#39;흡연상태&#39;]
df_04 = df[columns]
df_04 = df_04.copy()

# 필요한 데이터에서 문제 조건에 맞는 데이터를 골라냄
df_04_over = df_04.loc[df_04[&#39;체중(5Kg 단위)&#39;] &gt;= 120, [&#39;총콜레스테롤&#39;, &#39;감마지티피&#39;, &#39;음주여부&#39;, &#39;체중(5Kg 단위)&#39;, &#39;흡연상태&#39;]]
df_04_over.head()

# 체중이 120Kg 이상이고 흡연을 하지 않는 사람의 정보
df_04_over_smoke_1 = df_04.loc[(df_04[&#39;체중(5Kg 단위)&#39;] &gt;= 120) &amp; (df_04[&#39;흡연상태&#39;] == 1), 
[&#39;총콜레스테롤&#39;, &#39;감마지티피&#39;, &#39;음주여부&#39;, &#39;체중(5Kg 단위)&#39;, &#39;흡연상태&#39;]]

# 체중이 120Kg 이상이고 흡연을 중단한 사람의 정보
df_04_over_smoke_2 = df_04.loc[(df_04[&#39;체중(5Kg 단위)&#39;] &gt;= 120) &amp; (df_04[&#39;흡연상태&#39;] == 2), 
[&#39;총콜레스테롤&#39;, &#39;감마지티피&#39;, &#39;음주여부&#39;, &#39;체중(5Kg 단위)&#39;, &#39;흡연상태&#39;]]

# 체중이 120Kg 이상이고 흡연을 하는 사람의 정보
df_04_over_smoke_3 = df_04.loc[(df_04[&#39;체중(5Kg 단위)&#39;] &gt;= 120) &amp; (df_04[&#39;흡연상태&#39;] == 3), 
[&#39;총콜레스테롤&#39;, &#39;감마지티피&#39;, &#39;음주여부&#39;, &#39;체중(5Kg 단위)&#39;, &#39;흡연상태&#39;]]

# 똑같은 값이 계속 들어가서 변수로 치환
x = &#39;총콜레스테롤&#39;
y = &#39;감마지티피&#39;
hue = &#39;음주여부&#39;
palette = &#39;spring&#39;
height = 3

# ANSWER
# 문제랑 완전히 똑같지는 않지만 이것이 한계!
sns.lmplot(data=df_04_over_smoke_3, x=x, y=y, hue=hue, palette=palette, height=height)
plt.title(&#39;흡연=흡연중&#39;)
sns.lmplot(data=df_04_over_smoke_1, x=x, y=y, hue=hue, palette=palette, height=height)
plt.title(&#39;흡연=흡연 안 함&#39;)
sns.lmplot(data=df_04_over_smoke_2, x=x, y=y, hue=hue, palette=palette, height=height)
plt.title(&#39;흡연=끊음&#39;)</code></pre>
<pre><code class="language-python"># 모르겠어서 GTP한테 물어봄
# 근데 이러면 css를 어떻게 하는지는? 모르겠다. GPT가 가르쳐준 방법은 lmplot 추가할 때 palette를 넣는 건데 이건 안 됨.
# 서브셋을 하나의 데이터프레임으로 통합
df_combined = pd.concat([
    df_04_over_smoke_1.assign(흡연상태_설명=&#39;흡연 안 함&#39;),
    df_04_over_smoke_2.assign(흡연상태_설명=&#39;흡연 끊음&#39;),
    df_04_over_smoke_3.assign(흡연상태_설명=&#39;흡연 중&#39;)
])

# FacetGrid 생성
g = sns.FacetGrid(df_combined, col=&#39;흡연상태_설명&#39;, col_wrap=3, height=5, sharey=True)

# lmplot 추가
g.map_dataframe(sns.regplot, x=&#39;총콜레스테롤&#39;, y=&#39;감마지티피&#39;)

# 레전드 위치 조정 및 제목 설정
g.add_legend(title=&#39;음주 여부&#39;, bbox_to_anchor=(1.05, 1), loc=&#39;upper left&#39;)
g.set_titles(col_template=&quot;{col_name}&quot;)
g.set_axis_labels(&quot;총콜레스테롤&quot;, &quot;감마지티피&quot;)
g.set(xlim=(df_combined[&#39;총콜레스테롤&#39;].min(), df_combined[&#39;총콜레스테롤&#39;].max()),
     ylim=(df_combined[&#39;감마지티피&#39;].min(), df_combined[&#39;감마지티피&#39;].max()))
plt.tight_layout()
plt.show()</code></pre>
<br>

<p>📌 Q5. 연령대별로 시력은 얼마나 차이가 날까요? 연령대, 성별 좌우 평균 시력을 시각화 해주세요!</p>
<pre><code class="language-python"># 필요한 데이터만 가지고 옴
columns = [&#39;연령대코드(5세단위)&#39;, &#39;시력(좌)&#39;, &#39;시력(우)&#39;, &#39;성별코드&#39;]
df_05 = df[columns]
df_05 = df_05.copy()
# 연령대코드를 문자열로 치환
df_05[&#39;연령대코드(5세단위)&#39;] = df[&#39;연령대코드(5세단위)&#39;].map(age_code)

fig, axes = plt.subplots(1, 2, figsize=(14, 6), sharey=True)

sns.barplot(data=df_05, x=&#39;시력(좌)&#39;, y=&#39;연령대코드(5세단위)&#39;, hue=&#39;성별코드&#39;, errorbar=None, palette=&#39;spring&#39;, ax=axes[0])
sns.barplot(data=df_05, x=&#39;시력(우)&#39;, y=&#39;연령대코드(5세단위)&#39;, hue=&#39;성별코드&#39;, errorbar=None, palette=&#39;spring&#39;, ax=axes[1])

plt.tight_layout()
plt.show()</code></pre>
<hr>
<p>우리 팀에 한 7-8명 있는 것 같은데 수료 조건이 가능한 사람이 나만 남았다...^^
홧팅...^^
나 자신...^^</p>
]]></description>
        </item>
    </channel>
</rss>