<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>베짱이보다 개미처럼</title>
    <link>https://bombo96.tistory.com/</link>
    <description>항상 배우기를 바라고, 성장하기위해 노력하는 백엔드 개미 개발자의 블로그입니다.</description>
    <language>ko</language>
    <pubDate>Thu, 18 Jun 2026 14:15:07 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Bombo_</managingEditor>
    <image>
      <title>베짱이보다 개미처럼</title>
      <url>https://tistory1.daumcdn.net/tistory/5405033/attach/5520431e32634b91980fb4b27317b02a</url>
      <link>https://bombo96.tistory.com</link>
    </image>
    <item>
      <title>토스로의 여정</title>
      <link>https://bombo96.tistory.com/138</link>
      <description>&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;2024년 2월, 트렌비에서 서버 개발자로 커리어를 시작한 이후, 2025년 10월 20일 토스뱅크로 이직하게 되었다.&lt;br&gt;이 글은 이직을 결심하게 된 계기와 그 과정에서 느꼈던 생각들을 정리하고자 쓰게 되었다.&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;이직의 계기&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;이직의 이유는 사람마다 다르다. 누군가는 연봉을 누군가는 재밌는 일을 또 누군가는 더 나은 문화를 찾는다.&lt;br&gt;나에게는 ‘토스’라는 회사에 대한 동경이 있었다.&lt;br&gt;&lt;br&gt;세상에 혁신을 만들어내는 그들의 모습은 언제나 내게 깊은 인상을 주었다. 새로운 변화를 이끌어내고, 세상을 조금이라도 더 나은 방향으로 움직이는 그 여정 속에서 열정을 다해 무언가를 만들어가는 사람들의 모습은 내 마음 한켠에 오랫동안 자리하고 있었다.&lt;br&gt;&amp;nbsp;&lt;br&gt;하지만 동시에 &quot;내가 그런 곳에 합류하기엔 아직 부족하지 않을까?&quot; 하는 생각이 늘 따라붙었다. 지금 내가 몸담고 있는 트렌비에 대한 애정도 컸고, 이곳에서도 분명 내가 이루어낼 수 있는 무언가가 있을 것이라는 믿음이 있었다. 또한 회사 전체의 시스템과 과정을 바라볼수록, 아직 배워야 할 것이 많다는 사실을 더욱 절실히 느꼈다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그래서 나는 그저 동경에 머물지 않고, 지금의 자리에서 성장하며 스스로를 단단히 만들어가야 한다는 생각을 하며 지내게 되었다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그러던 어느 날, 문득 &quot;시장 평가를 받아보고 싶다&quot;는 생각이 들었다. 회사에서는 혹은 내 주변에서는 내가 잘하고 있다고 말씀을 해주셨지만, 어쩌면 ‘우물 안의 개구리’ 일 수도 있었다. 외부의 시선으로 나를 객관적으로 평가받고 싶었다. 지금와서 드는 생각은 더 열심히 하고 싶었던 마음과 더 앞으로 나아가고 싶다는 마음이 공존했을 것이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그렇게 이력서를 다듬기 시작했다. 트렌비에서는 서버뿐만 아니라 기획, 클라이언트, 인프라 등 다양한 역할을 경험할 수 있었기에, “무엇을 어떤 목적으로 만들어내고, 어떤 임팩트를 냈는가” 에 초점을 두었다.&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;이력서 피드백&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;지인들에게 이력서를 보여드렸고, 여러 피드백을 받았다.&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;이력서 내용이 방대하다.&lt;/li&gt;&lt;li&gt;Server Developer로 지원한다면 기술적인 부분이 더 잘 드러나면 좋겠다.&lt;/li&gt;&lt;li&gt;날짜순이 꼭 정답은 아니다. 임팩트 있는 프로젝트를 위로 올려보는 건 어떨까?&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;모든 피드백들이 와닿았지만 그 중 가장 와닿는 피드백은 아래와 같았다.&lt;/p&gt;&lt;blockquote data-end=&quot;1076&quot; data-start=&quot;1015&quot; data-ke-style=&quot;style2&quot;&gt; 
 &lt;p data-end=&quot;1076&quot; data-start=&quot;1017&quot; data-ke-size=&quot;size16&quot;&gt;“Server Developer로 지원하신다면, 기술적인 부분이 더 잘 드러나야 해요.”&amp;nbsp;&lt;/p&gt; 
&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;이력서를 작성하면서 기술적인 부분을 중점으로 적기 보다는 오히려 회사의 목표와 나의 사고를 얼마만큼 일치시켜서 일을 하고 임팩트를 내었는지에 초점을 두었기 때문 일 것이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;내가 면접관이라면 어떨까 다시 한 번 생각해보았다. 엔지니어로서 당연히 기술적인 부분은 뒷받침이 되어야 하고 그 외의 가지고 있는 부가적인 장점을 드러내는 사람을 좋아할 것이 자명했다. 피드백을 듣고 다시 이력서를 살펴보니 근본적인 엔지니어링에 대한 언급보다 부가적인 장점들이 더 부각되는 듯한 느낌이 들었다.&lt;br&gt;&amp;nbsp;&lt;br&gt;부가적인 부분에서 벗어나 엔지니어링적인 부분에 초점을 두고 부가적인 면은 자연스럽게 곁들이는 과정을 여러 번 거쳤다.&lt;br&gt;&amp;nbsp;&lt;br&gt;이후 모의면접을 부탁드리면서, 나의 이력서를 보고 내가 의도한대로 면접관 분들이 궁금한 부분들을 유도하고 답변 할 수 있는지, '나'라는 사람이 어떤 사람인지 드러날 수 있는지를 확인했다. 하지만, 이때까지도 나라는 사람은 토스에 입사하기에는 부족하다는 생각이 들었다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그럼에도 불구하고, 피드백이 인상 깊었던 부분도 있고 시장평가를 내가 생각하는 가장 멋진 곳에서 첫 도전을 시작하는 건 어떤가 하는 생각이 들었다. 그리고, 이 분들과 일을 한다면 무엇인가를 만들어낼 수 있을 것 만 같은 느낌이었다. 조심스럽게 사내 추천을 부탁드리며 토스의 면접을 준비하게 되었다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그렇게, 추천사를 잘 써주신 덕분에 여러 계열사에 합격을 하게 되었다.&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;1차 면접 – 토스코어&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;&lt;br&gt;첫 면접은 토스코어에서 진행되었다. 내가 동경하는 회사에서 처음 기술 면접을 보다 보니 긴장이 되는 것은 어쩔 수가 없었던 것 같다.&lt;br&gt;&amp;nbsp;&lt;br&gt;면접은 JD에 나와 있는 그대로, 그리고 익히 들었던 대로 한 프로젝트를 중심으로 기술적 깊이를 파고드는 방식이었다. 내가 맡았던 프로젝트 중 '기술적으로 더 있어 보이는 것’을 선택했고, “이 정도면 조금이라도 더 기술적으로 어필할 수 있지 않을까” 하는 마음이었다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그렇게 1시간 반 동안 토론 방식으로 면접을 진행하면서 시간이 흘러갔다. 하지만, 면접이 끝난 뒤 느낀 점은 단 하나였다.&lt;br&gt;&amp;nbsp;&lt;br&gt;“내가 면접관이어도 나를 안 뽑겠다.&quot; 부족함이 분명했고, 그 만큼 후회가 많이 남는 면접이었다.&lt;br&gt;하지만 그 과정 덕분에 내 부족한 부분이 무엇인지 명확히 알 수 있었다. 그리고 예상대로 결과는 불합격이었다.&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;1차 면접 – 토스뱅크&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;먼저 이야기에 앞서서 토스뱅크 채용팀에 대해서 얘기해보고 싶다.&lt;br&gt;&lt;br&gt;토스뱅크 1차 면접을 준비하면서 가장 놀랐던 점은, 토스 채용팀의 태도였다. 내가 서류를 통과시켜 미래의 팀원이 될 수 있는 사람이라면, 그 사람이 꼭 좋은 면접을 볼 수 있도록 진심을 다했다. 토스의 JD(Job Description)는 채용공고에도 잘 나와 있지만, 그보다 훨씬 더 자세하고 디테일하게, 면접을 잘 볼 수 있도록 세심한 지원을 아끼지 않았다.&lt;br&gt;(채용팀 함성준님, 진심으로 감사합니다!)&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;다시 이어서, 불합격을 예상해서 그랬을까. 생각보다 타격은 그렇게 크지 않았다. 내가 부족한 부분을 보완하는 것이 중요하다고 생각했다. 뱅크 면접을 보기 전에 두 가지 선택지 중에서 선택을 해야만 했다.&lt;br&gt;&amp;nbsp;&lt;br&gt;'이전에 설명했던 프로젝트를 보완해서 가져갈까' &lt;br&gt;'내가 리드를 했던 프로젝트에 대해서 설명을 드리는 것이 좋을까'&lt;br&gt;&amp;nbsp;&lt;br&gt;1차 면접을 보고 느낀 것 중에 하나는 기술적인 답이 정해져있는 것이 아니라면 답을 바라지 않는다는 점이었다. 그리고, 추천을 해주신 분 또한 다음과 같은 조언을 해주셨다.&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style2&quot;&gt;어떤 특정 기술을 잘 쓰기 보다는 그 상황에 합리적인 선택을 내리셨는가를 더 중점으로 보게 될거에요. &lt;br&gt;솔직히 기술적인 깊이는 저희도 사용해야 될 때 종종 까먹고는 해서 늘 docs 를 보거든요. &lt;br&gt;면접이 기술지식 자랑 배틀은 아니잖아요.&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;이 얘기를 듣고 내가 리딩했던 프로젝트를 주제로 면접을 보기로 진행했다.&lt;br&gt;정말 많은 고민이 있었으며 정말 많은 시도도 했기 때문이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;면접에서는 내가 리딩했던 프로젝트를 주제로, 왜 특정 기술을 선택했고 어떤 고민을 거쳤는지, 어떤 결과를 냈는지를 이야기했다. 면접을 잘 볼 수 있도록 편하게 분위기를 만들어주신 면접관분들과의 대화는 마치 같이 문제를 풀어가는 과정처럼 느껴졌고, 면접을 보는 그 순간에도 내가 성장하고 있음을 분명히 느꼈고, 2시간이라는 면접 과정이 즐거움 그 자체였다. 그리고 “이 사람들과 함께 일한다면 정말 즐겁겠다”는 생각이 들었다.&lt;br&gt;&amp;nbsp;&lt;br&gt;코어에서의 면접과 달리 뱅크에서의 면접을 마치고 나서 스스로에게 했던 말은 이번엔 달랐다.&lt;br&gt;“그래, 최선을 다했다. 후회는 없다. 다시 되돌아가도 내가 처한 상황에서는 이게 최선이었을거야.”&lt;br&gt;&amp;nbsp;&lt;br&gt;그리고 다음날, 합격 소식을 받았다.&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;2차 면접 – 문화적합성&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;2차 면접은 총 3시간 동안 진행예정이 되어있었다.&lt;br&gt;1차 면접에서와 동일하게 채용팀에서는 문화 면접을 꼭 잘보셨으면 하는 마음으로 문화 면접을 준비하기 전 보면 좋을 아티클, 태도 등을 디테일하게 가이드 해주셨다. 그 덕분에 재미있는 책도 많이 읽으며 새로운 인사이트를 얻을 수 있기도 했다.&lt;br&gt;&amp;nbsp;&lt;br&gt;2차 면접 전에도 추천인 분께서 해주신 말씀은 &quot;첫째도, 둘째도 솔직함&quot; 이라고 말씀을 해주셨다.&lt;br&gt;정말 그 말이 맞을 것이다. 사실과 다르게 자신을 꾸며 말하더라도, 결국 그 모습으로 회사 생활을 이어가긴 어렵다. 꾸며낸 말로 사람을 현혹시키기 보다 나라는 사람이 그 회사에서 같이 일을 할 수 있고 즐길 수 있는지가 정말 중요하다. 그것을 확인하기 위해 문화적합성 면접이 존재한다고 생각한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;이 면접을 준비하면서 글또를 운영하시는 성윤님이 추천해주신 삶의 지도가 큰 도움이 되었다. 세 번 정도 나의 인생을 되돌아보며, 태어난 순간부터 지금까지의 나를 정리하는 시간을 가졌다. 그 덕분에 내가 나를 더 잘 알 수 있었고, 놀랍게도 그 회고 속 경험들이 면접 중 자연스럽게 드러나며 이야기로 이어졌다.&lt;br&gt;&amp;nbsp;&lt;br&gt;“내가 왜 토스뱅크에 합류하고 싶은가”에 대한 나만의 이유를 스스로에게도 다시 물었다.&lt;br&gt;&amp;nbsp;&lt;br&gt;면접에서는 규칙 없음 책과 The Work 시리즈, 그 동안의 일 경험에서 느꼈던 고민들을 솔직하게 나눴다.&lt;br&gt;내 경험과 가치관을 그대로 이야기했으며, 궁금한 질문에 대해서는 면접관분들 역시 진심으로 답해주셨다.&lt;br&gt;회사에 대해서도 알아가고 나라는 사람을 되돌아 볼 수 있는 2차 면접에서 ‘나라는 사람’을 더 잘 이해할 수 있었다.&lt;br&gt;&amp;nbsp;&lt;br&gt;결과는 놀라울 만큼 빨리 도착했다. 면접이 끝난 지 1시간쯤 후에 인사 담당자님께 전화가 왔다. 결과는 합격이었다.&lt;br&gt;&lt;br&gt;이후, 레퍼런스 체크와 협상을 마치고 나는 면접에서의 경험과 같이 문제를 해결해나갈 수 있는 열정있는 동료들을 보고 토스뱅크로의 합류를 결심했다.&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;퇴사와 인수인계&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;토스로의 이직이 확정되었기에, 회사에도 그 소식을 전했다.&lt;br&gt;퇴사일은 17(금), 입사일은 20(월) 추석 연휴를 보내고 와서 더 쉬기 보다는 얼른 입사해서 토스 팀원분들과 같이 일하고 싶은 마음이 너무나도 컸다. 입사 전날까지만 해도 1주일 더 쉴걸 그랬나 싶었지만, 입사를 한 지금은 참 잘했다는 생각이 든다.&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;color: #333333;&quot;&gt;회사에서도 인수인계를 위해 충분한 시간을 배려해주시기는 하셨지만,&amp;nbsp;&lt;/span&gt;회사가 바쁜 시기였던만큼 미안한 마음이 너무 컸다.&lt;br&gt;하지만, 모든 팀원분들께서 본인 스타일과 잘 어울리는 회사를 가게 된 것 같다. 좋은 회사를 가게 되어서 축하한다며 진심으로 축하해주셨다. 그 말씀들이 참 고마웠다.&lt;br&gt;&amp;nbsp;&lt;br&gt;신입으로서 정말 부족한 부분이 많았지만, 좋은 팀원들이 있기에 앞으로 나아 갈 수 있었고 성장 할 수 있었다.&lt;br&gt;특히, 가장 좋은 경험은 문제를 해결해나가는 과정에서 '큰 그림을 볼 수 있도록 방향을 잡아주신 부분'이다.&lt;br&gt;내가 내린 기술적 고민과 선택들을 모두 존중해주셨고, 그 과정 속에서 자연스럽게 트레이드-오프를 배울 수 있었으며 경험하지 못했던 부분에 대해서 도전하는 것도 늘 응원해주시고 믿어주셨다. 그렇기에 수 없이 많은 경험들을 할 수 있었다고 생각한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;정말, 한 회사에서 클라이언트, 백엔드, 인프라를 다 건들 수 있는 기회와 권한을 주시는 것도 흔치 않다고 생각한다. 그러한 권한을 믿고 맡겨주셨다는 사실이 지금도 너무 감사하다. 모든 팀원분들께 이미 인사를 드렸지만, 다시 한 번 더 감사의 인사를 드리고 싶다.&lt;br&gt;&amp;nbsp;&lt;br&gt;한편으로는, 내가 인수인계를 받으면서 가장 어려웠던 것은 프로젝트의 히스토리 파악이 가장 어려웠다. 첫 입사로 어떻게 히스토리를 파악해야 했는지를 몰랐으며, 무엇을 보고 결정을 내려야하는지도 모호했다. &quot;가장 확실한 인수인계는 개발자가 남기는 코드이다.&quot; 라는 생각이 들기도 했지만 그럼에도 불구하고, 나와 달리 후임 개발자가 해당 도메인에 대해서 무엇보다 빠르게 이해하고 적응했으면 하는 마음도 있기에 인수인계에 그간 담당했던 히스토리와 의사결정 과정을 가능한 한 자세히 남겼다.&lt;br&gt;&amp;nbsp;&lt;br&gt;물론, 몇 가지는 미처 기록하지 못했을지도 모른다. 그래서 언제든 편하게 연락 달라는 말을 덧 붙였다.&lt;br&gt;보이스카우트 규칙은 어디에서나 중요하다고 생각한다.&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;앞으로의 여정&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: justify;&quot;&gt;토스뱅크에 입사한 지 어느덧 2주차가 넘어가고 있다.&lt;br&gt;아직은 온보딩 중이라 배워야 할 것도, 알아가야 할 것도 많지만 그 과정 하나하나가 정말 즐겁다.&lt;br&gt;&amp;nbsp;&lt;br&gt;새로운 환경 속에서 조심스럽게, 그러나 오래 함께할 수 있도록 스스로를 다잡고 있다.&lt;br&gt;운이 좋게도 같은 스쿼드에 동기 디자이너가 있었고, PO님과 서버 개발자님 모두 나와 비슷한 결을 가진 분들이라 함께 논의하고 문제를 풀어가는 과정이 늘 유쾌하고 자극적이다. 한편으로는, &quot;이래서 문화 적합성을 더 중요하게 보시는 것이지 않을까?&quot; 하는 생각을 하기도 했다.&lt;br&gt;&amp;nbsp;&lt;br&gt;앞으로의 여정이 기대된다. 매일이 배우는 시간이고, 함께 성장할 수 있는 순간이다.&lt;br&gt;그리고 혹시 토스로의 합류를 고민하고 있는 분이 있다면 주저하지 말고 도전하길 권하고 싶다.&lt;br&gt;합격이 아니더라도 분명 그 과정은 당신에게도 새로운 가능성이 되어줄 것이다.&lt;/p&gt;</description>
      <category>회고</category>
      <category>이직</category>
      <category>토스</category>
      <category>토스뱅크</category>
      <author>Bombo_</author>
      <guid isPermaLink="true">https://bombo96.tistory.com/138</guid>
      <comments>https://bombo96.tistory.com/138#entry138comment</comments>
      <pubDate>Sat, 8 Nov 2025 12:49:08 +0900</pubDate>
    </item>
    <item>
      <title>ThreadPoolExecutor와 VirtualThreadExecutor의 실행 차이</title>
      <link>https://bombo96.tistory.com/137</link>
      <description>&lt;h1 data-end=&quot;216&quot; data-start=&quot;155&quot;&gt;&lt;b&gt;Java Virtual Thread와 ThreadPoolExecutor 비교&amp;nbsp;&lt;/b&gt;&lt;/h1&gt;
&lt;p data-end=&quot;430&quot; data-start=&quot;218&quot; data-ke-size=&quot;size16&quot;&gt;Java 21부터 &lt;b&gt;가상 스레드(Virtual Thread)&lt;/b&gt;(이하 가상스레드)가 도입되면서, 기존 &lt;b&gt;ThreadPoolExecutor&lt;/b&gt;(이하 스레드풀)&amp;nbsp;방식과 비교해 스레드 관리 전략에 큰 변화가 생겼습니다. 이번 글에서는 스레드풀과 가상 스레드의 동작 방식을 코드로 비교하며, 언제 어떤 방식을 써야 하는지 정리해보겠습니다.&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-end=&quot;430&quot; data-start=&quot;218&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. ThreadPoolExecutor 동작 원리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;559&quot; data-start=&quot;473&quot; data-ke-size=&quot;size16&quot;&gt;우리가 일반적으로 사용하는 ThreadPoolExecutor 또는 ThreadTaskExecutor의 execute() 코드는 다음과 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1756392531715&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int c = ctl.get();
if (workerCountOf(c) &amp;lt; corePoolSize) {
    if (addWorker(command, true))
        return;
    c = ctl.get();
}
if (isRunning(c) &amp;amp;&amp;amp; workQueue.offer(command)) {
    int recheck = ctl.get();
    if (!isRunning(recheck) &amp;amp;&amp;amp; remove(command))
        reject(command);
    else if (workerCountOf(recheck) == 0)
        addWorker(null, false);
}
else if (!addWorker(command, false))
    reject(command);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-end=&quot;1000&quot; data-start=&quot;987&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;동작 방식&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1137&quot; data-start=&quot;1001&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1029&quot; data-start=&quot;1001&quot;&gt;현재 실행 중인 &lt;b&gt;worker 수&lt;/b&gt; 확인&lt;/li&gt;
&lt;li data-end=&quot;1063&quot; data-start=&quot;1030&quot;&gt;corePoolSize보다 작으면 새 스레드 생성&lt;/li&gt;
&lt;li data-end=&quot;1100&quot; data-start=&quot;1064&quot;&gt;아니면 작업 큐(workQueue)에 추가 후 대기&lt;/li&gt;
&lt;li data-end=&quot;1137&quot; data-start=&quot;1101&quot;&gt;스레드와 큐가 모두 꽉 차면 reject 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;즉, ThreadPoolExecutor는 스레드를 미리 생성해 두고 재사용하는 구조&lt;/b&gt;&lt;span style=&quot;text-align: start;&quot;&gt;이며, &lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;내부적으로&amp;nbsp;&lt;/span&gt;&lt;b&gt;event-loop&lt;/b&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&amp;nbsp;방식과 비슷하게 동작합니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-end=&quot;1272&quot; data-start=&quot;1242&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. Virtual Thread 동작 원리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;1328&quot; data-start=&quot;1274&quot; data-ke-size=&quot;size16&quot;&gt;반면, 가상 스레드를 사용하는 Executor의 execute() 구현은 매우 단순합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1756392579759&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public void execute(Runnable task) {
    this.virtualThreadFactory.newThread(task).start();
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-end=&quot;1450&quot; data-start=&quot;1437&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;동작 방식&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1553&quot; data-start=&quot;1451&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1474&quot; data-start=&quot;1451&quot;&gt;새로운 &lt;b&gt;가상 스레드&lt;/b&gt;를 매번 생성&lt;/li&gt;
&lt;li data-end=&quot;1502&quot; data-start=&quot;1475&quot;&gt;스레드 풀을 사용하지 않고 즉시 실행 후 종료&lt;/li&gt;
&lt;li data-end=&quot;1553&quot; data-start=&quot;1503&quot;&gt;corePoolSize, maxPoolSize, queue 설정이 필요 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;즉,&amp;nbsp;&lt;/span&gt;&lt;b&gt;Virtual Thread는 재사용하지 않고 요청마다 새로 생성&lt;/b&gt;&lt;span style=&quot;text-align: start;&quot;&gt;하는 모델입니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-end=&quot;1664&quot; data-start=&quot;1615&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. ThreadPoolExecutor vs Virtual Thread 비교&lt;/b&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 100px;&quot; border=&quot;1&quot; data-end=&quot;2071&quot; data-start=&quot;1666&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;2071&quot; data-start=&quot;1772&quot;&gt;
&lt;tr style=&quot;height: 20px;&quot; data-end=&quot;1816&quot; data-start=&quot;1772&quot;&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;1788&quot; data-start=&quot;1772&quot;&gt;&lt;b&gt;스레드 생성 방식&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-end=&quot;1802&quot; data-start=&quot;1788&quot; data-col-size=&quot;sm&quot;&gt;미리 생성 후 재사용&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-end=&quot;1816&quot; data-start=&quot;1802&quot; data-col-size=&quot;sm&quot;&gt;요청마다 새로 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot; data-end=&quot;1867&quot; data-start=&quot;1817&quot;&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;1832&quot; data-start=&quot;1817&quot;&gt;&lt;b&gt;설정 필요 여부&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-end=&quot;1857&quot; data-start=&quot;1832&quot; data-col-size=&quot;sm&quot;&gt;Core, Max, Queue 설정 필요&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-end=&quot;1867&quot; data-start=&quot;1857&quot; data-col-size=&quot;sm&quot;&gt;설정 불필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot; data-end=&quot;1921&quot; data-start=&quot;1868&quot;&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;1881&quot; data-start=&quot;1868&quot;&gt;&lt;b&gt;동시성 처리&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-end=&quot;1902&quot; data-start=&quot;1881&quot; data-col-size=&quot;sm&quot;&gt;제한적, 스레드 수에 따라 달라짐&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-end=&quot;1921&quot; data-start=&quot;1902&quot; data-col-size=&quot;sm&quot;&gt;매우 많은 동시성 처리 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot; data-end=&quot;1970&quot; data-start=&quot;1922&quot;&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;1936&quot; data-start=&quot;1922&quot;&gt;&lt;b&gt;메모리 사용량&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-end=&quot;1948&quot; data-start=&quot;1936&quot; data-col-size=&quot;sm&quot;&gt;상대적으로 안정적&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-end=&quot;1970&quot; data-start=&quot;1948&quot; data-col-size=&quot;sm&quot;&gt;많은 스레드 생성 시 OOM 위험&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot; data-end=&quot;2071&quot; data-start=&quot;2027&quot;&gt;
&lt;td style=&quot;height: 20px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;2040&quot; data-start=&quot;2027&quot;&gt;&lt;b&gt;제어 가능성&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-end=&quot;2061&quot; data-start=&quot;2040&quot; data-col-size=&quot;sm&quot;&gt;스레드 개수, 큐 크기 제어 가능&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot; data-end=&quot;2071&quot; data-start=&quot;2061&quot; data-col-size=&quot;sm&quot;&gt;제어 어려움&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;2110&quot; data-start=&quot;2078&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. Virtual Thread의 장점과 단점&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2246&quot; data-start=&quot;2125&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2151&quot; data-start=&quot;2125&quot;&gt;스레드 풀 설정이 필요 없어 코드가 단순해짐&lt;/li&gt;
&lt;li data-end=&quot;2224&quot; data-start=&quot;2193&quot;&gt;수천 개 이상의 I/O 요청을 병렬로 쉽게 처리 가능&lt;/li&gt;
&lt;li data-end=&quot;2246&quot; data-start=&quot;2225&quot;&gt;&lt;b&gt;컨텍스트 스위칭 비용이 낮음&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-end=&quot;2261&quot; data-start=&quot;2248&quot; data-ke-size=&quot;size20&quot;&gt;단점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2356&quot; data-start=&quot;2262&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2291&quot; data-start=&quot;2262&quot;&gt;스레드 생성 제한이 없어 &lt;b&gt;메모리 폭증 위험&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;2707&quot; data-start=&quot;2695&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5. 정리&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2891&quot; data-start=&quot;2709&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2760&quot; data-start=&quot;2709&quot;&gt;ThreadPoolExecutor &amp;rarr; 스레드를 &lt;b&gt;미리 만들고 재사용&lt;/b&gt;하는 방식&lt;/li&gt;
&lt;li data-end=&quot;2811&quot; data-start=&quot;2761&quot;&gt;Virtual Thread &amp;rarr; 요청마다 스레드를 &lt;b&gt;매번 새로 생성&lt;/b&gt;하는 방식&lt;/li&gt;
&lt;li data-end=&quot;2891&quot; data-start=&quot;2812&quot;&gt;Virtual Thread는 &lt;b&gt;I/O-bound 작업&lt;/b&gt;에서 큰 강점을 발휘하지만,&lt;b&gt;메모리 관리&lt;/b&gt;를 반드시 고려해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;3047&quot; data-start=&quot;2893&quot; data-ke-size=&quot;size16&quot;&gt;단순히 &lt;b&gt;가상 스레드니까 무조건 좋다.&lt;/b&gt; 가 아니라,&lt;br /&gt;&lt;b&gt;작업 성격에 맞게 ThreadPoolExecutor와 Virtual Thread를 적절히 혼용하는 전략&lt;/b&gt;이 필요합니다.&lt;/p&gt;
&lt;p data-end=&quot;3047&quot; data-start=&quot;2893&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;3047&quot; data-start=&quot;2893&quot; data-ke-size=&quot;size16&quot;&gt;번외) virtualThread에서는 최소 corePoolSize를 정할 수 없기 때문에, 매번 새로 생성을 해주어야하지만&lt;/p&gt;
&lt;p data-end=&quot;3047&quot; data-start=&quot;2893&quot; data-ke-size=&quot;size16&quot;&gt;ThreadPoolExecutor에서는 최소 corePoolSize를 지정 할 수 있기 때문에 가용 가능한 스레드가 많다면 오히려 처리 속도가 더 빠를 수 있어 적절하게 판단하고 사용하는 것이 중요합니다.&lt;/p&gt;</description>
      <category>Language/Java</category>
      <category>가상스레드</category>
      <category>스레드풀</category>
      <category>자바</category>
      <author>Bombo_</author>
      <guid isPermaLink="true">https://bombo96.tistory.com/137</guid>
      <comments>https://bombo96.tistory.com/137#entry137comment</comments>
      <pubDate>Thu, 28 Aug 2025 23:53:04 +0900</pubDate>
    </item>
    <item>
      <title>Hibernate Set&amp;lt;T&amp;gt; ClassCastException</title>
      <link>https://bombo96.tistory.com/136</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot와 JPA(HIbernate)를 사용하면서 Entity 에 Set이나 Map 같은 컬렉션을 사용하시나요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 JPA&amp;nbsp; 사용 시 Set 을 사용하면서 발생한 ClassCastException에 대해서 정리하고자 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 문제상황&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘 Spring Data JPA를 사용하는 환경에서 @OneToMany로 Set 컬렉션을 사용하다가 다음과 같은 에러를 만나게 되었습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1756122358195&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;java.lang.ClassCastException: class java.util.HashSet cannot be cast to class org.hibernate.collection.spi.PersistentCollection&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hashset은 Hibernate의 PersistentCollection으로 전환 될 수 없음을 말하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론부터 말씀드리자면, &lt;b&gt;외부에서 엔티티의 Set을 데이터 재처리된 Set으로 대체하면서 발생한 문제&lt;/b&gt;인데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 다음과 같은 상황에 발생한 케이스입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1756123646029&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public void foo() {
  Example example = findById(1L);
  ExampleItem exampleItem = findXXX();
  Set&amp;lt;ExampleItem&amp;gt; filteredExmapleItems = exampleItem.stream()
  	.filter(ExampleItem::isXXX)
    .collect(Collectors.toSet());
    
  example.set(filteredExampleItems); // 에러의 원인
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히, 맞는 자료구조에 매칭을 해주었는데 왜 발생한 것일까요?&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Hibernate Collection&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;1055&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3FkMC/btsP5ETEImu/Z7Q9PjWh82kJR744w0VEI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3FkMC/btsP5ETEImu/Z7Q9PjWh82kJR744w0VEI0/img.png&quot; data-alt=&quot;HibernateCollection 생태계 중 Set&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3FkMC/btsP5ETEImu/Z7Q9PjWh82kJR744w0VEI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3FkMC%2FbtsP5ETEImu%2FZ7Q9PjWh82kJR744w0VEI0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;646&quot; height=&quot;792&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;1055&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;HibernateCollection 생태계 중 Set&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hibernate는 엔티티의 Collection 필드(Set, List 등)를 단순한 데이터 컨테이너로 다루지 않습니다. 컬렉션을 &lt;b&gt;프록시(proxy) &lt;/b&gt;객체로 감싸서&lt;b&gt; 세션, 지연 로딩, 매핑 정보를 관리&lt;/b&gt;하기 위해 PersistentCollection이라는 특별한 래퍼를 사용해요. 그렇기에, Hibernate가 엔티티를 조회할 때, Set&amp;lt;ExampleItem&amp;gt;을 선언해놨다고 하더라도 실제로는 HashSet을 바로 주입하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;대신 PersistentSet, PersistentBag 같은 PersistentCollection의 하위 클래스를 주입해서 컬렉션을 제어합니다. 이 프록시 컬렉션들은 평소에는 DB에 접근하지 않지만, 컬렉션의 메서드(get, size, iterator 등)를 호출하여&amp;nbsp;&lt;b&gt;필요한 시점에만 데이터를 읽어&lt;/b&gt;오기 위해 DB 쿼리를 실행합니다. 즉, &lt;b&gt;지연 로딩(Lazy Loading)&lt;/b&gt; 을 효율적으로 구현하기 위해서 존재하는 컬렉션입니다.&lt;/p&gt;
&lt;p data-end=&quot;942&quot; data-start=&quot;875&quot; data-ke-size=&quot;size16&quot;&gt;추가적으로 이 프록시는 지연로딩과 같이 단순히 데이터를 읽어오는 데 그치지 않고, Hibernate 내부에서 다음과 같은 기능들을 담당하고 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1155&quot; data-start=&quot;944&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1043&quot; data-start=&quot;990&quot;&gt;&lt;b&gt;영속성 컨텍스트와의 동기화:&lt;/b&gt; 세션(Session)과 컬렉션 상태를 연결해 일관성 보장&lt;/li&gt;
&lt;li data-end=&quot;1103&quot; data-start=&quot;1044&quot;&gt;&lt;b&gt;변경 감지(Dirty Checking):&lt;/b&gt; 컬렉션 변경 이력을 추적해 flush 시점에 DB 반영&lt;/li&gt;
&lt;li data-end=&quot;1155&quot; data-start=&quot;1104&quot;&gt;&lt;b&gt;초기화 여부 추적:&lt;/b&gt; 아직 로딩되지 않은 컬렉션인지, 이미 초기화되었는지 구분 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 일반 HashSet 객체로 수정을 했다면 cascade, orphanRemoval 등 컬렉션에 필요한 모든 기능들을 사용하지 못하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼, 위와 같이 Set를 사용하는 상황에서는 어떻게 전환을 해야할까요? 답은 생각보다 간단합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 해결 방안&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옛날에는 자바빈패턴으로 데이터를 다루기 위하여 Setter와 Getter를 많이 사용하고는 했는데요. 규모가 큰 코드에 롬복의 Setter를 제거하기 어려운 상황이라면 다음과 같이 Setter를 재정의 해주어야 합니다. 위의 다이어그램에서 보이듯이 PersistentSet은 Set을 구현하고 있기 때문에 원소 대체가 가능합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1756124165677&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public void setExampleItems(Set&amp;lt;ExmapleItem&amp;gt; exampleItems) {
  this.exmapleItems.clear();
  this.exampleItems.addAll(exampleItems);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Setter를 사용하고 있지 않다면 의미에 맞게 다음과 같이 메서드를 재정의해주는 것이 좋겠지요.&lt;/p&gt;
&lt;pre id=&quot;code_1756124455987&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public void reviseExampleItems(Set&amp;lt;ExmapleItem&amp;gt; exampleItems) {
  this.exmapleItems.clear();
  this.exampleItems.addAll(exampleItems);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 결론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제의 원인은 &lt;b&gt;Hibernate의 컬렉션 관리 방식&lt;/b&gt;에 있었습니다. Hibernate는 &lt;b&gt;PersistentCollection이라는 프록시 객체&lt;/b&gt;를 주입해 세션, 지연 로딩, 변경 감지 등을 관리합니다. 따라서, 엔티티 컬렉션을 &lt;b&gt;통째로 교체&lt;/b&gt;해버리면 Hibernate가 주입한 프록시가 깨지고,&lt;br /&gt;다음과 같은 문제가 발생할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;565&quot; data-start=&quot;403&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;471&quot; data-start=&quot;403&quot;&gt;&lt;b&gt;ClassCastException&lt;/b&gt; 발생&lt;/li&gt;
&lt;li data-end=&quot;496&quot; data-start=&quot;472&quot;&gt;&lt;b&gt;Lazy Loading 동작 실패&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;528&quot; data-start=&quot;497&quot;&gt;&lt;b&gt;Dirty Checking(변경 감지) 무력화&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;565&quot; data-start=&quot;529&quot;&gt;&lt;b&gt;orphanRemoval, cascade 기능 비활성화&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 콜렉션을 사용하는 경우에는 컬렉션의 레퍼런스 자체를 바꾸지 않고, &lt;b&gt;프록시를 유지한 채 내부 데이터만을 교체하는 것이 중요하다&lt;/b&gt;는 것을 알아가게 되었습니다.&lt;/p&gt;</description>
      <category>Backend/JPA</category>
      <category>JPA</category>
      <author>Bombo_</author>
      <guid isPermaLink="true">https://bombo96.tistory.com/136</guid>
      <comments>https://bombo96.tistory.com/136#entry136comment</comments>
      <pubDate>Mon, 25 Aug 2025 21:27:00 +0900</pubDate>
    </item>
    <item>
      <title>Kotlin 리스트 확장함수 사용 시 타입을 조심하자.</title>
      <link>https://bombo96.tistory.com/135</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;해당 내용에 대한 예시코드는 &lt;a href=&quot;https://github.com/bombo-dev/extension-function&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[깃허브 예시코드]&lt;/a&gt; 에서 확인 하실 수 있습니다.&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;확장함수란&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코틀린의&lt;span&gt; &lt;/span&gt;&lt;b&gt;확장&lt;span&gt; &lt;/span&gt;함수&lt;span&gt;(Extension Function)&lt;/span&gt;&lt;/b&gt;는&lt;span&gt; &lt;/span&gt;기존&lt;span&gt; &lt;/span&gt;클래스의&lt;span&gt; &lt;/span&gt;소스&lt;span&gt; &lt;/span&gt;코드를&lt;span&gt; &lt;/span&gt;수정하거나&lt;span&gt; &lt;/span&gt;상속받지&lt;span&gt; &lt;/span&gt;않고&lt;span&gt;, &lt;/span&gt;마치&lt;span&gt; &lt;/span&gt;그&lt;span&gt; &lt;/span&gt;클래스의&lt;span&gt; &lt;/span&gt;멤버&lt;span&gt; &lt;/span&gt;메소드인&lt;span&gt; &lt;/span&gt;것처럼&lt;span&gt; &lt;/span&gt;새로운&lt;span&gt; &lt;/span&gt;함수를&lt;span&gt; &lt;/span&gt;추가할&lt;span&gt; &lt;/span&gt;수&lt;span&gt; &lt;/span&gt;있는&lt;span&gt; &lt;/span&gt;기능입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주요 특징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-pm-slice=&quot;3 3 []&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;기존 클래스 확장&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 외부 라이브러리나 수정 불가능한 클래스에도 새로운 기능 추가 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;정적 바인딩&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 컴파일 시점에 호출되는 함수가 결정됨&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;접근 제한&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 클래스의 public 멤버에만 접근 가능&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구현&amp;nbsp; 방법&lt;/h3&gt;
&lt;pre id=&quot;code_1748534524119&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fun 클래스명.함수명(파라미터): 반환타입 {
    // 함수 본문
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;활용 예시&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-pm-slice=&quot;3 3 []&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;외부 라이브러리 클래스에 추가 기능 제공&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;기본 클래스에 자주 쓰는 유틸리티 함수 정의&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;코드의 재사용성과 가독성 향상&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;리팩토링과 확장 함수 도입 배경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;프로젝트를 진행하면서 &lt;span style=&quot;text-align: start;&quot;&gt;리스트의 &lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;b&gt;!contains() &lt;/b&gt;&lt;/span&gt;가독성이 좋도록 특정 확장 함수를 정의했습니다. 시간이 갈수록 사람의 뇌가 단순해지는 것 같기는 하다만, 긍정 -&amp;gt; 부정 보다는 태초의 부정이 좀 더 코드의 가독성을 높이기 때문입니다. 코드는 아래와 같이 간단합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1880&quot; data-origin-height=&quot;194&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sp2jL/btsOk83YUMx/qszwKBXQWz0ZVkYrJwKZcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sp2jL/btsOk83YUMx/qszwKBXQWz0ZVkYrJwKZcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sp2jL/btsOk83YUMx/qszwKBXQWz0ZVkYrJwKZcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fsp2jL%2FbtsOk83YUMx%2FqszwKBXQWz0ZVkYrJwKZcK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;782&quot; height=&quot;81&quot; data-origin-width=&quot;1880&quot; data-origin-height=&quot;194&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그리고 특정 유스케이스를 한 번에 실행하는 Facade 로직을 추가했습니다. 이 Facade는 도메인 객체를 받아 특정 행위를 수행하는 역할을 합니다. 도메인 객체는 간단히 id만 가진다고 가정하였습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1880&quot; data-origin-height=&quot;1146&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lTPov/btsOmazGy4o/HTFnbLGiQK4ukdCimWVrGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lTPov/btsOmazGy4o/HTFnbLGiQK4ukdCimWVrGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lTPov/btsOmazGy4o/HTFnbLGiQK4ukdCimWVrGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlTPov%2FbtsOmazGy4o%2FHTFnbLGiQK4ukdCimWVrGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;776&quot; height=&quot;473&quot; data-origin-width=&quot;1880&quot; data-origin-height=&quot;1146&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;기존 코드가 잘 작동하고 있었지만, 메서드 내부 마다 중복된 코드가 발견되었으며, 공통된 비즈니스 사항을 다른 곳에서도 쓸 수 있기에 리스트 관련 코드를 더욱 도메인 친화적으로 리팩토링했고, 아래와 같이 간단한 코드 수정 이후 배포했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1878&quot; data-origin-height=&quot;354&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t635v/btsOlLUskcl/J8QlpaU7t2uTg4VdK0RJK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t635v/btsOlLUskcl/J8QlpaU7t2uTg4VdK0RJK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t635v/btsOlLUskcl/J8QlpaU7t2uTg4VdK0RJK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft635v%2FbtsOlLUskcl%2FJ8QlpaU7t2uTg4VdK0RJK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;786&quot; height=&quot;148&quot; data-origin-width=&quot;1878&quot; data-origin-height=&quot;354&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1878&quot; data-origin-height=&quot;930&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ez3Ldq/btsOkNFHaMc/Dj98DXewDZFkjgPjRdaiek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ez3Ldq/btsOkNFHaMc/Dj98DXewDZFkjgPjRdaiek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ez3Ldq/btsOkNFHaMc/Dj98DXewDZFkjgPjRdaiek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fez3Ldq%2FbtsOkNFHaMc%2FDj98DXewDZFkjgPjRdaiek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;787&quot; height=&quot;390&quot; data-origin-width=&quot;1878&quot; data-origin-height=&quot;930&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 발생과 재현 과정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그러나 배포 후 예상치 못한 동작이 발생했습니다. 리스트에 특정 원소가 포함되지 않으면 모든 유스케이스를 실행하고, 포함되어 있으면 하나만 실행되어야 했는데, 포함되지 않았음에도 특정 유스케이스가 실행된 것입니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;디버깅&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빠르게 롤백한 뒤 원인을 분석하기 위해 로컬 환경에서 디버깅을 진행했습니다. 해당 if문에 watch point를 걸었더니, 분명히 포함되지 않았는데도 불구하고 true를 반환하는 것을 확인했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1340&quot; data-origin-height=&quot;248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3LFI1/btsOl2BEdAh/RIBu3796qQ0lzCXmsAxvj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3LFI1/btsOl2BEdAh/RIBu3796qQ0lzCXmsAxvj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3LFI1/btsOl2BEdAh/RIBu3796qQ0lzCXmsAxvj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3LFI1%2FbtsOl2BEdAh%2FRIBu3796qQ0lzCXmsAxvj1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;806&quot; height=&quot;149&quot; data-origin-width=&quot;1340&quot; data-origin-height=&quot;248&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹시 IDE 캐시 문제일까 싶어 인텔리제이의 캐시를 삭제하고 다시 테스트했지만, 결과는 변함이 없었습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;테스트와 원인 검증&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;테스트 코드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이해가 되지 않아 더 명확한 검증을 위해 테스트 코드를 작성했습니다. 실제로 if문 내부의 로직이 수행되는지 궁금했기에, 빠르게 mock 객체를 사용하여 테스트했습니다. 하지만 테스트 결과 역시 같은 문제가 발생했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1282&quot; data-origin-height=&quot;716&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvkSIi/btsOnbdyl1U/52VJVSzqhoDNZebiFjJ4WK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvkSIi/btsOnbdyl1U/52VJVSzqhoDNZebiFjJ4WK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvkSIi/btsOnbdyl1U/52VJVSzqhoDNZebiFjJ4WK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvkSIi%2FbtsOnbdyl1U%2F52VJVSzqhoDNZebiFjJ4WK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;788&quot; height=&quot;440&quot; data-origin-width=&quot;1282&quot; data-origin-height=&quot;716&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1070&quot; data-origin-height=&quot;206&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpv8v7/btsOlhsPXAH/GXVU8Ckn3Iz69EdMXqYwRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpv8v7/btsOlhsPXAH/GXVU8Ckn3Iz69EdMXqYwRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpv8v7/btsOlhsPXAH/GXVU8Ckn3Iz69EdMXqYwRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbpv8v7%2FbtsOlhsPXAH%2FGXVU8Ckn3Iz69EdMXqYwRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;803&quot; height=&quot;155&quot; data-origin-width=&quot;1070&quot; data-origin-height=&quot;206&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;혹시 확장 함수 자체의 문제인가 싶어 확장 함수를 별도로 테스트했더니, 마찬가지로 true를 반환했습니다. &lt;br /&gt;&lt;br /&gt;더 나아가, contains 함수를 잘못 이해했나 하는 생각에 다시 테스트 코드를 작성했는데, 이 과정에서 갑작스러운 compile Error가 발생했습니다. 오류 메시지는 &quot;타입이 불일치한다&quot;는 내용이었습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2378&quot; data-origin-height=&quot;762&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ir7nt/btsOmxBpAzf/oaa5UjuXJptICi2wvKk7f0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ir7nt/btsOmxBpAzf/oaa5UjuXJptICi2wvKk7f0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ir7nt/btsOmxBpAzf/oaa5UjuXJptICi2wvKk7f0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIr7nt%2FbtsOmxBpAzf%2Foaa5UjuXJptICi2wvKk7f0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;811&quot; height=&quot;260&quot; data-origin-width=&quot;2378&quot; data-origin-height=&quot;762&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;일단 타입을 맞추고, not() 케이스도 추가하여 테스트했습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1988&quot; data-origin-height=&quot;396&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kzwqg/btsOmICJI3l/9fvVSe9sR8kbQysMEr3yrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kzwqg/btsOmICJI3l/9fvVSe9sR8kbQysMEr3yrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kzwqg/btsOmICJI3l/9fvVSe9sR8kbQysMEr3yrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fkzwqg%2FbtsOmICJI3l%2F9fvVSe9sR8kbQysMEr3yrK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;810&quot; height=&quot;161&quot; data-origin-width=&quot;1988&quot; data-origin-height=&quot;396&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이제 제가 의도한 대로 코드가 동작하기 시작했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;근본 원인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이때 문득 한 가지 생각이 들었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&quot;잠깐... 설마 명시된 타입이 다른가?&quot;&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;코틀린은 숫자 뒤에 &lt;b&gt;'L'&lt;/b&gt; 이 붙으면 Long 타입으로, 붙지 않으면 Int 타입으로 타입을 추론합니다. 문제는 실제 전달된 값은 Long 타입이었고 리스트 내 값은 Int 타입이었다는 점이었습니다. 실제로, 선언되어있던 리스트의 타입을 Long 타입으로 명시해주고(각, 원소에 L을 붙여도 되지만..) 다시 기존의 &lt;b&gt;mock 테스트를 돌려본 결과 추론이 맞음을 확인&lt;/b&gt; 할 수 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1874&quot; data-origin-height=&quot;350&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rH9WL/btsOlz04BxR/o7rYKlEeDhqJypSkUKYLak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rH9WL/btsOlz04BxR/o7rYKlEeDhqJypSkUKYLak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rH9WL/btsOlz04BxR/o7rYKlEeDhqJypSkUKYLak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrH9WL%2FbtsOlz04BxR%2Fo7rYKlEeDhqJypSkUKYLak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;796&quot; height=&quot;149&quot; data-origin-width=&quot;1874&quot; data-origin-height=&quot;350&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1992&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zM6kf/btsOmTxrlAR/5BKDyywapP3aH8kxRWeq41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zM6kf/btsOmTxrlAR/5BKDyywapP3aH8kxRWeq41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zM6kf/btsOmTxrlAR/5BKDyywapP3aH8kxRWeq41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzM6kf%2FbtsOmTxrlAR%2F5BKDyywapP3aH8kxRWeq41%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;798&quot; height=&quot;280&quot; data-origin-width=&quot;1992&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Arrays.contains()&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;타입 불일치로 인해 발생한 문제였음을 알게 되었고, 정확한 원인을 확인하기 위해 실제로 Array의 contains() 구현을 살펴봤습니다. 구현은 생각보다 단순했습니다. 기본 구현체의 Arrays는 배열 내의 시작부터 배열의 크기까지 해당 원소의 존재 유무를 판단합니다. 매번 탐색시 마다 O(N)의 시간 복잡도를 띄고 있습니다. 결국 equals()의 구현이 중요한 부분이기에, 각 클래스의 equals()를 다시 한 번 살펴봤습니다.&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;img style=&quot;text-align: center; caret-color: transparent; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot; src=&quot;https://blog.kakaocdn.net/dn/bSDgDT/btsOne9bhty/roZQi6Z2Z9bqphyJmZqMm1/img.png&quot; width=&quot;788&quot; height=&quot;379&quot; data-origin-width=&quot;1762&quot; data-origin-height=&quot;848&quot; data-is-animation=&quot;false&quot; /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Long.equals()&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Object의 equals()를 오버라이드한 Long 클래스의 equals()를 보면, 그리고 Int 클래스의 equals()를 보면 다 사진과 같은 구조로 해당 타입으로부터 타입 캐스팅이 가능한지에 대한 검증 이후 값 비교를 시작합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1816&quot; data-origin-height=&quot;264&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsOyC9/btsOlfBNlxn/EKoSXc8H73DMljCDtXLdlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsOyC9/btsOlfBNlxn/EKoSXc8H73DMljCDtXLdlK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsOyC9/btsOlfBNlxn/EKoSXc8H73DMljCDtXLdlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsOyC9%2FbtsOlfBNlxn%2FEKoSXc8H73DMljCDtXLdlK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;832&quot; height=&quot;121&quot; data-origin-width=&quot;1816&quot; data-origin-height=&quot;264&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 Int To Long, Long To Int 는 컴파일 시점에 타입 캐스팅이 안된다고 컴파일 에러를 나타내나, Object로 형변환을 한 후에 타입캐스팅을 해보면 False를 결과로 내뱉는 것을 테스트 코드를 통해 확인 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1990&quot; data-origin-height=&quot;934&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3ylQu/btsOneuzsrk/D9NLkel3Qyl2ZXTaWZ1e50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3ylQu/btsOneuzsrk/D9NLkel3Qyl2ZXTaWZ1e50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3ylQu/btsOneuzsrk/D9NLkel3Qyl2ZXTaWZ1e50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3ylQu%2FbtsOneuzsrk%2FD9NLkel3Qyl2ZXTaWZ1e50%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;834&quot; height=&quot;391&quot; data-origin-width=&quot;1990&quot; data-origin-height=&quot;934&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로, TypeCasting을 실패하여 &lt;b&gt;결과 값으로는 return false가 발생&lt;/b&gt;하고, 이는 &lt;b&gt;contains 함수의 결과 값도 false로 전파&lt;/b&gt;됩니다. 이에, 확장함수의 contains().not() 또한 &lt;b&gt;return true &lt;/b&gt;를 반환하게 된 것입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;회고&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;결국 이 문제는 휴먼 에러였지만, 작은 실수 하나가 큰 문제로 이어질 수 있음을 다시 한번 깨닫게 해줬습니다. 이전 유튜브에서 형변환을 잘못해주어서 5천만원... 5억..? 을 날린 개발자의 실수 라는 영상을 본 기억이 문득 떠올랐는데 저 또한 똑똑한 타입 캐스팅에 의존하다가 동일한 실수를 저지르고 있었음을 문득 깨달았습니다. 다행스럽게도 비용에 타격을 주는 코드가 아니여서 별 탈 없이 끝났음에 감사함을 느낍니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;무엇보다, 기본에 충실해야 하며 대부분의 장애는 시스템의 장애가 아닌 휴먼 에러임을 다시 한 번 깨닫습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Backend</category>
      <category>디버깅</category>
      <category>코틀린</category>
      <category>확장함수</category>
      <author>Bombo_</author>
      <guid isPermaLink="true">https://bombo96.tistory.com/135</guid>
      <comments>https://bombo96.tistory.com/135#entry135comment</comments>
      <pubDate>Sun, 1 Jun 2025 16:18:13 +0900</pubDate>
    </item>
    <item>
      <title>Outbox Pattern 의 이해와 구현 (1)</title>
      <link>https://bombo96.tistory.com/134</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;안녕하세요. 이번 글에서는 비동기 메시지 전송 시 데이터 일관성을 유지하기 위한 효과적인 접근법인 Outbox Pattern에 대해서 알아보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;해당 내용에 대한 예시코드는&lt;a href=&quot;https://github.com/bombo-dev/outbox-example&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; [깃허브 예시코드]&lt;/a&gt; 에서 확인 하실 수 있습니다.&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;Outbox Pattern이란?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Outbox Pattern(아웃박스 패턴)은 데이터베이스 트랜잭션과 메시지 브로커 간의 데이터 일관성을 보장하기 위해 사용하는 설계 패턴입니다. 특히 마이크로서비스 아키텍처(MSA)에서 데이터 변경 이벤트를 안전하게 외부 시스템에 안정적으로 전달하거나, 응답 여부가 크게 중요하지 않은 HTTP 비동기 호출 작업의 기록 추적에도 활용할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;Outbox Pattern의 필요성&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;먼저, 분산 시스템에서는 데이터베이스의 상태 변경과 메시지 브로커로의 이벤트 발행이 동시에 일어나야 하는 경우가 많습니다. 하지만 이 두 작업을 각각 별도의 트랜잭션으로 처리하면 다음과 같은 문제가 발생할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;데이터베이스에는 변경이 반영되었지만, 메시지 발행에 실패하는 경우 데이터 불일치 발생&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;메시지는 발행되었으나, 데이터베이스 트랜잭션이 롤백되어 잘못된 이벤트가 외부로 전달되는 문제&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이러한 문제를 해결하기 위해 Outbox Pattern을 사용하여 데이터와 메시지의 일관성을 보장할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메시지 브로커를 활용 할 수 없는 상황이라면 Kafka 처럼 메시지 발행의 로그를 남기는 역할을 가능하게 해줄 수 있으며, &lt;span&gt;또한 HTTP를 이용한 비동기 호출 방식의 경우, 네트워크 장애나 서비스 다운타임 등의 문제로 인해 메시지 손실률이 높아질 수 있습니다. Outbox Pattern은 이런 비동기 호출 방식의 메시지 손실 문제를 해결하여 데이터와 메시지의 일관성을 보장할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;Outbox Pattern의 동작 방식&lt;/span&gt;&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;데이터베이스 내에 별도의 Outbox 테이블을 만듭니다. 이 테이블은 발행할 메시지를 임시로 저장합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;비즈니스 로직 처리 중 데이터 변경이 이루어져 트랜잭션이 정상적으로 커밋되면, 이벤트 발행 이전에 Outbox 테이블에 데이터를 저장합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;별도의 프로세스가 Outbox 테이블을 주기적으로 조회(polling)하여 처리되지 않은 이벤트를 실제 메시지 브로커에 전달합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;메시지 전달이 성공하면 Outbox 테이블의 처리 완료 상태로 변경하거나, 실패 시 Failed 상태로 전환합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Failed 상태의 메시지는 비즈니스 정책 상 처리 할 수 있는 재시도 횟수를 지정하여 실패한 메시지를 읽어들여 재시도합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1748182954078&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE TABLE outbox (
        id bigint not null auto_increment PRIMARY KEY,
        message_id varchar(255) not null,
        payload varchar(255) not null,
        message_type varchar(255) not null,        
        fail_count integer not null,
        failed_at datetime(6),
        occurred_at datetime(6) not null,
        processed_at datetime(6),
        last_failed_reason varchar(255),
        status enum ('DONE','FAILED','WAITING') not null,
    )&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-pm-slice=&quot;1 3 []&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;일반적인 비동기 방식과의 비교&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Outbox Pattern은 기존의 일반적인 비동기 방식과 다음과 같은 차이점이 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;일반적인 비동기 호출 방식&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 메시지 전송과 데이터 변경이 독립된 트랜잭션으로 관리되어 실패 시 데이터 불일치 및 메시지 손실이 발생할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;Outbox Pattern&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 데이터 변경과 메시지 생성이 동일한 트랜잭션으로 묶여있어 데이터 일관성을 보장하고 메시지 손실 문제를 해결합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;Outbox Pattern의 장단점&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;장점&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;데이터베이스와 메시지 브로커 간의 일관성 보장&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;장애 상황에서도 메시지 손실 방지 및 재처리 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;마이크로서비스 환경에서 확장성 및 유연성 확보&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;단점&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;메시지 전달을 위한 별도의 프로세스를 운영해야 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Outbox 테이블의 크기가 관리되지 않으면 비대해질 수 있어 별도의 관리가 필요합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;사용 사례&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;결제 처리 시스템에서 결제 완료 이벤트 발송&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;주문 시스템에서 주문 상태 변경 이벤트 발송&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;재고 관리 시스템에서 재고 변동 이벤트 발송&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Backend</category>
      <category>데이터 정합성 관리</category>
      <category>아웃박스</category>
      <category>트랜잭션</category>
      <author>Bombo_</author>
      <guid isPermaLink="true">https://bombo96.tistory.com/134</guid>
      <comments>https://bombo96.tistory.com/134#entry134comment</comments>
      <pubDate>Sun, 25 May 2025 23:37:18 +0900</pubDate>
    </item>
    <item>
      <title>MySQL 복합 인덱스 순서 자동 지정, 이거 진짜에요?</title>
      <link>https://bombo96.tistory.com/133</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;배경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL에서 복합 인덱스를 지정하면 Where 절에는 복합 인덱스에 순서에 맞게 조건 절을 지정을 해주어야 의도한대로 동작하는 것으로 이해를 하고 있었습니다. 그런데, 어느 날 오랜만에 만난 개발자 지인분께서 갑자기 이런 말을 해주시는겁니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&quot;형, 그거 알아? 복합 인덱스를 지정하고 MySQL 컬럼 순서를 순서대로 지정하지 않아도 알아서 조건 절을 최적화해서 인덱스를 태울 수 있도록 바꿔준다?&quot;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진짜. 이 말이 나올 수 밖에 없다. &lt;b&gt;&quot;그게 진짜에요?&quot;&amp;nbsp;&lt;/b&gt;나도 모르는 사이에 놓쳤을 수 있고, 만약에 진짜 된다면 정말 좋은 기능이기에 얘기를 듣고 넘어가는 것이 아닌 직접 테스트를 진행해봤습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;환경 준비&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 테스트를 진행하기 위한 적절한 데이터 양을 가진 테이블이 필요하기때문에 TEST용 mysql schema를 찾아서 넣어주어야 했습니다. 찾아보니 깃허브에 &lt;a href=&quot;https://github.com/datacharmer/test_db&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/datacharmer/test_db&lt;/a&gt; 해당 깃허브에 Sample 데이터가 있는 것을 확인 할 수 있었고 Docker로 MySQL을 띄우고 employees.sql만 실행해주었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3716&quot; data-origin-height=&quot;172&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbi6sv/btsNRDRcfb1/3vKtuRuCTNt25ZepRVQuNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbi6sv/btsNRDRcfb1/3vKtuRuCTNt25ZepRVQuNk/img.png&quot; data-alt=&quot;테이블 정보&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbi6sv/btsNRDRcfb1/3vKtuRuCTNt25ZepRVQuNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbbi6sv%2FbtsNRDRcfb1%2F3vKtuRuCTNt25ZepRVQuNk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;817&quot; height=&quot;38&quot; data-origin-width=&quot;3716&quot; data-origin-height=&quot;172&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;테이블 정보&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1834&quot; data-origin-height=&quot;394&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/92dj9/btsNRIq705e/aJhqrRwoqBKoufu7vIYFG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/92dj9/btsNRIq705e/aJhqrRwoqBKoufu7vIYFG0/img.png&quot; data-alt=&quot;컬럼 정보&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/92dj9/btsNRIq705e/aJhqrRwoqBKoufu7vIYFG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F92dj9%2FbtsNRIq705e%2FaJhqrRwoqBKoufu7vIYFG0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;809&quot; height=&quot;174&quot; data-origin-width=&quot;1834&quot; data-origin-height=&quot;394&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;컬럼 정보&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재, 전체 데이터의 레코드 수는 299069개이고, 컬럼도 date 컬럼과 varchar 컬럼이 조합되어 테스트 해보기 아주 유용한 데이터임을 확인 할 수 있었습니다. 이제 테스트를 진행해보겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;테스트&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인덱스 X 테스트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 테스트를 하기 위해서 적절하게 가지고 올 수 있는 쿼리를 하나 만들었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히, 날짜 같은 경우에는 카디널리티가 적당히 높은 수준의 데이터 컬럼이기때문에 birth_date 컬럼을 중점적으로 적절한 수의 데이터를 가지고 와야 합니다. 너무 많은 데이터를 가지고오면 Optimizer가 자체적으로 Table Full Scan을 할 수도 있기 때문입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746951296249&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT COUNT(*) 
FROM employees 
WHERE birth_date BETWEEN '1950-01-01' AND '1952-12-31';&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1324&quot; data-origin-height=&quot;174&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dTFMQ8/btsNRd5SoHY/SR0GfYymVFp7ypcCzgoWn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dTFMQ8/btsNRd5SoHY/SR0GfYymVFp7ypcCzgoWn1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dTFMQ8/btsNRd5SoHY/SR0GfYymVFp7ypcCzgoWn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdTFMQ8%2FbtsNRd5SoHY%2FSR0GfYymVFp7ypcCzgoWn1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;829&quot; height=&quot;109&quot; data-origin-width=&quot;1324&quot; data-origin-height=&quot;174&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;약 7% 정도의 데이터를 가지고 오는 적절한 쿼리 인 것 같습니다. 이제 EXPLAIN ANALYZE를 수행하여 쿼리 탐색 결과를 한 번 살펴보도록 하겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746951316697&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;EXPLAIN ANALYZE SELECT * 
FROM employees 
WHERE birth_date BETWEEN '1950-01-01' AND '1952-12-31';&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2036&quot; data-origin-height=&quot;262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/coa15j/btsNRKbmaOS/aiOSPWRpkKdnOvmUAFXcYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/coa15j/btsNRKbmaOS/aiOSPWRpkKdnOvmUAFXcYk/img.png&quot; data-alt=&quot;테스트 1 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/coa15j/btsNRKbmaOS/aiOSPWRpkKdnOvmUAFXcYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcoa15j%2FbtsNRKbmaOS%2FaiOSPWRpkKdnOvmUAFXcYk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;822&quot; height=&quot;106&quot; data-origin-width=&quot;2036&quot; data-origin-height=&quot;262&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;테스트 1 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재는 인덱스가 없기 때문에, Table Full Scan을 통해서 데이터를 처리해서 가지고 오는 것을 볼 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인덱스 생성 후 테스트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제, 인덱스를 한 번 추가해보도록 합시다. 실무에서는 주로 기간 별로 데이터를 조회해서 가지고 오는 쿼리가 많기 때문에 birth_date를 선행 인덱스 컬럼으로 지정하고, first_name을 후행 인덱스 컬럼으로 지정하여 인덱스를 생성해보도록 하겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746951388000&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE INDEX employees_birth_date_first_name_index ON employees (birth_date, first_name);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;100&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dLAIWD/btsNRLOQorR/7DGeQvaNkyskWgR1akvzZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dLAIWD/btsNRLOQorR/7DGeQvaNkyskWgR1akvzZ1/img.png&quot; data-alt=&quot;인덱스 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dLAIWD/btsNRLOQorR/7DGeQvaNkyskWgR1akvzZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdLAIWD%2FbtsNRLOQorR%2F7DGeQvaNkyskWgR1akvzZ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;832&quot; height=&quot;61&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;100&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;인덱스 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스가 생성되었으므로, 이전과 동일하게 EXPLAIN ANALYZE 를 해보도록 하겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3786&quot; data-origin-height=&quot;378&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQQDNE/btsNTmtraqS/dB3sxhFwRs5LreNTk43tR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQQDNE/btsNTmtraqS/dB3sxhFwRs5LreNTk43tR0/img.png&quot; data-alt=&quot;인덱스 범위 스캔&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQQDNE/btsNTmtraqS/dB3sxhFwRs5LreNTk43tR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQQDNE%2FbtsNTmtraqS%2FdB3sxhFwRs5LreNTk43tR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;830&quot; height=&quot;83&quot; data-origin-width=&quot;3786&quot; data-origin-height=&quot;378&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;인덱스 범위 스캔&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제, 인덱스 범위 스캔이 이루어지고 있음을 확인 할 수 있습니다. 그럼 이어서, 복합 인덱스를 걸어두었으니 first_name으로 'Bezalel'인 사람을 기존 복합 인덱스 순서대로 조회하여 쿼리 분석을 해보도록 하겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746952351984&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;EXPLAIN ANALYZE
SELECT *
FROM employees
WHERE birth_date BETWEEN '1950-01-01' AND '1952-12-31' AND first_name = 'Bezalel';&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3774&quot; data-origin-height=&quot;332&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cOUpoy/btsNSvkffie/JvhiURZLYmk4QJKLZesQYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cOUpoy/btsNSvkffie/JvhiURZLYmk4QJKLZesQYk/img.png&quot; data-alt=&quot;복합 인덱스 순서에 맞는 쿼리 조회 테스트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cOUpoy/btsNSvkffie/JvhiURZLYmk4QJKLZesQYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcOUpoy%2FbtsNSvkffie%2FJvhiURZLYmk4QJKLZesQYk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;826&quot; height=&quot;73&quot; data-origin-width=&quot;3774&quot; data-origin-height=&quot;332&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;복합 인덱스 순서에 맞는 쿼리 조회 테스트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 알고 있던 사실과 동일하게 적절하게 Index 범위 스캔을 통해 데이터를 가지고 오고 있는 것을 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼, 이제 WHERE 조건 절의 순서를 바꿔서 시도해보도록 하겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;복합 인덱스 순서와 다르게 조건절 추가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, birth_date 와 first_name의 순서를 변경해보도록 하겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746952550005&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;EXPLAIN ANALYZE
SELECT *
FROM employees
WHERE first_name = 'Bezalel' AND birth_date BETWEEN '1950-01-01' AND '1952-12-31';&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3786&quot; data-origin-height=&quot;346&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boIcr8/btsNReX5BbE/JKtbI0k1TRqGEfH7zE6HBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boIcr8/btsNReX5BbE/JKtbI0k1TRqGEfH7zE6HBk/img.png&quot; data-alt=&quot;복합 인덱스 순서를 다르게&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boIcr8/btsNReX5BbE/JKtbI0k1TRqGEfH7zE6HBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboIcr8%2FbtsNReX5BbE%2FJKtbI0k1TRqGEfH7zE6HBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;827&quot; height=&quot;76&quot; data-origin-width=&quot;3786&quot; data-origin-height=&quot;346&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;복합 인덱스 순서를 다르게&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진짜입니다..! 정말로 복합 인덱스의 순서와 조건 절의 순서가 달라도 동일하게 범위 인덱스를 통해서 데이터를 조회하는 것을 볼 수 있습니다. 그렇다면, 중간에 카디널리티가 낮은 성별을 추가한다면 어떻게 되는지 문득 궁금해졌습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746952718357&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;EXPLAIN ANALYZE
SELECT *
FROM employees
WHERE first_name = 'Bezalel' AND gender = 'M' AND birth_date BETWEEN '1950-01-01' AND '1952-12-31';&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3782&quot; data-origin-height=&quot;370&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdj2Cd/btsNSO4RiAt/f67g7n3eYWf3tK5OmmqDL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdj2Cd/btsNSO4RiAt/f67g7n3eYWf3tK5OmmqDL0/img.png&quot; data-alt=&quot;복합 인덱스 중간에 다른 컬럼&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdj2Cd/btsNSO4RiAt/f67g7n3eYWf3tK5OmmqDL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbdj2Cd%2FbtsNSO4RiAt%2Ff67g7n3eYWf3tK5OmmqDL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;830&quot; height=&quot;81&quot; data-origin-width=&quot;3782&quot; data-origin-height=&quot;370&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;복합 인덱스 중간에 다른 컬럼&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이럴수가... 이 조차도 Engine이 Optimize하여 범위 스캔을 통해서 데이터를 먼저 가지고 오고 난 이후 조회를 시도&lt;/b&gt; 한 것을 확인 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;복합 인덱스가 있을 때 조건 절의 순서를 다르게 하더라도 ENGINE이 자체적으로 쿼리를 최적화해서 인덱스를 태울 수 있도록 해주고 있다&lt;/b&gt;는 부분을 확인 할 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 다음과 같은 질문을 하거나 이런 생각이 들 수도 있을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;text-align: left;&quot;&gt;&quot;이제, 그럼 쿼리를 작성 할 때 인덱스 순서에 맞게 WHERE 절을 작성하지 않아도 되겠다!&quot;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론, 현재는 데이터의 컬럼 개수도 적고 데이터 수 자체도 적어서 큰 문제는 없을 것 같으나 결국 최적화라는 것은 ENGINE 자체적으로 구성된 알고리즘을 통해서 최적화가 되는 것일텐데, 의도하지 않은 동작이 일어날 가능성은 배제 할 수 없지 않을까? 라는 생각이 한 편으로는 듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 가장 큰 이유는 아무래도 해당 사실을 모르고 있는 개발자가 쿼리를 봤을 때는 복합 인덱스의 순서랑 WHERE 절 순서가 다른데!?!? 하면서 애플리케이션 로직의 Query를 변경하는데 리소스를 사용할 수도 있기 때문입니다. 편하게 최적화해서 동작해주기는 하지만 개발은 혼자하는 것이 아니고 같이 하는 것이고 암시적이기보다는 명시적인 것이 더 명확하기에 그대로 사용하는게 좋지 않을까 싶습니다~&lt;/p&gt;</description>
      <category>Backend/MYSQL</category>
      <category>MySQL</category>
      <author>Bombo_</author>
      <guid isPermaLink="true">https://bombo96.tistory.com/133</guid>
      <comments>https://bombo96.tistory.com/133#entry133comment</comments>
      <pubDate>Mon, 12 May 2025 08:16:12 +0900</pubDate>
    </item>
    <item>
      <title>맹그로브 고성을 다녀오며</title>
      <link>https://bombo96.tistory.com/132</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;2025년 4월 26일 맹그로브 고성에 다녀왔다.&lt;br /&gt;다녀오던 지인, 친구들마다 꼭 가봐야 하는 곳이라 하여 버킷 리스트에 담아두고 있던 공간인데, 최근 지인이 공유해주신 고성의 파도가 내 마음을 크게 자극하기도 했고, 지금이 딱 다녀오기 좋은 시기라고 생각해서 다녀왔다. 이유는 모르겠다. 그냥 지금 가면 왠지 좋을 것 같은 느낌이었다. 본래 계획은 5월 연휴에 맞춰서 가려고 했었으나 생각보다 가격이 만만치 않아서 한 주 땡겨서 가게 되었다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;여행에서의 목적은 머릿속을 어지럽히고 있는 수많은 발산되고 있는 내용들을 정리하기도 하고, 사색을 좀 하고 싶었다. 요즘 혼자만의 시간을 많이 가지고 있기도 하고, 사람들 간의 관계 속에서 나라는 사람을 점차 알게 되고 있기 때문에 내면에서 정의 내리지 못한 것들이 상당히 많이 있었기 때문이었다. 늘 바다에 갔을 때는 이러한 고민들을 넓은 바다와 파도가 모든 고민들을 다 이해하고 받아들이며 흘려보내겠다는 느낌이 들었다. 이번에도 모든 고민들을 해결할 수 있을 것만 같은 기분이 들었다. 그렇게 이번 여행은 음식점도 알아보지 않고, 그 어떠한 주변의 휴양지를 알아보지 않았다. 그냥 이번 여행은 계획 없이 그 순간 나에게 떠오르는 것들을 따라 가기로 했다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;정말 우연찮게도 원래 알던 지인분들이 같은 날짜에 가게 된다는 것을 지인을 통해서 듣게 되었다. 원래는 무계획 여행이기에 버스를 타고 걸어 다니며 시간을 보내려고 했는데, 같은 날짜에 가시는 분들 모두 대중교통을 이용한다고 하셔서 운전을 할 수 있기도 하고 나에게는 비용이 너무 아까운 것 같아서 픽업을 해주기로 했다. 픽업만 해드리고 맹그로브에서는 사색을 즐기며 시간을 보낼 계획이었지만, 드라이브를 하는 내내 같이 가시는 분들의 이야기가 너무 즐겁고 너무 흥미로웠다. 아마 이때부터였던 것 같다. &amp;ldquo;이 분들과 대화를 나누다 보면 자연스럽게 내가 가지고 있는 고민들을 같이 이야기하며 해소 될 수 있겠다.&amp;ldquo;라는 생각이 자리를 잡은게 말이다. 가는 길에 평소보다 새똥을 많이 맞았는데 긍정적으로 길조라고 생각하기로 했다. 그리고 정말 좋은 일들이 많이 일어났다!&lt;br /&gt;&amp;nbsp;&lt;br /&gt;사촌들과 여행을 다니듯이 휴게소에 들러 가볍게 휴게소를 구경했다. 날이 좋아서 인지 많은 가족들이 나들이를 온 듯 했는데, 가족끼리 나들이를 온 그 광경도 참 좋았다. 언제 가족들과 휴게소에 들렀었는지 기억이 가물가물하다. 다음에는 내가 여행을 데려다 드리며 휴게소에 들러서 군것질을 해보는 건 어떨까 생각하기도 했다. 출발 전 지인분들께서 맹그로브에 가서 먹어야 할 음식을 추천받으셨다고 하셔서 고성에 있는 녹원식당으로 방향을 정하고 다시 출발하게 되었다.&lt;br /&gt;그리고, 도착한 그 음식점에서 한 번도 먹어보지 못한 가오리찜을 먹게 되었다. 세상에... 생선이 이렇게 부드러울 수가 있는가? 결대로 떨어지는 살이며, 약간 매콤하면서도 자극적인 양념은 아직도 너무나도 인상 깊게 남아있다. 생선과 함께 한 그릇... 한 공기를 추가하여 양념장에 반 그릇... 다음에 부모님과 고성을 온다면 꼭 부모님께 대접하고 싶다는 생각이 들만큼 너무나도 맛있는 식사였다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;행복한 식사를 마치고 나온 길 위에서 골목 저편을 보니 바다가 보였다. 녹원식당이 맹그로브의 숙소까지 거리가 어느 정도 되는지 몰라 그 순간을 담고 싶어 지인분들께 잠깐 바다를 보러 가자고 했고, 동해의 바다 색은 늘 그렇듯 보석같이 빛났다. 푸른 바다와 지인들의 뒷모습과 함께 들리는 웃음소리는 일주일이 지난 지금도 인상 깊은 장면으로 뇌리에 박혀있다. 눈에 담긴 그 광경은 참으로 아름답다라고 말할 수 있는 광경이었다. 그런 장면을 사진으로 찍고 건네주어 한편으로는 뿌듯하기도 했다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;이어서 맹그로브로 이동을 하려고 보니 식당이 도보로 5분 거리에 있었다는 것을 알게 되었다. 다들 어이없다는 듯이 함께 웃으며 맹그로브로 향하였다. 금방 맹그로브에 도착하고 각자가 정해진 숙소에서 짐을 풀고 이따 만나기로 했다. 숙소에 도착하자마자 왜 주변 지인들이 모두 이곳을 추천하는지 알 수 있었다. 탁 트인 공간과 사람이 많지 않은 바다를 숙소 내부에서 바라볼 수 있는 경험은 정말 쉽게 할 수 있는 경험은 아니었다. 발코니 공간에 테이블도 있어서 파도 소리와 넓고 푸른 바다를 바라보며 독서 혹은 개인 작업을 할 수 있는 환경 또한 마련되어 있었는데, 선선한 바람과 자연의 파도 소리와 함께 즐기는 독서의 경험이 너무나도 좋았다.&lt;br /&gt;그리고, 벚꽃이 지고 있는 계절이어서였는지 종종 책 위로 벚꽃이 떨어졌는데 그 떨어지는 벚꽃 조각들이 책에 묻어 있는 것이 너무나도 예쁘게 기억이 남았다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;나는 불멍보다는 물멍을 더 좋아하는 것 같다. 불멍은 내 안에 있는 것을 없애기 위해 태우는 느낌이라면, 물멍은 내 안에 있는 것을 유연하게 받아들이는 느낌이 들어서인 것 같다. 특히, 물 자체가 모든 것을 수용한다는 상이 많이 느껴지기 때문일지도 모르겠다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;같이 오신 지인분들도 시간이 지나면서 바다를 보러 나오셨고, 같이 바다를 보며 얘기도 나누고 사진도 찍어드렸다! 숙소에 올라가 커피를 마시며 책을 읽는데, 지인분들께서 슬슬 바다에 모이는 모습을 발코니에서 구경할 수 있었다. 숙소의 위치가 높다 보니 위에서 바라보는 그 광경은 모래사장에서 마치 오리 가족이 걸어가는 것처럼 보였다. &amp;lsquo;귀염뽀짝&amp;rsquo;이라는 단어가 딱 떠올랐다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;그러면서 은연중에 차를 타고 오면서 복권 이야기를 했는데, 우리 외가댁은 가족끼리 모일 때마다 늘 즉석 복권과 로또 복권을 몇 장 사곤 한다. 그리고 그 자리에서 모두가 함께 복권을 긁으며 서로의 당첨을 축하해주기도 하고 놀리기도 한다. 적은 금액으로 서로가 공통된 주제 공간 안에 같이 머물며, 서로가 축하하고 웃고 즐길 수 있는 순간은 너무나도 소중한 경험이었다.&lt;br /&gt;이 경험을 같이 놀러온 지인분들께 꼭 경험시켜드리고 싶었다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;그런 이유로 즉흥적으로 복권 가게를 찾아서 즉석 복권과 로또 복권을 사오기도 했다. 당일 밤에는 로또 당첨 결과를 같이 확인하기도 하고, 맹그로브를 떠나는 당일에는 다 같이 즉석 복권을 긁기도 했다. 한 분은 로또 5등에 당첨되기도 하시고, 나는 무려 즉석 복권 1만 원에 당첨되는 기염을 토해내기도 했다. 즉석 복권 1만 원이라니... 이 또한 처음 겪어보는 경험이었다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;첫날 저녁, 본래 가고자 했던 음식점에 생각보다 좌석이 적어 가지 못해서 주변에서 부랴부랴 음식점들을 찾기 시작했다. 그렇게 도착한 봉포머구리집에서 물회와 오징어순대를 시켜 먹었다. 물회에서 늘 세꼬시는 내 취향에 맞지 않는 것 같다. 이젠 정말 물회를 시켜 먹을 때 세꼬시 빼고를 꼭 얘기를 해야겠다! 먹기에 조금 불편했을 뿐이지 물회는 너무 맛있었다!!! 오징어순대는 생각보다 양이 많아서 놀랍기도 했다. 아버지의 고향이 동해여서 어린 시절에 오징어 물회를 사주셨던 경험이 너무 좋아서 오징어 물회를 먹고 싶었는데, 요즘 오징어가 진짜 금값이다.&lt;br /&gt;오징어 물회만 싯가다... 다음에는 투자를 해서라도 꼭 먹어봐야지. 일 열심히 해야겠다!!!&lt;br /&gt;&amp;nbsp;&lt;br /&gt;저녁을 먹으면서도 또 ENTX의 특성인 무한 발산을 하며 대화를 나누고 숙소로 돌아와 아쉬워서 더 얘기를 나누기로 했는데, 최근에 배운 복분자, 갈배 조합을 소개해주고 싶었다. 이후에 알게 된 사실은 갈배 사이다였어야 했다... 뒤늦은 깨달음으로 얘기를 못했다.ㅎㅎ 갈배 사이다는 아니었지만 다행히도 맛있게 먹어주셔서 정말 다행이었다! 그리고 지인분께서 시음을 하며 얻게 된 딸기 맥주를 챙겨오셨는데, 숙소에 병따개가 없어서 숟가락으로 따려고 시도했는데, 한 번에 따지 못해 압 차이로 인해 기포가 전부 빠져버렸다...ㅠㅠㅠㅠ&lt;br /&gt;하지만, 갑자기 맥주가 터지며 다 같이 수습을 하는 그 과정이 너무 웃기고 재밌는 장면이었다. 조금 남은 딸기 맥주를 먹었는데 조금 달달한 것이 탄산이 빠지지 않았었다면 정말 엄청 맛있었을 텐데 조금 아쉬웠다. 사실 제일 서럽고 아쉬운 건 딸기 맥주를 가져오신 그 지인분이시겠지... 다음에 따로 지인분께 물어보고 사 먹어봐야겠다~!&lt;br /&gt;&amp;nbsp;&lt;br /&gt;그날 밤은 밤새 많은 이야기들로 꽃을 피웠다. 평소에 할 수 없던 다소 깊은 이야기들, 어디선가 얘기하면 분위기가 싸해질 수 있는 철학 이야기들&amp;hellip; 서로가 각자의 관점에서 이해하고 얘기하며 정말 쉴 새 없이 이야기를 했다. 그리고 그 과정 속에서 내가 머릿속에서 정리되고 있지 않던 고민들, 그리고 다소 받아들이기 어려웠던 감정들을 고스란히 받아들일 수 있게 되었다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;다소 늦은 시간인 새벽 2시 30분까지 대화를 나누며 숙소로 돌아와 씻으면서 다음과 같은 생각을 하게 되었다. 내가 맹그로브에 혼자 와서 사색을 하고 있었다면 어땠을까를 생각해봤다. &amp;ldquo;과연 내가 나의 생각을 정리할 수 있었을까? 정의를 내리지 않겠다는 결론을 내릴 수 있었을까? 나의 내면을 깊숙이 들어가면서도 적당한 수준에서 빠져나올 수 있었을까?&amp;rdquo; 정답은 모르겠다. 하지만 지레짐작하기에 그 대화를 하면서 그 경험을 통해 결론을 내릴 수 있었던 것이니, 아마도 쉽지는 않았을 것 같다. 할 수 있다고 하더라도 더 오랜 시간이 필요했을 것 같다. 그렇게 편안한 마음으로 내일 있을 일출을 기대하며 침대에 누웠다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;나는 지금까지 여러 핑계들로 일출을 보지 않은 적이 많다. &amp;ldquo;새해에 일출을 보러 가면 사람이 많아서 불편할 것 같다.&amp;rdquo;, &amp;ldquo;아침에 그렇게까지 일찍 일어나서 보러 가기가 힘들 것 같다.&amp;rdquo;, &amp;ldquo;바쁜 일정이 있어서 그날 일출 보기는 힘들 것 같아.&amp;rdquo; 등등&amp;hellip; 지금 생각해보면 다 그 시점에 너무 귀찮고 힘들어서였던 것 같다. 아니, 일출을 보러 간다는 그 잠깐의 여유를 즐길 만한 마음의 여유가 없었던 것 같기도 하다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;강원도의 일출 시간은 5시 30분이라고 지인분들께서 알려주셨고, 내일 같이 일출을 보자고 약속하며 잠에 들 준비를 했다. 혹시나 5시 30분보다 일찍 뜨지 않을까 고심하며 5시에 알람을 맞춰두었지만, 분명 알람을 들었는데 끄고 다시 잠들고 말았다. 하지만 신께서 한 번의 기회를 주신 것일까. 다시 일어나니 5시 33분이었고, 혹시나 일출을 놓쳤을까 하는 생각에 심장은 크게 요동치기 시작했다. 부랴부랴 급하게 창문 밖을 쳐다봤다. 해가 보이지 않았지만, 붉게 물든 노을빛이 보였다. 이미 &amp;ldquo;해가 뜨고 구름 사이로 숨어버린 것일까, 아직 해가 뜨지 않고 노을만 있는 것일까&amp;rdquo; 생각하며 바다로 나갔다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;지인분들께서는 어제 많은 즐거운 이야기들로 피곤하셨던 것일까. 답변이 없었다. 그래도 아침바다와 노을이라는 이 아름다운 광경을 사진에라도 담아 전달하기 위해서 열심히 찍겠다는 책임감을 가지고(?) 사진을 찍고 있던 와중, 아침의 태양이 서서히 모습을 비추기 시작했다. 매우 정열적인 붉은 빛을 띠며 느린 속도로 천천히 올라왔다. 아버지가 &amp;ldquo;태양은 생각보다 눈 깜짝할 사이에 올라온다&amp;rdquo;고 하시더니, 정말 서서히 그리고 빠르게 태양이 올라오고 있었다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;인생 처음으로 보는 일출이었는데, 일출을 볼 때 고양감을 느낄 수 있다고 하더니 정말 뭔지 모를 고양감이 올라왔다. 그렇게 열심히 사진과 영상을 남기고 다시 잠깐 잠을 청하러 들어갔고, 그런 고양감 때문일까. 쉽게 잠에 들지는 못했다. 뒤척거리다 오전 11시에 맹그로브에서 퇴실을 해야 하여 평소보다 조금 빠른 시간에 일어나 다시 활동을 시작했다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;다시 한 번 오전의 바닷바람을 느끼고 바다를 바라보며 책을 읽을 수 있는 워케이션 공간으로 들어갔다. 바다를 바라보며 안마의자에 앉아 몸을 이완시키고 독서를 하며, 가져온 책을 다 읽고 그래도 공간을 잘 이용하기 위해서 본래 목적에 포함해두었던 작업 공간에서 작업을 해보려고 했다. 그런데 작업 공간 자체는 바다를 볼 수 없으니, 뭔가 그냥 회사에 온 듯한 느낌이었다. 그렇게 1시간 정도 작업을 했을까. 시간이 아깝다는 생각이 들었다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;2박 3일이라면 모르겠지만, 1박 2일 동안 여기 와서 몸으로 느끼고 체험할 것들이 정말 많은데, 일을 하고 있기에는 너무나 아까운 시간을 보내고 있다는 생각이랄까. 그렇게 다시 바다로 나가 바닷물에 발을 담가 잠깐 동안의 사색을 즐겼다. 바닷물이 조금 차기는 했지만, 금세 적응이 되었고 바다 속 안에 있는 미역들, 그리고 돌들, 그리고 같이 조개를 캐러 오기 위해 놀러온 가족들을 바라보며 따뜻한 에너지와 정적인 에너지를 같이 채울 수 있었다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;시간이 지나 같이 오신 지인분들과 함께 아침을 먹으러 가기로 했다. 아침은 근처에 있는 수제비집이었다. 이 또한 추천받은 집이고 꼭 먹어보면 좋다고 하여 갔는데...! 일요일 휴무였다. 보통의 나라면 휴무라는 사실을 알아보지 않은 것에 스트레스를 받고 있었겠지만, 무계획의 여행이었다. 바로 옆에 다른 가게가 있었는데, 지인분들과 &amp;ldquo;아이고 아쉽네~ 다음에 또 와야겠네~&amp;rdquo; 하며 웃음으로 넘기고 바로 옆집으로 들어갔다. 그렇게 생각지도 못한 가게에 들어가 아침 식사를 했다. 고성 해물순두부라는 가게였는데, 음식이 너무나도 맛있었을 뿐만 아니라 주인장님도 친절하셨다. 정말 시골에서만 느낄 수 있는 그런 따뜻한 정이었다. 다음에도 또 오고 싶다는 생각이 들 정도로 말이다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;식사를 마치고 같이 오신 지인분들께서 워케이션 공간을 제대로 즐기지 못해 아쉬워하여 같이 워케이션 공간을 이용하다가 카페를 가기로 했다. 워케이션 공간을 이용하던 중 나에게 맹그로브에 오도록 계기를 제공해준 분의 후기를 보게 되었다. 그분의 글 속에서는 뭐랄까, 늘 그때의 감정이 잘 느껴진다. 글 속에서 상황이 그려진달까. 배우고 싶기도 하고 신기한 능력이다. 나도 그래서 후기를 남기고 왔다. 후기는 큐레이션을 통해 전시된다고 하는데, 작성하다 보니 아직 나의 글 솜씨는 한참 멀었다는 생각을 했다. 만약 전시된다면 운이 엄청 좋아서 였을 것이다~&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;몰입하며 다른 책을 독서하다가, 아침 잠이 부족해서였을까. 차에서 잠깐 눈을 붙일까 하던 찰나에 지인분께서 마침 같은 생각을 가지고 계셔서 차로 가서 휴식을 취했다. 같이 휴식을 취하던 분 또한 당시의 느낌을 글로써 잘 표현하시는 사람이다. 늘 보고 배우는 게 참 많은 분이다.&lt;br /&gt;자신의 감정을 있는 그대로 바라보는 것 또한 잘하시는 분인데, 차 안에서 잠깐의 시간 동안 지인분을 통해 대화를 나누며 좀 더 내면을 바라볼 수 있게 되었다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;뭐랄까. 나는 따뜻해지고 싶었다. 하지만 그러한 따뜻함으로 인해서 받은 상처 또한 적지 않았다. 그런 따뜻함을 제공해주었을 때 되돌아오는 배신감. 그것이 트라우마로 자리를 잡아 계속 되돌아가는 그 과정에서 반발력이 있었던 것 같다. 요즈음에는 그렇다. 지금 있는 그 공간과 그 사람들만큼은 괜찮을 거라고, 나는 아직도 사람을 잘 믿고 사람을 좋아하는 사람이라는 것을 새삼 깨닫는다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;그렇게 시간이 지나 여러 카페를 돌아다니다가 통창으로 바다를 볼 수 있는 &amp;lsquo;아야트 커피&amp;rsquo;에 도착했다. 2시쯤 도착하여 5시까지 여러 가지 주제로 대화를 나누었는데, 대화를 나누다 지인분께서 나에게 별명을 하나 만들어주셨다. 스윗스위스문, 친구들과의 관계에서 다정하게 늘 중립적인 역할을 지키며 조율을 해주는 나에게 붙여준 별명이다. 나는 이 별명이 참 좋다. 나라는 사람의 가치관을 나타내는 별명이라고 생각해서인 것 같기도 하다~!&lt;br /&gt;&amp;nbsp;&lt;br /&gt;신이 나게 대화를 나누고, 2박 3일을 보내시는 분들이 계셔서 다시 숙소로 데려다드리고 복권을 긁었다. 복권을 긁는 모두의 표정이 너무 재밌었다. 내가 생각했던 그 표정이다. 그 표정을 바라보는 것이 너무 행복했다. 기대한 것보다 더 다채로운 표정들과 감정들이 다가왔다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;아쉬움을 뒤로 한 채, 차를 타고 다시 집으로 향했다. 시간이 조금 늦어서였는지 차는 막히지 않고 구름은 맑았다. 그리고 돌아가는 차 안에서도 정말 많은 이야기를 나누고, 중간에 배가 고파 휴게소에 들러 오랜만에 휴게소 음식을 먹었다. 늘 익숙했던 휴게소 음식의 맛을 생각했는데, 생각보다 휴게소 음식의 맛이 너무 맛있었다. 이제는 휴게소 특유의 음식 맛이라고 할 수 없을 정도이다. 맛있는 식사를 마치고 이제 정말 여행의 끝을 맞이하기 위하여 다시 운전을 시작했다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;그렇게 두 분을 판교역에 내려드렸다. 집으로 향하던 길에 이번 여행은 참 좋은 여행이었다라는 생각이 머리 속에 떠다녔다. 지인분들에게는 내 인생에서 가장 즐거운 여행 다섯 손가락 안에 들 것 같다고 말했지만, 단연코 이번 여행은 나에게 있어서 가장 즐거웠던 여행 TOP 1이었다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;이번 여행은 예상하지 못했던 우연과 그 우연들이 쌓이며 만들어낸 연결 속에서, 수많은 행복한 경험들을 할 수 있는 여행이었다. 혼자 사색과 공간을 유영하기 위해 떠난 여행에서, 우연찮게 아는 분들과 일정이 겹치게 되어 다 같이 가게 되었는데, 예상하지 못했던 우연이 본래 계획보다 더 좋은 경험들을 나에게 가져다주었다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;나에게 이런 좋은 경험들을 할 수 있도록 보이지 않는 곳에서 많은 자극을 준 지인들. 그리고 원래 계획되지 않은 상황 속에서 많은 즐거움과 경험들을 가져다 준 좋은 지인분들. 마지막으로, 이 세상에서 이런 생각들을 하는 것이 이상한 것이 아니라는 확신을 주게 된 사람들에게 너무나도 감사하다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;이런 생각과 경험을 꼭 간직하고 싶다. 나중에 자식이 태어난다면 꼭 전달해주고 싶다. 그만큼이나 인생에서 소중한 경험이었다.&lt;br /&gt;마지막으로, 글 속에 담긴 내용들을 사진으로 남긴다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1081&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m60oJ/btsNKpYStlj/B0XfmsXpQudPo7yIekT2kk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m60oJ/btsNKpYStlj/B0XfmsXpQudPo7yIekT2kk/img.jpg&quot; data-alt=&quot;녹원식당&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m60oJ/btsNKpYStlj/B0XfmsXpQudPo7yIekT2kk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm60oJ%2FbtsNKpYStlj%2FB0XfmsXpQudPo7yIekT2kk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;760&quot; height=&quot;571&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1081&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;녹원식당&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/esjRRT/btsNKztCQrG/EpEmIh34I2jtl59EBRmMI1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/esjRRT/btsNKztCQrG/EpEmIh34I2jtl59EBRmMI1/img.jpg&quot; data-alt=&quot;고성에 도착하며&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/esjRRT/btsNKztCQrG/EpEmIh34I2jtl59EBRmMI1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FesjRRT%2FbtsNKztCQrG%2FEpEmIh34I2jtl59EBRmMI1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;740&quot; height=&quot;987&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;고성에 도착하며&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYSwT3/btsNJKpc8UE/kweYAGREikyjW8V7iPqIVK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYSwT3/btsNJKpc8UE/kweYAGREikyjW8V7iPqIVK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYSwT3/btsNJKpc8UE/kweYAGREikyjW8V7iPqIVK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYSwT3%2FbtsNJKpc8UE%2FkweYAGREikyjW8V7iPqIVK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;743&quot; height=&quot;557&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;1562&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mWt76/btsNLAef9sc/GfKckODJUUS0xP3BcFWsK0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mWt76/btsNLAef9sc/GfKckODJUUS0xP3BcFWsK0/img.jpg&quot; data-alt=&quot;맹그로브 고성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mWt76/btsNLAef9sc/GfKckODJUUS0xP3BcFWsK0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmWt76%2FbtsNLAef9sc%2FGfKckODJUUS0xP3BcFWsK0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;739&quot; height=&quot;985&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;1562&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;맹그로브 고성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dBXslV/btsNK0drWOv/jTX3uPkoma9gQqIZpn7a8k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dBXslV/btsNK0drWOv/jTX3uPkoma9gQqIZpn7a8k/img.jpg&quot; data-alt=&quot;벚꽃잎이 떨어진 책&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dBXslV/btsNK0drWOv/jTX3uPkoma9gQqIZpn7a8k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdBXslV%2FbtsNK0drWOv%2FjTX3uPkoma9gQqIZpn7a8k%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;749&quot; height=&quot;999&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;벚꽃잎이 떨어진 책&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9nUa1/btsNJ3hG6cR/IkYYbtCWmsncGcKOHkgLek/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9nUa1/btsNJ3hG6cR/IkYYbtCWmsncGcKOHkgLek/img.jpg&quot; data-alt=&quot;노을이 지는 고성 바다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9nUa1/btsNJ3hG6cR/IkYYbtCWmsncGcKOHkgLek/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9nUa1%2FbtsNJ3hG6cR%2FIkYYbtCWmsncGcKOHkgLek%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;745&quot; height=&quot;993&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;노을이 지는 고성 바다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bv6TK2/btsNJUrB9Ig/6x1jf8cRiMbzo4CeIlzuJ0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bv6TK2/btsNJUrB9Ig/6x1jf8cRiMbzo4CeIlzuJ0/img.jpg&quot; data-alt=&quot;서로의 사진을 찍어주는 지인들&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bv6TK2/btsNJUrB9Ig/6x1jf8cRiMbzo4CeIlzuJ0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbv6TK2%2FbtsNJUrB9Ig%2F6x1jf8cRiMbzo4CeIlzuJ0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;736&quot; height=&quot;981&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;서로의 사진을 찍어주는 지인들&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c9JYGK/btsNLZEShq2/FywskMlWDKwcZpMozKUo41/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c9JYGK/btsNLZEShq2/FywskMlWDKwcZpMozKUo41/img.jpg&quot; data-alt=&quot;봉포 머구리 물회와 오징어 순대&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c9JYGK/btsNLZEShq2/FywskMlWDKwcZpMozKUo41/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc9JYGK%2FbtsNLZEShq2%2FFywskMlWDKwcZpMozKUo41%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;719&quot; height=&quot;959&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;봉포 머구리 물회와 오징어 순대&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sKzxZ/btsNLCC8q33/eRL91VbmAR1yvVKudgi3Yk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sKzxZ/btsNLCC8q33/eRL91VbmAR1yvVKudgi3Yk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sKzxZ/btsNLCC8q33/eRL91VbmAR1yvVKudgi3Yk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsKzxZ%2FbtsNLCC8q33%2FeRL91VbmAR1yvVKudgi3Yk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;723&quot; height=&quot;723&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1081&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xk5dm/btsNK2vzUIw/eiw8TDiaAtUI76lhudKvuk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xk5dm/btsNK2vzUIw/eiw8TDiaAtUI76lhudKvuk/img.jpg&quot; data-alt=&quot;일출 사진&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xk5dm/btsNK2vzUIw/eiw8TDiaAtUI76lhudKvuk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxk5dm%2FbtsNK2vzUIw%2Feiw8TDiaAtUI76lhudKvuk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;725&quot; height=&quot;544&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1081&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;일출 사진&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1081&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyOVXY/btsNKWIUd8G/RNBKRbi6eA3LugBtThukR1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyOVXY/btsNKWIUd8G/RNBKRbi6eA3LugBtThukR1/img.jpg&quot; data-alt=&quot;바다를 바라볼 수 있는 독서 공간&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyOVXY/btsNKWIUd8G/RNBKRbi6eA3LugBtThukR1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcyOVXY%2FbtsNKWIUd8G%2FRNBKRbi6eA3LugBtThukR1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;734&quot; height=&quot;551&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1081&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;바다를 바라볼 수 있는 독서 공간&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1081&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t35g4/btsNJ2QJsBv/3LwbjNrFzifZKhQigJfmAk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t35g4/btsNJ2QJsBv/3LwbjNrFzifZKhQigJfmAk/img.jpg&quot; data-alt=&quot;전병, 황태구이, 해물순두부찌개&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t35g4/btsNJ2QJsBv/3LwbjNrFzifZKhQigJfmAk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft35g4%2FbtsNJ2QJsBv%2F3LwbjNrFzifZKhQigJfmAk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;671&quot; height=&quot;504&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1081&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;전병, 황태구이, 해물순두부찌개&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t7qM9/btsNLBEed1V/ofMhFybkqYGkBCvxHZ8Wd1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t7qM9/btsNLBEed1V/ofMhFybkqYGkBCvxHZ8Wd1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t7qM9/btsNLBEed1V/ofMhFybkqYGkBCvxHZ8Wd1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft7qM9%2FbtsNLBEed1V%2FofMhFybkqYGkBCvxHZ8Wd1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;683&quot; height=&quot;911&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFRoyB/btsNJ4gCGzi/4FA100gZI3BtEknK8SxlQK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFRoyB/btsNJ4gCGzi/4FA100gZI3BtEknK8SxlQK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFRoyB/btsNJ4gCGzi/4FA100gZI3BtEknK8SxlQK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFRoyB%2FbtsNJ4gCGzi%2F4FA100gZI3BtEknK8SxlQK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;684&quot; height=&quot;912&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1081&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HwGlz/btsNLmtNaCR/Sl3jbCBewgm4kvyMrFiD50/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HwGlz/btsNLmtNaCR/Sl3jbCBewgm4kvyMrFiD50/img.jpg&quot; data-alt=&quot;아야트 커피에서의 풍경&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HwGlz/btsNLmtNaCR/Sl3jbCBewgm4kvyMrFiD50/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHwGlz%2FbtsNLmtNaCR%2FSl3jbCBewgm4kvyMrFiD50%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;718&quot; height=&quot;539&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1081&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;아야트 커피에서의 풍경&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>여행</category>
      <category>맹그로브 고성</category>
      <category>여행</category>
      <author>Bombo_</author>
      <guid isPermaLink="true">https://bombo96.tistory.com/132</guid>
      <comments>https://bombo96.tistory.com/132#entry132comment</comments>
      <pubDate>Sun, 4 May 2025 15:55:10 +0900</pubDate>
    </item>
    <item>
      <title>나를 찾아가던 2025년 1분기</title>
      <link>https://bombo96.tistory.com/131</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;2024년 연간 회고를 작년에 시간 순서로 작성을 해봤다. 연간 회고를 한 번에 작성하려고 하니 기억이 안나는 것도 많고, 작성하는데에도 생각보다 상당히 많은 시간이 소요됨을 경험 할 수 있었다. 그렇기에 이번년도는 먼저 분기별로 작성해보고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히, &lt;b&gt;&quot;글을 쓰는 또라이들의 모임&quot;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;이하&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&quot;글또&quot;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;에 대한 이야기가 많이 나올 예정이다. 다양한 사람이 모여있는 커뮤니티를 많이 접해보지도 못했고, 다양한 사람들을 만나며 1월, 2월, 3월은 알고 있다고 생각했던 나 자신을 다시 한 번 알아보고 몰랐던 모습들을 알아 볼 수 있었던 인생의 터닝포인트 였기 때문이다. 더욱 '나'로 살아 갈 수 있게 해준 커뮤니티이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1월&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;폭풍의 눈과 같던 회사 생활&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당시에는 알지 못했다. 지금 돌이켜 보면 번아웃이었다.&lt;br /&gt;이미 『마음 지구력』을 통해 '번아웃'에 대해 알고 있었음에도, 나는 그걸 쉽게 알아채지 못했다. 아마 마음의 여유가 없었기 때문일 것이다.&lt;/p&gt;
&lt;p data-end=&quot;574&quot; data-start=&quot;446&quot; data-ke-size=&quot;size16&quot;&gt;2024년 시장 상황은 계속해서 악화되었다. 함께 의지하던 동료들이 하나 둘씩 자리를 떠났다. 회사의 분위기도 급격히 위축되었다. 예정했던 프로젝트가 무산되면서, 나에게는 전혀 예상하지 못한 &lt;b&gt;'비용 감축 프로젝트'&lt;/b&gt;가 맡겨졌다. 처음에는 두려웠다. 하지만, 팀장님께서는 현재 내가 가장 잘 할 수 있다는 응원이 있었다.&lt;/p&gt;
&lt;p data-end=&quot;574&quot; data-start=&quot;446&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;'내가 과연 이걸 다 할 수 있을까?'&lt;/b&gt; 하는 불안과 함께, 동시에 묘한 흥분이 찾아왔다. 신입인 내가 크고 작은 인프라를 책임지며 프로젝트를 진행한다는 것이 신기하면서도 도전 의식을 자극했기 때문이다. 결국 해냈다.&lt;br /&gt;RI만 사용하기 위한 리소스 감축, 개발용 DB 통합, Redis 통합, Kubernetes 자원 관리, 대규모 데이터 압축 등을 진행했다. 인프라 작업은 예측할 수 없는 장애를 동반했기에 긴장했지만, 오히려 이 과정에서 나는 &lt;b&gt;'문제 해결을 즐기는 사람'&lt;/b&gt;임을 깨달았다.&lt;/p&gt;
&lt;p data-end=&quot;1009&quot; data-start=&quot;905&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1009&quot; data-start=&quot;905&quot; data-ke-size=&quot;size16&quot;&gt;지나고 보니, 그 시기는 말 그대로 폭풍의 눈 한가운데였다.&lt;br /&gt;바깥은 요란하게 흔들리고 있었지만, 나는 정신없이 일을 하며 중심을 잡고 있었다. 힘들었지만 그만큼 단단해졌던 시간이었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;웃는 남자&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2637&quot; data-origin-height=&quot;1775&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6ByAK/btsNcfpXtjI/ggHgFiUeZERxpKRFebHsZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6ByAK/btsNcfpXtjI/ggHgFiUeZERxpKRFebHsZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6ByAK/btsNcfpXtjI/ggHgFiUeZERxpKRFebHsZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6ByAK%2FbtsNcfpXtjI%2FggHgFiUeZERxpKRFebHsZk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;749&quot; height=&quot;504&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2637&quot; data-origin-height=&quot;1775&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각자의 영역에서 최선을 다하며 만들어지는 조화의 소리, 조화의 모습을 좋아하는 나는 뮤지컬과 오케스트라를 참 좋아한다. 이번에는 웃는 남자를 보러 가게 되었다. 뮤지컬을 좋아하는 마음에 비해 실제로 많이 보러 가지는 못했지만, 갈 때마다 뮤지컬은 늘 내 기대 이상의 감동을 안겨준다. 특히, 전혀 알지 못했던 뮤지컬 넘버를 들었을 때 극장 안을 가득 채우는 꽉 찬 소리는 언제나 나에게 짜릿한 전율을 선사한다. 그날도 마찬가지였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조시아나 여공작 역을 맡은 김소향님의 「내 안의 괴물」을 들었을 때의 전율은 아직도 생생히 기억난다. 그만큼의 감동은 쉽게 다시 오지 않지만, 가끔 그때의 감정을 떠올리고 싶을 땐 유튜브에 올라온 넘버를 찾아 듣기도 한다.&lt;/p&gt;
&lt;p data-end=&quot;643&quot; data-start=&quot;465&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;643&quot; data-start=&quot;465&quot; data-ke-size=&quot;size16&quot;&gt;또, 우르수스 역을 연기한 민영기님은 지금 내가 보고 있는 것이 무대라는 인식을 지워낼 정도로 나의 &lt;b&gt;현실감&lt;/b&gt;을 완전히 집어삼켰다.&lt;br /&gt;그 이후로 나는 어느새 무대 속 세계관의 한 &lt;b&gt;등장인물&lt;/b&gt;이 된 듯한 몰입감으로 작품을 바라보게 되었다. 그만큼 배우들의 연기와 노래, 그리고 그들이 만들어내는 서사는 강렬했다.&lt;/p&gt;
&lt;p data-end=&quot;764&quot; data-start=&quot;645&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;764&quot; data-start=&quot;645&quot; data-ke-size=&quot;size16&quot;&gt;그윈플렌 역을 맡은 규현님 역시 인상 깊었다. 일반적인 발라드뿐만 아니라 뮤지컬 넘버에서도 너무나 멋진 목소리를 들려주었고, 그 덕분에 이 공연은 비싼 돈을 주고도 아깝지 않은, 오히려 값진 경험으로 남게 되었다. 앞으로 매해 한 작품씩, 이렇게 뮤지컬이라는 예술을 꼭 경험하고 싶다. 내 감정의 스펙트럼을 넓혀주고, 나라는 사람의 결을 조금 더 풍부하게 만들어주는 시간이었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;러닝 10km&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 연도 8월 신림에 이사를 오게 되면서 가장 큰 복지 중에 하나는 집 앞에 도림천이 있다는 것이다. 그 전부터 러닝을 그렇게 싫어하지는 않았는데, 집 근처에서 러닝을 하기에는 집 앞 공터를 뛰는 정도..? 핑계 일수도 있지만, 그냥 풍경이 맘에 안 들었다. 그러다 보니 러닝을 자주하지는 못했는데 도림천은 가끔 부모님과 함께 산책하는 아이들, 운동을 하시는 어르신들, 도림천에 어떻게 있는건지 신기한 오리가족들을 보기도 듣기도 하면서 뛰는 것만으로도 살아있다는 느낌을 많이 받는다. 그런 감정으로 뛰게 되니 자연스럽게 자주 뛰게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'글또' 커뮤니티에서 소모임으로 '달리또' 라는 커뮤니티를 이정일님께서 운영하고 계신데, 러닝크루처럼 거창한 건 아니고 때때로 러닝을 하시는 분들끼리 각자의 러닝 앱으로 커뮤니티에 달렸다는 것을 인증하는 소모임이다. 달리또를 통해서 러닝을 하는데 필요한 여러가지 정보들도 획득 할 수 있고, 지금까지 딱 한 번 이기는 했지만 같이 세 분이서 모여서 뛰었는데 다 같이 뛰니 생각보다 더 힘이 나기도 했다. 당시에는 5km 뛰는 것도 힘들었는데 덕분에 지금은 한 번 나가면 대체로 5km는 뛰려고 하게 되었다.&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1744201900870&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;1차 러닝 목표, 10km 달리기까지의 여정&quot; data-og-description=&quot;러닝을 시작하게 된 계기개발자라는 직업을 택하고, 개발자가 된 지금도 개발자가 되기 이전에도 귀가 닳도록 들었던 조언이 있다.&amp;quot;개발자로서 롱런하려면 운동을 꾸준히 해야 합니다. 나중에&quot; data-og-host=&quot;bombo96.tistory.com&quot; data-og-source-url=&quot;https://bombo96.tistory.com/126&quot; data-og-url=&quot;https://bombo96.tistory.com/126&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eneJFY/hyYA9xRRHm/P1rw5c4Vy7pzjWyTagesqK/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bhzd19/hyYB8rAA34/Ha8NVaoBnkxdPg9oy3Jcmk/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://bombo96.tistory.com/126&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://bombo96.tistory.com/126&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eneJFY/hyYA9xRRHm/P1rw5c4Vy7pzjWyTagesqK/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bhzd19/hyYB8rAA34/Ha8NVaoBnkxdPg9oy3Jcmk/img.jpg?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;1차 러닝 목표, 10km 달리기까지의 여정&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;러닝을 시작하게 된 계기개발자라는 직업을 택하고, 개발자가 된 지금도 개발자가 되기 이전에도 귀가 닳도록 들었던 조언이 있다.&quot;개발자로서 롱런하려면 운동을 꾸준히 해야 합니다. 나중에&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;bombo96.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 10km를 뛰게 되면서 후기를 작성하기도 했었다. 이 날은 10km 달성했다는 사실에 도파민이 터져버려서 바로 카페로 달려가 글을 작성했었다. 회고를 작성하면서 10km 후기를 다시 살펴봤는데, 그 때의 기록이 새록새록 나기도 하고 정말 뿌듯했다는 그 감정이 파도처럼 밀려온다. 이 날 이후로 더 열심히 러닝을 하게 됐던 것 같다. 당시에는 페이스가 '6.50' 정도 나왔었다면 지금은 어느새 '5.50'이 되어있다. 사실 이것도 적으면서 생각보다 페이스가 빨라졌다는 사실을 알았다. &quot;많이 좋아졌구나. 나 자신 아주 대견해~&quot; 달리또에 들어와서 10km를 뛰겠다는 목표를 달성했으니, 올해는 20km 마라톤에 꼭 도전해 볼 계획이다. 계획을 위해서 4월 11일날 신청하는 10km 마라톤에 먼저 신청을 해서 한 차례 경험을 해 볼 생각이다. 나는 이 말을 참 좋아한다. &lt;b&gt;&quot;일단 해보죠?&quot;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;나의 작은 조찬 모임&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글또의 운영진인 용선님의 권유로 시작하게 된 조찬 모임. 성윤님의 인프런 회고 밋업을 시작으로 글또의 오프라인 모임을 적지 않게 가지게 되면서 다양한 사람들을 만나고, 만나면 만날수록 넓어지는 것 같은 나의 시야. 그리고 안정적으로 한 사람으로써 있을 수 있게 해주는 따뜻한 사람들. 내가 느낀 이러한 경험들을 경험해보지 못한 다양한 사람들에게 경험을 시켜주고 싶었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 매주 주말에 한 번 작은 조찬 모임을 통해 소소한 이야기와 함께 자신의 일주일이 어땠는지를 되돌아보는 시간을 가지게 되었다. 나 뿐만이 아니라 나로 인해서 타인도 좋은 경험을 가질 수 있게 만드는 경험은 나의 개발 가치관과도 일치하다보니 이 경험 또한 굉장히 즐겁게 다가오는 경험이었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;일기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;책상 구석에 방치해놓았던 일기를 쓰기 시작했다. 왜 쓰기 시작했는지 기억이 나지 않아 적기 시작한 일기장의 페이지를 펼쳐봤다. 3월의 이야기와 이어지겠지만 이 시기부터 여자친구와의 관계를 고민했던 흔적이 담겨있었다. &lt;b&gt;&quot;급하지는 않으니, 조금만 더 도와주자. 조금만 더 옆에 있어주자. 언젠가는 이런 상황도 다 흘러가겠지&quot; &lt;/b&gt;라는 말이 그 당시에 나의 감정을 보여준다. 일기장은 자신의 속에 있는 것을 전부 비워내는 것에 도움을 주는데 이 때, 당시에는 흘러넘치는 마음을 어떻게든 담아두려고 노력했던 것 같아보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이후로도 몇 번씩 일기를 적고는 했지만 일기가 내가 알고 있는 일기가 아닌 느낌이었다. 생각해보니 이때부터 였던 것 같다. 나 자신이 느끼고 있는 감정에 대해서 표현하는 것이 서툴다는 것을. 그리고, 그렇게 1월 말이 되어서야 나는 누구인가. 나라는 사람은 어떻게 구성이 되어 있고, 나라는 사람이 느끼는 현재의 감정은 무엇인지에 대해서 찾아가는 여정을 시작했던 것 같다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;독서 모임&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;12월에 글또에 계신 김은찬 님의 독서 모임을 통해, 난생처음 독서 모임이라는 것을 경험했다. 최근 읽었던 책 중 가장 인상 깊었던 내용을 소개하며 각자의 고민을 나누고, 서로가 가진 다양한 생각과 인사이트를 얻는 자리였다. 당시의 경험이 너무 좋아 이번엔 내가 직접 독서 모임을 주최하게 되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;사진 1.png&quot; data-origin-width=&quot;594&quot; data-origin-height=&quot;198&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nNTkp/btsNhvr1UnB/kqvf4z7ZAE0Kf9lpdxcxTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nNTkp/btsNhvr1UnB/kqvf4z7ZAE0Kf9lpdxcxTk/img.png&quot; data-alt=&quot;독서 모임&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nNTkp/btsNhvr1UnB/kqvf4z7ZAE0Kf9lpdxcxTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnNTkp%2FbtsNhvr1UnB%2Fkqvf4z7ZAE0Kf9lpdxcxTk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;594&quot; height=&quot;198&quot; data-filename=&quot;사진 1.png&quot; data-origin-width=&quot;594&quot; data-origin-height=&quot;198&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;독서 모임&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;404&quot; data-start=&quot;214&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;404&quot; data-start=&quot;214&quot; data-ke-size=&quot;size16&quot;&gt;그렇게 여섯 명이 모여 각자의 책과 함께 다양한 이야기를 펼쳤다. 우리는 각자 저마다의 고민과 그 고민이 담긴 책 속 주제들을 꺼내 놓았다. 이 모임은 단지 책 이야기를 나누는 자리에서 끝나지 않고, 이후에 내가 '나 자신'을 더 깊이 알아가는 결정적 계기가 되었다. 바로 이 자리에서 처음으로 '강점 검사'라는 것을 알게 되었기 때문이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2월&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;낮술 낭독회&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1월에 참여했던 독서 모임을 계기로, 책을 사랑하시는 이유영 님의 낮술 낭독회에 참석하게 되었다. 이름은 '낮술 낭독회'였지만, 실제로는 퇴근 후 저녁에 모여 진행된 &amp;lsquo;퇴근 길 낭독회&amp;rsquo; 였다. 모임 방식은 단순하지만 참 좋았다. 각자가 요즘 읽고 있는 책을 가져와 간단히 소개한 뒤, 그 중 가장 인상 깊었던 구절을 직접 낭독하는 시간. 그 때 나는 &lt;b&gt;&amp;lsquo;성장&amp;rsquo; &lt;/b&gt;이라는 키워드에 깊은 갈증을 느끼고 있었다.&lt;br /&gt;&lt;br /&gt;회사에 들어와 여러 가지 업무를 해내고 있었지만, 정말 내가 '제대로' 성장하고 있는지 확신이 들지 않았다. 그래서 선택한 책이 김창준 님의 『함께 자라기』였다. 개발자로서 애자일하게 일한다는 것이 무엇인지, 그리고 함께 성장하는 것이 왜 중요한지를 다루는 책이었다. 책을 읽으며 &amp;lsquo;아, 그래도 나는 잘하고 있구나&amp;rsquo;라는 작은 안도와 용기를 얻을 수 있었다. 물론 아직 팀을 리드하는 위치에 있진 않기에, 책에서 말하는 많은 부분들을 완전히 체화하긴 어렵지만.. 언젠가 리더가 된다면 그때 다시 이 책을 꺼내 읽게 될 것 같다.&lt;/p&gt;
&lt;p data-end=&quot;835&quot; data-start=&quot;600&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;그리고, 이날 이호동 님께서 &amp;lsquo;성숙의 4단계&amp;rsquo;에 대해 이야기해주셨다.&lt;br /&gt;&lt;b&gt;무지 &amp;rarr; 인지 &amp;rarr; 의심 &amp;rarr; 확신&lt;/b&gt;&lt;br /&gt;지금의 &lt;b&gt;나는 &amp;lsquo;인지&amp;rsquo;의 단계에 있는 게 아닐까&lt;/b&gt; 라는 말에 이상하리만큼 고개가 끄덕여졌다. 내가 겪고 있는 혼란과 고민들이 그저 헛된 방황이 아니란 사실이 위로가 되었다. 어쩌면 지금 나는 &amp;lsquo;의심&amp;rsquo;의 문턱에 발을 디딘 상태일지도 모른다. 그리고 현재는 &amp;lsquo;확신&amp;rsquo;의 자리에 도달 할 수 있게 되었다.&lt;/p&gt;
&lt;h3 data-end=&quot;835&quot; data-start=&quot;600&quot; data-ke-size=&quot;size23&quot;&gt;일기 쓰기의 변화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일기를 계속 써오고는 있었지만, 매번 쉽지 않았다.&lt;br /&gt;내가 지금 어떤 감정을 느끼고 있는지 표현하는 일이 참 어려웠고, 결국 일기는 그저 하루의 '기록'에 그치곤 했다. 오늘 무슨 일이 있었고, 무엇을 했고, 어떻게 마무리했는지. 그저 흐름만 적히는 날들이었다. 문득 이런 생각이 들었다.&lt;/p&gt;
&lt;p data-end=&quot;388&quot; data-start=&quot;268&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;&amp;ldquo;이렇게 쓰는 일기로 내가 '나'를 더 잘 알아갈 수 있을까?&amp;rdquo;&lt;/b&gt;&lt;br /&gt;답은 아니었다. 뭔가 방향이 잘못된 느낌이었고, 스스로를 더 깊이 들여다볼 수 있는 방식이 필요하다고 느꼈다. 그러던 어느 날, 낮술 낭독회에서 일기 쓰기를 오랫동안 해오셨다는 이유영 님께 조심스레 부탁드렸다.&lt;/p&gt;
&lt;p data-end=&quot;564&quot; data-start=&quot;390&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;&quot;혹시&amp;hellip; 일기장의 한 페이지를 보여주실 수 있을까요?&quot;&lt;/b&gt;&lt;br /&gt;일기를 보여달라는 말이 누군가에겐 무례하게 들릴 수 있다는 걸 알았기에, 부담을 드리지 않으려 조심스럽게 내 일기쓰기의 어려움을 털어놓으며 여쭤봤다. 놀랍게도, 그리고 정말 감사하게도, 유영 님은 선뜻 일기장 한 페이지를 보여주셨다.&lt;br /&gt;그 페이지에는 그날의 고민과 감정, 한 사람의 하루가 고스란히 담겨 있었다. 마치 한 편의 짧은 이야기처럼, 단어와 문장 사이로 그날의 풍경이 머릿속에 그려졌다. 그 일기를 보고 나는 깨달았다.&lt;/p&gt;
&lt;p data-end=&quot;564&quot; data-start=&quot;390&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;ldquo;아, 내가 쓰고 싶었던 건 이런 일기였구나.&amp;rdquo;&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;982&quot; data-start=&quot;758&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;982&quot; data-start=&quot;758&quot; data-ke-size=&quot;size16&quot;&gt;그 이후, 나는 조금씩 나에게 맞는 방식으로 일기를 바꾸기 시작했다.&lt;br /&gt;그리고 3월, 드디어 나만의 일기 쓰기 방식을 찾았다. 바로 &amp;lsquo;&lt;b&gt;너&lt;/b&gt;&amp;rsquo;라고 나에게 말을 거는 형식.&lt;br /&gt;예전엔 늘 '나는', '내가'라고 적었다면, 지금은 '너는', '네가'라는 말로 내 감정을 묻는다. 마치 나 자신과 대화를 하듯.&lt;br /&gt;&quot;너는 왜 그때 그렇게 말했을까?&quot;, &quot;그 말을 들었을 때, 네 기분은 어땠니?&quot;&lt;/p&gt;
&lt;p data-end=&quot;1078&quot; data-start=&quot;984&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 나에게 질문을 던지다 보면, 어렴풋했던 감정들이 또렷하게 떠오르고, 그날의 나는 조금 더 분명해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;OpenFeign 오픈 소스 기여&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글또를 통해 꾸준히 글을 써오던 어느 날, 나는 서비스 장애를 겪었던 일을 회고로 정리하기 시작했다.&lt;br /&gt;단순히 '무슨 일이 있었는지'를 적는 게 아니라, 문제의 원인을 처음부터 끝까지 직접 디버깅하며 분석했고, 그 과정에서 관련된 오픈소스를 깊게 파고들게 되었다. 문제를 쫓다 보니 문제 원인을 분석하는 과정이 너무 오래걸렸고 조심스럽게 PR(Pull Request)을 올렸다.&lt;br /&gt;비록 단순한 주석 하나의 추가였지만, OpenFeign 담당자와 이야기를 하며 PR이 승인되었다. 앞으로 이 코드를 접할 누군가에게는 작은 힌트가 되어줄 수 있을 거란 생각에 뿌듯함이 남았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 늘 생각했다. 오픈소스 기여는 먼 훗날, 내가 한참 더 성장한 뒤에나 가능한 일이라고.&lt;br /&gt;죽이 되든 밥이 되든 일단 해봐야 실패를 하든, 성공을 하든 뭐라도 생긴다.&lt;br /&gt;글또 덕분에 그 '뭐라도'를 만들어낼 수 있었다는 사실이, 참 감사하다.&lt;/p&gt;
&lt;figure id=&quot;og_1744550179029&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;docs: Improve SpringQueryMap documentation by bombo-dev &amp;middot; Pull Request #1173 &amp;middot; spring-cloud/spring-cloud-openfeign&quot; data-og-description=&quot;Problem When using the @SpringQueryMap annotation, it was difficult to discover the existence of the QueryMapEncoder class. The reference path is as follows: @SpringQueryMap -&amp;gt; @QueryMap -&amp;gt; @...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/spring-cloud/spring-cloud-openfeign/pull/1173&quot; data-og-url=&quot;https://github.com/spring-cloud/spring-cloud-openfeign/pull/1173&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/y3UE0/hyYCi2GKi2/blnsQt3kBk4lDZnW35mnm1/img.png?width=1200&amp;amp;height=600&amp;amp;face=999_95_1040_140,https://scrap.kakaocdn.net/dn/uaaNL/hyYFAgl074/P7nQBzeSymX79x41bbKRF0/img.png?width=1200&amp;amp;height=600&amp;amp;face=999_95_1040_140&quot;&gt;&lt;a href=&quot;https://github.com/spring-cloud/spring-cloud-openfeign/pull/1173&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/spring-cloud/spring-cloud-openfeign/pull/1173&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/y3UE0/hyYCi2GKi2/blnsQt3kBk4lDZnW35mnm1/img.png?width=1200&amp;amp;height=600&amp;amp;face=999_95_1040_140,https://scrap.kakaocdn.net/dn/uaaNL/hyYFAgl074/P7nQBzeSymX79x41bbKRF0/img.png?width=1200&amp;amp;height=600&amp;amp;face=999_95_1040_140');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;docs: Improve SpringQueryMap documentation by bombo-dev &amp;middot; Pull Request #1173 &amp;middot; spring-cloud/spring-cloud-openfeign&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Problem When using the @SpringQueryMap annotation, it was difficult to discover the existence of the QueryMapEncoder class. The reference path is as follows: @SpringQueryMap -&amp;gt; @QueryMap -&amp;gt; @...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;플랫폼 개발&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도메인 개발보다는 플랫폼과 인프라 중심의 작업이 계속되었다. 특히 IaC(Infrastructure as Code)를 활용해 YAML 파일을 수정하고, 라우팅을 변경하는 등 프로젝트 전반의 설정을 끊임없이 다루는 작업이 반복되었다. 처음엔 신선했던 일도, 비슷한 구조와 이슈를 반복하다 보니 반복 작업을 싫어하는 나에게는 스트레스로 이어졌던 것 같다. 물론, 이 일들이 &lt;b&gt;단순히 &amp;lsquo;귀찮은 일&amp;rsquo;&lt;/b&gt;은 아니었다.&lt;br /&gt;회사가 앞으로 나아가기 위해, 혹은 지금의 자리를 지키기 위해 &lt;b&gt;누군가는 반드시 해야 할 일&lt;/b&gt;이었다.&lt;br /&gt;그래서 나는 그 일들을 회피하지 않았고, 책임감을 가지고 묵묵히 해냈다. 그리고 시간이 지나 돌아보니, 그 반복적인 작업들이 나에게 &lt;b&gt;시야의 확장&lt;/b&gt;을 만들어 주었다.&lt;b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;698&quot; data-start=&quot;573&quot; data-ke-size=&quot;size16&quot;&gt;예전에는 눈앞의 서비스 코드만을 보고 있었다면, 지금은 인프라 전체 구조를 함께 바라보며 설계할 수 있게 되었다. 프로젝트의 흐름, 자원 배치, 배포 전략까지 고려할 수 있게 된 나 자신을 보면 분명 이전과는 다른 모습이다. 지루했고, 버거웠지만 그 시간이 있었기에 지금의 나도 있을 수 있었다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3월&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이별&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4년 반이라는 긴 시간 동안, 나의 일상 절반을 함께 채워온 여자친구와 이별을 맞이했다. 오랜 시간 고민 끝에 내린 결정이었다.&lt;br /&gt;&lt;b&gt;&quot;사랑하는 사람을 만나면서 오히려 가치관의 차이로 인해 스트레스를 받는 상황이 과연 맞는 것일까&quot;&lt;/b&gt;라는 질문이 마음 깊숙이 쌓여 있었다. 이전에도 비슷한 갈등을 겪었던 기억이 있었기에, 그런 말을 다시 꺼낸다는 것이 조심스러웠다. 하지만 결국, 언젠가는 반드시 마주해야 할 이야기였다. 용기를 내어 대화를 시도했지만, 준비가 부족했다.&lt;br /&gt;하고 싶은 말, 정리해야 했던 마음들이 아직 어수선한 상태로 대화의 자리에 나아갔고, 결국 우리는 온전한 대화를 나누지 못했다. 그럼에도 불구하고, 그 대화는 의미 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;서로가 감춰두었던 상처, 참아왔던 감정들을 조금씩 꺼내 놓았고, 끝내는 우리가 바라보는 방향이 다르다는 사실을 마주하게 되었다.&lt;/p&gt;
&lt;p data-end=&quot;763&quot; data-start=&quot;617&quot; data-ke-size=&quot;size16&quot;&gt;그리고 깨달았다. 우리가 서로를 아끼는 마음은 분명했지만, &lt;b&gt;그 마음이 서로를 더 나아가게 하기보단, 오히려 반작용을 만들어내고 있었다는 것. &lt;/b&gt;그리하여 우리는 &lt;b&gt;서로가 더 좋은 사람으로 빛나길 바라는 마음으로, 우리는 그렇게 마침표를 찍었다. &lt;/b&gt;함께했던 시간은 소중했다.&lt;/p&gt;
&lt;p data-end=&quot;763&quot; data-start=&quot;617&quot; data-ke-size=&quot;size16&quot;&gt;서로 다른 두 사람이 함께 보낸 시간은 수많은 추억으로 쌓였고, 다양한 취미도 공유하며 웃었던 순간들도 많았다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;연애의 끝에는 늘 성숙의 시간이 찾아온다.&lt;/b&gt;&lt;br /&gt;이번 이별 역시 마찬가지였다. 그 시간을 지나며 나는 내 안의 부족함을 마주했고, 특히 &lt;b&gt;대화 방식&lt;/b&gt;에 있어 갈증을 느끼고 있었다는 걸 알게 되었다. 그 갈증을 해소하기 위해 나는 글을 쓰기 시작했고, 책을 읽고, 나를 성찰했다. 그렇게 최종적으로는 글을 작성하여 나의 대화의 부족함을 정리한 글이 &lt;b&gt;'이전에 작성한 가까울수록 어려운 대화, 나는 어떻게 말해야 할까' &lt;/b&gt;이다.&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1744552349162&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;가까울수록 어려운 대화, 나는 어떻게 말해야 할까&quot; data-og-description=&quot;&amp;quot;대화&amp;quot;는 위키에 따르면 마주 대하여 이야기를 주고받는 것을 뜻한다.&amp;nbsp;우리는 살면서 다양한 갈등 상황과 마주하게 된다. 그러나 정작 대화를 나누는 과정에서는 대화의 정의처럼 서로의 마음&quot; data-og-host=&quot;bombo96.tistory.com&quot; data-og-source-url=&quot;https://bombo96.tistory.com/130&quot; data-og-url=&quot;https://bombo96.tistory.com/130&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cQrBPd/hyYFzhrKaF/xmAKONHoZdbJMI5nX56yi0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/DLp6j/hyYCkzq8g1/MKVPdrjZh92T71sPAVEZYK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/eVQMM/hyYFBTRAZP/i7ZKi4ITZ3tPcCh26uzu91/img.png?width=750&amp;amp;height=750&amp;amp;face=0_0_750_750&quot;&gt;&lt;a href=&quot;https://bombo96.tistory.com/130&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://bombo96.tistory.com/130&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cQrBPd/hyYFzhrKaF/xmAKONHoZdbJMI5nX56yi0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/DLp6j/hyYCkzq8g1/MKVPdrjZh92T71sPAVEZYK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/eVQMM/hyYFBTRAZP/i7ZKi4ITZ3tPcCh26uzu91/img.png?width=750&amp;amp;height=750&amp;amp;face=0_0_750_750');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;가까울수록 어려운 대화, 나는 어떻게 말해야 할까&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&quot;대화&quot;는 위키에 따르면 마주 대하여 이야기를 주고받는 것을 뜻한다.&amp;nbsp;우리는 살면서 다양한 갈등 상황과 마주하게 된다. 그러나 정작 대화를 나누는 과정에서는 대화의 정의처럼 서로의 마음&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;bombo96.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ENTJ 모임&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2월 어느 날, 글또봇의 아버지 김은찬 님으로부터 슬랙 DM이 도착했다. &lt;b&gt;&quot;ENTJ 모임을 열면 참석하실 의향 있으신가요?&quot;&lt;/b&gt;&lt;br /&gt;동일한 MBTI를 가진 사람들과의 대화는 언제나 나에게 특별한 에너지를 주곤 했다. 은찬 님과 대화를 나눌 때도, 또 다른 ENTJ들과 마주할 때도 느껴졌던 그 강한 연결감. 그래서 망설임 없이 &quot;참석하겠다&quot;고 답했다. ENTJ 다운 바쁜 일정 탓에 결국 모임은 3월로 미뤄졌지만, 그 날은 정말 신기한 하루였다. 은찬 님을 제외하면 대부분 처음 제대로 이야기 나눈 사람들이었음에도, 마치 &lt;b&gt;물 만난 물고기처럼&lt;/b&gt; 우리는 금세 서로의 에너지에 빠져들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공감, 스트레스 관리, 후임 리드 방법, 피드백 방식, 꿈, 상상, 가치관... 주제는 끊임없이 이어졌고, 대화는 단 한 순간도 지루하지 않았다.&lt;/p&gt;
&lt;p data-end=&quot;602&quot; data-start=&quot;557&quot; data-ke-size=&quot;size16&quot;&gt;4시간이 훌쩍 지나 있었고, 모두가 입을 모아 말했다. &quot;시간이 너무 짧다.&quot;&lt;/p&gt;
&lt;p data-end=&quot;602&quot; data-start=&quot;557&quot; data-ke-size=&quot;size16&quot;&gt;단지 MBTI가 같다는 이유만으로 이토록 깊은 이야기를 나눌 수 있다니 신기하면서도 따뜻한 경험이었다.&lt;/p&gt;
&lt;p data-end=&quot;602&quot; data-start=&quot;557&quot; data-ke-size=&quot;size16&quot;&gt;그렇게 아쉬움을 품은 채 헤어졌지만, 나는 그 자리에서 바로 결심했다. &lt;b&gt;좀 더 이야기할 수 있는 자리를 만들자.&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;818&quot; data-start=&quot;674&quot; data-ke-size=&quot;size16&quot;&gt;그렇게 바로 당일 탄생한 모임이 바로 &lt;b&gt;&amp;lsquo;ENTJ또&amp;rsquo; &lt;/b&gt;이다. 이후로 흩어져 있던 수많은 ENTJ들이 모였고, 다음에는 무려 &lt;b&gt;8시간짜리 모임&lt;/b&gt;을 준비했다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-end=&quot;986&quot; data-start=&quot;820&quot; data-ke-size=&quot;size16&quot;&gt;그리고 그 과정에서 나는 하나의 확신을 얻었다.&lt;br /&gt;&lt;b&gt;&amp;ldquo;나는 실행력이 정말 강한 사람이구나.&amp;rdquo; &lt;/b&gt;기획하고, 모으고, 만들고, 실행에 옮기는 그 모든 과정을 보며 스스로도 놀랄 만큼 빠르게 움직이는 나를 보았다. 그날 이후, 나는 &lt;b&gt;나의 &amp;lsquo;실행력&amp;rsquo;을 더는 우연이라 생각하지 않게 되었다.&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-end=&quot;986&quot; data-start=&quot;820&quot; data-ke-size=&quot;size23&quot;&gt;Rate Limiter 적용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 비용 감축과 안정화를 위해 &lt;b&gt;Rate Limiter&lt;/b&gt;를 도입해보기로 했다. 알고리즘은 다양했지만, 빠르게 적용 가능하면서도 효과적인 방식을 택해야 했다. 그래서 간단한 구조를 기반으로, 분산 환경에서도 원자적으로 동작할 수 있도록 Redis를 활용한 Token 방식으로 구현을 진행했다.&lt;/p&gt;
&lt;p data-end=&quot;504&quot; data-start=&quot;367&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;504&quot; data-start=&quot;367&quot; data-ke-size=&quot;size16&quot;&gt;도입 과정에서는 단순히 코드를 적용하는 데 그치지 않고, &lt;b&gt;실제 서비스 흐름에 맞는 적절한 임계값&lt;/b&gt;을 찾기 위해 트래픽 통계를 뽑아내며 여러 실험을 반복했다. 그 과정에서, 수치 기반으로 판단하고 문서화하는 능력도 함께 성장할 수 있었다.&lt;br /&gt;&lt;br /&gt;하지만, 파트너사에 추가 비용이 발생할 수 있다는 우려가 생기면서, 서비스의 편의성과의 균형을 고려해 결국 도입은 보류되었다.&lt;/p&gt;
&lt;p data-end=&quot;728&quot; data-start=&quot;603&quot; data-ke-size=&quot;size16&quot;&gt;비록, 서비스에 도입은 하지 못했지만 &lt;b&gt;데이터 기반의 판단과 설득, 그리고 빠른 진행과 소통을 통한 더 나은 해결책&lt;/b&gt;을 만들어나가는 뜻 깊은 경험을 하게 되었다.&lt;/p&gt;
&lt;h3 data-end=&quot;728&quot; data-start=&quot;603&quot; data-ke-size=&quot;size23&quot;&gt;다시 적어보는 삶의 지도&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2월 독서 모임에서 만난 사람들을 통해, 나는 뜻밖의 방식으로 &lt;b&gt;나의 강점들&lt;/b&gt;을 알아가게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 글또의 김주원 님이 제안해주신 &lt;b&gt;'삶의 지도 재작성 모임'&lt;/b&gt;은 그 여정을 더욱 깊이 있게 만들어주었다.&lt;br /&gt;&lt;b&gt;&amp;lsquo;삶의 지도&amp;rsquo;&lt;/b&gt;는 &lt;b&gt;내 인생 전반을 되돌아보며 스스로를 이해하고 기록하는 작업&lt;/b&gt;이었다. 나는 두 단계를 거쳐 글을 써내려갔다.&lt;/p&gt;
&lt;p data-end=&quot;524&quot; data-start=&quot;392&quot; data-ke-size=&quot;size16&quot;&gt;먼저, 전체적인 인생의 흐름을 되짚어보는 시간. 그 안에는 잊고 지내던 어린 시절의 상처들도 있었다.&lt;br /&gt;기억 저편에 밀어두었던 감정들을 꺼내는 건 두려웠지만, 이제는 마주할 수 있을 만큼 내 마음이 단단해졌다는 걸 느낄 수 있었다.&lt;/p&gt;
&lt;p data-end=&quot;691&quot; data-start=&quot;526&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;691&quot; data-start=&quot;526&quot; data-ke-size=&quot;size16&quot;&gt;그렇게 나는 나를 '발산'했다. 어릴 적 생활기록부를 꺼내 보고, 기억 속에서 희미해진 나를 만나고, 어렸을 때부터 청소년기까지 겪은 일들을 &lt;b&gt;있는 그대로&lt;/b&gt; 꺼내어 놓았다. 하지만 정리되지 않은 채로 꺼낸 기억들은 때론 혼란스러웠고, 나는 그것들을 나만의 언어로 다시 정리하고 싶었다. 주변에서 자주 듣던 말 &lt;b&gt;&quot;넌 열정이 대단해&quot;, &quot;꿈을 대하는 태도가 멋져&quot; &lt;/b&gt;이 말들이 떠올랐고, 나는 &lt;b&gt;&amp;lsquo;성격의 성장 과정&amp;rsquo;&lt;/b&gt;과 &lt;b&gt;&amp;lsquo;꿈을 대하는 태도&amp;rsquo;&lt;/b&gt;를 주제로 한 편의 소설처럼 글을 써나가기 시작했다. 그리고 오랜 시간을 거쳐 완성된 글은 삶의 지도를 함께 쓰는 분들께 공유했다.&lt;br /&gt;&lt;br /&gt;어느 날 문득 부모님과 식사를 하며 위와 같은 글을 작성했다는 이야기를 드렸고, 밤 늦게 부모님에게 한 통의 카카오톡이 도착했다.&lt;/p&gt;
&lt;p data-end=&quot;691&quot; data-start=&quot;526&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;시간되면 네가 쓴 글 보내줘. 읽어보고 싶어~&quot; &lt;/b&gt;카톡을 읽고 한편으로는 조심스러웠다. 자신의 아들의 어린 시절의 상처를 들여다본 부모의 심정이 많이 아플 것이라는 생각이 들었기 때문이다.&lt;/p&gt;
&lt;p data-end=&quot;691&quot; data-start=&quot;526&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;그럼에도 한 편으로는 이건 부끄러운 일이 아니었고, 오히려 부모님과 더 가까워질 수 있는 기회라고 생각했다.&lt;/p&gt;
&lt;p data-end=&quot;1086&quot; data-start=&quot;1015&quot; data-ke-size=&quot;size16&quot;&gt;그리고 그날, 그 덕분일까 나는 부모님과 아주 긴 이야기를 나눴고, 우리 가족의 마음은 이전보다 더 단단하게 묶이게 되었다.&lt;/p&gt;
&lt;p data-end=&quot;1220&quot; data-start=&quot;1088&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 1분기 동안 나는 '삶의 지도'라는 여정을 통해 &lt;b&gt;내 인생에서 구멍이 빠져있던 한 조각을 채워 넣었다.&lt;/b&gt;&lt;br /&gt;그리고 그렇게, 2025년 3월 30일, 글또 10기의 활동이 종료되었다.&lt;/p&gt;
&lt;h2 data-end=&quot;1220&quot; data-start=&quot;1088&quot; data-ke-size=&quot;size26&quot;&gt;함께여서 도달 할 수 있었던 1분기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나의 인생을 알아갈 수 있는 기회를 준 글또, 그리고 그 안에 있던 따뜻한 수많은 사람들 덕분에 나는 비로소 &lt;b&gt;&amp;lsquo;나&amp;rsquo;라는 사람을 있는 그대로 바라보고, 이해하고, 받아들일 수 있게&lt;/b&gt; 되었다. 이 여정은 결코 혼자서 걸을 수 있는 길이 아니었다. 용기 내어 글또라는 커뮤니티에 참여할 수 있도록 등을 밀어준 나의 친구들, 글또라는 공간 안에서 자신을 진심으로 나눠준 모든 이들,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 커뮤니티가 존재할 수 있도록 길을 열어준 &lt;b&gt;변성윤 님&lt;/b&gt;께 깊은 감사를 전하며,&lt;br /&gt;2025년 1분기의 회고를 여기서 마무리한다.&lt;/p&gt;</description>
      <category>회고</category>
      <category>1분기</category>
      <category>개발자</category>
      <category>글또</category>
      <category>회고</category>
      <author>Bombo_</author>
      <guid isPermaLink="true">https://bombo96.tistory.com/131</guid>
      <comments>https://bombo96.tistory.com/131#entry131comment</comments>
      <pubDate>Sun, 13 Apr 2025 23:22:16 +0900</pubDate>
    </item>
    <item>
      <title>가까울수록 어려운 대화, 나는 어떻게 말해야 할까</title>
      <link>https://bombo96.tistory.com/130</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;&quot;대화&quot;&lt;/b&gt;는 위키에 따르면 &lt;b&gt;마주 대하여 이야기를 주고받는 것&lt;/b&gt;을 뜻한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 살면서 다양한 갈등 상황과 마주하게 된다. 그러나 정작 대화를 나누는 과정에서는 대화의 정의처럼 서로의 마음이나 욕구를 진심으로 들여다보며 이야기하지 못하는 경우가 많다. 특히 가장 빈번하게 겪는 갈등은 대화 방식에서 비롯된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;상대방의 의견은 듣지 않고 자기 의견만 고집하는 형태&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;상대방이 싫어할까 두려워 자신의 의견을 표현하지 않는 형태&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;대화 자체가 두려워 아예 피하는 형태&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;나 또한 지금까지 예시로 든 모든 갈등 상황을 경험했다. 나 자신일 수도 있고, 타인일 수도 있다. 작은 오해에서 비롯된 다툼들로 인해 인간관계에서 아쉬움을 느끼면서 우리는 대화를 더 잘하는 법을 발전시키게 된다. 그리고 나 역시 앞으로 다양한 상황과 경험을 겪으면서, 지금 내가 가진 대화에 대한 가치관 또한 끊임없이 변화하고 발전할 수 있다고 생각한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이러한 대화에 대한 갈증을 해소하기 위해 최근 『비폭력 대화(NVC)』라는 책을 읽었다. 이 글은 나의 다양한 경험과 최근 읽은 책을 통해 현재까지 내가 정의 내린 대화 방식이다. 앞으로의 다양한 경험을 통해 나는 또 다른 방식으로 대화를 다시 정의하게 될 수도 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;『비폭력 대화(NVC)』에서는 대화를 네 가지 단계로 구분한다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;관찰: 판단하지 않고 객관적인 사실만을 이야기하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;느낌: 관찰한 내용에 대한 순수한 감정 표현하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;욕구: 그 느낌 뒤에 있는 나의 욕구 표현하기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;부탁: 앞선 과정을 바탕으로 구체적인 부탁하기&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그러나 실제로 많은 대화 상황에서 판단 없이 순수하게 관찰하기란 쉽지 않다. 대화는 단지 말로만 이루어지지 않고 표정과 같은 비언어적인 요소들도 포함하기 때문이다. 우리는 이러한 표정과 태도를 각자의 경험에 비추어 나름의 방식으로 해석하고, 이는 자연스럽게 주관적인 판단을 하도록 만든다. 나 또한 상대방과의 대화 중 무의식적으로 나만의 판단을 더하게 되면서, 때때로 생각지도 못한 갈등 상황을 만들어내곤 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;직장과 같이 명확한 소통이 중요한 환경에서는 NVC 방식을 적극적으로 사용하는 것이 효과적이다. 업무적 갈등을 최소화하고 서로의 생각을 분명하게 이해하는 데 큰 도움을 주기 때문이다. 예컨대 업무에서 질문을 주고받을 때, 명확한 컨텍스트를 설정하고 이해가 안 되는 부분을 솔직히 이야기하는 것은 오해를 줄이고 신뢰를 구축하는 기반이 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;예를 들어, 기획자가 개발자에게 새로운 기능을 요청할 때 다음과 같이 말할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;기획자: &quot;이번 주에 개발을 요청했던 기능이 아직 구현되지 않았다고 들었는데(관찰), 출시 일정이 밀릴까 봐 걱정이 돼요(느낌). 앞으로 업무 계획에 대한 커뮤니케이션을 좀 더 자주 했으면 좋겠어요(욕구). 혹시 진행 상황을 매일 아침 간단히 공유해 줄 수 있을까요?(부탁)&quot;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;개발자: &quot;아, 개발 일정에 대해 자주 공유되지 않아서 걱정하셨군요(느낌). 저도 일정 관리에 대한 명확한 소통이 필요하다고 느끼고 있었어요(욕구). 앞으로 매일 아침 진행 상황을 업데이트하겠습니다(부탁 수용).&quot;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그러나 일상생활에서, 특히 가족이나 연인과 같은 가까운 관계에서 NVC를 지나치게 적용하면 오히려 갈등이 증폭될 수 있다. 위의 예시에서도 볼 수 있듯이 지나치게 형식화된 대화는 효율적일 수는 있으나 기계적이고 딱딱한 느낌을 줄 수 있다. 사람은 본래 사회적 동물이며, 대화를 통해 어느 정도 감정을 공유하고 이해받기를 원하기 때문이다. 최근 느낀 '공감'은 그 사람과 완전히 같은 감정을 공유 혹은 동화 한다기보다, 그 사람이 느끼는 감정을 있는 그대로 이해하고 존중하는 것이라는 생각이 든다. 슬픈 일이 있을 때 그저 가만히 들어주거나, 좋은 일이 있을 때 상대방의 기쁨을 증폭시키는 것만으로도 충분한 공감이 될 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 가까운 인간관계에서는 NVC를 형식적으로 그대로 적용하기보다는, 상황에 맞게 보다 유연한 방식으로 접근하는 것이 중요하다. 일상 속의 사소한 갈등이라면, 가벼운 농담이나 편안한 말투만으로도 감정을 나누고 오해를 해소할 수 있다. 다만, 나에게는 사소한 문제일지라도 상대방에게는 그렇지 않을 수 있음을 인식하고, 언제나 배려하는 태도를 유지해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;506&quot; data-start=&quot;264&quot; data-ke-size=&quot;size16&quot;&gt;반면, 가치관의 충돌처럼 관계의 방향에 영향을 줄 수 있는 중요한 상황에서는 감정을 회피하지 않고, 진지하게 마주 앉아 이야기할 필요가 있다. 이럴 때는 서로의 감정과 욕구를 솔직하게 표현하고, 상대의 입장을 경청하며 절충점을 함께 찾아가는 과정이 관계를 더욱 깊고 단단하게 만든다. 만약 한쪽이 갈등을 두려워해 솔직한 대화를 피하게 된다면, 관계는 점차 표면적인 수준에 머무르게 되고 결국 일방적인 양보로 인해 지치게 될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;결국 내가 생각하는 이상적인 인간관계의 대화법은 다음과 같다. 상대방을 존중하면서도 나의 내면을 명확히 전달하고, 서로의 욕구를 이해하며 함께 절충점을 찾는 것이다. 갈등을 두려워 피하지 않고, 때로는 서로 부딪히면서도 꾸준히 솔직한 소통을 통해 관계를 발전시켜 나가는 것이 중요하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 다른 환경에서 자라난 돌들은 저마다 다른 모양을 가지고 있다. 이들이 부딪히고 맞닿는 과정은 때로 갈등처럼 느껴지기도 하고 아프기도 하다. 하지만, 그 속에서 조금씩 깎이고 다듬어지며 서로에게 맞춰가는 시간을 갖는다. 그렇게 서로를 이해하고 조율해 나가다 보면, 점차 함께 굴러갈 수 있는 동그란 돌이 되어간다. 대화의 갈등도 마찬가지다. 부딪힘 속에서 서로를 알아가고, 결국에는 더 깊은 신뢰와 공감을 쌓아갈 수 있게 된다.&lt;/p&gt;</description>
      <category>회고</category>
      <category>대화</category>
      <category>회고</category>
      <author>Bombo_</author>
      <guid isPermaLink="true">https://bombo96.tistory.com/130</guid>
      <comments>https://bombo96.tistory.com/130#entry130comment</comments>
      <pubDate>Fri, 28 Mar 2025 22:28:15 +0900</pubDate>
    </item>
    <item>
      <title>Rate Limiter 시리즈 - 이론편</title>
      <link>https://bombo96.tistory.com/129</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;배경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 사내 OpenAPI에 Rate Limiter를 적용하면서, 분산 환경에서 가장 구현이 간편한 &lt;b&gt;Fixed Window&amp;nbsp;Rate Limiter&lt;/b&gt;를 설계하고 도입하였습니다. Rate Limiter에는 여러 방식이 존재하는데, 이번 기회를 통해 각 방식의 개념과 특징을 정리하고자 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Rate Limiter Algorithm&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Token Bucket&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Token Bucket은 일정 개수의 토큰을 저장하는 &lt;b&gt;버킷(Bucket)&lt;/b&gt;을 이용하여 요청을 제어하는 알고리즘입니다. API 요청을 처리하려면 버킷에 남아 있는 토큰이 필요하며, 토큰이 모두 소진되면 추가 요청은 제한됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;동작 방식&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;토큰 생성 방식&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토큰은 &lt;b&gt;1 / 제한 요청량을 단위로 생성&lt;/b&gt;됩니다. 예를 들어서, 1분당 10개를 제한한다고 하면 6초에 1개씩 생성합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;요청 처리 방식&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;API 요청이 들어왔을 때 버킷에 토큰이 남아있는지 확인합니다.&lt;/li&gt;
&lt;li&gt;버킷에 토큰이 존재하면 토큰을 하나 소모하고, 요청을 허용합니다.&lt;/li&gt;
&lt;li&gt;버킷이 토큰이 남아있지 않으면 추가 요청은 거부합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;구현이 다른 알고리즘에 비해서 상대적으로 간단합니다.&lt;/li&gt;
&lt;li&gt;짧은 시간 안에 집중되는 트래픽을 커버 할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;동시성 문제가 발생 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;기준에 따라 버킷에 담아야 할 적절한 토큰 공급량을 정하기 어렵습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예시&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) 토큰이 존재하는 경우&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1354&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clcDKT/btsMNhzNmVl/XTEvNFgUaU8jm9M90IAQUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clcDKT/btsMNhzNmVl/XTEvNFgUaU8jm9M90IAQUk/img.png&quot; data-alt=&quot;Token Bucket 토큰이 존재하는 경우(1)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clcDKT/btsMNhzNmVl/XTEvNFgUaU8jm9M90IAQUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclcDKT%2FbtsMNhzNmVl%2FXTEvNFgUaU8jm9M90IAQUk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;765&quot; height=&quot;508&quot; data-origin-width=&quot;1354&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Token Bucket 토큰이 존재하는 경우(1)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) 토큰이 존재하지 않는 경우&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1354&quot; data-origin-height=&quot;860&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dmgzNz/btsMMCEqJIr/ILpeFHwSKUYM6Hfa8UjUk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dmgzNz/btsMMCEqJIr/ILpeFHwSKUYM6Hfa8UjUk0/img.png&quot; data-alt=&quot;Token Bucket 토큰이 존재하지 않는 경우(2)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dmgzNz/btsMMCEqJIr/ILpeFHwSKUYM6Hfa8UjUk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdmgzNz%2FbtsMMCEqJIr%2FILpeFHwSKUYM6Hfa8UjUk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;836&quot; height=&quot;531&quot; data-origin-width=&quot;1354&quot; data-origin-height=&quot;860&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Token Bucket 토큰이 존재하지 않는 경우(2)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Leaky Bucket&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Token Bucket이 버킷 내에 있는 토큰을 순서 없이 사용하는 방식이라면, &lt;b&gt;Leaky Bucket은 큐(Queue) 형태로 동작&lt;/b&gt;합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;동작 방식&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;버킷의 최대 크기&lt;/b&gt;를 &lt;b&gt;지정&lt;/b&gt;합니다.&lt;/li&gt;
&lt;li&gt;요청이 들어오면 &lt;b&gt;버킷의 남은 공간을 확인&lt;/b&gt;합니다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;공간이 남아 있다면 &lt;b&gt;요청을 큐에 추가&lt;/b&gt;합니다.&lt;/li&gt;
&lt;li&gt;공간이 부족하면 &lt;b&gt;요청을 버립니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;버킷에 담긴 요청이 처리되면 &lt;b&gt;큐에 새로운 공간이 생깁니다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;새로운 요청이 들어오면 다시 큐에 추가&lt;/b&gt;합니다. (2-4 반복)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;큐의 크기를 제한하여 &lt;b&gt;메모리를 효율적으로 관리&lt;/b&gt; 할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;큐에 쌓인 요청이 &lt;b&gt;제때 처리되지 못하면 이후 요청도 밀릴 수 있습니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결제 요청을 처리하는 &lt;b&gt;스레드 풀&lt;/b&gt;이 있다고 가정해보겠습니다. 만약 &lt;b&gt;특정 결제 PG사가 장애를 일으켜 스레드들이 요청을 계속 재시도&lt;/b&gt;한다면, 이 스레드들이 계속 점유되어 &lt;b&gt;새로운 결제 요청을 처리하지 못하는 문제가 발생&lt;/b&gt;할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 Leaky Bucket에서 큐가 가득 찬 상태로 &lt;b&gt;요청이 지연되어 이후 요청이 차단되는 상황과 유사&lt;/b&gt;합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) Bucket에 공간이 충분한 경우&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1352&quot; data-origin-height=&quot;1076&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dE433x/btsML3P61an/nOkKpoin92s8KAuk98f3K0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dE433x/btsML3P61an/nOkKpoin92s8KAuk98f3K0/img.png&quot; data-alt=&quot;Leaky Bucket (1)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dE433x/btsML3P61an/nOkKpoin92s8KAuk98f3K0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdE433x%2FbtsML3P61an%2FnOkKpoin92s8KAuk98f3K0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;774&quot; height=&quot;616&quot; data-origin-width=&quot;1352&quot; data-origin-height=&quot;1076&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Leaky Bucket (1)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) Bucket에 공간이 부족한 경우&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1358&quot; data-origin-height=&quot;1116&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCJ0yD/btsMLZNHliw/vT4XdOgTLuyL59XR5OkkC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCJ0yD/btsMLZNHliw/vT4XdOgTLuyL59XR5OkkC0/img.png&quot; data-alt=&quot;Leaky Bucket(2)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCJ0yD/btsMLZNHliw/vT4XdOgTLuyL59XR5OkkC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCJ0yD%2FbtsMLZNHliw%2FvT4XdOgTLuyL59XR5OkkC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;803&quot; height=&quot;660&quot; data-origin-width=&quot;1358&quot; data-origin-height=&quot;1116&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Leaky Bucket(2)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Fixed Window Counter&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름에 &lt;b&gt;Window&lt;/b&gt;가 포함된 것처럼, &lt;b&gt;고정된 시간 단위를 기준으로 요청을 제한하는 방식&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간에 따라 &lt;b&gt;처리 가능한 요청량을 미리 정해두고&lt;/b&gt;, 해당 임계치를 초과하면 &lt;b&gt;요청을 거부&lt;/b&gt;합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;동작 방식&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;고정된 시간 단위 동안 들어온 요청 수&lt;/b&gt;를 확인합니다.&lt;/li&gt;
&lt;li&gt;요청 수가 &lt;b&gt;임계치를 넘지 않았다면 요청을 처리&lt;/b&gt;합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;임계치를 넘었다면 요청을 거부&lt;/b&gt;합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;시간 단위 기준으로 요청을 제한&lt;/b&gt;하므로 &lt;b&gt;구현이 비교적 간단&lt;/b&gt;합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;고정된 시간 동안 일정한 요청량을 처리하므로 메모리를 효율적&lt;/b&gt;으로 사용할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;시간 &lt;b&gt;경계에서 요청이 집중되는 Traffic Burst 문제가 발생&lt;/b&gt; 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;초기에 &lt;b&gt;너무 많은 요청이 몰리면&lt;/b&gt; &lt;b&gt;다음 윈도우까지 대기&lt;/b&gt;해야 합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예시&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) 정상적으로 처리되고 있는 Fixed Window Counter&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1356&quot; data-origin-height=&quot;436&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwvyhx/btsMMaBAdbb/N4yL1HJspHKvETFW6i84r0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwvyhx/btsMMaBAdbb/N4yL1HJspHKvETFW6i84r0/img.png&quot; data-alt=&quot;Fixed Window Counter(1)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwvyhx/btsMMaBAdbb/N4yL1HJspHKvETFW6i84r0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbwvyhx%2FbtsMMaBAdbb%2FN4yL1HJspHKvETFW6i84r0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;789&quot; height=&quot;254&quot; data-origin-width=&quot;1356&quot; data-origin-height=&quot;436&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fixed Window Counter(1)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) 요청이 거부 된 Fixed Window Counter&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1358&quot; data-origin-height=&quot;486&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pTBZk/btsMNj5q7bM/KIteri3W4BBGSrhWgHZlb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pTBZk/btsMNj5q7bM/KIteri3W4BBGSrhWgHZlb0/img.png&quot; data-alt=&quot;Fixed Window Counter(2)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pTBZk/btsMNj5q7bM/KIteri3W4BBGSrhWgHZlb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpTBZk%2FbtsMNj5q7bM%2FKIteri3W4BBGSrhWgHZlb0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;748&quot; height=&quot;268&quot; data-origin-width=&quot;1358&quot; data-origin-height=&quot;486&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fixed Window Counter(2)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3) 짧은 시간에 요청이 몰리는 Traffic Burst 요청&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1354&quot; data-origin-height=&quot;458&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqMg2V/btsMM5sNznJ/xXRPBIHRzjJbaUombuvSQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqMg2V/btsMM5sNznJ/xXRPBIHRzjJbaUombuvSQ1/img.png&quot; data-alt=&quot;Fixed Window Counter(3)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqMg2V/btsMM5sNznJ/xXRPBIHRzjJbaUombuvSQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqMg2V%2FbtsMM5sNznJ%2FxXRPBIHRzjJbaUombuvSQ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;813&quot; height=&quot;275&quot; data-origin-width=&quot;1354&quot; data-origin-height=&quot;458&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fixed Window Counter(3)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 처럼, 한 윈도우의 끝과 다음 &lt;b&gt;윈도우의 시작 지점에 요청이 몰릴 경우 많은 요청이 처리&lt;/b&gt; 될 수 있습니다. &lt;br /&gt;예시처럼 &lt;b&gt;0.2초 사이에 10건의 요청이 처리&lt;/b&gt; 될 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4) 윈도우의 시작 시간에 요청이 몰린 경우&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1354&quot; data-origin-height=&quot;438&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btiDAJ/btsMMHMh3Ot/jkjCbs7YPVKMfKinmprVd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btiDAJ/btsMMHMh3Ot/jkjCbs7YPVKMfKinmprVd0/img.png&quot; data-alt=&quot;Fixed Window Counter(4)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btiDAJ/btsMMHMh3Ot/jkjCbs7YPVKMfKinmprVd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtiDAJ%2FbtsMMHMh3Ot%2FjkjCbs7YPVKMfKinmprVd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;820&quot; height=&quot;265&quot; data-origin-width=&quot;1354&quot; data-origin-height=&quot;438&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fixed Window Counter(4)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 처럼&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Sliding Window Log&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fixed Window Counter의 단점을 보완한 알고리즘입니다. 기존 &lt;b&gt;Sliding Window&lt;/b&gt; 방식과 동일하게, &lt;b&gt;시작점과 끝점이 현재 시간에 따라 동적으로 변경&lt;/b&gt;됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;동작 방식&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;현재 시간을 기준으로 시작점과 끝점을 설정합니다.&lt;/li&gt;
&lt;li&gt;해당 시간 구간 내 요청 수를 확인합니다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;임계치를 넘지 않았다면, 요청을 받아들입니다.&lt;/li&gt;
&lt;li&gt;임계치를 초과했다면, &lt;b&gt;해당 요청과 타임스탬프를 저장하지만 요청은 수행되지 않습니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;시간이 지남에 따라 시작점과 끝점이 이동하므로 특정 시간 경계에서의 Traffic Burst를 예방 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;어떤 시간대이든 균일하게 Rate Limit가 적용됩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;요청과 타임스탬프를 함께 저장해야 하므로 기존보다 메모리 사용량이 증가 할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예시&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;정상 처리 되고 있는 Sliding Window Log&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bH9PbC/btsMLIS8PFs/uEtdtrNKixtpIAtCgLcjdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bH9PbC/btsMLIS8PFs/uEtdtrNKixtpIAtCgLcjdk/img.png&quot; data-alt=&quot;Sliding Window Log (1)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bH9PbC/btsMLIS8PFs/uEtdtrNKixtpIAtCgLcjdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbH9PbC%2FbtsMLIS8PFs%2FuEtdtrNKixtpIAtCgLcjdk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;774&quot; height=&quot;262&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;416&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Sliding Window Log (1)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Sliding Window가 꽉차서 채울 수 없는 경우&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;402&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqNpXf/btsMMm9GyFD/wk2SIzNcrMr4BneruOXLQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqNpXf/btsMMm9GyFD/wk2SIzNcrMr4BneruOXLQk/img.png&quot; data-alt=&quot;Sliding Window Log (2)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqNpXf/btsMMm9GyFD/wk2SIzNcrMr4BneruOXLQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqNpXf%2FbtsMMm9GyFD%2Fwk2SIzNcrMr4BneruOXLQk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;790&quot; height=&quot;258&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;402&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Sliding Window Log (2)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Sliding Window가 꽉찬 상태에서 들어온 요청이 거부되어 남은 경우&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1234&quot; data-origin-height=&quot;382&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Eum2r/btsMM6FeBBz/30ZtJjPKbK24VuZSjboWSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Eum2r/btsMM6FeBBz/30ZtJjPKbK24VuZSjboWSk/img.png&quot; data-alt=&quot;Sliding Window Log (3)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Eum2r/btsMM6FeBBz/30ZtJjPKbK24VuZSjboWSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEum2r%2FbtsMM6FeBBz%2F30ZtJjPKbK24VuZSjboWSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;805&quot; height=&quot;249&quot; data-origin-width=&quot;1234&quot; data-origin-height=&quot;382&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Sliding Window Log (3)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청이 거부 된 경우에도 실패한 요청이 기록되어 있기 때문에 4개의 요청만 수용이 가능합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;의문점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Alex Xu의 System Design Interview 책에서는 위와 같은 방식으로 설명되고 있습니다. 그런데, &lt;b&gt;윈도우 크기 내에서 이미 초과된 요청도 타임스탬프와 함께 저장되며, 이후 윈도우 계산 시 버려진 요청까지 포함된다는 점&lt;/b&gt;은 상당히 의문이었습니다. 이로 인해 거부된 요청이 이후 요청에도 영향을 미치기 때문입니다. 그 예시를 살펴보도록 하겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1232&quot; data-origin-height=&quot;404&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnxSCS/btsMNrIZ7Ce/y0fe4J2OAZ2mkNiySAMsCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnxSCS/btsMNrIZ7Ce/y0fe4J2OAZ2mkNiySAMsCk/img.png&quot; data-alt=&quot;Sliding Window Log 의문 (1)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnxSCS/btsMNrIZ7Ce/y0fe4J2OAZ2mkNiySAMsCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnxSCS%2FbtsMNrIZ7Ce%2Fy0fe4J2OAZ2mkNiySAMsCk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;769&quot; height=&quot;252&quot; data-origin-width=&quot;1232&quot; data-origin-height=&quot;404&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Sliding Window Log 의문 (1)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 앞서 실패한 요청으로 인해서 슬라이딩 윈도우에서 다음 들어오는 요청들이 실패 상태로 남게 됩니다. 그리고, 이후 요청에서도 아래 사진과 같이 거부된 요청이 지속적으로 다음 요청에 영향을 미치게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1224&quot; data-origin-height=&quot;398&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qL7rz/btsMLJqXpCt/AKwR4KLbquCn8cAxT3wl41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qL7rz/btsMLJqXpCt/AKwR4KLbquCn8cAxT3wl41/img.png&quot; data-alt=&quot;Sliding Window Log 의문 (2)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qL7rz/btsMLJqXpCt/AKwR4KLbquCn8cAxT3wl41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqL7rz%2FbtsMLJqXpCt%2FAKwR4KLbquCn8cAxT3wl41%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;786&quot; height=&quot;256&quot; data-origin-width=&quot;1224&quot; data-origin-height=&quot;398&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Sliding Window Log 의문 (2)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;390&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DjGlN/btsMMJi27Ik/tEu7sSzdewTMacreFHx3o0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DjGlN/btsMMJi27Ik/tEu7sSzdewTMacreFHx3o0/img.png&quot; data-alt=&quot;Sliding Window Log 의문 (3)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DjGlN/btsMMJi27Ik/tEu7sSzdewTMacreFHx3o0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDjGlN%2FbtsMMJi27Ik%2FtEu7sSzdewTMacreFHx3o0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;772&quot; height=&quot;245&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;390&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Sliding Window Log 의문 (3)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 사진과 같이 지속적으로 요청이 들어온다면 이전에 실패한 요청들에 의해서 다음으로 들어오는 요청이 지속적으로 실패하게 되고 &lt;b&gt;성공하는 요청이 하나도 없는 상태가 되면서 결과적으로 불필요하게 메모리를 낭비하면서 정상적인 요청의 처리가 불가능&lt;/b&gt;해집니다. 의문을 가지고 있는 것은 저 뿐만이 아니라 다른 포럼에서도 논의가 되고 있는 것을 확인 할 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;포럼&quot; href=&quot;https://www.reddit.com/r/AskComputerScience/comments/xktn2j/rate_limiting_why_log_rejected_requests/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.reddit.com/r/AskComputerScience/comments/xktn2j/rate_limiting_why_log_rejected_requests/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1742100984849&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;From the AskComputerScience community on Reddit: Rate limiting - why log rejected requests' timestamps in Sliding window log alg&quot; data-og-description=&quot;Explore this post and more from the AskComputerScience community&quot; data-og-host=&quot;www.reddit.com&quot; data-og-source-url=&quot;https://www.reddit.com/r/AskComputerScience/comments/xktn2j/rate_limiting_why_log_rejected_requests/&quot; data-og-url=&quot;https://www.reddit.com/r/AskComputerScience/comments/xktn2j/rate_limiting_why_log_rejected_requests/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bwVrIi/hyYuqkmqLo/VoSLB3CRn5hUcPeiXrcVY1/img.jpg?width=1120&amp;amp;height=584&amp;amp;face=0_0_1120_584,https://scrap.kakaocdn.net/dn/Mlyl6/hyYugIOvB4/7XMGRqAlkG8FcAt54ZcEvK/img.jpg?width=1120&amp;amp;height=584&amp;amp;face=0_0_1120_584&quot;&gt;&lt;a href=&quot;https://www.reddit.com/r/AskComputerScience/comments/xktn2j/rate_limiting_why_log_rejected_requests/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.reddit.com/r/AskComputerScience/comments/xktn2j/rate_limiting_why_log_rejected_requests/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bwVrIi/hyYuqkmqLo/VoSLB3CRn5hUcPeiXrcVY1/img.jpg?width=1120&amp;amp;height=584&amp;amp;face=0_0_1120_584,https://scrap.kakaocdn.net/dn/Mlyl6/hyYugIOvB4/7XMGRqAlkG8FcAt54ZcEvK/img.jpg?width=1120&amp;amp;height=584&amp;amp;face=0_0_1120_584');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;From the AskComputerScience community on Reddit: Rate limiting - why log rejected requests' timestamps in Sliding window log alg&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Explore this post and more from the AskComputerScience community&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.reddit.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 포럼에서도 &lt;b&gt;거부된 요청을 기록하지 않아야 한다는 의견이 많았습니다. &lt;/b&gt;책에서는 &quot;&lt;b&gt;성공한 요청만을 저장해야 한다.&quot;&lt;/b&gt;는 내용을&lt;b&gt; &quot;모든 요청을 저장해야 한다.&quot; &lt;/b&gt;로 &lt;b&gt;잘못 기술 했을 가능성&lt;/b&gt;이 있을 것 같다는 생각이 들었습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Sliding Window Counter&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름에서 유추할 수 있듯이, &lt;b&gt;Fixed Window Counter와 Sliding Window Logs의 장점을 결합한 방식&lt;/b&gt;입니다. 고정된 시간대에서 &lt;b&gt;현재 시간 대비 윈도우 크기를 차지하는 비율&lt;/b&gt;을 계산하여, 허용할 수 있는 요청량을 조정합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;동작 방식&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;요청 수 계산 방식&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;{&amp;nbsp; (이전 시간대의 window size에서 sliding window가 겹치는 비율) * (이전 시간 대에 sliding window에 포함된 요청 수)&amp;nbsp; } &lt;br /&gt;+ { (현재 시간대의 고정 window size에서 sliding window가 겹치는 비율) * (현재 시간 대에 sliding window에 포함된 요청 수) }&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;요청이 들어오면 &lt;b&gt;현재 시간대에서 이전 시간대의 요청과 현재 시간대의 요청 비율을 계산하여 요청 수를 계산&lt;/b&gt;한다.&lt;/li&gt;
&lt;li&gt;위에서 계산한 요청 수와 Limit Size를 비교한다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;만약, Limit Size를 넘어섰다면 들어오는 요청을 거부합니다.&lt;/li&gt;
&lt;li&gt;만약, Limit Size를 넘어서지 않았다면 들어오는 요청을 수용합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;위 과정을 반복하여 요청을 처리합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;고정된 윈도우와 슬라이딩 윈도우를 함께 고려하므로, &lt;b&gt;특정 시간 경계에서 요청이 몰리는 문제를 줄일 수 있습니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;비율을 기반으로 요청을 허용하므로, 전체적으로 더 많은 요청이 처리&lt;/b&gt; 될 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예시&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1236&quot; data-origin-height=&quot;400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E3M0l/btsMNqcgbvN/IQlnJCthe94XRJcOadHfzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E3M0l/btsMNqcgbvN/IQlnJCthe94XRJcOadHfzK/img.png&quot; data-alt=&quot;Sliding Window Counter (1)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E3M0l/btsMNqcgbvN/IQlnJCthe94XRJcOadHfzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE3M0l%2FbtsMNqcgbvN%2FIQlnJCthe94XRJcOadHfzK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;789&quot; height=&quot;255&quot; data-origin-width=&quot;1236&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Sliding Window Counter (1)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 예시는 다음과 같이 계산됩니다. &lt;b&gt;(3 * (7 / 10)s&amp;nbsp; + (8 * (3 / 10)s ) = 2.1 + 2.4 = 4.9&lt;br /&gt;&lt;/b&gt;현재 윈도우 슬라이스에서 8개의 요청이 짧은 시간 내 들어왔지만, 윈도우가 일정한 요청 패턴을 유지한다고 가정하여 계산 된 요청 수가 2.4건으로 조정되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이, 예상한 것보다 더 많은 요청이 수용 될 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마치며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 &lt;b&gt;Rate Limiter를 적용하기 전 참고한 자료들을 바탕으로 개념을 정리&lt;/b&gt;했습니다. 이를 통해 다양한 Rate Limiting 방식과 그 장단점을 살펴볼 수 있었습니다. 다음 글에서는&lt;b&gt;&amp;nbsp;Rate Limiter를 어떻게 구현했는지&lt;/b&gt;에 대해 소개하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고자료&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/AskComputerScience/comments/xktn2j/rate_limiting_why_log_rejected_requests/&quot;&gt;https://www.reddit.com/r/AskComputerScience/comments/xktn2j/rate_limiting_why_log_rejected_requests/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Token_bucket&quot;&gt;https://en.wikipedia.org/wiki/Token_bucket&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kdh0518.tistory.com/64#:~:text=Rate%20Limiter%EB%8A%94%20Request%EB%A5%BC,%EB%A1%9C%20%EB%B6%84%EB%A5%98%ED%95%A0%20%EC%88%98%20%EC%9E%88%EB%8B%A4.&quot;&gt;https://kdh0518.tistory.com/64#:~:text=Rate%20Limiter%EB%8A%94%20Request%EB%A5%BC,%EB%A1%9C%20%EB%B6%84%EB%A5%98%ED%95%A0%20%EC%88%98%20%EC%9E%88%EB%8B%A4.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Backend</category>
      <category>API</category>
      <category>ratelimiter</category>
      <author>Bombo_</author>
      <guid isPermaLink="true">https://bombo96.tistory.com/129</guid>
      <comments>https://bombo96.tistory.com/129#entry129comment</comments>
      <pubDate>Sun, 16 Mar 2025 14:19:58 +0900</pubDate>
    </item>
  </channel>
</rss>