<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Hello Ocean!  </title>
    <link>https://bba-dda.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Wed, 15 Apr 2026 06:55:54 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>bba_dda</managingEditor>
    <image>
      <title>Hello Ocean!  </title>
      <url>https://tistory1.daumcdn.net/tistory/3639798/attach/aeb327a9dc2f427c891fbee57f422b2e</url>
      <link>https://bba-dda.tistory.com</link>
    </image>
    <item>
      <title>[Go] 데이터 경합을 감지해보자, Data Race Detector</title>
      <link>https://bba-dda.tistory.com/130</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;서론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동시 처리 시스템(Concurrent System)에서 데이터 경합은 매우 흔하고, 디버깅하기 힘든 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Go에서는 이런 문제 해결을 돕기 위한 built-in Data Race Detector가 존재한다! (야호 )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;어떻게 쓰나요?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용방법은 매우 단순하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;go command에 &lt;code&gt;-race&lt;/code&gt; 플래그를 추가하면 된다!&lt;/p&gt;
&lt;pre class=&quot;go&quot;&gt;&lt;code&gt;$ go test -race mypkg    // to test the package
$ go run -race mysrc.go  // to run the source file
$ go build -race mycmd   // to build the command
$ go install -race mypkg // to install the package&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;race build tag로 효율적으로 사용하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 코드와 모든 테스트에 대해 race detector를 사용하는 것이 비효율적일 수 있다.&lt;br /&gt;그럴 때는, race build tag를 사용하면 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 맨 첫번째 라인에 아래와 같이 작성해 활용할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;` // +build !race`&lt;/span&gt;&amp;nbsp;: -race 플래그가 없을 때만 빌드에 포함됨
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;race 검사가 불필요한 코드/테스트&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;` // +build race`&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&amp;nbsp;: -race 플래그가 있을 때만 빌드에 포함됨&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;race 검사만을 위한 코드/테스트&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;어떻게 알려주나요?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Race Decector가 race를 발견하면, report를 작성해준다.&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;report는 아래의 내용들을 포함한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;경고 메시지:&lt;/li&gt;
&lt;li&gt;읽기 작업을 수행한 goroutine의 스택 트레이스:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수 호출과 파일/라인 정보 포함&lt;/li&gt;
&lt;li&gt;예시:
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;net.(*pollServer).AddFD()
src/net/fd_unix.go:89 +0x398&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이전 쓰기 작업을 수행한 goroutine의 스택 트레이스:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수 호출과 파일/라인 정보 포함&lt;/li&gt;
&lt;li&gt;예시:
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;net.setWriteDeadline()
src/net/sockopt_posix.go:135 +0xdf&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;읽기 작업을 수행한 goroutine의 생성 위치:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수 호출과 파일/라인 정보 포함&lt;/li&gt;
&lt;li&gt;예시:
&lt;pre class=&quot;go&quot;&gt;&lt;code&gt;net.func&amp;middot;061()
src/net/timeout_test.go:609 +0x288&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;쓰기를 수행한 goroutine의 생성 위치:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수 호출과 파일/라인 정보 포함&lt;/li&gt;
&lt;li&gt;예시:
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;net.TestProlongTimeout()
src/net/timeout_test.go:618 +0x298&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;report 예시&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;==================
WARNING: DATA RACE
Write at 0x00c000098180 by goroutine 6:
  runtime.mapaccess2_faststr()
      /opt/homebrew/Cellar/go/1.20.2/libexec/src/runtime/map_faststr.go:108 +0x42c
  main.main.func1()
      /Users/bada/Projects/Concurrency-in-Go/main.go:67 +0x48

Previous write at 0x00c000098180 by main goroutine:
  runtime.mapaccess2_faststr()
      /opt/homebrew/Cellar/go/1.20.2/libexec/src/runtime/map_faststr.go:108 +0x42c
  main.main()
      /Users/bada/Projects/Concurrency-in-Go/main.go:70 +0xf4

Goroutine 6 (running) created at:
  main.main()
      /Users/bada/Projects/Concurrency-in-Go/main.go:66 +0xd8
==================
Found 1 data race(s)&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;report를 통해 데이터 경합 문제가 어디에서 어떻게 발생하고 있는지 파악할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;다양한 report관련 option들&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;GORACE&lt;/code&gt; 환경변수를 통해 race detector에 옵션들을 설정할 수 있다.&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;log_path&lt;/code&gt; (default : stderr)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;report 작성 위치&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stderr&lt;/code&gt;, &lt;code&gt;stdout&lt;/code&gt;로 설정하면 표준에러, 표준출력으로 작성됨&lt;/li&gt;
&lt;li&gt;특정 파일 경로를 입력하면 &lt;code&gt;file_name.pid&lt;/code&gt; 형식의 파일에 작성됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;exitcode&lt;/code&gt; (default : 66)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;경합상태 감지 후 프로그램이 종료될 때 사용할 exit status를 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;strip_path_prefix&lt;/code&gt; (default : &quot;&quot;)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;report에 포함될 모든 파일 경로에서 생략할 prefix 지정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;history_size&lt;/code&gt; (default : 1)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 goroutine의 메모리 접근 히스토리는 32K * 2**history_size 로 구성됨&lt;/li&gt;
&lt;li&gt;이 값을 증가시키면 &lt;code&gt;스택을 복원하지 못함(failed to restore the stack)&lt;/code&gt;에러를 피할 수 있지만, 메모리 사용량이 증가함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;halt_on_error&lt;/code&gt; (default : 0)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫 번째 race를 탐지하고 바로 종료할것인지, 이어서 계속 탐지할 것인지 설정&lt;/li&gt;
&lt;li&gt;0: 종료하지 않음 / 1: 종료함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;atexit_sleep_ms&lt;/code&gt; (default : 1000)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그램이 종료되기 전에 main goroutine에서 대기할 시간(ms)을 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용 예시&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;  GORACE=&quot;log_path=/tmp/race/report strip_path_prefix=/my/go/sources/&quot; go test -race&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Test를 통한 race탐지의 한계와 해결방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;go test -race를 사용하여 race detector를 실행할 경우, 실행중에 발생하는 race만 찾을 수 있다.&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;따라서, test coverage가 충분히 높지 않은 경우에는 ,&lt;br /&gt;&lt;code&gt;-race&lt;/code&gt;플래그로 build한 바이너리를 &lt;code&gt;실제와 유사한 환경&lt;/code&gt;에서 실행하면 더 많은 race를 탐지할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Runtime Overhead&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;race detector를 사용하면 메모리 사용량과 실행 시간이 크게 증가할 수 있다.&lt;br /&gt;그리고 race detector로 인한 메모리 증가는 &lt;code&gt;runtime.ReadMemStats&lt;/code&gt;나 &lt;code&gt;runtime/pprof&lt;/code&gt;와 같은 메모리 프로파일링 도구에서는 확인할 수 없기 때문에 주의해서 사용해야 한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;일반적인 프로그램의 경우 메모리 사용량이 5배 ~ 10배 증가할 수 있고, 실행 시간은 2배 ~ 20배 증가할 수 있음&lt;/li&gt;
&lt;li&gt;race dector는 &lt;code&gt;defer&lt;/code&gt; 및 &lt;code&gt;recover&lt;/code&gt; 문마다 추가로 8바이트를 할당
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;defer&lt;/code&gt; 및 &lt;code&gt;recover&lt;/code&gt;가 많이 사용되는 프로그램은 더 많은 메모리를 사용하게 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이러한 추가 메모리 할당은 고루틴이 종료되고 나서 회수됨
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;고루틴이 오랫동안 실행될 경우 메모리 사용량이 계속 증가할 수 있음&lt;br /&gt;==&amp;gt; 장기간 실행되는 고루틴이 주기적으로 &lt;code&gt;defer&lt;/code&gt;와 &lt;code&gt;recover&lt;/code&gt; 호출을 하는 경우, 프로그램의 &lt;b&gt;메모리 사용량이 무한히 증가&lt;/b&gt;할 수 있으므로 주의하자.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&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 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://go.dev/doc/articles/race_detector&quot;&gt;https://go.dev/doc/articles/race_detector&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1724399216053&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;Data Race Detector - The Go Programming Language&quot; data-og-description=&quot;Data Race Detector Introduction Data races are among the most common and hardest to debug types of bugs in concurrent systems. A data race occurs when two goroutines access the same variable concurrently and at least one of the accesses is a write. See the&quot; data-og-host=&quot;go.dev&quot; data-og-source-url=&quot;https://go.dev/doc/articles/race_detector&quot; data-og-url=&quot;https://go.dev/doc/articles/race_detector&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Fb51K/hyWSiiypLs/DejGAVV61tobQHRM7z1KrK/img.jpg?width=300&amp;amp;height=313&amp;amp;face=0_0_300_313&quot;&gt;&lt;a href=&quot;https://go.dev/doc/articles/race_detector&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://go.dev/doc/articles/race_detector&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Fb51K/hyWSiiypLs/DejGAVV61tobQHRM7z1KrK/img.jpg?width=300&amp;amp;height=313&amp;amp;face=0_0_300_313');&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;Data Race Detector - The Go Programming Language&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Data Race Detector Introduction Data races are among the most common and hardest to debug types of bugs in concurrent systems. A data race occurs when two goroutines access the same variable concurrently and at least one of the accesses is a write. See the&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;go.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Go</category>
      <category>data race detector</category>
      <category>go</category>
      <category>golang</category>
      <category>goroutine</category>
      <category>경합 탐지</category>
      <category>데이터 경합</category>
      <category>동시성 프로그래밍</category>
      <author>bba_dda</author>
      <guid isPermaLink="true">https://bba-dda.tistory.com/130</guid>
      <comments>https://bba-dda.tistory.com/130#entry130comment</comments>
      <pubDate>Fri, 23 Aug 2024 16:45:54 +0900</pubDate>
    </item>
    <item>
      <title>[CKA 취득 후기] 이제 나도 Kubernetes 전문가 ?!</title>
      <link>https://bba-dda.tistory.com/129</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;꿀팁 요약  &lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;b&gt;&lt;a href=&quot;https://www.udemy.com/course/certified-kubernetes-administrator-with-practice-tests/?utm_source=adwords&amp;amp;utm_medium=udemyads&amp;amp;utm_campaign=Kubernetes_Search_la.KR_cc.KR&amp;amp;campaigntype=Search&amp;amp;portfolio=SouthKorea&amp;amp;language=KR&amp;amp;product=Course&amp;amp;test=&amp;amp;audience=Keyword&amp;amp;topic=&amp;amp;priority=&amp;amp;utm_content=deal4584&amp;amp;utm_term=_._ag_147409783340_._ad_649912297581_._kw_cka_._de_c_._dm__._pl__._ti_kwd-92004011_._li_9197100_._pd__._&amp;amp;matchtype=b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwnK60BhA9EiwAmpHZw7dGlPd6o21joZ3m9ck7nquzkt9K9sge1Z6JiJNgSxNc02SzRPKVjhoCaTkQAvD_BwE&amp;amp;couponCode=ST9MT71624&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;유명한 뭄샤드마남밷 씨의 강의&lt;/a&gt;&lt;/b&gt;는 꼭 듣자 (앗, 강의 치킨보다 싸다!)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - 강의에서 제공하는 lab에서 문제를 풀어볼 수 있는게 너무 좋음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - 강의 후반부의 Lightning Lab, Mock Exam은 여러번 풀어보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 시험 신청시 제공해주는 &lt;b&gt;Killer.sh&lt;/b&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;&amp;nbsp; &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; &amp;nbsp; - 문제를 풀어볼수록, 유형이 비슷해서 문제를 끝까지 안읽고 풀게되는 경우가 있는데 함정이 있을 수 있음&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드디어! 6개월 동안 마음 한켠을 무겁게 했던 CKA 자격증을 취득했다 ~~~~&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;1518&quot; data-origin-height=&quot;1173&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sUTNd/btsIsMSC87V/2K0Ck4RkL9G39fgYQnhlG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sUTNd/btsIsMSC87V/2K0Ck4RkL9G39fgYQnhlG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sUTNd/btsIsMSC87V/2K0Ck4RkL9G39fgYQnhlG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsUTNd%2FbtsIsMSC87V%2F2K0Ck4RkL9G39fgYQnhlG0%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;521&quot; height=&quot;403&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;1518&quot; data-origin-height=&quot;1173&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;취득하게 된 계기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 현업에서 사용하기 위함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 운영중인 시스템이 쿠버네티스로 배포되어 있었기 때문에&amp;nbsp;직접 이슈 트래킹을 하기 위해 배우기 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 재미(?)있어 보여서 &lt;s&gt;개발자라면 배포도 할 줄 알아야지&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입사 초 백오피스 개발을 할 때, 쿠버네티스로 직접 배포해본 경험이 있었기 때문에 깊이 공부해보고 싶었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 개발자라면 내가 개발한 서버를 띄울 수 있고, 배포관련 이슈 트래킹도 가능해야 한다는 생각이 있었기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 팀 내 스터디 목표&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작년에 팀 내에서 쿠버네티스 스터디를 구성해 진행했는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공부에 대한 목표와 방향성이 필요하다고 생각되어 CKA 취득을 목표로 설정했다!&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;시험이 생각보다도 너무 비싸서 11월 블프 세일때 까지 기다려서 구매했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그치만 세일 해도 너무 비싼,,&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;1217&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGKi5x/btsItCBNS3i/EbP0kkiiVp2UI33GW8IMFK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGKi5x/btsItCBNS3i/EbP0kkiiVp2UI33GW8IMFK/img.jpg&quot; data-alt=&quot;와 세일해도 197.5$&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGKi5x/btsItCBNS3i/EbP0kkiiVp2UI33GW8IMFK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGKi5x%2FbtsItCBNS3i%2FEbP0kkiiVp2UI33GW8IMFK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;265&quot; height=&quot;389&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;1217&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;와 세일해도 197.5$&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;준비 과정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작년 11월에 시험을 구매한 이후, 다양한 이유로 쿠버네티스 자격증에 대한 우선순위가 하락해서 CKA 준비가 흐지부지 되었다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CKA 준비는 하지 않았지만, 현업에서 k8s 명령어는 꾸준히 사용해왔다.&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;그러다가, 6월에 퇴사를 하게 되면서 본격적으로 준비하게 되었다!!&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;1. &lt;a href=&quot;https://www.udemy.com/course/certified-kubernetes-administrator-with-practice-tests/?utm_source=adwords&amp;amp;utm_medium=udemyads&amp;amp;utm_campaign=Kubernetes_Search_la.KR_cc.KR&amp;amp;campaigntype=Search&amp;amp;portfolio=SouthKorea&amp;amp;language=KR&amp;amp;product=Course&amp;amp;test=&amp;amp;audience=Keyword&amp;amp;topic=&amp;amp;priority=&amp;amp;utm_content=deal4584&amp;amp;utm_term=_._ag_147409783340_._ad_649912297581_._kw_cka_._de_c_._dm__._pl__._ti_kwd-92004011_._li_9197100_._pd__._&amp;amp;matchtype=b&amp;amp;gad_source=1&amp;amp;gclid=CjwKCAjwnK60BhA9EiwAmpHZw7dGlPd6o21joZ3m9ck7nquzkt9K9sge1Z6JiJNgSxNc02SzRPKVjhoCaTkQAvD_BwE&amp;amp;couponCode=ST9MT71624&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;유명한 뭄샤드마남밷씨의 강의&lt;/a&gt; 수강&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작년부터 틈틈히 수강해서, 6월에 본격적으로 준비를 시작할 때 50퍼센트 정도 수강한 상태였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 영어 강의 이고, 한국어 자막이 제공되긴 하지만,, 자막 퀄리티가 좋진 않았다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - 수능 영어를 성실히 공부한 사람이라면 강의 수강에 큰 어려움이 있진 않을 것이다&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;span&gt;&amp;nbsp;&lt;/span&gt;- 강의에서 제공하는 lab에서 문제를 풀어볼 수 있는게 너무 좋음  &lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 강의 후반부의 Lightning Lab, Mock Exam은 여러번 풀어보자  &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;2. killer.sh&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강의를 어느정도 수강한 다음, 시험 날짜를 확정하게 되면 killer.sh 세션이 두개 제공된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름부터 killerㅋㅋㅋ 인 것 처럼 난이도가 좀 높긴했다,,,&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;강의에서 풀었던 lab환경과 비슷할 것이라 예상했는데, 시험환경은 vm을 통째로 주는 환경이라서 생각보다 적응이 필요했다.&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;- 시험환경과 동일하게 120분 카운트 해주지만, 시간이 초과되어도 종료되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 나의 경우는 25개의 문제가 제공되었고, 채점을 하고 나면 추가문제도 접근할 수 있는데 추가문제는 풀어보지 않았다&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;✅ 나의 killer.sh 활용 방법&lt;br /&gt;&amp;nbsp; &amp;nbsp; - 전날 한 회차&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - 2시간만 풀고 채점&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - solution 정독&lt;br /&gt;&amp;nbsp; &amp;nbsp; - 당일 오전에 한 회차 (오후 2시 시험이었다)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - 2시간만 풀고 채점&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - 어제 안풀어본 문제 위주로 풀기 위해서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - 뒤에서부터 품&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &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;절대평가로 66점만 넘으면 취득성공이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;점수가 중요한건 아니지만&lt;/s&gt; 생각보다 점수가 높게 나와서 너무 뿌듯했다ㅏㅏ는 후기&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;364&quot; data-origin-height=&quot;216&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eC8zNv/btsIsu5LIgQ/xXUYHdfb1KOHYM7KZBvK20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eC8zNv/btsIsu5LIgQ/xXUYHdfb1KOHYM7KZBvK20/img.png&quot; data-alt=&quot;7월 최대 업적&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eC8zNv/btsIsu5LIgQ/xXUYHdfb1KOHYM7KZBvK20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeC8zNv%2FbtsIsu5LIgQ%2FxXUYHdfb1KOHYM7KZBvK20%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;364&quot; height=&quot;216&quot; data-origin-width=&quot;364&quot; data-origin-height=&quot;216&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;7월 최대 업적&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;추천 여부?!&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버네티스 공부하면서 경험삼아 보기 좋았다. &lt;s&gt;금액 빼고....&lt;/s&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;/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; - 블프 할인을 꼭 받으시길....&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- DevOps 포지션으로 취업을 준비중인 사람&lt;/p&gt;</description>
      <category>TIL</category>
      <category>cka</category>
      <category>자격증</category>
      <category>쿠버네티스</category>
      <author>bba_dda</author>
      <guid isPermaLink="true">https://bba-dda.tistory.com/129</guid>
      <comments>https://bba-dda.tistory.com/129#entry129comment</comments>
      <pubDate>Tue, 9 Jul 2024 17:03:43 +0900</pubDate>
    </item>
    <item>
      <title>[AWS EC2] key-pair를 이용한 ssh 연결</title>
      <link>https://bba-dda.tistory.com/128</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;key-pair가 적용되어 있는 EC2 인스턴스에, ssh 연결하는 방법을 알아보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. EC2 인스턴스 생성&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1-1. key-pair 설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EC2를 생성할 때 key-pair를 적용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;key-pair를 적용하지 않는 옵션을 선택할 수도 있지만, 보안을 위해서 사용하는것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성 화면에서도 &lt;span style=&quot;background-color: #c0d1e7;&quot;&gt;키 페어 없이 계속 진행&lt;b&gt;(권장되지 않음)&lt;/b&gt;&lt;/span&gt; 이라고 강조하고 있으니 사용해보자~&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;690&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ngUnr/btsH98H0jYv/HmjcXRK1nUY1cAwNNQqADK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ngUnr/btsH98H0jYv/HmjcXRK1nUY1cAwNNQqADK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ngUnr/btsH98H0jYv/HmjcXRK1nUY1cAwNNQqADK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FngUnr%2FbtsH98H0jYv%2FHmjcXRK1nUY1cAwNNQqADK%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;1630&quot; height=&quot;690&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;690&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;EC2 인스턴스 생성할 때, key-pair를 사용하려면 key-pair를 먼저 생성해야 한다. key-pair를 생성하면 아래와 같이 키파일을 다운로드 해준다. 후에 이 파일을 사용해야 하기 때문에 보관해두자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;374&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u3Ge4/btsH9X7Jwhw/MC0nAKOEvHusqSf7lleSx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u3Ge4/btsH9X7Jwhw/MC0nAKOEvHusqSf7lleSx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u3Ge4/btsH9X7Jwhw/MC0nAKOEvHusqSf7lleSx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu3Ge4%2FbtsH9X7Jwhw%2FMC0nAKOEvHusqSf7lleSx0%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;523&quot; height=&quot;292&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;374&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1-2. SSH 접속을 위한 Inbound 설정 추가&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 터미널에서 EC2인스턴스에 SSH 접속을 하기 위해&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부에서 SSH접속을 위한 포트에 접근할 수 있도록 추가 설정이 필요하다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방법 1)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 보안그룹 생성 옵션 선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 아래 사진과 같이 모든 ip (0.0.0.0/0)에 대해 SSH 접속을 허용하는 인바운드 설정&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1648&quot; data-origin-height=&quot;1412&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKRjfZ/btsIbvoPNPK/JRLoqEWFkg0nQem8KlmQ7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKRjfZ/btsIbvoPNPK/JRLoqEWFkg0nQem8KlmQ7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKRjfZ/btsIbvoPNPK/JRLoqEWFkg0nQem8KlmQ7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKRjfZ%2FbtsIbvoPNPK%2FJRLoqEWFkg0nQem8KlmQ7K%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;674&quot; height=&quot;577&quot; data-origin-width=&quot;1648&quot; data-origin-height=&quot;1412&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;방법 2)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 기존 보안그룹 선택한 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 해당 보안그룹을 편집해, SSH에 대한 인바운드 설정을 추가하면 된다~&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2682&quot; data-origin-height=&quot;1016&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHkxTh/btsIbk8VIJF/Mn50jWeHSpIvylvJ6fAxdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHkxTh/btsIbk8VIJF/Mn50jWeHSpIvylvJ6fAxdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHkxTh/btsIbk8VIJF/Mn50jWeHSpIvylvJ6fAxdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHkxTh%2FbtsIbk8VIJF%2FMn50jWeHSpIvylvJ6fAxdk%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;744&quot; height=&quot;282&quot; data-origin-width=&quot;2682&quot; data-origin-height=&quot;1016&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 터미널에서 SSH 접속&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EC2가 정상적으로 생성된 후에, 터미널에서 ssh접속을 시도하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/connect-linux-inst-ssh.html#connect-linux-inst-sshClient&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;AWS 공식 문서&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1719208432675&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ssh -i /path/key-pair-name.pem instance-user-name@instance-public-dns-name&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 될 줄 알았는데! 문제가 생겼다. (이 글을 작성하게 된 이유..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;내 key에 무슨 문제가 있단 말인가? &lt;s&gt;방금 다운받은건데..&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1488&quot; data-origin-height=&quot;348&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biVAT2/btsH9hZ7xD7/mycwveM8UWMLYbFqkhvGtK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biVAT2/btsH9hZ7xD7/mycwveM8UWMLYbFqkhvGtK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biVAT2/btsH9hZ7xD7/mycwveM8UWMLYbFqkhvGtK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiVAT2%2FbtsH9hZ7xD7%2FmycwveM8UWMLYbFqkhvGtK%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;1488&quot; height=&quot;348&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1488&quot; data-origin-height=&quot;348&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;찾아보니, aws-key.pem 개인 키 파일의 권한이 너무 넓게 설정되어 있어 나타나는 에러였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안상의 이유로 &lt;b&gt;SSH는 개인 키 파일이 다른 사용자에게 접근할 수 없도록 설정&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;chmod로 key file의 접근 권한을 변경해주면 해결된다.&lt;/p&gt;
&lt;pre id=&quot;code_1719208791636&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;chmod 400 /path/key-pair-name.pem&lt;/code&gt;&lt;/pre&gt;</description>
      <category>TIL</category>
      <category>AWS</category>
      <category>chmod</category>
      <category>ec2</category>
      <category>publickey error</category>
      <category>ssh</category>
      <author>bba_dda</author>
      <guid isPermaLink="true">https://bba-dda.tistory.com/128</guid>
      <comments>https://bba-dda.tistory.com/128#entry128comment</comments>
      <pubDate>Mon, 24 Jun 2024 15:01:33 +0900</pubDate>
    </item>
    <item>
      <title>[gqlgen] gin.Context 이용하기</title>
      <link>https://bba-dda.tistory.com/127</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gin을&amp;nbsp;graphql(gqlgen)의 router로 사용하려고 할 때&lt;br /&gt;gin.Context에 담아놓은 값들이, graphql resolver에서 사용할 수 있는 ctx(context.Context)에 담겨있지 않았다.&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;u&gt;gin.Context에 key-value로 저장한 값을&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;resolver의 ctx.Value(&quot;key&quot;)로 접근해서 사용하고 싶은 상황&lt;/u&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;gqlgen 공식 문서에서 제공하는 example에 gin.Context를 사용하는 방법이 안내되어 있다. &lt;a href=&quot;https://github.com/99designs/gqlgen/blob/master/docs/content/recipes/gin.md#accessing-gincontext&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 이 방법은 context.Context에 gin.Context를 그대로 담기 때문에 각각의 resolver에서 매번 아래의 작업을 해줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. context.Context에서 gin.Context 꺼내기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. gin.Context에서 원하는 값 꺼내기&lt;/p&gt;
&lt;pre id=&quot;code_1691589779563&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func (r *resolver) Todo(ctx context.Context) (*Todo, error) {
	gc, err := GinContextFromContext(ctx) // 매번 gin.Context를 꺼내고
	if err != nil {
		return nil, err
	}

	// gc에서 필요한 값을 꺼내야 한다
	// ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;길지 않은 코드라고 생각할 수 있지만, 매 resolver에 불필요하게 중복된 코드가 들어가게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;원인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 왜, gqlgen resolver의 ctx에 gin.Context의 값이 들어있지 않은걸까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/99designs/gqlgen/blob/master/docs/content/recipes/gin.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;gqlgen에서 제공하는 gin을 이용한 example&lt;/a&gt;을 보면, graphql handler에&lt;b&gt; gin.Context.Request&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_1691488027500&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
// Defining the Graphql handler
func graphqlHandler() gin.HandlerFunc {
	// NewExecutableSchema and Config are in the generated.go file
	// Resolver is in the resolver.go file
	h := handler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{Resolvers: &amp;amp;graph.Resolver{}}))

	return func(c *gin.Context) {
		h.ServeHTTP(c.Writer, c.Request) // 여기!
	}
}
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/99designs/gqlgen/blob/2d8673a691deffe6ddd1f4d5f013a52dc91aef91/graphql/handler/server.go#L101&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;h.ServeHTTP()&lt;/a&gt;에서는, 넘겨받은 Request.Context() (==Request.ctx) 를 사용하게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1691488188037&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer func() {
		if err := recover(); err != nil {
			err := s.exec.PresentRecoveredError(r.Context(), err)
			gqlErr, _ := err.(*gqlerror.Error)
			resp := &amp;amp;graphql.Response{Errors: []*gqlerror.Error{gqlErr}}
			b, _ := json.Marshal(resp)
			w.WriteHeader(http.StatusUnprocessableEntity)
			w.Write(b)
		}
	}()

	r = r.WithContext(graphql.StartOperationTrace(r.Context())) // 여기!

	transport := s.getTransport(r)
	if transport == nil {
		sendErrorf(w, http.StatusBadRequest, &quot;transport not supported&quot;)
		return
	}

	transport.Do(w, r, s.exec)
}&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gin.Context 의 하위에는 아래와 같은 필드들이 존재하고&lt;/p&gt;
&lt;pre id=&quot;code_1691590441214&quot; class=&quot;go&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// gin.Context
type Context struct {
	...
	Request   *http.Request
	Keys      map[string]any
	...
}&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;gin.Context에 Set()을 통해 저장하는 값들은 &lt;b&gt;gin.Context.Keys&lt;/b&gt; 에 저장되는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gqlgen resolver에 넘어가는 context는 &lt;b&gt;gin.Context.Request.ctx&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;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 어떻게 원하는대로 사용할 수 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;gin.Context에 key-value로 저장한 값을&lt;/u&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;resolver의 ctx.Value(&quot;key&quot;)로 접근해서 사용하고 싶은 상황&lt;/u&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. 사용할 값들을 gin.Context.Keys가 아닌 gin.Context.Request.ctx에 저장하기&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 되면, 원하는 상황은 얻어낼 수 있지만 두 가지 단점이 생긴다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1) gin middleware에서 context에 값을 저장하는 로직이 복잡해지고&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2) 여러 개발자가 함께 작업할 때 gin.Context.Keys가 아니라 Request내부 ctx에 값을 저장해야 한다는 사실이 충분히 공유가 되어야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;-&amp;gt; gqlgen 공식문서에서 제공하는 방법과 비교해서 이점이 없음&lt;/p&gt;
&lt;pre id=&quot;code_1691591081305&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func ginMiddleware(c *gin.Context) {
    ctx := c.Request.Context()
    ctx = context.WithValue(ctx, key, value)
    c.Request = c.Request.WithContext(ctx)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. h.ServeHTTP() 이전에, gin.Context.Keys에 존재하는 값들을 한 번에 gin.Context.Request.ctx로 넣어주기&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;h.ServeHTTP()를 호출하기 직전에 gin.Context.Keys에 존재하는 값들을&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;한 번에 gin.Context.Request.ctx로 넣어주는 방법이다.&lt;/p&gt;
&lt;pre id=&quot;code_1691488381149&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;...
// Defining the Graphql handler
func graphqlHandler() gin.HandlerFunc {
	// NewExecutableSchema and Config are in the generated.go file
	// Resolver is in the resolver.go file
	h := handler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{Resolvers: &amp;amp;graph.Resolver{}}))

	return func(c *gin.Context) {
		ctx := c.Request.Context()
		for k, v := range c.Keys {
			ctx = context.WithValue(ctx, k, v)
		}
		req := c.Request.WithContext(ctx)
		h.ServeHTTP(c.Writer, req)
	}
}
...&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;후에 다른 개발자 혹은 이전 히스토리를 잊어버린 당사자가 context에 값을 추가로 저장해야할 상황에서 신경쓸 포인트가 줄어들기 때문이다.&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;/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>Go/GO-GraphQL</category>
      <category>Gin</category>
      <category>gin-gonic</category>
      <category>gin.Context</category>
      <category>go</category>
      <category>golang</category>
      <category>gqlgen</category>
      <category>Request.ctx</category>
      <author>bba_dda</author>
      <guid isPermaLink="true">https://bba-dda.tistory.com/127</guid>
      <comments>https://bba-dda.tistory.com/127#entry127comment</comments>
      <pubDate>Wed, 9 Aug 2023 23:43:08 +0900</pubDate>
    </item>
    <item>
      <title>[C++/프로그래머스] 야근 지수</title>
      <link>https://bba-dda.tistory.com/126</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/12927#&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/12927#&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1649161636206&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;회사원 Demi는 가끔은 야근을 하는데요, 야근을 하면 야근 피로도가 쌓입니다. 야근 피로도는 야근을 시작한 시점에서 남은 일의 작업량을 제곱하여 더한 값입니다. Demi는 N시간 동안 야근 피로도&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12927#&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12927&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/5H45K/hyNVLz5moA/cO4V0Glc7nObd1nAh1eg30/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/MXqlX/hyNVWBHw5f/OrvVKfKoZI7RFoYML4xSk1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/12927#&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12927#&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/5H45K/hyNVLz5moA/cO4V0Glc7nObd1nAh1eg30/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/MXqlX/hyNVWBHw5f/OrvVKfKoZI7RFoYML4xSk1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626');&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;회사원 Demi는 가끔은 야근을 하는데요, 야근을 하면 야근 피로도가 쌓입니다. 야근 피로도는 야근을 시작한 시점에서 남은 일의 작업량을 제곱하여 더한 값입니다. Demi는 N시간 동안 야근 피로도&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사원 Demi는 가끔은 야근을 하는데요, 야근을 하면 야근 피로도가 쌓입니다.&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Demi는 N시간 동안 야근 피로도를 최소화하도록 일할 겁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Demi가 1시간 동안 작업량 1만큼을 처리할 수 있다고 할 때,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퇴근까지 남은 N 시간과 각 일에 대한 작업량 works에 대해 &lt;b&gt;야근 피로도를 최소화한 값&lt;/b&gt;을 리턴하는 함수 solution을 완성해주세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
제한 사항
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;works는 길이 1 이상, 20,000 이하인 배열입니다.&lt;/li&gt;
&lt;li&gt;works의 원소는 50000 이하인 자연수입니다.&lt;/li&gt;
&lt;li&gt;n은 1,000,000 이하인 자연수입니다.&lt;/li&gt;
&lt;/ul&gt;
입출력 예
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;works&lt;/td&gt;
&lt;td&gt;n&lt;/td&gt;
&lt;td&gt;result&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[4, 3, 3]&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[2, 1, 2]&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입출력 예 #1&lt;br /&gt;n=4 일 때, 남은 일의 작업량이 [4, 3, 3] 이라면 야근 지수를 최소화하기 위해 4시간동안 일을 한 결과는 [2, 2, 2]입니다. 이 때 야근 지수는 22&lt;span&gt;&amp;nbsp;&lt;/span&gt;+ 22&lt;span&gt;&amp;nbsp;&lt;/span&gt;+ 22&lt;span&gt;&amp;nbsp;&lt;/span&gt;= 12 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입출력 예 #2&lt;br /&gt;n=1일 때, 남은 일의 작업량이 [2,1,2]라면 야근 지수를 최소화하기 위해 1시간동안 일을 한 결과는 [1,1,2]입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;야근지수는 12&lt;span&gt;&amp;nbsp;&lt;/span&gt;+ 12&lt;span&gt;&amp;nbsp;&lt;/span&gt;+ 22&lt;span&gt;&amp;nbsp;&lt;/span&gt;= 6입니다.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;야근지수를 남은 일들의 작업량의 &lt;b&gt;제곱수&lt;/b&gt;를 더해서 구하기 때문에, 일들의 작업량을 최대한 고르게 만드는 것이 중요하다. (가장 작은 최댓값이 되도록 n시간 분배하기)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) works = [8,8,9] / n = 3일 때, [8,8,6]으로 만드는 것 보다 [8,7,7]로 만드는게 야근지수가 작다.&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;따라서, works를 내림차순으로 정렬하고 idx = 0부터 시작해서 깎아나간다는 느낌으로 접근했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;861&quot; data-origin-height=&quot;929&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m0F7e/btryspt2tG4/KRHiK2LeKZRMG0we1z5TFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m0F7e/btryspt2tG4/KRHiK2LeKZRMG0we1z5TFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m0F7e/btryspt2tG4/KRHiK2LeKZRMG0we1z5TFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm0F7e%2Fbtryspt2tG4%2FKRHiK2LeKZRMG0we1z5TFk%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;250&quot; height=&quot;270&quot; data-origin-width=&quot;861&quot; data-origin-height=&quot;929&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;831&quot; data-origin-height=&quot;929&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ezYX5s/btryyWQ6BBd/r612g6uDypauv9FBCZmLS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ezYX5s/btryyWQ6BBd/r612g6uDypauv9FBCZmLS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ezYX5s/btryyWQ6BBd/r612g6uDypauv9FBCZmLS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FezYX5s%2FbtryyWQ6BBd%2Fr612g6uDypauv9FBCZmLS1%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;250&quot; height=&quot;279&quot; data-origin-width=&quot;831&quot; data-origin-height=&quot;929&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 단계에서 파란색 부분(깎아야 할 부분)이 남아있는 n보다 크면 깎고 max_idx++,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작으면 깎을 수 있을 만큼만 최대한 깎고 남은 works로 야근 지수를 구하는 방식으로 진행했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;2144&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FdzVg/btryyE4fa1G/I7rNH4R9hIhxEsVaZ49NqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FdzVg/btryyE4fa1G/I7rNH4R9hIhxEsVaZ49NqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FdzVg/btryyE4fa1G/I7rNH4R9hIhxEsVaZ49NqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFdzVg%2FbtryyE4fa1G%2FI7rNH4R9hIhxEsVaZ49NqK%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;400&quot; height=&quot;858&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;2144&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1649161804514&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

long long solution(int n, vector&amp;lt;int&amp;gt; works) {
    sort(works.begin(), works.end(), greater&amp;lt;int&amp;gt;());
    long long answer = 0;
    int max_idx = 0;
    int h;
    
    // 5 * 10^4
    while (max_idx &amp;lt; works.size() - 1) {
        while (max_idx &amp;lt; works.size() - 2 &amp;amp;&amp;amp; works[max_idx] == works[max_idx+1]) {
            max_idx++;
        }
        h = works[max_idx] - works[max_idx+1];
        if ((max_idx+1)*h &amp;gt; n) break;
        n -= (max_idx+1)*h;
        max_idx++;
    }
    int val = works[max_idx];
    
    if ((max_idx+1)*val &amp;lt;= n) return 0;

    val -= n/(max_idx+1);
    n -= n/(max_idx+1) * (max_idx+1);
    
    answer += (long long)val*(long long)val*(long long)(max_idx+1-n);
    answer += (long long)(val-1)*(long long)(val-1)*(long long)n;
    
    // 5 * 10^4
    for (int i = max_idx+1;i&amp;lt;works.size();i++) {
        answer += (long long)(works[i]*works[i]);
    }
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결과&lt;/h2&gt;
&lt;div&gt;정확성 테스트&lt;/div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 58.0233%; height: 467px;&quot; border=&quot;1&quot; data-category=&quot;correctness&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr data-testcase-id=&quot;23406&quot;&gt;
&lt;td&gt;테스트 1 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.01ms, 3.48MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;23407&quot;&gt;
&lt;td&gt;테스트 2 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.01ms, 4.11MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;23408&quot;&gt;
&lt;td&gt;테스트 3 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.01ms, 4.09MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;23409&quot;&gt;
&lt;td&gt;테스트 4 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.01ms, 4.18MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;23410&quot;&gt;
&lt;td&gt;테스트 5 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.01ms, 4.17MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;23411&quot;&gt;
&lt;td&gt;테스트 6 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.01ms, 4.17MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;23412&quot;&gt;
&lt;td&gt;테스트 7 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.01ms, 4.16MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;23413&quot;&gt;
&lt;td&gt;테스트 8 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.02ms, 4.09MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;23414&quot;&gt;
&lt;td&gt;테스트 9 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.03ms, 4.09MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;23415&quot;&gt;
&lt;td&gt;테스트 10 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.01ms, 4.09MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;23416&quot;&gt;
&lt;td&gt;테스트 11 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.01ms, 3.58MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;23417&quot;&gt;
&lt;td&gt;테스트 12 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.01ms, 4.18MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;23418&quot;&gt;
&lt;td&gt;테스트 13 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.01ms, 3.64MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div&gt;효율성 테스트&lt;/div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 58.0233%; height: 60px;&quot; border=&quot;1&quot; data-category=&quot;effectiveness&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot; data-testcase-id=&quot;23419&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;테스트 1 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;통과 (0.10ms, 3.78MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot; data-testcase-id=&quot;23420&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;테스트 2 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;통과 (0.09ms, 3.85MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div&gt;채점 결과&lt;/div&gt;
&lt;div&gt;정확성: 86.7&lt;/div&gt;
&lt;div&gt;효율성: 13.3&lt;/div&gt;
&lt;div&gt;합계: 100.0 / 100.0&lt;/div&gt;</description>
      <category>Algorithm</category>
      <category>C++</category>
      <category>야근 지수</category>
      <category>프로그래머스</category>
      <author>bba_dda</author>
      <guid isPermaLink="true">https://bba-dda.tistory.com/126</guid>
      <comments>https://bba-dda.tistory.com/126#entry126comment</comments>
      <pubDate>Tue, 5 Apr 2022 21:55:07 +0900</pubDate>
    </item>
    <item>
      <title>[C++/프로그래머스] 숫자 게임</title>
      <link>https://bba-dda.tistory.com/125</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/12987&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/12987&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1648556636840&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;xx 회사의 2xN명의 사원들은 N명씩 두 팀으로 나눠 숫자 게임을 하려고 합니다. 두 개의 팀을 각각 A팀과 B팀이라고 하겠습니다. 숫자 게임의 규칙은 다음과 같습니다. 먼저 모든 사원이 무작위로 &quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12987&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12987&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/8uSdd/hyNSeVnGTv/wkzzBu27EXqIWagZqCN4EK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/SMrVD/hyNRcrlO0b/Ifq4XmFcVjskSnDK09KVJK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/12987&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/12987&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/8uSdd/hyNSeVnGTv/wkzzBu27EXqIWagZqCN4EK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/SMrVD/hyNRcrlO0b/Ifq4XmFcVjskSnDK09KVJK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626');&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;xx 회사의 2xN명의 사원들은 N명씩 두 팀으로 나눠 숫자 게임을 하려고 합니다. 두 개의 팀을 각각 A팀과 B팀이라고 하겠습니다. 숫자 게임의 규칙은 다음과 같습니다. 먼저 모든 사원이 무작위로&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A와 B를 모두 내림차순으로 정렬한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;a_idx, b_max_idx를 0부터 시작해서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A[a_idx] vs B[b_max_idx] 이렇게 순차적으로 대결을 해 나갈건데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. A가 이기는 경우라면, B의 가장 작은 원소를 낸다고 생각 (어차피 질 바에 제일 작은 걸로 지자!)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. B가 이기는 경우라면, B[b_max_idx]를 낸다고 생각한다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1648556911048&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;

using namespace std;

int solution(vector&amp;lt;int&amp;gt; A, vector&amp;lt;int&amp;gt; B) {
    int answer = 0;
    int a_idx = 0;
    int b_max_idx = 0;
    
    sort(A.begin(), A.end(), greater&amp;lt;int&amp;gt;());
    sort(B.begin(), B.end(), greater&amp;lt;int&amp;gt;());
    
    for (int i=0;i&amp;lt;A.size();i++) {
        if (A[a_idx++] &amp;lt; B[b_max_idx]) { 
            b_max_idx++;
            answer++;
        }
        // else // 제일 작은 패로 lose
    }
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결과&lt;/h2&gt;
&lt;div&gt;정확성 테스트&lt;/div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-category=&quot;correctness&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr data-testcase-id=&quot;22278&quot;&gt;
&lt;td&gt;테스트 1 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.01ms, 4.16MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;22279&quot;&gt;
&lt;td&gt;테스트 2 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.01ms, 4.17MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;22280&quot;&gt;
&lt;td&gt;테스트 3 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.01ms, 4.18MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;22281&quot;&gt;
&lt;td&gt;테스트 4 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.01ms, 3.66MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;22282&quot;&gt;
&lt;td&gt;테스트 5 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.01ms, 4.09MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;22283&quot;&gt;
&lt;td&gt;테스트 6 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.01ms, 4.17MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;22284&quot;&gt;
&lt;td&gt;테스트 7 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.01ms, 4.17MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;22285&quot;&gt;
&lt;td&gt;테스트 8 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.01ms, 4.17MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;22286&quot;&gt;
&lt;td&gt;테스트 9 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.09ms, 4.09MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;22287&quot;&gt;
&lt;td&gt;테스트 10 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.04ms, 4.18MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;22288&quot;&gt;
&lt;td&gt;테스트 11 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.07ms, 3.7MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;22289&quot;&gt;
&lt;td&gt;테스트 12 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.04ms, 3.61MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;22290&quot;&gt;
&lt;td&gt;테스트 13 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.48ms, 4.11MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;22291&quot;&gt;
&lt;td&gt;테스트 14 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.94ms, 4.18MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;22292&quot;&gt;
&lt;td&gt;테스트 15 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.53ms, 4.17MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;22293&quot;&gt;
&lt;td&gt;테스트 16 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.90ms, 4.23MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;22294&quot;&gt;
&lt;td&gt;테스트 17 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.04ms, 3.71MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;22295&quot;&gt;
&lt;td&gt;테스트 18 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (0.08ms, 4.18MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div&gt;효율성 테스트&lt;/div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-category=&quot;effectiveness&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr data-testcase-id=&quot;22296&quot;&gt;
&lt;td&gt;테스트 1 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (12.01ms, 10.4MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;22297&quot;&gt;
&lt;td&gt;테스트 2 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (11.48ms, 10.1MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-testcase-id=&quot;22298&quot;&gt;
&lt;td&gt;테스트 3 &lt;span&gt;〉&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;통과 (11.63ms, 10.1MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div&gt;채점 결과&lt;/div&gt;
&lt;div&gt;정확성: 85.7&lt;/div&gt;
&lt;div&gt;효율성: 14.3&lt;/div&gt;
&lt;div&gt;합계: 100.0 / 100.0&lt;/div&gt;</description>
      <category>Algorithm</category>
      <category>C++</category>
      <category>숫자 게임</category>
      <category>프로그래머스</category>
      <author>bba_dda</author>
      <guid isPermaLink="true">https://bba-dda.tistory.com/125</guid>
      <comments>https://bba-dda.tistory.com/125#entry125comment</comments>
      <pubDate>Tue, 29 Mar 2022 21:29:09 +0900</pubDate>
    </item>
    <item>
      <title>[Go] go-socket.io의 Redis Adapter</title>
      <link>https://bba-dda.tistory.com/124</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;socket.io에서는 redis adapter를 사용할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Adapter가 무엇일까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://socket.io/docs/v4/adapter/&quot;&gt;https://socket.io/docs/v4/adapter/&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정의&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Adapter는 client의 부분집합 혹은 전체 client에게 event를 broadcast하는 server-side component이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;왜 필요할까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 socket.io에서 &lt;code&gt;in-memory adapter&lt;/code&gt;가 broadcast를 수행하는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;socket.io 서버가 여러대로 분산되어있을 때, in-memory adapter를 이용하면 문제가 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 그림처럼 두 개의 socket.io 서버가 있을 때, in-memory adapter를 이용하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;server A에서 server B에 연결되어있는 client들에게 메세지를 보내줄 수 없기 때문이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;913&quot; data-origin-height=&quot;629&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nJE62/btru2opU5b0/Gu3RlJpTfkXEpk23QCMP21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nJE62/btru2opU5b0/Gu3RlJpTfkXEpk23QCMP21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nJE62/btru2opU5b0/Gu3RlJpTfkXEpk23QCMP21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnJE62%2Fbtru2opU5b0%2FGu3RlJpTfkXEpk23QCMP21%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;647&quot; height=&quot;446&quot; data-origin-width=&quot;913&quot; data-origin-height=&quot;629&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 기본 in-memory adapter를 &lt;code&gt;다른 adapter로 교체&lt;/code&gt;할 필요가 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;지원하는 Adapter의 종류&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 in-memory adapter를 제외하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;js의 socket.io에서는 공식적으로 4종류의 adapter가 존재한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://socket.io/docs/v4/redis-adapter/&quot;&gt;Redis adapter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://socket.io/docs/v4/mongo-adapter/&quot;&gt;MongoDB adapter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://socket.io/docs/v4/postgres-adapter/&quot;&gt;Postgres adapter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://socket.io/docs/v4/cluster-adapter/&quot;&gt;Cluster adapter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;go-socket.io는 따로 정리되어있는 문서는 없지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/googollee/go-socket.io&quot;&gt;github 소스코드&lt;/a&gt;를 봤을 때, redis-adapter만 존재한다. (22년 3월 기준)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Redis Adapter&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://socket.io/docs/v4/redis-adapter/&quot;&gt;https://socket.io/docs/v4/redis-adapter/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis Adapter는 Redis의 Pub/Sub 매커니즘에 의존한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;Redis Pub/Sub 매커니즘에 대해 잘 모른다면 먼저 공부하고 오는 것을 추천한다!&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;redis adapter에 연결된 여러 socket.io서버 중 한 서버에서 emit을 하면,&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 서버에 연결된 클라이언트로 전송되는 것은 당연하고&lt;/li&gt;
&lt;li&gt;추가적으로 Redis 채널에 메세지가 publish되고, 클러스터의 다른 socket.io 서버에서 이를 수신해 해당 서버에 연결된 클라이언트에게도 전송된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;913&quot; data-origin-height=&quot;629&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2dG7B/btruXU4p9nM/yrifFpiiDP9OP0kKJFoYkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2dG7B/btruXU4p9nM/yrifFpiiDP9OP0kKJFoYkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2dG7B/btruXU4p9nM/yrifFpiiDP9OP0kKJFoYkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2dG7B%2FbtruXU4p9nM%2FyrifFpiiDP9OP0kKJFoYkK%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;536&quot; height=&quot;369&quot; data-origin-width=&quot;913&quot; data-origin-height=&quot;629&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지가 문서의 설명이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서에서는 socket.io 서버가 여러 대로 분산되어 있을 때 사용한다는 내용만 적혀있다.&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;하지만 이것을 응용하면, socket.io서버가 아닌 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;일반적인 서버를 socket.io 서버에 대한 trigger처럼 사용&lt;/b&gt;&lt;/span&gt;할 수도 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;특정 서버에서 Redis에 특정 형식의 메세지를 publish했을 때,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;동일한 Redis에 연결된 socket.io 서버의 Redis Adapter를 통해 emit하게 만들 수 있다는 것이다!&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;988&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nSw3T/btruXUi6Qcl/UT4r4K9NOCWBfejHZjkIgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nSw3T/btruXUi6Qcl/UT4r4K9NOCWBfejHZjkIgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nSw3T/btruXUi6Qcl/UT4r4K9NOCWBfejHZjkIgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnSw3T%2FbtruXUi6Qcl%2FUT4r4K9NOCWBfejHZjkIgk%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;527&quot; height=&quot;374&quot; data-origin-width=&quot;988&quot; data-origin-height=&quot;700&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;Redis의 &lt;code&gt;어떤 채널&lt;/code&gt;에 &lt;code&gt;어떤 형식의 메세지&lt;/code&gt;를 publish 해야 socket.io의 redis adapter가 알아챌 수 있는지 알아야 한다.&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;js의 경우에는 가이드라인을 제공하는데,&amp;nbsp;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;(&lt;/span&gt;&lt;a style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot; href=&quot;https://github.com/socketio/socket.io-redis-adapter&quot;&gt;js의 redis adapter github&lt;/a&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;go-socket.io는 가이드라인을 제공하지 않아서 직접 찾아야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;삽질  ️&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;postman으로 socket을&amp;nbsp;연결해 놓고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;redis-cli를 이용해, 터미널에서 &lt;code&gt;psubscribe socket.io*&lt;/code&gt; 명령어를 이용해 구독하게 한 뒤&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에서 broadcast와 emit을 하면서 터미널에 어떻게 로그가 찍히는지 확인해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;code style=&quot;letter-spacing: 0px;&quot;&gt;socket.io*&lt;/code&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;를 구독한 이유는, redis adapter 설정에 prefix의 기본값이 socket.io였기 때문이다. redis 채널 이름이 redis adapter의 prefix로 시작한다는 것을 &lt;/span&gt;&lt;a href=&quot;https://github.com/socketio/socket.io-redis-adapter#how-does-it-work-under-the-hood&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&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;size16&quot;&gt;go로 작성된 socket.io 서버에서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;server.BroadcastToNamespace(&quot;/test&quot;, &quot;hi&quot;, &quot;hello&quot;)&lt;/code&gt;의 결과로 아래와 같은 로그가 찍혔다.&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;1) &quot;pmessage&quot;
2) &quot;socket.io*&quot;
3) &quot;socket.io#/test#c3e57b2d-2400-45d9-9cda-6db7817929df&quot;
4) &quot;{\&quot;args\&quot;:[\&quot;hello\&quot;],\&quot;opts\&quot;:[\&quot;\&quot;,\&quot;hi\&quot;]}&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;채널 : &lt;code&gt;socket.io#/test#${uid}&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메세지: &lt;code&gt;{\&quot;args\&quot;:[\&quot;hello\&quot;],\&quot;opts\&quot;:[\&quot;\&quot;,\&quot;hi\&quot;]}&lt;/code&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;code&gt;prefix#namespace#&lt;/code&gt; 채널에 아래와 같은 형식의 메세지를 publish해야 한다는 것을 알게되었다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;{
    args: [payload...],
    opts: [&quot;roomId&quot;, &quot;event name&quot;],
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;args: 말 그대로 내용(payload)&lt;/li&gt;
&lt;li&gt;opts는 2칸짜리 배열인데,
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;opts[0] : roomId (roomId가 필요 없는 경우에는 &amp;ldquo;&amp;rdquo; 이렇게 빈 값으로 작성)&lt;/li&gt;
&lt;li&gt;opts[1] : event name 으로 작성하면 된다!&lt;/li&gt;
&lt;/ul&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;이렇게 publish하니 매우 잘 작동했다 :)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 분석&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만.. 뭔가 주먹구구식으로 찾은 것 같은 느낌이 들어, go-socket.io코드를 분석해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 전에도 코드 분석을 시도했으나 명확하게 보이지 않았는데, 답을 알고나서 코드를 보니까 더 잘 읽혔던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 redis adapter관련 코드가 한 파일에 들어있어서 그리 복잡하지 않게 볼 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/googollee/go-socket.io/blob/65235b23be9a8383b68d79a377552898f49386fc/redis_broadcast.go#L109&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;코드 링크&lt;/a&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;1. 채널 이름&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드에서 key field를 보면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;prefix#nsp(namespace)#uid&lt;/code&gt; 라고 되어있는데, 위에서 확인한 redis 로그의 형식과 일치한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;socket.io#/test#c3e57b2d-2400-45d9-9cda-6db7817929df&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot;&gt;&lt;code&gt;func newRedisBroadcast(nsp string, opts *RedisAdapterOptions) (*redisBroadcast, error) {
    ...
    rbc := &amp;amp;redisBroadcast{
        rooms:      make(map[string]map[string]Conn),
        requests:   make(map[string]interface{}),
        sub:        subConn,
        pub:        pubConn,
        key:        fmt.Sprintf(&quot;%s#%s#%s&quot;, opts.Prefix, nsp, uid),
        reqChannel: fmt.Sprintf(&quot;%s-request#%s&quot;, opts.Prefix, nsp),
        resChannel: fmt.Sprintf(&quot;%s-response#%s&quot;, opts.Prefix, nsp),
        nsp:        nsp,
        uid:        uid,
    }

    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만, 이건 socket.io 서버에서 broadcast했을 때 생기는 redis message이고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그냥 redis에 바로 publish할 때는 &lt;code&gt;uid&lt;/code&gt; 부분을 생략해도 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 defalut namespace (&lt;code&gt;/&lt;/code&gt;)는 &lt;code&gt;namespace&lt;/code&gt; 자리에 &lt;code&gt;/&lt;/code&gt;를 적는 것이 아니라 아예 생략해야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론 ⭐️&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 내가 만든 프로젝트에서는&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;prefix : socket.io&lt;/li&gt;
&lt;li&gt;namespace : defalut (/)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;였기 때문에 &lt;code&gt;socket.io##&lt;/code&gt; 채널에 publish를 해야 했다.&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;js는 &lt;a href=&quot;https://github.com/socketio/socket.io-redis-adapter#how-does-it-work-under-the-hood&quot;&gt;여기&lt;/a&gt;에 채널 네이밍 관련 내용이 나와있는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 따르면 &lt;code&gt;socket.io#namespace#roomId&lt;/code&gt; 로 publish 해야하는데, go에서는 채널 이름에 roomId를 적는 방법이 먹히지 않았다. (설계가 다르게 되어있나보다..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;go-socket.io에서는 위에서 말한 것 처럼 opts[0]에 roomId를 적어줘야 한다.&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;2. 메세지 형식&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드는 redis adapter가 redis에서 subscribe를 통해 받은 메세지를 처리하는 메소드이다.&lt;/p&gt;
&lt;pre class=&quot;go&quot;&gt;&lt;code&gt;func (bc *redisBroadcast) onMessage(channel string, msg []byte) error {
    channelParts := strings.Split(channel, &quot;#&quot;)
    nsp := channelParts[len(channelParts)-2]
    if bc.nsp != nsp {
        return nil
    }

    uid := channelParts[len(channelParts)-1]
    if bc.uid == uid {
        return nil
    }

    var bcMessage map[string][]interface{}
    err := json.Unmarshal(msg, &amp;amp;bcMessage)
    if err != nil {
        return errors.New(&quot;invalid broadcast message&quot;)
    }

    args := bcMessage[&quot;args&quot;]
    opts := bcMessage[&quot;opts&quot;]

    room, ok := opts[0].(string)
    if !ok {
        return errors.New(&quot;invalid room&quot;)
    }

    event, ok := opts[1].(string)
    if !ok {
        return errors.New(&quot;invalid event&quot;)
    }

    if room != &quot;&quot; {
        bc.send(room, event, args...)
    } else {
        bc.sendAll(event, args...)
    }

    return nil
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/googollee/go-socket.io/blob/850c4fb3336c9d821b4a5b8b0913433411f79437/redis_broadcast.go#L292&quot;&gt;코드 link&lt;/a&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;onMessage()에서 msg를 처리할 때,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;args, opts 필드를 사용하는 것을 확인할 수 있고, 위에서 redis 로그를 통해서 유추한 것과 정확히 일치했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;opts[0]&lt;/code&gt;번 room에 &lt;code&gt;opts[1]&lt;/code&gt; event로 &lt;code&gt;args&lt;/code&gt;를 broadcast&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;room단위가 아니라 broadcast하고 싶을 때에도 opts 길이는 2여야함 (roomid를 빈 값(&amp;rdquo;&amp;rdquo;)으로 넣어 보내기)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;opts: [&amp;rdquo;&amp;rdquo;, &amp;ldquo;event name&amp;rdquo;]&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;socket.io 문서에 따르면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;adapter를 통해서 단순 메세지 emit 뿐 아니라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;join room이나, leave room 등의 기능도 가능하다고 하는데 어떻게 하는지 아직 모른다!&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;다만 redis_broadcast.go의 dispatch() 에서 onMessage 말고도, onResponse, onRequest도 수행하고 있기 때문에 이쪽 코드를 더 자세하게 보면 알 수 있을 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;go&quot;&gt;&lt;code&gt;func (bc *redisBroadcast) dispatch() {
    for {
        switch m := bc.sub.Receive().(type) {
        case redis.Message:
            if m.Channel == bc.reqChannel {
                bc.onRequest(m.Data)
                break
            } else if m.Channel == bc.resChannel {
                bc.onResponse(m.Data)
                break
            }

            err := bc.onMessage(m.Channel, m.Data)
            if err != nil {
                return
            }

        case redis.Subscription:
            if m.Count == 0 {
                return
            }

        case error:
            return
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;추가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;go-socket.io에서 쓰는 redis package : redigo&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 개발한 socket server에서 쓰는 redis package : go-redis&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;redigo vs redis&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://go.libhunt.com/compare-redigo-vs-redis&quot;&gt;https://go.libhunt.com/compare-redigo-vs-redis&lt;/a&gt;&lt;/p&gt;</description>
      <category>Go</category>
      <category>go redis adapter</category>
      <category>go-socket.io</category>
      <category>golang</category>
      <category>redis adapter</category>
      <author>bba_dda</author>
      <guid isPermaLink="true">https://bba-dda.tistory.com/124</guid>
      <comments>https://bba-dda.tistory.com/124#entry124comment</comments>
      <pubDate>Thu, 3 Mar 2022 17:42:07 +0900</pubDate>
    </item>
    <item>
      <title>[C++/프로그래머스] 파괴되지 않은 건물</title>
      <link>https://bba-dda.tistory.com/123</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/92344&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/92344&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1646134124485&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;[[5,5,5,5,5],[5,5,5,5,5],[5,5,5,5,5],[5,5,5,5,5]] [[1,0,0,3,4,4],[1,2,0,2,3,2],[2,1,0,3,1,2],[1,0,1,3,3,1]] 10 [[1,2,3],[4,5,6],[7,8,9]] [[1,1,1,2,2,4],[1,0,0,1,1,2],[2,2,0,2,0,100]] 6&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/92344&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/92344&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ejZuW7/hyNzVccRMK/NTcTUIXDC2WOiQm1p3FnXK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/c82ozV/hyNzXulRGD/HStyeadkbGTXj4rlMOGMXk/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/92344&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/92344&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ejZuW7/hyNzVccRMK/NTcTUIXDC2WOiQm1p3FnXK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/c82ozV/hyNzXulRGD/HStyeadkbGTXj4rlMOGMXk/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626');&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;[[5,5,5,5,5],[5,5,5,5,5],[5,5,5,5,5],[5,5,5,5,5]] [[1,0,0,3,4,4],[1,2,0,2,3,2],[2,1,0,3,1,2],[1,0,1,3,3,1]] 10 [[1,2,3],[4,5,6],[7,8,9]] [[1,1,1,2,2,4],[1,0,0,1,1,2],[2,2,0,2,0,100]] 6&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제가 길다..&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요약하면,&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;NxM의 초기 borad에서 (r1, c1) ~(r2, c2)의 임의의 구간?부분을 공격(-)했다가 회복(+)했다가 하면서&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로 값이 0보다 큰 칸의 갯수를 세면 되는 문제이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난주 스터디시간에 들었던 IMOS법을 이용해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://driip.me/65d9b58c-bf02-44bf-8fba-54d394ed21e0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;IMOS법이란?&lt;/a&gt; &amp;lt;- 이 블로그에 아주 설명이 잘 되어 있다.&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;N+1 x M+1 크기의 cache배열을 만들어서 imos법을 이용해 공격, 회복 로그?를 기록하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맨 마지막에 가로로 한번, 세로로 한번 쭉 훑어 최종적인 cache배열 값을 구한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고, cache[r][c] + board[r][c] &amp;gt; 0 인 칸들의 갯수를 세어주면 된다.&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;이런 느낌의 (완전탐색으로하면 시간초과날거같은) 문제를 그동안 매우 어려워했는데, imos법을 알게되었으니 풀 수 있는 문제가 늘어날 것 같다.&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;* imos를 아이모스가 아니라, 이모스라고 읽는 것 같다 (일본어인듯?)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1646136946513&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;

using namespace std;

vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; cache;
int solution(vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; board, vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; skill) {
    int answer = 0;
    int N = board.size();
    int M = board[0].size();
    cache = vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt;(N+1, vector&amp;lt;int&amp;gt;(M+1, 0)); // imos위해 하나씩 크게 만들기
    for (auto s: skill) {
        int degree = s[5];
        int r1 = s[1]; int c1 = s[2];
        int r2 = s[3]; int c2 = s[4];
        if (s[0] == 2) { // 공격
            cache[r1][c1] += degree;
            cache[r1][c2+1] -= degree;
            cache[r2+1][c1] -= degree;
            cache[r2+1][c2+1] += degree;
        } else { // 회복
            cache[r1][c1] -= degree;
            cache[r1][c2+1] += degree;
            cache[r2+1][c1] += degree;
            cache[r2+1][c2+1] -= degree;
        }
    }
    // 가로로 휩쓸기
    for (int r=0;r&amp;lt;N;r++) {
        int sum = cache[r][0];
        for (int c=1;c&amp;lt;M;c++) {
            cache[r][c] += sum;
            sum = cache[r][c];
        }
    }
    // 세로로 휩쓸기
    for (int c=0;c&amp;lt;M;c++) {
        int sum = cache[0][c];
        for (int r=1;r&amp;lt;N;r++) {
            cache[r][c] += sum;
            sum = cache[r][c];
        }
    }
    
    for (int r=0;r&amp;lt;N;r++) {
        for (int c=0;c&amp;lt;M;c++) {
            if (cache[r][c] + board[r][c] &amp;gt; 0) answer++;
        }
    }
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결과&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-03-01 at 9.18.00 PM.png&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;976&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfkW8Q/btruKj4xHCE/ArInQhViElFYPIzrc3aUk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfkW8Q/btruKj4xHCE/ArInQhViElFYPIzrc3aUk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfkW8Q/btruKj4xHCE/ArInQhViElFYPIzrc3aUk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfkW8Q%2FbtruKj4xHCE%2FArInQhViElFYPIzrc3aUk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;351&quot; height=&quot;503&quot; data-filename=&quot;Screen Shot 2022-03-01 at 9.18.00 PM.png&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;976&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Algorithm</category>
      <category>2차원 배열 누적합</category>
      <category>C++</category>
      <category>imos</category>
      <category>카카오 블라인드 채용</category>
      <category>파괴되지 않은 건물</category>
      <author>bba_dda</author>
      <guid isPermaLink="true">https://bba-dda.tistory.com/123</guid>
      <comments>https://bba-dda.tistory.com/123#entry123comment</comments>
      <pubDate>Tue, 1 Mar 2022 21:20:28 +0900</pubDate>
    </item>
    <item>
      <title>[C++/프로그래머스] 이중우선순위큐</title>
      <link>https://bba-dda.tistory.com/122</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/42628&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/42628&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1646133553763&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;코딩테스트 연습 - 이중우선순위큐&quot; data-og-description=&quot;&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42628&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42628&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/E3mAD/hyNzPDdSwn/OpLdTPd536xl2iekks3jfK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/b18qOx/hyNA19Rsey/9ymDZqm7lRera2TbdKjtv1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/42628&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42628&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/E3mAD/hyNzPDdSwn/OpLdTPd536xl2iekks3jfK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/b18qOx/hyNA19Rsey/9ymDZqm7lRera2TbdKjtv1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;코딩테스트 연습 - 이중우선순위큐&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이중 우선순위 큐는 다음 연산을 할 수 있는 자료구조를 말합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;명령어&lt;/td&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;I 숫자&lt;/td&gt;
&lt;td&gt;큐에 주어진 숫자를 삽입합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;D 1&lt;/td&gt;
&lt;td&gt;큐에서 최댓값을 삭제합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;D -1&lt;/td&gt;
&lt;td&gt;큐에서 최솟값을 삭제합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이중 우선순위 큐가 할 연산 operations가 매개변수로 주어질 때,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 연산을 처리한 후 &lt;b&gt;큐가 비어있으면 [0,0] 비어있지 않으면 [최댓값, 최솟값]을 return&lt;/b&gt; 하도록 solution 함수를 구현해주세요.&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;operations는 길이가 1 이상 1,000,000 이하인 문자열 배열입니다.&lt;/li&gt;
&lt;li&gt;operations의 원소는 큐가 수행할 연산을 나타냅니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원소는 &amp;ldquo;명령어 데이터&amp;rdquo; 형식으로 주어집니다.- 최댓값/최솟값을 삭제하는 연산에서 최댓값/최솟값이 둘 이상인 경우, 하나만 삭제합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;빈 큐에 데이터를 삭제하라는 연산이 주어질 경우, 해당 연산은 무시합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C++ STL의 Priority Queue를 이용하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만, 내림차순의 큐(minQ)와 오름차순의 큐(maxQ) 두개를 이용하는 과정에서 minQ에서 pop한 원소를 maxQ에서도 지울 수 없기 때문에, count변수를 따로 이용하였다.&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;큐에 insert할 때 count++&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;maxQ or minQ에서 pop할 때 count--&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;count == 0 이 되면, minQ, maxQ를 모두 초기화해주었다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1646133902427&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;queue&amp;gt;
#include &amp;lt;iostream&amp;gt;

using namespace std;

int count = 0;
priority_queue&amp;lt;int, vector&amp;lt;int&amp;gt;, greater&amp;lt;int&amp;gt;&amp;gt; minQ;
priority_queue&amp;lt;int, vector&amp;lt;int&amp;gt;, less&amp;lt;int&amp;gt;&amp;gt; maxQ;

void operation(string op) {
    if (op[0] == 'I') {
        string num = op.substr(2);
        int n = stoi(num);
        maxQ.push(n);
        minQ.push(n);
        count++;
    }
    if (op[0] == 'D') {
        if (count &amp;lt;= 0) {
            return;
        }
        if (op[2] == '1') { // 최댓값 삭제
            maxQ.pop();
            count--;
        }
        else { // 최솟값 삭제
            minQ.pop();
            count--;
        }
        if (count == 0) {
            minQ= priority_queue&amp;lt;int, vector&amp;lt;int&amp;gt;, greater&amp;lt;int&amp;gt;&amp;gt;();
            maxQ = priority_queue&amp;lt;int, vector&amp;lt;int&amp;gt;, less&amp;lt;int&amp;gt;&amp;gt;();
        }
    }
}
vector&amp;lt;int&amp;gt; solution(vector&amp;lt;string&amp;gt; operations) {
    vector&amp;lt;int&amp;gt; answer;
    for (string op: operations) {
        operation(op);
    }
    if (count == 0) {
        return {0,0};
    }
    else {
        return {maxQ.top(), minQ.top()};
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결과&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-03-01 at 8.25.38 PM.png&quot; data-origin-width=&quot;686&quot; data-origin-height=&quot;554&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxFbMX/btruMnyDbot/4NRcFz1AEIxgFGrZqxpx9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxFbMX/btruMnyDbot/4NRcFz1AEIxgFGrZqxpx9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxFbMX/btruMnyDbot/4NRcFz1AEIxgFGrZqxpx9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxFbMX%2FbtruMnyDbot%2F4NRcFz1AEIxgFGrZqxpx9k%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;310&quot; height=&quot;250&quot; data-filename=&quot;Screen Shot 2022-03-01 at 8.25.38 PM.png&quot; data-origin-width=&quot;686&quot; data-origin-height=&quot;554&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;</description>
      <category>Algorithm</category>
      <category>C++</category>
      <category>C++ STL</category>
      <category>priority queue</category>
      <category>이중우선순위큐</category>
      <category>프로그래머스</category>
      <author>bba_dda</author>
      <guid isPermaLink="true">https://bba-dda.tistory.com/122</guid>
      <comments>https://bba-dda.tistory.com/122#entry122comment</comments>
      <pubDate>Tue, 1 Mar 2022 20:27:01 +0900</pubDate>
    </item>
    <item>
      <title>[go/프로그래머스] 섬 연결하기</title>
      <link>https://bba-dda.tistory.com/121</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/42861?language=go&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/42861?language=go&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1644319938382&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;4 [[0,1,1],[0,2,2],[1,2,5],[1,3,1],[2,3,8]] 4&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42861?language=go&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42861?language=go&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cATHYi/hyNlcZSp00/ZSoQlfcxf8C6k5FY9E9XkK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/KhScZ/hyNloF0uDd/KCv0H3uqrxLluWtm8rl5X1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/42861?language=go&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42861?language=go&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cATHYi/hyNlcZSp00/ZSoQlfcxf8C6k5FY9E9XkK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/KhScZ/hyNloF0uDd/KCv0H3uqrxLluWtm8rl5X1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626');&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;4 [[0,1,1],[0,2,2],[1,2,5],[1,3,1],[2,3,8]] 4&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;n개의 섬 사이에 다리를 건설하는 비용(costs)이 주어질 때, 최소의 비용으로 &lt;b&gt;모든 섬이 서로 통행 가능하도록 만들 때 필요한 최소 비용&lt;/b&gt;을 return 하도록 solution을 완성하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다리를 여러 번 건너더라도, 도달할 수만 있으면 통행 가능하다고 봅니다. 예를 들어 A 섬과 B 섬 사이에 다리가 있고, B 섬과 C 섬 사이에 다리가 있으면 A 섬과 C 섬은 서로 통행 가능합니다.&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;섬의 개수 &lt;b&gt;n은 1 이상 100 이하&lt;/b&gt;입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;costs의 길이는&lt;span&gt;&amp;nbsp;&lt;/span&gt;((n-1) * n) / 2이하&lt;/b&gt;입니다.&lt;/li&gt;
&lt;li&gt;임의의 i에 대해, costs[i][0] 와 costs[i] [1]에는 다리가 연결되는 두 섬의 번호가 들어있고, costs[i] [2]에는 이 두 섬을 연결하는 다리를 건설할 때 드는 비용입니다.&lt;/li&gt;
&lt;li&gt;같은 연결은 두 번 주어지지 않습니다. 또한 순서가 바뀌더라도 같은 연결로 봅니다. 즉 0과 1 사이를 연결하는 비용이 주어졌을 때, 1과 0의 비용이 주어지지 않습니다.&lt;/li&gt;
&lt;li&gt;모든 섬 사이의 다리 건설 비용이 주어지지 않습니다. 이 경우, 두 섬 사이의 건설이 불가능한 것으로 봅니다.&lt;/li&gt;
&lt;li&gt;연결할 수 없는 섬은 주어지지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;입출력 예&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 56px;&quot;&gt;n&lt;/td&gt;
&lt;td style=&quot;width: 695.969px;&quot;&gt;costs&lt;/td&gt;
&lt;td style=&quot;width: 56.0312px;&quot;&gt;return&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 56px;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;width: 695.969px;&quot;&gt;[[0,1,1],[0,2,2],[1,2,5],[1,3,1],[2,3,8]]&lt;/td&gt;
&lt;td style=&quot;width: 56.0312px;&quot;&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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;costs를 그림으로 표현하면 다음과 같으며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초록색 경로로 연결하는 것이 가장 적은 비용으로 모두를 통행할 수 있는 경우의 수 이다.&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;748&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9Bzuk/btrsMzV8m4y/UdYlZD7G4LkvtzYD2ju6X1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9Bzuk/btrsMzV8m4y/UdYlZD7G4LkvtzYD2ju6X1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9Bzuk/btrsMzV8m4y/UdYlZD7G4LkvtzYD2ju6X1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9Bzuk%2FbtrsMzV8m4y%2FUdYlZD7G4LkvtzYD2ju6X1%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;225&quot; height=&quot;205&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;748&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;costs를 건설비용(costs[2])를 기준으로 오름차순 정렬한 후,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;loop를 돌면서 다리를 하나씩 선택해 나가는데,&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;사이클이 생기는 경우를 판단하기 위해, Union-Find 알고리즘을 이용하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매 경우에, find를 이용해 두 노드의 부모가 같은지 확인해주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부모가 같으면 사이클이 생기는 것이기 때문에 패스,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같지 않으면 해당 다리를 건설하고, 두 노드를 연결해주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1644320322502&quot; class=&quot;go&quot; data-ke-language=&quot;go&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import &quot;sort&quot;
var parents []int
func solution(n int, costs [][]int) int {
    answer := 0
    parents = make([]int, n+1) 
    for i:=0;i&amp;lt;n;i++ {
        parents[i] = i;
    }
    // costs 오름차순 정렬
    sort.Slice(costs, func (i, j int) bool {
            return costs[i][2] &amp;lt; costs[j][2]
    })
    
    for _, route:= range(costs) {
        if union(route[0], route[1]) {
            answer += route[2]
        }
    }
    return answer
}

// 최상위 부모 찾기
func find(a int) int {
    if a == parents[a] {
        return a
    }
    parents[a] = find(parents[a])
    return parents[a]
}

func union(a, b int) bool {
    a = find(a)
    b = find(b)
    
    // 사이클이 생기는 경우 패스
    if (a == b) {
        return false
    }
    // 두 노드를 연결해주기
    if (a &amp;gt; b) {
        parents[a] = b // 작은애를 큰 애한테 달기
    } else {
        parents[b] = a
    }
    return true
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결과&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen Shot 2022-02-08 at 8.39.16 PM.png&quot; data-origin-width=&quot;684&quot; data-origin-height=&quot;668&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFwtzl/btrsQpL6u13/k4ALaY3pDABghKDKKbVRk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFwtzl/btrsQpL6u13/k4ALaY3pDABghKDKKbVRk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFwtzl/btrsQpL6u13/k4ALaY3pDABghKDKKbVRk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFwtzl%2FbtrsQpL6u13%2Fk4ALaY3pDABghKDKKbVRk0%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;310&quot; height=&quot;303&quot; data-filename=&quot;Screen Shot 2022-02-08 at 8.39.16 PM.png&quot; data-origin-width=&quot;684&quot; data-origin-height=&quot;668&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Algorithm</category>
      <category>go</category>
      <category>golang</category>
      <category>Union-Find</category>
      <category>섬 연결하기</category>
      <category>프로그래머스</category>
      <author>bba_dda</author>
      <guid isPermaLink="true">https://bba-dda.tistory.com/121</guid>
      <comments>https://bba-dda.tistory.com/121#entry121comment</comments>
      <pubDate>Tue, 8 Feb 2022 20:41:52 +0900</pubDate>
    </item>
  </channel>
</rss>