<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>minarae&amp;rsquo;s story</title>
    <link>https://minarae7.tistory.com/</link>
    <description>개발하면서 찾은 자료를 기록하고 일상을 기록하고 있습니다.</description>
    <language>ko</language>
    <pubDate>Mon, 8 Jun 2026 19:56:46 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>minarae7</managingEditor>
    <image>
      <title>minarae&amp;rsquo;s story</title>
      <url>https://t1.daumcdn.net/cfile/tistory/236E8E4353EDD56708</url>
      <link>https://minarae7.tistory.com</link>
    </image>
    <item>
      <title>비개발자를 위한 Vercel 배포 가이드</title>
      <link>https://minarae7.tistory.com/entry/%EB%B9%84%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%A5%BC-%EC%9C%84%ED%95%9C-Vercel-%EB%B0%B0%ED%8F%AC-%EA%B0%80%EC%9D%B4%EB%93%9C</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;vercel1868.jpg&quot; data-origin-width=&quot;866&quot; data-origin-height=&quot;650&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bc2kcF/btsNlBrGa60/F4eVEBz1R2TX0xP9cmBWZK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bc2kcF/btsNlBrGa60/F4eVEBz1R2TX0xP9cmBWZK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bc2kcF/btsNlBrGa60/F4eVEBz1R2TX0xP9cmBWZK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbc2kcF%2FbtsNlBrGa60%2FF4eVEBz1R2TX0xP9cmBWZK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;296&quot; height=&quot;222&quot; data-filename=&quot;vercel1868.jpg&quot; data-origin-width=&quot;866&quot; data-origin-height=&quot;650&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 가이드는 개발 경험이 없는 맥 사용자가 압축된 프로젝트 코드를 받아서 Vercel에 배포하는 전체 과정을 안내합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  시작하기 전에&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;준비물&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt; ️ 맥 컴퓨터&lt;/li&gt;
&lt;li&gt;  Vercel 계정 (이미 가입된 상태)&lt;/li&gt;
&lt;li&gt;  압축된 프로젝트 코드 파일&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  단계별 배포 가이드&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1️⃣ 압축 파일 풀기&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;받은 압축 파일을 찾아서 더블클릭하거나 우클릭 후 &quot;압축 풀기&quot; 옵션을 선택합니다.&lt;/li&gt;
&lt;li&gt;압축이 풀린 폴더를 기억하기 쉬운 위치(예: 바탕화면)에 저장합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2️⃣ 터미널 열기&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Spotlight 검색(&lt;code&gt;⌘ + Space&lt;/code&gt;)을 열고 &quot;Terminal&quot; 또는 &quot;터미널&quot;을 입력한 후 Enter를 누릅니다.&lt;/li&gt;
&lt;li&gt;터미널 창이 열립니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-04-16 오후 1.51.22.png&quot; data-origin-width=&quot;1304&quot; data-origin-height=&quot;749&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bF2h85/btsNmCyb5tT/rAH16AkM4fn5T0E9Mj5GSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bF2h85/btsNmCyb5tT/rAH16AkM4fn5T0E9Mj5GSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bF2h85/btsNmCyb5tT/rAH16AkM4fn5T0E9Mj5GSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbF2h85%2FbtsNmCyb5tT%2FrAH16AkM4fn5T0E9Mj5GSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1304&quot; height=&quot;749&quot; data-filename=&quot;스크린샷 2025-04-16 오후 1.51.22.png&quot; data-origin-width=&quot;1304&quot; data-origin-height=&quot;749&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3️⃣ Node.js 설치하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node.js는 자바스크립트 코드를 실행하기 위해 필요한 환경입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;터미널에서 다음 명령어를 복사하여 붙여넣고 Enter를 누릅니다:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;/bin/bash -c &quot;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;설치 과정에서 비밀번호를 입력하라는 메시지가 나타날 수 있습니다. 컴퓨터 로그인 비밀번호를 입력하세요.&lt;/li&gt;
&lt;li&gt;Homebrew 설치가 완료되면 터미널에 다음 명령어를 입력합니다:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;brew install node&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;4&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;설치가 완료되었는지 확인하기 위해 다음 명령어를 입력합니다:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;node -v&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버전 번호가 표시되면 Node.js가 성공적으로 설치된 것입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4️⃣ 프로젝트 폴더로 이동하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널에서 &lt;code&gt;cd&lt;/code&gt; 명령어를 사용하여 압축을 푼 프로젝트 폴더로 이동합니다. 예를 들어, 바탕화면에 &quot;project-name&quot;이라는 폴더가 있다면:&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;cd ~/Desktop/project-name&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5️⃣ 프로젝트 종속성 설치하기&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;프로젝트 폴더에서 다음 명령어를 입력하여 필요한 패키지들을 설치합니다:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;npm install&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;설치가 완료될 때까지 기다립니다. 이 과정은 인터넷 속도와 프로젝트 크기에 따라 몇 분 정도 소요될 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-04-16 오후 2.01.07.png&quot; data-origin-width=&quot;1304&quot; data-origin-height=&quot;749&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFPjt3/btsNnvESKXh/7nP7pISOxsaAn4VdDB3oyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFPjt3/btsNnvESKXh/7nP7pISOxsaAn4VdDB3oyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFPjt3/btsNnvESKXh/7nP7pISOxsaAn4VdDB3oyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFPjt3%2FbtsNnvESKXh%2F7nP7pISOxsaAn4VdDB3oyK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1304&quot; height=&quot;749&quot; data-filename=&quot;스크린샷 2025-04-16 오후 2.01.07.png&quot; data-origin-width=&quot;1304&quot; data-origin-height=&quot;749&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6️⃣ Vercel CLI 설치하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vercel CLI는 터미널에서 Vercel에 프로젝트를 배포할 수 있게 해주는 도구입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;다음 명령어를 입력하여 Vercel CLI를 설치합니다:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;npm install -g vercel&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;권한 오류가 발생하는 경우&lt;/b&gt;: 다음과 같은 오류 메시지가 나타날 수 있습니다:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;npm ERR! code EACCES
npm ERR! syscall mkdir
npm ERR! path /usr/local/lib/node_modules/vercel
npm ERR! errno -13
npm ERR! Error: EACCES: permission denied&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 다음 방법 중 하나로 해결할 수 있습니다:&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;방법 1: sudo 사용하기 (가장 간단한 방법)&lt;/h4&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;sudo npm install -g vercel&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 명령어를 입력한 후 컴퓨터 비밀번호를 입력하라는 메시지가 나타나면 입력합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;방법 2: npm의 글로벌 경로 변경하기 (더 안전한 방법)&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;먼저 사용자 홈 디렉토리에 .npm-global 폴더를 생성합니다:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;mkdir ~/.npm-global&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;npm 설정을 변경합니다:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;npm config set prefix '~/.npm-global'&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;3&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;환경 변수를 설정합니다:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;echo 'export PATH=~/.npm-global/bin:$PATH' &amp;gt;&amp;gt; ~/.zshrc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 bash를 사용하는 경우:&lt;/p&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;echo 'export PATH=~/.npm-global/bin:$PATH' &amp;gt;&amp;gt; ~/.bash_profile&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;4&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;변경사항을 적용합니다:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;source ~/.zshrc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 bash를 사용하는 경우:&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;source ~/.bash_profile&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;5&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;이제 다시 Vercel CLI를 설치합니다:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;npm install -g vercel&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7️⃣ Vercel에 로그인하기&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;터미널에 다음 명령어를 입력합니다:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;vercel login&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;이메일 주소를 입력하라는 메시지가 나타나면, Vercel 계정에 사용한 이메일 주소를 입력하고 Enter를 누릅니다.&lt;/li&gt;
&lt;li&gt;입력한 이메일로 확인 메일이 발송됩니다. 메일함을 확인하고 확인 링크를 클릭합니다.&lt;/li&gt;
&lt;li&gt;터미널에 로그인 성공 메시지가 표시됩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8️⃣ 프로젝트 배포하기&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;프로젝트 폴더에서 다음 명령어를 입력합니다:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;vercel&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;몇 가지 질문이 나타납니다:&lt;/li&gt;
&lt;/ol&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;질문&lt;/th&gt;
&lt;th&gt;대답&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&quot;Set up and deploy...&quot;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;y&lt;/code&gt;를 입력하고 Enter를 누릅니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&quot;Which scope...&quot;&lt;/td&gt;
&lt;td&gt;개인 계정이면 그냥 Enter를 누릅니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&quot;Link to existing project?&quot;&lt;/td&gt;
&lt;td&gt;새 프로젝트면 &lt;code&gt;n&lt;/code&gt;을 입력하고 Enter를 누릅니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&quot;What's your project's name?&quot;&lt;/td&gt;
&lt;td&gt;프로젝트 이름을 입력하거나 기본값을 사용하려면 Enter를 누릅니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&quot;In which directory is your code located?&quot;&lt;/td&gt;
&lt;td&gt;현재 디렉토리면 그냥 Enter를 누릅니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; start=&quot;3&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;배포가 시작되고, 완료되면 프로젝트 URL이 터미널에 표시됩니다(예: &lt;code&gt;https://your-project.vercel.app&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9️⃣ 배포 확인하기&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;터미널에 표시된 URL을 웹 브라우저에 붙여넣어 사이트가 성공적으로 배포되었는지 확인합니다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://vercel.com/dashboard&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Vercel 대시보드&lt;/a&gt;에 로그인하여 프로젝트 상태를 확인할 수도 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-04-16 오후 2.05.06.png&quot; data-origin-width=&quot;1221&quot; data-origin-height=&quot;839&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIIMsq/btsNn0jMPuG/Db6em5exIMqq1NLED8vKxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIIMsq/btsNn0jMPuG/Db6em5exIMqq1NLED8vKxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIIMsq/btsNn0jMPuG/Db6em5exIMqq1NLED8vKxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIIMsq%2FbtsNn0jMPuG%2FDb6em5exIMqq1NLED8vKxk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1221&quot; height=&quot;839&quot; data-filename=&quot;스크린샷 2025-04-16 오후 2.05.06.png&quot; data-origin-width=&quot;1221&quot; data-origin-height=&quot;839&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;⚠️ 문제 해결&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 오류가 발생한다면:&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;일반적인 문제 해결&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;에러 메시지를 잘 읽어보세요. 대부분의 에러는 문제를 설명하고 있습니다.&lt;/li&gt;
&lt;li&gt;Node.js와 npm이 제대로 설치되었는지 확인하세요:
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;node -v
npm -v&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;인터넷 연결을 확인하세요.&lt;/li&gt;
&lt;li&gt;프로젝트 코드에 문제가 있을 수 있으니 코드를 제공한 사람에게 문의하세요.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;권한 관련 문제 해결&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;권한 관련 오류가 계속 발생하는 경우:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;터미널에 다음 명령어를 입력하여 현재 사용자 이름을 확인합니다:
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;whoami&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;다음 명령어로 폴더 권한을 수정합니다(사용자이름을 본인의 것으로 변경하세요):
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;sudo chown -R 사용자이름 /usr/local/lib/node_modules&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  향후 업데이트 배포하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 수정한 후 다시 배포하려면:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;프로젝트 폴더에서 터미널을 열고 다음 명령어를 입력합니다:
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;vercel&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;기존 프로젝트에 연결하려면 &lt;code&gt;y&lt;/code&gt;를 선택하고 안내에 따라 진행합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  추가 자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://vercel.com/docs&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Vercel 공식 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nodejs.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Node.js 공식 웹사이트&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://devhints.io/npm&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;npm 명령어 치트시트&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  축하합니다! 이제 Vercel에 프로젝트를 성공적으로 배포하셨습니다. 문제가 발생하면 이 가이드를 다시 참조하세요.&lt;/p&gt;</description>
      <category>Programming/Nextjs</category>
      <category>deploy</category>
      <category>nextjs</category>
      <category>vercel</category>
      <category>무료호스팅</category>
      <category>배포</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/414</guid>
      <comments>https://minarae7.tistory.com/entry/%EB%B9%84%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%A5%BC-%EC%9C%84%ED%95%9C-Vercel-%EB%B0%B0%ED%8F%AC-%EA%B0%80%EC%9D%B4%EB%93%9C#entry414comment</comments>
      <pubDate>Wed, 16 Apr 2025 13:44:08 +0900</pubDate>
    </item>
    <item>
      <title>PostgreSQL 계정 관리 완벽 가이드</title>
      <link>https://minarae7.tistory.com/entry/PostgreSQL-%EA%B3%84%EC%A0%95-%EA%B4%80%EB%A6%AC-%EC%99%84%EB%B2%BD-%EA%B0%80%EC%9D%B4%EB%93%9C</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;PostgreSQL 계정 관리에 대한 상세한 블로그 포스트를 작성해드리겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;991&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dtQXzA/btsLK6Ul5QA/t8cMrSHfkEKAzGpxbKLkBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dtQXzA/btsLK6Ul5QA/t8cMrSHfkEKAzGpxbKLkBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dtQXzA/btsLK6Ul5QA/t8cMrSHfkEKAzGpxbKLkBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdtQXzA%2FbtsLK6Ul5QA%2Ft8cMrSHfkEKAzGpxbKLkBK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;991&quot; height=&quot;495&quot; data-origin-width=&quot;991&quot; data-origin-height=&quot;495&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;PostgreSQL 계정 관리 완벽 가이드&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;목차&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자 계정 생성&lt;/li&gt;
&lt;li&gt;권한 관리&lt;/li&gt;
&lt;li&gt;역할(Role) 관리&lt;/li&gt;
&lt;li&gt;패스워드 정책&lt;/li&gt;
&lt;li&gt;계정 관리 모범 사례&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 사용자 계정 생성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PostgreSQL에서는 새로운 사용자를 생성하기 위해 &lt;code&gt;CREATE USER&lt;/code&gt; 또는 &lt;code&gt;CREATE ROLE&lt;/code&gt; 명령을 사용합니다. 두 명령의 주요 차이점은 &lt;code&gt;CREATE USER&lt;/code&gt;는 기본적으로 LOGIN 권한이 있고, &lt;code&gt;CREATE ROLE&lt;/code&gt;은 LOGIN 권한이 없다는 점입니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;-- 기본적인 사용자 생성
CREATE USER john WITH PASSWORD 'secure_password123';

-- 추가 옵션을 포함한 사용자 생성
CREATE USER jane 
WITH PASSWORD 'secure_password456'
CREATEDB
VALID UNTIL '2025-12-31';&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 권한 관리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스와 테이블에 대한 권한을 관리하는 것은 보안에 매우 중요합니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;-- 데이터베이스 권한 부여
GRANT ALL PRIVILEGES ON DATABASE mydb TO john;

-- 특정 테이블에 대한 권한 부여
GRANT SELECT, INSERT, UPDATE ON table_name TO jane;

-- 스키마 권한 부여
GRANT USAGE ON SCHEMA public TO john;

-- 권한 취소
REVOKE ALL PRIVILEGES ON DATABASE mydb FROM john;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 역할(Role) 관리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역할을 사용하면 권한을 그룹화하여 효율적으로 관리할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;-- 역할 생성
CREATE ROLE readonly;

-- 역할에 권한 부여
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;

-- 사용자에게 역할 할당
GRANT readonly TO john;

-- 역할 확인
SELECT rolname FROM pg_roles;

-- 사용자의 역할 확인
SELECT r.rolname 
FROM pg_roles r 
JOIN pg_auth_members m ON m.roleid = r.oid 
JOIN pg_roles u ON m.member = u.oid 
WHERE u.rolname = 'john';&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 패스워드 정책&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안을 강화하기 위한 패스워드 관리 방법입니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;-- 패스워드 변경
ALTER USER john WITH PASSWORD 'new_secure_password';

-- 패스워드 만료 설정
ALTER USER jane VALID UNTIL '2025-12-31';

-- 패스워드 없이 접속 불가능하게 설정
ALTER USER john WITH PASSWORD NULL;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 계정 관리 모범 사례&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.1 보안 관련 권장사항&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;강력한 패스워드 정책 수립
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최소 12자 이상&lt;/li&gt;
&lt;li&gt;대소문자, 숫자, 특수문자 포함&lt;/li&gt;
&lt;li&gt;정기적인 패스워드 변경&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;최소 권한 원칙 적용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;필요한 최소한의 권한만 부여&lt;/li&gt;
&lt;li&gt;정기적인 권한 검토 및 불필요한 권한 제거&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.2 운영 관련 권장사항&lt;/h3&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;-- 불필요한 계정 잠금
ALTER USER unused_user NOLOGIN;

-- 계정 활성화 상태 확인
SELECT usename, valuntil, useconfig 
FROM pg_user 
JOIN pg_shadow ON usename = pg_shadow.shadname;

-- 접속 제한 설정
ALTER USER restricted_user CONNECTION LIMIT 5;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 유용한 관리 쿼리&lt;/h2&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;-- 모든 사용자 조회
SELECT usename, useconfig, valuntil 
FROM pg_user;

-- 특정 테이블에 대한 권한 조회
SELECT grantee, privilege_type 
FROM information_schema.role_table_grants 
WHERE table_name = 'your_table';

-- 잠긴 계정 조회
SELECT usename, valuntil 
FROM pg_user 
WHERE valuntil &amp;lt; CURRENT_TIMESTAMP;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마치며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PostgreSQL의 계정 관리는 데이터베이스 보안의 핵심입니다. 위에서 설명한 내용들을 바탕으로 적절한 보안 정책을 수립하고 구현하시기 바랍니다. 특히 운영 환경에서는 정기적인 계정 검토와 권한 관리가 매우 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 가이드가 PostgreSQL 계정 관리에 도움이 되었기를 바랍니다. 추가적인 보안 설정이나 특정 환경에 따른 구성은 공식 PostgreSQL 문서를 참조하시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#PostgreSQL&lt;br /&gt;#데이터베이스보안&lt;br /&gt;#DB계정관리&lt;br /&gt;#PostgreSQLRole&lt;br /&gt;#데이터베이스관리&lt;br /&gt;#PostgreSQLTutorial&lt;br /&gt;#PostgreSQL권한&lt;br /&gt;#DatabaseSecurity&lt;br /&gt;#PostgreSQLAdmin&lt;br /&gt;#데이터베이스운영&lt;/p&gt;</description>
      <category>Programming/Database</category>
      <category>databasesecurity</category>
      <category>db계정관리</category>
      <category>postgresql #</category>
      <category>postgresqladmin</category>
      <category>postgresqlrole</category>
      <category>postgresqltutorial</category>
      <category>postgresql권한</category>
      <category>데이터베이스관리</category>
      <category>데이터베이스보안</category>
      <category>데이터베이스운영</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/413</guid>
      <comments>https://minarae7.tistory.com/entry/PostgreSQL-%EA%B3%84%EC%A0%95-%EA%B4%80%EB%A6%AC-%EC%99%84%EB%B2%BD-%EA%B0%80%EC%9D%B4%EB%93%9C#entry413comment</comments>
      <pubDate>Mon, 13 Jan 2025 10:51:23 +0900</pubDate>
    </item>
    <item>
      <title>Fastapi와 svelte를 이용한 게시판 개발 #2</title>
      <link>https://minarae7.tistory.com/entry/Fastapi%EC%99%80-svelte%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EA%B2%8C%EC%8B%9C%ED%8C%90-%EA%B0%9C%EB%B0%9C-2</link>
      <description>&lt;h1&gt;데이터베이스 연결&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스에 연결하여서 게시판에서 생성되는 내용을 mysql에 저장하도록 할 것이다.&lt;br /&gt;앞에 &lt;b&gt;Setup.md&lt;/b&gt;에서 Database에 연결하기 위한 설정을 해두었다.&lt;br /&gt;이제 관련 내용을 하나씩 채워나가보도록 하겠다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;alembic 초기화&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;alembic을 사용하기 위해서 다음 명령어를 통해서 초기화를 진행한다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;alembic init migrations&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 명령어를 입력하고 나면 migrations라는 디렉토리가 생성되며 그 디렉토리 안에 alembic을 사용하기 위한 내용들이 채워진다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;alembic 코드 수정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;alembic을 사용할 준비를 하였다. 위의 명령어에서 migrations라는 디렉토리를 생성하였고 여기에 필요한 코드와 이후 마이그레이션에서 사용되는 생성 코드들이 저장된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;alembic.ini&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기화를 실행했던 루트 디렉토리에는 &lt;b&gt;alembic.ini&lt;/b&gt; 파일이 생성된다. 여기에는 초기에 데이터베이스에 접근하기 위한 기본 설정이 들어있다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;# sqlalchemy.url = driver://user:pass@localhost/dbname&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드와 같이 sqlalchemy.url로 시작하는 코드를 찾아서 주석 처리를 하도록 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;env.py&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 migrations 디렉토리 아래에 위치한 env.py 코드를 수정할 것이다.&lt;br /&gt;이 파일에 데이터베이스에 접속하기 위해서 DB 접속 정보를 읽어와서 접속하도록 코드를 작성하며, 메타정보를 읽어올 파일을 지정하도록 할 것이다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# 생략
import os
import models
from starlette.config import Config

from sqlalchemy.ext.declarative import declarative_base

# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
Base = declarative_base()


BASE_DIR = os.path.dirname(os.path.abspath(__file__))
CONFIG_FILE = os.path.join(BASE_DIR, '../config/.env')
db_config = Config(CONFIG_FILE)
DB_USER = db_config('DB_USER')
DB_PW = db_config('DB_PW')
DB_HOST = db_config('DB_HOST')
DB_PORT = db_config('DB_PORT')
DB_DATABASE = db_config('DB_DATABASE')

# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
if not config.get_main_option(&quot;sqlalchemy.url&quot;):
    config.set_main_option(
        &quot;sqlalchemy.url&quot;,
        f&quot;mysql+pymysql://{DB_USER}:{DB_PW}@{DB_HOST}:{DB_PORT}/{DB_DATABASE}&quot;,
    )

# 생략
target_metadata = models.Base.metadata

# 생략&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;config/.env 파일을 읽어와서 DB 접속 정보를 만들어서 alembic에서 접속할 url의 주소를 생성하도록 하였다.&lt;br /&gt;더불어 target_metadata가 기존의 None으로 설정되어 있는 것을 기존 Repository에서 복사해온 models로 연결하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 코드를 수정하면 alembic 명령어를 통해서 코드에서 models를 수정하면 Database에 적용할 수 있는 준비가 된 것이다.&lt;br /&gt;다음 명령어를 입력해서 정상적으로 작동하는지 확인해보도록 하자.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;alembic revision --autogenerate&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 명령어는 models.py를 수정한 후에 테이블에 적용하기 위해서 alembic으로 자동으로 스키마를 생성하도록 하는 명령어이다.&lt;br /&gt;이 명령어를 입력해서 정상적으로 작동하면 코드 수정이 정상적으로 이루어진 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정이 정상적으로 이루어졌다면 migrations 디렉토리 아래 versions 아래 스키마로 이루어진 파일이 생성되었을 것이다.&lt;br /&gt;이 파일은 queue 형태로 버전이 서로 연결되는 구조를 갖게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 생성된 파일을 삭제하고 models.py 파일을 수정할 것이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Schema 적용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원본이 되는 Repository에서는 DB를 sqlite를 사용하였는데 여기서는 mysql을 사용하기로 했다.&lt;br /&gt;mysql에서는 sqlite를 사용할 때와 다르게 각 컬럼을 지정할 때 좀 더 세부적으로 옵션을 지정해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 코드를 보자&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;class Question(Base):
    __tablename__ = &quot;question&quot;

    id = Column(Integer, primary_key=True)
    subject = Column(String, nullable=False)
    content = Column(Text, nullable=False)
    create_date = Column(DateTime, nullable=False)
    user_id = Column(Integer, ForeignKey(&quot;user.id&quot;), nullable=True)
    user = relationship(&quot;User&quot;, backref=&quot;question_users&quot;)
    modify_date = Column(DateTime, nullable=True)
    voter = relationship('User', secondary=question_voter, backref='question_voters')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원본 models.py에서는 Column을 지정할 때 단순히 Integer, Text, String과 같은 형태로만 지정하였다. sqlite를 사용할 때는 이렇게만 지정하여도 무방하다.&lt;br /&gt;하지만 mysql을 사용할 때는 String의 경우 각 컬럼의 길이를 지정하여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Question 클래스는 다음과 같이 재정의하였다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;class Question(Base):
    __tablename__ = &quot;question&quot;

    id = Column(INTEGER(unsigned=True), primary_key=True)
    subject = Column(String(length=255), nullable=False)
    content = Column(Text, nullable=False)
    create_date = Column(DateTime, nullable=False)
    user_id = Column(INTEGER(unsigned=True), ForeignKey(&quot;user.id&quot;), nullable=True)
    user = relationship(&quot;User&quot;, backref=&quot;question_users&quot;)
    modify_date = Column(DateTime, nullable=True)
    voter = relationship('User', secondary=question_voter, backref='question_voters')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;String 경우는 length를 지정하여 mysql에서 생성할 때 이 길이를 따르도록 하였다.&lt;br /&gt;더불어 Integer를 선언할 때도 unsigned를 True로 선언하여서 양수만 사용하도록 지정하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 models.py을 mysql에서 사용할 수 있도록 재정의한 코드이다.&lt;br /&gt;참고하면 된다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from sqlalchemy import Column, String, Text, DateTime, ForeignKey, Table
from sqlalchemy.dialects.mysql import INTEGER
from sqlalchemy.orm import relationship

from database import Base

question_voter = Table(
    'question_voter',
    Base.metadata,
    Column('user_id', INTEGER(unsigned=True), ForeignKey('user.id'), primary_key=True),
    Column('question_id', INTEGER(unsigned=True), ForeignKey('question.id'), primary_key=True)
)

answer_voter = Table(
    'answer_voter',
    Base.metadata,
    Column('user_id', INTEGER(unsigned=True), ForeignKey('user.id'), primary_key=True),
    Column('answer_id', INTEGER(unsigned=True), ForeignKey('answer.id'), primary_key=True)
)


class Question(Base):
    __tablename__ = &quot;question&quot;

    id = Column(INTEGER(unsigned=True), primary_key=True)
    subject = Column(String(length=255), nullable=False)
    content = Column(Text, nullable=False)
    create_date = Column(DateTime, nullable=False)
    user_id = Column(INTEGER(unsigned=True), ForeignKey(&quot;user.id&quot;), nullable=True)
    user = relationship(&quot;User&quot;, backref=&quot;question_users&quot;)
    modify_date = Column(DateTime, nullable=True)
    voter = relationship('User', secondary=question_voter, backref='question_voters')


class Answer(Base):
    __tablename__ = &quot;answer&quot;

    id = Column(INTEGER(unsigned=True), primary_key=True)
    content = Column(Text, nullable=False)
    create_date = Column(DateTime, nullable=False)
    question_id = Column(INTEGER(unsigned=True), ForeignKey(&quot;question.id&quot;))
    question = relationship(&quot;Question&quot;, backref=&quot;answers&quot;)
    user_id = Column(INTEGER(unsigned=True), ForeignKey(&quot;user.id&quot;), nullable=True)
    user = relationship(&quot;User&quot;, backref=&quot;answer_users&quot;)
    modify_date = Column(DateTime, nullable=True)
    voter = relationship('User', secondary=answer_voter, backref='answer_voters')


class User(Base):
    __tablename__ = &quot;user&quot;

    id = Column(INTEGER(unsigned=True), primary_key=True)
    username = Column(String(length=30), unique=True, nullable=False)
    password = Column(String(length=100), nullable=False)
    email = Column(String(length=150), unique=True, nullable=False)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 내용을 그대로 참조해서 생성하여도 되고 각자 다른 길이를 갖도록 추가적으로 수정하여도 무방하다.&lt;br /&gt;mysql에서 사용할 수 있는 옵션으로 생성하면 된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;테이블 생성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 model.py 파일을 재정의하였으니 실제로 DB에 테이블을 생성하면 된다.&lt;br /&gt;위에서 사용하였던 명령어를 다시 사용해보겠다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;alembic revision --autogenerate&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 명령어를 실행하고 나면 다시 migrations/versions 아래 파일이 새로 생성된다.&lt;br /&gt;새로 생성된 파일을 열어보면 처음에 생성되었던 구조와 약간 차이가 있는 것을 알 수 있다.&lt;br /&gt;이 구조는 mysql에서 사용하기 위한 구조이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이렇게 생성된 Table schema를 적용도록 하겠다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;alembic upgrade head&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 명령어를 입력하면 적용이 안된 파일부터 찾아서 자동으로 Table 수정 Schema가 적용된다.&lt;br /&gt;여기서는 version 파일이 하나만 있으면 해당 version이 적용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 내용이 정상적으로 동작하였으면 에러 없이 테이블이 생성된 것을 확인할 수 있을 것이다.&lt;br /&gt;이렇게 하고 나면 테이블 생성까지 마무리했고 실제로 서비스를 동작시키기 위한 진행을 하도록 하겠다.&lt;/p&gt;</description>
      <category>Programming/Project Log</category>
      <category>alembic</category>
      <category>FastAPI</category>
      <category>python</category>
      <category>sqlalchemy</category>
      <category>svelte</category>
      <category>게시판만들기</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/411</guid>
      <comments>https://minarae7.tistory.com/entry/Fastapi%EC%99%80-svelte%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EA%B2%8C%EC%8B%9C%ED%8C%90-%EA%B0%9C%EB%B0%9C-2#entry411comment</comments>
      <pubDate>Thu, 27 Jul 2023 22:36:26 +0900</pubDate>
    </item>
    <item>
      <title>Fastapi와 svelte를 이용한 게시판 개발 #1</title>
      <link>https://minarae7.tistory.com/entry/Fastapi%EC%99%80-svelte%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EA%B2%8C%EC%8B%9C%ED%8C%90-%EC%A0%9C%EC%9E%91-1</link>
      <description>&lt;h1&gt;초기화&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본 코드는 python 3.9 환경에서 구성되었다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로젝트 생성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트는 다음 코드로 생성한다.&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;poetry new fastapi-bbs&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 명령어를 입력하면 fastapi-bbs라는 디렉토리가 생성된다.&lt;br /&gt;poetry 라이브러리를 통해서 파이썬 패키지 의존성을 관리하도록 할 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 디렉토리 이름을 변경한다. 처음에 poetry 명령어로 디렉토리를 생성하면 하위에 같은 이름으로 디렉토리가 더 생성된다&lt;br /&gt;여기서는 fastapi-bbs로 생성하였기 때문에 fastapi-bbs 디렉토리 아래 fastapi-bbs라는 디렉토리가 생성된다.&lt;br /&gt;이 디렉토리 이름을 app으로 변경한다.&lt;br /&gt;그럼 디렉토리 구조가 아래와 같다.&lt;/p&gt;
&lt;pre class=&quot;gherkin&quot;&gt;&lt;code&gt;fastapi-bbs
|- app
| |- __init__.py
|- tests
| |- __init__.py
|- poetry.lock
|- pyproject.toml
|- README.md
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소스코드 클론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 코드를 가져오자. 이 프로젝트의 기본이 되는 코드는 아래 Repository이다.&lt;/p&gt;
&lt;figure id=&quot;og_1690464549238&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - pahkey/fastapi-book&quot; data-og-description=&quot;Contribute to pahkey/fastapi-book development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/pahkey/fastapi-book&quot; data-og-url=&quot;https://github.com/pahkey/fastapi-book&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://github.com/pahkey/fastapi-book&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/pahkey/fastapi-book&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - pahkey/fastapi-book&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to pahkey/fastapi-book development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;해당 프로젝트는 fastapi 기반의 Backend에 Svelte 기반의 Frontend를 얻어서 개발한 프로젝트이다.&lt;br /&gt;우선 적당한 위치에서 해당 Repository를 클론한다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;git clone git@github.com:pahkey/fastapi-book.git&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;clone으로 가져온 코드를 지금 생성한 프로젝트 내에 적당한 위치로 복사하여서 이동한다.&lt;br /&gt;기존 디렉토리에서 이 프로젝트 내에 어떤 위치로 이동할 것인지 아래 표로 정리한다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;기존 위치&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;이동할 위치&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;domain&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;app/domain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;frontend&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;frontend&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;.gitignore&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;.gitignore&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;main.py&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;app/main.py&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;database.py&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;database.py&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;models.py&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;models.py&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이 표에서 정리한 파일 이외의 파일은 복사하지 않고 새로 생성하여서 사용한다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;cd fastapi-book
cp -R domain ../fastapi-bbs/app/
cp -R frontend ../fastapi-bbs/
cp .gitignore ../fastapi-bbs/
cp main.py ../fastapi-bbs/app/
cp database.py ../fastapi-bbs/
cp models.py ../fastapi-bbs/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 기본이 되는 코드는 가져온 셈이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;의존성 패키지 설치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 이제 프로젝트 디렉토리로 이동해서 사용할 패키지들을 설치한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;python&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음의 명령어를 통해서 fastapi-bbs 디렉토리로 이동하고 프로젝트에 필요한 패키지를 설치한다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;cd fastapi-bbs
poetry add fastapi sqlalchemy &quot;uvicorn[standard]&quot; # 웹 서버 구동에 기본이 되는 패키지
poetry add pymysql alembic &quot;pydantic[email]=^1.10.9&quot; python-multipart aiomysql passlib bcrypt &quot;python-jose[cryptography]&quot; # 가져올 프로젝트 코드에서 사용할 패키지 리스트&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;svelte&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 frontend에서 사용할 node용 패키지를 설치한다.&lt;br /&gt;여기서는 node 자체를 설치하는 방법은 생략한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;cd frontend
npm install&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;node에서 필요한 패키지에 대해서는 구동할 수 있도록 정리가 잘 되어 있어서 그대로 사용하면 된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;config&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 설정 파일을 생성한다. 설정 파일은 .env로 생성할 것인데 이 프로젝트에서는 config 디렉토리를 생성하고 그 아래에 설정 파일을 두도록 할 것이다.&lt;br /&gt;이렇게 하는 이유는 본 코드를 이후에 도커를 통해서 배포 및 운영하는데 배포할 때마다 설정 파일을 생성하기 보다 호스트에서 config 디렉토리를 관리하고 도커에서는 해당 디렉토리를 공유하는 방식으로 운영을 할 예정이기 때문이다.&lt;br /&gt;이 프로젝트에서 우선 사용할 .env 항목은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;ACCESS_TOKEN_EXPIRE_MINUTES=60
SECRET_KEY=xxxxxxxxxxxxxx
DB_USER=test
DB_PW=database_password
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=fastapi_book&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 프로젝트에서는 Database를 mysql로 사용한다. 원래 가져온 코드에서는 DB를 sqlite로 사용하였으나 장기적으로 DB를 운영하기에는 mysql이 더 적합해보이기 때문에 mysql를 사용하기로 한다.&lt;br /&gt;위와 같이 접속 정보를 입력하고 저장한다.&lt;br /&gt;다시 말하지만 .env의 경로는 &lt;b&gt;config/.env&lt;/b&gt;이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;config 파일을 생성하였으니 config 파일을 읽어오는 코드에서 경로를 변경해주어야 한다.&lt;br /&gt;&lt;b&gt;app/domain/user/user_router.py&lt;/b&gt; 파일에서 로그인에 필요한 정보를 가져오기 위해서 config 파일을 읽어온다.&lt;br /&gt;이 파일을 열어서 15 라인을 다음과 같이 수정한다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;config = Config('config/./env')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 코드를 수정하면 위에서 설정한 .env 파일의 내용을 가져올 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Database 연결&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 DB는 mysql을 사용한다고 하였고 설정 파일에서 DB에 접속하기 위한 내용을 추가하였다.&lt;br /&gt;복사해온 코드에서는 언급한 바와 같이 sqlite를 사용하기 때문에 DB와 관련된 내용은 다소 수정을 하여야 한다.&lt;br /&gt;그 중에서 &lt;b&gt;database.py&lt;/b&gt; 파일은 DB에 접속하기 위한 코드가 있는데 이 내용으로 교체한다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import sessionmaker, Session
from starlette.config import Config

config = Config('config/.env')
DB_USER = config('DB_USER')
DB_PW = config('DB_PW')
DB_HOST = config('DB_HOST')
DB_PORT = config('DB_PORT')
DB_DATABASE = config('DB_DATABASE')

ASYNC_DB_URL = f&quot;mysql+aiomysql://{DB_USER}:{DB_PW}@{DB_HOST}:{DB_PORT}/{DB_DATABASE}&quot;
DB_URL = f&quot;mysql+pymysql://{DB_USER}:{DB_PW}@{DB_HOST}:{DB_PORT}/{DB_DATABASE}?charset=utf8&quot;

# SQLAlchemy 엔진 생성
engine = create_engine(DB_URL)
async_engine = create_async_engine(ASYNC_DB_URL, echo=True, pool_pre_ping=True)
session = sessionmaker(autocommit=False, autoflush=False, bind=engine)
async_session = sessionmaker(async_engine, class_=AsyncSession, expire_on_commit=False)

Base = declarative_base()

# 의존성 함수로 사용할 get_db 함수 정의
def get_db() -&amp;gt; Session:
    try:
        # 세션 생성
        db = session()
        # 생성한 세션을 제공
        yield db
    finally:
        # 세션 종료
        db.close()

async def get_async_db() -&amp;gt; AsyncSession:
    async with async_session() as session:
        try:
            yield session
        except SQLAlchemyError as e:
            await session.rollback()
            raise e
        finally:
            await session.close()
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB에 접속하는 함수를 두 가지로 작성하였다. 하나는 동기 방식이고 다른 하나는 비동기 방식이다.&lt;br /&gt;기존의 게시판 코드가 이렇게 두 가지로 되어있기 때문에 우선 여기서도 두 가지로 작성하여서 사용한다.&lt;br /&gt;추후에는 DB를 사용하는 코드는 모두 비동기로 동작하도록 바꿀 것이다.&lt;br /&gt;하지만 초기 설정에서는 일단 코드가 동작되게 하는데 초점을 두고 있기 때문에 두 개의 함수로 구성하도록 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;python 코드 호환성 수정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 python에서 사용하는 코드의 호환성을 맞추기 위해서 코드를 약간 수정하여야 한다.&lt;br /&gt;처음에 언급한 것과 같이 해당 코드는 python 3.9에서 개발하는데 복사해온 코드는 python 3.10 에서 작성하여서 3.9에서 사용할 수 없는 문법이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;python 3.10에서는 schema를 선언할 때 | 를 사용할 수 있는데 3.9에서는 허용하지 않는다.&lt;br /&gt;app/domain/answer/answer_shcema.py를 열어보면 다음과 같이 작성되어 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;...
class Answer(BaseModel):
    id: int
    content: str
    create_date: datetime.datetime
    user: User | None
    question_id: int
    modify_date: datetime.datetime | None = None
    voter: list[User] = []

    class Config:
        orm_mode = True
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 |를 사용하는 문법은 3.9에서 사용할 수 없기 때문에 다음과 같이 수정한다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;# 생략
from typing import Optional

class Answer(BaseModel):
    id: int
    content: str
    create_date: datetime.datetime
    user: Optional[User]
    question_id: int
    modify_date: Optional[datetime.datetime] = None
    voter: list[User] = []

    class Config:
        orm_mode = True
# 생략&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 작성하면 3.9에서 정상작동하게 된다.&lt;br /&gt;&lt;b&gt;app/domain/question/question_schema.py&lt;/b&gt;도 수정해준다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;# 생략
from typing import Optional

class Question(BaseModel):
    id: int
    subject: str
    content: str
    create_date: datetime.datetime
    answers: list[Answer] = []
    user: Optional[User]
    modify_date: Optional[datetime.datetime] = None
    voter: list[User] = []

    class Config:
        orm_mode = True
# 생략&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 서비스를 정상적으로 구동하기 위한 준비가 마무리 되었다.&lt;br /&gt;하지만 서비스가 올라오기만 할 뿐 아직 테이블 생성을 하지 않았기 때문에 어떤 동작도 하지 않는다.&lt;br /&gt;DatabaseSetup 에서 테이블에 관련된 내용을 정리한다.&lt;/p&gt;</description>
      <category>Programming/Project Log</category>
      <category>FastAPI</category>
      <category>python</category>
      <category>svelte</category>
      <category>게시판만들기</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/410</guid>
      <comments>https://minarae7.tistory.com/entry/Fastapi%EC%99%80-svelte%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EA%B2%8C%EC%8B%9C%ED%8C%90-%EC%A0%9C%EC%9E%91-1#entry410comment</comments>
      <pubDate>Thu, 27 Jul 2023 22:28:33 +0900</pubDate>
    </item>
    <item>
      <title>숫자 자리수 콤마 찍기</title>
      <link>https://minarae7.tistory.com/entry/javascript-%EC%88%AB%EC%9E%90-%EC%9E%90%EB%A6%AC%EC%88%98-%EC%BD%A4%EB%A7%88-%EC%B0%8D%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;javascript에서 숫자를 표현할 때 3자리마다 ,를 찍어서 숫자를 인식하기 좋게 만들어주는 코드를 작성할 일이 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매번 검색해서 추가하기 귀찮아서 블로그에 포스팅해서 저장해둔다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1688304638459&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const numberWithCommas = (number) =&amp;gt; {
  if (!number) {
    return '-';
  }
  number = parseInt(number);
  return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, &quot;,&quot;);
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming/javascript</category>
      <category>javascript</category>
      <category>Reference</category>
      <category>레퍼런스</category>
      <category>콤마찍기</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/407</guid>
      <comments>https://minarae7.tistory.com/entry/javascript-%EC%88%AB%EC%9E%90-%EC%9E%90%EB%A6%AC%EC%88%98-%EC%BD%A4%EB%A7%88-%EC%B0%8D%EA%B8%B0#entry407comment</comments>
      <pubDate>Sun, 2 Jul 2023 22:33:06 +0900</pubDate>
    </item>
    <item>
      <title>[강남콩 키우기] 꽃피우기</title>
      <link>https://minarae7.tistory.com/entry/%EA%B0%95%EB%82%A8%EC%BD%A9-%ED%82%A4%EC%9A%B0%EA%B8%B0-%EA%BD%83%ED%94%BC%EC%9A%B0%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오랜 시간 관심을 갖지 못했더니 그새 강낭코에 꽃들이 피었다. 근데 그 모양이 꼭 콩 모양으로 생겼더라.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나만 그렇게 생각한게 아니고 아내도 같은 말을 하는걸 보니 정말 콩꽃은 콩처럼 생겼나보다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8m0qA/btsg20h7QG6/zjlM4j4ELDO4alpLU7qrTK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8m0qA/btsg20h7QG6/zjlM4j4ELDO4alpLU7qrTK/img.jpg&quot; data-alt=&quot;꽃이 핀 강남꽃&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8m0qA/btsg20h7QG6/zjlM4j4ELDO4alpLU7qrTK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8m0qA%2Fbtsg20h7QG6%2FzjlM4j4ELDO4alpLU7qrTK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;960&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;꽃이 핀 강남꽃&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그냥 음료수를 먹고 컵을 씻어서 콩을 심어서 키웠는데도 이렇게 잘 자라는걸 보니 신기할 따름이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 보면서 앞으로는 이런 일회용 컵들을 버리지 말고 이렇게 화분처럼 사용하는 것도 좋겠다는 생각이 든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 처음에는 강낭콩으로 시작하지만 상추를 심을 수도 있고 작은 꽃들을 심을 수도 있을 듯하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dvF9pB/btsg2kA53GD/YbGvxWK73eGFUK9VMMb54K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dvF9pB/btsg2kA53GD/YbGvxWK73eGFUK9VMMb54K/img.jpg&quot; data-alt=&quot;꽃이 핀 강낭콩&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dvF9pB/btsg2kA53GD/YbGvxWK73eGFUK9VMMb54K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdvF9pB%2Fbtsg2kA53GD%2FYbGvxWK73eGFUK9VMMb54K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;960&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;꽃이 핀 강낭콩&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요새는 텀블러를 많이 가지고 다녀서 일회용 컵이 나올 일이 많지 않지만 가끔 집에 생기면 흙은 많으니 담아서 뭐라도 더 키워봐야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 케일 씨가 있으니 케일부터 심어볼까 싶다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwYyEP/btsg26o2OuX/et5pxMDySGFkjbsJzPKHr1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwYyEP/btsg26o2OuX/et5pxMDySGFkjbsJzPKHr1/img.jpg&quot; data-alt=&quot;꽃대가 보이는 강낭콩&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwYyEP/btsg26o2OuX/et5pxMDySGFkjbsJzPKHr1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwYyEP%2Fbtsg26o2OuX%2Fet5pxMDySGFkjbsJzPKHr1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;960&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;꽃대가 보이는 강낭콩&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;꽃이 핀 곳도 있고 꽃대까지만 올라온 곳도 있다. 이렇게 세어보니 그래도 꽃이 핀 자리에 다 열린다고 하면 밥그릇으로 한공기는 열릴듯하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이거 다시 심어서 싹을 틔울까 했는데 장모님이 이거 맛도 별로라고 뭐하러 그렇게 하냐고 하신다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 뭘 심고 성과가 있을거 볼 때 기분이가 좋으니 천상 이런걸 좋아하는 듯하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;꽃이 피고 열매가 맺히고 하는 작물들을 보면서 힐링을 느낀다.&lt;/p&gt;</description>
      <category>My Story/식물재배기</category>
      <category>강남콩재배기</category>
      <category>배란다농사</category>
      <category>초등관찰일기</category>
      <category>컵재활용</category>
      <category>컵화분</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/406</guid>
      <comments>https://minarae7.tistory.com/entry/%EA%B0%95%EB%82%A8%EC%BD%A9-%ED%82%A4%EC%9A%B0%EA%B8%B0-%EA%BD%83%ED%94%BC%EC%9A%B0%EA%B8%B0#entry406comment</comments>
      <pubDate>Mon, 22 May 2023 21:49:14 +0900</pubDate>
    </item>
    <item>
      <title>[가계부 만들기] 지출입 기록 - 설계</title>
      <link>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EC%A7%80%EC%B6%9C%EC%9E%85-%EA%B8%B0%EB%A1%9D-%EC%84%A4%EA%B3%84</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;제일 많이 사용하고 제일 중요한 자료가 되는 것이 바로 지출입 자료일 것이다. 가계부에서 사실 이게 전부라고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어쨌든 근본적으로 이 기록을 남기기 위한 기본 설정을 지금까지 해왔다고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가계부를 만들기로 한 이유는 바로 형태가 마음에 딱 맞는 것이 없었기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어서 대형마트에 가서 장을 본다고 했을 때, 이를 세부적으로 분류해서 기록할 수가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마트에서 장을 보면 장남감을 살 수 있고, 식재료를 살 수 있고, 또는 생활용품을 구매할 수도 있는데 이를 세부적으로 기록할 수 있는 툴을 제공하는 프로그램이 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 일단은 엑셀로 정리해서 사용했는데 아무래도 파일로 기록하다보니 부족함을 느끼게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 일단 프로그램을 만들어서 사용해보기로 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;기록 형태&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 작성할 때 사용하던 엑셀은 다음 이미지와 같다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-05-18 오후 11.20.21.png&quot; data-origin-width=&quot;1724&quot; data-origin-height=&quot;122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUw17D/btsguiRUqRR/rpAw9f0pLewBCdI5l7ZZYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUw17D/btsguiRUqRR/rpAw9f0pLewBCdI5l7ZZYK/img.png&quot; data-alt=&quot;가계부 스크린샷&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUw17D/btsguiRUqRR/rpAw9f0pLewBCdI5l7ZZYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUw17D%2FbtsguiRUqRR%2FrpAw9f0pLewBCdI5l7ZZYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1724&quot; height=&quot;122&quot; data-filename=&quot;스크린샷 2023-05-18 오후 11.20.21.png&quot; data-origin-width=&quot;1724&quot; data-origin-height=&quot;122&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;가계부 스크린샷&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용처는 홈플러스이지만 세부적으로 분류하여서 기록하였다. 이와 같은 형태로 데이터를 주고 받으려면 JSON 형태가 적합할 것으로 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 이미지를 Frontend에서 전달할 때 우선 날짜와 사용처를 담은 객체에 세부 내역을 리스트로 담아서 전달하도록 할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블 관계는 다음 이미지를 참조하면 된다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-05-18 오후 11.23.35.png&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;1014&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccETyg/btsgpYG2eZU/YOw8VIrQklNC1Z2US1G5DK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccETyg/btsgpYG2eZU/YOw8VIrQklNC1Z2US1G5DK/img.png&quot; data-alt=&quot;테이블 관계도&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccETyg/btsgpYG2eZU/YOw8VIrQklNC1Z2US1G5DK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FccETyg%2FbtsgpYG2eZU%2FYOw8VIrQklNC1Z2US1G5DK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;334&quot; height=&quot;515&quot; data-filename=&quot;스크린샷 2023-05-18 오후 11.23.35.png&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;1014&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;테이블 관계도&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; 위의 이미지를 Frontend에서 위의 내용을 Json으로 전달한다면 같이 생성해서 전달할 것이다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1684420268604&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;std_date&quot;: &quot;2023-04-15&quot;,
  &quot;opponent_name&quot;: &quot;홈플러스&quot;,
  &quot;items&quot;: [
    {
      &quot;detail_contents&quot;: &quot;해남고구마2kg&quot;,
      &quot;amounts&quot;: 10990,
      &quot;io_type&quot;: &quot;O&quot;,
      &quot;category_no&quot;: 23,
      &quot;important&quot;: 3,
      &quot;is_fixed_cost&quot;: &quot;X&quot;
    },
    {
      &quot;detail_contents&quot;: &quot;성주참외_1.2kg&quot;,
      &quot;amounts&quot;: 8990,
      &quot;io_type&quot;: &quot;O&quot;,
      &quot;category_no&quot;: 25,
      &quot;important&quot;: 2,
      &quot;is_fixed_cost&quot;: &quot;X&quot;
    },
    {
      &quot;detail_contents&quot;: &quot;청양고추_150g&quot;,
      &quot;amounts&quot;: 1990,
      &quot;io_type&quot;: &quot;O&quot;,
      &quot;category_no&quot;: 23,
      &quot;important&quot;: 2,
      &quot;is_fixed_cost&quot;: &quot;X&quot;
    }
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Schema 생성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 위의 내용을 기준으로 전달받고 결과로 전달할 스키마에 대한 정의를 하도록 한다. 기록과 수정에서 받는 정보는 크게 다르지 않다. 차이가는 index로 사용하는 번호가 있고 없고 차이일 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 생성과 수정은 같은 스키마를 사용하고 번호를 Optional로 사용하도록 하였다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1684504047371&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class LogDetailUpsert(BaseModel):
    log_detail_no: Optional[int] = Field(title=&quot;사용재역번호&quot;)
    detail_contents: str = Field(title=&quot;상세내역정보&quot;)
    amounts: int = Field(title=&quot;금액&quot;)
    io_type: Literal[&quot;I&quot;, &quot;O&quot;] = Field(title=&quot;수입/지출 구분(I: 수입, O: 지출)&quot;)
    category_no: Optional[int] = Field(title=&quot;카테고리 번호&quot;)
    important: Optional[int] = Field(title=&quot;중요도&quot;)
    is_fiexed_cost: Literal[&quot;T&quot;, &quot;F&quot;] = Field(title=&quot;고정비여부(T|F)&quot;)

class LogDetail(LogDetailUpsert):
    account_log_no: int = Field(title=&quot;내역번호&quot;)

    class Config:
        orm_mode = True

class AccountUpsert(BaseModel):
    std_date: date = Field(title=&quot;날짜&quot;)
    opponent_name: str = Field(title=&quot;메인거래처&quot;)
    detail_list: List[LogDetailUpsert] = Field(title=&quot;세부내역&quot;)

class AccountLog(AccountUpsert):
    account_log_no: int = Field(title=&quot;내역번호&quot;)
    member_no: int = Field(title=&quot;사용자번호&quot;)

    class Config:
        orm_mode = True&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tb_account_log 테이블에서는 account_log_no를 upsert 클래스에 넣지 않았다. 이 값은 생성할 때는 auto_increment로 생성되고 수정할 때는 스키마에 추가되어서 전달되는 대신 URI에 포함되어서 전달받을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Response로 전달할 때만 사용할 것이기 때문에 Upsert에 넣는 대신 AccountLog 클래스에 추가하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 스키마 정리까지 해서 코드를 작성하기 위한 기본 작업을 마쳤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Router와 Service를 추가하여서 로직을 구현하면 된다. 다음 포스팅에서 해당 작업을 진행하도록 하겠다.&lt;/p&gt;</description>
      <category>Programming/Project Log</category>
      <category>backend</category>
      <category>FastAPI</category>
      <category>python</category>
      <category>가계부만들기</category>
      <category>개발기록</category>
      <category>개인프로젝트</category>
      <category>프로젝트로그</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/405</guid>
      <comments>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EC%A7%80%EC%B6%9C%EC%9E%85-%EA%B8%B0%EB%A1%9D-%EC%84%A4%EA%B3%84#entry405comment</comments>
      <pubDate>Fri, 19 May 2023 22:52:33 +0900</pubDate>
    </item>
    <item>
      <title>[가계부 만들기] Category 생성/수정/삭제</title>
      <link>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-Category-%EC%83%9D%EC%84%B1%EC%88%98%EC%A0%95%EC%82%AD%EC%A0%9C</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전 포스팅에서 카테고리에 관련된 기능을 구현하기 위해 설계한 구조와 리스트를 가져오는 내용을 정리하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 생성/수정/삭제와 관련된 기능을 기록하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 생성/수정/삭제 기능은 이전에 회원 정보를 관리할 때 사용한 기능과 거의 동일하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블이 바뀌니 모델이 달라지고 패스워드가 없으니 암호화되는 과정이 생략되는 정도로 생각하면 된다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;카테고리 생성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 생성하는 코드를 먼저 구현한다. 이전과 동일하게 로직은 services 아래 category_service.py에서 구현하고 라우팅만 연결하도록 할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로직을 먼저 구현한다. 코드는 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1683988442961&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 카테고리 생성
async def create_category(
    db: AsyncSession,
    member_no: int,
    category: schemas.CategoryUpsert,
):
    db_category = models.Category(**category.dict(), member_no=member_no, has_children='F')
    db.add(db_category)
    await db.commit()
    await db.refresh(db_category)

    return db_category&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드는 매우 간단하다. 회원 정보와 같이 중복 아이디를 검사할 필요도 없고 패스워드를 암호화할 필요도 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그냥 들어온 정보를 테이블에 삽입하면 된다. 여기서는 ORM을 사용하고 있으니 add 함수로 처리하고 commit만 해주면 구현이 끝난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 router 코드를 생성하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;routers/category.py 파일에 아래 내용을 구현하였다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1683988576538&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 카테고리 생성
@router.post(&quot;/create&quot;, description=&quot;카테고리 생성&quot;, response_class=Response, responses={
    HTTP_400_BAD_REQUEST: {
        &quot;model&quot;: schemas.Message
    }
})
async def create(
    user_info: schemas.JWTPayload = Depends(decode_access_token),
    category: schemas.CategoryUpsert = Body(
        title=&quot;카테고리 정보&quot;,
        example={
            &quot;category_name&quot;: &quot;테스트&quot;,
            &quot;parent_no&quot;: None,
            &quot;class_name&quot;: None,
            &quot;inout_type&quot;: &quot;O&quot;,
        }
    ),
    db: AsyncSession = Depends(get_db)
):
    try:
        result = await category_service.create_category(db, user_info['member_no'], category)

        return Response(status_code=HTTP_201_CREATED)
    except Exception as e:
        return JSONResponse(content={&quot;detail&quot;: e.args[0]}, status_code=HTTP_400_BAD_REQUEST)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 코드는 5줄인데 함수를 선언하는 부분을 엄청 자세히 기재하였다. request 샘플을 기재하고 파라미터 내용과 response로 어떻게 전달하는지 내용을 자세히 기록하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 코드에서 자세히 기록해 두면 fastapi에서는 별도의 api 문서를 만들 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 이미지를 보자.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-05-13 오후 11.38.20.png&quot; data-origin-width=&quot;1882&quot; data-origin-height=&quot;988&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DYol5/btsfcN7mu7Y/etLW3LS8pxVH712jkCSQ9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DYol5/btsfcN7mu7Y/etLW3LS8pxVH712jkCSQ9k/img.png&quot; data-alt=&quot;카테고리 생성 redoc 내용&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DYol5/btsfcN7mu7Y/etLW3LS8pxVH712jkCSQ9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDYol5%2FbtsfcN7mu7Y%2FetLW3LS8pxVH712jkCSQ9k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1882&quot; height=&quot;988&quot; data-filename=&quot;스크린샷 2023-05-13 오후 11.38.20.png&quot; data-origin-width=&quot;1882&quot; data-origin-height=&quot;988&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;카테고리 생성 redoc 내용&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드에서 기록한 내용이 그대로 Redoc 페이지로 표현된다. 추가적으로 작업할 내용도 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 외부에 전달할 때는 이 주소만 전달하면 되기 때문에 매우 간편하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자는 기본적으로 문서를 만드는데 스트레스를 많이 받는데 이렇게 코드에서 다 해결할 수 있다면 매우 편리하다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Category 수정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카테고리를 수정하는 코드를 먼저 보고 내용을 설명하도록 하겠다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1683988929374&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 카테고리 수정
async def modify_category(
    db: AsyncSession,
    member_no: int,
    category_no: int,
    category: schemas.CategoryUpsert,
):
    result = await db.execute(
        select(models.Category).filter(
            models.Category.category_no == category_no,
            models.Category.is_deleted == 'F',
        )
    )
    db_category = result.scalars().first()

    if db_category is None:
        raise Exception('카테고리 정보를 찾을 수 없습니다.')

    if db_category.member_no != member_no:
        raise Exception('카테고리 수정에 대한 권한이 없습니다')

    category_info =  category.dict()
    category = {k: v for k, v in category_info.items()}
    for key, value in category.items():
        if value is None:
            continue

        setattr(db_category, key, value)

    await db.commit()
    return db_category&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성과 동일하게 회원 정보 수정과 내용이 거의 비슷하다. 먼저 수정하고자 하는 카테고리가 존재하는지 확인하고 해당 카테고리가 JWT에서 꺼낸 member_no에게 소유권이 있는지 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 각 항목을 비교해서 수정된 항목만 업데이트하고 commit 하면 작업이 끝난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 router를 구현한다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1683989060434&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 카테고리 항목 수정
@router.put(&quot;/modify/{category_no}&quot;, description=&quot;카테고리 정보 수정&quot;, response_class=Response, responses={
    HTTP_400_BAD_REQUEST: {
        &quot;model&quot;: schemas.Message
    }
})
async def modify(
    user_info: schemas.JWTPayload = Depends(decode_access_token),
    category_no: int = Query(title=&quot;카테고리번호&quot;),
    category: schemas.CategoryUpsert = Body(
        title=&quot;카테고리 정보&quot;,
        example={
            &quot;category_name&quot;: &quot;테스트&quot;,
            &quot;parent_no&quot;: None,
            &quot;class_name&quot;: None,
            &quot;inout_type&quot;: &quot;O&quot;,
        }
    ),
    db: AsyncSession = Depends(get_db)
):
    try:
        result = await category_service.modify_category(db, user_info['member_no'], category_no, category)

        return Response(status_code=HTTP_200_OK)
    except Exception as e:
        return JSONResponse(content={&quot;detail&quot;: e.args[0]}, status_code=HTTP_400_BAD_REQUEST)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Router를 선언할 때 URL 주소에 category_no를 query로 전달받도록 하였다. 이 내용에 대한 상세한 설명은 Fastapi 튜토리얼 내용을 참조하면 된다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;figure id=&quot;og_1683989257872&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;경로 매개변수 - FastAPI&quot; data-og-description=&quot;경로 매개변수 파이썬 포맷 문자열이 사용하는 동일한 문법으로 &amp;quot;매개변수&amp;quot; 또는 &amp;quot;변수&amp;quot;를 경로에 선언할 수 있습니다: from fastapi import FastAPI app = FastAPI() @app.get(&amp;quot;/items/{item_id}&amp;quot;) async def read_item(item_&quot; data-og-host=&quot;fastapi.tiangolo.com&quot; data-og-source-url=&quot;https://fastapi.tiangolo.com/ko/tutorial/path-params/&quot; data-og-url=&quot;https://fastapi.tiangolo.com/ko/tutorial/path-params/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/z22U1/hySBxRFFRK/5Ogb1B3t7m5lAwZ5UIdXrK/img.png?width=1199&amp;amp;height=1080&amp;amp;face=0_0_1199_1080,https://scrap.kakaocdn.net/dn/bKfgsc/hySButTXWa/5dLSMtOFQd50jNLB4VcK91/img.png?width=1199&amp;amp;height=1080&amp;amp;face=0_0_1199_1080,https://scrap.kakaocdn.net/dn/brtbXn/hySBzPuhBf/ZNHT9RJbvSvtdhNkBUsAgk/img.png?width=729&amp;amp;height=1080&amp;amp;face=0_0_729_1080&quot;&gt;&lt;a href=&quot;https://fastapi.tiangolo.com/ko/tutorial/path-params/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://fastapi.tiangolo.com/ko/tutorial/path-params/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/z22U1/hySBxRFFRK/5Ogb1B3t7m5lAwZ5UIdXrK/img.png?width=1199&amp;amp;height=1080&amp;amp;face=0_0_1199_1080,https://scrap.kakaocdn.net/dn/bKfgsc/hySButTXWa/5dLSMtOFQd50jNLB4VcK91/img.png?width=1199&amp;amp;height=1080&amp;amp;face=0_0_1199_1080,https://scrap.kakaocdn.net/dn/brtbXn/hySBzPuhBf/ZNHT9RJbvSvtdhNkBUsAgk/img.png?width=729&amp;amp;height=1080&amp;amp;face=0_0_729_1080');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;경로 매개변수 - FastAPI&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;경로 매개변수 파이썬 포맷 문자열이 사용하는 동일한 문법으로 &quot;매개변수&quot; 또는 &quot;변수&quot;를 경로에 선언할 수 있습니다: from fastapi import FastAPI app = FastAPI() @app.get(&quot;/items/{item_id}&quot;) async def read_item(item_&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;fastapi.tiangolo.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성할 때 router에서는 post로 주소를 전달받았는데 수정할 때는 put으로 http method를 전달받았다. 보통 데이터에 대한 접근할 때 RestAPI로 구현하면 생성은 post, 수정은 put, 정보를 가져오는 것은 get, 삭제할 때는 delete를 사용하게 된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;카테고리 삭제&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 카테고리를 삭제하는 내용을 작성한다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1683989420891&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 카테고리 삭제
async def delete_category(
    db: AsyncSession,
    member_no: int,
    category_no: int,
):
    # 카테고리가 존재하는지 확인
    result = await db.execute(
        select(models.Category).filter(
            models.Category.category_no == category_no,
            models.Category.is_deleted == 'F',
        )
    )
    db_category = result.scalars().first()

    if db_category is None:
        raise Exception(&quot;카테고리 정보를 찾을 수 없습니다.&quot;)

    if db_category.member_no != member_no:
        raise Exception('카테고리 수정에 대한 권한이 없습니다')

    setattr(db_category, 'is_deleted', 'T')
    setattr(db_category, 'del_dt', func.now())

    await db.commit()
    return db_category&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 프로젝트에서는 삭제가 발생했을 때 실제로 내용을 테이블에서 delete 처리하지 않고 삭제 플래그만 T로 바꾸어서 삭제되었다는 것을 표시하도록 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 처리하는 이유는 회원 탈퇴를 처리할 때 정리하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 가계부의 내용을 기록한 것이 없기 때문에 삭제처리할 때는 그냥 카테고리만 삭제하도록 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가계부의 내용을 기록하게 되면 카테고리가 삭제될 때 해당 카테고리로 되어있는 내용에서 카테고리를 null로 바꿔주는 작업을 해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 하지 않으면 해당 내용은 삭제하지 않았는데 조회되지 않게 된다. 이 내용은 이후에 따로 추가하도록 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;router에서는 다음과 같이 구현하였다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1683989619698&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@router.delete(&quot;/delete/{category_no}&quot;, description=&quot;카테고리 삭제&quot;, response_class=Response, responses={
    HTTP_400_BAD_REQUEST: {
        &quot;model&quot;: schemas.Message
    }
})
async def delete(
    user_info: schemas.JWTPayload = Depends(decode_access_token),
    category_no: int = Query(title=&quot;카테고리 번호&quot;),
    db: AsyncSession = Depends(get_db)
):
    try:
        result = await category_service.delete_category(db, user_info['member_no'], category_no)

        return Response(status_code=HTTP_200_OK)
    except Exception as e:
        return JSONResponse(content={&quot;detail&quot;: e.args[0]}, status_code=HTTP_400_BAD_REQUEST)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 내용에서는 그렇게 어려운 내용이 없고 앞선 내용의 반복이어서 추가 설명은 생략하도록 한다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 해서 기본적인 설정에 대한 내용은 다 정리하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 가계부 내역을 기록하는 코드를 구현하고 frontend를 구성하도록 하겠다.&lt;/p&gt;</description>
      <category>Programming/Project Log</category>
      <category>FastAPI</category>
      <category>python</category>
      <category>가계부만들기</category>
      <category>개인프로젝트</category>
      <category>파이썬</category>
      <category>프로젝트 로그</category>
      <category>프로젝트기록</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/404</guid>
      <comments>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-Category-%EC%83%9D%EC%84%B1%EC%88%98%EC%A0%95%EC%82%AD%EC%A0%9C#entry404comment</comments>
      <pubDate>Sun, 14 May 2023 00:02:53 +0900</pubDate>
    </item>
    <item>
      <title>[가계부 만들기] 카테고리 설정 - 구조/리스트조회</title>
      <link>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EC%B9%B4%ED%85%8C%EA%B3%A0%EB%A6%AC-%EA%B5%AC%EC%A1%B0-%EB%A6%AC%EC%8A%A4%ED%8A%B8</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;가계부에서 수입/지출 항목을 입력하는 기능을 만들기 전에 먼저 카테고리 설정에 관련된 기능을 구현하고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 수입/지출 내역은 카테고리로 정리할 수 있을 것이다. 물론 카테고리 항목이 비어있을 수도 있겠지만 카테고리를 정리해두면 추후에 통계 자료를 만들 때 활용할 수 있는 여지가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다년간 개발을 하다보니 원 데이터가 상세하면 이를 가공해서 만들 수 있는 통계도 훨씬 다양하게 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 내용에서 회원 가입시 기본 카테고리를 자동으로 생성해주는 코드를 붙여두었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적인 카테고리 정보가 있으니 먼저 카테고리 리스틑 가져오는 것부터 시작해서 카테고리를 새로 생성하고, 수정하고, 삭제하는 기능을 구현하도록 할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카테고리는 하나의 카테고리 정보가 때어내어서 관리하는 일보다는 리스트로 전체를 조회해서 수정하고 삭제하는 기능을 구현하게 될 것이다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Category 기본 구조&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; 먼저 카테고리 관련 기능을 구현하기 전에 필요한 파일을 만들 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 포스팅에서 services/category_service.py 파일을 만들어 두었으니 router 파일을 생성하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;routers/category.py 파일을 생성하고 다음과 같이 입력한다.&lt;/p&gt;
&lt;pre id=&quot;code_1683811601936&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from fastapi import APIRouter, Depends
from ..libraries.auth import decode_access_token

router = APIRouter(
    prefix=&quot;/category&quot;,
    tags=[&quot;category&quot;],
    dependencies=[Depends(decode_access_token)],
    responses={
        404: {&quot;description&quot;: &quot;Not Found&quot;},
    }
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적인 router 정보를 입력하였다. 이 구조는 이전 routers/member.py과 유사하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 member.py에서는 로그인을 하지 않은 상태에서 접근하는 경우가 있기 때문에 dependencies를 router를 선언할 때 지정하지 않았다. 하지만 그 외의 router는 기본적으로 로그인을 했다는 것을 가정하고 개발하기 때문에 router를 선언할 때 dependencies를 지정하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선언한 category.py의 라우터를 main에 등록해주어야 한다. main.py 파일을 열어서 아래와 같이 작성한다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1683811867252&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...생략...
from .routers import members, category


app = FastAPI(title=&quot;account-book-api&quot;)

app.include_router(members.router)
app.include_router(category.router)

...생략...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터를 등록하였으니 category.py에서 정의하는 router 정보를 이제 사용할 수 있게 되었다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Schema 정의&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 포스팅에서 models.py에서 category 테이블을 아래와 같이 정의하였다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1683811988316&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Category(Base):
    __tablename__ = &quot;tb_category&quot;

    category_no = Column(BIGINT(unsigned=True), nullable=False, autoincrement=True, comment=&quot;카테고리 번호&quot;)
    member_no = Column(SMALLINT(unsigned=True), nullable=True, comment=&quot;카테고리 생성자 번호(기본 카테고리일 경우 null)&quot;)
    category_name = Column(VARCHAR(length=50), nullable=False, comment=&quot;카테고리 이름&quot;)
    has_children = Column(CHAR(length=1), nullable=False, default=&quot;F&quot;, comment=&quot;자식을 가지고 있는지 여부(T|F)&quot;)
    inout_type = Column(CHAR(length=1), nullable=False, default=&quot;O&quot;, comment=&quot;수입/지출 구분(I: incomes, O: outgoings)&quot;)
    parent_no = Column(BIGINT(unsigned=True), nullable=True, comment=&quot;부모 카테고리 번호&quot;)
    class_name = Column(VARCHAR(length=30), nullable=True, comment=&quot;아이콘 클래스&quot;)
    sort_order = Column(TINYINT(unsigned=True), default=1, comment=&quot;정렬순서&quot;)
    reg_dt = Column(DATETIME(timezone=False), nullable=False, server_default=func.now(), comment=&quot;생성일시&quot;)
    upd_dt = Column(DATETIME(timezone=False), nullable=False, server_default=func.now(), onupdate=func.now(), comment=&quot;수정일시&quot;)
    is_deleted = Column(CHAR(length=1), default=&quot;F&quot;, server_default=&quot;F&quot;, nullable=False, comment=&quot;삭제여부(T|F)&quot;)
    del_dt = Column(DATETIME(timezone=False), nullable=True, comment=&quot;삭제일시&quot;)

    __table_args__ = (
        PrimaryKeyConstraint(category_no, name=&quot;pk_category&quot;),
        ForeignKeyConstraint(
            [&quot;member_no&quot;],
            [&quot;tb_members.member_no&quot;],
            name=&quot;fk_category__member_no&quot;,
            onupdate=&quot;NO ACTION&quot;,
            ondelete=&quot;NO ACTION&quot;,
        ),
        {
            &quot;comment&quot;: &quot;카테고리 정보&quot;
        }
    )&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 여기에 맞는 Schema를 정의할 것이다. 다른 내용은 생략하고 category 관련된 내용만 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;database/schemas.py 파일을 열어서 다음 내용을 추가한다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1683812080861&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class CategoryUpsert(BaseModel):
    category_name: str = Field(title=&quot;카테고리 이름&quot;)
    parent_no: Optional[int] = Field(title=&quot;부모 카테고리 번호&quot;, default=None)
    class_name: Optional[str] = Field(title=&quot;아이콘 정보&quot;, default=None)
    inout_type: Literal['I', 'O'] = Field(title=&quot;수입/지출구분&quot;)

class CategoryInfo(CategoryUpsert):
    category_no: int = Field(title=&quot;카테고리 번호&quot;)
    member_no: Optional[int] = Field(title=&quot;소유자 회원 번호&quot;)
    sort_order: int = Field(title=&quot;정렬순서&quot;)

    class Config:
        orm_mode = True&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 CategoryUpsert 클래스를 먼저 정의하고 CategoryInfo 테이블은 CategoryUpsert 테이블을 상속받아서 정의하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CategoryUpsert 클래스는 카테고리를 생성하거나 수정할 때 관련 카테고리 정보를 전달받는 request 형태를 정의하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카테고리를 생성하거나 수정할 때 사용자에게 받는 정보는 동일하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CategoryInfo 클래스는 사용자가 조희한 Category 관련 정보를 전달하는 형태를 정의한 것이다. 실제로 CategoryInfo 클래스가 Category 테이블과 연결된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Category 리스트 조회&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 리스트를 조회하는 기능을 구현하도록 하겠다. 우선 service에서 기능을 구현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;services/category_service.py 파일에 아래 내용을 구현하였다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1683812745478&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from typing import Optional, List, Literal
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from ..database import schemas, models

# 카테고리 리스트 조회
async def get_category_list(
    db: AsyncSession,
    member_no: int,
    inout_type: Literal['I', 'O'],
    parent_category_no: Optional[int]
) -&amp;gt; List[schemas.CategoryInfo]:
    stmt = select(models.Category).filter(
        models.Category.member_no == member_no,
        models.Category.inout_type == inout_type,
        models.Category.is_deleted == 'F'
    ).order_by(models.Category.sort_order.asc(), models.Category.category_no.asc())
    if parent_category_no is None:
        stmt = stmt.filter(models.Category.parent_no.is_(None))
    else:
        stmt = stmt.filter(models.Category.parent_no == parent_category_no)

    results = await db.execute(stmt)
    return results.scalars().all()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파라미터를 정의할 때 Literal과 Optional 타입을 사용하였다. Literal은 다음에 오는 리스트 안에 있는 값만을 변수에 담을 수 있으며 그외의 값이 들어오면 Exception이 발생하도록 한다. Optional은 Union[..., None]으로 치환된다. 실제 Optional 내부가 Union으로 치환되는 코드로 구현되어 있다. 지정한 타입으로 넘어오거나 None으로 지정할 수 있는 타입이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트를 조회할 때는 사용자에게 지정되었거나 사용자가 생성한 카테고리만을 가져오기 위해서 member_no로 조회하며 수입/지출을 구분하여서 가져오도록 하였다. 정렬 순서는 정렬 순서를 저장하는 sort_order 순으로 가져오도록 하였으며, 정렬 순서가 같을 경우는 먼저 등록된 카테고리 정보 먼저 가져오도록 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 부모 카테고리(parent_no)가 지정되었으면 비교해서 찾고 없으면 is null로 조회하도록 처리하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 조회된 리스트를 반환하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;router는 아래와 같이 구현하였다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1683813185527&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from fastapi import APIRouter, Depends, Query
from typing import List, Optional, Literal
from sqlalchemy.ext.asyncio import AsyncSession
from starlette.status import HTTP_200_OK, HTTP_201_CREATED, HTTP_400_BAD_REQUEST
from ..database import schemas
from ..database.connection import get_db
from ..services import category_service
from ..libraries.auth import decode_access_token

# 카테고리 리스트 조회
@router.get('/list', description=&quot;카테고리 리스트 조회&quot;, response_model=List[schemas.CategoryInfo], responses={
    HTTP_400_BAD_REQUEST: {
        &quot;model&quot;: schemas.Message
    }
})
async def get_list(
    user_info: schemas.JWTPayload = Depends(decode_access_token),
    inout_type: Literal['I', 'O'] = Query(title=&quot;수입/지출 구분&quot;),
    parent_no: Optional[int] = Query(default=None, title=&quot;부모카테고리 이름&quot;),
    db: AsyncSession = Depends(get_db)
):
    result = await category_service.get_category_list(db, user_info['member_no'], inout_type, parent_no)
    response = []
    for item in result:
        response.append(schemas.CategoryInfo.from_orm(item).dict())
    return response&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;router의 리스트 조회 기능은 특별히 설명할 내용이 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Query로 전달된 파라미터를 통해서 category_service.get_category_list를 조회하고 결과를 리스트에 담아서 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트는 GET으로 지정하였기 때문에 request는 Query에 전달할 값을 담아서 처리하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자의 member_no는 JWT에서 꺼내서 사용하기 때문에 따로 Query로 전달받지는 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 진행되었으면 리스트 관련 기능은 구현이 끝났다. 이제 테스트를 해볼 차례이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docs에서 리스트를 조회해보도록 한다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-05-11 오후 10.59.12.png&quot; data-origin-width=&quot;2862&quot; data-origin-height=&quot;1110&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bp0EyF/btseQsI8oWF/nnhKDhTyr7MeNnrwIdR7sK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bp0EyF/btseQsI8oWF/nnhKDhTyr7MeNnrwIdR7sK/img.png&quot; data-alt=&quot;리스트 조회 Reqeust&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bp0EyF/btseQsI8oWF/nnhKDhTyr7MeNnrwIdR7sK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbp0EyF%2FbtseQsI8oWF%2FnnhKDhTyr7MeNnrwIdR7sK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2862&quot; height=&quot;1110&quot; data-filename=&quot;스크린샷 2023-05-11 오후 10.59.12.png&quot; data-origin-width=&quot;2862&quot; data-origin-height=&quot;1110&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;리스트 조회 Reqeust&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 이미지와 같이 구성하여서 Request를 요청하면 다음 이미지와 같이 Response를 받을 수 있다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-05-11 오후 10.59.46.png&quot; data-origin-width=&quot;2856&quot; data-origin-height=&quot;1760&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d7RS8b/btseQlpY6QL/VXsCJ9bGZz29sBhCcI0S70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d7RS8b/btseQlpY6QL/VXsCJ9bGZz29sBhCcI0S70/img.png&quot; data-alt=&quot;리스트 조회에 대한 Response&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d7RS8b/btseQlpY6QL/VXsCJ9bGZz29sBhCcI0S70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd7RS8b%2FbtseQlpY6QL%2FVXsCJ9bGZz29sBhCcI0S70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2856&quot; height=&quot;1760&quot; data-filename=&quot;스크린샷 2023-05-11 오후 10.59.46.png&quot; data-origin-width=&quot;2856&quot; data-origin-height=&quot;1760&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;리스트 조회에 대한 Response&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 조회가 잘 되면 리스트 기능에 대한 구현은 마무리 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나머지 카테고리 생성/수정/삭제에 대한 기능은 다음 포스팅에서 정리하도록 하겠다.&lt;/p&gt;</description>
      <category>Programming/Project Log</category>
      <category>FastAPI</category>
      <category>sqlalchemy</category>
      <category>가계부만들기</category>
      <category>개인프로젝트</category>
      <category>파이썬</category>
      <category>프로젝트 기록</category>
      <category>프로젝트 로그</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/402</guid>
      <comments>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EC%B9%B4%ED%85%8C%EA%B3%A0%EB%A6%AC-%EA%B5%AC%EC%A1%B0-%EB%A6%AC%EC%8A%A4%ED%8A%B8#entry402comment</comments>
      <pubDate>Fri, 12 May 2023 23:05:13 +0900</pubDate>
    </item>
    <item>
      <title>Sqlalchemy를 통한 postgresql timezone 설정</title>
      <link>https://minarae7.tistory.com/entry/Sqlalchemy%EB%A5%BC-%ED%86%B5%ED%95%9C-postgresql-timezone-%EC%84%A4%EC%A0%95</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;connect-a-flask-app-to-a-mysql-database-with-sqlalchemy-and-pymysql.jpg&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BkbBo/btsetIc7UMX/rGSYPpfDx1fbqhC1cD6cGk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BkbBo/btsetIc7UMX/rGSYPpfDx1fbqhC1cD6cGk/img.jpg&quot; data-alt=&quot;SQLAlchemy&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BkbBo/btsetIc7UMX/rGSYPpfDx1fbqhC1cD6cGk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBkbBo%2FbtsetIc7UMX%2FrGSYPpfDx1fbqhC1cD6cGk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1500&quot; height=&quot;600&quot; data-filename=&quot;connect-a-flask-app-to-a-mysql-database-with-sqlalchemy-and-pymysql.jpg&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SQLAlchemy&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;개발을 하다보면 날짜를 다루는 일을 꽤 많이 하게 된다.&lt;/u&gt; 그만큼 중요한 업무이기도 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 테이블을 설계할 때 Row 단위로 해당 Row가 생성된 일시와 수정된 일시, 그리고 삭제된 일시를 자동으로 남기도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 &lt;u&gt;각 데이터베이스마다 날짜를 다루는 컬럼 타입이 조금씩 상이&lt;/u&gt;하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mysql에서는 timezone이라는 개념이 없이 timestamp, datetime, date 등의 형태로 지정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 다르게&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt; postgresql에서는 timestamp에 timezone을 같이 저장하는 형태와 저장하지 않고 timestamp만 저장하는 형태&lt;/b&gt;&lt;/span&gt; 두 가지로 구분하여서 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;국내용으로 개발할 때는 굳이 timezone을 저장할 필요없이 timestamp만 저장하면 될 것이고 해외도 같이 서비스를 할 경우 각 국가별 timezone을 반영하여야 하기 때문에 timezone 값을 같이 저장하여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컬럼을 선언할 때는 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;&quot;timestamp with time zone&quot;과 &quot;timestamp without time zone&quot;&lt;/b&gt;&lt;/span&gt;으로 선언하여서 사용하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얼마전 회사에서 postgresql에 테이블을 설계하였는데 늘 하던 습관과 같이 timestamp 값을 without time zone으로 선언하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬에서 개발할 때는 어차피 timezone이 Asis/Seoul로 되어 있으니 이렇게 선언하여서 사용해도 무관하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 테이블을 설계할 때도 계속 이렇게 했는데 이 때는 데이터베이스 서버를 별도로 구축하여서 사용하였기 때문에 timestamp에 민감하기 않았다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 AWS에서 데이터베이스 서비스를 이용하니 여기서 문제가 발생한다. &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;u&gt;AWS 데이터베이스 시스템 시간이 PST로 국제 표준시로 설정되어있다.&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되면 국내 timezone 값과 시차가 발생하는데 이걸 코드에서 해결하기가 쉽지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면 썸머타임이 적용되는 월과 그렇지 않은 월의 시차가 다르기 때문에 파이썬 코드에서 이걸 적용하려면 꽤 골치가 아프다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스 상에서 해결하려면 이미 등록되어 있는 Row도 값이 적용해주어야 해서 이것도 적용이 쉽지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 쿼리에서 해결해보는 방법을 찾아보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 postgresql에서 이런 쿼리를 지원한다. timestamp without time zone으로 설정된 created_at 컬럼이 있다고 했을 때 PST를 Asis/Seoul로 변경하는 쿼리는 다음과 같이 작성할 수 있다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1683554738033&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT
    *,
    timezone('Asia/Seoul', timezone('PST', created_at)) AS created_at
FROM
    tb_sample&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 timezone이 설정되지 않은 컬럼에 시스템의 timezone 값을 설정하고 이 값을 다시 원하는 timezone 값으로 변경하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;굳이 이렇게 쿼리를 작성한 이유는 개발환경과 운영환경의 timezone 값이 다르기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발할 때는 PST 대신 Asia/Seoul로 입력하면 한국 timezone을 설정하고 이걸 다시 한국 timezone으로 변경하는 것이니 문제없이 동작하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드를 이제 Sqlalchemy가 반영된 python 코드로 반영하면 다음과 같다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1683555783006&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from sqlalchemy.sql import func

def message_totals(dbsession, user):
    stmt = select(
        models.Sample.no,
        ....
        func.timezone('Asia/Seoul', func.timezone('PST', models.Sample.created_at,)).label('created_at'),
    )
    stmt = stmt.filter(models.Sample.is_deleted == &quot;F&quot;)
    result = await db.execute(stmt)
    return result.fetchall()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;sqlalchemy에서는 func를 통해서 각 데이터베이스에서 지원하는 함수를 호출&lt;/b&gt;&lt;/u&gt;할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 func를 통해서 postgresql에서 지원하는 timezone 함수를 호출하도록 하였으며, 이 결과를 다시 created_at으로 alias 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 실행하면 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;u&gt;timestamp without time zone으로 설정된 컬럼을 time zone이 있는 컬럼처럼 사용&lt;/u&gt;&lt;/span&gt;할 수 있게 된다.&lt;/p&gt;</description>
      <category>Programming/Python</category>
      <category>PostgreSQL</category>
      <category>sqlalchemy</category>
      <category>timestamp without time zone</category>
      <category>Timezone 설정</category>
      <category>개발기록</category>
      <category>개발팁</category>
      <category>파이썬</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/401</guid>
      <comments>https://minarae7.tistory.com/entry/Sqlalchemy%EB%A5%BC-%ED%86%B5%ED%95%9C-postgresql-timezone-%EC%84%A4%EC%A0%95#entry401comment</comments>
      <pubDate>Mon, 8 May 2023 23:31:09 +0900</pubDate>
    </item>
    <item>
      <title>[가계부 만들기] Category Table 구조 변경</title>
      <link>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-Category-Table-%EA%B5%AC%EC%A1%B0-%EB%B3%80%EA%B2%BD</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;카테고리 관련 기능을 구현하려고 보니 카테고리 관련 테이블 구조가 잘못된 것을 알게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최초에는 시스템에서 정의한 카테고리만 보이도록 하고 &lt;u&gt;시스템에서 정의한 카테고리는 변경을 할 수 없도록&lt;/u&gt; 하려고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 출력되는 순서를 변경하고 싶거나 시스템 카테고리에 서브로 카테고리를 더 추가하고 싶을 수도 있을 것이라고 판단된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 회원이 가입할 때 시스템 정의 카테고리를 회원별로 정의해 주고 회원별로 정의된 카테고리는 각 회원이 자신의 필요에 맞춰 수정할 수 있도록 해야 할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 최초 정의한 테이블에는 지출에 대한 카테고리만 생각하고 정의했는데 수입에 대해서도 카테고리를 정의하고 싶을 수 있으므로 카테고리 테이블에 수입/지출에 대한 구분 값을 넣을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 순서를 변경할 수 있도록 하기 위해서 출력하는 순서를 정의하도록 할 것이다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;테이블 구조 변경&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 정의되어 있는 테이블 스키마는 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-05-02 오후 10.12.42.png&quot; data-origin-width=&quot;2702&quot; data-origin-height=&quot;466&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ojtzF/btsdBOAjZ4E/6bhV8UwFEaYlBE3Ju3Bcak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ojtzF/btsdBOAjZ4E/6bhV8UwFEaYlBE3Ju3Bcak/img.png&quot; data-alt=&quot;tb_category 테이블 Schema&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ojtzF/btsdBOAjZ4E/6bhV8UwFEaYlBE3Ju3Bcak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FojtzF%2FbtsdBOAjZ4E%2F6bhV8UwFEaYlBE3Ju3Bcak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2702&quot; height=&quot;466&quot; data-filename=&quot;스크린샷 2023-05-02 오후 10.12.42.png&quot; data-origin-width=&quot;2702&quot; data-origin-height=&quot;466&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;tb_category 테이블 Schema&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 테이블에 수입/지출 구분 컬럼과 정렬 순서 칼럼을 추가할 것이다. 그리고 is_system 칼럼을 사용하지 않을 것이기 때문에 삭제할 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1683033439772&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;alter table tb_category
	add inout_type char(1) not null default 'O' comment '수입/지출 구분(I: incomes, O: outgoings)' after has_children,
	add sort_order tinyint unsigned not null default 1 comment '정렬순서' after class_name,
	drop is_system;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수입/지출 구분 컬럼은 enum 타입으로 지정해도 되지만 우선 여기서는 char로 지정하도록 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sort_order는 정렬 순서를 나타내며 각 사용자가 카테고리를 무지막지하게 생성할 일은 없으며 정렬 순서는 1부터 시작하며 음수로 내려가는 일이 없을 것이라고 판단하여 unsigned tinyint로 선언하였다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 추가로 카테고리 테이블을 수정한 내용이다.&lt;/p&gt;
&lt;pre id=&quot;code_1683294077381&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;alter table tb_log_detail drop foreign key fk_log_detail__category_no;
alter table tb_log_detail change category_no category_no bigint unsigned not null comment '카테고리 번호';
alter table tb_category 
	change category_no category_no bigint unsigned not null auto_increment comment '카테고리 번호',
	change parent_no parent_no bigint unsigned null comment '부모 카테고리 번호';
alter table tb_log_detail add constraint fk_log_detail__category_no foreign key  (category_no) references tb_category (category_no);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 내용은 category_no를 int에서 bigint로 변경하기 위해서 수정하였다. 회원이 한 명 가입할 때마다 총 146개의 카테고리가 생성되므로 회원 테이블 member_no가 int이면 category_no는 bigint로 생성하는 것이 낫다고 판단된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 tb_log_detail 테이블에서 category_no 테이블을 외부키로 잡고 있기 때문에 형 변경을 하려면 먼저 tb_log_detail 외부키를 삭제하고 모두 형 변경을 마친 이후에 다시 외부키를 잡아주도록 하였다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Model 변경&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블 구조가 변경되었으니 &lt;u&gt;&lt;b&gt;models.py 파일 구조도 이에 맞춰서 변경&lt;/b&gt;&lt;/u&gt;하여야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1683034299145&quot; class=&quot;sql&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
from sqlalchemy.dialects.mysql import (
    TINYINT,
    SMALLINT,
    INTEGER,
    TINYINT,
    BIGINT
)

...
class Category(Base):
    __tablename__ = &quot;tb_category&quot;

    category_no = Column(BIGINT(unsigned=True), nullable=False, autoincrement=True, comment=&quot;카테고리 번호&quot;)
    member_no = Column(SMALLINT(unsigned=True), nullable=True, comment=&quot;카테고리 생성자 번호(기본 카테고리일 경우 null)&quot;)
    category_name = Column(VARCHAR(length=50), nullable=False, comment=&quot;카테고리 이름&quot;)
    has_children = Column(CHAR(length=1), nullable=False, default=&quot;F&quot;, comment=&quot;자식을 가지고 있는지 여부(T|F)&quot;)
    inout_type = Column(CHAR(length=1), nullable=False, default=&quot;O&quot;, comment=&quot;수입/지출 구분(I: incomes, O: outgoings)&quot;)
    parent_no = Column(BIGINT(unsigned=True), nullable=True, comment=&quot;부모 카테고리 번호&quot;)
    class_name = Column(VARCHAR(length=30), nullable=True, comment=&quot;아이콘 클래스&quot;)
    sort_order = Column(TINYINT(unsigned=True), default=1, comment=&quot;정렬순서&quot;)
    reg_dt = Column(DATETIME(timezone=False), nullable=False, server_default=func.now(), comment=&quot;생성일시&quot;)
    upd_dt = Column(DATETIME(timezone=False), nullable=False, server_default=func.now(), onupdate=func.now(), comment=&quot;수정일시&quot;)
    is_deleted = Column(CHAR(length=1), default=&quot;F&quot;, server_default=&quot;F&quot;, nullable=False, comment=&quot;삭제여부(T|F)&quot;)
    del_dt = Column(DATETIME(timezone=False), nullable=True, comment=&quot;삭제일시&quot;)

    __table_args__ = (
        PrimaryKeyConstraint(category_no, name=&quot;pk_category&quot;),
        ForeignKeyConstraint(
            [&quot;member_no&quot;],
            [&quot;tb_members.member_no&quot;],
            name=&quot;fk_category__member_no&quot;,
            onupdate=&quot;NO ACTION&quot;,
            ondelete=&quot;NO ACTION&quot;,
        ),
        {
            &quot;comment&quot;: &quot;카테고리 정보&quot;
        }
    )
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sqlalchemy에서는 mysql 전용 자료형을 따로 지원한다. sort_order는 tinyint unsigned로 선언하였으므로 mysql 자료형에서 TINYINT로 지정하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;category_no는 mysql 전용 자료형으로 BIGINT로 선언하였고, 마찬가지로 parent_no도 BIGINT로 수정하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 명시하지 않았지만 tb_log_detail을 ORM으로 표현한 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;u&gt;&lt;b&gt;LogDetail에서도 category_no를 BIGINT로 변경&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;한다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;회원 가입 코드 재지정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 마지막으로 회원이 가입할 때 각 회원별로 카테고리를 추가해주는 코드를 생성하여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 생성할 카테고리 정보를 정의하여야 한다. 여기서는 다음과 같이 json 형태로 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발하는 가계부에는 지출/수입을 기준으로 하고 이체 관련 내용은 정리하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가계부의 카테고리 내역은 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1683120720766&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[
    {
        &quot;category_name&quot;: &quot;급여&quot;,
        &quot;class_name&quot;: &quot;&quot;,
        &quot;children&quot;: [],
        &quot;inout_type&quot;: &quot;I&quot;,
        &quot;sort_order&quot;: 1
    },
    {
        &quot;category_name&quot;: &quot;용돈&quot;,
        &quot;class_name&quot;: &quot;&quot;,
        &quot;children&quot;: [],
        &quot;inout_type&quot;: &quot;I&quot;,
        &quot;sort_order&quot;: 2
    },
    {
        &quot;category_name&quot;: &quot;금융수입&quot;,
        &quot;class_name&quot;: &quot;&quot;,
        &quot;children&quot;: [],
        &quot;inout_type&quot;: &quot;I&quot;,
        &quot;sort_order&quot;: 3
    },
    {
        &quot;category_name&quot;: &quot;사업수입&quot;,
        &quot;class_name&quot;: &quot;&quot;,
        &quot;children&quot;: [],
        &quot;inout_type&quot;: &quot;I&quot;,
        &quot;sort_order&quot;: 4
    },
    {
        &quot;category_name&quot;: &quot;기타수입&quot;,
        &quot;class_name&quot;: &quot;&quot;,
        &quot;children&quot;: [],
        &quot;inout_type&quot;: &quot;I&quot;,
        &quot;sort_order&quot;: 5
    },
    {
        &quot;category_name&quot;: &quot;식비&quot;,
        &quot;class_name&quot;: &quot;fa-solid fa-utensils&quot;,
        &quot;children&quot;: [&quot;한식&quot;, &quot;중식&quot;, &quot;일식&quot;, &quot;양식&quot;, &quot;아시안음식&quot;, &quot;뷔페&quot;, &quot;고기&quot;, &quot;치킨&quot;, &quot;피자&quot;, &quot;패스트푸드&quot;, &quot;배달&quot;, &quot;식재료&quot;],
        &quot;inout_type&quot;: &quot;O&quot;,
        &quot;sort_order&quot;: 1
    },
    {
        &quot;category_name&quot;: &quot;카페/간식&quot;,
        &quot;class_name&quot;: &quot;fa-solid fa-mug-hot&quot;,
        &quot;children&quot;: [&quot;커피/음료&quot;, &quot;베이커리&quot;, &quot;디저트/떡&quot;, &quot;도넛/핫도그&quot;, &quot;아이스크림/빙수&quot;, &quot;기타간식&quot;],
        &quot;inout_type&quot;: &quot;O&quot;,
        &quot;sort_order&quot;: 2
    },
    {
        &quot;category_name&quot;: &quot;술/유흥&quot;,
        &quot;class_name&quot;: &quot;fa-solid fa-champagne-glasses&quot;,
        &quot;children&quot;: [&quot;맥주/호프&quot;, &quot;이자카야&quot;, &quot;와인&quot;, &quot;바(BAR)&quot;, &quot;요리주점&quot;, &quot;민속주점&quot;, &quot;유흥시설&quot;],
        &quot;inout_type&quot;: &quot;O&quot;,
        &quot;sort_order&quot;: 3
    },
    {
        &quot;category_name&quot;: &quot;생활&quot;,
        &quot;class_name&quot;: &quot;fa-solid fa-basket-shopping&quot;,
        &quot;children&quot;: [&quot;생필품&quot;, &quot;편의점&quot;, &quot;마트&quot;, &quot;생활서비스&quot;, &quot;세탁&quot;, &quot;목욕&quot;, &quot;가구/가전&quot;],
        &quot;inout_type&quot;: &quot;O&quot;,
        &quot;sort_order&quot;: 4
    },
    {
        &quot;category_name&quot;: &quot;온라인쇼핑&quot;,
        &quot;class_name&quot;: &quot;fa-solid fa-truck-fast&quot;,
        &quot;children&quot;: [&quot;인터넷쇼핑&quot;, &quot;홈쇼핑&quot;, &quot;결제/충전&quot;, &quot;앱스토어&quot;, &quot;서비스구독&quot;],
        &quot;inout_type&quot;: &quot;O&quot;,
        &quot;sort_order&quot;: 5
    },
    {
        &quot;category_name&quot;: &quot;패션/쇼핑&quot;,
        &quot;class_name&quot;: &quot;fa-solid fa-bag-shopping&quot;,
        &quot;children&quot;: [&quot;패션&quot;, &quot;신발&quot;, &quot;아울렛/몰&quot;, &quot;스포츠의류&quot;, &quot;백화점&quot;],
        &quot;inout_type&quot;: &quot;O&quot;,
        &quot;sort_order&quot;: 6
    },
    {
        &quot;category_name&quot;: &quot;뷰티/미용&quot;,
        &quot;class_name&quot;: &quot;fa-solid fa-pump-soap&quot;,
        &quot;children&quot;: [&quot;화장품&quot;, &quot;헤어샵&quot;, &quot;미용관리&quot;, &quot;미용용품&quot;, &quot;네일&quot;, &quot;성형외과&quot;, &quot;피부과&quot;],
        &quot;inout_type&quot;: &quot;O&quot;,
        &quot;sort_order&quot;: 7
    },
    {
        &quot;category_name&quot;: &quot;교통&quot;,
        &quot;class_name&quot;: &quot;fa-solid fa-bus&quot;,
        &quot;children&quot;: [&quot;택시&quot;, &quot;대중교통&quot;, &quot;철도&quot;, &quot;시외버스&quot;],
        &quot;inout_type&quot;: &quot;O&quot;,
        &quot;sort_order&quot;: 8
    },
    {
        &quot;category_name&quot;: &quot;자동차&quot;,
        &quot;class_name&quot;: &quot;fa-solid fa-car&quot;,
        &quot;children&quot;: [&quot;주유&quot;, &quot;주차&quot;, &quot;세차&quot;, &quot;통행료&quot;, &quot;할부/리스&quot;, &quot;정비/수리&quot;, &quot;차량보험&quot;, &quot;대리운전&quot;],
        &quot;inout_type&quot;: &quot;O&quot;,
        &quot;sort_order&quot;: 9
    },
    {
        &quot;category_name&quot;: &quot;주거/통신&quot;,
        &quot;class_name&quot;: &quot;fa-solid fa-house-chimney&quot;,
        &quot;children&quot;: [&quot;휴대폰&quot;, &quot;인터넷&quot;, &quot;월세&quot;, &quot;관리비&quot;, &quot;가스비&quot;, &quot;전기세&quot;],
        &quot;inout_type&quot;: &quot;O&quot;,
        &quot;sort_order&quot;: 10
    },
    {
        &quot;category_name&quot;: &quot;의료/건강&quot;,
        &quot;class_name&quot;: &quot;fa-solid fa-stethoscope&quot;,
        &quot;children&quot;: [&quot;약국&quot;, &quot;종합병원&quot;, &quot;피부과&quot;, &quot;소아과&quot;, &quot;산부인과&quot;, &quot;안과&quot;, &quot;이비인후과&quot;, &quot;비노기과&quot;, &quot;성형외과&quot;, &quot;내과/가정의학&quot;, &quot;정형외과&quot;, &quot;치과&quot;, &quot;한의원&quot;, &quot;기타병원&quot;, &quot;보조식품&quot;, &quot;건강용품&quot;, &quot;운동&quot;],
        &quot;inout_type&quot;: &quot;O&quot;,
        &quot;sort_order&quot;: 11
    },
    {
        &quot;category_name&quot;: &quot;금융&quot;,
        &quot;class_name&quot;: &quot;fa-solid fa-won-sign&quot;,
        &quot;children&quot;: [&quot;보험&quot;, &quot;은행&quot;, &quot;증권/투자&quot;, &quot;카드&quot;, &quot;이자/대출&quot;, &quot;세금/과태료&quot;],
        &quot;inout_type&quot;: &quot;O&quot;,
        &quot;sort_order&quot;: 12
    },
    {
        &quot;category_name&quot;: &quot;문화/여가&quot;,
        &quot;class_name&quot;: &quot;fa-solid fa-ticket-sample&quot;,
        &quot;children&quot;: [&quot;영화&quot;, &quot;도서&quot;, &quot;게임&quot;, &quot;음악&quot;, &quot;공연&quot;, &quot;전시/관람&quot;, &quot;취미/체험&quot;, &quot;테마파크&quot;, &quot;스포츠&quot;, &quot;마사지/스파&quot;],
        &quot;inout_type&quot;: &quot;O&quot;,
        &quot;sort_order&quot;: 13
    },
    {
        &quot;category_name&quot;: &quot;여행/숙박&quot;,
        &quot;class_name&quot;: &quot;fa-solid fa-plane&quot;,
        &quot;children&quot;: [&quot;숙박비&quot;, &quot;항공권&quot;, &quot;여행&quot;, &quot;관광&quot;, &quot;여행용품&quot;, &quot;해외결제&quot;],
        &quot;inout_type&quot;: &quot;O&quot;,
        &quot;sort_order&quot;: 14
    },
    {
        &quot;category_name&quot;: &quot;교육/학습&quot;,
        &quot;class_name&quot;: &quot;fa-solid fa-graduation-cap&quot;,
        &quot;children&quot;: [&quot;학원/강의&quot;, &quot;학습교재&quot;, &quot;학교&quot;, &quot;시험료&quot;],
        &quot;inout_type&quot;: &quot;O&quot;,
        &quot;sort_order&quot;: 15
    },
    {
        &quot;category_name&quot;: &quot;자녀/육아&quot;,
        &quot;class_name&quot;: &quot;fa-solid fa-children&quot;,
        &quot;children&quot;: [&quot;육아용품&quot;, &quot;돌봄비&quot;, &quot;자녀용돈&quot;, &quot;자녀교육&quot;, &quot;놀이/체험&quot;],
        &quot;inout_type&quot;: &quot;O&quot;,
        &quot;sort_order&quot;: 16
    },
    {
        &quot;category_name&quot;: &quot;반려동물&quot;,
        &quot;class_name&quot;: &quot;fa-solid fa-paw&quot;,
        &quot;children&quot;: [&quot;동물병원&quot;, &quot;펫용품&quot;, &quot;사료/간식&quot;],
        &quot;inout_type&quot;: &quot;O&quot;,
        &quot;sort_order&quot;: 17
    },
    {
        &quot;category_name&quot;: &quot;경조/선물&quot;,
        &quot;class_name&quot;: &quot;fa-solid fa-envelope&quot;,
        &quot;children&quot;: [&quot;축의금&quot;, &quot;부의금&quot;, &quot;기부/헌금&quot;, &quot;선물&quot;, &quot;회비&quot;],
        &quot;inout_type&quot;: &quot;O&quot;,
        &quot;sort_order&quot;: 18
    }
]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;class_name 컬럼은 frontend 개발 시에 아이콘을 보여주기 위해서 정의하였으며, 각 카테고리는 하위 카테고리를 가질 수 있도록 구성하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 카테고리 구성은 다른 프로그램을 참조하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 회원 가입시 이 내용을 프로그램에 적용하는 작업을 진행하도록 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 services/category_service.py 파일을 생성하고 아래 내용을 추가한다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1683294634590&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from sqlalchemy.ext.asyncio import AsyncSession
from ..database import models
import json
import asyncio

# 회원 가입시 기본 카테고리 구성 추가
async def insert_default_categories(
    db: AsyncSession,
    member_no: int,
):
    # 우선 카테고리 내용을 정의한 파일을 읽어들인다.
    with open(&quot;./categories.json&quot;, &quot;r&quot;) as f:
        categories = json.load(f)

    # 메인 카테고리 리스트를 처리한다.
    for category in categories:
        new_category = {k: v for k, v in category.items() if k != 'children'}
        if len(category['children']) == 0:
            new_category['has_children'] = 'F'
        else:
            new_category['has_children'] = 'T'
        
        # 카테고리 정보를 DB에 저장한다.
        db_category = models.Category(**new_category, member_no=member_no)
        db.add(db_category)
        await db.commit()
        await db.refresh(db_category)
        insert_coros = []

        # 서브카테고리도 DB에 입력한다. 이 때 처리는 비동기로 한다.
        insert_coros = [db.execute(models.Category.__table__.insert().values(**{
                &quot;member_no&quot;: member_no,
                &quot;category_name&quot;: child,
                &quot;has_children&quot;: &quot;F&quot;,
                &quot;inout_type&quot;: category[&quot;inout_type&quot;],
                &quot;parent_no&quot;: db_category.category_no,
                &quot;sort_order&quot;: index + 1
            })) for index, child in enumerate(category['children'])]

        # 모든 데이터 추가 코루틴을 동시에 실행
        await asyncio.gather(*insert_coros)
        await db.commit()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 함수의 파라미터는 member_no가 포함된다. 모든 카테고리는 사용자에게 포함되기 때문에 카테고리 정보에 member_no를 포함시켜주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음으로 기본 카테고리로 정리한 내용을 프로그램에서 읽어오도록 하였다. 그러면 categories에 카테고리 정보가 List 형태로 담긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 categories를 하나씩 꺼내서 처리하는데 메인 카테고리 정보를 저장한 이후에 메인 카테고리에 포함된 서브 카테고리 내용을 비동기로 저장하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하는 이유는 서브 카테고리에는 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;부모가 되는 메인 카테고리 category_no가 포함되어야 하는데 메인 카테고리의 저장이 끝나야 메인 category_no를 알 수 있다.&lt;/b&gt;&lt;/span&gt; 반대로 &lt;u&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;같은 메인 카테고리에 포함된 서브 카테고리를 서로 영향을 주지 않기 때문에&lt;/span&gt;&lt;/u&gt; 각자 저장이 끝날 때까지 기다릴 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 서브 카테고리를 저장할 때는 비동기로 처리하도록 하고 메인 카테고리 루프가 끝날 때는 서브 카테고리의 비동기 처리가 모두 끝날 때까지 다음 루프가 대기하도록 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 routers/members.py 파일에서 회원 가입 기능이 create 함수에 다음 코드를 추가한다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1683295125475&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
from ..services import members_service, category_service

...
async def create(
    member: schemas.MemberCreate = Body(
        title=&quot;회원정보&quot;,
        example={
            &quot;member_id&quot;: &quot;foo&quot;,
            &quot;member_pw&quot;: &quot;1234567890&quot;,
            &quot;member_name&quot;: &quot;홍길동&quot;,
            &quot;member_email&quot;: &quot;test@example.com&quot;,
        }
    ),
    db: AsyncSession = Depends(get_db)
):
	try:
        result = await members_service.create_member(db, member)
        await category_service.insert_default_categories(db, result.member_no)

        return Response(status_code=HTTP_201_CREATED)
    except Exception as e:
        return JSONResponse(content={&quot;detail&quot;: e.args[0]}, status_code=HTTP_400_BAD_REQUEST)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;category_service를 추가로 로딩하도록 하였으며, members_service.create_member에서 회원 정보 저장에 대한 프로세스를 마무리 한 이후에 결과로 전달받은 result에서 member_no를 꺼내서 category_service.insert_default_categories 함수로 전달하도록 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;members_service.create_member에는 DB에 저장한 이후에 primary key인 member_no가 포함된 결괏값을 전달하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 가입할 때 각 회원에게 기본 카테고리를 생성해주도록 코드를 작성하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드를 테스트하면 다음 그림과 같이 카테고리 정보가 회원 가입 시에 동시에 생성된다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-05-05 오후 11.02.05.png&quot; data-origin-width=&quot;2314&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qcXvw/btsd0SBnjkO/sIs5ooQzBaVCtGx0PcMsc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qcXvw/btsd0SBnjkO/sIs5ooQzBaVCtGx0PcMsc1/img.png&quot; data-alt=&quot;기본 카테고리 생성 후 테이블 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qcXvw/btsd0SBnjkO/sIs5ooQzBaVCtGx0PcMsc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqcXvw%2Fbtsd0SBnjkO%2FsIs5ooQzBaVCtGx0PcMsc1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2314&quot; height=&quot;1080&quot; data-filename=&quot;스크린샷 2023-05-05 오후 11.02.05.png&quot; data-origin-width=&quot;2314&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;기본 카테고리 생성 후 테이블 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카테고리에 기본적인 구성이 마무리가 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 포스팅에서는 카테고리에 대한 정보를 생성/수정/삭제/열람하는 방법에 대한 코드를 기록하도록 하겠다.&lt;/p&gt;</description>
      <category>Programming/Project Log</category>
      <category>FastAPI</category>
      <category>MySQL</category>
      <category>가계부만들기</category>
      <category>테이블 수정</category>
      <category>파이썬</category>
      <category>프로젝트 기록</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/400</guid>
      <comments>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-Category-Table-%EA%B5%AC%EC%A1%B0-%EB%B3%80%EA%B2%BD#entry400comment</comments>
      <pubDate>Fri, 5 May 2023 23:14:25 +0900</pubDate>
    </item>
    <item>
      <title>[강낭콩 키우기] 폭풍 성장</title>
      <link>https://minarae7.tistory.com/entry/%EA%B0%95%EB%82%AD%EC%BD%A9-%ED%82%A4%EC%9A%B0%EA%B8%B0-%ED%8F%AD%ED%92%8D-%EC%84%B1%EC%9E%A5</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;큰 아이가 학교 과제로 강낭콩을 심어서 관찰일기를 써야 한다고 했던 게 4월 19일로 불과 열흘 전이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 강낭콩을 심으면 얼마나 빨리 크겠어라는 마음과 이거 잘 키워서 강낭콩을 수확하면 올해 수확할 수 있는 품종이 늘겠네라는 생각을 하였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 음료수 사 먹고 닦아서 사용하는 컵에서 얼마나 잘 클까 반신반의하였던 것이 사실이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강낭콩 3개를 심은 게 4월 19일인데 처음으로 싹이 관찰된 것이 4월 24일이다. 5일 만에 꿈틀꿈틀 싹이 올라오는 것을 관찰하게 되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7671.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2btmP/btsdjnJdoRR/2OGSrTwQgMoO63qdsEC8E0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2btmP/btsdjnJdoRR/2OGSrTwQgMoO63qdsEC8E0/img.jpg&quot; data-alt=&quot;강남콩 첫 싹을 관찰&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2btmP/btsdjnJdoRR/2OGSrTwQgMoO63qdsEC8E0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2btmP%2FbtsdjnJdoRR%2F2OGSrTwQgMoO63qdsEC8E0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;800&quot; data-filename=&quot;IMG_7671.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;강남콩 첫 싹을 관찰&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정작 강낭콩이 안 보이게 심은 쪽에서 강낭콩 첫 싹이 관찰된 것이다. 사실 초등학교 이후에 이렇게 뿌리가 나는 것부터 관찰한 게 처음이라 신기하기도 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;식물이 이렇게 자라기 위해서 씨앗에서 뿌리가 나오고 싹이 올라오는 것을 관찰하는 게 제법 흥미로운 관찰인 거 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 분명 3개를 심었는데 하나에서만 싹이 관찰되었다. 나머지에서는 싹이 나올 생각이 없는 듯하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7677.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NwBo3/btsdpzoJhBZ/wedNLlA8u4P76UnXM6l7I0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NwBo3/btsdpzoJhBZ/wedNLlA8u4P76UnXM6l7I0/img.jpg&quot; data-alt=&quot;흙 위로 올라온 싹&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NwBo3/btsdpzoJhBZ/wedNLlA8u4P76UnXM6l7I0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNwBo3%2FbtsdpzoJhBZ%2FwedNLlA8u4P76UnXM6l7I0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;800&quot; data-filename=&quot;IMG_7677.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;흙 위로 올라온 싹&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 이틀 뒤에 보니 흙속에 웅크리고 있던 싹이 흙을 밀어내고 흙 위에서 관찰되기 시작한다. 이렇게 보니 싹이 흙 위로 올라오기까지 흙 속에서 부지런히 성장하고 있었던 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 씨앗을 심었을 때도 심고 나서 이렇게 올라오는데 보통 5~7일 정도 걸리던데 그 사이에 이렇게 흙속에 뿌리는 내리며 성장하고 있었던 모양이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 빼꼼하고 올라온 싹은 정말 폭풍 성장을 거듭한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7689.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8LKLg/btsdeH9gRu4/mJLOToSD5h7CbwlEwnFNG1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8LKLg/btsdeH9gRu4/mJLOToSD5h7CbwlEwnFNG1/img.jpg&quot; data-alt=&quot;본잎이 나기 시작하는 강남콩&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8LKLg/btsdeH9gRu4/mJLOToSD5h7CbwlEwnFNG1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8LKLg%2FbtsdeH9gRu4%2FmJLOToSD5h7CbwlEwnFNG1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;800&quot; data-filename=&quot;IMG_7689.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;본잎이 나기 시작하는 강남콩&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싹이 흙 위로 올라온 것이 26일인데 3일 후에 보니 다른 아이가 되었다. 어느새 떡잎이 나고 본잎까지 올라왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 키가 제법 올라왔다. 30년 전에 초등학교 다닐 때 이렇게 열심히 봤었나 싶지만 강낭콩 성장하는 게 또 새삼스럽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 해바라기, 토마토 이런 애들 심을 때도 이렇게 폭풍 성장해서 신기했는데 강낭콩은 또 다르게 쑥 올라온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흡사 성장 속도가 해바라기처럼 빠르게 성장하고 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7695.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UqE2E/btsdlKD6Ycz/k3oOF3DXTGzULVS8ZCt301/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UqE2E/btsdlKD6Ycz/k3oOF3DXTGzULVS8ZCt301/img.jpg&quot; data-alt=&quot;잎이 제법 커진 강남콩&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UqE2E/btsdlKD6Ycz/k3oOF3DXTGzULVS8ZCt301/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUqE2E%2FbtsdlKD6Ycz%2Fk3oOF3DXTGzULVS8ZCt301%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;800&quot; data-filename=&quot;IMG_7695.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;잎이 제법 커진 강남콩&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이틀 후인 오늘 확인해 보니 잎이 제법 넓어졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직은 컵 아래쪽으로 성장할 공간이 남아있는데 좀 더 지나면 컵이 작을 거 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주말에는 스벅에서 벤티 사이즈를 먹고 가지고 와서 옮겨 심던지 해야 할 거 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성장하는 속도를 보아하니 학기가 끝나기 전에 큰 화분으로 옮겨 심어야 콩을 수확할 수 있을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올여름에는 심어둔 수확물들이 제법 쏠쏠할 것으로 기대하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생각지도 않았던 강낭콩까지 이렇게 잘 커주니 여러모로 수확 재미가 또 기대된다.&lt;/p&gt;</description>
      <category>My Story/식물재배기</category>
      <category>강낭콩키우기</category>
      <category>강낭콩폭풍성장</category>
      <category>식물관찰기</category>
      <category>식집사</category>
      <category>초4과학</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/399</guid>
      <comments>https://minarae7.tistory.com/entry/%EA%B0%95%EB%82%AD%EC%BD%A9-%ED%82%A4%EC%9A%B0%EA%B8%B0-%ED%8F%AD%ED%92%8D-%EC%84%B1%EC%9E%A5#entry399comment</comments>
      <pubDate>Mon, 1 May 2023 22:58:03 +0900</pubDate>
    </item>
    <item>
      <title>[nextjs] 서버 설정 파일 읽기</title>
      <link>https://minarae7.tistory.com/entry/nextjs-%EC%84%9C%EB%B2%84-%EC%84%A4%EC%A0%95-%ED%8C%8C%EC%9D%BC-%EC%9D%BD%EA%B8%B0</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;nextjs_logo.png&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;571&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/O7rhL/btsc1wei0lU/Gy2YXOA1o2pPhMoVZXU8K0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/O7rhL/btsc1wei0lU/Gy2YXOA1o2pPhMoVZXU8K0/img.png&quot; data-alt=&quot;Nextjs&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/O7rhL/btsc1wei0lU/Gy2YXOA1o2pPhMoVZXU8K0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FO7rhL%2Fbtsc1wei0lU%2FGy2YXOA1o2pPhMoVZXU8K0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;571&quot; data-filename=&quot;nextjs_logo.png&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;571&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Nextjs&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nextjs는 서버 사이드 렌더링임에도 불구하고 기본적으로는 스크립트 대부분의 동작은 Client 브라우저에서 작동되게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 nextjs에서는 fs 라이브러리를 사용할 수 없고 따라서 프로젝트 포함되어 있는 별도의 설정을 읽어올 수가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별별 꼼수를 다 사용해 봤지만 특별히 아 이런 방법이면 좋겠구나 이런 내용은 찾기 힘들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러던 중에서 가장 그럴듯한 방법을 찾아서 기록을 남겨두고자 한다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 왜 이런 변수가 필요한지 설명하고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발을 하던 중에 특정 사용자에 대해서는 Backend에 오는 정보를 살짝 바꾸어주어야 하는 필수가 발생했다. 그래서 이 사용자들의 아이디를 기준으로 이렇게 정보를 바꿔주고 싶은데 Frontend이다 보니 DB에 연결할 수도 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이 리스트를 특정 파일에 등록해 두고 이 사용자들에게 권한을 열어주는 작업을 하고자 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 프로젝트 루트에 파일을 생성하고 여기에 JSON 형태로 내용을 저장해 두었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 bypass.json이라고 하였고 내용은 다음과 같이 정의하였다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1682601488680&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[&quot;test&quot;, &quot;json&quot;, &quot;bypass&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 프로그램에서 이 파일을 읽어와서 저장해 두어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;next.config.js을 열고 다음과 같이 작성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1682601796600&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const fs = require('fs');
const path = require('path');

const filePath = path.join(process.cwd(), 'bypass.json');	// 파일 경로 생성
const bypassList = JSON.parse(fs.readFileSync(filePath, 'utf-8');	// 파일 읽기

module.exports = {
  ...
  env: {
    BYPASS_LIST: bypassList,	// 읽어온 내용을 환경 변수로 등록
  },
  ...
};&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;next.config.js 파일은 nexjs 프로그램이 서버에서 구동될 때 해당 내용이 실행되기 때문에 여기서는 fs 라이브러리를 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 정의한 내용을 읽어와서 모듈에서 환경 변수로 등록해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 이후 실행되는 파일에서는 process.env.BYPASS_LIST로 환경변수를 꺼내서 사용하는 것과 같이 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 서버에서 등록된 환경변수로 설정 파일을 내용을 가져다 쓸 수 있게 되었다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘도 하나 더 배웠다!&lt;/p&gt;</description>
      <category>Programming/Nextjs</category>
      <category>file 읽기</category>
      <category>nextjs</category>
      <category>SSR</category>
      <category>개발기록</category>
      <category>서버사이드랜더링</category>
      <category>설정파일읽기</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/398</guid>
      <comments>https://minarae7.tistory.com/entry/nextjs-%EC%84%9C%EB%B2%84-%EC%84%A4%EC%A0%95-%ED%8C%8C%EC%9D%BC-%EC%9D%BD%EA%B8%B0#entry398comment</comments>
      <pubDate>Thu, 27 Apr 2023 22:27:58 +0900</pubDate>
    </item>
    <item>
      <title>[베란다 농사] 베란다 정리, 강낭콩 싹</title>
      <link>https://minarae7.tistory.com/entry/%EB%B2%A0%EB%9E%80%EB%8B%A4-%EB%86%8D%EC%82%AC-%EB%B2%A0%EB%9E%80%EB%8B%A4-%EC%A0%95%EB%A6%AC-%EA%B0%95%EB%82%A8%EC%BD%A9-%EC%8B%B9</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;주말을 맞이하여서 두 번째 상추를 수확하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7667.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZcI9Y/btscvxMDHpo/5NA3u0ukDwOfH5g73eXhs0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZcI9Y/btscvxMDHpo/5NA3u0ukDwOfH5g73eXhs0/img.jpg&quot; data-alt=&quot;두 번째 수확한 상추&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZcI9Y/btscvxMDHpo/5NA3u0ukDwOfH5g73eXhs0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZcI9Y%2FbtscvxMDHpo%2F5NA3u0ukDwOfH5g73eXhs0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;800&quot; data-filename=&quot;IMG_7667.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;두 번째 수확한 상추&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상추 모종을 천원에 6개씩 팔아서 사다가 심었는데 이게 일주일에 한 번 정도 따서 먹을 정도는 되는 거 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직은 막 풍족하게 먹을 정도는 아니지만 그래도 일주일에 한 번 정도는 싱싱한 상추를 맛볼 수 있는 기회는 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날이 좀 더 더워지고 해가 들기 시작하면 본격적으로 크고 싱싱한 상추를 제공해 줄 것이라고 기대한다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7668.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8ov9Y/btsczJ5987U/sKrCMvN2CHizwpNfEJq15k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8ov9Y/btsczJ5987U/sKrCMvN2CHizwpNfEJq15k/img.jpg&quot; data-alt=&quot;상추를 수확한 후 화분&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8ov9Y/btsczJ5987U/sKrCMvN2CHizwpNfEJq15k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8ov9Y%2FbtsczJ5987U%2FsKrCMvN2CHizwpNfEJq15k%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7668.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;상추를 수확한 후 화분&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상추를 수확했는데 상추잎이 아직 남아있다. 저 잎들은 다음 주말을 위해서 남겨둔다. 다음 주말에도 싱싱한 상추를 기대하며.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바질도 꽤 싱싱하게 자라주고 있다. 물론 저렇게 다닥다닥 붙여서 심었더니 아래에 잎이 작은 친구들은 키 큰 아이들에게 가려 빛을 못 받고 있고, 잎이 너무 풍성하여서 아래쪽으로는 곰팡이들이 살자 보이기 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난번에 사온 라넌큘러스는 다 죽어버려서 뽑아버릴 심산이다. 바질은&amp;nbsp;화분으로 옮겨가야 되지 않을까 싶은 생각이다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7669.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eLJAes/btscBCTe5d4/ReQiQaKUXJdtENKEF8DJU0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eLJAes/btscBCTe5d4/ReQiQaKUXJdtENKEF8DJU0/img.jpg&quot; data-alt=&quot;화분 정리용 선반&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eLJAes/btscBCTe5d4/ReQiQaKUXJdtENKEF8DJU0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeLJAes%2FbtscBCTe5d4%2FReQiQaKUXJdtENKEF8DJU0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;800&quot; data-filename=&quot;IMG_7669.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;화분 정리용 선반&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어제 이케아에 갔다가 선반을 하나 들여왔다. 이런 높이가 좀 있는 선분을 사서 화분들을 올려두고 싶었는데 이케아에 간 김에 저렴한 선반으로 구매하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;층고가 높아야 화분이 들어갈 수 있을거 같아서 일부러 층고가 좀 있는 것으로 골랐는데 너비가 아쉬운 감이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금만 더 넓었으면 좋았을거 같은데 그래도 나름 괜찮은 녀석으로 골라왔고 베란다는 한결 깔끔해진 느낌이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7671-side.jpg&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/F8P7I/btscBBNyIjS/hlRUC5WSA8azpD13R0k090/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/F8P7I/btscBBNyIjS/hlRUC5WSA8azpD13R0k090/img.jpg&quot; data-alt=&quot;싹이 난 강남콩&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/F8P7I/btscBBNyIjS/hlRUC5WSA8azpD13R0k090/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF8P7I%2FbtscBBNyIjS%2FhlRUC5WSA8azpD13R0k090%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;800&quot; data-filename=&quot;IMG_7671-side.jpg&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;싹이 난 강남콩&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큰 아이 학교 숙제로 투명한 컵에 심은 강낭콩은 하나에서 싹이 나와서 올라오는 것이 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총 3개를 심었는데 하나에서는 싹이 터서 뿌리가 내려가고 싹이 올라오는 것이 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 하나는 밖으로 보이기는 하는데 아직 싹이 나지는 않았고 또 다른 녀석은 살짝 안쪽으로 심겼는지 통 보이지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3개가 모두 싹이 나면 이 녀석들은 또 어디로 이사를 가야하나 싶다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7675.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RG2DX/btscxbvl8Uc/0dQvAgGi6Gkc9ELdnK1yO1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RG2DX/btscxbvl8Uc/0dQvAgGi6Gkc9ELdnK1yO1/img.jpg&quot; data-alt=&quot;말라죽은 딸기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RG2DX/btscxbvl8Uc/0dQvAgGi6Gkc9ELdnK1yO1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRG2DX%2Fbtscxbvl8Uc%2F0dQvAgGi6Gkc9ELdnK1yO1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7675.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;말라죽은 딸기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;꽃이 꽤 여러 송이가 피었던 딸기는 두 모종 모두 잎이 말라서 죽어가고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 꽃까지 피었는지 찾아보니 저렇게 비료를 덕지덕지 주거나 물을 과습 하게 주면 과잉 영양으로 딸기는 말라죽는 경우가 생길 수 있다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 예민할 수가 있단 말인가. 잘 크라고 비료로 계분을 잔뜩 줬더니 과잉으로 말라죽다니.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주말까지 상태를 지켜보고 영 상태가 돌아오지 않으면 뽑고 다른 상추를 사다가 심어주어야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;식물들도 싹을 틔우는 거까지는 무난한데 그 다음 꽃을 보거나 열매를 얻을 때까지는 참 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭐든지 조급하면 안되는데 사람 마음이라는 게 그렇게 잘 안되니 원 참...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경험을 토대로 다른 녀석들이라도 잘 키워봐야겠다.&lt;/p&gt;</description>
      <category>My Story/식물재배기</category>
      <category>강남콩</category>
      <category>로메인상추</category>
      <category>베란다농사</category>
      <category>상추수확</category>
      <category>실험관찰</category>
      <category>이케아선반</category>
      <category>초등학교과제</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/397</guid>
      <comments>https://minarae7.tistory.com/entry/%EB%B2%A0%EB%9E%80%EB%8B%A4-%EB%86%8D%EC%82%AC-%EB%B2%A0%EB%9E%80%EB%8B%A4-%EC%A0%95%EB%A6%AC-%EA%B0%95%EB%82%A8%EC%BD%A9-%EC%8B%B9#entry397comment</comments>
      <pubDate>Mon, 24 Apr 2023 23:14:27 +0900</pubDate>
    </item>
    <item>
      <title>[가계부 만들기] DB 비동기 처리</title>
      <link>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-DB-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;cover.png&quot; data-origin-width=&quot;851&quot; data-origin-height=&quot;379&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mYtiN/btsb1RqICpO/l7d6u0ig9UmK0MaBKsL8Vk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mYtiN/btsb1RqICpO/l7d6u0ig9UmK0MaBKsL8Vk/img.png&quot; data-alt=&quot;FastAPI + SQLAlchemy&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mYtiN/btsb1RqICpO/l7d6u0ig9UmK0MaBKsL8Vk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmYtiN%2Fbtsb1RqICpO%2Fl7d6u0ig9UmK0MaBKsL8Vk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;851&quot; height=&quot;379&quot; data-filename=&quot;cover.png&quot; data-origin-width=&quot;851&quot; data-origin-height=&quot;379&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;FastAPI + SQLAlchemy&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램을 구현하다 보면 좀 더 효율적이고 빠르게 동작하는 프로그램으로 계속 수정해나가야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 프로그램의 퍼포먼스를 증가시켜서 더 사용자들에게 프로그램이 진화하고 있다는 것을 보여주는 것이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램을 개발하다 보면 보편적으로 가장 많은 리소스를 차지하는 것이 결국에는 IO 처리이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일을 읽고 쓰고, 네트워크로 데이터를 주고 받고 하는 일련의 과정들이 결국에는 IO인 셈이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이렇게 리소스를 많이 잡아먹고 대기시간을 길게하는 IO 작업을 한없이 기다리게 할 수 없을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 일반적으로는 이런 처리를 비동기로 처리하도록 개발한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동기와 비동기에 대한 차이를 여기서는 자세히 기술하지 않을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 설명하면 동기 방식으로 프로그램을 구현하면 네트워크에 질의를 하였을 때 응답이 올 때까지 대기하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 방식으로 구현하면 네트워크에 질의를 하고 응답이 올 때까지 기다리지 않고 다른 일을 먼저 처리하고 응답이 왔을 때 응답을 처리하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 내용은 아래 블로그 포스팅을 참조하면 된다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;figure id=&quot;og_1682176737975&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;동기, 비동기 처리&quot; data-og-description=&quot;데이터를 처리하는 방식인 동기, 비동기 처리에 대해 많은 글이 있지만 정확하게 와닿지가 않았다. 최대한 내가 이해한 방식대로 서술해 보려고 한다. 동기 (Synchronous)는 요청과 동시에 일어난다&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@daybreak/%EB%8F%99%EA%B8%B0-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC&quot; data-og-url=&quot;https://velog.io/@daybreak/동기-비동기-처리&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bDPOyi/hySmW4IAue/d4zJNMjIpCSOYrhke3hFB1/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/cv7c5p/hySm7yqhRD/jkHzJISO14kDSFB3dqH91k/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/bX96KQ/hySmUTmpzM/xwkKD23ciP5GA3ifzk2d6k/img.jpg?width=1800&amp;amp;height=1800&amp;amp;face=154_495_834_1237&quot;&gt;&lt;a href=&quot;https://velog.io/@daybreak/%EB%8F%99%EA%B8%B0-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@daybreak/%EB%8F%99%EA%B8%B0-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bDPOyi/hySmW4IAue/d4zJNMjIpCSOYrhke3hFB1/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/cv7c5p/hySm7yqhRD/jkHzJISO14kDSFB3dqH91k/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/bX96KQ/hySmUTmpzM/xwkKD23ciP5GA3ifzk2d6k/img.jpg?width=1800&amp;amp;height=1800&amp;amp;face=154_495_834_1237');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;동기, 비동기 처리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;데이터를 처리하는 방식인 동기, 비동기 처리에 대해 많은 글이 있지만 정확하게 와닿지가 않았다. 최대한 내가 이해한 방식대로 서술해 보려고 한다. 동기 (Synchronous)는 요청과 동시에 일어난다&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 가계부 프로그램을 만들다가 갑자기 동기/비동기 얘기는 왜 하느냐.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 구현한 프로그램은 디비를 조회할 때 동기방식으로 처리된다. 즉, mysql에 질의를 던졌을 때 응답이 올 때까지 대기하게 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 경우 규모가 작은 프로그램 경우 성능에 큰 차이가 없지만 추후 프로그램 규모가 커지고 사용자가 많아지면 성능이 떨어지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 때 프로그램을 수정하려면 많은 시간과 노력이 필요하기 때문에 처음 개발할 때 비동기로 개발을 시작하는 것이 유리하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서도 프로그램이 더 커지기 전에 동기 방식의 DB 질의를 비동기 방식으로 변경하고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 접속을 시도하는 models/connection.py 파일부터 수정하도록 한다.&lt;/p&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;pre id=&quot;code_1682177021670&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from sqlalchemy.orm import sessionmaker
import json
import os

from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.exc import SQLAlchemyError

BASE_DIR = os.path.dirname(os.path.abspath(__file__))

SERECT_FILE = os.path.join(BASE_DIR, 'secrets.json')
serects = json.loads(open(SERECT_FILE).read())
DB = serects[&quot;DB&quot;]

DB_URL = f&quot;mysql+aiomysql://{DB['user']}:{DB['password']}@{DB['host']}/{DB['database']}&quot;

engine = create_async_engine(DB_URL, echo=True, pool_pre_ping=True)
async_session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)

async def get_db() -&amp;gt; AsyncSession:
    async with async_session() as session:
        try:
            yield session
        except SQLAlchemyError as e:
            await session.rollback()
            raise e
        finally:
            await session.close()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sqlalchemy에서는 이미 비동기로 처리하기 위한 패키지를 포함하고 있다. 이와 관련된 패캐지들을 먼저 로드하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mysql에 접속하는 string도 mysql+pymysql에서 mysql+aiomysql로 변경하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에는 engine을 생성할 때 create_engine을 사용하였지만 이제 create_async_engine함수를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 밖에 get_db 함수 앞에 async를 붙여주고 반환되는 값의 타입도 Session에서 AsyncSession으로 변경되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 다음으로 database/models.py 파일을 열어서 가장 마지막 줄에 table을 생성해주던 코드를 아래와 같이 주석처리한다.&lt;/p&gt;
&lt;pre id=&quot;code_1682177413997&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#with engine.connect() as conn:
#    Base.metadata.create_all(conn)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 부분을 주석 처리하는 이유는 DB를 비동기로 처리하기로 하였기 때문에 함수를 호출할 때는 async를 붙여주어야 하는데 이건 함수에서만 사용할 수 있기 때문에 여기서 with 앞에는 async를 붙일 수 없기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이 역할을 하는 코드가 어딘가에 추가되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 프로그램에서는 main.py을 열어서 다음 코드를 추가하는 것으로 해결하였다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1682177593907&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from .database.connection import engine

...
@app.on_event(&quot;startup&quot;)
async def startup():
    async with engine.connect() as conn:
        await conn.run_sync(models.Base.metadata.create_all)

@app.on_event(&quot;shutdown&quot;)
async def shutdown_event():
    await engine.dispose()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드에서 fastapi 시작될 때와 종료될 때 이벤트를 설정하였다. 프로세스가 시작될 때 테이블 리스트를 검사하고 없는 테이블을 생성하도록 하였고, 종료될 때 접속을 정리하도록 하였다.'&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;on_event로 프로그램이 시작할 때 처리할 코드와 종료될 때 처리할 코드를 정의한다. 현재 fastapi 공식 문서에서는 두 함수를 앞으로 사용하지 말라고 되어있지만 아직까지 사용하는데는 무리가 없다.&lt;/p&gt;
&lt;figure id=&quot;og_1682255605564&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Lifespan Events - FastAPI&quot; data-og-description=&quot;Lifespan Events Warning The current page still doesn't have a translation for this language. But you can help translating it: Contributing. You can define logic (code) that should be executed before the application starts up. This means that this code will&quot; data-og-host=&quot;fastapi.tiangolo.com&quot; data-og-source-url=&quot;https://fastapi.tiangolo.com/ko/advanced/events/&quot; data-og-url=&quot;https://fastapi.tiangolo.com/ko/advanced/events/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://fastapi.tiangolo.com/ko/advanced/events/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://fastapi.tiangolo.com/ko/advanced/events/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Lifespan Events - FastAPI&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Lifespan Events Warning The current page still doesn't have a translation for this language. But you can help translating it: Contributing. You can define logic (code) that should be executed before the application starts up. This means that this code will&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;fastapi.tiangolo.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 마지막으로 실제로 쿼리를 질의하여서 결과를 얻어야 하는 곳에서의 코드를 수정하여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;membes_service.py 파일을 수정할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 곳에서는 수정할 내용이 많기는 하지만 내용은 간단한다. 이 파일을 수정하는 패턴은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 파라미터로 정의한 db가 이전에는 Session 형이었지만 이제 AsyncSession으로 바뀌었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 모든 함수의 db 파라미터는 Session 형에서 AsyncSession형으로 변경한다.&lt;/p&gt;
&lt;pre id=&quot;code_1682255842579&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async def create_member(db: AsyncSession, member: schemas.MemberCreate):
    ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 함수를 선언할 때 해당 함수들은 비동기 함수가 아니였음에도 모두 async를 붙여서 선언하였기 때문에 이 부분에 대해서는 수정할 것이 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 db에 질의를 던지는 부분에 모두 await를 붙여주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래는 이렇게 처리하지 않기 위해서 비동기로 처리하는 것인데, 해당 코드에서는 당장 비동기로 처리할 만큼 독립적인 코드가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 DB에 질의하는 모든 코드 앞에 await를 붙여주어도 무방하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;db.execute, db.commit, db.refresh 함수 앞에 await를 붙이면 된다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1682256117031&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async def create_member(db: AsyncSession, member: schemas.MemberCreate):
    # 아이디가 중복되는 계정이 있는지 확인
    stmt = select(models.Members.member_id).filter(models.Members.member_id == member.member_id)
    result = await db.execute(stmt)

    list = result.fetchall()
    if len(list) &amp;gt; 0:
        raise Exception(&quot;아이디가 이미 사용 중입니다&quot;)

    # 패스워드 해싱 처리
    password_hash = auth.get_password_hash(member.member_pw)

    # DB 저장
    db_member = models.Members(**member.dict(exclude={&quot;member_pw&quot;}), member_pw=password_hash)
    db.add(db_member)
    await db.commit()
    await db.refresh(db_member)

    return db_member&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자를 추가하는 코드인데 위에서 설명한 것과 같이 관련 함수를 사용하는 곳에서 모두 await를 붙여서 결과가 올 때까지 기다리도록 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 변경된 members_service.py 파일 전체 내용이다. 참고하시면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1682256224007&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.sql import func
from datetime import timedelta
from ..database import models, schemas
from ..libraries import auth

ACCESS_TOKEN_EXPIRE_MINUTES = 60
REFRESH_TOKEN_EXPIRE_HOURS = 24

async def create_member(db: AsyncSession, member: schemas.MemberCreate):
    # 아이디가 중복되는 계정이 있는지 확인
    stmt = select(models.Members.member_id).filter(models.Members.member_id == member.member_id)
    result = await db.execute(stmt)

    list = result.fetchall()
    if len(list) &amp;gt; 0:
        raise Exception(&quot;아이디가 이미 사용 중입니다&quot;)

    # 패스워드 해싱 처리
    password_hash = auth.get_password_hash(member.member_pw)

    # DB 저장
    db_member = models.Members(**member.dict(exclude={&quot;member_pw&quot;}), member_pw=password_hash)
    db.add(db_member)
    await db.commit()
    await db.refresh(db_member)

    return db_member

# login 처리
async def login_proc(db: AsyncSession, member_id: str, member_pw: str):
    # 해당 아이디가 있는지 찾는다/
    stmt = select(models.Members).filter(models.Members.member_id == member_id, models.Members.is_deleted == 'F')
    result = await db.execute(stmt)

    db_member = result.fetchone()
    if db_member is None:
        raise Exception(&quot;해당하는 아이디를 찾을 수 없습니다&quot;)

    if auth.verify_password(member_pw, db_member.Members.member_pw) == False:
        raise Exception(&quot;패스워드가 일치하지 않습니다.&quot;)

    return make_login_response(db_member)


# refresh token 처리
async def member_refresh(db: AsyncSession, refresh_token: schemas.Refresh):
    # refresh token 검사
    try:
        payload = auth.decode_refresh_token(refresh_token.refresh_token)
    except Exception:
        raise Exception

    # 해당 회원이 있는지 검사
    stmt = select(models.Members).filter(models.Members.member_no == payload['member_no'], models.Members.is_deleted == 'F')
    result = await db.execute(stmt)

    db_member = result.fetchone()
    if db_member is None:
        raise Exception(&quot;해당하는 아이디를 찾을 수 없습니다&quot;)

    return make_login_response(db_member)


def make_login_response(db_member):
    data = {
        &quot;member_no&quot;: db_member.Members.member_no,
        &quot;member_id&quot;: db_member.Members.member_id,
        &quot;member_name&quot;: db_member.Members.member_name,
        &quot;member_email&quot;: db_member.Members.member_email
    }
    data[&quot;access_token&quot;] = auth.create_access_token(data, timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES))
    data[&quot;refresh_token&quot;] = auth.create_access_token({
        &quot;member_no&quot;: data[&quot;member_no&quot;],
        &quot;member_id&quot;: data[&quot;member_id&quot;]
    }, timedelta(hours=REFRESH_TOKEN_EXPIRE_HOURS))

    return data


async def member_modify(db: AsyncSession, member: schemas.MemberModify, payload: schemas.JWTPayload):
    # 회원이 존재하는 아이디인지 확인
    result = await db.execute(select(models.Members).filter_by(member_no = payload['member_no'], is_deleted = 'F'))
    db_member = result.scalars().first()

    if db_member is None:
        raise Exception(&quot;해당하는 회원 정보를 찾을 수 없습니다.&quot;)

    member_info =  member.dict()
    member = {k: v for k, v in member_info.items()}
    for key, value in member.items():
        if value is None:
            continue

        if key == 'member_pw':
            setattr(db_member, key, auth.get_password_hash(value))
        else:
            setattr(db_member, key, value)

    await db.commit()
    return db_member


async def member_delete(db: AsyncSession, payload: schemas.JWTPayload):
    # 회원이 존재하는 아이디인지 확인
    db_member = await db.query(models.Members).filter_by(member_no = payload['member_no'], is_deleted = 'F').first()

    if db_member is None:
        raise Exception(&quot;해당하는 회원 정보를 찾을 수 없습니다.&quot;)

    setattr(db_member, 'is_deleted', 'T')
    setattr(db_member, 'del_dt', func.now())

    await db.commit()
    return db_member&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 프로그램이 정상 작동하는지만 확인하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서서 설명했던 것과 같이 docs 페이지에서 각 기능이 잘 작동하는지만 확인해보면 된다. 여기서는 이미 검증했던 기능들이서 생략한다.&lt;/p&gt;</description>
      <category>Programming/Project Log</category>
      <category>FastAPI</category>
      <category>MySQL</category>
      <category>python</category>
      <category>sqlalchemy</category>
      <category>비동기처리</category>
      <category>파이썬</category>
      <category>프로젝트 기록</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/396</guid>
      <comments>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-DB-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC#entry396comment</comments>
      <pubDate>Sun, 23 Apr 2023 22:29:43 +0900</pubDate>
    </item>
    <item>
      <title>[제품후기] 전동분무기</title>
      <link>https://minarae7.tistory.com/entry/%EC%A0%9C%ED%92%88%ED%9B%84%EA%B8%B0-%EC%A0%84%EB%8F%99%EB%B6%84%EB%AC%B4%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;베란다에서 화분을 키우기 시작하면서 이 화분들에게 물을 주는게 또 일이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 해보니 물을 줄 때 확 주면 물 때문에 흙이 파여버리거나 식물이 손상을 입을 수 있다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 물을 줄 때는 분무기를 쓴다고.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;집에 있는 분무기는 아이들 머리 빗길 때 사용하는 분무기로 잡고 분무양도 적다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 이러면 물을 줄 때 손목이 많이 아프다. 처음에 몇 번 펌프질 하다가 보면 벌써 팔이 아파온다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7595.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwCQhH/btsbHjCj4rT/CUhz52sqPxIv68Ced3mcN0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwCQhH/btsbHjCj4rT/CUhz52sqPxIv68Ced3mcN0/img.jpg&quot; data-alt=&quot;작고 소중한 분무기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwCQhH/btsbHjCj4rT/CUhz52sqPxIv68Ced3mcN0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwCQhH%2FbtsbHjCj4rT%2FCUhz52sqPxIv68Ced3mcN0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7595.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;작고 소중한 분무기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 계속 하면 팔이 꽤 두꺼워질듯 하다. 그러다가 광고에서 전동분무기라는 것이 있다는 사실을 알게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;온갖 광고에서는 나에게 필요한 물건을 그때그때 찾아준다. 정말 신기하고 소중하다~!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 생각보다 가격이 만만치않다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-21 오후 4.03.26.png&quot; data-origin-width=&quot;1982&quot; data-origin-height=&quot;1106&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nDINK/btsbOXrTbL8/muHEiLyDmsvdHeHDeQjPy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nDINK/btsbOXrTbL8/muHEiLyDmsvdHeHDeQjPy1/img.png&quot; data-alt=&quot;네이버 쇼핑 전동분무기 검색결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nDINK/btsbOXrTbL8/muHEiLyDmsvdHeHDeQjPy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnDINK%2FbtsbOXrTbL8%2FmuHEiLyDmsvdHeHDeQjPy1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1982&quot; height=&quot;1106&quot; data-filename=&quot;스크린샷 2023-04-21 오후 4.03.26.png&quot; data-origin-width=&quot;1982&quot; data-origin-height=&quot;1106&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;네이버 쇼핑 전동분무기 검색결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 이렇게 많은 돈을 투자할 생각이 없다. 가뜩이나 이것저것 사서 돈도 많이 썼는데 이것마저 2만원이 넘게 주고 살 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;찾아보니 샤오미 분무기도 있는데 이것도 싸지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 알리를 찾아보기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알리에서 검색해보니 그래도 제법 가격이 저렴한 애들이 있다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-21 오후 4.08.36.png&quot; data-origin-width=&quot;2348&quot; data-origin-height=&quot;1542&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N2h3Z/btsbQ2k4V2r/7U9RYJFQo96XSfWVDkEw01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N2h3Z/btsbQ2k4V2r/7U9RYJFQo96XSfWVDkEw01/img.png&quot; data-alt=&quot;알리익스프레스&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N2h3Z/btsbQ2k4V2r/7U9RYJFQo96XSfWVDkEw01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN2h3Z%2FbtsbQ2k4V2r%2F7U9RYJFQo96XSfWVDkEw01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2348&quot; height=&quot;1542&quot; data-filename=&quot;스크린샷 2023-04-21 오후 4.08.36.png&quot; data-origin-width=&quot;2348&quot; data-origin-height=&quot;1542&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;알리익스프레스&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제일 저렴한 녀석으로 찾아보니 배송료까지 해서 10달러 정도. 한화로 14000원 정도이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환율이 높아져서 그나마 조금 더 비싸졌지만 그래도 국내에서 똑같은 모양 제품보다 만원은 저렴하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 해서 4월 1일에 주문했는데 세월아 네월에 출발도 안하고 애간장을 녹이더니 4월 16일되어서야 출발했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 어제 4월 20일 주문한지 대략 20일이 다 되어서 도착하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제품을 받아봤으니 써봐야지.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7641.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kWrY2/btsbQ2yC0Kq/iVl82xhcs9NAi2hdapij30/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kWrY2/btsbQ2yC0Kq/iVl82xhcs9NAi2hdapij30/img.jpg&quot; data-alt=&quot;전동분무기 사용기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kWrY2/btsbQ2yC0Kq/iVl82xhcs9NAi2hdapij30/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkWrY2%2FbtsbQ2yC0Kq%2FiVl82xhcs9NAi2hdapij30%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;800&quot; data-filename=&quot;IMG_7641.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;전동분무기 사용기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 물이 많이 들어간다. 900ml가 들어가니 그만큼 무겁지만 한 번에 물을 많이 줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;펌프질을 계속하지 않아도 되니 팔이 안 아프다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 동영상으로.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;

            &lt;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&gt;
            &lt;/figure&gt;
        
&lt;p data-ke-size=&quot;size16&quot;&gt;버튼을 누르고 있으면 물이 분무되어서 흥건하게 물을 줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;식물들에 물을 주는게 훨씬 간편해졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;20일 걸려서 받았지만 저렴하게 편리한 제품을 잘 산거 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 이것보다 빨리 받을 수 있는 제품도 있지만 그럼 가격이 올라가서 국내제품과의 가격 경쟁력이 떨어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 이번에 구매한 제품 링크이다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;figure id=&quot;og_1682061385481&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;product&quot; data-og-title=&quot;9251.0₩ 26% OFF|전기 식물 스프레이 병 자동 급수 안개, USB 전기 살균 분무기, 손 급수 기계, 식물 &quot; data-og-description=&quot;Smarter Shopping, Better Living! Aliexpress.com&quot; data-og-host=&quot;ko.aliexpress.com&quot; data-og-source-url=&quot;https://ko.aliexpress.com/item/1005004124218764.html?spm=a2g0o.new_account_index.0.0.d54f1f98MaGF1G&amp;amp;gatewayAdapt=glo2kor&quot; data-og-url=&quot;https://ko.aliexpress.com/item/1005004124218764.html?src=ibdm_d03p0558e02r02&amp;amp;sk=&amp;amp;aff_platform=&amp;amp;aff_trace_key=&amp;amp;af=&amp;amp;cv=&amp;amp;cn=&amp;amp;dp=&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cfr5UT/hySlLQsqgk/rXU8zmMyzVrp09XnkKMQAK/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://ko.aliexpress.com/item/1005004124218764.html?spm=a2g0o.new_account_index.0.0.d54f1f98MaGF1G&amp;amp;gatewayAdapt=glo2kor&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ko.aliexpress.com/item/1005004124218764.html?spm=a2g0o.new_account_index.0.0.d54f1f98MaGF1G&amp;amp;gatewayAdapt=glo2kor&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cfr5UT/hySlLQsqgk/rXU8zmMyzVrp09XnkKMQAK/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;9251.0₩ 26% OFF|전기 식물 스프레이 병 자동 급수 안개, USB 전기 살균 분무기, 손 급수 기계, 식물&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Smarter Shopping, Better Living! Aliexpress.com&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ko.aliexpress.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배송료 포함 10달러 정도이고 이 제품과 동일하지만 약간씩 비싼 제품도 많이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;돈과 배송시간은 본인 선택이다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;덧. 토마토가 이사를 갔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장모님댁에서 크고 깊은 화분을 가져다가 집에 있던 흙을 모두 부어서 새로 자리를 마련해줬다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7644.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/djOOea/btsbQHBy9Nk/ctqu6kZbeao3NspmoJtGnk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/djOOea/btsbQHBy9Nk/ctqu6kZbeao3NspmoJtGnk/img.jpg&quot; data-alt=&quot;큰 화분으로 이사간 토마토&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/djOOea/btsbQHBy9Nk/ctqu6kZbeao3NspmoJtGnk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdjOOea%2FbtsbQHBy9Nk%2Fctqu6kZbeao3NspmoJtGnk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;800&quot; data-filename=&quot;IMG_7644.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;큰 화분으로 이사간 토마토&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이 화분에서 잘 자라서 싱싱하고 맛있는 방울토마토를 생성해줄 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옮겨 심고 물은 새로 산 분무기로~!!&lt;/p&gt;</description>
      <category>My Story/제품리뷰</category>
      <category>방울토마토</category>
      <category>베란다농사</category>
      <category>식물물주기</category>
      <category>알리익스프레스</category>
      <category>전동분무기</category>
      <category>제품후기</category>
      <category>직구</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/395</guid>
      <comments>https://minarae7.tistory.com/entry/%EC%A0%9C%ED%92%88%ED%9B%84%EA%B8%B0-%EC%A0%84%EB%8F%99%EB%B6%84%EB%AC%B4%EA%B8%B0#entry395comment</comments>
      <pubDate>Fri, 21 Apr 2023 16:19:44 +0900</pubDate>
    </item>
    <item>
      <title>[강낭콩 키우기] 강낭콩 심기</title>
      <link>https://minarae7.tistory.com/entry/%EA%B0%95%EB%82%A8%EC%BD%A9-%ED%82%A4%EC%9A%B0%EA%B8%B0-%EA%B0%95%EB%82%A8%EC%BD%A9-%EC%8B%AC%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;어제 퇴근길에 큰 아이에게 전화가 왔다. 내용은 학교 과제로 강낭콩 키우기가 나왔으니 쥬시에 들려서 음료수를 사 오라는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 무슨 말이냐면 투명컵에 강낭콩을 심고 이게 자라는 것을 관찰해야 하니 음료수를 사 와서 음료는 먹고 투명컵에 심겠다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학교 과제라고 하니 해드려야 한다. 2 정거장 전에 내려서 쥬시에 가서 딸바를 사서 귀가하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수영 강습이 있는 날이라 강습을 다녀오고 나니 음료수는 다 먹었으니 이제 강낭콩을 심어야 한다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 컵을 깨끗이 씻어야 한다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7626.jpg&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dgCaHQ/btsbElL6Spq/sEgjjBDKQNQ0KVL4BkfcRk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dgCaHQ/btsbElL6Spq/sEgjjBDKQNQ0KVL4BkfcRk/img.jpg&quot; data-alt=&quot;강남콩 심을 컵&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dgCaHQ/btsbElL6Spq/sEgjjBDKQNQ0KVL4BkfcRk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdgCaHQ%2FbtsbElL6Spq%2FsEgjjBDKQNQ0KVL4BkfcRk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;600&quot; data-filename=&quot;IMG_7626.jpg&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;강남콩 심을 컵&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컵은 준비가 되었으니 이제 4/5 정도 흙을 채워야 한다. 마침 집에 남는 흙이 좀 있으니 그 흙으로 채우기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모종삽으로 흙은 채운다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7627.jpg&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcLg49/btsbAssybLV/uLZklxRIJD0GEDpqKsikZk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcLg49/btsbAssybLV/uLZklxRIJD0GEDpqKsikZk/img.jpg&quot; data-alt=&quot;흙으로 채운 컵&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcLg49/btsbAssybLV/uLZklxRIJD0GEDpqKsikZk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcLg49%2FbtsbAssybLV%2FuLZklxRIJD0GEDpqKsikZk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;600&quot; data-filename=&quot;IMG_7627.jpg&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;흙으로 채운 컵&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 어느 정도 흙을 채웠으니 강남콩을 심을 차례이다. 흙이 잘 담길 수 있도록 흔들고 약간 눌러서 흙을 다져준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 공간이 비어있지 않게 되니 다져서 흙은 단단하게 해 주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 강낭콩을 심자.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7628.jpg&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G2zLW/btsbBYD9cDG/AQs63L3lOfBk7XTov29VH0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G2zLW/btsbBYD9cDG/AQs63L3lOfBk7XTov29VH0/img.jpg&quot; data-alt=&quot;강남콩 담기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G2zLW/btsbBYD9cDG/AQs63L3lOfBk7XTov29VH0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG2zLW%2FbtsbBYD9cDG%2FAQs63L3lOfBk7XTov29VH0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;600&quot; data-filename=&quot;IMG_7628.jpg&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;강남콩 담기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강낭콩은 학교에서 줬다고 한다. 학교에서 가져와서 미리 물에 불려두었다니 잘 배웠구나.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콩을 심을 때는 원래 가운데 심는데 이건 키우는 게 목적이 아니고 관찰이 목적이니 컵 가장자리에 붙여서 심는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래야 투명컵 밖으로 콩이 자라는 모습을 볼 수 있을 것이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 흙이불을 덮어주자.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7630.jpg&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWNkc5/btsbzJ8PX7t/nGVqFhOEKxcfcEK6XlPWe1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWNkc5/btsbzJ8PX7t/nGVqFhOEKxcfcEK6XlPWe1/img.jpg&quot; data-alt=&quot;흙으로 덮은 강남콩&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWNkc5/btsbzJ8PX7t/nGVqFhOEKxcfcEK6XlPWe1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWNkc5%2FbtsbzJ8PX7t%2FnGVqFhOEKxcfcEK6XlPWe1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;600&quot; data-filename=&quot;IMG_7630.jpg&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;흙으로 덮은 강남콩&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 컵 가장자리로 강낭콩이 보이도록 잘 배치하고 흙을 덮어주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흙을 덮어주고서 마찬가지로 흙을 조금씩 눌러서 다져주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 물을 주어서 싹이 날 수 있는 환경을 만들어주자.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7639.jpg&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4to0i/btsbFG3w1yk/dSInzwkebufOSa5TKRihS1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4to0i/btsbFG3w1yk/dSInzwkebufOSa5TKRihS1/img.jpg&quot; data-alt=&quot;물주기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4to0i/btsbFG3w1yk/dSInzwkebufOSa5TKRihS1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4to0i%2FbtsbFG3w1yk%2FdSInzwkebufOSa5TKRihS1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;600&quot; data-filename=&quot;IMG_7639.jpg&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;물주기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물을 너무 한 번에 주면 흙이 파일 수 있기 때문에 분무기로 조금씩 뿌려준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자기 학교 과제이지만 전체 과정은 아빠가 하고 마무리만 자기가 한다. 이러면 다 자기가 한 셈이 되나?&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7646.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5GlSl/btsbFHnQjwy/H4JUGFKH3vUPnt2gPZpwHk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5GlSl/btsbFHnQjwy/H4JUGFKH3vUPnt2gPZpwHk/img.jpg&quot; data-alt=&quot;심기 완료&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5GlSl/btsbFHnQjwy/H4JUGFKH3vUPnt2gPZpwHk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5GlSl%2FbtsbFHnQjwy%2FH4JUGFKH3vUPnt2gPZpwHk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;800&quot; data-filename=&quot;IMG_7646.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;심기 완료&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어제는 밤에 심었는데 낮에 보니 그래도 제법 잘 심은 거 같다. 밑이 뚫려있지 않기 때문에 아래에 물이 고이지 않도록 적당하게 물을 주어야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2~3일에 한 번씩 주면 되지 않을까 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;베란다에 바질, 상추, 딸기, 토마토, 강낭콩까지 수확해서 먹을 수 있는 애들이 많아졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 아이들이 잘 커서 우리 밥상을 장식해 주기를 기원해 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>My Story/식물재배기</category>
      <category>강남콩심기</category>
      <category>관찰일기</category>
      <category>베란다농사</category>
      <category>초등학교체험활동</category>
      <category>학교과제</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/394</guid>
      <comments>https://minarae7.tistory.com/entry/%EA%B0%95%EB%82%A8%EC%BD%A9-%ED%82%A4%EC%9A%B0%EA%B8%B0-%EA%B0%95%EB%82%A8%EC%BD%A9-%EC%8B%AC%EA%B8%B0#entry394comment</comments>
      <pubDate>Thu, 20 Apr 2023 23:03:53 +0900</pubDate>
    </item>
    <item>
      <title>[python-pptx] 파이썬을 이용한 파워포인트 차트 수정</title>
      <link>https://minarae7.tistory.com/entry/python-pptx-%ED%8C%8C%EC%9D%B4%EC%8D%AC%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%8C%8C%EC%9B%8C%ED%8F%AC%EC%9D%B8%ED%8A%B8-%EC%B0%A8%ED%8A%B8-%EC%88%98%EC%A0%95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램 코드를 통해서 파워포인트 파일을 수정해야할 일이 생겨서 자료를 찾아보니 python에서는 python-pptx라는 모듈을 활용하는 것을 찾았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;늘 마찬가지로 내용을 잊어먹기 전에 포스팅으로 정리해두도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 할 일은 2장 짜리 pptx 파일에서 2번째 슬라이드에 막대 그래프의 값을 변경하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;슬라이드는 다음과 같이 생겼다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-17 오후 10.43.35.png&quot; data-origin-width=&quot;2140&quot; data-origin-height=&quot;1200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EvR7s/btsaUhKi2bs/rN3sO9yz9ydZtIfeexyiL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EvR7s/btsaUhKi2bs/rN3sO9yz9ydZtIfeexyiL1/img.png&quot; data-alt=&quot;수정할 슬라이드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EvR7s/btsaUhKi2bs/rN3sO9yz9ydZtIfeexyiL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEvR7s%2FbtsaUhKi2bs%2FrN3sO9yz9ydZtIfeexyiL1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2140&quot; height=&quot;1200&quot; data-filename=&quot;스크린샷 2023-04-17 오후 10.43.35.png&quot; data-origin-width=&quot;2140&quot; data-origin-height=&quot;1200&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;수정할 슬라이드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서에서 연도별 수익으로 표기된 부분의 y-axis 인덱스 이름과 각 표기된 값을 수정하는 코드를 작성할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 pptx라는 모듈을 설치하도록 한다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1681739165179&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;% pip install python-pptx&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치할 때 모듈의 이름은 python-pptx이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 코드를 작성하도록 한다. 파일 이름은 test.pptx로 하였다. 우선 파일을 읽어들이는 부분을 작성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1681739245268&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from pptx import Presentation

# pptx 파일 열기
ppt = Presentation('test.pptx')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈을 로딩하고 수정할 파일을 읽어들이는 작업을 한다. 위의 코드에서 Presentation을 선언할 때 파일 이름을 주지 않으면 새 파일을 생성하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 기존 파일을 수정하는 것을 목적으로 하고 있기 때문에 파일 이름을 지정하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 로딩한 파일에서 수정할 슬라이드를 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 테스트로 해당 슬라이드의 제목 줄의 내용을 수정해보았다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1681739416662&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 두 번째 슬라이드의 제목 변경
slide = ppt.slides[1]
title = slide.shapes.title
title.text = '슬라이드 제목 변경'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아주 기초적인 내용이지만 슬라이드를 찾을 때도 1부터 찾는 것이 아니고 0부터 찾는다. python에서는 이것을 리스트로 지정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 첫 슬라이드 인덱스 값은 0이다. 여기서는 두 번째 슬라이드를 찾는 것이기 때문에 1로 지정하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;슬라이드에서 포함되어있는 제목을 찾아서 &quot;슬라이드 제목 변경&quot;으로 텍스트를 수정하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아마도 제목 줄은 특별한 객체여서 이렇게 지정할 수 있는듯 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 코드로 해당 슬라이드에 포함된 shapes 객체를 순차적으로 검색해서 차트가 어떤 것이 찾아보도록 한다.&lt;/p&gt;
&lt;div&gt;&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;pre id=&quot;code_1681739619075&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 슬라이드에서 차트를 찾기 위해 모든 Shape 객체를 확인
for shape in slide.shapes:
    print(shape)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일에서 이렇게 순차적으로 돌려보면 아래 그림과 같이 실행 결과가 나온다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-17 오후 10.54.50.png&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;222&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBh8r7/btsaKy0VdmL/QE0lYz2g02AjgvBkVM1DJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBh8r7/btsaKy0VdmL/QE0lYz2g02AjgvBkVM1DJ1/img.png&quot; data-alt=&quot;코드 실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBh8r7/btsaKy0VdmL/QE0lYz2g02AjgvBkVM1DJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBh8r7%2FbtsaKy0VdmL%2FQE0lYz2g02AjgvBkVM1DJ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;469&quot; height=&quot;120&quot; data-filename=&quot;스크린샷 2023-04-17 오후 10.54.50.png&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;222&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;코드 실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 우리는 Chart를 찾으면 된다. 검색을 통해서 찾아보니 각 shape에는 shape_type이라는 속성을 가지고 있고 여기서 수정할 chart라는 shape_type은 3이라는 것을 알 수 있었다. 이건 위 코드에서 print(shape, shape.shape_type) 이렇게 해서 찍어보면 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 코드를 이렇게 수정할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1681739979586&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 슬라이드에서 차트를 찾기 위해 모든 Shape 객체를 확인
for shape in slide.shapes:
    print(shape)
    if shape.shape_type == 3:  # 차트는 shape_type 11에 해당
        print(shape.chart)
        chart = shape.chart&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;슬라이드 안에서 수정할 shape를 찾아서 해당 shape가 가지고 있는 chart 속성을 chart라는 변수에 담아두었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 코드로 chart 안에 있는 값을 직접 수정하려고 하였더니 해당 변수들은 tuple로 되어있어서 직접 수정할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 데이터 변수를 선언하고 해당 값으로 대체해주는 코드로 작성하여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 코드는 아래와 같이 작성할 수 있다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1681740174409&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from pptx.chart.data import ChartData
# 슬라이드에서 차트를 찾기 위해 모든 Shape 객체를 확인
for shape in slide.shapes:
    if shape.shape_type == 3:  # 차트는 shape_type 3에 해당
        chart = shape.chart
        # 새로운 차트 데이터 생성
        chart_data = ChartData()
        chart_data.categories = ['2020년', '2021년', '2022년', '2023년']
        chart_data.add_series('Series 1', (10000.0, 23000.0, 34000.0, 23000.0))
        # 새로 정의한 chart_data로 대체
        chart.replace_data(chart_data)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;categories를 새로 선언해서 y-axis에 표기될 값을 수정하였고 차트에 포함될 데이터를 수정하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 마지막으로 chart 데이터를 교체해주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 수정한 내용을 저장하여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저장하는 코드는 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1681740247831&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# pptx 파일 저장
ppt.save('example_edited.pptx')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 새로운 파일 이름으로 저장하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 파일에 실행하고 나면 다음 그림에서 해당 내용이 수정된 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-17 오후 11.05.08.png&quot; data-origin-width=&quot;2660&quot; data-origin-height=&quot;1496&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AX9Bh/btsaVdOy6Zn/Ws7kIYXcwUWrMWMKCjjMX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AX9Bh/btsaVdOy6Zn/Ws7kIYXcwUWrMWMKCjjMX1/img.png&quot; data-alt=&quot;실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AX9Bh/btsaVdOy6Zn/Ws7kIYXcwUWrMWMKCjjMX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAX9Bh%2FbtsaVdOy6Zn%2FWs7kIYXcwUWrMWMKCjjMX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2660&quot; height=&quot;1496&quot; data-filename=&quot;스크린샷 2023-04-17 오후 11.05.08.png&quot; data-origin-width=&quot;2660&quot; data-origin-height=&quot;1496&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음의 위 코드를 정리한 전체 python 코드이다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1681740390437&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from pptx import Presentation
from pptx.chart.data import ChartData

# pptx 파일 열기
ppt = Presentation('test.pptx')

# 두 번째 슬라이드의 제목 변경
slide = ppt.slides[1]
title = slide.shapes.title
title.text = '슬라이드 제목 변경'

# 슬라이드에서 차트를 찾기 위해 모든 Shape 객체를 확인
for shape in slide.shapes:
    if shape.shape_type == 3:  # 차트는 shape_type 3에 해당
        chart = shape.chart
        # 새로운 차트 데이터 생성
        chart_data = ChartData()
        chart_data.categories = ['2020년', '2021년', '2022년', '2023년']
        chart_data.add_series('Series 1', (10000.0, 23000.0, 34000.0, 23000.0))
        # 새로 정의한 chart_data로 대체
        chart.replace_data(chart_data)

# pptx 파일 저장
ppt.save('example_edited.pptx')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 파워포인트 파일을 수정할 일이 계속 있을 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 내용을 계속 추가적으로 정리해보도록 하겠다.&lt;/p&gt;</description>
      <category>Programming/Python</category>
      <category>pptx 수정하기</category>
      <category>python</category>
      <category>python-pptx</category>
      <category>파워포인트 수정 코드 작성</category>
      <category>파이썬</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/393</guid>
      <comments>https://minarae7.tistory.com/entry/python-pptx-%ED%8C%8C%EC%9D%B4%EC%8D%AC%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%8C%8C%EC%9B%8C%ED%8F%AC%EC%9D%B8%ED%8A%B8-%EC%B0%A8%ED%8A%B8-%EC%88%98%EC%A0%95#entry393comment</comments>
      <pubDate>Mon, 17 Apr 2023 23:09:24 +0900</pubDate>
    </item>
    <item>
      <title>[베란다 농사] 봉선화 옮겨심기, 상추 수확</title>
      <link>https://minarae7.tistory.com/entry/%EB%B4%89%EC%84%A0%ED%99%94-%EC%98%AE%EA%B2%A8%EC%8B%AC%EA%B8%B0%EC%83%81%EC%B6%94-%EC%88%98%ED%99%95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 봉선화들이 너무 많이 난다고 장모님이 봉선화 몇 뿌리를 뽑아서 다른 화분에 옮겨 심어주셨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 어제 그 화분에서 벌레가 나는걸 보고 살충제를 좀 뿌려서 벌레를 잡으려고 했다. 근데 이게 이러면 안되나보다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘 아침에 보니 이 화분에 봉선화들이 다 잎이 말라서 죽어버렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아마도 살충제를 맞으면서 숨을 못 쉬어서 그대로 죽어버린 것이 아닐까 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 죽은 애들은 뽑아버리고 다시 봉선화 화분에 튼튼해 보이는 애들로 골라서 이 화분에 옮겨심어주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심고 마트에 다녀와보니 얘들이 같이 시들시들해져버렸다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7608.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ca1lhk/btsaKyZnLHg/jA4dVygwvUalDtoCgkleQK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ca1lhk/btsaKyZnLHg/jA4dVygwvUalDtoCgkleQK/img.jpg&quot; data-alt=&quot;시들시들한 봉선화&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ca1lhk/btsaKyZnLHg/jA4dVygwvUalDtoCgkleQK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fca1lhk%2FbtsaKyZnLHg%2FjA4dVygwvUalDtoCgkleQK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7608.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;시들시들한 봉선화&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옮겨심는걸 잘 못하는거 같다. 옮겨심기만 하면 상태들이 안 좋아진다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 급히 물을 주고 자리를 잡을 수 있게 흙은 단단하게 고정해주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흙에 뿌리를 내리고 단단해지면 다시 튼튼하게 자라기를 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 원래 봉선화 씨를 심어서 싹이 난 화분은 어떻게 해야할지 모르겠다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7612.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNxGHE/btsaw7uCFE2/zuuMLZBbzbbHhmIP7Fgbk1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNxGHE/btsaw7uCFE2/zuuMLZBbzbbHhmIP7Fgbk1/img.jpg&quot; data-alt=&quot;원래 봉선화 화분&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNxGHE/btsaw7uCFE2/zuuMLZBbzbbHhmIP7Fgbk1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNxGHE%2Fbtsaw7uCFE2%2FzuuMLZBbzbbHhmIP7Fgbk1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7612.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;원래 봉선화 화분&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애들이 키는 컸는데 아직 줄기가 가늘가늘 해서 어디에 옮겨심기도 조심스럽다. 근데 여기가 좁아서 더 못 크는거 같기도 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 일단 좀 더 지켜보고 큰 화분에 옮겨주던지 몇 개씩 나눠서 작은 화분 여러 개에 심을지 고민해봐야겠다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7613.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mOajv/btsamWtGFTX/CkpJ05s1NKVZZ9tFXcAKD0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mOajv/btsamWtGFTX/CkpJ05s1NKVZZ9tFXcAKD0/img.jpg&quot; data-alt=&quot;토마토 화분&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mOajv/btsamWtGFTX/CkpJ05s1NKVZZ9tFXcAKD0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmOajv%2FbtsamWtGFTX%2FCkpJ05s1NKVZZ9tFXcAKD0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7613.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;토마토 화분&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토마토는 이제 완벽하게 자리를 잡아서 줄기도 단단해지고 키도 많이 컸다. 슬슬 옮겨심어줄 곳을 찾아봐야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 녀석들은 다음 주말에 화분을 나눠서 심어주어야겠다. 마침 장모님이 제라늄 옮겨심으라고 가져다주신 화분이 있어서 그 쪽으로 옮겨심어야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토마토는 기대 이상으로 잘 성장해서 방울 토마토 수확을 기대해볼 수 있을거 같다.&lt;/p&gt;
&lt;div&gt;&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7610.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMLt9S/btsaje3jB2u/3w8EE4BozglEw6pZMXkf10/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMLt9S/btsaje3jB2u/3w8EE4BozglEw6pZMXkf10/img.jpg&quot; data-alt=&quot;딸기꽃&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMLt9S/btsaje3jB2u/3w8EE4BozglEw6pZMXkf10/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMLt9S%2Fbtsaje3jB2u%2F3w8EE4BozglEw6pZMXkf10%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7610.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;딸기꽃&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7611.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbbs2k/btsar3e0sm3/zLlIOY9fe4d7TJV6WrVxy1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbbs2k/btsar3e0sm3/zLlIOY9fe4d7TJV6WrVxy1/img.jpg&quot; data-alt=&quot;딸기꽃&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbbs2k/btsar3e0sm3/zLlIOY9fe4d7TJV6WrVxy1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbbbs2k%2Fbtsar3e0sm3%2FzLlIOY9fe4d7TJV6WrVxy1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7611.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;딸기꽃&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딸기는 신기하게도 꽃이 계속 핀다. 처음에 2 송이를 발견했었는데 그 녀석들은 이제 꽃잎이 떨어져서 열매를 맺을 준비를 하는데 옆에서 계속 꽃이 피고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잎이 타는거처럼 마르는 애들은 계속 잘라주고 있는데 그래도 꽃은 제법 잘 열린다. 몇 개나 열리겠나 싶어서 모종을 2개 사왔는데 이것들이 다 제법 열매를 가질 거처럼 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토마토와 딸기를 수확해서 먹을 수 있으면 그야말로 집에서 키워 먹는 열매들인 셈이다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7615.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0srbX/btsar6Qk05r/zkUxNCOmt10UnuO9skw5g0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0srbX/btsar6Qk05r/zkUxNCOmt10UnuO9skw5g0/img.jpg&quot; data-alt=&quot;채소 화분&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0srbX/btsar6Qk05r/zkUxNCOmt10UnuO9skw5g0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0srbX%2Fbtsar6Qk05r%2FzkUxNCOmt10UnuO9skw5g0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7615.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;채소 화분&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 보니 상추도 제법 많이 자랐다. 잎들이 너무 많아서 주체가 안되는 거처럼 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마침 저녁에 소고기를 먹기로 했으니 상추잎을 정리도 할겸 처음으로 제대로 상추를 수확해보기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직은 잎이 여리여리하지만 그래도 제법 큰 것도 있고 공간도 많이 만들어주기 위해서 좀 많이 솎아주기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먹을 수 있는건 먹고 시든거는 잘라서 버리기로 했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7618.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lhHAK/btsar30m86Q/6VKRclTCxQs0jPKFymp6EK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lhHAK/btsar30m86Q/6VKRclTCxQs0jPKFymp6EK/img.jpg&quot; data-alt=&quot;수확한 상추&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lhHAK/btsar30m86Q/6VKRclTCxQs0jPKFymp6EK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlhHAK%2Fbtsar30m86Q%2F6VKRclTCxQs0jPKFymp6EK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7618.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;수확한 상추&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 상추를 수확해보니 그래도 양이 제법 된다. 아직 잎이 여려서 맛이 제대로 나지는 않지만 그래도 먹을만한거 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큰 잎들이랑 누렇게 된 잎들을 솎아주었더니 그래도 공간이 제법 생기는 모양새이다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7617.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4syc8/btsajeIXgrh/NyVHkPHqf48kTCixeAXKyk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4syc8/btsajeIXgrh/NyVHkPHqf48kTCixeAXKyk/img.jpg&quot; data-alt=&quot;상추 수확 후 채소 화분&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4syc8/btsajeIXgrh/NyVHkPHqf48kTCixeAXKyk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4syc8%2FbtsajeIXgrh%2FNyVHkPHqf48kTCixeAXKyk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7617.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;상추 수확 후 채소 화분&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상추를 수확하고 모니 가운데 헹해지기는 했다. 저 잎들이 다 커지고 두꺼워질 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옆에 바질들도 열심히 크고 있다. 바질도 좀 더 크면 솎아주어야지 너무 촘촘하게 있다. 처음에 씨를 심었던 모양 그대로 옮겨 심었더니 난리도 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 정리해서 키우면 바질도 제법 크게 키울 수 있을듯 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 첫 수확을 했으니 주말마다 조금씩 뜯어먹을 수 있기를 기원해본다.&lt;/p&gt;</description>
      <category>My Story/식물재배기</category>
      <category>로메인상추</category>
      <category>방울토마토</category>
      <category>베란다농사</category>
      <category>봉선화</category>
      <category>상추수확</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/392</guid>
      <comments>https://minarae7.tistory.com/entry/%EB%B4%89%EC%84%A0%ED%99%94-%EC%98%AE%EA%B2%A8%EC%8B%AC%EA%B8%B0%EC%83%81%EC%B6%94-%EC%88%98%ED%99%95#entry392comment</comments>
      <pubDate>Sun, 16 Apr 2023 22:50:36 +0900</pubDate>
    </item>
    <item>
      <title>[베란다 농사] 모종 트레이</title>
      <link>https://minarae7.tistory.com/entry/%EB%B2%A0%EB%9E%80%EB%8B%A4-%EB%86%8D%EC%82%AC-%EB%AA%A8%EC%A2%85-%ED%8A%B8%EB%A0%88%EC%9D%B4</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 작은 화분에 씨앗을 사다가 키우기 시작하면서 얘네들이 크면 얼마나 크겠어라는 생각으로 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 이 집이 남향이라서 베란다에서 식물들이 초반에 엄청 잘 자란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 작은 화분을 심었던 아이들을 큰 화분으로 옮겨심어주는 일이 많아졌는데 이게 화분에서 키워서 옮겨심으려니 만만치 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러던 중에 모종판을 보게 되었고 검색해서 찾아보니 모종판 자체는 너무 싸서 인터넷으로 주문하기도 민망할 정도이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 인터넷 검색을 하다가 이런 아이를 찾았다.&lt;/p&gt;
&lt;figure id=&quot;og_1681360179795&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;다양한 사이즈 모종트레이 육묘 파종 지피 트레이 모종판 미니온실 낮은뚜껑6구 : 그룸그린&quot; data-og-description=&quot;[그룸그린] 가드닝의 모든것&quot; data-og-host=&quot;smartstore.naver.com&quot; data-og-source-url=&quot;https://smartstore.naver.com/groom_green/products/5262637362?site_preference=device&amp;amp;NaPm=ct%3Dlgemfkyo%7Cci%3Dshopn%7Ctr%3Dpla_myz%7Chk%3D7edd0b8fd2cfa5fa5d8fae6dcf4466441ffa6d52%7Ctrx%3Dundefined&quot; data-og-url=&quot;https://smartstore.naver.com/groom_green/products/5262637362?site_preference=device&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kuBZk/hySgwE8xOe/2nwrkP3t7G9kQifjeoS481/img.jpg?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000,https://scrap.kakaocdn.net/dn/bKat8v/hySgnBqz6f/t9TsjyJa0Twu08GFnTRe50/img.jpg?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000,https://scrap.kakaocdn.net/dn/1OA1A/hySglp4sVs/76LHFfw9uNTpKmkTnpXGWk/img.jpg?width=510&amp;amp;height=510&amp;amp;face=0_0_510_510&quot;&gt;&lt;a href=&quot;https://smartstore.naver.com/groom_green/products/5262637362?site_preference=device&amp;amp;NaPm=ct%3Dlgemfkyo%7Cci%3Dshopn%7Ctr%3Dpla_myz%7Chk%3D7edd0b8fd2cfa5fa5d8fae6dcf4466441ffa6d52%7Ctrx%3Dundefined&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://smartstore.naver.com/groom_green/products/5262637362?site_preference=device&amp;amp;NaPm=ct%3Dlgemfkyo%7Cci%3Dshopn%7Ctr%3Dpla_myz%7Chk%3D7edd0b8fd2cfa5fa5d8fae6dcf4466441ffa6d52%7Ctrx%3Dundefined&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kuBZk/hySgwE8xOe/2nwrkP3t7G9kQifjeoS481/img.jpg?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000,https://scrap.kakaocdn.net/dn/bKat8v/hySgnBqz6f/t9TsjyJa0Twu08GFnTRe50/img.jpg?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000,https://scrap.kakaocdn.net/dn/1OA1A/hySglp4sVs/76LHFfw9uNTpKmkTnpXGWk/img.jpg?width=510&amp;amp;height=510&amp;amp;face=0_0_510_510');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;다양한 사이즈 모종트레이 육묘 파종 지피 트레이 모종판 미니온실 낮은뚜껑6구 : 그룸그린&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;[그룸그린] 가드닝의 모든것&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;smartstore.naver.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름하여 모종트레이!! 작은 모종판을 플라스틱 틀 안에 넣어서 발아시키고 이렇게 해서 싹이 나면 얘네들을 옮겨 심어주는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;살까 라는 생각이 머리속을 스쳐지나가는 순간, 이거 사면 와이프한테 한소리 듣고 장모님께도 쓸데없는거 산다고 한소리 들을거 같아서 일단 찜리스트에 담아두었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 가만히 생각해보니 저거 만들 수 있을거 같다. 모양이 어디서 많이 보던 것과 비슷하게 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 모종판만 있으면 만들 수 있겠는데 라는 생각이 들었다. 근데 일단 제일 흔하지 않은 것이 모종판인데 이걸 어디서 구할까 고민했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 둘째 어린이집에서 식목일 행사에 다녀오게 되었고 마침 상추를 심고 모종판을 버리신다는 원장님 말씀에 좀 잘라간다고 하고 약간 잘라왔다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7584.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TA871/btr9NCjjQ6h/BdYmbZMUD13DLGs997FBVK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TA871/btr9NCjjQ6h/BdYmbZMUD13DLGs997FBVK/img.jpg&quot; data-alt=&quot;모종판&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TA871/btr9NCjjQ6h/BdYmbZMUD13DLGs997FBVK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTA871%2Fbtr9NCjjQ6h%2FBdYmbZMUD13DLGs997FBVK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7584.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;모종판&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 모판은 해결. 이제 트레이 역할을 해줄 녀석을 찾아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리집은 과일, 그 중에서도 딸기, 방울토마토를 많이 사다 먹는데 이 모양이 꼭 저 제품 모양과 흡사하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이번에는 딸기 트레이를 지정해서 내용물을 먹고 트레이만 남겨두었다.&lt;/p&gt;
&lt;div&gt;&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7583.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDPwxx/btr9NmgJX3r/A7O0CdradaValnvpTqjfM1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDPwxx/btr9NmgJX3r/A7O0CdradaValnvpTqjfM1/img.jpg&quot; data-alt=&quot;딸기 트레이&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDPwxx/btr9NmgJX3r/A7O0CdradaValnvpTqjfM1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDPwxx%2Fbtr9NmgJX3r%2FA7O0CdradaValnvpTqjfM1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7583.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;딸기 트레이&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완전 모양이 딱이다. 만듬새는 물론 파는 제품에 비해 볼품이 없지만 그래도 시도해볼직하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 저 모종판을 딸기 트레이 들어가는 모양으로 잘랐다. 크기를 대충 보니 3X2로 6개가 들어갈 사이즈이다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7586.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RrKVp/btr9Ozl7o2m/KifR3HZtdJKwQPsIBjWvjk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RrKVp/btr9Ozl7o2m/KifR3HZtdJKwQPsIBjWvjk/img.jpg&quot; data-alt=&quot;트레이 안에 모종판&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RrKVp/btr9Ozl7o2m/KifR3HZtdJKwQPsIBjWvjk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRrKVp%2Fbtr9Ozl7o2m%2FKifR3HZtdJKwQPsIBjWvjk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7586.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;트레이 안에 모종판&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크기가 딱이다! 여기서 좀 더 크면 안들어갈거 같다. 이제 흙을 채워보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흙은 이전에 상추랑 딸기 심으면서 사온 흙이 있으니 그 흙을 활용해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모종삽으로 조금씩 퍼서 담자. 어차피 트레이 안에 있으니 옆으로 좀 흘러도 상관없다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7587.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHbzZM/btr91xNK0dk/thG3zfK1S4PnwvfWNul1oK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHbzZM/btr91xNK0dk/thG3zfK1S4PnwvfWNul1oK/img.jpg&quot; data-alt=&quot;모종판에 흙채우기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHbzZM/btr91xNK0dk/thG3zfK1S4PnwvfWNul1oK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHbzZM%2Fbtr91xNK0dk%2FthG3zfK1S4PnwvfWNul1oK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7587.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;모종판에 흙채우기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7588.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YlZI7/btr9N3udmtZ/nr3rFzAPkKAreR5fJwP9Vk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YlZI7/btr9N3udmtZ/nr3rFzAPkKAreR5fJwP9Vk/img.jpg&quot; data-alt=&quot;흙을 눌러주기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YlZI7/btr9N3udmtZ/nr3rFzAPkKAreR5fJwP9Vk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYlZI7%2Fbtr9N3udmtZ%2Fnr3rFzAPkKAreR5fJwP9Vk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7588.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;흙을 눌러주기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모종판에 흙을 담고 손으로 정리하면서 흙을 약간씩 눌러주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 여기서 씨앗을 심어보자. 왔다갔다 하면서 씨를 종류별로 좀 사다두었는데 여기에는 꽃을 심어보려고 한다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7589.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCSRTK/btr9Ozl9dM6/KhgjIOXFMIKorZTfQZiKyk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCSRTK/btr9Ozl9dM6/KhgjIOXFMIKorZTfQZiKyk/img.jpg&quot; data-alt=&quot;다이소에서 사온 양귀비&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCSRTK/btr9Ozl9dM6/KhgjIOXFMIKorZTfQZiKyk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCSRTK%2Fbtr9Ozl9dM6%2FKhgjIOXFMIKorZTfQZiKyk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7589.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;다이소에서 사온 양귀비&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7590.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dzflUX/btr91zx1O6j/fz61gEgafAyH2iIT01StTK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dzflUX/btr91zx1O6j/fz61gEgafAyH2iIT01StTK/img.jpg&quot; data-alt=&quot;씨가 정말 작다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dzflUX/btr91zx1O6j/fz61gEgafAyH2iIT01StTK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdzflUX%2Fbtr91zx1O6j%2Ffz61gEgafAyH2iIT01StTK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7590.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;씨가 정말 작다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이소에서 양귀비 씨를 사왔었는데 이걸 심어보려고 한다. 조금만 봉투에 1000립이 있다고 하기에 작을걸로 생각했는데 정말 작다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;떨어지면 눈에 보이지도 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 녀석들을 틀 하나에 3~5개씩 심은거 같다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7591.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjC5Sy/btr90UJfjnY/ngwjDBXuEGuqeYDiKZO6U1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjC5Sy/btr90UJfjnY/ngwjDBXuEGuqeYDiKZO6U1/img.jpg&quot; data-alt=&quot;모종트레이에 씨를 심자&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjC5Sy/btr90UJfjnY/ngwjDBXuEGuqeYDiKZO6U1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjC5Sy%2Fbtr90UJfjnY%2FngwjDBXuEGuqeYDiKZO6U1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7591.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;모종트레이에 씨를 심자&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분명 모종판에 씨를 심었는데 보이지 않는다. 너무 작아서 흙과 구분이 안간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어찌되었건 이제 다시 흙을 덮어주자.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7592.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kVJce/btr9RwvFEk8/brMkLYZ1DHJG7ZNRkd2WUK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kVJce/btr9RwvFEk8/brMkLYZ1DHJG7ZNRkd2WUK/img.jpg&quot; data-alt=&quot;흙을 덮자&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kVJce/btr9RwvFEk8/brMkLYZ1DHJG7ZNRkd2WUK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkVJce%2Fbtr9RwvFEk8%2FbrMkLYZ1DHJG7ZNRkd2WUK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7592.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;흙을 덮자&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모종삽으로 조심조심 흙을 덮어주었다. 심기는 여기서 끝!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 물을 줍시다. 트레이 자체가 너무 소중하고 작으니 물은 그냥 분무기로.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7595.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1G7XP/btr9QU4swgG/LULRqeCfGeQWzSkckSk6y1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1G7XP/btr9QU4swgG/LULRqeCfGeQWzSkckSk6y1/img.jpg&quot; data-alt=&quot;분무기로 물주기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1G7XP/btr9QU4swgG/LULRqeCfGeQWzSkckSk6y1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1G7XP%2Fbtr9QU4swgG%2FLULRqeCfGeQWzSkckSk6y1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7595.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;분무기로 물주기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 흙이 흠뻑 젖을 수 있도록 분무기로 물을 충분히 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 해서 셀프로 모종트레이를 만들어봤다. 뚜껑까지 닫으니 높이가 꽤 있다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7602.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcFYnU/btr9N2IUH3R/nquLN3nE46UF0AIGnGVYr1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcFYnU/btr9N2IUH3R/nquLN3nE46UF0AIGnGVYr1/img.jpg&quot; data-alt=&quot;완성된 모종 트레이&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcFYnU/btr9N2IUH3R/nquLN3nE46UF0AIGnGVYr1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcFYnU%2Fbtr9N2IUH3R%2FnquLN3nE46UF0AIGnGVYr1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7602.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;완성된 모종 트레이&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뚜껑을 닫아두면 아마도 보온효과까지 있어서 꽤 싹이 잘 나지 않을까 라는 생각이 든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 생각보다 뚜껑이 많이 내려와서 싹이 나기 시작하면 뚜껑을 열어두고 키워주던지 아니면 뭔가 개조를 해주던지 해야할거 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 이렇게 해서 싹이 실제로 나는지부터 확인해볼 계획이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아마 저 트레이를 구매했으면 5천원 주었을 텐데 이렇게 집에서 버리는거 활용해서 만들어봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 되면 종종 이용하도록 하겠다. 이렇게 싹이 나면 옮겨심기도 수월할듯 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 키워봅시다~!!&lt;/p&gt;</description>
      <category>My Story/식물재배기</category>
      <category>모종트레이</category>
      <category>발아기</category>
      <category>베란다농사</category>
      <category>셀프제작</category>
      <category>양귀비</category>
      <category>플라스틱재활용</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/391</guid>
      <comments>https://minarae7.tistory.com/entry/%EB%B2%A0%EB%9E%80%EB%8B%A4-%EB%86%8D%EC%82%AC-%EB%AA%A8%EC%A2%85-%ED%8A%B8%EB%A0%88%EC%9D%B4#entry391comment</comments>
      <pubDate>Thu, 13 Apr 2023 13:52:24 +0900</pubDate>
    </item>
    <item>
      <title>[베란다 농사] 성장 일기</title>
      <link>https://minarae7.tistory.com/entry/%EB%B2%A0%EB%9E%80%EB%8B%A4-%EB%86%8D%EC%82%AC-%EC%84%B1%EC%9E%A5-%EC%9D%BC%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;다이소에서 씨앗과 화분을 사와서 키우기 시작한지 어느덧 한달이 흘렀다. 갑자기 왠 화분을 이렇게 키우나 싶다가도 식물들이 잘 자라는 모습을 보는게 어느덧 즐거움이 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요 며칠 일 때문에 정신도 없고 개인적인 슬픈 일도 있고 해서 신경을 못 썼는데 이 아이들은 그런거에 연연하지 않고 자기의 속도에 맞춰서 쑥쑥 크고 있다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7570-side.jpg&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cO6xfd/btr9Nh6ZiHf/eKortwJNF0DhnojHfU6Qz0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cO6xfd/btr9Nh6ZiHf/eKortwJNF0DhnojHfU6Qz0/img.jpg&quot; data-alt=&quot;봉선화&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cO6xfd/btr9Nh6ZiHf/eKortwJNF0DhnojHfU6Qz0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcO6xfd%2Fbtr9Nh6ZiHf%2FeKortwJNF0DhnojHfU6Qz0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1125&quot; height=&quot;500&quot; data-filename=&quot;IMG_7570-side.jpg&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;봉선화&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 봉선화는 벌써 이만큼 성장했다. 먼저 심었던 아이들은 둘째 어린이집 화단에 심어주고 남은 씨앗으로 다시 심었는데 성장하는 속도가 어마어마하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사진에서 왼쪽에 원 부분에 있던 봉선화를 뽑아서 오른쪽 화분에 심었다. 그랬더니 저렇게 원 안에 있는 것처럼 새로운 싹이 성장해서 올라온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 생명력이 어마어마하다. 원래 올라오고 있었던건지 아니면 다른 녀석들 때문에 못 올라오다가 자리가 나서 쑥 올라온건지 어쨌든 조그만 화분에 있는 틈, 없는 틈 만들어서 성장한다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7572-side.jpg&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ukgfj/btr9NnzfQ5b/QWeoOPRIoL2B4OIIucK411/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ukgfj/btr9NnzfQ5b/QWeoOPRIoL2B4OIIucK411/img.jpg&quot; data-alt=&quot;토마토&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ukgfj/btr9NnzfQ5b/QWeoOPRIoL2B4OIIucK411/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fukgfj%2Fbtr9NnzfQ5b%2FQWeoOPRIoL2B4OIIucK411%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;750&quot; height=&quot;500&quot; data-filename=&quot;IMG_7572-side.jpg&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;토마토&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방울토마토는 옮겨심으면서 다 죽으면 어떻게 하나 싶었는데 어느덧 자리를 잘 잡고 성장하고 있다. 줄기에 털같은 돌기가 나오는걸 보니 이제는 제법 자리를 잡은거 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 좀 더 커서 화분이 적게 느껴질 때까지 키우고 더 크면 그 때 가서 큰 화분으로 옮겨심어야 할 듯하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화분도 없거니와 섣불리 옮기다가 저번처럼 엄하게 식물을 죽이면 곤란하니 좀 느지막히 옮겨주어야겠다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7574.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Qypjy/btr9QUJpXnH/mkRlyULobPrIgRszJMW5fK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Qypjy/btr9QUJpXnH/mkRlyULobPrIgRszJMW5fK/img.jpg&quot; data-alt=&quot;제라늄&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Qypjy/btr9QUJpXnH/mkRlyULobPrIgRszJMW5fK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQypjy%2Fbtr9QUJpXnH%2FmkRlyULobPrIgRszJMW5fK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;800&quot; data-filename=&quot;IMG_7574.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;제라늄&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 식물을 하나 더 들였다. 제라늄은 원래 구매할 생각이 없었는데 장모님께 덴마크 무궁화를 주문해드리다가 배송료 아끼려고 보니 가장 만만한 가격이 이 제라늄이어서 주문하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옆에 빨간 덴마크 무궁화가 있는데 작년에 산 건데 정말 일 년 내내 꽃을 피운다. 이걸 보고 장모님이 사시겠다고 해서 인터넷으로 주문해드렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면서 2천원만 더 주문하면 무료배송이어서 가장 비슷한 가격으로 제라늄을 주문했다. 1년 식물이어도 꽃 피우는게 좋으니 꽃이 활짝 피기를 기원하며 구매했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어제 왔는데 마침 이래저래 옮겨심으면 남은 화분이 하나 있어서 조그만 화분에 옮겨주었다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7575.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bReEIZ/btr9N3NRExp/XjxssLUNldoPTeZKhcLOwk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bReEIZ/btr9N3NRExp/XjxssLUNldoPTeZKhcLOwk/img.jpg&quot; data-alt=&quot;해바라기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bReEIZ/btr9N3NRExp/XjxssLUNldoPTeZKhcLOwk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbReEIZ%2Fbtr9N3NRExp%2FXjxssLUNldoPTeZKhcLOwk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;800&quot; data-filename=&quot;IMG_7575.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;해바라기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 집에서 가장 잘 자라는 해바라기는 장모님이 화분이 작다고 하시면서 마침 시든 튤립을 뽑고 그 자리에 해바라기를 옮겨주셨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해바라기도 옮겨심고 와서 저 아이가 매우 시들시들해서 걱정했는데 이제는 언제 그랬냐는 듯이 쑥쑥 크고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아마 이 녀석이 가장 먼저 꽃을 피우지 않을까 기대하고 있다. 꽃 화단에 메인을 장식해주기를 기원해본다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7577.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpa7WY/btr9OxHWVa2/FtQhEfS1oPnmJ30BkJZOjk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpa7WY/btr9OxHWVa2/FtQhEfS1oPnmJ30BkJZOjk/img.jpg&quot; data-alt=&quot;바질&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpa7WY/btr9OxHWVa2/FtQhEfS1oPnmJ30BkJZOjk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbpa7WY%2Fbtr9OxHWVa2%2FFtQhEfS1oPnmJ30BkJZOjk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;800&quot; data-filename=&quot;IMG_7577.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;바질&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바질이도 걱정하게 했던 아이들이다. 너무 성급하게 옮겨심었나 싶을 정도로 성장이 더뎌졌었는데 어느덧 잎이 제법 두꺼워졌다. 너무 촘촘하게 심은 느낌이 있기는 하지만 그래도 제법 자리를 잡은거 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 더 크면 조금씩 수확해서 먹어도 될거 같다. 그만큼 잎이 꽤 크고 두꺼워졌다. 해를 열심히 보여주고 물도 꾸준히 주어야겠다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7578.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oqmvW/btr9NpcPv54/ExSxafpKmtHXeWkzKSLqz1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oqmvW/btr9NpcPv54/ExSxafpKmtHXeWkzKSLqz1/img.jpg&quot; data-alt=&quot;딸기꽃&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oqmvW/btr9NpcPv54/ExSxafpKmtHXeWkzKSLqz1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoqmvW%2Fbtr9NpcPv54%2FExSxafpKmtHXeWkzKSLqz1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;800&quot; data-filename=&quot;IMG_7578.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;딸기꽃&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딸기는 어느 정도 성장한 아이들을 가져다가 심었더니 꽤 잘 자란다. 이렇게 보니 어느덧 꽃이 2송이나 피었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 오후에 자세히 보니 꽃 옆으로도 꽃봉우리들이 앉아있는게 조만간 꽃이 더 필거 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얘네들 인공수정해서 과일이 열릴 수 있도록 해주어야겠다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7579.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HLgpf/btr9Ozsdlvv/m3a6aSv53gV0IqtVCJrnJK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HLgpf/btr9Ozsdlvv/m3a6aSv53gV0IqtVCJrnJK/img.jpg&quot; data-alt=&quot;문제의 상추&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HLgpf/btr9Ozsdlvv/m3a6aSv53gV0IqtVCJrnJK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHLgpf%2Fbtr9Ozsdlvv%2Fm3a6aSv53gV0IqtVCJrnJK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7579.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;문제의 상추&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 화분에서는 생각외로 상추가 제일 안 큰다. 잎이 얇고 바깥쪽 잎이 계속 마르는 것이 성장에 문제가 있는듯하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;너무 촘촘하게 심은 것이 문제인지 아니면 물을 생각보다 덜 준건지 성장이 잘 안되는걸 보니 속상하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 아예 해를 잘 볼 수 있도록 해가 제일 잘 드는 위치로도 옮겨주고 물도 흠뻑 주어서 잘 성장할 수 있도록 계기를 만들어주었다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7580.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SdB9p/btr9N1oZkUJ/Mkqjd5gBbmpZe8GMb4tKx1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SdB9p/btr9N1oZkUJ/Mkqjd5gBbmpZe8GMb4tKx1/img.jpg&quot; data-alt=&quot;야채 채소 화분&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SdB9p/btr9N1oZkUJ/Mkqjd5gBbmpZe8GMb4tKx1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSdB9p%2Fbtr9N1oZkUJ%2FMkqjd5gBbmpZe8GMb4tKx1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7580.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;야채 채소 화분&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 보니 꽤 많이들 큰거 같은데 생각같지 않다. 처음에는 싹 피고 막 성장해가는 것이 너무 재미있고 즐거운 일이었는데 저렇게 잘 안 크는거 같아서 속상하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;압축건계분으로 윗거름 아랫거름 다 주었는데도 생각만큼 크지는 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀 더 인내심을 가지고 지켜봐야할 듯 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점점 화분이 많아지는 것을 보고 와이프에게 계속 싫어한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 결과물이 있으면 좀 나을 것이라고 생각하고 베란다를 화단 놀이터 삼아서 열심히 키워봐야겠다.&lt;/p&gt;</description>
      <category>My Story/식물재배기</category>
      <category>딸기</category>
      <category>딸기꽃</category>
      <category>로메인상추</category>
      <category>바질</category>
      <category>방울토마토</category>
      <category>베란다농사</category>
      <category>봉선화</category>
      <category>제라늄</category>
      <category>해바라기</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/390</guid>
      <comments>https://minarae7.tistory.com/entry/%EB%B2%A0%EB%9E%80%EB%8B%A4-%EB%86%8D%EC%82%AC-%EC%84%B1%EC%9E%A5-%EC%9D%BC%EA%B8%B0#entry390comment</comments>
      <pubDate>Wed, 12 Apr 2023 23:13:18 +0900</pubDate>
    </item>
    <item>
      <title>주취자는 감형사유가 아니다. 가중처벌하라.</title>
      <link>https://minarae7.tistory.com/entry/%EC%A3%BC%EC%B7%A8%EC%9E%90%EB%8A%94-%EA%B0%90%ED%98%95%EC%82%AC%EC%9C%A0%EA%B0%80-%EC%95%84%EB%8B%88%EB%8B%A4-%EA%B0%80%EC%A4%91%EC%B2%98%EB%B2%8C%ED%95%98%EB%9D%BC</link>
      <description>&lt;figure id=&quot;og_1681132772556&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[단독] &amp;ldquo;음주운전 동네 아저씨에 아버지 잃어&amp;rdquo; 유족, 처벌 강화 울분&quot; data-og-description=&quot;&amp;ldquo;음주운전에 대한 처벌이 셌다면, 제 아버지는 돌아가시지 않았을겁니다.&amp;rdquo; 지난달 무면허 음주운전 차량에 아버지를 잃은 강성복(40&amp;middot;대전)씨는 10일 세계일보와의 통화에서 연신 한숨을 내쉬&quot; data-og-host=&quot;www.segye.com&quot; data-og-source-url=&quot;https://www.segye.com/newsView/20230410521274&quot; data-og-url=&quot;https://www.segye.com/view/20230410521274&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/1wWHS/hySdoH9j0w/ZQbrIY3fzP8OUwfspIGqyk/img.jpg?width=647&amp;amp;height=364&amp;amp;face=0_0_647_364,https://scrap.kakaocdn.net/dn/b9Aot8/hySdo2sGR2/AakrZpP2sNZ8K6azEDHdRK/img.jpg?width=647&amp;amp;height=364&amp;amp;face=0_0_647_364&quot;&gt;&lt;a href=&quot;https://www.segye.com/newsView/20230410521274&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.segye.com/newsView/20230410521274&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/1wWHS/hySdoH9j0w/ZQbrIY3fzP8OUwfspIGqyk/img.jpg?width=647&amp;amp;height=364&amp;amp;face=0_0_647_364,https://scrap.kakaocdn.net/dn/b9Aot8/hySdo2sGR2/AakrZpP2sNZ8K6azEDHdRK/img.jpg?width=647&amp;amp;height=364&amp;amp;face=0_0_647_364');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[단독] &amp;ldquo;음주운전 동네 아저씨에 아버지 잃어&amp;rdquo; 유족, 처벌 강화 울분&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;음주운전에 대한 처벌이 셌다면, 제 아버지는 돌아가시지 않았을겁니다.&amp;rdquo; 지난달 무면허 음주운전 차량에 아버지를 잃은 강성복(40&amp;middot;대전)씨는 10일 세계일보와의 통화에서 연신 한숨을 내쉬&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.segye.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1681132800651&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;6살 딸 태운 채 고속도로 만취운전한 30대 엄마..결국&quot; data-og-description=&quot;만취 상태로 6살 딸을 차에 태운 채 고속도로를 질주한 30대 여성이 가드레일을 들이받는 사고를 냈다.10일 인천경찰청 고속도로순찰대에 따르면 전날 오후 5시께 인천 계양구 인천공항고속도로 &quot; data-og-host=&quot;www.edaily.co.kr&quot; data-og-source-url=&quot;https://www.edaily.co.kr/news/read?newsId=03735926635574808&quot; data-og-url=&quot;https://www.edaily.co.kr/news/read?newsId=03735926635574808&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/G7ZYn/hySdtvVj9R/BpJXno0j748T4M2jkxzBG1/img.jpg?width=670&amp;amp;height=457&amp;amp;face=0_0_670_457,https://scrap.kakaocdn.net/dn/c8rcKo/hySdlxVFFD/02gIrRBj0ToDaF2mrRnHpk/img.jpg?width=670&amp;amp;height=457&amp;amp;face=0_0_670_457,https://scrap.kakaocdn.net/dn/uTFxz/hySe0eAMZt/6d81KQyKIkGRzzeP7XfZ50/img.jpg?width=670&amp;amp;height=457&amp;amp;face=0_0_670_457&quot;&gt;&lt;a href=&quot;https://www.edaily.co.kr/news/read?newsId=03735926635574808&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.edaily.co.kr/news/read?newsId=03735926635574808&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/G7ZYn/hySdtvVj9R/BpJXno0j748T4M2jkxzBG1/img.jpg?width=670&amp;amp;height=457&amp;amp;face=0_0_670_457,https://scrap.kakaocdn.net/dn/c8rcKo/hySdlxVFFD/02gIrRBj0ToDaF2mrRnHpk/img.jpg?width=670&amp;amp;height=457&amp;amp;face=0_0_670_457,https://scrap.kakaocdn.net/dn/uTFxz/hySe0eAMZt/6d81KQyKIkGRzzeP7XfZ50/img.jpg?width=670&amp;amp;height=457&amp;amp;face=0_0_670_457');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;6살 딸 태운 채 고속도로 만취운전한 30대 엄마..결국&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;만취 상태로 6살 딸을 차에 태운 채 고속도로를 질주한 30대 여성이 가드레일을 들이받는 사고를 냈다.10일 인천경찰청 고속도로순찰대에 따르면 전날 오후 5시께 인천 계양구 인천공항고속도로&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.edaily.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1681132811859&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;&amp;quot;더 좋은 곳에서 더 예쁘게 빛나길&amp;quot;&amp;hellip; 만취 운전자 초등생 희생 사고 현장 추모 물결&quot; data-og-description=&quot;&amp;quot;언니가 더 나은 세상을 만들어주지 못해 미안해. 미래를 앗아간 나쁜 어른이 꼭 벌 받도록 할게.&amp;quot; 지난 8일 오후 대전 서구 둔산동 어린이보호구역(스쿨존) 내에서 음주운전 차량에 치여 숨진 배&quot; data-og-host=&quot;www.hankookilbo.com&quot; data-og-source-url=&quot;https://www.hankookilbo.com/News/Read/A2023041008300002348&quot; data-og-url=&quot;https://www.hankookilbo.com/News/Read/A2023041008300002348?t=20230410181736&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/pwfrN/hySeYOCm3n/XYt3jOGL6wTcyNqbgHWRPK/img.png?width=640&amp;amp;height=436&amp;amp;face=0_0_640_436,https://scrap.kakaocdn.net/dn/daWo3F/hySdnbrobn/uCaYc1dn5eRInLKSBCDhz1/img.png?width=640&amp;amp;height=436&amp;amp;face=0_0_640_436,https://scrap.kakaocdn.net/dn/AKhBU/hySe4uw13k/xLDgIS1gdGW0nNDLmK9C3K/img.jpg?width=640&amp;amp;height=480&amp;amp;face=0_0_640_480&quot;&gt;&lt;a href=&quot;https://www.hankookilbo.com/News/Read/A2023041008300002348&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.hankookilbo.com/News/Read/A2023041008300002348&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/pwfrN/hySeYOCm3n/XYt3jOGL6wTcyNqbgHWRPK/img.png?width=640&amp;amp;height=436&amp;amp;face=0_0_640_436,https://scrap.kakaocdn.net/dn/daWo3F/hySdnbrobn/uCaYc1dn5eRInLKSBCDhz1/img.png?width=640&amp;amp;height=436&amp;amp;face=0_0_640_436,https://scrap.kakaocdn.net/dn/AKhBU/hySe4uw13k/xLDgIS1gdGW0nNDLmK9C3K/img.jpg?width=640&amp;amp;height=480&amp;amp;face=0_0_640_480');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;&quot;더 좋은 곳에서 더 예쁘게 빛나길&quot;&amp;hellip; 만취 운전자 초등생 희생 사고 현장 추모 물결&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&quot;언니가 더 나은 세상을 만들어주지 못해 미안해. 미래를 앗아간 나쁜 어른이 꼭 벌 받도록 할게.&quot; 지난 8일 오후 대전 서구 둔산동 어린이보호구역(스쿨존) 내에서 음주운전 차량에 치여 숨진 배&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.hankookilbo.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1681132849014&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;&amp;ldquo;제2의 승아 없길&amp;rdquo;&amp;hellip;추모 행렬 잇따라&quot; data-og-description=&quot;[앵커] 지난 주말 9살 승아 양이 음주운전 차량에 숨진 자리에는 꽃과 추모의 글이 쌓이고 있습니다. 만취...&quot; data-og-host=&quot;news.kbs.co.kr&quot; data-og-source-url=&quot;https://news.kbs.co.kr/news/view.do?ncd=7647703&quot; data-og-url=&quot;https://news.kbs.co.kr/news/view.do?ncd=7647703&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/biDbPn/hySdo9euwv/D4guBqtRaEgsqjeY0Tm2P0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/wvp4G/hySdpNNX4Y/vvd5qoEJBem5YimyOjrghk/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/cScnq9/hySdxLVPCx/hVIEAHOL11YyCYfRYmsTc0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot;&gt;&lt;a href=&quot;https://news.kbs.co.kr/news/view.do?ncd=7647703&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://news.kbs.co.kr/news/view.do?ncd=7647703&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/biDbPn/hySdo9euwv/D4guBqtRaEgsqjeY0Tm2P0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/wvp4G/hySdpNNX4Y/vvd5qoEJBem5YimyOjrghk/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/cScnq9/hySdxLVPCx/hVIEAHOL11YyCYfRYmsTc0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;제2의 승아 없길&amp;rdquo;&amp;hellip;추모 행렬 잇따라&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;[앵커] 지난 주말 9살 승아 양이 음주운전 차량에 숨진 자리에는 꽃과 추모의 글이 쌓이고 있습니다. 만취...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;news.kbs.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1681132865523&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;&amp;quot;차라리 죽었어야 했나&amp;quot;... 사라진 '7분'의 진실&quot; data-og-description=&quot;[리뷰] SBS 시사고발 &amp;lt;그것이 알고싶다&amp;gt;&quot; data-og-host=&quot;star.ohmynews.com&quot; data-og-source-url=&quot;https://star.ohmynews.com/NWS_Web/OhmyStar/at_pg.aspx?CNTN_CD=A0002917249&amp;amp;CMPT_CD=P0010&quot; data-og-url=&quot;https://star.ohmynews.com/NWS_Web/OhmyStar/at_pg.aspx?CNTN_CD=A0002917249&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/9Btbu/hySdtQebvU/cbc2UQzKifpK15AAXvPmy0/img.jpg?width=1200&amp;amp;height=623&amp;amp;face=675_206_881_431,https://scrap.kakaocdn.net/dn/kjrJm/hySe1R7itg/G9YdXNMJJBJcjhfFGmsRo0/img.jpg?width=1200&amp;amp;height=623&amp;amp;face=311_125_417_241,https://scrap.kakaocdn.net/dn/bev66a/hySdlxVI8w/cFc2oVKkW4ek0wLRhfPD11/img.jpg?width=1200&amp;amp;height=623&amp;amp;face=35_71_1066_528&quot;&gt;&lt;a href=&quot;https://star.ohmynews.com/NWS_Web/OhmyStar/at_pg.aspx?CNTN_CD=A0002917249&amp;amp;CMPT_CD=P0010&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://star.ohmynews.com/NWS_Web/OhmyStar/at_pg.aspx?CNTN_CD=A0002917249&amp;amp;CMPT_CD=P0010&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/9Btbu/hySdtQebvU/cbc2UQzKifpK15AAXvPmy0/img.jpg?width=1200&amp;amp;height=623&amp;amp;face=675_206_881_431,https://scrap.kakaocdn.net/dn/kjrJm/hySe1R7itg/G9YdXNMJJBJcjhfFGmsRo0/img.jpg?width=1200&amp;amp;height=623&amp;amp;face=311_125_417_241,https://scrap.kakaocdn.net/dn/bev66a/hySdlxVI8w/cFc2oVKkW4ek0wLRhfPD11/img.jpg?width=1200&amp;amp;height=623&amp;amp;face=35_71_1066_528');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;&quot;차라리 죽었어야 했나&quot;... 사라진 '7분'의 진실&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;[리뷰] SBS 시사고발 &amp;lt;그것이 알고싶다&amp;gt;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;star.ohmynews.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 사회는 주취범죄에 대해서 너무 관대한 부분이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심신미약이라는 미명하에 음주범죄, 주취범죄에 대해서 감형까지 해주는 형법이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;음주는 개인의 선택이기 때문에 이에 따른 심신미약은 가중처벌해야 한다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본인이 책임지지 못하는 음주는 가중처벌해야 음주에 있어서 좀 더 책임있는 자세를 갖지 않을까 싶다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 처벌이 능사는 아니다. 하지만 음주로 인한 사회적인 문제가 너무 많이 발생하는데 오히려 감형까지 된다면 음주로 인해서 기억이 안난다고 잡아 때는 일이 더 많아질 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 기억이 나고 안나고 주취로 심신미약인 상태는 본인만이 판단할 수 있는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심신미약 상태라고 우기면 감형이 된다면 누구나 그런 유혹에 빠지게 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더군다나 음주운전으로 너무도 많은 생명이 쓰러져간다. 하루가 멀다하고 이런 일이 반복되는 것이 안타깝기만 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하루 속히 음주운전 뿐만 아니라 모든 음주범죄는 감형이 아닌 가중처벌되는 법이 만들어져야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심신미약은 본인이 판단할 수 있지만 음주상태는 의학적으로 분명히 밝힐 수 있는 부분이기 때문에 논쟁의 여지가 줄 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이래야 법이 무서워서라도 음주에 대한 자세가 바뀌리라...&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-10 오후 10.29.34.png&quot; data-origin-width=&quot;1966&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ObXty/btr9kPcfw2t/omS9LNlpGHRtj9MyN1XdCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ObXty/btr9kPcfw2t/omS9LNlpGHRtj9MyN1XdCk/img.png&quot; data-alt=&quot;주취감형&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ObXty/btr9kPcfw2t/omS9LNlpGHRtj9MyN1XdCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FObXty%2Fbtr9kPcfw2t%2FomS9LNlpGHRtj9MyN1XdCk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1966&quot; height=&quot;600&quot; data-filename=&quot;스크린샷 2023-04-10 오후 10.29.34.png&quot; data-origin-width=&quot;1966&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;주취감형&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;figure id=&quot;og_1681133354565&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;주취감형 - 나무위키&quot; data-og-description=&quot;해당 규정은 단순히 '술을 먹었을 때 감형한다'는 목적이 아닌 일반적인 감형에 대한 사항이다. 음주는 약물, 정신장애 등과 함께 이러한 심신장애를 야기하는 한 원인인데, 음주에 관한 사항만 &quot; data-og-host=&quot;namu.wiki&quot; data-og-source-url=&quot;https://namu.wiki/w/%EC%A3%BC%EC%B7%A8%EA%B0%90%ED%98%95&quot; data-og-url=&quot;https://namu.wiki/w/%EC%A3%BC%EC%B7%A8%EA%B0%90%ED%98%95&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://namu.wiki/w/%EC%A3%BC%EC%B7%A8%EA%B0%90%ED%98%95&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://namu.wiki/w/%EC%A3%BC%EC%B7%A8%EA%B0%90%ED%98%95&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;주취감형 - 나무위키&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;해당 규정은 단순히 '술을 먹었을 때 감형한다'는 목적이 아닌 일반적인 감형에 대한 사항이다. 음주는 약물, 정신장애 등과 함께 이러한 심신장애를 야기하는 한 원인인데, 음주에 관한 사항만&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;namu.wiki&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>기사모음과 생각</category>
      <category>음주범죄를 가중처벌하라</category>
      <category>주취는 감형대상이 아니다</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/389</guid>
      <comments>https://minarae7.tistory.com/entry/%EC%A3%BC%EC%B7%A8%EC%9E%90%EB%8A%94-%EA%B0%90%ED%98%95%EC%82%AC%EC%9C%A0%EA%B0%80-%EC%95%84%EB%8B%88%EB%8B%A4-%EA%B0%80%EC%A4%91%EC%B2%98%EB%B2%8C%ED%95%98%EB%9D%BC#entry389comment</comments>
      <pubDate>Mon, 10 Apr 2023 22:30:41 +0900</pubDate>
    </item>
    <item>
      <title>[가계부 만들기] Backend - 회원 관리 기능 #5</title>
      <link>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-Backend-%ED%9A%8C%EC%9B%90-%EA%B4%80%EB%A6%AC-%EA%B8%B0%EB%8A%A5-5</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;회원 관리 기능에서 기초가 되는 기능에서 마지막은 refresh token을 통해서 access token과 refresh token을 갱신하는 기능일 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세션을 사용하지 않고 JWT만으로 사용자 정보를 검증하기 때문에 access token의 유효시간이 만료되었을 때 이를 새로 갱신해줄 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 포스팅에서 해당 기능을 간단하게 구현하도록 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Schema 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;refresh token을 갱신할 때 Response로 받을 schema를 추가적으로 생성할 것이다. 물론 그냥 str로 받아도 되지만 이렇게 받는 것보다 schema를 통해서 정의하는 것이 좀 더 좋아보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;databases/schemas.py 파일에 아래 코드를 추가한다.&lt;/p&gt;
&lt;pre id=&quot;code_1681048101493&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Refresh token을 위한 request 정의
class Refresh(BaseModel):
    refresh_token: str = Field(title=&quot;Refresh Token&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 refresh token을 먼저 정의하였다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Router 추가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;refresh token을 넘겨받아서 처리할 router를 추가로 생성하도록 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;routers/members.py 파일에 마찬가지로 아래 코드를 추가한다.&lt;/p&gt;
&lt;pre id=&quot;code_1681048202292&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#refresh token을 이용한 access token 재발급
@router.post(&quot;/refresh&quot;, description=&quot;token 갱신&quot;, response_model=schemas.LoginResponse, responses={
    HTTP_400_BAD_REQUEST: {
        &quot;model&quot;: schemas.Message
    }
})
async def refresh(
    refresh_token: schemas.Refresh,
    db: Session = Depends(get_db)
):
    try:
        result = await members_service.member_refresh(db, refresh_token)

        return result
    except Exception as e:
        return JSONResponse(content={&quot;detail&quot;: e.args[0]}, status_code=HTTP_400_BAD_REQUEST)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;response는 기본적으로 login과 동일하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;request에서는 바로 위에서 정의한 Refresh를 받도록 하였다. refresh token을 받아서 처리할 함수를 호출하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수를 아직 정의하지 않았지만 데이터베이스에서 조회하기 때문에 DB 접속 정보를 넘기고 처리할 refresh token을 함께 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;refresh token을 사용한다는 것을 제외하고는 로그인과 동일하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JWT 검증 추가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;refresh token을 처리하는 코드를 추가하기 전에 refresh token이 JWT로 유효한지 검사하는 코드를 먼저 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;libraries/auth.py 파일이 아래 코드를 추가한다.&lt;/p&gt;
&lt;pre id=&quot;code_1681048500346&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# JWT 검증 함수(refresh token용)
def decode_refresh_token(token: str):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        return payload
    except JWTError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=&quot;Invalid authentication credentials&quot;,
            headers={&quot;WWW-Authenticate&quot;: &quot;Bearer&quot;},
        )&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순하게 JWT를 검증하는 역할만 하지만 access token을 처리하는 방식이 달라서 별도의 함수로 빼서 처리하도록 한다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;처리 로직 추가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 refresh token을 검증하고 access token을 새로 생성하는 코드를 추가할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;services/members_service.py 파일에 아래 내용을 추가한다.&lt;/p&gt;
&lt;pre id=&quot;code_1681048667881&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# refresh token 처리
async def member_refresh(db: Session, refresh_token: schemas.Refresh):
    # refresh token 검사
    try:
        payload = auth.decode_refresh_token(refresh_token.refresh_token)
    except Exception:
        raise Exception

    # 해당 회원이 있는지 검사
    stmt = select(models.Members).filter(models.Members.member_no == payload['member_no'], models.Members.is_deleted == 'F')
    result = db.execute(stmt)

    db_member = result.fetchone()
    if db_member is None:
        raise Exception(&quot;해당하는 아이디를 찾을 수 없습니다&quot;)

    data = {
        &quot;member_no&quot;: db_member.Members.member_no,
        &quot;member_id&quot;: db_member.Members.member_id,
        &quot;member_name&quot;: db_member.Members.member_name,
        &quot;member_email&quot;: db_member.Members.member_email
    }
    data[&quot;access_token&quot;] = auth.create_access_token(data, timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES))
    data[&quot;refresh_token&quot;] = auth.create_access_token({
        &quot;member_no&quot;: data[&quot;member_no&quot;],
        &quot;member_id&quot;: data[&quot;member_id&quot;]
    }, timedelta(hours=REFRESH_TOKEN_EXPIRE_HOURS))

    return data&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 refresh token이 유효한지 검사한다. 만약 유효하지 않다면 전달받은 Exception을 그대로 다시 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 JWT에 있는 회원 번호를 통해서 회원번호로 회원 정보를 찾아서 access token과 refresh token을 다시 생성해서 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이렇게 반환받은 정보를 Frontend에 전달해서 새로운 access token으로 통신하게 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Refactoring&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 작성하고 보니 response 정보가 동일하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 response를 만드는 코드가 중복으로 사용된다. 만약 response의 정보가 수정된다면 중복되는 코드를 수정하여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런 경우를 대비하기 위해서 response data를 만드는 부분을 별도의 함수를 만들어서 중복되는 코드를 없애도록 하자.&lt;/p&gt;
&lt;pre id=&quot;code_1681049043875&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def make_login_response(db_member):
    data = {
        &quot;member_no&quot;: db_member.Members.member_no,
        &quot;member_id&quot;: db_member.Members.member_id,
        &quot;member_name&quot;: db_member.Members.member_name,
        &quot;member_email&quot;: db_member.Members.member_email
    }
    data[&quot;access_token&quot;] = auth.create_access_token(data, timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES))
    data[&quot;refresh_token&quot;] = auth.create_access_token({
        &quot;member_no&quot;: data[&quot;member_no&quot;],
        &quot;member_id&quot;: data[&quot;member_id&quot;]
    }, timedelta(hours=REFRESH_TOKEN_EXPIRE_HOURS))

    return data&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 중복되는 코드를 별도의 함수로 먼저 선언하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이제 login_proc 함수와 member_refresh 함수에서 중복으로 들어있던 코드를 지우고 새로 선언한 함수를 부르는 코드로 수정한다.&lt;/p&gt;
&lt;pre id=&quot;code_1681049149282&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# login 처리
async def login_proc(db: Session, member_id: str, member_pw: str):
    # 해당 아이디가 있는지 찾는다/
    stmt = select(models.Members).filter(models.Members.member_id == member_id, models.Members.is_deleted == 'F')
    result = db.execute(stmt)

    db_member = result.fetchone()
    if db_member is None:
        raise Exception(&quot;해당하는 아이디를 찾을 수 없습니다&quot;)

    if auth.verify_password(member_pw, db_member.Members.member_pw) == False:
        raise Exception(&quot;패스워드가 일치하지 않습니다.&quot;)

    return make_login_response(db_member)


# refresh token 처리
async def member_refresh(db: Session, refresh_token: schemas.Refresh):
    # refresh token 검사
    try:
        payload = auth.decode_refresh_token(refresh_token.refresh_token)
    except Exception:
        raise Exception

    # 해당 회원이 있는지 검사
    stmt = select(models.Members).filter(models.Members.member_no == payload['member_no'], models.Members.is_deleted == 'F')
    result = db.execute(stmt)

    db_member = result.fetchone()
    if db_member is None:
        raise Exception(&quot;해당하는 아이디를 찾을 수 없습니다&quot;)

    return make_login_response(db_member)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 코드가 다소 간단하게 정리되었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;테스트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이제 docs 페이지를 열어서 해당 기능을 테스트해도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 login을 진행하여서 refresh token을 생성한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-09 오후 11.07.58.png&quot; data-origin-width=&quot;2624&quot; data-origin-height=&quot;380&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMUCIP/btr8P0qR9HB/AftMwcMsjD9FT96KkWpUY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMUCIP/btr8P0qR9HB/AftMwcMsjD9FT96KkWpUY1/img.png&quot; data-alt=&quot;login 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMUCIP/btr8P0qR9HB/AftMwcMsjD9FT96KkWpUY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMUCIP%2Fbtr8P0qR9HB%2FAftMwcMsjD9FT96KkWpUY1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2624&quot; height=&quot;380&quot; data-filename=&quot;스크린샷 2023-04-09 오후 11.07.58.png&quot; data-origin-width=&quot;2624&quot; data-origin-height=&quot;380&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;login 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 login에서 전달받은 refresh token으로 access token을 갱신하는 테스트를 하도록 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-09 오후 11.09.08.png&quot; data-origin-width=&quot;2856&quot; data-origin-height=&quot;1344&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kGghM/btr8UUYdOiA/i4ewdS62LkWcfn23lej371/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kGghM/btr8UUYdOiA/i4ewdS62LkWcfn23lej371/img.png&quot; data-alt=&quot;refresh token 테스트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kGghM/btr8UUYdOiA/i4ewdS62LkWcfn23lej371/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkGghM%2Fbtr8UUYdOiA%2Fi4ewdS62LkWcfn23lej371%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2856&quot; height=&quot;1344&quot; data-filename=&quot;스크린샷 2023-04-09 오후 11.09.08.png&quot; data-origin-width=&quot;2856&quot; data-origin-height=&quot;1344&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;refresh token 테스트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docs에서 refresh token을 parameter로 전달하여서 작성한 페이지를 호출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 결과가 나오는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-09 오후 11.09.27.png&quot; data-origin-width=&quot;2864&quot; data-origin-height=&quot;1130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1Q3fL/btr8LriYL6Q/sFmXTyMr2mqfH528pk5rH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1Q3fL/btr8LriYL6Q/sFmXTyMr2mqfH528pk5rH0/img.png&quot; data-alt=&quot;refresh token 테스트 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1Q3fL/btr8LriYL6Q/sFmXTyMr2mqfH528pk5rH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1Q3fL%2Fbtr8LriYL6Q%2FsFmXTyMr2mqfH528pk5rH0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2864&quot; height=&quot;1130&quot; data-filename=&quot;스크린샷 2023-04-09 오후 11.09.27.png&quot; data-origin-width=&quot;2864&quot; data-origin-height=&quot;1130&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;refresh token 테스트 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비교적 간단한 코드이지만 로그인을 유지하기 위해서 꼭 필요한 기능이라고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음부터는 가계부에 관련된 데이터를 저장하고 불러오는 기능을 개발하도록 하겠다.&lt;/p&gt;</description>
      <category>Programming/Project Log</category>
      <category>FastAPI</category>
      <category>project log</category>
      <category>python</category>
      <category>개발기록</category>
      <category>개인프로젝트</category>
      <category>파이썬</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/388</guid>
      <comments>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-Backend-%ED%9A%8C%EC%9B%90-%EA%B4%80%EB%A6%AC-%EA%B8%B0%EB%8A%A5-5#entry388comment</comments>
      <pubDate>Sun, 9 Apr 2023 23:14:31 +0900</pubDate>
    </item>
    <item>
      <title>[가계부 만들기] Backend - 회원 관리 기능 #4</title>
      <link>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-Backend-%ED%9A%8C%EC%9B%90-%EA%B4%80%EB%A6%AC-%EA%B8%B0%EB%8A%A5-4</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이제 사용자가 전달한 값을 통해서 실제로 Database에 연동하는 코드를 넣을 것이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;서비스 디렉토리 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 비즈니스 로직을 담당하는 파일들을 모아둘 디렉토리를 생성하고 __init.py__ 파일을 추가해둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 회원 관련 로직을 작성할 members_service.py 파일을 생성한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-07 오전 11.32.16.png&quot; data-origin-width=&quot;614&quot; data-origin-height=&quot;144&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnLKCX/btr8uRV7pqD/CvguPDtauvukXVrn9il040/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnLKCX/btr8uRV7pqD/CvguPDtauvukXVrn9il040/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnLKCX/btr8uRV7pqD/CvguPDtauvukXVrn9il040/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnLKCX%2Fbtr8uRV7pqD%2FCvguPDtauvukXVrn9il040%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;405&quot; height=&quot;95&quot; data-filename=&quot;스크린샷 2023-04-07 오전 11.32.16.png&quot; data-origin-width=&quot;614&quot; data-origin-height=&quot;144&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 구조를 만들었으니 이제 내용을 작성해보자.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;외부 파일 참조 및 변수 선언&lt;/h3&gt;
&lt;pre id=&quot;code_1680834857390&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from sqlalchemy import select
from sqlalchemy.orm import Session
from sqlalchemy.sql import func
from datetime import timedelta
from ..database import models, schemas
from ..libraries import auth

ACCESS_TOKEN_EXPIRE_MINUTES = 60
REFRESH_TOKEN_EXPIRE_HOURS = 24&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;members_service.py 파일에서 참조하는 라이브러리 및 외부 함수를 먼저 불러온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 JWT의 유효시간을 선언해둔다. access_token은 한 시간, refresh_token은 하루로 유효시간을 설정했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Backend와 Frontend를 분리해서 개발하면 서버에서 session을 사용하기가 어렵기 때문에 JWT를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인할 때 내부적으로는 access_token과 refresh_token을 생성해서 전달하고 Frontend에서 데이터를 요청할 때는 항상 access_token을 헤더에 붙여서 보내도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 인증 만료 시간인 한 시간이 지나버리면 refresh_token을 통해서 새로운 access_token을 자동으로 받아오고 새로 발급받은 access_token을 이용해서 통신하게 된다. 여기에 사용하는 시간을 변수로 미리 설정해둔다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;회원가입&lt;/h3&gt;
&lt;pre id=&quot;code_1680835192163&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async def create_member(db: Session, member: schemas.MemberCreate):
    # 아이디가 중복되는 계정이 있는지 확인
    stmt = select(models.Members.member_id).filter(models.Members.member_id == member.member_id)
    result = db.execute(stmt)

    list = result.fetchall()
    if len(list) &amp;gt; 0:
        raise Exception(&quot;아이디가 이미 사용 중입니다&quot;)

    # 패스워드 해싱 처리
    password_hash = auth.get_password_hash(member.member_pw)

    # DB 저장
    db_member = models.Members(**member.dict(exclude={&quot;member_pw&quot;}), member_pw=password_hash)
    db.add(db_member)
    db.commit()
    db.refresh(db_member)

    return db_member&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회원 가입을 할 때는 먼저 입력한 아이디가 다른 사용자가 사용중인지 먼저 확인하여야 한다. 만약 다른 사용자가 해당 아이디를 이미 점령해서 사용 중이라면 Exception을 발생시키고 더 이상 진행하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아이디가 사용할 수 있다면 이제 패스워드를 해시처리해서 디비에서 패스워드를 확인할 수 없도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 해당 정보를 테이블에 저장하고 저장된 정보를 반환하도록 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1680835362477&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db_member = models.Members(**member.dict(exclude={&quot;member_pw&quot;}), member_pw=password_hash)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드에서는 member를 models.Members에 연결하는데 exclude를 넣어서 암호화되지 않은 패스워드는 제외하고 member_pw=password_hash에서 패스워드는 해쉬처리된 코드로 변경하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 작성했으니 테스트를 해볼 수 있다. http://localhost:8000/docs 페이지로 이동해서 /members/create 라우팅을 다음과 같이 테스트해볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-07 오전 11.49.43.png&quot; data-origin-width=&quot;2874&quot; data-origin-height=&quot;1360&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beaMiT/btr8FhrZBSz/So9aEafTQQKZyFU0UVIE9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beaMiT/btr8FhrZBSz/So9aEafTQQKZyFU0UVIE9K/img.png&quot; data-alt=&quot;회원 가입 라우팅 테스트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beaMiT/btr8FhrZBSz/So9aEafTQQKZyFU0UVIE9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeaMiT%2Fbtr8FhrZBSz%2FSo9aEafTQQKZyFU0UVIE9K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2874&quot; height=&quot;1360&quot; data-filename=&quot;스크린샷 2023-04-07 오전 11.49.43.png&quot; data-origin-width=&quot;2874&quot; data-origin-height=&quot;1360&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;회원 가입 라우팅 테스트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 테스트를 진행하면 다음과 같은 결과를 얻을 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-07 오전 11.50.08.png&quot; data-origin-width=&quot;2884&quot; data-origin-height=&quot;1044&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWxOQh/btr8zkQe07w/QoXASWrJKl7xougufgic71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWxOQh/btr8zkQe07w/QoXASWrJKl7xougufgic71/img.png&quot; data-alt=&quot;호출 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWxOQh/btr8zkQe07w/QoXASWrJKl7xougufgic71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWxOQh%2Fbtr8zkQe07w%2FQoXASWrJKl7xougufgic71%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2884&quot; height=&quot;1044&quot; data-filename=&quot;스크린샷 2023-04-07 오전 11.50.08.png&quot; data-origin-width=&quot;2884&quot; data-origin-height=&quot;1044&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;호출 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상적으로 처리 되었고 디비를 보면 실제로 값이 잘 들어갔고 패스워드도 암호화된 상태로 들어간 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-07 오전 11.50.25.png&quot; data-origin-width=&quot;2108&quot; data-origin-height=&quot;216&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GVdDC/btr8A4tbggo/ZO78S76BQzWgaUrXrdOyH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GVdDC/btr8A4tbggo/ZO78S76BQzWgaUrXrdOyH0/img.png&quot; data-alt=&quot;테이블 내용&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GVdDC/btr8A4tbggo/ZO78S76BQzWgaUrXrdOyH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGVdDC%2Fbtr8A4tbggo%2FZO78S76BQzWgaUrXrdOyH0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2108&quot; height=&quot;216&quot; data-filename=&quot;스크린샷 2023-04-07 오전 11.50.25.png&quot; data-origin-width=&quot;2108&quot; data-origin-height=&quot;216&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;테이블 내용&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;로그인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회원 가입이 되었으니 이후의 내용들은 다 로그인한 상태에서 진행된다. 그래서 먼저 로그인을 먼저 구현하도록 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1680840629007&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async def login_proc(db: Session, member_id: str, member_pw: str):
    # 해당 아이디가 있는지 찾는다/
    stmt = select(models.Members).filter(models.Members.member_id == member_id, models.Members.is_deleted == 'F')
    result = db.execute(stmt)

    db_member = result.fetchone()
    if db_member is None:
        raise Exception(&quot;해당하는 아이디를 찾을 수 없습니다&quot;)

    if auth.verify_password(member_pw, db_member.Members.member_pw) == False:
        raise Exception(&quot;패스워드가 일치하지 않습니다.&quot;)

    data = {
        &quot;member_no&quot;: db_member.Members.member_no,
        &quot;member_id&quot;: db_member.Members.member_id,
        &quot;member_name&quot;: db_member.Members.member_name,
        &quot;member_email&quot;: db_member.Members.member_email
    }
    data[&quot;access_token&quot;] = auth.create_access_token(data, timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES))
    data[&quot;refresh_token&quot;] = auth.create_access_token({
        &quot;member_no&quot;: data[&quot;member_no&quot;],
        &quot;member_id&quot;: data[&quot;member_id&quot;]
    }, timedelta(hours=REFRESH_TOKEN_EXPIRE_HOURS))

    return data&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아이디와 패스워드를 받아서 회원 정보 테이블에서 조회한다. 아이디가 없으면 Exception 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 입력한 패스워드와 테이블의 패스워드를 비교해서 일치하지 않으면 Exception 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모두 정상적으로 처리 되었다면 access_token과 refresh_token을 생성하여 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로 docs 페이지에서 테스트를 해보면 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-07 오후 1.14.56.png&quot; data-origin-width=&quot;2870&quot; data-origin-height=&quot;1344&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VKWMn/btr8vs92nPx/Z0114NimYhTxm3KHQFlI4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VKWMn/btr8vs92nPx/Z0114NimYhTxm3KHQFlI4k/img.png&quot; data-alt=&quot;로그인 테스트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VKWMn/btr8vs92nPx/Z0114NimYhTxm3KHQFlI4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVKWMn%2Fbtr8vs92nPx%2FZ0114NimYhTxm3KHQFlI4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2870&quot; height=&quot;1344&quot; data-filename=&quot;스크린샷 2023-04-07 오후 1.14.56.png&quot; data-origin-width=&quot;2870&quot; data-origin-height=&quot;1344&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;로그인 테스트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;json 형태로 아이디와 패스워드를 전송한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-07 오후 1.15.21.png&quot; data-origin-width=&quot;2874&quot; data-origin-height=&quot;1376&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bguxXq/btr8uUS8kYH/V4lK7JPcZ5DiPtV6m3hGAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bguxXq/btr8uUS8kYH/V4lK7JPcZ5DiPtV6m3hGAK/img.png&quot; data-alt=&quot;로그인데 대한 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bguxXq/btr8uUS8kYH/V4lK7JPcZ5DiPtV6m3hGAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbguxXq%2Fbtr8uUS8kYH%2FV4lK7JPcZ5DiPtV6m3hGAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2874&quot; height=&quot;1376&quot; data-filename=&quot;스크린샷 2023-04-07 오후 1.15.21.png&quot; data-origin-width=&quot;2874&quot; data-origin-height=&quot;1376&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;로그인데 대한 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인에 성공하면 위의 이미지와 같이 access_token과 refresh_token 값이 로그인 정보에 포함되어서 전송된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 결과로 온 access_token과 refresh_token은 frontend에서 저장하여서 사용하면 된다. 기본적인 통신은 모두 access_token을 통해서 사용하면 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;회원 정보 수정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 회원 정보를 수정하는 코드를 작성할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JWT 안에 기본적인 회원 정보를 저장하고 있기 때문에 기존의 회원 정보를 다시 보낼 필요없고 jwt 안에서 꺼내쓸 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1680841284880&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async def member_modify(db: Session, member: schemas.MemberModify, payload: schemas.JWTPayload):
    # 회원이 존재하는 아이디인지 확인
    db_member = db.query(models.Members).filter_by(member_no = payload['member_no'], is_deleted = 'F').first()

    if db_member is None:
        raise Exception(&quot;해당하는 회원 정보를 찾을 수 없습니다.&quot;)

    member_info =  member.dict()
    member = {k: v for k, v in member_info.items()}
    for key, value in member.items():
        if value is None:
            continue

        if key == 'member_pw':
            setattr(db_member, key, auth.get_password_hash(value))
        else:
            setattr(db_member, key, value)

    db.commit()
    return db_member&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드는 매우 간단하다. 우선 jwt에 포함된 회원 정보에서 회원 번호를 꺼내는데 이게 실제로 존재하는지 먼저 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인되었다면 수정을 원하는 컬럼을 하나씩 변경하도록 한다. 이 때 패스워드를 변경하는 경우에는 해당 값은 hash 처리해서 저장하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경이 끝났다면 변경된 내용을 저장하기 위해서 commit을 호출하고 변경된 회원 정보를 반환하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약에 변경을 원하지 않는 컬럼은 보내지 않으면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회원 정보를 변경할 때는 request 헤더에 로그인할 때 발급받은 access_token을 포함하여야 한다. 이 때 값의 형태는 &quot;Bearer ....&quot; 이와 같이 앞에 Bearer를 붙여 주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docs에서 테스트한 페이지를 보면 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-07 오후 1.24.39.png&quot; data-origin-width=&quot;2870&quot; data-origin-height=&quot;1566&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MykbX/btr8vN0rVcR/v2jCZWu1Xkltyt4nuyyDFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MykbX/btr8vN0rVcR/v2jCZWu1Xkltyt4nuyyDFk/img.png&quot; data-alt=&quot;회원 수정 테스트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MykbX/btr8vN0rVcR/v2jCZWu1Xkltyt4nuyyDFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMykbX%2Fbtr8vN0rVcR%2Fv2jCZWu1Xkltyt4nuyyDFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2870&quot; height=&quot;1566&quot; data-filename=&quot;스크린샷 2023-04-07 오후 1.24.39.png&quot; data-origin-width=&quot;2870&quot; data-origin-height=&quot;1566&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;회원 수정 테스트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지를 보면 다른 이미지와 달리 parameters 항목이 추가된 것을 볼 수 있다. token 값에 JWT 값을 추가하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음 이미지와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-07 오후 1.24.53.png&quot; data-origin-width=&quot;2890&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clDHVB/btr8zmuh5WP/YbnX6m9P52eKU1dMPczLUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clDHVB/btr8zmuh5WP/YbnX6m9P52eKU1dMPczLUk/img.png&quot; data-alt=&quot;회원 정보 수정 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clDHVB/btr8zmuh5WP/YbnX6m9P52eKU1dMPczLUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclDHVB%2Fbtr8zmuh5WP%2FYbnX6m9P52eKU1dMPczLUk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2890&quot; height=&quot;1080&quot; data-filename=&quot;스크린샷 2023-04-07 오후 1.24.53.png&quot; data-origin-width=&quot;2890&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;회원 정보 수정 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로 request를 보낼 때 curl에서 헤더에 token이 추가된 것을 확인할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;회원 탈퇴&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 회원 탈퇴를 구현한다.&lt;/p&gt;
&lt;pre id=&quot;code_1680841754714&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async def member_delete(db: Session, payload: schemas.JWTPayload):
    # 회원이 존재하는 아이디인지 확인
    db_member = db.query(models.Members).filter_by(member_no = payload['member_no'], is_deleted = 'F').first()

    if db_member is None:
        raise Exception(&quot;해당하는 회원 정보를 찾을 수 없습니다.&quot;)

    setattr(db_member, 'is_deleted', 'T')
    setattr(db_member, 'del_dt', func.now())

    db.commit()
    return db_member&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회원 탈퇴는 매우 간단하다. 회원 번호가 넘어오면 해당하는 회원 정보를 찾아서 삭제 플래그를 변경해주고 삭제 시간을 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삭제할 때 실제로 테이블에서 delete해서 처리하는 것이 올바른 방향이나 실제로 일을 하다가보면 그렇게 바로 삭제하는 경우는 많지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경된 히스토리를 찾아야하는 경우도 있고 드물고 잘못 삭제해서 다시 복원해야 하는 경우도 있기 때문에 최초에 탈퇴시에는 일단 플래그만 변경하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회원 탈퇴도 정보 수정과 마찬가지로 jwt 값을 헤더에 추가해서 보내며 어차피 회원 정보는 jwt에 포함되기 때문에 따로 전송하지 않는다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-07 오후 1.32.27.png&quot; data-origin-width=&quot;2912&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kXQoV/btr8FidS7So/wRld7ojR8xCxj3HEkjsifk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kXQoV/btr8FidS7So/wRld7ojR8xCxj3HEkjsifk/img.png&quot; data-alt=&quot;회원 탈퇴 테스트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kXQoV/btr8FidS7So/wRld7ojR8xCxj3HEkjsifk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkXQoV%2Fbtr8FidS7So%2FwRld7ojR8xCxj3HEkjsifk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2912&quot; height=&quot;800&quot; data-filename=&quot;스크린샷 2023-04-07 오후 1.32.27.png&quot; data-origin-width=&quot;2912&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;회원 탈퇴 테스트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-07 오후 1.34.48.png&quot; data-origin-width=&quot;2888&quot; data-origin-height=&quot;916&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G8iz9/btr8Gj4JsmN/Briaa5ExHVIKzzztKCdKy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G8iz9/btr8Gj4JsmN/Briaa5ExHVIKzzztKCdKy0/img.png&quot; data-alt=&quot;호출 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G8iz9/btr8Gj4JsmN/Briaa5ExHVIKzzztKCdKy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG8iz9%2Fbtr8Gj4JsmN%2FBriaa5ExHVIKzzztKCdKy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2888&quot; height=&quot;916&quot; data-filename=&quot;스크린샷 2023-04-07 오후 1.34.48.png&quot; data-origin-width=&quot;2888&quot; data-origin-height=&quot;916&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;호출 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-07 오후 1.37.10.png&quot; data-origin-width=&quot;3006&quot; data-origin-height=&quot;198&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbb8JY/btr8A32nubW/r7ICX6TLKc0AybpPYQqMX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbb8JY/btr8A32nubW/r7ICX6TLKc0AybpPYQqMX1/img.png&quot; data-alt=&quot;테이블 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbb8JY/btr8A32nubW/r7ICX6TLKc0AybpPYQqMX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbbb8JY%2Fbtr8A32nubW%2Fr7ICX6TLKc0AybpPYQqMX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3006&quot; height=&quot;198&quot; data-filename=&quot;스크린샷 2023-04-07 오후 1.37.10.png&quot; data-origin-width=&quot;3006&quot; data-origin-height=&quot;198&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;테이블 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출이 이루어지고 나면 테이블에서 is_deleted 값이 T로 변경된다. 이렇게 되면 해당 회원은 탈퇴된 것으로 간주된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 간단한 회원 관련 기능에 대한 기능 구현이 끝났다. 아직 구현하지 않은 내용은 refresh_token을 통해서 access_token을 갱신하는 코드인데 해당 코드는 다음 포스팅에서 다루도록 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;refresh_token을 통한 인증까지 마무리되면 가계부에 대한 기능 구현을 시작할 수 있을 수 있을듯 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 구현한 내용 및 앞으로 구현할 내용은 개인 github에 지속적으로 업데이트할 예정이니 아래 주소에서 확인이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/minarae/accountbook_backend&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/minarae/accountbook_backend&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1681046634675&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - minarae/accountbook_backend&quot; data-og-description=&quot;Contribute to minarae/accountbook_backend development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/minarae/accountbook_backend&quot; data-og-url=&quot;https://github.com/minarae/accountbook_backend&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/hIGn4/hySdthvE6t/lYzlYKCtLFgNwWBXoowjkk/img.png?width=1200&amp;amp;height=600&amp;amp;face=993_106_1034_151&quot;&gt;&lt;a href=&quot;https://github.com/minarae/accountbook_backend&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/minarae/accountbook_backend&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/hIGn4/hySdthvE6t/lYzlYKCtLFgNwWBXoowjkk/img.png?width=1200&amp;amp;height=600&amp;amp;face=993_106_1034_151');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - minarae/accountbook_backend&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to minarae/accountbook_backend development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming/Project Log</category>
      <category>FastAPI</category>
      <category>project log</category>
      <category>python</category>
      <category>가계부만들기</category>
      <category>개발기록</category>
      <category>개인프로젝트</category>
      <category>파이썬</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/387</guid>
      <comments>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-Backend-%ED%9A%8C%EC%9B%90-%EA%B4%80%EB%A6%AC-%EA%B8%B0%EB%8A%A5-4#entry387comment</comments>
      <pubDate>Fri, 7 Apr 2023 13:41:22 +0900</pubDate>
    </item>
    <item>
      <title>낳아놓은 아이들이라도 잘 키우자</title>
      <link>https://minarae7.tistory.com/entry/%EB%82%B3%EC%95%84%EB%86%93%EC%9D%80-%EC%95%84%EC%9D%B4%EB%93%A4%EC%9D%B4%EB%9D%BC%EB%8F%84-%EC%9E%98-%ED%82%A4%EC%9A%B0%EC%9E%90</link>
      <description>&lt;figure id=&quot;og_1680787527174&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;&amp;ldquo;친부 맞다&amp;rdquo; 판결에도 버틴다&amp;hellip;호칭도 없는 이들&quot; data-og-description=&quot;[앵커] 낯선 나라에 돈을 벌러 왔다가 한국 남성과 아이까지 낳았지만 아버지에게 외면당한 아이들의 실태...&quot; data-og-host=&quot;news.kbs.co.kr&quot; data-og-source-url=&quot;https://news.kbs.co.kr/news/view.do?ncd=7645188&quot; data-og-url=&quot;https://news.kbs.co.kr/news/view.do?ncd=7645188&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/zdYyC/hyScwLmxfu/bKIKKD3aaPfUhmckT6REb0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/dDSHNH/hyScIygweJ/gIVYTjOVbNDfRTk12rRhMk/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/dyUAuW/hyScB6Y85A/0bmcVFlv45uuOEglWPjCD0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot;&gt;&lt;a href=&quot;https://news.kbs.co.kr/news/view.do?ncd=7645188&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://news.kbs.co.kr/news/view.do?ncd=7645188&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/zdYyC/hyScwLmxfu/bKIKKD3aaPfUhmckT6REb0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/dDSHNH/hyScIygweJ/gIVYTjOVbNDfRTk12rRhMk/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/dyUAuW/hyScB6Y85A/0bmcVFlv45uuOEglWPjCD0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;친부 맞다&amp;rdquo; 판결에도 버틴다&amp;hellip;호칭도 없는 이들&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;[앵커] 낯선 나라에 돈을 벌러 왔다가 한국 남성과 아이까지 낳았지만 아버지에게 외면당한 아이들의 실태...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;news.kbs.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1680787587636&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[단독]친부가 &amp;lsquo;중혼 상태&amp;rsquo;&amp;hellip;한국에 온 &amp;lsquo;코피노&amp;rsquo;는 엄마와 함께 못 삽니다&quot; data-og-description=&quot;한국 법원 판결로 한국 국적을 취득한 이른바 &amp;lsquo;코피노(한국 남성과 필리핀 여성 사이에서 태어...&quot; data-og-host=&quot;www.khan.co.kr&quot; data-og-source-url=&quot;https://www.khan.co.kr/national/court-law/article/202303151713001&quot; data-og-url=&quot;https://www.khan.co.kr/article/202303151713001&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/hvdUt/hySa08n1uR/7NnA6o64rJnUjnsPxwlzl1/img.jpg?width=700&amp;amp;height=511&amp;amp;face=67_95_598_234,https://scrap.kakaocdn.net/dn/LaZQE/hyScDjplQr/ytYnICCqEza5QXxkrKAp40/img.jpg?width=700&amp;amp;height=511&amp;amp;face=67_95_598_234,https://scrap.kakaocdn.net/dn/b2TfSE/hyScCx2yt2/0Nx2n2IdtQiS3NNJykdcFk/img.jpg?width=700&amp;amp;height=1187&amp;amp;face=153_76_507_1131&quot;&gt;&lt;a href=&quot;https://www.khan.co.kr/national/court-law/article/202303151713001&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.khan.co.kr/national/court-law/article/202303151713001&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/hvdUt/hySa08n1uR/7NnA6o64rJnUjnsPxwlzl1/img.jpg?width=700&amp;amp;height=511&amp;amp;face=67_95_598_234,https://scrap.kakaocdn.net/dn/LaZQE/hyScDjplQr/ytYnICCqEza5QXxkrKAp40/img.jpg?width=700&amp;amp;height=511&amp;amp;face=67_95_598_234,https://scrap.kakaocdn.net/dn/b2TfSE/hyScCx2yt2/0Nx2n2IdtQiS3NNJykdcFk/img.jpg?width=700&amp;amp;height=1187&amp;amp;face=153_76_507_1131');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[단독]친부가 &amp;lsquo;중혼 상태&amp;rsquo;&amp;hellip;한국에 온 &amp;lsquo;코피노&amp;rsquo;는 엄마와 함께 못 삽니다&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;한국 법원 판결로 한국 국적을 취득한 이른바 &amp;lsquo;코피노(한국 남성과 필리핀 여성 사이에서 태어...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.khan.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코피노라는 말이 있다. 코피노는 한국 남자와 외국 특히 동남아쪽 어린 여자 사이에서 태어났지만 아빠에게 버림 받은 아이들을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 위키를 참조해보면 해당 나라에서는 이게 얼마나 사회적 이슈였는지 알 수 있다.&lt;/p&gt;
&lt;figure id=&quot;og_1680787711719&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;코피노 - 나무위키&quot; data-og-description=&quot;2백여 명의 코피노 엄마와 인터뷰가 남아있으며 팔십여 명 정도는 인터뷰 기록을 남기었다. 몇몇 사례는 코피노 아빠들과도 대화하며 양쪽 입장을 들은 경우도 있다. 코피노의 아빠가 되는 남성&quot; data-og-host=&quot;namu.wiki&quot; data-og-source-url=&quot;https://namu.wiki/w/%EC%BD%94%ED%94%BC%EB%85%B8&quot; data-og-url=&quot;https://namu.wiki/w/%EC%BD%94%ED%94%BC%EB%85%B8&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://namu.wiki/w/%EC%BD%94%ED%94%BC%EB%85%B8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://namu.wiki/w/%EC%BD%94%ED%94%BC%EB%85%B8&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;코피노 - 나무위키&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;2백여 명의 코피노 엄마와 인터뷰가 남아있으며 팔십여 명 정도는 인터뷰 기록을 남기었다. 몇몇 사례는 코피노 아빠들과도 대화하며 양쪽 입장을 들은 경우도 있다. 코피노의 아빠가 되는 남성&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;namu.wiki&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코피노라는 말에서 알 수 있듯이 코리아 한국과 필리핀 여자 사이에서 낳은 아이들을 의미했지만 지금은 넓은 의미에서 한국 남자에게 버린 받은 현지의 아이들을 의미한다고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어제 오늘 뉴스를 보면서 이런 아이들을 한국 정부에서 적극적으로 지원해서 한국에서 살아갈 수 있도록 해주어야 한다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한국에 아빠를 찾으러 왔고 한국 법원에서 친자로 인정해줬으면 그 이후의 절차는 정부에서 좀 더 적극적으로 진행해줄 필요가 있어 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;젊은 세대가 아이들을 안 낳는다고 걱정할 게 아니고 이렇게 이미 낳은 아이들이라도 적극적으로 받아들여서 한국인으로 인정하고 유치할 필요가 있어보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 나라는 유난히 타 나라 사람들을 받아들이는데 소극적이다. 하지만 이 아이들은 외국인이 아니고 한국인 아빠를 둔 한국인으로 인정해주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경계를 낮추고 우리의 아이들을 더 적극적으로 받아들이는 자세가 필요해보인다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-06 오후 10.38.03.png&quot; data-origin-width=&quot;1462&quot; data-origin-height=&quot;1084&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/z3D0W/btr8uTe2nmU/r3VkYrpNYRAOphRLwak9w1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/z3D0W/btr8uTe2nmU/r3VkYrpNYRAOphRLwak9w1/img.png&quot; data-alt=&quot;코피노 설명&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/z3D0W/btr8uTe2nmU/r3VkYrpNYRAOphRLwak9w1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fz3D0W%2Fbtr8uTe2nmU%2Fr3VkYrpNYRAOphRLwak9w1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1462&quot; height=&quot;1084&quot; data-filename=&quot;스크린샷 2023-04-06 오후 10.38.03.png&quot; data-origin-width=&quot;1462&quot; data-origin-height=&quot;1084&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;코피노 설명&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1680788440901&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;한국 출산율 최악, 10년째 OECD 꼴찌&quot; data-og-description=&quot;지난해 한국의 합계출산율이 0.78명으로 곤두박질쳤다. 2018년(0.98명)에 처음 0명대로 주저앉은 후 계속 감소세다. 2025년엔 ...&quot; data-og-host=&quot;www.yonhapmidas.com&quot; data-og-source-url=&quot;http://www.yonhapmidas.com/article/230404115009_543869&quot; data-og-url=&quot;http://www.yonhapmidas.com/article/230404115009_543869&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dzVgc3/hyScEQaPRJ/PpCiMqTrqPFdHPGjig2KC1/img.jpg?width=400&amp;amp;height=394&amp;amp;face=0_0_400_394,https://scrap.kakaocdn.net/dn/vyP2h/hySaYQhy6U/Cv8shL5w1S66Z5XMVOYg9K/img.jpg?width=400&amp;amp;height=394&amp;amp;face=0_0_400_394&quot;&gt;&lt;a href=&quot;http://www.yonhapmidas.com/article/230404115009_543869&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;http://www.yonhapmidas.com/article/230404115009_543869&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dzVgc3/hyScEQaPRJ/PpCiMqTrqPFdHPGjig2KC1/img.jpg?width=400&amp;amp;height=394&amp;amp;face=0_0_400_394,https://scrap.kakaocdn.net/dn/vyP2h/hySaYQhy6U/Cv8shL5w1S66Z5XMVOYg9K/img.jpg?width=400&amp;amp;height=394&amp;amp;face=0_0_400_394');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;한국 출산율 최악, 10년째 OECD 꼴찌&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;지난해 한국의 합계출산율이 0.78명으로 곤두박질쳤다. 2018년(0.98명)에 처음 0명대로 주저앉은 후 계속 감소세다. 2025년엔 ...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.yonhapmidas.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>기사모음과 생각</category>
      <category>9시뉴스</category>
      <category>사회적문제</category>
      <category>코피노</category>
      <category>핵심뉴스</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/386</guid>
      <comments>https://minarae7.tistory.com/entry/%EB%82%B3%EC%95%84%EB%86%93%EC%9D%80-%EC%95%84%EC%9D%B4%EB%93%A4%EC%9D%B4%EB%9D%BC%EB%8F%84-%EC%9E%98-%ED%82%A4%EC%9A%B0%EC%9E%90#entry386comment</comments>
      <pubDate>Thu, 6 Apr 2023 22:41:27 +0900</pubDate>
    </item>
    <item>
      <title>[가계부 만들기] Backend - 회원 관리 기능 #3</title>
      <link>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-Backend-%ED%9A%8C%EC%9B%90-%EA%B4%80%EB%A6%AC-%EA%B8%B0%EB%8A%A5-3</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;logo-teal.png&quot; data-origin-width=&quot;1023&quot; data-origin-height=&quot;369&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/D96oH/btr8f1xWCag/0gH6gDa0zom73pXuPFIRq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/D96oH/btr8f1xWCag/0gH6gDa0zom73pXuPFIRq0/img.png&quot; data-alt=&quot;FastAPI Logo&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/D96oH/btr8f1xWCag/0gH6gDa0zom73pXuPFIRq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FD96oH%2Fbtr8f1xWCag%2F0gH6gDa0zom73pXuPFIRq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1023&quot; height=&quot;369&quot; data-filename=&quot;logo-teal.png&quot; data-origin-width=&quot;1023&quot; data-origin-height=&quot;369&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;FastAPI Logo&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능 개발을 해가다보니 앞에 개발했던 부분에서 뭔가 부족한 부분을 발견하고 추가하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞선 회원 관리 기능 포스팅에서 schema에 대한 내용을 정리했었는데 회원 정보에 대한 관리 기능을 구현하다가 보니 처음에는 생각하지 못했던 부분을 추가하게 되었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Schema&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 버전의 schema.py 파일은 아래 내용과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1680703208483&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from pydantic import BaseModel, Field
from typing import Optional

# 회원 가입에 대한 Request Schema
class MemberCreate(BaseModel):
    member_id: str = Field(title=&quot;사용자 아이디&quot;, max_length=30)
    member_pw: str = Field(title=&quot;사용자 패스워드&quot;)
    member_name: str = Field(title=&quot;사용자 이름&quot;, max_length=20)
    member_email: str = Field(title=&quot;사용자 이메일&quot;, max_length=50)

    class Config:
        orm_mode = True

# 회원 정보 수정에 대한 Request Schema
class MemberModify(BaseModel):
    member_pw: Optional[str] = Field(title=&quot;사용자 패스워드&quot;)
    member_name: Optional[str] = Field(title=&quot;사용자 이름&quot;, max_length=20)
    member_email: Optional[str] = Field(title=&quot;사용자 이메일&quot;, max_length=50)

    class Config:
        orm_mode = True

# JWT Decode에 얻어지는 정보에 대한 Schema
class JWTPayload(BaseModel):
    member_no: int = Field(title=&quot;사용자 번호&quot;)
    member_id: str = Field(title=&quot;사용자 아이디&quot;)
    member_name: str = Field(title=&quot;사용자 이름&quot;)
    member_email: str = Field(title=&quot;사용자 이메일&quot;)

# 로그인 성공시 Response로 전송되는 Schema
class LoginResponse(BaseModel):
    member_no: int = Field(title=&quot;사용자 번호&quot;)
    member_id: str = Field(title=&quot;사용자 아이디&quot;)
    member_name: str = Field(title=&quot;사용자 이름&quot;)
    member_email: str = Field(title=&quot;사용자 이메일&quot;)
    access_token: str = Field(title=&quot;Access Token&quot;)
    refresh_token: str = Field(title=&quot;Refresh Token&quot;)

class Message(BaseModel):
    message: str&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 Member 클래스는 MemberCreate로 이름을 변경하고 이 클래스는 회원 정보를 추가할 때만 사용하도록 수정했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면서 회원 정보 수정시에 사용되는 MemberModify 클래스를 추가하였다. MemberCreate와 MemberModify의 차이는 id를 받을지에 대한 여부와 Create에서는 모든 변수가 필수값이지만 Modify에서는 모든 값이 옵션이라는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 JWT Decode 되어서 나오는 Dictionary로 별도의 Class로 선언하여서 라우터에서 받을 수 있도록 Schema로 선언하였다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Router&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 라우터를 수정해보도록 할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;members.py의 내용을 아래와 같이 변경한다.&lt;/p&gt;
&lt;pre id=&quot;code_1680703503165&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from fastapi import APIRouter, Depends, Body
from sqlalchemy.orm import Session
from starlette.responses import Response, JSONResponse
from starlette.status import HTTP_200_OK, HTTP_201_CREATED, HTTP_400_BAD_REQUEST
from typing import Optional
from ..database.connection import get_db
from ..database import schemas
from ..libraries import auth

router = APIRouter(
    prefix=&quot;/members&quot;,
    tags=[&quot;member&quot;],
    responses={
        404: {&quot;description&quot;: &quot;Not Found&quot;},
    }
)


# 회원 가입
@router.post(&quot;/create&quot;, description=&quot;회원 가입&quot;, response_class=Response, responses={
    HTTP_400_BAD_REQUEST: {
        &quot;model&quot;: schemas.Message
    }
})
async def create(
    member: schemas.MemberCreate = Body(
        title=&quot;회원정보&quot;,
        example={
            &quot;member_id&quot;: &quot;foo&quot;,
            &quot;member_pw&quot;: &quot;1234567890&quot;,
            &quot;member_name&quot;: &quot;홍길동&quot;,
            &quot;member_email&quot;: &quot;test@example.com&quot;,
        }
    ),
    db: Session = Depends(get_db)
):
    try:
        return Response(status_code=HTTP_201_CREATED)
    except Exception as e:
        return JSONResponse(content={&quot;detail&quot;: e.args[0]}, status_code=HTTP_400_BAD_REQUEST)


# 로그인
@router.post(&quot;/login&quot;, description=&quot;로그인&quot;, response_model=schemas.LoginResponse, responses={
    HTTP_400_BAD_REQUEST: {
        &quot;model&quot;: schemas.Message
    }
})
async def login(
    member_id: str = Body(title=&quot;사용자 아이디&quot;),
    member_pw: str = Body(title=&quot;사용자 패스워드&quot;),
    db: Session = Depends(get_db)
):
    try:
        return result
    except Exception as e:
        return JSONResponse(content={&quot;detail&quot;: e.args[0]}, status_code=HTTP_400_BAD_REQUEST)


# 회원 정보 수정
@router.put(&quot;/modify&quot;, description=&quot;회원 정보 수정&quot;, response_class=Response, responses={
    HTTP_400_BAD_REQUEST: {
        &quot;model&quot;: schemas.Message
    }
})
async def modify(
    payload: schemas.JWTPayload = Depends(auth.decode_access_token),
    member: schemas.MemberModify = Body(
        title=&quot;수정할 회원정보&quot;,
        example={
            &quot;member_pw&quot;: &quot;1234567890&quot;,
            &quot;member_name&quot;: &quot;홍길동&quot;,
            &quot;member_email&quot;: &quot;test@example.com&quot;,
        }
    ),
    db: Session = Depends(get_db)
):
    try:
        return Response(status_code=HTTP_200_OK)
    except Exception as e:
        return JSONResponse(content={&quot;detail&quot;: e.args[0]}, status_code=HTTP_400_BAD_REQUEST)


# 회원탈퇴
@router.post(&quot;/unsubscribing&quot;, description=&quot;회원탈퇴&quot;, response_class=Response, responses={
    HTTP_400_BAD_REQUEST: {
        &quot;model&quot;: schemas.Message
    }
})
async def unsubscribing(
    payload: schemas.JWTPayload = Depends(auth.decode_access_token),
    db: Session = Depends(get_db)
):
    try:
        return Response(status_code=HTTP_200_OK)
    except Exception as e:
        return JSONResponse(content={&quot;detail&quot;: e.args[0]}, status_code=HTTP_400_BAD_REQUEST)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 DB 작업에 대한 내용을 구현되지 않았으니 router에서 액션을 호출하는 일은 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 비즈니스 로직을 구현하기 전에 먼저 Exception이 발생하는 경우에 대한 예외 처리를 먼저 추가했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 reseponses를 추가해서 Exception이 발생했을 때 전달할 Response 타입을 미리 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;router를 수정한 후에 redoc 페이지를 확인하면 아래와 같이 작동하는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-06 오후 10.48.36.png&quot; data-origin-width=&quot;1894&quot; data-origin-height=&quot;746&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTF39X/btr8w8vLgL9/Mig280lCBjH4v7Vjc2kKF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTF39X/btr8w8vLgL9/Mig280lCBjH4v7Vjc2kKF0/img.png&quot; data-alt=&quot;회원가입&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTF39X/btr8w8vLgL9/Mig280lCBjH4v7Vjc2kKF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTF39X%2Fbtr8w8vLgL9%2FMig280lCBjH4v7Vjc2kKF0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1894&quot; height=&quot;746&quot; data-filename=&quot;스크린샷 2023-04-06 오후 10.48.36.png&quot; data-origin-width=&quot;1894&quot; data-origin-height=&quot;746&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;회원가입&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-06 오후 10.48.49.png&quot; data-origin-width=&quot;1850&quot; data-origin-height=&quot;504&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1Kb20/btr8vwYaq8Y/rbcQx00qh2KfZuJLBLnheK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1Kb20/btr8vwYaq8Y/rbcQx00qh2KfZuJLBLnheK/img.png&quot; data-alt=&quot;로그인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1Kb20/btr8vwYaq8Y/rbcQx00qh2KfZuJLBLnheK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1Kb20%2Fbtr8vwYaq8Y%2FrbcQx00qh2KfZuJLBLnheK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1850&quot; height=&quot;504&quot; data-filename=&quot;스크린샷 2023-04-06 오후 10.48.49.png&quot; data-origin-width=&quot;1850&quot; data-origin-height=&quot;504&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;로그인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-06 오후 10.49.01.png&quot; data-origin-width=&quot;1856&quot; data-origin-height=&quot;786&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mFan6/btr8zlBjuTW/jSNSKkuYdtYriJkKv4xYEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mFan6/btr8zlBjuTW/jSNSKkuYdtYriJkKv4xYEk/img.png&quot; data-alt=&quot;회원 정보 수정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mFan6/btr8zlBjuTW/jSNSKkuYdtYriJkKv4xYEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmFan6%2Fbtr8zlBjuTW%2FjSNSKkuYdtYriJkKv4xYEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1856&quot; height=&quot;786&quot; data-filename=&quot;스크린샷 2023-04-06 오후 10.49.01.png&quot; data-origin-width=&quot;1856&quot; data-origin-height=&quot;786&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;회원 정보 수정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-06 오후 10.49.14.png&quot; data-origin-width=&quot;1870&quot; data-origin-height=&quot;460&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vg65j/btr8llwLtsd/bMwh6xxLGxV3JaudrRazV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vg65j/btr8llwLtsd/bMwh6xxLGxV3JaudrRazV1/img.png&quot; data-alt=&quot;회원탈퇴&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vg65j/btr8llwLtsd/bMwh6xxLGxV3JaudrRazV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvg65j%2Fbtr8llwLtsd%2FbMwh6xxLGxV3JaudrRazV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1870&quot; height=&quot;460&quot; data-filename=&quot;스크린샷 2023-04-06 오후 10.49.14.png&quot; data-origin-width=&quot;1870&quot; data-origin-height=&quot;460&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;회원탈퇴&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;검증(auth)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 jwt 인증과 관련 코드도 약간 수정하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1680704508732&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from jose import JWTError, jwt
from fastapi import HTTPException, status, Header

# JWT 생성 함수
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=15)
    to_encode.update({&quot;exp&quot;: expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt


# JWT 검증 함수
def decode_access_token(token: str = Header(&quot;token&quot;)):
    encode = token.split(&quot; &quot;)
    if len(encode) != 2 or encode[0] != 'Bearer':
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=&quot;Invalid authentication credentials&quot;,
            headers={&quot;WWW-Authenticate&quot;: &quot;Bearer&quot;},
        )

    try:
        payload = jwt.decode(encode[1], SECRET_KEY, algorithms=[ALGORITHM])
        return payload
    except JWTError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=&quot;Invalid authentication credentials&quot;,
            headers={&quot;WWW-Authenticate&quot;: &quot;Bearer&quot;},
        )&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JWT 검증을 하기 위한 decode_access_token에서 파라미터로 전달하는 token의 값은 request의 header에서 꺼내서 사용하도록 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더불어 token은 &quot;Bearer&quot;로 시작하여야 하도록 검사하는 코드를 추가하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 decode_access_token에서 종료후에 전달되는 payload는 위의 Schema에서 정의한 JWTPayload가 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 회원 관련 기능을 구현하면서 변경된 부분을 추가로 정리하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음에는 포스팅에서는 마지막으로 라우터에서 호출할 비즈니스 로직을 담당하는 service 영역에 대한 코드를 작성하도록 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming/Project Log</category>
      <category>backend</category>
      <category>FastAPI</category>
      <category>python</category>
      <category>가계부만들기</category>
      <category>백엔드</category>
      <category>파이썬</category>
      <category>프로젝트로그</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/385</guid>
      <comments>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-Backend-%ED%9A%8C%EC%9B%90-%EA%B4%80%EB%A6%AC-%EA%B8%B0%EB%8A%A5-3#entry385comment</comments>
      <pubDate>Wed, 5 Apr 2023 23:28:21 +0900</pubDate>
    </item>
    <item>
      <title>[베란다 농사] 새로운 싹, 그리고 비료</title>
      <link>https://minarae7.tistory.com/entry/%EB%B2%A0%EB%9E%80%EB%8B%A4-%EB%86%8D%EC%82%AC-%EC%83%88%EB%A1%9C%EC%9A%B4-%EC%8B%B9-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EB%B9%84%EB%A3%8C</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;며칠 전에 옮겨심고 남은 해바라기와 방울토마토의 상태가 비리비리 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무래도 옮겨심으면서 뭔가 잘못한거 같다. 너무 빨리 했던지 아니면 영양소가 부족하던지.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 오늘은 얘들을 어떻게 하면 다시 빠릿빠릿하게 해줄 수 있을까 고민을 해봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마침 재택근무를 하기로 한 날이니 아침부터 비료를 좀 줘야겠다는 생각으로 이 화분 저 화분 전에 사준 계분을 주었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7535.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lIheT/btr72oUE9EX/ZgWbhxMjtMFUvgsxQxV7Rk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lIheT/btr72oUE9EX/ZgWbhxMjtMFUvgsxQxV7Rk/img.jpg&quot; data-alt=&quot;해바라기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lIheT/btr72oUE9EX/ZgWbhxMjtMFUvgsxQxV7Rk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlIheT%2Fbtr72oUE9EX%2FZgWbhxMjtMFUvgsxQxV7Rk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;800&quot; data-filename=&quot;IMG_7535.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;해바라기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해바라기는 정말 잘 크고 있었는데 옮겨 심은 뒤 자리를 잘 잡나 싶었는데 아래쪽 잎이 누렇게 변색되지 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해바라기 꽃이 활짝 피는걸 보고 싶은데 이렇게 잎이 변색되는걸 보자 영양분이라도 좀 줘야겠다고 싶었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전에 야채 키우려고 사온 계분을 윗거름으로 주고 분무기로 열심히 물을 줬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계분의 효과가 얼마나 있는지 며칠 지켜보아야겠다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7536.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GLaPn/btr745sQRek/pf3Aprmk1sqFGcssXbmg6k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GLaPn/btr745sQRek/pf3Aprmk1sqFGcssXbmg6k/img.jpg&quot; data-alt=&quot;방울토마토&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GLaPn/btr745sQRek/pf3Aprmk1sqFGcssXbmg6k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGLaPn%2Fbtr745sQRek%2Fpf3Aprmk1sqFGcssXbmg6k%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;800&quot; data-filename=&quot;IMG_7536.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;방울토마토&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해바라기에 비해서 아예 자리를 못 잡는거 같은 토마토는 아주 걱정이 많다. 혹시 몰라서 해바라기보다 뿌리도 더 많이 챙겨왔는데 다들 비리비리 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 약간 자리를 잡는 기운도 보여서 얼른 계분을 얻어주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뿌리를 많이 가져다가 심었으니 여기서 2~3 뿌리만 살아도 집에서 따먹는데는 큰 무리가 없을거 같고, 몇 뿌리 더 건지면 다른지에 나눠줄 수도 있을거 같은 기대가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 키워서 부디 주변에 나눠줄 수 있기를 소망해본다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7537.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzS9zZ/btr77PphAU7/hPPFkNwZsksdOwhuiC0PK0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzS9zZ/btr77PphAU7/hPPFkNwZsksdOwhuiC0PK0/img.jpg&quot; data-alt=&quot;봉선화 화분&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzS9zZ/btr77PphAU7/hPPFkNwZsksdOwhuiC0PK0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzS9zZ%2Fbtr77PphAU7%2FhPPFkNwZsksdOwhuiC0PK0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;800&quot; data-filename=&quot;IMG_7537.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;봉선화 화분&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 심었던 봉선화도 키가 너무 크고 잘 자라서 그 아이는 둘째 어린이집 화단에 양보하고 사와서 다 못 심고 남아있던 봉선화 씨를 추가 화분에 심어주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전에 싹이 나는데 일주일 정도 걸려서 이번에도 그렇겠지 했는데 이번에 더 빠릿빠릿하게 올라오는거 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7538_1.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/T4jeJ/btr74tHne6r/bxxK3nhro2AofhH5Ruaaj1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/T4jeJ/btr74tHne6r/bxxK3nhro2AofhH5Ruaaj1/img.jpg&quot; data-alt=&quot;봉성화 싹&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/T4jeJ/btr74tHne6r/bxxK3nhro2AofhH5Ruaaj1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FT4jeJ%2Fbtr74tHne6r%2FbxxK3nhro2AofhH5Ruaaj1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;800&quot; data-filename=&quot;IMG_7538_1.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;봉성화 싹&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;봉선화 씨를 심은게 아지 4일밖에 안되었는데 봉선화 싹이 발견되었다. 아마도 이 녀석들을 선두로 다른 씨앗도 발아되어서 싹이 올라오지 않을까 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날이 많이 더워졌다고 하더니만 싹이 나는 속도도 무지막지하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7539.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJ81Ff/btr76yBpM6J/y6AnthpYQr5iK4JEArnhX1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJ81Ff/btr76yBpM6J/y6AnthpYQr5iK4JEArnhX1/img.jpg&quot; data-alt=&quot;화분 구성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJ81Ff/btr76yBpM6J/y6AnthpYQr5iK4JEArnhX1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJ81Ff%2Fbtr76yBpM6J%2Fy6AnthpYQr5iK4JEArnhX1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7539.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;화분 구성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 해서 작은 화분들의 구성이 약간의 변화가 생겼다. 기존의 봉선화 화분은 어린이집에 봉선화를 심고 와서 빈 화분이 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난주에 수원시 행사에서 고무나무를 건져왔다. 그리고 새로 심은 봉선화 화분에서는 어느덧 싹이 올라오기 시작했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7540.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2ewKF/btr719DmaRC/PT2R68EpN5eBiVGLaAcckk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2ewKF/btr719DmaRC/PT2R68EpN5eBiVGLaAcckk/img.jpg&quot; data-alt=&quot;바질과 로메인 상추&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2ewKF/btr719DmaRC/PT2R68EpN5eBiVGLaAcckk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2ewKF%2Fbtr719DmaRC%2FPT2R68EpN5eBiVGLaAcckk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7540.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;바질과 로메인 상추&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이왕 비료를 주는 김에 따먹으려고 기르는 야채 화분에도 계분을 잔뜩 준다. 윗거름의 역할로 주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화분을 심으면서 아래에 계분에 넣어주었지만 맛나고 싱싱한 야채를 얻기 위해서 추가로 윗거름을 주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상추가 아직은 좀 비리비리한 느낌이 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7541.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vfUKE/btr752WZpqZ/s62kXncWEuMAUVaFQhbZf1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vfUKE/btr752WZpqZ/s62kXncWEuMAUVaFQhbZf1/img.jpg&quot; data-alt=&quot;딸기와 로메인 상추&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vfUKE/btr752WZpqZ/s62kXncWEuMAUVaFQhbZf1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvfUKE%2Fbtr752WZpqZ%2Fs62kXncWEuMAUVaFQhbZf1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7541.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;딸기와 로메인 상추&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면에 딸기는 완전히 자리를 잡고 쭉쭉 잘 자라고 있다. 과연 딸기에서는 딸기가 열려서 맛을 볼 수 있을까 신기하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로메인 상추도 약간 변색이 되는거 같아. 여기도 계분이 필요하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7542.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bc8koQ/btr73Oyn0Bm/kCa2d54gkbdi9JyHSWdKx1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bc8koQ/btr73Oyn0Bm/kCa2d54gkbdi9JyHSWdKx1/img.jpg&quot; data-alt=&quot;야채 화분&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bc8koQ/btr73Oyn0Bm/kCa2d54gkbdi9JyHSWdKx1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbc8koQ%2Fbtr73Oyn0Bm%2FkCa2d54gkbdi9JyHSWdKx1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7542.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;야채 화분&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 해서 야채 화분도 계분으로 비료주기까지 완료.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래는 여기서 모판을 챙겨와서 양귀비를 좀 심어보려고 했는데 너무 바빠서 그건 나중으로 미루고 일단 여기까지만.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비가 와서 이제 제법 건조한 것도 해결될거 같고 계분으로 비료도 잔뜩 줬으니 이 아이들이 주말까지 잘 자라주기만을 기대해본다.&lt;/p&gt;</description>
      <category>My Story/식물재배기</category>
      <category>계분비료</category>
      <category>딸기</category>
      <category>로메인상추</category>
      <category>바질</category>
      <category>방울토마토</category>
      <category>베란단농사</category>
      <category>봉선화</category>
      <category>새싹</category>
      <category>해바라기</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/384</guid>
      <comments>https://minarae7.tistory.com/entry/%EB%B2%A0%EB%9E%80%EB%8B%A4-%EB%86%8D%EC%82%AC-%EC%83%88%EB%A1%9C%EC%9A%B4-%EC%8B%B9-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EB%B9%84%EB%A3%8C#entry384comment</comments>
      <pubDate>Tue, 4 Apr 2023 23:11:52 +0900</pubDate>
    </item>
    <item>
      <title>[베란다 농사] 모종 옮겨심기</title>
      <link>https://minarae7.tistory.com/entry/%EB%B2%A0%EB%9E%80%EB%8B%A4-%EB%86%8D%EC%82%AC-%EB%AA%A8%EC%A2%85-%EC%98%AE%EA%B2%A8%EC%8B%AC%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;가볍게 생각하고 시작한 식물 키우기가 점점 일이 커지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주체할 수 없이 커버린 해바리기와 봉선화를 처리해야 하는데 마땅한 방법이 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 자라는 애들은 그냥 뽑아서 버리기도 아깝고 화분을 더 들이기도 뭐하고 해서 일단 부모님 댁 화단에 옮겨 심기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해바라기는 부모님 댁 화단에 옮겨 심기로 했고 봉선화는 둘째 어린이집 식목일 행사에 가져가서 심기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해바라기를 가져가면서 방울토마토도 옮겨심고 모종 몇 뿌리만 가져오기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부모님이 다행이 집에 화단이 있으셔서 이 아이들을 옮겨심고 잘 키워주시기를 부탁드렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도착해서 해바라기와 방울토마토를 화분에서 꺼내서 옮겨 심었다. 혼자 하다 보니 옮겨심는 사진이 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해바라기는 이미 뿌리가 얼기설기 엉켜서 있어서 그냥 한 뿌리씩 잘라서 옮겨 심었다. 해질녁에 심어서 헤롱헤롱하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 해바라기는 뿌리가 깊고 줄기도 커서 심고 나니 제법 모양새가 난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 방울토마토는 여리여리 해서 옮겨심기가 여간 까다롭지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;줄기만 길지 뿌리는 깊지 않아서 옮겨심기가 어렵다. 좀 더 키워서 옮겼어야 했나 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 일단 시작은 했으니 군데군데 옮겨심고 물을 주었다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7486.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/duxoA3/btr7d8ef7Lu/EiMl36kxKSXJMjQ8IQ37m0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/duxoA3/btr7d8ef7Lu/EiMl36kxKSXJMjQ8IQ37m0/img.jpg&quot; data-alt=&quot;해바라기와 토마토&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/duxoA3/btr7d8ef7Lu/EiMl36kxKSXJMjQ8IQ37m0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FduxoA3%2Fbtr7d8ef7Lu%2FEiMl36kxKSXJMjQ8IQ37m0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7486.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;해바라기와 토마토&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7487.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4naFv/btr7rs2BTb5/d8EO01fCBigivpxeWgaiqk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4naFv/btr7rs2BTb5/d8EO01fCBigivpxeWgaiqk/img.jpg&quot; data-alt=&quot;토마토&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4naFv/btr7rs2BTb5/d8EO01fCBigivpxeWgaiqk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4naFv%2Fbtr7rs2BTb5%2Fd8EO01fCBigivpxeWgaiqk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7487.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;토마토&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해바라기는 사진 찍어보니 그래도 모양새가 보이는데 토마토는 이렇게 멀리서 사진을 찍어두니 보이지도 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 애들이 잘 자라서 방울토마토를 주렁주렁 매달아주기를 바라는건 욕심일까?&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7489.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2ZjCE/btr7iKJLvG8/aEiW3YKDuHW5Y95CS6si91/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2ZjCE/btr7iKJLvG8/aEiW3YKDuHW5Y95CS6si91/img.jpg&quot; data-alt=&quot; 방울토마토&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2ZjCE/btr7iKJLvG8/aEiW3YKDuHW5Y95CS6si91/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2ZjCE%2Fbtr7iKJLvG8%2FaEiW3YKDuHW5Y95CS6si91%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;768&quot; data-filename=&quot;IMG_7489.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; 방울토마토&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;군데군데 물 자리를 자세히 보면 방울토마토 모종들이 보인다. 양이 꽤 많아서 꽤 넓은 부분에 옮겨심어야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 해서 일단 남는 식물들을 버리지 않고 옮겨 심어두기는 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얘네들이 죽으면 어쩔 수 없는 것이고 잘 크면 토마토 따러 가끔 부모님 댁에 방문해야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 식물을 옮겨 심고 남은 뿌리들은 집으러 가지고 와서 다시 화분에 잘 심어두었다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7490.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckLUAx/btr7s23Q4bY/hF6r9379mKM4TRDERHkKbK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckLUAx/btr7s23Q4bY/hF6r9379mKM4TRDERHkKbK/img.jpg&quot; data-alt=&quot;해바라기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckLUAx/btr7s23Q4bY/hF6r9379mKM4TRDERHkKbK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckLUAx%2Fbtr7s23Q4bY%2FhF6r9379mKM4TRDERHkKbK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;800&quot; data-filename=&quot;IMG_7490.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;해바라기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7491.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biGnub/btr7hQQXXcA/wKifaNrlOaaFOCgvFVif7k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biGnub/btr7hQQXXcA/wKifaNrlOaaFOCgvFVif7k/img.jpg&quot; data-alt=&quot;방울토마토&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biGnub/btr7hQQXXcA/wKifaNrlOaaFOCgvFVif7k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiGnub%2Fbtr7hQQXXcA%2FwKifaNrlOaaFOCgvFVif7k%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;800&quot; data-filename=&quot;IMG_7491.jpg&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;방울토마토&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 자기 화분들에 다시 심어주었고 이제 많은 애들이 자리를 떠났으니 나름 넓게 심어주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뿌리채 오래 두었고 밤에 심어서 그런지 시들시들한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내일 해를 좀 보고 그러면 다시 자리를 잡을 것이라고 기대해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;봉선화를 옮겨심고 그 자리 케일을 심으려고 씨를 사왔는데 그것까지 하면 당분간은 물만 잘 주면 될 듯하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전동분무기 하나 주문해야지 분무기로 물주기가 여간 힘들지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘 옮겨 심은 애들이 다 잘 자라기를 기대해본다.&lt;/p&gt;</description>
      <category>My Story/식물재배기</category>
      <category>모종옮기심기</category>
      <category>방울토마토</category>
      <category>베란다농사</category>
      <category>이제 잘 자라는 일만 남았다</category>
      <category>잘 커서 열매를 다오</category>
      <category>해바라기</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/383</guid>
      <comments>https://minarae7.tistory.com/entry/%EB%B2%A0%EB%9E%80%EB%8B%A4-%EB%86%8D%EC%82%AC-%EB%AA%A8%EC%A2%85-%EC%98%AE%EA%B2%A8%EC%8B%AC%EA%B8%B0#entry383comment</comments>
      <pubDate>Sat, 1 Apr 2023 23:50:02 +0900</pubDate>
    </item>
    <item>
      <title>[베란다 농사] 화분에 옮겨 심기</title>
      <link>https://minarae7.tistory.com/entry/%EB%B2%A0%EB%9E%80%EB%8B%A4-%EB%86%8D%EC%82%AC-%ED%99%94%EB%B6%84%EC%97%90-%EC%98%AE%EA%B2%A8-%EC%8B%AC%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 퇴근하고 화분을 옮겨 심어야겠다고 벼르고 집에 왔다. 어제 꽃시장에 가서 상추 묘종과 딸기 묘종을 사와서 그대로 주말까지 방치하면 말라죽을거 같아서 일단 옮겨 심어야겠다가 작정을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되면 한 쪽은 농사 분위기가 나겠지만 베란다에서 수확한 작물을 먹어보겠다는 일념하에 작정하고 진행했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 필요한 것들은 화장실로 옮겨두고 작업 준비를 했다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7462.jpg&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cDnw6S/btr6UDMqbj0/FKJI6FCWUQaHo9MF9B271k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cDnw6S/btr6UDMqbj0/FKJI6FCWUQaHo9MF9B271k/img.jpg&quot; data-alt=&quot;화분에 옮겨 심을 준비물&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cDnw6S/btr6UDMqbj0/FKJI6FCWUQaHo9MF9B271k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcDnw6S%2Fbtr6UDMqbj0%2FFKJI6FCWUQaHo9MF9B271k%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;900&quot; data-filename=&quot;IMG_7462.jpg&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;화분에 옮겨 심을 준비물&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 주말 시장에서 작물을 키우는 흙과 비료로 압축건계분을 사왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸로 심으면 애들이 잘 자란다는 말에 이 구성으로 사와서 식물들을 옮겨 심고자 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제일 오른쪽은 바질부터 해서 로메인상추, 딸기 순으로 심을 계획이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화분은 장모님 댁에서 가져온 놀고 있는 화분. 이게 길고 커서 여러 작물을 심기 좋을거 같다는 판단이다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7463.jpg&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clRCzB/btr61RP6zAv/hU9W1DvcgNVdruS7SK4nb1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clRCzB/btr61RP6zAv/hU9W1DvcgNVdruS7SK4nb1/img.jpg&quot; data-alt=&quot;화분에 흙채우기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clRCzB/btr61RP6zAv/hU9W1DvcgNVdruS7SK4nb1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclRCzB%2Fbtr61RP6zAv%2FhU9W1DvcgNVdruS7SK4nb1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;900&quot; data-filename=&quot;IMG_7463.jpg&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;화분에 흙채우기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 화분에 사온 흙은 먼저 채우고 그 위에 건계분을 올려준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 나서 바질부터 자리를 잡고 딸기를 오른쪽으로 붙여서 자리 잡고 마지막으로 가운데 상추를 심었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딸기와 상추는 묘종을 사온거라서 옮겨심기가 수월하게 되어있는데 집에서 씨를 심어 자란 바질은 아직은 식물 자체가 약한거 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀 더 있다가 옮겼어야 하나라는 생각이 들었을 때는 이미 다 풀어헤쳐진 상태여서 수습이 안되다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 될대로 되겠지라는 생각으로 쭉 심고 화장실에서 물까지 주었다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7464.jpg&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SgXf4/btr64mht62k/xrX5Dwf8kKkFkpMtfq2VzK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SgXf4/btr64mht62k/xrX5Dwf8kKkFkpMtfq2VzK/img.jpg&quot; data-alt=&quot;화분에 심어진 작물들&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SgXf4/btr64mht62k/xrX5Dwf8kKkFkpMtfq2VzK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSgXf4%2Fbtr64mht62k%2FxrX5Dwf8kKkFkpMtfq2VzK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;900&quot; data-filename=&quot;IMG_7464.jpg&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;화분에 심어진 작물들&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다 심고 나니 딸기와 로메인은 제법 모습이 나게 잘 심어진거 같은데 바질은 과연 얘들이 살 수 있을까 하는 모습이 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 내 손으로는 수습이 안되니 무작정 심어두고 물을 주면서 당분간 지켜보는 것으로 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 잘 큰 애들은 크겠지라는 생각이고 최악의 경우 다 죽어버려도 이것도 경험이라고 위안을 삼아볼 심산이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 바질을 심었단 화분이 하나 남으니 이전에 남았던 봉선화 씨앗을 여기에 추가로 심었다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7467.jpg&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWgwO6/btr62Ggt8at/MaLup0NWGJ15dCwPaF1hs1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWgwO6/btr62Ggt8at/MaLup0NWGJ15dCwPaF1hs1/img.jpg&quot; data-alt=&quot;새로운 화분 구성. 오늘 새로 심은 봉선화 화분 추가.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWgwO6/btr62Ggt8at/MaLup0NWGJ15dCwPaF1hs1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWgwO6%2Fbtr62Ggt8at%2FMaLup0NWGJ15dCwPaF1hs1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;900&quot; data-filename=&quot;IMG_7467.jpg&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;새로운 화분 구성. 오늘 새로 심은 봉선화 화분 추가.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해바라기는 이미 키가 너무 크고 화분을 들어보니 뿌리가 화분 밖으로 막 빠져나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일주일 늦게 심은 봉선화도 키가 제법 많이 커서 마찬가지로 뿌리가 화분 밑으로 삐져나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토마토는 아직 더디 크는거 같은데 좀 더 키워서 큰 화분에 2~3 뿌리 정도만 옮겨 심고 나머지는 다른데 분양을 하던지 해야할거 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 오늘 새로 심은 봉선화까지 해서 다시 새로운 화분 구성을 만들었다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7468.jpg&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;1200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dpA36Z/btr65o0rJWC/mejtkzoyxIYOHyTjPe9JKk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dpA36Z/btr65o0rJWC/mejtkzoyxIYOHyTjPe9JKk/img.jpg&quot; data-alt=&quot;새로 추가된 고무나무&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dpA36Z/btr65o0rJWC/mejtkzoyxIYOHyTjPe9JKk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdpA36Z%2Fbtr65o0rJWC%2FmejtkzoyxIYOHyTjPe9JKk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;603&quot; height=&quot;804&quot; data-filename=&quot;IMG_7468.jpg&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;1200&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;새로 추가된 고무나무&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;거기에 어제 어린이집에서 참석한 식목일 행사에서 고무나무 화분을 줘서 화분이 하나 더 추가 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 저 아래에 고무나무가 있는데 다른 나무보다 비교적 잘 크는 고무나무가 키우기는 수월할듯 해서 이걸로 가져왔다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7469.jpg&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;1200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVO6Xz/btr6ZA9GnAp/0yLDqK8aZW9RmH7wrSZBj0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVO6Xz/btr6ZA9GnAp/0yLDqK8aZW9RmH7wrSZBj0/img.jpg&quot; data-alt=&quot;라넌큘러스 화분&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVO6Xz/btr6ZA9GnAp/0yLDqK8aZW9RmH7wrSZBj0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVO6Xz%2Fbtr6ZA9GnAp%2F0yLDqK8aZW9RmH7wrSZBj0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;655&quot; height=&quot;873&quot; data-filename=&quot;IMG_7469.jpg&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;1200&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;라넌큘러스 화분&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 마지막으로 장모님과 아내와 함께 갔다 꽃시장에서 사온 라넌큘러스.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화분 별로 안좋아하는 와이프가 왠일로 꽃 화분을 다 사자고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 호응하여 아예 큰 화분으로 분갈이까지 해서 사온 녀석. 이미 사계절 내내 피는 덴마크 무궁화가 있어서 둘이 쌍으로 꽃을 피우기를 기대해본다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7470.jpg&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/duPsxW/btr60DR6Sfs/Q15GL47Bpf6iaAkEI9WKjk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/duPsxW/btr60DR6Sfs/Q15GL47Bpf6iaAkEI9WKjk/img.jpg&quot; data-alt=&quot;베란다에 자리한 화분&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/duPsxW/btr60DR6Sfs/Q15GL47Bpf6iaAkEI9WKjk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FduPsxW%2Fbtr60DR6Sfs%2FQ15GL47Bpf6iaAkEI9WKjk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;900&quot; data-filename=&quot;IMG_7470.jpg&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;베란다에 자리한 화분&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 분갈이한 화분을 베란데 내어두었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화분이 햇볕을 잘 받아서 얼른 자리 잡으라고 해를 잘 받을 포지션으로 자리해두었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;며칠 두고보면 이 식물들이 얼마나 잘 크는지 알 수 있을거 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올해는 이 녀석들을 잘 키워서 수확물을 얻어보는걸 목표로 삼았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사랑으로 잘 키워서 베란다 화단을 만들어보자.&lt;/p&gt;</description>
      <category>My Story/식물재배기</category>
      <category>딸기</category>
      <category>라넌큘러스</category>
      <category>로메인상추</category>
      <category>바질</category>
      <category>베란다농사</category>
      <category>베란다화단</category>
      <category>봉선화</category>
      <category>식물키우기</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/382</guid>
      <comments>https://minarae7.tistory.com/entry/%EB%B2%A0%EB%9E%80%EB%8B%A4-%EB%86%8D%EC%82%AC-%ED%99%94%EB%B6%84%EC%97%90-%EC%98%AE%EA%B2%A8-%EC%8B%AC%EA%B8%B0#entry382comment</comments>
      <pubDate>Thu, 30 Mar 2023 22:40:08 +0900</pubDate>
    </item>
    <item>
      <title>[python] Dictionary</title>
      <link>https://minarae7.tistory.com/entry/python-Dictionary</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서 Dictionary(딕셔너리)는 매우 중요한 자료형 중 하나이다. Dictionary는 key-value 쌍으로 이루어진 데이터를 다루기 위한 자료형이다. 이번에는 파이썬의 Dictionary 자료형의 특징, 제약 사항 및 사용 방법 등에 대해 자세히 알아보도록 하겠다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;dictionary.png&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;666&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8CZ5w/btr6N7zguTG/NGlf1bra5LKtJdO9qQHw51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8CZ5w/btr6N7zguTG/NGlf1bra5LKtJdO9qQHw51/img.png&quot; data-alt=&quot;dictionary의 선언&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8CZ5w/btr6N7zguTG/NGlf1bra5LKtJdO9qQHw51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8CZ5w%2Fbtr6N7zguTG%2FNGlf1bra5LKtJdO9qQHw51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;520&quot; height=&quot;282&quot; data-filename=&quot;dictionary.png&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;666&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;dictionary의 선언&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Dictionary의 특징&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Dictionary는 key-value 쌍으로 이루어져 있습니다. 즉, 값을 저장할 때 key를 지정하여 저장하며, 이 key를 이용하여 값을 참조합니다.&lt;/li&gt;
&lt;li&gt;Dictionary는 리스트나 튜플과 달리 순서가 없습니다. 즉, 값을 저장한 순서대로 출력하지 않습니다.&lt;/li&gt;
&lt;li&gt;Dictionary의 key는 변경 불가능한 자료형을 사용해야 합니다. 즉, 문자열, 정수, 실수, 불리언 등을 사용할 수 있지만 리스트나 딕셔너리 등은 사용할 수 없습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Dictionary의 제약사항&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Dictionary에서는 key의 중복이 불가능합니다. 즉, 하나의 key에 대해 여러 개의 value를 가질 수 없습니다.&lt;/li&gt;
&lt;li&gt;Dictionary에서 key는 변경이 불가능한 자료형이어야 합니다.&lt;/li&gt;
&lt;li&gt;Dictionary에서 value는 변경 가능한 자료형과 변경 불가능한 자료형 모두 사용할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Dictionary의 사용 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서부터는 Dictionary를 어떻게 생성하고 접근하고 추가/삭제하는지 알아보도록 하겠다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Dictionary의 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서는 고정된 키와 값으로 쉽게 Dictionary를 생성할 수 있다. 요소의 순서는 중괄호 내에 배치되며, 키:값은 쉼표로 구분된다. 키의 값은 반복될 수 있지만 중복될 수는 없다는 점에 유의해야 한다. 또한 키는 문자열, 튜플 또는 숫자와 같은 변경 불가능한 데이터 유형을 가져야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 예시입니다 -&lt;/p&gt;
&lt;pre id=&quot;code_1680099743747&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Creating a Dictionary
# with Integer Keys
Dict = {1: 'Learning', 2: 'For', 3: Life}
print(&quot;\nDictionary with the use of Integer Keys: &quot;)
print(Dict)

# Output: 
# Dictionary with the use of Integer Keys:
# {1: &amp;lsquo;Learning&amp;rsquo;, 2: &amp;lsquo;For&amp;rsquo;, 3: &amp;lsquo;Life&amp;rsquo;}
  
# Creating a Dictionary
# with Mixed keys
Dict = {'Name': &amp;lsquo;Great Learning&amp;rsquo;, 1: [1, 2, 3, 4]}
print(&quot;\nDictionary with the use of Mixed Keys: &quot;)
print(Dict)

# Output: 
# Dictionary with the use of Mixed Keys: 
# {&amp;lsquo;Name&amp;rsquo;: &amp;lsquo;GreatLearning&amp;rsquo;, 1: [1, 2, 3, 4]}

# 다양한 방법의 Dictionary 선언
dict_1 = {'apple': 1000, 'banana': 2000, 'orange': 1500}
dict_2 = dict([('apple', 1000), ('banana', 2000), ('orange', 1500)])
dict_3 = dict(apple=1000, banana=2000, orange=1500)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Dictionary의 접근&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dictionary의 키와 값은 다양한 방법으로 접근이 가능하다. 위의 코드에서 마지막 dict_x 변수에 대한 접근을 다음과 같이 할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1680100021619&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print(dict_1['apple'])  # 1000
print(dict_2['banana'])  # 2000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dictionary에서 key가 존재하지 않는 경우 KeyError가 발생할 수 있다. 이를 방지하기 위해, key가 존재하지 않는 경우에도 에러가 발생하지 않도록 하는 get() 함수를 사용할 수 있다. get() 함수를 사용하면 key가 존재하지 않는 경우 None을 반환한다. 예를 들어, 위의 dict_1에서 'orange'에 대한 value를 가져오기 위해서는 다음과 같이 코드를 작성할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1680100183903&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print(dict_1.get('orange'))  # 1500&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Dictionary의 추가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dictionary에 새로운 key-value 쌍을 추가하기 위해서는, 대괄호([]) 안에 새로운 key를 넣고, 이를 이용하여 value를 지정해준다다. 예를 들어, 위의 dict_1에 'grape'에 대한 value를 3000으로 추가하려면 다음과 같이 코드를 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1680100449353&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dict_1['grape'] = 3000
print(dict_1)  # {'apple': 1000, 'banana': 2000, 'orange': 1500, 'grape': 3000}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 추가하는 키가 Dictionary에 이미 존재한다면 해당 키에 대한 값을 덮어쓰게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1680100541334&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;my_dict = {&quot;username&quot;: &quot;ABC&quot;, &quot;email&quot;: &quot;abc@gmail.com&quot;, &quot;location&quot;:&quot;Gurgaon&quot;}
my_dict[&quot;username&quot;] = &quot;XYZ&quot; # {&amp;lsquo;username&amp;rsquo;: &amp;lsquo;XYZ&amp;rsquo;, &amp;rsquo;email&amp;rsquo;: &amp;lsquo;abc@gmail.com&amp;rsquo;, &amp;lsquo;location&amp;rsquo;: &amp;lsquo;Gurgaon&amp;rsquo;}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Dictionary의 삭제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dictionary의 요소를 삭제하기 위해서 del 키워드를 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1680100785857&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;del dict['yourkey']  # key에 해당하는 요소를 삭제
del my_dict # 해당 이름의 dictionary 전체를 삭제
your_dict.clear() # 해당 Dictionary의 요소를 모두 삭제하고 빈 Dictionary로 만듬&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제에서 각 요소를 삭제할 때 del 키워드를 사용하고 삭제할 키를 전달하도록 했다. 이 때 다른 방법으로 pop 메소드를 사용하는 방법도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pop은 아래와 같이 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1680101214899&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dict.pop(key, defaultvalue)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 key는 삭제하고자 하는 키를 의미하며, defaultvalue는 전달된 key가 dictionary 안에 없을 경우 전달할 값을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 pop에 대한 사용 예제이다.&lt;/p&gt;
&lt;pre id=&quot;code_1680101308428&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 딕셔너리 생성
dict_1 = {'apple': 1000, 'banana': 2000, 'orange': 1500}

# 'apple' key에 대한 value 제거
value = dict_1.pop('apple')

# 딕셔너리 출력
print(dict_1)  # {'banana': 2000, 'orange': 1500}

# 제거된 value 출력
print(value)  # 1000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pop을 하게 되면 해당 key의 value를 꺼내서 전달하고 dictionary에서 해당하는 key는 삭제한다. 이 때 만약 해당하는 key가 dictionary 안에 존재하지 않는다면 KeyError가 발생한다. 따라서, pop() 함수를 사용하기 전에 딕셔너리에 해당 key가 존재하는지 여부를 먼저 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 KeyError를 회피하기 위해서는 try-except 문을 사용하여거나 defaultvalue를 같이 넘겨주면 된다. 다음의 코드를 참조하자.&lt;/p&gt;
&lt;pre id=&quot;code_1680101597181&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 딕셔너리 생성
dict_1 = {'apple': 1000, 'banana': 2000, 'orange': 1500}

# default_value를 지정하는 경우
value = dict_1.pop('melon', 0)  # default_value를 0으로 설정

print(dict_1)  # {'apple': 1000, 'banana': 2000, 'orange': 1500}
print(value)  # 0

# try-except 구문 사용
try:
    value = my_dict.pop('melon')
except KeyError as e:
    print(f&quot;KeyError: {e} - 딕셔너리에 해당 key 값이 존재하지 않습니다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 Dictionary에 대한 간단한 내용과 사용법을 알아보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;javascript에서는 object과 비슷한 개념으로 웹개발에서는 빈번하게 사용되므로 그 사용법을 익혀두는 것이 좋을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 포스팅은 다음 사이트를 참조하여서 작성되었다.&lt;/p&gt;
&lt;figure id=&quot;og_1680101937328&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Python dictionary append: How to add Key-value Pair?&quot; data-og-description=&quot;Python dictionary append is simply used to add a key/value to the existing dictionary. The dictionary objects are mutable.&quot; data-og-host=&quot;www.mygreatlearning.com&quot; data-og-source-url=&quot;https://www.mygreatlearning.com/blog/python-dictionary-append/&quot; data-og-url=&quot;https://www.mygreatlearning.com/blog/python-dictionary-append/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cqAxnP/hyR6OkFaSw/Qk0kKpedSyDfvZVpLADjl0/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/9CNct/hyR6OLJz5n/apwWs3pvksA613NL2RSl9k/img.jpg?width=1920&amp;amp;height=1431&amp;amp;face=339_246_414_330,https://scrap.kakaocdn.net/dn/6y1zN/hyR6RaCrUF/7RXfK0pYkEcwdd4ioFe8uk/img.jpg?width=2000&amp;amp;height=1066&amp;amp;face=0_0_2000_1066&quot;&gt;&lt;a href=&quot;https://www.mygreatlearning.com/blog/python-dictionary-append/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.mygreatlearning.com/blog/python-dictionary-append/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cqAxnP/hyR6OkFaSw/Qk0kKpedSyDfvZVpLADjl0/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/9CNct/hyR6OLJz5n/apwWs3pvksA613NL2RSl9k/img.jpg?width=1920&amp;amp;height=1431&amp;amp;face=339_246_414_330,https://scrap.kakaocdn.net/dn/6y1zN/hyR6RaCrUF/7RXfK0pYkEcwdd4ioFe8uk/img.jpg?width=2000&amp;amp;height=1066&amp;amp;face=0_0_2000_1066');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Python dictionary append: How to add Key-value Pair?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Python dictionary append is simply used to add a key/value to the existing dictionary. The dictionary objects are mutable.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.mygreatlearning.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming/Python</category>
      <category>dictionary</category>
      <category>python</category>
      <category>python자료형</category>
      <category>딕셔너리</category>
      <category>딕셔너리사용법</category>
      <category>파이썬</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/381</guid>
      <comments>https://minarae7.tistory.com/entry/python-Dictionary#entry381comment</comments>
      <pubDate>Wed, 29 Mar 2023 23:59:17 +0900</pubDate>
    </item>
    <item>
      <title>[가계부 만들기] Backend - 회원 관리 기능 #2</title>
      <link>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-Backend-%ED%9A%8C%EC%9B%90%EA%B4%80%EB%A6%AC%EA%B8%B0%EB%8A%A5-2</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이제 여기서는 회원 정보를 처리하는 방법에 대해서 정리할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;암호화 또는 인증에 관련된 내용은 두 가지 정도로 정리할 수 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째로 회원의 암호를 저장할 때 일반 텍스트로 저장하는 것이 아니고 hash로 처리한 후에 저장하고 로그인할 때 이렇게 hash 처리된 값과 입력한 암호가 일치하는지 확인하는 기능이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째, 인증이 성공한 이후에 해당 사용자는 인증된 사용자임을 파악하기 위해서 토큰을 발생하고 이 토큰이 있는 요청에 대해서만 처리해줄 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해서 별도의 파일로 분리하여서 관련 코드를 정의하도록 하겠다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;인증&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증 관련 내용에 대한 파일은 app 디렉토리 밑에 libraries 디렉토리를 생성하고 auth.py 파일을 생성하여 해당 파일에서 정리하도록 할 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-03-29 오전 10.18.00.png&quot; data-origin-width=&quot;848&quot; data-origin-height=&quot;654&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpnXTR/btr6tYKecT3/KGHtPIv4bkTie5S0oA0J70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpnXTR/btr6tYKecT3/KGHtPIv4bkTie5S0oA0J70/img.png&quot; data-alt=&quot;디렉토리 및 파일 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpnXTR/btr6tYKecT3/KGHtPIv4bkTie5S0oA0J70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpnXTR%2Fbtr6tYKecT3%2FKGHtPIv4bkTie5S0oA0J70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;378&quot; height=&quot;292&quot; data-filename=&quot;스크린샷 2023-03-29 오전 10.18.00.png&quot; data-origin-width=&quot;848&quot; data-origin-height=&quot;654&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;디렉토리 및 파일 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 인증에서 사용할 파이썬 패키지들을 설치해보도록 할 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1680048094595&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ poetry add passlib bcrypt &quot;python-jose[cryptography]&quot;
or
$ pip install passlib bcrypt &quot;python-jose[cryptography]&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래는 poetry add를 하는 것만으로 패키지 설치가 되어야 하지만 이것만 해서 설치되지 않는 경우가 있어서 추가로 pip install을 해야하는 경우가 있을 수 있다. poetry로 설치가 안되면 pip install로 설치하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 작성해보도록 하겠다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;패스워드 암호화&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 사용자가 입력한 패스워드를 hash 처리해서 저장하고 로그인을 시도할 때 이렇게 hash 처리된 값이 올바른 값인지 확인하는 절차를 진행하도록 하겠다.&lt;/p&gt;
&lt;pre id=&quot;code_1680050773187&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from passlib.context import CryptContext

# 암호화를 위한 객체 생성
pwd_context = CryptContext(schemes=[&quot;bcrypt&quot;], deprecated=&quot;auto&quot;)

# 패스워드를 암호화하는 함수
def get_password_hash(password: str) -&amp;gt; str:
    return pwd_context.hash(password)

# 암호화된 패스워드와 입력된 패스워드를 비교하는 함수
def verify_password(plain_password: str, hashed_password: str) -&amp;gt; bool:
    return pwd_context.verify(plain_password, hashed_password)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패스워드를 암호화하는 함수와 암호화된 패스워드와 사용자가 입력한 패스워드가 일치하는 비교하는 함수를 작성하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 로그인하기 위한 코드를 작성해보도록 하겠다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;JWT&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JWT는 Json Web Token의 약자로 근래에 많이 사용하는 토큰 방식이다. 자세한 내용은 공식 사이트에서 확인할 수 있으며 잘 정리된 사이트를 첨부한다.&lt;/p&gt;
&lt;figure id=&quot;og_1680050945157&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;JWT.IO&quot; data-og-description=&quot;JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.&quot; data-og-host=&quot;jwt.io&quot; data-og-source-url=&quot;https://jwt.io/&quot; data-og-url=&quot;http://jwt.io/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/sfl6w/hyR5t9kagX/fSIqmKlkFrTdLqBBRvhwG1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/PVVWZ/hyR5tVOnsj/spJUxaSYwIP29TvuPMvUmK/img.png?width=1024&amp;amp;height=512&amp;amp;face=0_0_1024_512&quot;&gt;&lt;a href=&quot;https://jwt.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jwt.io/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/sfl6w/hyR5t9kagX/fSIqmKlkFrTdLqBBRvhwG1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/PVVWZ/hyR5tVOnsj/spJUxaSYwIP29TvuPMvUmK/img.png?width=1024&amp;amp;height=512&amp;amp;face=0_0_1024_512');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;JWT.IO&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;jwt.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1680051261743&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;JWT(Json Web Token) 알아가기&quot; data-og-description=&quot;jwt가 생겨난 이유부터 jwt의 실제 구조까지 | 사실 꾸준히 작성하고 싶었던 글이지만 JWT를 제대로 개념을 정리하고 구현을 진행해본 적이 없었는데 리얼월드 프로젝트를 진행하면서 JWT에 대한 &quot; data-og-host=&quot;brunch.co.kr&quot; data-og-source-url=&quot;https://brunch.co.kr/@jinyoungchoi95/1&quot; data-og-url=&quot;https://brunch.co.kr/@jinyoungchoi95/1&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/17t3W/hyR5uUIlhT/QHZrykUNkrjYWJaB4xf2Mk/img.png?width=772&amp;amp;height=360&amp;amp;face=0_0_772_360,https://scrap.kakaocdn.net/dn/tVvYe/hyR353jHn5/ZRTlFQfn3768B82ihuDRRk/img.png?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500,https://scrap.kakaocdn.net/dn/bs1Ygr/hyR37z1u4Z/CrJ306ifUfW2KIgVaGTKyK/img.png?width=1280&amp;amp;height=817&amp;amp;face=0_0_1280_817&quot;&gt;&lt;a href=&quot;https://brunch.co.kr/@jinyoungchoi95/1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://brunch.co.kr/@jinyoungchoi95/1&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/17t3W/hyR5uUIlhT/QHZrykUNkrjYWJaB4xf2Mk/img.png?width=772&amp;amp;height=360&amp;amp;face=0_0_772_360,https://scrap.kakaocdn.net/dn/tVvYe/hyR353jHn5/ZRTlFQfn3768B82ihuDRRk/img.png?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500,https://scrap.kakaocdn.net/dn/bs1Ygr/hyR37z1u4Z/CrJ306ifUfW2KIgVaGTKyK/img.png?width=1280&amp;amp;height=817&amp;amp;face=0_0_1280_817');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;JWT(Json Web Token) 알아가기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;jwt가 생겨난 이유부터 jwt의 실제 구조까지 | 사실 꾸준히 작성하고 싶었던 글이지만 JWT를 제대로 개념을 정리하고 구현을 진행해본 적이 없었는데 리얼월드 프로젝트를 진행하면서 JWT에 대한&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;brunch.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 로그인에 성공하면 사용자에게 사용자 정보와 앞으로 요청에서 사용할 토큰을 발급한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;auth.py에서는 사용자 정보를 기반으로 토큰을 생성하는 코드와 사용자에게 받은 토큰이 유효한지 확인하는 코드를 추가한다.&lt;/p&gt;
&lt;pre id=&quot;code_1680051984758&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from datetime import datetime, timedelta
from typing import Optional

from jose import JWTError, jwt
from fastapi import HTTPException, status
from fastapi.security import OAuth2PasswordBearer

# 임의로 사용할 비밀키(secret key)
SECRET_KEY = &quot;7395e755307a3276d60f253cc58ecd3b80ab82d95fdaeb30a424c8f52b9faa5c&quot;
ALGORITHM = &quot;HS256&quot;

oauth2_scheme = OAuth2PasswordBearer(tokenUrl=&quot;login&quot;)


# JWT 생성 함수
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=15)
    to_encode.update({&quot;exp&quot;: expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt


# JWT 검증 함수
def decode_access_token(token: str):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        return payload
    except JWTError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=&quot;Invalid authentication credentials&quot;,
            headers={&quot;WWW-Authenticate&quot;: &quot;Bearer&quot;},
        )&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련 코드에서 크게 어려운 부분은 없다. jwt 패키지를 통해서 encode하고 decode하는 방식이기 때문에 어려운 코드는 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전달된 토큰에서 인증이 실패하면 Exception을 발생시켜서 사용할 수 없는 토큰이라는 것을 전달하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토큰을 생성할 때는 토큰의 유효시간을 설정하는데 시간이 전달되면 전달된 시간으로 설정하고 값이 없다면 15분 동안만 유효하도록 설정하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 파일의 전체 코드는 아래와 같이 구성하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1680184301035&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from passlib.context import CryptContext
from datetime import datetime, timedelta
from typing import Optional

from jose import JWTError, jwt
from fastapi import HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm


# 암호화를 위한 객체 생성
pwd_context = CryptContext(schemes=[&quot;bcrypt&quot;], deprecated=&quot;auto&quot;)

# 임의로 사용할 비밀키(secret key)
SECRET_KEY = &quot;7395e755307a3276d60f253cc58ecd3b80ab82d95fdaeb30a424c8f52b9faa5c&quot;
ALGORITHM = &quot;HS256&quot;

oauth2_scheme = OAuth2PasswordBearer(tokenUrl=&quot;login&quot;)

# 패스워드를 암호화하는 함수
def get_password_hash(password: str) -&amp;gt; str:
    return pwd_context.hash(password)

# 암호화된 패스워드와 입력된 패스워드를 비교하는 함수
def verify_password(plain_password: str, hashed_password: str) -&amp;gt; bool:
    return pwd_context.verify(plain_password, hashed_password)


# JWT 생성 함수
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=15)
    to_encode.update({&quot;exp&quot;: expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt


# JWT 검증 함수
def decode_access_token(token: str):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        return payload
    except JWTError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=&quot;Invalid authentication credentials&quot;,
            headers={&quot;WWW-Authenticate&quot;: &quot;Bearer&quot;},
        )&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 해서 인증과 관련된 코드를 추가하였다. 이제 본격적으로 DB에 값을 넣고 로그인하고 데이터를 수정하는 작업을 진행할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증부분이 중요하기 때문에 따로 내용을 정리하였다. 다음 포스팅에서는 회원 가입하는 코드와 로그인하는 코드를 추가로 진행해보도록 하겠다.&lt;/p&gt;</description>
      <category>Programming/Project Log</category>
      <category>FastAPI</category>
      <category>JWT</category>
      <category>python</category>
      <category>인증</category>
      <category>파이썬</category>
      <category>패스워드 암호화</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/380</guid>
      <comments>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-Backend-%ED%9A%8C%EC%9B%90%EA%B4%80%EB%A6%AC%EA%B8%B0%EB%8A%A5-2#entry380comment</comments>
      <pubDate>Wed, 29 Mar 2023 23:07:35 +0900</pubDate>
    </item>
    <item>
      <title>[식물관찰일지] 2023년 3월 27일</title>
      <link>https://minarae7.tistory.com/entry/%EC%8B%9D%EB%AC%BC%EA%B4%80%EC%B0%B0%EC%9D%BC%EC%A7%80-2023%EB%85%84-3%EC%9B%94-27%EC%9D%BC</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이상하게 동물은 별로 관심이 없는데 식물 키우는데 재미가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날이 따뜻하고 집이 남향이라서 볕이 좋으니 식물들이 쑥쑥 크니 키오는 재미가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 주말에 분갈이를 하려고 했으나 바질과 토마토가 아직 분갈이할 정도로 크지 않아 일단 한 주 더 지켜보고 분갈이를 하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토마토랑 바질을 옮겨심으려고 흙이랑 유기농 비료를 사왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일주일에 한 번씩 시장에 다녀온다. 봄이라고 해서 시장에 가면 식물을 사려는 사람들이 참 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기 가서 식물을 키울 때도 어떤 식물을 키울지에 따라서 흙도 다른걸 구매하고 비료도 다르게 준다는 것을 알게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;산에 가서 흙을 퍼온다고 식물들이 다 잘 자라는건 아니라는 뜻이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;해바라기_20230327.jpg&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pIkLC/btr53FRiKxG/UBKnlssJ6oYQvvO5DlvXSK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pIkLC/btr53FRiKxG/UBKnlssJ6oYQvvO5DlvXSK/img.jpg&quot; data-alt=&quot;해바라기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pIkLC/btr53FRiKxG/UBKnlssJ6oYQvvO5DlvXSK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpIkLC%2Fbtr53FRiKxG%2FUBKnlssJ6oYQvvO5DlvXSK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;750&quot; height=&quot;500&quot; data-filename=&quot;해바라기_20230327.jpg&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;해바라기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해바라기는 정말로 잘 자란다. 생각도 못할 정도 빠르게 자라는 모습을 보면서 생명의 경이로움이라고 할까.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하루가 다르게 성장하는 해바라기는 좀 더 키워서 누나네 화단에 분양해야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저 뿌리들을 다 집에서 키우기는 너무 많고 클거 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;봉선화_20230327.jpg&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7r7ck/btr6bu2Wh66/BCeFvheHuOcnEOZgRMOXqk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7r7ck/btr6bu2Wh66/BCeFvheHuOcnEOZgRMOXqk/img.jpg&quot; data-alt=&quot;봉선화&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7r7ck/btr6bu2Wh66/BCeFvheHuOcnEOZgRMOXqk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7r7ck%2Fbtr6bu2Wh66%2FBCeFvheHuOcnEOZgRMOXqk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;750&quot; height=&quot;500&quot; data-filename=&quot;봉선화_20230327.jpg&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;봉선화&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제일 늦게 심어서 지난주까지만 해도 싹도 나지 않았던 봉선화는 어느새 훌쩍 커서 군집을 이루었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사진은 점심에 찍어두었는데 지금 다시 보니 가운데 사이에서도 싹이 올라온다. 씨가 너무 많아서 그냥 흩뿌려두었는데 모든 씨가 발아해서 올라오는거처럼 사방으로 올라온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;봉선화도 몇 송이만 두고 해바라기와 같이 누나네 화단에 옮겨 심어야겠다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;토마토_20230327.jpg&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/z1cb8/btr6eTO3sfg/voIAUhxVkmO8PK9ijrVwqk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/z1cb8/btr6eTO3sfg/voIAUhxVkmO8PK9ijrVwqk/img.jpg&quot; data-alt=&quot;방울토마토&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/z1cb8/btr6eTO3sfg/voIAUhxVkmO8PK9ijrVwqk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fz1cb8%2Fbtr6eTO3sfg%2FvoIAUhxVkmO8PK9ijrVwqk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;750&quot; height=&quot;500&quot; data-filename=&quot;토마토_20230327.jpg&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;방울토마토&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더디 자라서 걱정했던 토마토는 어느새 바질보다 크게 자랐다. 곧 분갈이해서 큰 화분에 듬성듬성 옮겨심어주어야 할 듯하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안 자라면 어떡하나 걱정했는데 쓸데없는 걱정이었던 듯 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토마토랑 바질을 옮겨심어서 열매가 열리면 따먹으려고 흙이랑 비료랑 사왔으니 열심히 커서 우리 집에 식비를 아껴주었으면 한다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;바질_20230327.jpg&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d7cM9m/btr6oYBHH6s/ubRr9Z6p8Fofakp3Q3Kb01/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d7cM9m/btr6oYBHH6s/ubRr9Z6p8Fofakp3Q3Kb01/img.jpg&quot; data-alt=&quot;바질&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d7cM9m/btr6oYBHH6s/ubRr9Z6p8Fofakp3Q3Kb01/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd7cM9m%2Fbtr6oYBHH6s%2FubRr9Z6p8Fofakp3Q3Kb01%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;750&quot; height=&quot;500&quot; data-filename=&quot;바질_20230327.jpg&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;바질&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 바질은 듬성듬성 나오더니 이제는 흙이 안 보일 정도 무성해졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 그 중에 토마토 하나 발견. 흙은 옮겨담으면서 쓸려들어갔는지 토마토 모종 하나가 섞여 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분갈이할 때 저 토마토는 친구들에게 보내주어야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다가오는 주말에는 분갈이해서 바질과 토마토는 넓은 화분에 옮겨심고 해바라기와 봉선화는 누나네 화단에 심고 몇 뿌리만 집에서 키워야 겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모종 시장에 갈 일이 있으면 남는 공간에 상추나 고추 같은 모종을 사다가 조금만 더 심어두어서 베란다 농사를 해봐야겠다.&lt;/p&gt;</description>
      <category>My Story/식물재배기</category>
      <category>바질</category>
      <category>방울토마토</category>
      <category>베란다농사</category>
      <category>봉선화</category>
      <category>식물관찰일지</category>
      <category>해바라기</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/379</guid>
      <comments>https://minarae7.tistory.com/entry/%EC%8B%9D%EB%AC%BC%EA%B4%80%EC%B0%B0%EC%9D%BC%EC%A7%80-2023%EB%85%84-3%EC%9B%94-27%EC%9D%BC#entry379comment</comments>
      <pubDate>Mon, 27 Mar 2023 17:07:52 +0900</pubDate>
    </item>
    <item>
      <title>[가계부 만들기] Backend - 회원 관리 기능#1</title>
      <link>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-Backend-%ED%9A%8C%EC%9B%90-%EA%B4%80%EB%A6%AC-%EA%B8%B0%EB%8A%A51</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Concept&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램을 만들면서 가장 기본이 되는 기능이 바로 회원 정보를 관리하는 것이다. 생성되는 모든 정보는 회원 정보에 종속되게 되므로 회원의 정보를 잘 관리하는 것이 매우 중요하다.&lt;br /&gt;이 프로젝트에서는 회원의 많은 정보를 사용하지 않는다. 이전에 공유했던 회원 정보 테이블을 다시 보자.&lt;/p&gt;
&lt;pre class=&quot;sql&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;CREATE TABLE `tb_members` (
&amp;nbsp;&amp;nbsp;`member_no` smallint unsigned NOT NULL AUTO_INCREMENT COMMENT '멤버번호',
&amp;nbsp;&amp;nbsp;`member_id` varchar(30) COLLATE utf8mb4_general_ci NOT NULL COMMENT '사용자 아이디',
&amp;nbsp;&amp;nbsp;`member_pw` varchar(128) COLLATE utf8mb4_general_ci NOT NULL COMMENT '사용자 패스워드',
&amp;nbsp;&amp;nbsp;`member_name` varchar(20) COLLATE utf8mb4_general_ci NOT NULL COMMENT '사용자 이름',
&amp;nbsp;&amp;nbsp;`member_email` varchar(50) COLLATE utf8mb4_general_ci NOT NULL COMMENT '이메일 주소',
&amp;nbsp;&amp;nbsp;`reg_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성일시',
&amp;nbsp;&amp;nbsp;`upd_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '수정일시',
&amp;nbsp;&amp;nbsp;`is_deleted` char(1) COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'F' COMMENT '삭제여부(T|F)',
&amp;nbsp;&amp;nbsp;`del_dt` datetime DEFAULT NULL COMMENT '삭제일시',
&amp;nbsp;&amp;nbsp;PRIMARY KEY (`member_no`),
&amp;nbsp;&amp;nbsp;UNIQUE KEY `ixn_members__member_id` (`member_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='사용자 정보';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블 스키마에서 보는 것과 같이 회원 정보라고 할 만한 정보는 아이디, 패스워드, 이름, 이메일 정도이다. 여기서 아이디와 이메일을 합쳐서 아이디를 이메일로 사용할 수도 있을 것이다. 일단 해당 프로젝트에서는 아이디와 이메일을 분리하여서 사용한다.&lt;br /&gt;이메일은 안 받아도 상관없지만 추후에 패스워드를 잊어버렸을 때 초기화하기 위한 수단으로 일단 저장하도록 한다.&lt;br /&gt;그리고 패스워드는 사용자에게 입력받은 그대로 저장하면 안되기 때문에 해당 정보는 암호화하여서 저장할 것이다.&lt;br /&gt;그럼 필요한 Router는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;회원가입&lt;/li&gt;
&lt;li&gt;회원정보수정(패스워드 변경 포함)&lt;/li&gt;
&lt;li&gt;로그인&lt;/li&gt;
&lt;li&gt;로그아웃&lt;/li&gt;
&lt;li&gt;회원탈퇴&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 기준으로 프로그램을 만들어보도록 하겠다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;구조추가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 프로그램에 대한 테스트를 진행하기 위해서 app 디렉토리 안에 main.py 파일을 생성했고 데이터베이스에 대한 연결 및 테이블 정의를 하기 위해서 databases라는 디렉토리를 추가하였다.&lt;br /&gt;여기서는 두 가지 디렉토리를 추가로 생성할 것이다. 우선 접속하는 주소를 만들기 위해서 routers 라는 디렉토리를 사용할 것이며 각 router에 대해서 실제로 처리해 줄 services 라는 디렉토리를 생성할 것이다.&lt;br /&gt;router에서는 각 URI 별로 전달받을 request의 형태와 역할, 그리고 반환하는 값의 형태를 정의할 것이다. 그리고 반복적이지 않으면서 아주 간단한 코드에 대해서는 router 자체에서 처리할 것이며 그렇지 않은 코드는 액션을 정의하는 services의 처리 로직을 호출할 것이다.&lt;br /&gt;services는 흔히 말하는 비즈니스 로직을 처리하도록 한다. 비슷한 역할을 하는 함수를 여러 번 정의하기 보다는 통합하여서 재활용성을 높이고 유지보수에 대한 편의성을 높이고자 한다.&lt;br /&gt;이외 추가적인 디렉토리가 추가적으로 필요할 수도 있지만 일단 이렇게 구성하고 개발을 시작하도록 한다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Routers&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 API 호출에 사용될 각 URI를 정의하여야 한다. 여기서는 여러 API를 사용할 것이고 각 API의 성격에 맞춰서 그룹화를 하여서 정의하도록 할 것이다. 먼저 app 디렉토리 하단에 routers라는 디렉토리를 생성하고 회원 관련 API를 모아두는 members.py 파일을 생성하도록 한다.&lt;br /&gt;위에서 언급한 회원 관리에 필요한 기능 5가지 중에서 로그아웃은 Frontend에서 토큰을 삭제하는 것으로 처리하도록 할 것이다. Backend에서는 로그인시 JWT만 생성해서 전달하고 이를 관리하는 기능은 Frontend에서 할 것이며 로그아웃은 Frontend에서 이 JWT를 삭제하는 것으로 대체한다.&lt;br /&gt;그럼 우선 회원 관리에 필요한 4가지 URI를 생성하도록 한다. 아래 내용을 참고하면 된다.&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;from fastapi import APIRouter

router = APIRouter(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;prefix=&quot;/members&quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;tags=[&quot;member&quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;responses={
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;404: {&quot;description&quot;: &quot;Not Found&quot;},
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
)

# 회원 가입
@router.post(&quot;/create&quot;, description=&quot;회원 가입&quot;)
async def create():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pass

# 회원 정보 수정
@router.put(&quot;/modify&quot;, description=&quot;회원 정보 수정&quot;)
async def modify():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pass

# 로그인
@router.post(&quot;/login&quot;, description=&quot;로그인&quot;)
async def login():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pass

# 회원탈퇴
@router.post(&quot;/unsubscribing&quot;, description=&quot;회원탈퇴&quot;)
async def unsubscribing():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pass&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드에서는 아직 내용을 채우지는 않고 우선 구조만 잡아둔다. router를 먼저 정의하고 회원 관련된 URI에는 prefix로 /members를 붙이도록 하였다. 만약 해당 파일에 정의되지 않은 URI로 접근하면 404에서 처리되어서 response로 &quot;Not Found&quot;라는 메시지가 전송된다.&lt;br /&gt;이제 생성한 router를 main.py에 연결하는 코드를 넣어야 한다.&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;...
from .routers import members

app = FastAPI(title=&quot;account-book-api&quot;)

app.include_router(members.router)
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 정의하면 브라우저의 redoc에서 다음과 같은 화면을 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3038&quot; data-origin-height=&quot;1736&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SGzJA/btr5ZlSbFIA/b2cL9MM01VxX7cc89tEi50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SGzJA/btr5ZlSbFIA/b2cL9MM01VxX7cc89tEi50/img.png&quot; data-alt=&quot;members routers의 redoc 문서&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SGzJA/btr5ZlSbFIA/b2cL9MM01VxX7cc89tEi50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSGzJA%2Fbtr5ZlSbFIA%2Fb2cL9MM01VxX7cc89tEi50%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3038&quot; height=&quot;1736&quot; data-origin-width=&quot;3038&quot; data-origin-height=&quot;1736&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;members routers의 redoc 문서&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 request 내용과 response 내용을 정의하지 않았기 때문에 단순히 리스트만 보여지는 볼 수 있다.&lt;br /&gt;이제부터 기본 내용을 채우도록 해보자.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Schema&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Schema는 ORM을 기반으로 받을 데이터의 형태와 전달할 결과값을 형태를 정의한다. 이렇게 정의된 스키마는 JSON 형태로 주고 받는 형태를 정의하게 된다.&lt;br /&gt;여기서는 각 request에 따른 parameter를 정의하고 결과로 어떤 값을 전송해줄 것인지 정의하도록 할 것이다.&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;from pydantic import BaseModel, Field

class Member(BaseModel):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;member_id: str = Field(title=&quot;사용자 아이디&quot;, max_length=30)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;member_pw: str = Field(title=&quot;사용자 패스워드&quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;member_name: str = Field(title=&quot;사용자 이름&quot;, max_length=20)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;member_email: str = Field(title=&quot;사용자 이메일&quot;, max_length=50)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;class Config:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;orm_mode = True

class LoginResponse(BaseModel):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;member_no: int = Field(title=&quot;사용자 번호&quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;memeber_id: str = Field(title=&quot;사용자 아이디&quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;member_name: str = Field(title=&quot;사용자 이름&quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;member_email: str = Field(title=&quot;사용자 이메일&quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;access_token: str = Field(title=&quot;Access Token&quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;refresh_totke: str = Field(title=&quot;Refresh Token&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;app/databases/schemas.py 파일을 생성하고 위의 코드를 만들었다. 해당 schema는 회원 가입시에 사용할 정보와 로그인시 로그인에 성공했을 때 사용자가 인증된 사용자임을 알려주는 정보에 대한 Schema를 정의한다.&lt;br /&gt;이제 각 router 별로 request와 response를 정의하도록 한다. 위의 router 파일을 아래와 같이 변경해보자.&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;from fastapi import APIRouter, Depends, Body
from sqlalchemy.orm import Session
from starlette.responses import Response
from starlette.status import HTTP_201_CREATED, HTTP_202_ACCEPTED
from typing import Optional
from ..database.connection import get_db
from ..database import schemas

router = APIRouter(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;prefix=&quot;/members&quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;tags=[&quot;member&quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;responses={
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;404: {&quot;description&quot;: &quot;Not Found&quot;},
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
)

# 회원 가입
@router.post(&quot;/create&quot;, description=&quot;회원 가입&quot;, response_class=Response)
async def create(
    member: schemas.Member = Body(
        title=&quot;회원정보&quot;,
        example={
            &quot;member_id&quot;: &quot;foo&quot;,
            &quot;member_pw&quot;: &quot;1234567890&quot;,
            &quot;member_name&quot;: &quot;홍길동&quot;,
            &quot;member_email&quot;: &quot;test@example.com&quot;,
        }
    ),
    db: Session = Depends(get_db)
):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return Response(status_code=HTTP_201_CREATED)

# 회원 정보 수정
@router.put(&quot;/modify&quot;, description=&quot;회원 정보 수정&quot;)
async def modify(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;member_no: int = Body(title=&quot;사용자 번호&quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;member_pw: Optional[str] = Body(title=&quot;사용자 패스워드&quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;member_name: Optional[str] = Body(title=&quot;사용자 이름&quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;member_email: Optional[str] = Body(title=&quot;사용자 이메일&quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;db: Session = Depends(get_db)
):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return Response(HTTP_202_ACCEPTED)

# 로그인
@router.post(&quot;/login&quot;, description=&quot;로그인&quot;, response_model=schemas.LoginResponse)
async def login(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;member_id: str = Body(title=&quot;사용자 아이디&quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;member_pw: str = Body(title=&quot;사용자 패스워드&quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;db: Session = Depends(get_db)
):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pass

# 회원탈퇴
@router.post(&quot;/unsubscribing&quot;, description=&quot;회원탈퇴&quot;, response_class=Response)
async def unsubscribing(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;member_no: int = Body(title=&quot;삭제할 사용자의 번호&quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;db: Session = Depends(get_db)
):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return Response(status_code=HTTP_202_ACCEPTED)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 routers/members.py 파일이 제법 코드 작동하는 코드 같이 보인다. 이렇게 해서 각 API 주소별로 request에서 어떤 parameter를 받고 수행 결과로 어떤 response를 전달하는지 모양을 만들었다.&lt;br /&gt;해당 파일을 만들고 나면 위에서 캡쳐했던 redoc 파일은 아래 이미지와 같이 보일 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3802&quot; data-origin-height=&quot;2002&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mQaPE/btr5UllsRSO/fMdsl6fs6KKte3fZTSm4q0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mQaPE/btr5UllsRSO/fMdsl6fs6KKte3fZTSm4q0/img.png&quot; data-alt=&quot;request과 response를 정의한 이후의 redoc&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mQaPE/btr5UllsRSO/fMdsl6fs6KKte3fZTSm4q0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmQaPE%2Fbtr5UllsRSO%2FfMdsl6fs6KKte3fZTSm4q0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3802&quot; height=&quot;2002&quot; data-origin-width=&quot;3802&quot; data-origin-height=&quot;2002&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;request과 response를 정의한 이후의 redoc&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 swagger 문서인 docs의 모양은 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2906&quot; data-origin-height=&quot;1042&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkDRh8/btr6nXJNJgi/L3mk1S5b0wL5ejNDQwmnO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkDRh8/btr6nXJNJgi/L3mk1S5b0wL5ejNDQwmnO1/img.png&quot; data-alt=&quot;docs&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkDRh8/btr6nXJNJgi/L3mk1S5b0wL5ejNDQwmnO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkDRh8%2Fbtr6nXJNJgi%2FL3mk1S5b0wL5ejNDQwmnO1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2906&quot; height=&quot;1042&quot; data-origin-width=&quot;2906&quot; data-origin-height=&quot;1042&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;docs&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docs에서는 실제로 try out을 해볼 수 있다. 정상 작동하는 API도 있고 내용을 작성하지 않았기 때문에 정상 작동하지 않는 API도 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2894&quot; data-origin-height=&quot;1920&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvG6bY/btr6oJRTNe8/a0I1G3dKCDu81ULy6FxezK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvG6bY/btr6oJRTNe8/a0I1G3dKCDu81ULy6FxezK/img.png&quot; data-alt=&quot;create API를 호출&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvG6bY/btr6oJRTNe8/a0I1G3dKCDu81ULy6FxezK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvG6bY%2Fbtr6oJRTNe8%2Fa0I1G3dKCDu81ULy6FxezK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2894&quot; height=&quot;1920&quot; data-origin-width=&quot;2894&quot; data-origin-height=&quot;1920&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;create API를 호출&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 해서 회원 관리를 위한 기본적인 모양을 잡았다. 다음 포스팅에서는 실제로 사용자 정보를 생성해서 디비에 입력해보고 입력된 사용자 정보로 로그인도 해보고 정보 수정해볼 것이다.&lt;/p&gt;</description>
      <category>Programming/Project Log</category>
      <category>backend</category>
      <category>FastAPI</category>
      <category>python</category>
      <category>Router</category>
      <category>Schema</category>
      <category>가계부만들기</category>
      <category>개발기록</category>
      <category>웹개발</category>
      <category>파이썬</category>
      <category>프로젝트로그</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/378</guid>
      <comments>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-Backend-%ED%9A%8C%EC%9B%90-%EA%B4%80%EB%A6%AC-%EA%B8%B0%EB%8A%A51#entry378comment</comments>
      <pubDate>Mon, 27 Mar 2023 15:02:35 +0900</pubDate>
    </item>
    <item>
      <title>[sqlalchemy] Postgresql Query ORM 변환</title>
      <link>https://minarae7.tistory.com/entry/sqlalchemy-Postgresql-Query-ORM-%EB%B3%80%ED%99%98</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;connect-a-flask-app-to-a-mysql-database-with-sqlalchemy-and-pymysql.jpg&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c9tXVr/btr5fO9jBbX/kPLOtPg9amHrwnavfKYfyK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c9tXVr/btr5fO9jBbX/kPLOtPg9amHrwnavfKYfyK/img.jpg&quot; data-alt=&quot;SQL Alchemy&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c9tXVr/btr5fO9jBbX/kPLOtPg9amHrwnavfKYfyK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc9tXVr%2Fbtr5fO9jBbX%2FkPLOtPg9amHrwnavfKYfyK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1500&quot; height=&quot;600&quot; data-filename=&quot;connect-a-flask-app-to-a-mysql-database-with-sqlalchemy-and-pymysql.jpg&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SQL Alchemy&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어제 회사에서 코드를 보다가 뭔가 개선점을 발견하고 한참을 작업해서 코드를 수정했는데 기록을 남겨두고자 포스팅한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AS-IS&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 코드는 이런 역할을 한다. 검색에서 광고로 사용할 키워드를 등록해두면 사용자가 검색 질의를 하면 해당 키워드가 현재 사용중인 광고에 포함되는 키워드인지 확인해보는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 광고는 여러개의 키워드를 가질 수 있으면 광고와 키워드 간의 관계는 m:n의 관계이며 각 광고는 서비스의 시작일과 종료일을 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 키워드를 추출하는 이유는 DB의 부하를 줄이기 위해서 키워드 리스트만 따로 추출해서 레디스에 들고 있다가 질의가 들어오면 해당 키워드로 등록된 광고가 있는지 확인하고 있으면 정보를 가져오고 없으면 빈 값을 보내기 위해서이다. 즉, 디비의 부하를 줄이기 위한 조치이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에는 사용중이 키워드의 전체 리스트를 가져와서 결과에서 루프를 돌리며 키워드를 추출하고 시작일 또는 만료일이 가장 먼저 도래하는 날짜를 찾았다. 날짜를 찾는 이유는 레디스의 TTL을 구워서 해당 시간이 되면 레디스를 날리고 다시 레디스를 생성하기 위해서이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 다음과 같이 SQL문을 작성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1679464213695&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select
    keyword,
    show_start_timestamp,
    show_end_timestamp
from
    tb_ads
        join tb_ads_keyword on tb_ads.ad_no = tb_ads_keyword.ad_no
where
    tb_ads.is_deleted = 'F'
and
    tb_ads_keyword.is_deleted = 'F'
and
    tb_ads.is_published = 'T'
and
    (tb_ads.show_start_timestamp &amp;gt;= now() or tb_ads.show_end_timestamp &amp;gt;= now())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 사용중인 키워드 리스트와 각 키워드의 사용 시작일과 종료일을 알 수 있다. 해당 쿼리를 sqlalchemy를 통해서 statement로 작성하면 아래 코드와 같이 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1679464495554&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;stmt = select(models.AdKeyword.keyword, models.Ad.show_start_timestamp, models.Ad.show_end_timestamp).join(
    models.Ad,
    models.Ad.ad_no == models.AdKeyword.ad_no
).filter(
    models.AdKeyword.is_deleted == 'F',
    models.Ad.is_deleted == 'F',
    models.Ad.is_published == 'T',
    ((models.Ad.show_start_timestamp &amp;gt;= func.now()) | (models.Ad.show_end_timestamp &amp;gt;= func.now()))
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 코드를 작성해서 쿼리를 돌리고 나면 결과가 리스트로 나올테니 for문을 통해서 결과를 만들면 된다. 다음 코드가 해당 역할을 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1679464689884&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;result = await db.execute(stmt)
data = []
for row in result.fetchall():
    data.append(row[0])
    ....&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;TO-BE&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 생각해보니 이 정도 작업은 그냥 쿼리에서 하면 될거 같다. 그래서 다음과 같이 쿼리를 작성했다.&lt;/p&gt;
&lt;pre id=&quot;code_1679464907121&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select
    string_agg(distinct keyword, ','),
    min(case when show_start_timestamp &amp;lt; now() then null else show_start_timestamp end),
    min(show_end_timestamp)
from
    tb_ads_keyword
        inner join tb_ads on tb_ads.ad_no = tb_ads_keyword.ad_no
where
    tb_ads.is_deleted = 'F'
and
    tb_ads_keyword.is_deleted = 'F'
and
    tb_ads.is_published = 'T'
and
    (tb_ads.show_start_timestamp &amp;gt;= now() or tb_ads.show_end_timestamp &amp;gt;= now())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 구현하면 keyword 리스트는 DB에서 ,으로 이어지는 문자열로 찾아주고 show_start_timestamp와 show_end_timestamp의 각 최소값을 가져오게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드를 sqlalchemy 코드를 변환하면 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1679465226246&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;stmt = select(
    func.string_agg(distinct(models.AdKeyword.keyword), literal_column(&quot;','&quot;)),
    func.min(case(
        [
            (models.Ad.show_start_timestamp &amp;lt;= func.now(), None),
        ],
        else_=models.Ad.show_start_timestamp
    )),
    func.min(models.Ad.show_end_timestamp),
).join(
    models.Ad,
    models.Ad.ad_no == models.AdKeyword.ad_no
).filter(
    models.AdKeyword.is_deleted == 'F',
    models.Ad.is_deleted == 'F',
    models.Ad.is_published == 'T',
    ((models.Ad.show_start_timestamp &amp;gt;= func.now()) | (models.Ad.show_end_timestamp &amp;gt;= func.now()))
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 해서 결과를 얻으면 python에서는 루프를 돌릴 필요없이 바로 결과를 도출할 수 있다. 다음의 코드를 참조하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1679465472353&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;result = await db.execute(stmt)
row = result.fetchone()
data = row[0].split(&quot;,&quot;)
min_datetime = row[2]
if row[1] is not None:
    min_datetime = min(row[1], row[2])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;row[2]는 show_end_timestamp 값인데 이 값은 null인 값이 올 수 없기 때문에 show_start_timestamp를 case문으로 null로 처리한 부분에 대한 예외처리만 해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;python 코드 자체는 성능상의 큰 차이가 없을 것이고 데이터 자체가 많지 않다면 굳이 이렇게 할 필요없이 위의 코드만으로도 충분히 잘 작동할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 코드를 늘 발전하고 더 나은 코드를 작성하기 위해서 노력해야하므로 이렇게 최적화를 진행하는 것은 늘 옳다고 생각한다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming/Python</category>
      <category>case when 문</category>
      <category>Optimize</category>
      <category>PostgreSQL</category>
      <category>python</category>
      <category>sqlalchemy</category>
      <category>코드개선</category>
      <category>파이썬</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/377</guid>
      <comments>https://minarae7.tistory.com/entry/sqlalchemy-Postgresql-Query-ORM-%EB%B3%80%ED%99%98#entry377comment</comments>
      <pubDate>Wed, 22 Mar 2023 15:16:28 +0900</pubDate>
    </item>
    <item>
      <title>[Nextjs] API 기능을 통한 파일 업로드 처리(base64) #2</title>
      <link>https://minarae7.tistory.com/entry/Nextjs-API-%EA%B8%B0%EB%8A%A5%EC%9D%84-%ED%86%B5%ED%95%9C-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C-%EC%B2%98%EB%A6%AC-2</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;nextjs_logo.png&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;571&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eMlkKm/btr5gRcAPKi/dpBsb5VU1rZuyL7vI31Zkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eMlkKm/btr5gRcAPKi/dpBsb5VU1rZuyL7vI31Zkk/img.png&quot; data-alt=&quot;Nextjs&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eMlkKm/btr5gRcAPKi/dpBsb5VU1rZuyL7vI31Zkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeMlkKm%2Fbtr5gRcAPKi%2FdpBsb5VU1rZuyL7vI31Zkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;571&quot; data-filename=&quot;nextjs_logo.png&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;571&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Nextjs&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개요&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얼마 전 포스팅에서 nextjs를 통해서 api 구조에서 파일을 업로드하는 내용을 다루었다. 이게 로컬에서 개발할 때는 잘 작동하고 별 문제 없이 백엔드로 파일이 전달이 되었는데, 배포하려고 서버에 올려두고 테스트를 진행하니 backend에서 파일을 전달받을 때 자꾸 파일이 바뀌어서 업로드 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미세하게 파일 용량이 바뀌어서 업로드되는데 이렇게 되면 파일이 손상되어서 이미지 파일이 브라우저에서 열리지 않는 문제가 발생하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 원인 때문일지 디버깅을 해보다가 파일이 로컬 브라우저에서 nextjs API로 전달될 때 이미 파일이 바뀌 상태에서 업로드되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;flowchart.jpeg&quot; data-origin-width=&quot;481&quot; data-origin-height=&quot;81&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OKQ9w/btr5jb9H735/3aU5R7ZEg1iPsOOzCmXG41/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OKQ9w/btr5jb9H735/3aU5R7ZEg1iPsOOzCmXG41/img.jpg&quot; data-alt=&quot;파일 흐름도&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OKQ9w/btr5jb9H735/3aU5R7ZEg1iPsOOzCmXG41/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOKQ9w%2Fbtr5jb9H735%2F3aU5R7ZEg1iPsOOzCmXG41%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;481&quot; height=&quot;81&quot; data-filename=&quot;flowchart.jpeg&quot; data-origin-width=&quot;481&quot; data-origin-height=&quot;81&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;파일 흐름도&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 그림과 같이 파일이 전달되는데 브라우저에서 nextjs server로 전달될 때 이미 파일이 바뀐다. 근데 로컬 nextjs를 구동할 때는 문제가 없다. 결론은 컴퓨터에서 설치된 보안 프로그램이 파일을 검사하면서 파일 자체에 뭔가 변경을 주는 것이 아닌가라는 의심이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 해결책은 파일을 업로드할 때 이 파일을 파일 자체로 업로드하지 않고 base64로 변경해서 String으로 업로드하여 보안프로그램이 파일을 검색하는 로직을 우회하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 이렇게 하면 일단 base64로 인코딩하면서 전달해야하는 용량이 커지고 그래서 트래픽이 증가한다는 단점이 있다. 하지만 일단 프로그램이 정상적으로 작동하도록 하는게 우선이다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Client Side&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이제 프로그램을 작성해보도록 하겠다. 우선 브라우저에서 작동될 스크립트를 만들어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 아래와 같이 두 가지 state 변수를 사용하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1679450228327&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const [targetFiles, setTargetFiles] = useState({}); // 파일의 내용을 저장
const [isFilesReady, setIsFilesReady] = useState(true); // 파일을 다 읽었는지 확인&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주석의 내용처럼 targetFiles는 실제 파일을 읽어서 base64로 저장하기 위해서 사용하고 isFilesReady는 파일을 다 읽어서 서버에 전달할 준비가 되었는지 확인하기 위해서 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 isFilesReady는 파일을 읽기 시작할 때 false로 변경하였다가 파일을 읽어서 변수에 담는 작업이 끝나면 다시 true로 변경하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Form에서 File Input을 다음과 같이 작성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1679450529245&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Form.Control
    type=&quot;file&quot;
    name=&quot;image&quot;
    className=&quot;form-control-sm&quot;
    accept=&quot;*.png, *.jpg, *.gif&quot;
    onChange={(e) =&amp;gt; {
    	setFile(e.target.files);
    }
/&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 react-bootstrap을 사용한다고 가정하고 코드를 작성하였다. 위의 코드는 이미지 파일만 업로드할 수 있도록 하였으며 내용이 변경되면 setFile 함수를 호출하도록 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 실제로 파일을 읽어서 변수에 담는 역할을 하는 코드를 작성하도록 하겠다.&lt;/p&gt;
&lt;pre id=&quot;code_1679451507173&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const setFile = async (files) =&amp;gt; {
  let list = {};
  setIsFilesReady(false);  // 파일을 읽기 시작할 때 false로 변경해 둠
  // 파일을 비동기로 읽기 시작한다.
  // parameter가 Object이므로 Object.entries로 array로 치환하여 map 함수를 사용
  const filePromises = Object.entries(files).map(item =&amp;gt; {
    return new Promise((resolve, reject) =&amp;gt; {
      const [index, file] = item;
      // 파일을 읽기 위해서 FileReader를 생성하고 파일을 읽기 시작
      const reader = new FileReader();
      reader.readAsDataURL(file);
      
      // 파일 읽기가 끝났을 때 처리할 함수
      reader.onload = (event) =&amp;gt; {
        list[index] = event.target.result; // 읽은 데이터를 배열에 추가
        resolve();
      };
      
      // 파일 읽기에 실패했을 때 처리 함수
      reader.onerror = () =&amp;gt; {
        console.log(&quot;Couldn't read the file&quot;);
        reject();
      };
    });
  });
  
  // 여기서 비동기로 읽고 있는 파일들이 모두 읽혀질 때까지 대기
  Promise.all(filePromises)
    .then(() =&amp;gt; {
      // 파일을 정상적으로 읽었다면 읽은 결과를 targetFiles에 담기
      setTargetFiles(list);
      console.log(&quot;Ready to submit&quot;);
      // 파일 읽기가 모두 정상적으로 끝났다면 state를 다시 true로 변경
      setIsFilesReady(true);
    })
    .catch((error) =&amp;gt; {
      // 예외가 발생했다면 여기서 처리
      console.log(error);
      console.log(&quot;Something wrong happened&quot;);
    });
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일의 내용이 좀 긴데 코드 사이사이에 주석이 작성하였으니 코드를 보는데는 문제가 없을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 submit을 하는 곳에서 읽은 데이터를 추가하는 작업을 진행하여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저에서 동작할 script에서 할 일이 많다.&lt;/p&gt;
&lt;pre id=&quot;code_1679451834255&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const onSubmit = (e) =&amp;gt; {
  ...
  let formData = new FormData(); // 서버로 전달할 form을 생성
  // formData에 전달할 변수들 담기
  ...
  
  // 파일을 아직 읽고 있을 경우에 대한 처리
  if (isFilesReady === false) {
    alert(&quot;파일을 읽는 동안 잠시만 기다려주세요.&quot;);
    return;
  }
  
  // 파일에서 읽은 내용을 formData에 추가
  if (targetFiles[0] !== undefined) {
    formData.append('image', targetFiles[0]);
  }
  
  // 이하 내용 생략
  ...
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 이미지 파일 하나면 전송할 것이기 때문에 위의 코드와 같이 작성하였으며 파일이 많을 경우는 위의 코드를 참조해서 개발할 수 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 브라우저에서 할 일은 끝났다. api 쪽 코드를 작성해보자.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Nextjs API Side&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;api에서 처리하는 코드에서도 해야할 일이 많다. base64로 전달된 내용을 기반으로 다시 파일을 생성하고 이 파일을 다시 전달할 form에 추가해주어야 하고, 파일의 확장자는 뭔지도 알아내야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 파일에 대한 정보를 추출하는 함수를 작성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1679452400677&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function DataURIToBlob(dataURI) {
  const splitDataURI = dataURI.split(',');
  const byteString = splitDataURI[0].indexOf('base64') &amp;gt;= 0 
    ? atob(splitDataURI[1])
    : decodeURI(splitDataURI[1]);
  // 파일의 mime값을 추출한다.(확장자를 만들기 위해서)
  const mimeString = splitDataURI[0].split(':')[1].split(';')[0];
  
  // 파일을 다시 binary로 복원
  const ia = new Uint8Array(byteString.length);
  for (let i = 0; i &amp;lt; byteString.length; i++) {
    ia[i] = byteString.charCodeAt(i);
  }
  return {
    data: ia,
    type: mimeString,
  };
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 위의 함수를 통해서 전달된 base64에서 파일의 내용과 mime를 찾을 수 있다. mime 값을 통해서 확장자를 만들어낼 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 파일에 대한 처리를 진행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1679452958096&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default async function (req, res) {
  try {
    ...
    const formData = new ForData();
    const info = DataURIToBlob(fields['image']);
    // 확장자 추출
    const ext = info.type.split('/')[1];
    const filename = moment().format('YYYYMMDDHHmmss') + `.${ext}`;
    fs.writeFileSync('image', fs.createReadStream(filename), {
      filename: filename,
      contextType: info.type,
    });
    
    // 이제 다른 필드를 formData에 추가
    for (const key in fields) {
      // 파일 업로드 관련 항목을 추가하지 않음
      if (['image', 'filename', 'image_path'].includes(key)) {
        continue;
      }
      form.append(key, fields[key]);
    }
    
    // post로 Backend에 전송
    const result = await client.post(url, req, formData);
    // 처리가 완료되었으니 임시로 생성한 파일은 삭제
    fs.unlinkSync(filename);
    ...
  } catch (error) {
    res.status(error.response 
      ? error.response.status : 500)
    ).send(error.response ? error.response.data : error.message);
  }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드에서는 DataURI에서 stream과 mime값을 생성해서 임시로 파일을 만들고 이걸 서버로 전송한 다음에 임시로 생성한 파일은 삭제하도록 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 파일을 브라우저에서 nextjs server까지는 파일이 바이너리가 아닌 dataURI로 전송되고 nextjs 서버부터는 이렇게 생성한 정보로 임시로 파일을 만들어서 Backend 서버로 파일을 전달할 수 있다.&lt;/p&gt;</description>
      <category>Programming/Nextjs</category>
      <category>dataURI</category>
      <category>javascript</category>
      <category>nextjs</category>
      <category>React</category>
      <category>개발팁</category>
      <category>보안프로그램우회 업로드</category>
      <category>파일전송</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/376</guid>
      <comments>https://minarae7.tistory.com/entry/Nextjs-API-%EA%B8%B0%EB%8A%A5%EC%9D%84-%ED%86%B5%ED%95%9C-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C-%EC%B2%98%EB%A6%AC-2#entry376comment</comments>
      <pubDate>Wed, 22 Mar 2023 11:47:04 +0900</pubDate>
    </item>
    <item>
      <title>대통령을 반역죄로 벌하라!</title>
      <link>https://minarae7.tistory.com/entry/%EB%8C%80%ED%86%B5%EB%A0%B9%EC%9D%84-%EB%B0%98%EC%97%AD%EC%A3%84%EB%A1%9C-%EB%B2%8C%ED%95%98%EB%9D%BC</link>
      <description>&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;[속보] 윤 대통령 &amp;ldquo;일본 이미 수십차례 사과&amp;hellip;이제 일본 당당하고 자신있게 대해야&amp;rdquo;&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;윤석열 대통령은 21일 강제동원(징용) 피해자 문제에 일본이 사과하지 않은 것을 두고 &amp;ldquo;일본은 이미 수십 차례에 걸쳐 우리에게 과거사 문제에 대해 반성과 사과를 표한 바 있다&amp;rdquo;고 말했다. 윤&quot; data-og-host=&quot;n.news.naver.com&quot; data-og-source-url=&quot;https://n.news.naver.com/article/032/0003212106&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/gk1Vg/hyR0yCFr4i/TIWGRyALzr6k7LzExsvYu1/img.png?width=500&amp;amp;height=333&amp;amp;face=222_74_284_142&quot; data-og-url=&quot;https://n.news.naver.com/article/032/0003212106&quot;&gt;&lt;a href=&quot;https://n.news.naver.com/article/032/0003212106&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://n.news.naver.com/article/032/0003212106&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/gk1Vg/hyR0yCFr4i/TIWGRyALzr6k7LzExsvYu1/img.png?width=500&amp;amp;height=333&amp;amp;face=222_74_284_142');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[속보] 윤 대통령 &amp;ldquo;일본 이미 수십차례 사과&amp;hellip;이제 일본 당당하고 자신있게 대해야&amp;rdquo;&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;윤석열 대통령은 21일 강제동원(징용) 피해자 문제에 일본이 사과하지 않은 것을 두고 &amp;ldquo;일본은 이미 수십 차례에 걸쳐 우리에게 과거사 문제에 대해 반성과 사과를 표한 바 있다&amp;rdquo;고 말했다. 윤&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;n.news.naver.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;작금의 우리 대통령을 바라보며 저 사람이 과연 대한민국의 대통령인가 하는 의심이 든다.&lt;br /&gt;일본에 가서 살살 기어서 밥 얻어먹고 오더니 똥인지 된장인지도 구분 못하는 사람이 같다는 생각이 들게 한다.&lt;br /&gt;대통령은 우리 나라의 최고 통치권자로 우리 나라의 국익을 위해서 일해야 하는 위치에 있는 사람이라고 생각한다. 지금까지의 대통령들이 모두 그렇게 행동했던 것은 아닐지라도 외교적인 입장에서 최소한의 도리를 지켰다고 생각한다.&lt;br /&gt;근데 이상한 사람이 되더니 한일 관계에 있어서 우리 나라의 국익이 아니고 일본의 이익만을 위해서 복무하는 사람처럼 보인다.&lt;br /&gt;대법원의 판결마저 뒤집어버리더니 이제는 역사 왜곡하며 있지도 않은 사실을 날조한다.&lt;br /&gt;일본은 우리 나라를 침탈한 것을 단 한 번도 인정한 적이 없다. 불법적 통치는 없었다고 한다.&lt;br /&gt;그런 일본이 우리나라에 사과했다는 건가? 무슨 근거로 이런 말을 하는지 모르겠다.&lt;br /&gt;세계적으로도 일본은 자신들의 신민 통치를 인정하고 사과한 적이 없는 나라로 유명한 나라이다. 그런 나라가 무슨 사과를 하는가.&lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;기시다 &amp;ldquo;윤 대통령, 건배하고 술 다 마셔 깜짝 놀랐다&amp;rdquo;&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;기시다 후미오 일본 총리가 한-일 정상회담을 계기로 지지율이 큰 폭으로 상승하면서 국정 운영의 자신감을 되찾고 있다는 일본 언론의 분석이 나왔다. 기시다 내각의 지지율이 언론사별로 한 &quot; data-og-host=&quot;n.news.naver.com&quot; data-og-source-url=&quot;https://n.news.naver.com/article/028/0002632278&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/xUILI/hyR0tO1yRa/QIyZ05Y1WlnJNBXgvGIhc0/img.jpg?width=647&amp;amp;height=431&amp;amp;face=151_138_525_205&quot; data-og-url=&quot;https://n.news.naver.com/article/028/0002632278&quot;&gt;&lt;a href=&quot;https://n.news.naver.com/article/028/0002632278&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://n.news.naver.com/article/028/0002632278&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/xUILI/hyR0tO1yRa/QIyZ05Y1WlnJNBXgvGIhc0/img.jpg?width=647&amp;amp;height=431&amp;amp;face=151_138_525_205');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;기시다 &amp;ldquo;윤 대통령, 건배하고 술 다 마셔 깜짝 놀랐다&amp;rdquo;&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;기시다 후미오 일본 총리가 한-일 정상회담을 계기로 지지율이 큰 폭으로 상승하면서 국정 운영의 자신감을 되찾고 있다는 일본 언론의 분석이 나왔다. 기시다 내각의 지지율이 언론사별로 한&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;n.news.naver.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;아이고 정말이지 나라가 창피해서 살 수가 없다.&lt;br /&gt;나라를 대표해서 남의 나라에 갔으면 최소한 지켜야할 선이라는 것이 있는 것인데 그런거는 안중에도 없는거 같다.&lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;尹 &amp;ldquo;반도체 클러스터에 日소부장기업 대거 유치&amp;hellip;한일 공급망 협력&amp;rdquo; [종합]&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;윤석열 대통령은 21일 &amp;ldquo;한일 양국 기업 간 공급망 협력이 가시화되면, 용인에 조성할 예정인 반도체 클러스터에 일본의 기술력 있는 반도체 소부장(소재&amp;middot;부품&amp;middot;장비) 업체들을 대거 유치함으로&quot; data-og-host=&quot;n.news.naver.com&quot; data-og-source-url=&quot;https://n.news.naver.com/article/016/0002119174&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/1ivSM/hyR0kkfdJ4/6OO4BH6YIGWJkMDTfGIlm1/img.jpg?width=640&amp;amp;height=435&amp;amp;face=305_148_386_237&quot; data-og-url=&quot;https://n.news.naver.com/article/016/0002119174&quot;&gt;&lt;a href=&quot;https://n.news.naver.com/article/016/0002119174&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://n.news.naver.com/article/016/0002119174&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/1ivSM/hyR0kkfdJ4/6OO4BH6YIGWJkMDTfGIlm1/img.jpg?width=640&amp;amp;height=435&amp;amp;face=305_148_386_237');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;尹 &amp;ldquo;반도체 클러스터에 日소부장기업 대거 유치&amp;hellip;한일 공급망 협력&amp;rdquo; [종합]&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;윤석열 대통령은 21일 &amp;ldquo;한일 양국 기업 간 공급망 협력이 가시화되면, 용인에 조성할 예정인 반도체 클러스터에 일본의 기술력 있는 반도체 소부장(소재&amp;middot;부품&amp;middot;장비) 업체들을 대거 유치함으로&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;n.news.naver.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이쯤되면 그냥 일본 사람이라고 해도 되지 않을까 싶다. 우리나라 땅에 우리나라 기업을 육성해서 산업의 기초체력을 길러야 하는데 여기에 도대체 왜 일본을 끌어들이는지 알 수가 없다.&lt;br /&gt;자신을 일본 사람으로 생각하고 있거나 아니면 아직도 일제시대라고 생각하는 것이 아니라면 이렇게 행동할 수 없을 것이다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1541&quot; data-origin-height=&quot;586&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Omop1/btr41IIfRsS/v7e0jk3p27gub0RyPw3ZS0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Omop1/btr41IIfRsS/v7e0jk3p27gub0RyPw3ZS0/img.jpg&quot; data-alt=&quot;반역죄란 무엇을 의미하는가&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Omop1/btr41IIfRsS/v7e0jk3p27gub0RyPw3ZS0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOmop1%2Fbtr41IIfRsS%2Fv7e0jk3p27gub0RyPw3ZS0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;631&quot; height=&quot;240&quot; data-origin-width=&quot;1541&quot; data-origin-height=&quot;586&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;반역죄란 무엇을 의미하는가&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;네이버 사전에 반역죄를 찾아보면 &amp;ldquo;국가를 배반하고 모역하는 죄&amp;rdquo; 라고 나온다.&lt;br /&gt;지금 대통령이 하는 행위가 국가를 배신하고 상대국을 이롭게 하는 행위로 반역의 행위라고 생각한다.&lt;br /&gt;다른 정책이야 나라 안에서 힘들게 버티면 된다고 생각하지만 지금의 한일 관계를 다루는 문제는 그렇게 생각할게 아닌거 같다.&lt;br /&gt;지난 역사를 돌이켜보면 잘못 끼워진 외교관계를 다시 회복하는데 엄청난 노력과 시간이 필요하다는 것을 알 수 있다.&lt;br /&gt;더 큰 문제가 발생하기 전에 당장 그 방향으로 가는 것을 막아야 한다.&lt;/p&gt;</description>
      <category>기사모음과 생각</category>
      <category>국가의 이익을 반하는 자는 반역죄로 처벌하라</category>
      <category>기사모음</category>
      <category>반역죄</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/375</guid>
      <comments>https://minarae7.tistory.com/entry/%EB%8C%80%ED%86%B5%EB%A0%B9%EC%9D%84-%EB%B0%98%EC%97%AD%EC%A3%84%EB%A1%9C-%EB%B2%8C%ED%95%98%EB%9D%BC#entry375comment</comments>
      <pubDate>Tue, 21 Mar 2023 23:06:16 +0900</pubDate>
    </item>
    <item>
      <title>[식물관찰일지] 3월 20일</title>
      <link>https://minarae7.tistory.com/entry/%EC%8B%9D%EB%AC%BC%EA%B4%80%EC%B0%B0%EC%9D%BC%EC%A7%80-3%EC%9B%94-20%EC%9D%BC</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;지난 3월 11일에 다이소에 갔다가 우연히 씨앗과 화분을 좀 사왔다. 잘 자라면 얼마나 잘 자라겠냐는 생각에 조그만 화분에 흙도 조금만 사왔는데 생각보다 식물들이 너무 잘 자란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 식물들이 자라는 모습을 블로그로 기록해보려고 한다. 아이들이 잘 자라는 모습을 보니 왠지 뿌듯하기까지 하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7419.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SvUSI/btr40Gwydfs/hf60sjeHFWUPBFmPIMbJr0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SvUSI/btr40Gwydfs/hf60sjeHFWUPBFmPIMbJr0/img.jpg&quot; data-alt=&quot;해바라기 화분&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SvUSI/btr40Gwydfs/hf60sjeHFWUPBFmPIMbJr0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSvUSI%2Fbtr40Gwydfs%2Fhf60sjeHFWUPBFmPIMbJr0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;420&quot; height=&quot;560&quot; data-filename=&quot;IMG_7419.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;해바라기 화분&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 해바라기는 너무 잘 자란다. 씨앗을 12~13개 정도 심은거 같은데 싹이 올라온 것만 9개이다. 조그만 화분이 미안할 정도로 서로 경쟁하면 정말이지 미친듯이 잘 자란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해바라기가 키가 크고 잘 자란다는 건 알고 있었지만 일주일만에 이렇게 크게 성장할거라고는 미쳐 예상하지 못했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해바라기는 곧 큰 화분으로 분갈이를 하고 몇개는 버리던지 다른데 가져다가 심던지 해야겠다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7422.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k55CK/btr45ldDK06/v1mbCIDLY4Gpbk3PGSDNnk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k55CK/btr45ldDK06/v1mbCIDLY4Gpbk3PGSDNnk/img.jpg&quot; data-alt=&quot;바질&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k55CK/btr45ldDK06/v1mbCIDLY4Gpbk3PGSDNnk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk55CK%2Fbtr45ldDK06%2Fv1mbCIDLY4Gpbk3PGSDNnk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;420&quot; height=&quot;560&quot; data-filename=&quot;IMG_7422.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;바질&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음으로 잘 자라는 아이는 바질이다. 토마토랑 바질이랑 같이 들어있어서 토마토 사면서 같이 구매하게 되었는데 바질은 잡초 자라듯이 자란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;씨앗이 작아서 그냥 흩뿌려서 심었는데 흩뿌려진 모양대로 잘 자라나고 있는 모양이다. 이거 잘 키워서 샐러드 해먹을 수 있을지 모르겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바질은 장모님이 긴 화분을 가져다 주시면 그리로 옮겨심고 이 화분은 해바라기에게 양보해야할 듯하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7421.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVMkXe/btr4WHCzkzf/DBA1wKo8ODbPNqd9q4ZGc1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVMkXe/btr4WHCzkzf/DBA1wKo8ODbPNqd9q4ZGc1/img.jpg&quot; data-alt=&quot;토마토&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVMkXe/btr4WHCzkzf/DBA1wKo8ODbPNqd9q4ZGc1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVMkXe%2Fbtr4WHCzkzf%2FDBA1wKo8ODbPNqd9q4ZGc1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;420&quot; height=&quot;560&quot; data-filename=&quot;IMG_7421.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;토마토&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토마토는 씨앗도 정말 작고 싹도 안올라와서 실패라고 생각했는데 어느 순간 보니 싹들이 옹기종이 올라왔다. 생명의 경이로움이라고나 할까. 제일 늦게 싹이 올라오기는 했지만 그래도 얘네도 나름 열심히 자라고 있는 모양이다. 아직은 연약해보이는데 얼마나 잘 자랄지 지켜봐야 할 듯하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큰 딸 소민이가 가장 기대하는 화분인데 이유는 토마토가 열리면 따먹으려는 것이다. 토마토를 엄청 좋아하니 토마토 화분을 길러서 잘 키워서 따먹으면 이것도 하나의 학습이 되지 않을까 싶다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_7420.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c55ni0/btr42yYQ4Ra/XLOk3Hz1hJ60KKBWlKPA5K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c55ni0/btr42yYQ4Ra/XLOk3Hz1hJ60KKBWlKPA5K/img.jpg&quot; data-alt=&quot;봉선화&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c55ni0/btr42yYQ4Ra/XLOk3Hz1hJ60KKBWlKPA5K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc55ni0%2Fbtr42yYQ4Ra%2FXLOk3Hz1hJ60KKBWlKPA5K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;420&quot; height=&quot;560&quot; data-filename=&quot;IMG_7420.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;봉선화&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흙이 모자라서 가장 마지막에 심은 봉선화 화분. 다른 아이들보다 일주일 늦게 심었으니 잘 지켜보면 얘네 잘 자랄거라고 믿는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 화분을 키울 때 꽃도 안 피고 먹을 수도 없는 애들보다는 꽃이 피거나 키워서 먹을 수 있는 아이들이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어렸을 때도 문구점에서 봉선화 씨를 사다가 키워서 씨도 받았던 기억이 있다. 독립해서 이런 재미를 들여보려고 했는데 작년에는 너무 정신없이 보내서 이런 생각도 못했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올해는 생각난 김에 조그만한 화분으로 시작해본다. 일단 먹을 수 있는 토마토, 바질, 그리고 꽃 피는 봉선화, 해바라기.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;봉선화는 잘 키워서 꽃 피우면 소민이 손톱에 들여주고 해바라기는 분양하려고 한다. 잘 키워서 씨 받으면 어딘가 가져다가 심으려고...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소민이도 이제는 고학년에 되어서 별로 안 좋아할 줄 알았는데 화분에 이름표도 나서서 써주고 관심을 보이는걸 보니 시작하길 잘 했다는 생각이 든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸로 장모님과도 대화거리가 더 생긴거 같아서 그 점도 훌륭한 선택이었던듯 하다. 올해 잘 키워서 수확물을 얻을 수 있기를 소망해본다.&lt;/p&gt;</description>
      <category>My Story/식물재배기</category>
      <category>관찰일기</category>
      <category>바질</category>
      <category>방울토마토</category>
      <category>베란다화분</category>
      <category>봉선화</category>
      <category>식물키우기</category>
      <category>해바라기</category>
      <category>화분</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/374</guid>
      <comments>https://minarae7.tistory.com/entry/%EC%8B%9D%EB%AC%BC%EA%B4%80%EC%B0%B0%EC%9D%BC%EC%A7%80-3%EC%9B%94-20%EC%9D%BC#entry374comment</comments>
      <pubDate>Mon, 20 Mar 2023 23:48:19 +0900</pubDate>
    </item>
    <item>
      <title>지역 상생 솔루션</title>
      <link>https://minarae7.tistory.com/entry/%EC%A7%80%EC%97%AD-%EC%83%81%EC%83%9D-%EC%86%94%EB%A3%A8%EC%85%98</link>
      <description>&lt;figure id=&quot;og_1679232034330&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;중소기업제품 쇼핑몰 '동백상회' 신세계 센텀시티점서 새출발&quot; data-og-description=&quot;부산지역 우수 중소기업 제품의 홍보&amp;middot;판매를 지원하는 '동백상회'가 신세계백화점 센텀시티점에서 새롭게 출발한다. 부산시와 신세계는 9일 오전 해운대구 신세계 센텀시티점에서 중소기업 우&quot; data-og-host=&quot;n.news.naver.com&quot; data-og-source-url=&quot;https://n.news.naver.com/article/003/0011731630?sid=102&quot; data-og-url=&quot;https://n.news.naver.com/article/003/0011731630&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Xz2Yc/hyRY6s0nT6/rI3OCuDJXk5xmxWCl65upK/img.jpg?width=720&amp;amp;height=341&amp;amp;face=0_0_720_341,https://scrap.kakaocdn.net/dn/eVP8W/hyRY07pBaO/w1h39J3CUKI8EuY1k7zKXK/img.jpg?width=720&amp;amp;height=341&amp;amp;face=0_0_720_341&quot;&gt;&lt;a href=&quot;https://n.news.naver.com/article/003/0011731630?sid=102&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://n.news.naver.com/article/003/0011731630?sid=102&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Xz2Yc/hyRY6s0nT6/rI3OCuDJXk5xmxWCl65upK/img.jpg?width=720&amp;amp;height=341&amp;amp;face=0_0_720_341,https://scrap.kakaocdn.net/dn/eVP8W/hyRY07pBaO/w1h39J3CUKI8EuY1k7zKXK/img.jpg?width=720&amp;amp;height=341&amp;amp;face=0_0_720_341');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;중소기업제품 쇼핑몰 '동백상회' 신세계 센텀시티점서 새출발&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;부산지역 우수 중소기업 제품의 홍보&amp;middot;판매를 지원하는 '동백상회'가 신세계백화점 센텀시티점에서 새롭게 출발한다. 부산시와 신세계는 9일 오전 해운대구 신세계 센텀시티점에서 중소기업 우&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;n.news.naver.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얼마 전에 회사에서 마케팅 아이디어 대회를 진행했다. 거기에 참석해서 이런 저런 아이디어를 내다가 이런 비슷한 아이디어를 낸 적이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지역과 상생하는 유통회사. 수많은 점포들에서 각 지역별로 지역 거점 중소기업 또는 사회적 기업의 물건을 소개하고 이 기업들의 판로를 만들어주는 것을 하는 것이 어떨까 하는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사회적 기업에서 생산하는 물품을 판매할 수 있는 공간을 만들어서 기업 홍보를 할 수 있게 해준다면 흔히 말하는 ESG 경영의 하나가 되지 않을까 싶은 생각도 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 기사를 찾아보니 완전히 동일하지는 않지만 비슷한 맥락의 일을 이미 진행하고 있었구나 라는 생각을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제품을 발굴하고 전시하는 모든 일을 하는 대신 이런 일을 하는 기업을 찾아서 그 기업을 참여하게 하는 것도 하나의 플랫폼을 제공해주고 상생의 이미지도 가져올 수 있을거 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역시 내가 생각하는 것은 남도 다 생각하고 있구나를 다시 한 번 깨닫게 되는 순간이다.&lt;/p&gt;</description>
      <category>기사모음과 생각</category>
      <category>ESG경영</category>
      <category>대기업의역할</category>
      <category>지역상생</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/373</guid>
      <comments>https://minarae7.tistory.com/entry/%EC%A7%80%EC%97%AD-%EC%83%81%EC%83%9D-%EC%86%94%EB%A3%A8%EC%85%98#entry373comment</comments>
      <pubDate>Sun, 19 Mar 2023 22:25:44 +0900</pubDate>
    </item>
    <item>
      <title>[쇼핑 정보] 이마트 모바일 상품권을 활용한 저렴한 장보기 팁!</title>
      <link>https://minarae7.tistory.com/entry/%EC%87%BC%ED%95%91-%EC%A0%95%EB%B3%B4-%EC%9D%B4%EB%A7%88%ED%8A%B8-%EC%83%81%ED%92%88%EA%B6%8C%EC%9D%B4%EB%A7%88%ED%8B%B0%EC%BD%98-%EC%A0%80%EB%A0%B4%ED%95%98%EA%B2%8C-%EC%9D%B4%EB%A7%88%ED%8A%B8-%EC%9E%A5%EB%B3%B4%EA%B8%B0</link>
      <description>&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;대형 마트는 여러 가지가 있지만 개인적으로 가장 많이 가는 마트는 이마트/트레이더스이다.&lt;br /&gt;트레이더스도 이마트와 같은 계열이고 같은 결제 시스템을 가지고 있으니 여기서는 그냥 퉁 쳐서 이마트라고 통칭한다.&lt;br /&gt;마트에 가서 저렴하게 장을 보려고 여러 수단을 간구해봤는데 가장 좋은 방법은 상품권을 이용하는 것이라는 판단을 하게 되었다.&lt;br /&gt;이마트에는 오프라인 매장에서 사용할 수 있는 모바일 상품권이 있다. 이마트에서는 이마티콘이라고 부르는데 일반적으로는 이마트상품권 또는 이마트모바일상품권이라고 부른다.&lt;br /&gt;이마트에서 전략적으로 밀고 있는 상품권으로 지류로 된 실물 상품권이 있는게 아니고 모바일에서 인증을 통해서 사용할 수 있는 모바일 전용 상품권이다.&lt;br /&gt;이 상품권은 다시 두 가지 형태로 분류한다.&lt;br /&gt;우선 &quot;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;u&gt;&lt;b&gt;전액사용형&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&quot;이다. 전액 사용권은 다음과 같이 생겼다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzhdGt/btr3WcqBz76/3iMohHvHKO7GMRvqG4Eqk0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzhdGt/btr3WcqBz76/3iMohHvHKO7GMRvqG4Eqk0/img.jpg&quot; data-alt=&quot;전액사용형 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzhdGt/btr3WcqBz76/3iMohHvHKO7GMRvqG4Eqk0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzhdGt%2Fbtr3WcqBz76%2F3iMohHvHKO7GMRvqG4Eqk0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;302&quot; height=&quot;302&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;전액사용형 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전액사용형은 말 그대로 해당 금액을 쪼개 사용할 수 없고 일단 사용하면 전체 금액을 사용하여야 한다.&lt;br /&gt;다음은 &quot;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;u&gt;&lt;b&gt;잔액관리형&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&quot;이다. 잔액관리형은 다음과 같이 생겼다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;856&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/x1ILu/btr34WmFGwk/oiWjMX3Ljur6XH8TbLagw0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/x1ILu/btr34WmFGwk/oiWjMX3Ljur6XH8TbLagw0/img.jpg&quot; data-alt=&quot;잔액관리형 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/x1ILu/btr34WmFGwk/oiWjMX3Ljur6XH8TbLagw0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fx1ILu%2Fbtr34WmFGwk%2FoiWjMX3Ljur6XH8TbLagw0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;386&quot; height=&quot;386&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;856&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;잔액관리형 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잔액관리형은 전액사용형과 다르게 색상이 다르다. 그래서 카드의 색상을 보면 어떤 권형인지 알 수 있다. 그리고 잔액관리형은 말 그대로 상품권의 금액을 여러 차례로 나누어서 사용할 수 있다.&lt;br /&gt;그리고 각각 5,000원/10,000원/30,000원/50,000원/100,000원 권으로 다시 나눌 수 있다.&lt;br /&gt;그럼 왜 여기서 이런 포스팅을 하고 있는가 하면 바로 이 상품권을 통해서 이마트에서 장을 보면 좀 더 저렴하게 장을 볼 수 있기 때문이다.&lt;br /&gt;온라인 쇼핑몰에서 해당 상품권을 검색해보면 해당 상품권들을 구매할 수 있다.&lt;br /&gt;현재 검색을 통해서 잡히는 온라인 쇼핑몰은 다음과 같다.&lt;br /&gt;G마켓/11번가/티몬&lt;br /&gt;각 쇼핑몰별로 구매할 수 있는 특징이 다르다. 그래서 아래 표로 각 쇼핑몰별로 구매할 때 사용하는 특징을 정리해보았다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;쇼핑몰&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;할인율&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;특징&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;G마켓&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;5%&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;- 기본이 5%&lt;br /&gt;- 상시적으로 5% 전액 사용권을 판매&lt;br /&gt;- 잔액관리형은 아주 드물게 할인되어서 판매&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;11번가&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;6%&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;- 할인율이 제일 높음&lt;br /&gt;- 전액사용권만 판매&lt;br /&gt;- 거의 상시적으로 판매하지만 판매기간이 아닌 경우가 있음&lt;br /&gt;- 같은 금액권은 2장 또는 4장 등으로 구매제한 있음(특정 기간 동안 구매 못함)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;티몬&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;5%&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;- 할인율은 5%로 G마켓과 동일&lt;br /&gt;- 티몬은 메리트가 잔액관리형을 판매&lt;br /&gt;- 상시 할인 판매는 아니고 판매 기간이 정해져 있음.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 특징을 이용하면 이마트에서 장을 볼 때 좀 더 저렴하게 구매할 수 있다.&lt;br /&gt;11번가에서 일단 6% 할인권을 구매하고 티몬에서 5 % 잔액 관리형을 구매해두면 마트에서 장을 볼 때 대략 5~6% 정도 할인된 금액으로 결제를 할 수 있다. 더불어서 만약 가지고 있는 상품권이 모자란다고 생각되면 바로 G마켓에서 상품권을 필요한 만큼 구매하면 모바일 상품권이 바로 전송되지 때문에 당황할 필요없다.&lt;br /&gt;요새 같은 고물가 시대에 한 푼이라도 아끼려면 이런 수고로움을 마다하지 않아야 할 듯 하다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMupBT/btr37AC6rJ4/OMf8Ow6ephHP9N7QomXrmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMupBT/btr37AC6rJ4/OMf8Ow6ephHP9N7QomXrmk/img.png&quot; data-alt=&quot;티몬에서 구매한 잔액관리형 이마티콘&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMupBT/btr37AC6rJ4/OMf8Ow6ephHP9N7QomXrmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMupBT%2Fbtr37AC6rJ4%2FOMf8Ow6ephHP9N7QomXrmk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;434&quot; height=&quot;939&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;티몬에서 구매한 잔액관리형 이마티콘&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bONyoX/btr33ymjdt9/9XHzYtvTikEFX44nCiPbj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bONyoX/btr33ymjdt9/9XHzYtvTikEFX44nCiPbj0/img.png&quot; data-alt=&quot;11번가에서 구매한 전액사용형 이마티콘&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bONyoX/btr33ymjdt9/9XHzYtvTikEFX44nCiPbj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbONyoX%2Fbtr33ymjdt9%2F9XHzYtvTikEFX44nCiPbj0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;401&quot; height=&quot;868&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;2532&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;11번가에서 구매한 전액사용형 이마티콘&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>My Story/일상</category>
      <category>고물가시대에 살아남기</category>
      <category>꿀정보</category>
      <category>모바일상품권</category>
      <category>상품권</category>
      <category>이마트</category>
      <category>이마트장보기</category>
      <category>이마티콘</category>
      <category>저렴하게 장보기</category>
      <category>할인</category>
      <category>할인권구매</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/371</guid>
      <comments>https://minarae7.tistory.com/entry/%EC%87%BC%ED%95%91-%EC%A0%95%EB%B3%B4-%EC%9D%B4%EB%A7%88%ED%8A%B8-%EC%83%81%ED%92%88%EA%B6%8C%EC%9D%B4%EB%A7%88%ED%8B%B0%EC%BD%98-%EC%A0%80%EB%A0%B4%ED%95%98%EA%B2%8C-%EC%9D%B4%EB%A7%88%ED%8A%B8-%EC%9E%A5%EB%B3%B4%EA%B8%B0#entry371comment</comments>
      <pubDate>Wed, 15 Mar 2023 23:43:39 +0900</pubDate>
    </item>
    <item>
      <title>[가계부 만들기] Backend - Database #2</title>
      <link>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-Backend-Database-2</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전 포스팅에서 connection.py 파일을 통해서 mysql 서버에 접근하는 코드를 추가하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 ORM을 사용하기 위해서 models.py 파일을 작성할 것이다. ORM에 대한 내용은 아래 링크에서 확인할 수 있다.&lt;/p&gt;
&lt;figure id=&quot;og_1678846345452&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;ORM(Object Relational Mapping)이 뭘까?  &quot; data-og-description=&quot;ORM이란? ORM은 Object Relational Mapping 즉, 객체-관계 매핑의 줄임말이다. 객체-관계 매핑을 풀어서 설명하자면 우리가 OOP(Object Oriented Programming)에서 쓰이는 객체라는 개념을 구현한 클래스와 RDB(Relatio&quot; data-og-host=&quot;geonlee.tistory.com&quot; data-og-source-url=&quot;https://geonlee.tistory.com/207&quot; data-og-url=&quot;https://geonlee.tistory.com/207&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/gDiae/hyRU4pKJLe/54jKtG7ckAX4jIjsRHvY7k/img.jpg?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420,https://scrap.kakaocdn.net/dn/d4RPtP/hyRWBzt7B7/Xk88UkRrdbexPa5Li6Kjc1/img.jpg?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420,https://scrap.kakaocdn.net/dn/bW2UV1/hyRWsoZHGY/j5kxoAk72qkCuoA3hzLJ2k/img.jpg?width=880&amp;amp;height=462&amp;amp;face=0_0_880_462&quot;&gt;&lt;a href=&quot;https://geonlee.tistory.com/207&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://geonlee.tistory.com/207&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/gDiae/hyRU4pKJLe/54jKtG7ckAX4jIjsRHvY7k/img.jpg?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420,https://scrap.kakaocdn.net/dn/d4RPtP/hyRWBzt7B7/Xk88UkRrdbexPa5Li6Kjc1/img.jpg?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420,https://scrap.kakaocdn.net/dn/bW2UV1/hyRWsoZHGY/j5kxoAk72qkCuoA3hzLJ2k/img.jpg?width=880&amp;amp;height=462&amp;amp;face=0_0_880_462');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;ORM(Object Relational Mapping)이 뭘까?  &lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;ORM이란? ORM은 Object Relational Mapping 즉, 객체-관계 매핑의 줄임말이다. 객체-관계 매핑을 풀어서 설명하자면 우리가 OOP(Object Oriented Programming)에서 쓰이는 객체라는 개념을 구현한 클래스와 RDB(Relatio&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;geonlee.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fastapi에서는 sqlalchemy 라이브러리를 사용하고 있고 여기서 지원해주는 ORM 기능을 통해서 데이터베이스와 연결한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sqlalchemy는 기본적인 ORM을 제공해줌과 동시에 각 RDBMS 별 특성에 맞는 키워드나 기능을 추가로 지원해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 mysql을 사용할 것이기 때문에 Column 형에서 mysql에서만 사용할 수 있는 타입을 사용하게 된다. 예를 들어서 unsigned smallint는 mysql에서만 사용하는 타입이므로 다른 DBMS에서는 지원하지 않는다. 여기서는 이 타입을 사용하는데 이걸 사용하기 위해서 sqlalchemy에서는 mysql용 column 형을 제공해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 상대에 Column 형으로 사용할 타입과 라이브러리 또는 DB 연결에서 사용할 변수 등을 import 하도록 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1678888512708&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from sqlalchemy import (
    Column,
    VARCHAR,
    DATETIME,
    DATE,
    CHAR,
    PrimaryKeyConstraint,
    UniqueConstraint,
    ForeignKeyConstraint,
)
from sqlalchemy.dialects.mysql import (
    TINYINT,
    SMALLINT,
    INTEGER,
)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql import func
from .connection import engine

Base = declarative_base()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞의 포스팅에서 적용한 연결에 사용한 engine 변수를 import 하여서 해당 파일에서 사용하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 각 ORM Class에서 넘겨줄 Base를 선언하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 tb_members 테이블에 대해서 정의한다.&lt;/p&gt;
&lt;pre id=&quot;code_1678888664808&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Members(Base):
    __tablename__ = &quot;tb_members&quot;

    member_no = Column(SMALLINT(unsigned=True), nullable=False, autoincrement=True, comment=&quot;멤버번호&quot;)
    member_id = Column(VARCHAR(length=30), nullable=False, comment=&quot;사용자 아이디&quot;)
    member_pw = Column(VARCHAR(length=128), nullable=False, comment=&quot;사용자 패스워드&quot;)
    member_name = Column(VARCHAR(length=20), nullable=False, comment=&quot;사용자 이름&quot;)
    member_email = Column(VARCHAR(length=50), nullable=False, comment=&quot;이메일 주소&quot;)
    reg_dt = Column(DATETIME(timezone=False), nullable=False, server_default=func.now(), comment=&quot;생성일시&quot;)
    upd_dt = Column(DATETIME(timezone=False), nullable=False, server_default=func.now(), onupdate=func.now(), comment=&quot;수정일시&quot;)
    is_deleted = Column(CHAR(length=1), default=&quot;F&quot;, server_default=&quot;F&quot;, nullable=False, comment=&quot;삭제여부(T|F)&quot;)
    del_dt = Column(DATETIME(timezone=False), nullable=True, comment=&quot;삭제일시&quot;)

    __table_args__ = (
        PrimaryKeyConstraint(member_no, name=&quot;pk_members&quot;),
        UniqueConstraint(member_id, name=&quot;ixn_members__member_id&quot;),
        {
            &quot;comment&quot;: &quot;사용자 정보&quot;
        }
    )&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블의 이름을 지정하고 각 Column에서 대해서 기술하였다. 그리고 마지막에 해당 테이블에 대한 특성을 기술하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 테이블에 대해서 primary key를 선언하는 방법은 두 가지가 있다. 우선 위의 코드처럼 PrimaryKeyConstraint를 통해서 컬럼과 인덱스 이름을 지정하는 방법이 있다. mysql에서는 primary key의 이름을 지정하여서 사용하지 않기 때문에 name을 쓰지 않아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 방법은 primary key로 사용할 컬럼에서 primary_key=True와 같이 사용하는 것이다. 아래 코드를 참조하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1678889006469&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;member_no = Column(SMALLINT(unsigned=True), nullable=False, autoincrement=True, primary_key=True, comment=&quot;멤버번호&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 작성하면 create table 구문이 위의 방식과 약간 다르게 생성된다. 하지만 실제 mysql 내부에서는 이 두 가지 방법을 모두 동일하게 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 다음으로 tb_category 테이블에 대한 Class 정의를 한다. 다음 코드와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1678889115659&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Category(Base):
    __tablename__ = &quot;tb_category&quot;

    category_no = Column(INTEGER(unsigned=True), nullable=False, autoincrement=True, comment=&quot;카테고리 번호&quot;)
    member_no = Column(SMALLINT(unsigned=True), nullable=True, comment=&quot;카테고리 생성자 번호(기본 카테고리일 경우 null)&quot;)
    category_name = Column(VARCHAR(length=50), nullable=False, comment=&quot;카테고리 이름&quot;)
    has_children = Column(CHAR(length=1), nullable=False, default=&quot;F&quot;, comment=&quot;자식을 가지고 있는지 여부(T|F)&quot;)
    parent_no = Column(INTEGER(unsigned=True), nullable=True, comment=&quot;부모 카테고리 번호&quot;)
    class_name = Column(VARCHAR(length=30), nullable=True, comment=&quot;아이콘 클래스&quot;)
    is_system = Column(CHAR(length=1), default=&quot;F&quot;, comment=&quot;시스템 기본 카테고리&quot;)
    reg_dt = Column(DATETIME(timezone=False), nullable=False, server_default=func.now(), comment=&quot;생성일시&quot;)
    upd_dt = Column(DATETIME(timezone=False), nullable=False, server_default=func.now(), onupdate=func.now(), comment=&quot;수정일시&quot;)
    is_deleted = Column(CHAR(length=1), default=&quot;F&quot;, server_default=&quot;F&quot;, nullable=False, comment=&quot;삭제여부(T|F)&quot;)
    del_dt = Column(DATETIME(timezone=False), nullable=True, comment=&quot;삭제일시&quot;)

    __table_args__ = (
        PrimaryKeyConstraint(category_no, name=&quot;pk_category&quot;),
        ForeignKeyConstraint(
            [&quot;member_no&quot;],
            [&quot;tb_members.member_no&quot;],
            name=&quot;fk_category__member_no&quot;,
            onupdate=&quot;NO ACTION&quot;,
            ondelete=&quot;NO ACTION&quot;,
        ),
        {
            &quot;comment&quot;: &quot;카테고리 정보&quot;
        }
    )&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Category 테이블에서는 tb_members 테이블의 member_no 컬럼을 foreign key로 사용한다. 이에 대해서 위의 코드와 같이 정리하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 두 테이블에 대한 정의는 아래 같이 정리한다.&lt;/p&gt;
&lt;pre id=&quot;code_1678889290639&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class AccountLog(Base):
    __tablename__ = &quot;tb_account_log&quot;

    account_log_no = Column(INTEGER(unsigned=True), nullable=False, autoincrement=True, comment=&quot;내역번호&quot;)
    member_no = Column(SMALLINT(unsigned=True), nullable=False, comment=&quot;사용자번호&quot;)
    std_date = Column(DATE, nullable=False, comment=&quot;날짜&quot;)
    opponent_name = Column(VARCHAR(length=150), nullable=False, comment=&quot;메인거래처&quot;)
    reg_dt = Column(DATETIME(timezone=False), nullable=False, server_default=func.now(), comment=&quot;생성일시&quot;)
    upd_dt = Column(DATETIME(timezone=False), nullable=False, server_default=func.now(), onupdate=func.now(), comment=&quot;수정일시&quot;)
    is_deleted = Column(CHAR(length=1), default=&quot;F&quot;, server_default=&quot;F&quot;, nullable=False, comment=&quot;삭제여부(T|F)&quot;)
    del_dt = Column(DATETIME(timezone=False), nullable=True, comment=&quot;삭제일시&quot;)

    __table_args__ = (
        PrimaryKeyConstraint(account_log_no, name=&quot;pk_account_log&quot;),
        ForeignKeyConstraint(
            [&quot;member_no&quot;],
            [&quot;tb_members.member_no&quot;],
            name=&quot;fk_account_log__member_no&quot;,
            onupdate=&quot;NO ACTION&quot;,
            ondelete=&quot;NO ACTION&quot;,
        ),
        {
            &quot;comment&quot;: &quot;가계부 메인 내역&quot;
        }
    )

class LogDefault(Base):
    __tablename__ = &quot;tb_log_detail&quot;

    log_detail_no = Column(INTEGER(unsigned=True), nullable=False, autoincrement=True, comment=&quot;사용내역 상세 번호&quot;)
    account_log_no = Column(INTEGER(unsigned=True), nullable=False, comment=&quot;내역번호&quot;)
    detail_contents = Column(VARCHAR(length=300), nullable=False, comment=&quot;상세내역정보&quot;)
    amounts = Column(INTEGER(unsigned=True), nullable=False, comment=&quot;금액&quot;)
    io_type = Column(CHAR(length=1), nullable=False, comment=&quot;수입/지출 구분(I: 수입, O: 지출)&quot;)
    category_no = Column(INTEGER(unsigned=True), nullable=True, comment=&quot;카테고리 번호&quot;)
    important = Column(TINYINT(unsigned=True), nullable=False, default=1, comment=&quot;중요도(지출일 경우만)&quot;)
    is_fixed_cost = Column(CHAR(length=1), nullable=False, default=&quot;F&quot;, comment=&quot;고정비여부(지출일 경우만, T|F)&quot;)
    reg_dt = Column(DATETIME(timezone=False), nullable=False, server_default=func.now(), comment=&quot;생성일시&quot;)
    upd_dt = Column(DATETIME(timezone=False), nullable=False, server_default=func.now(), onupdate=func.now(), comment=&quot;수정일시&quot;)
    is_deleted = Column(CHAR(length=1), default=&quot;F&quot;, server_default=&quot;F&quot;, nullable=False, comment=&quot;삭제여부(T|F)&quot;)
    del_dt = Column(DATETIME(timezone=False), nullable=True, comment=&quot;삭제일시&quot;)

    __table_args__ = (
        PrimaryKeyConstraint(log_detail_no, name=&quot;pk_log_detail&quot;),
        ForeignKeyConstraint(
            [&quot;account_log_no&quot;],
            [&quot;tb_account_log.account_log_no&quot;],
            name=&quot;fk_log_detail__account_log_no&quot;,
            onupdate=&quot;NO ACTION&quot;,
            ondelete=&quot;NO ACTION&quot;,
        ),
        ForeignKeyConstraint(
            [&quot;category_no&quot;],
            [&quot;tb_category.category_no&quot;],
            name=&quot;fk_log_detail__category_no&quot;,
            onupdate=&quot;NO ACTION&quot;,
            ondelete=&quot;NO ACTION&quot;,
        ),
        {
            &quot;comment&quot;: &quot;가계부 상세 내역&quot;
        }
    )&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 마지막으로 다음 코드를 추가한다.&lt;/p&gt;
&lt;pre id=&quot;code_1678889325176&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;with engine.connect() as conn:
    Base.metadata.create_all(conn)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 코드를 입력하면 프로그램이 구동될 때 ORM 정의에서 현재 생성되지 않은 테이블이 있으면 비교해서 자동으로 테이블을 생성해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ORM을 작성하면서 실제로 생성되는 테이블 스키마를 확인할 수 있는데 이를 보면서 코드를 변경하면서 개발하면 개발을 용이하게 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더불어 connection.py에서 create_engine 함수를 호출하면서 echo 옵션을 True로 선언하면 실제로 sqlalchemy에서 사용하는 쿼리를 확인할 수 있다. 개발 단계에서는 이렇게 쿼리를 확인하면서 하는 것이 많은 도움을 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1678889554016&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;engine = create_engine(
    DB_URL, encoding = 'utf-8',
    echo=True,
    future=True,
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 통해서 생성된 테이블의 DDL은 다음 이미지들과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-03-15 오후 11.16.01.png&quot; data-origin-width=&quot;1406&quot; data-origin-height=&quot;426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/419UF/btr35vvzLVX/3InGYUszz9qqcx7cUUjSDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/419UF/btr35vvzLVX/3InGYUszz9qqcx7cUUjSDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/419UF/btr35vvzLVX/3InGYUszz9qqcx7cUUjSDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F419UF%2Fbtr35vvzLVX%2F3InGYUszz9qqcx7cUUjSDk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1406&quot; height=&quot;426&quot; data-filename=&quot;스크린샷 2023-03-15 오후 11.16.01.png&quot; data-origin-width=&quot;1406&quot; data-origin-height=&quot;426&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-03-15 오후 11.16.19.png&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;514&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYpIYe/btr38KFuh0a/A8AOqx7QnFfaO1Cgtxkr20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYpIYe/btr38KFuh0a/A8AOqx7QnFfaO1Cgtxkr20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYpIYe/btr38KFuh0a/A8AOqx7QnFfaO1Cgtxkr20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYpIYe%2Fbtr38KFuh0a%2FA8AOqx7QnFfaO1Cgtxkr20%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1490&quot; height=&quot;514&quot; data-filename=&quot;스크린샷 2023-03-15 오후 11.16.19.png&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;514&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-03-15 오후 11.16.33.png&quot; data-origin-width=&quot;1522&quot; data-origin-height=&quot;406&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Wg405/btr4aMpw2KF/kkrdzi4cth33Y1hb685AO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Wg405/btr4aMpw2KF/kkrdzi4cth33Y1hb685AO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Wg405/btr4aMpw2KF/kkrdzi4cth33Y1hb685AO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWg405%2Fbtr4aMpw2KF%2Fkkrdzi4cth33Y1hb685AO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1522&quot; height=&quot;406&quot; data-filename=&quot;스크린샷 2023-03-15 오후 11.16.33.png&quot; data-origin-width=&quot;1522&quot; data-origin-height=&quot;406&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-03-15 오후 11.16.47.png&quot; data-origin-width=&quot;1798&quot; data-origin-height=&quot;594&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OROCg/btr33y0UtzZ/G3lJxojBAekmbiKUo1ORS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OROCg/btr33y0UtzZ/G3lJxojBAekmbiKUo1ORS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OROCg/btr33y0UtzZ/G3lJxojBAekmbiKUo1ORS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOROCg%2Fbtr33y0UtzZ%2FG3lJxojBAekmbiKUo1ORS0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1798&quot; height=&quot;594&quot; data-filename=&quot;스크린샷 2023-03-15 오후 11.16.47.png&quot; data-origin-width=&quot;1798&quot; data-origin-height=&quot;594&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로는 models를 기반으로 schemes.py를 만들어야 하는데, 해당 파일을 route를 만들면서 request parameter와 response body를 어떻게 만들건지 정의하면서 계속 수정해나가야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 포스팅에서는 로그인을 하기 위한 scheme 정의와 login 하기 위한 코드 작성을 진행하도록 하겠다.&lt;/p&gt;</description>
      <category>Programming/Project Log</category>
      <category>FastAPI</category>
      <category>ORM</category>
      <category>python</category>
      <category>sqlalchemy</category>
      <category>개발로그</category>
      <category>파이썬</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/372</guid>
      <comments>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-Backend-Database-2#entry372comment</comments>
      <pubDate>Wed, 15 Mar 2023 11:14:21 +0900</pubDate>
    </item>
    <item>
      <title>[식물관찰일지] 씨앗심기</title>
      <link>https://minarae7.tistory.com/entry/%EC%94%A8%EC%95%97%EC%8B%AC%EA%B8%B0</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m1Hd4/btr3dT5QjV4/hSLVkKUJy7nsuBDUFqoD81/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m1Hd4/btr3dT5QjV4/hSLVkKUJy7nsuBDUFqoD81/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m1Hd4/btr3dT5QjV4/hSLVkKUJy7nsuBDUFqoD81/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm1Hd4%2Fbtr3dT5QjV4%2FhSLVkKUJy7nsuBDUFqoD81%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4032&quot; height=&quot;3024&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfYi6l/btr3vOV56eB/6yEKMmt1JKuzPNeTxCKB3K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfYi6l/btr3vOV56eB/6yEKMmt1JKuzPNeTxCKB3K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfYi6l/btr3vOV56eB/6yEKMmt1JKuzPNeTxCKB3K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfYi6l%2Fbtr3vOV56eB%2F6yEKMmt1JKuzPNeTxCKB3K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4032&quot; height=&quot;3024&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;주말에 아이들과 마트에 갔다가 화분을 집어 왔다. 한 번 키워보고 싶다는 생각은 늘 하고 있었는데 생각처럼 잘 안되었는데 생각난 김에 씨앗과 화분을 사와봤다.&lt;br /&gt;일단 토마토, 바질, 해바라기, 봉선화를 사왔는데 흙을 적게 사와서 봉선화는 아직 심지 못했다.&lt;br /&gt;그냥 내가 키워보고 싶어서 구매했는데 큰 아이가 신이 나서 화분에 이름표를 붙여두었다.&lt;br /&gt;아이와 이렇게 교류해 볼 수 있는 시간이 생기니 좋다.&lt;br /&gt;토마토와 바질을 키워서 먹을 수 있다면 더 좋을거 같다.&lt;br /&gt;이렇게 해서 잘 키워지면 내년에는 더 많이 키워서 먹을 수 있는 것과 꽃들을 더 키워봐야겠다.&lt;/p&gt;</description>
      <category>My Story/식물재배기</category>
      <category>다이소</category>
      <category>바질</category>
      <category>토마토</category>
      <category>해바라기</category>
      <category>화분심기</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/370</guid>
      <comments>https://minarae7.tistory.com/entry/%EC%94%A8%EC%95%97%EC%8B%AC%EA%B8%B0#entry370comment</comments>
      <pubDate>Mon, 13 Mar 2023 08:20:29 +0900</pubDate>
    </item>
    <item>
      <title>닭 쫓던 개 지붕 쳐다보게 생김</title>
      <link>https://minarae7.tistory.com/entry/%EB%8B%AD-%EC%AB%93%EB%8D%98-%EA%B0%9C-%EC%A7%80%EB%B6%95-%EC%B3%90%EB%8B%A4%EB%B3%B4%EA%B2%8C-%EC%83%9D%EA%B9%80</link>
      <description>&lt;figure id=&quot;og_1678545514838&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;video&quot; data-og-title=&quot;앙숙 이란-사우디 관계 정상화...美, 中 역할 평가절하&quot; data-og-description=&quot;[앵커]중동의 오랜 앙숙인 이란과 사우디아라비아가 7년 ...&quot; data-og-host=&quot;www.ytn.co.kr&quot; data-og-source-url=&quot;https://www.ytn.co.kr/_ln/0104_202303111433589370&quot; data-og-url=&quot;https://www.ytn.co.kr/_ln/0104_202303111433589370&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c4wI6t/hyRTWqJcHP/2kCcG196U0sUkDpZ5syHI0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot;&gt;&lt;a href=&quot;https://www.ytn.co.kr/_ln/0104_202303111433589370&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.ytn.co.kr/_ln/0104_202303111433589370&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c4wI6t/hyRTWqJcHP/2kCcG196U0sUkDpZ5syHI0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;앙숙 이란-사우디 관계 정상화...美, 中 역할 평가절하&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;[앵커]중동의 오랜 앙숙인 이란과 사우디아라비아가 7년 ...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.ytn.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얼마 전 우리 나라 대통령께서 사우디에 가셔서 사우디의 주적은 이란이라고 선언하신 일이 있다. 근데 얼마 지나지 않아서 그 두 나라가 관계 정상화를 한다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러면 어떻게 되는건가. 사우디는 우리 친구고 친구랑 적은 이란이라고 했는데 그래서 이란에게 사우디를 지켜주려고 파병한거라고 했는데 이러면 관계가 어떻게 되는건가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이란이랑도 친구인가 아니면 그대로 주적인가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대통령에게 물어봐야할거 같다.&lt;/p&gt;</description>
      <category>기사모음과 생각</category>
      <category>사우디아라비아</category>
      <category>이란</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/369</guid>
      <comments>https://minarae7.tistory.com/entry/%EB%8B%AD-%EC%AB%93%EB%8D%98-%EA%B0%9C-%EC%A7%80%EB%B6%95-%EC%B3%90%EB%8B%A4%EB%B3%B4%EA%B2%8C-%EC%83%9D%EA%B9%80#entry369comment</comments>
      <pubDate>Sat, 11 Mar 2023 23:42:10 +0900</pubDate>
    </item>
    <item>
      <title>[가계부 만들기] Backend - Database #1</title>
      <link>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-Backend-Database-1</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이제 본격적인 개발을 진행해볼 것이다. 본격적인 개발을 시작하면서 데이터를 실제로 저장할 데이터베이스를 연결해두어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 app/databases 디렉토리를 생성하고 거기서 관련 코드를 넣을 계획이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발은 vscode IDE를 사용하여서 진행할 것이다. uvicorn으로 프로그램을 실행하면 app 디렉토리 하단에 __pycache__라는 디렉토리가 생성된다. 이 디렉토리가 계속 생성되고 갱신되는데 개발에서 사용하는 디렉토리가 아니다보니 제법 신경이 쓰인다. 그리서 여기서는 해당 디렉토리를 보이지 않도록 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;backend 디렉토리에서 .vscode 라는 디렉토리는 생성하고 settings.json 파일을 생성한다. 이렇게 하고 나면 디렉토리 구조가 다음과 같아 진다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-03-09 오후 10.40.32.png&quot; data-origin-width=&quot;406&quot; data-origin-height=&quot;328&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yM0Rd/btr2TBqZW0T/hWLBY0juEAOCCQLjnWsnw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yM0Rd/btr2TBqZW0T/hWLBY0juEAOCCQLjnWsnw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yM0Rd/btr2TBqZW0T/hWLBY0juEAOCCQLjnWsnw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyM0Rd%2Fbtr2TBqZW0T%2FhWLBY0juEAOCCQLjnWsnw0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;153&quot; height=&quot;124&quot; data-filename=&quot;스크린샷 2023-03-09 오후 10.40.32.png&quot; data-origin-width=&quot;406&quot; data-origin-height=&quot;328&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;settings.json 파일에 다음의 내용을 입력하면 __pycache__ 디렉토리가 파일 트리에서 사라지게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1678369307439&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;files.exclude&quot;: {
        &quot;**/__pycache__&quot;: true
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 app 하단에 데이터베이스 관련 파일을 모으기 위한 디렉토리로 database를 생성하고 그 아래 __init__.py 파일을 넣어둔다. 그러면 다음과 같은 구조가 될 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-03-09 오후 10.43.53.png&quot; data-origin-width=&quot;410&quot; data-origin-height=&quot;506&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VNDQr/btr24MEbw7h/X9BE8LDxstbzqAMUQjv9ak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VNDQr/btr24MEbw7h/X9BE8LDxstbzqAMUQjv9ak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VNDQr/btr24MEbw7h/X9BE8LDxstbzqAMUQjv9ak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVNDQr%2Fbtr24MEbw7h%2FX9BE8LDxstbzqAMUQjv9ak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;199&quot; height=&quot;246&quot; data-filename=&quot;스크린샷 2023-03-09 오후 10.43.53.png&quot; data-origin-width=&quot;410&quot; data-origin-height=&quot;506&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 database 디렉토리 아래에 3개의 파일을 생성할 것이다. 각각은 다음과 같은 역할을 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;serects.json : Database 접속 정보를 저장&lt;/li&gt;
&lt;li&gt;connection.py : 데이터베이스 접속과 관련된 내용을 작성&lt;/li&gt;
&lt;li&gt;models.py : 테이블에 대한 스키마 정보를 작성&lt;/li&gt;
&lt;li&gt;scheme.py : request/response에 대한 형식 내용을 작성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 데이터베이스로 mysql을 사용한다. 먼저 접속 정보를 입력한 json 파일을 생성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1678370882166&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;DB&quot;: {
        &quot;user&quot;: &quot;user&quot;,
        &quot;password&quot;: &quot;password&quot;,
        &quot;host&quot;: &quot;localhost&quot;,
        &quot;port&quot;: &quot;3306&quot;,
        &quot;database&quot;: &quot;account_book&quot;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 파일에서는 데이터베이스 사용하는 정보를 채워넣으면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 connection.py 파일을 작성하겠다.&lt;/p&gt;
&lt;pre id=&quot;code_1678370943727&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
import json
import os
BASE_DIR = os.path.dirname(os.path.abspath(__file__))

SERECT_FILE = os.path.join(BASE_DIR, 'serects.json')
serects = json.loads(open(SERECT_FILE).read())
DB = serects[&quot;DB&quot;]

DB_URL = f&quot;mysql+pymysql://{DB['user']}:{DB['password']}@{DB['host']}/{DB['database']}?charset=utf8&quot;

engine = create_engine(
    DB_URL, encoding = 'utf-8'
)

SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;connection.py에서는 위에서 작성한 serects.json 파일을 읽어서 데이터베이스 접속 정보를 가져오고 이 정보를 통해서 데이터베이스 접속 URL을 만들둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 정상적으로 접속이 되는지 확인하여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 mysql에 접속하기 위해서 필요한 패키지인 pymysql를 추가한다.&lt;/p&gt;
&lt;pre id=&quot;code_1678371105135&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;% poetry add pymysql
% pip install pymysql&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래는 위의 명령어만 입력하여서 설치가 되는데 가끔 패키지가 정상적으로 설치가 안되는 경우가 발생한다. 그런 경우에는 pip를 통해서 패키지를 설치하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 정상적으로 접속이 되는지 확인하도록 하겠다. app/main.py 파일을 아래 코드와 같이 수정한다.&lt;/p&gt;
&lt;pre id=&quot;code_1678371209386&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from fastapi import FastAPI, Depends
from sqlalchemy.orm import Session
from .database.connection import get_db

app = FastAPI(title=&quot;account-book-api&quot;)

@app.get(&quot;/&quot;)
async def main(
    db: Session = Depends(get_db)
):
    return {&quot;message&quot;: &quot;Hello World&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 코드에서 main이 호출될 때 파라미터로 db를 받도록 하였는데 이 때 자동으로 데이터베이스에 접속하여서 접속 Session을 넘겨주도록 하였고 현재는 프로그램에서 실제로 db 변수를 사용하지는 않는다. 그냥 정상적으로 데이터베이스 접속이 되는지만 확인하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 프로그램을 재구동하여서 문제가 페이지가 정상적으로 출력되면 데이터베이스 접속이 정상적으로 이루어지는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-03-04 오후 11.27.05.png&quot; data-origin-width=&quot;2334&quot; data-origin-height=&quot;1506&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pvBnn/btr2ZBDWru6/NkjKsds6RhDXmKAWyMYKB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pvBnn/btr2ZBDWru6/NkjKsds6RhDXmKAWyMYKB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pvBnn/btr2ZBDWru6/NkjKsds6RhDXmKAWyMYKB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpvBnn%2Fbtr2ZBDWru6%2FNkjKsds6RhDXmKAWyMYKB0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2334&quot; height=&quot;1506&quot; data-filename=&quot;스크린샷 2023-03-04 오후 11.27.05.png&quot; data-origin-width=&quot;2334&quot; data-origin-height=&quot;1506&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 포스팅에서는 앞에서 설계한 테이블들을 models.py 파일을 기술하고 이를 설명하도록 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 글의 내용은 아래 사이트를 레퍼런스로 작성되었다.&lt;/p&gt;
&lt;figure id=&quot;og_1678458236060&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Fastapi 사용법 mysql 연결, crud , 외부 api 연동&quot; data-og-description=&quot;이번에, 운좋게 과제를 진행한 곳에서 fast-api 를 사용할 기회가 생겼다.개인적으로 진행하면서 너무 재밌게, 많은 공부가 된 것 같다.우선 패키지 관리를 poetry 로 하였는데 생각보다 어렵지 않다&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@gkwlsdl1/Fastapi-%EC%82%AC%EC%9A%A9%EB%B2%95-mysql-%EC%97%B0%EA%B2%B0-crud-%EC%99%B8%EB%B6%80-api-%EC%97%B0%EB%8F%99&quot; data-og-url=&quot;https://velog.io/@gkwlsdl1/Fastapi-사용법-mysql-연결-crud-외부-api-연동&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bG40rR/hyRTM12CBB/Ics8bHE34zIMiMol1xqsm1/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/bwpOz3/hyRSIGUQjF/UzQJ4TZAKWzMUnEbzPnxj1/img.jpg?width=860&amp;amp;height=860&amp;amp;face=319_146_465_305&quot;&gt;&lt;a href=&quot;https://velog.io/@gkwlsdl1/Fastapi-%EC%82%AC%EC%9A%A9%EB%B2%95-mysql-%EC%97%B0%EA%B2%B0-crud-%EC%99%B8%EB%B6%80-api-%EC%97%B0%EB%8F%99&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@gkwlsdl1/Fastapi-%EC%82%AC%EC%9A%A9%EB%B2%95-mysql-%EC%97%B0%EA%B2%B0-crud-%EC%99%B8%EB%B6%80-api-%EC%97%B0%EB%8F%99&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bG40rR/hyRTM12CBB/Ics8bHE34zIMiMol1xqsm1/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/bwpOz3/hyRSIGUQjF/UzQJ4TZAKWzMUnEbzPnxj1/img.jpg?width=860&amp;amp;height=860&amp;amp;face=319_146_465_305');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Fastapi 사용법 mysql 연결, crud , 외부 api 연동&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이번에, 운좋게 과제를 진행한 곳에서 fast-api 를 사용할 기회가 생겼다.개인적으로 진행하면서 너무 재밌게, 많은 공부가 된 것 같다.우선 패키지 관리를 poetry 로 하였는데 생각보다 어렵지 않다&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1678458250842&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[FastAPI] 2. SQLAlchemy를 이용한 간단한 CRUD API 만들기&quot; data-og-description=&quot;이번 글에서는 ORM에 대한 사용 방법에 대해 알아보도록 하겠습니다. ORM은 Object Relation Mapping의 약자로 객체를 이용해서 데이터베이스 Entity에 접근하는 방법입니다. 보통 애플리케이션 레벨에서 &quot; data-og-host=&quot;blog.neonkid.xyz&quot; data-og-source-url=&quot;https://blog.neonkid.xyz/253&quot; data-og-url=&quot;https://blog.neonkid.xyz/253&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c7nEmk/hyRSN2y7SD/gXe7rjdfOSUIwOKGisUKT0/img.png?width=800&amp;amp;height=520&amp;amp;face=0_0_800_520,https://scrap.kakaocdn.net/dn/bUTJuS/hyRTR96RH4/XKMCoxrPGosQyt9MvadFK1/img.png?width=800&amp;amp;height=520&amp;amp;face=0_0_800_520,https://scrap.kakaocdn.net/dn/l6fqT/hyRTRPNp1r/6Kkvh1iZhyW5yGkmDuLVf0/img.png?width=2508&amp;amp;height=1468&amp;amp;face=0_0_2508_1468&quot;&gt;&lt;a href=&quot;https://blog.neonkid.xyz/253&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://blog.neonkid.xyz/253&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c7nEmk/hyRSN2y7SD/gXe7rjdfOSUIwOKGisUKT0/img.png?width=800&amp;amp;height=520&amp;amp;face=0_0_800_520,https://scrap.kakaocdn.net/dn/bUTJuS/hyRTR96RH4/XKMCoxrPGosQyt9MvadFK1/img.png?width=800&amp;amp;height=520&amp;amp;face=0_0_800_520,https://scrap.kakaocdn.net/dn/l6fqT/hyRTRPNp1r/6Kkvh1iZhyW5yGkmDuLVf0/img.png?width=2508&amp;amp;height=1468&amp;amp;face=0_0_2508_1468');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[FastAPI] 2. SQLAlchemy를 이용한 간단한 CRUD API 만들기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이번 글에서는 ORM에 대한 사용 방법에 대해 알아보도록 하겠습니다. ORM은 Object Relation Mapping의 약자로 객체를 이용해서 데이터베이스 Entity에 접근하는 방법입니다. 보통 애플리케이션 레벨에서&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;blog.neonkid.xyz&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming/Project Log</category>
      <category>FastAPI</category>
      <category>MySQL</category>
      <category>project log</category>
      <category>pymysql</category>
      <category>python</category>
      <category>가계부만들기</category>
      <category>파이썬</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/368</guid>
      <comments>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-Backend-Database-1#entry368comment</comments>
      <pubDate>Thu, 9 Mar 2023 23:17:59 +0900</pubDate>
    </item>
    <item>
      <title>21세기 대한민국의 민낯</title>
      <link>https://minarae7.tistory.com/entry/21%EC%84%B8%EA%B8%B0%EC%9D%98-%EB%8C%80%ED%95%9C%EB%AF%BC%EA%B5%AD%EC%9D%98-%EB%AF%BC%EB%82%AF</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1678251179744&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;&amp;lsquo;尹 절친&amp;rsquo; 석동현 &amp;ldquo;식민지배 받은 나라 중 사죄&amp;middot;배상 악쓰는 건 한국뿐&amp;rdquo;&quot; data-og-description=&quot;석동현 민주평화통일자문회의(민주평통) 사무처장이 7일 정부가 발표한 일제 강제동원(징용) 피해자 ...&quot; data-og-host=&quot;m.khan.co.kr&quot; data-og-source-url=&quot;https://www.khan.co.kr/politics/politics-general/article/202303071515021/&quot; data-og-url=&quot;https://m.khan.co.kr/article/202303071515021&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ceMH4H/hyRSJYd5aN/RSXKLcEFRpMTgjlodDFd20/img.jpg?width=700&amp;amp;height=464&amp;amp;face=229_85_463_156,https://scrap.kakaocdn.net/dn/cVqzjh/hyRRRjolbr/Mt2mF3QTWnpBKEWPEOuKB1/img.jpg?width=700&amp;amp;height=464&amp;amp;face=229_85_463_156,https://scrap.kakaocdn.net/dn/bEZDNv/hyRRRwXJdu/e61hKKIcY41ZCZ3bMjAZ01/img.png?width=500&amp;amp;height=338&amp;amp;face=201_86_306_201&quot;&gt;&lt;a href=&quot;https://www.khan.co.kr/politics/politics-general/article/202303071515021/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.khan.co.kr/politics/politics-general/article/202303071515021/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ceMH4H/hyRSJYd5aN/RSXKLcEFRpMTgjlodDFd20/img.jpg?width=700&amp;amp;height=464&amp;amp;face=229_85_463_156,https://scrap.kakaocdn.net/dn/cVqzjh/hyRRRjolbr/Mt2mF3QTWnpBKEWPEOuKB1/img.jpg?width=700&amp;amp;height=464&amp;amp;face=229_85_463_156,https://scrap.kakaocdn.net/dn/bEZDNv/hyRRRwXJdu/e61hKKIcY41ZCZ3bMjAZ01/img.png?width=500&amp;amp;height=338&amp;amp;face=201_86_306_201');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lsquo;尹 절친&amp;rsquo; 석동현 &amp;ldquo;식민지배 받은 나라 중 사죄&amp;middot;배상 악쓰는 건 한국뿐&amp;rdquo;&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;석동현 민주평화통일자문회의(민주평통) 사무처장이 7일 정부가 발표한 일제 강제동원(징용) 피해자 ...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;m.khan.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1678251202172&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;김영환 충북지사 &amp;quot;기꺼이 친일파 되겠다&amp;quot;&amp;hellip;정부 해법 지지&quot; data-og-description=&quot;김영환 충북도지사가 &amp;ldquo;나는 기꺼이 친일파가 되겠다&amp;rdquo;며 윤석열 정부 강제징용 피해자 배상 해법을 옹호했다.김 지사(국민의힘)는 7일 자신의 유튜브 채널에 전날 발표된 정부 해법을 옹호하&quot; data-og-host=&quot;www.edaily.co.kr&quot; data-og-source-url=&quot;https://www.edaily.co.kr/news/read?newsId=03601446635541024&quot; data-og-url=&quot;https://www.edaily.co.kr/news/read?newsId=03601446635541024&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/gFkX4/hyRRGhJZrI/SqHH9BF9KHYDyOdwbnCpHk/img.jpg?width=670&amp;amp;height=402&amp;amp;face=317_112_366_165,https://scrap.kakaocdn.net/dn/cQUVTV/hyRRGvgWs3/kxoWpEdR5HLDeL7SOeI560/img.jpg?width=670&amp;amp;height=402&amp;amp;face=317_112_366_165,https://scrap.kakaocdn.net/dn/9Kjx2/hyRRNgRk30/6COvoW1okpzssCgtpsNjK1/img.jpg?width=670&amp;amp;height=402&amp;amp;face=317_112_366_165&quot;&gt;&lt;a href=&quot;https://www.edaily.co.kr/news/read?newsId=03601446635541024&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.edaily.co.kr/news/read?newsId=03601446635541024&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/gFkX4/hyRRGhJZrI/SqHH9BF9KHYDyOdwbnCpHk/img.jpg?width=670&amp;amp;height=402&amp;amp;face=317_112_366_165,https://scrap.kakaocdn.net/dn/cQUVTV/hyRRGvgWs3/kxoWpEdR5HLDeL7SOeI560/img.jpg?width=670&amp;amp;height=402&amp;amp;face=317_112_366_165,https://scrap.kakaocdn.net/dn/9Kjx2/hyRRNgRk30/6COvoW1okpzssCgtpsNjK1/img.jpg?width=670&amp;amp;height=402&amp;amp;face=317_112_366_165');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;김영환 충북지사 &quot;기꺼이 친일파 되겠다&quot;&amp;hellip;정부 해법 지지&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;김영환 충북도지사가 &amp;ldquo;나는 기꺼이 친일파가 되겠다&amp;rdquo;며 윤석열 정부 강제징용 피해자 배상 해법을 옹호했다.김 지사(국민의힘)는 7일 자신의 유튜브 채널에 전날 발표된 정부 해법을 옹호하&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.edaily.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1678251246652&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;삼일절 일장기 주민 &amp;lsquo;목사&amp;rsquo;였다&amp;hellip;&amp;ldquo;대일본제국&amp;rdquo; 설교&quot; data-og-description=&quot;한 교회에서 목사로 재직 항의하는 주민 수사 청원, 3&amp;middot;1절에 일장기를 내걸었던 세종시 주민은 한국인 목사였던 것으로 드러났다. 지난 6일 JTBC 보도에 따르면 세종시 한솔동 한 아파트 자기 집 &quot; data-og-host=&quot;www.seoul.co.kr&quot; data-og-source-url=&quot;https://www.seoul.co.kr/news/newsView.php?id=20230307500008&quot; data-og-url=&quot;https://www.seoul.co.kr/news/newsView.php?id=20230307500008&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/9uoYV/hyRSKpivvd/4GIh74cbuQmairokt2d240/img.jpg?width=666&amp;amp;height=444&amp;amp;face=0_0_666_444,https://scrap.kakaocdn.net/dn/b3Rlsy/hyRSRIJaD2/SbJbkCMA4EmylxkpXtY5nK/img.jpg?width=666&amp;amp;height=444&amp;amp;face=0_0_666_444&quot;&gt;&lt;a href=&quot;https://www.seoul.co.kr/news/newsView.php?id=20230307500008&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.seoul.co.kr/news/newsView.php?id=20230307500008&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/9uoYV/hyRSKpivvd/4GIh74cbuQmairokt2d240/img.jpg?width=666&amp;amp;height=444&amp;amp;face=0_0_666_444,https://scrap.kakaocdn.net/dn/b3Rlsy/hyRSRIJaD2/SbJbkCMA4EmylxkpXtY5nK/img.jpg?width=666&amp;amp;height=444&amp;amp;face=0_0_666_444');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;삼일절 일장기 주민 &amp;lsquo;목사&amp;rsquo;였다&amp;hellip;&amp;ldquo;대일본제국&amp;rdquo; 설교&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;한 교회에서 목사로 재직 항의하는 주민 수사 청원, 3&amp;middot;1절에 일장기를 내걸었던 세종시 주민은 한국인 목사였던 것으로 드러났다. 지난 6일 JTBC 보도에 따르면 세종시 한솔동 한 아파트 자기 집&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.seoul.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 지금 21세기 대학민국의 살고 있는 것이 의심스러운 요즘이다. 대통령부터 시작해서 사회 고위층들이 하는 말을 듣고 있으면 귀를 의심하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 세대간의 갈등이나 이념 갈등이 아니다. 일제 강점기의 잔재를 깨끗하게 정리하지 못한 우리의 과오인가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저 사람들은 일본 사람인지 아니면 한국 사람인지 의심스럽기까지 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 대통령이라는 사람과 그 주변 사람들은 일본에 대해서 우호적인 자세라고도 설명조차 할 수 없는 저자세를 취하며 흡사 일본에서 파견 나온 한국 사람인 척하는 사람들처럼 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대한민국의 대통령은 자국의 이익을 최우선으로 하며 국민의 떠받들어 국민에게 최대의 이익을 줄 수 있도록 하여야 한다. 이건 가장 기본적인 민주주의의 가치라고 생각한다. 비단 역대 대통령 중에 이런 사람이 드물었고 심지어 자기 배속을 채우는데 급급했던 사람들도 있었으리라.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 이건 아니지 않은가. 자기의 이익을 주장하거나 국민의 내팽겨쳐두는 정도가 아니다. 이들은 우리 나라를 일본의 속국 정도로만 생각하는 사람들인 것처럼 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 &quot;보수&quot;라고 쓰고 &quot;친일&quot;이라고 읽어야 하는 시대에 살고 있는 것은 아닌지 의심스럽다. 보통 보수라 칭함은 민족주의라고 하는데 유독 우리의 보수는 민족주의가 아니고 친일주의인 것처럼 보이는 것은 나만의 착각인 것인가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;눈을 의심하고 귀를 의심해야하는 시대에 살고 있는 현실이 슬프기만 하다.&lt;/p&gt;</description>
      <category>기사모음과 생각</category>
      <category>국민의 위한 국가는 존재하나</category>
      <category>친일주의</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/367</guid>
      <comments>https://minarae7.tistory.com/entry/21%EC%84%B8%EA%B8%B0%EC%9D%98-%EB%8C%80%ED%95%9C%EB%AF%BC%EA%B5%AD%EC%9D%98-%EB%AF%BC%EB%82%AF#entry367comment</comments>
      <pubDate>Wed, 8 Mar 2023 14:02:08 +0900</pubDate>
    </item>
    <item>
      <title>도대체 이해할 수 없는 김문수</title>
      <link>https://minarae7.tistory.com/entry/%EB%8F%84%EB%8C%80%EC%B2%B4-%EC%9D%B4%ED%95%B4%ED%95%A0-%EC%88%98-%EC%97%86%EB%8A%94-%EA%B9%80%EB%AC%B8%EC%88%98</link>
      <description>&lt;figure id=&quot;og_1678025170733&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;김문수 &amp;ldquo;감동 받았다&amp;rdquo;는 회사, 무노조&amp;middot;휴대폰 압수&amp;middot;임금 40%&quot; data-og-description=&quot;경사노위 위원장으로 &amp;lsquo;부적절&amp;rsquo; 인식 또 드러내&quot; data-og-host=&quot;www.hani.co.kr&quot; data-og-source-url=&quot;https://www.hani.co.kr/arti/society/labor/1082110.html&quot; data-og-url=&quot;https://www.hani.co.kr/arti/society/labor/1082110.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cQ6WmN/hyRQsJLDoq/lCHp2BwKdoqWaMBgkOwhWK/img.jpg?width=732&amp;amp;height=1280&amp;amp;face=0_0_732_1280,https://scrap.kakaocdn.net/dn/b2jdsG/hyRQhn1ngK/ZoVCeczVqRnBteaoxTjd01/img.jpg?width=732&amp;amp;height=1280&amp;amp;face=0_0_732_1280&quot;&gt;&lt;a href=&quot;https://www.hani.co.kr/arti/society/labor/1082110.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.hani.co.kr/arti/society/labor/1082110.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cQ6WmN/hyRQsJLDoq/lCHp2BwKdoqWaMBgkOwhWK/img.jpg?width=732&amp;amp;height=1280&amp;amp;face=0_0_732_1280,https://scrap.kakaocdn.net/dn/b2jdsG/hyRQhn1ngK/ZoVCeczVqRnBteaoxTjd01/img.jpg?width=732&amp;amp;height=1280&amp;amp;face=0_0_732_1280');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;김문수 &amp;ldquo;감동 받았다&amp;rdquo;는 회사, 무노조&amp;middot;휴대폰 압수&amp;middot;임금 40%&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;경사노위 위원장으로 &amp;lsquo;부적절&amp;rsquo; 인식 또 드러내&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.hani.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 사람 도대체 이해할 수가 없다. 저런 근무 환경이라면 당장이라도 큰 이슈가 되어야 한다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무슨 고등학생들도 아니고 핸드폰을 압수해서 사용할 수 없게 한다니!! 무슨 발상이 이런가..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;급한 연락을 받아야 하는 상황이라면 어쩌라는 건가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 노조가 없다는 것은 자랑이 아니지 않은가. 노조는 헌법에 명시된 노동자의 가장 기본적인 권리이다. 노조가 없다는 것에 감명을 받았다는 것인가.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-03-05 오후 11.11.21.png&quot; data-origin-width=&quot;1652&quot; data-origin-height=&quot;160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Zkyqx/btr2gku4VET/rYooYLeQCJajUgn9mwGVjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Zkyqx/btr2gku4VET/rYooYLeQCJajUgn9mwGVjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Zkyqx/btr2gku4VET/rYooYLeQCJajUgn9mwGVjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZkyqx%2Fbtr2gku4VET%2FrYooYLeQCJajUgn9mwGVjk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1652&quot; height=&quot;160&quot; data-filename=&quot;스크린샷 2023-03-05 오후 11.11.21.png&quot; data-origin-width=&quot;1652&quot; data-origin-height=&quot;160&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노동운동가 출신이라는 사람이 어찌 이럴 수 있단 말인가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무리 변절을 했다고 하지만 지켜야할 선이라는 것이 있는 것이 아닌가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 사람을 볼 때마다 한숨이 절로 나온다. 어찌 이런 사람을 이런 중책에 가져다가 앉혀두는지 이 정부를 도무지 이해할 수가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;친 기업 정부라고 하지만 정도라는 것이 있는 것인데, 대다수의 국민과 등을 돌리는 현실이 화가 난다.&lt;/p&gt;</description>
      <category>기사모음과 생각</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/366</guid>
      <comments>https://minarae7.tistory.com/entry/%EB%8F%84%EB%8C%80%EC%B2%B4-%EC%9D%B4%ED%95%B4%ED%95%A0-%EC%88%98-%EC%97%86%EB%8A%94-%EA%B9%80%EB%AC%B8%EC%88%98#entry366comment</comments>
      <pubDate>Sun, 5 Mar 2023 23:13:46 +0900</pubDate>
    </item>
    <item>
      <title>[가계부 만들기] Backend - Initialize</title>
      <link>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-Backend-Initialize</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Backend 프로그램을 만들기 위해서 우선 프로그램을 초기화해야 한다. 여기서는 Python 기반의&amp;nbsp; fastapi를 웹 프레임워크로 사용할 것이며 ORM을 사용하기 위해서 sqlalchemy 라이브러리를 사용할 것이다.&lt;br&gt;프로젝트는 Backend와 Frontend로 분리해서 개발할 것이지만 어쨌든 같은 디렉토리 안에서 작업하는 것이 개발이 용이할 것이라고 판단하여 account-book 디렉토리를 생성하고 그 하단에 Backend와 Frontend 디렉토리를 생성할 것이다.&lt;br&gt;다음과 명령어를 통해서 프로젝트 디렉토리를 생성한다.&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;% mkdir account-book
% cd account-book
% poetry new backend&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 프로젝트 의존성 관리를 위해서 poetry를 사용하였으며 이에 대한 설명은 아래 포스트에서 작성하였다.&lt;/p&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;[Python] Mac에서 개발 환경 구축&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;이직을 하면서 회사에서 Backend 언어로 Python을 기반으로 fastapi를 사용하게 되었다. Python으로 Object detection을 위한 모델 학습하는 코드는 작성해보았지만 웹서비스를 만들어보기는 처음이다. 예전&quot; data-og-host=&quot;minarae.com&quot; data-og-source-url=&quot;https://minarae7.tistory.com/359&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/MVnCR/hyROLbK6VR/OyHGRaH2MLy9GKQm1qEXIK/img.png?width=800&amp;amp;height=259&amp;amp;face=0_0_800_259,https://scrap.kakaocdn.net/dn/dvpsT1/hyROMV1odF/gjx2kOtJiNkkHUKwlJA4o1/img.png?width=800&amp;amp;height=259&amp;amp;face=0_0_800_259,https://scrap.kakaocdn.net/dn/cwVMCg/hyROWRTiaW/5ctWmyqjeVzo4mnmLDcAY1/img.png?width=1486&amp;amp;height=482&amp;amp;face=0_0_1486_482&quot; data-og-url=&quot;https://minarae.com/359&quot;&gt;&lt;a href=&quot;https://minarae.com/359&quot; target=&quot;_blank&quot; data-source-url=&quot;https://minarae7.tistory.com/359&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/MVnCR/hyROLbK6VR/OyHGRaH2MLy9GKQm1qEXIK/img.png?width=800&amp;amp;height=259&amp;amp;face=0_0_800_259,https://scrap.kakaocdn.net/dn/dvpsT1/hyROMV1odF/gjx2kOtJiNkkHUKwlJA4o1/img.png?width=800&amp;amp;height=259&amp;amp;face=0_0_800_259,https://scrap.kakaocdn.net/dn/cwVMCg/hyROWRTiaW/5ctWmyqjeVzo4mnmLDcAY1/img.png?width=1486&amp;amp;height=482&amp;amp;face=0_0_1486_482')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;[Python] Mac에서 개발 환경 구축&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;이직을 하면서 회사에서 Backend 언어로 Python을 기반으로 fastapi를 사용하게 되었다. Python으로 Object detection을 위한 모델 학습하는 코드는 작성해보았지만 웹서비스를 만들어보기는 처음이다. 예전&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;minarae.com&lt;/p&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;이제 backend 디렉토리로 이동해서 개발에 필요한 라이브러리들을 의존성에 주입한다.&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;% cd backend
% poetry add fastapi sqlalchemy &quot;uvicorn[standard]&quot;&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;우선 fastapi와 이를 실행하기 위한 uvicorn을 설치하고 ORM을 위해서 sqlalchemy를 같이 설치해준다.&lt;br&gt;여기까지 되었으면 backend 디렉토리 하단에서 파일 리스트를 조회해보면 다음과 같이 보인다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;428&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oz4ZU/btr1YLfV6Xt/kvq6qUOwaNtR73XkTQTAS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oz4ZU/btr1YLfV6Xt/kvq6qUOwaNtR73XkTQTAS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oz4ZU/btr1YLfV6Xt/kvq6qUOwaNtR73XkTQTAS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Foz4ZU%2Fbtr1YLfV6Xt%2Fkvq6qUOwaNtR73XkTQTAS0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;244&quot; height=&quot;237&quot; data-origin-width=&quot;428&quot; data-origin-height=&quot;416&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;backend 디렉토리 밑에 backend라는 디렉토리가 다시 생성된다. 여기서는 하단의 backend를 app이라고 이름을 변경한다. 그리고 app 디렉토리 하단에 main.py 파일을 생성하고 다음의 코드를 입력한다.&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;from fastapi import FastAPI

app = FastAPI(title=&quot;account-book-api&quot;)

@app.get(&quot;/&quot;)
async def main():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return {&quot;message&quot;: &quot;Hello World&quot;}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;일단 간단한 프로그램 코드를 입력하여서 프로그램이 정상적으로 작동되는지 확인할 것이다.&lt;br&gt;디렉토리 이름을 변경하고 main.py 파일을 생성하고 나면 아래와 같은 파일 구성이 된다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;356&quot; data-origin-height=&quot;398&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWgEsH/btr1Y8otGi9/4IK1kG8RDJDKckSq8bU1bK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWgEsH/btr1Y8otGi9/4IK1kG8RDJDKckSq8bU1bK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWgEsH/btr1Y8otGi9/4IK1kG8RDJDKckSq8bU1bK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWgEsH%2Fbtr1Y8otGi9%2F4IK1kG8RDJDKckSq8bU1bK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;200&quot; height=&quot;224&quot; data-origin-width=&quot;356&quot; data-origin-height=&quot;398&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 프로그램을 구동할 준비가 되었다. 다음 명령어를 입력해서 프로그램을 구동해보자.&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;% uvicorn app.main:app --reload&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 프로그램을 실행시키면 뒤에 붙은 옵션 reload에 의해서 프로그램 코드가 변경될 때마다 자동으로 코드를 reload 하여서 변경된 코드를 반영해준다.&lt;br&gt;이제 브라우저로 가서 http://127.0.0.1:8000 주소를 입력하면 다음과 같이 프로그램이 구동되는 것을 확인할 수 있다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2334&quot; data-origin-height=&quot;1506&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MW6r3/btr1Vewq6hD/nhS9KPjB2IbkW89aKWjNd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MW6r3/btr1Vewq6hD/nhS9KPjB2IbkW89aKWjNd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MW6r3/btr1Vewq6hD/nhS9KPjB2IbkW89aKWjNd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMW6r3%2Fbtr1Vewq6hD%2FnhS9KPjB2IbkW89aKWjNd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2334&quot; height=&quot;1506&quot; data-origin-width=&quot;2334&quot; data-origin-height=&quot;1506&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 문서에서 확인할 수 있는 것과 같이 http://127.0.0.1:8000/docs 와 http://127.0.0.1:8000/redoc이 동작하는 것도 같이 확인할 수 있다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2334&quot; data-origin-height=&quot;1506&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLwI5l/btr1PNsUFx3/LFJvG5gCFsKuHManGzk9F1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLwI5l/btr1PNsUFx3/LFJvG5gCFsKuHManGzk9F1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLwI5l/btr1PNsUFx3/LFJvG5gCFsKuHManGzk9F1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLwI5l%2Fbtr1PNsUFx3%2FLFJvG5gCFsKuHManGzk9F1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2334&quot; height=&quot;1506&quot; data-origin-width=&quot;2334&quot; data-origin-height=&quot;1506&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2334&quot; data-origin-height=&quot;1506&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E2DsT/btr1YK2qpnn/VZi6TkBlFeX3Rs47OYsje1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E2DsT/btr1YK2qpnn/VZi6TkBlFeX3Rs47OYsje1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E2DsT/btr1YK2qpnn/VZi6TkBlFeX3Rs47OYsje1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE2DsT%2Fbtr1YK2qpnn%2FVZi6TkBlFeX3Rs47OYsje1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2334&quot; height=&quot;1506&quot; data-origin-width=&quot;2334&quot; data-origin-height=&quot;1506&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 해서 프로젝트의 초기 셋팅을 완료하였다.&lt;br&gt;이제 코드를 입력하기 시작할 것이다. 다음 문서에서는 데이터베이스를 연동하고 앞에서 설계한 테이블을 ORM으로 코드와 연결하는 과정을 기술할 것이다.&lt;br&gt;fastapi는 공식 문서가 잘 되어 있어서 공식 문서만 잘 습득하여서 개발을 시작하는데는 무리가 없다.&lt;/p&gt;</description>
      <category>Programming/Project Log</category>
      <category>backend</category>
      <category>FastAPI</category>
      <category>python</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/365</guid>
      <comments>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-Backend-Initialize#entry365comment</comments>
      <pubDate>Sat, 4 Mar 2023 23:32:01 +0900</pubDate>
    </item>
    <item>
      <title>[가계부 만들기] 테이블 설계</title>
      <link>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-%ED%85%8C%EC%9D%B4%EB%B8%94-%EC%84%A4%EA%B3%84</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;엑셀로 가계부를 쓰기 시작했는데 이걸 일일이 쓰려니 상당히 귀찮다. 일일이 손으로 입력하는 것도 귀찮고 생각보다 엑셀이 스마트하지 않고 그래프나 이런 부분이 매번 손으로 입력하는 것이 꽤 귀찮아서 내가 만든 양식을 범용으로 사용할 수 있도록 가계부 프로그램을 만들어볼까 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 Backend 쪽 프롣그램을 먼저 개발을 하고 프론트를 붙이는 방향으로 개발을 진행할 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 언어는 현재 회사에서 사용하고 있는 python의 fastapi를 사용할 것이고 frontend는 React를 사용하며 개인적으로 디자인에 많이 쓰는 bootstrap을 활용할 계획이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발하는 내용들을 여기에 기록을 남기면서 개발을 진행할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엑셀에서 작성한 내용은 다음 그림과 같이 정리하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_스크린샷 2023-02-28 오후 7.38.21.png&quot; data-origin-width=&quot;1814&quot; data-origin-height=&quot;1680&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbk1TK/btr1f1RmhhS/tqwNuKlJLOTM8K2kxJUzN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbk1TK/btr1f1RmhhS/tqwNuKlJLOTM8K2kxJUzN1/img.png&quot; data-alt=&quot;현재 작성중인 가계부 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbk1TK/btr1f1RmhhS/tqwNuKlJLOTM8K2kxJUzN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbbk1TK%2Fbtr1f1RmhhS%2FtqwNuKlJLOTM8K2kxJUzN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;575&quot; height=&quot;533&quot; data-filename=&quot;edited_스크린샷 2023-02-28 오후 7.38.21.png&quot; data-origin-width=&quot;1814&quot; data-origin-height=&quot;1680&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;현재 작성중인 가계부 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 내용을 프로그램으로 구현하기 위해서 먼저 테이블 설계를 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 테이블을 생성한 쿼리이다.&lt;/p&gt;
&lt;pre id=&quot;code_1677568485055&quot; class=&quot;routeros&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE TABLE `tb_members` (
  `member_no` smallint unsigned NOT NULL AUTO_INCREMENT COMMENT '멤버번호',
  `member_id` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '사용자 아이디',
  `member_pw` char(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '사용자 패스워드',
  `member_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '사용자 이름',
  `member_email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '이메일 주소',
  `reg_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성일시',
  `upd_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일시',
  `is_deleted` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'F' COMMENT '삭제여부(T|F)',
  `del_dt` datetime DEFAULT NULL COMMENT '삭제일시',
  PRIMARY KEY (`member_no`),
  UNIQUE KEY `members__member_id` (`member_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

CREATE TABLE `tb_category` (
  `category_no` int unsigned NOT NULL AUTO_INCREMENT COMMENT '카테고리 번호',
  `member_no` smallint unsigned DEFAULT NULL COMMENT '카테고리 생성자 번호(기본 카테고리일 경우 null)',
  `category_name` varchar(50) COLLATE utf8mb4_general_ci NOT NULL COMMENT '카테고리 이름',
  `is_children` char(1) COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'F' COMMENT '자식을 가지고 있는지 여부(T|F)',
  `parent_no` int unsigned DEFAULT NULL COMMENT '부모 카테고리 번호',
  `class_name` varchar(30) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '아이콘 카테고리',
  `is_system` char(1) COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'T' COMMENT '시스템 기본 카테고리',
  `reg_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성일시',
  `upd_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일시',
  `is_deleted` char(1) COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'F' COMMENT '삭제여부(T|F)',
  `del_dt` datetime DEFAULT NULL COMMENT '삭제일시',
  PRIMARY KEY (`category_no`),
  KEY `ixn_category__member_no` (`member_no`),
  CONSTRAINT `fk_category__member_no` FOREIGN KEY (`member_no`) REFERENCES `tb_members` (`member_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

CREATE TABLE `tb_account_log` (
  `account_log_no` int unsigned NOT NULL AUTO_INCREMENT COMMENT '내역번호',
  `member_no` smallint unsigned NOT NULL COMMENT '사용자번호',
  `std_date` date NOT NULL COMMENT '날짜',
  `opponent_name` varchar(150) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '메인거래처',
  `reg_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성일시',
  `upd_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일시',
  `is_deleted` char(1) COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'F' COMMENT '삭제여부(T|F)',
  `del_dt` datetime DEFAULT NULL COMMENT '삭제일시',
  PRIMARY KEY (`account_log_no`),
  KEY `ixn_account_log__member_no` (`member_no`),
  CONSTRAINT `fk_account_log__member_no` FOREIGN KEY (`member_no`) REFERENCES `tb_members` (`member_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

CREATE TABLE `tb_log_detail` (
  `log_detail_no` int unsigned NOT NULL AUTO_INCREMENT COMMENT '사용내역 상세 번호',
  `account_log_no` int unsigned NOT NULL COMMENT '내역번호',
  `detail_contents` varchar(300) COLLATE utf8mb4_general_ci NOT NULL COMMENT '상세내역정보',
  `amounts` int unsigned NOT NULL COMMENT '금액',
  `io_type` char(1) COLLATE utf8mb4_general_ci NOT NULL COMMENT '수입/지출 구분(I: 수입, O: 지출)',
  `category_no` int unsigned DEFAULT NULL COMMENT '카테고리 번호',
  `importance` tinyint unsigned NOT NULL DEFAULT '1' COMMENT '중요도(지출일 경우만)',
  `is_fixed_cost` char(1) COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'F' COMMENT '고정비여부(지출일 경우만, T|F)',
  `reg_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성일시',
  `upd_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일시',
  `is_deleted` char(1) COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'F' COMMENT '삭제여부(T|F)',
  `del_dt` datetime DEFAULT NULL COMMENT '삭제일시',
  PRIMARY KEY (`log_detail_no`),
  KEY `ixn_log_detail__account_log_no` (`account_log_no`),
  KEY `ixn_log_detail__category_no` (`category_no`),
  CONSTRAINT `fk_log_detail__account_log_no` FOREIGN KEY (`account_log_no`) REFERENCES `tb_account_log` (`account_log_no`),
  CONSTRAINT `fk_log_detail__category_no` FOREIGN KEY (`category_no`) REFERENCES `tb_category` (`category_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 구현하면 일단 범용적으로 다수의 사용자가 이용이 가능하다. 각 기록은 사용자에게 할당되기 때문에 다른 사용자의 내용을 확인할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 테이블간의 관계는 다음 이미지의 ERD에서 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-02-28 오후 7.42.39.png&quot; data-origin-width=&quot;1034&quot; data-origin-height=&quot;1028&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgFReI/btr01TUqrSA/LnReHujsOfClJh7NVR2qPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgFReI/btr01TUqrSA/LnReHujsOfClJh7NVR2qPk/img.png&quot; data-alt=&quot;테이블 ERD&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgFReI/btr01TUqrSA/LnReHujsOfClJh7NVR2qPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgFReI%2Fbtr01TUqrSA%2FLnReHujsOfClJh7NVR2qPk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;448&quot; height=&quot;445&quot; data-filename=&quot;스크린샷 2023-02-28 오후 7.42.39.png&quot; data-origin-width=&quot;1034&quot; data-origin-height=&quot;1028&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;테이블 ERD&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 개발을 시작하는 단계이기 때문에 이렇게 테이블을 설계하고 코드를 만들어나갈 계획이다. 코드를 만들어가는 과정에서 필요한 부분이 있다면 테이블을 수정하거나 다른 관계를 갖는 테이블을 추가할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 테이블을 만들었으니 본격적으로 Backend 코드를 개발하기 시작할 것이다. 본격적인 개발은 다음 포스트에서 진행하도록 한다.&lt;/p&gt;</description>
      <category>Programming/Project Log</category>
      <category>가계부</category>
      <category>개발기록</category>
      <category>데이터베이스설계</category>
      <category>프로젝트기록</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/364</guid>
      <comments>https://minarae7.tistory.com/entry/%EA%B0%80%EA%B3%84%EB%B6%80-%EB%A7%8C%EB%93%A4%EA%B8%B0-%ED%85%8C%EC%9D%B4%EB%B8%94-%EC%84%A4%EA%B3%84#entry364comment</comments>
      <pubDate>Tue, 28 Feb 2023 19:46:09 +0900</pubDate>
    </item>
    <item>
      <title>선택적 사법</title>
      <link>https://minarae7.tistory.com/entry/%EC%84%A0%ED%83%9D%EC%A0%81-%EC%82%AC%EB%B2%95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;선택적 사법이라는 말이 선명하게 떠오른다. 지난 주, 그리고 이번 주 사회적 이슈가 될 만한 재판 결과가 쏟아져 나왔다.&lt;/p&gt;
&lt;figure id=&quot;og_1676018700513&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;자녀 입시비리 대부분 &amp;lsquo;유죄&amp;rsquo;&amp;hellip;조국 징역 2년&quot; data-og-description=&quot;자녀 입시비리와 &amp;lsquo;유재수 감찰무마&amp;rsquo; 혐의로 기소된 조국 전 법무부 장관이 1심에서 징역 2년의...&quot; data-og-host=&quot;m.khan.co.kr&quot; data-og-source-url=&quot;https://www.khan.co.kr/national/court-law/article/202302032050005&quot; data-og-url=&quot;https://m.khan.co.kr/article/202302032050005&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fca2O/hyRAfwjnan/jVjcLwT5z1fpRlR8K1J560/img.jpg?width=700&amp;amp;height=507&amp;amp;face=0_0_700_507,https://scrap.kakaocdn.net/dn/ePJ45/hyRzvALSbH/uQaHzw81BZV7T9OMt86AuK/img.jpg?width=700&amp;amp;height=507&amp;amp;face=0_0_700_507,https://scrap.kakaocdn.net/dn/dPHRA4/hyRAlwwQn4/5K8WcSrT5JaxQqyzE9AKjK/img.png?width=540&amp;amp;height=306&amp;amp;face=109_56_439_152&quot;&gt;&lt;a href=&quot;https://www.khan.co.kr/national/court-law/article/202302032050005&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.khan.co.kr/national/court-law/article/202302032050005&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fca2O/hyRAfwjnan/jVjcLwT5z1fpRlR8K1J560/img.jpg?width=700&amp;amp;height=507&amp;amp;face=0_0_700_507,https://scrap.kakaocdn.net/dn/ePJ45/hyRzvALSbH/uQaHzw81BZV7T9OMt86AuK/img.jpg?width=700&amp;amp;height=507&amp;amp;face=0_0_700_507,https://scrap.kakaocdn.net/dn/dPHRA4/hyRAlwwQn4/5K8WcSrT5JaxQqyzE9AKjK/img.png?width=540&amp;amp;height=306&amp;amp;face=109_56_439_152');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;자녀 입시비리 대부분 &amp;lsquo;유죄&amp;rsquo;&amp;hellip;조국 징역 2년&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;자녀 입시비리와 &amp;lsquo;유재수 감찰무마&amp;rsquo; 혐의로 기소된 조국 전 법무부 장관이 1심에서 징역 2년의...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;m.khan.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1676018759642&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;&amp;lsquo;대장동 50억 클럽&amp;rsquo; 곽상도 뇌물 무죄&quot; data-og-description=&quot;곽상도 전 국회의원(사진)이 대장동 민간업자에게 도움을 주고 아들 퇴직금 명목으로 거액의 뇌물...&quot; data-og-host=&quot;m.khan.co.kr&quot; data-og-source-url=&quot;https://www.khan.co.kr/national/court-law/article/202302082059005&quot; data-og-url=&quot;https://m.khan.co.kr/article/202302082059005&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bQHJPM/hyRzyK1JM6/opVasB5bNVBTmS13M5k04k/img.jpg?width=250&amp;amp;height=313&amp;amp;face=28_85_185_256,https://scrap.kakaocdn.net/dn/bvdlTi/hyRAcl3kb2/e9K3tBSr1wkYlkDkB6A5x0/img.jpg?width=250&amp;amp;height=313&amp;amp;face=28_85_185_256,https://scrap.kakaocdn.net/dn/kZjQU/hyRAjla8te/taq44YfvkFdyFrFddZLgJk/img.png?width=540&amp;amp;height=306&amp;amp;face=109_56_439_152&quot;&gt;&lt;a href=&quot;https://www.khan.co.kr/national/court-law/article/202302082059005&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.khan.co.kr/national/court-law/article/202302082059005&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bQHJPM/hyRzyK1JM6/opVasB5bNVBTmS13M5k04k/img.jpg?width=250&amp;amp;height=313&amp;amp;face=28_85_185_256,https://scrap.kakaocdn.net/dn/bvdlTi/hyRAcl3kb2/e9K3tBSr1wkYlkDkB6A5x0/img.jpg?width=250&amp;amp;height=313&amp;amp;face=28_85_185_256,https://scrap.kakaocdn.net/dn/kZjQU/hyRAjla8te/taq44YfvkFdyFrFddZLgJk/img.png?width=540&amp;amp;height=306&amp;amp;face=109_56_439_152');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lsquo;대장동 50억 클럽&amp;rsquo; 곽상도 뇌물 무죄&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;곽상도 전 국회의원(사진)이 대장동 민간업자에게 도움을 주고 아들 퇴직금 명목으로 거액의 뇌물...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;m.khan.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1676018799967&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;&amp;ldquo;곽병채 산재와 김용균 산재는 왜 이렇게 다른가&amp;rdquo;&amp;middot;&amp;middot;&amp;middot;원청 무죄에 시민사회 부글&quot; data-og-description=&quot;&amp;ldquo;곽상도 전 의원의 아들은 이명 산재위로금으로 50억을 받는데 한 어머니는 청년 가장의 무참한...&quot; data-og-host=&quot;m.khan.co.kr&quot; data-og-source-url=&quot;https://www.khan.co.kr/national/national-general/article/202302101711001/?nv=stand&amp;amp;utm_source=naver&amp;amp;utm_medium=portal_news&amp;amp;utm_content=230210&amp;amp;utm_campaign=newsstand_top_imageC&quot; data-og-url=&quot;https://m.khan.co.kr/article/202302101711001&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cms4I3/hyRzCNshi5/3N7K0AsEQ0AXldixTNNulK/img.jpg?width=700&amp;amp;height=480&amp;amp;face=247_106_344_212,https://scrap.kakaocdn.net/dn/berMFa/hyRzCUdFlo/D6iozkYdZmb4DzQh5GJhoK/img.jpg?width=700&amp;amp;height=480&amp;amp;face=247_106_344_212,https://scrap.kakaocdn.net/dn/r3hEM/hyRAngOm5s/lZ1sfeQaSXnD5aYTYoD8D0/img.jpg?width=700&amp;amp;height=480&amp;amp;face=247_106_344_212&quot;&gt;&lt;a href=&quot;https://www.khan.co.kr/national/national-general/article/202302101711001/?nv=stand&amp;amp;utm_source=naver&amp;amp;utm_medium=portal_news&amp;amp;utm_content=230210&amp;amp;utm_campaign=newsstand_top_imageC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.khan.co.kr/national/national-general/article/202302101711001/?nv=stand&amp;amp;utm_source=naver&amp;amp;utm_medium=portal_news&amp;amp;utm_content=230210&amp;amp;utm_campaign=newsstand_top_imageC&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cms4I3/hyRzCNshi5/3N7K0AsEQ0AXldixTNNulK/img.jpg?width=700&amp;amp;height=480&amp;amp;face=247_106_344_212,https://scrap.kakaocdn.net/dn/berMFa/hyRzCUdFlo/D6iozkYdZmb4DzQh5GJhoK/img.jpg?width=700&amp;amp;height=480&amp;amp;face=247_106_344_212,https://scrap.kakaocdn.net/dn/r3hEM/hyRAngOm5s/lZ1sfeQaSXnD5aYTYoD8D0/img.jpg?width=700&amp;amp;height=480&amp;amp;face=247_106_344_212');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;곽병채 산재와 김용균 산재는 왜 이렇게 다른가&amp;rdquo;&amp;middot;&amp;middot;&amp;middot;원청 무죄에 시민사회 부글&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;곽상도 전 의원의 아들은 이명 산재위로금으로 50억을 받는데 한 어머니는 청년 가장의 무참한...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;m.khan.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1676018827175&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;윤미향 의원 &amp;lsquo;정의연 후원금 횡령 의혹&amp;rsquo; 1심서 벌금 1500만원&amp;middot;&amp;middot;&amp;middot;횡령 외 나머지 무죄&quot; data-og-description=&quot;정의기억연대(옛 한국정신대문제대책협의회) 후원금을 유용한 혐의 등으로 기소된 윤미향 무소속 의...&quot; data-og-host=&quot;m.khan.co.kr&quot; data-og-source-url=&quot;https://www.khan.co.kr/national/national-general/article/202302101500001&quot; data-og-url=&quot;https://m.khan.co.kr/article/202302101500001&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/nhEnF/hyRApZZ5X3/JkTRtjEutHciYnET5B3Cyk/img.jpg?width=700&amp;amp;height=439&amp;amp;face=284_76_354_152,https://scrap.kakaocdn.net/dn/pvolA/hyRAhATE5w/heCdXP8m2AiK7ghpn6XSk1/img.jpg?width=700&amp;amp;height=439&amp;amp;face=284_76_354_152,https://scrap.kakaocdn.net/dn/iHZWF/hyRzC0XxmJ/RekEDiQdZEPk6E160fBK2k/img.png?width=540&amp;amp;height=306&amp;amp;face=109_56_439_152&quot;&gt;&lt;a href=&quot;https://www.khan.co.kr/national/national-general/article/202302101500001&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.khan.co.kr/national/national-general/article/202302101500001&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/nhEnF/hyRApZZ5X3/JkTRtjEutHciYnET5B3Cyk/img.jpg?width=700&amp;amp;height=439&amp;amp;face=284_76_354_152,https://scrap.kakaocdn.net/dn/pvolA/hyRAhATE5w/heCdXP8m2AiK7ghpn6XSk1/img.jpg?width=700&amp;amp;height=439&amp;amp;face=284_76_354_152,https://scrap.kakaocdn.net/dn/iHZWF/hyRzC0XxmJ/RekEDiQdZEPk6E160fBK2k/img.png?width=540&amp;amp;height=306&amp;amp;face=109_56_439_152');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;윤미향 의원 &amp;lsquo;정의연 후원금 횡령 의혹&amp;rsquo; 1심서 벌금 1500만원&amp;middot;&amp;middot;&amp;middot;횡령 외 나머지 무죄&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;정의기억연대(옛 한국정신대문제대책협의회) 후원금을 유용한 혐의 등으로 기소된 윤미향 무소속 의...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;m.khan.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1676018907527&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;김건희 연루 의혹 도이치모터스 주가조작 권오수 집행유예&quot; data-og-description=&quot;징역 2년&amp;middot;집행유예 3년 선고&amp;hellip;벌금 3억원도&quot; data-og-host=&quot;www.hani.co.kr&quot; data-og-source-url=&quot;https://www.hani.co.kr/arti/society/society_general/1079167.html&quot; data-og-url=&quot;https://www.hani.co.kr/arti/society/society_general/1079167.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dWSXQU/hyRAn2bvwO/BVRFsptGyALoGT4UxvrI10/img.jpg?width=970&amp;amp;height=660&amp;amp;face=133_75_593_287,https://scrap.kakaocdn.net/dn/3P5Oj/hyRzDlhKZW/AjQFcbpFkNSBusAMKGEaEK/img.jpg?width=970&amp;amp;height=660&amp;amp;face=133_75_593_287&quot;&gt;&lt;a href=&quot;https://www.hani.co.kr/arti/society/society_general/1079167.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.hani.co.kr/arti/society/society_general/1079167.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dWSXQU/hyRAn2bvwO/BVRFsptGyALoGT4UxvrI10/img.jpg?width=970&amp;amp;height=660&amp;amp;face=133_75_593_287,https://scrap.kakaocdn.net/dn/3P5Oj/hyRzDlhKZW/AjQFcbpFkNSBusAMKGEaEK/img.jpg?width=970&amp;amp;height=660&amp;amp;face=133_75_593_287');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;김건희 연루 의혹 도이치모터스 주가조작 권오수 집행유예&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;징역 2년&amp;middot;집행유예 3년 선고&amp;hellip;벌금 3억원도&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.hani.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얼마 전 와이프랑 나눈 대화에서 정치인들은 가족까지 괴롭다는 와이프의 말에 과연 그런가 천천히 생각해보았다. 이 말이 맞는 말일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론은 한 쪽으로만 그렇다는 생각에 들었다. 한 쪽 편, 더군다나 검사 출신이라면 더더욱 편향적인 것이 우리 사회인 것으로 판단된다. 쏟아져 나온 결과를 보더라도 한 쪽으로는 더없이 너그럽지만 다른 한 편으로는 가족까지 탈탈 털어서 사회적 문제로 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 정작 판결 결과가 나오면 나몰라라 하는 것이 정치판인 것인가. 언론도 마찬가지라고 생각한다. 한 쪽으로만 엄정한 잦대를 들이밀고 다른 한 쪽으로는 한없이 너그럽다. 판결 결과를 보도하는 자세 또한 편향적이다. 대부분이 무죄가 나오고 일부에서 유죄가 나오면 유죄가 나온 쪽으로만 보도한다. 정작 수사하고 기소하면서 한참 떠들어대던 부분에 대해서는 무죄가 나오고 한쪽으로 일부가 유죄가 나오면 유죄가 나온 쪽으로 두각시켜서 보도한다. 매우 편향적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가진 자의 사법과 못 가진 자의 사법은 이렇게 다른 것이다. 똑같은 산재인데 누군가는 목숨을 잃어도 책임지는 사람이 없는데 누군가는 산재인데 골프치러 다니고 페러리 끌고 다니는데 퇴직금으로 50억을 받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누군가는 아이의 출석 여부를 잘못 기재했다고 그리고 온라인 시험에서 틀린 답을 가르쳐줬다고 징역을 선고받지만 아들이 퇴직금으로 50억을 받아도 독립 경제라고 무죄를 받고, 누군가는 검사의 아내라고 조사조차 받지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 정의로운 사회인지 묻고 싶다. 봉사활동 시간을 잘못 기재했다고 징역을 살아야 한다면 대한민국에서 몇이나 떳떳할까.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2023년 대한민국을 살고 있는 자식을 가진 부모로, 아이들에게 창피하고 이 부도덕하고 비상식적인 사회가 너무 부끄럽다.&lt;/p&gt;</description>
      <category>기사모음과 생각</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/363</guid>
      <comments>https://minarae7.tistory.com/entry/%EC%84%A0%ED%83%9D%EC%A0%81-%EC%82%AC%EB%B2%95#entry363comment</comments>
      <pubDate>Fri, 10 Feb 2023 17:58:08 +0900</pubDate>
    </item>
    <item>
      <title>[Nextjs] API 기능을 통한 파일 업로드 처리</title>
      <link>https://minarae7.tistory.com/entry/Nextjs-API-%EA%B8%B0%EB%8A%A5%EC%9D%84-%ED%86%B5%ED%95%9C-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C-%EC%B2%98%EB%A6%AC</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;nextjs_logo.png&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;571&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dxPlW6/btrYIxsuwXQ/hR1VmdRLRwbo5gvuENiPVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dxPlW6/btrYIxsuwXQ/hR1VmdRLRwbo5gvuENiPVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dxPlW6/btrYIxsuwXQ/hR1VmdRLRwbo5gvuENiPVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdxPlW6%2FbtrYIxsuwXQ%2FhR1VmdRLRwbo5gvuENiPVk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;571&quot; data-filename=&quot;nextjs_logo.png&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;571&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 Frontend 개발시에 nextjs를 사용하고 있다. 지금까지 React + nextjs를 공부하고 개발하면서 여러 고비를 넘기면서 개발을 이어왔는데 다시 한 가지 난제에 부딪히게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nextjs는 SSR(Server Side Rendering)을 지원해주는 프레임워크이지만 어쨌든 이미 로딩된 페이지 안에서의 동작은 브라우저에서 처리되기 때문에 비동기로 호출되는 데이터에 대한 정보는 서버에서는 잡을 수가 없다. 그래서 Nextjs에서는 Page 디렉토리 안에 api로 된 페이지에 대해서는 특별히 서버에서 직접 처리되도록 하여서 서버에서 처리할 수 있도록 기능을 제공한다. 자세한 내용은 아래 nextjs 문서에서 확인할 수 있다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;figure id=&quot;og_1675951026968&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;API Routes: Introduction | Next.js&quot; data-og-description=&quot;Next.js supports API Routes, which allow you to build your API without leaving your Next.js app. Learn how it works here.&quot; data-og-host=&quot;nextjs.org&quot; data-og-source-url=&quot;https://nextjs.org/docs/api-routes/introduction&quot; data-og-url=&quot;https://nextjs.org/docs/api-routes/introduction&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/oCxEh/hyRzuAVVYC/I38JbzVTwKkAlk1KXWh3ok/img.png?width=1686&amp;amp;height=882&amp;amp;face=0_0_1686_882&quot;&gt;&lt;a href=&quot;https://nextjs.org/docs/api-routes/introduction&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nextjs.org/docs/api-routes/introduction&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/oCxEh/hyRzuAVVYC/I38JbzVTwKkAlk1KXWh3ok/img.png?width=1686&amp;amp;height=882&amp;amp;face=0_0_1686_882');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;API Routes: Introduction | Next.js&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Next.js supports API Routes, which allow you to build your API without leaving your Next.js app. Learn how it works here.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;nextjs.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 개발할 때는 모든 비동기 api에 대해서 nextjs의 api-route를 이용해서 request와 response에 대한 값을 로그로 남기기 위해서 사용하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 여기서 문제가 발생했다. POST나 PUT일 경우 지금까지는 http-method를 통해서 데이터를 잡았다. 이 때 request body는 json으로 처리하도록 하였다. 이렇게 처리했기 때문에 지금까지는 별 문제없이 처리가 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 발생한 문제는 파일 업로드를 해야한다는 것이다. 파일 전송시에는 POST에서도 content-type를 따로 지정해서 보내야하기 때문에 그대로 json을 사용할 수 없고 Form 데이터로 처리해야 하는데 이게 잘 안되었다. 수없는 삽질 끝에 문제의 원인을 찾아냈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저에서 데이터를 보낼 때 FormData로 묶어서 정상적으로 전송이 되는데 Backend에 오면 데이터가 이상하게 잡히는 것을 확인하였다. 분명히 데이터 자체에서는 문제가 안 보이는데 Fastapi api에서 처리가 안되는 것을 확인했다. 어떤 문제인지 수없이 테스트해본 결과 문제가 되는 구간이 api-route를 거치면서 form 데이터가 손상되는 것을 확인하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위한 해결책을 찾다가 좋은 솔루션을 찾아서 기록을 위해서 여기에 남겨두도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 해결을 위해서 다음과 같이 코드를 작성하였다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1675952513165&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import formidable from &quot;formidable&quot;;
import FormData from &quot;form-data&quot;;
import fs from &quot;fs&quot;;
import axios from &quot;axios&quot;;

export const config = {
  api: {
    bodyParser: false,
  }
};

export default async function (req, res) {
  const url = &quot;http://test.co.kr&quot;;
  try {
    if (req.method === &quot;POST&quot;) {
      // file 업로드 처리
      const {fileData, fields} = await new Promise((resolve, reject) {
        const form = new formidable.IncomingForm({
          maxFiles: 5 * 1024 * 1024,  // 최대 파일 크기 지정
          keepExtension: true,
        });
        
        form.parse(req, (error, fields, files) =&amp;gt; {
          if (error) return reject(error);
          return resolve({
            fileData: files,
            fields: fields,
          });
        });
      });
      
      const formData = new FormData();
      const image = fileData.image; // 업로드된 파일의 post 항목의 이름이 image 임.
      const readStream = fs.createReadStream(image.filepath);  // 업로드된 파일을 버퍼로 읽음
      
      formData.append(&quot;image&quot;, readStream); // formData에 읽은 파일을 추가함
      for (const key in fields) {
        // 다른 필드가 있으면 역시 formData에 추가
        formData.append(key, fields[key];
      }
      
      // 데이터를 처리할 서버로 데이터를 다시 전달
      const result = await axios.post(url, formData);
      res.status(200).json(result);
    } else {
      // 지원하지 않는 http method에 대한 예외처리
      res.statue(200).json({
        result: false,
        message: `Do not support to ${req.method}`;
      });
    }
  } catch (error) {
    res
      .status(error.response?error.response.status : 500)
      .send(error.response ? error.response.data : error.message);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adsense responsive&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;반응형&lt;/div&gt;
    &lt;script src=&quot;//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
    &lt;ins class=&quot;adsbygoogle&quot; style=&quot;display: block;&quot; data-ad-host=&quot;ca-host-pub-9691043933427338&quot; data-ad-client=&quot;ca-pub-5642455207890069&quot; data-ad-format=&quot;auto&quot;&gt;&lt;/ins&gt;
    &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대충 해결책은 api-route 페이지에서 FormData를 새로 생성하고 이 FormData를 백엔드 서버로 전송하도록 하는 것이다. 여기서 핵심은 두 가지이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. bodyParser를 false로 지정하는 것이다. 이렇게 함으로 nextjs 자체에서 request body에 대한 처리를 하는 것을 막는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 새로운 FormData를 생성하기 위해서 formidable 라이브러리를 통해서 request body를 처리하여서 파일 부분과 다른 필드들을 분리해서 변수에 담는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nextjs에서 사용하는 bodyParser 옵션 및 다른 옵션에 대해서 아래 페이지에서 확인할 수 있다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;figure id=&quot;og_1675952794195&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;API Routes: Request Helpers | Next.js&quot; data-og-description=&quot;API&amp;nbsp;Routes&amp;nbsp;provide built-in request helpers that parse the incoming request. Learn more about them here.&quot; data-og-host=&quot;nextjs.org&quot; data-og-source-url=&quot;https://nextjs.org/docs/api-routes/request-helpers&quot; data-og-url=&quot;https://nextjs.org/docs/api-routes/request-helpers&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/gExhl/hyRzoAK8Tq/KaU2U3EYoRJ2Oo5DKkUS61/img.png?width=1686&amp;amp;height=882&amp;amp;face=0_0_1686_882&quot;&gt;&lt;a href=&quot;https://nextjs.org/docs/api-routes/request-helpers&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nextjs.org/docs/api-routes/request-helpers&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/gExhl/hyRzoAK8Tq/KaU2U3EYoRJ2Oo5DKkUS61/img.png?width=1686&amp;amp;height=882&amp;amp;face=0_0_1686_882');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;API Routes: Request Helpers | Next.js&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;API&amp;nbsp;Routes&amp;nbsp;provide built-in request helpers that parse the incoming request. Learn more about them here.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;nextjs.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 처리하면 nextjs의 api-route를 거쳐가면서도 파일의 손상없이 정상적으로 Backend 서버로 전송되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 코드는 아래 사이트를 참조하였으며 실제 사용하는 코드를 수정해서 내부 라이브러리를 호출하는 부분을 제외하고 작성한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드의 오류 또는 문의 사항은 댓글로 남겨주시면 처리하도록 하겠습니다.&lt;/p&gt;
&lt;div class=&quot;revenue_unit_wrap&quot;&gt;
  &lt;div class=&quot;revenue_unit_item adfit&quot;&gt;
    &lt;div class=&quot;revenue_unit_info&quot;&gt;728x90&lt;/div&gt;
    &lt;ins class=&quot;kakao_ad_area&quot; style=&quot;display: none;&quot; data-ad-unit=&quot;DAN-gDlawjFowAOofwKh&quot; data-ad-width=&quot;728px&quot; data-ad-height=&quot;90px&quot;&gt;&lt;/ins&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;//t1.daumcdn.net/kas/static/ba.min.js&quot; async=&quot;async&quot;&gt;&lt;/script&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;figure id=&quot;og_1675952907930&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;폴시랩&quot; data-og-description=&quot;자바스트립트, PHP, 워드프레스, HTML, CSS, 팁, 가이드, 일상이야기.&quot; data-og-host=&quot;falsy.me&quot; data-og-source-url=&quot;https://falsy.me/nextjs-api-routes%EB%A5%BC-%ED%86%B5%ED%95%B4-api-%EC%84%9C%EB%B2%84%EB%A1%9C-%ED%8C%8C%EC%9D%BC-%EB%B3%B4%EB%82%B4%EA%B8%B0/&quot; data-og-url=&quot;https://falsy.me&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kDfxq/hyRzqehsVe/BlX6ukP1BK4J5JBZ1KhHZK/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/pNrHA/hyRzzWA3gc/pZurwcLIiRxCjkuaqIgyK0/img.png?width=540&amp;amp;height=320&amp;amp;face=0_0_540_320,https://scrap.kakaocdn.net/dn/HTYQL/hyRzBtlIxv/1gAuyHhVVTOLznJtoK3Ll1/img.png?width=540&amp;amp;height=320&amp;amp;face=0_0_540_320&quot;&gt;&lt;a href=&quot;https://falsy.me/nextjs-api-routes%EB%A5%BC-%ED%86%B5%ED%95%B4-api-%EC%84%9C%EB%B2%84%EB%A1%9C-%ED%8C%8C%EC%9D%BC-%EB%B3%B4%EB%82%B4%EA%B8%B0/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://falsy.me/nextjs-api-routes%EB%A5%BC-%ED%86%B5%ED%95%B4-api-%EC%84%9C%EB%B2%84%EB%A1%9C-%ED%8C%8C%EC%9D%BC-%EB%B3%B4%EB%82%B4%EA%B8%B0/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kDfxq/hyRzqehsVe/BlX6ukP1BK4J5JBZ1KhHZK/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/pNrHA/hyRzzWA3gc/pZurwcLIiRxCjkuaqIgyK0/img.png?width=540&amp;amp;height=320&amp;amp;face=0_0_540_320,https://scrap.kakaocdn.net/dn/HTYQL/hyRzBtlIxv/1gAuyHhVVTOLznJtoK3Ll1/img.png?width=540&amp;amp;height=320&amp;amp;face=0_0_540_320');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;폴시랩&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;자바스트립트, PHP, 워드프레스, HTML, CSS, 팁, 가이드, 일상이야기.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;falsy.me&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming/Nextjs</category>
      <category>javascript</category>
      <category>next.js</category>
      <category>nextjs</category>
      <category>nextjs api</category>
      <category>개발가이드</category>
      <category>개발팀</category>
      <category>파일업로드</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/360</guid>
      <comments>https://minarae7.tistory.com/entry/Nextjs-API-%EA%B8%B0%EB%8A%A5%EC%9D%84-%ED%86%B5%ED%95%9C-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C-%EC%B2%98%EB%A6%AC#entry360comment</comments>
      <pubDate>Thu, 9 Feb 2023 23:31:45 +0900</pubDate>
    </item>
    <item>
      <title>조국의 600만원과 곽상도의 50억원</title>
      <link>https://minarae7.tistory.com/entry/%EC%A1%B0%EA%B5%AD%EC%9D%98-600%EB%A7%8C%EC%9B%90%EA%B3%BC-%EA%B3%BD%EC%83%81%EB%8F%84%EC%9D%98-50%EC%96%B5%EC%9B%90</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오늘 곽상도 전 의원의 1심 판결이 나왔다는데 너무 어이가 없고 허탈감이 생겨서 기록두고자 한다.&lt;/p&gt;
&lt;figure id=&quot;og_1675862921026&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;곽상도, '아들 화천대유 50억' 뇌물 1심 무죄(종합) | 연합뉴스&quot; data-og-description=&quot;(서울=연합뉴스) 황재하 이영섭 기자 = 곽상도 전 국회의원이 '대장동 일당'에게서 아들의 퇴직금과 성과금 명목으로 뇌물을 수수한 혐의에 대해 ...&quot; data-og-host=&quot;www.yna.co.kr&quot; data-og-source-url=&quot;https://www.yna.co.kr/view/AKR20230208102752004?input=1195m&quot; data-og-url=&quot;https://www.yna.co.kr/view/AKR20230208102752004&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c1smC2/hyRxY3Trpf/lAq9EvAfuThm30Fs7C1YEK/img.jpg?width=1024&amp;amp;height=685&amp;amp;face=388_184_612_428,https://scrap.kakaocdn.net/dn/fjJp7/hyRxWydlt4/MI7aH1PLjpBzSo7fY8zhn0/img.jpg?width=1024&amp;amp;height=685&amp;amp;face=388_184_612_428,https://scrap.kakaocdn.net/dn/bE2kZ0/hyRxUtDpRn/0E3jITw9FsZuw1YQPlat1k/img.jpg?width=1024&amp;amp;height=724&amp;amp;face=152_118_548_273&quot;&gt;&lt;a href=&quot;https://www.yna.co.kr/view/AKR20230208102752004?input=1195m&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.yna.co.kr/view/AKR20230208102752004?input=1195m&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c1smC2/hyRxY3Trpf/lAq9EvAfuThm30Fs7C1YEK/img.jpg?width=1024&amp;amp;height=685&amp;amp;face=388_184_612_428,https://scrap.kakaocdn.net/dn/fjJp7/hyRxWydlt4/MI7aH1PLjpBzSo7fY8zhn0/img.jpg?width=1024&amp;amp;height=685&amp;amp;face=388_184_612_428,https://scrap.kakaocdn.net/dn/bE2kZ0/hyRxUtDpRn/0E3jITw9FsZuw1YQPlat1k/img.jpg?width=1024&amp;amp;height=724&amp;amp;face=152_118_548_273');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;곽상도, '아들 화천대유 50억' 뇌물 1심 무죄(종합) | 연합뉴스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;(서울=연합뉴스) 황재하 이영섭 기자 = 곽상도 전 국회의원이 '대장동 일당'에게서 아들의 퇴직금과 성과금 명목으로 뇌물을 수수한 혐의에 대해 ...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.yna.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1675862943860&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;조국은 유죄, 곽상도는 무죄&amp;hellip;판결 다른 이유는 &amp;lsquo;경제적 독립&amp;rsquo;&quot; data-og-description=&quot;곽상도 아들, 화천대유서 퇴직금 등으로 50억원 받아 법원 &amp;ldquo;아들, 독립 생계 유지&amp;hellip;父가 받았다고 볼 수 없어&amp;rdquo;, 곽상도 전 국회의원의 아들이 &amp;lsquo;대장동 의혹&amp;rsquo;의 핵심인 화천대유자산관리(화&quot; data-og-host=&quot;www.seoul.co.kr&quot; data-og-source-url=&quot;https://www.seoul.co.kr/news/newsView.php?id=20230208500185&quot; data-og-url=&quot;https://www.seoul.co.kr/news/newsView.php?id=20230208500185&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bco91j/hyRzA1AA1C/hMWv01F9t1ozWA0OXQbTk1/img.jpg?width=1000&amp;amp;height=667&amp;amp;face=104_157_878_473,https://scrap.kakaocdn.net/dn/cIWkDD/hyRzw5XcYP/f69bqHkzdeop2BaRiaiIQk/img.jpg?width=1000&amp;amp;height=667&amp;amp;face=104_157_878_473&quot;&gt;&lt;a href=&quot;https://www.seoul.co.kr/news/newsView.php?id=20230208500185&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.seoul.co.kr/news/newsView.php?id=20230208500185&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bco91j/hyRzA1AA1C/hMWv01F9t1ozWA0OXQbTk1/img.jpg?width=1000&amp;amp;height=667&amp;amp;face=104_157_878_473,https://scrap.kakaocdn.net/dn/cIWkDD/hyRzw5XcYP/f69bqHkzdeop2BaRiaiIQk/img.jpg?width=1000&amp;amp;height=667&amp;amp;face=104_157_878_473');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;조국은 유죄, 곽상도는 무죄&amp;hellip;판결 다른 이유는 &amp;lsquo;경제적 독립&amp;rsquo;&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;곽상도 아들, 화천대유서 퇴직금 등으로 50억원 받아 법원 &amp;ldquo;아들, 독립 생계 유지&amp;hellip;父가 받았다고 볼 수 없어&amp;rdquo;, 곽상도 전 국회의원의 아들이 &amp;lsquo;대장동 의혹&amp;rsquo;의 핵심인 화천대유자산관리(화&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.seoul.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조국의 딸인 받은 장학금 명목의 600만원은 김영란법 위반이라며 징역형을 때린 사법부는 곽상도의 아들이 퇴직금 명목으로 받은 50억은 상관없다며 무죄를 내렸다. 과연 이 판결에 납득이 가는 사람이 얼마나 있을까. 이 판결들로 이제부터 공직자들은 아무리 집안이 찢어지게 가난해도 학교나 사회에서 주는 장학금은 받으면 안된다. 판례가 있으니 김영란법 위반이 될 소지가 다분하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;법원에서 판결을 내렸으니 이왕에 이렇게 된거 판사 검사 국회의원들 모두 자녀들이 혹시 장학금을 받은건 없는지 전수조사해야 한다고 본다. 이런 판결을 내렸으니 똑같은 잦대로 모두를 확인해봐야 한다고 본다. 그리고 이제부터 인사청문회에 올라오는 후보들도 동일한 기준으로 판단을 해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;돈을 너무 정직하게 받았기 때문에 위법인건가 하는 생각도 든다. 이왕 받을거면 저쪽 애들처럼 떳떳하게 받고 금액도 몇 백만원 이런거 아니고 몇 십억씩 받아줘야 무죄가 되는건가보다. 그리고 이왕이면 판검사 출신이면 이건 뭐 그냥 재판을 하나마나 어차피 무죄일테니 기소도 하지 말아야 한다. 어차피 무죄가 나올거 뭐하러 에너지 낭비를 하는가. 국민 세금으로 월급 주는데 이렇게 하면 인원이라도 줄일 수 있을테니 세금이라도 아낄 수 있지 않을까.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;너무 어이가 없고 허탈감이 든다. 아무리 일은 잘 한다고 어떤 미친 회사에서 기껏 대리급에게 퇴직금을 50억씩 주는가. 나도 일 하나는 기똥차게 잘 하는데 그럼 나도 퇴직금 50억 받을 수 있는건가? 퇴직금은 법적으로 최소한 이만큼 줘야한다는 기준이 있고 회사에서는 딱 그 기준에 맞게 지급한다. 내가 지금까지 이직을 수차례 하면서 이렇게 퇴직금을 퍼준 경우는 본 적이 없다. 아무리 사측과 관계가 좋고 어쩌고 해도 노동자에게는 돈을 적게 주기 위해서 노력하는게 회사라는 조직이다. 근데 산재로 아팠기 때문에 50억을 퇴직금 줬다? 그렇게 일을 잘 했으면 대리가 아니고 부장이나 이사를 시켰어야지. 그리고 산재로 아프면 그냥 병원비 준다. 니가 우리 회사에서 아팠으니까 퇴직금이라도 많이 줄게 이런 회사는 단 한번도 본적이 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 금액은 통념적으로 많은 금액 정도가 아니다. 보통 사람들은 로또 1등이 당첨되어서 한번 보기 힘든 큰 금액이다. 이런 금액을 대가성 없이 준다는건 도무지 이해가 안간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뇌물이 아니면 그 검사들이 좋아하는 제 3자 뇌물자라도 걸어야 하는거 아닌가? 곽상도가 편의를 봐주고 돈을 주므로 곽상도는 자기 돈 안들이고 아들에게 증여를 한 셈이지 않은가? 이렇게 바라볼 수 있을거라는 생각도 든다. 기껏 대리급에게 퇴직금 + 산재 위로금 + 성과금으로 50억 주는 회사가 있으면 좀 소개시켜달라. 나두 가서 빡세게 1년 굴르고 50억 좀 받아보자.&lt;/p&gt;</description>
      <category>기사모음과 생각</category>
      <category>곽상도</category>
      <category>그퇴직금나도받아보자</category>
      <category>기왕받으려면크게받자</category>
      <category>장학금</category>
      <category>조국</category>
      <category>퇴직금</category>
      <author>minarae7</author>
      <guid isPermaLink="true">https://minarae7.tistory.com/362</guid>
      <comments>https://minarae7.tistory.com/entry/%EC%A1%B0%EA%B5%AD%EC%9D%98-600%EB%A7%8C%EC%9B%90%EA%B3%BC-%EA%B3%BD%EC%83%81%EB%8F%84%EC%9D%98-50%EC%96%B5%EC%9B%90#entry362comment</comments>
      <pubDate>Wed, 8 Feb 2023 22:44:13 +0900</pubDate>
    </item>
  </channel>
</rss>