<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Lob!</title>
    <link>https://lob-dev.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sun, 5 Jul 2026 18:06:21 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Junior Lob!</managingEditor>
    <image>
      <title>Lob!</title>
      <url>https://tistory1.daumcdn.net/tistory/4336900/attach/53cb0fc08443454080e2f2ceb15ecca4</url>
      <link>https://lob-dev.tistory.com</link>
    </image>
    <item>
      <title>2025년 회고</title>
      <link>https://lob-dev.tistory.com/95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;회고는 블로그에 있는 글과 다른 느낌으로 작성해봤습니다. 글은 제가 다 쓰고.. Gemini로 평서체로 한번 다듬었네요. 2024년 내용도 약간 포함되어 있습니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;업무 방식의 변화&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;만 5년 정도 개발자 커리어를 이어오며 경험한 제품 개발 프로세스를 간략히 정리해본다면 다음과 같은 흐름인 것 같다. (특정 지점은 반복될 여지가 있다)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;고객 정의 &amp;rarr; 가설 정의 (문제 인식) &amp;rarr; 워크플로우 정의 &amp;rarr; 도메인 모델링 &amp;rarr; 설계 및 구현 &amp;rarr; 테스트 &amp;rarr; 배포&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;첫 번째 SI 회사에서는 고객, 가설, 프로세스, 모델링이 이미 정의된 상태에서 주어진 지시와 제약에 맞게 빠르게 구현하는 역할을 수행했었고, 두 번째 SM 회사에서는 프로세스가 설계되는 과정부터 참여해 리더를 거쳐 기획자와 소통하고 피드백을 주고받으며 프로젝트를 설계하고 구현했다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;세 번째 회사에 이르러서는 가설 수립 이후 단계부터 논의에 참여하여 타 직군(PO, Product Designer, Front/App Engineer 등)과 커뮤니케이션하고 내가 담당하는 백엔드 영역에 대한 의사결정(일정 등)을 직접 내려야 하는 순간들이 많았다. 이러한 역할을 처음 맡게 되었을 때에는 그 책임감의 무게가 나에게 큰 부담으로 다가왔다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;타 직군과 원활히 소통하기 위해 기술 중심적인 용어와 맥락을 비기술자도 이해할 수 있게 변환하고 그 사람이 이미 알 것이다 라는 전제를 하지 않는 것이 너무나 힘들었으며, 작업 범위에 따라 스스로 일정을 결정해야 한다는 사실도 심리적인 압박으로 다가왔다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;당시 이 문제를 해결하기 위해 정기적인 1 on 1이나 야근을 하며 이러한 부분을 너무나 잘 하시던 팀 리더님에게 질문드리고 피드백을 요청했던 기억이 난다. (리더님에게 정말 많은 것을 배웠다)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;지금은 그러한 역할을 완벽하게 수행하고 있다고 할 수 없지만, 이 시기에 어떻게 일해야 하는지, 무엇을 해야 하는지, 어떻게 말해야 하는지를 가장 많이 배웠다고 말할 수 있겠다. 결국에는 강점도 커뮤니케이션, 단점도 커뮤니케이션인 박찬호 스타일(?)의 개발자가 되어가고 있다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이때의 나는 업무에 대한 자세와 마인드셋이 구조적인 &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;틀로 정립되었다고 생각했고(이 부분은 지금도 그렇다), 이 정도만으로 4~5년차 개발자로서 충분히 영향력을 가지고 기여하고 있다고 생각했었다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 시기를 지나 제품 개발 그 자체에 더 깊이 몰입하는 순간이 있다면, 그게 지금이라면 향후 커리어에 있어서 좋지 않을까 라는 생각을 하게 되었고, 하루에 3개의 면접을 연달아보는 고난을 거쳐(?) 현재 회사에 합류하게 되었다. (여기가 떨어진다면, 몇년간 이직하지 않겠단 생각도 했었다) &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;백엔드 엔지니어가 아닌 프로덕트 엔지니어라는 생소한 직무로 합류한 환경에서는 이전까지의 업무 영역보다 더 넓게, 즉 고객 정의, 문제 식별 &amp;amp; 가설 정의라는 앞단부터 고민을 시작해야 했다. (SOPT MIND23에서 인프랩 이동욱님이 발표하셨던 내용 중 프로덕트 엔지니어가 있었음을 기억하고 찾아보기도 했었다)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;좋은 동료들과 치열하게 고민하며 고객이 어떻게 일하는지, 무엇을 원하는지를 파악하는 과정에서 도메인에 대한 이해는 필수적이었고 자연스레 어떻게 구현할 것인가는 후순위가 되었다. AI가 발전할수록 기술적인 구현 방식을 고민하는 비중은 더욱 줄어들 것이라는게 개인적인 생각이다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;나는 지난 5년이라는 짧은(?) 기간 동안 자의 반 타의 반(TF 참여 등)으로 메신저, AI, 리워드 포인트, 상품 전시, 카탈로그 매칭 등 다양한 도메인을 경험했다. 하지만 이렇게 제도, 정책, 실무, 그리고 다양한 페르소나가 얽힌 복잡한 도메인은 처음이다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;전자결재, 세금계산서, 재무 영역까지 여전히 알아야 할 영역이 산더미 같고 고민해야 할 것들도 많지만 그럼에도 '아직 갈 길이 멀다'는 생각과 환경이 항상 나를 성장시켜 왔다는 것을 알기에 오히려 공부할 맛이 난다. 매번 내 부족함을 깨닫는 과정이 성장의 원동력이 되고 있다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;대학교&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2년 안에 졸업하겠다는 호기로운 생각으로 2023년 2학기에 방통대 3학년으로 편입을 하게 되었다. 하지만 첫 학기부터 직장 &amp;amp; 학업 병행이라는 매운맛을 본 뒤 2024년은 내리 휴학했다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2025년에는 이대로 안 되겠다 싶어 1, 2학기를 모두 악착같이 보냈는데, 그간 채우지 못한 학점을 보충하려다 보니 2학기에는 무려 27학점(방통대 18, 프라임칼리지 9)을 신청하는 무식한 일을 벌였다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2학기는 쏟아지는 과제와 시험 전날의 벼락치기로 점철된 시간이었지만 다행히 적당한 학점을 받고 무사히 마무리할 수 있었다. (다시는 이런 무모한 짓을 하지 않으리라..)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;커뮤니티&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2024년부터 한국 스프링 사용자 모임의 일꾼(오거나이저)으로 활동하고 있다. 커뮤니티에 기여하게 된 계기는 앞선 글 &amp;lt;왜 개발자 커뮤니티에 기여하기 시작했을까?&amp;gt; 에서 다루었으므로 생략하겠다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;컨퍼런스 참여자에서 기여자로 역할이 바뀌면서 커뮤니티 행사를 준비할 때 만나는 여러 현실적인 제약들을 깊이 경험하게 되었는데, 이제는 다른 커뮤니티 행사에 갈 때마다 준비한 분들의 노고를 먼저 떠올리게 된다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;기여자가 된 이후 더 많은 컨퍼런스와 밋업에 참석하고 개인적인 후원(3회)도 늘렸는데, 개인 후원의 목적은 다른 커뮤니티 기여자들이 행사를 준비하는데 약간의 도움을 줄 수 있기를 원했기 때문이며, 많이 참석한 이유는 어떤 고민을 했을지 직접 보고 싶었기 때문이다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;다양한 행사를 다녀보며 건강한 커뮤니티를 위해서는 기여자를 위한 행사와 참가자를 위한 행사 모두가 조화롭게 필요하다는 것을 깨닫기도 했다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;올해 다녀온 커뮤니티 행사는 다음과 같다. (순서는 생각나는대로 적었다)&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Devchat Night (테디 감사해요!)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;UbuCon Korea 2025&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;FEConf 2025&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;K-DEVCON&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;데이터야. 놀자 2025&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;DEVFEST INCHEON&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;GopherCon Korea&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;PYWEB SYMPOSIUM 2025&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;GDG I/O Extended Incheon&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Tech Friends Mixer 2025&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;표준프레임워크 오픈커뮤니티 오프라인 세미나&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;올해는 큰일꾼으로서 커뮤니티에서 이루어질 활동 전반을 기획하거나 운영과 관련된 실무들을 맡았다. 내가 예상하지 못했던 갑작스러운 상황(?)이였고 사내외를 통틀어 처음으로 리더라는 역할을 감당하기 때문에 시행착오도 많았지만, 무사히 2025년을 보낸 것에 안도감이 든다. (과로를 겪기도 했지만..)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;항상 든든하게 곁을 지켜주신 일꾼단(형준님, 혜진님, 상표님, 효영님, 동기님, 경석님 등)에게 다시한번 진심으로 감사드린다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Spring Camp 2024, KSUG 리더 세미나도 좋은 기억, 순간이었지만, 올해 다른 관점에서 준비한 Spring Camp 2025, KSUG Spring AI Meetup, FOSS For All 커뮤니티 부스 운영, 오프라인 스터디 지원 등은 내가 개발자 커뮤니티를 위해 무엇을 할 수 있는지 깨닫게 해준 귀중한 시간이었다. 그렇다고 그만둔다는 것은 아니고.. 내년에는 새로운 큰일꾼님과 함께 더욱 왕성한 활동을 이어갈 예정이다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;올해 운영하거나 지원했던 행사는 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Spring Camp 2025&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;KSUG Spring AI Meetup&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(기도님과 경석님께서 모집하고 운영한) 취업 준비생 스터디 오프라인 밋업&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;FOSS For All Conference 2025 - 커뮤니티 부스 운영&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그 외에도 방통대 동아리 그로스로그에 참여해 직장인 대학생들의 뜨거운 열정을 접하며 긍정적인 자극을 받기도 했다. 또한, 지인들의 권유로 진행한 대학교 졸업생 특강이나 개인적으로 진행한 커피챗, 모의 면접, 이력서 리뷰 활동 등을 통해 타인에게 도움을 주며 나 또한 많은 동기부여를 얻었다. 이렇게 어려운 시기에 취업과 이직에 성공한 분들은 정말 대단하시다고 생각한다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;집&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;4년 전 매매하여 살고 있는 40년된 구축 빌라가 재건축이 확정되었다. 기쁜 일이지만 준비해야할 분담금과 여러 현실적인 이슈로 인해 2026년은 절약을 생활화해야 할 것 같다. 요새 요동치는 집값을 보니 이주 기간에 어디서 지내야 할지 막막하기도 한데, 내년 1~2분기에는 부동산 공부를 병행하며 구체적인 대책을 세워야 할 듯싶다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;책&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2025년 동안 구매한 책은 80권 정도이며 읽은 책은 39권이였다. 구매한 카테고리는 40%가 프로덕트 매니지먼트와 UX 였고, 40%가 기술 서적이였으며, 나머지 20%는 AI 서적이다. (2026년에는 AI 서적을 거의 구매하지 않을 예정이다) 읽은 책 중 기억에 남는 책은 &amp;lt;해결 할 프로덕트&amp;gt;, &amp;lt;&lt;span style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(사용자를) 생각하게 하지 마!&amp;gt;, &amp;lt;&lt;/span&gt;&lt;span style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Code Complete 2판&amp;gt;, &amp;lt;&lt;/span&gt;&lt;span style=&quot;text-align: center;&quot;&gt;모듈러 아키텍처&amp;gt;, &amp;lt;이해관계자중심 소프트웨어 개발&amp;gt;, &amp;lt;밑바닥부터 만들면서 배우는 LLM&amp;gt; 이다. &lt;/span&gt;&lt;span style=&quot;text-align: center;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;text-align: center; color: #000000;&quot;&gt;&lt;span style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;text-align: center;&quot;&gt;그리고 최근 2년간 읽었던 150권의 책을 알라딘에 판매했다. &lt;/span&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 style=&quot;text-align: center; color: #000000;&quot;&gt;&lt;span style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;text-align: center;&quot;&gt;내년 1분기에는 아직 다 읽지 못한 &lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;lt;&lt;span style=&quot;text-align: center;&quot;&gt;Code Complete 2판&amp;gt;,&lt;/span&gt; &amp;lt;오픈 소스 소프트웨어 아키텍처&amp;gt; 그리고 &amp;lt;이제부터 내가 회계 시스템 담당자라는데&amp;gt; 를 집중해서 볼 생각이다.&lt;/span&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;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;토이, 사이드 프로젝트&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2025년은 AI 프로토타이핑 도구와 에이전트 등이 그야말로 범람한 시기라고 생각한다. 이러한 AI 도구들을 바탕으로 다양한 개인 프로젝트가 쏟아지고 있고 창업으로 이어지는 사례도 많이 보이는 것 같다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하지만 이전에 동갑내기 지인들과 인도네시아에 소셜 매칭 앱을 출시해 본 경험에 비추어 볼 때, 아무리 도구의 퍼포먼스가 5~10배 좋아졌더라도 내가 가용할 수 있는 리소스로는 그정도까지 사이드 프로젝트를 하기 어렵다고 판단했다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그래서 개인적으로 사소하지만, 진짜 필요하기에 만들거나 AI 활용 방식을 학습하기 위한 테스트 베드 정도의 프로젝트만 만들기로 했다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2025년 1분기에는 v0를 통해 &lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/brody-0125/Instafit&quot; target=&quot;_self&quot;&gt;Instafit&lt;/a&gt;을 만들고 Vercel로 호스팅하여 지금까지 사용 중이다. 인스타그램에 추억 / 보관용 사진을 올릴 때마다 이미지가 잘리는 것이 싫었고 시중의 앱들은 광고가 너무 많거나 구독 모델이었기에 직접 개발해서 쓰는 방향으로 틀었다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;지금도 잘 사용하고 있고 최근에는 앨리어싱이나 픽셀 깨짐 현상을 방지하기 위해 캔버스 뷰포트와 렌더링 로직을 개선했다. 아이폰 17 Pro로 스마트폰을 바꾸면서 고해상도 이미지를 업로드하다 보니 확인한 문제였다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&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;1280&quot; data-origin-height=&quot;1280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lPi4H/dJMb99SydQ9/NxuqKmOxiExVudU0UNM1l0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lPi4H/dJMb99SydQ9/NxuqKmOxiExVudU0UNM1l0/img.png&quot; data-alt=&quot;두더지?&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lPi4H/dJMb99SydQ9/NxuqKmOxiExVudU0UNM1l0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlPi4H%2FdJMb99SydQ9%2FNxuqKmOxiExVudU0UNM1l0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;374&quot; height=&quot;374&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1280&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;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;지난달 중순부터는 로컬 파일 탐색기인 &lt;a style=&quot;color: #000000;&quot; href=&quot;https://github.com/brody-0125/L4MOLE&quot; target=&quot;_self&quot;&gt;L4MOLE(Local Mole)&lt;/a&gt;라는 애플리케이션을 만들고 있다. 윈도우 PC에 무분별하게 쌓인 파일들을 이름이나 컨텐츠 내용으로 빠르게 찾고 싶다는 나와 지인의 불편함에서 출발했는데 정작 지금 내가 글을 쓰고 있는 PC는 M4 맥미니이다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;로컬 환경에서 추가 비용 없이 동작해야 했고, 긴 문맥 처리와 계산 비용 최적화가 중요하다고 판단하여 BERT 아키텍처 기반의 &lt;a style=&quot;color: #000000;&quot; href=&quot;https://ollama.com/library/nomic-embed-text&quot; target=&quot;_self&quot;&gt;nomic-embed-text&lt;/a&gt; 모델을 선택했다. 그리고 개발과 친숙하지 않은 지인도 사용할 수 있도록 PyQT를 활용해 GUI를 구현했다. 중간에 검색 방식을 HNSW에서 BM25 기반으로 변경하는 등 실험을 거쳐 현재는 깃허브에 업로드해 둔 상태다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;최근에는 이 프로젝트를 모바일 앱으로 포팅해 보라는 개발자 지인의 제안을 받아 Flutter 기반의 PoC와 안드로이드 환경 리서치를 병행하고 있다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;모바일 환경은 데스크톱보다 파일 접근 권한이나 모니터링 등 제약 사항이 훨씬 많아보이는데 제대로 포팅할 수 있을지 걱정이 많다. 지금은 네트워크 연결 없이 엣지 디바이스에서 AI 모델을 돌리기 위해 ONNX Runtime 기반으로 nomic-embed-text-onnx 모델을 실행하는 PoC를 진행 중이다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;제미나이가 리서치 과정에서 Dart 언어가 BERT 토크나이저나 Tensor 전처리를 충분히 지원하지 않는다고 알려줘서 Claude Code와 함께 &lt;a style=&quot;color: #000000;&quot; href=&quot;https://pub.dev/packages/dart_bert_tokenizer&quot; target=&quot;_self&quot;&gt;dart_bert_tokenizer&lt;/a&gt;와 &lt;a style=&quot;color: #000000;&quot; href=&quot;https://pub.dev/packages/dart_tensor_preprocessing&quot; target=&quot;_self&quot;&gt;dart_tensor_preprocessing&lt;/a&gt; 라이브러리를 제작하여 pub.dev, Github에 배포했었다. (겸사겸사 블로그도 만들고 Verified Publisher 받으려고 2년짜리 도메인도 샀다) &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;다음날 얻은 키워드를 바탕으로 직접 리서치하다 보니 sherpa-onnx라는 활발한 라이브러리(중국인 기여자가 많은듯 하다)가 있었다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;괜히 만들었나 약간 후회할뻔 했지만, 생각을 약간 바꿔서 바퀴를 재발명하는 재미도 있겠다며 PoC에 활용하고 있다. 내년에는 폴더만 만들고 놔둔 Immersive Translation 크롬 익스텐션을 직접 만들어 볼 계획이다. 익스텐션을 제작할 때는 오픈소스 번역기인 &lt;a style=&quot;color: #000000;&quot; href=&quot;https://libretranslate.com/&quot; target=&quot;_self&quot;&gt;libretranslate&lt;/a&gt; 를 로컬에 띄워서 연동하는 형태로 개발하려 한다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;나를 위한 시간&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;앞서 열거한 여러 활동을 병행하면서도 나를 위한 시간을 확보하기 위해 노력했는데 크게 여행과 운동으로 나눌 수 있을 듯하다. 작년에 대만 여행을 다녀오며 처음으로 해외여행을 경험했고, 진작 이런 경험을 해볼 걸 하는 아쉬움이 컸던 터라 이후로는 1년에 한 번은 꼭 여행을 떠나겠다는 목표를 세웠다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;&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;4284&quot; data-origin-height=&quot;5712&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BsPLc/dJMcabv4aKe/cdI7RUjbpaJhDaG0RkxkzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BsPLc/dJMcabv4aKe/cdI7RUjbpaJhDaG0RkxkzK/img.png&quot; data-alt=&quot;북해도 오타루&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BsPLc/dJMcabv4aKe/cdI7RUjbpaJhDaG0RkxkzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBsPLc%2FdJMcabv4aKe%2FcdI7RUjbpaJhDaG0RkxkzK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;475&quot; height=&quot;633&quot; data-origin-width=&quot;4284&quot; data-origin-height=&quot;5712&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;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그 목표에 따라 올해는 일본 홋카이도(4월)와 부산 여행(10월)을 다녀왔다. 방문했던 장소와 먹었던 음식 하나하나가 소중한 기억으로 남았고, 그 추억들이 2025년을 버티고 활동을 지속하게 하는 큰 힘이 되어주었다고 생각한다. 지금은 일본 여행 중 가장 잘 찍은 사진을 하나 선택해 아이디어스에 폰 케이스 제작을 요청하고 기다리는 중이다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;운동은 나의 생존을 위해 시작했는데, PT를 다닌지 4년이 되어가지만 살이 빠지기는커녕 5kg가 더 붙었다. 불 규칙한 식사와 수면 시간(3년동안 평일에는 3~4시간을 자고 주말에 몰아잤다) 때문인 것 같아서 올해 3분기 부터는 최소 7시간 수면을 지키고 있다. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;PT 외에도 지하철 역 3정거장 전에 내려서 걷기나 필라테스를 병행하고 있는데 최근 한달간 3kg가 빠져서 기분이 좋은 상태다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 외에도 아직 진행 중이라 미처 다 적지 못한 일들이 남아 있고 하고 싶은 일도 많다. (티스토리를 버리는 것도..) 내년에는 이 모든 일들을 잘 마무리하고 싶으면서도, 앞으로 마주하게 될 새로운 일이 기대된다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>2025년 회고</category>
      <category>대학교</category>
      <category>여행</category>
      <category>집</category>
      <category>토이 프로젝트</category>
      <category>회고</category>
      <author>Junior Lob!</author>
      <guid isPermaLink="true">https://lob-dev.tistory.com/95</guid>
      <comments>https://lob-dev.tistory.com/95#entry95comment</comments>
      <pubDate>Sun, 28 Dec 2025 19:55:13 +0900</pubDate>
    </item>
    <item>
      <title>왜 개발자 커뮤니티에 기여하기 시작했을까?</title>
      <link>https://lob-dev.tistory.com/94</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;제가&amp;nbsp;커뮤니티에&amp;nbsp;기여하게&amp;nbsp;된&amp;nbsp;계기는&amp;nbsp;복합적이지만&amp;nbsp;결정적인&amp;nbsp;트리거는&amp;nbsp;2024년&amp;nbsp;1월&amp;nbsp;27일에&amp;nbsp;진행된&amp;nbsp;&lt;b&gt;제1회&amp;nbsp;게으른&amp;nbsp;개발자&amp;nbsp;컨퍼런스&lt;/b&gt;였습니다.&amp;nbsp;외부에&amp;nbsp;공개되지&amp;nbsp;않는&amp;nbsp;대학교&amp;nbsp;컴퓨터&amp;nbsp;동아리,&amp;nbsp;교육&amp;nbsp;학원,&amp;nbsp;사내에서의&amp;nbsp;발표&amp;nbsp;경험만&amp;nbsp;가지고&amp;nbsp;있던&amp;nbsp;제가&amp;nbsp;&lt;b&gt;처음으로&amp;nbsp;공개된&amp;nbsp;행사에서&amp;nbsp;발표했던&lt;/b&gt; 한 순간이며, &lt;b&gt;처음으로 연사로서 자리했던&lt;/b&gt; 시간이었습니다. (이때 발표를 권유해 주신 ㅅㅇ님, ㅎㅈ님, ㅈㅇ님께 감사드립니다.)&amp;nbsp;&lt;br /&gt;&lt;br /&gt;발표를&amp;nbsp;준비했던&amp;nbsp;약&amp;nbsp;2달간의&amp;nbsp;시간은&amp;nbsp;나누고자&amp;nbsp;하는&amp;nbsp;인사이트를&amp;nbsp;정하고,&amp;nbsp;모호하게&amp;nbsp;알고&amp;nbsp;있었거나&amp;nbsp;정리하지&amp;nbsp;않았던&amp;nbsp;지식을&amp;nbsp;정리하고&amp;nbsp;다듬는&amp;nbsp;시간이었습니다.&amp;nbsp;처음에는&amp;nbsp;그때&amp;nbsp;당시&amp;nbsp;재직&amp;nbsp;중이던&amp;nbsp;회사에서&amp;nbsp;경험했던&amp;nbsp;GraphQL에&amp;nbsp;대한&amp;nbsp;인사이트를&amp;nbsp;제공하고자&amp;nbsp;했다가,&amp;nbsp;좀&amp;nbsp;더&amp;nbsp;근본적인&amp;nbsp;API&amp;nbsp;설계에&amp;nbsp;대한&amp;nbsp;이야기를&amp;nbsp;하고&amp;nbsp;싶었고&amp;nbsp;(자세한&amp;nbsp;내용은&amp;nbsp;&lt;a title=&quot;https://lob-dev.tistory.com/89&quot; href=&quot;https://lob-dev.tistory.com/89&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://lob-dev.tistory.com/89&lt;/a&gt; 에서 주저리 주저리 했습니다) 발표 준비를 한 지 1달이 넘어가는 시점에 급하게 주제를 바꾸게 되었습니다. &lt;br /&gt;&lt;br /&gt;결과적으로 발표의 난이도나 설명이 생각한 것보다 매끄럽게 되지 않아 아쉬웠지만 이 경험을 통해 밀어 넣었던 지식이 체계적으로 정리되고 제 생각이 더욱 명확해지는 것을 경험했습니다. 이런 경험을 했다면 발표를 더 하려고 노력해 볼 법도 한데 문득 그런 생각이 들었습니다. &lt;b&gt;이런 기회가 있음을 알지 못하거나 하고 싶은 사람들이 많을 텐데 그들에게도 이런 기회를 주고 싶다.&lt;/b&gt; 이 생각을 한 이후로 제가 어떻게 기여할 수 있을지 고민하기 시작했는데, 마침 행사가 끝난 지 얼마 되지 않아 한국 스프링 사용자 모임에서 운영진을 모집하는 것을 볼 수 있었습니다.&lt;br /&gt;&lt;br /&gt;JVM 관련 스택을 다루는 개발자로서 한국 스프링 사용자 모임이 주최하는 Spring Camp는 가보고 싶지만 기회가 닿지 않던 유명한 컨퍼런스이었는데, 여기에 기여할 수 있다는 것을 보자마자 고민도 하지 않고 바로 지원했던 것이 지금까지 계속 이어지고 있네요.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;2025년에는 한 명의 개인 기여자가 아닌 커뮤니티 리더로서 Spring Camp와 세미나, 밋업을 기획하고 다른 커뮤니티 운영진들과 교류하고, 부스 등을 운영하면서 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;회사에서 얻을 수 없는 다양한 경험들을 쌓고 성장 동력을 채우고 있습니다. 또한 새로운 인연을 얻고 좋은 기억도 많이 쌓아가고 있네요..&lt;span&gt; &lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;커뮤니티에 기여하고자 하는 마음이 있으시다면 보이는 기회를 잡아보시길 추천드립니다.&lt;/p&gt;</description>
      <category>KSUG</category>
      <category>게으른개발자컨퍼런스</category>
      <category>커뮤니티</category>
      <category>커뮤니티 오거나이저</category>
      <category>한국 스프링 사용자 모임</category>
      <author>Junior Lob!</author>
      <guid isPermaLink="true">https://lob-dev.tistory.com/94</guid>
      <comments>https://lob-dev.tistory.com/94#entry94comment</comments>
      <pubDate>Sat, 8 Nov 2025 23:10:12 +0900</pubDate>
    </item>
    <item>
      <title>내가 Redis를 사용할 때 바라보는 관점 1편 - 적절한 Key 설계하기</title>
      <link>https://lob-dev.tistory.com/93</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;항상 Single Redis Cluster / 단일 Primary 기반의 Replication 구조만 운영할 수는 없다.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장애 민감도가 낮거나, 투자할 수 있는 비용 또는 발생 트래픽이 적은 서비스는 Redis Cluster(또는 Dragonfly, Memcached 등 다양한 솔루션이 있겠지만 가장 평범한 예를 들어봅니다) / 단일 Primary 기반의 Replication 구조를 공용으로 만들어 사용하는 것이 도메인 / 서비스 별로 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Redis Cluster / Primary - Secondary&lt;/span&gt;를 나누어 운영하는 것보다 인프라 인력에 대한 비용이나 유지 보수 측면에서 이점이 있다고 볼 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;*&lt;b&gt;EOL 도래 시 무중단 버전 업그레이드 같은 운영 작업 리소스, 운영 모니터링 등을 인프라 인력 비용으로 간주&lt;/b&gt;합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 Redis 뿐만 아니라 MySQL, MongoDB 등 다양한 데이터 저장소나 Kafka, RabbitMQ 같은 메시지 큐 서비스 모두에게 해당한다고 볼 수 있죠. 하지만 서비스가 커짐에 따라 아무리 현재의 Single Cluster / Primary가 여러 Node의 집합 또는 연계로 이루어져 있더라도 &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;장기적으로 트래픽, 서비스와 기술 조직의 규모가 커짐에 따라 여러 Cluster로 나누어 운영하는 것이 서비스 단위로 장애를 격리하고 결합도를 낮추며 비용 최적화에도 도움이 됩니다. 여기서 비용 최적화의 대상은 서비스 별 워크로드, 트래픽에 따른 VM Spec의 최적화를 의미합니다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;*여기서 워크로드는 서비스 / 팀 별 사용 사례를 의미하는 것&lt;/b&gt;으로 비즈니스에 필요한 핵심적인 일부 키만 캐싱하고자 하는 경우, 원천 데이터를 제공하고 있으며 전반적으로 모든 데이터가 고루 조회되고 있어 Cache Hit이 낮을 때 대부분의 데이터를 Cache에 적재하여 운용하는 경우라던지, Geospatial Search를 사용하는 경우 등 다양한 사용 사례가 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 &lt;b&gt;서비스가 망하지 않는 한 (내가 배포하지 않더라도) Cluster를 분리할 정도로 성장할 것이다&lt;/b&gt; 라는 생각으로 다른 서비스 / 도메인과의 의존성 없이 Redis를 사용해야 한다고 생각하며, 그 첫 번째 실천이 바로 적절한 Redis Key를 설계하는 것이라고 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Redis에는 Database라는 단위로 Key가 존재할 수 있는 논리적인 공간 (Namespace)를 할당할 수 있고, 각 서비스, Redis Client 마다 다른 Database를 바라볼 수 있는데 왜 Redis Key를 통해 논리적인 구분이 되어야 하나요 라고 이야기하실 수도 있겠습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;뭐 그렇게 논리적으로 격리해 처리하는 것도 의도된 방법이고 나쁘지 않겠다고 생각합니다만.. 저는 실무에서 그렇게 나누어 운영하는 경우를 본 적이 없다시피 합니다. (딱 1번 있는 경험도 제대로 문서화되지 않아 Key를 찾는데 애먹었던 기억만 남아있네요.)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;*개인적으로 Database를 쪼개어 관리하는 것 보다는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Redis Key의 이름을 도메인 / 기능 단위로 잘 구분하는 것&lt;/b&gt;이 운영하기에 더 좋은 방법이라고 생각합니다. Database로 나눈다고 해서 장애가 발생했을 때 격리되는 것도 아니구요.&amp;nbsp;&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;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Single Cluster / 단일 Primary 기반의 Replication 구조에서 Redis Key를 설계하는 방법&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis Key를 설계하는 팀이나 조직마다 선호하는 패턴이나 약속은 다르겠지만 일반적으로 다음과 같이 구분자를 두는 것이 기본적인 방법이라고 생각합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;자주 변하지 않는 것 / 뒤에 붙을 용어, 개념을 포괄하는 것을 가장 앞에 둡니다.&lt;/b&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;이를 통해 키의 prefix 만 보더라도 찾고자하는 키가 포함되어 있는지 구분할 수 있고, 패턴 매칭의 용이성을 가집니다. (Scan을 통한 특정 Key 집합 삭제 등)&lt;/li&gt;
&lt;li&gt;e.g. FEED:WEAKLY_BEST_FEED_LIST:V1 (도메인 / 데이터 / 버전)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;특별한 이유가 없는 한, 모든 Cache에 버전을 부여합니다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Cache에 부여된 버전은 &lt;b&gt;운영 편의성을 제공&lt;/b&gt;합니다. 같은 Cache 라고 하더라도 저장 / 변환하는 데이터 포맷이 변경되거나 압축 알고리즘이 적용 또는 바뀌었을 때, 기존에 저장된 Cache를 조회해 오는 것은 장애로 이어질 수 있습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;발생 트래픽이 높은 환경에서, 아직 만료되지 않은 V1은 이전 버전의 Application이 점유해 계속 서빙하고 신규 버전의 Application은 Warmup 또는 Look-Aside 방식을 통해 V2 Cache를 적재 / 제공한다면 안정적인 무중단 배포가 가능해집니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;*Warmup, Look-Aside 방식으로는 트래픽 대응을 할 수 없는 경우, V1 Key를 별도 Batch로 읽어와 V2로 데이터를 마이그레이션 하고 V2 Application을 배포할 수도 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;만약 신규 버전에서 예상치 못한 이슈가 발생하더라도 변경 없이 이전 버전으로 롤백만 수행하여 큰 장애를 방지할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;가능하면 약어를 쓰지 않습니다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가끔 글을 읽다보면 추상적인 기준으로 &lt;b&gt;Key의 크기는 작아야한다.&lt;/b&gt; 라는 식으로 언급하는 글들이 많지만 제 경험상 문제가 있을 정도로 Key 길어지는 것은 쉽지 않습니다. (그걸 의도하지 않는 한) 사실 이해할 수 없는 Redis Key를 만들 바에 이해 가능한 Redis Key를 설계하는 것이 훨씬 좋습니다.&lt;/li&gt;
&lt;li&gt;큰 Key를 감주하는 기준은 사용 시나리오에 따라 다릅니다. 높은 동시성, 낮은 지연 시간을 강조하는 서비스에서는 1KB도 매우 큰 키로 간주될 수 있지만 그렇지 않은 서비스에서는 10KB 정도로 사용하는 것이 괜찮을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 Redis Key를 설계한다면 추후 서비스 간의 점진적 분리나 별도 Cluster 구축 시에 Side Effect를 최소화하고 분리할 수 있습니다. (기가막힌 방법을 통해 그렇지 않게끔 만드는 경우도 있겠습니다만..) 물리적인 분리 전에 논리적인 단계에서의 분리부터 차근차근 해보면 좋겠습니다.&lt;/p&gt;</description>
      <category>cache</category>
      <category>Redis</category>
      <category>Redis Key</category>
      <category>versioning</category>
      <author>Junior Lob!</author>
      <guid isPermaLink="true">https://lob-dev.tistory.com/93</guid>
      <comments>https://lob-dev.tistory.com/93#entry93comment</comments>
      <pubDate>Sun, 19 Oct 2025 21:27:24 +0900</pubDate>
    </item>
    <item>
      <title>Optimistic Locking의 한계와 활용 사례</title>
      <link>https://lob-dev.tistory.com/92</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;생각을 정리하는 의식적인 연습을 위해 지난번 글부터 하나를 작성하는 데 1시간만 투자해보고 있습니다. 아직 세밀한 디테일을 챙기거나 충분한 부연 설명을 덧붙이는 것은 어렵게 느껴지지만 기존의 생각을 빠르게 정리하는 데에는 큰 도움이 된다고 느껴 당분간은 이 방식을 유지해보고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; Optimistic Locking&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Optimistic&amp;nbsp;Locking&lt;/b&gt;(낙관적 잠금, 또는 Optimistic Concurrency Control)은 비관적 잠금(Pessimistic Locking)과 달리 잠금을 사용하지 않는 동시성 제어 방법으로 &lt;b&gt;트랜잭션 간의 충돌이 자주 발생하지 않을 것이라는 가정&lt;/b&gt;하에 고안되었습니다. 이 기법이 적용된 대표적인 예로는 NoSQL인 MongoDB의 WiredTiger 스토리지 엔진이 있습니다.&lt;/p&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;WiredTiger는 RDBMS처럼 트랜잭션 기능을 제공하는데&lt;/b&gt; 락 경합으로 발생하는 병목 현상을 줄이기 위해 Optimistic Locking을 활용하고 있습니다. (그 외에도 Lock-Free 알고리즘을 많이 활용하고 있으니 살펴보시길 바랍니다) WriedTiger는 기본적으로 Repeatable-read 격리 수준을 유지하면서 업데이트 충돌이 발생하면 이를 감지할 수 있도록 구현되어 있고, &lt;b&gt;MongoDB 4.2&lt;/b&gt;부터는 Callback API를 통해 &lt;b&gt;Retry&lt;/b&gt;를 알아서 수행하도록 통합되어 사용자가 직접 Callback Handler를 구현하지 않아도 되게끔 개선되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;MongoDB로 보는 Optimistic Locking의 한계&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 락 경합에 의한 병목 현상을 해소하고자 한 것은 좋았으나 서비스에 따라 앞서 이야기했던&lt;b&gt; &quot;트랜잭션 간의 충돌이 자주 발생하지 않을 것이라는 가정&quot;&lt;/b&gt;이 부합되지 않는 경우가 발생하게 되었습니다. Optimistic Locking을 사용하면서 성공할 때까지 매번 Retry를 수행한다는 것은 매번 실패한 Transaction이 수행되기 전으로 Resorce를 Rollback 해야 한다는 의미인데, Resorce 경합이 과도하게 발생할 경우 수 많은 Transaction이 계속 재시도되고 복구되는 상황에 빠지기 때문입니다. (WiredTiger는 Transaction에서 다루는 데이터를 Shared Cache에 담아놓고 Checkpoint라는 기법을 통해 Commit 또는 Rollback을 수행합니다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매번 Shared Cache의 메모리 영역의 데이터를 이전 시점으로 복구시키는 동작의 성능 부하와 완료되지 못한 Transaction이 계속 늘어남에 따라 할당된 Shared Cache의 여유 공간이 없어지는 것 또한 또 다른 장애로 번질 수 있는 상황까지 야기하게 되었습니다. (이 경우 수행되던 Transaction이 갑작스래 중단되는 경우도 있습니다.)&lt;br /&gt;&lt;br /&gt;결국&lt;b&gt; MongoDB 6.2부터는&lt;/b&gt; &lt;b&gt;transactionTooLargeForCacheThreshold&lt;/b&gt;라는 임계값을 지정하고, 지정된 임계값을 넘었을 때 &lt;b&gt;TransactionTooLargeForCache&lt;/b&gt;를 발생시켜 더 이상 Retry를 수행하지 않도록 변경되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MongoDB의 사례를 돌아보면 Optimistic Locking 기법은 특정 리소스에 대해 데이터 경합이 낮은 경우에만 고려되어야 하는 기법임을 명확히 알 수 있고 현재 서비스에서 대량의 트래픽이 발생하고 특정 리소스에 경합이 발생할 때 Optimistic Locking과 Retry 전략을 고민하고 있다면 적절하지 못한 고민이라 볼 수 있습니다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;그럼 언제 사용할 수 있을까요?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 단점에도 Optimistic Locking을 활용할 수 있는 사례는 여러가지 있겠죠 (최종 값이 반영되는 것이 아닌 지속적으로 증감되는 것, 경합이 발생할 여지는 적지만 갱신 유실이 생겨서는 안되는 것 등). 제가 실제로 제품에 도입한 사례는 원장 테이블에 대한 Distributed Lock과의 혼용 사례입니다. &lt;b&gt;&quot;왜 Pessimistic Locking의 구현체를 사용하고 있으면서 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Optimistic&lt;/span&gt;Locking까지 사용하는 거지?&quot;&lt;/b&gt;라고 의아하게 생각하실 수도 있는데 Distributed Lock에는 필연적으로 신뢰할 수 없는 영역이 존재하기 때문입니다. &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Distributed&lt;span&gt; &lt;/span&gt;&lt;/span&gt;Lock에 접근하는 다수의 서버 인스턴스가 있고, 경합이 자주 발생하는 Resource 이며, 각 서버 인스턴스의 GC Cycle이 다르고 (e.g. Lock을 점유한 뒤 STW가 발생하여 Commit 전에 Lock의 유지 시간을 초과하는 등의 상황), Distributed Lock을 관리하는 저장소의 장애 및 데이터 유실 등이 발생하였을 때 등 다양한 요인으로 인해 아무리 Distributed Lock이 걸려있다고 할지라도 동일 Resorce에 대한 중복 요청 및 갱신 유실은 충분히 발생할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 각 Resource에 Version을 부여하고 갱신 작업을 충돌시킨 뒤 Retry를 수행하지 않는다면 최소한 데이터 정합성은 보장할 수 있게 됩니다. (유사하게 Lock에 대한 Version을 부여하여 과거의 요청을 거부, 무시하는 fencing token 기법도 있습니다) &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;이렇게 부여된 Version은 추후에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Domain Event, Event Store와 통합하여 활용하는 Event Sourcing 구조로 개선할 때에 동일 리소스에 대한 변경을 추적 또한 손쉽게 달성할 수 있는 장점도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Reference&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://en.wikipedia.org/wiki/Optimistic_concurrency_control&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://en.wikipedia.org/wiki/Optimistic_concurrency_control&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://source.wiredtiger.com/1.4.2/architecture.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://source.wiredtiger.com/1.4.2/architecture.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.mongodb.com/ko-kr/docs/manual/core/transactions-in-applications/#std-label-unknown-transaction-commit-result&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.mongodb.com/ko-kr/docs/manual/core/transactions-in-applications/#std-label-unknown-transaction-commit-result&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming</category>
      <category>distributed lock</category>
      <category>MongoDB</category>
      <category>Optimistic Locking</category>
      <category>transaction</category>
      <category>WiredTiger</category>
      <author>Junior Lob!</author>
      <guid isPermaLink="true">https://lob-dev.tistory.com/92</guid>
      <comments>https://lob-dev.tistory.com/92#entry92comment</comments>
      <pubDate>Thu, 9 Oct 2025 17:10:33 +0900</pubDate>
    </item>
    <item>
      <title>Cache Stampede을 대응하는 최후의 수단 중 하나, Request Collapsing</title>
      <link>https://lob-dev.tistory.com/91</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Cache Stampede&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Cache Stampede&lt;/b&gt;(또는 Thundering Herd)는 읽기 요청이 몰리는 특정 Resource(또는 Reosurce Collection)을 Cache에 저장하고 Look Aside 전략을 사용하고 있는 상황에서 Cache Server에서 어떠한 사유로 인해 Cache Miss가 발생하였을 때 &lt;b&gt;SSoT&lt;/b&gt;(Single Source Of Truth, 여기서는 DBMS, Upstream API 등을 의미)로 &lt;b&gt;다량의 요청이 동시에 몰려&lt;/b&gt; 심한 경우 일정 시간 장애도 발생시키는 위험한 상황을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 어떠한 사유란 다른 글에서 자주 언급하는 &lt;b&gt;Cache의 TTL 만료&lt;/b&gt;를 대표적인 예로 들 수 있으나, &lt;b&gt;잘못된 만료 정책 설정&lt;/b&gt;, Global Cache Server를 사용하는 경우에 Cache를 조회하는 Server(Edge, Downstream API)와 Global Cache Server 간의 &lt;b&gt;네트워크 순단&lt;/b&gt;으로 인한 Cache Miss 또는 Global Cache Server &lt;b&gt;장애로 인한 요청 처리 불가&lt;/b&gt;(또는 복구는 되었으나 Cache가 메모리에서 유실된 경우) 상태도 포함될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 순단으로 인한 Cache Miss나 장애로 인한 메모리 내에서의 Cache 유실은 (TTL 만료에 비해) 흔한 상황이 아니지만 대표적인 대응책으로 언급되는 &lt;b&gt;Pre-Warming&lt;/b&gt;이나 &lt;b&gt;PER(Probablistic Early Recomputation)&lt;/b&gt;으로는 대응할 수 없는 영역의 문제입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PER 알고리즘은 Cache가 만료되기 전, 지수 분포(Exponential Distribution)를 바탕으로 TTL을 재 계산하여 Cache의 TTL을 연장하는 알고리즘입니다. &lt;br /&gt;&lt;br /&gt;해당 알고리즘은 다음의 공식을 사용하고 있는데 &lt;br /&gt;currentTime&amp;minus;(&amp;Delta;&amp;times;&amp;beta;&amp;times;log(rand()))&amp;ge;expiryTime &lt;br /&gt;이를 살펴보면 고정적인 TTL에 대비하여 Cache의 남은 TTL과 해당 Cache를 계산하는데 걸리는 시간(&amp;Delta;), 가중치 상수 (&amp;beta;), rand 함수를 이용해 요청을 통해 확률적으로 TTL을 연장되도록 하는 것임을 확인할 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 상황이 발생했을 때 어떻게 대응할 수 있을까요? 이때 Request Collapsing 기법을 고민해 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Request Collapsing&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Request Collapsing&lt;/b&gt;이란 말 그대로 동일한 Resource에 대해 동시에 접근하는 요청들을 병합하여 실제 SSoT에 도달하는 요청 수를 최소화하는 기법입니다. 해당 기법을 사용하는 대표적인 서비스로는 &lt;b&gt;Discord&lt;/b&gt;(Data Service)가 있으며, HTTP Rever Proxy 중 하나인 &lt;b&gt;Varnish Cache&lt;/b&gt;에서도 이를 제공하고 있습니다. 이 기법을 실제 구현 래밸에서 분석하고 싶다면 각 언어의 &lt;b&gt;GraphQL DataLoader&lt;/b&gt; 구현체, &lt;b&gt;Golang의 singleflight&lt;/b&gt;, 유지 관리 중인 (사실상 방치된) &lt;b&gt;Netflix Hystrix&lt;/b&gt;의 HystrixCollapser를 살펴 보실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Request Collapsing를 구현하는 것은 생각보다 간단합니다. 각 Resource 별로 Unique Key를 할당하고 (e.g. identifier), Atomic 한 Map을 통해 해당하는 Key가 이미 in-flight(접근 중인)인지 확인하며 맞다면 Lock 등을 이용해 접근을 막는 형태로 구현하면 됩니다. (이때 Map은 Local Memory 위에 올려둘 수 있습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kotlin의 Coroutine을 이용해서는 다음과 같이 간단하게 구현해볼 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;class RequestCollapser&amp;lt;K, V&amp;gt;(
    private val coroutineScope: CoroutineScope,
) {
    private val calls = ConcurrentHashMap&amp;lt;K, Deferred&amp;lt;V&amp;gt;&amp;gt;()

    suspend fun execute(key: K, block: suspend () -&amp;gt; V): V {
        val deferred = calls.computeIfAbsent(key) {
            coroutineScope.async {
                try {
                    block()
                } finally {
                    calls.remove(key)
                }
            }
        }

        return deferred.await()
    }
}&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;이러한 RequestCollapser는 중복된 요청을 최소화함으로써 SSoT가 받는 부하를 획기적으로 감소시킬 수 있습니다. 실제 운영 중인 서비스 내에 도입했을 때 (동일한 코드는 아니지만) 실제 요청 수 대비하여 90%가량의 요청이 SSoT에 접근하지 않고 대기한 뒤 동일한 결과를 받는 것을 확인할 수 있었습니다. &lt;br /&gt;&lt;br /&gt;(그렇다고 막무가내로 이를 도입하는 것은 지양하시길 바랍니다. 최소한 각 Server 별로 트래픽 기준 같은 시간동안 대기하는 스레드의 수를 고려해 메모리 사용량을 검토할 필요가 있고, 철저한 성능 테스트가 필요합니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글을 통해 가볍게 Request Collapsing 기법에 대해서 다루어보았습니다. 오랜만에 글을 작성하다보니 힘을 빼고 가볍게 작성해 보았는데 어떤 느낌일지 아직 잘 모르겠네요. ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀 더 자세한 내용은 아래의 링크를 참고하시길 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Reference&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://en.wikipedia.org/wiki/Single_source_of_truth&quot;&gt;https://en.wikipedia.org/wiki/Single_source_of_truth&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Cache_stampede&quot;&gt;https://en.wikipedia.org/wiki/Cache_stampede&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://discord.com/blog/how-discord-stores-trillions-of-messages&quot;&gt;https://discord.com/blog/how-discord-stores-trillions-of-messages&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Programming</category>
      <category>cache stampede</category>
      <category>Request Collapsing</category>
      <category>single source of truth</category>
      <category>Thundering Herd</category>
      <author>Junior Lob!</author>
      <guid isPermaLink="true">https://lob-dev.tistory.com/91</guid>
      <comments>https://lob-dev.tistory.com/91#entry91comment</comments>
      <pubDate>Thu, 25 Sep 2025 00:40:22 +0900</pubDate>
    </item>
    <item>
      <title>RESTful 설계 원칙에 대한 못다 한 이야기</title>
      <link>https://lob-dev.tistory.com/90</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;이 글의 결론만 알고 싶다면?&lt;/b&gt;&lt;/span&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;span style=&quot;font-family: Noto Serif KR;&quot;&gt;최근 RESTful API라고 부르는 결과물들은 엄밀히 따지자면 대부분 RESTful한 API가 아닙니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;RESTful API라는 키워드를 만족하는데 매몰되기보다는 표준 HTTP API 요소를 활용해 API를 적절히 표현하고 확장하는 아이디어를 이해하는 것이 중요합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;RESTful API가 적절하지 않은 시점과 상황을 고려하는 유연한 사고가 필요하다고 생각합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;이 글을 쓰게 된 배경&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;2024년 1월 말 게으른 개발자 컨퍼런스에서 &amp;ldquo;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;소비자 관점의 API 설계 패턴, 사례 훑어보기&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;rdquo; 라는 주제로 발표하며 맥락상 내용에서 제외했거나 시간 관계상 언급하지 못했던 부분을 글로 남겨보고자 합니다. 하단 참고 링크에서 발표 자료를 확인하실 수 있습니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;RESTful 설계 원칙에 대한 이해도가 있고, 클라이언트와의 협업 경험과 함께 API를 실제로 개발하거나 운영해 보신 경험이 있지 않으시다면 아래 내용이 뜬구름 잡는 소리로 들리실 수 있습니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;개인적인 개발 경험과 지식을 기반으로 예측하는 부분도 포함하여 서술하다 보니 틀린 내용이 있을 수 있습니다. 이런 부분은 댓글을 통해 피드백 부탁드립니다. &lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;첫 번째. 여러 사람들은 RESTful 설계 원칙에 대해 잘못 이해하고 있습니다.&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;RESTful 설계 원칙은 Hypermedia를 기반으로 &lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;웹 페이지와 API에 대한 표준화를 위해 고안되었습니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이 원칙은&lt;/span&gt; &lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이전부터 사용해 오던 SOAP, CORBA와 같은 구현이나 연동 방법이 복잡하면서도 표준화된 설계 원칙이 없었던 프로토콜을 대체하기 위해 만들어졌으며, HTTP API의 표준 기능과 Hypermedia를 최대한으로 활용해 API의 응답이 변경되더라도 브라우저에서 문제가 발생하지 않는 하위호환성을 가질 수 있도록 정리되었습니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;RESTful 설계 원칙은 현재 대다수의 웹 애플리케이션에 적용된 구조인 SPA 개념이 없었던 시기에 고안되었고 (2003년 제안) Non-Hypermedia인 JSON(2001년 제안), XML을 응답 포맷으로 허용하지 않습니다. 그렇기에 원칙적으로는 SPA와 함께할 수 없는 API 설계 원칙이라고 볼 수 있습니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;어라? 저희는 React, Vue 등을 기반으로 한 SPA에 대해 RESTful한 API를 잘 제공하고 있는데요?&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;분명 이렇게 생각하시는 분들이 있을 것 같습니다. 원칙을 기반으로 확실히 말씀드리자면, 지금까지 만들어오신 &amp;ldquo;RESTful 한 API&amp;rdquo;라고 생각하신 결과물음 로이필딩이 제안한 RESTful 설계 원칙과는 괴리감이 있는 결과물입니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;단순히 여러 블로그에서 언급하는 &amp;ldquo;리처드슨의 API 성숙도 모델(Richardson Maturity Model)을 만족하지 못했기에 RESTful API가 아니다&amp;rdquo;를 이야기하고자 하는 것이 아닙니다. &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;API 성숙도 모델의 레밸 3을 준수하고 있더라도 API의 응답 포맷을 Hypermedia가 아닌 JSON, XML로 제공하고 있다면 이것은 RESTful API가 아니라 일부 설계 원칙을 차용한 HTTP API를 구현하신 겁니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;리처드슨의 API 성숙도 모델은 개별 리소스에 맞는 엔드포인트 정의하고 (래밸 1), 적절하게 HTTP 메서드를 매핑하며 (래밸 2), 링크를 통한 연관 리소스, API 엔드포인트를 제공해야 (래밸 3) RESTful API로 간주할 수 있다고 정리한 모델입니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;여러 블로그에서 RESTful API에 대한 글을 작성하시며 놓치는 부분을 언급해 보겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;Hypermedia로 분류할 수 있으려면 링크와 폼이라는 표준 방식으로 제어될 수 있어야 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;RESTful 설계 원칙을 만족하는 API는 클라이언트와 서버간의 의존성을 명확히 분리합니다. 즉 API의 응답이 바뀌더라도 브라우저의 어떠한 수정 없이도 정상적으로 화면을 그려내는 것이 가능해야 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;JSON, XML 응답을 기반으로 한 HTTP &amp;amp; JSON RPC는 첫번째로 언급한 조건을 만족하지 못합니다. 애초에 Hypermedia가 아닌 응답을 Javascript 코드를 통해 구문 분석하여 HTML로 변환하는 방법을 취하고 있기 때문이죠.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt; I am getting frustrated by the number of people calling any HTTP-based interface a REST API. Today&amp;rsquo;s example is the&amp;nbsp;&lt;a href=&quot;http://wikis.glassfish.org/socialsite/Wiki.jsp?page=FinalizeRESTAPI&quot;&gt;SocialSite REST API&lt;/a&gt;. That is RPC. It screams RPC. There is so much coupling on display that it should be given an X rating.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;The OpenSocial RESTful protocol is not RESTful. It could be made so with some relatively small changes, but right now it is just wrapping RPC results in common Web media types.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt; When representations are provided in hypertext form with typed relations (using microformats of HTML, RDF in N3 or XML, or even SVG), then automated agents can traverse these applications almost as well as any human.&lt;br /&gt;&lt;br /&gt;로이 필딩의 답변 중 일부 발췌 &lt;br /&gt;(&lt;a href=&quot;https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven)&lt;/a&gt;&lt;/span&gt;&lt;/blockquote&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;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그러다 보니 자연스럽게 두 번째 조건도 만족하지 못합니다. (API 응답을 구문 분석하고 Hypermedia로 변환하는)이런 API 구조상 응답의 변경이 발생하면 브라우저에 그려지는 화면에도 영향을 미치게 됩니다. 이는 RESTful에서 주장하는 것과 달리 클라이언트와 서버 모델 간에 강한 의존 관계가 생겼다는 것을 의미합니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;RESTful 설계 원칙의 창시자인 로이 필딩이나 몇몇 원칙주의자들은 &lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333; text-align: start;&quot;&gt;JSON, XML 포맷을 기반으로 하는 HTTP &lt;/span&gt;API를 RESTful API로 인정하지 않으며, HTTP + JSON RPC 또는 Data API로 구분합니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;좀 더 자세한 내용은(로이 필딩이 뭐라고 하는지 알고 싶으시다면..) 참고 링크 항목의 로이 필딩이 작성한 REST APIs must be hypertext-driven와 Hypermedia systems JSON Data APIs 아티클을 읽어보시길 바랍니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;백엔드 개발자는 항상 HTTP API를 개발하는 사람은 아닙니다. 그렇기에 RESTful한 API를 개발하는 것이 역량과 관련된 주요한 포인트가 아니라고 생각합니다. 그래서 제 생각에는 중요하지 않은 RESTful 라는 &lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333; text-align: start;&quot;&gt;주제&lt;/span&gt;에 대해 잘못된 내용을 작성한 블로그들을 지적한다거나 RESTful 아키텍처를 준수하지 않으면 좋지 않은 API다 라는 태클을 걸지는 않습니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;하지만 위와 같은 정확한 이해 없이 RESTful라는 용어를 들먹이며 원칙을 만족해야 한다고 강조하지만 실제로 API 설계에 대한 인사이트는 없다던지 하는 이상하게 엮이고 꼬인 상황들을 보면 정말 의미 없이 리소스나 낭비하고 있구나 라고 생각하게 되는 것 같습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;두 번째. RESTful 설계 원칙은 모든 케이스에 &lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;적합한 &lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;&quot;정답&amp;ldquo;이 아닙니다.&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;2000년대 초중반(2000~2008), 대다수에게 보급된 인터넷과 컴퓨터의 영향으로 웹 서비스의 사용자에는 소수의 전문가 외에도 일반 사용자 들이 많이 포함되게 되었습니다. (&quot;웹 서비스를 사용하는 방법이나 지식을 모르는&quot;)&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt; &lt;span style=&quot;font-family: 'Noto Serif KR'; color: #333333; text-align: start;&quot;&gt;RESTful 설계 원칙이 고안된지 얼마되지 않아&lt;span&gt; &lt;/span&gt;&lt;/span&gt;기존 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Hypermedia&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt; 기반의 웹 페이지가 아닌 새로운 방법들도 제안되기 시작했습니다. 대표적으로 AJAX (2005년 제안, Ajax: A New Approach to Web Applications) 기법을 기반으로한 서비스들이 출시되기 시작했습니다. (&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;SoundCloud, Facebook, Twitter, Linkedin 등이 이때부터 시작된 서비스입니다.)&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이때부터 RESTful 설계 원칙들을 JSON RPC에 차용하기 시작했습니다. (그러면서도 RESTful API라고 불렀죠.)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;RESTful 설계 원칙이 고안된지 얼마되지 않아서 사용자 층의 변화, 새로운 유형의 서비스로 인해 웹 개발 생태계에 새로운 접근 방식과 패러다임이 퍼지기 시작했습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;그러다보니 RESTful 설계 원칙은 AJAX 접근 방식을 적용한 웹 서비스가 복합 리소스나 플랫하게 여러 리소스를 표현하는 것이나 동적인 수정 및 요청을 제공하는 것을 고려하지 못한 원칙입니다. 이를 RESTful 설계 원칙에 포함된 것 중 하나인 HATEOAS(Hypermedia as the engine of application state)를 통해 간접적으로도 이해해볼 수 있습니다.&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;HATEOAS는 연관 관계가 있는 리소스 간의 이동을 링크라는 Hypermedia 요소를 통해 클라이언트에게 제공하도록 가이드합니다. 이를 통해 클라이언트는 어떠한 행위(수정, 추가 등)를 하지 않더라도 제공된 링크를 통해 연관된 리소스를 탐색하는 것이 가능하게 됩니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이 원칙을 기반으로 Twitter와 같은 피드 웹 페이지를 구현한다고 가정해 봅시다. 만약 본인이 작성한 피드의 댓글과 반응을 확인하고 싶다고 한다면 아래와 같이 탐색해야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;피드 목록에서 작성한 피드를 선택하고&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;연관된 댓글 리소스에 대한 링크를 눌러 해당 피드에 작성된 댓글 목록을 확인한 후&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;피드 페이지로 돌아가서 반응과 연관된 링크를 눌러 어떤 반응들이 있는지 확인한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이렇게 필요한 데이터를 탐색해야 한다면, 이 서비스의 유저 경험 및 사용성은 좋다고 할 수 있을까요? HATEOAS가 적극적으로 사용되지 않은 것은 이러한 사용성도 영향을 미쳤을거라고 생각합니다. (이에 대해 정확한 히스토리를 알고 계신다면 첨언 부탁드립니다.)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;어찌 됐든 간에 앞서 언급한 서비스들은 RESTful 설계 원칙에 포함된 API 표현 방식을 기반으로 인터페이스를 설계하였으나 Hypermedia와 HATEOAS를 활용하기보다는 JSON 포맷을 이용한 HTTP + JSON RPC 형태로 서비스를 개발하였습니다.&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;하지만 그 이후 발생한 여러 문제점으로 인하여 대체, 보완할 새로운 방법을 물색하기 시작했습니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;해당 서비스들은 아래와 같은 문제들을 겪었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;HTTP API 엔드포인트가 기하급수적으로 늘어남에 따라 클라이언트의 복잡성이 증가하였고 이로 인한 병목 현상이 발생하였습니다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;언제 어떤 API를 호출할지 파악하고 연동한 뒤 필요한 응답을 패칭하거나 다른 모델로 변환하는 등 실제 사용자의 기능을 제공하기 위해 반복적이고 복잡한 구조를 유지하는데 들어가는 리소스가 증가했습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;클라이언트 환경, 정책에 따라 응답 데이터가 달라지는 경우 이를 모두 하나의 응답 모델에 포함하에 따라서 모델과 응답 페이로드가 비대해지고 그로 인한 이력 관리 문제나 메모리 이슈가 발생하였습니다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;수백 개의 필드와 수십 MB의 응답 페이로드에서 필요한 속성만을 골라내니 수 kb 수준만 사용하는 경우도 있었습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;네트워크 경유 횟수, 즉 통신 횟수가 늘어남에 따라 자연스레 지연 시간이 증가하고 성능적인 문제가 심화되었습니다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;일반적으로 클라이언트와 서비스를 제공하는 서버 간의 물리적 거리보다, 서비스 간의 물리적 거리가 짧으며, 통신 경로를 구성하는 요소(e.g. 라우터)도 적습니다. (통신이 전달되는 오버헤드도 감소)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;서비스의 요청을 중간에 캐싱하는 CDN과 같은 Edge Server나 Composite API와 같이 API 요청을 병합하는 레이어를 두는 방법이 네트워크 지연 시간을 감소시키는 효과적인 방법에 속합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;그리고 이러한 문제를 개선하기 위해 다양한 설계 및 실험을 진행하였고 아래와 같은 개선된 API 레이어를 제안 및 구성하기 시작했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;Data API 앞에 추가적인 레이어를 두거나 (Adapter Layer, BFF, Composite API)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;클라이언트 관점의 쿼리 언어(FSQL, GraphQL 등)를 만들어 개발 및 도입&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이러한 시도를 통해 (위 서비스들은) 제품 성장을 더욱 가속화할 수 있었습니다. 자세한 내용은 아래 참고 링크를 확인해 주시길 바랍니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;위 사례를 생각해 보면 어니언, 레이어, 헥사고날 등의 아키텍처 패턴에서 레이어라는 단위로 추상화 수준을 나눔으로써 하위 레이어의 변화를 최소화하고 순수성, 재사용성을 향상시키는 것처럼 API 또한 공급자, 소비자 관점에 따라 명확하게 분리하고 단계적으로 구성하는 것이 필요하다 볼 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;API Composition, BFF Pattern, headless-commerce나 API Led Connectivity로 불리는 패턴, 설계 원칙은 이러한 시도로 인해 만들어진 원칙과 템플릿입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;위 패턴들은 백엔드 개발자가 제공해야만 하는 것들이 아닙니다. 특히 BFF 패턴의 경우 최근 백엔드 개발자가 구축하는 내용으로 많이 소개되었으나, 최초에는 클라이언트 개발팀이 자율성을 가지고 구축하였으며 현재도 그렇게 구성하는 회사들이 존재합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;HTTP API의 표준 요소를 통해 쉽게 확장하는 아이디어를 이해하고 적절히 활용하는 것이 중요합니다.&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;AWS의 CTO인 Werner Vogels도 이야기했던 부분이지만, RESTful 설계 원칙을 일부 차용함으로써 일관성 있게 API를 설계하면서도 표현력이 있으며 API를 최소 단위로 구성해 비즈니스를 유연하게 확장하는 데 도움을 받을 수 있습니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;여기서 강조하고 싶은 부분은 RESTful API에 목매어 충족시키려고 한 것이 아니라 필요한 부분만 차용한 것이라는 겁니다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;다양한 데이터를 제공하거나 복잡한 비정형 데이터를 다루는 도메인, 분산 시스템을 기반으로한 모듈 간 분리 등을 언급하는 현재의 개발 트렌드에서 언제 RESTful 설계 원칙을 차용한 우리의 API 인터페이스가 병목 지점이 될지는 모르는 일이라고 생각합니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;Redis, MongoDB, RabbitMQ, Kafka 등 오픈소스 간의 트레이드오프만 고려하지 말고 이러한 API 설계 영역부터 차근차근 고민할 수 있는 유연한 사고가 필요하지 않을까 싶습니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/span&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://github.com/lazyconf-dev/2024-lazydevconf/blob/main/presentation/lazydev_%ED%85%8C%EC%9D%B4%ED%81%AC%EC%98%A4%ED%94%84_%EC%84%B8%EC%85%982_%EC%86%8C%EB%B9%84%EC%9E%90%20%EA%B4%80%EC%A0%90%EC%9D%98%20API%20%EC%84%A4%EA%B3%84%20%ED%8C%A8%ED%84%B4,%20%EC%82%AC%EB%A1%80%20%ED%9B%91%EC%96%B4%EB%B3%B4%EA%B8%B0.pdf&quot; target=&quot;_self&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;발표 자료 링크&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven&quot; target=&quot;_self&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;REST APIs must be hypertext-driven&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hypermedia.systems/json-data-apis/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;Hypermedia systems JSON Data APIs&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hypermedia.systems/hypermedia-reintroduction/#_so_what_isnt_hypermedia&quot; target=&quot;_self&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;Hypermedia systems reintroduction&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://designftw.mit.edu/lectures/apis/ajax_adaptive_path.pdf&quot; target=&quot;_self&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;Ajax: A New Approach to Web Applications&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://philcalcado.com/2015/09/18/the_back_end_for_front_end_pattern_bff.html&quot; target=&quot;_self&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;The Back-End for Front-End Pattern (BFF)&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://netflixtechblog.com/embracing-the-differences-inside-the-netflix-api-redesign-15fd8b3dc49d&quot; target=&quot;_self&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;Netflix API Redesign&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/blog/engineering/architecture/how-linkedin-adopted-a-graphql-architecture-for-product-developm&quot; target=&quot;_self&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;LinkedIn GraphQL Architecture&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.infoq.com/news/2019/12/airbnb-graphql-migration/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;Airbnb GraphQL Migration&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.popit.kr/graphql-%EA%B7%B8%EB%A6%AC%EA%B3%A0-msa/&quot; target=&quot;_self&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;GraphQL and MSA&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Programming</category>
      <category>hypermedia</category>
      <category>RESTful</category>
      <category>게으른개발자컨퍼런스</category>
      <category>소비자 관점의 api 설계 패턴과 사례 훑어보기</category>
      <author>Junior Lob!</author>
      <guid isPermaLink="true">https://lob-dev.tistory.com/90</guid>
      <comments>https://lob-dev.tistory.com/90#entry90comment</comments>
      <pubDate>Sat, 15 Jun 2024 23:46:29 +0900</pubDate>
    </item>
    <item>
      <title>게으른 개발자 컨퍼런스 발표 후기</title>
      <link>https://lob-dev.tistory.com/89</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[링크드인 전문]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&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 style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&amp;ldquo;게으른 개발자 컨퍼런스&amp;rdquo; 연사로 참여한 후기를 공유합니다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;(오늘까진 부지런한 상태로 보내보겠습니다..!)&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;발표 자료는 해당 레포지토리에서 확인하실 수 있습니다! &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;a href=&quot;https://lnkd.in/gwAr9w2J&quot;&gt;https://lnkd.in/gwAr9w2J&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;계기는 참 가볍고 단순했습니다. 서로가 취업 준비생, 주니어로 만나 이런저런 이야기를 나누던 커뮤니티에서&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&amp;ldquo;석현님 연사 한번 해보시죠. 이렇게 시키지 않으면 평생 안 하실 것 같아서요.&amp;rdquo;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;라는 이야기를 들었습니다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;처음에는 장난치는구나~ 했던 이야기가 현실이 되는 것을 보니 갑자기 손이 떨리고 멘탈이 흔들리더라고요.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;(긴장을 잘하는 편이라 바로 신호가 왔습니다.)&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;이러한 기회를 잘 부여해주시는 유스콘도 제가 너무 부족하다 생각해서 지원해볼 생각조차 못했었는데.. 아찔했습니다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;(이렇게 판이 커질 줄도 몰랐고요..)&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;결국 &amp;ldquo;무엇을 말할 수 있을까?&amp;rdquo;를 열심히 고민하였는데요. 아무리 무료라지만 오는 사람들의 마음과 시간을 가볍게 버릴 수는 없는 노릇이라 정말 머리가 아팠습니다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;처음에는 최근 이직한 회사에서 메인으로 사용하고 있는 GraphQL을 메인 주제로 이야기해 볼까 싶었습니다. 고민한 당시에는 국내 레퍼런스 대부분 이 Node.js 기반이었고, Netflix DGS Framework를 사용하는 글도 정말 찍먹하는 수준이었거든요. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;(본격 GraphQL 약팔..)&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;어떤 아이디어를 기반으로 GraphQL이 만들어졌고 어떻게 발전해왔고 사용하고 개선할 수 있는지 등을 목차로 뽑아서 열심히 내용을 작성했는데요.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;정리된 내용을 읽어보니 GraphQL 사용의 당위성이 너무 부실하게 느껴지는 겁니다. 사용하는 상황에 대한 맥락도 부족했고요. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;(현 회사에 와서 경험하기 전까지 말하는 장점이 와닿지 않았었는데 제 글도 &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;다를 바 없더군요&lt;/span&gt;.)&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;GraphQL 사용의 당위성, 맥락을 채우기 위해서 API가 어떤 상황에서 이러한 변화가 있어야 하는지, 그래서 언제쯤 GraphQL을 고려하게 되는지를 제 한줌의 경험과 사내 문서부터 시작해 다양한 기업들의 사례를 찾아보며 열심히 정리했습니다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;그렇게 만들어서 &amp;ldquo;해치웠나?!&amp;rdquo; 싶었는데 이제는 API 자체에 대한 맥락이 부족하다는 느낌이 들었습니다. 생각해보니 제가 이야기하고 싶었던 API는 개별 도메인 서비스, 시스템 API가 아니었거든요.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;큰 그림에서 &quot;API가 무엇인지&quot;, &quot;조직에서 경험, 프로세스 API나 BFF 계층 등을 도입할 때는 어떤 고민이 필요하다&quot;라는 것도 이야기하고 싶었습니다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;(많은 부분을 이야기하지 못한 것 같지만요.)&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;결국 앞에서 이야기했던 것처럼 주말을 열심히 사용해서 이에 대한 내용을 찾고 정리했는데 이제는 정리한 내용이 너무 많더라고요. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;(30~40분 안에 이야기할 수준이 아니었습니다.)&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;결국 GraphQL 관련 구현 내용은 싹 날려버렸고 정리했던 변경 사례도 2개까지만 남기고 다 지웠으며 감명 깊었던 MuleSoft의 API Led Connectivity 내용이나 패턴들의 구체적인 사례도 추가하지 못했습니다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;뭐 이러 이러한 과정을 통해서 참여하신 분들 모두가 예상하지 못한 (통수?) 발표 자료를 만들게 되었네요.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;(피드백 주신 모든 분이 이런 내용일 줄은 모르셨다고 하셨습니다.)&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;너무 긴장한 나머지 행사 장소에서 최종 리허설을 할 때에도 다리를 떨면서 한숨도 푹푹 쉬고 했었는데 발표를 하는 동안에는 긴장이 조금은 풀린 상태에서 할 수 있었던 것 같아 정말 다행이라고 생각합니다. &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;(역시 우황청심원 효과는 대단합니다.)&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;이제 뒷이야기는 이만 줄이려고 합니다.&amp;nbsp;다른 곳에서 연사, 스태프, 참여자로 만나게 될 분들을 기대합니다. 감사합니다!&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;링크 댓글에 발표 관련 레퍼런스를 남겨놓았습니다!&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;a href=&quot;https://www.linkedin.com/feed/update/urn:li:activity:7157385163050479616/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.linkedin.com/feed/update/urn:li:activity:7157385163050479616/&lt;/a&gt;&lt;/p&gt;</description>
      <category>게으른 개발자 컨퍼런스</category>
      <author>Junior Lob!</author>
      <guid isPermaLink="true">https://lob-dev.tistory.com/89</guid>
      <comments>https://lob-dev.tistory.com/89#entry89comment</comments>
      <pubDate>Sun, 28 Jan 2024 23:59:20 +0900</pubDate>
    </item>
    <item>
      <title>소소한 글 : 2023년 결산과 2024년에 할 것들</title>
      <link>https://lob-dev.tistory.com/88</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;2023년 결산&lt;/h2&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://youtu.be/N-YuSKeFMxY?si=6zs9FKBSvLyn8KsF&quot;&gt;Wake Me Up&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/Z8agqyGIaD8?si=QlV1PWLm8D9rQI-A&quot;&gt;Echo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&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://product.kyobobook.co.kr/detail/S000202093794&quot;&gt;디자인 패턴의 아름다움&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000000408125&quot;&gt;일의 철학&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000001619177&quot;&gt;The One Thing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&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;/li&gt;
&lt;li&gt;캡슐 커피머신&lt;/li&gt;
&lt;li&gt;커피 포트&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;/li&gt;
&lt;/ul&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;GraphQL: Inevitable Next Wave (Tridge)&lt;/li&gt;
&lt;/ul&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;파이브가이즈 오픈런 (4시간 대기..)&lt;/li&gt;
&lt;li&gt;네이버 부스트캠프 웹 모바일 8기 네트워킹 데이&lt;/li&gt;
&lt;li&gt;정수리 꼬순내 (등산 모임)&lt;/li&gt;
&lt;/ul&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;방통대 편입 (~ing)&lt;/li&gt;
&lt;li&gt;사이드 프로젝트 (인도네시아 앱 출시 상태, 작성일 기준 사용자 500명+)&lt;/li&gt;
&lt;/ul&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;네트워킹 모임 (3회)&lt;/li&gt;
&lt;li&gt;커피챗 (3분기 이후, 6회)&lt;/li&gt;
&lt;li&gt;등산 (3분기 이후, 5회)&lt;/li&gt;
&lt;li&gt;운동 (19개월+, 먹는거 안줄여서 살 안빠짐)&lt;/li&gt;
&lt;/ul&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;이직 (중소 -&amp;gt; 중견 -&amp;gt; 대기업 이직을 경험함)&lt;/li&gt;
&lt;li&gt;저녁 운동 -&amp;gt; 아침 운동 전환&lt;/li&gt;
&lt;li&gt;맥북 인생 첫 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2024년에 할 것들&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;회사 프로젝트 구조 개선하기&lt;/li&gt;
&lt;li&gt;방통대 계속 다니기&lt;/li&gt;
&lt;li&gt;외부 공개 장소에서 발표해보기 (1월 말 예정)&lt;/li&gt;
&lt;li&gt;해외 여행가기 (대만 타이페이 - 2월 초 예정, 중국 사천지방 - 4월?)&lt;/li&gt;
&lt;li&gt;사이드 프로젝트 (추천 시스템 - 필터링 패턴 공부도..)&lt;/li&gt;
&lt;li&gt;기획, 제품 관련 서적 읽기 (해결 할 프로덕트, 프로덕트 매니저 원칙, 프로덕트 - 유저를 사로잡는 서비스 기획의 모든 것)&lt;/li&gt;
&lt;li&gt;A/B 테스트 관련 서적 읽기 (실리콘밸리의 실험실, A/B 테스트)&lt;/li&gt;
&lt;li&gt;기술 서적 조금 읽기 (MySQL 성능 최적화 스터디, 대규모 시스템 설계 1, 2)&lt;/li&gt;
&lt;li&gt;스터디 참여하기 (카프카 오픈소스 분석 스터디 - Log, Segment 영역 분석, MySQL 성능 최적화 스터디)&lt;/li&gt;
&lt;li&gt;회의에 잘 참여하는 법 고민하기&lt;/li&gt;
&lt;li&gt;커피챗 더 많이 해보기 (1월 - 3회 예정)&lt;/li&gt;
&lt;li&gt;아침 운동 계속하기&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>2023년</category>
      <category>2024년</category>
      <category>결산</category>
      <category>목표 설정</category>
      <author>Junior Lob!</author>
      <guid isPermaLink="true">https://lob-dev.tistory.com/88</guid>
      <comments>https://lob-dev.tistory.com/88#entry88comment</comments>
      <pubDate>Mon, 1 Jan 2024 16:16:57 +0900</pubDate>
    </item>
    <item>
      <title>Performance : 기본적인 Application Cache 개념과 종류</title>
      <link>https://lob-dev.tistory.com/87</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 특정 구현에 종속되는 내용을 제외한 이론 위주의 정리 글이며, Look-Aside, Write-Back 패턴 등 Cache Strategy에 대한 내용들은 &amp;ldquo;Redis : Cache Strategy Pattern&amp;rdquo; 에서 다루었기 때문에 제외하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘못된 내용이 있을 수 있습니다. 이런 부분은 댓글을 통해 피드백 부탁드립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Cache가 필요한 이유?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스에서 자주 조회되는 데이터에 대해서 매번 접근하고 조합한 뒤 모델을 만들어 응답하는 것은 생각보다 많은 리소스를 사용하게 합니다. (Disk I/O, Join, Calculate, Sort 등..) 데이터 정규화 수준이 높거나 볼륨이 크고 서비스가 구현된 방식이 동기식이라고 가정한다면, 1개의 Request 당 최소한 1개의 Thread를 사용하기 때문에 연산의 지연 시간에 따라 사용자의 요청 수를 처리량이 따라가지 못해 처리되지 못하는 요청들이 생길 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;SNS의 Timeline, Game Ranking List, Portal Site의 Main Page 등&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 우리는 제공할 데이터 모델을 만들거나 접근한 다음 이 정보를 특정 시간 동안 보관하면서 바로 이용하거나 응답하도록 하고 서비스가 사용하는 리소스 (연산, I/O)를 최소화할 방법을 찾아야 하는데 이것이 바로 Cache입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Cache?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cache란 앞서 설명한 것처럼 반복되는 데이터에 대한 접근 및 연산 비용을 줄이기 위해 좀 더 빠르게 접근 가능한 영역에 완성된 데이터 모델을 보관하는 것이라고 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로 사용되는 Cache의 Use Case로는 OS Level의 3 Level Cache와 Web Cache가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3 Level Cache&lt;/b&gt;는 L1 ~ L3 Cache가 계층적인 구조로 배치되어 있는 형태를 말하며, JVM과 같은 VM의 Memory Model 설계에서 동작하는 Thread들이 Memory 영역에 있는 데이터를 Capturing 하여 구성되어 있는 3 Level Cache에 저장하고 데이터 전송으로 발생하는 CPU의 Idle을 최소화해 전체적인 연산 성능을 향상 시킵니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이것은 Thread 간의 Visibility 문제가 발생하는 근본 원인이기도 합니다.&lt;/blockquote&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;Web Cache&lt;/b&gt;는 Reverse Proxy이나 CDN과 Browser의 Local Cache 등 다양한 요소를 통해 제공되는 Cache이며, HTML Document나 Static Resource (Image, Video 등) 제공 시 실제 서버와의 물리적인 거리로 인한 응답 지연 이슈, 물리 서버가 존재하는 또는 경로에 해당하는 네트워크의 대역폭에 의한 bottleneck, 갑작스럽게 발생하는 Flash Crowds를 대처하기 위해 사용되고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 Application Cache에 대한 개념을 정리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Cache design&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cache의 효용성을 뒷받침하는 원칙으로는 전체 결과의 80%가 원인의 20%에 의해 일어나는 현상을 가리키는 &lt;b&gt;Pareto principle&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;이 원칙을 기반으로 Cache에 저장할 데이터를 선택할 때 모든 데이터를 보관하는 것보단 20%에 해당하는 데이터 모델을 판단하는 것을 기본으로 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Cache가 효율적으로 동작하려면 Hit rate를 극대화시켜야 하는데 만약 모든 데이터가 골고루 사용되는 서비스에 억지로 작은 Size의 Cache를 도입하려고 한다면, Cache를 사용하지 못하고 실제 데이터를 가진 곳을 접근하고 Cache를 추가하는 Cache Miss가 빈번하게 일어날 수 있습니다.&lt;/blockquote&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;Cache의 대상이 되는 데이터는 Request Pattern이 Read에 집중되고 Write가 적거나 없는 또는 결정적인 특성을 가지는 데이터여야 합니다. 만약 Write가 자주 발생하는 데이터를 Caching 한다면, 최신 데이터 보장을 위해 write가 있을 때마다 Cache를 Evition 하고 조회 시 다시 Cache를 등록하는 Overhead가 지속적으로 발생하게 됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이러한 경우에는 Cache를 활용하지 않거나 실제 응답 결과가 아닌 사용되는 데이터를 Caching 하여야 합니다. ex) id&lt;/blockquote&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;Cache에 보관할 데이터 모델을 판단하는 기준으로는 Data Locality principle을 따르는데, 이는 크게 Temporal locality와 Spatial locality로 구분할 수 있습니다.&lt;/p&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;Temporal locality&lt;/b&gt;란 요청이 접근한 특정 데이터는 가까운 시일 내에 또 접근할 가능이 높다는 원칙입니다. 이때 데이터는 Loop 등에서 활용되는 Condition 값이나 다른 데이터가 참조하는 Reference id 일 가능성이 높습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;loop count, branch condition, fk&amp;hellip;&lt;/blockquote&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;Spatial locality&lt;/b&gt;란 요청이 접근한 특정 데이터의 주변은 같이 접근할 가능성이 높다는 원칙입니다. 이때 데이터는 메모리 공간을 연속적으로 사용하는 Array 형태일 가능성이 높습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;iterate over array or array list (Dynamic array)&amp;hellip;&lt;/blockquote&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;Cache를 eviction 하는 방식으로는 대표적으로 Expire, LRU, LFU, FIFO 등이 있습니다.&lt;/p&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;Expire &lt;/b&gt;방식은 단순하게 Cache를 생성하였을 때 Memory에 유지될 시간을 지정하는 방식입니다.&lt;/p&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;LRU&lt;/b&gt; 방식은 최근에 사용되지 않은지 오래된 Cache를 Timestamp 등을 통해 판단하여 우선적으로 제거하는 방식입니다.&lt;/p&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;LFU&lt;/b&gt; 방식은 가장 적게 사용된 Cache를 Cache Hit count 등을 통해 판단하여 우선적으로 제거하는 방식입니다. 이 방식은 앞서 언급한 &lt;b&gt;Pareto principle&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;FIFO&lt;/b&gt; 방식은 Cache가 저장된 순서대로 제거하는 방식입니다. 이 방식은 일정 시간이 지나면 접근하지 않을 데이터(ex Access Token과 같은 인증 정보) 등을 관리하는데 효과적인 수단이 될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Cache 종류?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application에서 Cache를 적용하는 범위는 크게 Local, Global, Distributed Cache 등이 있습니다.&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;870&quot; data-origin-height=&quot;202&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4Z2EI/btrFKn1zY4K/KnB9zg1MHNpWibJCkAyA70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4Z2EI/btrFKn1zY4K/KnB9zg1MHNpWibJCkAyA70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4Z2EI/btrFKn1zY4K/KnB9zg1MHNpWibJCkAyA70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4Z2EI%2FbtrFKn1zY4K%2FKnB9zg1MHNpWibJCkAyA70%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;700&quot; height=&quot;163&quot; data-origin-width=&quot;870&quot; data-origin-height=&quot;202&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Local Cache란&lt;/b&gt; Application마다 가지고 있는 In-Memory, Disk에 구성되어있는 방식을 의미합니다. &lt;i&gt;Cache Abstraction, Caffeine, EhCache&amp;hellip;&lt;/i&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;Application에서 바로 접근 가능하기 때문에 성능이 뛰어나지만 동일 Application이 Scale-out 된 경우에는 각 Local Cache가 동기화되지 않기 때문에 데이터의 정합성 문제가 발생하게 됩니다.&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;887&quot; data-origin-height=&quot;318&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7EDXr/btrFFQEQ221/C6E6z5DjwpetHnhoYkxppK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7EDXr/btrFFQEQ221/C6E6z5DjwpetHnhoYkxppK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7EDXr/btrFFQEQ221/C6E6z5DjwpetHnhoYkxppK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7EDXr%2FbtrFFQEQ221%2FC6E6z5DjwpetHnhoYkxppK%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;700&quot; height=&quot;251&quot; data-origin-width=&quot;887&quot; data-origin-height=&quot;318&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 Local Cache 간의 데이터를 동기화하기 위해 Event를 발행하여 다른 Application에 변경된 사항을 전달하는 방식을 많이 사용하나 동기화 로직이 p2p 형태로 구현되어 있다면 전체 서비스 확장성에 영향을 줄 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이는 데이터의 특징에 따라 expire를 짧게 두는 것으로 최소화할 수 있고 Message Queue나 Redis를 이용한 Pub/Sub Pattern을 사용할 수도 있지만, 결국 Eveuntual Consistency 문제가 발생하게 됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;778&quot; data-origin-height=&quot;426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIK3HZ/btrFKHlqVDZ/RxskNo2Fjqb9rKLFP0mklk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIK3HZ/btrFKHlqVDZ/RxskNo2Fjqb9rKLFP0mklk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIK3HZ/btrFKHlqVDZ/RxskNo2Fjqb9rKLFP0mklk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIK3HZ%2FbtrFKHlqVDZ%2FRxskNo2Fjqb9rKLFP0mklk%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;700&quot; height=&quot;383&quot; data-origin-width=&quot;778&quot; data-origin-height=&quot;426&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Global Cache란&lt;/b&gt; 네트워크로 접근 가능한 Cache 서버를 구성하고 Application들이 이를 접근하도록 하는 방식을 의미합니다. &lt;i&gt;Redis Server, MemCached&amp;hellip;&lt;/i&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;777&quot; data-origin-height=&quot;414&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GDSXK/btrFFBHma7q/zkKbH0k4V8G3z2F8rL0f00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GDSXK/btrFFBHma7q/zkKbH0k4V8G3z2F8rL0f00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GDSXK/btrFFBHma7q/zkKbH0k4V8G3z2F8rL0f00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGDSXK%2FbtrFFBHma7q%2FzkKbH0k4V8G3z2F8rL0f00%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;700&quot; height=&quot;373&quot; data-origin-width=&quot;777&quot; data-origin-height=&quot;414&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Local Cache에서 발생하는 데이터 정합성 문제를 해결하는 방법이지만 네트워크를 경유하여 데이터에 접근하기 때문에 Local Cache보다 성능이 낮으며 Application 단위로만 장애가 한정되는 Local Cache에 비해서 Global Cache는 SPoF가 되기 때문에 장애 발생 시 모든 Application에 장애가 전파될 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이를 방지하기 위해 기본적으로 이중화 구성이 필요하며 약간의 Infra 구성 및 운영 비용이 발생하게 됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;823&quot; data-origin-height=&quot;734&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k5zqQ/btrFFC0s8Z4/skjs0PkYLKLbHHjrfeR2ik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k5zqQ/btrFFC0s8Z4/skjs0PkYLKLbHHjrfeR2ik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k5zqQ/btrFFC0s8Z4/skjs0PkYLKLbHHjrfeR2ik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk5zqQ%2FbtrFFC0s8Z4%2Fskjs0PkYLKLbHHjrfeR2ik%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;700&quot; height=&quot;624&quot; data-origin-width=&quot;823&quot; data-origin-height=&quot;734&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Distributed Cache란&lt;/b&gt; 여러 서버를 Cluster로 묶어 Memory, Disk에 데이터를 분산 저장시키고 서버들이 Hash와 같은 특정 Key를 통해 접근할 서버를 지정하는 방식을 의미합니다. &lt;i&gt;Redis Cluster, Acrus, Hazelcast, Infinispan...&lt;/i&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;새로운 Cache 서버를 쉽게 추가할 수 있어 확장성이 매우 높고 Scale out의 이점을 통해 높은 처리량을 가질 수 있지만 상대적으로 높은 infra 구성 및 운영 비용이 발생하며, 특정 서버의 장애로 인한 일부 데이터 유실이 발생할 수 있고 Consistency Hashing을 사용하는 경우 서버의 요청 부하가 다음 서버로 전달되어 연쇄적인 장애가 발생할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Cache 종류?&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ehcache.org/documentation/2.8/apis/cache-eviction-algorithms.html#:~:text=A%20cache%20eviction%20algorithm%20is,determine%20which%20elements%20are%20evicted.&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.ehcache.org/documentation/2.8/apis/cache-eviction-algorithms.html#:~:text=A%20cache%20eviction%20algorithm%20is,determine%20which%20elements%20are%20evicted.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hazelcast.com/use-cases/in-memory-data-grid/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://hazelcast.com/use-cases/in-memory-data-grid/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/swlh/what-is-distributed-caching-d2f1750e7e7a&quot;&gt;https://medium.com/swlh/what-is-distributed-caching-d2f1750e7e7a&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>Programming</category>
      <category>Cache Design</category>
      <category>Data Locality</category>
      <category>Distributed Cache</category>
      <category>Global Cache</category>
      <category>Local Cache</category>
      <author>Junior Lob!</author>
      <guid isPermaLink="true">https://lob-dev.tistory.com/87</guid>
      <comments>https://lob-dev.tistory.com/87#entry87comment</comments>
      <pubDate>Sat, 25 Jun 2022 16:35:52 +0900</pubDate>
    </item>
    <item>
      <title>소소한 글 : 멘토링 회고와 약간의 근황</title>
      <link>https://lob-dev.tistory.com/86</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;멘토링 회고&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;지금까지 약 3달 동안 한 취업 준비생 분을 대상으로 무료 멘토링을 진행하고 있었습니다. 그분은 현재 취업하신 상태이고 공부해왔던 것들과 다른 언어와 기술을 사용하시게 되어 이제는 그러한 것에 대해서 같이 스터디를 하고 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; letter-spacing: 0px;&quot;&gt;멘토링을 진행하기에 제 역량이 충분한 것도 아니었고 지속할 수 있을지 걱정이 많았지만 취업 준비생을 도우면서 오는 뿌듯함과 교육에 대한 경험을 얻어보려고 시작하였는데, 지금 와서 돌아보니 그런 경험 외에도 성장에 대한 자극과 개념의 정립, 복기를 할 수 있었던 좋은 시간이었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;631&quot; data-origin-height=&quot;292&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cC46M4/btrDoTWIcnX/BVpE9Yf3frifs0m5dokwTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cC46M4/btrDoTWIcnX/BVpE9Yf3frifs0m5dokwTk/img.png&quot; data-alt=&quot;질문 중 일부 발췌&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cC46M4/btrDoTWIcnX/BVpE9Yf3frifs0m5dokwTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcC46M4%2FbtrDoTWIcnX%2FBVpE9Yf3frifs0m5dokwTk%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;631&quot; height=&quot;292&quot; data-origin-width=&quot;631&quot; data-origin-height=&quot;292&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;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그럴 수 있었던 이유로는 멘토링을 진행할 때마다 &amp;ldquo;왜 이렇게 만들어졌는지, 왜 이런 상황에서 이점이 있는지&amp;rdquo; 등의 질문을 준비했던 것 덕분인 듯합니다. 이렇게 하다 보니 자연스레 알고 있던 개념들을 정리할 수 있었고 더 알려주기 위해서 개인적으로 사례를 찾아보는 등 자기 개발을 열심히 하는 계기가 되었습니다.&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;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이제 멘티님이 취업하셨기 때문에 또 다른 분을 더 진행하려고 계획 중에 있는데, 앞서 아까웠던 부분들을 잘 보완하고 좀 더 좋은 경험을 드리고자 여러 방법을 구상해보고 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;개념 학습 이후 제공되는 Spec을 기준으로 PoC 만들기 등..&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;근황&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;최근 회사에서 학습된 여러 딥러닝 모델을 연동하고 프론트에 API를 제공해서 사용자의 요청을 관리하는 미들웨어 서버와 모델을 생성하고 인자 값 등을 전달해 추론하는 서버들을 개발하고 있는데요. 그러다 보니 취업 준비생 때나 전 회사, 지금 회사에서 진행했던 업무들과는 &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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;주니어 연차에서 다양한 경험을 해보려고 했던 저로써는 이러한 작업이 꽤나 재미있고 신선했는데요. 이런 경험들을 잘 습득해서 좀 더 다양한 역량들을 키울 수 있도록 노력하고 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;끝!&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>근황</category>
      <category>멘토링 회고</category>
      <author>Junior Lob!</author>
      <guid isPermaLink="true">https://lob-dev.tistory.com/86</guid>
      <comments>https://lob-dev.tistory.com/86#entry86comment</comments>
      <pubDate>Sun, 29 May 2022 12:11:08 +0900</pubDate>
    </item>
  </channel>
</rss>