<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Jinho Tech</title>
    <link>https://jinho-study.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Mon, 3 Aug 2026 16:12:43 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>kimjinho1</managingEditor>
    <image>
      <title>Jinho Tech</title>
      <url>https://tistory1.daumcdn.net/tistory/3585993/attach/11fcfae6c2bc4fce966a0c9c390ac9d3</url>
      <link>https://jinho-study.tistory.com</link>
    </image>
    <item>
      <title>Poetry, Scrapy, MongoDB 사용하여 크롤링하기</title>
      <link>https://jinho-study.tistory.com/1150</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;1. poetry 설치하기(Mac)&lt;/p&gt;
&lt;pre id=&quot;code_1738045412848&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;brew install pipx
pipx ensurepath
sudo pipx ensurepath --global #Optional

pipx install poetry
# poetry shell 명령어 사용(가상환경)
poetry self add poetry-plugin-shell&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;2. MongoDB 설치 및 DB 추가(Mac)&lt;/p&gt;
&lt;pre id=&quot;code_1738047418392&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;brew tap mongodb/brew
brew update
brew install mongodb-community@8.0

mongod --version
# mongodb 서비스를 백그라운드에서 실행
brew services start mongodb-community@8.0

# mongod 실행(mac)
mongod --config /opt/homebrew/etc/mongod.conf --fork&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;데이터베이스 관련은 mongodb compass 사용 시 손쉽게 설정할 수 있다.&amp;nbsp;&lt;br /&gt;필자의 경우에는 localhost 27017로 접속하여 books라는 이름의 데이터베이스를 추가했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1240&quot; data-origin-height=&quot;576&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pmFw1/btsL26lUdzm/8oa1bmcAHAb8RPY5bnTTJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pmFw1/btsL26lUdzm/8oa1bmcAHAb8RPY5bnTTJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pmFw1/btsL26lUdzm/8oa1bmcAHAb8RPY5bnTTJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpmFw1%2FbtsL26lUdzm%2F8oa1bmcAHAb8RPY5bnTTJK%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;543&quot; height=&quot;252&quot; data-origin-width=&quot;1240&quot; data-origin-height=&quot;576&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;a href=&quot;https://www.mongodb.com/ko-kr/products/tools/compass&quot;&gt;https://www.mongodb.com/ko-kr/products/tools/compass&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1738064092247&quot; contenteditable=&quot;false&quot; data-og-image=&quot;&quot; data-og-url=&quot;https://www.mongodb.com/products/tools/compass&quot; data-og-source-url=&quot;https://www.mongodb.com/ko-kr/products/tools/compass&quot; data-og-host=&quot;www.mongodb.com&quot; data-og-description=&quot;Explore and interact with your data using Compass, the GUI for MongoDB. Query, modify, delete, and more &amp;mdash; all from one interface.&quot; data-og-title=&quot;MongoDB Compass&quot; data-og-type=&quot;website&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://www.mongodb.com/ko-kr/products/tools/compass&quot; data-source-url=&quot;https://www.mongodb.com/ko-kr/products/tools/compass&quot;&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;MongoDB Compass&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;Explore and interact with your data using Compass, the GUI for MongoDB. Query, modify, delete, and more &amp;mdash; all from one interface.&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;www.mongodb.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;3. poetry init&lt;br /&gt;이미 프로젝트가 존재하는 경우라면 &lt;b&gt;poetry init&lt;/b&gt; 명령어로 &lt;b&gt;pyproject.toml&lt;/b&gt;(Poetry 설정 파일) 파일을 생성 가능하다.&lt;br /&gt;없는 경우라면 &lt;b&gt;poetry new poetry-demo&amp;nbsp;&lt;/b&gt;명령어로 프로젝트를 생성&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. package install&lt;/p&gt;
&lt;pre id=&quot;code_1738046833080&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;poetry install
poetry add scrapy
poetry add pymongo
poetry show&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;poetry install시 poetry.lock 파일이 생성된다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. vscode poetry 가상환경 연동&lt;br /&gt;&lt;b&gt;poetry shell&lt;/b&gt; 명령어 실행 시 가상환경에 접속되고 아래와 같이 env path가 출력된다.&lt;/p&gt;
&lt;pre id=&quot;code_1738050404436&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Virtual environment already activated: /Users/kimjinho/Library/Caches/pypoetry/virtualenvs/books-scraper-wBYwd1Bg-py3.13&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 경로를 아래와 같은 방식으로 지정해줘야 한다.&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;vscode -&amp;gt; command + shift + p -&amp;gt; Python: Select Interpreter -&amp;gt; Enter interpreter path...&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;138&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MYqCn/btsL3RIrWAn/pLRe9EjuYT3so0IU1Ukzek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MYqCn/btsL3RIrWAn/pLRe9EjuYT3so0IU1Ukzek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MYqCn/btsL3RIrWAn/pLRe9EjuYT3so0IU1Ukzek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMYqCn%2FbtsL3RIrWAn%2FpLRe9EjuYT3so0IU1Ukzek%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;1198&quot; height=&quot;138&quot; data-origin-width=&quot;1198&quot; data-origin-height=&quot;138&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1184&quot; data-origin-height=&quot;176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhqQQ2/btsL2aib5F4/s0PJVQGN27mvi5SPQzH6hK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhqQQ2/btsL2aib5F4/s0PJVQGN27mvi5SPQzH6hK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhqQQ2/btsL2aib5F4/s0PJVQGN27mvi5SPQzH6hK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhqQQ2%2FbtsL2aib5F4%2Fs0PJVQGN27mvi5SPQzH6hK%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;1184&quot; height=&quot;176&quot; data-origin-width=&quot;1184&quot; data-origin-height=&quot;176&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;6. scrapy 시작&lt;br /&gt;해당 명령어 실행 시 폴더구조가 아래와 같이 생성된다.&lt;/p&gt;
&lt;pre id=&quot;code_1738050945969&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 스파이더 생성
scrapy startproject scrapy_test
cd scrapy_test
# 스파이더 생성
scrapy genspider test_spider quotes.toscrape.com&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.&lt;br /&gt;├──&amp;nbsp;scrapy.cfg&lt;br /&gt;└──&amp;nbsp;scrapy_test&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;├──&amp;nbsp;__init__.py&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;├──&amp;nbsp;items.py&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;├──&amp;nbsp;middlewares.py&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;├──&amp;nbsp;pipelines.py&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;├──&amp;nbsp;settings.py&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;└──&amp;nbsp;spiders&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;├──&amp;nbsp;__init__.py&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;└──&amp;nbsp;test_spider.py&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 파일을 용도는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #292a2d; color: #f8faff; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;scrapy.cfg&lt;/b&gt;: 프로젝트 설정 파일&lt;/li&gt;
&lt;li&gt;&lt;b&gt;items.py&lt;/b&gt;: 데이터 구조 정의&lt;/li&gt;
&lt;li&gt;&lt;b&gt;middlewares.py&lt;/b&gt;: 요청/응답 처리 로직&lt;/li&gt;
&lt;li&gt;&lt;b&gt;pipelines.py&lt;/b&gt;: 데이터 처리 파이프라인&lt;/li&gt;
&lt;li&gt;&lt;b&gt;settings.py&lt;/b&gt;: 프로젝트 설정&lt;/li&gt;
&lt;li&gt;&lt;b&gt;spiders/&lt;/b&gt;: 크롤링 로직이 작성된 스파이더 파일&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 spiders 폴더에는 각 웹사이트 별로 파싱 로직을 따로 구현한다.&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에 저장하는 것이 목표이기에 해당 파일들 중 middlewares.py는 사용하지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 spiders/test_spider.py 파일의 start_urls를 파싱할 url 전체 경로로 지정해 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1738054306771&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class TestSpiderSpider(scrapy.Spider):
    name = 'test_spider'
    allowed_domains = ['quotes.toscrape.com']
    start_urls = ['http://quotes.toscrape.com/']&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;7. scrapy 파싱 시작&lt;br /&gt;items.py에 아래와 같이 파싱할 데이터의 타입을 지정해 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1738054647313&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import scrapy

class QuoteItem(scrapy.Item):
    author = scrapy.Field()
    text = scrapy.Field()
    tags = scrapy.Field()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;test_spider.py에는 파싱 코드를 추가해 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1738054681764&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import scrapy
from ..items import QuoteItem

class TestSpiderSpider(scrapy.Spider):
    name = 'test_spider'
    allowed_domains = ['quotes.toscrape.com']
    start_urls = ['http://quotes.toscrape.com/']

    def parse(self, response):
        for quote in response.css('div.quote'):
            item = QuoteItem()

            item['author'] = quote.css('small.author::text').get()
            item['text'] = quote.css('span.text::text').re(r'&amp;ldquo;(.+)&amp;rdquo;')[0]
            item['tags'] = quote.css('div.tags a.tag::text').getall()
            print(&quot;==============\n&quot;, item)

            yield item&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;http://quotes.toscrape.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;http://quotes.toscrape.com/&lt;/a&gt;)의 html 구조를 확인해 보면 quote라는 이름의 div에 책 정보들이&amp;nbsp;&lt;br /&gt;차곡차곡 저장되어 있는 것을 확인할 수 있다. 이런 식으로 패턴을 알았다면 해당 div 안에서 필요한 정보의 css를 각각 확인하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3228&quot; data-origin-height=&quot;722&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/egSYBF/btsL07NjmWI/ip5uSrVp0zqlUHKWwfk6X1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/egSYBF/btsL07NjmWI/ip5uSrVp0zqlUHKWwfk6X1/img.png&quot; data-alt=&quot;div.quote&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/egSYBF/btsL07NjmWI/ip5uSrVp0zqlUHKWwfk6X1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FegSYBF%2FbtsL07NjmWI%2Fip5uSrVp0zqlUHKWwfk6X1%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;3228&quot; height=&quot;722&quot; data-origin-width=&quot;3228&quot; data-origin-height=&quot;722&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;div.quote&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3168&quot; data-origin-height=&quot;454&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wrrgv/btsL2oN4m5J/BJgs22av1WZHeArXtjFO31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wrrgv/btsL2oN4m5J/BJgs22av1WZHeArXtjFO31/img.png&quot; data-alt=&quot;small.auther&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wrrgv/btsL2oN4m5J/BJgs22av1WZHeArXtjFO31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwrrgv%2FbtsL2oN4m5J%2FBJgs22av1WZHeArXtjFO31%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;3168&quot; height=&quot;454&quot; data-origin-width=&quot;3168&quot; data-origin-height=&quot;454&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;small.auther&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올바르게 css selecter를 지정했다면 scrapy를 실행해 보자&lt;br /&gt;그냥 실행하면 출력을 알아볼 수 없어서 a라는 파일에 출력내용을 저장했다.&lt;/p&gt;
&lt;pre id=&quot;code_1738060471707&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;scrapy crawl test_spider &amp;gt; a&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 후 a 파일을 확인해 보면 아래와 같이 파싱한 내용들을 확인할 수 있다. 파싱은 완료되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1264&quot; data-origin-height=&quot;882&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MMRN8/btsL2KcjvLe/y5iJbQhKDBpVwdsOkjHgjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MMRN8/btsL2KcjvLe/y5iJbQhKDBpVwdsOkjHgjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MMRN8/btsL2KcjvLe/y5iJbQhKDBpVwdsOkjHgjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMMRN8%2FbtsL2KcjvLe%2Fy5iJbQhKDBpVwdsOkjHgjk%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;1264&quot; height=&quot;882&quot; data-origin-width=&quot;1264&quot; data-origin-height=&quot;882&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;7. 파싱 결과를 mongodb에 저장&lt;br /&gt;pipelines.py 에 process_item, from_crawler, open_spider, close_spider 메서드를 추가한다.&lt;br /&gt;process_item 메서드만 필수이고 나머지는 선택이다.&lt;br /&gt;여기서 collection 변수는 추가할  collection 이름이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;process_item(self, item, spider)&lt;/b&gt;:&lt;br /&gt;각 크롤링된 아이템을 처리함&lt;/li&gt;
&lt;li&gt;&lt;b&gt;from_crawler(cls, crawler)&lt;/b&gt;:&lt;br /&gt;mongo_uri, mongo_db와&amp;nbsp;같은 설정 정보를 가지는 파이프라인 인스턴스를 생성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;open_spider(self, spider)&lt;/b&gt;:&lt;br /&gt;크롤러가 열릴 때 호출됨&lt;/li&gt;
&lt;li&gt;&lt;b&gt;close_spider(self, spider)&lt;/b&gt;:&lt;br /&gt;크롤러가 닫힐 때 호출됨&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1738062595111&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import pymongo
import sys
from .items import QuoteItem

class ScrapyTestPipeline:

    collection = 'books'

    def __init__(self, mongo_uri, mongo_db):
        self.mongo_uri = mongo_uri
        self.mongo_db = mongo_db
        if not self.mongo_uri: sys.exit(&quot;You need to provide a MongoDB Connection String using the -s parameter.&quot;)

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            mongo_uri=crawler.settings.get('MONGO_URI'),
            mongo_db=crawler.settings.get('MONGO_DATABASE', 'items')
        )

    def open_spider(self, spider):
        self.client = pymongo.MongoClient(self.mongo_uri)
        self.db = self.client[self.mongo_db]

    def close_spider(self, spider):
        self.client.close()

    def process_item(self, item, spider):
        data = dict(QuoteItem(item))
        self.db[self.collection].insert_one(data)
        return item&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로직 순서로 보면 다음과 같다.&amp;nbsp;&lt;br /&gt;1. from_crawler -&amp;gt; __init__ 메서드 순으로 실행되어 초기 설정 정보(mongo_uri, mongo_db)를 저장&lt;br /&gt;2.  크롤러 시작 시 open_spider 메서드가 실행되어 mongodb와 연결됨&lt;br /&gt;3. 각 크롤링 결과를 process_item 메서드에서 mongodb에 추가&lt;br /&gt;4. 크롤러 종료 시 mongodb 연결 끊음&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 settings.py에 해당 설정을 추가하면 된다.&lt;br /&gt;초기 프로젝트 생성 시 주석으로 추가되어 있다. 여기서 숫자는 우선순위를 의미한다. 낮은 것부터 실행된다.&lt;/p&gt;
&lt;pre id=&quot;code_1738063639901&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ITEM_PIPELINES = {
   &quot;scrapy_test.pipelines.ScrapyTestPipeline&quot;: 300,
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;아래 명령어로 실행 시 mongodb에 차곡차곡 쌓이는 것을 확인할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1738063725249&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;scrapy crawl test_spider -s MONGO_URI=&quot;mongodb://localhost:27017&quot; -s MONGO_DATABASE=&quot;books&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2098&quot; data-origin-height=&quot;1158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGSUtC/btsL2GOwhyY/SOuCmq0i6REBWRIWFdaLp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGSUtC/btsL2GOwhyY/SOuCmq0i6REBWRIWFdaLp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGSUtC/btsL2GOwhyY/SOuCmq0i6REBWRIWFdaLp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGSUtC%2FbtsL2GOwhyY%2FSOuCmq0i6REBWRIWFdaLp0%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;2098&quot; height=&quot;1158&quot; data-origin-width=&quot;2098&quot; data-origin-height=&quot;1158&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;참고 URL)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;poetry 설치: &lt;a href=&quot;https://python-poetry.org/docs/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://python-poetry.org/docs/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1738045608538&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;Introduction | Documentation | Poetry - Python dependency management and packaging made easy&quot; data-og-description=&quot;pipx is used to install Python CLI applications globally while still isolating them in virtual environments. pipx will manage upgrades and uninstalls when used to install Poetry. Install pipx If pipx is not already installed, you can follow any of the opti&quot; data-og-host=&quot;python-poetry.org&quot; data-og-source-url=&quot;https://python-poetry.org/docs/&quot; data-og-url=&quot;https://python-poetry.org/docs/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://python-poetry.org/docs/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://python-poetry.org/docs/&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;Introduction | Documentation | Poetry - Python dependency management and packaging made easy&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;pipx is used to install Python CLI applications globally while still isolating them in virtual environments. pipx will manage upgrades and uninstalls when used to install Poetry. Install pipx If pipx is not already installed, you can follow any of the opti&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;python-poetry.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;
&lt;p data-ke-size=&quot;size16&quot;&gt;poetry setup: &lt;a href=&quot;https://python-poetry.org/docs/basic-usage/#project-setup&quot;&gt;https://python-poetry.org/docs/basic-usage/#project-setup&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1738048978251&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;Basic usage | Documentation | Poetry - Python dependency management and packaging made easy&quot; data-og-description=&quot;If managing your own virtual environment externally, you do not need to use poetry run since you will, presumably, already have activated that virtual environment and made available the correct python instance. For example, these commands should output the&quot; data-og-host=&quot;python-poetry.org&quot; data-og-source-url=&quot;https://python-poetry.org/docs/basic-usage/#project-setup&quot; data-og-url=&quot;https://python-poetry.org/docs/basic-usage/#project-setup&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://python-poetry.org/docs/basic-usage/#project-setup&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://python-poetry.org/docs/basic-usage/#project-setup&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;Basic usage | Documentation | Poetry - Python dependency management and packaging made easy&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;If managing your own virtual environment externally, you do not need to use poetry run since you will, presumably, already have activated that virtual environment and made available the correct python instance. For example, these commands should output the&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;python-poetry.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mongodb 설치: &lt;a href=&quot;https://www.mongodb.com/ko-kr/docs/manual/tutorial/install-mongodb-on-os-x/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.mongodb.com/ko-kr/docs/manual/tutorial/install-mongodb-on-os-x/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1738048991709&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;macOS에 MongoDB Community Edition 설치 - MongoDB 매뉴얼 v8.0&quot; data-og-description=&quot;설치 후 macOS가 mongod 실행을 차단할 수 있습니다. mongod을 시작할 때 개발자를 식별하거나 확인할 수 없다는 보안 오류를 수신한 경우 다음을 실행하여 mongod에 액세스 권한을 부여합니다. 시스템 &quot; data-og-host=&quot;www.mongodb.com&quot; data-og-source-url=&quot;https://www.mongodb.com/ko-kr/docs/manual/tutorial/install-mongodb-on-os-x/&quot; data-og-url=&quot;https://www.mongodb.com/ko-kr/docs/manual/tutorial/install-mongodb-on-os-x/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b91xZE/hyX7VFnUAi/vW7WuXvCMTiYdsuUxjWER0/img.png?width=1200&amp;amp;height=601&amp;amp;face=0_0_1200_601,https://scrap.kakaocdn.net/dn/ieYXZ/hyX4lsgXhR/ZvXcoAtJlhnRQKBJTHAYT0/img.png?width=1200&amp;amp;height=601&amp;amp;face=0_0_1200_601&quot;&gt;&lt;a href=&quot;https://www.mongodb.com/ko-kr/docs/manual/tutorial/install-mongodb-on-os-x/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.mongodb.com/ko-kr/docs/manual/tutorial/install-mongodb-on-os-x/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b91xZE/hyX7VFnUAi/vW7WuXvCMTiYdsuUxjWER0/img.png?width=1200&amp;amp;height=601&amp;amp;face=0_0_1200_601,https://scrap.kakaocdn.net/dn/ieYXZ/hyX4lsgXhR/ZvXcoAtJlhnRQKBJTHAYT0/img.png?width=1200&amp;amp;height=601&amp;amp;face=0_0_1200_601');&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;macOS에 MongoDB Community Edition 설치 - MongoDB 매뉴얼 v8.0&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;설치 후 macOS가 mongod 실행을 차단할 수 있습니다. mongod을 시작할 때 개발자를 식별하거나 확인할 수 없다는 보안 오류를 수신한 경우 다음을 실행하여 mongod에 액세스 권한을 부여합니다. 시스템&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.mongodb.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;scrapy 사용: &lt;a href=&quot;https://docs.scrapy.org/en/latest/topics/commands.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.scrapy.org/en/latest/topics/commands.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;poetry shell 명령어 사용(가상환경): &lt;a href=&quot;https://www.reddit.com/r/learnpython/comments/1hxftvw/poetry_shell_the_command_shell_does_not_exist/?rdt=56737&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.reddit.com/r/learnpython/comments/1hxftvw/poetry_shell_the_command_shell_does_not_exist/?rdt=56737&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1738048801425&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;From the learnpython community on Reddit&quot; data-og-description=&quot;Explore this post and more from the learnpython community&quot; data-og-host=&quot;www.reddit.com&quot; data-og-source-url=&quot;https://www.reddit.com/r/learnpython/comments/1hxftvw/poetry_shell_the_command_shell_does_not_exist/?rdt=56737&quot; data-og-url=&quot;https://www.reddit.com/r/learnpython/comments/1hxftvw/poetry_shell_the_command_shell_does_not_exist/?rdt=56737&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c86aXP/hyX7OGk8K5/Z3EPx4Yqhi4RONiKZ4Qy1K/img.jpg?width=1120&amp;amp;height=584&amp;amp;face=0_0_1120_584,https://scrap.kakaocdn.net/dn/bbwaA3/hyX7TgA0lq/ADbsH2qwn3X6HJ83Im0ex0/img.jpg?width=1120&amp;amp;height=584&amp;amp;face=0_0_1120_584&quot;&gt;&lt;a href=&quot;https://www.reddit.com/r/learnpython/comments/1hxftvw/poetry_shell_the_command_shell_does_not_exist/?rdt=56737&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.reddit.com/r/learnpython/comments/1hxftvw/poetry_shell_the_command_shell_does_not_exist/?rdt=56737&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c86aXP/hyX7OGk8K5/Z3EPx4Yqhi4RONiKZ4Qy1K/img.jpg?width=1120&amp;amp;height=584&amp;amp;face=0_0_1120_584,https://scrap.kakaocdn.net/dn/bbwaA3/hyX7TgA0lq/ADbsH2qwn3X6HJ83Im0ex0/img.jpg?width=1120&amp;amp;height=584&amp;amp;face=0_0_1120_584');&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;From the learnpython community on Reddit&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Explore this post and more from the learnpython community&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.reddit.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vscode poetry 가상환경 연동: &lt;a href=&quot;https://takeheed.tistory.com/33&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://takeheed.tistory.com/33&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1738049585560&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] Python 개발 환경 세팅 : Poetry 가상환경&quot; data-og-description=&quot;1. Poetry 설치 1.1 Poetry란? Poetry는 pipvenv (pip + virtualenv)와 비슷하게, pip와 virtualenv를 동시에 사용할 수 있게 해주는 packaging과 dependency management를 위한 tool이다. Poetry는 pip와 비교하면 쉽게 이해할 수 &quot; data-og-host=&quot;takeheed.tistory.com&quot; data-og-source-url=&quot;https://takeheed.tistory.com/33&quot; data-og-url=&quot;https://takeheed.tistory.com/33&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/mqRSe/hyX70te14c/LYAxdHHjYCH1FbcFc7ZPL1/img.png?width=800&amp;amp;height=418&amp;amp;face=0_0_800_418,https://scrap.kakaocdn.net/dn/vybd1/hyX4zxiuJR/kCgoD2Xzm8Ef8DflTERo51/img.png?width=800&amp;amp;height=418&amp;amp;face=0_0_800_418,https://scrap.kakaocdn.net/dn/jpuDm/hyX7UT4uwJ/dLzoSfGrQDBaAjNH5vkUC1/img.png?width=833&amp;amp;height=536&amp;amp;face=0_0_833_536&quot;&gt;&lt;a href=&quot;https://takeheed.tistory.com/33&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://takeheed.tistory.com/33&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/mqRSe/hyX70te14c/LYAxdHHjYCH1FbcFc7ZPL1/img.png?width=800&amp;amp;height=418&amp;amp;face=0_0_800_418,https://scrap.kakaocdn.net/dn/vybd1/hyX4zxiuJR/kCgoD2Xzm8Ef8DflTERo51/img.png?width=800&amp;amp;height=418&amp;amp;face=0_0_800_418,https://scrap.kakaocdn.net/dn/jpuDm/hyX7UT4uwJ/dLzoSfGrQDBaAjNH5vkUC1/img.png?width=833&amp;amp;height=536&amp;amp;face=0_0_833_536');&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] Python 개발 환경 세팅 : Poetry 가상환경&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;1. Poetry 설치 1.1 Poetry란? Poetry는 pipvenv (pip + virtualenv)와 비슷하게, pip와 virtualenv를 동시에 사용할 수 있게 해주는 packaging과 dependency management를 위한 tool이다. Poetry는 pip와 비교하면 쉽게 이해할 수&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;takeheed.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;scrapy pipeline: &lt;a href=&quot;https://docs.scrapy.org/en/latest/topics/item-pipeline.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.scrapy.org/en/latest/topics/item-pipeline.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Crawling</category>
      <category>MongoDB</category>
      <category>poetry</category>
      <category>Python</category>
      <category>scrapy</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1150</guid>
      <comments>https://jinho-study.tistory.com/1150#entry1150comment</comments>
      <pubDate>Tue, 28 Jan 2025 20:38:15 +0900</pubDate>
    </item>
    <item>
      <title>근본없이 공부하는 sql</title>
      <link>https://jinho-study.tistory.com/1146</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;필드 선택 : SELECT&lt;/p&gt;
&lt;pre id=&quot;code_1710066599510&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT ANIMAL_ID,&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블 선택 : FROM&lt;/p&gt;
&lt;pre id=&quot;code_1710066590691&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FROM ANIMAL_INS&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필드 조건 지정 : WHERE&lt;/p&gt;
&lt;pre id=&quot;code_1710066568014&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;WHERE INTAKE_CONDITION = &quot;Sick&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필드 조건 추가 지정 : AND, OR, NOT&lt;/p&gt;
&lt;pre id=&quot;code_1710068132801&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;WHERE INTAKE_CONDITION = &quot;Sick&quot; AND NAME IS NOT NULL&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필드 선택하여 정렬 : ORDER BY ~ ASC&lt;br /&gt;ASC는 생략 가능&lt;/p&gt;
&lt;pre id=&quot;code_1710066577024&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ORDER BY id&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필드 선택하여 역정렬 : ORDER BY ~ DESC&lt;/p&gt;
&lt;pre id=&quot;code_1710067065021&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ORDER BY ANIMAL_ID DESC&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개수로 변환 : COUNT&lt;br /&gt;중복 제거 : DISTINCT&lt;br /&gt;조회한 필드 이름 변환 : AS&lt;/p&gt;
&lt;pre id=&quot;code_1710066677875&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT COUNT(DISTINCT NAME) AS count&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개수 제한 : LIMIT&lt;/p&gt;
&lt;pre id=&quot;code_1710066740725&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;LIMIT 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NULL 인지 확인 : IS, IS NOT&lt;/p&gt;
&lt;pre id=&quot;code_1710066916940&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;WHERE AGE IS NULL&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대, 최소 값 : MAX, MIN&lt;/p&gt;
&lt;pre id=&quot;code_1710067540493&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;MAX(PRICE) AS MAX_PRICE
MIN(PRICE) AS MIN_PRICE&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필드 지정하여 따로 처리하기 : GROUP BY, HAVING&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1710067853250&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT NAME, COUNT(NAME) AS COUNT
FROM ANIMAL_INS
WHERE NAME IS NOT NULL
GROUP BY NAME
HAVING COUNT(NAME) &amp;gt; 1
ORDER BY NAME&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포함된 글자 찾기(includes) : LIKE&lt;br /&gt;% =&amp;gt; 모든 경우, _ =&amp;gt; 하나는 존재함&amp;nbsp;&lt;br /&gt;el이 포함된 경우 =&amp;gt; LIKE &quot;%el%&quot;&lt;br /&gt;el로 시작하는 경우 -=&amp;gt; LIKE &quot;el%&quot;&lt;br /&gt;el로 끝나는 경우 -=&amp;gt; LIKE &quot;%el&quot;&lt;br /&gt;el뒤에 한 글자만 오는 경우 =&amp;gt; LIKE &quot;%el_&quot;&lt;/p&gt;
&lt;pre id=&quot;code_1710239570695&quot; class=&quot;pgsql&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;LOWER(NAME) LIKE &quot;%el%&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간 포멧(format) 변경 : DATE_FORMAT&lt;br /&gt;Y =&amp;gt; 2017, y =&amp;gt; 17&lt;br /&gt;M =&amp;gt; March, m =&amp;gt; 03&lt;br /&gt;D =&amp;gt; 1st, d =&amp;gt; 01&lt;/p&gt;
&lt;pre id=&quot;code_1710239570697&quot; class=&quot;perl&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;DATE_FORMAT(HIRE_YMD, &quot;%Y-%m-%d&quot;) AS HIRE_YMD&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조회 결과가 null인 경우 디폴트값 채워주기 : IFNULL&lt;/p&gt;
&lt;pre id=&quot;code_1710239570698&quot; class=&quot;stylus&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;IFNULL(FREEZER_YN, &quot;N&quot;) AS FREEZER_YN&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조회 결과를 삼항 연산자처럼 처리하기 : IF&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; IF(조건, 참인 경우의 값, 참이 아닌 경우의 값)&lt;/p&gt;
&lt;pre id=&quot;code_1710239570699&quot; class=&quot;pgsql&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;IF(FREEZER_YN IS NULL, &quot;N&quot;, FREEZER_YN) AS FREEZER_YN&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열 자르기 : LEFT, MID, RIGHT, SUBSTRING &lt;br /&gt;SUBSTRING(필드, 시작 위치(1이 맨 앞), 길이)&lt;/p&gt;
&lt;pre id=&quot;code_1710239570699&quot; class=&quot;bash&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;LEFT(PRODUCT_CODE, 2) AS CATEGORY

SUBSTRING(PRODUCT_CODE, 1, 2)

CONCAT(LEFT(tlno,3), '-', MID(tlno,4,4),'-', RIGHT(tlno,4)) AS 전화번호&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열 합치기: CONCAT&lt;/p&gt;
&lt;pre id=&quot;code_1710824590831&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT USER_ID, NICKNAME, CONCAT(CITY, &quot; &quot;, STREET_ADDRESS1, STREET_ADDRESS2) AS 전체주소, 
FROM USED_GOODS_USER&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열 중간에 삽입: INSERT&lt;/p&gt;
&lt;pre id=&quot;code_1710825752059&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;INSERT(INSERT(U.TLNO, 8, 0, '-'), 4, 0, '-') AS 전화번호&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필드에 여러 조건 처리 : CASE&lt;/p&gt;
&lt;pre id=&quot;code_1710239570699&quot; class=&quot;bash&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;SELECT ORDER_ID, PRODUCT_ID, DATE_FORMAT(OUT_DATE, &quot;%Y-%m-%d&quot;) AS OUT_DATE, 
    CASE 
        WHEN DATE_FORMAT(OUT_DATE, &quot;%m-%d&quot;) &amp;lt;= &quot;05-01&quot;  
            THEN '출고완료'
        WHEN DATE_FORMAT(OUT_DATE, &quot;%m-%d&quot;) &amp;gt; &quot;05-01&quot;  
            THEN '출고대기'
        ELSE '출고미정'
    END AS 출고여부
FROM FOOD_ORDER 
ORDER BY ORDER_ID&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일치하는 단어 여러 개 찾기 : IN&lt;br /&gt;=&amp;gt; OR LIKE를 여러 번 사용한 것과 같은 효과&lt;/p&gt;
&lt;pre id=&quot;code_1710239710423&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;WHERE CAR_TYPE IN (&quot;세단&quot;, &quot;SUV&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정규표현식 : REGEXP&lt;br /&gt;| =&amp;gt; LIKE &quot;%가죽시트%&quot; 를 여러개 한 것과 같은 효과 =&amp;gt; 포함된 단어 여러 개 찾기&lt;/p&gt;
&lt;pre id=&quot;code_1710239882771&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;WHERE NOT AI.SEX_UPON_INTAKE REGEXP('Spayed|Neutered')
    AND AO.SEX_UPON_OUTCOME REGEXP('Spayed|Neutered')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 테이블 함께 조회 : join&lt;br /&gt;inner, left, right =&amp;gt; 교집합,&amp;nbsp; 왼쪽 원만 포함, 오른쪽 원만 포함&lt;/p&gt;
&lt;pre id=&quot;code_1710240847128&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT B.CATEGORY AS CATEGORY, SUM(SALES) AS TOTAL_SALES
FROM BOOK_SALES AS BS
LEFT JOIN BOOK AS B USING(BOOK_ID)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JOIN 문에서 WHERE : ON, USING&lt;br /&gt;ON =&amp;gt; WHERE 처럼 사용&lt;/p&gt;
&lt;pre id=&quot;code_1710240901634&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;LEFT JOIN BOOK AS B ON B.BOOK_ID = BS.BOOK_ID&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;USING =&amp;gt; 괄호로 쉽게 사용&lt;/p&gt;
&lt;pre id=&quot;code_1710244348468&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;LEFT JOIN BOOK AS B USING(BOOK_ID)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반올림 : ROUND&lt;/p&gt;
&lt;pre id=&quot;code_1710300374980&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT ROUND(SUM(DAILY_FEE) / COUNT(DAILY_FEE)) AS AVERAGE_FEE&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내림 : TRUNCATE&lt;br /&gt;group by와 함께 사용하여 가격대 별로 조회가 가능함&lt;/p&gt;
&lt;pre id=&quot;code_1710300422853&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT TRUNCATE(SUM(DAILY_FEE) / COUNT(DAILY_FEE)) AS AVERAGE_FEE

SELECT TRUNCATE(PRICE, -4) AS PRICE_GROUP, COUNT(PRODUCT_CODE) AS PRODUCTS
FROM PRODUCT 
GROUP BY PRICE_GROUP&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;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;추가, 수정, 삭제 관련&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블 추가 : INSERT INTO&lt;/p&gt;
&lt;pre id=&quot;code_1710068242567&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;INSERT INTO table_name (column1, column2, column3, ...)
VALUES (value1, value2, value3, ...);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블 수정 : UPDATE ~ SET&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1710068308978&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;UPDATE table_name
SET column1 = value1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블 삭제 : DELETE&lt;/p&gt;
&lt;pre id=&quot;code_1710068326731&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DELETE FROM table_name&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;DATETIME 요일 차이 : DATEDIFF&lt;/p&gt;
&lt;pre id=&quot;code_1710071313900&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DATEDIFF('2021-12-16', '2020-01-01')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DATETIME에서 year, month, day, hour, month, second 추출&lt;br /&gt;이름 그래도 함수가 존재하여 사용하면 됨&lt;/p&gt;
&lt;pre id=&quot;code_1710071606032&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;WHERE YEAR(APNT_YMD) = &quot;2022&quot; AND MONTH(APNT_YMD) = &quot;05&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간 구간 별로 조회 : GROUP BY, HOUR&lt;/p&gt;
&lt;pre id=&quot;code_1710072112792&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT HOUR(DATETIME) AS HOUR, COUNT(DATETIME) AS COUNT
FROM ANIMAL_OUTS
GROUP BY HOUR(DATETIME)
HAVING HOUR &amp;gt;= 9 AND HOUR &amp;lt;= 19
ORDER BY HOUR&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;서브 쿼리 =&amp;gt; 서브 쿼리가 필요한 경우가 상당히 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EX) 첫번째 쿼리는 정상 동작하지 않는다. group_by는 조회된 내용 중 제일 상단에 있는 것을 조회할 뿐이어서 FAVORITES의 최대값으로 조회를 한다고 해도 매칭되는 REST_ID, REST_NAME이 조회되지는 않는다.&lt;br /&gt;그렇기에 2번째 쿼리 처럼 서브쿼리를 통해 조회를 해야한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1710728067827&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT FOOD_TYPE, REST_ID, REST_NAME, MAX(FAVORITES) AS FAVORITES
FROM REST_INFO 
GROUP BY FOOD_TYPE
ORDER BY FOOD_TYPE DESC&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1710728046274&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT FOOD_TYPE, REST_ID, REST_NAME, FAVORITES AS FAVORITES
FROM REST_INFO 
WHERE (FOOD_TYPE, FAVORITES)
IN (
    SELECT FOOD_TYPE, MAX(FAVORITES) AS FAVORITES
    FROM REST_INFO
    GROUP BY FOOD_TYPE
)
ORDER BY FOOD_TYPE DESC&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>데이터베이스/SQL</category>
      <category>MySQL</category>
      <category>SQL</category>
      <category>근본이없음</category>
      <category>프로그래머스</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1146</guid>
      <comments>https://jinho-study.tistory.com/1146#entry1146comment</comments>
      <pubDate>Sun, 10 Mar 2024 19:27:35 +0900</pubDate>
    </item>
    <item>
      <title>Invalid create() Unique constraint failed on the fields</title>
      <link>https://jinho-study.tistory.com/1145</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;예전에 csv로 저장되어 있던 내용을 데이터베이스로 옮기는 작업을 진행했었다.&lt;br /&gt;여기서 회사 정보가 있는데, 이 회사들의 ID(숫자)는 채용 공고, 이미지 등등 다른 정보들과 관계가 있는 상황이었다.&lt;br /&gt;데이터베이스로 옮기면서 id 바꾸게 되면 다른 정보들도 그에 맞춰서 동기화를 해야 하니 번거롭다는 생각이 들었다.&lt;br /&gt;그렇기에 회사 ID를 autoincrement로 만들고 기존 csv의 회사 ID를 그냥 직접 대입하며 테이블에 추가했다.&lt;br /&gt;아래는 당시 코드이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1132&quot; data-origin-height=&quot;102&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dqOXc7/btsEGh82YqQ/BSkVamaZvgsIjaNfDEsRpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dqOXc7/btsEGh82YqQ/BSkVamaZvgsIjaNfDEsRpk/img.png&quot; data-alt=&quot;스키마&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dqOXc7/btsEGh82YqQ/BSkVamaZvgsIjaNfDEsRpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdqOXc7%2FbtsEGh82YqQ%2FBSkVamaZvgsIjaNfDEsRpk%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;659&quot; height=&quot;59&quot; data-origin-width=&quot;1132&quot; data-origin-height=&quot;102&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;스키마&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;614&quot; data-origin-height=&quot;626&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnUv1u/btsEFjlYate/SrKsdthFAOC3BUZk36JhK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnUv1u/btsEFjlYate/SrKsdthFAOC3BUZk36JhK1/img.png&quot; data-alt=&quot;데이터 삽입&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnUv1u/btsEFjlYate/SrKsdthFAOC3BUZk36JhK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnUv1u%2FbtsEFjlYate%2FSrKsdthFAOC3BUZk36JhK1%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;323&quot; height=&quot;329&quot; data-origin-width=&quot;614&quot; data-origin-height=&quot;626&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;이후에 회사 정보를 추가하면 id는 어떻게 되는 걸까?라는 생각이 먼저 들었다.&lt;br /&gt;그때 처음 든 생각은 autoincrement로 설정했으니 1,2,..., 319, 321과 같은 경우라면 알아서&amp;nbsp;&lt;br /&gt;비어있는 320이나 다음으로 제일 큰 숫자인 322로 채워주지 않을까였다.&lt;br /&gt;하지만 회사를 추가하니 companyId를 2로 삽입하여 중복된 id라고 에러가 나왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;autoincrement의 시퀸스가 자동이 아니라 직접 저장한 값들은 기억을 못 해서 생긴 문제였다.&lt;br /&gt;그렇기에 이런 경우에는 아래 코드처럼  autoincrement 시퀸스를 직접 업데이트해줘야 한다.&lt;br /&gt;아래는 postgresql, prisma 코드이다.&lt;/p&gt;
&lt;pre id=&quot;code_1707402567937&quot; class=&quot;pgsql&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;  await prisma.$executeRaw`SELECT setval(
    pg_get_serial_sequence('&quot;company&quot;', 'company_id'),
    coalesce(max(company_id) + 1, 1),
    false
  ) FROM &quot;company&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://github.com/prisma/prisma/discussions/5256&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/prisma/prisma/discussions/5256&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1707402927722&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;Invalid create() Unique constraint failed on the fields: (&amp;#96;id&amp;#96;) &amp;middot; prisma prisma &amp;middot; Discussion #5256&quot; data-og-description=&quot;Hello I have this model: model Somename { id Int @id @default(autoincrement()) name String } I get this error when trying to create an entry for the model: Invalid prisma.somename.create() Unique c...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/prisma/prisma/discussions/5256&quot; data-og-url=&quot;https://github.com/prisma/prisma/discussions/5256&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://github.com/prisma/prisma/discussions/5256&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/prisma/prisma/discussions/5256&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;Invalid create() Unique constraint failed on the fields: (`id`) &amp;middot; prisma prisma &amp;middot; Discussion #5256&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Hello I have this model: model Somename { id Int @id @default(autoincrement()) name String } I get this error when trying to create an entry for the model: Invalid prisma.somename.create() Unique c...&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;</description>
      <category>에러 일기/DATABASE</category>
      <category>AUTOINCREMENT</category>
      <category>Database</category>
      <category>PostgreSQL</category>
      <category>Prisma</category>
      <category>Unique constraint</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1145</guid>
      <comments>https://jinho-study.tistory.com/1145#entry1145comment</comments>
      <pubDate>Thu, 8 Feb 2024 23:36:54 +0900</pubDate>
    </item>
    <item>
      <title>AWS CodePipeline(CI/CD) Example with CDK</title>
      <link>https://jinho-study.tistory.com/1144</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;AWS를 사용해 보니 내가 어떤 것을 사용하고 있는지 보이지가 않아서 답답할 때가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 서버를 배포하는데도 이런데, 훨씬 복잡한 구조라면 어떻게 기억을 할까...라는 생각이 들 때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름만 알고 있던 Terraform이 생각났고, 이런 기술들을 Iac라고 한다는 것을 알게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;aws에서는 CloudFormation와 CDK가 있는데 오늘은 CDK를 사용해서 간단하게 CI/CD를 구현해보려고 합니다.&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;그전에 우선 CDK 같은 기술들이 나오기까지의 과정을 살펴봅시다.&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 관리 콘솔로 리소스 만들기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS 콘솔과 같이 수동으로 한 땀 한 땀 작업하여 인프라를 구성하는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 모두 이렇게 하겠지만 정말 많은 수의 인프라를 구축해야 한다면 거의 불가능한 방식이 아닐까라는 생각이 듭니다...&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 코드 형태의 명령형 인프라&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS CLI, SDK 등을 이용하여 스크립트를 작성하여 운영 및 관리를 하는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 아직 리소스 관리와 구성은 수동으로 이루어지기에 오류 발생 가능성이 높고 반복적인 작업에 시간이 많이 소요됩니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 코드로서의 선언적 인프라 -&amp;gt; IaC(Infrastructure as Code)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS CloudFormation, Terraform를 이용하여 인프라스트럭처를 코드로 정의하고 관리하여, 리소스를 프로그래밍 방식으로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성 및 업데이트하는 방식입니다. 하지만 서비스 별로 JSON, YAML 템플릿을 구성하는 방법과, 옵션에 관한 러닝 커브가 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. AWS CDK(Cloud Development Kit)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CDK는 주요 aws 서비스를 명령어로 정의하는 높은 수준의 객체지향 추상화를 지원합니다. 제가 이해한 바로는 CDK는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CloudFormation을 좀 더 고도화한 기술이고, CDK로 정의한 인프라는 CloudFormation 템플릿으로 변환하여 배포할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Typescript, Python, Java와 같은 많이 사용되는 프로그래밍 언어를 지원하기에 루프 및 조건문 같은 코드 논리를 자연스럽게&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;h3 data-ke-size=&quot;size23&quot;&gt;CDK를 사용해 CI/CD 파이프라인 구현&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 github token을 생성해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 아래 경로로 들어가 Generate new token -&amp;gt; Generate new token(classic)을 선택합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;528&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LQS0r/btss71XPEtV/SJJFMMiTjoiRfxbWqDd7xK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LQS0r/btss71XPEtV/SJJFMMiTjoiRfxbWqDd7xK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LQS0r/btss71XPEtV/SJJFMMiTjoiRfxbWqDd7xK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLQS0r%2Fbtss71XPEtV%2FSJJFMMiTjoiRfxbWqDd7xK%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;596&quot; height=&quot;194&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;528&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/settings/tokens&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/settings/tokens&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1693836953536&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: Let&amp;rsquo;s build from here&quot; data-og-description=&quot;GitHub is where over 100 million developers shape the future of software, together. Contribute to the open source community, manage your Git repositories, review code like a pro, track bugs and fea...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/settings/tokens&quot; data-og-url=&quot;https://github.com&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/NUHJo/hyTPulCgwk/M7xycmX9BYlXTyWlLzNFpk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://github.com/settings/tokens&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/settings/tokens&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/NUHJo/hyTPulCgwk/M7xycmX9BYlXTyWlLzNFpk/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;GitHub: Let&amp;rsquo;s build from here&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;GitHub is where over 100 million developers shape the future of software, together. Contribute to the open source community, manage your Git repositories, review code like a pro, track bugs and fea...&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;2. Note에 원하는 이름을 넣어주고, repo와 admin:repo_hook 권한을 체크해 줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1164&quot; data-origin-height=&quot;730&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cVASpd/btss71jdgyW/BDjHofSZpP1NOO2U62YkW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cVASpd/btss71jdgyW/BDjHofSZpP1NOO2U62YkW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cVASpd/btss71jdgyW/BDjHofSZpP1NOO2U62YkW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcVASpd%2Fbtss71jdgyW%2FBDjHofSZpP1NOO2U62YkW0%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;475&quot; height=&quot;298&quot; data-origin-width=&quot;1164&quot; data-origin-height=&quot;730&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;978&quot; data-origin-height=&quot;166&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q7y9f/btss72WIsYH/ccNEfHGqfiAItFIpBozdx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q7y9f/btss72WIsYH/ccNEfHGqfiAItFIpBozdx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q7y9f/btss72WIsYH/ccNEfHGqfiAItFIpBozdx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq7y9f%2Fbtss72WIsYH%2FccNEfHGqfiAItFIpBozdx1%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;471&quot; height=&quot;80&quot; data-origin-width=&quot;978&quot; data-origin-height=&quot;166&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;3. 생성된 토큰 값을 복사하고 AWS Secrets Manager에서 새 암호를 저장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Step 1) 다른 유형의 보안 암호 -&amp;gt; 일반 텍스트 -&amp;gt; 복사한 토큰 값을 넣어주고 다음을 누릅니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2200&quot; data-origin-height=&quot;1072&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blsglM/btss87XNlrR/KVsxQryJdDcj4EOFDcVNNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blsglM/btss87XNlrR/KVsxQryJdDcj4EOFDcVNNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blsglM/btss87XNlrR/KVsxQryJdDcj4EOFDcVNNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblsglM%2Fbtss87XNlrR%2FKVsxQryJdDcj4EOFDcVNNK%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;556&quot; height=&quot;271&quot; data-origin-width=&quot;2200&quot; data-origin-height=&quot;1072&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Step 2) 암호 이름을 github-token으로 넣어줍니다. 이 이름이 아니면 추가로 설정을 해야 하는 것으로 보입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1358&quot; data-origin-height=&quot;684&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4u5AC/btss91pkU7g/rnbaK0AEErVZNZrATJjKe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4u5AC/btss91pkU7g/rnbaK0AEErVZNZrATJjKe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4u5AC/btss91pkU7g/rnbaK0AEErVZNZrATJjKe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4u5AC%2Fbtss91pkU7g%2FrnbaK0AEErVZNZrATJjKe0%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;554&quot; height=&quot;279&quot; data-origin-width=&quot;1358&quot; data-origin-height=&quot;684&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Step 3, 4) 넘어가줍니다.&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_1693850090217&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo npm install -g typescript aws-cdk-lib
alias cdk=&quot;npx aws-cdk&quot;

mkdir cdk-cicd
cd cdk-cicd
cdk init app --language typescript&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;만약 위 과정에서 에러가 나왔다면 aws와 연결이 안 되어있기 때문일 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 명령어로 aws cli를 설치한 후 aws configure 명령어를 통해 연동을 해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1693850219176&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl &quot;https://awscli.amazonaws.com/AWSCLIV2.pkg&quot; -o &quot;AWSCLIV2.pkg&quot;
sudo installer -pkg ./AWSCLIV2.pkg -target /
aws --version

aws configure&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;IAM 사용자에 있는 access_key(유저 ID), secret access key와 region(ap-northeast-2), output format(json)을&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차례대로 넣어주면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2026&quot; data-origin-height=&quot;762&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pSN76/btss85Ms0yy/daKwmzMqqpjaEZHolqFDsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pSN76/btss85Ms0yy/daKwmzMqqpjaEZHolqFDsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pSN76/btss85Ms0yy/daKwmzMqqpjaEZHolqFDsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpSN76%2Fbtss85Ms0yy%2FdaKwmzMqqpjaEZHolqFDsk%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;546&quot; height=&quot;205&quot; data-origin-width=&quot;2026&quot; data-origin-height=&quot;762&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;그 후 cdk-cicd 폴더에서 아래 명령어들을 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;esbuild는 람다를 실행할 때 필요하기 때문에 설치해 줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1693850982291&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install
npm install esbuild&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;bin: NestJS의 main.ts 같은 느낌입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cdk.out: CloudFormation에 적용될 JSON이 저장되는 공간입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lib: 사실상 메인 폴더이고 여기서 infrastructure를 정의합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;test: jest 테스트용 폴더입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;396&quot; data-origin-height=&quot;790&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6S6HZ/btss85Z1f3U/XC8ZOazM65SUsEiL0dg1JK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6S6HZ/btss85Z1f3U/XC8ZOazM65SUsEiL0dg1JK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6S6HZ/btss85Z1f3U/XC8ZOazM65SUsEiL0dg1JK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6S6HZ%2Fbtss85Z1f3U%2FXC8ZOazM65SUsEiL0dg1JK%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;349&quot; height=&quot;696&quot; data-origin-width=&quot;396&quot; data-origin-height=&quot;790&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;이제 main 브랜치에 push가 되면 CI/CD가 시작되어 간단한 람다 함수를 테스트하도록 코드를 작성해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드에서 보이는 Stage와 Stack은 아래의 의미를 가집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stage: CDK에서 배포 단계를 나타내는 개념입니다. 예를 들어 develop, test, prod 같은 이름으로 지정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stack: CDK에서 인프라 리소스의 논리적 그룹을 나타내는 단위입니다. 스택은 하나 이상의 AWS 리소스를 정의하고,&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;lib/cdk-cicd-stack.ts&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #444654; color: #d1d5db; text-align: start;&quot;&gt;&lt;/span&gt;소스 코드 변경을 감지하고 CI/CD 파이프라인을 실제로 실행시켜 주는 코드입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1693853663725&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import * as cdk from &quot;aws-cdk-lib&quot;;
import {
  CodeBuildStep,
  CodePipeline,
  CodePipelineSource,
  ShellStep,
} from &quot;aws-cdk-lib/pipelines&quot;;
import { Construct } from &quot;constructs&quot;;
import { PipelineStage } from &quot;./PipelineStage&quot;;

export class CdkCicdStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const pipeline = new CodePipeline(this, &quot;MyPipeline&quot;, {
      pipelineName: &quot;MyPipeline&quot;,
      synth: new ShellStep(&quot;Synth&quot;, {
        // input: CodePipelineSource.gitHub('kimjinho1/cdk-cicd-practice', 'cdk-cicd-practice'),
        input: CodePipelineSource.gitHub(&quot;kimjinho1/cdk-cicd-practice&quot;, &quot;main&quot;),
        commands: [&quot;cd cdk-cicd&quot;, &quot;npm ci&quot;, &quot;npx cdk synth&quot;],
        primaryOutputDirectory: &quot;cdk-cicd/cdk.out&quot;,
      }),
    });

    const testStage = pipeline.addStage(
      new PipelineStage(this, &quot;PipelineTestStage&quot;, {
        stageName: &quot;test&quot;,
      })
    );

    testStage.addPre(
      new CodeBuildStep(&quot;unit-tests&quot;, {
        commands: [&quot;cd cdk-cicd&quot;, &quot;npm ci&quot;, &quot;npm test&quot;],
      })
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 input 부분은 본인의 레포지터리 이름과 브랜치 이름을 넣어주셔야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1693853706473&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;input: CodePipelineSource.gitHub(&quot;kimjinho1/cdk-cicd-practice&quot;, &quot;main&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;lib/PipelineStage.ts&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;span style=&quot;background-color: #444654; color: #d1d5db; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CodePipeline 스테이지를 구성하는 코드입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1693853599876&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { Stage, StageProps } from &quot;aws-cdk-lib&quot;;
import { Construct } from &quot;constructs&quot;;
import { LambdaStack } from &quot;./LambdaStack&quot;;

export class PipelineStage extends Stage {
  constructor(scope: Construct, id: string, props: StageProps) {
    super(scope, id, props);

    new LambdaStack(this, &quot;LambdaStack&quot;, {
      stageName: props.stageName,
    });
  }
}&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;lib/LambdaStack.ts&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lambda 함수를 정의하고 생성하는 코드입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1693853629141&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { Stack, StackProps } from &quot;aws-cdk-lib&quot;;
import { Runtime } from &quot;aws-cdk-lib/aws-lambda&quot;;
import { NodejsFunction } from &quot;aws-cdk-lib/aws-lambda-nodejs&quot;;
import { Construct } from &quot;constructs&quot;;
import { join } from &quot;path&quot;;

interface LambdaStackProps extends StackProps {
  stageName?: string;
}

export class LambdaStack extends Stack {
  constructor(scope: Construct, id: string, props: LambdaStackProps) {
    super(scope, id, props);

    new NodejsFunction(this, &quot;hello-lambda&quot;, {
      runtime: Runtime.NODEJS_18_X,
      handler: &quot;handler&quot;,
      entry: join(__dirname, &quot;..&quot;, &quot;lambda&quot;, &quot;hello.ts&quot;),
      environment: {
        STAGE: props.stageName!,
      },
    });
  }
}&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;lambda/hello.ts&lt;/b&gt;&lt;b&gt;&lt;/b&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_1693853784657&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async function handler(event: any, context: any) {
  return {
    statusCode: 200,
    body: &quot;Hello AWS&quot;,
  };
}

export { handler };&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;test/cdk-cicd.test.ts&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;람다 함수를 테스트하는 코드입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1693853831304&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { handler } from &quot;../lambda/hello&quot;;

describe(&quot;Hello describe test suite&quot;, () =&amp;gt; {
  test(&quot;handler should return 200&quot;, async () =&amp;gt; {
    const result = await handler({}, {});
    expect(result.statusCode).toBe(200);
  });
});&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;이제 cdk deploy 명령어를 실행하면 설정한 AWS 서비스들이 CloudFormation으로 자동으로 세팅이 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1693861765122&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cdk deploy&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CodePipeline 내부에서 실행된 결과를 확인할 수 있습니다. 생각보다 시간이 오래 걸립니다. 4분? 정도&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2154&quot; data-origin-height=&quot;596&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0hWxd/btssUI6bR7N/GrkArUNv4BI4vuBjyJrtKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0hWxd/btssUI6bR7N/GrkArUNv4BI4vuBjyJrtKK/img.png&quot; data-alt=&quot;CodePipeline&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0hWxd/btssUI6bR7N/GrkArUNv4BI4vuBjyJrtKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0hWxd%2FbtssUI6bR7N%2FGrkArUNv4BI4vuBjyJrtKK%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;599&quot; height=&quot;166&quot; data-origin-width=&quot;2154&quot; data-origin-height=&quot;596&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CodePipeline&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;538&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sGGgP/btss3E9BQcu/NnK1iHIspBbYTFPGfzTM11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sGGgP/btss3E9BQcu/NnK1iHIspBbYTFPGfzTM11/img.png&quot; data-alt=&quot;unit-tests&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sGGgP/btss3E9BQcu/NnK1iHIspBbYTFPGfzTM11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsGGgP%2Fbtss3E9BQcu%2FNnK1iHIspBbYTFPGfzTM11%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;471&quot; height=&quot;309&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;538&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;unit-tests&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;친숙한 프로그래밍 언어로 인프라를 구축할 수 있다는 점이 개발자 친화적이어서 아주 유망한 기술인 것 같습니다만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그전에&lt;span&gt;&amp;nbsp;&lt;/span&gt;aws에 대해 조금 더 친숙해지고 나서&lt;span&gt;&amp;nbsp;&lt;/span&gt;활용해야겠다는&lt;span&gt;&amp;nbsp;&lt;/span&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;그리고 의아한 점은 CodePipeline이 생각보다 엄청 느리다는 점입니다. 이렇게 간단한 것을 실행하는데도 5분 정도가 걸립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 진행하고 있는 프로젝트에서 CodePipeline을 사용할 생각이었는데, github actions과 비교해봐야 할 것 같습니다.&lt;/p&gt;</description>
      <category>클라우드/AWS</category>
      <category>aws</category>
      <category>AWS CDK</category>
      <category>AWS CLI</category>
      <category>CI/CD</category>
      <category>codepipeline</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1144</guid>
      <comments>https://jinho-study.tistory.com/1144#entry1144comment</comments>
      <pubDate>Tue, 5 Sep 2023 07:56:22 +0900</pubDate>
    </item>
    <item>
      <title>웹의 동작 방식</title>
      <link>https://jinho-study.tistory.com/1140</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;클라이언트와 서버&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;클라이언트: 웹 사용자의 인터넷이 연결된 장치들과 이런 장치들에서 사용 가능한 크롬 같은 웹 브라우저&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;서버: &amp;nbsp;웹페이지, 사이트, 앱 등을 저장하는 컴퓨터&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1192&quot; data-origin-height=&quot;394&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPLXBo/btrVgj4TwBR/5rV1WA80VHmrTokFGFzsN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPLXBo/btrVgj4TwBR/5rV1WA80VHmrTokFGFzsN0/img.png&quot; data-alt=&quot;클라이언트와 서버&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPLXBo/btrVgj4TwBR/5rV1WA80VHmrTokFGFzsN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPLXBo%2FbtrVgj4TwBR%2F5rV1WA80VHmrTokFGFzsN0%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;506&quot; height=&quot;167&quot; data-origin-width=&quot;1192&quot; data-origin-height=&quot;394&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;클라이언트와 서버&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;웹 -&amp;gt; 도로&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;클라이언트 -&amp;gt; 우리의 집&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;서버 -&amp;gt; 상점&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;라고 생각하고 생각하고 아래 글을 이해하면 쉽습니다.&lt;/b&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;웹에서 실제로 무슨 일이 일어날까?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;우리가 브라우저에 웹 주소를 입력하는 것은 우리가 상점에 걸어가는 것과 비슷합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. 브라우저는 DNS 서버로 가서 웹사이트 서버의 실제 ip 주소를 찾습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;EX) 우리가 네이버 지도로 상점의 주소를 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2. 브라우저가 서버에게 웹사이트의 사본을 보내달라는 HTTP 요청 메시지를 서버로 전송합니다.&amp;nbsp;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;메시지, 클라이언트와 서버 사이에 전송되는 모든 데이터는 TCP/IP 연결을 통해서 전송됩니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;EX) 상점에 가서 상품을 주문한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3. 이 메시지를 받은 서버는 클라이언트의 요청을 승인하고 &quot;200 OK&quot; 메시지를 클라이언트에게 전송합니다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&quot;200 OK&quot;는 넌 웹사이트를 볼 자격이 있어! 라는 의미입니다. 그 후 서버는 웹사이트의 파일들을 데이터 패킷이라 불리는&lt;/b&gt;&lt;br /&gt;&lt;b&gt;작은 덩어리들로 브라우저에 전송을 합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;EX) 상점에서 우리가 주문한 상품을 전달받고, 우리는 그 상품을 집으로 가져간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;4. 브라우저는 이 데이터 패킷들을 완전환 웹 사이트로 조립하고 우리에게 보여줍니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;EX) 상품이 우리 집 문 앞에 도착한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;인터넷 연결&lt;/b&gt;: 웹에서 데이터를 보내고 받게 해준다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;EX) 집과 상점 사이의 거리&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;TCP/IP&lt;/b&gt;: Transmission Control Protocol(전송 제어 규약) / Internet Protocol(인터넷 규약)&lt;br /&gt;-&amp;gt; 데이터가 어떻게 웹을 돌아다닐지 정의하는 통신 규약&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;EX) 주문을 하고, 상점에 가고, 우리가 상품을 살 수 있게 해주는 운송 장치(우리의 다리 또는 자동차 등등)와 같습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;DNS&lt;/b&gt;: Domain Name System Servers(도메인 이름 시스템 서버) -&amp;gt; 웹사이트를 위한 주소록&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;우리가 63.245.217.105 같은 ip 주소를 직접 기억하는 것은 쉽지가 않기에 DNS가 발명되었습니다.&amp;nbsp;DNS는 우리가 &lt;br /&gt;브라우저에 입력하는 웹주소&amp;nbsp;예를 들어 naver.com 같은 웹사이트를 실제 ip 주소에 맞춰주는 특별한 서버입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;HTTP&lt;/b&gt;: Hypertext Transfer Protocol(하이퍼텍스트 전송 규약)은 클라이언트와 서버가 서로 통신할 수 있게 하기 위한 언어를 정의하는 규약? 약속 같은 친구입니다.&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;EX) 우리가 상품을 주문하기 위해 사용하는 언어와 같습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;packet:&amp;nbsp;&lt;/span&gt;&lt;/b&gt;패킷&lt;b&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;기본적으로 웹에서 데이터가 전송될 때, 아주 작은 덩어리들로 전송됩니다. 그렇기에 다양한 웹 사용자들이 동시에 같은&lt;br /&gt;웹 사이트를 다운로드할 수 있습니다. 만약 데이터가 하나의 큰 덩어리로 전송된다면, 한 번에 한 명의 사용자만 다운로드가&lt;br /&gt;가능해져서 매우 비효율적이게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Reference&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Learn/Getting_started_with_the_web/How_the_Web_works#패킷_설명&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.mozilla.org/ko/docs/Learn/Getting_started_with_the_web/How_the_Web_works#패킷_설명&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1672726132632&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;웹의 동작 방식 - Web 개발 학습하기 | MDN&quot; data-og-description=&quot;*'웹의 동작 방식'*은 여러분의 컴퓨터나 폰의 웹 브라우저 안에서 웹페이지를 볼 때 무슨 일이 발생하는지에 대한 간소화된 개념을 제공할 것입니다.&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/ko/docs/Learn/Getting_started_with_the_web/How_the_Web_works#패킷_설명&quot; data-og-url=&quot;https://developer.mozilla.org/ko/docs/Learn/Getting_started_with_the_web/How_the_Web_works&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bbhI0H/hyQ84CTaJC/NgynFvM0ZXiFhtytRMUgV1/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/VX7Vg/hyQ8WSmF6M/dKPQWjDKlfnfrG7kDSHQ5K/img.jpg?width=640&amp;amp;height=427&amp;amp;face=0_0_640_427,https://scrap.kakaocdn.net/dn/dzPvfg/hyQ86gni8e/hIpzL8GaKI8yFiOgJuYk9K/img.jpg?width=792&amp;amp;height=290&amp;amp;face=0_0_792_290&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Learn/Getting_started_with_the_web/How_the_Web_works#패킷_설명&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/ko/docs/Learn/Getting_started_with_the_web/How_the_Web_works#패킷_설명&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bbhI0H/hyQ84CTaJC/NgynFvM0ZXiFhtytRMUgV1/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/VX7Vg/hyQ8WSmF6M/dKPQWjDKlfnfrG7kDSHQ5K/img.jpg?width=640&amp;amp;height=427&amp;amp;face=0_0_640_427,https://scrap.kakaocdn.net/dn/dzPvfg/hyQ86gni8e/hIpzL8GaKI8yFiOgJuYk9K/img.jpg?width=792&amp;amp;height=290&amp;amp;face=0_0_792_290');&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;웹의 동작 방식 - Web 개발 학습하기 | MDN&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;developer.mozilla.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>42 SEOUL/webserv</category>
      <category>42</category>
      <category>client</category>
      <category>DNS</category>
      <category>Packet</category>
      <category>server</category>
      <category>TCP/IP</category>
      <category>web</category>
      <category>webserv</category>
      <category>웹</category>
      <category>웹의 동작 방식</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1140</guid>
      <comments>https://jinho-study.tistory.com/1140#entry1140comment</comments>
      <pubDate>Tue, 3 Jan 2023 15:14:40 +0900</pubDate>
    </item>
    <item>
      <title>42 서울 pipex Mandatory</title>
      <link>https://jinho-study.tistory.com/1139</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;허용 함수를 다 안다는 전제로 글을 작성했습니다. 모르면 아래 글 보기.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;a href=&quot;https://jinho-study.tistory.com/1138&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://jinho-study.tistory.com/1138&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1671451101486&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;42 서울 pipex 정리 2 (access, dup2, execve, perror, strerror)&quot; data-og-description=&quot;이어서 나머지 함수들을 알아보자. https://jinho-study.tistory.com/1137 pipex 정리 1 ( 프로세스, fork, pipe, wait, waitpid) 개요 pipex는 우리가 만들 pipex 프로그램을 위와 같은 방식으로 돌렸을 때, 아래 명령어&quot; data-og-host=&quot;jinho-study.tistory.com&quot; data-og-source-url=&quot;https://jinho-study.tistory.com/1138&quot; data-og-url=&quot;https://jinho-study.tistory.com/1138&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/tRX7u/hyQYz90BbE/XlB6n6aNbSEOKjxjEuvVJ0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/2Gy13/hyQWGXeTvf/JPRsgotDeEbKiSr99MNQF0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://jinho-study.tistory.com/1138&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jinho-study.tistory.com/1138&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/tRX7u/hyQYz90BbE/XlB6n6aNbSEOKjxjEuvVJ0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/2Gy13/hyQWGXeTvf/JPRsgotDeEbKiSr99MNQF0/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;42 서울 pipex 정리 2 (access, dup2, execve, perror, strerror)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이어서 나머지 함수들을 알아보자. https://jinho-study.tistory.com/1137 pipex 정리 1 ( 프로세스, fork, pipe, wait, waitpid) 개요 pipex는 우리가 만들 pipex 프로그램을 위와 같은 방식으로 돌렸을 때, 아래 명령어&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;jinho-study.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;전체적으로 봤을 때 로직은 아래와 같이 생각보다 간단하다. 실제로 파일 3개면 구현 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1671454101576&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int	main(int ac, char **av, char **envp)
{
	int		i;

	if (ac != 5)
		에러 출력 후 종료
	i = -1;
	while (++i &amp;lt; 2)
	{
		if (i == 0)
       		명령어 파싱 -&amp;gt; 첫 번째 프로세스 실행(infile을 stdin으로 받고 명령어 실행)
		else if (i == 1)
			명령어 파싱 -&amp;gt; 두 번째 프로세스 실행(첫 번째 프로세스 실행 결과를 stdin으로 
            받고 명령어 실행 결과를 outfile에 저장)
	}
	포크된 프로세스 만큼 wait를 해준다. 멘덴토리의 경우에는 2번
	i = -1;
	while (++i &amp;lt; 2)
		wait(NULL);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;다만 여기서 알고 넘어가야 될게 2가지가 있다. minishell 내용 같긴 한데 pipex 하면서 알고 간다면&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;minishell 할 때 아주 큰 도움이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1 ) 쉘에서 명령어들은 병렬로 돌아간다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;예를 들어 sleep 3 | sleep 3은 6초를 기다릴 것 같지만, 실제로는 병렬로 돌기에 3초를 기다린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;병렬이더라도 cat | cat 같은 경우에는 애초에 앞에서 입력이 들어와야 넘어가기 때문에 프로그램이 끝나지 않는다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2) 파이프 개수만큼 fork 되는 것이 아니라 파이프 개수 + 1 만큼 fork 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;실행되는 명령어가 2개(ls | cat)라서 pipe가 한 개라고 fork를 한번 하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;실제로 bash에서 그냥 exit은 당연히 꺼지지만 exit | exit 은 bash가 꺼지지 않는다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;156&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjg9bs/btrT4G8a8o6/2MI5kT2rxx2MDtSCEg3ADk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjg9bs/btrT4G8a8o6/2MI5kT2rxx2MDtSCEg3ADk/img.png&quot; data-alt=&quot;bash 꺼짐&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjg9bs/btrT4G8a8o6/2MI5kT2rxx2MDtSCEg3ADk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbjg9bs%2FbtrT4G8a8o6%2F2MI5kT2rxx2MDtSCEg3ADk%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;294&quot; height=&quot;92&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;156&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;bash 꺼짐&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;528&quot; data-origin-height=&quot;120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LfwS3/btrT4G8a7Zs/BmCkKrCDCxAKtJO3lcSFlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LfwS3/btrT4G8a7Zs/BmCkKrCDCxAKtJO3lcSFlk/img.png&quot; data-alt=&quot;bash 안 꺼짐&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LfwS3/btrT4G8a7Zs/BmCkKrCDCxAKtJO3lcSFlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLfwS3%2FbtrT4G8a7Zs%2FBmCkKrCDCxAKtJO3lcSFlk%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;295&quot; height=&quot;67&quot; data-origin-width=&quot;528&quot; data-origin-height=&quot;120&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;bash 안 꺼짐&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그냥 exit은 fork가 안되고 바로 실행돼서 bash가 꺼지지만, exit | exit의 경우에는 fork가 2번 실행되어서&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;프로세스가 3개라 bash가 꺼지지 않는다. 이러한 구조를 가지는 것은 자식 프로세스에서 명령어를 수행하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;부모(main) 프로세스에서 모든 자식 프로세스를 기다리게 하기 위함이 아닐까?라고 작성자는 이해했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;각 프로세스 별로 구현해야 되는 것들을 순서대로 쭉 나열해 보면&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1) 첫 번째 프로세스&amp;nbsp;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1.1) infile 읽고 pipe 세팅&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;자식 프로레스)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;infile을 stdin으로 dup2,&amp;nbsp;fd[1]을 stdout으로 dup2 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;여기서 fd[0]만 사용 안 하니까 fd[0]만 close 하는 경우가 많은데, 둘 다 close 해줘야 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;fd[0]만 close 하면 yes | head -1 같은 테스트의 경우 프로그램이 끝나지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;부모 프로레스)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;fd[0]을 stdin으로 dup2 해준다. 여기서 fd[0]은 자식 프로세스에서의 명령어 실행 결과가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;결국 첫 번째 명령어의 실행 결과가 stdin으로 들어와 있는 상태라고 생각하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;여기서도 마찬가지로 fd[0], fd[1] 둘 다 close 해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1671458876953&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void	dup_child_1(char **av, int *fd)
{
	int	infile;

	infile = open(av[1], O_RDONLY);
	if (infile == -1)
		perror_exit(&quot;infile error&quot;);
	if (dup2(infile, STDIN_FILENO) == -1)
		perror_exit(&quot;dup2 error&quot;);
	if (dup2(fd[1], STDOUT_FILENO) == -1)
		perror_exit(&quot;dup2 error&quot;);
	close(fd[0]);
	close(fd[1]);
}

static void	child_process_1(char **av, char **envp)
{
	int		fd[2];
	pid_t	pid;

	if (pipe(fd) == -1)
		perror_exit(&quot;pipe error&quot;);
	pid = fork();
	if (pid == -1)
		perror_exit(&quot;fork error&quot;);
	if (pid == 0)
	{
		dup_child_1(av, fd);
		execute(av[2], envp);
	}
	if (dup2(fd[0], STDIN_FILENO) == -1)
		perror_exit(&quot;dup2 error&quot;);
	close(fd[0]);
	close(fd[1]);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1.2) 명령어 파싱 후 실행&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1774&quot; data-origin-height=&quot;1548&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAlSWm/btrT4y3x8hc/vzFjm0by3byQD8KWtMnQKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAlSWm/btrT4y3x8hc/vzFjm0by3byQD8KWtMnQKK/img.png&quot; data-alt=&quot;env 실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAlSWm/btrT4y3x8hc/vzFjm0by3byQD8KWtMnQKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAlSWm%2FbtrT4y3x8hc%2FvzFjm0by3byQD8KWtMnQKK%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;480&quot; height=&quot;1548&quot; data-origin-width=&quot;1774&quot; data-origin-height=&quot;1548&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;env 실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;메인에서 인자로 받는 envp 안에는 이런 식으로 환경변수들이 저장되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;여기서 우리가 필요한 것은&amp;nbsp;PATH=/Users/jinhokim/.brew/bin:/Users/jinhokim/.brew/bin:/usr/local/bin:/usr/bin:/&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;bin:/usr/sbin:/sbin:/usr/local/munki이다. PATH= 뒤의 문자열을 : 기준으로 split 하게 되면&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;[&quot;/bin&quot;, &quot;/usr/bin&quot;, ...] 같은 결과가 되는데 총 6개의 경로가 나오게 된다. 이 경로들 안에 명령어 파일들이 들어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;예를 들어 ls는 /bin/ 안에 들어있는데 그렇기에 쉘에서 /bin/ls를 실행하면 ls가 실행된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;894&quot; data-origin-height=&quot;406&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/W1s4H/btrT4HF1b29/c9f7mbBDUT2h8wOjyFjZK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/W1s4H/btrT4HF1b29/c9f7mbBDUT2h8wOjyFjZK0/img.png&quot; data-alt=&quot;/bin/ls&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/W1s4H/btrT4HF1b29/c9f7mbBDUT2h8wOjyFjZK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FW1s4H%2FbtrT4HF1b29%2Fc9f7mbBDUT2h8wOjyFjZK0%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;374&quot; height=&quot;170&quot; data-origin-width=&quot;894&quot; data-origin-height=&quot;406&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;/bin/ls&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이제 저 6개의 경로와 첫 번째 명령어를 ft_strjoin한 결과를 access 함수를 통해 실행 가능한 파일인지 확인하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;없는 명령어의 경우에는 에러 출력, 있는 명령어의 경우에는 그냥 execve를 실행해주면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1671458418751&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// path -&amp;gt; [&quot;/bin&quot;, &quot;/usr/bin&quot;, ...]
while (path[i])
	{
	// joined_cmd = &quot;/ls&quot;
	// ret_cmd = &quot;/bin/ls&quot;
        ret_cmd = ft_strjoin(path[i++], joined_cmd);
		if (access(ret_cmd, X_OK) != -1)
		{
			free(joined_cmd);
			return (ret_cmd);
		}
		free(ret_cmd);
	}
	free(joined_cmd);
	return (NULL);
}&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;2) 두 번째 프로세스&amp;nbsp;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.1) outfile에 실행 결과 저장&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;자식 프로레스)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;outfile을 생성하고 stdout으로 dup2&amp;nbsp;해준다. 그러고 위에 설명했던 명령어 파싱 후 실행을 반복하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;stdout이 outfile에 연결되어 있기 때문에 이 프로세스에서 표준 출력되는 내용들은 모두 outfile 안에&amp;nbsp;저장된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;부모 프로레스)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;아무것도 할 필요가 없다.&lt;/p&gt;
&lt;pre id=&quot;code_1671459593073&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void	child_process_2(char **av, char **envp)
{
	pid_t	pid;
	int		outfile;

	pid = fork();
	if (pid == -1)
		perror_exit(&quot;fork error&quot;);
	if (pid == 0)
	{
		outfile = open(av[4], O_RDWR | O_CREAT | O_TRUNC, 0644);
		if (outfile == -1)
			perror_exit(&quot;outfile error&quot;);
		if (dup2(outfile, STDOUT_FILENO) == -1)
			perror_exit(&quot;dup2 error&quot;);
		execute(av[3], envp);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>42 SEOUL/pipex</category>
      <category>42 pipex</category>
      <category>42 Seoul</category>
      <category>42 서울</category>
      <category>42 서울 pipex</category>
      <category>C</category>
      <category>Pipe</category>
      <category>pipex</category>
      <category>pipex mandatory</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1139</guid>
      <comments>https://jinho-study.tistory.com/1139#entry1139comment</comments>
      <pubDate>Mon, 19 Dec 2022 20:53:26 +0900</pubDate>
    </item>
    <item>
      <title>42 서울 pipex 정리 2 (access, dup2, execve, perror, strerror)</title>
      <link>https://jinho-study.tistory.com/1138</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://jinho-study.tistory.com/1137&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://jinho-study.tistory.com/1137&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1662725516774&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;pipex 정리 1 ( 프로세스, fork,  pipe, wait, waitpid)&quot; data-og-description=&quot;개요 pipex는 우리가 만들 pipex 프로그램을 위와 같은 방식으로 돌렸을 때, 아래 명령어와 똑같이 동작하도록 구현해야 되는 과제이다. 즉 infile 파일을 읽고 명령어 2개를 실행한 결과를 outfile에 &quot; data-og-host=&quot;jinho-study.tistory.com&quot; data-og-source-url=&quot;https://jinho-study.tistory.com/1137&quot; data-og-url=&quot;https://jinho-study.tistory.com/1137&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ciWhps/hyPJSQ2ZL4/pjMsXhREK2t0MejtJKeemk/img.png?width=574&amp;amp;height=45&amp;amp;face=0_0_574_45,https://scrap.kakaocdn.net/dn/vqV86/hyPJ02BNGE/SKH0jIzEISrALC32itV8ek/img.png?width=574&amp;amp;height=45&amp;amp;face=0_0_574_45,https://scrap.kakaocdn.net/dn/AmJn9/hyPIAEuoVz/nkHUGWkd5lzWKx2FehxyHK/img.png?width=503&amp;amp;height=691&amp;amp;face=0_0_503_691&quot;&gt;&lt;a href=&quot;https://jinho-study.tistory.com/1137&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jinho-study.tistory.com/1137&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ciWhps/hyPJSQ2ZL4/pjMsXhREK2t0MejtJKeemk/img.png?width=574&amp;amp;height=45&amp;amp;face=0_0_574_45,https://scrap.kakaocdn.net/dn/vqV86/hyPJ02BNGE/SKH0jIzEISrALC32itV8ek/img.png?width=574&amp;amp;height=45&amp;amp;face=0_0_574_45,https://scrap.kakaocdn.net/dn/AmJn9/hyPIAEuoVz/nkHUGWkd5lzWKx2FehxyHK/img.png?width=503&amp;amp;height=691&amp;amp;face=0_0_503_691');&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;pipex 정리 1 ( 프로세스, fork, pipe, wait, waitpid)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;개요 pipex는 우리가 만들 pipex 프로그램을 위와 같은 방식으로 돌렸을 때, 아래 명령어와 똑같이 동작하도록 구현해야 되는 과제이다. 즉 infile 파일을 읽고 명령어 2개를 실행한 결과를 outfile에&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;jinho-study.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;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;함수 정리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. access&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662725602307&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;unistd.h&amp;gt;
int access(const char *pathname, int mode);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;access 함수는 파일의 권한을 확인하고 성공하면 0 실패하면 -1을 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mode에 따라 확인하는 내용이 달라지는데 mode는 아래 4가지가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;R_OK(파일 존재, 읽기 권한), W_OK(파일 존재, 쓰기 권한), X_OK(파일 존재, 실행 권한), R_OK(파일 존재)&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;예시 1) 파일이&amp;nbsp;있으면&amp;nbsp;Success,&amp;nbsp;없으면&amp;nbsp;Fail이&amp;nbsp;출력된다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662726228533&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

int main(void)
{
	char *pathname = &quot;./test.txt&quot;;
	if ( access(pathname, R_OK | W_OK) == 0)
		printf(&quot;읽고 쓰기 가능\n&quot;);
	else
		printf(&quot;권한이 없거나 파일이 없음&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;size18&quot;&gt;&lt;b&gt;2. dup2&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662726415339&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;unistd.h&amp;gt;
int dup2(int fd1, int fd2)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dup2 함수는 디스크립터를 변경해주는 함수이다. fd2가 fd1을 가리키게 하고&amp;nbsp;기존 fd2는 close 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 함수는 pipex 과제 중 만든 함수인데, 정상적으로 동작할 시 std_in을 표준 입력으로 std_out을 표준 출력으로 바꿔준다.&lt;/p&gt;
&lt;pre id=&quot;code_1662726710522&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static void	pipe_control(int close_fd, int std_in, int std_out, t_info *info)
{
	close(close_fd);
	if (dup2(std_in, STDIN_FILENO) == -1)
		invalid_exit_opt(info, &quot;STDIN dup2 error&quot;, 1);
	if (dup2(std_out, STDOUT_FILENO) == -1)
		invalid_exit_opt(info, &quot;STDOUT dup2 error&quot;, 1);
	close(std_in);
	close(std_out);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pipex 하면서 보기는 쉽지 않은 경우이긴 하지만 stdin에 dup2를 쓰고 close를 한 경우 stdin이 죽어서 계속 입력으로&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EOF를 받게 된다. minishell 정리할 때도 작성하겠지만 아래와 같은 식으로 stdin, stdout을 dup 함수를 통해 기억해두고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막에 dup2를 사용해 stdin, stdout을 되돌려 줄 수 있다. 작성자는 이 에러 때문에 minsihell 할 때 10시간을 버렸다.&lt;/p&gt;
&lt;pre id=&quot;code_1671448744626&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int	main(int ac, char **av, char **envp)
{
	int		stdin_dup;
	int		stdout_dup;

	stdin_dup = dup(0);
	stdout_dup = dup(1);
	//~~~
	//process
	//~~~
	dup2(stdin_dup, 0);
	dup2(stdout_dup, 1);
	close(stdin_dup);
	close(stdout_dup);
}&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;size18&quot;&gt;&lt;b&gt;3. execve&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662726880455&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;unistd.h&amp;gt;
int execve(const char *filename, char *const argv[], char *const envp[])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;execve 함수는 exec 계열 함수 중 하나로 프로세스를 하나 생성해서 명령어를 실행시키고 자신을 종료시킨다.&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;&lt;b&gt;예시 1) Running&amp;nbsp;ls&amp;nbsp;with&amp;nbsp;execve&amp;nbsp;출력&amp;nbsp;후&amp;nbsp;ls&amp;nbsp;명령어가 실행된다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662728563176&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

int	main(int argc, char **argv, char **envp)
{
	char *arg[2] = {&quot;ls&quot;};
	printf(&quot;Running ls with execve\n&quot;);
	execve(&quot;/bin/ls&quot;, arg, envp);
	printf(&quot;execve failed to run ls\n&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마음대로 출력할 거라면 그냥 fd_putstr 같은 함수를 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. perror&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662728848407&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
void perror(const char* str);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #3c3c3c;&quot;&gt;perror 함수는 전역 변수&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;errno에&amp;nbsp;&lt;span style=&quot;background-color: #ffffff; color: #3c3c3c;&quot;&gt;해당하는 에러 메시지를 &lt;/span&gt;&lt;span style=&quot;color: #3c3c3c;&quot;&gt;출력해준다. str이 NULL이 아닐 시 str도 출력해준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #3c3c3c;&quot;&gt;예시 1) myfile이 없을 시&amp;nbsp;Could&amp;nbsp;not&amp;nbsp;open&amp;nbsp;data&amp;nbsp;file:&amp;nbsp;No&amp;nbsp;such&amp;nbsp;file&amp;nbsp;or&amp;nbsp;directory가 출력된다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662729099525&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
 
int main(void)
{
   FILE *fh;
 
   if ((fh = fopen(&quot;myfile&quot;, &quot;r&quot;)) == NULL)
      perror(&quot;Could not open data file&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;5. strerror&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662729176857&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string.h&amp;gt;
char* strerror(int errnum);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;strerror 함수는 errnum&lt;span style=&quot;background-color: #ffffff; color: #3c3c3c;&quot;&gt;의 값을 통해 발생했던 에러에 맞는 에러 메시지를 반환해준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시 1) myfile이 없을 시&amp;nbsp;No&amp;nbsp;such&amp;nbsp;file&amp;nbsp;or&amp;nbsp;directory,&amp;nbsp;2가 출력된다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662729359399&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;errno.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string.h&amp;gt;

int main()
{
	FILE* fh;

	if ((fh = fopen(&quot;myfile&quot;, &quot;r&quot;)) == NULL)
		printf(&quot;%s, %d\n&quot;, strerror(errno), errno);
	return 0;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>42 SEOUL/pipex</category>
      <category>42 pipex</category>
      <category>42seoul</category>
      <category>42서울</category>
      <category>access</category>
      <category>C</category>
      <category>dup2</category>
      <category>execve</category>
      <category>perror</category>
      <category>pipex</category>
      <category>strerror</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1138</guid>
      <comments>https://jinho-study.tistory.com/1138#entry1138comment</comments>
      <pubDate>Fri, 9 Sep 2022 22:06:07 +0900</pubDate>
    </item>
    <item>
      <title>42 서울 pipex 정리 1 ( 프로세스, fork,  pipe, wait, waitpid)</title>
      <link>https://jinho-study.tistory.com/1137</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;개요&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;57&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIorOX/btrLvDdWJ4H/dHoCT60Lbxons9vzemgcrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIorOX/btrLvDdWJ4H/dHoCT60Lbxons9vzemgcrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIorOX/btrLvDdWJ4H/dHoCT60Lbxons9vzemgcrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIorOX%2FbtrLvDdWJ4H%2FdHoCT60Lbxons9vzemgcrK%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;475&quot; height=&quot;36&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;57&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pipex는 우리가 만들 pipex 프로그램을 위와 같은 방식으로 돌렸을 때, 아래 명령어와 똑같이 동작하도록 구현해야 되는 과제이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;599&quot; data-origin-height=&quot;40&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cg6hTv/btrLy2KPnrg/qCu1ZW8X3dPjW67lQIT5sK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cg6hTv/btrLy2KPnrg/qCu1ZW8X3dPjW67lQIT5sK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cg6hTv/btrLy2KPnrg/qCu1ZW8X3dPjW67lQIT5sK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcg6hTv%2FbtrLy2KPnrg%2FqCu1ZW8X3dPjW67lQIT5sK%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;465&quot; height=&quot;31&quot; data-origin-width=&quot;599&quot; data-origin-height=&quot;40&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 infile 파일을 읽고 명령어 2개를 실행한 결과를 outfile에 저장해야 된다.&amp;nbsp;여기서 이 작대기 |가 파이프다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 이 과제를 통해 프로세스와 프로세스 간 통신을 할 때 사용하는 pipe에 대해 공부해야 한다.&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;b&gt;프로세스(process)란?&lt;/b&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;메모리에 적재되면 프로세스가 된다. 간단히 말하면&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;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;함수 정리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. fork&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662445005220&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;unistd.h&amp;gt;
pid_t fork(void);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fork 함수는 프로세스를 복사해준다. 기존의 프로세스를 부모 프로세스, 복사된 프로세스를 자식 프로세스라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fork 함수는 프로세스의 고유 id인 pid를 반환한다. fork 함수&amp;nbsp;실행 실패 시 -1을 반환하고 자식 프로세스를 생성하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공 시에는 부모 프로세스에는 자식 프로세스 피드 값을, 자식 프로세스에는 0이 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시 1) &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;코드만 보면 if, else문이 다&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;돌아가는 게&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;말이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;안 되는&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;것 같지만 부모, 자식 프로세스에서 각각&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt;main문이 돌아가기 &lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt;때문에 2번 출력된다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662445787231&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int	main(void)
{
	pid_t pid = fork();

	if (pid == 0)
	{
		printf(&quot;Hello from Child\n&quot;);
		printf(&quot;%d %d\n&quot;, pid, getpid());
	}
	else
	{
		printf(&quot;Hello from Parent\n&quot;);
		printf(&quot;%d %d\n&quot;, pid, getpid());
	}
}

/*
실행 결과
Hello from Parent
48280 48279
Hello from Child
0 48280
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시 2) 자식과 부모 프로세스는 데이터와 상태가 다르기에 변수의 변화가 영향을 끼치지 않는다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662446253458&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int	main(void)
{
	int x = 1;
	if (fork() == 0)
    	printf(&quot;Child has x = %d\n&quot;, ++x);
	else
		printf(&quot;Parent has x = %d\n&quot;, --x);
}

/*
실행 결과
Parent has x = 0
Child has x = 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;size18&quot;&gt;&lt;b&gt;2. pipe&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662451038643&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;unistd.h&amp;gt;
int pipe(int fd[2]);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pipe 함수는 파이프를 생성하고 2개의 디스크립터(읽기 전용, 쓰기 전용)를 생성해준다. 실패했을 경우에는 -1을 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성한 2개의 디스크립터와 파이프를 사용해 아래와 같은 방식으로 부모 프로세스를 자식 프로세스를&amp;nbsp;통신시킬 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;802&quot; data-origin-height=&quot;592&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBcmdH/btrLwI0YZWR/xUFVo4kVKi7z6adRuc6faK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBcmdH/btrLwI0YZWR/xUFVo4kVKi7z6adRuc6faK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBcmdH/btrLwI0YZWR/xUFVo4kVKi7z6adRuc6faK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBcmdH%2FbtrLwI0YZWR%2FxUFVo4kVKi7z6adRuc6faK%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;380&quot; height=&quot;280&quot; data-origin-width=&quot;802&quot; data-origin-height=&quot;592&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;open 함수를&lt;span&gt;&amp;nbsp;&lt;/span&gt;사용할 때와&lt;span&gt;&amp;nbsp;&lt;/span&gt;마찬가지로 디스크립터를 사용하기에 pipe 함수 역시 사용하지 않는 디스크립터는 close 함수로 닫아줘야 한다.&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) pipe 생성&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662451848634&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int	main(void)
{
	int fd[2];
	
	pipe(fd);
	printf(&quot;fd[0]: %d fd[1]: %d\n&quot;, fd[0], fd[1]);
}

/*
실행 결과
fd[0]: 3 fd[1]: 4
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시 2) pipe를 사용한 부모와 자식 프로세스 간 통신&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662454988938&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#define MAX_BUF 1024

int main()
{
	int fd[2];
	pid_t pid;
	char buf[MAX_BUF];

	if (pipe(fd) &amp;lt; 0)
		printf(&quot;pipe error\n&quot;);
	pid = fork();
	if (pid &amp;lt; 0)
	{
		printf(&quot;fork error\n&quot;);
		exit(1);
	}
	if (pid == 0)
	{
		close(fd[1]);
		read(fd[0], buf, MAX_BUF);
		printf(&quot;Child got message : %s\n&quot;, buf);
	}
	else
	{
		close(fd[0]);
		ft_strcpy(buf, &quot;Massage from Parent&quot;);
		write(fd[1], buf, ft_strlen(buf));
	}
}

/*
실행 결과
Child got message : Massage from Parent
*/&lt;/code&gt;&lt;/pre&gt;
&lt;figure data-ke-type=&quot;image&quot; data-ke-style=&quot;alignLeft&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3. wait&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662455499592&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;sys/wait.h&amp;gt;
pid_t  wait(int *statloc);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;wait 함수는 자식 프로세스가 종료되었을 때 자식 프로세스의 pid값을 반환하고, 오류가 났을 때는 -1을 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;wait 함수를 사용하면 부모 프로세스를 자식 프로세스가 종료될 때까지 기다리게 할 수 있습니다.&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;wait 함수를 사용해 이를 방지할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시 1) 부모 프로세스가 자식 프로세스가 끝난 후 다시 돌아가는 것을 확인할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662456736433&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int main()
{
	int stat, stat_res;

    if ((fork()) == 0)
        printf(&quot;Child Hi\n&quot;);
    else
    {
		stat_res = wait(&amp;amp;stat);
		printf(&quot;Parent Hi\n&quot;);
		printf(&quot;wait 인자값: %d\nwait 반환값: %d\n&quot;, stat, stat_res);
        wait(NULL);
        printf(&quot;Child Bye\n&quot;);
    }
    printf(&quot;Process Bye\n&quot;);
    return 0;
}

/*
실행 결과
Child Hi
Process Bye
Parent Hi
wait 인자값: 0
wait 반환값: 57724
Child Bye
Process Bye
*/&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;아래 WIF 매크로를 사용해서 stat의 정보를 확인할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WIFEXITED(status): 자식 프로세스가 정상적으로&amp;nbsp;종료했으면 참을 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WEXITSTATUS(status): 자식 프로세스의 종료 상태를 반환한다. 이 매크로는&lt;span&gt;&amp;nbsp;&lt;/span&gt;WIFEXITED가 참을 반환했을 때만 써야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WIFSIGNALED(status): 자식 프로세스가 시그널로 종료되었으면 참을 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WIFSTOPPED(status)&lt;span style=&quot;background-color: #ffffff; color: #273239;&quot;&gt;: 자식 프로세스가 정지된 상태라면 참을 반환한다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시 2) 자식 프로세스가 정상적으로 종료됐으므로 WIFEXITED의 조건문이 실행된다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662520454170&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int main()
{
	int pid, status;

	printf(&quot;Parent: %d\n&quot;, getpid());
	pid = fork();
	if (pid == 0)
	{
		printf(&quot;Child %d\n&quot;, getpid());
		sleep(2);
		return (0);
	}
	waitpid(pid, &amp;amp;status, 0);
	if (WIFSIGNALED(status))
		printf(&quot;Error\n&quot;);
	else if (WEXITSTATUS(status))
		printf(&quot;Exited Normally\n&quot;);
	else if (WIFEXITED(status))
		printf(&quot;Parent: %d\n&quot;, getpid());
	return 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;size18&quot;&gt;&lt;b&gt;4. waitpid&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662457405835&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;sys/wait.h&amp;gt;
pid_t  waitpid(pid_t pid, int *statloc, int options);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;waitpid 함수는 wait 함수와 거의 똑같은데, 옵션을 추가해 동작 방식을 변경할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 옵션들(3번째 인자 종류)이다. 인자가 0일 경우에는&amp;nbsp;wait 함수와 동일하게 작동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WNOHANG: &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;기다리는 PID가 종료되지 않아서 즉시 종료 상태를 회수할 수 없는 상황일 때 0을 반환&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WUNTRACED: &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;WCONTINUED: &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;중단되었다가 재개된 자식 프로세스의 상태를 반환&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;에시 1) WNOHANG 사용&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1662519159576&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;time.h&amp;gt;

int main()
{
	pid_t pid;
	int status;
	time_t t;

	pid = fork();
	if (pid &amp;lt; 0)
		perror(&quot;fork error&quot;);
	else if (pid == 0)
	{
		sleep(3);
		exit(1);
	}
	else do
	{
		pid = waitpid(pid, &amp;amp;status, WNOHANG);
		if (pid == -1)
			perror(&quot;wait error&quot;);
    	else if (pid == 0)
		{
			time(&amp;amp;t);
			printf(&quot;child is still running at %s&quot;, ctime(&amp;amp;t));
			sleep(1);
		}
    	else
		{
			if (WIFEXITED(status))
				printf(&quot;child exited with status of %d\n&quot;, WEXITSTATUS(status));
			else
				puts(&quot;child did not exit successfully&quot;);
    	}
	} while (pid == 0);
}

/*
child is still running at Wed Sep  7 12:27:32 2022
child is still running at Wed Sep  7 12:27:33 2022
child is still running at Wed Sep  7 12:27:34 2022
child exited with status of 1
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이 과제에서&amp;nbsp;WNOHANG을 사용했다는 것은 좋은 징조가 아니다. dup2 후 파이프를 전부 다 닫아주면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WNOHANG을 사용하지 않아도 잘 넘어가진다. 파이프를 잘 안 닫았을 경우 yes | head -1이나&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;/dev/urandom | head -1 같은 경우에 head 한 결과가 출력도 안되고 프로그램이 끝나지도 않는다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;</description>
      <category>42 SEOUL/pipex</category>
      <category>42 pipex</category>
      <category>42 서울</category>
      <category>42seoul</category>
      <category>C</category>
      <category>fork</category>
      <category>Pipe</category>
      <category>Process</category>
      <category>wait</category>
      <category>waitpid</category>
      <category>프로세스</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1137</guid>
      <comments>https://jinho-study.tistory.com/1137#entry1137comment</comments>
      <pubDate>Tue, 6 Sep 2022 19:40:04 +0900</pubDate>
    </item>
    <item>
      <title>42서울 복귀</title>
      <link>https://jinho-study.tistory.com/1136</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;소집해제 후 1년 9개월 만에 42로 돌아왔다. 과제들이 상당히 많이 바뀐 것 같다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;netwhat, ft_server, ft_service 등등이 사라지고 ft_printf가 쉬워졌고, 다른 과제들이 많이 추가됐다.&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;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하는지도 예전보다 뚜렷하게 보이는 것 같아서 참 좋다. Ex) pipex -&amp;gt; 프로세스, push_swap -&amp;gt; 자료구조 알고리즘&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 norminette가 바뀌어서 기존 코드 다 수정하는 건 조금 귀찮긴 했다. libftㅜㅜ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 지금 하고 있는 pipex 과제부터 정리하려고 한다. get_nextline, ft_printf는 exam02 볼 때 정리할 것 같다.&lt;/p&gt;</description>
      <category>42 SEOUL/잡담</category>
      <category>42</category>
      <category>42seoul</category>
      <category>42서울</category>
      <category>복학</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1136</guid>
      <comments>https://jinho-study.tistory.com/1136#entry1136comment</comments>
      <pubDate>Tue, 6 Sep 2022 13:16:16 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 2022년 코딩테스트 실전 대비 모의고사</title>
      <link>https://jinho-study.tistory.com/1135</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 프로그래머스에서 진행되는 코테 &lt;span&gt;모의고사&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;2차 문제를 풀어봤다. &lt;span&gt;난이도가&lt;span&gt; 조금&amp;nbsp;&lt;/span&gt;&lt;/span&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;&lt;a href=&quot;https://career.programmers.co.kr/competitions/2627&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://career.programmers.co.kr/competitions/2627&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1659204297940&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;접수 &amp;nbsp; 22년 07월 13일 10:00 ~ 08월 23일 23:59 테스트 &amp;nbsp; 22년 07월 13일 10:00 ~ 08월 23일 23:59&quot; data-og-host=&quot;career.programmers.co.kr&quot; data-og-source-url=&quot;https://career.programmers.co.kr/competitions/2627&quot; data-og-url=&quot;https://career.programmers.co.kr/competitions/2627/2022-%EC%8B%A4%EC%A0%84-%EB%8C%80%EB%B9%84-%EB%AA%A8%EC%9D%98%EA%B3%A0%EC%82%AC&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://career.programmers.co.kr/competitions/2627&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://career.programmers.co.kr/competitions/2627&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; 22년 07월 13일 10:00 ~ 08월 23일 23:59 테스트 &amp;nbsp; 22년 07월 13일 10:00 ~ 08월 23일 23:59&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;career.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;b&gt;1번&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;503&quot; data-origin-height=&quot;691&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/draD2A/btrIyxn9HN8/OzyBNCeTYootRwtGOoArt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/draD2A/btrIyxn9HN8/OzyBNCeTYootRwtGOoArt1/img.png&quot; data-alt=&quot;1&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/draD2A/btrIyxn9HN8/OzyBNCeTYootRwtGOoArt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdraD2A%2FbtrIyxn9HN8%2FOzyBNCeTYootRwtGOoArt1%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;503&quot; height=&quot;691&quot; data-origin-width=&quot;503&quot; data-origin-height=&quot;691&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;1&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;설명할 필요도 없을 듯 하다. python의 combinations를 쓰면 조합을 바로 구할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1659203361108&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 solution(number):
    return len([s for s in combinations(number, 3) if sum(s) == 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;&lt;b&gt;2번&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;922&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/by0IRt/btrICZ4IiYz/yXuXpaArk7eR8Ng2ntXjaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/by0IRt/btrICZ4IiYz/yXuXpaArk7eR8Ng2ntXjaK/img.png&quot; data-alt=&quot;2&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/by0IRt/btrICZ4IiYz/yXuXpaArk7eR8Ng2ntXjaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fby0IRt%2FbtrICZ4IiYz%2FyXuXpaArk7eR8Ng2ntXjaK%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;935&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;922&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;2&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;처음에 그냥 리스트를 쪼개고 set 함수를 사용해서 풀었는데 시간 초과가 나왔다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;EX) len(set(topping[:i]) == len(set(topping[i:])&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;pre id=&quot;code_1659203385778&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from collections import Counter, defaultdict

def solution(topping):
    front, back = defaultdict(int), dict(Counter(topping))
    ans = 0
    
    for t in topping:
        front[t] += 1
        back[t] -= 1
        if back[t] == 0:
            del back[t]
        if len(front) == len(back):
            ans += 1

    return ans&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;3번&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;502&quot; data-origin-height=&quot;984&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8HsNn/btrIyYeEGAx/zUfIdofXmdbLGwSVQpbJ80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8HsNn/btrIyYeEGAx/zUfIdofXmdbLGwSVQpbJ80/img.png&quot; data-alt=&quot;3&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8HsNn/btrIyYeEGAx/zUfIdofXmdbLGwSVQpbJ80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8HsNn%2FbtrIyYeEGAx%2FzUfIdofXmdbLGwSVQpbJ80%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;502&quot; height=&quot;984&quot; data-origin-width=&quot;502&quot; data-origin-height=&quot;984&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;3&lt;/figcaption&gt;
&lt;/figure&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;&lt;b&gt;우리가 문제를 풀 때는 목적지가 군인들을 찾아간다고 생각하는 것이 풀기 쉽다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;목적지 기준으로 bfs 알고리즘을 사용해서 다른 모든 위치까지의 거리를 구하면 사실상 끝이다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1659203396024&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from collections import deque

def solution(n, roads, sources, destination):
    graph = [[] for _ in range(n+1)]
    ans = []
    
    for s, e in roads:
        graph[s].append(e)
        graph[e].append(s)

    check = [-1 for _ in range(n+1)]
    check[destination] = 0
    q = deque([destination])
    while q:
        i = q.popleft()
        for node in graph[i]:
            if check[node] == -1:
                check[node] = check[i]+1
                q.append(node)

    for s in sources:
        ans.append(check[s])

    return ans&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Agorithm/프로그래머스</category>
      <category>2022</category>
      <category>algorithm</category>
      <category>Python</category>
      <category>알고리즘</category>
      <category>코딩테스트</category>
      <category>코테</category>
      <category>파이썬</category>
      <category>프로그래머스</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1135</guid>
      <comments>https://jinho-study.tistory.com/1135#entry1135comment</comments>
      <pubDate>Sun, 31 Jul 2022 03:04:04 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 Level 2 가장 먼 노드</title>
      <link>https://jinho-study.tistory.com/1134</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;거리와 탐색 관련된 문제이기 때문에 BFS를 사용해서 풀었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;BFS 풀이&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막에 check_li.count(max(check_li))로 한 번에 제일 먼 노드의 개수를 구할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1656486558400&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from collections import deque

def solution(N, edge):
    graph = [[] for _ in range(N+1)]
    q = deque([1])
    check_li = [-1]*(N+1)
    check_li[1] = 0
    
    for s, e in edge:
        graph[s].append(e)
        graph[e].append(s)
    
    while q:
        node = q.popleft()
        for n in graph[node]:
            if check_li[n] == -1:
                q.append(n)
                check_li[n] = check_li[node] + 1
    
    return check_li.count(max(check_li))&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Agorithm/프로그래머스</category>
      <category>algorithm</category>
      <category>BFS</category>
      <category>level 3</category>
      <category>Python</category>
      <category>가장 먼 노드</category>
      <category>알고리즘</category>
      <category>파이썬</category>
      <category>프로그래머스</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1134</guid>
      <comments>https://jinho-study.tistory.com/1134#entry1134comment</comments>
      <pubDate>Wed, 29 Jun 2022 16:10:34 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 Level 2 타켓 넘버</title>
      <link>https://jinho-study.tistory.com/1133</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 DFS, BFS 문제이다. DFS, BFS를 안 사용하고도 풀어봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;b&gt;DFS 풀&lt;/b&gt;이&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1656484174864&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def dfs(n, numbers, target, i):
    if i == len(numbers):
        return 1 if n == target else 0
    return dfs(n + numbers[i], numbers, target, i+1) + dfs(n - numbers[i], numbers, target, i+1)

def solution(numbers, target):
    return dfs(0, numbers, target, 0)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생각해보니 굳이 dfs 함수를 만들 필요도 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 방식으로 numbers 리스트의 앞에 값을 빼가면서, target이 0이 되는지 확인하는 방법도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1656484645935&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(numbers, target):
    if not numbers:
        return 1 if target == 0 else 0
    return solution(numbers[1:], target + numbers[0]) + solution(numbers[1:], target - numbers[0])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;b&gt;BFS &lt;/b&gt;풀이&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1656485274411&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from collections import deque

def solution(numbers, target):
    q = deque([(0, 0)])
    ans = 0

    while q:
        n, i = q.popleft()
        if i == len(numbers):
            if n == target:
                ans += 1
        else:
            q.append([n + numbers[i], i+1])                
            q.append([n - numbers[i], i+1])                
        
    return ans&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;풀이 DFS, BFS 사용 X&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1656484270601&quot; class=&quot;vim&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(numbers, target):
    res_li = [0]
    for n in numbers:
        li = []
        for res in res_li:
            li.append(res + n)
            li.append(res - n)
        res_li = li
    
    return res_li.count(target)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Agorithm/프로그래머스</category>
      <category>algorithm</category>
      <category>BFS</category>
      <category>dfs</category>
      <category>Python</category>
      <category>알고리즘</category>
      <category>타켓 넘버</category>
      <category>파이썬</category>
      <category>프로그래머스</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1133</guid>
      <comments>https://jinho-study.tistory.com/1133#entry1133comment</comments>
      <pubDate>Wed, 29 Jun 2022 15:50:25 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 Level 2 스킬트리</title>
      <link>https://jinho-study.tistory.com/1132</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;맨 앞 스킬부터 순서대로 나오는지 확인해야 되기 때문에, 큐를 사용해서 풀었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 풀이는 파이썬의 for-else문을 몰라서 check 변수를 사용한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 풀이는 for-else문을 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;풀이 for-else X&lt;/b&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. 스킬 순서에 포함되는 스킬이 나왔는데 선행 스킬이 아니면 불가능한 스킬트리이므로 check = 0을 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 반대로 선행 스킬이 맞다면 스킬 순서 큐를 popleft 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. ans += check(1: 올바른 스킬트리, 0: 불가능한 스킬트리)&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1656398913838&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(skill, skill_trees):
    ans = 0
    for skill_tree in skill_trees:
        q = list(skill)
        check = 1
        for s in skill_tree:
            if s in q and s != q.pop(0):
                check = 0
                break
        ans += check
        
    return ans&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;풀이 for-else O&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for 문이 break 등으로 중간에 빠져나오지 않고 끝까지 실행됐을 경우 else문이 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;check 변수가 필요 없어지기에 코드 한 줄이 줄었다.&lt;/p&gt;
&lt;pre id=&quot;code_1656399526458&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from collections import deque

def solution(skill, skill_trees):
    ans = 0
    for skill_tree in skill_trees:
        q = deque(skill)
        for s in skill_tree:
            if s in q and s != q.popleft():
                break
        else:
            ans += 1
        
    return ans&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Agorithm/프로그래머스</category>
      <category>algorithm</category>
      <category>level 2</category>
      <category>Python</category>
      <category>스킬트리</category>
      <category>알고리즘</category>
      <category>파이썬</category>
      <category>프로그래머스</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1132</guid>
      <comments>https://jinho-study.tistory.com/1132#entry1132comment</comments>
      <pubDate>Tue, 28 Jun 2022 16:02:49 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 Level 2 기능개발</title>
      <link>https://jinho-study.tistory.com/1131</link>
      <description>&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;풀이 Q 사용 O&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 각 progress 별 필요한 요일을 큐에 저장해준다 -&amp;gt; [ceil((100-progresses[i])/speeds[i]) for i in range(len(speeds))]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. popleft 한 값이 최대값일 경우 ans에&amp;nbsp; append 1, 아닐 경우 ans 마지막 요소에 1 더함&lt;/p&gt;
&lt;pre id=&quot;code_1656337024908&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from math import ceil
from collections import deque

def solution(progresses, speeds):
    ans = []
    q = deque([ceil((100-progresses[i])/speeds[i]) for i in range(len(speeds))])
    max_t = 0
    while q:
        t = q.popleft()
        if t &amp;gt; max_t:
            max_t = t
            ans.append(1)
        else:
            ans[-1] += 1

    return ans&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;풀이 Q 사용 X&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1656337034254&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from math import ceil

def solution(progresses, speeds):
    ans = []
    li = [ceil((100-progresses[i])/speeds[i]) for i in range(len(speeds))]
    for i in range(len(li)):        
        if ans == [] or max(li[:i]) &amp;lt; li[i]:
            ans.append(1)
        else:
            ans[-1] += 1

    return ans&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Agorithm/프로그래머스</category>
      <category>algorithm</category>
      <category>Python</category>
      <category>Queue</category>
      <category>기능개발</category>
      <category>알고리즘</category>
      <category>큐</category>
      <category>파이썬</category>
      <category>프로그래머스</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1131</guid>
      <comments>https://jinho-study.tistory.com/1131#entry1131comment</comments>
      <pubDate>Mon, 27 Jun 2022 23:18:41 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 Level 1 다트 게임</title>
      <link>https://jinho-study.tistory.com/1130</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;단순한 구현 문제이다. dart 딕셔너리를 써서 조건문을 많이 줄일 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;풀이&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1656171323402&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(dartResult):
    dart = {'S': 1, 'D': 2, 'T': 3}
    n = ''
    score = []

    for c in dartResult:
        if c.isnumeric():
            n += c
        elif c in dart:
            score.append(int(n) ** dart[c])
            n = ''
        elif c == '*':
            score[-2:] = [s*2 for s in score[-2:]]
        elif c == '#':
            score[-1] = -score[-1]       

    return sum(score)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Agorithm/프로그래머스</category>
      <category>algorithm</category>
      <category>Python</category>
      <category>구현</category>
      <category>다트 게임</category>
      <category>알고리즘</category>
      <category>파이썬</category>
      <category>프로그래머스</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1130</guid>
      <comments>https://jinho-study.tistory.com/1130#entry1130comment</comments>
      <pubDate>Sun, 26 Jun 2022 00:38:43 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 Level 1 비밀지도</title>
      <link>https://jinho-study.tistory.com/1129</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;비트연산자를 쓸 수 있느냐를 물어보는 문제이다. 처음에는 그냥 bin을 써서 풀었는데,&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;풀이 1&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1656169137734&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def binary(num, n):
    b = bin(num)[2:]
    return (n - len(b))*'0' + b

def str_sum(n, s1, s2):
    s = ''
    for i in range(n):
        s += '#' if s1[i] == '1' or s2[i] == '1' else ' '
    return s

def solution(n, arr1, arr2):
    ans = [0]*n
    for i in range(n):
        ans[i] = str_sum(n, binary(arr1[i], n), binary(arr2[i], n))
    return ans&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;풀이 2&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비트연산자와 replace를 사용하니 훨씬 쉽게 풀린다&lt;/p&gt;
&lt;pre id=&quot;code_1656170188418&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(n, arr1, arr2):
    ans = []
    for i in range(n):
        b = bin(arr1[i] | arr2[i])[2:]
        ans.append((n - len(b))*' ' + b.replace('1', '#').replace('0', ' '))
        
    return ans&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Agorithm/프로그래머스</category>
      <category>algorithm</category>
      <category>Level 1</category>
      <category>Python</category>
      <category>비밀지도</category>
      <category>비트연산자</category>
      <category>알고리즘</category>
      <category>파이썬</category>
      <category>프로그래머스</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1129</guid>
      <comments>https://jinho-study.tistory.com/1129#entry1129comment</comments>
      <pubDate>Sun, 26 Jun 2022 00:18:28 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 Level 2 거리두기 확인하기</title>
      <link>https://jinho-study.tistory.com/1128</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;그냥 조건문(노가다), DFS, BFS 등의 방식으로 풀 수 있는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 BFS 알고리즘을 사용해서 풀었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;place에 map함수를 쓴 이유는 place가 문자열로 이루어진 리스트이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어&lt;span&gt;&amp;nbsp;place[0]이&lt;/span&gt; 문자열이라 place[0][0] = 'X'이 안된다. 그래서 쓰기 쉽게 map함수를 써서 리스트로 바꿔줬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;풀이&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;1. place 탐색 -&amp;gt; place의 현재 위치가 'P'라면 'X'로 바꾸고 BFS 탐색 시작&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. BFS 탐색)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;상하좌우로 탐색하는데 맨 처음 탐색 시작 위치(i, j)와 다음 위치(Y, X)의&amp;nbsp;맨헤튼 거리가 2 이하일 경우에만 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;다음 위치가 'O' 라면 현재 위치를 'X'로 바꾸고 계속 탐색 진행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;다음 위치가 'P'라면 거리 두기 위반이므로 바로 0 리턴&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 한 명이라도 위반하면 0을 리턴해야 되므로 BFS 탐색이 끝날 때마다 check &amp;amp;= bfs(i, j) 같은 방식으로 check를 업데이트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 작은 반복문 끝날 때마다 ans에 check를 추가&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;pre id=&quot;code_1656051159357&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(10000)

def solution(places):    
    def bfs(i, j):
        q = [(i, j)]
        while q:
            y, x = q.pop(0)
            for dy, dx in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
                Y, X = y+dy, x+dx
                if 0 &amp;lt;= Y &amp;lt; 5 and 0 &amp;lt;= X &amp;lt; 5 and abs(Y-i) + abs(X-j) &amp;lt;= 2:
                    if place[Y][X] == 'O':
                        place[Y][X] = 'X'
                        q.append((Y, X))
                    elif place[Y][X] == 'P':
                        return 0
        return 1
    
    ans = []
    for place in places:
        place = list(map(list, place))
        check = 1
        for i in range(5):
            for j in range(5):
                if place[i][j] == 'P':
                    place[i][j] = 'X'
                    check &amp;amp;= bfs(i, j)
        ans.append(check)                       
                    
    return ans&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Agorithm/프로그래머스</category>
      <category>algorithm</category>
      <category>BFS</category>
      <category>level 2</category>
      <category>Python</category>
      <category>거리두기 확인하기</category>
      <category>알고리즘</category>
      <category>파이썬</category>
      <category>프로그래머스</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1128</guid>
      <comments>https://jinho-study.tistory.com/1128#entry1128comment</comments>
      <pubDate>Fri, 24 Jun 2022 15:50:55 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 Level 1 실패율</title>
      <link>https://jinho-study.tistory.com/1127</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;문제에 맞는 자료구조를 생성하고 활용해야 하는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제의 경우에는 Counter를 사용해서 쉽게 풀 수 있다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;풀이&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 각 스테이지별 도달한 유저의 수를 담은 딕셔너리를 생성(Counter)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 각 스테이지별 실패율 계산&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 실패율을 기준으로 내림차순 정렬한 리스트의 인덱스를 반환&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1655882014893&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from collections import Counter

def solution(N, stages):
    ans = []
    n = len(stages)
    counter = Counter(stages)
    for i in range(1, N+1):
        if n:
            ans.append(counter[i] / n)
            n -= counter[i]
        else:
            ans.append(0)
                
    return [i+1 for i in sorted(range(N), key=lambda x : ans[x], reverse=True)]&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Agorithm/프로그래머스</category>
      <category>algorithm</category>
      <category>Level 1</category>
      <category>Python</category>
      <category>실패율</category>
      <category>알고리즘</category>
      <category>파이썬</category>
      <category>프로그래머스</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1127</guid>
      <comments>https://jinho-study.tistory.com/1127#entry1127comment</comments>
      <pubDate>Wed, 22 Jun 2022 16:19:10 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 Level 2 메뉴 리뉴얼</title>
      <link>https://jinho-study.tistory.com/1126</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;문제 상황에 맞는 적절한 자료구조를 생성하고 활용해야 되는 문제다.&amp;nbsp;&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;Collections의 Counter를 사용하면 상당히 쉽게 풀 수 있다. 처음에 안 쓰고 풀어서 그런지 쉽지 않았다.&amp;nbsp;&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;itertools의&lt;span&gt;&amp;nbsp;&lt;/span&gt;combinations 함수를 사용했다.&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;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;풀이 1 (Counter 사용 X)&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;*&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;cource의 개수 만큼 반복([2, 3]의 경우 -&amp;gt; 2개짜리 조합, 3개짜리 조합 찾기)&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;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;1. 모든 order에서 가능한 n&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;(&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;cource 값&lt;/span&gt;)개 짜리 조합을 &lt;/span&gt;comb_li에 저장&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;2. comb_li에서 {주문된 개수: [메뉴1, ...], ... }의 형태의 딕셔너리 d를 생성&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;3. d에서 주문된 개수가 제일 많은 메뉴들을 ans에 추가&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1655821208773&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
from itertools import combinations

def solution(orders, course):   
    ans = []
    for n in course:
        d = defaultdict(list)
        comb_li = []
        max_cnt = 0        
        for order in orders:
            if len(order) &amp;gt;= n:
                comb_li.extend(list(combinations(sorted(order), n)))
        
        for comb in set(comb_li):            
            cnt = comb_li.count(comb)
            d[cnt].append(''.join(comb))
            if cnt &amp;gt; max_cnt:
                max_cnt = cnt                
        
        if max_cnt &amp;gt; 1:
            ans.extend(d[max_cnt])

    return sorted(ans)&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;span&gt;풀이 2 (Counter 사용 O)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Counter를 사용했기에 d 딕셔러니가 필요가 없다. d 딕셔너리를 생성하는데 필요했던 자잘한 코드들을 &lt;/span&gt;&lt;span&gt;지우니 &lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;훨씬 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;코드가 간결해졌다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1655823978275&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from collections import Counter
from itertools import combinations

def solution(orders, course):   
    ans = []
    for n in course:
        comb_li = []
        for order in orders:
            comb_li.extend(list(combinations(sorted(order), n)))

        counter = Counter(comb_li)
        if counter and max(counter.values()) &amp;gt; 1:
            ans.extend([''.join(k) for k in counter if counter[k] == max(counter.values())])

    return sorted(ans)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Agorithm/프로그래머스</category>
      <category>algorithm</category>
      <category>level 2</category>
      <category>Python</category>
      <category>메뉴 리뉴얼</category>
      <category>알고리즘</category>
      <category>파이썬</category>
      <category>프로그래머스</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1126</guid>
      <comments>https://jinho-study.tistory.com/1126#entry1126comment</comments>
      <pubDate>Wed, 22 Jun 2022 00:12:21 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 Level 1 신고 결과 받기</title>
      <link>https://jinho-study.tistory.com/1125</link>
      <description>&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 style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;2개의 &lt;/span&gt;Dictionary를 사용했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;python의 defaultdict를 거의 처음 사용해봤는데 꽤 좋은 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dict의 메서드인 setdefault도 사용해봤지만 defaultdict 쪽이 조금 더 편한 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;풀이&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 유저별 신고한 id를 저장하는 report_user_dict, 유저별 신고당한 횟수를 저장하는 report_cnt_dict를 생성한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 위의 두 사전을 사용해 유저가 신고한 id가 k번 이상인지 확인하고 카운트해준다.&lt;/b&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655818286913&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

def solution(id_list, report, k):
    report_user_dict = defaultdict(set)
    report_cnt_dict = defaultdict(int)
    ans = []
    
    for arg in set(report):
        id, reported_id = arg.split()
        report_user_dict[id].add(reported_id)
        report_cnt_dict[reported_id] += 1
        
    for id in id_list:
        cnt = 0
        for reported_id in report_user_dict[id]:
            if report_cnt_dict[reported_id] &amp;gt;= k:
                cnt += 1 
        ans.append(cnt)
    
    return ans&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Agorithm/프로그래머스</category>
      <category>algorithm</category>
      <category>Level 1</category>
      <category>Python</category>
      <category>신고 결과 받기</category>
      <category>알고리즘</category>
      <category>파이썬</category>
      <category>프로그래머스</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1125</guid>
      <comments>https://jinho-study.tistory.com/1125#entry1125comment</comments>
      <pubDate>Tue, 21 Jun 2022 22:54:33 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 Level 1 신규 아이디 추천</title>
      <link>https://jinho-study.tistory.com/1124</link>
      <description>&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;size18&quot;&gt;&lt;b&gt;첫 번째 풀이&lt;/b&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트 사용&lt;/p&gt;
&lt;pre id=&quot;code_1655816400144&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(new_id):
    # 1
    new_id = new_id.lower()
    
    # 2
    li = [c for c in new_id if c.isalnum() or c in &quot;-_.&quot;]
    
    # 3
    i = 0
    while i &amp;lt; len(li)-1:
        if li[i] == li[i+1] == '.':
            li.pop(i)
        else:
            i += 1
    
    # 4
    if li and li[0] == '.':
        li.pop(0)
    if li and li[-1] == '.':
        li.pop()
    
    # 5
    if li == []:
        li.append('a')
    
    # 6
    if len(li) &amp;gt;= 16:
        li = li[:15]
        if li[-1] == '.':
            li.pop()
    
    # 7
    if len(li) &amp;lt;= 2:
        while len(li) &amp;lt; 3:
            li.append(li[-1])
    answer = ''.join(li)
    
    return answer&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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_1655817456382&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(new_id):
    ans = &quot;&quot;

    # 1
    new_id = new_id.lower()
    
    # 2
    for c in new_id: 
        if c.isalnum() or c in &quot;-_.&quot;:
            ans += c
    
    # 3
    while &quot;..&quot; in ans:
        ans = ans.replace(&quot;..&quot;, '.')
    
    # 4
    if ans and ans[0] == '.':
        ans = ans[1:]
    if ans and ans[-1] == '.':
        ans = ans[:-1]
    
    # 5
    if ans == '':
        ans = 'a'
    
    # 6
    if len(ans) &amp;gt;= 16:
        ans = ans[:15]
        if ans[-1] == '.':
            ans = ans[:-1]
    
    # 7
    while len(ans) &amp;lt; 3:
        ans += ans[-1]
        
    return ans&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Agorithm/프로그래머스</category>
      <category>algorithm</category>
      <category>Level 1</category>
      <category>Python</category>
      <category>신규 아이디 추천</category>
      <category>알고리즘</category>
      <category>파이썬</category>
      <category>프로그래머스</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1124</guid>
      <comments>https://jinho-study.tistory.com/1124#entry1124comment</comments>
      <pubDate>Tue, 21 Jun 2022 22:20:37 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 Level 2 오픈채팅방</title>
      <link>https://jinho-study.tistory.com/1123</link>
      <description>&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;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;풀이 1&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;record를 따라&amp;nbsp;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;유저의 id와 이름을 기록한 딕셔너리인 user_dic, 유저 id와 출력돼야 하는 메시지들이 담긴 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;리스트인 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;msg_li를&lt;/span&gt; 만들어줬다. 아래와 같은 느낌이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1559&quot; data-origin-height=&quot;99&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/x8Pvu/btrESEEmPh8/nqOqukqE4dkqEn4Kai0JY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/x8Pvu/btrESEEmPh8/nqOqukqE4dkqEn4Kai0JY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/x8Pvu/btrESEEmPh8/nqOqukqE4dkqEn4Kai0JY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fx8Pvu%2FbtrESEEmPh8%2FnqOqukqE4dkqEn4Kai0JY0%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;1559&quot; height=&quot;99&quot; data-origin-width=&quot;1559&quot; data-origin-height=&quot;99&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;msg_li에서 메시지 부분을 유저 id에 매칭 되는 이름으로 수정한 결과를 담은 리스트를 반환해주면 끝이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; [msg.replace(user_id,&amp;nbsp;user_dic[user_id])&amp;nbsp;for&amp;nbsp;user_id,&amp;nbsp;msg&amp;nbsp;in&amp;nbsp;msg_li]&lt;/p&gt;
&lt;pre id=&quot;code_1655307126492&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(records):
    user_dic = {}
    msg_li = []

    for record in records:
        li = record.split()
        if li[0] == &quot;Enter&quot;:
            command, user_id, user_name = li
            user_dic[user_id] = user_name
            msg_li.append([user_id, f&quot;{user_id}님이 들어왔습니다.&quot;])
        elif li[0] == &quot;Leave&quot;:
            command, user_id = li
            msg_li.append([user_id, f&quot;{user_id}님이 나갔습니다.&quot;])
        else:
            command, user_id, user_name = li
            user_dic[user_id] = user_name
    
    ans = [msg.replace(user_id, user_dic[user_id]) for user_id, msg in msg_li]
    
    return ans&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;size18&quot;&gt;&lt;b&gt;풀이 2&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 첫 번째 반복문을 보면 Enter와 Change의 경우일 때 중복인 코드가 있는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유는 내가 굳이 필요 없는 자료구조인 msg_li를 생성하고 msg_li를 통해 ans를 완성하려고 했기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;msg_li를 생성하지 않고, 그냥 user_dic을 첫 반복문에서 완성하고 두 번째 반복문에서 바로 ans를 완성하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 방식으로 짠 결과 코드가 &lt;span&gt;훨씬&lt;span&gt; &lt;/span&gt;&lt;/span&gt;간단해졌다. printer는 두 번째 반복문에 조건문을 넣기 싫어서 만들었다.&lt;/p&gt;
&lt;pre id=&quot;code_1655310510498&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(records):
    user_dic = {}
    printer = {&quot;Enter&quot;: &quot;님이 들어왔습니다.&quot;, &quot;Leave&quot;: &quot;님이 나갔습니다.&quot;}
    ans = []
    
    for record in records:
        li = record.split()
        if li[0] in [&quot;Enter&quot;, &quot;Change&quot;]:
            user_dic[li[1]] = li[2]

    for record in records:
        li = record.split()
        if li[0] != &quot;Change&quot;:
            ans.append(user_dic[li[1]] + printer[li[0]])
            
    return ans&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Agorithm/프로그래머스</category>
      <category>algorithm</category>
      <category>Python</category>
      <category>알고리즘</category>
      <category>오픈채팅방</category>
      <category>파이썬</category>
      <category>프로그래머스</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1123</guid>
      <comments>https://jinho-study.tistory.com/1123#entry1123comment</comments>
      <pubDate>Thu, 16 Jun 2022 02:08:13 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 Level 1 로또의 최고 순위와 최저 순위</title>
      <link>https://jinho-study.tistory.com/1122</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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;풀이&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 0의 개수, 일치하는 숫자의 개수를 센다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 0의 개수 + 일치하는 숫자 -&amp;gt; 순위가 제일 높은 경우, 일치하는 숫자 -&amp;gt; 순위가 제일 낮은 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이므로 이에 맞게 순위를 반환해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1655306139755&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(lottos, win_nums):
    zero_cnt = lottos.count(0)
    matching_num_cnt = sum([n in win_nums for n in lottos])
    rank = [6, 6, 5, 4, 3, 2, 1]
    
    return rank[zero_cnt+matching_num_cnt], rank[matching_num_cnt]&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Agorithm/프로그래머스</category>
      <category>algorithm</category>
      <category>Python</category>
      <category>로또의 최고 순위와 최저 순위</category>
      <category>알고리즘</category>
      <category>파이썬</category>
      <category>프로그래머스</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1122</guid>
      <comments>https://jinho-study.tistory.com/1122#entry1122comment</comments>
      <pubDate>Thu, 16 Jun 2022 00:27:35 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 Level 1 크레인 인형뽑기 게임</title>
      <link>https://jinho-study.tistory.com/1121</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;스택을 사용하면 쉽게 풀 수 있는 간단한 구현 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭔가를 차곡차곡 쌓고 마지막에 쌓은 것부터 처리해야 되는 식의 문제 -&amp;gt; 스택을 생각하자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;풀이&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 인형을 뽑을 때마다 인형을 스택에 추가&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 스택의 길이가 2 이상일 때 맨 뒤에 있는 인형 2개가 같으면 제거해야 하므로 stack pop 2번 후 ans += 2를 해준다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1655212166369&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(board, moves):
    ans = 0
    stack = []
    for m in moves:
        for i in range(len(board)):       
            if board[i][m-1]:
                stack.append(board[i][m-1])
                board[i][m-1] = 0

                if len(stack) &amp;gt; 1:
                    if stack[-1] == stack[-2]:
                        stack.pop(); stack.pop()
                        ans += 2
                break
                
    return ans&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Agorithm/프로그래머스</category>
      <category>algorithm</category>
      <category>Python</category>
      <category>알고리즘</category>
      <category>크레인 인형뽑기 게임</category>
      <category>파이썬</category>
      <category>프로그래머스</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1121</guid>
      <comments>https://jinho-study.tistory.com/1121#entry1121comment</comments>
      <pubDate>Tue, 14 Jun 2022 22:22:59 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 Level 1 숫자 문자열과 영단어</title>
      <link>https://jinho-study.tistory.com/1120</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;풀이&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 내장함수 replace를 사용하면 아주 쉽게 풀 수 있다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655211228202&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(s):
    li = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']
    for i in range(10):
        s = s.replace(li[i], str(i))
    
    return int(s)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Agorithm/프로그래머스</category>
      <category>algorithm</category>
      <category>Python</category>
      <category>숫자 문자열과 영단어</category>
      <category>알고리즘</category>
      <category>파이썬</category>
      <category>프로그래머스</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1120</guid>
      <comments>https://jinho-study.tistory.com/1120#entry1120comment</comments>
      <pubDate>Tue, 14 Jun 2022 22:05:01 +0900</pubDate>
    </item>
    <item>
      <title>프로그래머스 Level 1 키패드 누르기</title>
      <link>https://jinho-study.tistory.com/1119</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;전에는 알고리즘 문제를 설렁설렁 어떻게든 풀기만 하는 느낌으로 넘어갔었는데, 이번에는 다시 공부한다는 생각으로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그래머스 문제들을 풀어보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클린코드도 복습하는 겸 나름 최대한 보기 좋게 짜보려고 한다.&lt;span&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;size18&quot;&gt;&lt;b&gt;풀이)&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 거리를 구하기 위해서 우선 1: [0,0], 2:[0, 1] ... 9:[2, 2], 0:[3, 1]과 같이 숫자를 좌표로 변환해준다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;2. 숫자와 왼손, 오른손과의 거리를 구한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 그 이후는 각 상황에 맞게 ans에 'L', 'R'을 더하고 왼손과 오른손의 위치를 업데이트해주면 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1, 4, 7 -&amp;gt; 왼손, 3, 6, 9 -&amp;gt; 오른손, 2, 5, 8, 0 -&amp;gt; 왼손과 오른손 중 가까운 손&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1655049248173&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def get_idx(n):
    n = 10 if n == 0 else n-1
    
    return [n//3, n%3]

def get_distance(n1, n2):
    d1, d2 = get_idx(n1), get_idx(n2)
    d = abs(d1[0] - d2[0]) + abs(d1[1] - d2[1])

    return d
    
def get_nexthand(left, right, num, hand):
    if num in [1, 4, 7]:
        return 'left'
    elif num in [3, 6, 9]:
        return 'right'
    else:
        dl = get_distance(left, num)
        dr = get_distance(right, num)
        if dl &amp;lt; dr:
            return 'left'
        elif dl &amp;gt; dr:
            return 'right'
        elif dl == dr:
            return hand
    
def solution(numbers, hand):    
    left, right = 10, 12
    ans = ''
    
    for num in numbers:
        nexthand = get_nexthand(left, right, num, hand)
        if nexthand == &quot;left&quot;:
            left = num
            ans += 'L'
        else:
            right = num
            ans += 'R'
    
    return ans&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Agorithm/프로그래머스</category>
      <category>algorithm</category>
      <category>Python</category>
      <category>알고리즘</category>
      <category>카카오</category>
      <category>키패드 누르기</category>
      <category>파이썬</category>
      <category>프로그래머스</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1119</guid>
      <comments>https://jinho-study.tistory.com/1119#entry1119comment</comments>
      <pubDate>Mon, 13 Jun 2022 01:32:24 +0900</pubDate>
    </item>
    <item>
      <title>10장 클래스</title>
      <link>https://jinho-study.tistory.com/1118</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TIL (Today I Learned)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;2022.03.08&lt;/span&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;&lt;span style=&quot;color: #ef6f53;&quot;&gt;10장 클래스&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;책에서 기억하고 싶은 내용을 써보세요.&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;클래스는 작아야 한다! -&amp;gt; 간결한 이름이 떠오르지 않는다면 클래스 필경 클래스 크기가 너무 커서 그렇다 (p.172)&lt;/li&gt;
&lt;li&gt;단일 책임 원칙(Single Responsibility Principle,&amp;nbsp; SRP) -&amp;gt; 클래스나 모듈을 변경할 이유는 하나뿐이어야 한다 (p.175)&lt;/li&gt;
&lt;li&gt;응집도(Cohesion) (p.177)&lt;br /&gt;클래스는 인스턴스 변수 수가 작아야 한다. 각 클래스 메서드는 클래스 인스턴스 변수를 하나 이상 사용해야 한다.&lt;br /&gt;일반적으로 메서드가 변수를 더 많이 사용할수록 메서드와 클래스는 응집도가 높다. 응집도가 높다는 말은 클래스에 속한 메서드와 변수가 서로 의존하며 논리적인 단위로 묶인다는 의미이기에, 우리는 응집도가 높은 클래스를 선호한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;함수를 작게, 매개변수 목록을 짧게라는 전략을 따르다 보면 때때로 몇몇 메서드만이 사용하는 인스턴스 변수가&lt;br /&gt;많아지는데, 이는 새로운 클래스로 쪼개야 한다는 신호이다. 응집도가 높아지도록 변수와 메서드를 적절히 분리해 새로운 클래스 두세 개로 쪼개도록 하자 (p.178)&lt;/li&gt;
&lt;li&gt;개방-폐쇄 원칙(Open-Closed Principle, OCP) -&amp;gt; 클래스는 확장에 개방적이고 수정에 폐쇄적이어야 한다 (p.188)&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #202124; letter-spacing: 0px;&quot;&gt;의존관계 역전 원칙(Dependency Inversion Principle, DIP) -&amp;gt; 클래스는 상세한 구현이 아니라 추상화에 의존해야 한다&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #202124; letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&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;오늘 읽은 소감은? 떠오르는 생각을 가볍게 적어보세요&lt;/b&gt;&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;클래스를 구현해본 적도 없었고, 변수와 메서드의 응집도를 높일수록 클래스가 작게 쪼개질 수밖에 없겠구나라는&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>책 리뷰/클린코드</category>
      <category>개발</category>
      <category>노개북</category>
      <category>노마드코더</category>
      <category>코딩</category>
      <category>클래스</category>
      <category>클린코드</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1118</guid>
      <comments>https://jinho-study.tistory.com/1118#entry1118comment</comments>
      <pubDate>Tue, 8 Mar 2022 13:51:25 +0900</pubDate>
    </item>
    <item>
      <title>9장 단위 테스트</title>
      <link>https://jinho-study.tistory.com/1117</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TIL (Today I Learned)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;2022.03.06&lt;/span&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;&lt;span style=&quot;color: #ef6f53;&quot;&gt;9장 단위 테스트&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;책에서 기억하고 싶은 내용을 써보세요.&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;TDD 법칙 세 가지&lt;br /&gt;- 첫째 법칙: 실패하는 단위 테스트를 작성할 때까지 실제 코드를 작성하지 않는다.&lt;br /&gt;- 둘째 법칙: 컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트를 작성한다.&lt;br /&gt;- 셋째 법칙: 현재 실패하는 테스트를 통과할 정도로만 실제 코드를 작성한다.&lt;/li&gt;
&lt;li&gt;테스트 코드는 실제 코드 못지않게 중요하다 -&amp;gt; 테스트 코드가 복잡할수록 실제 코드를 짜는 시간보다 테스트 케이스를 추가하는 시간이 더 걸리기 십상이다 (p.156)&lt;/li&gt;
&lt;li&gt;테스트는 유연성, 유지보수성, 재사용성을 제공한다 -&amp;gt; 테스트 케이스가 있으면 &lt;b&gt;변경&lt;/b&gt;이 쉬워지기 때문이다 (p.157)&lt;/li&gt;
&lt;li&gt;테스트 코드도 가독성이 좋아야 한다. 어쩌면 가독성은 실제 코드보다 테스트 코드에 더더욱 중요하다 (p.158)&lt;/li&gt;
&lt;li&gt;이중 표준 -&amp;gt; 실제 환경에서는 절대로 안 되지만 테스트 환경에서는 전혀 문제없는 방식이 있다. 대게 메모리나 CPU 효율과 관련 있는 경우다. 코드의 깨끗함과는 철저히 무관하다 (p.164)&lt;/li&gt;
&lt;li&gt;개념 당 assert 문 수를 최소로 줄여라 -&amp;gt; 때로는 함수 하나에 여러 assert 문을 넣을 수도 있지만, assert문 개수는 최대한 줄이는 것이 좋다 (p.165)&lt;/li&gt;
&lt;li&gt;테스트 함수 하나는 개념 하나만 테스트하라 (p.166)&lt;/li&gt;
&lt;li&gt;깨끗한 테스트는 F.I.R.S.T 규칙을 따르는데&lt;br /&gt;- 빠르게(Fast): 테스트는 빨라야 한다.&lt;br /&gt;- 독립적으로(Independent): 각 테스트는 서로 의존하면 안 된다.&lt;br /&gt;- 반복 가능하게(Repeatable): 테스트는 어떤 환경에서도 반복 가능해야 한다.&lt;br /&gt;- 자가 검증하는(Self-Validating): 테스트는 부울 값으로 결과를 내야 한다.&lt;br /&gt;- 적시에(Timely): 테스트는 적시에 작성해야 한다.&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;오늘 읽은 소감은? 떠오르는 생각을 가볍게 적어보세요&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;/p&gt;</description>
      <category>책 리뷰/클린코드</category>
      <category>개발자</category>
      <category>노개북</category>
      <category>노마드코더</category>
      <category>단위 테스트</category>
      <category>코딩</category>
      <category>클린코드</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1117</guid>
      <comments>https://jinho-study.tistory.com/1117#entry1117comment</comments>
      <pubDate>Sun, 6 Mar 2022 14:14:10 +0900</pubDate>
    </item>
    <item>
      <title>7장 오류 처리</title>
      <link>https://jinho-study.tistory.com/1116</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TIL (Today I Learned)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;2022.03.04&lt;/span&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;&lt;span style=&quot;color: #ef6f53;&quot;&gt;7장 오류 처리&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;책에서 기억하고 싶은 내용을 써보세요.&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;오류 코드보다 예외를 사용하라 -&amp;gt; 논리가 오류 처리 코드와 뒤썩이지 않으므로 호출자 코드가 깔끔해진다 (p.130)&lt;/li&gt;
&lt;li&gt;Try-Catch-Finally 문부터 작성하라 -&amp;gt; try블록에서 무슨 일이 생기든 호출자가 기대하는 상태를 정의하기 쉬어진다(p.132)&lt;/li&gt;
&lt;li&gt;미확인(unchecked) 예외를 사용하라 -&amp;gt; 확인된 예외를 사용할 경우, 하위 단계에서 코드를 변경하면 상위 단계 메서드 선언부를 전부 고쳐야 하는 경우가 생길 수도 있다 (p.133)&lt;/li&gt;
&lt;li&gt;예외에 의미를 제공하라 (p.135)&lt;/li&gt;
&lt;li&gt;특수 사례 패턴 -&amp;gt; 클래스를 만들거나 객체를 조작해 특수 사례를 처리하는 방식 -&amp;gt; 클래스가 예외적인 상황을 캡슐화해서 처리하므로, 클라이언트 코드가 예외적인 상황을 처리할 필요가 없어진다 (p.137)&lt;/li&gt;
&lt;li&gt;null을 반환하지 마라 -&amp;gt; 메서드에서 null을 반환하고픈 유혹이 든다면 그 대신 예외를 던지거나 특수 사례 객체를 반환하는 것이 좋다. (p.137)&lt;/li&gt;
&lt;li&gt;null을 전달하지 마라 -&amp;gt; 정상적인 인수로 null을 기대하는 API가 아니라면 메서드로 null을 전달하는 코드는 최대한 피한다 -&amp;gt; 대다수 프로그래밍 언어는 호출자가 실수로 넘기는 null을 적절히 처리하는 방법이 없다. 그러니까 애초에 null을 전달하지 말도록 하자 (p.140)&lt;/li&gt;
&lt;li&gt;오류 처리를 프로그램 논리와 분리하면 독립적인 추론이 가능해지며 코드 유지보수성도 크게 높아진다 (p.142)&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;오늘 읽은 소감은? 떠오르는 생각을 가볍게 적어보세요&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;다음에 프로젝트를 하게 된다면 오늘 읽은 내용을 생각하면서 오류를 잘 분리하여 처리해봐야겠다.&lt;/p&gt;</description>
      <category>책 리뷰/클린코드</category>
      <category>개발자</category>
      <category>노개북</category>
      <category>노마드코더</category>
      <category>오류 처리</category>
      <category>코딩</category>
      <category>클린코드</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1116</guid>
      <comments>https://jinho-study.tistory.com/1116#entry1116comment</comments>
      <pubDate>Fri, 4 Mar 2022 14:26:40 +0900</pubDate>
    </item>
    <item>
      <title>6장 객체와 자료구조</title>
      <link>https://jinho-study.tistory.com/1115</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TIL (Today I Learned)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;2022.03.01&lt;/span&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;&lt;span style=&quot;color: #ef6f53;&quot;&gt;6장 객체와 자료구조&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;책에서 기억하고 싶은 내용을 써보세요.&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;변수 사이에 함수라는 계층을 넣는다고 구현이 감춰지지는 않는다. 구현을 감추려면 추상화가 필요하다 (p.119)&lt;/li&gt;
&lt;li&gt;추상 인터페이스를 제공해 사용자가 구현을 모른 채 자료의 핵심을 조작할 수 있어야 진정한 의미의 클래스다 (p.119)&lt;/li&gt;
&lt;li&gt;디미터 법칙 -&amp;gt; 모듈은 자신이 조작하는 객체의 속사정을 몰라야 한다 (p.123)&lt;/li&gt;
&lt;li&gt;잡종 구조 -&amp;gt; 절반은 객체, 절반은 자료 구조인 구조 -&amp;gt; 함수나 타입을 보호할지 공개할지 확신하지 못해서 나오는 어중간한 설계인데, 되도록 피하도록 하자&lt;/li&gt;
&lt;li&gt;새로운 자료 타입을 추가하는 유연성이 필요하면 객체, 새로운 동작을 추가하는 유연성이 필요하면 자료구조와 절차적인 코드가 더 적합하다 (p.128)&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;오늘 읽은 소감은? 떠오르는 생각을 가볍게 적어보세요&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;우수한 소프트웨어 개발자는 편견 없이 직면한 문제에 최적인 해결책을 선택한다&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 개발자가 해야 하는 것이 &lt;b&gt;문제 해결&lt;/b&gt;인데 그에 딱 맞는 말인 것 같다.&amp;nbsp;&lt;/p&gt;</description>
      <category>책 리뷰/클린코드</category>
      <category>개발자</category>
      <category>노개북</category>
      <category>노마드코더</category>
      <category>코딩</category>
      <category>클린코드</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1115</guid>
      <comments>https://jinho-study.tistory.com/1115#entry1115comment</comments>
      <pubDate>Tue, 1 Mar 2022 23:22:56 +0900</pubDate>
    </item>
    <item>
      <title>5장 형식 맞추기</title>
      <link>https://jinho-study.tistory.com/1114</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TIL (Today I Learned)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;2022.02.28&lt;/span&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;&lt;span style=&quot;color: #ef6f53;&quot;&gt;5장 형식 맞추기&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;책에서 기억하고 싶은 내용을 써보세요.&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;맨 처음 잡아놓은 구현 스타일과 가독성 수준은 유지보수 용이성과 확장성에 계속 영향을 미친다 (p.96)&lt;/li&gt;
&lt;li&gt;신문 기사처럼 작성하라(고차원 개념과 알고리즘에서 저차원 함수와 세부 내역 순서로 묘사하자) (p.97)&lt;/li&gt;
&lt;li&gt;줄 바꿈이 개념을 분리한다면 세로 밀집도는 연관성을 의미함 -&amp;gt; 밀접한 코드는 세로로 가까이 놓여야 함 (p.100)&lt;/li&gt;
&lt;li&gt;변수는 사용하는 위치에 최대한 가까이 선언, 인스턴스 변수는 클래스 맨 처음에 선언 (p.101, 103)&lt;/li&gt;
&lt;li&gt;한 함수가 다른 함수를 호출한다면 두 함수는 세로로 가까이 배치 -&amp;gt; 가능하면 호출하는 함수를 위로 배치 (p.103)&lt;/li&gt;
&lt;li&gt;개념적인 친화도가 높을수록 코드를 가까이 배치한다 (p.106)&lt;/li&gt;
&lt;li&gt;코드 스타일은 일관적이고 매끄러워야 한다. 한 소스 파일에서 봤던 형식이 다른 소스 파일에도 쓰이리라는 신뢰감을 독자에게 줘야 한다 (p.114)&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;오늘 읽은 소감은? 떠오르는 생각을 가볍게 적어보세요&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5장은 코드 형식에 대한 이야기를 하고 있다. 전에 42 Seoul에 참여할 때 코드 형식 때문에 고민했던 일이 기억나서&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;것 같지가 않다.&amp;nbsp; &amp;nbsp;&lt;/p&gt;</description>
      <category>책 리뷰/클린코드</category>
      <category>개발자</category>
      <category>노개북</category>
      <category>노마트코더</category>
      <category>코딩</category>
      <category>클린코드</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1114</guid>
      <comments>https://jinho-study.tistory.com/1114#entry1114comment</comments>
      <pubDate>Tue, 1 Mar 2022 03:17:19 +0900</pubDate>
    </item>
    <item>
      <title>4장 주석</title>
      <link>https://jinho-study.tistory.com/1113</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TIL (Today I Learned)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;2022.02.27&lt;/span&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;&lt;span style=&quot;color: #ef6f53;&quot;&gt;4장 주석&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;책에서 기억하고 싶은 내용을 써보세요.&lt;/b&gt;&lt;b&gt;&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;코드만이 자기가 하는 일을 진실되게 말한다 (p.69)&lt;/li&gt;
&lt;li&gt;주석은 나쁜 코드를 보완하지 못한다 -&amp;gt; 주석을 가능한 줄이도록 꾸준히 노력해야 한다 (p.69)&lt;/li&gt;
&lt;li&gt;코드로 의도를 표현하라 (p.70)&lt;/li&gt;
&lt;li&gt;이해가 안 되어 다른 모듈까지 뒤져야 하는 주석은 독자와 제대로 소통하지 못하는 주석이다 (p.76)&lt;/li&gt;
&lt;li&gt;주석으로 처리한 코드는 밉상이다 -&amp;gt; 웬만하면 지우자 (p.86)&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;오늘 읽은 소감은? 떠오르는 생각을 가볍게 적어보세요&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4장은 주석을 만약 쓰게 된다면 이런 경우에 사용하자라는 내용을 담고 있다. 법적 정보, 기본적 정보 제공, 의도 설명,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경고, TODO, 강조 등등이 좋은 주석이고 나머지는 안 좋은 주석이다. 이런 것들은 코드에 대한 설명보다는 정보를&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;주석은 나쁜 코드를 보완하지 못한다&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;</description>
      <category>책 리뷰/클린코드</category>
      <category>개발자</category>
      <category>노개북</category>
      <category>노마드코더</category>
      <category>코딩</category>
      <category>클린코드</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1113</guid>
      <comments>https://jinho-study.tistory.com/1113#entry1113comment</comments>
      <pubDate>Sun, 27 Feb 2022 22:44:39 +0900</pubDate>
    </item>
    <item>
      <title>백준 알고리즘 14501번 퇴사(python)</title>
      <link>https://jinho-study.tistory.com/1112</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 아래와 같이 첫날부터 확인하는 방식으로 풀었다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1645494119013&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;N = int(input())
li = [list(map(int, input().split())) for _ in range(N)]
dp = [0 for _ in range(N+1)]

for i in range(N):
    for j in range(i + li[i][0], N+1):
        if dp[j] &amp;lt; dp[i] + li[i][1]:
            dp[j] = dp[i] + li[i][1]

print(dp[-1])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;통과되긴 해도 일단 반복문이 2개라 아래 사진과 같이 무의미한 동작이 상당히 많다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;422&quot; data-origin-height=&quot;247&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VH7dt/btrt0L7hgkj/1OKOwkKEJUGcKnQqqMq0W1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VH7dt/btrt0L7hgkj/1OKOwkKEJUGcKnQqqMq0W1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VH7dt/btrt0L7hgkj/1OKOwkKEJUGcKnQqqMq0W1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVH7dt%2Fbtrt0L7hgkj%2F1OKOwkKEJUGcKnQqqMq0W1%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;186&quot; height=&quot;109&quot; data-origin-width=&quot;422&quot; data-origin-height=&quot;247&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복문 하나로 끝낼 방법이 없을까 하다가 그냥 마지막 날부터 확인하면 되겠다는 생각이 들어 아래와 같이 수정했다.&lt;/p&gt;
&lt;pre id=&quot;code_1645494292672&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;N = int(input())
li = [list(map(int, input().split())) for _ in range(N)]
dp = [0 for _ in range(N+1)]

for i in range(N-1, -1, -1):
    if i + li[i][0] &amp;gt; N:
        dp[i] = dp[i+1]
    else:
        dp[i] = max(dp[i+1], li[i][1] + dp[i + li[i][0]])
    
print(dp[0])&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Agorithm/백준 알고리즘</category>
      <category>14501</category>
      <category>Python</category>
      <category>다이나믹프로그래밍</category>
      <category>백준</category>
      <category>백준알고리즘</category>
      <category>퇴사</category>
      <category>파이썬</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1112</guid>
      <comments>https://jinho-study.tistory.com/1112#entry1112comment</comments>
      <pubDate>Tue, 22 Feb 2022 10:47:18 +0900</pubDate>
    </item>
    <item>
      <title>3장 함수</title>
      <link>https://jinho-study.tistory.com/1111</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TIL (Today I Learned)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;2022.02.22&lt;/span&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;&lt;span style=&quot;color: #ef6f53;&quot;&gt;3장 함수&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;/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;작게 만들어라! -&amp;gt; 중첩&amp;nbsp;구조가&amp;nbsp;생길 만큼&amp;nbsp;함수가&amp;nbsp;커져서는&amp;nbsp;안&amp;nbsp;된다 (p.42, 44)&lt;/li&gt;
&lt;li&gt;함수는&amp;nbsp;한&amp;nbsp;가지를&amp;nbsp;해야&amp;nbsp;한다.&amp;nbsp;그&amp;nbsp;한&amp;nbsp;가지를&amp;nbsp;잘해야&amp;nbsp;한다.&amp;nbsp;그&amp;nbsp;한&amp;nbsp;가지만을&amp;nbsp;해야&amp;nbsp;한다 (p.44)&lt;/li&gt;
&lt;li&gt;함수당&amp;nbsp;추상화&amp;nbsp;수준은&amp;nbsp;하나로! -&amp;gt; 일련의&amp;nbsp;TO&amp;nbsp;문단을&amp;nbsp;읽듯이&amp;nbsp;프로그램이&amp;nbsp;읽혀야&amp;nbsp;한다&amp;nbsp;(p.45, 46)&lt;/li&gt;
&lt;li&gt;서술적인&amp;nbsp;이름을&amp;nbsp;사용하라! (p.49)&lt;/li&gt;
&lt;li&gt;함수 인수 -&amp;gt; 인수는&amp;nbsp;개념을&amp;nbsp;이해하기&amp;nbsp;어렵게&amp;nbsp;만든다 -&amp;gt; 3개 이상부터는 좀... 그렇다 (p.50)&lt;/li&gt;
&lt;li&gt;명령과&amp;nbsp;조회를&amp;nbsp;분리하라! (p.56)&lt;/li&gt;
&lt;li&gt;오류&amp;nbsp;코드보다&amp;nbsp;예외를&amp;nbsp;사용하라! -&amp;gt; try&amp;nbsp;/&amp;nbsp;catch&amp;nbsp;블록을 별도&amp;nbsp;함수로&amp;nbsp;뽑아내는&amp;nbsp;편이&amp;nbsp;좋다.&amp;nbsp;(p.57, 58)&lt;/li&gt;
&lt;li&gt;반복하지&amp;nbsp;마라! -&amp;gt; 중복은&amp;nbsp;소프트웨어에서&amp;nbsp;모든&amp;nbsp;악의&amp;nbsp;근원이다.&amp;nbsp;많은&amp;nbsp;원칙과&amp;nbsp;기법이&amp;nbsp;중복을&amp;nbsp;없애거나&amp;nbsp;제어할&amp;nbsp;목적으로&amp;nbsp;나왔다 (p.60)&lt;b&gt;&lt;/b&gt;&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;오늘 읽은 소감은? 떠오르는 생각을 가볍게 적어보세요&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;하나로 해라 등등이 다 포함하는 내용이라고 생각하기 때문이다. 그리고 인수 관련된 내용도 참 좋았는데 평소에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수에 인수를 3, 4개씩 박았던 기억이 있어서 앞으로는 주의해야겠다는 생각이 들었다.&lt;/p&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;</description>
      <category>책 리뷰/클린코드</category>
      <category>개발자</category>
      <category>노개북</category>
      <category>노마드코더</category>
      <category>코딩</category>
      <category>클린코드</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1111</guid>
      <comments>https://jinho-study.tistory.com/1111#entry1111comment</comments>
      <pubDate>Tue, 22 Feb 2022 09:26:57 +0900</pubDate>
    </item>
    <item>
      <title>2장 의미 있는 이름</title>
      <link>https://jinho-study.tistory.com/1110</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TIL (Today I Learned)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;2022.02.20&lt;/span&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;&lt;span style=&quot;color: #ef6f53;&quot;&gt;2장 의미 있는 이름&amp;nbsp;&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;/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;의도를 분명히 밝혀라 -&amp;gt; 따로 주석이 필요하다면 의도를 분명히 드러내지 못했다는 말이다 (p.22)&lt;/li&gt;
&lt;li&gt;그릇된 정보를 피하라 -&amp;gt; 일관성이 떨어지는 표기법은 그릇된 정보다 (p.24, 25)&lt;/li&gt;
&lt;li&gt;의미 있게 구분하라 -&amp;gt; 읽는 사람이 차이를 알도록 이름을 지어라 (p.25, 27)&lt;/li&gt;
&lt;li&gt;검색하기 쉬운 이름을 사용하라, 긴 이름이 짧은 이름보다, 검색하기 쉬운 이름이 상수보다 좋다&amp;nbsp;(p.28)&lt;/li&gt;
&lt;li&gt;단 이름 길이는 범위 크기에 비례해야 한다 (p.28)&lt;/li&gt;
&lt;li&gt;클래스 이름 -&amp;gt; 명사, 명사구(Customer, Account), 메서드 이름 -&amp;gt; 동사, 동사구(postPayment, deletePage) (p.32)&lt;/li&gt;
&lt;li&gt;한 개념에 한 단어를 사용하라 -&amp;gt; 메서드 이름은 독자적이고 일관적이어야 한다 (p.33)&lt;/li&gt;
&lt;li&gt;의미 있는 맥락을 추가하라 -&amp;gt; 맥락만 개선해도 함수 쪼개기가 쉬워지고 알고리즘도 좀 더 명확해진다 (p.35)&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;오늘 읽은 소감은? 떠오르는 생각을 가볍게 적어보세요&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;이렇게 짠 코드는 시간이 지나고 봤을 때 정확하게 무슨 동작을 하는 코드였는지 기억이 안나는 경우가 대부분이었다.&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;의도를 분명히 밝혀라&lt;/b&gt;이다. 생각해보면 함수나 클래스의 의도가 확실하지 않을수록 내&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주석이 길어졌던 것 같다. 주석이 필요 없는 코드가 1장에서 말했던 아름다운, 읽는 재미가 있는 코드가 아닐까 싶다!&amp;nbsp;&amp;nbsp;&lt;/p&gt;</description>
      <category>책 리뷰/클린코드</category>
      <category>개발자</category>
      <category>노개북</category>
      <category>노마드코더</category>
      <category>코딩</category>
      <category>클린코드</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1110</guid>
      <comments>https://jinho-study.tistory.com/1110#entry1110comment</comments>
      <pubDate>Sun, 20 Feb 2022 22:31:04 +0900</pubDate>
    </item>
    <item>
      <title>1장 깨끗한 코드</title>
      <link>https://jinho-study.tistory.com/1109</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;TIL (Today I Learned)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;2022.02.19&lt;/span&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;&lt;span style=&quot;color: #ef6f53;&quot;&gt;1장 깨끗한 코드&amp;nbsp;&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;/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;깨끗한&amp;nbsp;코드는&amp;nbsp;&amp;lsquo;보기에&amp;nbsp;즐거운&amp;rsquo;&amp;nbsp;코드다.&amp;nbsp;(p.9)&lt;/li&gt;
&lt;li&gt;깨끗한&amp;nbsp;코드는&amp;nbsp;잘&amp;nbsp;쓴&amp;nbsp;문장처럼&amp;nbsp;읽힌다.&amp;nbsp;깨끗한&amp;nbsp;코드는&amp;nbsp;결코&amp;nbsp;설계자의&amp;nbsp;의도를&amp;nbsp;숨기지&amp;nbsp;않는다. (p.10)&lt;/li&gt;
&lt;li&gt;깨끗한&amp;nbsp;코드는&amp;nbsp;작성자가&amp;nbsp;아닌&amp;nbsp;사람도&amp;nbsp;읽기&amp;nbsp;쉽고&amp;nbsp;고치기&amp;nbsp;쉽다. (p.11)&lt;/li&gt;
&lt;li&gt;깨끗한&amp;nbsp;코드는&amp;nbsp;주의&amp;nbsp;깊게&amp;nbsp;작성한&amp;nbsp;코드다. (p.12)&lt;/li&gt;
&lt;li&gt;중복을 피하라. 한 기능만 수행하라. 제대로 표현하라. 작게 추상화하라. (p.14)&lt;/li&gt;
&lt;li&gt;새 코드를 짜면서 우리는 끊임없이 기존 코드를 읽는다. 비율이 이렇게 높으므로 읽기 쉬운 코드가 매우 중요하다. (p.18)&lt;/li&gt;
&lt;li&gt;보이스카우트 규칙: 캠핑장은 처음 왔을 때보다 더 깨끗하게 해놓고 떠나라&lt;b&gt;.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(p.19)&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;오늘 읽은 소감은? 떠오르는 생각을 가볍게 적어보세요&lt;/b&gt;&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;문장처럼 읽힌다&quot; 였던 것 같다. 평소에 코드를 짤 때 구현만 하고 리팩토링을 안 해본 나로선 배울 수 있는 것이 엄청 많을 책인 것 같고,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나중에 직장 동료들이 내 코드를 봤을 때 짜증나고 답답하기보다는&amp;nbsp;즐거운 게 좋지 않을까라는 생각도 들었다.&lt;/p&gt;</description>
      <category>책 리뷰/클린코드</category>
      <category>개발자</category>
      <category>노개북</category>
      <category>노마드코더</category>
      <category>코딩</category>
      <category>클린코드</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1109</guid>
      <comments>https://jinho-study.tistory.com/1109#entry1109comment</comments>
      <pubDate>Sun, 20 Feb 2022 03:43:42 +0900</pubDate>
    </item>
    <item>
      <title>노마드 코더 클린코드 시작</title>
      <link>https://jinho-study.tistory.com/1108</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2256&quot; data-origin-height=&quot;1504&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wVHjk/btrtG7J354Q/rbWFKo4Qtb1GHFcFCkjpPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wVHjk/btrtG7J354Q/rbWFKo4Qtb1GHFcFCkjpPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wVHjk/btrtG7J354Q/rbWFKo4Qtb1GHFcFCkjpPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwVHjk%2FbtrtG7J354Q%2FrbWFKo4Qtb1GHFcFCkjpPK%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;2256&quot; height=&quot;1504&quot; data-origin-width=&quot;2256&quot; data-origin-height=&quot;1504&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;클린코드 책 인증~&lt;/b&gt;&lt;/p&gt;</description>
      <category>책 리뷰/클린코드</category>
      <category>개발자</category>
      <category>노개북</category>
      <category>노마드북클럽</category>
      <category>코딩</category>
      <category>클린코드</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1108</guid>
      <comments>https://jinho-study.tistory.com/1108#entry1108comment</comments>
      <pubDate>Fri, 18 Feb 2022 15:03:26 +0900</pubDate>
    </item>
    <item>
      <title>백준 알고리즘 9311번 Robot in a Maze(python)</title>
      <link>https://jinho-study.tistory.com/1107</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;간단한 BFS 문제이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;S에서 G로 가는 최단경로의 길이를 출력해주면 된다. 경로가 없다면 No Exit 출력&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1640101274586&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from collections import deque

def bfs(i, j):
    d = [(-1, 0), (1, 0), (0, -1), (0, 1)]
    q = deque()
    q.append((i, j))
    check[i][j] = 0
    while q:
        y, x = q.popleft()
        for dy, dx in d:
            Y, X = y+dy, x+dx
            if (0 &amp;lt;= Y &amp;lt; R) and (0 &amp;lt;= X &amp;lt; C) and graph[Y][X] != 'X' and check[Y][X] == -1:
                check[Y][X] = check[y][x] + 1
                if graph[Y][X] == 'G':
                    return check[Y][X]
                q.append((Y, X))
    return None

for _ in range(int(input())):
    R, C = map(int, input().split())
    graph = [input() for a in range(R)]
    check = [[-1]*C for b in range(R)]
    
    for i in range(R):
        for j in range(C):
            if graph[i][j] == 'S':
                ans = bfs(i, j)
                print(f&quot;Shortest Path: {ans}&quot; if ans else &quot;No Exit&quot;)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Agorithm/백준 알고리즘</category>
      <category>9311</category>
      <category>BFS</category>
      <category>Python</category>
      <category>Robot in a Maze</category>
      <category>그래프 이론</category>
      <category>그래프 탐색</category>
      <category>너비 우선 탐색</category>
      <category>백준</category>
      <category>백준 알고리즈</category>
      <category>파이썬</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1107</guid>
      <comments>https://jinho-study.tistory.com/1107#entry1107comment</comments>
      <pubDate>Wed, 22 Dec 2021 00:44:12 +0900</pubDate>
    </item>
    <item>
      <title>pythonanywhere에 flask 사이트 배포하기</title>
      <link>https://jinho-study.tistory.com/1106</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;pythonanywhere 사이트에서 flask로 만든 사이트를 쉽게 배포할 수 있다.&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1 &lt;a href=&quot;https://www.pythonanywhere.com&quot;&gt;https://www.pythonanywhere.com에&lt;/a&gt; 접속하여 회원가입 후 로그인&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2 소스파일을 압축한 다음 아래 그림과 같이 업로드하고, Open Bash console here를 클릭&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/29765855/145246021-d73c32d3-258f-477c-82c3-a35397527315.png&quot; alt=&quot;image&quot; width=&quot;594&quot; height=&quot;300&quot; /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3 압축 해제 -&amp;gt; 가상 환경 생성 -&amp;gt; 가상 환경에 필요한 라이브러리를 설치&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/29765855/145246735-5dd7f448-42b6-47e1-b089-33229cec68c0.png&quot; alt=&quot;image&quot; width=&quot;594&quot; height=&quot;590&quot; /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;4 Web으로 넘어가 Add a new web app -&amp;gt; Next -&amp;gt; Manual configuration -&amp;gt; Python 3.7 -&amp;gt; Next 순으로 진행&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/29765855/145247238-022b060d-ca74-4cb2-93a4-2fbb8aa6444b.png&quot; alt=&quot;image&quot; width=&quot;638&quot; height=&quot;375&quot; /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;5 그러면 설정이 가능해지는데 소스 코드, 가상 환경 경로를 수정해준다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/29765855/145247867-82dca05a-77ee-45f1-817c-91dd65089939.png&quot; alt=&quot;image&quot; width=&quot;652&quot; height=&quot;307&quot; /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;6 WSGI configuration file을 클릭&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/29765855/145248371-86f2c884-21a8-4e1c-b79d-59f1aeb9adb6.png&quot; alt=&quot;image&quot; width=&quot;633&quot; height=&quot;284&quot; /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;괄호 친 코드를 주석 처리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/29765855/145248516-4561c20e-fff6-4c12-8c77-66efed89a8e8.png&quot; alt=&quot;image&quot; width=&quot;733&quot; height=&quot;363&quot; /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;맨 아래 FLASK 부분 아래와 같이 수정하고 Save&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/29765855/145248886-f8e6fe5a-5ce3-4ab8-9906-bb4a773b372d.png&quot; alt=&quot;image&quot; width=&quot;736&quot; height=&quot;351&quot; /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;7 Web으로 넘어와 Reload를 클릭하면 끝이다!&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/29765855/145249820-c66d102d-8990-4cbb-86fb-9252e5a79188.png&quot; alt=&quot;image&quot; width=&quot;632&quot; height=&quot;292&quot; /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;잘 접속되는 것을 확인할 수 있다!!&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1208&quot; data-origin-height=&quot;1199&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWdU1X/btrns37u3Up/U6ktccfqbMF9LPV69XsJt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWdU1X/btrns37u3Up/U6ktccfqbMF9LPV69XsJt0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWdU1X/btrns37u3Up/U6ktccfqbMF9LPV69XsJt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWdU1X%2Fbtrns37u3Up%2FU6ktccfqbMF9LPV69XsJt0%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;571&quot; height=&quot;567&quot; data-origin-width=&quot;1208&quot; data-origin-height=&quot;1199&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;github: &lt;a href=&quot;https://github.com/kimjinho1/flask-study/tree/master/pythonanywhere&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/kimjinho1/flask-study/tree/master/pythonanywhere&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1638983795482&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 - kimjinho1/flask-study: Flask study&quot; data-og-description=&quot;Flask study. Contribute to kimjinho1/flask-study development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/kimjinho1/flask-study/tree/master/pythonanywhere&quot; data-og-url=&quot;https://github.com/kimjinho1/flask-study&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b8Bvcb/hyMC9bV7qH/K5Lkp80VYxzKiPIoVKE7Lk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/kimjinho1/flask-study/tree/master/pythonanywhere&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/kimjinho1/flask-study/tree/master/pythonanywhere&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b8Bvcb/hyMC9bV7qH/K5Lkp80VYxzKiPIoVKE7Lk/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 - kimjinho1/flask-study: Flask study&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Flask study. Contribute to kimjinho1/flask-study 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;</description>
      <category>웹</category>
      <category>FLASK</category>
      <category>Python</category>
      <category>pythonanywhere</category>
      <category>사이트 배포</category>
      <category>파이썬</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1106</guid>
      <comments>https://jinho-study.tistory.com/1106#entry1106comment</comments>
      <pubDate>Thu, 9 Dec 2021 02:10:22 +0900</pubDate>
    </item>
    <item>
      <title>MVC란?</title>
      <link>https://jinho-study.tistory.com/1105</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;MVC는 M&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;odel View Controller의 약자로 애플리케이션을 세 가지의 역할로 구분한 &lt;/span&gt;디자인 패턴 중 하나입니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Model: 데이터베이스&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;View: 사용자가 보는 화면(UI)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Controller: Model이 데이터를 어떻게 처리할지 알려주고 그 결과를 View에 전달해주는 역할&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;-&amp;gt; 접근 URL에 따라 비즈니스 로직을 수행&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;&lt;span style=&quot;color: #666666;&quot;&gt;아래의 그림처럼 사용자가 Controller를 조작하면 &lt;/span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;Model을 통해서&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;데이터를 가져오고 그 결과를 View에 전달해서 &lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #666666;&quot;&gt;사용자에게 화면을 보여주는 방식입니다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;350&quot; data-origin-height=&quot;160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4T7Zz/btrmjaBfJXr/2b1kh38SCZvpmcpkjblnqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4T7Zz/btrmjaBfJXr/2b1kh38SCZvpmcpkjblnqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4T7Zz/btrmjaBfJXr/2b1kh38SCZvpmcpkjblnqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4T7Zz%2FbtrmjaBfJXr%2F2b1kh38SCZvpmcpkjblnqk%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;174&quot; data-origin-width=&quot;350&quot; data-origin-height=&quot;160&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>웹</category>
      <category>controller</category>
      <category>Model</category>
      <category>MVC</category>
      <category>View</category>
      <category>디자인 패턴</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1105</guid>
      <comments>https://jinho-study.tistory.com/1105#entry1105comment</comments>
      <pubDate>Sun, 28 Nov 2021 19:41:21 +0900</pubDate>
    </item>
    <item>
      <title>도커파일(Dockerfile) 작성</title>
      <link>https://jinho-study.tistory.com/1104</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;도커파일(Dockerfile)이란 사용자가 도커 이미지를 쉽게 만들 수 있도록 도와주는 템플릿입니다.&lt;/b&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;b&gt;기본 명령어&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;FROM -&amp;gt; Dockerfile이 어떤 이미지를 base image로 사용할 것인지 명시해줌&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1637487482521&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# FROM &amp;lt;image&amp;gt;[:&amp;lt;tag&amp;gt;] [AS &amp;lt;name&amp;gt;]

# EX)
FROM ubuntu
FROM ubuntu:18.04
FROM nginx:latest AS ngx&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;COPY -&amp;gt; &amp;lt;src&amp;gt;의 파일, 디렉토리를 &amp;lt;dest&amp;gt; 경로에 복사&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1637487639358&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# COPY &amp;lt;src&amp;gt;... &amp;lt;dest&amp;gt;

# EX)
COPY 1.txt /test/1.txt
COPY my-dir /test-dir&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;RUN -&amp;gt; 명시한 커맨드를 도커 컨테이너에서 실행&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1637487747029&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# RUN &amp;lt;command&amp;gt;

# EX)
RUN pip install numpy
RUN pip install -r requirements.txt&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;CMD -&amp;gt; 명시한 커맨드를 도커 컨테이너가 시작될 때 실행&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 도커 이미지에서 하나의 CMD만 실행 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1637487916351&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# CMD &amp;lt;command&amp;gt;

# EX)
CMD python main.py&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;WORKDIR -&amp;gt; 이후 작성될 명령어를 컨테이너 내의 어떤 디렉토리에서 수행할 것인지를 명시해줌&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1637488035599&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# WORKDIR /path/to/workdir

# EX)
WORKDIR /home/test&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;ENV -&amp;gt; 컨테이너 내부에서 지속적으로 사용될 환경 변수의 값을 설정&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1637488186992&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# ENV &amp;lt;key&amp;gt; &amp;lt;value&amp;gt;
# ENV &amp;lt;key&amp;gt;=&amp;lt;value&amp;gt;

# EX)
ENV LANG ko_KR.UTF-8
ENV LC_ALL ko_KR.UTF-8&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;EXPOSE -&amp;gt; 컨테이너에서 뚫어줄 포트/프토로콜을 지정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로토콜을 지정하지 않으면 TCP가 디폴트로 설정됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1637488263209&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# EXPOSE &amp;lt;port&amp;gt;
# EXPOSE &amp;lt;port&amp;gt;/&amp;lt;protocol&amp;gt;

# EX)
EXPOSE 8080&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;b&gt;Dockerfile 작성 예시&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1637488483903&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# base image를 ubuntu 최신 버전으로 설정
FROM ubuntu:latest

#  apt-get update 명령어 실행
RUN apt-get update

# 도커 컨테이너가 시작될 때, &quot;Hello World!&quot; 출력
CMD [&quot;echo&quot;, &quot;Hello World!&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 Dockefile을 생성한 후 아래 명령어를 실행해주면 도커 이미지가 생성됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1637488907378&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker build -t test-image:v1.0.0 .&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명하자면 이름이 my-image고 태그가 v1.0.0인 이미지를 현재 경로에 있는 Dockerfile로부터&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1180&quot; data-origin-height=&quot;1021&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VqWgb/btrl00dyn33/z0dTVJkoaQkjVYhcKFvnv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VqWgb/btrl00dyn33/z0dTVJkoaQkjVYhcKFvnv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VqWgb/btrl00dyn33/z0dTVJkoaQkjVYhcKFvnv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVqWgb%2Fbtrl00dyn33%2Fz0dTVJkoaQkjVYhcKFvnv0%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;539&quot; data-origin-width=&quot;1180&quot; data-origin-height=&quot;1021&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;Hello World!가 출력되는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1637489168806&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run test-image:v1.0.0&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;827&quot; data-origin-height=&quot;59&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bROSBR/btrlY4AY6kn/OGxDKHKhEDz84WPNGs3EV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bROSBR/btrlY4AY6kn/OGxDKHKhEDz84WPNGs3EV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bROSBR/btrlY4AY6kn/OGxDKHKhEDz84WPNGs3EV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbROSBR%2FbtrlY4AY6kn%2FOGxDKHKhEDz84WPNGs3EV0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;575&quot; height=&quot;41&quot; data-origin-width=&quot;827&quot; data-origin-height=&quot;59&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>MLOps</category>
      <category>Docker</category>
      <category>docker build</category>
      <category>Dockerfile</category>
      <category>MLOps</category>
      <category>도커</category>
      <category>도커 파일</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1104</guid>
      <comments>https://jinho-study.tistory.com/1104#entry1104comment</comments>
      <pubDate>Sun, 21 Nov 2021 19:06:34 +0900</pubDate>
    </item>
    <item>
      <title>도커(Docker) 설치, 명령어</title>
      <link>https://jinho-study.tistory.com/1103</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 MLOps 공부 그 첫 번째 도커에 대해 간단히 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 도커는 리눅스의 응용 프로그램들을 프로세스 격리 기술들을 사용해 컨테이너로 실행하고 관리하는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오픈 소스 프로젝트입니다. 사용해보시면 느낌이 확 오는데 가상 머신에 비해 배포하는 것이 너무 편합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;도커 설치&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커를 리눅스 환경에 설치하는 방법은 아주 간단합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1637240038668&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo wget -qO- http://get.docker.com/ | sh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 명령어를 실행하면 바로 설치가 됩니다. 설치 후 아래 명령어를 실행시켰을 때 아래 이미지처럼 출력되면 끝입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1637240131379&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo docker run hello-world&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1052&quot; data-origin-height=&quot;818&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mNdV3/btrlxKQfs3r/kyl4h5NjyUyJpTkr6rZiK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mNdV3/btrlxKQfs3r/kyl4h5NjyUyJpTkr6rZiK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mNdV3/btrlxKQfs3r/kyl4h5NjyUyJpTkr6rZiK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmNdV3%2FbtrlxKQfs3r%2Fkyl4h5NjyUyJpTkr6rZiK1%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;402&quot; height=&quot;313&quot; data-origin-width=&quot;1052&quot; data-origin-height=&quot;818&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;명령어&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;docker run -&amp;gt; 도커 컨테이너를 실행&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 옵션들이 있는데 아래 명령어로 예시를 들자면&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1637243681777&quot; class=&quot;html xml&quot; style=&quot;margin: 20px auto 0px; display: block; overflow: auto; padding: 20px; color: #383a42; background: #f8f8f8; font-size: 14px; font-family: 'SF Mono', Menlo, Consolas, Monaco, monospace; border: 1px solid #ebebeb; line-height: 1.71; cursor: default; z-index: 1;&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run -it --name test ubuntu:latest&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-it -&amp;gt; 컨테이너를 실행시키는 동시에&lt;span&gt;&amp;nbsp;&lt;/span&gt;interactive 한&lt;span&gt;&amp;nbsp;&lt;/span&gt;터미널로 접속&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--name -&amp;gt; 컨테이너 id는 알아보기가 어렵기에 구분하기 쉽도록 이름을 지정&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1538&quot; data-origin-height=&quot;273&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bccN8o/btrlxKQhScg/1JlZk3FZJeOKfKxlYUMrt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bccN8o/btrlxKQhScg/1JlZk3FZJeOKfKxlYUMrt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bccN8o/btrlxKQhScg/1JlZk3FZJeOKfKxlYUMrt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbccN8o%2FbtrlxKQhScg%2F1JlZk3FZJeOKfKxlYUMrt1%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;770&quot; height=&quot;137&quot; data-origin-width=&quot;1538&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;-it 옵션을 붙였기에 명령어 실행 후 새로운 터미널에 접속된 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1559&quot; data-origin-height=&quot;107&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E00un/btrlvBUngzB/xqMNovHQIXO1Qgskx6VI0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E00un/btrlvBUngzB/xqMNovHQIXO1Qgskx6VI0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E00un/btrlvBUngzB/xqMNovHQIXO1Qgskx6VI0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE00un%2FbtrlvBUngzB%2FxqMNovHQIXO1Qgskx6VI0k%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;641&quot; height=&quot;44&quot; data-origin-width=&quot;1559&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;컨테이너의 이름이 test1인 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;docker images -&amp;gt; 도커 이미지 리스트를 출력&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #777777;&quot;&gt;위에서 도커가 설치됐는지 확인할 때 생성한 hello-world 이미지와 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #777777;&quot;&gt;docker run 명령어 예시를 들 때 생성한 &lt;/span&gt;&lt;span style=&quot;color: #777777;&quot;&gt;ubuntu 이미지가 출력되는 것을 확인할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;784&quot; data-origin-height=&quot;119&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KCFcm/btrlsjfmCVx/DKNh4UDYpRU4UutKeBwHK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KCFcm/btrlsjfmCVx/DKNh4UDYpRU4UutKeBwHK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KCFcm/btrlsjfmCVx/DKNh4UDYpRU4UutKeBwHK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKCFcm%2FbtrlsjfmCVx%2FDKNh4UDYpRU4UutKeBwHK0%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;435&quot; height=&quot;66&quot; data-origin-width=&quot;784&quot; data-origin-height=&quot;119&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;docker ps -&amp;gt; 실행 중인 도커 컨테이너 리스트를 출력&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인을 위해 아래 명령어를 실행시키고 나서 docker ps 명령어를 실행하면&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1637244243561&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run -it -d --name test1 ubuntu:latest&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker run 명령어에서 -d 옵션은 백그라운드에서 컨테이너가 계속 실행되도록 해준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1180&quot; data-origin-height=&quot;150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kRyLG/btrlshBTdSO/PyDGZ3QrQ3sDP4rlvrtcC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kRyLG/btrlshBTdSO/PyDGZ3QrQ3sDP4rlvrtcC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kRyLG/btrlshBTdSO/PyDGZ3QrQ3sDP4rlvrtcC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkRyLG%2FbtrlshBTdSO%2FPyDGZ3QrQ3sDP4rlvrtcC1%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;569&quot; height=&quot;72&quot; data-origin-width=&quot;1180&quot; data-origin-height=&quot;150&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #777777;&quot;&gt;test1 컨테이너가 출력되는 것을 확인할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #777777;&quot;&gt;옵션으로 -a를 넣고 실행하면 실행되었던&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #777777;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #777777;&quot;&gt;컨테이너까지 출력이 됩니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1720&quot; data-origin-height=&quot;155&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5px5W/btrlvBGOiP8/VjmupX4GTLTOwFwOG3KnXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5px5W/btrlvBGOiP8/VjmupX4GTLTOwFwOG3KnXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5px5W/btrlvBGOiP8/VjmupX4GTLTOwFwOG3KnXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5px5W%2FbtrlvBGOiP8%2FVjmupX4GTLTOwFwOG3KnXk%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;706&quot; height=&quot;155&quot; data-origin-width=&quot;1720&quot; data-origin-height=&quot;155&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;size18&quot;&gt;&lt;b&gt;docker stop -&amp;gt; 실행 중인 도커 컨테이너를 중단&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker stop [컨테이너 id]와 같은 방식으로 사용하는데 id는 다 칠 필요 없고 겹치는 숫자가 없을 때까지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력해주면 됩니다. 예를 들어 1234, 1289 2개의 컨테이너가 있다면 123 까지만 처도 1234 컨테이너가 중단됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1155&quot; data-origin-height=&quot;215&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MuZyX/btrlwIyVLz9/rsuNJfM5kqnb09JFNvMSU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MuZyX/btrlwIyVLz9/rsuNJfM5kqnb09JFNvMSU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MuZyX/btrlwIyVLz9/rsuNJfM5kqnb09JFNvMSU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMuZyX%2FbtrlwIyVLz9%2FrsuNJfM5kqnb09JFNvMSU0%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;699&quot; height=&quot;130&quot; data-origin-width=&quot;1155&quot; data-origin-height=&quot;215&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;size18&quot;&gt;&lt;b&gt;docker rm -&amp;gt; 도커 컨테이너를 삭제&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1720&quot; data-origin-height=&quot;155&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5px5W/btrlvBGOiP8/VjmupX4GTLTOwFwOG3KnXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5px5W/btrlvBGOiP8/VjmupX4GTLTOwFwOG3KnXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5px5W/btrlvBGOiP8/VjmupX4GTLTOwFwOG3KnXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5px5W%2FbtrlvBGOiP8%2FVjmupX4GTLTOwFwOG3KnXk%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;706&quot; height=&quot;155&quot; data-origin-width=&quot;1720&quot; data-origin-height=&quot;155&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1702&quot; data-origin-height=&quot;217&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWba6t/btrlyp58x1Z/VChTj2pxgA1OhehgCp1R40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWba6t/btrlyp58x1Z/VChTj2pxgA1OhehgCp1R40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWba6t/btrlyp58x1Z/VChTj2pxgA1OhehgCp1R40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWba6t%2Fbtrlyp58x1Z%2FVChTj2pxgA1OhehgCp1R40%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;704&quot; height=&quot;90&quot; data-origin-width=&quot;1702&quot; data-origin-height=&quot;217&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;932&quot; data-origin-height=&quot;61&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SO8zK/btrlyiF08PO/v0JImES0tJ97R3eJV9UXEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SO8zK/btrlyiF08PO/v0JImES0tJ97R3eJV9UXEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SO8zK/btrlyiF08PO/v0JImES0tJ97R3eJV9UXEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSO8zK%2FbtrlyiF08PO%2Fv0JImES0tJ97R3eJV9UXEK%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;621&quot; height=&quot;41&quot; data-origin-width=&quot;932&quot; data-origin-height=&quot;61&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;test, test1 컨테이너를 중단하니 docker ps, 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;size18&quot;&gt;&lt;b&gt;docker rmi -&amp;gt; 도커 이미지 삭제&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1161&quot; data-origin-height=&quot;367&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cb2bxF/btrlyYtMkAj/qa2479PNhqi4SQqbAygwsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cb2bxF/btrlyYtMkAj/qa2479PNhqi4SQqbAygwsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cb2bxF/btrlyYtMkAj/qa2479PNhqi4SQqbAygwsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcb2bxF%2FbtrlyYtMkAj%2Fqa2479PNhqi4SQqbAygwsk%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;195&quot; data-origin-width=&quot;1161&quot; data-origin-height=&quot;367&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ubuntu 이미지가 삭제된 것을 확인할 수 있습니다.&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_1637240879844&quot; class=&quot;html xml&quot; style=&quot;margin: 20px auto 0px; display: block; overflow: auto; padding: 20px; color: #383a42; background: #f8f8f8; font-size: 14px; font-family: 'SF Mono', Menlo, Consolas, Monaco, monospace; border: 1px solid #ebebeb; line-height: 1.71; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get &quot;http://%2Fvar%2Frun%2Fdocker.sock/v1.24/images/json&quot;: dial unix /var/run/docker.sock: connect: permission denied&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 명령어를 실행시키시면 됩니다. 저는 이걸로&lt;span&gt;&amp;nbsp;&lt;/span&gt;해결됐습니다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;sudo chmod 666 /var/run/docker.sock&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>MLOps</category>
      <category>Docker</category>
      <category>docker images</category>
      <category>docker ps</category>
      <category>docker rm</category>
      <category>docker rmi</category>
      <category>docker run</category>
      <category>docker stop</category>
      <category>docker 설치</category>
      <category>도커</category>
      <category>도커 설치</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1103</guid>
      <comments>https://jinho-study.tistory.com/1103#entry1103comment</comments>
      <pubDate>Thu, 18 Nov 2021 23:25:59 +0900</pubDate>
    </item>
    <item>
      <title>MLOps란?</title>
      <link>https://jinho-study.tistory.com/1102</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;MLOps는 머신러닝 시스템 개발과 시스템 운영을 통합하는 것을 목표로 하는 머신러닝 엔지니어링 &lt;/b&gt;&lt;b&gt;방식입니다. &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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;MLOps의 구성 요소로는 크게 데이터, 모델, 서빙 3가지로 나눌 수 있는데 &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;아래와 같이 아주 많은 플랫폼, 오픈 소스들이 존재합니다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;데이터&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;데이터 수집 파이프라인(ETL Pipeline) -&amp;gt; Sqoop, Flume, Kafka, Flink, Spark Streaming, Airflow&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터 저장 -&amp;gt; MySQL, Hadoop, Amazon S3, MinIO&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터 관리 -&amp;gt; TFDV, DVC, Feast, Amundsen&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;모델&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;모델 개발 -&amp;gt; Jupyter Hub, Docker, Kubeflow, Optuna, Ray, katib&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;모델 버전 관리 -&amp;gt; Git, MLflow, Github Action, Jenkins&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;모델 학습 스케즐링 관리 -&amp;gt; Grafana, Kubernetes&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;서빙&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;모델 패키징 -&amp;gt; Docker, Flask, FastAPI, BentoML, Kubeflow, TFServing, seldon-core&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;서빙 모니터링 -&amp;gt; Prometheus, Grafana, Thanos&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;파이프라인 매니징 -&amp;gt; Kubeflow, argo workflows, Airflow&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;여기서 서빙이란 개념은 조금 낯선데 다른 애플리케이션에서도 ML 모델을 사용할 수 있도록 모델을 배포하거나,&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;모델 API를 제공한다고 이해하면 될 것 같습니다.&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;그리고 위의 기능들을 제공해주는 Aws SageMaker, GCP Vertex AI, Azure Machine Learning 등이 있습니다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;</description>
      <category>MLOps</category>
      <category>DevOps</category>
      <category>MLOps</category>
      <category>MLOps 구성요소</category>
      <category>SaaS</category>
      <category>데이터</category>
      <category>머신러닝</category>
      <category>모델</category>
      <category>서빙</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1102</guid>
      <comments>https://jinho-study.tistory.com/1102#entry1102comment</comments>
      <pubDate>Tue, 16 Nov 2021 00:22:20 +0900</pubDate>
    </item>
    <item>
      <title>백준 알고리즘 11256번 사탕(python)</title>
      <link>https://jinho-study.tistory.com/1101</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기본적인 그리디 알고리즘 문제이다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1635254865981&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for _ in range(int(input())):
    j, N = map(int, input().split())
    li = []
    for i in range(N):
        r, c = map(int, input().split())
        li.append(r*c)
    li.sort(reverse=True)
    cnt = 0
    while j &amp;gt; 0:
        j -= li[cnt]
        cnt += 1
    print(cnt)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Agorithm/백준 알고리즘</category>
      <category>11256</category>
      <category>Python</category>
      <category>그리디 알고리즘</category>
      <category>백준</category>
      <category>백준 알고리즘</category>
      <category>사탕</category>
      <category>정렬</category>
      <category>파이썬</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1101</guid>
      <comments>https://jinho-study.tistory.com/1101#entry1101comment</comments>
      <pubDate>Tue, 26 Oct 2021 22:27:59 +0900</pubDate>
    </item>
    <item>
      <title>백준 알고리즘 17204번 죽음의 게임(python)</title>
      <link>https://jinho-study.tistory.com/1100</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기본적인 그래프 탐색 문제이다. 최단거리를 찾는 문제이므로 BFS로 풀었다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1635130914420&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from collections import deque

def bfs():
    q = deque()
    q.append((0))
    while q:
        node = q.popleft()
        n = li[node]
        if check[n] == 0 and node != n:
            q.append(n)
            check[n] = check[node] + 1
            if n == K:
                return ;
    
N, K = map(int, input().split())
li = [int(input()) for _ in range(N)]
check = [0]*N
bfs()
print(check[K] if check[K] else -1)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Agorithm/백준 알고리즘</category>
      <category>17204</category>
      <category>BFS</category>
      <category>Python</category>
      <category>구현</category>
      <category>그래프 탐색</category>
      <category>백준</category>
      <category>백준 알고리즘</category>
      <category>죽음의 게임</category>
      <category>탐색</category>
      <category>파이썬</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1100</guid>
      <comments>https://jinho-study.tistory.com/1100#entry1100comment</comments>
      <pubDate>Mon, 25 Oct 2021 12:02:04 +0900</pubDate>
    </item>
    <item>
      <title>백준 알고리즘 16173번 점프왕 쩰리 (Small)(python)</title>
      <link>https://jinho-study.tistory.com/1099</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기본적인 BFS, DFS 문제이다. (N, N) 좌표에 도착할 수 있는지를 확인해주면 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;BFS 풀이&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1634826610568&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from collections import deque

def bfs(y, x):
    q = deque()
    q.append((y, x, li[y][x]))
    while q:
        y, x, d = q.popleft()
        for i, j in [(1, 0), (0, 1)]:
            Y, X = y + d*i, x+ d*j
            if 0 &amp;lt;= Y &amp;lt; N and 0 &amp;lt;= X &amp;lt; N and d != 0:
                if Y == X == N-1:
                    return True
                q.append((Y, X, li[Y][X]))
    return False
        
N = int(input())
li = [list(map(int, input().split())) for _ in range(N)]    
print(&quot;HaruHaru&quot; if bfs(0, 0) else &quot;Hing&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;DFS 풀이&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1634826572501&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(10000)

def dfs(y, x):
    global ok
    d = li[y][x]
    for i, j in [(1, 0), (0, 1)]:
        Y, X = y + d*i, x+ d*j
        if 0 &amp;lt;= Y &amp;lt; N and 0 &amp;lt;= X &amp;lt; N and d != 0:
            if Y == X == N-1:
                ok = 1
                return ;
            dfs(Y, X)
        
N = int(input())
li = [list(map(int, input().split())) for _ in range(N)]
ok = 0
dfs(0, 0)
print(&quot;HaruHaru&quot; if ok else &quot;Hing&quot;)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Agorithm/백준 알고리즘</category>
      <category>16173</category>
      <category>BFS</category>
      <category>dfs</category>
      <category>Python</category>
      <category>그래프 이론</category>
      <category>그래프 탐색</category>
      <category>백준</category>
      <category>백준 알고리즘</category>
      <category>점프왕 쩰리 (Small)</category>
      <category>파이썬</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1099</guid>
      <comments>https://jinho-study.tistory.com/1099#entry1099comment</comments>
      <pubDate>Thu, 21 Oct 2021 23:30:32 +0900</pubDate>
    </item>
    <item>
      <title>vscode 원격 서버에 연결하기(+ 비밀번호 입력 안뜨게 하기)</title>
      <link>https://jinho-study.tistory.com/1098</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이번엔 vscode와 원격 서버를 연결해보자. 생각보다 간단하다.&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;Extensions에서 Remote Development를 설치하고&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;845&quot; data-origin-height=&quot;211&quot; width=&quot;593&quot; height=&quot;148&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tld5a/btrhfyszaZG/xrHPJzZGIm0NJXD8e9gZFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tld5a/btrhfyszaZG/xrHPJzZGIm0NJXD8e9gZFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tld5a/btrhfyszaZG/xrHPJzZGIm0NJXD8e9gZFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ftld5a%2FbtrhfyszaZG%2FxrHPJzZGIm0NJXD8e9gZFk%2Fimg.png&quot; data-origin-width=&quot;845&quot; data-origin-height=&quot;211&quot; width=&quot;593&quot; height=&quot;148&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&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;ctrl + shift + p를 눌러 Command Palette를 열고 Remote-SSH: Connect to Host를 실행한 후&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Add New SSH Host -&amp;gt; ssh 계정@ip주소or도메인 -&amp;gt; 비밀번호를 입력하면 서버에 접속된다.&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;이렇게 접속은 됐는데 뭐만 하면 계속 비밀번호 입력하라 하고 되게 귀찮게 하는데&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;내 로컬 컴퓨터의 보안키 파일을 서버에 옮기고 vscode 설정을 조금만 해주면 해결된다.&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;우선 키를 발급하자. cmd에다 ssh-keygen -t rsa -b 4096 명령어를 실행하고 엔터 몇 번 누르면 &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;아래와 같이 키(id_rsa.pub)가 발급된다. 나는 이미 발급해서 저렇게 나왔지만 원래는 이상한 이미지가 나온다!&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;901&quot; data-origin-height=&quot;369&quot; width=&quot;542&quot; height=&quot;222&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beZyjd/btrhcFtqmhV/WHToV4zie9M8O4CfVgJJrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beZyjd/btrhcFtqmhV/WHToV4zie9M8O4CfVgJJrk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beZyjd/btrhcFtqmhV/WHToV4zie9M8O4CfVgJJrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeZyjd%2FbtrhcFtqmhV%2FWHToV4zie9M8O4CfVgJJrk%2Fimg.png&quot; data-origin-width=&quot;901&quot; data-origin-height=&quot;369&quot; width=&quot;542&quot; height=&quot;222&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&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;scp를 사용해서 서버에 id_rsa.pub파일을 보내야 되는데 아래와 같은 방식으로 명령어를 입력해주면 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;scp -P [서버 포트] [전송 파일] [서버 계정]@[서버 아이피]:[파일 저장 경로]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;EX) scp -P 포트 .ssh/id_rsa.pub jinho@~.~.~.~:.ssh/&lt;/span&gt;&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;이제 서버에서 .ssh 경로로 들어간 다음 아래 명령어를 입력해주고&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1633709963817&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cat id_rsa.pub &amp;gt;&amp;gt; .ssh/authorized_keys&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;vscode로 넘어와 ctrl + shift + p -&amp;gt; Remote-SSH:&amp;nbsp;Open&lt;span&gt; SHH &lt;/span&gt;Configuration&lt;span&gt;&amp;nbsp;&lt;/span&gt;File&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;-&amp;gt; .ssh 폴더의 config 파일을 열어주자.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;588&quot; data-origin-height=&quot;113&quot; width=&quot;427&quot; height=&quot;82&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSJEvO/btrhd6pUyDk/lUQjUGW5L3hPx5LEPD4DHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSJEvO/btrhd6pUyDk/lUQjUGW5L3hPx5LEPD4DHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSJEvO/btrhd6pUyDk/lUQjUGW5L3hPx5LEPD4DHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSJEvO%2Fbtrhd6pUyDk%2FlUQjUGW5L3hPx5LEPD4DHK%2Fimg.png&quot; data-origin-width=&quot;588&quot; data-origin-height=&quot;113&quot; width=&quot;427&quot; height=&quot;82&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&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;그 후 아래와 같은 방식으로 config 파일을 작성해주면 끝이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #32ceff;&quot;&gt;Host, HostName: ip주소&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #32ceff;&quot;&gt;User: 계정 이름&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #32ceff;&quot;&gt;Port: 포트 번호&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #32ceff;&quot;&gt;IdentifyFile: ~/.ssh/id_rsa&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;294&quot; data-origin-height=&quot;155&quot; width=&quot;341&quot; height=&quot;180&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KZrGD/btrhbxblutw/zStR3mL8xKkBcgxaThB0cK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KZrGD/btrhbxblutw/zStR3mL8xKkBcgxaThB0cK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KZrGD/btrhbxblutw/zStR3mL8xKkBcgxaThB0cK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKZrGD%2Fbtrhbxblutw%2FzStR3mL8xKkBcgxaThB0cK%2Fimg.png&quot; data-origin-width=&quot;294&quot; data-origin-height=&quot;155&quot; width=&quot;341&quot; height=&quot;180&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&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;이제 vscode를 껐다 켜도 비밀번호를 물어보지 않고 바로 접속이 된다!!&lt;/b&gt;&lt;/p&gt;</description>
      <category>설치, 삽질, 마크다운 등등</category>
      <category>id_rsa.pub</category>
      <category>Remote Development</category>
      <category>SSH</category>
      <category>vscode</category>
      <category>vscode 원격 서버 연결</category>
      <category>원격 서버 접속</category>
      <category>키 발급</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1098</guid>
      <comments>https://jinho-study.tistory.com/1098#entry1098comment</comments>
      <pubDate>Sat, 9 Oct 2021 01:32:56 +0900</pubDate>
    </item>
    <item>
      <title>vscode 배경 이미지 넣기</title>
      <link>https://jinho-study.tistory.com/1097</link>
      <description>&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;b&gt;최근에는 주피터 노트북에서 벗어나 vscode를 사용해보고 있는 중이다. 서버 접속도 뚝딱이고 아주 편리한 것 같다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;오늘은 vscode에 배경 이미지를 추가해보자.&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Extensions에서 background를 설치하고&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;190&quot; width=&quot;514&quot; height=&quot;133&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXLMSm/btrg9DQcLBY/R0OEhGQQB96ATk47MfkUq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXLMSm/btrg9DQcLBY/R0OEhGQQB96ATk47MfkUq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXLMSm/btrg9DQcLBY/R0OEhGQQB96ATk47MfkUq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXLMSm%2Fbtrg9DQcLBY%2FR0OEhGQQB96ATk47MfkUq0%2Fimg.png&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;190&quot; width=&quot;514&quot; height=&quot;133&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&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;ctrl + shift + p를 눌러 Command Palette를 열고 Preferences: Open Settings (JSON)를 검색해서&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;settings.json파일을 연다. 그러면 아래와 같이 뭔가 잡다하게 적혀있거나 아예 비어있거나 둘 중 하나일 건데&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;585&quot; data-origin-height=&quot;297&quot; width=&quot;368&quot; height=&quot;187&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xTv5f/btrhfReo5kB/sPrR4tpJyhKTYSIYNE318k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xTv5f/btrhfReo5kB/sPrR4tpJyhKTYSIYNE318k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xTv5f/btrhfReo5kB/sPrR4tpJyhKTYSIYNE318k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxTv5f%2FbtrhfReo5kB%2FsPrR4tpJyhKTYSIYNE318k%2Fimg.png&quot; data-origin-width=&quot;585&quot; data-origin-height=&quot;297&quot; width=&quot;368&quot; height=&quot;187&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&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;{} 안에 아래 코드를 추가해주면&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1633701345896&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    &quot;background.enabled&quot;: true,
    &quot;background.loop&quot;: false,
    &quot;background.useDefault&quot;: false,
    &quot;background.useFront&quot;: true,
    &quot;background.style&quot;: {
        &quot;content&quot;: &quot;''&quot;,
        &quot;pointer-events&quot;: &quot;none&quot;,
        &quot;position&quot;: &quot;absolute&quot;,
        &quot;z-index&quot;: &quot;99999&quot;,
        &quot;width&quot;: &quot;100%&quot;,
        &quot;height&quot;: &quot;100%&quot;,
        &quot;background-position&quot;: &quot;left&quot;,
        &quot;background-size&quot;: &quot;cover&quot;,
        &quot;background-repeat&quot;: &quot;no-repeat&quot;,
        &quot;opacity&quot;: 0.25
    },
    &quot;background.customImages&quot;: [
        &quot;https://media.vlpt.us/images/wjdgml3834/post/04747625-84d3-4d6e-a940-5e00b73babf7/image.jpeg&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;이런 느낌으로 배경이 적용된다. background.style에서 스타일을 바꿀 수 있고&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;background.customImages에서 배경으로 쓰고 싶은 이미지를 지정해줄 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;944&quot; data-origin-height=&quot;838&quot; width=&quot;682&quot; height=&quot;605&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/okptp/btrhdV29byd/rN5heOTrQqGeU1bn1MQnok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/okptp/btrhdV29byd/rN5heOTrQqGeU1bn1MQnok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/okptp/btrhdV29byd/rN5heOTrQqGeU1bn1MQnok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fokptp%2FbtrhdV29byd%2FrN5heOTrQqGeU1bn1MQnok%2Fimg.png&quot; data-origin-width=&quot;944&quot; data-origin-height=&quot;838&quot; width=&quot;682&quot; height=&quot;605&quot; data-ke-mobilestyle=&quot;widthOrigin&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>설치, 삽질, 마크다운 등등</category>
      <category>Background</category>
      <category>Background Image</category>
      <category>vscode</category>
      <category>vscode extensions</category>
      <category>배경 이미지</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1097</guid>
      <comments>https://jinho-study.tistory.com/1097#entry1097comment</comments>
      <pubDate>Fri, 8 Oct 2021 23:10:50 +0900</pubDate>
    </item>
    <item>
      <title>백준 알고리즘 3182번 한동이는 공부가 하기 싫어!(python)</title>
      <link>https://jinho-study.tistory.com/1096</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기본적인 그래프 탐색 문제이다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;각 노드별로 몇 명의 사람을 만날 수 있는지 확인하고, 최댓값의 인덱스를 출력해주면 된다 -&amp;gt; res.index(max(res))&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1628579261212&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def dfs(node, cnt):
    check[node] = 1
    n = graph[node][0]
    if check[n] == 0:
        cnt = dfs(n, cnt+1)
    return cnt

N = int(input())
graph = [[] for _ in range(N+1)]
res = [0]*(N+1)
for u in range(1, N+1):
    v = int(input())
    graph[u].append(v)
for i in range(1, N+1):
    check = [0]*(N+1)
    res[i] = dfs(i, 1)
print(res.index(max(res)))&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Agorithm/백준 알고리즘</category>
      <category>3182</category>
      <category>BFS</category>
      <category>dfs</category>
      <category>Python</category>
      <category>그래프 이론</category>
      <category>그래프 탐색</category>
      <category>백준</category>
      <category>백준 알고리즘</category>
      <category>파이썬</category>
      <category>한동이는 공부가 하기 싫어!</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1096</guid>
      <comments>https://jinho-study.tistory.com/1096#entry1096comment</comments>
      <pubDate>Tue, 10 Aug 2021 20:44:08 +0900</pubDate>
    </item>
    <item>
      <title>백준 알고리즘 14888번 연산자 끼워넣기(python)</title>
      <link>https://jinho-study.tistory.com/1095</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;브루트포스 알고리즘 &amp;amp; 백트래킹 문제이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;나누기 처리를 할 때 int(n1 / n2) 또는 -(-n1 / n2) 같은 방식으로 나눠야 하는데,&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;pre id=&quot;code_1628350365127&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def dfs(res, i, add, sub, mul, div):
    global N
    if i == N:
        res_li.append(res)
    else:
        if add:
            dfs(res + nums[i], i+1, add-1, sub, mul, div)            
        if sub:
            dfs(res - nums[i], i+1, add, sub-1, mul, div)
        if mul:
            dfs(res * nums[i], i+1, add, sub, mul-1, div)
        if div:
            dfs(int(res / nums[i]), i+1, add, sub, mul, div-1)          
    
N = int(input())
nums = list(map(int, input().split()))
add, sub, mul, div = map(int, input().split())
res_li = []

dfs(nums[0], 1, add, sub, mul, div)
print(max(res_li))
print(min(res_li))&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Agorithm/백준 알고리즘</category>
      <category>14888</category>
      <category>Python</category>
      <category>백준</category>
      <category>백준 알고리즘</category>
      <category>백트래킹</category>
      <category>브루트포스 알고리즘</category>
      <category>연산자 끼워넣기</category>
      <category>파이썬</category>
      <author>kimjinho1</author>
      <guid isPermaLink="true">https://jinho-study.tistory.com/1095</guid>
      <comments>https://jinho-study.tistory.com/1095#entry1095comment</comments>
      <pubDate>Sun, 8 Aug 2021 00:33:28 +0900</pubDate>
    </item>
  </channel>
</rss>