<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <author>
    <name>어썸블로그</name>
  </author>
  <id>국내의 좋은 블로그 글들을 매일 배달해줍니다.</id>
  <title>개발자 어썸블로그</title>
  <updated>2026-09-08T10:00:29+09:00</updated>
  <entry>
    <author>
      <name>류광</name>
    </author>
    <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;p&gt;오랜만에 근황입니다. 번역한/하는 책들 이야기뿐.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
</content>
    <id>https://occamsrazr.net/tt/451</id>
    <link href="https://occamsrazr.net/tt/451"/>
    <summary type="html">오랜만에 근황입니다. 번역한/하는 책들 이야기뿐.</summary>
    <title>근황 - 2026-09-07 </title>
    <updated>2026-09-07T13:02:00+09:00</updated>
    <dc:date>2026-09-07T13:02:00+09:00</dc:date>
  </entry>
  <entry>
    <author>
      <name>김재호</name>
    </author>
    <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;카카오 메일로 계속 스팸메일이 옵니다.&lt;br&gt;
이메일 주소를 바꿔가면서.&lt;br&gt;
제목은 처음에는 &lt;code class="language-plaintext highlighter-rouge"&gt;(광고)&lt;/code&gt;였는데 스팸필터에 추가했더니 &lt;code class="language-plaintext highlighter-rouge"&gt;(광.고)&lt;/code&gt;로, 또 &lt;code class="language-plaintext highlighter-rouge"&gt;(광고.)&lt;/code&gt;로, 또 &lt;code class="language-plaintext highlighter-rouge"&gt;(광 .고 )&lt;/code&gt; 로.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://jeho.page/assets/img/kakao-spam.jpg" alt=""&gt;&lt;/p&gt;

&lt;p&gt;더 짜증나는 것은 이 메일들이 스팸메일함이 아닌 Inbox 로 들어와서 알림이 온다는 것.&lt;br&gt;
불필요한 알림 하나라도 줄이기 위해서 애를 쓰고있는데… 😂&lt;/p&gt;

&lt;p&gt;메일 하단의 발송자 정보에는 &lt;a href="https://namu.wiki/w/Namepr"&gt;네임피알&lt;/a&gt;이라는 이름이 적혀 있습니다.&lt;br&gt;
이 업체나 광고주에게 광고 메일 수신을 동의한 적은 당연히 없습니다.&lt;br&gt;
법인이 실제로 존재하지도 않는 유령회사인 것 같더군요.&lt;/p&gt;

&lt;p&gt;이런 식으로 메일을 보내는 업체가 얄미운 것은 당연한데, 메일 서비스를 운영하는 카카오도 아쉽습니다.&lt;br&gt;
&lt;a href="https://mail.kakao.com/policy?lang=ko"&gt;카카오메일의 광고 발송 안내&lt;/a&gt;에는 &lt;code class="language-plaintext highlighter-rouge"&gt;(광.고)&lt;/code&gt;를 쓰면 안 된다고 적혀 있습니다.&lt;br&gt;
&lt;img src="https://jeho.page/assets/img/kakao-mail-policy.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;떡하니 안 된다고 써놓은 바로 그 제목이 스팸메일함도 아닌 받은편지함까지 들어오는 겁니다.&lt;/p&gt;

&lt;p&gt;사용자가 할 수 있는 일은 스팸으로 신고하고, 바뀐 주소와 제목을 그때그때 차단 목록에 보태는 정도.&lt;br&gt;
그러면 상대는 점 하나를 더 찍습니다. 😂&lt;/p&gt;

&lt;p&gt;사용자들이 피곤해하는데 너무 방치해두는 것 같습니다. 서비스 제공자가 이 정도는 해줘야 하는 것 아닐까?&lt;br&gt;
자기 정책에 금지 사례로 박아둔 패턴 정도는 사용자가 신고하기 전에 서비스가 먼저 막아주면 좋겠습니다.&lt;/p&gt;

&lt;div class="section-divider"&gt;&lt;span class="ornament"&gt;✔️&lt;/span&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;함께 읽으면 좋은 글:&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://jeho.page/essay/2022/05/18/email-spam.html"&gt;스팸 문자 좀 그만보내요&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://jeho.page/essay/2022/05/02/kakao-ten-years.html"&gt;완성되지 않은 회사&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/body&gt;&lt;/html&gt;
</content>
    <id>https://jeho.page/essay/2026/09/03/kakao-mail.html</id>
    <link href="https://jeho.page/essay/2026/09/03/kakao-mail.html"/>
    <summary type="html">카카오 메일로 계속 스팸메일이 옵니다. 이메일 주소를 바꿔가면서. 제목은 처음에는 (광고)였는데 스팸필터에 추가했더니 (광.고)로, 또 (광고.)로, 또 (광 .고 ) 로.</summary>
    <title>카카오 메일 유감</title>
    <updated>2026-09-03T14:50:05+09:00</updated>
    <dc:date>2026-09-03T14:50:05+09:00</dc:date>
  </entry>
  <entry>
    <author>
      <name>박해선</name>
    </author>
    <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;&lt;p&gt;&amp;lt;개발자를 위한 필수 수학&amp;gt;(한빛미디어, 2024)의 깃허브 코드를 코랩에서 실행하여 모두 업데이트해 놓았습니다. 사용한 최신 라이브러리는 코랩 기준으로 sklearn 1.6.1, pandas 2.2.3, sympy 1.14.0입니다. 코드 실행 결과에 큰 차이가 없으며 소수점 이하 자릿수에서 약간의 변화만 있었습니다. 본문에 대한 수정 사항은 에러타 페이지를 참고해 주세요. 감사합니다!&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;
</content>
    <id>https://tensorflow.blog/2026/09/06/%ea%b0%9c%eb%b0%9c%ec%9e%90%eb%a5%bc-%ec%9c%84%ed%95%9c-%ed%95%84%ec%88%98-%ec%88%98%ed%95%99-%ea%b9%83%ed%97%88%eb%b8%8c-%ec%97%85%eb%8d%b0%ec%9d%b4%ed%8a%b8-%ec%95%88%eb%82%b4/</id>
    <link href="https://tensorflow.blog/2026/09/06/%ea%b0%9c%eb%b0%9c%ec%9e%90%eb%a5%bc-%ec%9c%84%ed%95%9c-%ed%95%84%ec%88%98-%ec%88%98%ed%95%99-%ea%b9%83%ed%97%88%eb%b8%8c-%ec%97%85%eb%8d%b0%ec%9d%b4%ed%8a%b8-%ec%95%88%eb%82%b4/"/>
    <summary type="html">&amp;#60;개발자를 위한 필수 수학&gt;(한빛미디어, 2024)의 깃허브 코드를 코랩에서 실행하여 모두 업데이트해 놓았습니다. 사용한 최신 라이브러리는 코랩 기준으로 sklearn 1.6.1, pandas 2.2.3, sympy 1.14.0입니다. 코드 실행 결과에 큰 차이가 없으며 소수점 이하 자릿수에서 약간의 변화만 있었습니다. 본문에 대한 수정 사항은 에러타 페이지를 참고해 주세요. 감사합니다!</summary>
    <title>“개발자를 위한 필수 수학” 깃허브 업데이트 안내</title>
    <updated>2026-09-06T16:02:00+09:00</updated>
    <dc:date>2026-09-06T16:02:00+09:00</dc:date>
  </entry>
  <entry>
    <author>
      <name>향로 (기억보단 기록을)</name>
    </author>
    <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;일본 드라마 중에 가장 좋아하고 종종 다시 보는 것이 "중쇄를 찍자" 이다.  &lt;/p&gt;
&lt;p&gt;주인공이 일 하고 있는 바이브스는 코토칸 이란 출판사의 계열사 잡지이다.&lt;br&gt;코토칸 출판사의 사장님은 그 큰 회사의 사장님이지만 검소하게 생활을 한다.&lt;/p&gt;
&lt;p&gt;매일 지하철로 출퇴근을 하고,&lt;br&gt;술/담배/도박도 전혀 하지 않고,&lt;br&gt;잔돈은 항상 불우 이웃 모금함에 넣고,&lt;br&gt;셋방살이를 하면서 최소한의 수준으로 생활 한다.&lt;/p&gt;
&lt;p&gt;주인공은 본인의 사수인 오디기리 죠에게 왜 사장님은 그런 생활을 하는지 물어본다.&lt;br&gt;사장님은 모두 운을 모으기 위한 행동이라고 한다.  &lt;/p&gt;
&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;자세한건 블로그에 작성해두었다.&lt;br&gt;&lt;a href="https://jojoldu.tistory.com/453"&gt;중쇄를 찍자 5회 - 한점 돌파&lt;/a&gt;&lt;br&gt;일본에는 이런 '운' 도 하나의 '리소스' 처럼 관리하는 콘텐츠가 많은 것 같다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;"운을 통제한다" 라는 표현은 나한테는 과하다.&lt;br&gt;그래서 중쇄를 찍자의 "운을 모은다" 는 표현이 좀 더 마음에 든다.  &lt;/p&gt;
&lt;p&gt;책 속에서 PPIH 그룹의 회장인 야스다 다카오의 '기세'가 느껴졌다.&lt;br&gt;이렇게 강한 기세가 느껴지는데, 책 속에서는 권한 이양, 독재 반대 등이 나오니깐 이질감이 느껴지긴 했지만 그럼에도 요즘의 복잡한 마음과 머릿 속을 한번은 정리해주는 책이였다.  &lt;/p&gt;
&lt;p&gt;조직의 분위기, 기세라는 것이 정말 중요하다고 느낀다.  &lt;/p&gt;
&lt;p&gt;요즘은 내부에서 '학습' 이라는 것이 여전히 사람들에겐 필요할까? 라는 주제의 이야기를 많이 한다.&lt;br&gt;AI로 인해 누구나 일정 수준 이상의 다양한 분야의 실무 결과물을 낼 수 있는 시대에,&lt;br&gt;"더 나은 사람" 이 되기 위한 콘텐츠는 더 많은 사람들이 찾게 되는 반면, "더 나은 직업인" 이 되기 위한 실무 중심의 콘텐츠들에 대해서는 이젠 배울 동기가 없어지는 시대라고 느껴진다.&lt;br&gt;(문학 전문 출판사와 IT 전문 출판사의 매출이나 내부 이야기를 들어보면 극과극이더라)  &lt;/p&gt;
&lt;p&gt;이 책의 이야기에 따르면, 지금은 때가 아닌 것이라고 볼 수 있다.&lt;br&gt;근데 잘 될때 충분히 비축한 것이 없는데, 때도 아니라고 하면 그땐 어떻게 하는 것이 좋을까?  &lt;/p&gt;
&lt;p&gt;아마 지금의 스타트업들에선 그런 상황이 많은 것 같다.  &lt;/p&gt;
&lt;p&gt;물론 내가 이 이야기를 하면 야스다 다카오 회장님은 "이놈! 그런 부정적인 소리나 하니 운이 따라주지 않지!" 라고 할 것 같다.  &lt;/p&gt;
&lt;p&gt;어찌됐든 요즘은 이 악물고 긍정적인 표현들을 쓴다.&lt;br&gt;그런 훈련을 하기에 더할나위 없이 좋은 시기라고 생각해서 일종의 게임처럼 느껴지기도 한다.  &lt;/p&gt;
&lt;p&gt;눈엔 보이지 않지만, 조직의 기세라는 것이 있다.&lt;br&gt;그것들을 제대로 터트리려면 어떻게 하는 것이 좋을까? 라는 고민하던 차에 기존에 봐왔던 많은 책들의 내용을 다시 한번 정리해볼 수 있는 좋은 책이었다.&lt;br&gt;특히나 OR 보다는 AND라는 이야기는 좋아하는 &lt;a href="https://jojoldu.tistory.com/832"&gt;이와타 사토루 대표님의 이야기&lt;/a&gt; 가 다시금 생각났다.  &lt;/p&gt;
&lt;p&gt;이번주 수요일에 기다리고 기다리던 일본 최대 IT 회사인 DeNA의 3천명의 직원들을 AI 네이티브로 전환한 과정을 다룬 &lt;a href="https://www.inflearn.com/ko/course/dena-ai-day-2026?cid=343290"&gt;DeNA x AI Day 2026 컨퍼런스&lt;/a&gt;가 인프런에서 정식 공개 된다.  &lt;/p&gt;
&lt;p&gt;기세를 잘 몰아봐야겠다.&lt;/p&gt;
&lt;h2&gt;책 속 문장&lt;/h2&gt;
&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;'운의 경영', 즉 '중, 장기적 인생의 운 통제'가 대수의 법칙에 기반한 수법이다.&lt;br&gt;p.45&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;모처럼 기회가 왔는데 '적당히' 해서 '배를 70% 채웠다' 또는 '80%나 채웠다' 라고 만족하면 결국 운이 나빠진다.&lt;br&gt;얻을 수 있었던 성과를 완벽히 거둬들이지 못했다며 분통을 터뜨릴 줄 아는 사람이 진짜 승부사로서 행운을 얻는다.&lt;br&gt;p.60&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;운이 나쁠 때는 무슨 짓을 해도 십중팔구 헛수고로 끝난다.&lt;br&gt;나아가 더 나쁜 일을 불러들이는 불운의 악순환이 시작된다.&lt;br&gt;게다가 불운을 이겨내려고 힘을 짜내다보면 다음에 모처럼 행운이 찾아왔을 때 힘을 쓰지 못 할 수 있다.&lt;br&gt;...&lt;br&gt;행운의 최대화로 성과를 충분히 비축했다면 동굴에 계속 틀어박혀 있어도 식량이 떨어지지 않으니 굳이 위험한 사냥에 나설 필요가 없다.&lt;br&gt;p.62&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;그럼 주식 투자로 돈을 벌려면, 적어도 잃지 않으려면 어떻게 해야 할까?&lt;br&gt;일정 수준 이상 손해가 나면 매도한다는 규칙을 미리 정하고 그 규칙을 예외없이 실행하면 된다.&lt;br&gt;그렇게 과감히 손절매해서 얻은 자금을 다른 유망 주식에 투자하는 것이 낫다.&lt;br&gt;p.66&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;철수하는 시점을 판단하려면 '어느 수준을 실패로 보느냐' 를 미리 정해두어야 한다.&lt;br&gt;...&lt;br&gt;"성공 시나리오를 써놓는 것이 중요하다" 라고 말하는 사람이 많지만, 성공은 어디까지나 결과에 불과하므로 미리 시나리오를 쓸 필요는 없다.&lt;br&gt;오히려 포기의 기준이 되는 '실패 시나리오'를 미리 써두는 것이야말로 성공 비결이다.&lt;br&gt;p.69&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;"과감한 도전의 기세를 유지하면서도 현실을 직시한 신속한 철수를 두려워하지 말 것."&lt;br&gt;...&lt;br&gt;그래서 나는 '단행숙려'를 지향한다.&lt;br&gt;우선 결심하고 실행한 다음에 곰곰이 생각하자는 의미다.&lt;br&gt;p.85&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;상대방과 첫 만남에서 아무리 훌륭하고 매력적이라는 인상을 받아도, 아니 그럴수록 '사람을 과대평가하거나 지나치게 믿으면 안 된다' 라고 마음을 다잡아야 한다.&lt;br&gt;이러한 시간 테스트는 짧으면 3~4개월, 길면 1년 정도가 좋다.&lt;br&gt;...&lt;br&gt;사람은 남의 속을 알 수 없기 때문에 시간 테스트가 필요하다.&lt;br&gt;...&lt;br&gt;개성이나 직업을 불문하고 어떤 사람을 만나든지 항상 일정하고 적절한 거리를 유지하는 것이 중요하다.&lt;br&gt;좋은 운을 유지하려면 꼭 그래야 한다.&lt;br&gt;적절한 거리를 유지하며 사람을 사귀는 능력에 비례해 인생이 충실해질 것이다.&lt;br&gt;p.116&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;"인간관계에서 지녀야 할 마음가짐 세 가지&lt;br&gt;첫째, 인간의 질투가 얼마나 무서운지 인식하고 어떻게든 질투의 대상이 되지 않도록 주의해야 한다.&lt;br&gt;...&lt;br&gt;인생이나 사업에 성공했다고 으스대며 자만하는 사람이 있는데, 이것은 악운을 부르는 최악의 수다.&lt;br&gt;세상에는 틈만 나면 남의 발목을 잡으려는 사람들이 득실거린다.&lt;br&gt;...&lt;br&gt;둘째, 질투를 느끼는 사람은 상대에게 결코 '부럽다' 라고 말하지 않는다.&lt;br&gt;그렇게 말하는 순간 진 것 같은 기분이 들기 때문이다.&lt;br&gt;그런 의미에서 다른 사람이 나에게 '부럽다' 라고 말할 때 특히 조심해야 한다.&lt;br&gt;그 말은 재앙을 불러들이는 저주와도 같다.&lt;br&gt;"나는 성공했다" 라고 자랑하는 사람치고 그 성공을 오래 유지하는 것을 보지 못했다.&lt;br&gt;새삼 질투란 무엇인지 생각해보니 '상대의 실패를 바라는 마음이 극대화된 상태' 라고 할 수 있을 듯 하다.&lt;br&gt;...&lt;br&gt;마지막으로 내가 가장 싫어해서 스스로에게도 철저히 금지한 '독재'를 간단히 언급하려 한다.&lt;br&gt;...&lt;br&gt;창업 사장이든 샐러리맨 사장이든 독재를 휘두르며 지위를 누리려 하면 직원이 반드시 불행해진다.&lt;br&gt;독재는 공포로 남을 지배하는 가장 평범하고도 안이한 관리법으로, 직원 개개인의 열정을 단숨에 앗아간다.&lt;br&gt;그러니 정반대의 길을 선택해야 한다.&lt;br&gt;p.118&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;'흔한 상품으로 폭리를 취하려 하면 당연히 들키겠지만 우리 가게에만 있는 상품이라면 괜찮지 않을까?' 라고 생각하는 사람도 있을 것이다.&lt;br&gt;그것도 오산이다.&lt;br&gt;반드시 들킨다.&lt;br&gt;논리적 증거가 있어서가 아니라 정직하지 않은 기운, 교활한 분위기가 매장을 자욱하게 뒤덮어 결국은 고객이 눈치챌 수 밖에 없다.&lt;br&gt;나는 이 사실을 깨닫고 사심 없이 정직하게 장사하기로 마음먹었다.&lt;br&gt;'앞으로는 돈(매출과 이익)이 아닌 인기(고객의 지지)를 우선해야겠다' 라고 결심한 것이다.&lt;br&gt;p.138&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;나카노 노부코는 자신의 저서 "뇌의 어둠" 에서 '모호함을 허용하는 것이 뇌 과학적으로도 좋다' 라고 말하며 '모호함을 허용하는 겸허함이 없으면 뇌가 착각을 저지른다' 라고 덧붙였다.&lt;br&gt;p.148&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;돈키호테에서도 이 노하우를 활용하려 했지만 번번이 실패했다.&lt;br&gt;내가 지향하는 독특한 방식이 유통 상식과 너무 멀어 직원들이 이해하지 못했기 때문이다.&lt;br&gt;...&lt;br&gt;'대체 어떻게 해야 직원들에게 내 생각을 전달할 수 있을까?'&lt;br&gt;당시 돈키호테에는 창업자의 뜻을 실천하는 유능한 사원이 없었다.&lt;br&gt;게다가 아무리 가르쳐도 직원들은 내 뜻을 제대로 구현하지 못했다.&lt;br&gt;...&lt;br&gt;깊이 고민한 끝에 가르쳐도 안 되는 것은 가르치는 행위 자체가 무의미하다는 결론을 내렸다.&lt;br&gt;p.167&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;결국은 확장성이나 경쟁력 중 하나를 선택해야 했다.&lt;br&gt;그런에 이때 이런 생각이 떠올랐다.&lt;br&gt;'애초에 경쟁력이 있어야 확장성을 노릴 수 있는 게 아닐까?'&lt;br&gt;...&lt;br&gt;일반적인 소매업체였다면 단순히 확장성을 선택했을 것이다.&lt;br&gt;하지만 그러면 운이 확실히 나빠진다.&lt;br&gt;경쟁력이라는 최고의 운 요소를 희생했기 때문이다.&lt;br&gt;...&lt;br&gt;사업에서는 '둘 중 하나를 선택하자' 라는 OR 발상보다 '이것도 잡고 저것도 잡자' 라는 AND 발상을 활용해야 성공할 수 있다.&lt;br&gt;p.172&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;자기만을 내세우며 자신의 성공만 좇으면 어떤 직원도 협력하지 않는다.&lt;br&gt;'왜 내가 저 사람의 돈벌이를 도와야 하지?' 라는 마음일 것이다.&lt;br&gt;...&lt;br&gt;여러 사람이 모인 회사에서는 '나(경영자)의 성공과 행복' 이라는 단수형을 '우리(직원)의 성공과 행복' 이라는 복수형으로 바꾸어야 좋은 운을 끌어당길 수 있다.&lt;br&gt;p.179&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;경영자의 한 걸음보다 직원의 반걸음이 회사에 훨씬 중요하다.&lt;br&gt;경영자가 고군분투할 때보다 직원 각자가 열정적으로 일할 때 회사가 몇 배나 발전한다.&lt;br&gt;p.196&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;부하 직원이나 주변 사람들을 '이 사람을 위해서라면 열심히 일해보고 싶다' 라고 생각하게 만드는 사람이 가장 강한 사람이다.&lt;br&gt;p.197&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;직원들의 관점으로 상상력을 발휘해 '어떤 말을 들으면 의욕이 샘솟을까?' 라고 궁리해보자.&lt;br&gt;현장 직원들은 "바쁜 와중에도 정말 고마워요. 앞으로도 여러분과 함께 신나게 일하고 싶습니다" 라며 진심으로 공감하고 경의를 표할 줄 아는 사람을 따르게 마련이다.&lt;br&gt;p.200&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;경영자의 개별 운을 복수형인 집단 운으로 바꾸는데도 요령이 있다.&lt;br&gt;경영자 개인의 욕망과 야심, 지향하는 목표등을 주어 전환을 통해 각각의 직원들에게 알맞은 내용으로 바꾸는 것이다.&lt;br&gt;주어를 직원 개개인으로 바꾼 다음에 경영자가 마음속으로 그리는 회사의 방향성이나 전망 등을 '직원들이 들으면 의욕을 느낄 듯한 말'로 바꾸어 제시하면 된다.&lt;br&gt;...&lt;br&gt;내 뒤를 이을 경영자에게 가장 필요한 능력이 무엇인지 말하겠다.&lt;br&gt;그것은 복잡한 현상의 본질을 꿰뚫어 보고 단순화하는 능력,&lt;br&gt;다양한 사람을 끌어들여 이해하고 수긍하게 함으로써 의욕을 불러일으키는 능력,&lt;br&gt;문제 해결책을 동시/복합적으로 모색할 뿐만 아니라 변화에 제때 대응해 그 해결책을 응용하는 능력이다.&lt;br&gt;p.201&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;'기적의 연쇄'를 일으킨 비결은 일의 게임화, 그리고 그 게임을 모두가 공유하는 것이다.&lt;br&gt;일을 게임으로 바꾸는 4대 조건은 다음과 같다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;명확한 승부: 승부가 뚜렷하지 않으면 게임이 아니다.&lt;/li&gt;
&lt;li&gt;시간제한: 시간이 무제한이면 긴장감이 없다.&lt;/li&gt;
&lt;li&gt;최소한의 규칙: 규칙이 많고 복잡하면 이해하기 어려워서 재미가 없다.&lt;/li&gt;
&lt;li&gt;대폭의 자유재량권: 주변의 간섭만큼 게임의 재미를 앗아가는 것이 없다.&lt;br&gt;p.204&lt;/li&gt;
&lt;/ol&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;어느 정도 경험을 쌓고 본격적으로 사회생활을 하고 있다면 단점과 열등감을 무리하게 극복하려 애쓰기보다 본인의 개성으로 인정하고 내려놓는 게 좋다.&lt;br&gt;...&lt;br&gt;사회인이 되면 자신의 약점에는 눈을 감고 장점을 키우는 데 전념하는 게 좋다.&lt;br&gt;p.211&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;사람은 '호혜성의 법칙' 에 따라 누군가에게 인정받으면 거기에 부응하려 하기 마련이다.&lt;br&gt;반대로 부족함을 질책당하면 의욕을 잃는다.&lt;br&gt;그래서 잘한 일을 충분히 칭찬해주는 것이 중요하다.&lt;br&gt;p.217&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;경영자에게는 '인격'만 한 능력이 없다는 것이 나의 변함없는 지론이다.&lt;br&gt;다른 사람에게 공함하는 능력을 발휘해 '저 사람과 함께 미래를 꿈꾸고 싶다' 라고 생각하게 만드는 능력이다.&lt;br&gt;...&lt;br&gt;모든 직원이 '저 사람을 위해서라면 힘든 일이라도 하고 싶다' 라며 진심으로 사모할 정도의 경영자여야 집단 운 조직을 이끌 수 있다.&lt;br&gt;p.223&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;일부러라도 철저히 이겨서 "이래도 덤빌래?" 라고 말할 만한 대승을 거두는 게 중요하다.&lt;br&gt;나는 경험상 '압승을 지향하는 기세가 좋은 운을 끌어당긴다' 라고 확신한다.&lt;br&gt;...&lt;br&gt;p.235&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;PPIH 그룹 기업 이념집 "원류"&lt;/p&gt;
&lt;p&gt;"제 3조 자기 의견을 확실히 말할 것"&lt;br&gt;권한 이양을 중시하는 우리 회사에서는 직무상 옳다고 생각하는 의견이나 주장이 있으면 상사에게 그것을 확실히 말해야 한다.&lt;br&gt;상사가 어떻게 판단할지는 그때그때 다르지만, 부하의 의견에 항상 귀 기울이는 것이 우리 회사 상사의 요건이므로 쓸데없는 걱정은 하지 않아도 된다.&lt;br&gt;맹목적으로 명령을 따르는 부하만 있으면 애초에 권한 이양이 성립하지 않을 것이다.&lt;/p&gt;
&lt;p&gt;"제 9조 채찍과 당근"&lt;br&gt;"당근과 채찍"은 순서가 틀렸다.&lt;br&gt;처음에 웃으며 달콤한 말을 건네면 그것을 당연하게 생각한다.&lt;br&gt;처음에는 엄격하게 대한 다음 나중에 장점을 찾아 인정하고 칭찬해야 진정한 신뢰 관계가 구축된다.&lt;/p&gt;
&lt;p&gt;"제 11조" 부하는 상사의 부하가 아니라 회사의 자산이다.&lt;br&gt;부하라는 소중한 자산이 최대한 활용되도록 하는 것이 상사의 임무다.&lt;br&gt;상사는 그 인재를 활용하기 좋은 환경을 만드는 일, 적성에 맞는 직무를 찾아주는 일에 최대한의 관심과 집중력을 쏟아야 한다.&lt;br&gt;p.260&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;/body&gt;&lt;/html&gt;
</content>
    <id>https://jojoldu.tistory.com/885</id>
    <link href="https://jojoldu.tistory.com/885"/>
    <summary type="html">&lt;p&gt;일본 드라마 중에 가장 좋아하고 종종 다시 보는 것이 &amp;quot;중쇄를 찍자&amp;quot; 이다.  &lt;/p&gt;
&lt;p&gt;주인공이 일 하고 있는 바이브스는 코토칸 이란 출판사의 계열사 잡지이다.&lt;br&gt;코토칸 출판사의 사장님은 그 큰 회사의 사장님이지만 검소하게 생활을 한다.&lt;/p&gt;
&lt;p&gt;매일 지하철로 출퇴근을 하고,&lt;br&gt;술/담배/도박도 전혀 하지 않고,&lt;br&gt;잔돈은 항상 불우 이웃 모금함에 넣고,&lt;br&gt;셋방살이를 하면서 최소한의 수준으로 생활 한다.&lt;/p&gt;
&lt;p&gt;주인공은 본인의 사수인 오디기리 죠에게 왜 사장님은 그런 생활을 하는지 물어본다.&lt;br&gt;사장님은 모두 운을 모으기 위한 행동이라고 한다.  &lt;/p&gt;
&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;자세한건 블로그에 작성해두었다.&lt;br&gt;&lt;a href="https://jojoldu.tistory.com/453"&gt;중쇄를 찍자 5회 - 한점 돌파&lt;/a&gt;&lt;br&gt;일본에는 이런 &amp;#39;운&amp;#39; 도 하나의 &amp;#39;리소스&amp;#39; 처럼 관리하는 콘텐츠가 많은 것 같다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&amp;quot;운을 통제한다&amp;quot; 라는 표현은 나한테는 과하다.&lt;br&gt;그래서 중쇄를 찍자의 &amp;quot;운을 모은다&amp;quot; 는 표현이 좀 더 마음에 든다.  &lt;/p&gt;
&lt;p&gt;책 속에서 PPIH 그룹의 회장인 야스다 다카오의 &amp;#39;기세&amp;#39;가 느껴졌다.&lt;br&gt;이렇게 강한 기세가 느껴지는데, 책 속에서는 권한 이양, 독재 반대 등이 나오니깐 이질감이 느껴지긴 했지만 그럼에도 요즘의 복잡한 마음과 머릿 속을 한번은 정리해주는 책이였다.  &lt;/p&gt;
&lt;p&gt;조직의 분위기, 기세라는 것이 정말 중요하다고 느낀다.  &lt;/p&gt;
&lt;p&gt;요즘은 내부에서 &amp;#39;학습&amp;#39; 이라는 것이 여전히 사람들에겐 필요할까? 라는 주제의 이야기를 많이 한다.&lt;br&gt;AI로 인해 누구나 일정 수준 이상의 다양한 분야의 실무 결과물을 낼 수 있는 시대에,&lt;br&gt;&amp;quot;더 나은 사람&amp;quot; 이 되기 위한 콘텐츠는 더 많은 사람들이 찾게 되는 반면, &amp;quot;더 나은 직업인&amp;quot; 이 되기 위한 실무 중심의 콘텐츠들에 대해서는 이젠 배울 동기가 없어지는 시대라고 느껴진다.&lt;br&gt;(문학 전문 출판사와 IT 전문 출판사의 매출이나 내부 이야기를 들어보면 극과극이더라)  &lt;/p&gt;
&lt;p&gt;이 책의 이야기에 따르면, 지금은 때가 아닌 것이라고 볼 수 있다.&lt;br&gt;근데 잘 될때 충분히 비축한 것이 없는데, 때도 아니라고 하면 그땐 어떻게 하는 것이 좋을까?  &lt;/p&gt;
&lt;p&gt;아마 지금의 스타트업들에선 그런 상황이 많은 것 같다.  &lt;/p&gt;
&lt;p&gt;물론 내가 이 이야기를 하면 야스다 다카오 회장님은 &amp;quot;이놈! 그런 부정적인 소리나 하니 운이 따라주지 않지!&amp;quot; 라고 할 것 같다.  &lt;/p&gt;
&lt;p&gt;어찌됐든 요즘은 이 악물고 긍정적인 표현들을 쓴다.&lt;br&gt;그런 훈련을 하기에 더할나위 없이 좋은 시기라고 생각해서 일종의 게임처럼 느껴지기도 한다.  &lt;/p&gt;
&lt;p&gt;눈엔 보이지 않지만, 조직의 기세라는 것이 있다.&lt;br&gt;그것들을 제대로 터트리려면 어떻게 하는 것이 좋을까? 라는 고민하던 차에 기존에 봐왔던 많은 책들의 내용을 다시 한번 정리해볼 수 있는 좋은 책이었다.&lt;br&gt;특히나 OR 보다는 AND라는 이야기는 좋아하는 &lt;a href="https://jojoldu.tistory.com/832"&gt;이와타 사토루 대표님의 이야기&lt;/a&gt; 가 다시금 생각났다.  &lt;/p&gt;
&lt;p&gt;이번주 수요일에 기다리고 기다리던 일본 최대 IT 회사인 DeNA의 3천명의 직원들을 AI 네이티브로 전환한 과정을 다룬 &lt;a href="https://www.inflearn.com/ko/course/dena-ai-day-2026?cid=343290"&gt;DeNA x AI Day 2026 컨퍼런스&lt;/a&gt;가 인프런에서 정식 공개 된다.  &lt;/p&gt;
&lt;p&gt;기세를 잘 몰아봐야겠다.&lt;/p&gt;
&lt;h2&gt;책 속 문장&lt;/h2&gt;
&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;&amp;#39;운의 경영&amp;#39;, 즉 &amp;#39;중, 장기적 인생의 운 통제&amp;#39;가 대수의 법칙에 기반한 수법이다.&lt;br&gt;p.45&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;모처럼 기회가 왔는데 &amp;#39;적당히&amp;#39; 해서 &amp;#39;배를 70% 채웠다&amp;#39; 또는 &amp;#39;80%나 채웠다&amp;#39; 라고 만족하면 결국 운이 나빠진다.&lt;br&gt;얻을 수 있었던 성과를 완벽히 거둬들이지 못했다며 분통을 터뜨릴 줄 아는 사람이 진짜 승부사로서 행운을 얻는다.&lt;br&gt;p.60&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;운이 나쁠 때는 무슨 짓을 해도 십중팔구 헛수고로 끝난다.&lt;br&gt;나아가 더 나쁜 일을 불러들이는 불운의 악순환이 시작된다.&lt;br&gt;게다가 불운을 이겨내려고 힘을 짜내다보면 다음에 모처럼 행운이 찾아왔을 때 힘을 쓰지 못 할 수 있다.&lt;br&gt;...&lt;br&gt;행운의 최대화로 성과를 충분히 비축했다면 동굴에 계속 틀어박혀 있어도 식량이 떨어지지 않으니 굳이 위험한 사냥에 나설 필요가 없다.&lt;br&gt;p.62&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;그럼 주식 투자로 돈을 벌려면, 적어도 잃지 않으려면 어떻게 해야 할까?&lt;br&gt;일정 수준 이상 손해가 나면 매도한다는 규칙을 미리 정하고 그 규칙을 예외없이 실행하면 된다.&lt;br&gt;그렇게 과감히 손절매해서 얻은 자금을 다른 유망 주식에 투자하는 것이 낫다.&lt;br&gt;p.66&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;철수하는 시점을 판단하려면 &amp;#39;어느 수준을 실패로 보느냐&amp;#39; 를 미리 정해두어야 한다.&lt;br&gt;...&lt;br&gt;&amp;quot;성공 시나리오를 써놓는 것이 중요하다&amp;quot; 라고 말하는 사람이 많지만, 성공은 어디까지나 결과에 불과하므로 미리 시나리오를 쓸 필요는 없다.&lt;br&gt;오히려 포기의 기준이 되는 &amp;#39;실패 시나리오&amp;#39;를 미리 써두는 것이야말로 성공 비결이다.&lt;br&gt;p.69&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;&amp;quot;과감한 도전의 기세를 유지하면서도 현실을 직시한 신속한 철수를 두려워하지 말 것.&amp;quot;&lt;br&gt;...&lt;br&gt;그래서 나는 &amp;#39;단행숙려&amp;#39;를 지향한다.&lt;br&gt;우선 결심하고 실행한 다음에 곰곰이 생각하자는 의미다.&lt;br&gt;p.85&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;상대방과 첫 만남에서 아무리 훌륭하고 매력적이라는 인상을 받아도, 아니 그럴수록 &amp;#39;사람을 과대평가하거나 지나치게 믿으면 안 된다&amp;#39; 라고 마음을 다잡아야 한다.&lt;br&gt;이러한 시간 테스트는 짧으면 3~4개월, 길면 1년 정도가 좋다.&lt;br&gt;...&lt;br&gt;사람은 남의 속을 알 수 없기 때문에 시간 테스트가 필요하다.&lt;br&gt;...&lt;br&gt;개성이나 직업을 불문하고 어떤 사람을 만나든지 항상 일정하고 적절한 거리를 유지하는 것이 중요하다.&lt;br&gt;좋은 운을 유지하려면 꼭 그래야 한다.&lt;br&gt;적절한 거리를 유지하며 사람을 사귀는 능력에 비례해 인생이 충실해질 것이다.&lt;br&gt;p.116&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;&amp;quot;인간관계에서 지녀야 할 마음가짐 세 가지&lt;br&gt;첫째, 인간의 질투가 얼마나 무서운지 인식하고 어떻게든 질투의 대상이 되지 않도록 주의해야 한다.&lt;br&gt;...&lt;br&gt;인생이나 사업에 성공했다고 으스대며 자만하는 사람이 있는데, 이것은 악운을 부르는 최악의 수다.&lt;br&gt;세상에는 틈만 나면 남의 발목을 잡으려는 사람들이 득실거린다.&lt;br&gt;...&lt;br&gt;둘째, 질투를 느끼는 사람은 상대에게 결코 &amp;#39;부럽다&amp;#39; 라고 말하지 않는다.&lt;br&gt;그렇게 말하는 순간 진 것 같은 기분이 들기 때문이다.&lt;br&gt;그런 의미에서 다른 사람이 나에게 &amp;#39;부럽다&amp;#39; 라고 말할 때 특히 조심해야 한다.&lt;br&gt;그 말은 재앙을 불러들이는 저주와도 같다.&lt;br&gt;&amp;quot;나는 성공했다&amp;quot; 라고 자랑하는 사람치고 그 성공을 오래 유지하는 것을 보지 못했다.&lt;br&gt;새삼 질투란 무엇인지 생각해보니 &amp;#39;상대의 실패를 바라는 마음이 극대화된 상태&amp;#39; 라고 할 수 있을 듯 하다.&lt;br&gt;...&lt;br&gt;마지막으로 내가 가장 싫어해서 스스로에게도 철저히 금지한 &amp;#39;독재&amp;#39;를 간단히 언급하려 한다.&lt;br&gt;...&lt;br&gt;창업 사장이든 샐러리맨 사장이든 독재를 휘두르며 지위를 누리려 하면 직원이 반드시 불행해진다.&lt;br&gt;독재는 공포로 남을 지배하는 가장 평범하고도 안이한 관리법으로, 직원 개개인의 열정을 단숨에 앗아간다.&lt;br&gt;그러니 정반대의 길을 선택해야 한다.&lt;br&gt;p.118&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;&amp;#39;흔한 상품으로 폭리를 취하려 하면 당연히 들키겠지만 우리 가게에만 있는 상품이라면 괜찮지 않을까?&amp;#39; 라고 생각하는 사람도 있을 것이다.&lt;br&gt;그것도 오산이다.&lt;br&gt;반드시 들킨다.&lt;br&gt;논리적 증거가 있어서가 아니라 정직하지 않은 기운, 교활한 분위기가 매장을 자욱하게 뒤덮어 결국은 고객이 눈치챌 수 밖에 없다.&lt;br&gt;나는 이 사실을 깨닫고 사심 없이 정직하게 장사하기로 마음먹었다.&lt;br&gt;&amp;#39;앞으로는 돈(매출과 이익)이 아닌 인기(고객의 지지)를 우선해야겠다&amp;#39; 라고 결심한 것이다.&lt;br&gt;p.138&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;나카노 노부코는 자신의 저서 &amp;quot;뇌의 어둠&amp;quot; 에서 &amp;#39;모호함을 허용하는 것이 뇌 과학적으로도 좋다&amp;#39; 라고 말하며 &amp;#39;모호함을 허용하는 겸허함이 없으면 뇌가 착각을 저지른다&amp;#39; 라고 덧붙였다.&lt;br&gt;p.148&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;돈키호테에서도 이 노하우를 활용하려 했지만 번번이 실패했다.&lt;br&gt;내가 지향하는 독특한 방식이 유통 상식과 너무 멀어 직원들이 이해하지 못했기 때문이다.&lt;br&gt;...&lt;br&gt;&amp;#39;대체 어떻게 해야 직원들에게 내 생각을 전달할 수 있을까?&amp;#39;&lt;br&gt;당시 돈키호테에는 창업자의 뜻을 실천하는 유능한 사원이 없었다.&lt;br&gt;게다가 아무리 가르쳐도 직원들은 내 뜻을 제대로 구현하지 못했다.&lt;br&gt;...&lt;br&gt;깊이 고민한 끝에 가르쳐도 안 되는 것은 가르치는 행위 자체가 무의미하다는 결론을 내렸다.&lt;br&gt;p.167&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;결국은 확장성이나 경쟁력 중 하나를 선택해야 했다.&lt;br&gt;그런에 이때 이런 생각이 떠올랐다.&lt;br&gt;&amp;#39;애초에 경쟁력이 있어야 확장성을 노릴 수 있는 게 아닐까?&amp;#39;&lt;br&gt;...&lt;br&gt;일반적인 소매업체였다면 단순히 확장성을 선택했을 것이다.&lt;br&gt;하지만 그러면 운이 확실히 나빠진다.&lt;br&gt;경쟁력이라는 최고의 운 요소를 희생했기 때문이다.&lt;br&gt;...&lt;br&gt;사업에서는 &amp;#39;둘 중 하나를 선택하자&amp;#39; 라는 OR 발상보다 &amp;#39;이것도 잡고 저것도 잡자&amp;#39; 라는 AND 발상을 활용해야 성공할 수 있다.&lt;br&gt;p.172&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;자기만을 내세우며 자신의 성공만 좇으면 어떤 직원도 협력하지 않는다.&lt;br&gt;&amp;#39;왜 내가 저 사람의 돈벌이를 도와야 하지?&amp;#39; 라는 마음일 것이다.&lt;br&gt;...&lt;br&gt;여러 사람이 모인 회사에서는 &amp;#39;나(경영자)의 성공과 행복&amp;#39; 이라는 단수형을 &amp;#39;우리(직원)의 성공과 행복&amp;#39; 이라는 복수형으로 바꾸어야 좋은 운을 끌어당길 수 있다.&lt;br&gt;p.179&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;경영자의 한 걸음보다 직원의 반걸음이 회사에 훨씬 중요하다.&lt;br&gt;경영자가 고군분투할 때보다 직원 각자가 열정적으로 일할 때 회사가 몇 배나 발전한다.&lt;br&gt;p.196&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;부하 직원이나 주변 사람들을 &amp;#39;이 사람을 위해서라면 열심히 일해보고 싶다&amp;#39; 라고 생각하게 만드는 사람이 가장 강한 사람이다.&lt;br&gt;p.197&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;직원들의 관점으로 상상력을 발휘해 &amp;#39;어떤 말을 들으면 의욕이 샘솟을까?&amp;#39; 라고 궁리해보자.&lt;br&gt;현장 직원들은 &amp;quot;바쁜 와중에도 정말 고마워요. 앞으로도 여러분과 함께 신나게 일하고 싶습니다&amp;quot; 라며 진심으로 공감하고 경의를 표할 줄 아는 사람을 따르게 마련이다.&lt;br&gt;p.200&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;경영자의 개별 운을 복수형인 집단 운으로 바꾸는데도 요령이 있다.&lt;br&gt;경영자 개인의 욕망과 야심, 지향하는 목표등을 주어 전환을 통해 각각의 직원들에게 알맞은 내용으로 바꾸는 것이다.&lt;br&gt;주어를 직원 개개인으로 바꾼 다음에 경영자가 마음속으로 그리는 회사의 방향성이나 전망 등을 &amp;#39;직원들이 들으면 의욕을 느낄 듯한 말&amp;#39;로 바꾸어 제시하면 된다.&lt;br&gt;...&lt;br&gt;내 뒤를 이을 경영자에게 가장 필요한 능력이 무엇인지 말하겠다.&lt;br&gt;그것은 복잡한 현상의 본질을 꿰뚫어 보고 단순화하는 능력,&lt;br&gt;다양한 사람을 끌어들여 이해하고 수긍하게 함으로써 의욕을 불러일으키는 능력,&lt;br&gt;문제 해결책을 동시/복합적으로 모색할 뿐만 아니라 변화에 제때 대응해 그 해결책을 응용하는 능력이다.&lt;br&gt;p.201&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;&amp;#39;기적의 연쇄&amp;#39;를 일으킨 비결은 일의 게임화, 그리고 그 게임을 모두가 공유하는 것이다.&lt;br&gt;일을 게임으로 바꾸는 4대 조건은 다음과 같다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;명확한 승부: 승부가 뚜렷하지 않으면 게임이 아니다.&lt;/li&gt;
&lt;li&gt;시간제한: 시간이 무제한이면 긴장감이 없다.&lt;/li&gt;
&lt;li&gt;최소한의 규칙: 규칙이 많고 복잡하면 이해하기 어려워서 재미가 없다.&lt;/li&gt;
&lt;li&gt;대폭의 자유재량권: 주변의 간섭만큼 게임의 재미를 앗아가는 것이 없다.&lt;br&gt;p.204&lt;/li&gt;
&lt;/ol&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;어느 정도 경험을 쌓고 본격적으로 사회생활을 하고 있다면 단점과 열등감을 무리하게 극복하려 애쓰기보다 본인의 개성으로 인정하고 내려놓는 게 좋다.&lt;br&gt;...&lt;br&gt;사회인이 되면 자신의 약점에는 눈을 감고 장점을 키우는 데 전념하는 게 좋다.&lt;br&gt;p.211&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;사람은 &amp;#39;호혜성의 법칙&amp;#39; 에 따라 누군가에게 인정받으면 거기에 부응하려 하기 마련이다.&lt;br&gt;반대로 부족함을 질책당하면 의욕을 잃는다.&lt;br&gt;그래서 잘한 일을 충분히 칭찬해주는 것이 중요하다.&lt;br&gt;p.217&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;경영자에게는 &amp;#39;인격&amp;#39;만 한 능력이 없다는 것이 나의 변함없는 지론이다.&lt;br&gt;다른 사람에게 공함하는 능력을 발휘해 &amp;#39;저 사람과 함께 미래를 꿈꾸고 싶다&amp;#39; 라고 생각하게 만드는 능력이다.&lt;br&gt;...&lt;br&gt;모든 직원이 &amp;#39;저 사람을 위해서라면 힘든 일이라도 하고 싶다&amp;#39; 라며 진심으로 사모할 정도의 경영자여야 집단 운 조직을 이끌 수 있다.&lt;br&gt;p.223&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;일부러라도 철저히 이겨서 &amp;quot;이래도 덤빌래?&amp;quot; 라고 말할 만한 대승을 거두는 게 중요하다.&lt;br&gt;나는 경험상 &amp;#39;압승을 지향하는 기세가 좋은 운을 끌어당긴다&amp;#39; 라고 확신한다.&lt;br&gt;...&lt;br&gt;p.235&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style="style1"&gt;&lt;p data-ke-size="size16"&gt;&lt;span style="font-family: 'Noto Serif KR';"&gt;&lt;p&gt;PPIH 그룹 기업 이념집 &amp;quot;원류&amp;quot;&lt;/p&gt;
&lt;p&gt;&amp;quot;제 3조 자기 의견을 확실히 말할 것&amp;quot;&lt;br&gt;권한 이양을 중시하는 우리 회사에서는 직무상 옳다고 생각하는 의견이나 주장이 있으면 상사에게 그것을 확실히 말해야 한다.&lt;br&gt;상사가 어떻게 판단할지는 그때그때 다르지만, 부하의 의견에 항상 귀 기울이는 것이 우리 회사 상사의 요건이므로 쓸데없는 걱정은 하지 않아도 된다.&lt;br&gt;맹목적으로 명령을 따르는 부하만 있으면 애초에 권한 이양이 성립하지 않을 것이다.&lt;/p&gt;
&lt;p&gt;&amp;quot;제 9조 채찍과 당근&amp;quot;&lt;br&gt;&amp;quot;당근과 채찍&amp;quot;은 순서가 틀렸다.&lt;br&gt;처음에 웃으며 달콤한 말을 건네면 그것을 당연하게 생각한다.&lt;br&gt;처음에는 엄격하게 대한 다음 나중에 장점을 찾아 인정하고 칭찬해야 진정한 신뢰 관계가 구축된다.&lt;/p&gt;
&lt;p&gt;&amp;quot;제 11조&amp;quot; 부하는 상사의 부하가 아니라 회사의 자산이다.&lt;br&gt;부하라는 소중한 자산이 최대한 활용되도록 하는 것이 상사의 임무다.&lt;br&gt;상사는 그 인재를 활용하기 좋은 환경을 만드는 일, 적성에 맞는 직무를 찾아주는 일에 최대한의 관심과 집중력을 쏟아야 한다.&lt;br&gt;p.260&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;</summary>
    <title>운의 경영학</title>
    <updated>2026-09-07T23:54:06+09:00</updated>
    <dc:date>2026-09-07T23:54:06+09:00</dc:date>
  </entry>
  <entry>
    <author>
      <name>admin@wonkooklee.com (Wonkook Lee)</name>
    </author>
    <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;출근길 바나프레소에서 커피를 받을 때마다 운세 스티커를 확인합니다. 믿지도 않는 운세를 왜 매일 읽는지, 작은 궁금증과 재미에 관한 연구를 찾아봤습니다.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.wonkooklee.com/blog/20260908_01/?utm_source=rss&amp;amp;utm_medium=referral&amp;amp;utm_campaign=feed-blog&amp;amp;utm_content=cta"&gt;전문 보기 →&lt;/a&gt;&lt;/p&gt;
&lt;/body&gt;&lt;/html&gt;
</content>
    <id>https://blog.wonkooklee.com/blog/20260908_01/?utm_source=rss&amp;utm_medium=referral&amp;utm_campaign=feed-blog&amp;utm_content=title</id>
    <link href="https://blog.wonkooklee.com/blog/20260908_01/?utm_source=rss&amp;utm_medium=referral&amp;utm_campaign=feed-blog&amp;utm_content=title"/>
    <summary type="html">출근길 바나프레소에서 커피를 받을 때마다 운세 스티커를 확인합니다. 믿지도 않는 운세를 왜 매일 읽는지, 작은 궁금증과 재미에 관한 연구를 찾아봤습니다.</summary>
    <title>믿지도 않는 오늘의 운세를 매일 읽는 이유</title>
    <updated>2026-09-08T09:00:00+09:00</updated>
    <dc:date>2026-09-08T09:00:00+09:00</dc:date>
  </entry>
  <entry>
    <author>
      <name>JusticeHui</name>
    </author>
    <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;&lt;img src="https://justicehui.github.io/img/gb/problemset.png" alt=""&gt;
대회보다 온라인 저지가 먼저 없어졌습니다.&lt;/p&gt;

&lt;h3 id="서론"&gt;서론&lt;/h3&gt;

&lt;p&gt;Good Bye, BOJ! / Hello, BOJ! 대회는 2019년부터 매년 연말연시에 백준 온라인 저지에서 개최(하는 것을 목표로)하는 행사입니다.&lt;/p&gt;

&lt;p&gt;운영진 풀이 7년째 거의 변함이 없는 점에는 장단점이 모두 있습니다. 가장 큰 단점은 시간이 지남에 따라 운영진들이 점점 PS와 멀어지고 있어 언제든지 대회가 열리지 않을 수 있다는 것입니다. 실제로 2025년 2월에 개최한 Hello, BOJ 2025! 와 2025년 12월에 개최한 Good Bye, BOJ 2025! 는 여러 사정으로 열리지 못할 뻔했습니다. 대회보다 온라인 저지가 먼저 없어지는 건 예상 못 했지만…&lt;/p&gt;

&lt;p&gt;반면, 이제는 다들 고일 만큼 고여서 적당한 규모의 온라인 대회 정도는 운영진 모집부터 대회 개최까지 일주일 안에 해낼 수 있다는 장점 아닌 장점도 있습니다. 이 대회는 이러한 장점을 가장 잘 보여주는 케이스였습니다.&lt;/p&gt;

&lt;h3 id="대회-준비-시작"&gt;대회 준비 시작&lt;/h3&gt;

&lt;p&gt;BOJ가 머지않은 시일 내에 서비스를 종료할 것이라 짐작하고 있었고, 언젠가 그런 날이 온다면 대회 한 번 열고 보내줘야겠다고 오래전부터 생각하고 있었습니다.&lt;/p&gt;

&lt;p&gt;4월 15일 15시 14분에 BOJ 서비스 종료 공지가 뜬 것을 보고, 바로 다음 날인 4월 16일부터 함께 대회를 개최할 사람을 모집하기 시작했습니다. 대회 전날까지 운영진이 계속해서 늘어나는 것을 보며 의아해하신 분들이 계시던데, 이는 부족한 시간 안에서 문제의 완성도를 최대한 끌어올리기 위해 계속해서 검수자를 모집한 결과였습니다.&lt;/p&gt;

&lt;p&gt;4월 16일 14시 15분에 운영진 모집을 시작함과 동시에 저와 김준겸(ryute), 이종서(leejseo)는 대회 개최 공지를 쓰기 시작했습니다. 20시에 공지 초안이 나왔고, 약간의 수정을 거친 끝에 21시 43분에 아래와 같은 공지가 올라갔습니다. 개인적인 감상이지만 이 글은 5달이 지난 지금 다시 봐도 굉장히 잘 쓴 글 같습니다. 하하…&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;안녕하세요. "Good Bye, BOJ!" 대회를 지금까지 운영해 온 김준겸(@ryute), 나정휘(@jhnah917), 이종서(@leejseo) 입니다.

모두들 아시겠지만, 백준 온라인 저지가 곧 서비스 종료를 앞두고 있습니다.

시간이 참 빠릅니다.

저희 세 명의 첫 AC는 모두 10년 전입니다. 중학생이었던 사람들이 직장인이 되었고, 그 사이 제출 번호도 6자리에서 무려 9자리가 되었습니다. 그동안 백준 온라인 저지에서 정말 많은 추억을 쌓았고, 여러 좋은 사람을 만나고, 좋은 직장을 구할 수 있었습니다. 돌이켜보면 저희의 지난 10년은 백준과 함께한 10년이라고 해도 과언이 아닐 것 같습니다.

백준 온라인 저지가 사라진다는 것은 많은 분께 갑작스럽고 또 가슴 아픈 소식일 것이라고 생각합니다. 2019년부터 매년 연말대회 Good Bye, BOJ! 를 개최해 왔고, 언젠가 그만하겠다는 생각은 했지만 이런 식으로 끝내게 될 줄은 몰랐습니다.

애정 어린 온라인 저지 하나를 떠나보내는 것은 슬픈 일입니다. 하지만 온라인 저지를 떠나보내게 되었을 때, 어떤 식으로 마무리해야 하는지는 꽤 명확하다고 생각했습니다. 저희가 할 수 있는 가장 멋진 작별 인사를 보내려고 합니다. 비록 연말은 아니지만, 백준 온라인 저지를 저희보다도 많이 사랑하는 분 중 몇몇을 모아 마지막 대회를 열어 보기로 했습니다.

백준 온라인 저지와 마지막까지 함께해 주셔서 감사합니다.

- 대회 링크: https://www.acmicpc.net/contest/view/1675
- 대회 일시: 2026년 4월 26일(일) 18시-22시(4시간)
- 대회 문제 수: 10문제
- 참가 방법: 이번 대회는 별도의 참가 신청을 받지 않습니다. 대회 시작 후 대회 문제에 1번 이상의 제출을 진행하면 참가자로 등록됩니다.
- 이 대회는 악마의 유혹(https://www.acmicpc.net/problem/16075)의 제출 제한이 적용되지 않습니다.

- 마지막 문제가 가장 쉬운 문제입니다.
- 마지막 문제를 제외한 다른 문제는 운영진이 생각하기에 난이도가 낮은 것부터 높은 것까지 정렬되어 있습니다.
- 각 문제에는 배점이 있습니다. 등수는 배점의 합을 우선하고, 동점이라면 패널티를 기준으로 계산합니다. 서브태스크는 없습니다.
- WA당 패널티는 20분입니다. 각 문제의 패널티는 첫 AC 기준으로 계산되므로, 첫 AC 이후의 모든 제출은 패널티에 반영되지 않습니다.

- 별도의 상품은 없습니다. 혹시 생기게 된다면 이 글을 수정하여 안내하겠습니다.
- 대회 종료 후 스코어보드 공개 세션이 있을 예정입니다.

이런 BOJ에서 2019년부터 'Good Bye, BOJ %d!' 대회를 개최할 수 있었던 것은 저희에게 더없는 영광이었습니다. 그동안 대회에 참가해 즐겨주신 분들, 함께 출제와 검수, 운영을 맡아주신 분들, 그리고 이 모든 장을 열어주신 @baekjoon 님께 글로 다 표현할 수 없는 깊은 감사를 전합니다.

이 대회가 BOJ에게 바치는 멋진 작별 인사가 되기를 바랍니다.
Good Bye, BOJ!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;공지까지 올렸으니 이제 돌이킬 수 없습니다. 대회 7일 전인 4월 19일에 문제를 확정하는 것을 목표로 문제 모집을 시작했습니다. 실제로는 4월 19일 3시에 문제 모집을 마감한 뒤, 4월 20일 18시 47분에 모든 문제를 확정 짓고 23일 10시에 세팅을 끝냈습니다.&lt;/p&gt;

&lt;h3 id="문제-준비"&gt;문제 준비&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://youtu.be/c1aJKyg25bg"&gt;아는 만큼 들리는 노래 2015&lt;/a&gt; 라는 영상이 2016년 초에 굉장히 유명했었습니다. 2015년 한 해 동안 인기가 많았던 곡들을 연결해 하나의 노래로 만든 영상입니다. 저도 10년 전에 연말연시의 기분을 느끼며 여러 번 들었던 기억이 있습니다.&lt;/p&gt;

&lt;p&gt;이번 대회의 문제 컨셉은 “아는 만큼 보이는 문제” 였습니다. BOJ에서 알고리즘을 공부한 사람들이라면 다들 한 번씩은 풀어봤을 유명한 문제를 오마주해서 문제를 만들었습니다. 문제의 제목뿐만 아니라, 지문에 있는 수많은 하이퍼 링크도 모두 BOJ에 있는 문제와 연결되어 있었습니다. 이에 대한 설명은 뒤에서 더 자세하게 다룹니다.&lt;/p&gt;

&lt;p&gt;처음에는 문제 컨셉을 정하지 않고 문제를 모집했지만, 제목이 &lt;code class="language-plaintext highlighter-rouge"&gt;Good Bye, %s!&lt;/code&gt; 형식인 문제 초안이 여러 개 올라오는 것을 보고 컨셉을 이렇게 정했습니다. 문제를 확정한 뒤에 저와 김준겸(ryute), 이상헌(evenharder), 그리고 각 문제의 출제자분들은 지문을 컨셉에 맞게 수정하는 데에 많은 시간을 들였습니다.&lt;/p&gt;

&lt;p&gt;수많은 고인물들이 대회 참가를 포기하면서까지 검수에 지원해 주셔서, 가장 어려운 문제를 제외하면 문제 검수는 별문제 없이 잘 진행되었습니다. 가장 어려운 문제였던 “Good Bye, 두 트리!” 의 검수는 대회 전날까지 끝내지 못했습니다. 결국 96vCPU + 384GB 머신에서 $O(N^2)$ 코드를 이용해 모든 입출력 데이터가 정확함을 검증하는 방법을 사용했습니다. 사실 이 방법은 LGCPC 2025 본선 검수할 때도 사용한 유명한(?) 검수 방법입니다.&lt;/p&gt;

&lt;p&gt;대회 준비의 전체 일정은 다음과 같습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;4/15 15:14 BOJ 서비스 종료 공지&lt;/li&gt;
  &lt;li&gt;4/16 14:15 운영진 모집 시작&lt;/li&gt;
  &lt;li&gt;4/16 21:43 대회 개최 공지&lt;/li&gt;
  &lt;li&gt;4/19 03:00 문제 모집 마감&lt;/li&gt;
  &lt;li&gt;4/20 18:47 대회 문제 확정&lt;/li&gt;
  &lt;li&gt;4/23 10:00 문제 세팅 마감&lt;/li&gt;
  &lt;li&gt;4/26 15:13 문제 검수 마감&lt;/li&gt;
  &lt;li&gt;4/26 18:00 대회 시작&lt;/li&gt;
  &lt;li&gt;4/26 22:00 대회 종료&lt;/li&gt;
  &lt;li&gt;4/26 22:10 방송 슬라이드 완성&lt;/li&gt;
  &lt;li&gt;4/26 22:15 방송 시작&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="대회-진행"&gt;대회 진행&lt;/h3&gt;

&lt;p&gt;정말 감사하게도 1500여 명에 달하는 분들이 대회에 참여해 주셨고, 이렇게 많은 사람이 몰렸음에도 불구하고 채점 서버에는 아무런 문제가 없었습니다. 스코어보드 로딩이 느린 건 runs.json을 사용하는 Spotboard 계열의 한계상 어쩔 수 없었습니다. 여기에서 별로 할 이야기는 없으니 문제별 리뷰만 간단하게 적겠습니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A. Good Bye, 별 찍기!&lt;/strong&gt; - 출제/세팅: jhnah917&lt;/p&gt;

&lt;p&gt;&lt;img src="https://justicehui.github.io/img/gb/A.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;반복문과 재귀를 공부하는 사람이라면 모두가 풀어본 별 찍기 시리즈입니다. 아래와 같이 세로 길이가 $2N$, 가로 길이가 $4N+2$인 BOJ 로고를 출력하는 문제였습니다.&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;     *   * *  
    *   *   *
   *   *     *
  *    *     *
 *      *   *
*        * *  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;B. Good Bye, 설탕 배달!&lt;/strong&gt; - 출제: queued_q, 세팅: ryute&lt;/p&gt;

&lt;p&gt;&lt;img src="https://justicehui.github.io/img/gb/B.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;제목은 BOJ의 통곡의 벽 중 하나인 설탕 배달 문제에서 따왔습니다. 단계별로 풀어보기의 앞부분에 있으면서 문제 자체도 쉬워 보이지만, 그렇다고 아무 생각 없이 풀면 틀리는 문제라 수많은 초보자를 고생시킨 문제입니다. BOJ 서비스 종료 전에 질문 게시판이 먼저 막히는 바람에 게시판 사진을 문제에 넣지 못한 것이 아쉬웠습니다. 고등학생 때 아래와 같은 질의응답을 본 기억이 있습니다.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Q: 1부터 30까지 다 넣어봤는데 반례가 없어요&lt;br&gt;A: N = 22가 반례입니다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;C. Good Bye, 토마토!&lt;/strong&gt; - 출제/세팅: 79brue&lt;/p&gt;

&lt;p&gt;&lt;img src="https://justicehui.github.io/img/gb/C.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;토마토는 &lt;a href="https://solved.ac/problems/archive/group/214"&gt;2013년 KOI 지역본선&lt;/a&gt; 초등부와 고등부에 출제된 문제입니다. BFS 연습 문제로 잘 알려져 있으며, 고등부에 나온 2차원 버전은 BOJ에 있는 &lt;a href="https://solved.ac/problems/level/11?page=1&amp;amp;sort=solved&amp;amp;direction=desc"&gt;골드 5 문제 중 두 번째로 많이 풀린 문제&lt;/a&gt;입니다.&lt;/p&gt;

&lt;p&gt;주제가 요리 대결이기도 하고, 출제자인 79brue가 2017년 KOI 대상을 받았기 때문에 처음에는 2017년 &lt;a href="https://solved.ac/problems/archive/contest/1773"&gt;KOI 전국본선 고등부 3번&lt;/a&gt;인 요리 강좌를 문제 제목으로 사용하려고 했습니다. 하지만 (제 생각과는 다르게) 요리 강좌가 많이 유명한 문제는 아닌 것 같아서, 토마토로 요리하는 문제로 바꾸었습니다. 대신 요리 강좌는 문제 지문에서 언급했습니다. 이 밖에도 두 심사 위원과 관련된 대회(wookje - 천하제일 코딩대회)와 문제(evenharder)가 하나씩 하이퍼링크로 들어갔습니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;D. Good Bye, 최소 스패닝 트리!&lt;/strong&gt; - 출제/세팅: queued_q&lt;/p&gt;

&lt;p&gt;&lt;img src="https://justicehui.github.io/img/gb/D.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;최소 스패닝 트리와 관련된 문제라서 제목을 정하는 것은 쉬웠습니다. 풀이의 난이도도 원본 문제와 크게 차이나지 않습니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E. Good Bye, Scenery!&lt;/strong&gt; - 출제: 79brue, 세팅: evenharder&lt;/p&gt;

&lt;p&gt;&lt;img src="https://justicehui.github.io/img/gb/E.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;A부터 D까지는 원본 문제의 난이도와 대회 문제의 난이도가 비슷했지만, E에서는 &lt;a href="https://solved.ac/problems/archive/contest/1747"&gt;2017 ICPC World Finals&lt;/a&gt;의 보스급 문제로 나온 Scenery를 제목으로 사용했습니다. ICPC WF에 어려운 문제는 수도 없이 많은데 이 문제가 언제부터 어떻게 유명해졌는지는 잘 모르겠습니다. solved.ac 가 만들어지기 전부터 유명했던 것은 기억합니다. 2018 IOI 가을 통신교육 때문일 수도 있고, 아니면 제가 모르는 과거 BOJ 슬랙 시절에 밈 같은 존재였을지도 모르겠습니다.&lt;/p&gt;

&lt;p&gt;아무튼 문제가 너무 유명한 덕분에 이 문제는 BOJ에 있는 &lt;a href="https://solved.ac/problems/level/29?page=1&amp;amp;sort=solved&amp;amp;direction=desc"&gt;루비 2 문제 중 가장 많이 풀린 문제&lt;/a&gt;가 되었습니다. BOJ에서 문제를 열심히 푼 사람들이라면 문제 지문에 있는 사진, 그리고 $N$명의 사진가가 래피드 시티에서 사진을 찍는 스토리를 보며 반가웠을 것입니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;F. Good Bye, 습격자 초라기!&lt;/strong&gt; - 출제/세팅: serin&lt;/p&gt;

&lt;p&gt;&lt;img src="https://justicehui.github.io/img/gb/F.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;습격자 초라기는 설탕 배달과는 조금 다른 의미로 BOJ의 뉴비 분쇄기로 유명한 문제입니다. 1000번 문제부터 차례대로 문제를 풀면 1006번에 있는 습격자 초라기에서 막힌다는 이야기가 있습니다. 하지만 개인적으로는 1006에서 막히면 그것만으로도 정말 똑똑한 사람이라고 생각합니다. 대부분 1003번 피보나치 함수 또는 1005번 ACM Craft 에서 막히지 않을까요?&lt;/p&gt;

&lt;p&gt;사실 이 문제는 제목보다는 문제 지문이 중요합니다. 이 문제에서 언급된 문제는 다음과 같습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;대장장이 토르비욘 - BOJ 13361 최고인 대장장이 토르비욘 (2016 NCPC)&lt;/li&gt;
  &lt;li&gt;고정된 카메라로 매일 밤 자정에 하늘 사진을 찍고 있다가 - BOJ 13310 먼 별 (2016 KOI 전국본선)&lt;/li&gt;
  &lt;li&gt;1, 2, …, R-L+1마리 - BOJ 17353 하늘에서 떨어지는 1, 2, …, R-L+1개의 별 (제3회 천하제일 코딩대회 본선)&lt;/li&gt;
  &lt;li&gt;초라기 - BOJ 1006 습격자 초라기&lt;/li&gt;
  &lt;li&gt;지도 - BOJ 3392 화성 지도&lt;/li&gt;
  &lt;li&gt;수아는 사탕 - BOJ 2419 사수아탕 (2009 Baltic OI)&lt;/li&gt;
  &lt;li&gt;연구소 - BOJ 14502 연구소 (삼성 코딩테스트)&lt;/li&gt;
  &lt;li&gt;특공대 - BOJ 4008 특공대 (2010 APIO)&lt;/li&gt;
  &lt;li&gt;엔빵 - BOJ 21725 더치페이 (제3회 IDT Cup)&lt;/li&gt;
  &lt;li&gt;순간 이동 - BOJ 숨바꼭질 시리즈&lt;/li&gt;
  &lt;li&gt;이동하는 중 - BOJ 이동하기 시리즈&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;문제의 그림 또한 2016 KOI 전국본선 고등부 4번에 출제된 먼 별의 이미지에 초라기를 합성한 것입니다. 이미지는 GPT-Image-2를 이용해 만들었습니다. 참가자가 생성형 인공지능을 사용하는 것은 허용하지 않지만, 운영진은 자유롭게 사용하는 모순(?)적인 대회입니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;G. Good Bye, 소가 길을 건너간 이유!&lt;/strong&gt; - 출제/세팅: queued_q&lt;/p&gt;

&lt;p&gt;&lt;img src="https://justicehui.github.io/img/gb/G.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://solved.ac/problems/archive/group/367"&gt;USACO 2017 February Contest&lt;/a&gt;에 출제된 12개의 문제는 &lt;code class="language-plaintext highlighter-rouge"&gt;소가 길을 건너간 이유 %d&lt;/code&gt; 형식의 제목으로 번역되어 BOJ에 업로드되어 있습니다. 이 밖에도 다른 대회에서 비슷한 제목의 문제가 여러 번 출제되었습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
&lt;a href="https://solved.ac/problems/archive/contest/2272"&gt;2020 UCPC 본선&lt;/a&gt; - BOJ 19545 소가 길을 건너간 이유 2020&lt;/li&gt;
  &lt;li&gt;
&lt;a href="https://solved.ac/problems/archive/contest/2335"&gt;2020 Goricon&lt;/a&gt; - BOJ 20118 호반우가 길을 건너간 이유&lt;/li&gt;
  &lt;li&gt;
&lt;a href="https://solved.ac/problems/archive/contest/2345"&gt;2020 CPC&lt;/a&gt; - BOJ 20206 푸앙이가 길을 건너간 이유&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이 문제도 F번과 마찬가지로 지문에 여러 문제가 언급되어 있습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;소들이 … 모습을 사진에 담았다 - BOJ 5914 Cow Photography (USACO 2011 December)&lt;/li&gt;
  &lt;li&gt;잉크 통을 엎지르는 - BOJ 16857 잉크를 엎질렀다 (leejseonal ddforces (ryuted for div.1))&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;H. Good Bye, 요세푸스 문제!&lt;/strong&gt; - 출제/세팅: onjo0127&lt;/p&gt;

&lt;p&gt;&lt;img src="https://justicehui.github.io/img/gb/H.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;큐와 재귀 함수, 점화식을 공부하는 사람이라면 한 번씩은 풀어본 문제인 요세푸스 시리즈에서 따왔습니다. 문제 풀이가 정말 아름답습니다. 당시 최신 모델이었던 GPT 5.5가 풀지 못했다는 점을 생각하면, 여러 방면에서 정말 좋은 문제입니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I. Good Bye, 두 트리!&lt;/strong&gt; - 출제: yclock($\tilde O(N^2)$ 버전) &amp;amp; leejseo($O(N \sqrt N)$ 버전), 세팅: leejseo&lt;/p&gt;

&lt;p&gt;&lt;img src="https://justicehui.github.io/img/gb/I.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;이 대회에서 가장 어려운 문제입니다.&lt;/p&gt;

&lt;p&gt;백준 온라인 저지에는 제목이 “두 트리”인 문제가 4개 있습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;BOJ 13341 두 트리&lt;/li&gt;
  &lt;li&gt;BOJ 23052 두 트리 (2021 SNUPC)&lt;/li&gt;
  &lt;li&gt;BOJ 24027 두 트리 (2021 나코더 송년)&lt;/li&gt;
  &lt;li&gt;BOJ 28402 두 트리 (2023 UCPC 본선)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이중 SNUPC에 출제된 BOJ 23052 두 트리는 문제를 matroid intersection으로 모델링한 뒤, 그래프의 성질을 이용해 일반적인 $O(N^3)$ 알고리즘보다 빠른 $\tilde O(N^2)$ 시간에 계산하는 것을 의도한 문제입니다. matroid partition을 이용해서 풀 수도 있지만, 어떤 방법을 쓰더라도 어려운 문제인 건 변치 않습니다. Good Bye, 두 트리! 문제는 $N$의 상한을 3000에서 100000으로 늘린 문제입니다.&lt;/p&gt;

&lt;p&gt;제 블로그에 &lt;a href="https://justicehui.github.io/ps/2023/02/01/BOJ23052/"&gt;BOJ 23052 풀이&lt;/a&gt;를 포함해 &lt;a href="https://justicehui.github.io/tag/#/Matroid"&gt;매트로이드 문제의 풀이&lt;/a&gt;가 몇 개 있어서 그런지, LLM을 이용한 부정행위자들이 제출한 코드에서 재미있는 점을 여럿 발견할 수 있었습니다.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://justicehui.github.io/img/gb/cheat.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;J. Bood Bye, BOJ!&lt;/strong&gt;&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;다음 중 하나 이상의 문자열을 부분 문자열로 포함하는 문자열 $S$를 구하여라. 단 알파벳의 경우, 대소문자를 구별하지 않는다.

* BOJ
* Baekjoon
* 백준

문자열 $A$가 문자열 $B$의 부분 문자열이라는 것은, $B$의 앞과 뒤에서 문자를 적절히 삭제했을 때 $A$가 되도록 할 수 있다는 뜻이다. 즉, $A$가 $B$의 연속한 구간으로 나타난다면 $A$는 $B$의 부분 문자열이다.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://solved.ac/problems/archive/contest/1971"&gt;제1회 키파컵&lt;/a&gt;의 Happy Birthday, kipa00!, &lt;a href="https://solved.ac/problems/archive/contest/2206"&gt;제1회 논산 코드 페스티벌&lt;/a&gt;의 편지 꼭 해다오 같은 문제에서 사용되었던 편지를 쓰는 컨셉의 문제입니다. 문제의 예제 출력에는 운영진들의 편지가 들어 있습니다. BOJ의 마지막 문제에 자신의 이름을 넣을 수 있는 운영진의 특권 아닌 특권입니다.&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;Good Bye, BOJ!
- leejseo

Good Bye, Good Bye, BOJ!
- serin

BOJ에서 11년 동안 정말 재미있었습니다. 그동안 정말 감사했습니다.
- 79brue

BOJ 덕분에 알고리즘 문제 풀이를 즐기는 여러분들과 함께 10년 동안 행복한 추억 정말 많이 만들 수 있었습니다.
Good Bye, BOJ!
- onjo0127

10년 동안 재미있게 놀다 갑니다. 백준님 감사합니다!
- jhnah917

BOJ 유저분들 사랑해요~!
- yclock

BOJ를 통해 많은 것을 배우고 성장할 수 있었습니다. 항상 감사합니다.
Good Bye, BOJ!
- qwerasdfzxcl

BOJ 덕분에 많이 성장했고, 잊지 못할 소중한 추억들도 정말 많이 만들었습니다.
그동안 빌어먹게 신세 많이 졌습니다!
- kyo20111

욱백망자 (욱제님 백준을 망치는걸 자제해주세요) (edited)
- wookje

누가 뭐래도 가장 재밌게 코딩하던 그 순간들을 추억하며,
Good Bye, BOJ!
- junseo

BOJ와 함께여서 행복했습니다! 그동안 맛있는 문제들 정말 잘 먹었습니다! 감사합니다!
이 공간에서 함께 했던 추억은 영원하기를
Good Bye, BOJ!
- stonejjun03

문제 하나에 컴파일 에러와
문제 하나에 출력 초과와
문제 하나에 메모리 초과와
문제 하나에 시간 초과와
문제 하나에 런타임 에러와
문제 하나에 틀렸습니다, 틀렸습니다,
그리고 맞았습니다!!
BOJ의 문제들과 함께 성장할 수 있어서,
BOJ와 함께여서 행복했습니다!
감사했습니다.
Good Bye, BOJ!!
- cs71107

BOJ는 저의 오랜 취미이자, 배움의 장소였고, 또 제 생각들을 펼칠 수 있는 곳이었습니다.
다양한 문제들을 풀어볼 수 있어 행복했고, 또 수많은 쾌감을 느꼈습니다.
제가 낸 온갖 이상한 문제들을 풀어주셨던 분들, 또 BOJ를 통해 저와 교류하셨던 모든 분들께 감사합니다.
마지막으로, Thank you, BOJ.
- cozyyg

짧은 일정 속에서도 Good Bye, BOJ! 대회를 준비해주신 운영진 여러분,
아껴두셨던 소중한 문제를 흔쾌히 내어주신 출제자 여러분,
바쁜 와중에도 시간을 내어 문제를 검수해주신 검수자 여러분,
지금까지 BOJ를 운영해주신 백준님, 그리고 오랜 시간 함께해주신 PS러 여러분,
모두 진심으로 감사드립니다. BOJ와 함께한 시간, 정말 행복했습니다.
- Silverwolf(Numbering)

백준 온라인 저지와 함께한 시간이 제 20대 삶의 거의 전부였습니다.
12년의 세월 동안 이 공간에서 단순히 알고리즘뿐만 아니라 더 나은 인간으로 살아가는 법을 배운 것 같습니다.
저는 삶의 목표를 이곳에서 찾았으니 더 바랄 것이 없겠습니다.
그동안의 여정에 함께할 수 있어서 영광이었고, 저의 여정에 지금까지 함께해 주셔서 감사합니다.

- koosaga

백준 온라인 저지 덕분에 10년 넘도록 problem solving을 즐길 수 있었습니다.
제 인생에 있어서 빼놓을 수 없는 놀이터이자 교류의 장을 가꾸어 주셔서 감사드립니다.

Farewell, Baekjoon Online Judge!

*     ____             __     _
*    / __ )____ ____  / /__  (_)___  ____  ____
*   / __  / __ `/ _ \/ //_/ / / __ \/ __ \/ __ \
*  / /_/ / /_/ /  __/ ,&amp;lt;   / / /_/ / /_/ / / / /
* /_____/\__,_/\___/_/|_|_/ /\____/\____/_/ /_/
*        ____        __/___/
*       / __ \____  / (_)___  ___
*      / / / / __ \/ / / __ \/ _ \
*     / /_/ / / / / / / / / /  __/
*     \____/_/ /_/_/_/_/ /_/\___/
*               / /_  ______/ /___ ____
*          __  / / / / / __  / __ `/ _ \
*         / /_/ / /_/ / /_/ / /_/ /  __/
*         \____/\__,_/\__,_/\__, /\___/
*                          /____/
- evenharder

끝이라는 건 참 익숙해지기 어려운 존재인 듯합니다.

사실 저는 아직 실감이 별로 들지 않습니다.
BOJ를 처음 시작한 때가 바로 어제처럼 생생한데,
막연히 영원히 이어질 것이라 생각했던 장소와, 시간과 작별이라는 걸
오래 함께한 만큼 더 받아들이기 어려운 걸지도 모릅니다.

문제를 풀지 않아도 습관이 된 BOJ 접속을 쉽사리 끊지 못할 것이고,
낯선 화면을 마주하는 것에 적응하지 못하고 새로고침도 해보겠지요.

섭섭하고 아쉬우면서도,
지금껏 여러 문제를 풀고, 대회에 참가하고, 사람을 만나며
변치 않을 기억과, 추억과, 경험을 남기게 해준 BOJ에게
마지막으로 인사를 남깁니다.

헤어짐이 아니라 언젠가 다시 만남을 뜻하는 안녕이었기를 바라며.
안녕, BOJ!
- cgiosy

이별의 의미는 분명 이별하는 사람의 수만큼이겠죠
누군가가 우리의 추억을 가십거리로 소비한다고 한탄하면서도
어제 잊어버린 가십 하나하나에 담긴 마음들을 전부 합하면 남은 생을 바쳐도 모자랄 정도겠죠
우리들은 거친 시대의 흐름에 지나간 모든 것과의 절연을 강요받고
그 속도를 알면서도 잠시 마찰하고 싶다고 생각한 여러분과
나만이 흘릴 수 있는 눈물을 이 자리에 확실히 모아서 외치는
이별하는 사람 수만큼의
GoodBye, BOJ!
- blackking26

같은 취미를 가진 인연들을 만난다든지,
함께 합을 맞춰 팀 대회에서 상을 받아본다든지,
때로는 긴긴 고뇌 끝에 어려운 문제의 답을 알아낸다든지,
동아리를 만들고 대회를 운영하며 알고리즘의 재미를 전파한다든지.

모두 백준 온라인 저지가 없었더라면
만끽하지 못했을 소중한 즐거움이었습니다.

BOJ가 있었기에 지금의 제가 있을 수 있었습니다.
그동안 감사했습니다!
- queued_q

BOJ를 통해 새로운 세상을 접하게 되었고, 오랜 세월을 같이 해왔습니다.
인생을 바꾸었다고 해도 과언이 아닐 만큼 많은 영향을 받았습니다.
저에게 뜻깊은 의미가 있던 플랫폼이었던 만큼,
수많은 분들도 BOJ에서의 각각의 추억을 가지고 있을 것입니다.

BOJ라는 하나의 커뮤니티에서 교류해 주신 모든 분들의 추억을 담아서,
그동안 즐거웠습니다!
Farewell, BOJ!
- lighton

영원할 것 같은 시간들도 TL이 있다는 게 참 슬프네요.
머리로는 MLE가 날만큼 많은 순간이 추억으로 남아 있습니다.
WA를 받던 순간들도 지금 돌아보니 얼마나 즐거웠는지 모르겠습니다.
BOJ와 함께한 모두의 앞날이 언제나 AC이기를 빕니다.

우리의 졸업식에 함께해 준 여러분 모두 감사드립니다.
Good Bye, BOJ!
- SongC

어려운 문제를 풀었을 때의 성취감.
대회에서 간발의 차로 문제를 풀지 못했을 때의 아쉬움.
문제풀이에 대해 의논하며 얻은 소중한 친구들.
대회에 처음으로 문제를 출제해봤을 때의 즐거움.
5년이라는 시간동안, BOJ는 제게 잊을 수 없는 소중한 추억을 만들어주었습니다.
감사했습니다!
Good Bye, BOJ!
- ibm2006

여기까지 Good Bye, BOJ! 대회 운영진들이 남긴 말들이었습니다.

읽어주신 여러분께 감사드리며,

이 대회가 백준 온라인 저지를 보내는 여러분에게
추억을 되돌아볼 수 있는 이정표가 되기를 희망합니다.

이제 대회를 즐겨 주세요!
- ryute
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;실제로는 정상적인 편지만 예제로 들어갔지만, 문제를 준비하는 과정에서는 아래와 같은 괴상한(…) 편지도 많이 나왔습니다.&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;[Web발신]
너는나를존중해야한다나는백준온라인저지를운영하는사람이며수많은개발자와알고리즘유저들이나를통해성장했고대한민국코딩테스트문화의기반을만든장본인이다수만문제의채점데이터를관리하며공정한평가시스템을구축했고전세계유저들이이용하는플랫폼을직접운영하고있다또한수많은대회와교육의기반이되는사이트를만들어코딩실력향상에기여했다내가이분야에서차지하는위상은그누구도부정할수없다
- 호날두

안녕BOJ야너를처음본순간부터좋아했어대회전에고백하고싶었는데바보같이그땐용기가없더라지금은이수많은참가자들앞에서오로지너만사랑한다고말하고싶어서큰마음먹고용기내어봐매일매일깃허브에서너볼때마다두근댔고온라인저지대회랑해커톤에서도너만보이고너생각만나고지난3월부터계속그랬어니가다른언어랑헤어지고니맘이아파울때내마음도너무아팠지만내심좋은맘두있었어이런내맘을어떻게말할지고민하다가정말인생에서제일크게용기내어세상에서제일멋지게많은사람들앞에서너한테고백해주고싶었어사랑하는백준너내코딩파트너가되줄래?아니나만의최고난이도문제가되어줄래?난너의디버거가될게내일AC받은시점에너채점결과앞에서기다리고있을게너를사랑하는xx이가
(최백준 님이 나갔습니다)
이제 boj 누가 운영해주냐

백준 유저들은 완전히 멘탈이 나가버렸습니다.

평소처럼 문제를 풀고,
평소처럼 제출을 누르고,
평소처럼 맞았습니다!!를 기다리고 있었는데요.

갑자기 백준이 서비스 종료를 발표해버렸거든요.

평소처럼 보던 화면이었는데,
갑자기 평소처럼 볼 수 없게 될 화면이 되어버린 거죠.

PS인들에게 백준은 단순한 사이트가 아니었습니다.

처음으로 1000번 A+B를 맞힌 곳도 백준이었고,
새벽 4시에 “이거 왜 틀림?”을 외치게 만든 곳도 백준이었고,
solved.ac 티어를 보며 이상한 동기부여를 얻던 곳도 백준이었거든요.

그래서 PS 고수들이 기발한 생각을 했는데요.

"그렇다면 마지막으로 대회를 열자."

그렇게 탄생한 것이 바로 Goodbye BOJ였습니다.

그런데 진짜 문제는 여기서부터였습니다.

대회를 열기로 한 순간, 모두가 깨달아버린 겁니다.

서비스 종료까지 남은 시간이 너무 짧았다는 걸요.

평소 같았으면 대회를 하나 열기 위해
문제를 만들고, 검수하고, 데이터를 짜고,
공지와 운영 방식을 정리하고,
참가자들이 납득할 수 있는 난이도 곡선을 만드는 데까지
긴 시간이 필요했습니다.

하지만 이번에는 달랐습니다.

남은 시간은 너무 짧았고,
해야 할 일은 너무 많았습니다.

바로 그 순간, 대회 운영자들이 무식하지만 확실한 아이디어를 냈습니다.

"그냥 우리가 시간을 갈아 넣자."

그때부터 조용한 전쟁이 시작됐습니다.

누군가는 밤새 문제 지문을 다듬었고,
누군가는 반례를 찾아 테스트 데이터를 고쳤고,
누군가는 난이도가 튀지 않도록 문제 순서를 다시 짰습니다.

또 누군가는 공지를 정리하고,
누군가는 운영 방식을 조율하고,
누군가는 마지막까지 “이 풀이 진짜 막히는 거 맞나요?”라고 물으며
검수를 이어 갔죠.

그렇게 짧은 시간 안에
원래라면 불가능해 보였던 대회가 조금씩 모양을 갖추기 시작했습니다.

그리고 마침내, 마지막 대회가 열렸습니다.

사람들은 평소처럼 코드를 짰고,
평소처럼 틀렸고,
평소처럼 다시 고쳤고,
평소처럼 맞았습니다!!를 기다렸습니다.

하지만 모두가 알고 있었습니다.

이번 대회는 평소와 같으면서도,
절대로 평소와 같을 수 없는 대회라는 것을요.

대회가 끝난 뒤에도 사람들은 곧바로 떠나지 않았습니다.

누군가는 마지막 스코어보드를 캡처했고,
누군가는 처음으로 맞았던 문제 번호를 떠올렸고,
누군가는 끝내 풀지 못한 문제를 다시 열어 보았습니다.

그곳에서 우리가 배운 건 단순히 문제를 푸는 법이 아니었습니다.

틀리고, 고치고, 다시 시도하는 법.
혼자 막혔을 때 질문하는 법.
그리고 누군가가 만든 문제 위에서
또 다른 누군가가 성장할 수 있다는 사실이었죠.

그래서 백준은 조용히 문을 닫게 되었지만,
그 안에서 자라난 사람들은 각자의 자리로 돌아가
새로운 문제를 풀기 시작했습니다.

Goodbye BOJ.

그리고 고마웠습니다.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id="아무말"&gt;아무말&lt;/h3&gt;

&lt;p&gt;대회는 별 사고 없이 잘 마무리되었습니다. 급박한 일정 속에서도 대회를 개최하기 위해 힘 써주신 분들, 그리고 지금까지 백준 온라인 저지를 운영해 주신 최백준님께 모두 감사드립니다.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;핸들&lt;/th&gt;
      &lt;th&gt;소속&lt;/th&gt;
      &lt;th&gt;핸들&lt;/th&gt;
      &lt;th&gt;소속&lt;/th&gt;
      &lt;th&gt;핸들&lt;/th&gt;
      &lt;th&gt;소속&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;79brue&lt;/td&gt;
      &lt;td&gt;MIT&lt;/td&gt;
      &lt;td&gt;junseo&lt;/td&gt;
      &lt;td&gt;한양대학교&lt;/td&gt;
      &lt;td&gt;qwerasdfzxcl&lt;/td&gt;
      &lt;td&gt;서울대학교&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;blackking26&lt;/td&gt;
      &lt;td&gt;서울대학교&lt;/td&gt;
      &lt;td&gt;kdh9949&lt;/td&gt;
      &lt;td&gt;Furiosa AI&lt;/td&gt;
      &lt;td&gt;ryute&lt;/td&gt;
      &lt;td&gt;NEXON&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;cgiosy&lt;/td&gt;
      &lt;td&gt;NEXON&lt;/td&gt;
      &lt;td&gt;koosaga&lt;/td&gt;
      &lt;td&gt;MIT&lt;/td&gt;
      &lt;td&gt;serin&lt;/td&gt;
      &lt;td&gt;KAIST&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;cozyyg&lt;/td&gt;
      &lt;td&gt;삼성전자&lt;/td&gt;
      &lt;td&gt;kyo20111&lt;/td&gt;
      &lt;td&gt;삼성전자&lt;/td&gt;
      &lt;td&gt;shiftpsh&lt;/td&gt;
      &lt;td&gt;NEXON&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;cs71107&lt;/td&gt;
      &lt;td&gt;서울대학교&lt;/td&gt;
      &lt;td&gt;leejseo&lt;/td&gt;
      &lt;td&gt;KAIST&lt;/td&gt;
      &lt;td&gt;silverwolf&lt;/td&gt;
      &lt;td&gt;서울대학교&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;evenharder&lt;/td&gt;
      &lt;td&gt;Furiosa AI&lt;/td&gt;
      &lt;td&gt;leinad2&lt;/td&gt;
      &lt;td&gt;서울대학교&lt;/td&gt;
      &lt;td&gt;songc&lt;/td&gt;
      &lt;td&gt;KAIST&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ibm2006&lt;/td&gt;
      &lt;td&gt;KAIST&lt;/td&gt;
      &lt;td&gt;lighton&lt;/td&gt;
      &lt;td&gt;서울대학교&lt;/td&gt;
      &lt;td&gt;stonejjun03&lt;/td&gt;
      &lt;td&gt;프레스토 리서치 코리아&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;jhnah917&lt;/td&gt;
      &lt;td&gt;Quora&lt;/td&gt;
      &lt;td&gt;onjo0127&lt;/td&gt;
      &lt;td&gt;KAIST&lt;/td&gt;
      &lt;td&gt;wookje&lt;/td&gt;
      &lt;td&gt;Google&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;jhwest2&lt;/td&gt;
      &lt;td&gt;서울대학교&lt;/td&gt;
      &lt;td&gt;queued_q&lt;/td&gt;
      &lt;td&gt;삼성전자&lt;/td&gt;
      &lt;td&gt;yclock&lt;/td&gt;
      &lt;td&gt;서울대학교&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;저도 대충 서울대/KAIST 나온 것처럼 보이도록 잘 숨어봤는데 성공적이었는지는 모르겠습니다. 대회 운영하면서 똑똑한 친구들을 정말 많이 알게 되었습니다. 덕분에 이 대회도, 그리고 저도 여기까지 올 수 있었습니다.&lt;/p&gt;

&lt;p&gt;2026년 1월 19일에 제 블로그에 올라간 &lt;a href="https://justicehui.github.io/2026/01/19/good-bye-boj/"&gt;이 글&lt;/a&gt;을 언급하며, Good Bye, BOJ! 대회 운영진은 BOJ 서비스 종료 소식을 미리 알고 있었던 것이 아니냐는 의문을 제기한 글을 보았습니다. 이제 와서 말하는 거지만, 저 글은 Good Bye, BOJ 2025! 후기를 쓰기 위해 파일을 생성했지만 까먹고 내용을 작성하지도 파일을 삭제하지 않은 채로 커밋해서 올라가게 된 포스트입니다. 히스토리 보존을 위해 삭제하진 않았습니다.&lt;/p&gt;

&lt;p&gt;-&lt;/p&gt;

&lt;p&gt;그전까지는 한 번도 콘서트에 안 다니다가, 작년 4월을 시작으로 약 1년 반 동안 7번의 콘서트/팬미팅에 갔습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;250427 DAESUNG 2025 ASIA TOUR: D’s WAVE IN SEOUL Day 2&lt;/li&gt;
  &lt;li&gt;260102 DAESUNG 2025 ASIA TOUR: D’s WAVE ENCORE - SEOUL Day 1&lt;/li&gt;
  &lt;li&gt;260104 DAESUNG 2025 ASIA TOUR: D’s WAVE ENCORE - SEOUL Day 3&lt;/li&gt;
  &lt;li&gt;260208 2026 G-DRAGON ‘FAM’ MEETING [FAM+ILY : FAMILY : FAM I LOVE YOU] Day 3&lt;/li&gt;
  &lt;li&gt;260821 BIGBANG 2026-2027 WORLD TOUR &amp;lt;XX : COSMOS&amp;gt; IN GOYANG Day 1&lt;/li&gt;
  &lt;li&gt;260822 BIGBANG 2026-2027 WORLD TOUR &amp;lt;XX : COSMOS&amp;gt; IN GOYANG Day 2&lt;/li&gt;
  &lt;li&gt;260823 BIGBANG 2026-2027 WORLD TOUR &amp;lt;XX : COSMOS&amp;gt; IN GOYANG Day 3&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;약 15년 동안 공부할 때 듣던 노래들을 현장에서 들어보는 것은 정말 좋은 경험이었습니다.&lt;/p&gt;

&lt;p&gt;공연을 보면서, 공연하는 가수를 보면서, 그리고 공연을 보는 관객들을 보면서 여러 생각이 들었습니다. 예술은 다른 사람에게 추억과 감동을 주고, 과학과 공학은 세상을 더 편리하게 만들어 줍니다. 제가 전공한 CSE 또한 과학과 공학 그 사이 어딘가에 속하고, 마찬가지로 세상을 더 편리하게 만드는 데에 지난 수십 년 동안 많은 기여를 했습니다. 그러나 Problem Solving이라는 작은 분야는 다른 사람에게 어떤 가치를 줄 수 있을까요? 가만히 앉아서 문제를 푸는 것만으로 어떠한 가치를 창출하기는 어렵습니다. 또한, 온라인 저지에서 푸는 문제는 이미 풀린 문제이기 때문에 인류 지식의 경계를 확장하는 것에도 (직접적으로) 기여하지 않습니다.&lt;/p&gt;

&lt;p&gt;가수들은 무대 위에서(또는 사람들 앞에서) 공연할 때 정말 행복해 보였습니다. 제가 무언가를 하면서 그 정도의 행복과 기쁨을 느꼈을 때가 언제였는지도 다시 한번 돌아봤습니다.&lt;/p&gt;

&lt;p&gt;두 가지 질문을 한 번에 해결할 수 있는 답을 떠올리는 데에는 오랜 시간이 걸리지 않았습니다. BOJ는 서비스를 종료했지만, 저는 앞으로도 대회를 운영하며 저와 다른 사람들이 좋은 추억을 남길 수 있게 도움을 주려고 합니다. LLM의 발전 속도를 보면 알고리즘 대회가 언제까지 유지될 수 있을진 모르겠습니다.&lt;/p&gt;

&lt;p&gt;-&lt;/p&gt;

&lt;p&gt;8개의 시즌 동안 총 11개의 대회를 개최했고, 그중 4번의 대회는 오프라인(2023 삼성전자 서울대 공동 연구소, 2024-2025 LG사이언스파크)으로도 개최했습니다. 참 많이도 했네요… 학교에서 학생들을 가르치시는 50대 선생님, 과거에 정말 뛰어난 실력으로 대회를 휩쓸고 다니셨던 40대 직장인, 그리고 고등학생과 중학생까지, 다양한 연령대의 참가자가 한자리에 모여 문제를 푸는 모습을 지켜볼 수 있어서 행복했습니다.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;전체 참가자&lt;/th&gt;
      &lt;th&gt;오프라인 참가자&lt;/th&gt;
      &lt;th&gt;운영진&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Good Bye, BOJ 2019!&lt;/td&gt;
      &lt;td&gt;133&lt;/td&gt;
      &lt;td&gt;0&lt;/td&gt;
      &lt;td&gt;14&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Good Bye, BOJ 2020!&lt;/td&gt;
      &lt;td&gt;515&lt;/td&gt;
      &lt;td&gt;0&lt;/td&gt;
      &lt;td&gt;22&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Good Bye, BOJ 2021!&lt;/td&gt;
      &lt;td&gt;773&lt;/td&gt;
      &lt;td&gt;0&lt;/td&gt;
      &lt;td&gt;21&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Hello, BOJ 2022!&lt;/td&gt;
      &lt;td&gt;705&lt;/td&gt;
      &lt;td&gt;0&lt;/td&gt;
      &lt;td&gt;20&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Good Bye, BOJ 2022!&lt;/td&gt;
      &lt;td&gt;599&lt;/td&gt;
      &lt;td&gt;0&lt;/td&gt;
      &lt;td&gt;25&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Hello, BOJ 2023!&lt;/td&gt;
      &lt;td&gt;272&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;92&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;26&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Good Bye, BOJ 2023!&lt;/td&gt;
      &lt;td&gt;284&lt;/td&gt;
      &lt;td&gt;0&lt;/td&gt;
      &lt;td&gt;18&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Hello, BOJ 2024!&lt;/td&gt;
      &lt;td&gt;272&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;84&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;18&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Hello, BOJ 2025!&lt;/td&gt;
      &lt;td&gt;247&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;68&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;15&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Good Bye, BOJ 2025!&lt;/td&gt;
      &lt;td&gt;200&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;72&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;15&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Good Bye, BOJ!&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;1501&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;0&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;28&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;각 대회의 난이도 분포는 아래와 같습니다. 노란색 배경은 오프라인 참가자를 선발하기 위한 예선 성격의 대회, 하늘색 배경은 오프라인 참가자를 대상으로 한 대회라서 난이도 분포가 고르지 않을 수 있습니다. 하지만 그 점을 고려하더라도 최단 대회의 난이도 분포는 이상적이지 않았던 것 같은데, 마지막 대회는 예쁘게 잘 나와서 다행입니다.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://justicehui.github.io/img/gb/tier.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;BOJ가 돌아오면 Hello, BOJ! 를 개최해야 할까요? 그때 가서 생각해 보는 걸로 하겠습니다. 문제와 사람과 시간이 있다면 불가능하진 않아 보입니다.&lt;/p&gt;

&lt;p&gt;그동안 수고 많으셨습니다.&lt;/p&gt;
&lt;/body&gt;&lt;/html&gt;
</content>
    <id>https://justicehui.github.io/review/2026/09/08/good-bye-boj/</id>
    <link href="https://justicehui.github.io/review/2026/09/08/good-bye-boj/"/>
    <summary type="html">대회보다 온라인 저지가 먼저 없어졌습니다.</summary>
    <title>Good Bye, BOJ! 개최 후기</title>
    <updated>2026-09-08T09:00:00+09:00</updated>
    <dc:date>2026-09-08T09:00:00+09:00</dc:date>
  </entry>
  <entry>
    <author>
      <name>망나니개발자</name>
    </author>
    <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;h2 data-ke-size="size26"&gt;&lt;b&gt;&lt;span style="color: #f15f5f;"&gt;&lt;span&gt;&lt;span&gt;1. 멀티 코어 기반의 처리량 극대화를 위한 Fork/Join 프레임워크&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable="false" data-ke-type="horizontalRule" data-ke-style="style5"&gt;
&lt;h3 data-ke-size="size23"&gt;&lt;b&gt;[ 하드웨어의 발전과 기존 방식의 한계 ]&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size="size16"&gt;Fork/Join 프레임워크를 이해하려면 하드웨어의 발전에 대해서도 이해할 필요가 있다. 초기의 하드웨어는 1개의 CPU 안에 1개의 코어만 담겨있는 단일 코어 구조였다. 따라서 해당 코어의 처리 성능을 높이는 사실상 유일한 방법은 클럭 속도를 올리는 것이었다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;하지만 2000년대 단일 코어 클럭 속도 상승이 전력과 발열 한계에 부딪히면서 CPU 제조사들은 클럭 대신 코어 수를 늘리는 방향으로 선회했다. 이제는 여러 개의 코어를 활용할 수 있게 되었고, 멀티 코어 환경에서 컴퓨팅 리소스를 잘 활용하기 위한 도구들이 필요해졌다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;2004년, ThreadPoolExecutor와 Executors 같은 자바의 동시성 도구들이 Java 5에 들어올 당시에는 "많은 독립적인 요청을 여러 스레드가 나눠 처리한다"는 전제로 설계됐다. 하지만 이제는 코어를 최대로 활용할 수 있게, 1개의 큰 계산을 서로 다른 코어에서 나누어 처리해야 할 필요가 생겼다. 위와 같은 문제를 해결하기 위해, Java 7에 Fork/Join 프레임워크가 등장하게 됐다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;h3 data-ke-size="size23"&gt;&lt;b&gt;[ Fork/Join 프레임워크 ]&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size="size20"&gt;&lt;b&gt;ForkJoinPool&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size="size16"&gt;Fork/Join 프레임워크는 1개의 큰 작업을 재귀적인 작은 작업으로 분할하여 여러 코어가 나누어 처리하고, 각각의 서브태스크 작업 결과를 합쳐서 전체 결과를 만들도록 설계되었다. Fork/Join 프레임워크는 이를 위해 서브태스크를 스레드 풀의 워커 스레드에 분산 할당시키는 ForkJoinPool을 제공한다. 해당 클래스는 스레드 풀을 활용해 작업 실행과 관리를 하는 ExecutorService 인터페이스를 구현한다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;즉, Fork/Join 프레임워크는 ForkJoinPool을 활용해 사용자에게 보이지 않는 스레드 풀에서 작업을 자동으로 스케줄링하는 것이다. ForkJoinPool은 Thread보다 "작은" 동시성 단위인 ForkJoinTask를 처리한다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;h4 data-ke-size="size20"&gt;&lt;b&gt;ForkJoinTask&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size="size16"&gt;우리는 보통 "동시에 실행되는 일 = 스레드"라고 생각한다. 100개를 동시에 처리하려면 스레드 100개가 필요하다고 여기는 것이다. 하지만 자바의 스레드는 무겁다. java.lang.Thread(플랫폼 스레드)는 OS 커널 스레드와 1:1로 매핑된다. 임의의 시점에 블로킹할 수 있고, 인터럽트를 받을 수 있고, 우선순위를 가지며, 각자 스택을 갖고 OS 스케줄러의 관리를 받는다. 따라서 자바 스레드는 무겁고, 플랫폼에 따라 객체당 &lt;b&gt;1~2MB가 요구&lt;/b&gt;되고, 생성과 컨텍스트 스위치마다 커널이 개입한다. 수십 개라면 문제없지만, 분할정복처럼 작업을 수만 개로 쪼개는 상황에서는 스레드 생성 비용이 그 작업의 계산 시간보다 커진다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;그래서 Fork/Join 프레임워크는 실행 단위를 스레드에서 떼어내고자 ForkJoinTask라는 규약을 고안했다. ForkJoinTask는 추상 클래스로, new IncrementTask(arr, 0, 1000)와 같이 처리할 정보만 담은 가벼운 객체를 만들 수 있게 한다. 이는 스레드와 달리 단순 자바 객체이므로 수만 개를 만들어도 부담이 없다.&lt;br&gt;하지만 우리가 ForkJoinTask를 직접 상속할 일은 거의 없다. ForkJoinTask는 실행 규약만을 갖는 추상 클래스이고, 실제로는 셋 중 하나를 골라 활용하게 된다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;이름 &lt;/p&gt;
&lt;table style="border-collapse: collapse; width: 100%;" border="1" data-ke-align="alignLeft" data-ke-style="style4"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style="color: #333333; text-align: start;"&gt;클래스&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;언제 쓰나&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RecursiveTask&amp;lt;V&amp;gt;&lt;/td&gt;
&lt;td&gt;결과를 반환할 때 (합계, 최댓값, 정렬 결과)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RecursiveAction&lt;/td&gt;
&lt;td&gt;결과 없이 부수 효과만 (제자리 정렬, 이미지 필터)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CountedCompleter&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td&gt;join 없이 완료 콜백으로 결과를 전파할 때&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt;배열의 모든 원소를 1씩 증가시키는 예제 코드로 살펴보자. 이 경우에는 처리 결과가 필요 없기에 RecursiveAction을 활용하면 되고, 요청을 서브태스크로 분할하고 더 이상 분할할 수 없을 때 개별 서브태스크를 처리하는 compute() 메소드를 구현하면 된다. 나머지, 즉 태스크를 어느 스레드가 언제 실행할지는 ForkJoinPool이 알아서 정한다.&lt;/p&gt;
&lt;pre class="angelscript"&gt;&lt;code&gt;class IncrementTask extends RecursiveAction {
    final long[] array;
    final int lo, hi;

    IncrementTask(long[] array, int lo, int hi) {
        this.array = array;
        this.lo = lo;
        this.hi = hi;
    }

    protected void compute() {
        if (hi - lo &amp;lt; THRESHOLD) {
            for (int i = lo; i &amp;lt; hi; ++i)
                array[i]++;
        }
        else {
            int mid = (lo + hi) &amp;gt;&amp;gt;&amp;gt; 1;
            invokeAll(
                new IncrementTask(array, lo, mid),
                new IncrementTask(array, mid, hi)
            );
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt;실제 실행은 별도로 존재하는 소수의 워커 스레드가 담당한다. 이들이 태스크 객체를 주워다 compute()를 호출한다. 즉, 작업의 정의(태스크)와 실행(스레드)이 분리된 구조이다. 이를 통해 블로킹 작업이 없는 순수 계산 태스크라면 작업을 아무리 잘게 쪼개도 스레드 수는 코어 수 그대로 유지할 수 있다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;반대로 말하면, 태스크 안에서 블로킹이 일어나면 이 전제가 깨진다. 워커가 join()이나 ManagedBlocker에서 막히면 ForkJoinPool은 병렬성을 유지하기 위해 보상 스레드(compensation thread) 를 추가로 만든다. parallelism=4인 풀에서 12개 태스크를 블로킹시키고 getPoolSize()를 찍어보면 13이 나온다. 기본 상한은 DEFAULT_COMMON_MAX_SPARES = 256이다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;h4 data-ke-size="size20"&gt;&lt;b&gt;분할 정복(divide and conquer) 알고리즘&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size="size16"&gt;대부분의 compute 메서드 구현은 다음과 같은 의사코드 형식을 유지한다. 그리고 이는 우리에게 익숙한 분할 정복(divide and conquer) 알고리즘의 병렬화 버전이다.&lt;/p&gt;
&lt;pre class="dust"&gt;&lt;code&gt;if (태스크 분할이 필요한 경우) {
    태스크를 두 서브태스크로 분할
    태스크가 다시 서브태스크로 분할되도록 이 메서드를 재귀적으로 호출함
    모든 서브태스크의 연산이 완료될 때까지 기다림
    각 서브태스크의 결과를 합침
} else {
    순차적으로 태스크 계산
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt;따라서 Fork/Join 프레임워크의 활용에 적합한 문제 역시, 분할할 수 있는 문제라고 말할 수 있다. ForkJoinTask의 &lt;a href="https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/concurrent/ForkJoinTask.html"&gt;javadoc&lt;/a&gt;은 이 제약을 다음과 같이 명시한다.&lt;/p&gt;
&lt;blockquote data-ke-style="style1"&gt;
&lt;p data-ke-size="size16"&gt;The efficiency of ForkJoinTasks stems from a set of restrictions ... reflecting their main use as computational tasks &lt;b&gt;calculating pure functions or operating on purely isolated objects&lt;/b&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt;문제가 잘 축소될 가능성이 있는지 판단하는 실용적인 방법 중 하나는, 문제와 그 하위 작업에 다음 체크리스트를 적용하는 것이다.&lt;/p&gt;
&lt;ul style="list-style-type: disc;" data-ke-list-type="disc"&gt;
&lt;li&gt;하위 작업들이 명시적인 협력이나 동기화 없이 작동할 수 있는가?&lt;/li&gt;
&lt;li&gt;하위 작업들이 순수 함수인가? 즉, 데이터 변경 없이 데이터로부터 값을 계산할 수 있는가?&lt;/li&gt;
&lt;li&gt;하위 작업들에 대한 분할과 정복은 자연스러운가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt;여기에 Oracle의 공식 가이드가 덧붙이는 실무 조건이 두 가지 더 있다.&lt;/p&gt;
&lt;ul style="list-style-type: disc;" data-ke-list-type="disc"&gt;
&lt;li&gt;I/O를 섞지 말 것: "fork/join tasks should operate as 'pure' in-memory algorithms in which no I/O operations come into play"&lt;/li&gt;
&lt;li&gt;너무 잘게 쪼개지 말 것: 태스크 하나가 "프레임워크의 관리 오버헤드를 상쇄할 만큼"의 계산량은 가져야 한다. THRESHOLD가 존재하는 이유다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;h3 data-ke-size="size23"&gt;&lt;b&gt;[ 작업 훔치기(Work Stealing) 알고리즘 ]&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size="size20"&gt;&lt;b&gt;작업 훔치기 알고리즘의 필요성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size="size16"&gt;우리 컴퓨터에 8개의 코어가 존재하고, ForkJoinPool이 위의 IncrementTask 작업을 내부적으로 8개의 서브태스크로 나누어 처리한다고 하자. 이론적으로는 코어 개수만큼 병렬화된 태스크로 작업 부하를 분할하면 모든 CPU 코어에서 태스크를 실행할 것이고, 크기가 같은 각각의 태스크는 같은 시간에 종료될 것이라 생각할 수 있다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;하지만 훨씬 복잡한 시나리오를 다루는 현실에서는 각각의 서브태스크의 작업완료 시간이 크게 달라질 수 있다. 분할 기법이 비효율적이었을 수도 있고, 아니면 예기치 않게 디스크 접근 속도가 저하되었거나 외부 서비스와 협력하는 과정에서 지연이 생길 수 있기 때문이다. 실제로 하나의 서브태스크가 다른 서브태스크에 비해 10배 걸리는 일은 흔하다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;문제는 빨리 끝난 코어들이 놀면서 가장 느린 서브태스크를 기다리는 것이다. 그리고 전체 소요 시간은 가장 느린 서브태스크에 의해 결정된다. 작업을 나누어 처리하지만, 어느 조각이 오래 걸릴지 알 수 없다는 근본 한계가 있는 것이다. Fork/Join 프레임워크는 이러한 문제를 해결하고자 작업 훔치기(Work Stealing) 알고리즘을 구현한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class="imageblock alignCenter" data-ke-mobilestyle="widthOrigin" data-origin-width="1120" data-origin-height="664"&gt;&lt;span data-url="https://blog.kakaocdn.net/dn/lNNR9/dJMcagNJAiP/dRmji67zKJalhiIpCATx4K/img.png" data-phocus="https://blog.kakaocdn.net/dn/lNNR9/dJMcagNJAiP/dRmji67zKJalhiIpCATx4K/img.png"&gt;&lt;img src="https://blog.kakaocdn.net/dn/lNNR9/dJMcagNJAiP/dRmji67zKJalhiIpCATx4K/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlNNR9%2FdJMcagNJAiP%2FdRmji67zKJalhiIpCATx4K%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="1120" height="664" data-origin-width="1120" data-origin-height="664"&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;h4 data-ke-size="size20"&gt;&lt;b&gt;작업 훔치기 알고리즘의 구현&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size="size16"&gt;작업 훔치기(work-stealing)의 발상은 단순하다. 일이 떨어진 스레드가 남의 일감을 가져가게 하여, 실시간으로 부하를 분배하는 것이다. 이를 위해 양쪽 끝에서 작업을 넣고 뺄 수 있는 작업 큐(WorkQueue) 가 필요한데, 덱(deque, double-ended queue) 자료구조를 내부적으로 구현하여 사용한다. 참고로 이는 ForkJoinPool의 내부 클래스일 뿐, 자바 표준 컬렉션 java.util.Deque가 아니다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;base, top 두 인덱스가 작업 큐의 전부다. base는 도둑이, top은 주인이 쓴다.&lt;/p&gt;
&lt;pre class="angelscript"&gt;&lt;code&gt;// JDK 25 기준
static final class WorkQueue {
    final ForkJoinWorkerThread owner;  // 큐의 주인, 외부 제출 큐면 null
    ForkJoinTask&amp;lt;?&amp;gt;[] array;           // 태스크를 담는 배열 (2의 거듭제곱 크기 순환 버퍼)
    int base;                          // 도둑이 훔쳐갈 위치 (poll)
    int top;                           // 주인이 넣고 빼는 위치 (push/pop)
    ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt;모든 작업 큐는 배열의 형태로 ForkJoinPool 내부에서 관리된다. 실제로 작업을 처리하는 워커 스레드(ForkJoinWorkerThread)는 자신이 기본적으로 처리하는 WorkQueue에 대한 참조를 갖는다. 만약 필요하다면 남의 큐에 접근할 수 있도록 ForkJoinPool에 대한 참조도 존재한다.&lt;/p&gt;
&lt;pre class="angelscript"&gt;&lt;code&gt;class ForkJoinPool {
    WorkQueue[] queues;                     // ①  전체 작업 큐
}

class ForkJoinWorkerThread {
    final ForkJoinPool pool;                // ②
    final ForkJoinPool.WorkQueue workQueue; // ③  ← 자기 것
}

class WorkQueue {
    final ForkJoinWorkerThread owner;       // ④  ← 주인으로 되돌아가는 링크
    ForkJoinTask&amp;lt;?&amp;gt;[] array;                // ⑤
}

class ForkJoinTask {
    volatile int status;   // 풀이나 큐를 가리키는 참조는 없다
    volatile Aux aux;      // 완료를 기다리는 스레드 목록, 또는 던져진 예외
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt;이를 도식화된 구조로 살펴보면 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class="imageblock alignCenter" data-ke-mobilestyle="widthOrigin" data-origin-width="1040" data-origin-height="800"&gt;&lt;span data-url="https://blog.kakaocdn.net/dn/TO4pn/dJMcafnJUTf/ePys3qWWXDiqIwP0y6UTb0/img.png" data-phocus="https://blog.kakaocdn.net/dn/TO4pn/dJMcafnJUTf/ePys3qWWXDiqIwP0y6UTb0/img.png"&gt;&lt;img src="https://blog.kakaocdn.net/dn/TO4pn/dJMcafnJUTf/ePys3qWWXDiqIwP0y6UTb0/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTO4pn%2FdJMcafnJUTf%2FePys3qWWXDiqIwP0y6UTb0%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="1040" height="800" data-origin-width="1040" data-origin-height="800"&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;h4 data-ke-size="size20"&gt;&lt;b&gt;최초 제출부터 훔치기까지의 과정&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size="size16"&gt;먼저 최초에 외부 스레드가 작업을 제출하면, 제출큐에 첫 번째 태스크가 적재된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class="imageblock alignCenter" data-ke-mobilestyle="widthOrigin" data-origin-width="1040" data-origin-height="780"&gt;&lt;span data-url="https://blog.kakaocdn.net/dn/6P79P/dJMcah6VWGY/gRx0y70HU6ACUWEOob1ggK/img.png" data-phocus="https://blog.kakaocdn.net/dn/6P79P/dJMcah6VWGY/gRx0y70HU6ACUWEOob1ggK/img.png"&gt;&lt;img src="https://blog.kakaocdn.net/dn/6P79P/dJMcah6VWGY/gRx0y70HU6ACUWEOob1ggK/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6P79P%2FdJMcah6VWGY%2FgRx0y70HU6ACUWEOob1ggK%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="1040" height="780" data-origin-width="1040" data-origin-height="780"&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt;이후에 임의의 워커 스레드가 제출큐의 작업을 가져간다. 여기서 한 가지 짚을 것은, 가져온 태스크를 자기 덱에 넣는 게 아니라 곧바로 실행한다는 점이다. 덱에 쌓이는 것은 그 태스크가 fork()로 만들어낸 자식들이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class="imageblock alignCenter" data-ke-mobilestyle="widthOrigin" data-origin-width="1040" data-origin-height="780"&gt;&lt;span data-url="https://blog.kakaocdn.net/dn/zhYME/dJMcaiLmHJJ/8Jq68YTAdeM7KtIVhzEp4k/img.png" data-phocus="https://blog.kakaocdn.net/dn/zhYME/dJMcaiLmHJJ/8Jq68YTAdeM7KtIVhzEp4k/img.png"&gt;&lt;img src="https://blog.kakaocdn.net/dn/zhYME/dJMcaiLmHJJ/8Jq68YTAdeM7KtIVhzEp4k/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzhYME%2FdJMcaiLmHJJ%2F8Jq68YTAdeM7KtIVhzEp4k%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="1040" height="780" data-origin-width="1040" data-origin-height="780"&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt;그리고 다른 유휴 상태의 워커가 이제 작업을 A의 덱 base로부터 가져와서 자기 자신에게 할당하고 처리한다. 앞에서 본 대로, base에 있는 것은 가장 오래된, 곧 가장 큰 태스크이므로, B는 이것을 다시 쪼개면서 새로운 공급원이 된다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p&gt;&lt;figure class="imageblock alignCenter" data-ke-mobilestyle="widthOrigin" data-origin-width="1040" data-origin-height="780"&gt;&lt;span data-url="https://blog.kakaocdn.net/dn/dw0na2/dJMcahTbqNp/ekpRbnTSrU2eCvFKhEviFK/img.png" data-phocus="https://blog.kakaocdn.net/dn/dw0na2/dJMcahTbqNp/ekpRbnTSrU2eCvFKhEviFK/img.png"&gt;&lt;img src="https://blog.kakaocdn.net/dn/dw0na2/dJMcahTbqNp/ekpRbnTSrU2eCvFKhEviFK/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdw0na2%2FdJMcahTbqNp%2FekpRbnTSrU2eCvFKhEviFK%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="1040" height="780" data-origin-width="1040" data-origin-height="780"&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;h3 data-ke-size="size23"&gt;&lt;b&gt;[ 작업 훔치기 알고리즘의 구현 상세 ]&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size="size20"&gt;&lt;b&gt;왜 덱의 양 끝을 다르게 쓰는가&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size="size16"&gt;양방향 큐를 쓴다고만 하면 절반만 설명한 것이다. 핵심은 주인과 도둑이 서로 반대편 끝을 쓴다는 비대칭에 있다.&lt;/p&gt;
&lt;ul style="list-style-type: disc;" data-ke-list-type="disc"&gt;
&lt;li&gt;주인은 top에서 LIFO(youngest-first) 로 pop 한다&lt;/li&gt;
&lt;li&gt;도둑은 base에서 FIFO(oldest-first) 로 steal 한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class="imageblock alignCenter" data-ke-mobilestyle="widthOrigin" data-origin-width="1040" data-origin-height="528"&gt;&lt;span data-url="https://blog.kakaocdn.net/dn/bhWSIB/dJMcahZZ2Cp/rxwAHECYtjKTeSHSkRxbNk/img.png" data-phocus="https://blog.kakaocdn.net/dn/bhWSIB/dJMcahZZ2Cp/rxwAHECYtjKTeSHSkRxbNk/img.png"&gt;&lt;img src="https://blog.kakaocdn.net/dn/bhWSIB/dJMcahZZ2Cp/rxwAHECYtjKTeSHSkRxbNk/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhWSIB%2FdJMcahZZ2Cp%2FrxwAHECYtjKTeSHSkRxbNk%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="1040" height="528" data-origin-width="1040" data-origin-height="528"&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt;top과 base를 구분하여 쓰는 이유는 다음의 장점들이 있기 때문이다.&lt;/p&gt;
&lt;ol style="list-style-type: decimal;" data-ke-list-type="decimal"&gt;
&lt;li&gt;경합 감소: 주인은 배열의 오른쪽 끝, 도둑은 왼쪽 끝에 접근하여, 큐에 태스크가 어느 정도 쌓여 있는 한 둘은 물리적으로 부딪히지 않음&lt;/li&gt;
&lt;li&gt;큰 덩어리 훔치기 가능: 분할정복은 큰 태스크를 먼저 만들어 쪼개므로, 가장 오래된 태스크가 가장 큼. 여유로운 도둑이 base에서 큰 작업을 가져가면, 이를 다시 분해하면서 새로운 공급원이 될 수 있음. 훔치기 자체가 비싼 연산이므로, 훔치는 횟수를 줄이는 것이 중요함&lt;/li&gt;
&lt;li&gt;캐시 지역성: 주인이 방금 fork한 태스크를 바로 다시 꺼내므로, 그 태스크가 참조하는 데이터가 캐시에 살아있을 확률이 높음&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size="size23"&gt; &lt;/h3&gt;
&lt;h3 data-ke-size="size23"&gt; &lt;/h3&gt;
&lt;h4 data-ke-size="size20"&gt;&lt;b&gt;작업큐 배열의 크기와 인덱스 구분&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size="size16"&gt;ForkJoinPool에서 할당되는 queues[] 배열의 크기는 병렬성(parallelism, 동시에 계산을 진행시키려는 목표 스레드 수)의 &lt;b&gt;약 2배&lt;/b&gt;다. 정확히는 병렬성을 2의 거듭제곱으로 올림한 값의 두 배이고, 여기에 MIN_QUEUES_SIZE = 16이라는 하한이 걸린다. 그래서 병렬성이 8 이하면 배열은 언제나 16칸이다. 왜 이렇게 넉넉히 잡는지 이해하려면 먼저 ForkJoinPool에 작업이 제출되는 방식을 볼 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;ForkJoinPool에 제출되는 작업은 대개 compute 메서드 내부에서 작은 작업으로 세분화되면서 만들어지고, 워커 스레드를 통해 처리된다. 하지만 작업이 최초 제출되는 경우에는 메인 스레드가 이 역할을 맡는다. 메인 스레드는 워커 스레드가 아니라서 자기 덱이 없는데, ForkJoinPool 어딘가에는 태스크를 제출해야 한다.&lt;/p&gt;
&lt;pre class="arduino"&gt;&lt;code&gt;public static void main(String[] args) {
    ForkJoinPool.commonPool().invoke(new IncrementTask(arr, 0, 1000));
    //          ↑ main 스레드. 워커가 아님
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt;따라서 ForkJoinPool은 작업을 2가지로 구분하여 다루어야 한다.&lt;/p&gt;
&lt;ul style="list-style-type: disc;" data-ke-list-type="disc"&gt;
&lt;li&gt;외부 스레드가 제출한 작업: 외부 스레드가 작업을 제출하면, 유휴 상태의 워커가 이를 가로채고 요청을 처리함&lt;/li&gt;
&lt;li&gt;워커 스레드가 처리하는 작업: 워커는 자기가 처리하는 작업들을 관리하고, 필요 시 다른 워커가 접근하여 작업 훔치기를 할 수 있도록 함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt;따라서 ForkJoinPool은 WorkQueue 배열을 인덱스 기준으로 두 종류로 나누어 사용한다.&lt;/p&gt;
&lt;ul style="list-style-type: disc;" data-ke-list-type="disc"&gt;
&lt;li&gt;짝수 인덱스: 외부 스레드의 제출큐 (shared queue)&lt;/li&gt;
&lt;li&gt;홀수 인덱스: 워커의 작업큐의 덱 (worker queue)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt;실제로 parallelism=8인 풀에 외부 스레드 8개가 작업을 제출한 뒤 배열을 들여다보면 정확히 이 규칙대로 채워진다.&lt;/p&gt;
&lt;pre class="angelscript"&gt;&lt;code&gt;parallelism=8, queues.length=16

[ 0] 제출큐(shared)    [ 1] 워커덱 owner=ForkJoinPool-1-worker-6
[ 2] 제출큐(shared)    [ 3] 워커덱 owner=ForkJoinPool-1-worker-3
[ 4] 제출큐(shared)    [ 5] 워커덱 owner=ForkJoinPool-1-worker-1
[ 6] 제출큐(shared)    [ 7] 워커덱 owner=ForkJoinPool-1-worker-4
[ 8] 제출큐(shared)    [ 9] 워커덱 owner=ForkJoinPool-1-worker-5
[10] 제출큐(shared)    [11] 워커덱 owner=ForkJoinPool-1-worker-7
[12] 제출큐(shared)    [13] 워커덱 owner=ForkJoinPool-1-worker-8
[14] 제출큐(shared)    [15] 워커덱 owner=ForkJoinPool-1-worker-2
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt;인덱스를 짝/홀로 나누어 사용하는 방식은 다음과 같은 부가적인 이점이 있다.&lt;/p&gt;
&lt;ul style="list-style-type: disc;" data-ke-list-type="disc"&gt;
&lt;li&gt;스캔 로직의 간소화: 워커가 일감을 찾을 때는 배열을 무작위 시작점부터 훑는데, 둘을 별도 배열로 분리했다면 "제출큐 배열을 먼저 보고, 없으면 작업큐 배열을 보고" 하는 이중 로직이 필요해짐&lt;/li&gt;
&lt;li&gt;작업 구분의 간소화: 짝/홀 인덱스 여부만으로 작업 종류를 구분할 수 있음&lt;/li&gt;
&lt;li&gt;배열 확장의 용이함: 작업큐 배열은 워커가 늘어나면 두 배로 확장되는데, 구간으로 나누지 않았으므로 경계값을 다시 계산하거나 큐를 옮길 일이 없음. 워커 큐는 홀수 인덱스를 그대로 유지함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;h4 data-ke-size="size20"&gt;&lt;b&gt;그런데 왜 제출큐도 이만큼이나 필요한가?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size="size16"&gt;작업을 제출하는 스레드의 수는 워커의 수와 무관하다. 스프링 애플리케이션과 같이 멀티 스레드 기반으로 요청을 처리하는 경우에는 작업을 동시 제출하는 스레드가 200개를 넘을 수도 있다. 그런데도 슬롯이 이 정도면 충분한 이유는, 애초에 충돌을 없애려는 설계가 아니기 때문이다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;ForkJoinPool은 제출 스레드를 슬롯에 해싱해서 배치한다. 해시 값으로는 ThreadLocalRandom의 probe를 쓰고, 충돌하면 probe를 재배치해서 다른 슬롯으로 옮겨간다. 즉 이 설계의 전제는 "동시 제출자가 슬롯 수보다 적다"가 아니라, "충돌은 나겠지만 싸고 빠르게 우회할 수 있다" 이다. 따라서 슬롯 하나를 두고 다투지 않는다. 큐가 잠겨 있으면 풀리기를 기다리는 대신 probe를 굴려 곧바로 다른 슬롯으로 옮겨간다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;여기에 두 가지 실용적인 사실이 더해진다.&lt;/p&gt;
&lt;ul style="list-style-type: disc;" data-ke-list-type="disc"&gt;
&lt;li&gt;제출큐는 배열에 슬롯만 잡혀 있고, 실제 WorkQueue 객체는 필요할 때 만들어진다(lazy). 200개 스레드가 몰려도 슬롯 수만큼만 만들어진다.&lt;/li&gt;
&lt;li&gt;제출은 push 한 번으로 끝나는 짧은 연산이다. 실제 계산 시간은 워커 쪽에서 소비되므로, 제출 슬롯이 병목이 되는 구간은 매우 짧다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;h3 data-ke-size="size23"&gt;&lt;b&gt;[ 실무에서 알아두면 좋은 것들 ]&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size="size20"&gt;&lt;b&gt;parallel stream을 위한 commonPool&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size="size16"&gt;ForkJoinPool.commonPool()은 애플리케이션 전체가 공유하는 정적 풀이고, parallel stream이 바로 이 풀을 쓴다. 병렬성은 다음과 같이 결정된다. 즉 8코어 머신이면 워커는 7개다(제출 스레드가 하나 거들 것을 감안한 값이다).&lt;/p&gt;
&lt;pre class="reasonml"&gt;&lt;code&gt;pc = Math.max(1, Runtime.getRuntime().availableProcessors() - 1);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt;이로 인해 parallel stream 안에서 I/O 처리 같은 블로킹 작업이 있으면, 한 곳의 블로킹이 전역에 퍼진다. commonPool 워커가 묶이고, 관계없는 다른 parallel stream이 함께 느려지는 것이다. 격리가 필요하면 전용 ForkJoinPool을 만들어 그 안에서 submit()하도록 하자. 만약 튜닝이 필요하다면, java.util.concurrent.ForkJoinPool.common.parallelism 시스템 프로퍼티를 설정해주어야 한다. 참고로 해당 값을 0으로 설정하면, 워커 스레드를 아예 만들지 않고 제출한 스레드가 직접 실행하는 caller-runs 모드가 된다(getParallelism()은 여전히 1을 보고한다).&lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;h4 data-ke-size="size20"&gt;&lt;b&gt;블로킹이 불가피하다면 ManagedBlocker&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size="size16"&gt;앞서 "I/O를 섞지 말라"고 했지만 현실에서 피할 수 없다면, 최소한 풀에게 알려줘야 한다. ForkJoinPool.ManagedBlocker로 감싸면 풀이 블로킹을 인지하고 보상 스레드를 만들어 병렬성을 유지할 수 있다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;h4 data-ke-size="size20"&gt;&lt;b&gt;asyncMode, 그리고 가상 스레드&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size="size16"&gt;생성자의 asyncMode 플래그를 켜면 주인도 자기 덱을 FIFO로 처리한다. 조인하지 않는 이벤트성 태스크에 적합한 모드다. 그리고 이 모드에는 오늘날 가장 중요한 사용처가 하나 있다. Java 21 가상 스레드의 기본 스케줄러가 바로 asyncMode ForkJoinPool이다.&lt;/p&gt;
&lt;pre class="reasonml"&gt;&lt;code&gt;// java.lang.VirtualThread#createDefaultScheduler
boolean asyncMode = true; // FIFO
return new ForkJoinPool(parallelism, factory, handler, asyncMode, ...);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt;가상 스레드의 컨티뉴에이션이 곧 이 풀에 제출되는 태스크다. 즉 Fork/Join은 "옛날에 나온 병렬 계산용 도구"가 아니라, 지금 가상 스레드의 기반이 되었다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt;참고 자료&lt;/p&gt;
&lt;ul style="list-style-type: disc;" data-ke-list-type="disc"&gt;
&lt;li&gt;Doug Lea, "A Java Fork/Join Framework", ACM Java Grande 2000 — &lt;a href="https://gee.cs.oswego.edu/dl/papers/fj.pdf"&gt;https://gee.cs.oswego.edu/dl/papers/fj.pdf&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;OpenJDK ForkJoinPool.java 설계 주석 (JDK 8 / 17 / 21 / 25) — 짝·홀 인덱스, 제출큐 해싱, LIFO/FIFO 규칙&lt;/li&gt;
&lt;li&gt;OpenJDK ForkJoinPool.java (JDK 7) — 단일 제출큐 시절의 초기 구현&lt;/li&gt;
&lt;li&gt;ForkJoinPool / ForkJoinTask / RecursiveAction javadoc&lt;/li&gt;
&lt;li&gt;java.lang.VirtualThread#createDefaultScheduler (JDK 21+)&lt;/li&gt;
&lt;li&gt;Blumofe &amp;amp; Leiserson, "Scheduling Multithreaded Computations by Work Stealing", JACM 1999 — 작업 훔치기의 이론적 최적성&lt;/li&gt;
&lt;li&gt;Oracle, "Fork and Join: Java Can Excel at Painless Parallel Programming Too!" — &lt;a href="https://www.oracle.com/technical-resources/articles/java/fork-join.html"&gt;https://www.oracle.com/technical-resources/articles/java/fork-join.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Herb Sutter, "The Free Lunch Is Over", Dr. Dobb's Journal, 2005 — 클럭 한계와 멀티코어 전환&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;/body&gt;&lt;/html&gt;
</content>
    <id>https://mangkyu.tistory.com/475</id>
    <link href="https://mangkyu.tistory.com/475"/>
    <summary type="html">&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size="size26"&gt;&lt;b&gt;&lt;span style="color: #f15f5f;"&gt;&lt;span&gt;&lt;span&gt;1. 멀티 코어 기반의 처리량 극대화를 위한 Fork/Join 프레임워크&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable="false" data-ke-type="horizontalRule" data-ke-style="style5" /&gt;
&lt;h3 data-ke-size="size23"&gt;&lt;b&gt;[ 하드웨어의 발전과 기존 방식의 한계 ]&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size="size16"&gt;Fork/Join 프레임워크를 이해하려면 하드웨어의 발전에 대해서도 이해할 필요가 있다. 초기의 하드웨어는 1개의 CPU 안에 1개의 코어만 담겨있는 단일 코어 구조였다. 따라서 해당 코어의 처리 성능을 높이는 사실상 유일한 방법은 클럭 속도를 올리는 것이었다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;하지만 2000년대 단일 코어 클럭 속도 상승이 전력과 발열 한계에 부딪히면서 CPU 제조사들은 클럭 대신 코어 수를 늘리는 방향으로 선회했다. 이제는 여러 개의 코어를 활용할 수 있게 되었고, 멀티 코어 환경에서 컴퓨팅 리소스를 잘 활용하기 위한 도구들이 필요해졌다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;2004년, ThreadPoolExecutor와 Executors 같은 자바의 동시성 도구들이 Java 5에 들어올 당시에는 "많은 독립적인 요청을 여러 스레드가 나눠 처리한다"는 전제로 설계됐다. 하지만 이제는 코어를 최대로 활용할 수 있게, 1개의 큰 계산을 서로 다른 코어에서 나누어 처리해야 할 필요가 생겼다. 위와 같은 문제를 해결하기 위해, Java 7에 Fork/Join 프레임워크가 등장하게 됐다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size="size23"&gt;&lt;b&gt;[ Fork/Join 프레임워크 ]&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size="size20"&gt;&lt;b&gt;ForkJoinPool&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size="size16"&gt;Fork/Join 프레임워크는 1개의 큰 작업을 재귀적인 작은 작업으로 분할하여 여러 코어가 나누어 처리하고, 각각의 서브태스크 작업 결과를 합쳐서 전체 결과를 만들도록 설계되었다. Fork/Join 프레임워크는 이를 위해 서브태스크를 스레드 풀의 워커 스레드에 분산 할당시키는 ForkJoinPool을 제공한다. 해당 클래스는 스레드 풀을 활용해 작업 실행과 관리를 하는 ExecutorService 인터페이스를 구현한다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;즉, Fork/Join 프레임워크는 ForkJoinPool을 활용해 사용자에게 보이지 않는 스레드 풀에서 작업을 자동으로 스케줄링하는 것이다. ForkJoinPool은 Thread보다 "작은" 동시성 단위인 ForkJoinTask를 처리한다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size="size20"&gt;&lt;b&gt;ForkJoinTask&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size="size16"&gt;우리는 보통 "동시에 실행되는 일 = 스레드"라고 생각한다. 100개를 동시에 처리하려면 스레드 100개가 필요하다고 여기는 것이다. 하지만 자바의 스레드는 무겁다. java.lang.Thread(플랫폼 스레드)는 OS 커널 스레드와 1:1로 매핑된다. 임의의 시점에 블로킹할 수 있고, 인터럽트를 받을 수 있고, 우선순위를 가지며, 각자 스택을 갖고 OS 스케줄러의 관리를 받는다. 따라서 자바 스레드는 무겁고, 플랫폼에 따라 객체당 &lt;b&gt;1~2MB가 요구&lt;/b&gt;되고, 생성과 컨텍스트 스위치마다 커널이 개입한다. 수십 개라면 문제없지만, 분할정복처럼 작업을 수만 개로 쪼개는 상황에서는 스레드 생성 비용이 그 작업의 계산 시간보다 커진다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;그래서 Fork/Join 프레임워크는 실행 단위를 스레드에서 떼어내고자 ForkJoinTask라는 규약을 고안했다. ForkJoinTask는 추상 클래스로, new IncrementTask(arr, 0, 1000)와 같이 처리할 정보만 담은 가벼운 객체를 만들 수 있게 한다. 이는 스레드와 달리 단순 자바 객체이므로 수만 개를 만들어도 부담이 없다.&lt;br /&gt;하지만 우리가 ForkJoinTask를 직접 상속할 일은 거의 없다. ForkJoinTask는 실행 규약만을 갖는 추상 클래스이고, 실제로는 셋 중 하나를 골라 활용하게 된다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;이름&amp;nbsp;&lt;/p&gt;
&lt;table style="border-collapse: collapse; width: 100%;" border="1" data-ke-align="alignLeft" data-ke-style="style4"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style="color: #333333; text-align: start;"&gt;클래스&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;언제 쓰나&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RecursiveTask&amp;lt;V&amp;gt;&lt;/td&gt;
&lt;td&gt;결과를 반환할 때 (합계, 최댓값, 정렬 결과)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RecursiveAction&lt;/td&gt;
&lt;td&gt;결과 없이 부수 효과만 (제자리 정렬, 이미지 필터)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CountedCompleter&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td&gt;join 없이 완료 콜백으로 결과를 전파할 때&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;배열의 모든 원소를 1씩 증가시키는 예제 코드로 살펴보자. 이 경우에는 처리 결과가 필요 없기에 RecursiveAction을 활용하면 되고, 요청을 서브태스크로 분할하고 더 이상 분할할 수 없을 때 개별 서브태스크를 처리하는 compute() 메소드를 구현하면 된다. 나머지, 즉 태스크를 어느 스레드가 언제 실행할지는 ForkJoinPool이 알아서 정한다.&lt;/p&gt;
&lt;pre class="angelscript"&gt;&lt;code&gt;class IncrementTask extends RecursiveAction {
    final long[] array;
    final int lo, hi;

    IncrementTask(long[] array, int lo, int hi) {
        this.array = array;
        this.lo = lo;
        this.hi = hi;
    }

    protected void compute() {
        if (hi - lo &amp;lt; THRESHOLD) {
            for (int i = lo; i &amp;lt; hi; ++i)
                array[i]++;
        }
        else {
            int mid = (lo + hi) &amp;gt;&amp;gt;&amp;gt; 1;
            invokeAll(
                new IncrementTask(array, lo, mid),
                new IncrementTask(array, mid, hi)
            );
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;실제 실행은 별도로 존재하는 소수의 워커 스레드가 담당한다. 이들이 태스크 객체를 주워다 compute()를 호출한다. 즉, 작업의 정의(태스크)와 실행(스레드)이 분리된 구조이다. 이를 통해 블로킹 작업이 없는 순수 계산 태스크라면 작업을 아무리 잘게 쪼개도 스레드 수는 코어 수 그대로 유지할 수 있다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;반대로 말하면, 태스크 안에서 블로킹이 일어나면 이 전제가 깨진다. 워커가 join()이나 ManagedBlocker에서 막히면 ForkJoinPool은 병렬성을 유지하기 위해 보상 스레드(compensation thread) 를 추가로 만든다. parallelism=4인 풀에서 12개 태스크를 블로킹시키고 getPoolSize()를 찍어보면 13이 나온다. 기본 상한은 DEFAULT_COMMON_MAX_SPARES = 256이다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size="size20"&gt;&lt;b&gt;분할 정복(divide and conquer) 알고리즘&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size="size16"&gt;대부분의 compute 메서드 구현은 다음과 같은 의사코드 형식을 유지한다. 그리고 이는 우리에게 익숙한 분할 정복(divide and conquer) 알고리즘의 병렬화 버전이다.&lt;/p&gt;
&lt;pre class="dust"&gt;&lt;code&gt;if (태스크 분할이 필요한 경우) {
    태스크를 두 서브태스크로 분할
    태스크가 다시 서브태스크로 분할되도록 이 메서드를 재귀적으로 호출함
    모든 서브태스크의 연산이 완료될 때까지 기다림
    각 서브태스크의 결과를 합침
} else {
    순차적으로 태스크 계산
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;따라서 Fork/Join 프레임워크의 활용에 적합한 문제 역시, 분할할 수 있는 문제라고 말할 수 있다. ForkJoinTask의 &lt;a href="https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/concurrent/ForkJoinTask.html"&gt;javadoc&lt;/a&gt;은 이 제약을 다음과 같이 명시한다.&lt;/p&gt;
&lt;blockquote data-ke-style="style1"&gt;
&lt;p data-ke-size="size16"&gt;The efficiency of ForkJoinTasks stems from a set of restrictions ... reflecting their main use as computational tasks &lt;b&gt;calculating pure functions or operating on purely isolated objects&lt;/b&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;문제가 잘 축소될 가능성이 있는지 판단하는 실용적인 방법 중 하나는, 문제와 그 하위 작업에 다음 체크리스트를 적용하는 것이다.&lt;/p&gt;
&lt;ul style="list-style-type: disc;" data-ke-list-type="disc"&gt;
&lt;li&gt;하위 작업들이 명시적인 협력이나 동기화 없이 작동할 수 있는가?&lt;/li&gt;
&lt;li&gt;하위 작업들이 순수 함수인가? 즉, 데이터 변경 없이 데이터로부터 값을 계산할 수 있는가?&lt;/li&gt;
&lt;li&gt;하위 작업들에 대한 분할과 정복은 자연스러운가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;여기에 Oracle의 공식 가이드가 덧붙이는 실무 조건이 두 가지 더 있다.&lt;/p&gt;
&lt;ul style="list-style-type: disc;" data-ke-list-type="disc"&gt;
&lt;li&gt;I/O를 섞지 말 것: "fork/join tasks should operate as 'pure' in-memory algorithms in which no I/O operations come into play"&lt;/li&gt;
&lt;li&gt;너무 잘게 쪼개지 말 것: 태스크 하나가 "프레임워크의 관리 오버헤드를 상쇄할 만큼"의 계산량은 가져야 한다. THRESHOLD가 존재하는 이유다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size="size23"&gt;&lt;b&gt;[ 작업 훔치기(Work Stealing) 알고리즘 ]&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size="size20"&gt;&lt;b&gt;작업 훔치기 알고리즘의 필요성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size="size16"&gt;우리 컴퓨터에 8개의 코어가 존재하고, ForkJoinPool이 위의 IncrementTask 작업을 내부적으로 8개의 서브태스크로 나누어 처리한다고 하자. 이론적으로는 코어 개수만큼 병렬화된 태스크로 작업 부하를 분할하면 모든 CPU 코어에서 태스크를 실행할 것이고, 크기가 같은 각각의 태스크는 같은 시간에 종료될 것이라 생각할 수 있다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;하지만 훨씬 복잡한 시나리오를 다루는 현실에서는 각각의 서브태스크의 작업완료 시간이 크게 달라질 수 있다. 분할 기법이 비효율적이었을 수도 있고, 아니면 예기치 않게 디스크 접근 속도가 저하되었거나 외부 서비스와 협력하는 과정에서 지연이 생길 수 있기 때문이다. 실제로 하나의 서브태스크가 다른 서브태스크에 비해 10배 걸리는 일은 흔하다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;문제는 빨리 끝난 코어들이 놀면서 가장 느린 서브태스크를 기다리는 것이다. 그리고 전체 소요 시간은 가장 느린 서브태스크에 의해 결정된다. 작업을 나누어 처리하지만, 어느 조각이 오래 걸릴지 알 수 없다는 근본 한계가 있는 것이다. Fork/Join 프레임워크는 이러한 문제를 해결하고자 작업 훔치기(Work Stealing) 알고리즘을 구현한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="1120" data-origin-height="664"&gt;&lt;span data-url="https://blog.kakaocdn.net/dn/lNNR9/dJMcagNJAiP/dRmji67zKJalhiIpCATx4K/img.png" data-phocus="https://blog.kakaocdn.net/dn/lNNR9/dJMcagNJAiP/dRmji67zKJalhiIpCATx4K/img.png"&gt;&lt;img src="https://blog.kakaocdn.net/dn/lNNR9/dJMcagNJAiP/dRmji67zKJalhiIpCATx4K/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlNNR9%2FdJMcagNJAiP%2FdRmji67zKJalhiIpCATx4K%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="1120" height="664" data-origin-width="1120" data-origin-height="664"/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size="size20"&gt;&lt;b&gt;작업 훔치기 알고리즘의 구현&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size="size16"&gt;작업 훔치기(work-stealing)의 발상은 단순하다. 일이 떨어진 스레드가 남의 일감을 가져가게 하여, 실시간으로 부하를 분배하는 것이다. 이를 위해 양쪽 끝에서 작업을 넣고 뺄 수 있는 작업 큐(WorkQueue) 가 필요한데, 덱(deque, double-ended queue) 자료구조를 내부적으로 구현하여 사용한다. 참고로 이는 ForkJoinPool의 내부 클래스일 뿐, 자바 표준 컬렉션 java.util.Deque가 아니다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;base, top 두 인덱스가 작업 큐의 전부다. base는 도둑이, top은 주인이 쓴다.&lt;/p&gt;
&lt;pre class="angelscript"&gt;&lt;code&gt;// JDK 25 기준
static final class WorkQueue {
    final ForkJoinWorkerThread owner;  // 큐의 주인, 외부 제출 큐면 null
    ForkJoinTask&amp;lt;?&amp;gt;[] array;           // 태스크를 담는 배열 (2의 거듭제곱 크기 순환 버퍼)
    int base;                          // 도둑이 훔쳐갈 위치 (poll)
    int top;                           // 주인이 넣고 빼는 위치 (push/pop)
    ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;모든 작업 큐는 배열의 형태로 ForkJoinPool 내부에서 관리된다. 실제로 작업을 처리하는 워커 스레드(ForkJoinWorkerThread)는 자신이 기본적으로 처리하는 WorkQueue에 대한 참조를 갖는다. 만약 필요하다면 남의 큐에 접근할 수 있도록 ForkJoinPool에 대한 참조도 존재한다.&lt;/p&gt;
&lt;pre class="angelscript"&gt;&lt;code&gt;class ForkJoinPool {
    WorkQueue[] queues;                     // ①  전체 작업 큐
}

class ForkJoinWorkerThread {
    final ForkJoinPool pool;                // ②
    final ForkJoinPool.WorkQueue workQueue; // ③  &amp;larr; 자기 것
}

class WorkQueue {
    final ForkJoinWorkerThread owner;       // ④  &amp;larr; 주인으로 되돌아가는 링크
    ForkJoinTask&amp;lt;?&amp;gt;[] array;                // ⑤
}

class ForkJoinTask {
    volatile int status;   // 풀이나 큐를 가리키는 참조는 없다
    volatile Aux aux;      // 완료를 기다리는 스레드 목록, 또는 던져진 예외
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;이를 도식화된 구조로 살펴보면 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="1040" data-origin-height="800"&gt;&lt;span data-url="https://blog.kakaocdn.net/dn/TO4pn/dJMcafnJUTf/ePys3qWWXDiqIwP0y6UTb0/img.png" data-phocus="https://blog.kakaocdn.net/dn/TO4pn/dJMcafnJUTf/ePys3qWWXDiqIwP0y6UTb0/img.png"&gt;&lt;img src="https://blog.kakaocdn.net/dn/TO4pn/dJMcafnJUTf/ePys3qWWXDiqIwP0y6UTb0/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTO4pn%2FdJMcafnJUTf%2FePys3qWWXDiqIwP0y6UTb0%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="1040" height="800" data-origin-width="1040" data-origin-height="800"/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size="size20"&gt;&lt;b&gt;최초 제출부터 훔치기까지의 과정&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size="size16"&gt;먼저 최초에 외부 스레드가 작업을 제출하면, 제출큐에 첫 번째 태스크가 적재된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="1040" data-origin-height="780"&gt;&lt;span data-url="https://blog.kakaocdn.net/dn/6P79P/dJMcah6VWGY/gRx0y70HU6ACUWEOob1ggK/img.png" data-phocus="https://blog.kakaocdn.net/dn/6P79P/dJMcah6VWGY/gRx0y70HU6ACUWEOob1ggK/img.png"&gt;&lt;img src="https://blog.kakaocdn.net/dn/6P79P/dJMcah6VWGY/gRx0y70HU6ACUWEOob1ggK/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6P79P%2FdJMcah6VWGY%2FgRx0y70HU6ACUWEOob1ggK%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="1040" height="780" data-origin-width="1040" data-origin-height="780"/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;이후에 임의의 워커 스레드가 제출큐의 작업을 가져간다. 여기서 한 가지 짚을 것은, 가져온 태스크를 자기 덱에 넣는 게 아니라 곧바로 실행한다는 점이다. 덱에 쌓이는 것은 그 태스크가 fork()로 만들어낸 자식들이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="1040" data-origin-height="780"&gt;&lt;span data-url="https://blog.kakaocdn.net/dn/zhYME/dJMcaiLmHJJ/8Jq68YTAdeM7KtIVhzEp4k/img.png" data-phocus="https://blog.kakaocdn.net/dn/zhYME/dJMcaiLmHJJ/8Jq68YTAdeM7KtIVhzEp4k/img.png"&gt;&lt;img src="https://blog.kakaocdn.net/dn/zhYME/dJMcaiLmHJJ/8Jq68YTAdeM7KtIVhzEp4k/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzhYME%2FdJMcaiLmHJJ%2F8Jq68YTAdeM7KtIVhzEp4k%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="1040" height="780" data-origin-width="1040" data-origin-height="780"/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;그리고 다른 유휴 상태의 워커가 이제 작업을 A의 덱 base로부터 가져와서 자기 자신에게 할당하고 처리한다. 앞에서 본 대로, base에 있는 것은 가장 오래된, 곧 가장 큰 태스크이므로, B는 이것을 다시 쪼개면서 새로운 공급원이 된다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="1040" data-origin-height="780"&gt;&lt;span data-url="https://blog.kakaocdn.net/dn/dw0na2/dJMcahTbqNp/ekpRbnTSrU2eCvFKhEviFK/img.png" data-phocus="https://blog.kakaocdn.net/dn/dw0na2/dJMcahTbqNp/ekpRbnTSrU2eCvFKhEviFK/img.png"&gt;&lt;img src="https://blog.kakaocdn.net/dn/dw0na2/dJMcahTbqNp/ekpRbnTSrU2eCvFKhEviFK/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdw0na2%2FdJMcahTbqNp%2FekpRbnTSrU2eCvFKhEviFK%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="1040" height="780" data-origin-width="1040" data-origin-height="780"/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size="size23"&gt;&lt;b&gt;[ 작업 훔치기 알고리즘의 구현 상세 ]&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size="size20"&gt;&lt;b&gt;왜 덱의 양 끝을 다르게 쓰는가&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size="size16"&gt;양방향 큐를 쓴다고만 하면 절반만 설명한 것이다. 핵심은 주인과 도둑이 서로 반대편 끝을 쓴다는 비대칭에 있다.&lt;/p&gt;
&lt;ul style="list-style-type: disc;" data-ke-list-type="disc"&gt;
&lt;li&gt;주인은 top에서 LIFO(youngest-first) 로 pop 한다&lt;/li&gt;
&lt;li&gt;도둑은 base에서 FIFO(oldest-first) 로 steal 한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-origin-width="1040" data-origin-height="528"&gt;&lt;span data-url="https://blog.kakaocdn.net/dn/bhWSIB/dJMcahZZ2Cp/rxwAHECYtjKTeSHSkRxbNk/img.png" data-phocus="https://blog.kakaocdn.net/dn/bhWSIB/dJMcahZZ2Cp/rxwAHECYtjKTeSHSkRxbNk/img.png"&gt;&lt;img src="https://blog.kakaocdn.net/dn/bhWSIB/dJMcahZZ2Cp/rxwAHECYtjKTeSHSkRxbNk/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhWSIB%2FdJMcahZZ2Cp%2FrxwAHECYtjKTeSHSkRxbNk%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="1040" height="528" data-origin-width="1040" data-origin-height="528"/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;top과 base를 구분하여 쓰는 이유는 다음의 장점들이 있기 때문이다.&lt;/p&gt;
&lt;ol style="list-style-type: decimal;" data-ke-list-type="decimal"&gt;
&lt;li&gt;경합 감소: 주인은 배열의 오른쪽 끝, 도둑은 왼쪽 끝에 접근하여, 큐에 태스크가 어느 정도 쌓여 있는 한 둘은 물리적으로 부딪히지 않음&lt;/li&gt;
&lt;li&gt;큰 덩어리 훔치기 가능: 분할정복은 큰 태스크를 먼저 만들어 쪼개므로, 가장 오래된 태스크가 가장 큼. 여유로운 도둑이 base에서 큰 작업을 가져가면, 이를 다시 분해하면서 새로운 공급원이 될 수 있음. 훔치기 자체가 비싼 연산이므로, 훔치는 횟수를 줄이는 것이 중요함&lt;/li&gt;
&lt;li&gt;캐시 지역성: 주인이 방금 fork한 태스크를 바로 다시 꺼내므로, 그 태스크가 참조하는 데이터가 캐시에 살아있을 확률이 높음&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size="size23"&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size="size23"&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h4 data-ke-size="size20"&gt;&lt;b&gt;작업큐 배열의 크기와 인덱스 구분&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size="size16"&gt;ForkJoinPool에서 할당되는 queues[] 배열의 크기는 병렬성(parallelism, 동시에 계산을 진행시키려는 목표 스레드 수)의 &lt;b&gt;약 2배&lt;/b&gt;다. 정확히는 병렬성을 2의 거듭제곱으로 올림한 값의 두 배이고, 여기에 MIN_QUEUES_SIZE = 16이라는 하한이 걸린다. 그래서 병렬성이 8 이하면 배열은 언제나 16칸이다. 왜 이렇게 넉넉히 잡는지 이해하려면 먼저 ForkJoinPool에 작업이 제출되는 방식을 볼 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;ForkJoinPool에 제출되는 작업은 대개 compute 메서드 내부에서 작은 작업으로 세분화되면서 만들어지고, 워커 스레드를 통해 처리된다. 하지만 작업이 최초 제출되는 경우에는 메인 스레드가 이 역할을 맡는다. 메인 스레드는 워커 스레드가 아니라서 자기 덱이 없는데, ForkJoinPool 어딘가에는 태스크를 제출해야 한다.&lt;/p&gt;
&lt;pre class="arduino"&gt;&lt;code&gt;public static void main(String[] args) {
    ForkJoinPool.commonPool().invoke(new IncrementTask(arr, 0, 1000));
    //          &amp;uarr; main 스레드. 워커가 아님
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;따라서 ForkJoinPool은 작업을 2가지로 구분하여 다루어야 한다.&lt;/p&gt;
&lt;ul style="list-style-type: disc;" data-ke-list-type="disc"&gt;
&lt;li&gt;외부 스레드가 제출한 작업: 외부 스레드가 작업을 제출하면, 유휴 상태의 워커가 이를 가로채고 요청을 처리함&lt;/li&gt;
&lt;li&gt;워커 스레드가 처리하는 작업: 워커는 자기가 처리하는 작업들을 관리하고, 필요 시 다른 워커가 접근하여 작업 훔치기를 할 수 있도록 함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;따라서 ForkJoinPool은 WorkQueue 배열을 인덱스 기준으로 두 종류로 나누어 사용한다.&lt;/p&gt;
&lt;ul style="list-style-type: disc;" data-ke-list-type="disc"&gt;
&lt;li&gt;짝수 인덱스: 외부 스레드의 제출큐 (shared queue)&lt;/li&gt;
&lt;li&gt;홀수 인덱스: 워커의 작업큐의 덱 (worker queue)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;실제로 parallelism=8인 풀에 외부 스레드 8개가 작업을 제출한 뒤 배열을 들여다보면 정확히 이 규칙대로 채워진다.&lt;/p&gt;
&lt;pre class="angelscript"&gt;&lt;code&gt;parallelism=8, queues.length=16

[ 0] 제출큐(shared)    [ 1] 워커덱 owner=ForkJoinPool-1-worker-6
[ 2] 제출큐(shared)    [ 3] 워커덱 owner=ForkJoinPool-1-worker-3
[ 4] 제출큐(shared)    [ 5] 워커덱 owner=ForkJoinPool-1-worker-1
[ 6] 제출큐(shared)    [ 7] 워커덱 owner=ForkJoinPool-1-worker-4
[ 8] 제출큐(shared)    [ 9] 워커덱 owner=ForkJoinPool-1-worker-5
[10] 제출큐(shared)    [11] 워커덱 owner=ForkJoinPool-1-worker-7
[12] 제출큐(shared)    [13] 워커덱 owner=ForkJoinPool-1-worker-8
[14] 제출큐(shared)    [15] 워커덱 owner=ForkJoinPool-1-worker-2
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;인덱스를 짝/홀로 나누어 사용하는 방식은 다음과 같은 부가적인 이점이 있다.&lt;/p&gt;
&lt;ul style="list-style-type: disc;" data-ke-list-type="disc"&gt;
&lt;li&gt;스캔 로직의 간소화: 워커가 일감을 찾을 때는 배열을 무작위 시작점부터 훑는데, 둘을 별도 배열로 분리했다면 "제출큐 배열을 먼저 보고, 없으면 작업큐 배열을 보고" 하는 이중 로직이 필요해짐&lt;/li&gt;
&lt;li&gt;작업 구분의 간소화: 짝/홀 인덱스 여부만으로 작업 종류를 구분할 수 있음&lt;/li&gt;
&lt;li&gt;배열 확장의 용이함: 작업큐 배열은 워커가 늘어나면 두 배로 확장되는데, 구간으로 나누지 않았으므로 경계값을 다시 계산하거나 큐를 옮길 일이 없음. 워커 큐는 홀수 인덱스를 그대로 유지함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size="size20"&gt;&lt;b&gt;그런데 왜 제출큐도 이만큼이나 필요한가?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size="size16"&gt;작업을 제출하는 스레드의 수는 워커의 수와 무관하다. 스프링 애플리케이션과 같이 멀티 스레드 기반으로 요청을 처리하는 경우에는 작업을 동시 제출하는 스레드가 200개를 넘을 수도 있다. 그런데도 슬롯이 이 정도면 충분한 이유는, 애초에 충돌을 없애려는 설계가 아니기 때문이다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;ForkJoinPool은 제출 스레드를 슬롯에 해싱해서 배치한다. 해시 값으로는 ThreadLocalRandom의 probe를 쓰고, 충돌하면 probe를 재배치해서 다른 슬롯으로 옮겨간다. 즉 이 설계의 전제는 "동시 제출자가 슬롯 수보다 적다"가 아니라, "충돌은 나겠지만 싸고 빠르게 우회할 수 있다" 이다. 따라서 슬롯 하나를 두고 다투지 않는다. 큐가 잠겨 있으면 풀리기를 기다리는 대신 probe를 굴려 곧바로 다른 슬롯으로 옮겨간다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;여기에 두 가지 실용적인 사실이 더해진다.&lt;/p&gt;
&lt;ul style="list-style-type: disc;" data-ke-list-type="disc"&gt;
&lt;li&gt;제출큐는 배열에 슬롯만 잡혀 있고, 실제 WorkQueue 객체는 필요할 때 만들어진다(lazy). 200개 스레드가 몰려도 슬롯 수만큼만 만들어진다.&lt;/li&gt;
&lt;li&gt;제출은 push 한 번으로 끝나는 짧은 연산이다. 실제 계산 시간은 워커 쪽에서 소비되므로, 제출 슬롯이 병목이 되는 구간은 매우 짧다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size="size23"&gt;&lt;b&gt;[ 실무에서 알아두면 좋은 것들 ]&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size="size20"&gt;&lt;b&gt;parallel stream을 위한 commonPool&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size="size16"&gt;ForkJoinPool.commonPool()은 애플리케이션 전체가 공유하는 정적 풀이고, parallel stream이 바로 이 풀을 쓴다. 병렬성은 다음과 같이 결정된다. 즉 8코어 머신이면 워커는 7개다(제출 스레드가 하나 거들 것을 감안한 값이다).&lt;/p&gt;
&lt;pre class="reasonml"&gt;&lt;code&gt;pc = Math.max(1, Runtime.getRuntime().availableProcessors() - 1);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;이로 인해 parallel stream 안에서 I/O 처리 같은 블로킹 작업이 있으면, 한 곳의 블로킹이 전역에 퍼진다. commonPool 워커가 묶이고, 관계없는 다른 parallel stream이 함께 느려지는 것이다. 격리가 필요하면 전용 ForkJoinPool을 만들어 그 안에서 submit()하도록 하자. 만약 튜닝이 필요하다면, java.util.concurrent.ForkJoinPool.common.parallelism 시스템 프로퍼티를 설정해주어야 한다. 참고로 해당 값을 0으로 설정하면, 워커 스레드를 아예 만들지 않고 제출한 스레드가 직접 실행하는 caller-runs 모드가 된다(getParallelism()은 여전히 1을 보고한다).&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size="size20"&gt;&lt;b&gt;블로킹이 불가피하다면 ManagedBlocker&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size="size16"&gt;앞서 "I/O를 섞지 말라"고 했지만 현실에서 피할 수 없다면, 최소한 풀에게 알려줘야 한다. ForkJoinPool.ManagedBlocker로 감싸면 풀이 블로킹을 인지하고 보상 스레드를 만들어 병렬성을 유지할 수 있다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size="size20"&gt;&lt;b&gt;asyncMode, 그리고 가상 스레드&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size="size16"&gt;생성자의 asyncMode 플래그를 켜면 주인도 자기 덱을 FIFO로 처리한다. 조인하지 않는 이벤트성 태스크에 적합한 모드다. 그리고 이 모드에는 오늘날 가장 중요한 사용처가 하나 있다. Java 21 가상 스레드의 기본 스케줄러가 바로 asyncMode ForkJoinPool이다.&lt;/p&gt;
&lt;pre class="reasonml"&gt;&lt;code&gt;// java.lang.VirtualThread#createDefaultScheduler
boolean asyncMode = true; // FIFO
return new ForkJoinPool(parallelism, factory, handler, asyncMode, ...);
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;가상 스레드의 컨티뉴에이션이 곧 이 풀에 제출되는 태스크다. 즉 Fork/Join은 "옛날에 나온 병렬 계산용 도구"가 아니라, 지금 가상 스레드의 기반이 되었다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;참고 자료&lt;/p&gt;
&lt;ul style="list-style-type: disc;" data-ke-list-type="disc"&gt;
&lt;li&gt;Doug Lea, "A Java Fork/Join Framework", ACM Java Grande 2000 &amp;mdash; &lt;a href="https://gee.cs.oswego.edu/dl/papers/fj.pdf"&gt;https://gee.cs.oswego.edu/dl/papers/fj.pdf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;OpenJDK ForkJoinPool.java 설계 주석 (JDK 8 / 17 / 21 / 25) &amp;mdash; 짝&amp;middot;홀 인덱스, 제출큐 해싱, LIFO/FIFO 규칙&lt;/li&gt;
&lt;li&gt;OpenJDK ForkJoinPool.java (JDK 7) &amp;mdash; 단일 제출큐 시절의 초기 구현&lt;/li&gt;
&lt;li&gt;ForkJoinPool / ForkJoinTask / RecursiveAction javadoc&lt;/li&gt;
&lt;li&gt;java.lang.VirtualThread#createDefaultScheduler (JDK 21+)&lt;/li&gt;
&lt;li&gt;Blumofe &amp;amp; Leiserson, "Scheduling Multithreaded Computations by Work Stealing", JACM 1999 &amp;mdash; 작업 훔치기의 이론적 최적성&lt;/li&gt;
&lt;li&gt;Oracle, "Fork and Join: Java Can Excel at Painless Parallel Programming Too!" &amp;mdash; &lt;a href="https://www.oracle.com/technical-resources/articles/java/fork-join.html"&gt;https://www.oracle.com/technical-resources/articles/java/fork-join.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Herb Sutter, "The Free Lunch Is Over", Dr. Dobb's Journal, 2005 &amp;mdash; 클럭 한계와 멀티코어 전환&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;</summary>
    <title>[Java] 멀티 코어 기반의 처리량 극대화를 위한 Fork/Join 프레임워크</title>
    <updated>2026-09-08T10:00:29+09:00</updated>
    <dc:date>2026-09-08T10:00:29+09:00</dc:date>
  </entry>
  <entry>
    <author>
      <name>행복한 시지프</name>
    </author>
    <content type="html">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;
&lt;html&gt;&lt;body&gt;
&lt;p&gt;&lt;figure class="imageblock alignCenter" data-ke-mobilestyle="widthOrigin" data-filename="menton-2.png" data-origin-width="1080" data-origin-height="708"&gt;&lt;span data-url="https://blog.kakaocdn.net/dn/ebqAdh/dJMcajcsOMd/uOqyL675iT9T3uN0OMjK81/img.png" data-phocus="https://blog.kakaocdn.net/dn/ebqAdh/dJMcajcsOMd/uOqyL675iT9T3uN0OMjK81/img.png"&gt;&lt;img src="https://blog.kakaocdn.net/dn/ebqAdh/dJMcajcsOMd/uOqyL675iT9T3uN0OMjK81/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FebqAdh%2FdJMcajcsOMd%2FuOqyL675iT9T3uN0OMjK81%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="1080" height="708" data-filename="menton-2.png" data-origin-width="1080" data-origin-height="708"&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt;당신에게는 귀인이 있나요?&lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt;귀인은 어떻게 생길까? 은사는 어떻게 생길까? 왜 누구는 은사가 있고, 누구는 없는가? 나는 일생에 걸쳐, 은사가 없다고 말하고 다녔다. 실제로 그렇다. 나의 삶에 크게 영향을 준 사람이 없다. 차라리, 헤르만 헤세, 알베르 카뮈, 서머셋 모옴이 나의 은사라고 말했다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt;나는 그것을 내가 가진 운의 문제, 환경의 문제로 돌렸다. 내가 시골에서 자라서, 내가 마산에서 자라서, 교육환경이 좋지 않은 학교를 나와서, 학교 선생님이 나에게 관심을 가져주지 않아서, 경제학부에는 사람이 많기에 학부생에게 관심을 주지 않아서.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt;최근에 클로브AI 대표 도은욱님의 인터뷰를 보았다. 그의 삶에 귀인이 많았다. BZCF 는 은욱님에게 어떻게 그렇게 귀인이 많냐고 물었다. 모두가 귀인을 만날 수 있는 기회는 동일하다고 했다. 다만, 내가 무엇을 하고 있는지 broadcast 하냐의 차이가 있다고 했다. 내가 줄곧 창업 얘기를 하고 있다면, 주변 사람이 나를 추천해주려고 노력할 거라는 얘기다. 너무 공감이 갔다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt;이걸 들으며 귀인을 만나는 3가지 조건을 생각해보았다. 첫 번째는 한 가지에 몰입하기. 두 번째는 자신이 몰입하고 있다는 사실을 외부에 알리기. 마지막으로 도움을 요청하기&lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt;내 삶에도 여러 귀인의 가능성을 가진 분들이 있었다. 내가 경제학을 공부하던 시절에, 강교수님은 수백명의 학생 중 나를 꼽아서 전액 장학생으로 추천해주셨다. 그 교수님은 나에게 잠깐 귀인이 되었지만, 1년 후 나는 경제학을 놓고 개발자가 되기로 했다. 그렇게 한명의 귀인을 떠나보냈다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt;개발자가 된 이후에도 기회가 많았다. 토스에서 처음 들어간 팀에 승건님이 있었다. 그리고 사수는 엔지니어링 역량이 뛰어나기로 유명한 분이었다. 정말 좋은 환경에 있었다고 생각한다. 하지만 나는 도움을 먼저 요청하고, 귀찮게 하는 것에 익숙하지 않았다. 줄곧 혼자 결정하고, 혼자 공부해서 올라왔다. 이번에도 승건님이 옆에 있지만, 혼자 그로스 해킹을 공부하고, 혼자 승건님의 PO 세션 녹화 강의를 봤다. 뛰어난 사수가 옆에 있었지만, 상대적으로 혼자서 학습했던 시간이 많았다. 그렇게 또 몇 분의 귀인을 떠나보냈다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt; &lt;/p&gt;
&lt;p data-ke-size="size16"&gt;이런 시행착오를 겪으면서, 귀인은 수동적으로 다가오는게 아니라, 능동적으로 만들어간다는 것을 깨달았다. 운이나 환경의 문제를 따지기 전에, 귀인은 도처에 존재한다. 누군가를 귀인으로 섬기기까지의 일은 나에게 여전히 어려운일이다. 그렇지만 조금씩 그 과정을 밟아가고 있는 것 같다. 예전에는 존경하는 사람도 없었지만, 이제는 여럿 있다. 그들에게 도움을 요청할 수도 있다. 나의 어려움을 털어놓을 수도 있다. 중요한 출발을 했다고 생각하고, 이것이 큰 차이를 만드리라 확신한다.&lt;/p&gt;
&lt;/body&gt;&lt;/html&gt;
</content>
    <id>https://happysisyphe.tistory.com/106</id>
    <link href="https://happysisyphe.tistory.com/106"/>
    <summary type="html">&lt;p&gt;&lt;figure class="imageblock alignCenter" data-ke-mobileStyle="widthOrigin" data-filename="menton-2.png" data-origin-width="1080" data-origin-height="708"&gt;&lt;span data-url="https://blog.kakaocdn.net/dn/ebqAdh/dJMcajcsOMd/uOqyL675iT9T3uN0OMjK81/img.png" data-phocus="https://blog.kakaocdn.net/dn/ebqAdh/dJMcajcsOMd/uOqyL675iT9T3uN0OMjK81/img.png"&gt;&lt;img src="https://blog.kakaocdn.net/dn/ebqAdh/dJMcajcsOMd/uOqyL675iT9T3uN0OMjK81/img.png" srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FebqAdh%2FdJMcajcsOMd%2FuOqyL675iT9T3uN0OMjK81%2Fimg.png" onerror="this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';" loading="lazy" width="1080" height="708" data-filename="menton-2.png" data-origin-width="1080" data-origin-height="708"/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;당신에게는 귀인이 있나요?&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;귀인은 어떻게 생길까? 은사는 어떻게 생길까? 왜 누구는 은사가 있고, 누구는 없는가? 나는 일생에 걸쳐, 은사가 없다고 말하고 다녔다. 실제로 그렇다. 나의 삶에 크게 영향을 준 사람이 없다. 차라리, 헤르만 헤세, 알베르 카뮈, 서머셋 모옴이 나의 은사라고 말했다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;나는 그것을 내가 가진 운의 문제, 환경의 문제로 돌렸다. 내가 시골에서 자라서, 내가 마산에서 자라서, 교육환경이 좋지 않은 학교를 나와서, 학교 선생님이 나에게 관심을 가져주지 않아서, 경제학부에는 사람이 많기에 학부생에게 관심을 주지 않아서.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;최근에 클로브AI 대표 도은욱님의 인터뷰를 보았다. 그의 삶에 귀인이 많았다. BZCF 는 은욱님에게 어떻게 그렇게 귀인이 많냐고 물었다. 모두가 귀인을 만날 수 있는 기회는 동일하다고 했다. 다만, 내가 무엇을 하고 있는지 broadcast 하냐의 차이가 있다고 했다. 내가 줄곧 창업 얘기를 하고 있다면, 주변 사람이 나를 추천해주려고 노력할 거라는 얘기다. 너무 공감이 갔다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;이걸 들으며 귀인을 만나는 3가지 조건을 생각해보았다. 첫 번째는 한 가지에 몰입하기. 두 번째는 자신이 몰입하고 있다는 사실을 외부에 알리기. 마지막으로 도움을 요청하기&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;내 삶에도 여러 귀인의 가능성을 가진 분들이 있었다. 내가 경제학을 공부하던 시절에, 강교수님은 수백명의 학생 중 나를 꼽아서 전액 장학생으로 추천해주셨다. 그 교수님은 나에게 잠깐 귀인이 되었지만, 1년 후 나는 경제학을 놓고 개발자가 되기로 했다. 그렇게 한명의 귀인을 떠나보냈다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;개발자가 된 이후에도 기회가 많았다. 토스에서 처음 들어간 팀에 승건님이 있었다. 그리고 사수는 엔지니어링 역량이 뛰어나기로 유명한 분이었다. 정말 좋은 환경에 있었다고 생각한다. 하지만 나는 도움을 먼저 요청하고, 귀찮게 하는 것에 익숙하지 않았다. 줄곧 혼자 결정하고, 혼자 공부해서 올라왔다. 이번에도 승건님이 옆에 있지만, 혼자 그로스 해킹을 공부하고, 혼자 승건님의 PO 세션 녹화 강의를 봤다. 뛰어난 사수가 옆에 있었지만, 상대적으로 혼자서 학습했던 시간이 많았다. 그렇게 또 몇 분의 귀인을 떠나보냈다.&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size="size16"&gt;이런 시행착오를 겪으면서, 귀인은 수동적으로 다가오는게 아니라, 능동적으로 만들어간다는 것을 깨달았다. 운이나 환경의 문제를 따지기 전에, 귀인은 도처에 존재한다. 누군가를 귀인으로 섬기기까지의 일은 나에게 여전히 어려운일이다. 그렇지만 조금씩 그 과정을 밟아가고 있는 것 같다. 예전에는 존경하는 사람도 없었지만, 이제는 여럿 있다. 그들에게 도움을 요청할 수도 있다. 나의 어려움을 털어놓을 수도 있다. 중요한 출발을 했다고 생각하고, 이것이 큰 차이를 만드리라 확신한다.&lt;/p&gt;</summary>
    <title>당신에게는 귀인이 있나요?</title>
    <updated>2026-09-06T23:33:22+09:00</updated>
    <dc:date>2026-09-06T23:33:22+09:00</dc:date>
  </entry>
  <dc:date>2026-09-08T10:00:29+09:00</dc:date>
</feed>
