<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Sangmun</title>
    <link>https://bitrader.tistory.com/</link>
    <description>Deep learning &amp;amp; NLP</description>
    <language>ko</language>
    <pubDate>Thu, 18 Jun 2026 05:30:42 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>상상2</managingEditor>
    <image>
      <title>Sangmun</title>
      <url>https://tistory1.daumcdn.net/tistory/5051067/attach/f206058a4d3e4b4e82cfc0734b3c2a67</url>
      <link>https://bitrader.tistory.com</link>
    </image>
    <item>
      <title>install chrome in linux</title>
      <link>https://bitrader.tistory.com/875</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.lesstif.com/lpt/linux-chrome-106857342.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.lesstif.com/lpt/linux-chrome-106857342.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1713662114006&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;Linux 에 크롬(Chrome) 브라우저 설치하기&quot; data-og-description=&quot;&quot; data-og-host=&quot;www.lesstif.com&quot; data-og-source-url=&quot;https://www.lesstif.com/lpt/linux-chrome-106857342.html&quot; data-og-url=&quot;https://www.lesstif.com/lpt/linux-chrome-106857342.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.lesstif.com/lpt/linux-chrome-106857342.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.lesstif.com/lpt/linux-chrome-106857342.html&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;Linux 에 크롬(Chrome) 브라우저 설치하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.lesstif.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://medium.com/@arslanshah_80326/install-chrome-specific-version-on-ubuntu-d8989cc62414&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://medium.com/@arslanshah_80326/install-chrome-specific-version-on-ubuntu-d8989cc62414&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/Linux</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/875</guid>
      <comments>https://bitrader.tistory.com/875#entry875comment</comments>
      <pubDate>Sun, 21 Apr 2024 10:15:18 +0900</pubDate>
    </item>
    <item>
      <title>Go stack 자료구조</title>
      <link>https://bitrader.tistory.com/865</link>
      <description>&lt;pre id=&quot;code_1710766346265&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package main

import (&quot;fmt&quot;)

type Stack struct {
	data []int
}

func (s *Stack) Push(x int) {
	s.data = append(s.data, x)
}

func (s *Stack) Pop() int {
	if len(s.data) == 0 {
		return -1
	}
	x := s.data[len(s.data)-1]
	s.data = s.data[:len(s.data)-1]
	return x
}

func (s *Stack) Size() int {
	return len(s.data)
}

func (s *Stack) IsEmpty() int {
	if len(s.data) == 0 {
		return 1
	}
	return 0
}

func (s *Stack) Top() int {
	if len(s.data) == 0 {
		return -1
	}
	return s.data[len(s.data)-1]
}

func main() {

	stack := Stack{}
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>개발/Go</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/865</guid>
      <comments>https://bitrader.tistory.com/865#entry865comment</comments>
      <pubDate>Mon, 18 Mar 2024 21:52:33 +0900</pubDate>
    </item>
    <item>
      <title>리눅스 프로세스 관리 명령어(Linux process management command)</title>
      <link>https://bitrader.tistory.com/837</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.linuxteck.com/linux-process-management-command-cheat-sheet/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.linuxteck.com/linux-process-management-command-cheat-sheet/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1706687615736&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;Linux Process Management Command Cheat Sheet | LinuxTeck&quot; data-og-description=&quot;Process management commands are utilized on a Linux system to oversee running processes. These commands permit users to examine details on active processes,&quot; data-og-host=&quot;www.linuxteck.com&quot; data-og-source-url=&quot;https://www.linuxteck.com/linux-process-management-command-cheat-sheet/&quot; data-og-url=&quot;https://www.linuxteck.com/linux-process-management-command-cheat-sheet/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/7xY9w/hyVbZZpGI2/Ui5uIk0B8LjZhrAOeOP8bK/img.png?width=734&amp;amp;height=455&amp;amp;face=0_0_734_455,https://scrap.kakaocdn.net/dn/cOnk1V/hyVf54IjLT/91fHm151Jrsr8DBlOkov1K/img.png?width=734&amp;amp;height=455&amp;amp;face=0_0_734_455,https://scrap.kakaocdn.net/dn/0fzMY/hyVgcbHlur/c3CWmkapgvyZVK7nTOlpL0/img.png?width=734&amp;amp;height=455&amp;amp;face=0_0_734_455&quot;&gt;&lt;a href=&quot;https://www.linuxteck.com/linux-process-management-command-cheat-sheet/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.linuxteck.com/linux-process-management-command-cheat-sheet/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/7xY9w/hyVbZZpGI2/Ui5uIk0B8LjZhrAOeOP8bK/img.png?width=734&amp;amp;height=455&amp;amp;face=0_0_734_455,https://scrap.kakaocdn.net/dn/cOnk1V/hyVf54IjLT/91fHm151Jrsr8DBlOkov1K/img.png?width=734&amp;amp;height=455&amp;amp;face=0_0_734_455,https://scrap.kakaocdn.net/dn/0fzMY/hyVgcbHlur/c3CWmkapgvyZVK7nTOlpL0/img.png?width=734&amp;amp;height=455&amp;amp;face=0_0_734_455');&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;Linux Process Management Command Cheat Sheet | LinuxTeck&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Process management commands are utilized on a Linux system to oversee running processes. These commands permit users to examine details on active processes,&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.linuxteck.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/LINUX-%F0%9F%93%9A-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EA%B4%80%EB%A6%AC-%EB%AA%85%EB%A0%B9%EC%96%B4-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC-Foreground-Background&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://inpa.tistory.com/entry/LINUX-%F0%9F%93%9A-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EA%B4%80%EB%A6%AC-%EB%AA%85%EB%A0%B9%EC%96%B4-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC-Foreground-Background&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1706687626350&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;[LINUX]   프로세스 관리 명령어   정리 (ps / top / fg / bg / kill / nice ...)&quot; data-og-description=&quot;리눅스 프로세스 동작 원리 시스템이 구동 될 때, 커널은 /etc 에 위치한 init 이라는 스크립트를 실행함으로써 시스템 서비스들을 차례대로 시작시킨다. 이 서비스들은 데몬 프로그램(백그라운드&quot; data-og-host=&quot;inpa.tistory.com&quot; data-og-source-url=&quot;https://inpa.tistory.com/entry/LINUX-%F0%9F%93%9A-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EA%B4%80%EB%A6%AC-%EB%AA%85%EB%A0%B9%EC%96%B4-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC-Foreground-Background&quot; data-og-url=&quot;https://inpa.tistory.com/entry/LINUX-%F0%9F%93%9A-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EA%B4%80%EB%A6%AC-%EB%AA%85%EB%A0%B9%EC%96%B4-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC-Foreground-Background&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/j6sow/hyVgcW4SO4/BtROAcOUnZyfZK3HkKOpZk/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/beg2ss/hyVcaT9z24/uZDHclazS6xg66gHK7NFr0/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/DN2eT/hyVf36TL8I/jcSyWrf8YQEtuv701ofKHk/img.png?width=961&amp;amp;height=394&amp;amp;face=0_0_961_394&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/LINUX-%F0%9F%93%9A-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EA%B4%80%EB%A6%AC-%EB%AA%85%EB%A0%B9%EC%96%B4-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC-Foreground-Background&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inpa.tistory.com/entry/LINUX-%F0%9F%93%9A-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EA%B4%80%EB%A6%AC-%EB%AA%85%EB%A0%B9%EC%96%B4-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC-Foreground-Background&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/j6sow/hyVgcW4SO4/BtROAcOUnZyfZK3HkKOpZk/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/beg2ss/hyVcaT9z24/uZDHclazS6xg66gHK7NFr0/img.jpg?width=800&amp;amp;height=450&amp;amp;face=0_0_800_450,https://scrap.kakaocdn.net/dn/DN2eT/hyVf36TL8I/jcSyWrf8YQEtuv701ofKHk/img.png?width=961&amp;amp;height=394&amp;amp;face=0_0_961_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;[LINUX]   프로세스 관리 명령어   정리 (ps / top / fg / bg / kill / nice ...)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;리눅스 프로세스 동작 원리 시스템이 구동 될 때, 커널은 /etc 에 위치한 init 이라는 스크립트를 실행함으로써 시스템 서비스들을 차례대로 시작시킨다. 이 서비스들은 데몬 프로그램(백그라운드&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;inpa.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://m.blog.naver.com/tmk0429/222318530824&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://m.blog.naver.com/tmk0429/222318530824&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/Linux</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/837</guid>
      <comments>https://bitrader.tistory.com/837#entry837comment</comments>
      <pubDate>Wed, 31 Jan 2024 16:53:46 +0900</pubDate>
    </item>
    <item>
      <title>Fine-tune mixtral</title>
      <link>https://bitrader.tistory.com/826</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://generativeai.pub/a-beginners-guide-to-fine-tuning-mixtral-instruct-model-7f6a30aacf61&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://generativeai.pub/a-beginners-guide-to-fine-tuning-mixtral-instruct-model-7f6a30aacf61&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1705460441612&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;A Beginner&amp;rsquo;s Guide to Fine-Tuning Mixtral Instruct Model&quot; data-og-description=&quot;Unleashing the Power of MixTRAL: A Comprehensive Guide to Fine-Tuning&quot; data-og-host=&quot;generativeai.pub&quot; data-og-source-url=&quot;https://generativeai.pub/a-beginners-guide-to-fine-tuning-mixtral-instruct-model-7f6a30aacf61&quot; data-og-url=&quot;https://generativeai.pub/a-beginners-guide-to-fine-tuning-mixtral-instruct-model-7f6a30aacf61&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/trWS0/hyU5QgYnfo/W2eahlLuynETaZp7DuPlHk/img.jpg?width=1200&amp;amp;height=617&amp;amp;face=0_0_1200_617,https://scrap.kakaocdn.net/dn/h2TjS/hyU5Rz9Vnc/K7nPYuVk1ZhQrcYPqlH55k/img.png?width=1358&amp;amp;height=671&amp;amp;face=0_0_1358_671&quot;&gt;&lt;a href=&quot;https://generativeai.pub/a-beginners-guide-to-fine-tuning-mixtral-instruct-model-7f6a30aacf61&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://generativeai.pub/a-beginners-guide-to-fine-tuning-mixtral-instruct-model-7f6a30aacf61&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/trWS0/hyU5QgYnfo/W2eahlLuynETaZp7DuPlHk/img.jpg?width=1200&amp;amp;height=617&amp;amp;face=0_0_1200_617,https://scrap.kakaocdn.net/dn/h2TjS/hyU5Rz9Vnc/K7nPYuVk1ZhQrcYPqlH55k/img.png?width=1358&amp;amp;height=671&amp;amp;face=0_0_1358_671');&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;A Beginner&amp;rsquo;s Guide to Fine-Tuning Mixtral Instruct Model&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Unleashing the Power of MixTRAL: A Comprehensive Guide to Fine-Tuning&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;generativeai.pub&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>sLLM</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/826</guid>
      <comments>https://bitrader.tistory.com/826#entry826comment</comments>
      <pubDate>Wed, 17 Jan 2024 12:00:45 +0900</pubDate>
    </item>
    <item>
      <title>GGML, GGUF 차이</title>
      <link>https://bitrader.tistory.com/824</link>
      <description>&lt;p style=&quot;color: #374151; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;GGML(GPT-Generated Model Language)과 GGUF(GPT-Generated Unified Format)는 주로 GPT와 같은 언어 모델의 추론용으로 설계된 파일 형식입니다. Georgi Gerganov가 개발한 GGML은 GPT 모델을 위한 파일 형식을 만들기 위한 초기 시도로, 단일 파일 공유와 CPU 호환성을 가능케 했지만, 모델 정보 추가에 어려움을 겪었으며 새로운 기능 도입시 기존 모델과의 호환성 문제, 그리고 사용자가 로프 빈도 기본값, 로프 빈도 스케일, GQA 및 RMS 정규화 엡실론과 같은 설정을 수동으로 조정해야 하는 복잡함이 있었습니다.&lt;/p&gt;
&lt;p style=&quot;color: #374151; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #374151; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;GGML의 후속작으로 2023년 8월에 발표된 GGUF는 언어 모델 파일 형식의 중요한 발전을 나타냅니다. Georgi Gerganov를 포함한 AI 커뮤니티의 기여자들이 개발한 GGUF는 GGML의 한계를 극복하기 위해 설계되었습니다. GGUF는 확장성, 안정성, 그리고 다양한 모델을 지원하는 등의 이점을 제공하며, llama 모델 이상의 다양한 모델을 지원합니다. 장점에도 불구하고 기존 모델을 GGUF로 전환하는 데는 상당한 시간이 소요될 수 있으며 사용자와 개발자들은 이 새로운 형식에 적응해야 합니다.&lt;/p&gt;
&lt;p style=&quot;color: #374151; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #374151; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;출처:&lt;/p&gt;
&lt;p style=&quot;color: #374151; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://medium.com/@phillipgimmi/what-is-gguf-and-ggml-e364834d241c&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://medium.com/@phillipgimmi/what-is-gguf-and-ggml-e364834d241c&lt;/a&gt;&lt;/p&gt;</description>
      <category>논문리뷰</category>
      <category>GGML</category>
      <category>GGUF</category>
      <category>LLM</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/824</guid>
      <comments>https://bitrader.tistory.com/824#entry824comment</comments>
      <pubDate>Wed, 17 Jan 2024 11:33:42 +0900</pubDate>
    </item>
    <item>
      <title>vscode extension 설치시 XHR error 해결방법</title>
      <link>https://bitrader.tistory.com/790</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원격 서버를 접속시에 vscode에서 extension을 설치시에 XHR 에러가 발생하는 문제가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;proxy 세팅을 바꾸는등 여러가지 방법이 있지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 market place에서 설치 파일을 다운로드해서 extension을 설치하는 방법이 가장 빠르고 간편했던거 같다.&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;&amp;nbsp;&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;a href=&quot;https://bobbyhadz.com/blog/error-while-fetching-extensions-xhr-failed&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://bobbyhadz.com/blog/error-while-fetching-extensions-xhr-failed&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1699574707585&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;Fix Error while fetching extensions. XHR failed in VS Code | bobbyhadz&quot; data-og-description=&quot;The VS Code &amp;quot;Error while extensions. XHR failed&amp;quot; error occurs when you have internet connectivity, proxy or firewall issues.&quot; data-og-host=&quot;bobbyhadz.com&quot; data-og-source-url=&quot;https://bobbyhadz.com/blog/error-while-fetching-extensions-xhr-failed&quot; data-og-url=&quot;https://bobbyhadz.com/blog/error-while-fetching-extensions-xhr-failed&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/jzncl/hyUrxQt7Al/aUxMyu4423HKrO0STAt87k/img.jpg?width=632&amp;amp;height=355&amp;amp;face=0_0_632_355&quot;&gt;&lt;a href=&quot;https://bobbyhadz.com/blog/error-while-fetching-extensions-xhr-failed&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://bobbyhadz.com/blog/error-while-fetching-extensions-xhr-failed&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/jzncl/hyUrxQt7Al/aUxMyu4423HKrO0STAt87k/img.jpg?width=632&amp;amp;height=355&amp;amp;face=0_0_632_355');&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;Fix Error while fetching extensions. XHR failed in VS Code | bobbyhadz&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The VS Code &quot;Error while extensions. XHR failed&quot; error occurs when you have internet connectivity, proxy or firewall issues.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;bobbyhadz.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;market place :&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://open-vsx.org/extension/mhutchie/git-graph&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://open-vsx.org/extension/mhutchie/git-graph&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1699574719053&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;https://open-vsx.org/extension/mhutchie/git-graph&quot; data-og-description=&quot;&quot; data-og-host=&quot;open-vsx.org&quot; data-og-source-url=&quot;https://open-vsx.org/extension/mhutchie/git-graph&quot; data-og-url=&quot;https://open-vsx.org/extension/mhutchie/git-graph&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://open-vsx.org/extension/mhutchie/git-graph&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://open-vsx.org/extension/mhutchie/git-graph&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;https://open-vsx.org/extension/mhutchie/git-graph&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;open-vsx.org&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;a href=&quot;https://seobway.tistory.com/entry/vscode-XHR-failed&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://seobway.tistory.com/entry/vscode-XHR-failed&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1699574825292&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;vscode에서 서버 연결할 때, XHR failed&quot; data-og-description=&quot;예전에 vscode-ssh로 서버에 연결하려고 할 때, XHR failed가 뜨면서 문제가 됐던적이있다. 검색해보니 DNS문제 등등 다양한 문제가 있었는데 내 경우는 서버가 인터넷에 연결이 안되어있어서 필요한 v&quot; data-og-host=&quot;seobway.tistory.com&quot; data-og-source-url=&quot;https://seobway.tistory.com/entry/vscode-XHR-failed&quot; data-og-url=&quot;https://seobway.tistory.com/entry/vscode-XHR-failed&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bsk8Fp/hyUrulR6ox/k7PMgibzCyw4Og2Jq6mnKk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cbKH8l/hyUuTYC6mH/RhbRiyk29odLUbtsbQvywK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://seobway.tistory.com/entry/vscode-XHR-failed&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://seobway.tistory.com/entry/vscode-XHR-failed&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bsk8Fp/hyUrulR6ox/k7PMgibzCyw4Og2Jq6mnKk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cbKH8l/hyUuTYC6mH/RhbRiyk29odLUbtsbQvywK/img.png?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;vscode에서 서버 연결할 때, XHR failed&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;예전에 vscode-ssh로 서버에 연결하려고 할 때, XHR failed가 뜨면서 문제가 됐던적이있다. 검색해보니 DNS문제 등등 다양한 문제가 있었는데 내 경우는 서버가 인터넷에 연결이 안되어있어서 필요한 v&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;seobway.tistory.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>SSH</category>
      <category>vscode</category>
      <category>xhr</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/790</guid>
      <comments>https://bitrader.tistory.com/790#entry790comment</comments>
      <pubDate>Fri, 10 Nov 2023 09:11:29 +0900</pubDate>
    </item>
    <item>
      <title>emerging architectures for llm</title>
      <link>https://bitrader.tistory.com/745</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://a16z.com/2023/06/20/emerging-architectures-for-llm-applications/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://a16z.com/2023/06/20/emerging-architectures-for-llm-applications/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/745</guid>
      <comments>https://bitrader.tistory.com/745#entry745comment</comments>
      <pubDate>Sun, 3 Sep 2023 17:25:13 +0900</pubDate>
    </item>
    <item>
      <title>유명 테크 블로그들을 모아놓은 사이트</title>
      <link>https://bitrader.tistory.com/741</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://tech-blogs.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://tech-blogs.dev/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1693063018030&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;Awesome Tech Blogs&quot; data-og-description=&quot;This is a list of Awesome Tech Blogs. Add yourself.&quot; data-og-host=&quot;tech-blogs.dev&quot; data-og-source-url=&quot;https://tech-blogs.dev/&quot; data-og-url=&quot;https://tech-blogs.dev/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cpZI6l/hyTMhsdlwU/eueODEazrmj0vYtJLtwSq0/img.jpg?width=1024&amp;amp;height=512&amp;amp;face=0_0_1024_512&quot;&gt;&lt;a href=&quot;https://tech-blogs.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://tech-blogs.dev/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cpZI6l/hyTMhsdlwU/eueODEazrmj0vYtJLtwSq0/img.jpg?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;Awesome Tech Blogs&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;This is a list of Awesome Tech Blogs. Add yourself.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;tech-blogs.dev&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>blog</category>
      <category>Programming</category>
      <category>tech</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/741</guid>
      <comments>https://bitrader.tistory.com/741#entry741comment</comments>
      <pubDate>Sun, 27 Aug 2023 00:17:09 +0900</pubDate>
    </item>
    <item>
      <title>459. Repeated Substring Pattern</title>
      <link>https://bitrader.tistory.com/710</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/repeated-substring-pattern/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://leetcode.com/problems/repeated-substring-pattern/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1691800634727&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;Repeated Substring Pattern - LeetCode&quot; data-og-description=&quot;Can you solve this real interview question? Repeated Substring Pattern - Given a string s, check if it can be constructed by taking a substring of it and appending multiple copies of the substring together. &amp;nbsp; Example 1: Input: s = &amp;quot;abab&amp;quot; Output: true Expl&quot; data-og-host=&quot;leetcode.com&quot; data-og-source-url=&quot;https://leetcode.com/problems/repeated-substring-pattern/&quot; data-og-url=&quot;https://leetcode.com/problems/repeated-substring-pattern/description&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fBlcG/hyTCxCrJ54/kjMPKZr0w8cwhrMAehZ96k/img.png?width=500&amp;amp;height=260&amp;amp;face=0_0_500_260&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/repeated-substring-pattern/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://leetcode.com/problems/repeated-substring-pattern/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fBlcG/hyTCxCrJ54/kjMPKZr0w8cwhrMAehZ96k/img.png?width=500&amp;amp;height=260&amp;amp;face=0_0_500_260');&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;Repeated Substring Pattern - LeetCode&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Can you solve this real interview question? Repeated Substring Pattern - Given a string s, check if it can be constructed by taking a substring of it and appending multiple copies of the substring together. &amp;nbsp; Example 1: Input: s = &quot;abab&quot; Output: true Expl&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;leetcode.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;p data-ke-size=&quot;size16&quot;&gt;문자열의 길이가 최대 10000까지 주어져서 일일이 substring을 나눠서 True/False를 판독하면 시간초과가 걸릴 줄 알았으나 그렇게 풀어도 통과과 됨&lt;/p&gt;
&lt;pre id=&quot;code_1691800638038&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def repeatedSubstringPattern(s: str) -&amp;gt; bool:
    n = len(s)
    for i in range(1, n // 2 + 1):
        if n % i == 0:
            substring = s[:i]
            if substring * (n // i) == s:
                return True
    return False&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘/리트코드</category>
      <category>leetcode</category>
      <category>python</category>
      <category>String</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/710</guid>
      <comments>https://bitrader.tistory.com/710#entry710comment</comments>
      <pubDate>Sat, 12 Aug 2023 09:38:46 +0900</pubDate>
    </item>
    <item>
      <title>deep learning inference 서버 구축하기 - 2</title>
      <link>https://bitrader.tistory.com/620</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. torchserve로 huggingface 모델 서빙하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;torchserve는 transformer 모델을 torchserve로 서빙하는 예제를 친절하게 잘 기술해 놓았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/pytorch/serve/tree/master/examples/Huggingface_Transformers&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/pytorch/serve/tree/master/examples/Huggingface_Transformers&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1684131953844&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 - pytorch/serve: Serve, optimize and scale PyTorch models in production&quot; data-og-description=&quot;Serve, optimize and scale PyTorch models in production - GitHub - pytorch/serve: Serve, optimize and scale PyTorch models in production&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/pytorch/serve/tree/master/examples/Huggingface_Transformers&quot; data-og-url=&quot;https://github.com/pytorch/serve&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bVZA4B/hySCNNeQTd/gvuQO7SHflCWKkSWf7HYK1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/pytorch/serve/tree/master/examples/Huggingface_Transformers&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/pytorch/serve/tree/master/examples/Huggingface_Transformers&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bVZA4B/hySCNNeQTd/gvuQO7SHflCWKkSWf7HYK1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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 - pytorch/serve: Serve, optimize and scale PyTorch models in production&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Serve, optimize and scale PyTorch models in production - GitHub - pytorch/serve: Serve, optimize and scale PyTorch models in production&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;
&lt;p data-ke-size=&quot;size16&quot;&gt;torchserve로 transformer를 서빙하기 위해 필요한 파일은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pytorch로 모델을 저장하면 생성되는 모델 파일 및 설정파일이다.&lt;/p&gt;
&lt;pre id=&quot;code_1684131993488&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pytorch_model.bin vocab.txt config.json&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;하지만 훈련 중인 모델이 없고 모델을 huggingface에서 모델을 다운로드를 하여 서빙을 하고 싶으면 설정파일을 설정하고 아래의 코드를 실행하면 알아서 Transformer_model 디렉터리를 생성하고 필요한 파일들을 해당 경로에 저장을 해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1684132162896&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# setup_config.json 파일
# bert-base sequence_classification 모델 다운로드 설정파일
{
 &quot;model_name&quot;:&quot;bert-base-uncased&quot;,
 &quot;mode&quot;:&quot;sequence_classification&quot;,
 &quot;do_lower_case&quot;:true,
 &quot;num_labels&quot;:&quot;2&quot;,
 &quot;save_mode&quot;:&quot;pretrained&quot;,
 &quot;max_length&quot;:&quot;150&quot;,
 &quot;captum_explanation&quot;:true,
 &quot;embedding_name&quot;: &quot;bert&quot;,
 &quot;FasterTransformer&quot;:false,
 &quot;BetterTransformer&quot;:false,
 &quot;model_parallel&quot;:false
}&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;이후에 아래의 명령어로 필요한 모델을 huggingface에서 바로 다운로드 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Download_Transformer_models.py와 setup_config.json파일 모두 torchserve 깃허브에서 제공하고 있으며 Sequence classification 뿐만 아니라 token classification, question answering 등 다양한 예제에 대한 setup_config.json 파일도 제공해 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1684132378903&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;python Download_Transformer_models.py setup_config.json&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;다음으로는 아래의 명령를 실행하여 필요한 파일들을 하나로 모아 서빙에 필요한. mar 파일을 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 다운로드한 파일 외에 Transformer_handler_generalized.py는 입력으로 받은 데이터와 출력으로 나온 데이터를 전후처리 하기 위한 파일로 원래는 세부적으로 구현이 필요한 사항이나 일단은 예시파일이 제공되길래 그냥 가져다가 썼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;index_to_name.json 파일은 모델의 출력으로 나온 int32 값을 해당하는 문자열로 매핑해 주는 역할을 하는 설정파일이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 파일 모두 torchserve 깃허브에서 제공이 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1684132548390&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;torch-model-archiver --model-name BERTSeqClassification --version 1.0 --serialized-file Transformer_model/pytorch_model.bin --handler ./Transformer_handler_generalized.py --extra-files &quot;Transformer_model/config.json,./setup_config.json,./Seq_classification_artifacts/index_to_name.json&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;Sequence classification 모델에 해당하는 json파일로 위 경우는 2가지 case 밖에 없다.&lt;/p&gt;
&lt;pre id=&quot;code_1684132722774&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#index_to_name.json 예시
{
 &quot;0&quot;:&quot;Not Accepted&quot;,
 &quot;1&quot;:&quot;Accepted&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;위의 명령어를 실행하고 나면 BERTSeqClassification.mar 파일이 생성이 되며 model_store라는 지정된 경로로 해당파일을 이동해 주고 torchserve를 사용하여 서빙을 시작한다.&lt;/p&gt;
&lt;pre id=&quot;code_1684132800987&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mkdir model_store
mv BERTSeqClassification.mar model_store/
torchserve --start --model-store model_store --models my_tc=BERTSeqClassification.mar --ncs&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;torchserve를 시작하면 출력되는 화면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;torchserve.PNG&quot; data-origin-width=&quot;894&quot; data-origin-height=&quot;565&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBUn0s/btsf5en3vyH/3I6tzZgMLeBRZeDW7kHN11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBUn0s/btsf5en3vyH/3I6tzZgMLeBRZeDW7kHN11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBUn0s/btsf5en3vyH/3I6tzZgMLeBRZeDW7kHN11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBUn0s%2Fbtsf5en3vyH%2F3I6tzZgMLeBRZeDW7kHN11%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;563&quot; height=&quot;356&quot; data-filename=&quot;torchserve.PNG&quot; data-origin-width=&quot;894&quot; data-origin-height=&quot;565&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;curl 명령어로 해당 모델들이 잘 서빙되는지 확인&lt;/p&gt;
&lt;pre id=&quot;code_1684151901501&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl http://localhost:8081/models/ # 서빙되는 전체 모델 확인
curl http://localhost:8081/models/{model_name} # 각각의 모델 확인&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 같은 과정으로 서빙을 하고 싶은 다른 모델들의. mar 파일을 만들어준다.&lt;/p&gt;
&lt;pre id=&quot;code_1684151931015&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# BertSeqClassification의 version 2를 만드는 명령어
torch-model-archiver --model-name BERTSeqClassification2 --version 2.0 --serialized-file Transformer_model/pytorch_model.bin --handler ./config/Transformer_handler_sequence.py --extra-files &quot;Transformer_model/config.json,./config/setup_config_sequence.json,./config/Seq_classification_artifacts/index_to_name.json&quot;
mv BERTSeqClassification2.mar model_store&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;두 번째 모델부터는 같은 명령어로 서빙이 안됨으로 그 대신 management api의 register 기능을 사용하여 새로운 모델을 등록해 준다. curl 명령어로 post 요청을 보내면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1684151948992&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl -X POST  &quot;http://localhost:8081/models?url=BERTSeqClassification2.mar&amp;amp;model_name=bert_seqcls&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;새로 등록한 모델은 worker가 default로 0개로 지정되는 듯 보여 min_worker를 1개로 지정해 주는 명령어를 전송해 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1684151956047&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl -v -X PUT &quot;http://localhost:8081/models/bert_seqcls/2.0?min_worker=1&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;총 4개의 모델이 잘 등록되고 worker를 할당받은 것을 확인하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;curl.PNG&quot; data-origin-width=&quot;484&quot; data-origin-height=&quot;221&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dRUQR3/btsf43UouT7/1jnZ18C18a4d0OWLLHE6PK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dRUQR3/btsf43UouT7/1jnZ18C18a4d0OWLLHE6PK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dRUQR3/btsf43UouT7/1jnZ18C18a4d0OWLLHE6PK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdRUQR3%2Fbtsf43UouT7%2F1jnZ18C18a4d0OWLLHE6PK%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;484&quot; height=&quot;221&quot; data-filename=&quot;curl.PNG&quot; data-origin-width=&quot;484&quot; data-origin-height=&quot;221&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. FastAPI로 v2 inference protocol을 준수하는 API 서버 구축&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 서버와 모델에 대한 정보 및 추론 결과를 제공하는 API를 구현해야 한다.&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;* FastAPI에서 요청을 받으면 다시 받은 요청을 기반으로 하여 torchserve로 관련된 요청을 보내서 필요한 정보를 받는 방식으로 구현을 하였다. 즉 api서버에서 또 다른 api서버로 요청을 보내는 것인데 이 방식이 맞는 방식인지 모르겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정에서 동기적으로 작동하는 python의 request 패키지는 사용이 불가능하여 비동기로 request 요청을 보낼 수 있는 함수를 구현하여 사용하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1684151338717&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# async get request
async def request(client, url):
    response = await client.get(url)
    return response


async def request_handler(url):
    async with httpx.AsyncClient() as client:
        tasks = request(client, url)
        result = await asyncio.gather(tasks)
        return result[0]

# Health
@app.get(&quot;/v2/health/live&quot;)
async def health_live():
    response = await request_handler(f&quot;{management_server}/api-description&quot;)

    if response.status_code == 200:
        Server_live_response = {&quot;response&quot;: &quot;Inference server is alive&quot;}
        return Server_live_response
    else:
        error_handler(response)&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;* 또한 inferece 부분을 구현하는 과정에서 torchserve로 추론 요청을 하려면 추론하고자 하는 내용을 file의 형태로 전송을 해야 하는 것으로 확인을 하였다. 따라서 FastAPI에서 요청받는 추론 요청 내용을 임시로 파일로 저장해서 해당 파일을 torchserver로 보낸 형식으로 구현을 하였는데 이렇게 구현을 하는 것이 맞는지 모르겠다. 일단 돌아는 간다..&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;마지막으로 부분적으로 v2 inference protocol을 준수하지 않은 부분이 있다. 예시를 보면 모델 메타데이터에서 input으로 받을 데이터의 형식을 명시하라고 되어있는데 입력으로 받을 tensor의 datatype이나 shape의 정보를 제공해주어야 한다. 하지만 나는 그저 string으로 데이터를 받고 싶은데 해당 부분은 어떻게 처리를 해야 하는지 추후에 알아봐야겠다. 우선은 임의로 string이라고만 하고 처리하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1684151358593&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Inference
@app.post(&quot;/v2/models/{model_name}/infer&quot;)
async def model_infer(model_name: str, inputstr: InputStr):

    input_json = jsonable_encoder(inputstr)

    file_hash = hash(datetime.now())
    file_path = f&quot;./tmp/{file_hash}.txt&quot;

    with open(file_path, &quot;w&quot;) as file:
        file.write(str(input_json))

    url = f&quot;{inference_server}/predictions/{model_name}&quot;
    response = await request_post_handler(url, file_path)

    os.remove(file_path)

    if response.status_code == 200:
        inference_response = {
            &quot;model_name&quot;: model_name,
            &quot;id&quot;: &quot;tmp_string&quot;,
            &quot;outputs&quot;: {
                &quot;Positive_negative&quot;: {
                    &quot;name&quot;: &quot;inference_result&quot;,
                    #&quot;shape&quot;:[]
                    &quot;datatype&quot;:&quot;string&quot;,
                    #&quot;parameter&quot;:[]
                    &quot;result&quot;: response.text,
                }
            },
        }

        return inference_response

    else:
        error_handler(response)&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 외에는 torchserve의 management, inference api의 가이드를 보고 예외에 대한 처리를 해주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;management api, inference api 서버 모두 404, 500, 503 에러 케이스만 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1684151413800&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def error_handler(response):
    error_message = json.loads(response.text)[&quot;message&quot;]

    if response.status_code == 404:
        raise HTTPException(status_code=404, detail=error_message)
    elif response.status_code == 500:
        raise HTTPException(status_code=500, detail=error_message)
    elif response.status_code == 503:
        raise HTTPException(status_code=503, detail=error_message)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. dockerize 및 github action을 이용한 ci/cd 구축&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 FastAPI와 torchserve의 구현사항을 dockerize를 구현하는데 사실 torchserve에서 기본적으로 제공하는 docker 이미지가 있음을 좀 나중에 알았다. 다음부터는 공식 document에서 제공하는 이미지를 사용하도록 하자.&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;내가 만든 dockerfile은 아래와 같으며 torchserve를 사용하기 위해서는 python 3.8 버전 이상이 필요해서 python:3.8-slim-buster를 사용하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1684133910074&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FROM python:3.8-slim-buster

# torchserve를 위한 jdk 설치
RUN apt-get update -y \
    &amp;amp;&amp;amp; apt-get install -y curl git openjdk-11-jdk

RUN git clone https://github.com/pytorch/serve.git \
    &amp;amp;&amp;amp; python ./serve/ts_scripts/install_dependencies.py

WORKDIR /dl_inference

COPY . .
# python package 설치 및
# huggingface model 다운로드 및 .mar file을 만드는 스크립트 실행
RUN pip install --no-cache-dir -r requirements.txt \
    &amp;amp;&amp;amp; bash ./model_down_register.sh

ENV server_name=&quot;my_server&quot; \
    server_version=&quot;1.0&quot; \
    management_server=&quot;http://localhost:8081&quot; \
    inference_server=&quot;http://localhost:8080&quot;

CMD [&quot;uvicorn&quot;, &quot;main:app&quot;, &quot;--reload&quot;, &quot;--host&quot;, &quot;0.0.0.0&quot;, &quot;--port&quot;, &quot;8000&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;model_down_register.sh는 1번 과정에서 수행한 명령어들을 한 번에 실행하도록 sh 파일을 만들어 놓아 실행하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총 4개의. mar파일을 만들어 model_store에 저장까지 해놓는다. transformer 파일을 직접 다운로드하고 저장함으로 빌드시간도 꽤 걸리고 이미지의 용량도 굉장히 크다...&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;이후 github action을 ssh로 설정을 하여 main branch에 push가 들어올 시 자동으로 docker 파일을 빌드하고 실행하도록 구현하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI가 쓸 8000번 포트를 만 export 하였으며 model_register.sh파일은 1번 과정에서 모델들을 torchserve에 등록하는 curl post 명령어들을 모아서 실행하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1684134186357&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# github action yml파일의 script 부분
script: |
          cd /root/dl_inference
          git pull
          docker stop test || True
          docker rm test || True
          docker build . -t test
          docker run --name test -d -p 8000:8000 test
          docker exec -i test bash ./model_register.sh&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;github action 실행 시 성공하는 장면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;success.PNG&quot; data-origin-width=&quot;888&quot; data-origin-height=&quot;714&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bG9tqL/btsf2IcyPNX/yOGq144JEPkqGkEgKhnXH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bG9tqL/btsf2IcyPNX/yOGq144JEPkqGkEgKhnXH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bG9tqL/btsf2IcyPNX/yOGq144JEPkqGkEgKhnXH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbG9tqL%2Fbtsf2IcyPNX%2FyOGq144JEPkqGkEgKhnXH1%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;486&quot; height=&quot;391&quot; data-filename=&quot;success.PNG&quot; data-origin-width=&quot;888&quot; data-origin-height=&quot;714&quot;/&gt;&lt;/span&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;위의 과정을 통하여 FastAPI를 이용하여 deep learning model을 서빙하는 프로젝트를 완료해 보았다. 큰 틀에서는 요구사항을 전부 만족시킨듯하나 세부적인 사항을 일일이 따지만 production level로 운영되기에는 다소 하자가 있는 것 같다. 예를 들면 docker image를 빌드하는 데 걸리는 시간이라든지.... 용량이라든지....&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;p data-ke-size=&quot;size16&quot;&gt;issue : tokenclassification은 inference 요청을 보내면 503 error만 반환한다.. 구체적인 원인은 내부의 로그를 봐야지 해결가능할 것 같다.&lt;/p&gt;</description>
      <category>개발/numble 프로젝트</category>
      <category>FastAPI</category>
      <category>GitHub Action</category>
      <category>huggingface</category>
      <category>python</category>
      <category>torchserve</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/620</guid>
      <comments>https://bitrader.tistory.com/620#entry620comment</comments>
      <pubDate>Mon, 15 May 2023 14:38:45 +0900</pubDate>
    </item>
    <item>
      <title>deep learning inference 서버 구축하기 - 1</title>
      <link>https://bitrader.tistory.com/619</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.&amp;nbsp; Intro&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;넘블에서 진행하는 프로젝트로 FastAPI를 활용하여 딥러닝 모델 추론결과를 return 해주는 서버를 production 기준에 맞추어서 구축해 보는 프로젝트이다.&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;* FastAPI를 사용할것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* v2 inference protocol의 기준에 맞춰 구현할 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* Torchserve, Kserve와 같은 딥러닝 서빙 프레임워크를 사용할 것 등이다.&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;그 외 코드의 가독성이 좋은지 CI/CD가 구축되어 있는지도 주요한 평가 사항이다.&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;h3 data-ke-size=&quot;size23&quot;&gt;2. torchserve&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;torchserve는 pytorch 오픈소스 프로젝트의 일환으로 &lt;span style=&quot;background-color: #ffffff; color: #1e1e1e; text-align: start;&quot;&gt;모델 배포 프로세스를 단순화하는 모델 서버이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1e1e1e; text-align: start;&quot;&gt;즉, 프로덕션에서 머신러닝 추론을 제공하도록 특별히 설계된 상용 웹 응용 프로그램으로 모델 서버를 사용하면 하나 이상의 모델을 쉽게 로드할 수 있으므로 확장 가능한 웹 서버가 지원하는 추론 API가 자동으로 생성된다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #1e1e1e; text-align: start;&quot;&gt;&lt;/span&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;span style=&quot;background-color: #ffffff; color: #1e1e1e; text-align: start;&quot;&gt;&lt;a href=&quot;https://github.com/pytorch/serve&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/pytorch/serve&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1684127102373&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 - pytorch/serve: Serve, optimize and scale PyTorch models in production&quot; data-og-description=&quot;Serve, optimize and scale PyTorch models in production - GitHub - pytorch/serve: Serve, optimize and scale PyTorch models in production&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/pytorch/serve&quot; data-og-url=&quot;https://github.com/pytorch/serve&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kFvs5/hySCTs37CM/gjeVMki5gZMuPJ4gxtaDEK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/pytorch/serve&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/pytorch/serve&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kFvs5/hySCTs37CM/gjeVMki5gZMuPJ4gxtaDEK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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 - pytorch/serve: Serve, optimize and scale PyTorch models in production&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Serve, optimize and scale PyTorch models in production - GitHub - pytorch/serve: Serve, optimize and scale PyTorch models in production&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;내가 torchserve를 사용하기로 한 것은 단순히 torchserve document에 huggingface를 이용하는 예제가 잘되어서 있어서였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;kserve는 kubernetes를 이용해야 되는 것 같은데(자세히는 안 읽어봤다.) 더 복잡해질 것 같아서 우선 torchserve를 택했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. v2 inference protocol&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 모델 추론 서버를 구축한다면 그냥 단순히 FastAPI 서버를 띄우고 post request로 받은 데이터를 output = model(*input) 하는 형식으로 추론을 한 결과를 return 해주었는데 이런 허접한 방식 말고 좀 더 체계화된 서빙 프로토콜이 있는 것을 처음 알게 되었다.&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;v2 inference protocol은 예측/추론 api로 머신러닝 프레임워크나 모델서버에 제약되지 않은 규약이다. 고성능과 사용하기 편한 유저 케이스를 지원한다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;v2 inference protocol을 따르기로 한 서버는 아래와 같이 기술된 api들을 구현해서 서버, 모델에 대한 메타데이터 및 추론 결과를 제공해야 한다. HTTP와 GRPC를 둘 다 지원한다.&lt;/p&gt;
&lt;pre id=&quot;code_1684127442659&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Health:

GET v2/health/live GET v2/health/ready GET v2/models/${MODEL_NAME}[/versions/${MODEL_VERSION}]/ready

Server Metadata:

GET v2

Model Metadata:

GET v2/models/${MODEL_NAME}[/versions/${MODEL_VERSION}]

Inference:

POST v2/models/${MODEL_NAME}[/versions/${MODEL_VERSION}]/infer&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;자세한 사항은 아래 사이트에 기술되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://kserve.github.io/website/0.8/modelserving/inference_api/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://kserve.github.io/website/0.8/modelserving/inference_api/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1684127290346&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;V2 Inference Protocol - KServe Documentation Website&quot; data-og-description=&quot;Predict Protocol - Version 2 This document proposes a predict/inference API independent of any specific ML/DL framework and model server. The proposed APIs are able to support both easy-to-use and high-performance use cases. By implementing this protocol b&quot; data-og-host=&quot;kserve.github.io&quot; data-og-source-url=&quot;https://kserve.github.io/website/0.8/modelserving/inference_api/&quot; data-og-url=&quot;https://kserve.github.io/website/0.8/modelserving/inference_api/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://kserve.github.io/website/0.8/modelserving/inference_api/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://kserve.github.io/website/0.8/modelserving/inference_api/&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;V2 Inference Protocol - KServe Documentation Website&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Predict Protocol - Version 2 This document proposes a predict/inference API independent of any specific ML/DL framework and model server. The proposed APIs are able to support both easy-to-use and high-performance use cases. By implementing this protocol b&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;kserve.github.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;h3 data-ke-size=&quot;size23&quot;&gt;4. 아키텍처&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나의 이번 프로젝트의 주요한 목적은 모델 서빙 프레임워크를 사용해 보는 것, v2 inference protocol을 따르는 API 서버를 구축하여 dockerize 하는 것임으로 아키텍처는 복잡하게 생각하지 않기로 하였다. API 서버와 모델 서빙 서버가 같은 인스턴스에 있는 형태로 구축하여 네이버 클라우드에서 배포를 하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;diagram.png&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;372&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/deEocO/btsfjJqrG9S/GnN2oVJ2ssTsWagL54dWA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/deEocO/btsfjJqrG9S/GnN2oVJ2ssTsWagL54dWA0/img.png&quot; data-alt=&quot;출처 : https://github.com/mercari/ml-system-design-pattern/blob/master/Serving-patterns/Web-single-pattern/design_ko.md&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/deEocO/btsfjJqrG9S/GnN2oVJ2ssTsWagL54dWA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdeEocO%2FbtsfjJqrG9S%2FGnN2oVJ2ssTsWagL54dWA0%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;474&quot; height=&quot;212&quot; data-filename=&quot;diagram.png&quot; data-origin-width=&quot;832&quot; data-origin-height=&quot;372&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : https://github.com/mercari/ml-system-design-pattern/blob/master/Serving-patterns/Web-single-pattern/design_ko.md&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 디자인 패턴의 정식적인 명칭은 Web-single-pattern이라고 한다. 위 부분에서 로드밸런서도 빼고 구현을 하였다.&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;2편에 계속..&lt;/p&gt;</description>
      <category>개발/numble 프로젝트</category>
      <category>FastAPI</category>
      <category>pytorch</category>
      <category>torchserve</category>
      <category>v2 inference protocol</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/619</guid>
      <comments>https://bitrader.tistory.com/619#entry619comment</comments>
      <pubDate>Mon, 15 May 2023 13:01:05 +0900</pubDate>
    </item>
    <item>
      <title>백준 17976번 Thread knots</title>
      <link>https://bitrader.tistory.com/616</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/17976&quot;&gt;17976번: Thread Knots (acmicpc.net)&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1683904625112&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;17976번: Thread Knots&quot; data-og-description=&quot;Your program is to read from standard input. The input starts with a line containing one integer, n (2 &amp;le; n &amp;le; 100,000), where n is the number of threads.&amp;nbsp;In the following n lines, the i-th line contains two integers xi (0 &amp;le; xi &amp;le; 109) and li (1 &amp;le; &quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/17976&quot; data-og-url=&quot;https://www.acmicpc.net/problem/17976&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bOx4UR/hySAfqJAF7/pKkwSK61GxjGOV2NleRCck/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/17976&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/17976&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bOx4UR/hySAfqJAF7/pKkwSK61GxjGOV2NleRCck/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;17976번: Thread Knots&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Your program is to read from standard input. The input starts with a line containing one integer, n (2 &amp;le; n &amp;le; 100,000), where n is the number of threads.&amp;nbsp;In the following n lines, the i-th line contains two integers xi (0 &amp;le; xi &amp;le; 109) and li (1 &amp;le;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&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;* 이분 탐색으로 풀이하는 문제&lt;/p&gt;
&lt;pre id=&quot;code_1683904634648&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import sys
input = sys.stdin.readline
n = int(input())

lines = []
for _ in range(n):
    a,b = map(int,input().split())
    lines.append((a,b))

lines.sort()

def search(x):

    before = lines[0][0]

    for each in lines[1:]:

        now = each[0]
        if now - before &amp;lt; x:
            tmp = before + x

            if each[0] &amp;lt;= tmp &amp;lt;= each[0] + each[1]:
                before = tmp
            else:
                return False
        else:
            before = now

    return True

start = 0
end = 2000000000

while start &amp;lt;= end:

    mid = (start + end) // 2
    result = search(mid)

    # 이분탐색으로 문제 풀이
    if result:
        start = mid + 1
    else:
        end = mid - 1

if result: print(mid)
else: print(mid-1)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘/백준</category>
      <category>Parametric Search</category>
      <category>python</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/616</guid>
      <comments>https://bitrader.tistory.com/616#entry616comment</comments>
      <pubDate>Sat, 13 May 2023 00:17:43 +0900</pubDate>
    </item>
    <item>
      <title>283. Move Zeroes</title>
      <link>https://bitrader.tistory.com/573</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/move-zeroes/submissions/944108528/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://leetcode.com/problems/move-zeroes/submissions/944108528/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1683166011686&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;Move Zeroes - LeetCode&quot; data-og-description=&quot;Can you solve this real interview question? Move Zeroes - Given an integer array nums, move all 0's to the end of it while maintaining the relative order of the non-zero elements. Note that you must do this in-place without making a copy of the array. &amp;nbsp; E&quot; data-og-host=&quot;leetcode.com&quot; data-og-source-url=&quot;https://leetcode.com/problems/move-zeroes/submissions/944108528/&quot; data-og-url=&quot;https://leetcode.com/problems/move-zeroes/description&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cirAYU/hySvwYIgJS/DH7pFSxBCZ1Z2bBkKOx9b0/img.png?width=500&amp;amp;height=260&amp;amp;face=0_0_500_260,https://scrap.kakaocdn.net/dn/csikIn/hyStYJjAj1/s4o4hLPFGoMmqPbiIRdlW0/img.png?width=924&amp;amp;height=222&amp;amp;face=0_0_924_222&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/move-zeroes/submissions/944108528/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://leetcode.com/problems/move-zeroes/submissions/944108528/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cirAYU/hySvwYIgJS/DH7pFSxBCZ1Z2bBkKOx9b0/img.png?width=500&amp;amp;height=260&amp;amp;face=0_0_500_260,https://scrap.kakaocdn.net/dn/csikIn/hyStYJjAj1/s4o4hLPFGoMmqPbiIRdlW0/img.png?width=924&amp;amp;height=222&amp;amp;face=0_0_924_222');&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;Move Zeroes - LeetCode&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Can you solve this real interview question? Move Zeroes - Given an integer array nums, move all 0's to the end of it while maintaining the relative order of the non-zero elements. Note that you must do this in-place without making a copy of the array. &amp;nbsp; E&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;leetcode.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;pre id=&quot;code_1683166008667&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution:
    def moveZeroes(self, nums: List[int]) -&amp;gt; None:

        l = 0
        for r in range(len(nums)):
            if nums[r]:
                nums[l], nums[r] = nums[r], nums[l]
                l += 1&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘/리트코드</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/573</guid>
      <comments>https://bitrader.tistory.com/573#entry573comment</comments>
      <pubDate>Thu, 4 May 2023 11:06:54 +0900</pubDate>
    </item>
    <item>
      <title>KMP algorithm</title>
      <link>https://bitrader.tistory.com/558</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=UcjK_k5PLHI&quot;&gt;https://www.youtube.com/watch?v=UcjK_k5PLHI&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=UcjK_k5PLHI&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/clQDl2/hySqWCZjL8/8gCHHykhkkOGHLAQFNf56K/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-original-url=&quot;&quot; data-video-title=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/UcjK_k5PLHI&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;pre id=&quot;code_1682691106043&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Python3 program for KMP Algorithm
def KMPSearch(pat, txt):
    M = len(pat)
    N = len(txt)

    # create lps[] that will hold the longest prefix suffix
    # values for pattern
    lps = [0] * M
    j = 0  # index for pat[]

    # Preprocess the pattern (calculate lps[] array)
    computeLPSArray(pat, M, lps)

    i = 0  # index for txt[]
    while (N - i) &amp;gt;= (M - j):
        if pat[j] == txt[i]:
            i += 1
            j += 1

        if j == M:
            print(&quot;Found pattern at index &quot; + str(i - j))
            j = lps[j - 1]

        # mismatch after j matches
        elif i &amp;lt; N and pat[j] != txt[i]:
            # Do not match lps[0..lps[j-1]] characters,
            # they will match anyway
            if j != 0:
                j = lps[j - 1]
            else:
                i += 1

# 접두사와 접미사
def computeLPSArray(pat, M, lps):
    len = 0  # length of the previous longest prefix suffix

    lps[0] = 0  # lps[0] is always 0
    i = 1

    # the loop calculates lps[i] for i = 1 to M-1
    while i &amp;lt; M:
        if pat[i] == pat[len]:

            len += 1
            lps[i] = len
            i += 1
        else:
            # This is tricky. Consider the example.
            # AAACAAAA and i = 7. The idea is similar
            # to search step.
            if len != 0:
                len = lps[len - 1]

                # Also, note that we do not increment i here
            else:
                lps[i] = 0
                i += 1


txt = &quot;ABABDABACDABABCABAB&quot;
pat = &quot;ABABCABAB&quot;
KMPSearch(pat, txt)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;code 해설&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://devbull.xyz/python-kmp-algorijeumeuro-munjayeol-cajgi/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://devbull.xyz/python-kmp-algorijeumeuro-munjayeol-cajgi/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘/알고리즘(고급)</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/558</guid>
      <comments>https://bitrader.tistory.com/558#entry558comment</comments>
      <pubDate>Thu, 27 Apr 2023 23:11:46 +0900</pubDate>
    </item>
    <item>
      <title>Rabin-Karp Substring Search Algorithm</title>
      <link>https://bitrader.tistory.com/557</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Rabin Karp 알고리즘은 문자열 매칭 알고리즘이며 문자열 매칭을 O(n)의 시간안에 수행하게 해주는 알고리즘이다.&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;우리가 찾고자 하는 문자열을 target이라고 하고 대상이 되는 문자열을 S라고 했을때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 그림처럼 문자열을 하나씩 옮겨가면서 BruteForce 방식으로 찾게되면 len(target)*len(S)의 time complexity가 소요되게 된다. 즉 O(n**2)이라고 봐도 무방한것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;uIPjisbiCM-bruteforce.gif&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;194&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yZYad/btsddlYyOaT/HKLNMP5DSdATPcX5R7zY71/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yZYad/btsddlYyOaT/HKLNMP5DSdATPcX5R7zY71/img.gif&quot; data-alt=&quot;출처 :&amp;amp;nbsp;https://brilliant.org/wiki/rabin-karp-algorithm/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yZYad/btsddlYyOaT/HKLNMP5DSdATPcX5R7zY71/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/yZYad/btsddlYyOaT/HKLNMP5DSdATPcX5R7zY71/img.gif&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;699&quot; height=&quot;194&quot; data-filename=&quot;uIPjisbiCM-bruteforce.gif&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;194&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 :&amp;nbsp;https://brilliant.org/wiki/rabin-karp-algorithm/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rabin karp 알고리즘은 이러한 brute force방식의 문제점을 해결하여 O(n) time complexity로 문자열을 찾는 알고리즘이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심아이디어는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* brute force방식으로 문자열의 자리를 하나씩 옮겨가며 비교하지 말고 해시값을 구하여 일치하는지 비교하기(해시값은 유일함으로)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 만약 해시값이 일치하지 않다면 한자리를 옮기고 옮긴 한자리에 대해서만 hash value를 구해서 비교를 해주면 된다. 즉 이전처럼 새롭게 한자리를 옮긴 문자열을 비교할때 len(target)*len(S)의 시간이 소요되는것이 아닌 새롭게 옮긴 값에 대해서만 hash value를 변경해주면 됨으로 O(1)의 시간이 소요되게 된다. (자세한 사항은 사실 영상을 봐야된다..)&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-filename=&quot;rabin0.gif&quot; data-origin-width=&quot;416&quot; data-origin-height=&quot;144&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cDedpR/btsddsJWZ04/LklD9KkNdFWui07kqtTO31/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cDedpR/btsddsJWZ04/LklD9KkNdFWui07kqtTO31/img.gif&quot; data-alt=&quot;출처 : https://www.sparknotes.com/cs/searching/hashtables/section4/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cDedpR/btsddsJWZ04/LklD9KkNdFWui07kqtTO31/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cDedpR/btsddsJWZ04/LklD9KkNdFWui07kqtTO31/img.gif&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;416&quot; height=&quot;144&quot; data-filename=&quot;rabin0.gif&quot; data-origin-width=&quot;416&quot; data-origin-height=&quot;144&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : https://www.sparknotes.com/cs/searching/hashtables/section4/&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;hash value를 구하는 방법으로는 여러가지 방법이 활용될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드는 hash value를 문자열을 ascii의 숫자로 변경을 해주어 더해주고 임의로 정한 수의 나머지를 구해주는 원리로 구하여 문자열을 비교해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1682684890588&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# pat  -&amp;gt; pattern
# txt  -&amp;gt; text
# q    -&amp;gt; A prime number
d = 12 # hash value를 구하기 위한 임의의값
q = 102 # hash value를 구하기 위한 임의의값

def search(pat, txt):
    M = len(pat)
    N = len(txt)

    i = 0
    j = 0
    p = 0  # hash value for pattern
    t = 0  # hash value for txt
    h = 1

    # The value of h would be &quot;pow(d, M-1)%q&quot;
    for i in range(M - 1):
        h = (h * d) % q

    # Calculate the hash value of pattern and first window of text
    for i in range(M):
        p = (d * p + ord(pat[i])) % q
        t = (d * t + ord(txt[i])) % q

    # Slide the pattern over text one by one
    for i in range(N - M + 1):
        # Check the hash values of current window of text and
        # pattern if the hash values match then only check
        # for characters one by one
        if p == t:
            # Check for characters one by one
            for j in range(M):
                if txt[i + j] != pat[j]:
                    break
                else:
                    j += 1

            # if p == t and pat[0...M-1] = txt[i, i+1, ...i+M-1]
            if j == M:
                print(&quot;Pattern found at index &quot; + str(i))

        # Calculate hash value for next window of text: Remove
        # leading digit, add trailing digit
        if i &amp;lt; N - M:
            t = (d * (t - ord(txt[i]) * h) + ord(txt[i + M])) % q

            # We might get negative values of t, converting it to
            # positive
            if t &amp;lt; 0:
                t = t + q


# Driver Code
if __name__ == '__main__':
    txt = &quot;GEEKS FOR GEEKS&quot;
    pat = &quot;GEEK&quot;

    # Function Call
    search(pat, txt)
    
    
#### output ####
Pattern found at index 0
Pattern found at index 10&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처 : &lt;a href=&quot;https://www.youtube.com/watch?v=qgsAZe5XhP0&amp;amp;t=402s&quot;&gt;https://www.youtube.com/watch?v=qgsAZe5XhP0&amp;amp;t=402s&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=qgsAZe5XhP0&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/iJiX2/hySqP5FdtO/OLiIVw5rgGtnBToMPKAw3k/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=916_132_1126_360&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;스트링 매칭 O(N) 만에 하는 법 | Rabin-Karp Substring Search Algorithm&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/qgsAZe5XhP0&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/rabin-karp-algorithm-for-pattern-searching/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.geeksforgeeks.org/rabin-karp-algorithm-for-pattern-searching/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1682684487433&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;Rabin-Karp Algorithm for Pattern Searching - GeeksforGeeks&quot; data-og-description=&quot;A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.&quot; data-og-host=&quot;www.geeksforgeeks.org&quot; data-og-source-url=&quot;https://www.geeksforgeeks.org/rabin-karp-algorithm-for-pattern-searching/&quot; data-og-url=&quot;https://www.geeksforgeeks.org/rabin-karp-algorithm-for-pattern-searching/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cf5jVc/hySq2DRzL2/5A63vHpHKZYebSKZF8hUI0/img.png?width=200&amp;amp;height=200&amp;amp;face=0_0_200_200,https://scrap.kakaocdn.net/dn/X6Gaa/hySqWwSbOD/K7VWIqxei6JGcX7FPMINk0/img.png?width=717&amp;amp;height=432&amp;amp;face=0_0_717_432,https://scrap.kakaocdn.net/dn/chgWl6/hySqVxWB3J/dnaaBNUrQmKkFbCphXrK4K/img.png?width=717&amp;amp;height=432&amp;amp;face=0_0_717_432&quot;&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/rabin-karp-algorithm-for-pattern-searching/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.geeksforgeeks.org/rabin-karp-algorithm-for-pattern-searching/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cf5jVc/hySq2DRzL2/5A63vHpHKZYebSKZF8hUI0/img.png?width=200&amp;amp;height=200&amp;amp;face=0_0_200_200,https://scrap.kakaocdn.net/dn/X6Gaa/hySqWwSbOD/K7VWIqxei6JGcX7FPMINk0/img.png?width=717&amp;amp;height=432&amp;amp;face=0_0_717_432,https://scrap.kakaocdn.net/dn/chgWl6/hySqVxWB3J/dnaaBNUrQmKkFbCphXrK4K/img.png?width=717&amp;amp;height=432&amp;amp;face=0_0_717_432');&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;Rabin-Karp Algorithm for Pattern Searching - GeeksforGeeks&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.geeksforgeeks.org&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>Algorithm</category>
      <category>python</category>
      <category>Rabin-Karp</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/557</guid>
      <comments>https://bitrader.tistory.com/557#entry557comment</comments>
      <pubDate>Thu, 27 Apr 2023 22:46:52 +0900</pubDate>
    </item>
    <item>
      <title>Palindrome</title>
      <link>https://bitrader.tistory.com/555</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Yjgmw3rMof4&amp;amp;t=3s&quot;&gt;https://www.youtube.com/watch?v=Yjgmw3rMof4&amp;amp;t=3s&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=Yjgmw3rMof4&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/iCvo4/hySq1K2mx6/kLdIk69aotMLfXubOhJqzk/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-original-url=&quot;&quot; data-video-title=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/Yjgmw3rMof4&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/valid-palindrome/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://leetcode.com/problems/valid-palindrome/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1682602856932&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;Valid Palindrome - LeetCode&quot; data-og-description=&quot;Can you solve this real interview question? Valid Palindrome - A phrase is a palindrome if, after converting all uppercase letters into lowercase letters and removing all non-alphanumeric characters, it reads the same forward and backward. Alphanumeric cha&quot; data-og-host=&quot;leetcode.com&quot; data-og-source-url=&quot;https://leetcode.com/problems/valid-palindrome/&quot; data-og-url=&quot;https://leetcode.com/problems/valid-palindrome/description&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/doJxRd/hySqTF60qB/sCsFjHPCdEdMHape3yVxT0/img.png?width=500&amp;amp;height=260&amp;amp;face=0_0_500_260,https://scrap.kakaocdn.net/dn/x500e/hySpJSE7jC/7y4kGsIXusBkIPN9KEXp31/img.png?width=924&amp;amp;height=222&amp;amp;face=0_0_924_222&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/valid-palindrome/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://leetcode.com/problems/valid-palindrome/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/doJxRd/hySqTF60qB/sCsFjHPCdEdMHape3yVxT0/img.png?width=500&amp;amp;height=260&amp;amp;face=0_0_500_260,https://scrap.kakaocdn.net/dn/x500e/hySpJSE7jC/7y4kGsIXusBkIPN9KEXp31/img.png?width=924&amp;amp;height=222&amp;amp;face=0_0_924_222');&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;Valid Palindrome - LeetCode&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Can you solve this real interview question? Valid Palindrome - A phrase is a palindrome if, after converting all uppercase letters into lowercase letters and removing all non-alphanumeric characters, it reads the same forward and backward. Alphanumeric cha&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;leetcode.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;pre id=&quot;code_1683958752936&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution:
    def isPalindrome(self, s: str) -&amp;gt; bool:

        tmp = ''
        for each in s:
            if each.isalnum():
                tmp += each.lower()
        
        if tmp == tmp[::-1]:
            return True
        else:
            return False&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘/리트코드</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/555</guid>
      <comments>https://bitrader.tistory.com/555#entry555comment</comments>
      <pubDate>Thu, 27 Apr 2023 22:41:01 +0900</pubDate>
    </item>
    <item>
      <title>516. Longest Palindromic Subsequence</title>
      <link>https://bitrader.tistory.com/546</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/longest-palindromic-subsequence/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://leetcode.com/problems/longest-palindromic-subsequence/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1682124124940&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;Longest Palindromic Subsequence - LeetCode&quot; data-og-description=&quot;Can you solve this real interview question? Longest Palindromic Subsequence - Given a string s, find the longest palindromic subsequence's length in s. A subsequence is a sequence that can be derived from another sequence by deleting some or no elements wi&quot; data-og-host=&quot;leetcode.com&quot; data-og-source-url=&quot;https://leetcode.com/problems/longest-palindromic-subsequence/&quot; data-og-url=&quot;https://leetcode.com/problems/longest-palindromic-subsequence/description&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cjJ8fQ/hySmUyzf7V/zAGuAhKTUj7KK1QhbN9Yok/img.png?width=500&amp;amp;height=260&amp;amp;face=0_0_500_260,https://scrap.kakaocdn.net/dn/cXbb8s/hySmXovQuZ/yTaz5Iq8X35LulpnJ5kNZ0/img.png?width=924&amp;amp;height=222&amp;amp;face=0_0_924_222&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/longest-palindromic-subsequence/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://leetcode.com/problems/longest-palindromic-subsequence/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cjJ8fQ/hySmUyzf7V/zAGuAhKTUj7KK1QhbN9Yok/img.png?width=500&amp;amp;height=260&amp;amp;face=0_0_500_260,https://scrap.kakaocdn.net/dn/cXbb8s/hySmXovQuZ/yTaz5Iq8X35LulpnJ5kNZ0/img.png?width=924&amp;amp;height=222&amp;amp;face=0_0_924_222');&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;Longest Palindromic Subsequence - LeetCode&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Can you solve this real interview question? Longest Palindromic Subsequence - Given a string s, find the longest palindromic subsequence's length in s. A subsequence is a sequence that can be derived from another sequence by deleting some or no elements wi&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;leetcode.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;pre id=&quot;code_1682124135459&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution:
    def longestPalindromeSubseq(self, s: str) -&amp;gt; int:
        n = len(s)
        # create a 2D array of size n x n to store the lengths of longest palindromic subsequence
        dp = [[0]*n for _ in range(n)]
        
        # each character is a palindrome of length 1
        for i in range(n):
            dp[i][i] = 1
            
        # loop through the string s in reverse order
        for i in range(n-1, -1, -1):
            # loop through the string s in forward order from i+1
            for j in range(i+1, n):
                if s[i] == s[j]:
                    # if characters at i and j match, add 2 to the length of longest palindromic subsequence
                    dp[i][j] = dp[i+1][j-1] + 2
                else:
                    # if characters at i and j don't match, take the maximum of two possibilities:
                    # 1. exclude character at i and find longest palindromic subsequence in the remaining substring s[i+1:j+1]
                    # 2. exclude character at j and find longest palindromic subsequence in the remaining substring s[i:j]
                    dp[i][j] = max(dp[i+1][j], dp[i][j-1])
                    
        # length of longest palindromic subsequence is stored in dp[0][n-1]
        return dp[0][n-1]&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘/리트코드</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/546</guid>
      <comments>https://bitrader.tistory.com/546#entry546comment</comments>
      <pubDate>Sat, 22 Apr 2023 09:42:19 +0900</pubDate>
    </item>
    <item>
      <title>662. Maximum Width of Binary Tree</title>
      <link>https://bitrader.tistory.com/545</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/maximum-width-of-binary-tree/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://leetcode.com/problems/maximum-width-of-binary-tree/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1681956283059&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;Maximum Width of Binary Tree - LeetCode&quot; data-og-description=&quot;Can you solve this real interview question? Maximum Width of Binary Tree - Given the root of a binary tree, return the maximum width of the given tree. The maximum width of a tree is the maximum width among all levels. The width of one level is defined as &quot; data-og-host=&quot;leetcode.com&quot; data-og-source-url=&quot;https://leetcode.com/problems/maximum-width-of-binary-tree/&quot; data-og-url=&quot;https://leetcode.com/problems/maximum-width-of-binary-tree/description&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fjDSE/hySkZVSI8r/WYkhylgKAls4CODhxdvhrK/img.png?width=500&amp;amp;height=260&amp;amp;face=0_0_500_260,https://scrap.kakaocdn.net/dn/bt6rXy/hySkNOEvBS/MXvveYKjZD5dTa0G8DZK40/img.png?width=924&amp;amp;height=222&amp;amp;face=0_0_924_222,https://scrap.kakaocdn.net/dn/b7uwN5/hySkVTqIVQ/D5CeHOu3n93o4KWtKSAbhK/img.jpg?width=442&amp;amp;height=422&amp;amp;face=0_0_442_422&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/maximum-width-of-binary-tree/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://leetcode.com/problems/maximum-width-of-binary-tree/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fjDSE/hySkZVSI8r/WYkhylgKAls4CODhxdvhrK/img.png?width=500&amp;amp;height=260&amp;amp;face=0_0_500_260,https://scrap.kakaocdn.net/dn/bt6rXy/hySkNOEvBS/MXvveYKjZD5dTa0G8DZK40/img.png?width=924&amp;amp;height=222&amp;amp;face=0_0_924_222,https://scrap.kakaocdn.net/dn/b7uwN5/hySkVTqIVQ/D5CeHOu3n93o4KWtKSAbhK/img.jpg?width=442&amp;amp;height=422&amp;amp;face=0_0_442_422');&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;Maximum Width of Binary Tree - LeetCode&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Can you solve this real interview question? Maximum Width of Binary Tree - Given the root of a binary tree, return the maximum width of the given tree. The maximum width of a tree is the maximum width among all levels. The width of one level is defined as&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;leetcode.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트리를 이용한 문제이며 트리의 전위순회, 후위순외같은 개념은 흔하지만 level order를 활용한 문제는 흔하지 않아서 생각하는데 약간 시간이 걸렸을 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 Tree의 node마다 값이 주어지는데 가장 넓은 넓이를 가진 층을 찾기 위해서는 각 node들의 값은 쓸모가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;풀이 방법은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 각 층마다의 Node의 위치 값을 계산 (왼쪽노드 : 부모노드 * 2, 오른쪽노드 : 부모노드 * 2 + 1)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* deque를 이용하여 Node의 위치값들을 dictionary에 층마다 정리해서 넣기&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;pre id=&quot;code_1681956297917&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from collections import defaultdict, deque

def level_order(root, level, level_dict):
    
    q = deque()
    q.append((root, level, 1))

    while q:
        
        now, depth, num = q.popleft()

        if not level_dict[depth]:
            level_dict[depth] = []

        level_dict[depth].append(num)
        
        # left append
        if now.left:
            q.append((now.left, depth+1, num*2))
        
        # right append
        if now.right:
            q.append((now.right, depth+1, num*2 + 1))

    return level_dict

class Solution:
    def widthOfBinaryTree(self, root: Optional[TreeNode]) -&amp;gt; int:

        level_dict = defaultdict(list)
        result_dict = level_order(root, 0, level_dict)

        max_length = -1
        for key in result_dict.keys():

            max_value = max(result_dict[key])
            min_value = min(result_dict[key])

            max_length = max(max_length, (max_value - min_value + 1))

        return max_length&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘/리트코드</category>
      <category>leetcode</category>
      <category>levelorder</category>
      <category>Tree</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/545</guid>
      <comments>https://bitrader.tistory.com/545#entry545comment</comments>
      <pubDate>Thu, 20 Apr 2023 11:09:35 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 코딩 테스트 공부</title>
      <link>https://bitrader.tistory.com/540</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/118668&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/118668&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1681392278800&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;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/118668&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bdbJCJ/hyShFOCO7V/WHLkopcKDbWKl24GLTQMwk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/du67am/hySgiHv5JS/Wd0kIPxS54cBeSQDhN77K1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/118668&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/118668&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bdbJCJ/hyShFOCO7V/WHLkopcKDbWKl24GLTQMwk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/du67am/hySgiHv5JS/Wd0kIPxS54cBeSQDhN77K1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&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;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어지는 알고력과 코딩력의 범위가 0 ~ 150으로 범위가 작기 때문에 완전탐색이 가능할 것 같았으나 완전탐색으로 어떻게 접근해야할지도 모르겠고 어차피 시간초과가 나게 되어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DP로 풀어야하는 문제이며 DP로 풀어야겠다고 생각이 든 순간 굉장히 문제가 쉬워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한가지 주의점은 주어진 문제들보다 애초에 알고력 또는 코딩력이 높은 경우의 수인데 이러한 경우의 수를 생각하지 못해서 꽤나 시간을 낭비했다...&lt;/p&gt;
&lt;pre id=&quot;code_1681392270164&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(alp, cop, problems):

    max_alp = 0
    max_cop = 0
    
    for a, b, c, d, e in problems:
        max_alp = max(max_alp, a)
        max_cop = max(max_cop, b)
        
    # 목표 알고력
    alp = min(alp, max_alp)
    cop = min(cop, max_cop)
    
    INF = float('inf')
    dp = [[INF] * (max_cop + 1) for _ in range(max_alp + 1)]
    dp[alp][cop] = 0

    for i in range(alp,max_alp+1):

        for j in range(cop,max_cop+1):

            if (j+1) &amp;lt;= max_cop:
                dp[i][j + 1] = min(dp[i][j] + 1, dp[i][j+1])

            if (i+1) &amp;lt;= max_alp:
                dp[i+1][j] = min(dp[i][j] + 1, dp[i+1][j])

            # alp가 i
            for p_al, p_co, r_al, r_co, cost in problems:
                if i &amp;gt;= p_al and j &amp;gt;= p_co:
                    new_p_al = i + r_al
                    new_p_co = j + r_co

                    if new_p_al &amp;gt; max_alp: new_p_al = max_alp
                    if new_p_co &amp;gt; max_cop: new_p_co = max_cop

                    dp[new_p_al][new_p_co] = min(dp[i][j] + cost, dp[new_p_al][new_p_co])

    return dp[-1][-1]&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘/프로그래머스</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/540</guid>
      <comments>https://bitrader.tistory.com/540#entry540comment</comments>
      <pubDate>Thu, 13 Apr 2023 22:27:15 +0900</pubDate>
    </item>
    <item>
      <title>Leet code Validate Stack Sequences</title>
      <link>https://bitrader.tistory.com/539</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/validate-stack-sequences/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://leetcode.com/problems/validate-stack-sequences/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1681383616850&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;Validate Stack Sequences - LeetCode&quot; data-og-description=&quot;Can you solve this real interview question? Validate Stack Sequences - Given two integer arrays pushed and popped each with distinct values, return true if this could have been the result of a sequence of push and pop operations on an initially empty stack&quot; data-og-host=&quot;leetcode.com&quot; data-og-source-url=&quot;https://leetcode.com/problems/validate-stack-sequences/&quot; data-og-url=&quot;https://leetcode.com/problems/validate-stack-sequences/description&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cz2pfB/hySgrQ7z3l/QD3iAvc40XATjZ3xkK6O50/img.png?width=500&amp;amp;height=260&amp;amp;face=0_0_500_260,https://scrap.kakaocdn.net/dn/bcVGsQ/hySgiNpL6g/6YRgG832c1z1u3HYboDZk1/img.png?width=924&amp;amp;height=222&amp;amp;face=0_0_924_222&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/validate-stack-sequences/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://leetcode.com/problems/validate-stack-sequences/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cz2pfB/hySgrQ7z3l/QD3iAvc40XATjZ3xkK6O50/img.png?width=500&amp;amp;height=260&amp;amp;face=0_0_500_260,https://scrap.kakaocdn.net/dn/bcVGsQ/hySgiNpL6g/6YRgG832c1z1u3HYboDZk1/img.png?width=924&amp;amp;height=222&amp;amp;face=0_0_924_222');&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;Validate Stack Sequences - LeetCode&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Can you solve this real interview question? Validate Stack Sequences - Given two integer arrays pushed and popped each with distinct values, return true if this could have been the result of a sequence of push and pop operations on an initially empty stack&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;leetcode.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;pre id=&quot;code_1681383609133&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution:
    def validateStackSequences(self, pushed: List[int], popped: List[int]) -&amp;gt; bool:

        # pop을 이용하기 위해 역으로 정렬
        popped = popped[::-1]
        pushed_bucket = []

        # pushed에서 하나씩 iter하면서 popped와 일치하는것이 있는지 확인
        for pushed_number in pushed:

            # popped에 일치하는 것이 있다면 로직 진입
            if pushed_number == popped[-1]:
                
                pushed_bucket.append(pushed_number)
                now = pushed_number

                # 이전에 삽입한 원소들도 일치하는것이 있는지 확인하면서
                # 일치한다면 제거
                while True:
                    if now == popped[-1]:
                        pushed_bucket.pop()
                        popped.pop()
                        
                        if len(pushed_bucket):
                            now = pushed_bucket[-1]
                        else:
                            break
                    else:
                        break
            
            # popped[-1]과 일치하지 않는다면 일단 삽입
            else:
                pushed_bucket.append(pushed_number)

        # 아직 남아있는게 있다면 False
        if popped:
            return False
        else:
            return True&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘/리트코드</category>
      <category>leetcode</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/539</guid>
      <comments>https://bitrader.tistory.com/539#entry539comment</comments>
      <pubDate>Thu, 13 Apr 2023 20:00:40 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 표 편집</title>
      <link>https://bitrader.tistory.com/533</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/81303&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/81303&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1680869323640&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;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/81303&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dGKaCo/hyScwrSLu8/IB76OiodkvmqnSeb5Kg6gk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/WsaPR/hyScvsYReS/YWuTw2AlIhhIWI4opKURy1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/81303&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/81303&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dGKaCo/hyScwrSLu8/IB76OiodkvmqnSeb5Kg6gk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/WsaPR/hyScvsYReS/YWuTw2AlIhhIWI4opKURy1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&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;programmers.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;뭔가 잘 푼 풀이같지는 않지만 그래도 한번에 풀어서 블로그에 올려본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선택된 행을 기준으로 위아래로만 움직이며 주어지는 모든 케이스에서 전체 범위를 벗어나는 케이스는 없다는 조건이 있음으로 deque를 사용해도 될 것 같았으나 삭제한 행을 다시 복구하는 과정에서 순서를 맞춰줘야한다는 조건이 있음으로 heap을 사용해서 풀었다.&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;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1680869344161&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import heapq
def solution(n, k, cmds):

    upper_heap = []
    lower_heap = []
    point = -1
    # 현재 가리키고 있는 위치를 기준으로 작으면 upper_heap, 크면 lower_heap
    for i in range(n):
        if i &amp;lt; k: heapq.heappush(upper_heap,-i)
        elif i == k: point = k
        else: heapq.heappush(lower_heap,i)

    removed = []

    for cmd in cmds:
        cmd = cmd.split()


        if cmd[0] == 'D':
            move = int(cmd[1])
            heapq.heappush(upper_heap, -point)
            for i in range(move):
                if i == (move-1):
                    point = heapq.heappop(lower_heap)
                else:
                    heapq.heappush(upper_heap, -heapq.heappop(lower_heap))

        # 대부분의 케이스에서는 현재 포인터를 제거하고 포인터가 아래로 움직이나
        # 상황에 따라서 구분해줄 필요가 있다.
        elif cmd[0] == 'C':
            removed.append(point)
            if not len(upper_heap):
                point = heapq.heappop(lower_heap)
            elif not len(lower_heap):
                point = -heapq.heappop(upper_heap)
            else:
                point = heapq.heappop(lower_heap)

        elif cmd[0] == 'U':
            move = int(cmd[1])
            heapq.heappush(lower_heap, point)
            for i in range(move):
                if i == (move - 1):
                    point = -heapq.heappop(upper_heap)
                else:
                    heapq.heappush(lower_heap, -heapq.heappop(upper_heap))

        # point를 기준으로 upper_heap에 삽입할지 lower_heap에 삽입할지 결정
        elif cmd[0] == 'Z':
            tobe_add = removed.pop()
            if tobe_add &amp;lt; point:
                heapq.heappush(upper_heap, -tobe_add)
            else:
                heapq.heappush(lower_heap, tobe_add)

    # 단순히 정답의 형태로 만들어주기 위한 문자열 작업
    result = ['X']*n
    while upper_heap:
        tmp = -heapq.heappop(upper_heap)
        result[tmp] = 'O'
    result[point] = 'O'
    while lower_heap:
        tmp = heapq.heappop(lower_heap)
        result[tmp] = 'O'

    return ''.join(result)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘/프로그래머스</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/533</guid>
      <comments>https://bitrader.tistory.com/533#entry533comment</comments>
      <pubDate>Fri, 7 Apr 2023 21:12:33 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 [1차] 추석 트래픽 python</title>
      <link>https://bitrader.tistory.com/530</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/17676?language=python3&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/17676?language=python3&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1680788160852&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;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/17676?language=python3&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bcovra/hyScH7bNWp/hKk3AxRyegMkl2apbJnUFK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/eAdnz/hyScFathtc/aWNsgXW7NhDxxtNCdC2ZF0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/17676?language=python3&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/17676?language=python3&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bcovra/hyScH7bNWp/hKk3AxRyegMkl2apbJnUFK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/eAdnz/hyScFathtc/aWNsgXW7NhDxxtNCdC2ZF0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&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;programmers.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;프로그래머스에서 Level3로 분류된 문제이지만 굉장히 쉽다 중간의 까다로운 구현과정과 곂치는 구간에 대한 판단에서 실수가 있다면 헤멜수도 있을것 같다. 중요포인트는 아래와 같다.&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;* python timedelta를 이용하여 시간에 대한 계산을 간편하게 하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 트래픽이 곂치는 조건을 명확하게 파악하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 입력의 총 길이가 2000이 최대임으로 이중 for문 즉 완전탐색으로 곂치는 트래픽을 탐색해도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1680788294847&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import datetime

def solution(lines):

    time_lines = []
    for line in lines:

        tmp = line.split()
        end = tmp[1]
        new_end = datetime.timedelta(hours=int(end[:2]), minutes=int(end[3:5]), \
                                     seconds=int(end[6:8]), milliseconds=int(end[9:]))

        seconds = tmp[2].split('.')
        second = seconds[0]

        # s만 딸랑 들어오는 case가 있음 짜증 ...
        try:
            mili_s = seconds[1][:-1]
        except:
            second = second.replace('s','')
            mili_s = 0

        second_delta = datetime.timedelta(seconds=int(second), milliseconds=int(mili_s))
        # 3초가 넘어가는 처리 요청은 timeout으로 3초로 처리함
        if second_delta &amp;gt; datetime.timedelta(seconds=3): second_delta = datetime.timedelta(seconds=3)
        second_delta -= datetime.timedelta(milliseconds=1)

        start = new_end - second_delta
        time_lines.append((start, new_end))

    result = []
    # 2중 for문으로 모든 케이스를 탐색
    for i in range(len(time_lines)):
        count = 0
        start_time = time_lines[i][1]
        end_time = start_time + datetime.timedelta(milliseconds=999)

        for j in range(len(time_lines)):

            tmp_start = time_lines[j][0]
            tmp_end = time_lines[j][1]

            # 곂치는지 안곂치는지 판단하는 조건
            if start_time &amp;lt;= tmp_end and tmp_start &amp;lt;= end_time:
                count += 1

        result.append(count)

    return max(result)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘/프로그래머스</category>
      <category>python</category>
      <category>timedelta</category>
      <category>프로그래머스</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/530</guid>
      <comments>https://bitrader.tistory.com/530#entry530comment</comments>
      <pubDate>Thu, 6 Apr 2023 22:38:56 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 문제 후보키</title>
      <link>https://bitrader.tistory.com/528</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42890&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/42890&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1680597858053&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;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42890&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bbywGk/hySaY1T1mX/XRWMOEC2echlGNvijNGoD1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cfE1An/hyR9JFek7g/PkKIVrgkTs4x0lGGl3ff30/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42890&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42890&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bbywGk/hySaY1T1mX/XRWMOEC2echlGNvijNGoD1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cfE1An/hyR9JFek7g/PkKIVrgkTs4x0lGGl3ff30/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&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;programmers.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;카카오 코딩테스트의 문제 특성상 Level2의 문제는 탐색 공간이 크지 않아 완전탐색으로 간단하게 해결이 가능하지만 세부적인 구현사항에서는 나름 시간을 요구하는 구현사항이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* combination으로 모든 column의 조합을 구하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* set을 이용해서 unique한 특성을 가졌는지 체크하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 이미 구해진 후보키들과 비교해서 minimality한지 체크하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1680597865582&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from itertools import combinations
def check_minimality(candidate_key, comb):

    # 이미 조건을 만족한 후보키 전부와 비교해서 곂치는 값이 있으면 False 없으면 True
    for key in candidate_key:
        tmp_count = 0
        for column in key:
            if column in comb:
                tmp_count +=1
        if tmp_count == len(key):
            return False
    return True

def solution(relation):

    # column의 개수
    num = len(relation[0])

    # 1개 부터 column의 최대개수까지 모든 조합을 구해서 넣어줌
    all_combs = []
    for i in range(1, num+1):
        tmp = list(combinations(range(num),i))
        all_combs.extend(tmp)

    # 결과 저장과 minimality를 확인하기 위한 list
    result = []

    for comb in all_combs:
        tmp_set = set()

        # 각각의 column 조합별로 직접 set에 넣어줌, 중복되는 row는 자동 삭제
        for row in relation:
            uniqueness = []
            for idx in comb:
                uniqueness.append(row[idx])
            tmp_set.add(tuple(uniqueness))

        # set의 길이와 row의 길이가 같다면 unique 하다고 판단
        if len(tmp_set) == len(relation):

            # minimality도 만족하면 최종결과에 저장
            if check_minimality(result, comb): result.append(comb)

    return len(result)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘/프로그래머스</category>
      <category>python</category>
      <category>완전탐색</category>
      <category>프로그래머스</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/528</guid>
      <comments>https://bitrader.tistory.com/528#entry528comment</comments>
      <pubDate>Tue, 4 Apr 2023 17:45:57 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 징검다리</title>
      <link>https://bitrader.tistory.com/522</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이분탐색을 활용하여 해결이 가능한 문제이다.&lt;/p&gt;
&lt;pre id=&quot;code_1679531821984&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(stones, k):

    min_value = min(stones)
    max_value = max(stones)
    result = []
    while min_value &amp;lt;= max_value:

        flag = False
        mid = (min_value + max_value) // 2

        count = 0
        for each in stones:

            if each &amp;lt;= mid:
                count += 1
            else:
                count = 0

            if count == k:
                flag = True
                break

        if flag:
            result.append(mid)
            max_value = mid - 1
        else:
            min_value = mid + 1

    return min(result)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘/프로그래머스</category>
      <category>BinarySearch</category>
      <category>프로그래머스</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/522</guid>
      <comments>https://bitrader.tistory.com/522#entry522comment</comments>
      <pubDate>Thu, 23 Mar 2023 09:37:03 +0900</pubDate>
    </item>
    <item>
      <title>udemy mongodb 강의 후기</title>
      <link>https://bitrader.tistory.com/513</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로나 이전 직장에서나 mongodb를 접할 기회는 많이 있었으나 query나 기타 부가적인 기능에 대해서는 자세하게 알지 못해서 이번 기회에 강의가 싸게 풀려서 한번 결제를 하고 들어보기로 했다. 내가 결제했을 때는 23000원이었는데 지금은 10만 원이 넘어가는 가격이 되었다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2만 원의 값어치보단 좋은 강의인 것 같은데 10만 원까지는 하지는 않는 것 같다...&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;a href=&quot;https://www.udemy.com/course/mongodb-the-complete-developers-guide/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.udemy.com/course/mongodb-the-complete-developers-guide/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678792236423&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;udemy_com:course&quot; data-og-title=&quot;MongoDB - The Complete Developer's Guide 2023&quot; data-og-description=&quot;Master MongoDB Development for Web &amp;amp; Mobile Apps. CRUD Operations, Indexes, Aggregation Framework - All about MongoDB!&quot; data-og-host=&quot;www.udemy.com&quot; data-og-source-url=&quot;https://www.udemy.com/course/mongodb-the-complete-developers-guide/&quot; data-og-url=&quot;https://www.udemy.com/course/mongodb-the-complete-developers-guide/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/HAb1p/hyRU2SH5yM/8VfAPNKNasnkjK48PalO50/img.jpg?width=480&amp;amp;height=270&amp;amp;face=0_0_480_270,https://scrap.kakaocdn.net/dn/NrqET/hyRWAUzh43/XVdfqZ8jaKQBqHwkWEUFr0/img.jpg?width=480&amp;amp;height=270&amp;amp;face=0_0_480_270&quot;&gt;&lt;a href=&quot;https://www.udemy.com/course/mongodb-the-complete-developers-guide/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.udemy.com/course/mongodb-the-complete-developers-guide/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/HAb1p/hyRU2SH5yM/8VfAPNKNasnkjK48PalO50/img.jpg?width=480&amp;amp;height=270&amp;amp;face=0_0_480_270,https://scrap.kakaocdn.net/dn/NrqET/hyRWAUzh43/XVdfqZ8jaKQBqHwkWEUFr0/img.jpg?width=480&amp;amp;height=270&amp;amp;face=0_0_480_270');&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;MongoDB - The Complete Developer's Guide 2023&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Master MongoDB Development for Web &amp;amp; Mobile Apps. CRUD Operations, Indexes, Aggregation Framework - All about MongoDB!&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.udemy.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;p data-ke-size=&quot;size16&quot;&gt;강의 구성내용은 아래와 같다. 총 19개의 section으로 이루어져 있으며 전체 강의 시간은 17시간이다. 하지만 17시간에 담기 힘든 내용을 꽤나 잘 압축해서 하지만 빠지는 내용은 없이 거의 다 다루었다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 나의 경우에는 mongodb를 처음에는 official document만 보고 기본적인 crud 기능을 사용하면서 배웠었는데 그때 가장 힘들었던 부분이 mongodb의 특성이 nested array나 nested document의 요소들을 건드려야 할 때였는데 구글이나 스택오버 플로우에 아무리 검색을 해봐도 만족스러울 만한 내용을 찾지 못했다. 하지만 해당 강의를 듣고는 이제는 그러한 부분에서는 더 이상 발목 잡히지 않을 것 같다.&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;또한 aggregation의 내용같은 경우에도 차근차근 내용을 알려주어서 쉽게 이해할 수 있었다. aggregation에 대한 내용을 이해하기 전까지는 당연히 단순히 find로 모든 데이터를 찾아와서 처리 로직은 내가 작성했다. 그러한 비효율적이고 느린 작업을 이제는 더 이상 반복하지 않아도 된다.&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;indexing 같은 경우에는 얕게 다루지 않지만 그렇다고 엄청 깊게는 다루지 않는 느낌이다. 하지만 충분히 실제 사용에서 도움이 될 만한 내용을 많이 배우게 되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUvzHb/btr3Q8HQhGX/eErnwNMkGdJOScoazaeTyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUvzHb/btr3Q8HQhGX/eErnwNMkGdJOScoazaeTyK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;714&quot; data-origin-height=&quot;595&quot; data-filename=&quot;mongodb course.PNG&quot; style=&quot;width: 41.8003%; margin-right: 10px;&quot; data-widthpercent=&quot;42.29&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUvzHb/btr3Q8HQhGX/eErnwNMkGdJOScoazaeTyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUvzHb%2Fbtr3Q8HQhGX%2FeErnwNMkGdJOScoazaeTyK%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;714&quot; height=&quot;595&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjgQkt/btr3QwaS1Bj/1qeQ6jvc4BDfmGXxQGyxI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjgQkt/btr3QwaS1Bj/1qeQ6jvc4BDfmGXxQGyxI0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;709&quot; data-origin-height=&quot;433&quot; data-filename=&quot;mongodb course2.PNG&quot; style=&quot;width: 57.0369%;&quot; data-widthpercent=&quot;57.71&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjgQkt/btr3QwaS1Bj/1qeQ6jvc4BDfmGXxQGyxI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjgQkt%2Fbtr3QwaS1Bj%2F1qeQ6jvc4BDfmGXxQGyxI0%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;709&quot; height=&quot;433&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;출처 :&amp;nbsp;https://www.udemy.com/course/mongodb-the-complete-developers-guide/&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;사실 원래 해당 강의를 듣는 목적은 mongodb의 sharding을 구성하는 방법이라든지 좀 더 시스템적이고 딥한 내용까지 알기 위함이었는데 해당 내용에 대해서는 개념정도만 강의에 설명되어 있고 실제 구축하는 방법 등은 포함되어있지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에 Complete developer's Guide인것 같다. 그 외에도 보안설정이라는가 암호화 설정등의 내용도 기본정도의 내용이 포함되어 있다.&amp;nbsp;&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;또한 마지막에는 mongodb atlas의 사용방법과 stitch를 이용하여 실제 어플리케이션 개발 예제를 2시간이 넘게 할애해서 실제 사용예시를 보여준다.&amp;nbsp;&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;전반적으로 굉장히 좋은 내용이였다고 생각한다. 몰랐다면 계속 비효율적이고 느린 작업을 계속하고 있었을 텐데 해당 내용을 알게 되어서 이제는 비효율적으로는 작업을 하지 않게 될 것 같다. 백엔드 개발자들은 mongodb를 사용하게 된다면 필수로 알아야 되는 내용이라고 생각한다(mongodb 잘 안 쓰는 것 같은데 상관없나..)&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;그리고 수료증도 준다. 이제 mongodb를 이용해서 뭔가를 만들러 가봐야겠다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;수료증.PNG&quot; data-origin-width=&quot;958&quot; data-origin-height=&quot;644&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MzD18/btr3Q9tc94T/lAlyLErrQhBKxvKcACJkM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MzD18/btr3Q9tc94T/lAlyLErrQhBKxvKcACJkM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MzD18/btr3Q9tc94T/lAlyLErrQhBKxvKcACJkM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMzD18%2Fbtr3Q9tc94T%2FlAlyLErrQhBKxvKcACJkM1%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;655&quot; height=&quot;440&quot; data-filename=&quot;수료증.PNG&quot; data-origin-width=&quot;958&quot; data-origin-height=&quot;644&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>개발</category>
      <category>MongoDB</category>
      <category>Udemy</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/513</guid>
      <comments>https://bitrader.tistory.com/513#entry513comment</comments>
      <pubDate>Tue, 14 Mar 2023 20:32:22 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 무인도 여행</title>
      <link>https://bitrader.tistory.com/505</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/154540&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/154540&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678428424410&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;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/154540&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/crqRyE/hyRTSgCxHg/knukSIo4nKwBhxxba85nj1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/ckTxt0/hyRSLQXDZI/KUGbzkvYByTAa4FoszPRYK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/154540&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/154540&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/crqRyE/hyRTSgCxHg/knukSIo4nKwBhxxba85nj1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/ckTxt0/hyRSLQXDZI/KUGbzkvYByTAa4FoszPRYK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&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;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 dfs, bfs 문제이며 나는 dfs로 풀었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어지는 탐색공간의 크기가 작아서 recursionlimit에 안 걸릴 줄 알았건만 recursion limit 때문에 오류가 발생한 지도 모르고 잠깐 고생했었다. 문제를 dfs로 푼다면 recursionlimit을 설정해 주는 것을 까먹지 말자&lt;/p&gt;
&lt;pre id=&quot;code_1678428407254&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import sys
sys.setrecursionlimit(1000000)

idx = [[0,1],[1,0],[-1,0],[0,-1]]

def dfs(x, y, visited, matrix, food):
    
    visited[x][y] = True
    food = food + int(matrix[x][y])
    
    for each in idx:
        
        new_x = x + each[0]
        new_y = y + each[1]
        
        if 0 &amp;lt;= new_x &amp;lt; len(matrix) \
            and 0 &amp;lt;= new_y &amp;lt; len(matrix[0]) \
            and not visited[new_x][new_y] \
            and matrix[new_x][new_y] != 'X':

            food = dfs(new_x,new_y, visited, matrix, food)
    
    return food
            

def solution(maps):
    
    matrix = maps
    visited = [[False]*len(matrix[0]) for _ in range(len(matrix))]
    
    result = []
    for i in range(len(matrix)):
        for j in range(len(matrix[0])):
            if matrix[i][j] != 'X' and not visited[i][j]:
                tmp = dfs(i,j,visited, matrix,0)
                result.append(tmp)

    if not len(result):
        return [-1]
    else:
        return sorted(result)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘/프로그래머스</category>
      <category>BFS</category>
      <category>dfs</category>
      <category>python</category>
      <category>프로그래머스</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/505</guid>
      <comments>https://bitrader.tistory.com/505#entry505comment</comments>
      <pubDate>Fri, 10 Mar 2023 15:08:54 +0900</pubDate>
    </item>
    <item>
      <title>python 동시성에 대한 정리</title>
      <link>https://bitrader.tistory.com/501</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;동시성이라는 의미는 동시에 여러 가지 일이 발생하는 것을 의미하며 프로그래밍에서는 여러 작업을 동시에 처리하는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;python에서는 해당개념을 언급할때 thread, task, multiprocessing이 언급되는데 진정한 의미에서 동시에 작업을 처리하는 것은 multiprocessing만 해당된다. 따라서 본 글에서는 위의 3가지 개념에 대한 차이점과 용도를 정리하고자 한다.&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;먼저 Threading과 Asyncio는 하나의 CPU 코어에서만 작업이 이루어지만 동시에 여러 작업을 왔다 갔다 하면서 처리하기 때문에 여러 작업을 동시에 수행하는 것처럼 보인다. 하지만 언급했듯이 실제로 작업을 수행하는 CPU 코어는 하나이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 것을 multitasking이라고 하는데 Threading과 Asyncio는 작업을 차지하는 방식에서 차이가 있으며, Threading은 pre-emptive multitasking이고, Asyncio는 cooperative multitasking이다.&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;Pre-emptive multitasking은 운영체가 각각의 thread에 대해서 인지하고 있고 언제든 다른 작업을 실행하기 위해 현재 작업 중인 thread를 잠시 중지할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 cooperative multitasking은 하나의 작업이 준비가 되었을때 다른 작업과 협력하여 작업이 스위칭된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;병렬화란 무엇인가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;thread, Asyncio, multiprocessing 중에서 진정으로 병렬작업을 수행하는 것은 multiprocessing이며 multiprocessing으로 생성된 작업은 각각의 cpu 코어에서 작업을 수행하며 완전히 다른 프로그램으로 생각해도 될 정도이다. 또한 각각의 프로세스가 메모리등의 자원을 따로 가지고 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;diff.PNG&quot; data-origin-width=&quot;722&quot; data-origin-height=&quot;336&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGNZKY/btr2xUc7OnE/AvjSXqZEjO50Zg7QxtE120/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGNZKY/btr2xUc7OnE/AvjSXqZEjO50Zg7QxtE120/img.png&quot; data-alt=&quot;Threading, asyncio, multiprocessing 차이점&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGNZKY/btr2xUc7OnE/AvjSXqZEjO50Zg7QxtE120/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGNZKY%2Fbtr2xUc7OnE%2FAvjSXqZEjO50Zg7QxtE120%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;623&quot; height=&quot;290&quot; data-filename=&quot;diff.PNG&quot; data-origin-width=&quot;722&quot; data-origin-height=&quot;336&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Threading, asyncio, multiprocessing 차이점&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;CPU-bound와 I/O-bound&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I/O-bound는 우리의 프로그램의 속도를 늦게하는 프로그램으로 외부 자원의 input/output을 받아 수행해야 하는 작업을 말한다. 외부 자원의 작업은 보통 속도가 느림으로 우리의 프로그램은 대부분의 시간을 외부 작업을 기다리느라 보내게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 I/O-bound에는 network 연결, file system 작업등이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU-bound는 네트워크 연결이나 file에 접근하지 않고 계산에 집중하는 작업을 의미하며 프로그램의 속도를 제한하는 요소로는 오직 CPU의 성능밖에 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OdVOL/btr2D9Hgp8e/dKq9j0bz8kDzoBk0uxN5J0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OdVOL/btr2D9Hgp8e/dKq9j0bz8kDzoBk0uxN5J0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;702&quot; data-origin-height=&quot;234&quot; data-filename=&quot;cpu_bound.PNG&quot; style=&quot;width: 46.2815%; margin-right: 10px;&quot; data-widthpercent=&quot;46.83&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OdVOL/btr2D9Hgp8e/dKq9j0bz8kDzoBk0uxN5J0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOdVOL%2Fbtr2D9Hgp8e%2FdKq9j0bz8kDzoBk0uxN5J0%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;702&quot; height=&quot;234&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6qNbb/btr2CvD5kGh/j8C6BYacfKXInAacXwklm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6qNbb/btr2CvD5kGh/j8C6BYacfKXInAacXwklm1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;712&quot; data-origin-height=&quot;209&quot; data-filename=&quot;io_cound.PNG&quot; style=&quot;width: 52.5557%;&quot; data-widthpercent=&quot;53.17&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6qNbb/btr2CvD5kGh/j8C6BYacfKXInAacXwklm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6qNbb%2Fbtr2CvD5kGh%2Fj8C6BYacfKXInAacXwklm1%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;712&quot; height=&quot;209&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;I/O bound, CPU bound&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;diff2.PNG&quot; data-origin-width=&quot;711&quot; data-origin-height=&quot;273&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPlf6f/btr2wt7TTN6/MxBQB9S4zaSyllC4uNt2Rk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPlf6f/btr2wt7TTN6/MxBQB9S4zaSyllC4uNt2Rk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPlf6f/btr2wt7TTN6/MxBQB9S4zaSyllC4uNt2Rk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPlf6f%2Fbtr2wt7TTN6%2FMxBQB9S4zaSyllC4uNt2Rk%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;643&quot; height=&quot;247&quot; data-filename=&quot;diff2.PNG&quot; data-origin-width=&quot;711&quot; data-origin-height=&quot;273&quot;/&gt;&lt;/span&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;따라서 I/O-bound 의 속도문제를 해결하기 위해서는 외부작업을 기다리는 작업을 순차적으로 실행하지 않고 기다리는 작업을 overlapping 하면서 해결을 해야 한다. 따라서 Thread와 asyncio가 해당 작업에 적합하다고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면에 CPU-bound 작업에서는 thread와 asyncio를 사용한다고 해도 overlapping할 수 있는 작업이 없으며 오히려 여러 개가 생성된 thread나 asyncio의 multitasking에 사용되는 오버헤드 때문에 그냥 코드를 작성한 것보다 시간이 더 소요될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 CPU-bound 작업에는 아예 작업을 나눠서 동시에 실행할 수 있는 multiprocessing이 더 적합하다고 할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MbzaS/btr2C8vlLFM/i4daBze9axfyVlOCNMNxcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MbzaS/btr2C8vlLFM/i4daBze9axfyVlOCNMNxcK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;510&quot; data-origin-height=&quot;238&quot; data-filename=&quot;thread_multi.PNG&quot; style=&quot;width: 39.7829%; margin-right: 10px;&quot; data-widthpercent=&quot;40.73&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MbzaS/btr2C8vlLFM/i4daBze9axfyVlOCNMNxcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMbzaS%2Fbtr2C8vlLFM%2Fi4daBze9axfyVlOCNMNxcK%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;510&quot; height=&quot;238&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btMC8A/btr2FfNYIVa/CVbVUWq9sAHEk3XOOqIfl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btMC8A/btr2FfNYIVa/CVbVUWq9sAHEk3XOOqIfl0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;491&quot; data-origin-height=&quot;280&quot; data-filename=&quot;asyncio_mutli.PNG&quot; style=&quot;width: 32.5557%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btMC8A/btr2FfNYIVa/CVbVUWq9sAHEk3XOOqIfl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtMC8A%2Fbtr2FfNYIVa%2FCVbVUWq9sAHEk3XOOqIfl0%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;491&quot; height=&quot;280&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9nMPD/btr2H5D7klY/YzRVYy6u7EUfHNnC0IA5Dk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9nMPD/btr2H5D7klY/YzRVYy6u7EUfHNnC0IA5Dk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;711&quot; data-origin-height=&quot;521&quot; data-filename=&quot;cpubound_multi.PNG&quot; style=&quot;width: 25.3358%;&quot; data-widthpercent=&quot;25.94&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9nMPD/btr2H5D7klY/YzRVYy6u7EUfHNnC0IA5Dk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9nMPD%2Fbtr2H5D7klY%2FYzRVYy6u7EUfHNnC0IA5Dk%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;711&quot; height=&quot;521&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;threading I/O bound, Asyncio I/O bound, CPU bound multiprocessing&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;a href=&quot;https://realpython.com/python-concurrency/#how-to-speed-up-an-io-bound-program&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://realpython.com/python-concurrency/#how-to-speed-up-an-io-bound-program&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678200126935&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;Speed Up Your Python Program With Concurrency &amp;ndash; Real Python&quot; data-og-description=&quot;Learn what concurrency means in Python and why you might want to use it. You'll see a simple, non-concurrent approach and then look into why you'd want threading, asyncio, or multiprocessing.&quot; data-og-host=&quot;realpython.com&quot; data-og-source-url=&quot;https://realpython.com/python-concurrency/#how-to-speed-up-an-io-bound-program&quot; data-og-url=&quot;https://realpython.com/python-concurrency/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/n0JpN/hyRRPZzMV5/KMtxd52nitpO0MUYG0HwLk/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/jmakq/hyRRMIwcuW/mi23Rna7Kh9HZhc2kyPHWK/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/jSTQu/hyRRMocPm5/xus8MMBfgbzKm8etESpGP0/img.png?width=1893&amp;amp;height=2097&amp;amp;face=0_0_1893_2097&quot;&gt;&lt;a href=&quot;https://realpython.com/python-concurrency/#how-to-speed-up-an-io-bound-program&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://realpython.com/python-concurrency/#how-to-speed-up-an-io-bound-program&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/n0JpN/hyRRPZzMV5/KMtxd52nitpO0MUYG0HwLk/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/jmakq/hyRRMIwcuW/mi23Rna7Kh9HZhc2kyPHWK/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/jSTQu/hyRRMocPm5/xus8MMBfgbzKm8etESpGP0/img.png?width=1893&amp;amp;height=2097&amp;amp;face=0_0_1893_2097');&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;Speed Up Your Python Program With Concurrency &amp;ndash; Real Python&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn what concurrency means in Python and why you might want to use it. You'll see a simple, non-concurrent approach and then look into why you'd want threading, asyncio, or multiprocessing.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;realpython.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>concurrency</category>
      <category>multiprocessing</category>
      <category>python</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/501</guid>
      <comments>https://bitrader.tistory.com/501#entry501comment</comments>
      <pubDate>Tue, 7 Mar 2023 23:42:20 +0900</pubDate>
    </item>
    <item>
      <title>python Producer-Consumer(생산자 소비자 문제) Threading</title>
      <link>https://bitrader.tistory.com/500</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Producer-consumer Problem(생산자 소비자 문제는) threading이나 프로세스 동기화 이슈와 관련된 computer science에서 자주 볼 수 있는 문제이다.&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;현재 글에서는 python의 Lock을 이용하여 Producer-comsumer problem을 어떻게 해결하는지 다루고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 예제가 있다. 외부로부터 message를 수신하는 작업과 수신한 message를 db에 저장하는 작업이 있다. 이때 외부에서 들어오는 message는 언제 들어올지 모르며 때때로 너무 많은 양이 들어올 때가 있다. 이 message를 수신하는 작업이 Producer이다. 수신한 message를 db에 저장하는 작업 Consumer이며 작업의 속도가 상대적으로 느리다. 따라서 외부에서 들어오는 message를 누락 없이 제대로 관리를 하려면 Producer와 Comsumer가 동기화되어서 처리되어야 할 필요가 있다.&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;아래는 message를 생성하여 수신하는 상황을 가정한 producer 코드이며 10개의 message만 받고 종료하는 상황을 가정한 코드이다.&lt;/p&gt;
&lt;pre id=&quot;code_1678195022297&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import random 

SENTINEL = object()

def producer(pipeline):
    &quot;&quot;&quot;Pretend we're getting a message from the network.&quot;&quot;&quot;
    for index in range(10):
        message = random.randint(1, 101)
        logging.info(&quot;Producer got message: %s&quot;, message)
        pipeline.set_message(message, &quot;Producer&quot;)

    # Send a sentinel message to tell consumer we're done
    pipeline.set_message(SENTINEL, &quot;Producer&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 consumer 코드이며 producer가 message를 받으면 출력(db에 저장하는 작업을 가정)하며 SENTINEL object가 들어오면 출력은 하지 않는다.&lt;/p&gt;
&lt;pre id=&quot;code_1678195066004&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def consumer(pipeline):
    &quot;&quot;&quot;Pretend we're saving a number in the database.&quot;&quot;&quot;
    message = 0
    while message is not SENTINEL:
        message = pipeline.get_message(&quot;Consumer&quot;)
        if message is not SENTINEL:
            logging.info(&quot;Consumer storing message: %s&quot;, message)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 main section이며 2개의 thread를 생성하여 producer작업과 consumer 작업을 실시한다.&lt;/p&gt;
&lt;pre id=&quot;code_1678195127091&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if __name__ == &quot;__main__&quot;:
    format = &quot;%(asctime)s: %(message)s&quot;
    logging.basicConfig(format=format, level=logging.INFO,
                        datefmt=&quot;%H:%M:%S&quot;)
    # logging.getLogger().setLevel(logging.DEBUG)

    pipeline = Pipeline()
    with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
        executor.submit(producer, pipeline)
        executor.submit(consumer, pipeline)&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;가장 중요한 message가 전달되는 Pipeline class 코드로 Lock을 이용하여 Producer와 Consumer 간에 주고받는 message가 동기화되게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 클래스의 인스턴스가 생성되었을 때는 message가 아무것도 없음으로 consumer를 lock 하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 producer가 message를 생성할 때는 producer를 lock 하고 message를 저장한 이후 consumer를 release 하면서 consumer가 producer로부터 생성된 message에 접근을 할 수 있게 해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;consumer는 get_message 함수를 통하여 우선 다시 consumer lock을 실시하고 producer로부터 생성된 message에 접근을 한다. 이후 message는 처리되었으므로 다시 producer release를 실시하여 producer가 message에 접근을 할 수 있는 상태로 만들어 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1678195171204&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Pipeline:
    &quot;&quot;&quot;
    Class to allow a single element pipeline between producer and consumer.
    &quot;&quot;&quot;
    def __init__(self):
        self.message = 0
        self.producer_lock = threading.Lock()
        self.consumer_lock = threading.Lock()
        self.consumer_lock.acquire()

    def get_message(self, name):
        logging.debug(&quot;%s:about to acquire getlock&quot;, name)
        self.consumer_lock.acquire()
        logging.debug(&quot;%s:have getlock&quot;, name)
        message = self.message
        logging.debug(&quot;%s:about to release setlock&quot;, name)
        self.producer_lock.release()
        logging.debug(&quot;%s:setlock released&quot;, name)
        return message

    def set_message(self, message, name):
        logging.debug(&quot;%s:about to acquire setlock&quot;, name)
        self.producer_lock.acquire()
        logging.debug(&quot;%s:have setlock&quot;, name)
        self.message = message
        logging.debug(&quot;%s:about to release getlock&quot;, name)
        self.consumer_lock.release()
        logging.debug(&quot;%s:getlock released&quot;, name)&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;위의 코드는 producer와 consumer가 한 번의 하나의 메시지만을 동기화하도록 설정한 코드이며 실행을 하면 아래와 같이 message가 한 번에 하나씩만 처리되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1678195458569&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ ./prodcom_lock.py
Producer got data 43
Producer got data 45
Consumer storing data: 43
Producer got data 86
Consumer storing data: 45
Producer got data 40
Consumer storing data: 86
Producer got data 62
Consumer storing data: 40
Producer got data 15
Consumer storing data: 62
Producer got data 16
Consumer storing data: 15
Producer got data 61
Consumer storing data: 16
Producer got data 73
Consumer storing data: 61
Producer got data 22
Consumer storing data: 73
Consumer storing data: 22&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Producer-Consumer Using&lt;span&gt;&amp;nbsp;&lt;/span&gt;Queue&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제는 한번에 하나의 message만 처리할 수 있는 코드이며 여러 개의 message를 처리하고 싶으면 python의 Queue Class를 이용하여 구현이 가능하다.&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;변경된 사항은 thread의 event object를 사용한 것으로 producer와 consumer는 특정 event가 일어날 때까지 thread를 생산하며 소비한다. threading.Event는 하나의 thread가 어떠한 event가 발생했을 때 다른 thread에게 signal을 주는 것을 가능하게 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1678195599251&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if __name__ == &quot;__main__&quot;:
    format = &quot;%(asctime)s: %(message)s&quot;
    logging.basicConfig(format=format, level=logging.INFO,
                        datefmt=&quot;%H:%M:%S&quot;)
    # logging.getLogger().setLevel(logging.DEBUG)

    pipeline = Pipeline()
    event = threading.Event()
    with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
        executor.submit(producer, pipeline, event)
        executor.submit(consumer, pipeline, event)

        time.sleep(0.1)
        logging.info(&quot;Main: about to set event&quot;)
        event.set()&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;producer 코드로 이전의 코드가 SENTINEL을 이용해서 작업의 끝을 알렸다면 이번에는 event.set()으로 확인하도록 만들었다.&lt;/p&gt;
&lt;pre id=&quot;code_1678195725456&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def producer(pipeline, event):
    &quot;&quot;&quot;Pretend we're getting a number from the network.&quot;&quot;&quot;
    while not event.is_set():
        message = random.randint(1, 101)
        logging.info(&quot;Producer got message: %s&quot;, message)
        pipeline.set_message(message, &quot;Producer&quot;)

    logging.info(&quot;Producer received EXIT event. Exiting&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;consumer 코드로 event가 set이 되었거나 pipline이 빈 것이 아닌 이상 작업은 계속된다.&lt;/p&gt;
&lt;pre id=&quot;code_1678195778013&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def consumer(pipeline, event):
    &quot;&quot;&quot;Pretend we're saving a number in the database.&quot;&quot;&quot;
    while not event.is_set() or not pipeline.empty():
        message = pipeline.get_message(&quot;Consumer&quot;)
        logging.info(
            &quot;Consumer storing message: %s  (queue size=%s)&quot;,
            message,
            pipeline.qsize(),
        )

    logging.info(&quot;Consumer received EXIT event. Exiting&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 Pipline class이며 Queue를 상속받았고 maxsize를 설정해 줌으로써 최대로 처리될 수 있는 양을 설정하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 get과 set함수로 이전의 Lock의 구현사항을 대체하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1678195805723&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Pipeline(queue.Queue):
    def __init__(self):
        super().__init__(maxsize=10)

    def get_message(self, name):
        logging.debug(&quot;%s:about to get from queue&quot;, name)
        value = self.get()
        logging.debug(&quot;%s:got %d from queue&quot;, name, value)
        return value

    def set_message(self, value, name):
        logging.debug(&quot;%s:about to add %d to queue&quot;, name, value)
        self.put(value)
        logging.debug(&quot;%s:added %d to queue&quot;, name, value)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 아래와 같다. 최대로 설정한 queue의 사이즈만큼 동시에 message가 처리되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1678195864178&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ ./prodcom_queue.py
Producer got message: 32
Producer got message: 51
Producer got message: 25
Producer got message: 94
Producer got message: 29
Consumer storing message: 32 (queue size=3)
Producer got message: 96
Consumer storing message: 51 (queue size=3)
Producer got message: 6
Consumer storing message: 25 (queue size=3)
Producer got message: 31

[many lines deleted]

Producer got message: 80
Consumer storing message: 94 (queue size=6)
Producer got message: 33
Consumer storing message: 20 (queue size=6)
Producer got message: 48
Consumer storing message: 31 (queue size=6)
Producer got message: 52
Consumer storing message: 98 (queue size=6)
Main: about to set event
Producer got message: 13
Consumer storing message: 59 (queue size=6)
Producer received EXIT event. Exiting
Consumer storing message: 75 (queue size=6)
Consumer storing message: 97 (queue size=5)
Consumer storing message: 80 (queue size=4)
Consumer storing message: 33 (queue size=3)
Consumer storing message: 48 (queue size=2)
Consumer storing message: 52 (queue size=1)
Consumer storing message: 13 (queue size=0)
Consumer received EXIT event. Exiting&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;출처 : &lt;a href=&quot;https://realpython.com/intro-to-python-threading/#producer-consumer-threading&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://realpython.com/intro-to-python-threading/#producer-consumer-threading&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678195932489&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;An Intro to Threading in Python &amp;ndash; Real Python&quot; data-og-description=&quot;In this intermediate-level tutorial, you'll learn how to use threading in your Python programs. You'll see how to create threads, how to coordinate and synchronize them, and how to handle common problems that arise in threading.&quot; data-og-host=&quot;realpython.com&quot; data-og-source-url=&quot;https://realpython.com/intro-to-python-threading/#producer-consumer-threading&quot; data-og-url=&quot;https://realpython.com/intro-to-python-threading/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/clmSKQ/hyRRQD9vzs/PTeIAktd0WZTGMSQBD3920/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/d7MCLw/hyRREXXh42/LfctsLGLoTkt3tGBSRJi4K/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/yHSrj/hyRRFo1RHt/wKazZiSpvEGGjMfAqIjUUK/img.png?width=1383&amp;amp;height=2901&amp;amp;face=0_0_1383_2901&quot;&gt;&lt;a href=&quot;https://realpython.com/intro-to-python-threading/#producer-consumer-threading&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://realpython.com/intro-to-python-threading/#producer-consumer-threading&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/clmSKQ/hyRRQD9vzs/PTeIAktd0WZTGMSQBD3920/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/d7MCLw/hyRREXXh42/LfctsLGLoTkt3tGBSRJi4K/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/yHSrj/hyRRFo1RHt/wKazZiSpvEGGjMfAqIjUUK/img.png?width=1383&amp;amp;height=2901&amp;amp;face=0_0_1383_2901');&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;An Intro to Threading in Python &amp;ndash; Real Python&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;In this intermediate-level tutorial, you'll learn how to use threading in your Python programs. You'll see how to create threads, how to coordinate and synchronize them, and how to handle common problems that arise in threading.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;realpython.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>producer-consumer problem</category>
      <category>python</category>
      <category>threading</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/500</guid>
      <comments>https://bitrader.tistory.com/500#entry500comment</comments>
      <pubDate>Tue, 7 Mar 2023 22:32:21 +0900</pubDate>
    </item>
    <item>
      <title>Python Thread의 개념과 사용예시</title>
      <link>https://bitrader.tistory.com/499</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;python에서 thread는 동시성을 가지는 코드를 작성을 위해 필요로 한다. 하지만 multiprocessing과는 다르게 실제로 여러 개의 작업을 동시에 하는 것은 아니다. 단지 하나의 코어로 여러 작업을 왔다 갔다 하면서 동시에 처리하는 것처럼 보일뿐이다.&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;Thread는 보통 외부의 이벤트를 오래동안 기다려야(I/O bound) 하는 작업에 적용하면 효과적이다. 하지만 많은 CPU연산을 필요로 하는 작업에는 Thread 보단 multiprocessing이 어울린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Python Thread의 예시&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드는 main section에서 thread를 하나 생성하여 원하는 함수를 실행하는 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;threading.Thread 함수안에 target으로 실행하고자 하는 함수와 args에 변수를 넘겨준다.&lt;/p&gt;
&lt;pre id=&quot;code_1678191858671&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import logging
import threading
import time

def thread_function(name):
    logging.info(&quot;Thread %s: starting&quot;, name)
    time.sleep(2)
    logging.info(&quot;Thread %s: finishing&quot;, name)

if __name__ == &quot;__main__&quot;:
    format = &quot;%(asctime)s: %(message)s&quot;
    logging.basicConfig(format=format, level=logging.INFO,
                        datefmt=&quot;%H:%M:%S&quot;)

    logging.info(&quot;Main    : before creating thread&quot;)
    x = threading.Thread(target=thread_function, args=(1,))
    logging.info(&quot;Main    : before running thread&quot;)
    x.start()
    logging.info(&quot;Main    : wait for the thread to finish&quot;)
    # x.join()
    logging.info(&quot;Main    : all done&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;출력결과는 아래와 같다. Thread를 생성하여 실행한 thread_function이 main thread보다 늦게 종료된 것을 확인할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1678192022883&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ ./single_thread.py
Main    : before creating thread
Main    : before running thread
Thread 1: starting
Main    : wait for the thread to finish
Main    : all done
Thread 1: finishing&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 다음과 같다. python에는 daemon thread 라는것이 존재하는데, python에서의 daemon thread는 프로그램이 종료되면 즉시 같이 종료된다. 하지만 그렇지 않은 thread라면 해당 thread가 종료될 때까지 프로그램이 기다리게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 생성한 thread는 daemon thread가 아닌 thread이며 따라서 프로그램이 해당 thread가 종료될때까지 기다렸다가 종료된 예시이다.&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;따라서 아래처럼 thread를 생성할때 thread가 daemon thread라는 점을 명시를 해주면&lt;/p&gt;
&lt;pre id=&quot;code_1678192312943&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;x = threading.Thread(target=thread_function, args=(1,), daemon=True)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;main thread가 해당 thread를 기다리지 않고 바로 프로그램을 종료해 버리는 기록을 확인할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1678192353305&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ ./daemon_thread.py
Main    : before creating thread
Main    : before running thread
Thread 1: starting
Main    : wait for the thread to finish
Main    : all done&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;또한 daemon thread가 아닌 thread를 프로그램 종료시에만 기다리지 않고 원하는 지점에서 기다리게 할 수 있다. 그럴 때 사용되는 것이 join() 함수이며 위의 코드에서 x.join()의 주석을 해제하면 x.join() 코드가 있는 지점까지만 main thread가 진행되고 다른 thread를 기다리게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Thread를 많이 생성하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 예시 코드는 위의 예시 코드와는 달리 여러개의 thread를 생성하여 작업을 수행하는 코드이며. join() 함수를 사용하여 main thread가 특정 지점에서 다른 thread를 기다리게 만들어 놓은 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;thread object를 list로 넘여서 각각 start와 join 함수를 사용한것을 확인할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1678192507149&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import logging
import threading
import time

def thread_function(name):
    logging.info(&quot;Thread %s: starting&quot;, name)
    time.sleep(2)
    logging.info(&quot;Thread %s: finishing&quot;, name)

if __name__ == &quot;__main__&quot;:
    format = &quot;%(asctime)s: %(message)s&quot;
    logging.basicConfig(format=format, level=logging.INFO,
                        datefmt=&quot;%H:%M:%S&quot;)

    threads = list()
    for index in range(3):
        logging.info(&quot;Main    : create and start thread %d.&quot;, index)
        x = threading.Thread(target=thread_function, args=(index,))
        threads.append(x)
        x.start()

    for index, thread in enumerate(threads):
        logging.info(&quot;Main    : before joining thread %d.&quot;, index)
        thread.join()
        logging.info(&quot;Main    : thread %d done&quot;, index)&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;아래는 실행결과이며 결과를 살펴보면 thread가 실행된 순서되로 종료되지 않았음을 확인할 수 있다. 코드를 실행할 때마다 다른 결과를 확인할 수 있으며 이것은 운영체제에 의해 결정되는 사항으로 결과를 예측하기는 매우 어렵다.&lt;/p&gt;
&lt;pre id=&quot;code_1678192525094&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ ./multiple_threads.py
Main    : create and start thread 0.
Thread 0: starting
Main    : create and start thread 1.
Thread 1: starting
Main    : create and start thread 2.
Thread 2: starting
Main    : before joining thread 0.
Thread 2: finishing
Thread 1: finishing
Thread 0: finishing
Main    : thread 0 done
Main    : before joining thread 1.
Main    : thread 1 done
Main    : before joining thread 2.
Main    : thread 2 done&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;같은 역할을 하는 코드를 ThreadPoolExecutor class를 context manager를 이용하여 작성이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;context manager의 with 블록이 ThreadPoolExecutor 각각의 thread에 join함수를 자동으로 적용시켜 주어서 안전하게 thread를 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1678192737137&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import concurrent.futures

# [rest of code]

if __name__ == &quot;__main__&quot;:
    format = &quot;%(asctime)s: %(message)s&quot;
    logging.basicConfig(format=format, level=logging.INFO,
                        datefmt=&quot;%H:%M:%S&quot;)

    with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
        executor.map(thread_function, range(3))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Race Condition&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Race condition은 thread를 사용하다 마주칠 수 있는 문제로 두개 혹은 그 이상의 thread가 공유된 데이터나 자원에 접근할 때 발생할 수 있는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 코드가 있다고 해보자. main section에서 thread를 2개 생성하여서 각각의 thread가 FakeDatabase의 value를 하나씩 증가시키면 우리는 최종적으로 self.value의 값이 2가 될 것으로 기대를 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1678193023026&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class FakeDatabase:
    def __init__(self):
        self.value = 0

    def update(self, name):
        logging.info(&quot;Thread %s: starting update&quot;, name)
        local_copy = self.value
        local_copy += 1
        time.sleep(0.1)
        self.value = local_copy
        logging.info(&quot;Thread %s: finishing update&quot;, name)
        
if __name__ == &quot;__main__&quot;:
    format = &quot;%(asctime)s: %(message)s&quot;
    logging.basicConfig(format=format, level=logging.INFO,
                        datefmt=&quot;%H:%M:%S&quot;)

    database = FakeDatabase()
    logging.info(&quot;Testing update. Starting value is %d.&quot;, database.value)
    with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
        for index in range(2):
            executor.submit(database.update, index)
    logging.info(&quot;Testing update. Ending value is %d.&quot;, database.value)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제로 코드를 동작시키면 self.value의 값은 1로 확인이 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1678193086776&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ ./racecond.py
Testing unlocked update. Starting value is 0.
Thread 0: starting update
Thread 1: starting update
Thread 0: finishing update
Thread 1: finishing update
Testing unlocked update. Ending value is 1.&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;그 원인은 다음과 같다. 첫번째첫 번째 thread가 self.value의 값(0)을 local_copy로 복사해서 1을 증가시킨 후 다시 값을 self.value에 저장하지 않고 sleep에 들어간다. 첫 번째 thread가 sleep에 들어간 사이 두 번째 thread가 똑같이 self.value를 local_copy로 복사해서 1을 증가시키고 sleep에 들어간다. 이후 sleep이 끝난 첫 번째 thread가 값(1)을 다시 self.value에 복사를 하고 두 번째 thread도 같은 행위를 하면서 똑같은 값인 1을 self.value에 복사하게 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 첫번째 thread와 두 번째 thread가 연계되지 않고 각각 self.value의 값을 가져와서 증가시킨 후 각자 다시 값을 복사해 놓은 결과인 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cN6rRW/btr2CwXhynV/TgExiR1RwkXPfByHtwoFOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cN6rRW/btr2CwXhynV/TgExiR1RwkXPfByHtwoFOk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;515&quot; data-origin-height=&quot;388&quot; data-filename=&quot;1.PNG&quot; style=&quot;width: 32.3966%; margin-right: 10px;&quot; data-widthpercent=&quot;33.17&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cN6rRW/btr2CwXhynV/TgExiR1RwkXPfByHtwoFOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcN6rRW%2Fbtr2CwXhynV%2FTgExiR1RwkXPfByHtwoFOk%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;515&quot; height=&quot;388&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJ8iAc/btr2D9N16YC/pk8sxITrkoutQOp9VsEadK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJ8iAc/btr2D9N16YC/pk8sxITrkoutQOp9VsEadK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;495&quot; data-origin-height=&quot;379&quot; data-filename=&quot;2.PNG&quot; style=&quot;width: 31.878%; margin-right: 10px;&quot; data-widthpercent=&quot;32.64&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJ8iAc/btr2D9N16YC/pk8sxITrkoutQOp9VsEadK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJ8iAc%2Fbtr2D9N16YC%2Fpk8sxITrkoutQOp9VsEadK%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;495&quot; height=&quot;379&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bA012f/btr2Gs0Azfx/0qzvwbSBqKOb3tcreiL7K0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bA012f/btr2Gs0Azfx/0qzvwbSBqKOb3tcreiL7K0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;494&quot; data-origin-height=&quot;361&quot; data-filename=&quot;3.PNG&quot; style=&quot;width: 33.3998%;&quot; data-widthpercent=&quot;34.19&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bA012f/btr2Gs0Azfx/0qzvwbSBqKOb3tcreiL7K0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbA012f%2Fbtr2Gs0Azfx%2F0qzvwbSBqKOb3tcreiL7K0%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;494&quot; height=&quot;361&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;왼쪽에서 오른쪽으로 진행되며 최종값은 결국 1이된다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Lock을 이용한 동기와와 DeadLock&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 서술된 문제를 해결하기 위한 개념이 Lock이다. 즉 각각의 thread가 서로 동기화 되서 작업을 하게 만드는 기능이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lock은 다른 프로그래밍 언어에서는 mutex라고 불리며 MUTual EXclusion이라는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lock object는 마치 입장권처럼 동작으로 하며 하나의 시점에는 오직 하나의 thread만이 Lock을 획득할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통은 lock.acquire(), lock.release() 함수로 Lock을 획득하거나 해제할 수 있다.&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;위의 예시에서는 아래처럼 코드를 수정하면 race condition 문제를 해결할 수 있다. .acquire()와 .release() 대신에 context manager를 사용한 예시로 하나의 thread가 self.value값에 접근하여 조작한 값을 다시 self.value에 저장할 때까지 다른 thread는 self.value값에 접근할 수가 없다.&lt;/p&gt;
&lt;pre id=&quot;code_1678193966140&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class FakeDatabase:
    def __init__(self):
        self.value = 0
        self._lock = threading.Lock()

    def locked_update(self, name):
        logging.info(&quot;Thread %s: starting update&quot;, name)
        logging.debug(&quot;Thread %s about to lock&quot;, name)
        with self._lock:
            logging.debug(&quot;Thread %s has lock&quot;, name)
            local_copy = self.value
            local_copy += 1
            time.sleep(0.1)
            self.value = local_copy
            logging.debug(&quot;Thread %s about to release lock&quot;, name)
        logging.debug(&quot;Thread %s after release&quot;, name)
        logging.info(&quot;Thread %s: finishing update&quot;, name)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 해당 코드를 실행한 결과이다.&lt;/p&gt;
&lt;pre id=&quot;code_1678194071941&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ ./fixrace.py
Testing locked update. Starting value is 0.
Thread 0: starting update
Thread 1: starting update
Thread 0: finishing update
Thread 1: finishing update
Testing locked update. Ending value is 2.&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;DeadLock은 lock을 잘못사용 하는 경우로 한 시점에는 하나의 thread만 Lock을 획득할 수 있는데 두개 이상의 thread가 서로 Lock을 획득하려 하기만 하고 release는 하지 않아서 작업이 병목현상에 빠지는 현상이다.&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;아래의 코드를 실행하면 마치 프로그램이 무한 루프에 빠진것처럼 &quot;before second acquire&quot;에서 더 이상 진행되지 않는다.&lt;/p&gt;
&lt;pre id=&quot;code_1678194140094&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import threading

l = threading.Lock()
print(&quot;before first acquire&quot;)
l.acquire()
print(&quot;before second acquire&quot;)
l.acquire()
print(&quot;acquired lock twice&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;출처 : &lt;a href=&quot;https://realpython.com/intro-to-python-threading/#basic-synchronization-using-lock&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://realpython.com/intro-to-python-threading/#basic-synchronization-using-lock&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678194223018&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;An Intro to Threading in Python &amp;ndash; Real Python&quot; data-og-description=&quot;In this intermediate-level tutorial, you'll learn how to use threading in your Python programs. You'll see how to create threads, how to coordinate and synchronize them, and how to handle common problems that arise in threading.&quot; data-og-host=&quot;realpython.com&quot; data-og-source-url=&quot;https://realpython.com/intro-to-python-threading/#basic-synchronization-using-lock&quot; data-og-url=&quot;https://realpython.com/intro-to-python-threading/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dd8PX7/hyRRLW2PhX/Xg0Kc1LVASSp6KUIYWOu2k/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/bWioOZ/hyRRLbEU71/iVlF9nIZnCAXwJtLRqYrOk/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/GIbZr/hyRRLW2Pth/DmqHL6sIAogCS5ieyLdPZ1/img.png?width=1383&amp;amp;height=2901&amp;amp;face=0_0_1383_2901&quot;&gt;&lt;a href=&quot;https://realpython.com/intro-to-python-threading/#basic-synchronization-using-lock&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://realpython.com/intro-to-python-threading/#basic-synchronization-using-lock&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dd8PX7/hyRRLW2PhX/Xg0Kc1LVASSp6KUIYWOu2k/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/bWioOZ/hyRRLbEU71/iVlF9nIZnCAXwJtLRqYrOk/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/GIbZr/hyRRLW2Pth/DmqHL6sIAogCS5ieyLdPZ1/img.png?width=1383&amp;amp;height=2901&amp;amp;face=0_0_1383_2901');&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;An Intro to Threading in Python &amp;ndash; Real Python&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;In this intermediate-level tutorial, you'll learn how to use threading in your Python programs. You'll see how to create threads, how to coordinate and synchronize them, and how to handle common problems that arise in threading.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;realpython.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>concurrency</category>
      <category>deadlock</category>
      <category>mutext</category>
      <category>python</category>
      <category>Race Condition</category>
      <category>thread</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/499</guid>
      <comments>https://bitrader.tistory.com/499#entry499comment</comments>
      <pubDate>Tue, 7 Mar 2023 21:52:36 +0900</pubDate>
    </item>
    <item>
      <title>LayoutLM: Pre-training of Text and Layout for Document Image Understanding</title>
      <link>https://bitrader.tistory.com/495</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://arxiv.org/abs/1912.13318&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://arxiv.org/abs/1912.13318&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678013476826&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;LayoutLM: Pre-training of Text and Layout for Document Image Understanding&quot; data-og-description=&quot;Pre-training techniques have been verified successfully in a variety of NLP tasks in recent years. Despite the widespread use of pre-training models for NLP applications, they almost exclusively focus on text-level manipulation, while neglecting layout and&quot; data-og-host=&quot;arxiv.org&quot; data-og-source-url=&quot;https://arxiv.org/abs/1912.13318&quot; data-og-url=&quot;https://arxiv.org/abs/1912.13318v5&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dembnE/hyRQlKsmv8/iOABy7RLHbGSEO4RdQH8a0/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000&quot;&gt;&lt;a href=&quot;https://arxiv.org/abs/1912.13318&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://arxiv.org/abs/1912.13318&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dembnE/hyRQlKsmv8/iOABy7RLHbGSEO4RdQH8a0/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000');&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;LayoutLM: Pre-training of Text and Layout for Document Image Understanding&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Pre-training techniques have been verified successfully in a variety of NLP tasks in recent years. Despite the widespread use of pre-training models for NLP applications, they almost exclusively focus on text-level manipulation, while neglecting layout and&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;arxiv.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Introduction&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Document AI는 사업 관련 문서를 자동으로 분석하고 이해하는데 활용될 수 있는 task로써 사업 관련 문서는 디지털 문서, 혹은 스캔된 문서를 포함하며, 거래 내역, 보고서 등을 포함한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서들의 형식이나 스캔된 문서의 퀄리티등에 따라 문서를 이해하는 task는 굉장히 어려운 일이다. 따라서 이러한 어려움을 해결하기 위해 Document AI모델은 computer vision과 NLP 기술을 이용하여 자동으로 문서를 분류하고 유용한 정보를 추출하도록 디자인되었다. 하지만 그동안의 제시되었던 방법에는 2가지 한계점이 있었다.&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;(1) 그동안의 방법들은 NLP의 Large 모델들 처럼 대규모의 unlabeled 된 데이터를 사전학습하지 않고 소규모의 label 데이터만으로 학습을 하였고 (2) CV나 NLP의 사전학습된 모델을 각각 차용만 하였지 두 개의 이종 데이터를 연계해서 학습하지는 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 대규모의 데이터를 사전학습하는것과 두 개의 이종데이터를 연계해서 학습하는 것이 매우 중요하며 LayoutLM은 BERT로부터 영감을 받아. 2-D position embedding과, an image enbedding 등 2가지의 추가적인 임베딩 데이터를 더하여 학습에 확 용한다.&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;또한 사전학습 방법으로 multi-task learning objective인 Masked Visual-Language Model (MVLM)과 a Multi-label Document Classification (MDC) loss을 이용하여 대규모의 데이터에 사전학습을 실시한다.&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;마지막으로 FUNSD dataset, SROIE dataset, RVL-CDIP dataset을 활용하여 fine-tuning을 진행하였고 각각의 task에서 sota 성능을 달성하였다.&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;본 논문의 기여점은 크게 2가지이다. 처음으로 문자데이터와 layout정보를 하나의 프레임워크에 합쳐서 학습을 하는 시도를 하였고, masked visual-language model, multi-label document classification pre-training objective를 이용하여 down stream task에서 sota 성능을 달성하였다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;LayoutLM&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;The LayoutLM Model&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LayoutLM model은 BERT로부터 영감을 받아 BERT의 구조를 backbond으로 이용하고 추가적인 2가지 embedding 정보를 학습에 활용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째는 Document Layout Information으로 document에서 추출된 문자의 layout에서의 상대적인 위치정보이다. 예를 들어서 document에서 &quot;Passport ID:&quot;라는 문자가 추출되었다면 이것과 상응되는 값은 &quot;Passport :ID&quot;보다 오른쪽이나 아래쪽에 있을 확률이 높다. 이러한 위치정보를 2-D position representation으로 표현하여 학습에 활 요한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 position 정보는 (x0, y0, x1, y1) 형태로 제공되며 (x0, y0)는 bounding box의 왼쪽 위, (x1, y1)는 bouding box의 오른쪽 아래를 의미한다.&amp;nbsp;&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;두 번째는 Visual Information으로 각각의 텍스트에 해당하는 실제 이미지이다. 실제로 Document에서 중요한 부분은 밑줄이나 다른 폰트를 사용등으로 강조되어 있다. 이러한 시각적인 정보를 학습에 활용하는 것이다. 이러한 이미지들은 OCR로부터 얻은 bounding box로 전체 layout에서 해당하는 부분만 이미지를 잘라내어 그 이미지와 상응하는 text의 embedding으로 넣어준다. text에 상응하는 image embedding은 Faster R-CNN을 이용하여 embedding을 생성한다. [CLS] 토큰은 전체 layout의 embedding에 해당하며 문서 전체의 카테고리 분류에 활용될 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;layoutlm.PNG&quot; data-origin-width=&quot;702&quot; data-origin-height=&quot;342&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8w14e/btr2hIvSQco/SxzLhnPsFWcpnGiDxk1g4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8w14e/btr2hIvSQco/SxzLhnPsFWcpnGiDxk1g4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8w14e/btr2hIvSQco/SxzLhnPsFWcpnGiDxk1g4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8w14e%2Fbtr2hIvSQco%2FSxzLhnPsFWcpnGiDxk1g4k%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;616&quot; height=&quot;300&quot; data-filename=&quot;layoutlm.PNG&quot; data-origin-width=&quot;702&quot; data-origin-height=&quot;342&quot;/&gt;&lt;/span&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;b&gt;Pre-training LayoutLM&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Task #1: Masked Visual-Language Model&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BERT의 Masked Language Model에서 영감을 얻었고 pre-train 간에 text token은 마스크를 하지만 그에 상응하는 2-D positional embedding은 마스크를 하지 않고 학습을 한다. 이러한 학습 과정을 거치면서 visual 정보와 언어정보의 차이를 어느 정도 반영하게 된다.&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;Task #2: Multi-label Document Classification (MDC)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Document image를 이해하려면 높은 수준의 document representation이 필요하다. 따라서 (MDC)를 통하여 문서의 표현을 학습하고 분류를 하는데 이러한 학습과정은 document의 label이 필요함으로 대규모의 학습데이터에서는 사용하기가 한계가 있다. 해당 pre-train은 선택적이며 이후의 단락에서 MDC의 효과에 대해서 비교결과를 제시한다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Fine-tuning LayoutLM&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LayoutLM이라는 사전 학습된 모델은 양식 이해 작업, 영수증 이해 작업 및 문서 이미지 분류 작업을 포함한 세 가지 문서 이미지 이해 task에 대해 fine-tuning 되었으며, 양식 및 영수증 이해 작업에서 LayoutLM은 각 토큰에 대해 {B, I, E, S, O} 태그를 예측하고 순차적 레이블링을 사용하여 데이터셋에서 각 유형의 엔티티를 감지한다. 문서 이미지 분류 작업에서는 LayoutLM이 [CLS] 토큰의 표현을 사용하여 클래스 레이블을 예측한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Experiments&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Result&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FUNSD dataset을 이용하여 form understanding을 수행해 본 결과는 아래와 같으며 LayoutLM이 Bert와 Roberta보다 더 성능이 뛰어났으며 LayoutLM에서도 large 모델이, 더 많은 데이터로 사전 훈련을 시켰을 때 성능이 더 뛰어났다. 또한 image데이터의 유무도 성능에 영향을 미쳤다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;result1.PNG&quot; data-origin-width=&quot;675&quot; data-origin-height=&quot;341&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/U8Nj9/btr1Wyhx0ex/BVRdKdkGX0NRNO7Cv1jE9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/U8Nj9/btr1Wyhx0ex/BVRdKdkGX0NRNO7Cv1jE9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/U8Nj9/btr1Wyhx0ex/BVRdKdkGX0NRNO7Cv1jE9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FU8Nj9%2Fbtr1Wyhx0ex%2FBVRdKdkGX0NRNO7Cv1jE9K%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;558&quot; height=&quot;282&quot; data-filename=&quot;result1.PNG&quot; data-origin-width=&quot;675&quot; data-origin-height=&quot;341&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 구체적으로 FUNSD dataset에 fine-tuning 하기 이전에 사전학습에 사용된 데이터의 양과 epoch에 따른 비교이다. 더 많은 데이터와 epoch를 사용하였을 때 성능이 올라가는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;result2.PNG&quot; data-origin-width=&quot;702&quot; data-origin-height=&quot;441&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcGgCC/btr1UtHL20j/MLEEXyZHM7plGzL4efec00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcGgCC/btr1UtHL20j/MLEEXyZHM7plGzL4efec00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcGgCC/btr1UtHL20j/MLEEXyZHM7plGzL4efec00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcGgCC%2Fbtr1UtHL20j%2FMLEEXyZHM7plGzL4efec00%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;629&quot; height=&quot;395&quot; data-filename=&quot;result2.PNG&quot; data-origin-width=&quot;702&quot; data-origin-height=&quot;441&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 LayoutLM의 파라미터를 어떠한 모델의 파라미터로 초기화하느냐도 영향을 미쳤다. RoBERTa가 가장 성능에 좋게 영향을 미치는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;result3.PNG&quot; data-origin-width=&quot;628&quot; data-origin-height=&quot;193&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEa9rp/btr1XQhFT1h/28gzLM4Z7e1H04z46LVJvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEa9rp/btr1XQhFT1h/28gzLM4Z7e1H04z46LVJvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEa9rp/btr1XQhFT1h/28gzLM4Z7e1H04z46LVJvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEa9rp%2Fbtr1XQhFT1h%2F28gzLM4Z7e1H04z46LVJvK%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;628&quot; height=&quot;193&quot; data-filename=&quot;result3.PNG&quot; data-origin-width=&quot;628&quot; data-origin-height=&quot;193&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Receipt Understanding과 관련해서도 FUNSD datset에 fine-tune 하였을 때와 비슷한 결과를 얻을 수 있었으며 심지어 SROIE competition에서 최종적으로 제출되었던 모델보다 더 좋은 성능을 보여주었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;result4.PNG&quot; data-origin-width=&quot;660&quot; data-origin-height=&quot;373&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7JJ5e/btr16iYWnKU/OpU4u11CJo9CNtZarbcli1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7JJ5e/btr16iYWnKU/OpU4u11CJo9CNtZarbcli1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7JJ5e/btr16iYWnKU/OpU4u11CJo9CNtZarbcli1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7JJ5e%2Fbtr16iYWnKU%2FOpU4u11CJo9CNtZarbcli1%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;660&quot; height=&quot;373&quot; data-filename=&quot;result4.PNG&quot; data-origin-width=&quot;660&quot; data-origin-height=&quot;373&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로, RVL-CDIP 데이터셋을 사용하여 문서 이미지 분류 task의 결과는 아래와 같다. 문서 이미지는 대부분 다양한 스타일과 레이아웃으로 된 텍스트이기 때문에 일반적인 이미지와는 다르며. 이를 위해 기존의 이미지 기반 모델과 BERT, RoBERTa와 같은 텍스트 기반 모델을 비교해 보았고, LayoutLM 모델이 가장 성능이 좋았다. 결과적으로, LayoutLM는 이미지 기반 모델과 비교했을 때도 더 좋은 성능을 보였으며, 이미지 임베딩을 추가로 사용하면 94.42%의 높은 정확도를 달성하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;result5.PNG&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;471&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eTCmWn/btr1Y8vAGHH/Q3cmdNLgAX5t90QYOaN9G1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eTCmWn/btr1Y8vAGHH/Q3cmdNLgAX5t90QYOaN9G1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eTCmWn/btr1Y8vAGHH/Q3cmdNLgAX5t90QYOaN9G1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeTCmWn%2Fbtr1Y8vAGHH%2FQ3cmdNLgAX5t90QYOaN9G1%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;450&quot; height=&quot;373&quot; data-filename=&quot;result5.PNG&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;471&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LayoutLM은 텍스트와 레이아웃 정보를 하나의 프레임워크에서 학습할 수 있는 모델이며, Transformer 아키텍처를 백본으로 사용하여 토큰 임베딩, 레이아웃 임베딩 및 이미지 임베딩과 같은 다중 모달 입력을 활용한다. 동시에 대규모 라벨 없는 스캔 문서 이미지를 기반으로 자기 지도 학습 방식으로 모델을 쉽게 학습시킬 수 있으며, LayoutLM 모델은 양식 이해, 영수증 이해 및 스캔 문서 이미지 분류 세 가지 작업에서 평가되었으며, 실험 결과 LayoutLM은 이러한 작업에서 SOTA 사전 학습 모델보다 크게 우수한 성능을 발휘하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;향후 연구에서는 더 많은 데이터 및 계산 리소스를 사용하여 사전 학습 모델을 조사할 예정이라고 하며, 또한, LARGE 아키텍처를 사용하여 텍스트 및 레이아웃을 포함하고, 사전 학습 단계에서 이미지 임베딩을 활용하여 LayoutLM을 학습할 것이라고 예정하고 있다.&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;&amp;nbsp;&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;&amp;nbsp;&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;&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>논문리뷰</category>
      <category>cv</category>
      <category>DeepLearning</category>
      <category>LayoutLM</category>
      <category>multimodal</category>
      <category>NLP</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/495</guid>
      <comments>https://bitrader.tistory.com/495#entry495comment</comments>
      <pubDate>Sun, 5 Mar 2023 21:26:35 +0900</pubDate>
    </item>
    <item>
      <title>ICDAR2019 Competition on Scanned Receipt OCR and Information Extraction</title>
      <link>https://bitrader.tistory.com/494</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://arxiv.org/abs/2103.10213&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://arxiv.org/abs/2103.10213&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1677999413990&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;ICDAR2019 Competition on Scanned Receipt OCR and Information Extraction&quot; data-og-description=&quot;Scanned receipts OCR and key information extraction (SROIE) represent the processeses of recognizing text from scanned receipts and extracting key texts from them and save the extracted tests to structured documents. SROIE plays critical roles for many doc&quot; data-og-host=&quot;arxiv.org&quot; data-og-source-url=&quot;https://arxiv.org/abs/2103.10213&quot; data-og-url=&quot;https://arxiv.org/abs/2103.10213v1&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bdcnVh/hyRQwryW9l/QEOa1pwYoKR61dtIzpEEbk/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000&quot;&gt;&lt;a href=&quot;https://arxiv.org/abs/2103.10213&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://arxiv.org/abs/2103.10213&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bdcnVh/hyRQwryW9l/QEOa1pwYoKR61dtIzpEEbk/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000');&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;ICDAR2019 Competition on Scanned Receipt OCR and Information Extraction&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Scanned receipts OCR and key information extraction (SROIE) represent the processeses of recognizing text from scanned receipts and extracting key texts from them and save the extracted tests to structured documents. SROIE plays critical roles for many doc&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;arxiv.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Introduction&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SROIE는 Scanned receipts OCR and key information extraction (SROIE)라는 뜻으로 영수증의 내용을 OCR 기반의 기술을 인식하고 그 중에서 중요한 key 정보들만을 추출해내는 task이다. 해당 태스크는 산업에 광범위하게 응용될 수 있어 많은 잠재력을 가졌으나 정확도가 매우 중요한 task의 특성상 아직은 해결해야될 부분이 많은 task이다.&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;또한 본 논문의 저자들은 receipt OCR과 그에 따른 key information으로 이어지는 task에 대해서는 그동안 연구되었던 것이 적었던것을 지적하며 관련 분야에 관심을 불러일으키기 위해 ICDAR 2019 competition on SROIE을 개최하였다고 한다.&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;SROIE의 task는 3가지로 이루어지며 아래에 상세하기 기술하겠지만 Scanned Receipt Text Localisation, Scanned Receipt OCR, Key Information Extraction from Scanned Receipts로 이루어져 있으며 2개의 task에 대해서는 새로운 통합적인 평가 metric을 제안하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 dataset도 새롭게 제안하였으며 그동안의 OCR dataset과의 차이점은 영수증 데이터라는 특성을 반역하기 위하여 영수증 이미지의 낮은 퀄리티, 잉크와 프린팅이 제대로 되지 않는 특성을 반영하여 데이터셋을 제공한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Dataset and Annotiontions&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터셋은 총 1000개로 이루어져 있으며 trainval이 600개 test가 400개로 구성되어 있다. 각각의 영수증 이미지는 대략 4개의 중요 텍스트 필드를 가지고 있으며 text anntation들은 영어와 숫자로만 구성되어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;receipt detection과 OCR task를 위해서 각각의 text box마다 bounding box(bbox)와 transcript로 이루어져 있으며 좌표 정보는 맨위부터 시작해서 시계방향으로 구성되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Information extraction task를 위해서는 각각의 이미지의 중요정보는 아래의 format형태로 저장이 되어있다.(Fig.1(b))&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;dataset.PNG&quot; data-origin-width=&quot;384&quot; data-origin-height=&quot;316&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bL2r2Y/btr2gkPf23W/wlukwTu2SktoMKBMurY4m0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bL2r2Y/btr2gkPf23W/wlukwTu2SktoMKBMurY4m0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bL2r2Y/btr2gkPf23W/wlukwTu2SktoMKBMurY4m0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbL2r2Y%2Fbtr2gkPf23W%2FwlukwTu2SktoMKBMurY4m0%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;320&quot; height=&quot;263&quot; data-filename=&quot;dataset.PNG&quot; data-origin-width=&quot;384&quot; data-origin-height=&quot;316&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Competition Tasks&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Task 1 - Scanned Receipt Text Localisation&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영수증에서 text를 4개의 축을 기반으로 찾아 인식하는 task로 text레벨로 인식을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평가는 mean average precision(mAP), average recall이 사용되었으며 f1으로 등수를 계산한다.&amp;nbsp;&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;Task2 - Scanned Receipt OCR&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번째 task는 영수증 이미지에서 text를 정확하게 인식하는 것으로 위치정보는 필요가 없다. 본 task의 ground truth는 transcription에서 나타나는 글자 전부이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평가는 ground truth에서 나타나는 단어 전체이며 만약 ground truth에서 어떠한 단어가 반복이 된다면 예측값에서 해당 단어가 반복되어야 한다. precision, recall, f1을 metric으로 사용하였다.&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;Task3 - Key Information Extraction from Scanned receipts&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세번째 task는 추출된 text에서 key information만을 추출하는 것으로 추출된 정보는 json파일로 저장하여 제출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평가방법은 제출된 text와 text의 카테고리가 ground truth와 정확히 일치해야하며 그렇지 않으면 정답으로 인식이 되지 않는다. 평가 metric으로는 mAP, F1이 사용되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;receipt.PNG&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;413&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBTccx/btr133HnCD9/ldlfkeb512l5emDBcaAZG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBTccx/btr133HnCD9/ldlfkeb512l5emDBcaAZG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBTccx/btr133HnCD9/ldlfkeb512l5emDBcaAZG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBTccx%2Fbtr133HnCD9%2Fldlfkeb512l5emDBcaAZG1%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;656&quot; height=&quot;359&quot; data-filename=&quot;receipt.PNG&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;413&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Discussion&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 task와 dataset에 따라 대회가 진행되었고 많은팀이 제출을 해주었으나 본 포스팅을 쓰는 시점에는 다소 트렌드가 지난 모델들이라 따로 기술은 하지 않을 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체적으로 task1과 task2에 해당하는 detection와 OCR의 결과는 매우 우수하였으며 task1에서 16개의 팀이 90%이상의 Hmean을 얻었으며 task2에서는 7개의 팀이 90%이상의 Hmean을 획득하였다. 하지만 영수증 인식이라는 task의 관점에서 보면 99%의 정확도가 요구됨으로 아직은 해결해야 할 부분이 남았음을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Task 3의 key information extraction의 결과를 보면 오직 한가지의 솔루션이 90% 이상의 Hmean을 획득하였고 대부분은 80%대의 점수를 얻었다. 따라서 Task 3에 대해서도 역시 해결해야 할 부분이 많이 있음을 확인할 수 있었다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 본 논문의 저자들은 해당 대회를 개최함으로서 SROIE라는 task에 대해서 새롭게 정의를 하고 관심을 불러일으키는데는 성공했으며 어느 정도 성능이 보장되는 솔루션들을 확인하였다. 하지만 아직은 요구되는 성능과는 큰 차이가 있는것을 확인하였으면 추후 연구를 통하여 해결해야 되는 부분을 확인하였다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>논문리뷰</category>
      <category>DeepLearning</category>
      <category>NLP</category>
      <category>OCR</category>
      <category>SROIE</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/494</guid>
      <comments>https://bitrader.tistory.com/494#entry494comment</comments>
      <pubDate>Sun, 5 Mar 2023 17:18:12 +0900</pubDate>
    </item>
    <item>
      <title>kubernetes label and selector</title>
      <link>https://bitrader.tistory.com/464</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버네티스의 Label은 쿠버네티스의 리소스를 논리적인 그룹으로 나누기 위해 붙이는 레이블링이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;selector는 Label을 이용하여 조회하려는 리소스만 조회할 수 있게 하는 기능이다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Picture2.png&quot; data-origin-width=&quot;484&quot; data-origin-height=&quot;477&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8Jyv1/btrZrv1j68x/bo3LhoYplvlzAfdc45KAg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8Jyv1/btrZrv1j68x/bo3LhoYplvlzAfdc45KAg1/img.png&quot; data-alt=&quot;출처 :&amp;amp;nbsp;https://k21academy.com/docker-kubernetes/labels-and-annotations-in-kubernetes/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8Jyv1/btrZrv1j68x/bo3LhoYplvlzAfdc45KAg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8Jyv1%2FbtrZrv1j68x%2Fbo3LhoYplvlzAfdc45KAg1%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;381&quot; height=&quot;375&quot; data-filename=&quot;Picture2.png&quot; data-origin-width=&quot;484&quot; data-origin-height=&quot;477&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 :&amp;nbsp;https://k21academy.com/docker-kubernetes/labels-and-annotations-in-kubernetes/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;yaml 파일 안에서 Label은 아래와 같이 metadata아래에서 선언을 하며 해당 이미지에는 app=backend, version=v1, env=prod라는 라벨링을 생성하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1676464254677&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;apiVersion: v1 
kind: Pod 
metadata: 
  name: my-pod 
  labels:
    app: backend 
    version: v1
    env: prod
spec:
  containers:
  - image: my-pod
    name: my-pod&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 yaml에서 선언하지 않아도 kubectl 명령어로도 label을 생성하거나 삭제가 가능하다.&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;b&gt;kubectl을 이용한 pod에 label 생성 및 삭제 명령어&lt;/b&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;* kubectl을 이용하여 pod의 라벨을 조회하는 명령어&lt;/p&gt;
&lt;pre id=&quot;code_1676464426862&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# kubectl get pod &amp;lt;pod_name&amp;gt; --show-labels
$ kubectl get pod my-pod --show-labels
NAME READY STATUS LABELS
my-pod 1/1 Running &amp;lt;none&amp;gt;&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;* 라벨이 없던 이미지에 app:backend라는 key value로 라벨을 생성한 후의 겨로가&lt;/p&gt;
&lt;pre id=&quot;code_1676464454079&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ kubectl label pod my-pod app=backend 
$ kubectl get pod my-pod --show-labels
NAME READY STATUS LABELS
my-pod 1/1 Running app=backend&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 라벨을 오버라이딩하는 명령어&lt;/p&gt;
&lt;pre id=&quot;code_1676464526980&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$kubectl label pod &amp;lt;pod_name&amp;gt; &amp;lt;key&amp;gt;=&amp;lt;value&amp;gt; --overwrite&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 원하는 라벨만 조회하는 명령어&lt;/p&gt;
&lt;pre id=&quot;code_1676464609134&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$kubectl get pod &amp;lt;pod_name&amp;gt; --label-columns &amp;lt;key1,key2...&amp;gt;
or
$kubectl get pod &amp;lt;pod_name&amp;gt; -L &amp;lt;key1,key2&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 라벨을 삭제하는 명령어&lt;/p&gt;
&lt;pre id=&quot;code_1676464639662&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$kubectl label pod &amp;lt;pod_name&amp;gt; &amp;lt;key1,key2...&amp;gt;-
# ex)kubectl label pod/my-pod app-&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;&lt;b&gt;kubectl get 명령어와 함께 Selector를 사용하는 방법&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1676465164970&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$kubectl get &amp;lt;object&amp;gt; --selector &amp;lt;label query1, ... label query N&amp;gt;
$kubectl get &amp;lt;object&amp;gt; -l &amp;lt;label query1, ... label query N&amp;gt;
# label query: key=value&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;* pod에서 env가 prod인가 아닌가로 쿼리 (Equality-Based Selector)&lt;/p&gt;
&lt;pre id=&quot;code_1676465202061&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# env가 prod인 경우
$kubectl get pod --selector env=prod

# env가 prod가 아닌 경우
$kubectl get pod --selector env!=prod&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 값이 어떤 집합에 속해 있는지 아닌지로 조회 (Set-Based Selector)&lt;/p&gt;
&lt;pre id=&quot;code_1676465452029&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# env가 dev,stage,prod일 경우 조회
$kubectl get pod --selector &amp;lsquo;env in (dev,stage,prod)&amp;rsquo;
# env가 dev,stage,prod가 아닐 경우 조회
$kubectl get pod --selector &amp;lsquo;env notin (dev,stage,prod)&amp;rsquo;&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;출처 : 패스트캠퍼스&lt;/p&gt;</description>
      <category>개발/kubernetes</category>
      <category>kubectl label</category>
      <category>kubectl selector</category>
      <category>Kubernetes</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/464</guid>
      <comments>https://bitrader.tistory.com/464#entry464comment</comments>
      <pubDate>Wed, 15 Feb 2023 21:52:14 +0900</pubDate>
    </item>
    <item>
      <title>kubernetes pod</title>
      <link>https://bitrader.tistory.com/463</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버네티스에서 Pod는 노드에서 컨테이너를 실행하기 위한 가장 기본적인 배포 단위이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러개의 노드에서 1개이상의 pod를 배포하는것이 가능하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img (1).png&quot; data-origin-width=&quot;2392&quot; data-origin-height=&quot;1380&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/F0tmv/btrZrfdjXbl/pPZLNSmtnVx5NZlmiCwKv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/F0tmv/btrZrfdjXbl/pPZLNSmtnVx5NZlmiCwKv0/img.png&quot; data-alt=&quot;출처 :&amp;amp;nbsp;https://ooeunz.tistory.com/119&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/F0tmv/btrZrfdjXbl/pPZLNSmtnVx5NZlmiCwKv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF0tmv%2FbtrZrfdjXbl%2FpPZLNSmtnVx5NZlmiCwKv0%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;548&quot; height=&quot;316&quot; data-filename=&quot;img (1).png&quot; data-origin-width=&quot;2392&quot; data-origin-height=&quot;1380&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 :&amp;nbsp;https://ooeunz.tistory.com/119&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;쿠버네티스 Pod의 특징&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쿠버네티스를 Pod를 생성할때 노드에서 유일한 IP를 할당&lt;/li&gt;
&lt;li&gt;Pod 내부 컨테이너간에 localhost로 통신, 포트 충돌에 유의&lt;/li&gt;
&lt;li&gt;Pod 내부에서는 네트워크와 볼륨 자원을 공유&lt;/li&gt;
&lt;li&gt;외부에서는 접근이 불가능하고 클러스터 내부에서끼리 통신이 가능하다 (외부 트래픽을 수신하려면 Service, Ingress 오브젝트가 필요)&lt;/li&gt;
&lt;li&gt;또한 자가 치유 능력이 없음으로, Pod나 노드에 이상이 발생하면 종료되고 재생성 되지 않는다. (ReplicaSet 오브젝트로 해당 부분 관리 가능)&lt;/li&gt;
&lt;li&gt;Pod와 컨테이너를 1:1로 기본 설계하고 특별한 사유가 있는 경우 1:N 구조를 고민하는것이 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;노드에 배포되는 과정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 사용자로부터 Pod 배포 요청 수신 및 수락&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 요청 받은 수만큼 Pod Replica를 생성한다 (Pod desired state == current state)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. Pod을 배포할 적절한 노드를 선택 (nodeSelector)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 5에게 이미지 다운로드를 명령하고 Pod 실행을 준비한다. Pod 상태를 업데이트한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 이미지를 다운로드하고 컨테이너를 실행&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;배포되는과정.PNG&quot; data-origin-width=&quot;729&quot; data-origin-height=&quot;212&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QzXXL/btrZuGAJ6RL/yK47rqN7MzP0F9jekTa5bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QzXXL/btrZuGAJ6RL/yK47rqN7MzP0F9jekTa5bk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QzXXL/btrZuGAJ6RL/yK47rqN7MzP0F9jekTa5bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQzXXL%2FbtrZuGAJ6RL%2FyK47rqN7MzP0F9jekTa5bk%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;664&quot; height=&quot;193&quot; data-filename=&quot;배포되는과정.PNG&quot; data-origin-width=&quot;729&quot; data-origin-height=&quot;212&quot;/&gt;&lt;/span&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;b&gt;yaml 파일 예시&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gpu 라벨이 true인 노드에만 배포&lt;/p&gt;
&lt;pre id=&quot;code_1676463605691&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# apiVersion등 최상위 항목들 생략
spec:
  nodeSelector: # Pod 배포를 위한 노드를 선택
    gpu: &amp;ldquo;true&amp;rdquo;&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;쿠버네티스 기본 이미지를 다운로드 받아서 배포&lt;/p&gt;
&lt;pre id=&quot;code_1676463648100&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# apiVersion등 최상위 항목들 생략
spec:
  containers:
  - name: kube-basic # 컨테이너 이름
    image: kube-basic:1.0 # 도커 이미지 주소
    imagePullPolicy: &amp;ldquo;Always&amp;rdquo; # 도커 이미지 다운로드 정책 (Always/IfNotPresent/Never)
    ports:
    - containerPort: 80 # 통신에 사용할 컨테이너 포트&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;이미지의 환경변수 설정 예시&lt;/p&gt;
&lt;pre id=&quot;code_1676463733251&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spec:
  containers:
  - name: kube-basic
    image: kube-basic:1.0
    env: # 컨테이너에 설정할 환경변수 목록
    - name: PROFILE # 환경변수 이름
      value: production # 환경변수 값
    - name: LOG_DIRECTORY
      value: /logs
    - name: MESSAGE
      value: This application is running on $(PROFILE) # 다른 환경변수 참조&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;호스트의 볼륨을 마운트해서 pod에서 해당 볼륨을 마운트 할 수 있게함&lt;/p&gt;
&lt;pre id=&quot;code_1676463970896&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spec:
  containers:
  volumes: # 컨테이너가 사용할 수 있는 볼륨 목록
  - name: host-volume # 볼륨 이름
    hostPath: # 볼륨 타입, 노드에 있는 파일이나 디렉토리를 마운트하고자 할 때
      path: /data/mysql&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;쿠버네티스가 지원하는 볼륨 타입 : &lt;a href=&quot;https://kubernetes.io/docs/concepts/storage/volumes/&quot;&gt;Volumes | Kubernetes&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1676463478016&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;Volumes&quot; data-og-description=&quot;On-disk files in a container are ephemeral, which presents some problems for non-trivial applications when running in containers. One problem is the loss of files when a container crashes. The kubelet restarts the container but with a clean state. A second&quot; data-og-host=&quot;kubernetes.io&quot; data-og-source-url=&quot;https://kubernetes.io/docs/concepts/storage/volumes/&quot; data-og-url=&quot;https://kubernetes.io/docs/concepts/storage/volumes/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/wAEOP/hyRCZI8b95/Ake5gSeNqCTZSp1Cxj3ock/img.png?width=1727&amp;amp;height=373&amp;amp;face=0_0_1727_373,https://scrap.kakaocdn.net/dn/bHalkh/hyRC2eLM8u/LsGaPeBAibR82kYmCp1220/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512&quot;&gt;&lt;a href=&quot;https://kubernetes.io/docs/concepts/storage/volumes/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://kubernetes.io/docs/concepts/storage/volumes/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/wAEOP/hyRCZI8b95/Ake5gSeNqCTZSp1Cxj3ock/img.png?width=1727&amp;amp;height=373&amp;amp;face=0_0_1727_373,https://scrap.kakaocdn.net/dn/bHalkh/hyRC2eLM8u/LsGaPeBAibR82kYmCp1220/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_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;Volumes&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;On-disk files in a container are ephemeral, which presents some problems for non-trivial applications when running in containers. One problem is the loss of files when a container crashes. The kubelet restarts the container but with a clean state. A second&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;kubernetes.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;p data-ke-size=&quot;size16&quot;&gt;출처 : 패스트캠퍼스&lt;/p&gt;</description>
      <category>개발/kubernetes</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/463</guid>
      <comments>https://bitrader.tistory.com/463#entry463comment</comments>
      <pubDate>Wed, 15 Feb 2023 21:26:26 +0900</pubDate>
    </item>
    <item>
      <title>쿠버네티스 오브젝트</title>
      <link>https://bitrader.tistory.com/462</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;쿠버네티스로 애플리케이션을 배포하고 관리하기&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;K8s-2.png&quot; data-origin-width=&quot;951&quot; data-origin-height=&quot;405&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nFbGP/btrZrcUR8Z7/nkrKM9qXOB7Q0SwsXE6go0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nFbGP/btrZrcUR8Z7/nkrKM9qXOB7Q0SwsXE6go0/img.png&quot; data-alt=&quot;출처 : https://blog.christophetd.fr/using-k3s-for-command-and-control-on-compromised-linux-hosts/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nFbGP/btrZrcUR8Z7/nkrKM9qXOB7Q0SwsXE6go0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnFbGP%2FbtrZrcUR8Z7%2FnkrKM9qXOB7Q0SwsXE6go0%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;557&quot; height=&quot;237&quot; data-filename=&quot;K8s-2.png&quot; data-origin-width=&quot;951&quot; data-origin-height=&quot;405&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : https://blog.christophetd.fr/using-k3s-for-command-and-control-on-compromised-linux-hosts/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버네티스를 사용하여 사용자의 의도대로 어플리케이션을 배포하는 방법은 쿠버네티스 오브젝트를 정의하여 Master node(API Server)에 명령을 내리는것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 명령을 내리는 표현방식은 yaml형식의 파일을 작성하여 REST API로 마스터 노드에 전달을 하면 된다.&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;쿠버네티스 오브젝트란 &quot;쿠버네티스 클러스터를 이용해 애플리케이션을 배포하고 운영하기 위해 필요한 모든 쿠버네티스 리소르스를 의미하며&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;b&gt;Pod &lt;/b&gt;: 애플리케이션&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Replicaset &lt;/b&gt;: 복제 횟수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Node, Namespace &lt;/b&gt;: 어디에(서버 등)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Deployment &lt;/b&gt;: 배포 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Service, Endpoints &lt;/b&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;이러한 오브젝트에 대한 요구사항을 yaml 파일로 작성해서 master node에 api 형식으로 보내게 된다.&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;아래의 spec 이하에 작성된 내용이 이용자가 최종적으로 원하는 상태이며 간단한 설명을 하자면 nginx 이미지를 다운로드를 받고 80번 포트를 오픈한 컨테이너를 2개 생성하라는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 필드의 의미는 아래와 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;apiVersion : 오브젝트를 생성할 떄 사용하는 API 버전&lt;/li&gt;
&lt;li&gt;kind : 생성하고자 하는 오브젝트 종류&lt;/li&gt;
&lt;li&gt;metadata : 오브젝트를 구분 지을 수 있는 정보 (name, label 등)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;spec : 사용자가 원하는 오브젝트 상태&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1676451604329&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# yaml 파일 예시
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
  spec:
    containers:
    - name: nginx
      image: nginx:1.14.2
      ports:
      - containerPort: 80&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 status 필드로 현재의 쿠버네티스 클러스터의 상태를 확인할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676451829427&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# status 필드 예시
status:
  availableReplicas: 2
  conditions:
    - lastTransitionTime: '2022-02-06T12:28:39Z'
      lastUpdateTime: '2022-02-06T12:28:39Z'
      message: Deployment has minimum availability.
      reason: MinimumReplicasAvailable
      status: 'True'
      type: Available
    - lastTransitionTime: '2022-02-06T12:28:16Z'
      lastUpdateTime: '2022-02-06T12:28:39Z'
      message: ReplicaSet &quot;my-app-5b7548d6b&quot; has successfully progressed.
      reason: NewReplicaSetAvailable
      status: 'True'
      type: Progressing
  observedGeneration: 1
  readyReplicas: 2
  replicas: 2
  updatedReplicas: 2&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;따라서 쿠버네티스를 이용하여 클러스터를 구성하는 과정을 아래를 목적으로 한다고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;spec(쿠버네티스가 달성해야할 목표) == status(오브젝트의 현재 상태)&lt;/b&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;1. 사용자가 쿠버네티스 오브젝트 yaml 파일 작성(yaml 파일안에 spec 필드가 요구사항)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 쿠버네티스 API를 이용해서 쿠버네티스에 생성 요청&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 쿠버네티스 API 서버가 오브젝트 파일의 spec 파일을 읽고 오브젝트 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 쿠버네티스 ControllerManager가 spec과 status를 비교하면서 계속 조정하고 상태를 업데이트함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Kubectl 기본 명령어&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Kubectl api-resources&lt;/b&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;kubectl explain &amp;lt;type&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 쿠버네티스 오브젝트의 설명과 1레벨 속성들의 설명(apiVersion, kind, metadata 등)&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;b&gt;kubectl explain &amp;lt;type&amp;gt;.&amp;lt;fieldNmae&amp;gt;[.&amp;lt;fieldName&amp;gt;]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- ex) kubectl explain pods.spec.containers&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;kubectl get nodes&lt;/b&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;kubectl scale -f &amp;lt;object-file-name&amp;gt; --replicas=#&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- kubectl scale -f deployment.yaml --replicas=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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;kubectl diff -f &amp;lt;object-file-name&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- kubectl diff -f deployment.yaml&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;kubectl edit &amp;lt;type&amp;gt;/&amp;lt;name&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- kubectl edit deployment/nginx-deployment: replicas를 4로 변경&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 쿠버네티스 오브젝트의 spec을 editor로 편집&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;b&gt;kubectl port-forward &amp;lt;type&amp;gt;/&amp;lt;name&amp;gt; &amp;lt;local-port&amp;gt;:&amp;lt;container-port&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 로컬에서 쿠버네티스 오브젝트(pod or container)에 통신할 수 있도록 포트 포워딩&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;b&gt;kubectl attach &amp;lt;type&amp;gt;/&amp;lt;name&amp;gt; -c &amp;lt;container-name&amp;gt;&lt;/b&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;kubectl logs &amp;lt;type&amp;gt;/&amp;lt;name&amp;gt; -c &amp;lt;container-name&amp;gt; -f&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 현재 실행중인 컨테이너 프로세스에 모든 로그 출력 (-f : watch 모드)&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;&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/kubernetes</category>
      <category>k8s</category>
      <category>Kubernetes</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/462</guid>
      <comments>https://bitrader.tistory.com/462#entry462comment</comments>
      <pubDate>Wed, 15 Feb 2023 20:23:27 +0900</pubDate>
    </item>
    <item>
      <title>github action으로 google cloud에 CI/CD 환경 구축하기</title>
      <link>https://bitrader.tistory.com/457</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;구글 클라우드의 compute engine에 간단한 fastapi 어플을 띄우고 이를 github action을 통하여 간단한 CI/CD 환경을 구축하는것을 정리해보고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Compute engine에 접속할 secret을 github repo에 등록하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 compute engine에 접속하여 다음과 같은 명령어를 입력하여 rsa key를 생성을 해준다. rsa key 생성은 굳이 compute engine에서 실행하지 않아도 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1676189290291&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ssh-keygen -t rsa -b 4096 -C [your_email]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;키생성.PNG&quot; data-origin-width=&quot;656&quot; data-origin-height=&quot;320&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M09OY/btrYY59hpBe/TDN05CnsBj8ZJqaxxDl4a0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M09OY/btrYY59hpBe/TDN05CnsBj8ZJqaxxDl4a0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M09OY/btrYY59hpBe/TDN05CnsBj8ZJqaxxDl4a0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM09OY%2FbtrYY59hpBe%2FTDN05CnsBj8ZJqaxxDl4a0%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;513&quot; height=&quot;250&quot; data-filename=&quot;키생성.PNG&quot; data-origin-width=&quot;656&quot; data-origin-height=&quot;320&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 cat으로 생성한 public key를 확인해주고 이를 복사하여&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;public key.PNG&quot; data-origin-width=&quot;1009&quot; data-origin-height=&quot;107&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nYEOt/btrYXi8TsU1/pEyvexDx0Y1CybXjZkhvj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nYEOt/btrYXi8TsU1/pEyvexDx0Y1CybXjZkhvj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nYEOt/btrYXi8TsU1/pEyvexDx0Y1CybXjZkhvj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnYEOt%2FbtrYXi8TsU1%2FpEyvexDx0Y1CybXjZkhvj0%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;1009&quot; height=&quot;107&quot; data-filename=&quot;public key.PNG&quot; data-origin-width=&quot;1009&quot; data-origin-height=&quot;107&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GCP&amp;nbsp; 콘솔의 Compute engine &amp;gt; 설정 &amp;gt; 메타데이터 항목에서 SSH키에 추가를 해준다. 정상적으로 키가 입력이 되었으면 사용자 이름은 자동으로 입력이 된다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;메타데이타.PNG&quot; data-origin-width=&quot;1083&quot; data-origin-height=&quot;317&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Asbk1/btrY1mCZGHh/sveQbkEqlFtcwkua6rOwK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Asbk1/btrY1mCZGHh/sveQbkEqlFtcwkua6rOwK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Asbk1/btrY1mCZGHh/sveQbkEqlFtcwkua6rOwK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAsbk1%2FbtrY1mCZGHh%2FsveQbkEqlFtcwkua6rOwK0%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;727&quot; height=&quot;213&quot; data-filename=&quot;메타데이타.PNG&quot; data-origin-width=&quot;1083&quot; data-origin-height=&quot;317&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 아래의 명령어로 public key를 authorized_keys로 복사를 해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1676200178354&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cat id_rsa.pub &amp;gt;&amp;gt; authorized_keys&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 authorized_keys에 퍼블릭 키가 등록되면 외부에서 접근이 가능해진다. 단 GCP는 주기적으로 authorized_keys 파일을 삭제해서 외부에서 키파일을 등록하는 과정이 필요하다.&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;다음으로는 Compute engine에서 운영할 어플의 소스코드 github 저장소로 이동을 하여 Settings &amp;gt; Secrets &amp;gt; Actions로 이동을 하여 New repository secret을 클릭하여 우리의 Compute engine으로 접속하는 key들을 저장을 해준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;github secret.PNG&quot; data-origin-width=&quot;1538&quot; data-origin-height=&quot;892&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/T0e7D/btrYSCHAa2j/qYs4GwFpZh7PZgzmPgU6YK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/T0e7D/btrYSCHAa2j/qYs4GwFpZh7PZgzmPgU6YK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/T0e7D/btrYSCHAa2j/qYs4GwFpZh7PZgzmPgU6YK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FT0e7D%2FbtrYSCHAa2j%2FqYs4GwFpZh7PZgzmPgU6YK%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;676&quot; height=&quot;392&quot; data-filename=&quot;github secret.PNG&quot; data-origin-width=&quot;1538&quot; data-origin-height=&quot;892&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성이 필요한 secrets는 총 3개이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HOST : Compute engine의 IP 주소&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;USERNAME : Compute engine의 사용자 이름(터미널에서 맨 왼쪽에 뜨는 이름)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSH_KEY : 위에서 생성한 rsa private key&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;secret_completed.PNG&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;732&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/14wP6/btrYYpteSha/OOK8bhAFfBYIXS3OC4xMK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/14wP6/btrYYpteSha/OOK8bhAFfBYIXS3OC4xMK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/14wP6/btrYYpteSha/OOK8bhAFfBYIXS3OC4xMK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F14wP6%2FbtrYYpteSha%2FOOK8bhAFfBYIXS3OC4xMK1%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;519&quot; height=&quot;476&quot; data-filename=&quot;secret_completed.PNG&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;732&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 secrets는 github action에서 아래와 같이 활용이 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;secret 활용.PNG&quot; data-origin-width=&quot;991&quot; data-origin-height=&quot;506&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7FKVA/btrYVgQ3E9a/3T6KwCAZ0C0luNNxJ2eK9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7FKVA/btrYVgQ3E9a/3T6KwCAZ0C0luNNxJ2eK9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7FKVA/btrYVgQ3E9a/3T6KwCAZ0C0luNNxJ2eK9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7FKVA%2FbtrYVgQ3E9a%2F3T6KwCAZ0C0luNNxJ2eK9k%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;690&quot; height=&quot;352&quot; data-filename=&quot;secret 활용.PNG&quot; data-origin-width=&quot;991&quot; data-origin-height=&quot;506&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Compute engine에서 fastapi 어플 띄우기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 명령어를 실행하여 python 환경과 fastapi를 설치&lt;/p&gt;
&lt;pre id=&quot;code_1676189958585&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 업데이트 및 파이썬 설치
sudo apt-get update
sudo apt-get install python3.8-venv -y

# repository clone 및 python 가상환경 설치
git clone [repository url]
cd [repository]
python3 -m venv fastapi
source fastapi/bin/activate

# fastapi 설치
pip install fastapi
pip install &quot;uvicorn[standard]&quot;

# github action간 추가인증이 없도록 하는 명령어
git config --global credential.helper store&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 간단한 fastapi script를 작성해준다 fastapi 공식 문서에 있는 코드를 가져왔다.&lt;/p&gt;
&lt;pre id=&quot;code_1676189973480&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# main.py
from fastapi import FastAPI

app = FastAPI()


@app.get(&quot;/&quot;)
async def root():
    return {&quot;message&quot;: &quot;Hello World&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 fastapi를 run 해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1676190051134&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;uvicorn main:app --reload --host=0.0.0.0 --port=8000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하고 현재 돌아가고 있는 Compute engine의 IP:8000으로 접속을 하면 접속이 안되는데 이는 Compute engine의 기본 방화벽이 있기 때문이다. 따라서 우리가 접속할 포트는 방화벽 정책에서 허용을 해주어야 한다.&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;Compute engine 인스턴스 목록에서 오른쪽 아래의 방화벽 규칙 설정으로 이동을 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;방화벽규칙설정.PNG&quot; data-origin-width=&quot;1467&quot; data-origin-height=&quot;433&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beEGC0/btrY4lDOPce/KFrgCbkXR7hPUOGYBgEwG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beEGC0/btrY4lDOPce/KFrgCbkXR7hPUOGYBgEwG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beEGC0/btrY4lDOPce/KFrgCbkXR7hPUOGYBgEwG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeEGC0%2FbtrY4lDOPce%2FKFrgCbkXR7hPUOGYBgEwG0%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;1467&quot; height=&quot;433&quot; data-filename=&quot;방화벽규칙설정.PNG&quot; data-origin-width=&quot;1467&quot; data-origin-height=&quot;433&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이동을 해서 확인해보면 현재 방화벽 규칙을 적용을 하려는 instance-1 Compute engine은 네트워크 태그가 없는것을 확인할 수 있다. 이부분이 추가가 필요한 부분이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;네트워크태그.PNG&quot; data-origin-width=&quot;1444&quot; data-origin-height=&quot;765&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/B2ArC/btrYSCVaBv4/jPvxfIpKrl2L4ywRLlRjy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/B2ArC/btrYSCVaBv4/jPvxfIpKrl2L4ywRLlRjy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/B2ArC/btrYSCVaBv4/jPvxfIpKrl2L4ywRLlRjy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FB2ArC%2FbtrYSCVaBv4%2FjPvxfIpKrl2L4ywRLlRjy1%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;617&quot; height=&quot;327&quot; data-filename=&quot;네트워크태그.PNG&quot; data-origin-width=&quot;1444&quot; data-origin-height=&quot;765&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;IPv4의 소스 IPv4는 현재 예제에서는 0.0.0.0/0로 추가를 하였지만 이는 모든 접속을 허용한다는 의미로 실무에서는 진짜로 허용할 IP만 허용을 해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포트는 fastapi가 사용할 8000포트만 열어주었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yWrFu/btrY5kY8GAH/6F7WDagiIgCq0FaKk8QMO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yWrFu/btrY5kY8GAH/6F7WDagiIgCq0FaKk8QMO0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;783&quot; data-filename=&quot;방화벽규칙.PNG&quot; style=&quot;width: 47.0591%; margin-right: 10px;&quot; data-widthpercent=&quot;47.61&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yWrFu/btrY5kY8GAH/6F7WDagiIgCq0FaKk8QMO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyWrFu%2FbtrY5kY8GAH%2F6F7WDagiIgCq0FaKk8QMO0%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;587&quot; height=&quot;783&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8NjUW/btrY5lwYFYi/sUFSrj5UbCW9cm9KYhhzUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8NjUW/btrY5lwYFYi/sUFSrj5UbCW9cm9KYhhzUK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;708&quot; data-filename=&quot;방화벽규칙2.PNG&quot; style=&quot;width: 51.7781%;&quot; data-widthpercent=&quot;52.39&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8NjUW/btrY5lwYFYi/sUFSrj5UbCW9cm9KYhhzUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8NjUW%2FbtrY5lwYFYi%2FsUFSrj5UbCW9cm9KYhhzUK%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;584&quot; height=&quot;708&quot;/&gt;&lt;/span&gt;&lt;/div&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-filename=&quot;방화벽규칙생성.PNG&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;70&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnlKDF/btrYSN3pb9R/iVwoGnYFoki7S7a27iDHtK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnlKDF/btrYSN3pb9R/iVwoGnYFoki7S7a27iDHtK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnlKDF/btrYSN3pb9R/iVwoGnYFoki7S7a27iDHtK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnlKDF%2FbtrYSN3pb9R%2FiVwoGnYFoki7S7a27iDHtK%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;1230&quot; height=&quot;70&quot; data-filename=&quot;방화벽규칙생성.PNG&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;70&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 방화벽 규칙을 네트워크 태그에 추가를 해준 후 다시 IP:포트로 접속을 해보면 다음과 같이 접속이 되는것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;접속완료.PNG&quot; data-origin-width=&quot;507&quot; data-origin-height=&quot;129&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Wj88t/btrYYo8Vgmx/LTec4rbl3qmzvI453QcK5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Wj88t/btrYYo8Vgmx/LTec4rbl3qmzvI453QcK5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Wj88t/btrYYo8Vgmx/LTec4rbl3qmzvI453QcK5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWj88t%2FbtrYYo8Vgmx%2FLTec4rbl3qmzvI453QcK5K%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;507&quot; height=&quot;129&quot; data-filename=&quot;접속완료.PNG&quot; data-origin-width=&quot;507&quot; data-origin-height=&quot;129&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;CI/CD 환경을 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CI/CD 환경을 구축하기 위하여 간단한 github action 파일을 작성해 보았다.&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;main branch의 모든 파일에 대하여 변경이 감지되면 해당 action이 실시가 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1676200882246&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;name: CICD-SSH
on:
  push:
      branches: [ main ]
      paths:
        - '**'

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - name: executing remote ssh commands using ssh key
      uses: appleboy/ssh-action@master
      with:
        host: ${{ secrets.HOST }} 
        username: ${{ secrets.USERNAME }}
        key: ${{ secrets.SSH_KEY }}
        port: 22
        script: |
            cd ${{ github.event.repository.name }}
            pwd
            sh deploy-ssh.sh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;deploy-ssh.sh 파일 내용은 아래와 같다. 정말 간단하다 단순히 main brach에서 푸시된 코드내용을 git pull 해서 내용을 업데이트 하는것 뿐이다. fastapi는 autoload가 됨으로 서버를 재시작하지 않아도 알아서 업데이트된 코드로 서버가 돌아간다.&lt;/p&gt;
&lt;pre id=&quot;code_1676200932974&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash
git pull origin main&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;github action의 디테일에 들어가서 확인해보면 작업이 성공적으로 수행된 로그기록도 확인이 가능하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;complete_job.png&quot; data-origin-width=&quot;1885&quot; data-origin-height=&quot;949&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAc71b/btrY4m3S9wj/F9A7HyupKDKPZZ5LXEMwrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAc71b/btrY4m3S9wj/F9A7HyupKDKPZZ5LXEMwrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAc71b/btrY4m3S9wj/F9A7HyupKDKPZZ5LXEMwrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAc71b%2FbtrY4m3S9wj%2FF9A7HyupKDKPZZ5LXEMwrK%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;665&quot; height=&quot;335&quot; data-filename=&quot;complete_job.png&quot; data-origin-width=&quot;1885&quot; data-origin-height=&quot;949&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/appleboy/ssh-action#setting-up-ssh-key&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/appleboy/ssh-action#setting-up-ssh-key&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1676200232331&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 - appleboy/ssh-action: GitHub Actions for executing remote ssh commands.&quot; data-og-description=&quot;GitHub Actions for executing remote ssh commands. Contribute to appleboy/ssh-action development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/appleboy/ssh-action#setting-up-ssh-key&quot; data-og-url=&quot;https://github.com/appleboy/ssh-action&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/7Dw4X/hyRAf5EuBX/YZZubcZf8v1T0hdHXVzdKK/img.png?width=1200&amp;amp;height=600&amp;amp;face=978_105_1021_153&quot;&gt;&lt;a href=&quot;https://github.com/appleboy/ssh-action#setting-up-ssh-key&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/appleboy/ssh-action#setting-up-ssh-key&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/7Dw4X/hyRAf5EuBX/YZZubcZf8v1T0hdHXVzdKK/img.png?width=1200&amp;amp;height=600&amp;amp;face=978_105_1021_153');&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 - appleboy/ssh-action: GitHub Actions for executing remote ssh commands.&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;GitHub Actions for executing remote ssh commands. Contribute to appleboy/ssh-action 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>개발/github</category>
      <category>github</category>
      <category>GitHub Action</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/457</guid>
      <comments>https://bitrader.tistory.com/457#entry457comment</comments>
      <pubDate>Sun, 12 Feb 2023 17:53:35 +0900</pubDate>
    </item>
    <item>
      <title>github action 기본사항</title>
      <link>https://bitrader.tistory.com/455</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;CI/CD 도구로는 많은 것들이 있지만 github action이라는 것도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;github action은 github에서 출시한 기능으로 소프트웨어 Workflow 자동화를 도와주는 도구이다.&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;b&gt;workflow 예시&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TEST CODE&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;Prod, Staging, Dev 서버에 코드 배포&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;github Repo에 저장된 스크립트를 일정 주기를 가지고 실행&lt;/li&gt;
&lt;li&gt;crontab의 대용&lt;/li&gt;
&lt;li&gt;setup-python v2가 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Github Tag, Release 자동으로 설정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Main 브랜치에 Merge 될 경우 특정 작업 주행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;그 외에도 다양한 Workflow를 만들 수 있고 Workflow 템플릿도 커스텀이 가ㅡㅇ
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Action Marketplace :&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Awesome Github Action :&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;하지만 private repo를 운영을 하면 유료 플랜을 사용해야함&lt;/li&gt;
&lt;/ul&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;b&gt;Github Action을 사용하는방식&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1)&amp;nbsp; 코드 작업&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) 코드 작업 후, Github Action으로 무엇을 할 것인지 생각&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3) 사용할 Workflow 정의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4) Workflow 정의 후 정상 작동하는지 확인&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;Github Action의 핵심개념&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Workflow, Event, Job, Step, Action, Runner&lt;/b&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;b&gt;workflow&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러개의 Job으로 구성이되고 Event Trigger되는 자동화된 Process이며 최상위 개념&lt;/li&gt;
&lt;li&gt;Workflow 파일은 YAML으로 작성되며, github repo의 .github/workflows에 저장됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Event&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Workflow를 trigger하는 특정 활동, 규칙&lt;/li&gt;
&lt;li&gt;특정 Branch를 push하는 경우&lt;/li&gt;
&lt;li&gt;특정 Branch로 pull request하는 경우&lt;/li&gt;
&lt;li&gt;특정 시간대에 반복(Cron)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Jobs&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Runner에서 실행되는 step의 조합&lt;/li&gt;
&lt;li&gt;여러 job이 있는 경우 병렬로 실행하며, 순차적으로 실행도 가능&lt;/li&gt;
&lt;li&gt;job들간에 의존 관계를 가질 수도 있음(A process after B process)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Step&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Step은 Job에서 실행되는 개별 작업&lt;/li&gt;
&lt;li&gt;Action을 실행하거나 쉘 커맨드 실행&lt;/li&gt;
&lt;li&gt;하나의 Job에선 데이터를 공유하는것이 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Actions&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Workflow의 제일 작은 단위&lt;/li&gt;
&lt;li&gt;Job을 생성하기 위해 여러 Step을 묶은 개념이며 재사용이 가능한 Component&lt;/li&gt;
&lt;li&gt;Action을 개인적으로 만들 수도 있고, Marketplace의 Action을 사용할 수도 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Runner&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Github Action도 일종의 서버에서 실행되는 개념이며 Workflow가 실행될 서버&lt;/li&gt;
&lt;li&gt;Github-hosted Runner : Github Action의 서버를 사용&lt;/li&gt;
&lt;li&gt;Self-hosted Runner : 직접 서버를 호스팅해서 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Github action 간단한 예제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;github action에 대한 간단한 python 예제를 정리해보고자 한다. 먼저 github으로 이동해서 간단한 repo와 간단한 python script를 추가해준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wymf4/btrYRSDRBDS/yEvPDBFChQYvmlm4DwOsWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wymf4/btrYRSDRBDS/yEvPDBFChQYvmlm4DwOsWK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;973&quot; data-origin-height=&quot;406&quot; data-filename=&quot;creat_new.PNG&quot; style=&quot;width: 36.1482%; margin-right: 10px;&quot; data-widthpercent=&quot;36.57&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wymf4/btrYRSDRBDS/yEvPDBFChQYvmlm4DwOsWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwymf4%2FbtrYRSDRBDS%2FyEvPDBFChQYvmlm4DwOsWK%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;973&quot; height=&quot;406&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQpj3K/btrYSCtLSUS/Aah2KycDF7k4xQdo8tAwc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQpj3K/btrYSCtLSUS/Aah2KycDF7k4xQdo8tAwc1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1251&quot; data-origin-height=&quot;301&quot; data-filename=&quot;create_file.PNG&quot; style=&quot;width: 62.689%;&quot; data-widthpercent=&quot;63.43&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQpj3K/btrYSCtLSUS/Aah2KycDF7k4xQdo8tAwc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQpj3K%2FbtrYSCtLSUS%2FAah2KycDF7k4xQdo8tAwc1%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;1251&quot; height=&quot;301&quot;/&gt;&lt;/span&gt;&lt;/div&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;다음으로 repo의 action을 눌러서 이동을 하면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;action.PNG&quot; data-origin-width=&quot;888&quot; data-origin-height=&quot;264&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qYvBA/btrYRUPaaXo/QZ3j8TsM4En297nPxkJyxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qYvBA/btrYRUPaaXo/QZ3j8TsM4En297nPxkJyxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qYvBA/btrYRUPaaXo/QZ3j8TsM4En297nPxkJyxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqYvBA%2FbtrYRUPaaXo%2FQZ3j8TsM4En297nPxkJyxK%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;705&quot; height=&quot;210&quot; data-filename=&quot;action.PNG&quot; data-origin-width=&quot;888&quot; data-origin-height=&quot;264&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 사용할 수 있는 다양한 github actions들이 나온다. 나는 여기서 python application을 사용할 예정임으로 python application에 있는 configure를 눌러주면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;python application.PNG&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;652&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMEcwD/btrYUuIr8U1/aJRNCkL23uWyg7RpXhOKw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMEcwD/btrYUuIr8U1/aJRNCkL23uWyg7RpXhOKw1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMEcwD/btrYUuIr8U1/aJRNCkL23uWyg7RpXhOKw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMEcwD%2FbtrYUuIr8U1%2FaJRNCkL23uWyg7RpXhOKw1%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;669&quot; height=&quot;360&quot; data-filename=&quot;python application.PNG&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;652&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 python-app.yml 파일이 생성되고 start commit을 누르면 github action이 시작된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 빨간 박스는 이전에 작성했던 python file을 수행하기 위한 shell 명령어이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;start-commit.PNG&quot; data-origin-width=&quot;1875&quot; data-origin-height=&quot;829&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J4qq0/btrYWaJAK2a/gmWge9HqHCnYGpdESYVvY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J4qq0/btrYWaJAK2a/gmWge9HqHCnYGpdESYVvY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J4qq0/btrYWaJAK2a/gmWge9HqHCnYGpdESYVvY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ4qq0%2FbtrYWaJAK2a%2FgmWge9HqHCnYGpdESYVvY1%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;1875&quot; height=&quot;829&quot; data-filename=&quot;start-commit.PNG&quot; data-origin-width=&quot;1875&quot; data-origin-height=&quot;829&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;start commit을 누르면 아래와 같이 commit id 옆에 노랑 동그라미가 생성이 되며 python-app.yml 명세에 적힌대로 action이 수행되고 있다는 뜻이다. 노랑 동그라미를 눌러 세부사항으로 이동을 해보면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;yellow.PNG&quot; data-origin-width=&quot;919&quot; data-origin-height=&quot;232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4aA2Z/btrYSi3pG2B/Jmd7WRlbnpDZmzy5ZVn6o1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4aA2Z/btrYSi3pG2B/Jmd7WRlbnpDZmzy5ZVn6o1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4aA2Z/btrYSi3pG2B/Jmd7WRlbnpDZmzy5ZVn6o1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4aA2Z%2FbtrYSi3pG2B%2FJmd7WRlbnpDZmzy5ZVn6o1%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;610&quot; height=&quot;154&quot; data-filename=&quot;yellow.PNG&quot; data-origin-width=&quot;919&quot; data-origin-height=&quot;232&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령을 수행하는 동안의 세부사항을 확인할 수 있다. 지금의 경우는 성공적으로 github action이 수행되어 이전에 작성했던 python script의 hello world가 출력된것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;details.PNG&quot; data-origin-width=&quot;1886&quot; data-origin-height=&quot;784&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJSrbo/btrYVgb8xT1/wnDtbd1n30M3nYPe0C4yNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJSrbo/btrYVgb8xT1/wnDtbd1n30M3nYPe0C4yNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJSrbo/btrYVgb8xT1/wnDtbd1n30M3nYPe0C4yNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJSrbo%2FbtrYVgb8xT1%2FwnDtbd1n30M3nYPe0C4yNk%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;1886&quot; height=&quot;784&quot; data-filename=&quot;details.PNG&quot; data-origin-width=&quot;1886&quot; data-origin-height=&quot;784&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Python-app.yml 파일 상세&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;on : Event, 언제 Workflow가 실행될 것인가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 케이스는 main branch가 업데이트될때 또는 pull_request로 main branch의 코드가 업데이트될때 실행이 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_python-app.yml.PNG&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;273&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXojSu/btrYVhB8nye/XasCyDXLN2HbaweIcUUPo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXojSu/btrYVhB8nye/XasCyDXLN2HbaweIcUUPo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXojSu/btrYVhB8nye/XasCyDXLN2HbaweIcUUPo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXojSu%2FbtrYVhB8nye%2FXasCyDXLN2HbaweIcUUPo0%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;660&quot; height=&quot;228&quot; data-filename=&quot;edited_python-app.yml.PNG&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;273&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 jobs는 jobs의 정의이며 이경우는 build이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VOVmt/btrYUuBIqMG/KsIG1m5nha9MfwTfrakCtK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VOVmt/btrYUuBIqMG/KsIG1m5nha9MfwTfrakCtK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;313&quot; data-origin-height=&quot;188&quot; data-filename=&quot;edited_python-app-base2.png&quot; width=&quot;408&quot; height=&quot;361&quot; style=&quot;width: 32.6441%; margin-right: 10px;&quot; data-widthpercent=&quot;33.03&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VOVmt/btrYUuBIqMG/KsIG1m5nha9MfwTfrakCtK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVOVmt%2FbtrYUuBIqMG%2FKsIG1m5nha9MfwTfrakCtK%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;313&quot; height=&quot;188&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CB9At/btrYSCHkLiG/HOMK7L5ihKONPpJNgN24kK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CB9At/btrYSCHkLiG/HOMK7L5ihKONPpJNgN24kK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;449&quot; data-origin-height=&quot;133&quot; data-filename=&quot;jobs.PNG&quot; style=&quot;width: 66.1931%;&quot; data-widthpercent=&quot;66.97&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CB9At/btrYSCHkLiG/HOMK7L5ihKONPpJNgN24kK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCB9At%2FbtrYSCHkLiG%2FHOMK7L5ihKONPpJNgN24kK%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;449&quot; height=&quot;133&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;details에서 확인을 해보면 jobs기 build로 되어있는것을 확인할 수 있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;runs-on : 실행환경을 의미하며 아래는 ubuntu 환경에서 실시한다는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;uses :&amp;nbsp; 사용할 Github Action&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;name : Step의 이름&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;run : run에 작성된 쉘 커맨드가 실행되며 uses가 없는 경우에 run을 사용한다.&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-filename=&quot;edited_python-app-base.PNG&quot; data-origin-width=&quot;766&quot; data-origin-height=&quot;510&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWKrC1/btrYSN9QLBu/rHfgP1rSmYf08xKVZ34Z4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWKrC1/btrYSN9QLBu/rHfgP1rSmYf08xKVZ34Z4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWKrC1/btrYSN9QLBu/rHfgP1rSmYf08xKVZ34Z4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWKrC1%2FbtrYSN9QLBu%2FrHfgP1rSmYf08xKVZ34Z4k%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;766&quot; height=&quot;510&quot; data-filename=&quot;edited_python-app-base.PNG&quot; data-origin-width=&quot;766&quot; data-origin-height=&quot;510&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>개발/github</category>
      <category>github</category>
      <category>GitHub Action</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/455</guid>
      <comments>https://bitrader.tistory.com/455#entry455comment</comments>
      <pubDate>Sun, 12 Feb 2023 00:31:15 +0900</pubDate>
    </item>
    <item>
      <title>Google cloud Compute Engine에서 인스턴스 만들기</title>
      <link>https://bitrader.tistory.com/453</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;구글 클라우드는 무료체험하기 서비스가 있어서 처음 결제수단을 등록을 하면 300$의 크레딧을 공짜로 준다. GPU가 달린 서버를 생성하는것 까지는 할 수 없지만 300$내에서 다양한 인스턴스들을 만들어서 규모가 크지 않은 프로젝트는 실습을 할 수가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장기본이 되는 compute engine을 하나 만드는 예제를 정리해보고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;GCP에서 Compute Engine 생성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 GCP 콘솔로 이동해서 Compute Engine으로 이동하면 VM 인스턴스 메뉴 윗단에 인스턴스 만들기가 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;make_instance.PNG&quot; data-origin-width=&quot;849&quot; data-origin-height=&quot;141&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crmbP7/btrYRVAxp5P/BwLDI8ywkN6w9vJLqgdvz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crmbP7/btrYRVAxp5P/BwLDI8ywkN6w9vJLqgdvz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crmbP7/btrYRVAxp5P/BwLDI8ywkN6w9vJLqgdvz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrmbP7%2FbtrYRVAxp5P%2FBwLDI8ywkN6w9vJLqgdvz1%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;741&quot; height=&quot;123&quot; data-filename=&quot;make_instance.PNG&quot; data-origin-width=&quot;849&quot; data-origin-height=&quot;141&quot;/&gt;&lt;/span&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;인스턴스 만들기를 누르면 다음과 같은 옵션들을 선택할 수가 있다. 인스턴스의 이름 리전 그리고 서버의 기본적인 스펙을 정할수가 있다. 예상되는 금액도 계산이 된다. 내가 띄울 예정인 서버는 그닥 기능이 없는 서버라 기본적인 스펙으로 정하였다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;instance2.PNG&quot; data-origin-width=&quot;1035&quot; data-origin-height=&quot;612&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IaJRz/btrYRTQhYdE/PQeuNDSg57y5vO46AIocL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IaJRz/btrYRTQhYdE/PQeuNDSg57y5vO46AIocL1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IaJRz/btrYRTQhYdE/PQeuNDSg57y5vO46AIocL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIaJRz%2FbtrYRTQhYdE%2FPQeuNDSg57y5vO46AIocL1%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;713&quot; height=&quot;422&quot; data-filename=&quot;instance2.PNG&quot; data-origin-width=&quot;1035&quot; data-origin-height=&quot;612&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 부팅 디스크 옵션도 정할 수 있다. 나는 Ubuntu 이미지에 기본적이 30G의 용량만 주었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;부팅디스크2.PNG&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;666&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLjp2c/btrYSqAgmfQ/8FCqdpm945KH7Vy8TWdRdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLjp2c/btrYSqAgmfQ/8FCqdpm945KH7Vy8TWdRdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLjp2c/btrYSqAgmfQ/8FCqdpm945KH7Vy8TWdRdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLjp2c%2FbtrYSqAgmfQ%2F8FCqdpm945KH7Vy8TWdRdk%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;483&quot; height=&quot;504&quot; data-filename=&quot;부팅디스크2.PNG&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;666&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;인스턴스생성됨.PNG&quot; data-origin-width=&quot;1353&quot; data-origin-height=&quot;249&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ncqD7/btrYSj2h6MD/qZ2slRvIJJ6fAfUVHx7g6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ncqD7/btrYSj2h6MD/qZ2slRvIJJ6fAfUVHx7g6K/img.png&quot; data-alt=&quot;instance-1가 방금 만든 인스턴스이다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ncqD7/btrYSj2h6MD/qZ2slRvIJJ6fAfUVHx7g6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FncqD7%2FbtrYSj2h6MD%2FqZ2slRvIJJ6fAfUVHx7g6K%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;1353&quot; height=&quot;249&quot; data-filename=&quot;인스턴스생성됨.PNG&quot; data-origin-width=&quot;1353&quot; data-origin-height=&quot;249&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;instance-1가 방금 만든 인스턴스이다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;인스턴스의 IP를 고정IP로 변경하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 Compute engine의 IP는 인스턴스가 다시 시작되면 IP가 변경된다. 서비스를 고정적으로 운영을 하려면 해당 인스턴스에 고정 IP를 부여해주어야한다.&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;Compute Engine 탭에서 나와서 VPC 네트워크 탭의 IP 주소 탭으로 이동하면 다음과 같이 현재 내가 운영중인 인스턴스들의 IP 현황이 나온다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IVDc7/btrYUwfb9kl/ZqM3rp7CtA02JJF8Jkxks0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IVDc7/btrYUwfb9kl/ZqM3rp7CtA02JJF8Jkxks0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;271&quot; data-origin-height=&quot;610&quot; data-filename=&quot;vpc네트워크.PNG&quot; style=&quot;width: 12.459%; margin-right: 10px;&quot; data-widthpercent=&quot;12.61&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IVDc7/btrYUwfb9kl/ZqM3rp7CtA02JJF8Jkxks0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIVDc7%2FbtrYUwfb9kl%2FZqM3rp7CtA02JJF8Jkxks0%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;271&quot; height=&quot;610&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UopSh/btrYSN9NWgK/8YKSOtCN6UmT1XycQp45Y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UopSh/btrYSN9NWgK/8YKSOtCN6UmT1XycQp45Y1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;487&quot; data-filename=&quot;외부IP.PNG&quot; style=&quot;width: 86.3783%;&quot; data-widthpercent=&quot;87.39&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UopSh/btrYSN9NWgK/8YKSOtCN6UmT1XycQp45Y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUopSh%2FbtrYSN9NWgK%2F8YKSOtCN6UmT1XycQp45Y1%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;1500&quot; height=&quot;487&quot;/&gt;&lt;/span&gt;&lt;/div&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;IP주소의 상위 메뉴에서 외부 IP 주소탭의 맨오른쪽을 보면 예약이라는 글자를 클릭할 수 있는데 클릭하면 다음과 같은 메뉴가 뜨면서 IP를 고정적으로 운영할수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;고정IP 설명.PNG&quot; data-origin-width=&quot;393&quot; data-origin-height=&quot;405&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s0xCC/btrYSjgUtlk/yVIfKhut5KGBUqkyAGdNo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s0xCC/btrYSjgUtlk/yVIfKhut5KGBUqkyAGdNo0/img.png&quot; data-alt=&quot;해당 내용을 채워서 예약을 클릭하면 된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s0xCC/btrYSjgUtlk/yVIfKhut5KGBUqkyAGdNo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs0xCC%2FbtrYSjgUtlk%2FyVIfKhut5KGBUqkyAGdNo0%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;292&quot; height=&quot;301&quot; data-filename=&quot;고정IP 설명.PNG&quot; data-origin-width=&quot;393&quot; data-origin-height=&quot;405&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;다시 Compute engine의 인스턴스 메뉴로 들어가면 해당 인스턴스에 고정 IP가 할당되었다고 표시되게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;고정됨.PNG&quot; data-origin-width=&quot;1185&quot; data-origin-height=&quot;129&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/duaDxm/btrYTMCGlWo/MXaVxZm5QhiuzLMtYmAQEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/duaDxm/btrYTMCGlWo/MXaVxZm5QhiuzLMtYmAQEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/duaDxm/btrYTMCGlWo/MXaVxZm5QhiuzLMtYmAQEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FduaDxm%2FbtrYTMCGlWo%2FMXaVxZm5QhiuzLMtYmAQEK%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;769&quot; height=&quot;84&quot; data-filename=&quot;고정됨.PNG&quot; data-origin-width=&quot;1185&quot; data-origin-height=&quot;129&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;로컬 컴퓨터에서 ssh로 인스턴스에 접근하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compute engine에서 각 인스턴스의 맨 오늘쪽에 있는 SSH 메뉴를 클릭해보면 인스턴스에 접근할 수 있는 다양한 메뉴들이 있다. 간단하게 브라우저를 이용해서 접속도 가능하다. 하지만 로컬에서 접속하는 방법을 알아볼 예정이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ssh.PNG&quot; data-origin-width=&quot;1378&quot; data-origin-height=&quot;239&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blUw3m/btrYTNByCUv/TvEDFREhgZJNGhXnubClq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blUw3m/btrYTNByCUv/TvEDFREhgZJNGhXnubClq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blUw3m/btrYTNByCUv/TvEDFREhgZJNGhXnubClq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblUw3m%2FbtrYTNByCUv%2FTvEDFREhgZJNGhXnubClq1%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;733&quot; height=&quot;127&quot; data-filename=&quot;ssh.PNG&quot; data-origin-width=&quot;1378&quot; data-origin-height=&quot;239&quot;/&gt;&lt;/span&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;먼저 로컬에서 SSH 키를 생성을 해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1676122241861&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ssh-keygen -t rsa -f ~/.ssh/[KEY_FILE_NAME] -C [USERNAME]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 명령어를 입력하면 .ssh 폴더 및에 KEY_FILE_NAME의 private key와 KEY_FILE_NAME.pub의 public key가 생성되게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;public key를 GCP의 메타데이터에 등록을 해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cat 명령어로 public key의 내용을 확인하면 아래와 같이 출력이 되고 해당 내용을 복사해서 gcp의 메타데이터로 가져가면 된다. 나는 생성할때 USERNAME에 sml162655@gmail.com이란 주소를 사용해서 아래와 같이 출력이 되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;public_key.PNG&quot; data-origin-width=&quot;869&quot; data-origin-height=&quot;113&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u1AOt/btrYSOHESSl/vnMNWi5BadnCBl4aJ1HKLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u1AOt/btrYSOHESSl/vnMNWi5BadnCBl4aJ1HKLk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u1AOt/btrYSOHESSl/vnMNWi5BadnCBl4aJ1HKLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu1AOt%2FbtrYSOHESSl%2FvnMNWi5BadnCBl4aJ1HKLk%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;869&quot; height=&quot;113&quot; data-filename=&quot;public_key.PNG&quot; data-origin-width=&quot;869&quot; data-origin-height=&quot;113&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로는 Compute Engine 탭의 설정 &amp;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-filename=&quot;메타데이터2.PNG&quot; data-origin-width=&quot;1439&quot; data-origin-height=&quot;617&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUVwn0/btrYRUVUoRJ/uKNJYKigicrrEbt1Mt64s0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUVwn0/btrYRUVUoRJ/uKNJYKigicrrEbt1Mt64s0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUVwn0/btrYRUVUoRJ/uKNJYKigicrrEbt1Mt64s0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUVwn0%2FbtrYRUVUoRJ%2FuKNJYKigicrrEbt1Mt64s0%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;741&quot; height=&quot;318&quot; data-filename=&quot;메타데이터2.PNG&quot; data-origin-width=&quot;1439&quot; data-origin-height=&quot;617&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메타데이터의 ssh 키에서 수정을 누르면 아래와 같이 public key를 입력이 가능하다. public key를 보고 자동으로 생성이 됨으로 만약 그렇지 않다면 복붙을 잘못했을 수도 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;메타데이터3.PNG&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;625&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pX25W/btrYUSJcdO6/uaXDhKXkqSj0LtLvJQ25m0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pX25W/btrYUSJcdO6/uaXDhKXkqSj0LtLvJQ25m0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pX25W/btrYUSJcdO6/uaXDhKXkqSj0LtLvJQ25m0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpX25W%2FbtrYUSJcdO6%2FuaXDhKXkqSj0LtLvJQ25m0%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;673&quot; height=&quot;467&quot; data-filename=&quot;메타데이터3.PNG&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;625&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 로컬에서 아래와 같은 명령어로 ssh 명령어를 실행해주면 잘 접속이 되는것을 확인할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1676122789187&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ssh -i [file_path] [user_id]@[외부ip]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ssh3.PNG&quot; data-origin-width=&quot;548&quot; data-origin-height=&quot;435&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHpxFd/btrYTV0CdpF/8TtmdlITo7KjSKJAmLdGR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHpxFd/btrYTV0CdpF/8TtmdlITo7KjSKJAmLdGR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHpxFd/btrYTV0CdpF/8TtmdlITo7KjSKJAmLdGR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHpxFd%2FbtrYTV0CdpF%2F8TtmdlITo7KjSKJAmLdGR0%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;548&quot; height=&quot;435&quot; data-filename=&quot;ssh3.PNG&quot; data-origin-width=&quot;548&quot; data-origin-height=&quot;435&quot;/&gt;&lt;/span&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;a href=&quot;https://jybaek.gitbook.io/with-gcp/appendix/gce_to_ssh&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://jybaek.gitbook.io/with-gcp/appendix/gce_to_ssh&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1676122195916&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;인스턴스에 SSH로 접속하기 - GCP 탐구생활&quot; data-og-description=&quot;본문에서는 Cloud Shell 을 이용해서 인스턴스에 접속하는 방법을 살펴봤는데, 이번에는 사용자의 머신에서 직접 SSH 를 접속하는 방법에 대해서 설명한다. 환경은 리눅스(우분투)지만 다른 OS라도 &quot; data-og-host=&quot;jybaek.gitbook.io&quot; data-og-source-url=&quot;https://jybaek.gitbook.io/with-gcp/appendix/gce_to_ssh&quot; data-og-url=&quot;https://jybaek.gitbook.io/with-gcp/appendix/gce_to_ssh&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bFdfrP/hyRAj7hWh8/VVAQztEW3tNkq9mJKLg7ek/img.jpg?width=880&amp;amp;height=505&amp;amp;face=0_0_880_505,https://scrap.kakaocdn.net/dn/bfCM3J/hyRAfqhQpJ/1dknIfN2VkVJkaT7g6OUjk/img.png?width=764&amp;amp;height=552&amp;amp;face=0_0_764_552,https://scrap.kakaocdn.net/dn/DJCVO/hyRAfX86CB/BIYxQgRZrvxOPRGQng4t1K/img.jpg?width=732&amp;amp;height=438&amp;amp;face=0_0_732_438&quot;&gt;&lt;a href=&quot;https://jybaek.gitbook.io/with-gcp/appendix/gce_to_ssh&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jybaek.gitbook.io/with-gcp/appendix/gce_to_ssh&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bFdfrP/hyRAj7hWh8/VVAQztEW3tNkq9mJKLg7ek/img.jpg?width=880&amp;amp;height=505&amp;amp;face=0_0_880_505,https://scrap.kakaocdn.net/dn/bfCM3J/hyRAfqhQpJ/1dknIfN2VkVJkaT7g6OUjk/img.png?width=764&amp;amp;height=552&amp;amp;face=0_0_764_552,https://scrap.kakaocdn.net/dn/DJCVO/hyRAfX86CB/BIYxQgRZrvxOPRGQng4t1K/img.jpg?width=732&amp;amp;height=438&amp;amp;face=0_0_732_438');&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;인스턴스에 SSH로 접속하기 - GCP 탐구생활&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;본문에서는 Cloud Shell 을 이용해서 인스턴스에 접속하는 방법을 살펴봤는데, 이번에는 사용자의 머신에서 직접 SSH 를 접속하는 방법에 대해서 설명한다. 환경은 리눅스(우분투)지만 다른 OS라도&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;jybaek.gitbook.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;</description>
      <category>개발</category>
      <category>cloud</category>
      <category>Compute engine</category>
      <category>GCP</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/453</guid>
      <comments>https://bitrader.tistory.com/453#entry453comment</comments>
      <pubDate>Sat, 11 Feb 2023 21:58:01 +0900</pubDate>
    </item>
    <item>
      <title>백준 1495번 기타리스트</title>
      <link>https://bitrader.tistory.com/448</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1495&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/1495&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1675648680532&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;1495번: 기타리스트&quot; data-og-description=&quot;첫째 줄에&amp;nbsp;N,&amp;nbsp;S, M이 주어진다. (1 &amp;le; N &amp;le; 50, 1 &amp;le; M &amp;le; 1,000, 0 &amp;le; S &amp;le; M)&amp;nbsp;둘째 줄에는 각 곡이 시작하기 전에 줄 수 있는 볼륨의 차이가 주어진다. 이 값은 1보다 크거나 같고, M보다 작거나 같다.&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/1495&quot; data-og-url=&quot;https://www.acmicpc.net/problem/1495&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1495&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/1495&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;1495번: 기타리스트&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;첫째 줄에&amp;nbsp;N,&amp;nbsp;S, M이 주어진다. (1 &amp;le; N &amp;le; 50, 1 &amp;le; M &amp;le; 1,000, 0 &amp;le; S &amp;le; M)&amp;nbsp;둘째 줄에는 각 곡이 시작하기 전에 줄 수 있는 볼륨의 차이가 주어진다. 이 값은 1보다 크거나 같고, M보다 작거나 같다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이나믹 프로그래밍으로 해결이 가능한 문제이며 또한 탐색해야되는 범위가 적기 때문에 2중 for문으로도 해결이 가능한 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;볼륨을 변경해야하는 단계마다 어떠한 볼륨으로 변경이 가능한지를 dp matrix에 저장을 해놓고 모든 경우의 수를 계산하해주면 된다.&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;문제의 예제 1번은&lt;/p&gt;
&lt;pre id=&quot;code_1675648854639&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;3 5 10
5 3 7&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;아래와 같은 형태로 dp matrix의 이전의 상태를 기록해주게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1675648869966&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
 [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
 [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0],
 [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 정답은 dp maxrix의 마지막 row에서의 최대값인 10이다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 전체코드&lt;/p&gt;
&lt;pre id=&quot;code_1675648750793&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import sys
input = sys.stdin.readline
n, s, m = map(int,input().split())
volume = list(map(int,input().split()))

dp = [[0]*(m+1) for _ in range(n+1)]

dp[0][s] = 1

for idx, each in enumerate(volume):
    for idx2, k in enumerate(dp[idx]):
        if k:
            upper = idx2 + each
            lower = idx2 - each

            if upper &amp;lt;= m:
                dp[idx+1][upper] = 1

            if lower &amp;gt;= 0:
                dp[idx+1][lower] = 1

max_value = -1
for idx, each in enumerate(dp[n]):
    if each:
        if idx &amp;gt; max_value:
            max_value = idx

print(max_value)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘/백준</category>
      <category>백준</category>
      <category>알고리즘</category>
      <category>코딩테스트</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/448</guid>
      <comments>https://bitrader.tistory.com/448#entry448comment</comments>
      <pubDate>Mon, 6 Feb 2023 11:01:54 +0900</pubDate>
    </item>
    <item>
      <title>Google Cloud Registry에 Docker Image push</title>
      <link>https://bitrader.tistory.com/447</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Docker Image를 저장하는 Registry는 아래와 같이 있으며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별로도 지정을 하지 않으면 기본적으로 Dockerhub를 사용하게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;registry.PNG&quot; data-origin-width=&quot;1314&quot; data-origin-height=&quot;405&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uyIRL/btrX30BXKaD/ame0FzQD7dB994weYjlKf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uyIRL/btrX30BXKaD/ame0FzQD7dB994weYjlKf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uyIRL/btrX30BXKaD/ame0FzQD7dB994weYjlKf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuyIRL%2FbtrX30BXKaD%2Fame0FzQD7dB994weYjlKf1%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;1314&quot; height=&quot;405&quot; data-filename=&quot;registry.PNG&quot; data-origin-width=&quot;1314&quot; data-origin-height=&quot;405&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;GCR에 Docker 이미지 푸시하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 GCP의 Container Registry에 이동해서 확인을 해준다. 현재는 아직 푸시된 이미지가 없기 때문에 아무것도 있지 않다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6c50F/btrX4l0ioEL/uRkKZtq9n3oyPoqd3ez5uk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6c50F/btrX4l0ioEL/uRkKZtq9n3oyPoqd3ez5uk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;289&quot; data-origin-height=&quot;500&quot; data-filename=&quot;Container Registry.PNG&quot; style=&quot;width: 28.3113%; margin-right: 10px;&quot; data-widthpercent=&quot;28.64&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6c50F/btrX4l0ioEL/uRkKZtq9n3oyPoqd3ez5uk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6c50F%2FbtrX4l0ioEL%2FuRkKZtq9n3oyPoqd3ez5uk%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;289&quot; height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpyVhd/btrYaoIm55h/tKHO67iyzNgWu3GiWcx26k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpyVhd/btrYaoIm55h/tKHO67iyzNgWu3GiWcx26k/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;766&quot; data-origin-height=&quot;532&quot; data-filename=&quot;registry.PNG&quot; style=&quot;width: 70.5259%;&quot; data-widthpercent=&quot;71.36&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpyVhd/btrYaoIm55h/tKHO67iyzNgWu3GiWcx26k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpyVhd%2FbtrYaoIm55h%2FtKHO67iyzNgWu3GiWcx26k%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;766&quot; height=&quot;532&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로는 로컬에서 gcloud를 이용해서 클라우드에 있는 우리의 프로젝트에 로그인을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약에 gcloud sdk가 없다면 설치를 해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://cloud.google.com/sdk?hl=ko&quot;&gt;https://cloud.google.com/sdk?hl=ko&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1675578073419&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;Cloud SDK - 라이브러리 및 명령줄 도구 &amp;nbsp;|&amp;nbsp; Google Cloud&quot; data-og-description=&quot;Cloud SDK는 Google Cloud에서의 개발을 위한 도구 모음으로 gcloud, gsutil, bq 명령줄 도구, 클라이언트 라이브러리, 로컬 에뮬레이터가 포함되어 있습니다.&quot; data-og-host=&quot;cloud.google.com&quot; data-og-source-url=&quot;https://cloud.google.com/sdk?hl=ko&quot; data-og-url=&quot;https://cloud.google.com/sdk?hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bdzWbZ/hyRvj1GNxf/1O1wPV6Rm30mTl3GN6eyVk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bqMl46/hyRvnwhMDQ/TiQNS8te6skPvNKEH4nrS0/img.jpg?width=600&amp;amp;height=338&amp;amp;face=0_0_600_338,https://scrap.kakaocdn.net/dn/bfJOSg/hyRwGOvagu/Vn7ZPsfFF0dsEsuLjf3DO1/img.jpg?width=512&amp;amp;height=279&amp;amp;face=357_42_434_126&quot;&gt;&lt;a href=&quot;https://cloud.google.com/sdk?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://cloud.google.com/sdk?hl=ko&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bdzWbZ/hyRvj1GNxf/1O1wPV6Rm30mTl3GN6eyVk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bqMl46/hyRvnwhMDQ/TiQNS8te6skPvNKEH4nrS0/img.jpg?width=600&amp;amp;height=338&amp;amp;face=0_0_600_338,https://scrap.kakaocdn.net/dn/bfJOSg/hyRwGOvagu/Vn7ZPsfFF0dsEsuLjf3DO1/img.jpg?width=512&amp;amp;height=279&amp;amp;face=357_42_434_126');&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;Cloud SDK - 라이브러리 및 명령줄 도구 &amp;nbsp;|&amp;nbsp; Google Cloud&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Cloud SDK는 Google Cloud에서의 개발을 위한 도구 모음으로 gcloud, gsutil, bq 명령줄 도구, 클라이언트 라이브러리, 로컬 에뮬레이터가 포함되어 있습니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;cloud.google.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gcloud가 설치가 되었으면 다음의 명령어로 gcloud에 로그인을 한 후 프로젝트로 접근을 해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1675578132978&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$gcloud auth login
$gcloud config set project &amp;lt;your_project_id&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_프로젝트 id.PNG&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;267&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0btc0/btrX30Wloyb/VHlBcBOy3YEHHUW0Dqn7Mk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0btc0/btrX30Wloyb/VHlBcBOy3YEHHUW0Dqn7Mk/img.png&quot; data-alt=&quot;왼쪽은 ID column에 있는게 project id이다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0btc0/btrX30Wloyb/VHlBcBOy3YEHHUW0Dqn7Mk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0btc0%2FbtrX30Wloyb%2FVHlBcBOy3YEHHUW0Dqn7Mk%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;483&quot; height=&quot;171&quot; data-filename=&quot;edited_프로젝트 id.PNG&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;267&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;왼쪽은 ID column에 있는게 project id이다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로는 gcloud의 docker 설정을 해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1675578311635&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$gcloud auth configure-docker&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;이제 기존에 있던 docker image를 gcr에 업로드 하기 위해 tag를 붙여줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gcr에 올릴 이미지 이름은 &lt;b&gt;gcr.io/gcp 프로젝트 이름/이미지 이름&lt;/b&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;docker tag 명령어를 사용해서 기존의&amp;nbsp; my-fastapi-app 이미지를 grc.io 이미지로 바꿔주고&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_docker gcr.PNG&quot; data-origin-width=&quot;959&quot; data-origin-height=&quot;124&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4RBXy/btrYap1zwYU/4nrPdCAjba2PbfahUC9RcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4RBXy/btrYap1zwYU/4nrPdCAjba2PbfahUC9RcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4RBXy/btrYap1zwYU/4nrPdCAjba2PbfahUC9RcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4RBXy%2FbtrYap1zwYU%2F4nrPdCAjba2PbfahUC9RcK%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;959&quot; height=&quot;124&quot; data-filename=&quot;edited_docker gcr.PNG&quot; data-origin-width=&quot;959&quot; data-origin-height=&quot;124&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tag로 새로 만들어준 이미지를 push하면 gcr에 업로드가 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_docker gcr2.PNG&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;197&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dQwHEx/btrX8jm0pcn/LdwKRv3E2wKp9xLPnF5Rf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dQwHEx/btrX8jm0pcn/LdwKRv3E2wKp9xLPnF5Rf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dQwHEx/btrX8jm0pcn/LdwKRv3E2wKp9xLPnF5Rf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdQwHEx%2FbtrX8jm0pcn%2FLdwKRv3E2wKp9xLPnF5Rf1%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;844&quot; height=&quot;197&quot; data-filename=&quot;edited_docker gcr2.PNG&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;197&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;grc2.PNG&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;406&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JuhOE/btrYaptKn36/cdSEOuGoh32YR4MhlY17w0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JuhOE/btrYaptKn36/cdSEOuGoh32YR4MhlY17w0/img.png&quot; data-alt=&quot;gcr에 이미지가 업로드된 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JuhOE/btrYaptKn36/cdSEOuGoh32YR4MhlY17w0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJuhOE%2FbtrYaptKn36%2FcdSEOuGoh32YR4MhlY17w0%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;584&quot; height=&quot;406&quot; data-filename=&quot;grc2.PNG&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;406&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;gcr에 이미지가 업로드된 모습&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;그리고 업로드된 이미지는 docker pull &lt;b&gt;gcr.io/gcp 프로젝트 이름/이미지 이름&amp;nbsp;&lt;/b&gt;명령어 형태로 다시 다운로드가 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;GCP Cloud Run으로 GCR에 올린 이미지 배포 하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cloud run으로 gcr에 올린 이미지를 배포하는 방법은 매우 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cloud Run 생성에서 컨테이너 이미지 URL에 방금전 GCR에 push할때 사용했던 &lt;b&gt;gcr.io/gcp 프로젝트 이름/이미지 이름 &lt;/b&gt;형태의 주소를 넣어주기만 하면 되기 때문이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_cloud run.PNG&quot; data-origin-width=&quot;952&quot; data-origin-height=&quot;463&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pjIKf/btrYaqzqyqR/XUBfmOEF7khHQhI9o0bGL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pjIKf/btrYaqzqyqR/XUBfmOEF7khHQhI9o0bGL1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pjIKf/btrYaqzqyqR/XUBfmOEF7khHQhI9o0bGL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpjIKf%2FbtrYaqzqyqR%2FXUBfmOEF7khHQhI9o0bGL1%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;952&quot; height=&quot;463&quot; data-filename=&quot;edited_cloud run.PNG&quot; data-origin-width=&quot;952&quot; data-origin-height=&quot;463&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성이 완료가 되면 아래와 같이 URL이 생성이 되고&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;cloudrun2.PNG&quot; data-origin-width=&quot;721&quot; data-origin-height=&quot;376&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xuVGU/btrX2WAadJ7/d1sjz7TyoeDmDW9d6lLEsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xuVGU/btrX2WAadJ7/d1sjz7TyoeDmDW9d6lLEsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xuVGU/btrX2WAadJ7/d1sjz7TyoeDmDW9d6lLEsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxuVGU%2FbtrX2WAadJ7%2Fd1sjz7TyoeDmDW9d6lLEsK%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;721&quot; height=&quot;376&quot; data-filename=&quot;cloudrun2.PNG&quot; data-origin-width=&quot;721&quot; data-origin-height=&quot;376&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 주소로 curl 명령어를 날려주면 아래와 같이 응답이 오는것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;cloud run3.PNG&quot; data-origin-width=&quot;693&quot; data-origin-height=&quot;105&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0qYgi/btrX9ER5D67/PkSHAEKP2A57PMxUMP9aaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0qYgi/btrX9ER5D67/PkSHAEKP2A57PMxUMP9aaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0qYgi/btrX9ER5D67/PkSHAEKP2A57PMxUMP9aaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0qYgi%2FbtrX9ER5D67%2FPkSHAEKP2A57PMxUMP9aaK%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;693&quot; height=&quot;105&quot; data-filename=&quot;cloud run3.PNG&quot; data-origin-width=&quot;693&quot; data-origin-height=&quot;105&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>개발/docker</category>
      <category>docker</category>
      <category>GCP</category>
      <category>gcr</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/447</guid>
      <comments>https://bitrader.tistory.com/447#entry447comment</comments>
      <pubDate>Sun, 5 Feb 2023 15:36:02 +0900</pubDate>
    </item>
    <item>
      <title>백준 7490번 0 만들기</title>
      <link>https://bitrader.tistory.com/446</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/7490&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/7490&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1675521954220&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;7490번: 0 만들기&quot; data-og-description=&quot;각 테스트 케이스에 대해 ASCII 순서에 따라 결과가 0이 되는 모든&amp;nbsp;수식을 출력한다. 각 테스트 케이스의 결과는 한 줄을 띄워 구분한다.&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/7490&quot; data-og-url=&quot;https://www.acmicpc.net/problem/7490&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cd6TQN/hyRvnitoD7/hmc2XbfEgKYRkK9mAHK3y0/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/7490&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/7490&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cd6TQN/hyRvnitoD7/hmc2XbfEgKYRkK9mAHK3y0/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;7490번: 0 만들기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;각 테스트 케이스에 대해 ASCII 순서에 따라 결과가 0이 되는 모든&amp;nbsp;수식을 출력한다. 각 테스트 케이스의 결과는 한 줄을 띄워 구분한다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;' ', '+', '-'을 조합하여 만들 수 있는 수식중에서 0이 되는것을 찾는 문제이며 문제에서 제시되는 수의 범위가 굉장히 좁기 때문에 백트래킹을 이용하여 풀어도 시간내에 해결이 되는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1675521959358&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import sys
input = sys.stdin.readline
import copy

t = int(input())

result = []
def backtracking(i):

    global s

    if i == n:
        s += str(i)
        tmp = copy.deepcopy(s)
        tmp = tmp.replace(' ','')
        if eval(tmp) == 0:
            result.append(s)
        s = s[:-1]
        return

    s += str(i)

    for each in [' ','+','-']:
        s += each
        backtracking(i+1)
        s = s[:-1]

    s = s[:-1]


for _ in range(t):
    n = int(input())
    s = ''
    backtracking(1)
    result.append('')

for each in result:
    print(each)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘/백준</category>
      <category>백준</category>
      <category>알고리즘</category>
      <category>완전탐색</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/446</guid>
      <comments>https://bitrader.tistory.com/446#entry446comment</comments>
      <pubDate>Sat, 4 Feb 2023 23:46:52 +0900</pubDate>
    </item>
    <item>
      <title>Docker compose</title>
      <link>https://bitrader.tistory.com/445</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지는 하나의 Docker Image만을 띄우는 것을 알아보았는데 하난의 Docker Image가 아니라 여러 Docker Image를 띄우고 싶거나 A Image를 띄우고 B Image를 띄워야 하는 상황이 있다면?(B가 A에 의존)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker compose를 활용을 할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Docker compose 설치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mac이나 Window라면 docker desktop을 설치하면서 docker compose도 자동으로 설치가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;linux라면 docker compose는 따로 설치를 해줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.docker.com/compose/install/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.docker.com/compose/install/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1675584335354&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;Overview&quot; data-og-description=&quot; &quot; data-og-host=&quot;docs.docker.com&quot; data-og-source-url=&quot;https://docs.docker.com/compose/install/&quot; data-og-url=&quot;https://docs.docker.com/compose/install/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/4lc40/hyRwAU6mwS/FnfUXjtbj6Rp0brcSH0bW1/img.png?width=129&amp;amp;height=128&amp;amp;face=0_0_129_128,https://scrap.kakaocdn.net/dn/cErxZQ/hyRvhv2IuG/tpPVutj2Fa1MwA83UA6wk1/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500&quot;&gt;&lt;a href=&quot;https://docs.docker.com/compose/install/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.docker.com/compose/install/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/4lc40/hyRwAU6mwS/FnfUXjtbj6Rp0brcSH0bW1/img.png?width=129&amp;amp;height=128&amp;amp;face=0_0_129_128,https://scrap.kakaocdn.net/dn/cErxZQ/hyRvhv2IuG/tpPVutj2Fa1MwA83UA6wk1/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500');&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;Overview&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.docker.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;pre id=&quot;code_1675584369743&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; $docker compose version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;docker-compose.yml&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker image의 내용을 Dockerfile에 작성했던 것처럼 docker-compose의 내용은 docker-compose.yml에 작성을 한다.&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;먼저 그냥 docker run 명령어로 node image를 띄웠을 때와 docker-compose에 작성된 내용을 비교해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;node서버의 포트, volume, 환경 변수등 많은 옵션들이 주어진다.&lt;/p&gt;
&lt;pre id=&quot;code_1675584535556&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; docker run -dp 3000:3000 \
  -w /app -v &quot;$(pwd):/app&quot; \
  --network todo-app \
  -e MYSQL_HOST=mysql \
  -e MYSQL_USER=root \
  -e MYSQL_PASSWORD=secret \
  -e MYSQL_DB=todos \
  node:18-alpine \
  sh -c &quot;yarn install &amp;amp;&amp;amp; yarn run dev&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;위의 명령어를 docker-compose.yml 파일로는 아래와 같이 옮길 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1675584610061&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;services:
  # app은 어떠한 이름도 될 수 있음 사용자 정의에 따름
  # 자동적으로 네트워크의 이름이 됨
  app:
    image: node:18-alpine
    command: sh -c &quot;yarn install &amp;amp;&amp;amp; yarn run dev&quot;
    ports:
      - 3000:3000
    working_dir: /app
    volumes:
      - ./:/app
    environment:
      MYSQL_HOST: mysql
      MYSQL_USER: root
      MYSQL_PASSWORD: secret
      MYSQL_DB: todos&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;다음은 docker run으로 mysql을 생성하는 명령어이다.&lt;/p&gt;
&lt;pre id=&quot;code_1675584877665&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; docker run -d \
  --network todo-app --network-alias mysql \
  -v todo-mysql-data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=secret \
  -e MYSQL_DATABASE=todos \
  mysql:8.0&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;docker-compose.yml로는 아래와 같이 작성할 수 있다.&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;p data-ke-size=&quot;size16&quot;&gt;* docker run 명령어에서는 network-alias를 지정해 주었지만 docker-compose.yml에서는 service이름을 mysql로 지정해 주면서 자동적으로 network-alias가 mysql로 지정되게 된다.&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;* 또한 docker run 명령어에서는 지정된 volume이 자동으로 생성되었지만 compose에서는 그렇지 않아서 사용할 volumne을 volumes: 카테고리 아래에 지정해 준 것을 확인할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1675584888579&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;services:
  app:
    # The app service definition
  mysql:
    image: mysql:8.0
    volumes:
      - todo-mysql-data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: todos

volumes:
  todo-mysql-data:&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;따라서 전체 docker-compose.yml 파일은 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1675585202765&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;services:
  app:
    image: node:18-alpine
    command: sh -c &quot;yarn install &amp;amp;&amp;amp; yarn run dev&quot;
    ports:
      - 3000:3000
    working_dir: /app
    volumes:
      - ./:/app
    environment:
      MYSQL_HOST: mysql
      MYSQL_USER: root
      MYSQL_PASSWORD: secret
      MYSQL_DB: todos

  mysql:
    image: mysql:8.0
    volumes:
      - todo-mysql-data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: todos

volumes:
  todo-mysql-data:&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 docker-compose run -d 명령어로 실행해 주면 다음과 같은 로그가 뜨면서 잘 실행되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1675585504862&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Creating network &quot;app_default&quot; with the default driver
 Creating volume &quot;app_todo-mysql-data&quot; with default driver
 Creating app_app_1   ... done
 Creating app_mysql_1 ... done&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker-compose.yml 파일에 작성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;docker-compose로 fastapi와 mysql 연동&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 다른 예시로는 fastapi와 mysql을 연동하는 예시가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mysql 부분은 위의 예시와 크게 달라진 것은 없다.&lt;/p&gt;
&lt;pre id=&quot;code_1675515834584&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;version: '3'

services:
  db:
    image: mysql:5.7.12
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: my_database
    ports:
      - 3306:3306
    
  app:
    build:
      context: .
    environment:
      DB_URL: mysql+mysqldb://root:root@db:3306/my_database?charset=utf8mb4
    ports:
      - 8000:8000
    depends_on:
      - db
    restart: always&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;&lt;b&gt;* version : compose file의 버전을 명시합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;versioning은 버전별로 다음과 같은 특징이 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Version 1에서는 버저닝을 생략&lt;/li&gt;
&lt;li&gt;Version 2부터 마이너 버전(2.x)까지 설정해야 함 (생략 시 2.0으로 적용된다.)&lt;/li&gt;
&lt;li&gt;Version 3은 도커 스웜과 같이 사용되도록 디자인됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;* build : Dockerfile 빌드를 하기 위해 Dockerfile이 있는 경로를 지정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;context:.라는 의미는 현재 경로에 Dockerfile이 있다는 의미이며 위의 예제에서 사용된 Dockerfile은 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1675586108367&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FROM python:3.8.7-slim-buster

COPY . /app
WORKDIR /app
ENV PYTHONPATH /app
ENV PYTHONBUFFERED=1

RUN pip install pip==21.2.4 &amp;amp;&amp;amp; \
	pip install -r requirements.txt
    
CMD [&quot;python&quot;, &quot;main.py&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;* depends_on: service 간 종속을 의미하며 위에서는 db가 실행되고 난 후에 app이 실행되어야 한다는 의미이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;* restart : 몇 가지 옵션이 있으며 각각 아래의 뜻을 의미한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;no : 수동으로 재시작&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;always : 컨테이너를 수동으로 off 하기 전까진 항상 재시작&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;on-failure : 오류가 있을 때 재시작&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;&amp;nbsp;&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;a href=&quot;https://meetup.nhncloud.com/posts/277&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://meetup.nhncloud.com/posts/277&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1675585995476&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;Docker Compose와 버전별 특징 : NHN Cloud Meetup&quot; data-og-description=&quot;도커는 이제 대부분의 개발자 노트북이나 PC에 하나씩은 설치되어있는 필수품이 되어가는데요 편하고 유용한 도커를 좀 더 유익하고 편하게 사용할 수 있는 도구인 Docker Compose에 대해서 알기 쉽&quot; data-og-host=&quot;meetup.nhncloud.com&quot; data-og-source-url=&quot;https://meetup.nhncloud.com/posts/277&quot; data-og-url=&quot;https://meetup.toast.com/posts/277&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dnOsUV/hyRwEXvOjd/fdtXhG5SRiWSRPkDAcI5sk/img.png?width=425&amp;amp;height=204&amp;amp;face=0_0_425_204,https://scrap.kakaocdn.net/dn/dgmyFT/hyRvo9Q673/lKoAkJlWuuKdbXWOJQAwFk/img.png?width=425&amp;amp;height=204&amp;amp;face=0_0_425_204&quot;&gt;&lt;a href=&quot;https://meetup.nhncloud.com/posts/277&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://meetup.nhncloud.com/posts/277&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dnOsUV/hyRwEXvOjd/fdtXhG5SRiWSRPkDAcI5sk/img.png?width=425&amp;amp;height=204&amp;amp;face=0_0_425_204,https://scrap.kakaocdn.net/dn/dgmyFT/hyRvo9Q673/lKoAkJlWuuKdbXWOJQAwFk/img.png?width=425&amp;amp;height=204&amp;amp;face=0_0_425_204');&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;Docker Compose와 버전별 특징 : NHN Cloud Meetup&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;도커는 이제 대부분의 개발자 노트북이나 PC에 하나씩은 설치되어있는 필수품이 되어가는데요 편하고 유용한 도커를 좀 더 유익하고 편하게 사용할 수 있는 도구인 Docker Compose에 대해서 알기 쉽&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;meetup.nhncloud.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://medium.com/@jyson88/docker-compose-yml-%EA%B5%AC%EC%84%B1-%EC%9A%94%EC%86%8C-%EC%82%AC%EC%9A%A9%EB%B2%95-8dffb6a57da3&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://medium.com/@jyson88/docker-compose-yml-%EA%B5%AC%EC%84%B1-%EC%9A%94%EC%86%8C-%EC%82%AC%EC%9A%A9%EB%B2%95-8dffb6a57da3&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1675585958812&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;Docker-compose.yml 구성 요소 사용법&quot; data-og-description=&quot;Docker compose 파일에서 사용되는 모듈에 대해서 확인 해보도록 하겠습니다.&quot; data-og-host=&quot;medium.com&quot; data-og-source-url=&quot;https://medium.com/@jyson88/docker-compose-yml-%EA%B5%AC%EC%84%B1-%EC%9A%94%EC%86%8C-%EC%82%AC%EC%9A%A9%EB%B2%95-8dffb6a57da3&quot; data-og-url=&quot;https://medium.com/@jyson88/docker-compose-yml-%EA%B5%AC%EC%84%B1-%EC%9A%94%EC%86%8C-%EC%82%AC%EC%9A%A9%EB%B2%95-8dffb6a57da3&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://medium.com/@jyson88/docker-compose-yml-%EA%B5%AC%EC%84%B1-%EC%9A%94%EC%86%8C-%EC%82%AC%EC%9A%A9%EB%B2%95-8dffb6a57da3&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://medium.com/@jyson88/docker-compose-yml-%EA%B5%AC%EC%84%B1-%EC%9A%94%EC%86%8C-%EC%82%AC%EC%9A%A9%EB%B2%95-8dffb6a57da3&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;Docker-compose.yml 구성 요소 사용법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Docker compose 파일에서 사용되는 모듈에 대해서 확인 해보도록 하겠습니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;medium.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.docker.com/get-started/08_using_compose/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.docker.com/get-started/08_using_compose/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1675586252586&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;Use Docker Compose&quot; data-og-description=&quot; &quot; data-og-host=&quot;docs.docker.com&quot; data-og-source-url=&quot;https://docs.docker.com/get-started/08_using_compose/&quot; data-og-url=&quot;https://docs.docker.com/get-started/08_using_compose/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/TdMHm/hyRwvTN8Po/DJ7ze4ndrxVhXSnK80tt7K/img.png?width=129&amp;amp;height=128&amp;amp;face=0_0_129_128,https://scrap.kakaocdn.net/dn/NrjF2/hyRu9LEUud/B0OxaZ1mqCmo3VKW7cyDvk/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/VrkvV/hyRva4OZK7/A711sObLJ6j16kvnVUZQI1/img.png?width=1274&amp;amp;height=724&amp;amp;face=0_0_1274_724&quot;&gt;&lt;a href=&quot;https://docs.docker.com/get-started/08_using_compose/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.docker.com/get-started/08_using_compose/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/TdMHm/hyRwvTN8Po/DJ7ze4ndrxVhXSnK80tt7K/img.png?width=129&amp;amp;height=128&amp;amp;face=0_0_129_128,https://scrap.kakaocdn.net/dn/NrjF2/hyRu9LEUud/B0OxaZ1mqCmo3VKW7cyDvk/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/VrkvV/hyRva4OZK7/A711sObLJ6j16kvnVUZQI1/img.png?width=1274&amp;amp;height=724&amp;amp;face=0_0_1274_724');&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;Use Docker Compose&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.docker.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>docker</category>
      <category>docker-compose</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/445</guid>
      <comments>https://bitrader.tistory.com/445#entry445comment</comments>
      <pubDate>Sat, 4 Feb 2023 21:39:29 +0900</pubDate>
    </item>
    <item>
      <title>Docker Image를 만들기 위한 Dockerfile 작성 기본</title>
      <link>https://bitrader.tistory.com/444</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 docker image를 어떻게 생성을 하는지에 대해서 알아보려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 fast api 애플리케이션을 실행하는 docker image 서버를 만들어 보려고 합니다.&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;먼저 폴더를 하나 생성하고 관련 패키지를 다운로드 그리고 간단한 fastapi 코드를 작성해 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1675570622501&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$mkdir fastapi_example
$cd fastapi_example
$pip install fastapi[all]
$pip list --not-required --format=freeze &amp;gt;&amp;gt; requirements.txt&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerfile을 이용하여 docker build할때 사용할 requirements.txt도 pip list 명령어를 사용해서 만들어 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 pip freeze를 사용하지 않는 이유는 의존성에 따라 설치된 라이브러리를 포함하지 않기 위해서 입니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 Dockerfile이라는 파일을 만들어서 아래와 같이 작성해줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1675513996822&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FROM python:3.8.7-slim-buster

COPY . /app
WORKDIR /app
ENV PYTHONPATH /app
ENV PYTHONBUFFERED=1

# layer를 하나로 묶음 layer가 늘어나면 보통 안좋음
RUN pip install pip==21.2.4 &amp;amp;&amp;amp; \
	pip install -r requirements.txt
    
CMD [&quot;python&quot;, &quot;main.py&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;&lt;b&gt;*&amp;nbsp; FROM python:3.8.7-slim-buster&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FROM &quot;이미지 이름:태그&quot;&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;보통 처음부터 만들지는 않고 docker hub와 같은 저장소에 공개된 이미지를 기반으로 하여 이후에 레이어를 추가하여 이미지를 만들어 간다.&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;b&gt;* COPY . /app&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬호스트의 .(현재 디렉토리의 폴더 및 파일)을 컨테이너의 /app 경로에 복사한다는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 경우에는 main.py와 requirements.txt 가 복사되게 된다.&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;b&gt;* WORKIDR /app&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerfile의 RUN, CMD, ENTRYPOINT등의 명령어를 실행할 컨테이너의 경로라는 의미이다.&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;b&gt;* ENV PYTHONPATH /app, ENV PYTHONBUFFERED=1&lt;/b&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;* RUN pip install pip==21.2.4 &amp;amp;&amp;amp; pip install -r requirements.txt&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RUN 다음에 오는 명령어를 실행하라는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fast api를 실행해야 함으로 pip install과 requirements를 이용하여 패키지를 설치해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우에는 pip 명령어가 2개지만 한번에 이어서 작성을 해주었는데 이는 Docker image의 layer를 줄이기 위함이다.&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;COPY, ENV등등의 명령어가 Dockerfile에 추가될때마다 하나씩 layer가 늘어나는것인데 보통은 layer를 적게 하는것이 더 좋다.&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;b&gt;* CMD [&quot;python&quot;, &quot;main.py&quot;]&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker run으로 이 이미지를 기반으로 컨테이너를 만들 때 실행할 명령어이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 이미지가 실행되는 즉시 python main.py를 실행한다.&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;b&gt;* docker build&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;다음으로 &quot;Dockerfile이 위치한 경로&quot; docker build 명령어를 사용하면 알아서 Dockerfile을 찾아서 이미지를 빌드를 해준다.&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;docker build . -t my-fastapi-app 처럼 -t 옵션을 사용하여 이름과 태그를 설정할수 있고 tag는 미지정시 latest로 지정된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;. 이라고 쓰면 무조건 Dockerfile을 찾아서 빌드를 하고 Dockerfile이 아닌 다른 파일 이름을 사용하고 싶으면 . 대신 다른 파일 이름을 지정해주면 된다.&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;b&gt;* RUN vs CMD vs ENTRYPOINT&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerfile에서 실행될 수 있는 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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 RUN은 CMD와 ENTRYPOINT와 확실히 구분된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;RUN은 이미지를 빌드할때&lt;/b&gt; 사용하는 명령어이고 CMD와 ENTRYPOINT는 이미지가 빌드되고 컨테이너가 실행될 때 수행되는 명령어다&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;그렇다면 CMD와 ENTRYPOINT의 차이는 무엇일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ENTRYPOINT는 실행이 무조건 되는 명령어이고 CMD는 실행 될 수도 아닐수도 있다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EXPOSE : 컨테이너 외부에 노출할 포트 지정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ENTRYPOINT : CMD랑 유사, 이미지를 컨테이너로 띄울때 항상 실행하는 커맨드이다&lt;/p&gt;
&lt;pre id=&quot;code_1675576021881&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run my-fastapi-app echo hello&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 my-fastapi-app 이미지를 실행하고 추가로 echo hello로 인자를 전달해주면 run main.py는 실행되지 않고 컨테이너 내부서 hello만 출력한다.&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;하지만 CMD가 아닌 ENTRYPOINT를 사용했으면 fastapi도 실행하고 echo hello도 실행하게 된다.&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;b&gt;* docker run -p vs EXPOSE&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerfile의 EXPOSE 명령어는 해당 포트를 외부로 개방할 것이다.라고 명시하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EXPOSE를 사용하였다고 하더라고 해당 포트가 즉각 개방되어 외부에서 접속할 수 있는것이 아니다.&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;docker run -p 옵션으로 포트를 매핑을 해주어야 한다.&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/docker</category>
      <category>docker</category>
      <category>dockerfile</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/444</guid>
      <comments>https://bitrader.tistory.com/444#entry444comment</comments>
      <pubDate>Sat, 4 Feb 2023 21:37:44 +0900</pubDate>
    </item>
    <item>
      <title>Docker 기본 명령어 정리</title>
      <link>https://bitrader.tistory.com/442</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Docker Image 와 Container&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker Image는 컨테이너를 실행할 때 사용하는 템플릿과 같은 것. Read Only 한 특성이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker Container는 Docker Image를 활용해 실행된 인스턴스이다. Write가 가능하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;docker_run.PNG&quot; data-origin-width=&quot;608&quot; data-origin-height=&quot;494&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTOVmt/btrX3ZXhXjL/0uMvX1KKE9yLHjAfBkdy10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTOVmt/btrX3ZXhXjL/0uMvX1KKE9yLHjAfBkdy10/img.png&quot; data-alt=&quot;docker Image와 Container&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTOVmt/btrX3ZXhXjL/0uMvX1KKE9yLHjAfBkdy10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTOVmt%2FbtrX3ZXhXjL%2F0uMvX1KKE9yLHjAfBkdy10%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;351&quot; height=&quot;285&quot; data-filename=&quot;docker_run.PNG&quot; data-origin-width=&quot;608&quot; data-origin-height=&quot;494&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;docker Image와 Container&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Docker 기본 명령어&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* docker search&lt;/p&gt;
&lt;pre id=&quot;code_1675569754546&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker search [option] TERM&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker serach 명령어를 실행해서 다운로드 받기를 원하는 이미지를 찾을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mysql 관련 이미지를 찾고 싶으면 docker search mysql명령어를 입력하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다운로드를 받을때는 웬만하면 OFFICIAL한 이미지를 다운로드 받는게 좋다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;docker.PNG&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;369&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRzMPi/btrX6iBWYSN/ObiN9fn515J0jglPO1CKBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRzMPi/btrX6iBWYSN/ObiN9fn515J0jglPO1CKBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRzMPi/btrX6iBWYSN/ObiN9fn515J0jglPO1CKBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRzMPi%2FbtrX6iBWYSN%2FObiN9fn515J0jglPO1CKBk%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;890&quot; height=&quot;369&quot; data-filename=&quot;docker.PNG&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;369&quot;/&gt;&lt;/span&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;* docker pull&lt;/p&gt;
&lt;pre id=&quot;code_1675566163387&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker pull [option] image_name:tag&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker pull 명령어를 사용하여 docker image를 다운로드를 받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker pull mysql:8 이라고 명령어를 실행하면 mysql의 이미지 이름을 가진 tag가 8로 붙은 이미지를 내려 받는다.&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;image_name 앞에는 url을 명시하여 특정한 저장소(ex &lt;span style=&quot;background-color: #ffffff; color: #555555;&quot;&gt;docker pull test.registry.net/mysql:8)&lt;/span&gt;에 저장된 이미지를 다운로드 받을 수 가 있는데 url이 명시가 되지 않는다면 docker hub에서 기본적으로 다운로드를 받는다.&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;* docker images&lt;/p&gt;
&lt;pre id=&quot;code_1675567768048&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker images [option] [repository[:tag]]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker images 명령어는 다운로드 받아서 사용가능한 이미지가 어떤게 있는지 확인하는 명령어이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식문서에는 repository와 tag를 추가로 입력으로 받지만 repository는 image name을 뜻한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker images mysql:8이라고 입력을 하면 다운로드한 이미지 중에 image_name이 mysql인것 중 tag가 8인것만을 보여준다.&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;* docker run&lt;/p&gt;
&lt;pre id=&quot;code_1675567907914&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run [OPTIONS] IMAGE [COMMAND] [ARG...]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다운로드한 이미지를 run을 해주는 명령어이다. 따라서 image 이름은 필수적으로 포함이 되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex : docker run &quot;이미지 이름:태그&quot;&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;pre id=&quot;code_1675568469790&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run --name mysql-tutorial -e MYSQL_ROOT_PASSWORD=1234 -d -p 3306:3306 mysql:8&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--name 컨테이너 이름&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-e 컨테이너 안에서의 환경변수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-d demon (background) 실행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-p port forwarding : 왼쪽이 로컬 호스트의 포트를 의미하고 오른쪽이 컨테이너의 포트를 의미하며 두 포트를 연결지어 주겠다는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mysql:8 : mysql:8의 이미지를 실행하겠다는 의미이다.&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;* docker ps&lt;/p&gt;
&lt;pre id=&quot;code_1675568601460&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker ps [option]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker ps로 현재 실행중인 컨테이너를 확인가능하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;docker_ps.PNG&quot; data-origin-width=&quot;961&quot; data-origin-height=&quot;85&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/npNcO/btrX2ZwSLAs/hVEPyoUS0xC0TsKnKkF3Ik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/npNcO/btrX2ZwSLAs/hVEPyoUS0xC0TsKnKkF3Ik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/npNcO/btrX2ZwSLAs/hVEPyoUS0xC0TsKnKkF3Ik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnpNcO%2FbtrX2ZwSLAs%2FhVEPyoUS0xC0TsKnKkF3Ik%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;961&quot; height=&quot;85&quot; data-filename=&quot;docker_ps.PNG&quot; data-origin-width=&quot;961&quot; data-origin-height=&quot;85&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker ps -a 는 작동을 멈춘 컨테이너도 보여준다.&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;* docker exec&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1675568742625&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker exec [option] CONTAINER COMMAND [ARG...]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떠한 서버에 ssh로 접속하는 것과 유사하며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 docker exec -it &quot;컨테이너 이름(혹은 ID)&quot; /bin/bash 와 같은 명령어를 사용해서 container내부에 접속하고 /bin/bash 명령어를 실행해준다.&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;좀 전에 생성한 mysql 서버에 접속을 해보면 아래와 같이 접속이 되고 mysql도 mysql -u root -p 명령어로 접속이 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cHeAbd/btrX2WUjjTT/K18ObfWwWnpaz3WqPuM4u0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cHeAbd/btrX2WUjjTT/K18ObfWwWnpaz3WqPuM4u0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;717&quot; data-origin-height=&quot;86&quot; data-filename=&quot;docker exec.PNG&quot; style=&quot;width: 75.3129%; margin-right: 10px;&quot; data-widthpercent=&quot;76.2&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cHeAbd/btrX2WUjjTT/K18ObfWwWnpaz3WqPuM4u0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcHeAbd%2FbtrX2WUjjTT%2FK18ObfWwWnpaz3WqPuM4u0%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;717&quot; height=&quot;86&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6QCwq/btrX4Zbw4aI/KhfJAKOKcj6RHcb0JCKZB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6QCwq/btrX4Zbw4aI/KhfJAKOKcj6RHcb0JCKZB1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;625&quot; data-origin-height=&quot;240&quot; data-filename=&quot;mysql.png&quot; style=&quot;width: 23.5243%;&quot; data-widthpercent=&quot;23.8&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6QCwq/btrX4Zbw4aI/KhfJAKOKcj6RHcb0JCKZB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6QCwq%2FbtrX4Zbw4aI%2FKhfJAKOKcj6RHcb0JCKZB1%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;625&quot; height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;/div&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;* docker rm&lt;/p&gt;
&lt;pre id=&quot;code_1675569082593&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker rm [option] container [CONTAINER...]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker rm &quot;컨테이너 이름(ID)&quot; 으로 container를 삭제가 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-f는&amp;nbsp; force라는 뜻으로 해당 옵션을 주면 실행중인 컨테이너도 삭제가 가능하다.&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;* docker run을 할때 로컬에 있는 파일을 컨테이너와 공유하는법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 방법을 Volume Mount라고 부르며 도커 컨테이너를 실행하는 호스트와 컨테이너의 파일 시스템을 연동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉&amp;nbsp; 보통 container를 종료하고 다시 실행되면 container에 축적되었던 파일들은 전부 날아가는데 해당 방법으로 container들의 파일들을 보존시킬 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1675569573111&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run -it -p 8888:8888 -v /some/host/folder/for/work:/home/jovyan/workspace jupyter/minimal-notebook&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라는 명령어를 실행하면 jupyter/minimal-notebook이라는 이미지를 실행하며 로컬호스트의 /some/host/folder/for/work 폴더와 container의 /home/jovyan/workspace는 연동이 된다.&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;Dockerhub에 공개된 모든 이미지를 다운받을 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 pytorch랑 cuda 설정도 된 파일을 다운로드 받을 수 도 있으므로 빠르게 docker 환경으로 deeplearing 환경을 구성할 수 있다.&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;&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발</category>
      <category>docker</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/442</guid>
      <comments>https://bitrader.tistory.com/442#entry442comment</comments>
      <pubDate>Sat, 4 Feb 2023 20:33:39 +0900</pubDate>
    </item>
    <item>
      <title>백준 1449번 수리공</title>
      <link>https://bitrader.tistory.com/441</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1449&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/1449&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1675406191152&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;1449번: 수리공 항승&quot; data-og-description=&quot;첫째 줄에 물이 새는 곳의 개수 N과 테이프의 길이 L이 주어진다. 둘째 줄에는 물이 새는 곳의 위치가 주어진다. N과 L은 1,000보다 작거나 같은 자연수이고, 물이 새는 곳의 위치는 1,000보다 작거나&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/1449&quot; data-og-url=&quot;https://www.acmicpc.net/problem/1449&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Nf71v/hyRtRxtVEK/TC8hX8nXkcoM8NtgJxJMfk/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1449&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/1449&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Nf71v/hyRtRxtVEK/TC8hX8nXkcoM8NtgJxJMfk/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;1449번: 수리공 항승&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;첫째 줄에 물이 새는 곳의 개수 N과 테이프의 길이 L이 주어진다. 둘째 줄에는 물이 새는 곳의 위치가 주어진다. N과 L은 1,000보다 작거나 같은 자연수이고, 물이 새는 곳의 위치는 1,000보다 작거나&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&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;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1675406170299&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import sys
input = sys.stdin.readline

n, l = map(int,input().split())

leak_point = list(map(int,input().split()))
leak_point.sort()

start_point = -1
end_point = -1

answer = 0
for idx, each in enumerate(leak_point):

    if start_point == -1:
        start_point = each - 0.5
        end_point = each + 0.5
    else:
        end_point = each + 0.5

    length = end_point - start_point

    if length == l:
        answer += 1
        start_point = -1
    elif length &amp;gt; l:
        answer += 1
        start_point = each - 0.5

        # 마지막에 하나 남았을때 처리하기 위한 로직
        if idx + 1 == n:
            answer += 1
    else:
        # 마지막에 하나 남았을때 처리하기 위한 로직
        if idx + 1 == n:
            answer += 1

print(answer)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘/백준</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/441</guid>
      <comments>https://bitrader.tistory.com/441#entry441comment</comments>
      <pubDate>Fri, 3 Feb 2023 15:37:34 +0900</pubDate>
    </item>
    <item>
      <title>백준 1049번 기타줄</title>
      <link>https://bitrader.tistory.com/440</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1049&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.acmicpc.net/problem/1049&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1675404136379&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;1049번: 기타줄&quot; data-og-description=&quot;첫째 줄에 N과 M이 주어진다. N은 100보다 작거나 같은 자연수이고, M은 50보다 작거나 같은 자연수이다. 둘째 줄부터 M개의 줄에는 각 브랜드의 패키지 가격과 낱개의 가격이 공백으로 구분하여 주&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/1049&quot; data-og-url=&quot;https://www.acmicpc.net/problem/1049&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/emsHzh/hyRtXLd7Ag/TfJSbt3OTFaiEZZTknkEqk/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1049&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/1049&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/emsHzh/hyRtXLd7Ag/TfJSbt3OTFaiEZZTknkEqk/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&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;1049번: 기타줄&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;첫째 줄에 N과 M이 주어진다. N은 100보다 작거나 같은 자연수이고, M은 50보다 작거나 같은 자연수이다. 둘째 줄부터 M개의 줄에는 각 브랜드의 패키지 가격과 낱개의 가격이 공백으로 구분하여 주&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&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;그리디 알고리즘을 이용하면 쉽게 풀 수 있는 문제이다.&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개뿐이라 3개의 케이스를 모두 계산을 해서 다시 최소값을 구하면 되는 문제이다.&lt;/p&gt;
&lt;pre id=&quot;code_1675404139818&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import sys
input = sys.stdin.readline

n, m = map(int,input().split())

set_list = []
each_list = []
for _ in range(m):

    a, b = map(int,input().split())
    set_list.append(a)
    each_list.append(b)

set_list.sort()
each_list.sort()

# 3가지 케이스가 존재함
# 그냥 낱개로 사기
# 그냥 세트로 다 사버리기
# 세트로 살수있는 만큼만 사고 나머지는 낱개로 사기

each_price = each_list[0]*n
number = n // 6
if n % 6:
    number += 1
set_price = set_list[0] * number
number = n // 6
set_with_each_price = set_list[0]*number + each_list[0]*(n%6)

print(min(each_price, set_price, set_with_each_price))&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘/백준</category>
      <category>그리디알고리즘</category>
      <category>백준</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/440</guid>
      <comments>https://bitrader.tistory.com/440#entry440comment</comments>
      <pubDate>Fri, 3 Feb 2023 15:05:00 +0900</pubDate>
    </item>
    <item>
      <title>정규표현식 심화</title>
      <link>https://bitrader.tistory.com/405</link>
      <description>&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;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1674442631797&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;p = re.compile('(ABC)+')
m = p.search('ABCABCABC OK?')
print(m)
&amp;lt;re.Match object; span=(0, 9), match='ABCABCABC'&amp;gt;
print(m.group())
ABCABCABC&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예시처럼 (ABC)가 반복되는 구간을 찾을 수 있다.&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;pre id=&quot;code_1674442692482&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;p = re.compile(r&quot;(\w+)\s+\d+[-]\d+[-]\d+&quot;)
m = p.search(&quot;park 010-1234-1234&quot;)
print(m.group(1))
park&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;이름에 해당하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;\w+&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;부분을 그룹&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;(\w+)&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;으로 만들면 match 객체의 group(인덱스) 메서드를 사용하여 그루핑 된 부분의 문자열만 뽑아낼 수 있다. group 메서드의 인덱스는 다음과 같은 의미를 갖는다.&lt;/span&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;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;* 그루핑된 문자열 재참조&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;또한 그루핑한 문자열을 재참조하여서 반복하는 문자열을 매칭할 수 도 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;정규식&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;(\b\w+)\s+\1&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;(그룹) + &quot; &quot; + 그룹과 동일한 단어&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;와 매치됨을 의미한다&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1674442969464&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;p = re.compile(r'(\b\w+)\s+\1')
p.search('Paris in the the spring').group()
'the the'&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;&lt;b&gt;* 그루핑된 문자열에 이름 붙이기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그룹에 이름을 넣어서 참조를 할 때 이름으로도 할 수가 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1674443065065&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;p = re.compile(r&quot;(?P&amp;lt;name&amp;gt;\w+)\s+((\d+)[-]\d+[-]\d+)&quot;)
m = p.search(&quot;park 010-1234-1234&quot;)
print(m.group(&quot;name&quot;))
park&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;전방 탐색 후방 탐색&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;* 긍정형 전방 탐색 : &lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;(?=...)&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;) -&lt;/span&gt;...&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;에 해당되는 정규식과 매치되어야 하며 조건이 통과되어도 문자열이 소비되지 않는다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;긍정형 전방 탐색을 하면 'http://google.com'에서 ':'를 기준으로 http만 추출하는 것이 가능하다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1674443353986&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;p = re.compile(&quot;.+(?=:)&quot;)
m = p.search(&quot;http://google.com&quot;)
print(m.group())
http&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;b&gt;* 부정형 전방 탐색&lt;/b&gt; : &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;(&lt;/span&gt;(?!...)&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;) -... 에&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt; 해당되는 정규식과 매치되지 않아야 하며 조건이 통과되어도 문자열이 소비되지 않는다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;. bat이나. exe로 끝나는 확장자 파일만을 제외해서 탐지하고 싶으면 아래의 정규식을 이용할 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1674443386539&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.*[.](?!bat$|exe$).*$&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;* 후방 탐색&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(?&amp;lt;=...) 구문을 사용하면 매칭된 문자열 뒤의 문자열을 리턴해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1674443961729&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;p = re.compile(r&quot;(?&amp;lt;=:).+&quot;)
m = p.search(&quot;http://google.com&quot;)
print(m)
&amp;lt;re.Match object; span=(5, 17), match='//google.com'&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문자열 바꾸기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sub 메서드를 사용하면 정규식과 매치되는 문자열을 다른 문자로 치환할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1674444149584&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;p = re.compile('(blue|white|red)')
p.sub('colour', 'blue socks and red shoes')
'colour socks and colour shoes'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sub 함수의 첫 번째 인자가 바꿀 문자열, 두 번째 문자열이 대상이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;치환 횟수를 제한하고 싶으면 아래와 같이 옵션을 주면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1674444194024&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;p.sub('colour', 'blue socks and red shoes', count=1)
'colour socks and red shoes&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Greedy와 Non - Greedy&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정규표현식의 메타 문자들은 기본적으로 greedy하다고 할 수 있다. 매칭이 되는 가능한 문자열중 최대한 긴 것을 리턴해준다.&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;아래의 예를 보면 우리가 찾고 싶었던것은 &amp;lt;html&amp;gt;이었을 수도 있지만 전체 문자열 s도 정규표현식 패턴에 매칭이 됨과 동시에 가장 긴 매칭이 되는 문자열임으로 리턴이 되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1674444385947&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;s = '&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;Title&amp;lt;/title&amp;gt;'
len(s)
32
print(re.match('&amp;lt;.*&amp;gt;', s).span())
(0, 32)
print(re.match('&amp;lt;.*&amp;gt;', s).group())
&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;Title&amp;lt;/title&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 다음과 같이 ?문자를 같이 사용해 주면 메타 문자의 탐욕을 제한할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1674444501584&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print(re.match('&amp;lt;.*?&amp;gt;', s).group())
&amp;lt;html&amp;gt;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://wikidocs.net/4309&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://wikidocs.net/4309&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1674444245420&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;08-3 강력한 정규 표현식의 세계로&quot; data-og-description=&quot;이제 07-2에서 배우지 않은 몇몇 메타 문자의 의미를 살펴보고 그룹(Group)을 만드는 법, 전방 탐색 등 더욱 강력한 정규 표현식에 대해서 살펴보자. [TOC] ## 메&amp;hellip;&quot; data-og-host=&quot;wikidocs.net&quot; data-og-source-url=&quot;https://wikidocs.net/4309&quot; data-og-url=&quot;https://wikidocs.net/4309&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cEMLHv/hyRm5VSj2o/1ZQGdAb9kPw0mLDQlkjrH1/img.jpg?width=95&amp;amp;height=130&amp;amp;face=0_0_95_130&quot;&gt;&lt;a href=&quot;https://wikidocs.net/4309&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://wikidocs.net/4309&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cEMLHv/hyRm5VSj2o/1ZQGdAb9kPw0mLDQlkjrH1/img.jpg?width=95&amp;amp;height=130&amp;amp;face=0_0_95_130');&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;08-3 강력한 정규 표현식의 세계로&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이제 07-2에서 배우지 않은 몇몇 메타 문자의 의미를 살펴보고 그룹(Group)을 만드는 법, 전방 탐색 등 더욱 강력한 정규 표현식에 대해서 살펴보자. [TOC] ## 메&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;wikidocs.net&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>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/405</guid>
      <comments>https://bitrader.tistory.com/405#entry405comment</comments>
      <pubDate>Mon, 23 Jan 2023 12:28:32 +0900</pubDate>
    </item>
    <item>
      <title>저작권에 대한 내용 정리</title>
      <link>https://bitrader.tistory.com/404</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;NLP로 뉴스데이터를 처리하는 작업을 하고 있는데 저작권에 대한 좀 더 깊은 이해가 필요한것 같아서 필요한 내용을 정리를 해보았다. 아래에 후술할 CCL을 보면 당연히 뉴스데이터는 구매를 하지 않고는 저작권에 위배 되어 NLP 프로젝트에 보통 적용이 불가능하다.. 위키트리를 제외하고는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;저작권법 제1조 (목적)&lt;/h3&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;h3 data-ke-size=&quot;size23&quot;&gt;저작권&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람의 생각이나 감정을 표현한 결과물(저작물)에 대하여 창작자에게 주는 권리로 &quot;창작성&quot;이 있다면 별도의 등록절차없이 자연히 발생&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;1. 소설, 시, 논문, 강연, 연설, 각본 그 밖의 어문저작물&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;3. 연극 및 무용, 무언극 그 밖의 연극저작물&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;5. 건출물, 건출을 위한 모형 및 설계도서 그 밖의 건축저작물&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;7. 영상저작물&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8. 지도, 도표, 설계도, 약도, 모형 그 밖의 도형 저작물&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;9. 컴퓨터프로그램저작물&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;1. 헌법, 법률, 조약, 명령, 조례 및 규칙&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;3. 법원의 판결, 결정, 명령 및 심판이나 행정심판절차 그 밖에 이와 유사한 절차에 의한 의결, 결정 등&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 국가 또는 지방자치단체가 작성한 것으로서 제1호 내지 제3호에 규정된 것의 편집물 또는 번역물&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;합법적으로 데이터를 사용하는법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 저작자와 협의&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&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;독점적 이용허락의 경우, 저작자는 계약을 체결한 이용자에게 데이터 이용에 대한 '독점적'인 권리를 행사하는 것을 허락&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;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라이센스를 발급하는 단체는 다양하며 가장 유명한것은 Creative Commons 라는 비영리 단체에서 제공하는 &lt;b&gt;CCL&lt;/b&gt;이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;국내에는 문화체육관광부에서 제공하는 &lt;b&gt;공공누리&lt;/b&gt;가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CCL의 종류
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CC BY : 저작자 표시&lt;/li&gt;
&lt;li&gt;CC BY-NC : 저작자표시-비영리&lt;/li&gt;
&lt;li&gt;CC BY-ND : 저작자표시-변경금기&lt;/li&gt;
&lt;li&gt;CC BY-SA : 저작자표시-동일조건변경허락&lt;/li&gt;
&lt;li&gt;CC BY-NC-SA : 저작자표시-비영리-동일조건 변경 허락&lt;/li&gt;
&lt;li&gt;CC BY-NC-ND : 저작자표시-비영리-변경금지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;LicencesSet_Resized3.png&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dqtMde/btrWS2OlWiQ/rWMqN79exOBhoQzmmDy9FK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dqtMde/btrWS2OlWiQ/rWMqN79exOBhoQzmmDy9FK/img.png&quot; data-alt=&quot;출처 :&amp;amp;nbsp;https://ccl.cckorea.org/about/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dqtMde/btrWS2OlWiQ/rWMqN79exOBhoQzmmDy9FK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdqtMde%2FbtrWS2OlWiQ%2FrWMqN79exOBhoQzmmDy9FK%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;584&quot; height=&quot;438&quot; data-filename=&quot;LicencesSet_Resized3.png&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 :&amp;nbsp;https://ccl.cckorea.org/about/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&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;제1유형 : 출처표시&lt;/li&gt;
&lt;li&gt;제2유형 : 출처표시 + 상업적 이용금지&lt;/li&gt;
&lt;li&gt;제3유형 : 출처표시 + 변경금지&lt;/li&gt;
&lt;li&gt;제4유형 : 출처표시 + 상업적 이용금지 + 변경금지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bp7KR8/btrWUHXqBLP/JUlCcTUzphxfwANx9Ze9vK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bp7KR8/btrWUHXqBLP/JUlCcTUzphxfwANx9Ze9vK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1221&quot; data-origin-height=&quot;437&quot; data-filename=&quot;공공누리1.PNG&quot; style=&quot;width: 37.601%; margin-right: 10px;&quot; data-widthpercent=&quot;38.04&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bp7KR8/btrWUHXqBLP/JUlCcTUzphxfwANx9Ze9vK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbp7KR8%2FbtrWUHXqBLP%2FJUlCcTUzphxfwANx9Ze9vK%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;437&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N20cb/btrWXiJAlZn/BG4CCB7KY80w7zqh7fWdFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N20cb/btrWXiJAlZn/BG4CCB7KY80w7zqh7fWdFk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;678&quot; data-origin-height=&quot;149&quot; data-filename=&quot;공공누리2.PNG&quot; style=&quot;width: 61.2362%;&quot; data-widthpercent=&quot;61.96&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N20cb/btrWXiJAlZn/BG4CCB7KY80w7zqh7fWdFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN20cb%2FbtrWXiJAlZn%2FBG4CCB7KY80w7zqh7fWdFk%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;678&quot; height=&quot;149&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;출처 :&amp;nbsp;https://www.copyright.or.kr/gov/nuri/rule_info/index.do&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;1. 교육, 등등&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;3. 정치적 연설등의 이용&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;5. 시사 보도를 위한 이용&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;7. 영리를 목적으로 하지 않은 공연, 방송&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8. 사적 이용을 위한 복제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;9. 도서관 등에서의 복제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10. 시험 문제로서의 복제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;11. 시각장애인 등을 위한 복제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;12. 방송사업가의 일시적 녹음, 녹화&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;13. 미술, 사진, 건축저작물의 전시 또는 복제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;14. 번역 등에 의한 이용&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;16. 프로그램 코드 역분석&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;17. 정당한 이용자에 의한 보존을 위한 프로그램 복제&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;저작권.PNG&quot; data-origin-width=&quot;449&quot; data-origin-height=&quot;149&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dMDRv2/btrWR7oPAVg/OkMGDNyeHKKKxECadUJlA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dMDRv2/btrWR7oPAVg/OkMGDNyeHKKKxECadUJlA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dMDRv2/btrWR7oPAVg/OkMGDNyeHKKKxECadUJlA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdMDRv2%2FbtrWR7oPAVg%2FOkMGDNyeHKKKxECadUJlA1%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;449&quot; height=&quot;149&quot; data-filename=&quot;저작권.PNG&quot; data-origin-width=&quot;449&quot; data-origin-height=&quot;149&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처 : 네이버커넥트 재단 AItect&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ccl.cckorea.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ccl.cckorea.org/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1674431854351&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;ccl.cckorea.org&quot; data-og-source-url=&quot;https://ccl.cckorea.org/&quot; data-og-url=&quot;http://ccl.cckorea.org/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/FVdIm/hyRmXXIPwu/JWl21grJPcUfSi0ufkk3Ik/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/EeBru/hyRm8EW2n4/9Sfqo5iPSnRcXnpSh6Kpb1/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/LMtnw/hyRm07XMic/KoL9stFXfavgK4ZYVKTfDk/img.png?width=258&amp;amp;height=258&amp;amp;face=0_0_258_258&quot;&gt;&lt;a href=&quot;https://ccl.cckorea.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ccl.cckorea.org/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/FVdIm/hyRmXXIPwu/JWl21grJPcUfSi0ufkk3Ik/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/EeBru/hyRm8EW2n4/9Sfqo5iPSnRcXnpSh6Kpb1/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/LMtnw/hyRm07XMic/KoL9stFXfavgK4ZYVKTfDk/img.png?width=258&amp;amp;height=258&amp;amp;face=0_0_258_258');&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;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ccl.cckorea.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.kogl.or.kr/index.do&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.kogl.or.kr/index.do&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1674431861430&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;www.kogl.or.kr&quot; data-og-source-url=&quot;https://www.kogl.or.kr/index.do&quot; data-og-url=&quot;https://www.kogl.or.kr/index.do&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.kogl.or.kr/index.do&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.kogl.or.kr/index.do&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;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.kogl.or.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;</description>
      <category>개발</category>
      <category>CCL</category>
      <category>공공누리</category>
      <category>저작권</category>
      <category>지적재산권</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/404</guid>
      <comments>https://bitrader.tistory.com/404#entry404comment</comments>
      <pubDate>Mon, 23 Jan 2023 08:58:44 +0900</pubDate>
    </item>
    <item>
      <title>정규표현식 기초 python re 모듈</title>
      <link>https://bitrader.tistory.com/399</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서는 정규표현식을 지원하는 re(regular expression) 모듈이 있으며 기본 모듈임으로 따로 설치할 필요는 없다.&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;pre id=&quot;code_1674362914215&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import re
pattern = re.compile('ab*')&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. RE 모듈 기본 함수&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열을 검색할 수 있는 함수는 아래와 같이 4개가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;match() : 문자열의 처음부터 정규식과 매치되는지 조사&lt;/li&gt;
&lt;li&gt;search() : 문자열 전체를 검색하여 정규식과 매치되는지 조사&lt;/li&gt;
&lt;li&gt;findall() : 정규식과 매치되는 모든 substring을 리스트로 리턴&lt;/li&gt;
&lt;li&gt;finditer() : 정규식과 매치되는 모든 substring을 반복 가능한 객체로 리턴&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 예시&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 알파벳 소문자가 0부터 ~ 무한대까지 반복될 수 있는 패턴을 만든다고 하면&lt;/p&gt;
&lt;pre id=&quot;code_1674363124836&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import re
p = re.compile('[a-z]+')&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;문자열 'python'은 그에 부합함으로 매치된 결과를 match 객체로 리턴해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1674363161962&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;m = p.match(&quot;python&quot;)
print(m)
&amp;lt;re.Match object; span=(0, 6), match='python'&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 '3 python'은 문자열의 시작이 알파벳 소문자가 아닌 '3' 문자열로 시작함으로 매칭이 되지 않아 None을 리턴한다.&lt;/p&gt;
&lt;pre id=&quot;code_1674363204650&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;m = p.match(&quot;3 python&quot;)
print(m)
None&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면에 search 함수는 처음부터 검색을 하는 것이 아니라 문자열 전체를 대상으로 검색을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'3 python' 문자열을 대상으로 매칭이 되는 문자열을 찾아서 리턴을 해주는 결과를 확인할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1674363526251&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;m = p.search(&quot;python&quot;)
print(m)
&amp;lt;re.Match object; span=(0, 6), match='python'&amp;gt;

m = p.search(&quot;3 python&quot;)
print(m)
&amp;lt;re.Match object; span=(2, 8), match='python'&amp;gt;&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;findall 함수는 매칭이 되는 대상 전부를 찾아서 리스트로 리턴을 해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1674363585641&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;result = p.findall(&quot;life is too short&quot;)
print(result)
['life', 'is', 'too', 'short']&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;finditer 함수는 findall 함수와 같지만 리턴되는 대상이 리스트가 아니라 match 객체로 리턴해주며 iter 하면서 결과를 확인할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1674364720103&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;result = p.finditer(&quot;life is too short&quot;)
print(result)
&amp;lt;callable_iterator object at 0x01F5E390&amp;gt;

 for r in result: print(r)
&amp;lt;re.Match object; span=(0, 4), match='life'&amp;gt;
&amp;lt;re.Match object; span=(5, 7), match='is'&amp;gt;
&amp;lt;re.Match object; span=(8, 11), match='too'&amp;gt;
&amp;lt;re.Match object; span=(12, 17), match='short'&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. match 객체의 매써드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;re 모듈의 함수를 사용하여 반환된 match객체에는 아래와 같은 함수들을 사용할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;group() : 매치된 문자열을 리턴한다.&lt;/li&gt;
&lt;li&gt;start() :&amp;nbsp; 매치된 문자열의 시작 위치를 리턴&lt;/li&gt;
&lt;li&gt;end() : 매치된 문자열의 끝 위치를 리턴&lt;/li&gt;
&lt;li&gt;span() : 매치된 문자열의 구간을 튜플 형태로 리턴&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1674365091475&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import re
p = re.compile('[a-z]+')
m = p.match('python')

m.group()
&amp;gt;&amp;gt;'python'
m.start()
&amp;gt;&amp;gt;0
m.end()
&amp;gt;&amp;gt;6
m.span()
(0, 6)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 컴파일 옵션&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정규 표현식을 사용할 때 컴파일 할때 다양한 옵션을 사용할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DOTALL(S) -.이 줄 바꿈 문자를 포함하여 모든 문자와 매치할 수 있도록 한다.&lt;/li&gt;
&lt;li&gt;IGNORECASE(I) - 대소문자에 관계없이 매치할 수 있도록 한다.&lt;/li&gt;
&lt;li&gt;MULTILINE(M) - 여러 줄과 매치할 수 있도록 한다. (^,&lt;span&gt;&amp;nbsp;&lt;/span&gt;$&lt;span&gt;&amp;nbsp;&lt;/span&gt;메타문자의 사용과 관계가 있는 옵션이다)&lt;/li&gt;
&lt;li&gt;VERBOSE(X) - verbose 모드를 사용할 수 있도록 한다. (정규식을 보기 편하게 만들 수 있고 주석 등을 사용할 수 있게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ 파이썬 re 모듈 모듈단위로 수행하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예시들은 정규표현식을 컴파일하고 매치를 실시하였으나 축약된 형태로 사용이 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1674365262881&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# before
p = re.compile('[a-z]+')
m = p.match(&quot;python&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1674365277620&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# after
m = re.match('[a-z]+', &quot;python&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://wikidocs.net/4308&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://wikidocs.net/4308&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1674364939824&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;08-2 정규 표현식 시작하기&quot; data-og-description=&quot;[TOC] ## 정규 표현식의 기초, 메타 문자 정규 표현식에서 사용하는 메타 문자(meta characters)에는 다음과 같은 것이 있다. &amp;gt; 메타 문자란 원래 그 문자&amp;hellip;&quot; data-og-host=&quot;wikidocs.net&quot; data-og-source-url=&quot;https://wikidocs.net/4308&quot; data-og-url=&quot;https://wikidocs.net/4308&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/i0oDR/hyRnaI8HIl/F6LAvb83gFJQKPgG0KiHwk/img.jpg?width=95&amp;amp;height=130&amp;amp;face=0_0_95_130&quot;&gt;&lt;a href=&quot;https://wikidocs.net/4308&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://wikidocs.net/4308&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/i0oDR/hyRnaI8HIl/F6LAvb83gFJQKPgG0KiHwk/img.jpg?width=95&amp;amp;height=130&amp;amp;face=0_0_95_130');&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;08-2 정규 표현식 시작하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;[TOC] ## 정규 표현식의 기초, 메타 문자 정규 표현식에서 사용하는 메타 문자(meta characters)에는 다음과 같은 것이 있다. &amp;gt; 메타 문자란 원래 그 문자&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;wikidocs.net&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>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/399</guid>
      <comments>https://bitrader.tistory.com/399#entry399comment</comments>
      <pubDate>Sun, 22 Jan 2023 14:00:20 +0900</pubDate>
    </item>
    <item>
      <title>정규표현식 기초 (메타문자)</title>
      <link>https://bitrader.tistory.com/398</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;pre id=&quot;code_1674360680796&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;. ^ $ * + ? { } [ ] \ | ( )&lt;/code&gt;&lt;/pre&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;[abc]라는 정규표현식이 있다면 'a,b,c' 중 한개의 문자와의 매치를 의미한다. 예시를 들어보면 아래와 같이 적용이 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;a&quot;는 정규식과 일치하는 문자인 &quot;a&quot;가 있으므로 매치&lt;/li&gt;
&lt;li&gt;&quot;before&quot;는 정규식과 일치하는 문자인 &quot;b&quot;가 있으므로 매치&lt;/li&gt;
&lt;li&gt;&quot;dude&quot;는 정규식과 일치하는 문자인 a, b, c 중 어느 하나도 포함하고 있지 않으므로 매치되지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'[ ]'안의 문자 사이에 하이픈을(-)을 사용하면 범위를 의미한다. 즉 [a-c]는 [abc]와 동일하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;[a-zA-Z] : 알파벳 전부&lt;/li&gt;
&lt;li&gt;[0-9] : 숫자&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 '^'은 반대를 의미한다. [^0-9]는 숫자가를 제외하고 매치를 한다.&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;[0-9], [a-zA-Z]등은 매우 자주 사용하는 표현식이라 따로 표기법을 만들어 두었다고 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1674361218300&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;\d - 숫자와 매치, [0-9]와 동일한 표현식이다.
\D - 숫자가 아닌 것과 매치, [^0-9]와 동일한 표현식이다.
\s - whitespace 문자와 매치, [ \t\n\r\f\v]와 동일한 표현식이다. 맨 앞의 빈 칸은 공백문자(space)를 의미한다.
\S - whitespace 문자가 아닌 것과 매치, [^ \t\n\r\f\v]와 동일한 표현식이다.
\w - 문자+숫자(alphanumeric)와 매치, [a-zA-Z0-9_]와 동일한 표현식이다.
\W - 문자+숫자(alphanumeric)가 아닌 문자와 매치, [^a-zA-Z0-9_]와 동일한 표현식이다.&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Dot(.)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정규표현식의 '.' 메타문자는 줄바꿈 문자 '\n'를 제외하고 모든 문자와 매치됨을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 a.b 라고 하는 정규표현식이 있으면 'aab', 'a0b' 와는 매치가 되지만 'abc'와는 매치가 되지 않는다.&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;또한 a[.]b라고 하는 정규표현식은 위의 a.b 정규표현식과 혼동될 수 있으나 'a.b'와 같은 문자열과 매치가 됨으로 메타문자 '.'을 의미하는것이 아닌 Dot(.)문자 자체를 의미하는것이라고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;반복 (*)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복(*) 메타 문자는 * 바로 앞의 문자가 0부터 무한대까지 반복이 될 수 있다라는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ca*t 라는 정규표현식이 있으면 *앞의 a라는 문자가 0부터 무한대까지 반복이 될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;반복 (+)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복(+) 메타 문자는 반복(*) 메타 문자와는 달리 1부터 무한대까지 반복 될 수 있음을 뜻한다.&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;h3 data-ke-size=&quot;size23&quot;&gt;반복 ({m,n}, ?)&lt;/h3&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1.&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;{m}&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;해당 횟수 만큼 반복이라는 의미이며 ca{2}t라는 정규표현식이 있으면 a가 무조건 두번 반복되는 문자열을 매칭을 한다.&lt;/span&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;b&gt;2.&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;{m, n}&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;m에서 n까지를 의미한다. ca{2,5}t는 a가 2에서 5회 반복하면 매칭이 된다.&lt;/span&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;b&gt;3.&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;?&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;&amp;nbsp;&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;&amp;nbsp;&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;이러한 메타 문자들을 (zerowidth assertions) 이라고 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;| 문자&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;or 과 동일한 의미 이며 A|B 라는 정규식이 있다면 A 또는 B라는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 문자열이 소비되지 않기 때문에 Crow Hello 둘다 맞추는 것을 확인할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1674437023574&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import re
p = re.compile('Crow|Hello')
m = p.finditer('CrowHello')
for each in m:
    print(each)
    
&amp;lt;re.Match object; span=(0, 4), match='Crow'&amp;gt;
&amp;lt;re.Match object; span=(4, 9), match='Hello'&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;^문자&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;^ 메타 문자는 문자열의 맨처음과 일치함을 의미한다. re.MULTILINE 옵션을 사용하면 여러 줄을 매칭하게 됨으로 각 줄의 시작과 패턴이 일치한다면 매칭이된다. 해당 옵션이 없으면 정말로 딱 처음의 패턴만 탐지하게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1674437228243&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print(re.search('^Life', 'Life is too short'))
&amp;lt;re.Match object; span=(0, 4), match='Life'&amp;gt;
print(re.search('^Life', 'My Life'))
None&lt;/code&gt;&lt;/pre&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_1674437959233&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print(re.search('short$', 'Life is too short'))
&amp;lt;re.Match object; span=(12, 17), match='short'&amp;gt;
print(re.search('short$', 'Life is too short, you need python'))
None&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;\A&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 메타 문자는 ^과 같은 의미지만 re.MULTILINE 옵션을 사용해도 전체 문자열의 처음 하고만 매치가 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;\Z&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 문자열도 $문자와 같은 의미지만 re.MULTILINE 옵션을 사용해도 전체 문자열의 끝하고만 매치가 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;\b&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\b는 단어 구분자(word boundary)이며 whitespace에 의해 구분되어 있는 문자열만을 매칭한다.&lt;/p&gt;
&lt;pre id=&quot;code_1674438905231&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;p = re.compile(r'\bclass\b')
print(p.search('no class at all'))  
&amp;lt;re.Match object; span=(3, 8), match='class'&amp;gt;
print(p.search('the declassified algorithm'))
None&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;\B&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\b와 반대로 whitespace로 구분된 단어가 아닌 경우에 매칭이 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1674438954704&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;p = re.compile(r'\Bclass\B')
print(p.search('no class at all'))  
None
print(p.search('the declassified algorithm'))
&amp;lt;re.Match object; span=(6, 11), match='class'&amp;gt;
print(p.search('one subclass is'))&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;출처 : &lt;a href=&quot;https://wikidocs.net/4308&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://wikidocs.net/4308&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/정규표현식</category>
      <category>python</category>
      <category>regularexpression</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/398</guid>
      <comments>https://bitrader.tistory.com/398#entry398comment</comments>
      <pubDate>Sun, 22 Jan 2023 13:32:57 +0900</pubDate>
    </item>
    <item>
      <title>자연어 전처리 관련 유용한 패키지</title>
      <link>https://bitrader.tistory.com/396</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. KoNLPy&lt;/h3&gt;
&lt;pre id=&quot;code_1674358296183&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pip install konlpy&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;* Kkma, Komoran, Hannanum, Okt, Mecab 등등...&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;a href=&quot;https://docs.google.com/spreadsheets/d/1OGAjUvalBuX-oZvZ_-9tEfYD2gQe7hTGsgUpiiBSXI8/edit#gid=0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.google.com/spreadsheets/d/1OGAjUvalBuX-oZvZ_-9tEfYD2gQe7hTGsgUpiiBSXI8/edit#gid=0&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈별 성능 비교 : &lt;a href=&quot;https://konlpy.org/ko/latest/morph/#comparison-between-pos-tagging-classes&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://konlpy.org/ko/latest/morph/#comparison-between-pos-tagging-classes&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Khaiii&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;KoNLPy는 통계기반으로 형태소를 분석하지만 Khaiii는 딥러닝 기반으로 형태소를 분석을 하는 패키지이다.&lt;/p&gt;
&lt;pre id=&quot;code_1674358497465&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;!git clone https://github.com/kakao/khaiii.git
!pip install cmake
!mkdir build
!cd build &amp;amp;&amp;amp; cmake ../khaiii
!cd build &amp;amp;&amp;amp; make all
!cd build &amp;amp;&amp;amp; make resource
!cd build &amp;amp;&amp;amp; make install
!cd build &amp;amp;&amp;amp; make package_python
!pip install build/package_python&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;깃허브 주소 : &lt;a href=&quot;https://github.com/kakao/khaiii/wiki/CNN-%EB%AA%A8%EB%8D%B8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/kakao/khaiii/wiki/CNN-%EB%AA%A8%EB%8D%B8&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1674358650991&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 - kakao/khaiii: Kakao Hangul Analyzer III&quot; data-og-description=&quot;Kakao Hangul Analyzer III. Contribute to kakao/khaiii development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/kakao/khaiii/wiki/CNN-%EB%AA%A8%EB%8D%B8&quot; data-og-url=&quot;https://github.com/kakao/khaiii&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bplWhz/hyRlq05MQF/beBi3Xk826j2sGmaMSw1N0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/kakao/khaiii/wiki/CNN-%EB%AA%A8%EB%8D%B8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/kakao/khaiii/wiki/CNN-%EB%AA%A8%EB%8D%B8&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bplWhz/hyRlq05MQF/beBi3Xk826j2sGmaMSw1N0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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 - kakao/khaiii: Kakao Hangul Analyzer III&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Kakao Hangul Analyzer III. Contribute to kakao/khaiii 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;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. PyKoSpacing&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PykoSpacing은 한국어의 띄어쓰기를 주성해주는 패키지로 딥러닝을 기반으로 한 패키지이다.&lt;/p&gt;
&lt;pre id=&quot;code_1674358600947&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pip install git+https://github.com/haven-jeon/PyKoSpacing.git&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/haven-jeon/PyKoSpacing&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/haven-jeon/PyKoSpacing&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1674358649545&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 - haven-jeon/PyKoSpacing: Automatic Korean word spacing with Python&quot; data-og-description=&quot;Automatic Korean word spacing with Python . Contribute to haven-jeon/PyKoSpacing development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/haven-jeon/PyKoSpacing&quot; data-og-url=&quot;https://github.com/haven-jeon/PyKoSpacing&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/HiavF/hyRlAJrQPr/Wiky3S0kPBpMTcTCwhf2R0/img.jpg?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640&quot;&gt;&lt;a href=&quot;https://github.com/haven-jeon/PyKoSpacing&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/haven-jeon/PyKoSpacing&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/HiavF/hyRlAJrQPr/Wiky3S0kPBpMTcTCwhf2R0/img.jpg?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640');&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 - haven-jeon/PyKoSpacing: Automatic Korean word spacing with Python&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Automatic Korean word spacing with Python . Contribute to haven-jeon/PyKoSpacing 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;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. Py-Hanspell&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PyKoSpacing과 마찬가지로 딥러닝을 기반으로 하여 맞춤법을 교정해주는 패키지이며, '네이버 한국어 맞춤법 검사기'를 기반으로 제작이 되었다.&lt;/p&gt;
&lt;pre id=&quot;code_1674358727850&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;!pip install git+https://github.com/ssut/py-hanspell.git&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/ssut/py-hanspell&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/ssut/py-hanspell&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1674358742040&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 - ssut/py-hanspell: 파이썬 한글 맞춤법 검사 라이브러리. (네이버 맞춤법 검사기 사용)&quot; data-og-description=&quot;파이썬 한글 맞춤법 검사 라이브러리. (네이버 맞춤법 검사기 사용). Contribute to ssut/py-hanspell development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/ssut/py-hanspell&quot; data-og-url=&quot;https://github.com/ssut/py-hanspell&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/A55QD/hyRm2K66Ut/7cqPpcADSmaMK8CSNEMgck/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/ssut/py-hanspell&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/ssut/py-hanspell&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/A55QD/hyRm2K66Ut/7cqPpcADSmaMK8CSNEMgck/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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 - ssut/py-hanspell: 파이썬 한글 맞춤법 검사 라이브러리. (네이버 맞춤법 검사기 사용)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;파이썬 한글 맞춤법 검사 라이브러리. (네이버 맞춤법 검사기 사용). Contribute to ssut/py-hanspell 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;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. NLTK&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. kss&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. spacy&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;</description>
      <category>개발</category>
      <author>상상2</author>
      <guid isPermaLink="true">https://bitrader.tistory.com/396</guid>
      <comments>https://bitrader.tistory.com/396#entry396comment</comments>
      <pubDate>Sun, 22 Jan 2023 12:40:48 +0900</pubDate>
    </item>
  </channel>
</rss>