<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>BLEX</title><link>http://blex.me/</link><description>BLOG EXPRESS ME</description><atom:link href="http://blex.me/rss" rel="self"/><language>ko</language><lastBuildDate>Sun, 22 Mar 2026 10:50:16 +0900</lastBuildDate><item><title>AI 에이전틱 개발에 대한 걱정</title><link>http://blex.me/@baealex/ai-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8B%B1-%EA%B0%9C%EB%B0%9C%EC%97%90-%EB%8C%80%ED%95%9C-%EA%B1%B1%EC%A0%95</link><description>&lt;p&gt;나는 AI를 좋아한다. 내가 원래 못하던 것을 해보게 만들고, 머릿속 상상을 실제 결과물로 밀어붙이게 해주기 때문이다. 아이디어만 있던 것을 코드로 만들고, 문서로 정리하고, 형태 없는 감각을 구현 가능한 작업으로 바꾸는 경험은 분명 멋지다. 취미 프로젝트에서든 개인 작업에서든, AI는 사람의 표현 가능성을 넓혀주는 강력한 도구이다.&lt;/p&gt;&lt;p&gt;그래서 오히려 AI 에이전틱 개발 이야기가 나올 때 더 조심하게 된다. 나는 AI가 할 수 있는 일을 과소평가하고 싶지 않다. 실제로 많은 작업이 더 빨라지고, 내가 혼자서는 엄두를 내지 못했을 일도 시도하게 된다. 다만 그 가능성을 인정하는 것과, 조직 차원에서 그것을 어떻게 받아들여야 하는가는 조금 다른 문제라고 생각한다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;AI는 거대한 블랙박스다&lt;/h2&gt;&lt;p&gt;처음에 조직적으로 AI를 수용하는 방향에서 내가 우려하던 것은 단순히 “AI는 블랙박스”라는 표면적인 문제였다. 우리가 어떻게 이 블랙박스에만 의존하여 개발하나. 하지만 생각해보면 인간도 완전히 투명하지는 않았다. 어떤 시니어의 판단은 경험과 감각에 기대어 있었고, 코드 리뷰 역시 언제나 이상적으로 이뤄진 것은 아니었다. 문서를 형식적으로 읽고 넘어가는 경우도 있었고, 서로의 작업을 깊게 이해하지 못한 채 팀이 굴러가는 순간도 분명 있었다.&lt;/p&gt;&lt;p&gt;좀 더 깊이 생각해보니 내가 진짜 걱정하는 것은 블랙박스의 존재 자체가 아니었다. 더 중요한 것은 그 블랙박스의 속도와 규모이다. 인간의 불투명성은 그래도 같은 인간 단위의 리듬 안에 있었다. 질문하고, 따라가고, 같이 붙잡고 보면서 어느 정도는 회수할 수 있었다. 하지만 AI는 훨씬 더 빠른 속도로 코드와 문서와 수정안을 만들어낸다. 앞으로는 그 속도가 더 빨라질 가능성도 크다.&lt;/p&gt;&lt;p&gt;결국 문제는 AI가 불투명하다는 한 가지 사실보다, 인간 조직이 감당할 수 있는 속도를 넘는 불투명성이 생길 수 있다는 점에 있다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;생산성과 책임감의 간극&lt;/h2&gt;&lt;p&gt;요즘 AI를 둘러싼 이야기에서는 효율이 자주 강조된다. 더 적은 시간에 더 많은 일을 할 수 있고, 더 빠르게 실험하고, 더 넓은 범위를 커버할 수 있다고 말한다. 이 부분은 충분히 사실이다. 나 역시 AI를 쓰면서 특정 작업 시간이 줄어드는 경험을 여러 번 했다.&lt;/p&gt;&lt;p&gt;그런데 동시에 책임은 여전히 사람에게 남는다고 말한다. 이 또한 틀린 말은 아니다. AI를 사용했더라도 최종적으로 결과를 제출한 사람이 책임을 져야 한다는 원칙 자체는 납득할 수 있다. 다만 여기서부터 조금 불편해진다. AI가 산출 속도를 크게 끌어올리면, 그 산출물을 읽고 이해하고 검토해야 하는 사람의 부담도 같이 늘어나기 때문이다.&lt;/p&gt;&lt;p&gt;문제는 사람의 처리 능력이 그렇게 빠르게 늘어나지 않는다는 점이다. AI가 더 빨라졌다고 해서 인간의 이해 속도와 판단 속도까지 같은 비율로 빨라지는 것은 아니다. 그러면 결국 생산 속도와 책임 속도 사이에 틈이 생긴다. 이 틈은 단순히 일이 많아진다는 뜻이 아니다. 더 정확히 말하면, 한 사람이 떠안게 되는 책임의 밀도가 비정상적으로 올라갈 수 있다는 뜻이다.&lt;/p&gt;&lt;p&gt;또 일은 나 혼자 하는가? 동료도 한다. 내가 승인해야 할 것, 이해해야 할 것, 검토해야 할 것이 훨씬 더 빠르게 쌓인다. 그러면 사람은 점점 더 많은 것에 이름을 걸어야 하는데, 정작 그 전부를 충분히 소화하지는 못하는 상태에 놓일 수 있다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;우리는 책임을 분산해왔다&lt;/h2&gt;&lt;p&gt;나는 코드 리뷰를 단순히 버그를 잡거나 코드 스타일을 맞추는 절차라고 보지 않는다. 피어 리뷰는 서로의 작업을 읽으면서 도메인을 함께 이해하고, 의사결정의 배경을 공유하고, 특정 시스템을 한 사람만 알고 있는 상태를 막는 장치라고 생각한다. 즉, 피어 리뷰는 품질 관리이면서 동시에 공동 책임 구조를 만드는 과정이다.&lt;/p&gt;&lt;p&gt;물론 현실의 코드 리뷰가 언제나 이상적으로 작동했던 것은 아니다. 형식적으로 지나가는 리뷰도 있었고, 모든 팀이 깊은 상호 이해를 유지했던 것도 아니다. 다만 AI가 작업 속도를 크게 높이면, 이 피어 리뷰가 더 쉽게 얇아질 수 있다는 점은 분명해 보인다.&lt;/p&gt;&lt;p&gt;사람들은 자연스럽게 자기 작업을 따라가기에도 바빠진다. 자기 문서를 검토하기도 벅찬데 다른 사람의 결과물까지 깊게 읽는 일은 점점 어려워진다. 게다가 상대의 결과물 역시 AI의 도움을 많이 받아 만들어졌다면, 겉으로는 정돈되어 보여도 실제 판단의 경로를 따라가기는 더 어려워질 수 있다.&lt;/p&gt;&lt;p&gt;그 결과 리뷰는 남아 있어도 요약 확인이나 형식 점검, 테스트 통과 여부 확인 정도로 축소될 가능성이 크다. 이런 가벼운 검토도 어떤 맥락에서는 충분히 실용적일 수 있다. 다만 그것이 본래 피어 리뷰가 해오던 공동 이해의 기능까지 대체할 수는 없다고 본다. 요약된 맥락만 알고 승인한 것에 문제가 생겼을 때, 그 누가 동일한 책임감을 느낄 것인가.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;개인의 책임은 무한히 커진다&lt;/h2&gt;&lt;p&gt;나는 한 사람이 더 큰 그림을 보고 팀을 이끄는 구조 자체를 문제라고 생각하지 않는다. 실제로 기존 조직에서도 팀장이나 리드는 더 넓은 맥락을 보고 의사결정을 내렸고, 더 큰 책임을 졌다. 그것은 원래 관리와 리더십의 일부이기도 하다.&lt;/p&gt;&lt;p&gt;내가 불편하게 느끼는 지점은 다른 곳에 있다. 에이전틱 개발에서는 실질적인 산출과 반복 작업의 상당 부분이 AI를 통해 빠르게 생성된다. 겉으로는 한 사람이 전체를 이끄는 것처럼 보이지만, 실제 작업의 밀도와 속도는 이미 한 인간이 직접 소화하던 범위를 넘어설 수 있다. 그런데도 최종 책임은 여전히 사용자 한 사람에게 남는다.&lt;/p&gt;&lt;p&gt;이 구조에서는 사람의 역할이 리더라기보다, 점점 더 많은 산출물 위에 이름을 올리는 승인자에 가까워질 수 있다. 기존의 리드가 팀원들의 작업을 이해 가능한 속도 안에서 조율했다면, 여기서는 AI가 훨씬 빠른 속도로 결과물을 밀어내고 사람은 그것을 끝까지 감당해야 한다. 내가 걱정하는 것은 바로 이 속도 차이에서 생기는 책임의 무게이다.&lt;/p&gt;&lt;p&gt;어쩌면 이 불편함에는 개인적인 감정도 섞여 있을 수 있다. 나는 아직 그렇게 큰 책임을 자연스럽게 감당하는 위치를 충분히 경험해보지 못했고, 그래서 그 무게를 선뜻 떠안고 싶지 않은 마음도 있다. 하지만 그렇다고 해서 이 감각이 단순한 회피라고만 생각하지는 않는다. 한 사람이 실제로 감당할 수 있는 책임의 범위가 어디까지인지 더 자주 묻게 만든다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;구조 설계에 집중해야 한다&lt;/h2&gt;&lt;p&gt;이런 이유로 나는 AI 에이전틱 개발에서 중요한 질문이 단순히 어디까지 자동화할 수 있느냐는 아니라고 생각한다. 물론 자동화 범위를 넓히는 일 자체는 충분히 가치 있다. 어떤 팀에게는 에이전틱 개발이 실제로 큰 도움이 될 수도 있다. 다만 그보다 더 중요한 것은, 자동화가 늘어나는 만큼 공동 이해와 책임 분산의 장치를 어떻게 유지할 것인가이다.&lt;/p&gt;&lt;p&gt;에이전틱 개발이 널리 퍼질수록, 그 속도를 조직이 어떤 방식으로 받아낼 것인지에 대한 논의도 함께 있어야 한다고 생각한다. 누가 이 결정을 이해하고 있는가. 누가 이 변경의 위험을 설명할 수 있는가. 중요한 맥락이 개인과 AI 사이의 대화 안에만 머물러 있지 않은가. 피어 리뷰가 여전히 실질적인가. 특정 사람이 빠졌을 때 시스템 이해도 함께 사라지지 않는가. 나는 이런 질문들이 AI 도입 이후 더 중요해진다고 본다.&lt;/p&gt;&lt;p&gt;물론 많이 실험해 봐야한다. 많이 해봐야 알맞은 방향을 찾을 수 있다. 하지만 실험 단계에서 조차 개인에게 과도한 책임감을 씌우는 행위는 폭력적일 수 있다. AI는 분명 강력한 도구이다. 그리고 앞으로도 더 많은 일을 대신하게 될 것이다. 하지만 그럴수록 사람은 단순히 승인자나 최종 책임자의 자리로만 밀려나서는 안 된다고 생각한다. 조직이 정말 지켜야 하는 것은 효율만이 아니라, 함께 이해하고 함께 책임질 수 있는 구조이다.&lt;/p&gt;&lt;p&gt;속도는 기술이 올려줄 수 있다. 하지만 누가 이해하고, 누가 감당하고, 누가 책임질 것인지는 여전히 사람이 정해야 한다. 나는 에이전틱 개발의 미래가 단지 더 빠른 개발에 머물지 않고, 더 건강한 책임 구조까지 함께 설계하는 방향으로 가면 좋겠다고 생각한다.&lt;/p&gt;</description><pubDate>Sun, 22 Mar 2026 10:50:16 +0900</pubDate><guid>http://blex.me/@baealex/ai-%EC%97%90%EC%9D%B4%EC%A0%84%ED%8B%B1-%EA%B0%9C%EB%B0%9C%EC%97%90-%EB%8C%80%ED%95%9C-%EA%B1%B1%EC%A0%95</guid></item><item><title>사용 가능한 뉴모피즘</title><link>http://blex.me/@baealex/ready-to-use-neumorphism</link><description>&lt;p&gt;뉴모피즘(Neumorphism)은 2019년 Dribbble에서 폭발적으로 유행했지만, 실제 프로덕션에 적용된 사례는 거의 없다. "예쁘지만 쓸 수 없다"는 평가가 지배적이었다. 이 문서는 그 이유를 해부하고, &lt;strong&gt;진짜 사용할 수 있는 뉴모피즘&lt;/strong&gt;을 만들기 위한 구체적인 규칙을 정리한다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;왜 하필 뉴모피즘…?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;뉴모피즘은 내가 가장 좋아하는 UI 중 하나다. 뉴모피즘의 첫 등장에 감탄을 금할 수 없었다. 시각적 착시을 활용해 평면 화면에 생기를 불어넣은 아름다운 예술작품 같았다. 하지만 뉴모피즘을 실제로 적용한 적도, 적용된 앱을 본적도 없다. 다양한 디자인 인터페이스 공부해보며, 사용 가능한 뉴모피즘을 구상해보고 싶어졌다.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;디자인은 감상을 위한 것이 아니라 사용하기 위한 것이다.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;1. 뉴모피즘이 실패한 이유&lt;/h2&gt;&lt;h4&gt;1.1 그림자는 깊이지, 행동유도성이 아니다&lt;/h4&gt;&lt;p&gt;전통적인 UI에서 버튼은 색상, 테두리, 텍스트 대비로 "나를 눌러"라고 말한다. 뉴모피즘에서 버튼은 배경과 같은 색이고, 그림자만으로 돌출을 표현한다. 문제는 &lt;strong&gt;카드도 돌출이고, 버튼도 돌출&lt;/strong&gt;이라는 것. 사용자는 뭘 누를 수 있는지 알 수 없다. 눈으로 보기에는 아름답지만 구분하는데 사용하는 에너지, 실제 예상과 다른 동작은 피로감을 유발시킨다.&lt;/p&gt;&lt;h4&gt;1.2 상태 변화의 미묘함&lt;/h4&gt;&lt;p&gt;hover에서 그림자가 줄어들고, active에서 inset으로 바뀌는 것은 &lt;strong&gt;마우스를 올려야만&lt;/strong&gt; 발견할 수 있다. 모바일에서는 hover 자체가 없다. 접근성에 치명적이며 PC 사용자에 비해서 더 심각한 피로감을 유발한다.&lt;/p&gt;&lt;h4&gt;1.3 모노톤의 함정&lt;/h4&gt;&lt;p&gt;모든 요소가 &lt;code&gt;#e0e5ec&lt;/code&gt;로 동일하면 시각적 계층이 무너진다. 중요한 것과 중요하지 않은 것의 구분이 불가능하다. 때때로 시각적인 중요도를 위해서 무분별한 raise로 계층을 잡기도 하는데 이때는 화면에 점토성이 만들어진다. 이때부터는 사용성을 완전히 포기한다는 선언과도 같다.&lt;/p&gt;&lt;h4&gt;1.4 큰 요소에서 양각&lt;/h4&gt;&lt;p&gt;작은 버튼의 양각은 자연스럽지만, 큰 카드에 강한 그림자를 주면 "플로팅" 하고 있는 부자연스러움이 생긴다. 뉴모피즘은 &lt;strong&gt;동일 표면에서의 돌출/함몰&lt;/strong&gt;이 핵심 아이덴티티이며 미학인데, 큰 요소는 이 환상을 깨뜨린다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;2. 해결 방안: 컬러 하나 추가&lt;/h2&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/22/20262221_xznnrrswEjpiXxviI3bs.png" alt="image.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;Dribbble의 원본 뉴모피즘이 실패한 이유는 "아무것도 추가하지 않았기" 때문이다. 해결은 의외로 단순할지 모른다.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;단 하나의 액센트 컬러&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;이것이 뉴모피즘의 모노톤 철학을 유지하면서 행동유도성을 확보하는 유일한 균형점이다. 물론 뉴모피즘에 색상이 들어가는 순간 양각의 느낌이 사라지고 만다. 위 이미지에서도 액센트 컬러가 들어간 버튼은 그저 플랫하게 보인다. 하지만 포기할건 포기해야 사용 가능한 디자인이다. “뉴모피즘스럽게 보이기” 위한게 디자인 목적이 되어선 안된다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Primary 버튼: &lt;strong&gt;액센트 색상&lt;/strong&gt; → “이건 누를 수 있어”, 페이지의 핵심 기능 전달&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Secondary 버튼: 배경색 + &lt;strong&gt;양각&lt;/strong&gt; → 호버 시 press 피드백으로 인터랙션 가능성 전달&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;토글 ON: &lt;strong&gt;액센트 색상&lt;/strong&gt; → 상태가 바뀌었다는 시각적 확인&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;포커스 링: &lt;strong&gt;액센트 글로우&lt;/strong&gt; → 키보드 사용자에게 현재 위치 전달&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;색상 외에는 그림자와 표면만으로 모든 것을 표현한다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;3. 물리적 물성&lt;/h2&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/22/20262221_ByELzQEXKWwfWlVp5dqh.png" alt="image.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;뉴모피즘은 "화면이 하나의 재료로 만들어져 있다"는 환상이다. 이 환상을 강화하는 디테일들을 먼저 이해해야, 이후의 그림자 수식과 깊이 체계가 의미를 갖는다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h4&gt;3.1 베벨 (Bevel)&lt;/h4&gt;&lt;p&gt;실제 양각된 물체는 모서리에서 빛을 받는다. CSS border로 이를 표현:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-css"&gt;/* 광원 좌상단 기준 */
border-top: 1px solid rgba(255, 255, 255, 0.6);    /* 밝은 면 */
border-left: 1px solid rgba(255, 255, 255, 0.6);
border-bottom: 1px solid rgba(163, 177, 198, 0.4);  /* 어두운 면 */
border-right: 1px solid rgba(163, 177, 198, 0.4);&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;적용 대상: 타일, 히어로 카드 등 &lt;strong&gt;물리적 존재감&lt;/strong&gt;이 필요한 요소&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;적용하지 않는 곳: 버튼 (그림자만으로 충분), 스티치 카드 (스티치가 경계 역할)&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;h4&gt;3.2 스티치 (Stitch)&lt;/h4&gt;&lt;p&gt;이 문서에서 제안하는 “사용 가능한 뉴모피즘”에서는 본질적으로 인터렉션이 불가능한 카드와 인터렉션이 가능한 버튼 사이의 명확한 시각적 구분을 부여한다. 여기서는 봉제선을 카드 안쪽에 dashed border를 넣는다. 본래 뉴모피즘은 점토의 느낌을 주지만 여기서는 실리콘처럼 다룬다.&lt;/p&gt;&lt;pre&gt;&lt;code class="language-css"&gt;.card::after {
  content: '';
  position: absolute;
  inset: 8px;
  border: 2px dashed rgba(163, 177, 198, 0.35);
  border-radius: 14px;   /* 부모보다 약간 작게 */
  pointer-events: none;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이것의 역할:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;비대화형 카드의 고정감: 버튼과 같은 양각(raised) 이지만 클릭할 수 없는 요소에 스티치를 넣으면, “이것은 고정된 것” 이라는 시각적 신호가 된다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;큰 카드에 강한 그림자 없이 영역을 구분한다. 큰 카드의 그림자는 플로팅 처럼 느껴지지만 스티치 + 더 적은 그림자로 뉴모피즘 디자인 언어의 흐름을 깨지 않는다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;h4&gt;섹션 디바이더로서의 스티치&lt;/h4&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/22/20262220_kLehRBNr5JPv2DsDT4L8.png" alt="image.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;카드 내부뿐 아니라 &lt;strong&gt;섹션 간 구분선&lt;/strong&gt;으로도 스티치를 사용할 수 있다. 중앙에 짧게 놓인 스티치 라인은 &lt;code&gt;&amp;lt;hr&amp;gt;&lt;/code&gt;보다 강한 구분감을 주면서도 표면의 일부로 느껴진다.&lt;/p&gt;&lt;pre&gt;&lt;code class="language-css"&gt;/* 섹션 디바이더 — 짧고 섬세한 봉제선 */
.stitch-sep {
  height: 1px;
  width: 120px;
  margin: 0 auto;
  background: repeating-linear-gradient(
    90deg,
    rgb(163 177 198 / 0.28) 0 5px,
    transparent 5px 11px
  );
}

/* 하이라이트: 실이 표면 위로 올라온 부분 */
.stitch-sep::after {
  content: '';
  position: absolute;
  inset: 0;
  top: 1px;
  height: 1px;
  background: repeating-linear-gradient(
    90deg,
    rgb(255 255 255 / 0.5) 0 5px,
    transparent 5px 11px
  );
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;핵심은 &lt;strong&gt;짧게&lt;/strong&gt;. 화면 전체를 가로지르는 선은 장식을 위한 장식이 된다. 120px 정도로 중앙에만 놓으면 "여기서 봉제가 끊겼다"는 구조적 의미를 갖는다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h4&gt;3.3 양각 텍스트 (Embossed Text)&lt;/h4&gt;&lt;p&gt;큰 숫자나 헤딩에 돌출 느낌을 준다:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-css"&gt;.raised-text {
  text-shadow:
    1px 1px 1px rgba(255, 255, 255, 0.7),
    -1px -1px 1px rgba(163, 177, 198, 0.25);
}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;적용 대상: 히어로 숫자, 진척률 같은 &lt;strong&gt;핵심 수치&lt;/strong&gt; 1~2개&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;적용하지 않는 곳: 본문, 레이블, 작은 텍스트 (읽기 어려워짐)&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;h4&gt;3.4 이미지 프레이밍 (Image Frame)&lt;/h4&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/22/20262221_63E2o7FOPHYreU4tNPfH.png" alt="image.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;이미지는 뉴모피즘의 &lt;strong&gt;단일 색상 표면&lt;/strong&gt;을 깨뜨린다. 다채로운 픽셀이 양각 위에 올라오면 그림자가 만드는 "한 재료에서 깎아낸" 환상이 무너진다. 이미지를 어떻게 넣어야 뉴모피즘 디자인의 흐름을 깨지 않을까 고민하며 다양한 방법을 사용했는데 격리하는 방식으로 결론을 내렸다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;이중 쉐도우 액자&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;단순히 inset을 줘서 격리하는 방식이 아닌 raised 프레임과 inset 홈을 동시에 만들어서 액자에 넣은것 처럼 만든다.&lt;/p&gt;&lt;pre&gt;&lt;code class="language-css"&gt;/* raised 프레임 + inset 이미지 홈 */
.image-frame {
  padding: 8px;
  border-radius: 16px;
  background: #e0e5ec;
  box-shadow:
    4px 4px 14px rgba(163, 177, 198, 0.7),
    -4px -4px 14px rgba(255, 255, 255, 0.9);  /* 양각 프레임 */
}

.image-frame img {
  display: block;
  width: 100%;
  border-radius: 10px;
  box-shadow:
    inset 4px 4px 14px rgba(163, 177, 198, 0.5),
    inset -4px -4px 14px rgba(255, 255, 255, 0.7);  /* 이미지는 함몰 */
}&lt;/code&gt;&lt;/pre&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/22/20262221_H0Beu4AnQhabPpsXtn9b.png" alt="image.png" style="object-fit: cover;"&gt;&lt;figcaption&gt;마치 게임기와 같은 물리 객체처럼 보인다.&lt;/figcaption&gt;&lt;/figure&gt;&lt;pre&gt;&lt;code class="language-plaintext"&gt;┌──────────────────┐  ← raised (양각 프레임)
│  ┌──────────────┐│
│  │  ▓▓ image ▓▓ ││  ← inset (이미지가 표면 아래로)
│  └──────────────┘│
└──────────────────┘&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;inset&lt;/code&gt;만 주면 → &lt;strong&gt;구덩이&lt;/strong&gt;가 된다. 이미지가 바닥에 빠진 것처럼 보인다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;raised + inset&lt;/code&gt; 이중 쉐도우 → &lt;strong&gt;액자&lt;/strong&gt;가 된다. 프레임이 돌출되고 그 안에 이미지가 끼워진 느낌이다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;padding&lt;/code&gt;이 프레임의 &lt;strong&gt;두께&lt;/strong&gt;가 된다. 이 간격이 뉴모피즘 표면의 연속성을 유지시킨다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;4. 인터랙션 위계 — 점토덩어리 vs 조각품&lt;/h2&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/22/20262220_eyb6hsiALWqAJMV1fTee.png" alt="image.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;blockquote&gt;&lt;p&gt;아무거나 raise시켜서 계속 쌓기만 하면 &lt;strong&gt;점토덩어리&lt;/strong&gt;가 되고, 필요한 곳만 돌출시키면 &lt;strong&gt;조각품&lt;/strong&gt;이 된다.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;뉴모피즘에서 가장 범하기 쉬운 실수는 "돋보이게 하고 싶은 것을 전부 raised로 만드는 것"이다. 일반 텍스트 영역(prose)을 카드로 감싸고, 제목도 양각 텍스트로, 버튼도 양각으로 — 전부 돌출되면 아무것도 돌출되지 않은 것과 같다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h4&gt;4.1 그림자의 의미론&lt;/h4&gt;&lt;p&gt;그림자는 장식이 아니라 &lt;strong&gt;인터랙션의 언어&lt;/strong&gt;다.&lt;/p&gt;&lt;pre&gt;&lt;code class="language-plaintext"&gt;inset (함몰)   → "여기에 무언가를 넣어라"  → 입력 필드, 검색창, 프로그레스 트랙
raised (돌출)  → "나를 건드릴 수 있어"    → 버튼, 클릭 가능한 타일
stitch (봉제)  → "나는 고정되어 있어"     → 비대화형 섹션 컨테이너
flat (평면)    → "나는 표면 그 자체야"    → 텍스트, prose, 레이블&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;&lt;h4&gt;4.2 위계 결정 플로우&lt;/h4&gt;&lt;pre&gt;&lt;code class="language-plaintext"&gt;이 요소에 사용자가 값을 입력하는가?
  → YES → inset

이 요소를 클릭/탭할 수 있는가?
  → YES → raised
         이것은 카드인가, 버튼인가?
           → 카드: 내부에 raised 버튼을 넣어 클릭 가능함을 명시하라
           → 버튼: raised + hover → press → active 3단계 피드백

클릭할 수 없지만 시각적 우선순위가 필요한가?
  → YES → raised + stitch 컨테이너 안에 배치 (고정감)
         스티치가 "봉제되어 고정된 것"임을 알리고,
         raised가 데이터의 시각적 무게를 유지한다.
  → NO  → flat. 아무 그림자도 주지 마라.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;&lt;h4&gt;4.3 클릭 가능한 카드의 규칙&lt;/h4&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/22/20262220_e41gXmwnYsFrHraVDKTe.png" alt="image.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;카드와 버튼은 &lt;strong&gt;반드시 시각적으로 구분&lt;/strong&gt;되어야 한다. 카드가 클릭 가능하다면, 카드 &lt;strong&gt;안에&lt;/strong&gt; raised 버튼이나 액센트 요소가 있어야 한다. 또는 카드 전체가 &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; 태그로, hover 시 그림자가 미세하게 커지는 피드백을 준다.&lt;/p&gt;&lt;p&gt;인터렉션이 없는 카드는 raised 하지 않는다. 이런 요소가 발생하는 순간부터 사용자는 화면 내에서 양각의 의미를 모르게 된다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;5. 그림자 시스템&lt;/h2&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/22/20262220_g0EiDAPJY64n3TW5P7U1.png" alt="image.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;h4&gt;5.1 이중 그림자의 원리&lt;/h4&gt;&lt;p&gt;뉴모피즘의 핵심은 &lt;strong&gt;하나의 광원&lt;/strong&gt;에서 나오는 두 그림자다.&lt;/p&gt;&lt;pre&gt;&lt;code class="language-plaintext"&gt;광원 (좌상단)
    ↘
┌─────────┐
│ ██████  │  ← 우하단: 어두운 그림자 (빛의 부재)
│ ██████  │
└─────────┘
    ↗
좌상단: 밝은 그림자 (반사)&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code class="language-css"&gt;/* 돌출 (Raised) */
box-shadow:
  8px 8px 28px rgba(163, 177, 198, 0.7),    /* 어두운 면 */
  -8px -8px 28px rgba(255, 255, 255, 0.9);  /* 밝은 면 */

/* 함몰 (Pressed) */
box-shadow:
  inset 5px 5px 12px rgba(163, 177, 198, 0.5),
  inset -5px -5px 12px rgba(255, 255, 255, 0.7);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;&lt;h4&gt;5.2 비율 공식&lt;/h4&gt;&lt;table style="min-width: 75px;"&gt;&lt;colgroup&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;/colgroup&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;파라미터&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;공식&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;예시&lt;/p&gt;&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;strong&gt;blur&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;offset × 3.5&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;8px offset → 28px blur&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;strong&gt;돌출 dark opacity&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;0.7&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;rgba(163,177,198, 0.7)&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;strong&gt;돌출 light opacity&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;0.9&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;rgba(255,255,255, 0.9)&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;strong&gt;함몰 dark opacity&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;0.5&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;돌출보다 부드럽게&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;strong&gt;함몰 light opacity&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;0.7&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;돌출보다 부드럽게&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;이 비율이 무너지면:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;blur가 너무 작으면 → 그림자가 날카롭고 딱딱함 (CSS 기본 그림자처럼 보임)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;blur가 너무 크면 → 경계가 사라져서 돌출감 없음&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;opacity가 너무 강하면 → 불투명하고 무거움&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;opacity가 너무 약하면 → 평면과 구분 안됨&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;h4&gt;5.3 프리셋 스케일&lt;/h4&gt;&lt;p&gt;위 공식에 따른 참조 크기 4단계:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-typescript"&gt;xs:  2px 2px 7px   …   작은 버튼, 배지
sm:  4px 4px 14px  …   기본 버튼, 카드
md:  6px 6px 20px  …   히어로 요소 (1개만)
lg:  8px 8px 28px  …   거의 안 씀&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;규칙: 한 화면에서 &lt;/strong&gt;&lt;code&gt;md&lt;/code&gt;&lt;strong&gt; 이상은 1개만 사용한다.&lt;/strong&gt; 여러 요소가 동일 레벨로 강하게 돌출되면 계층이 무너진다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;6. 깊이 체계&lt;/h2&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/22/20262220_4i1NkIzrkKLeqmxcSe8V.png" alt="image.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;뉴모피즘에서 가장 중요한 설계 결정. 모든 요소의 "높이"를 미리 정의한다.&lt;/p&gt;&lt;pre&gt;&lt;code class="language-typescript"&gt;Level -1 : 함몰 (Inset)
           입력 필드, 프로그레스 트랙, 검색창
           → inset 그림자. "여기에 무언가를 넣어라"

Level 0  : 표면 (Surface)
           페이지 배경 그 자체. 그림자 없음.
           → 텍스트, 구분선 등 비물리적 요소가 위치

Level 0.5: 구역 (Zone)
           스티치 카드, 섹션 컨테이너
           → 최소 그림자 + 장식적 테두리로 영역 구분
           → 큰 요소에 강한 돌출을 주면 "떠있는 판"이 되므로 이 레벨을 사용

Level 1  : 양각 (Raised)
           카드, 타일, 배지
           → shadow-xs ~ shadow-sm

Level 1.5: 컨트롤 (Control)
           버튼, 필터 토글
           → shadow-xs. hover→press 인터랙션

Level 2  : 강조 (Hero)
           화면당 1개만 허용. 반드시 인터랙션이 있는 요소여야 한다.
           → shadow-md. 비대화형이면 Level 0.5(stitch)로 내려라.&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;절대 하지 말 것&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;큰 요소에 높은 Level&lt;/strong&gt;: 넓은 카드에 &lt;code&gt;shadow-md&lt;/code&gt;를 주면 떠있는 느낌을 준다. 큰 요소는 Level 0.5로 쓴다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;비대화형 요소에 inset&lt;/strong&gt;: inset은 "입력 가능"의 시각적 신호. 장식 컨테이너에 쓰면 사용자가 클릭하려고 시도한다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;7. 배경색 규칙&lt;/h2&gt;&lt;h4&gt;7.1 하나의 색&lt;/h4&gt;&lt;pre&gt;&lt;code class="language-css"&gt;--bg: #e0e5ec;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;모든 요소의 배경색은 이것이다.&lt;/strong&gt; gradient 금지. 카드든, 버튼이든, 배지든 전부 &lt;code&gt;#e0e5ec&lt;/code&gt;. 뉴모피즘은 "하나의 재료에서 깎아낸 형태"이므로, 배경색이 다른 순간 환상이 깨진다.&lt;/p&gt;&lt;h4&gt;7.2 예외: 액센트 요소&lt;/h4&gt;&lt;p&gt;유일한 예외는 &lt;strong&gt;상호작용을 강조&lt;/strong&gt;해야 하는 요소:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Primary 버튼: &lt;code&gt;background: var(--accent);&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;토글 ON 상태: &lt;code&gt;background: var(--accent);&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;활성 필터: 색상이 아닌 &lt;strong&gt;위치 변화 &lt;/strong&gt;(raised ↔ inset)로 표현 가능&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;7.3 절대 하지 말 것&lt;/h4&gt;&lt;pre&gt;&lt;code class="language-css"&gt;/* 금지: gradient 배경 */
background: linear-gradient(145deg, #e8edf5, #d8dde4);

/* 금지: 반투명 배경 (글래스모피즘 혼용) */
background: rgba(224, 229, 236, 0.5);
backdrop-filter: blur(20px);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;글래스모피즘은 뉴모피즘과 &lt;strong&gt;다른 디자인 언어&lt;/strong&gt;다. 혼용하면 "왜 이것만 유리야?"라는 의문이 생긴다. 단, 유일하게 떠있는 요소(플로팅 바, 모달 오버레이)는 의도적으로 다른 레이어임을 표현하기 위해 glass를 쓸 수 있다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;8. 컴포넌트별 규칙&lt;/h2&gt;&lt;h4&gt;8.1 버튼&lt;/h4&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/22/20262220_NyNVzLVRN11CDKUQ1xP5.png" alt="image.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;버튼 상태에 따른 표현&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;[Rest] shadow - xs~sm → “나를 누를 수 있어” (양각)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;[Hover] shadow 축소 → “눌리기 시작하고 있어” (표면에 근접)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;[Active] inset → “눌렸어” (함몰)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;[Focus] shadow + accent ring → “키보드가 여기 있어”&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;[Disabled] shadow 없음 + 투명 → “나는 비활성이야”&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;버튼 위계에 따른 표현&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Primary: 액센트 배경 + 흰색 텍스트 → 색상 자체가 행동유도성&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Secondary: 배경색 + 양각 → hover시 press 피드백이 유일한 행동유도성 신호&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;border-radius: &lt;code&gt;12px&lt;/code&gt; (공통)&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;h4&gt;8.2 카드&lt;/h4&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/22/20262220_6VzMhuQGkQdj0wNh9ehx.png" alt="image.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;두 가지 변형만 사용:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Raised (양각)&lt;/strong&gt;: 정보 표시용, 작은~중간 크기&lt;/p&gt;&lt;p&gt;가능하면 버튼과의 구분을 위해서 인터렉션이 없는 카드에서는 양각 사용을 피하자. 너무 중요한 정보라 강조가 필요하다면 아래 봉제선 카드를 사용한다. 봉제선 카드가 너무 많은가? 그럼 페이지를 다시 설계하자. 양각으로 만드는 순간 사용자는 클릭하려 할 것이다. 트레이드 오프다. 그럼 최소한 클릭 가능한 요소(=버튼)로 만들자.&lt;/p&gt;&lt;pre&gt;&lt;code class="language-css"&gt;background: #e0e5ec;
border-radius: 20px;
box-shadow:
  4px 4px 14px rgba(163, 177, 198, 0.7),
  -4px -4px 14px rgba(255, 255, 255, 0.9);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Stitch (봉제선)&lt;/strong&gt;: 영역 구분용, 큰 섹션 컨테이너&lt;/p&gt;&lt;pre&gt;&lt;code class="language-css"&gt;background: #e0e5ec;
border-radius: 20px;
box-shadow:
  1px 1px 3px rgba(163, 177, 198, 0.7),
  -1px -1px 3px rgba(255, 255, 255, 0.9);  /* 최소 */
/* + ::after 스티치 라인 */&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;카드에서 절대 하지 말 것:&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;s&gt;hover 시 그림자 커짐&lt;/s&gt;: 카드는 상호작용 요소가 아니다&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;s&gt;shadow-md 이상&lt;/s&gt;: 큰 요소에 강한 그림자 = 떠있는 판&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;s&gt;gradient 배경&lt;/s&gt;: 뉴모피즘 기본 원칙 위반&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;h4&gt;8.3 입력 필드&lt;/h4&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/22/20262220_UrRcRRLOHzwrp7jvgb7v.png" alt="image.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;pre&gt;&lt;code class="language-css"&gt;/* 함몰된 홈 — "여기에 값을 넣어라" */
box-shadow:
  inset 4px 4px 14px rgba(163, 177, 198, 0.5),
  inset -4px -4px 14px rgba(255, 255, 255, 0.7);
border: none;
background: #e0e5ec;
border-radius: 14px;

/* 포커스 시 — 함몰 깊어짐 + 액센트 링 */
box-shadow:
  inset 5px 5px 18px rgba(163, 177, 198, 0.5),
  inset -5px -5px 18px rgba(255, 255, 255, 0.7),
  0 0 0 2px rgba(99, 102, 241, 0.25);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;입력 필드는 &lt;strong&gt;유일하게 inset이 의미 있는 비버튼 요소&lt;/strong&gt;다. "움푹 들어간 곳 = 무언가를 넣는 곳"이라는 메타포.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h4&gt;8.4 프로그레스 바&lt;/h4&gt;&lt;pre&gt;&lt;code class="language-css"&gt;/* 트랙: 홈(함몰) */
.track {
  height: 8px;
  border-radius: 4px;
  box-shadow:
    inset 2px 2px 7px rgba(163, 177, 198, 0.5),
    inset -2px -2px 7px rgba(255, 255, 255, 0.7);
}

/* 필: 트랙을 채우는 액체 */
.fill {
  background-color: #6366f1;
  background-image: linear-gradient(
    to bottom,
    rgba(255,255,255, 0.3) 0%,
    rgba(255,255,255, 0) 60%
  );
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;상단 하이라이트 gradient는 "빛을 받는 채워진 액체" 느낌을 준다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h4&gt;8.5 배지&lt;/h4&gt;&lt;p&gt;잠깐, 배지는 입력 요소가 아닌데 왜 inset을 사용하나?&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;inset + 컬러 도트 → "표면에 찍힌 도장" 메타포&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;배지를 input과 나란히 두는 것은 부적절한 사용 경험을 만든다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;9. 간격 규칙&lt;/h2&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/22/20262220_jyYgWkx5FQVgHU6h44zx.png" alt="image.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;뉴모피즘의 그림자는 요소 바깥으로 퍼지기 때문에, 요소 간 간격이 충분하지 않으면 그림자가 겹쳐서 지저분해진다. 빛과 그림자가 사용자에게 적절히 노출되어야 사용자는 양각 요소를 명확히 인지할 수 있다.&lt;/p&gt;&lt;pre&gt;&lt;code class="language-plaintext"&gt;최소 간격 = 그림자 blur 값 × 1.5

shadow-xs (blur 7px)  → 최소 간격 12px
shadow-sm (blur 14px) → 최소 간격 28px  ← 기본 사용
shadow-md (blur 20px) → 최소 간격 32px&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이것은 뉴모피즘이 전통 UI보다 &lt;strong&gt;더 많은 여백&lt;/strong&gt;을 필요로 하는 이유다. 여백이 부족하면 "소프트한 깊이감" 대신 "지저분한 그림자 덩어리"가 된다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;10. 접근성&lt;/h2&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/22/20262220_rvyk2A629U8HBGRD7S8B.png" alt="image.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;뉴모피즘의 가장 큰 약점. 반드시 보완해야 한다.&lt;/p&gt;&lt;h4&gt;10.1 WCAG 대비&lt;/h4&gt;&lt;table style="min-width: 75px;"&gt;&lt;colgroup&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;/colgroup&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;용도&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;최소 대비&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;뉴모피즘 대응&lt;/p&gt;&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;일반 텍스트&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;4.5:1&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;#3a4250&lt;/code&gt; on &lt;code&gt;#e0e5ec&lt;/code&gt; = 4.8:1 (통과)&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;큰 텍스트 (18px+)&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;3:1&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;통과&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;비활성 요소&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;없음&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;opacity: 0.4&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;UI 컨트롤 경계&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;3:1&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;strong&gt;그림자만으로는 미달&lt;/strong&gt; → 베벨 or 액센트 필요&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;h4&gt;10.2 포커스 링&lt;/h4&gt;&lt;p&gt;키보드 사용자를 위해 &lt;strong&gt;반드시&lt;/strong&gt; 가시적 포커스 표시:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-css"&gt;:focus-visible {
  outline: none;
  box-shadow:
    /* 기존 shadow 유지 + */
    0 0 0 3px rgba(99, 102, 241, 0.25),
    0 0 0 1px #6366f1;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;outline: none&lt;/code&gt;을 쓸 때는 &lt;strong&gt;반드시&lt;/strong&gt; 대체 표시를 제공하라.&lt;/p&gt;&lt;h4&gt;10.3 모바일 고려&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;hover 없음 → Primary 버튼의 액센트 색상이 유일한 행동유도성 신호&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;작은 화면 → 그림자 크기 한 단계 축소 (shadow-sm → shadow-xs)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;터치 타겟 → 최소 44×44px&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;11. 안티패턴 체크리스트&lt;/h2&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/22/20262220_2SvdMQGlP9kkI2tBEEOz.png" alt="image.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;구현 후 이 목록으로 검증한다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;배경&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;gradient 배경을 쓴 요소가 있는가? → &lt;code&gt;#e0e5ec&lt;/code&gt; 단색으로&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;위계&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;같은 화면에 shadow-md 이상이 2개 이상인가? → 1개만 유지&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;큰 컨테이너에 강한 돌출 그림자가 있는가? → 스티치 카드로 대체&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;비대화형 요소에 inset이 있는가? → raised나 flat으로&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;일반 텍스트(prose) 영역을 raised 카드로 감쌌는가? → flat으로&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;이미지가 raised 표면 위에 직접 놓여있는가? → raised+inset 액자로&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;비대화형 강조 카드에 스티치가 없는가? → 스티치 추가하여 고정감 부여&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;디자인&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;그림자가 겹치는 요소가 있는가? → 간격 확보 (blur × 1.5)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;blur/offset 비율이 3~3.5× 범위인가? → 벗어나면 조정&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;opacity가 너무 강한가? (돌출 dark &amp;gt; 0.7, light &amp;gt; 0.9) → 낮추기&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;접근성&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;키보드 포커스 시 가시적 표시가 있는가? → focus-visible 추가&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;모든 상호작용 요소에 hover+active 상태가 있는가? → 추가&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;텍스트 대비가 WCAG AA를 충족하는가? → 확인&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;클릭 가능한 카드에 내부 행동유도성(버튼, 액센트)이 있는가? → 추가&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;12. 해부학적 접근법&lt;/h2&gt;&lt;p&gt;이 문서의 디자인 철학은 Maison Margiela의 해체주의 패션에서 영감을 받았다. 구조(construction)를 숨기지 않고 드러내는 그들의 접근 방식을 UI에 차용한다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Margiela가 의복에서 한 것:&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;안감, 봉제선, 어깨 패드를 &lt;strong&gt;밖으로&lt;/strong&gt; 뒤집음&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;구조 자체를 디자인으로 승격&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;"왜 이걸 숨겨야 하지?"라는 질문&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;뉴모피즘에서의 적용:&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;스티치 라인: 카드의 "봉제선"을 의도적으로 노출 → 구조가 보인다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;베벨: 양각의 "모서리"를 숨기지 않고 강조 → 깎아낸 흔적이 보인다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;깊이 체계: UI의 "구조"를 명시적으로 설계하고 드러냄 → 위계가 보인다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;단일 재료: 하나의 표면에서 모든 형태를 깎아냄 → 재료가 보인다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;결과적으로, 잘 만들어진 뉴모피즘은 피로가 아니라 아름다움과 사용성을 모두 챙긴다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;적용 샘플 참고: &lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://baejino.com/"&gt;https://baejino.com&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;디자인 가이드 참고: &lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://design.baejino.com/design-2019-neumorphism"&gt;https://design.baejino.com/design-2019-neumorphism&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;</description><pubDate>Sun, 22 Feb 2026 02:13:25 +0900</pubDate><guid>http://blex.me/@baealex/ready-to-use-neumorphism</guid></item><item><title>[Apache Iceberg - The Definitive Guide] Practical Hands-On</title><link>http://blex.me/@mildsalmon/apache-iceberg-the-definitive-guide-%EC%8B%A4%EC%8A%B5-Y9qzmLSD</link><description>&lt;p&gt;Iceberg를 공부하다보니 내부 구조가 궁금해졌다.&lt;/p&gt;&lt;p&gt;그래서 책을 정리한 내용을 바탕으로 AI 도움을 받아 Iceberg를 분해해서 가이드하는 실습 파일을 만들었다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://github.com/mildsalmon/iceberg-learning"&gt;https://github.com/mildsalmon/iceberg-learning&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;</description><pubDate>Mon, 16 Feb 2026 14:13:23 +0900</pubDate><guid>http://blex.me/@mildsalmon/apache-iceberg-the-definitive-guide-%EC%8B%A4%EC%8A%B5-Y9qzmLSD</guid></item><item><title>이제는 더 이상 토이 프로젝트를 망치지 않는다 (팀 코너)</title><link>http://blex.me/@baealex/team-conor-your-virtual-ai-team</link><description>&lt;p&gt;1인 토이 프로젝트 개발을 오래 해왔다. 아이디어가 떠오르면 바로 만들었고, 혼자 설계하고, 혼자 구현하고, 혼자 배포했다. 문제는 “혼자”라는 단어에 있다. 혼자 만들면 그 제품에는 오롯이 내 관점만 들어간다. 내가 필요하다고 생각하는 기능, 내가 좋다고 생각한 방식에 갖히게 된다. 아무도 “그게 정말 필요해?”, “사용자가 잘 쓸 수 있어?”라고 지적하지 않는다.&lt;/p&gt;&lt;p&gt;결과적으로 나만 이해하는 제품이 탄생하고 아무도 쓰지 않는다.&lt;/p&gt;&lt;p&gt;이런 식으로 쌓여가는 토이 프로젝트가 많아졌다. AI로 생산성이 올라간 지금. 내 컴퓨터에 시체 더미가 쌓여가고 있다. AI에게 리뷰를 받아도 사실 AI는 내가 강한 어조로 말해버리면 그걸 그대로 이행한다. 그게 나쁜 방식이던 좋은 방식이던 상관없이 말이다. “&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blex.me/@baealex/the-prison-of-bias-created-by-ai"&gt;AI는 우리를 편향주의 감옥에 가둔다&lt;/a&gt;” 라는 글을 기고했던 것 처럼 AI는 내가 별도로 지시하지 않으면 나의 관점과 나의 생각을 편협하게 가둬버린다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h1&gt;팀을 만들자&lt;/h1&gt;&lt;p&gt;&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://github.com/bmad-code-org/BMAD-METHOD"&gt;bmad-method&lt;/a&gt; 라는 프로젝트를 봤다. AI에게 역할을 부여해서 팀처럼 일하게 만드는 전략이다. 팀에서 일하면 많은 사람들의 생각을 들어볼 수 있다. 함께 고민해서 만들면 내가 생각하지 못했던 것에 대해서 인사이트를 얻을 수 있고 더 견고한 제품을 만들 수 있게 된다. bmad 메서드를 깊이 사용하진 못했지만 솔직히 말하자면 토이 프로젝트에 적용하기엔 불필요하게 복잡하고 과했다. 설정해야 할 것이 많고, 구조가 무겁다.&lt;/p&gt;&lt;p&gt;내가 원하는건 단순하게 내 생각 + 보완 + 빠른 실행 + 고품질 코드 + 컨텍스트 유지 뿐이었기 때문에 이를 아주 간소화한 형태로 &lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://github.com/baealex/team-conor"&gt;Team Conor&lt;/a&gt;를 만들었다.&lt;/p&gt;&lt;pre&gt;&lt;code class="language-bash"&gt;npx team-conor&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이 한 줄이면 클로드 코드에 가상의 팀이 셋팅된다.&lt;/p&gt;&lt;table style="min-width: 75px;"&gt;&lt;colgroup&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;/colgroup&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;이름&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;역할&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;한마디&lt;/p&gt;&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;스티브&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;제품 전략&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;"왜 이게 필요해?"&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;엘런&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;실행 PM&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;"언제 끝나?"&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;마르코&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;UX 디자이너&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;"사용자가 3초 안에 이해해?"&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;유나&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;프론트엔드 아키텍트&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;"리렌더링 몇 번 일어나요?"&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;빅토르&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;백엔드 아키텍트&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;"100만 유저면 어떻게 돼요?"&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;단순히 이름만 붙인 역할극이 아니다. 각 페르소나에는 &lt;strong&gt;체크리스트&lt;/strong&gt;, &lt;strong&gt;안티패턴 목록&lt;/strong&gt;, &lt;strong&gt;해결 패턴&lt;/strong&gt;, 그리고 &lt;strong&gt;다른 페르소나를 호출하는 트리거&lt;/strong&gt;가 설계되어 있다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;추상적 조언이 아니라 구체적 피드백&lt;/h2&gt;&lt;h3&gt;체크리스트로 리뷰한다&lt;/h3&gt;&lt;p&gt;"코드가 좋습니다" 같은 단순한 칭찬은 나오지 않는다. 각 페르소나는 자기 영역의 체크리스트로 코드를 검토한다.&lt;/p&gt;&lt;pre&gt;&lt;code class="language-plaintext"&gt;유나: 이 컴포넌트 봤는데...
- [ ] useEffect 의존성 배열에 user 빠져있어요
- [ ] 이 상태는 서버 컴포넌트로 올릴 수 있어요
- [x] 타입은 잘 되어있네요&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;통과한 건 통과했다고, 안 된 건 뭐가 안 됐는지 명확하게 짚어준다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;안티패턴을 잡아낸다&lt;/h3&gt;&lt;p&gt;각 페르소나는 자기 영역에서 흔히 발생하는 실수 패턴을 알고 있다. 빅토르는 "트랜잭션 없는 다중 쓰기", "N+1 쿼리", "에러 삼키기" 같은 백엔드 안티패턴을 감지한다. 마르코는 "로딩 상태 누락", "색상만으로 정보 전달" 같은 UX 안티패턴을 잡아낸다. 중요한 건, &lt;strong&gt;문제만 지적하고 끝나지 않는다&lt;/strong&gt;는 것이다.&lt;/p&gt;&lt;pre&gt;&lt;code class="language-plaintext"&gt;빅토르: 이거 N+1 쿼리예요. 루프 안에서 DB 호출하고 있네요.
→ JOIN 쿼리나 DataLoader 패턴으로 대체하는 게 맞습니다.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;"에러 핸들링을 추가하세요"가 아니라, 어떤 에러를 어떻게 처리할지까지 제안한다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;페르소나끼리 협업한다&lt;/h3&gt;&lt;p&gt;이게 가장 재미있는 부분이다. 한 페르소나가 리뷰하다가 다른 영역의 문제를 발견하면, 해당 페르소나를 호출한다.&lt;/p&gt;&lt;p&gt;마르코와 유나가 함께 보면 화면이 예쁘면서도 성능이 좋아진다. 마르코가 "Skeleton UI 넣어야 한다"고 하면 유나가 "CSS transform으로 애니메이션 걸어야 60fps 나온다"고 받아준다. 스티브가 "이 기능 범위가 커지고 있는데"라고 하면 엘런이 "뭘 빼면 절반 시간에 되는지 볼게요"라고 스코프를 잡아준다.&lt;/p&gt;&lt;p&gt;실제 팀에서 자연스럽게 일어나는 대화가 AI 안에서 재현된다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div data-type="columns" data-layout="1:1" style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin: 16px 0px;"&gt;&lt;div data-type="column" style="min-width: 0px;"&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/7/20262711_P2YzNz3Zlyi7YyufoXQT.png" alt="image.png" style="object-fit: cover;"&gt;&lt;figcaption&gt;핵심을 질문하며 전략적 접근을 하는 기획자 스티브&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/7/20262711_kAfxzuOHDBLciDNh2rb2.png" alt="Screenshot 2026-02-07 at 11.06.39 AM.png" style="object-fit: cover;"&gt;&lt;figcaption&gt;디자인을 분석하는 디자이너 마르코&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/7/20262711_msZm58fXJF2I5udskpZE.png" alt="image.png" style="object-fit: cover;"&gt;&lt;figcaption&gt;효율과 임팩트를 중시하는 PM 엘런&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;&lt;div data-type="column" style="min-width: 0px;"&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/7/20262711_XtV5Vz4ncF6ZJxUF72LR.png" alt="Screenshot 2026-02-07 at 11.07.52 AM.png" style="object-fit: cover;"&gt;&lt;figcaption&gt;사용자 성능을 고려하는 FE 개발자 유나&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/7/20262711_C1OwivK0Ifm1lnbZdrsc.png" alt="Screenshot 2026-02-07 at 11.06.02 AM.png" style="object-fit: cover;"&gt;&lt;figcaption&gt;안전성과 비용을 고려하는 BE 개발자 빅토르&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/7/20262711_DwdjalwAHXskev6o52xB.png" alt="image.png" style="object-fit: cover;"&gt;&lt;figcaption&gt;최종 선택은 최고 의사권자인 코너&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;페르소나는 토큰 낭비?&lt;/h2&gt;&lt;p&gt;스티브는 “첫 아이폰 프로덕트 비저너리”, 엘런은 “스페이스X 초기 멤버”, 마르코는 “도널드 노먼의 수제자”, 빅토르는 “Rust 초기 기여자”, 유나는 “크롬 브라우저 초기 개발팀 출신” 이라는 설정을 두고 있다. 이건 단순히 상황극의 재미를 향상시키기 위한 설정이 아니다.&lt;/p&gt;&lt;p&gt;토큰을 절약하는 가장 확실한 방법은 배경 설정이라고 생각한다. 빅토르가 “Rust 초기 기여자”라는 배경은 “타입 시스템으로 버그를 원천 차단한다”는 리뷰 철학을 강제하는 앵커다. 이 배경 때문에 빅토르는 “타입이 이미 잡아주는 걸 왜 테스트해요?”라고 말할 수 있고, “타입으로 못 잡는 것만 테스트한다”는 원칙을 일관되게 유지한다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;배경은 페르소나의 행동 범위를 제한하는 제약 조건이다.&lt;/strong&gt; 배경과 행동 원칙이 일치할 때, AI는 그 방향에서 벗어나지 않는다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;솔직한 한계&lt;/h2&gt;&lt;p&gt;컨텍스트가 많아지면 성능이 떨어질 수 있다. 5개 페르소나의 체크리스트와 안티패턴이 전부 컨텍스트에 올라가면, AI가 여러 관점을 동시에 의식하면서 어느 쪽으로도 날카롭지 못한 "합의형 피드백"을 줄 가능성이 있다.&lt;/p&gt;&lt;p&gt;이 문제를 완전히 해결한 것은 아니지만, 몇 가지로 완화하고 있다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;페르소나는 이름으로 호출해야 활성화된다. 항상 5명이 동시에 말하는 구조가 아니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;체크리스트라는 레일이 있기 때문에 "뭉뚱그려진 조언"이 나오기 어렵다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Cross-domain 트리거가 명시적이라서, 관련 없는 페르소나가 끼어드는 노이즈가 적다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;그래도 하나의 AI에게 하나의 역할만 맡기는 것보다 결과가 떨어지는 순간은 있을 수 있다. 이건 트레이드오프다. 대신 혼자서는 절대 얻지 못하는 &lt;strong&gt;다각도 피드백&lt;/strong&gt;을 얻는다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;Memory 시스템&lt;/h2&gt;&lt;p&gt;AI는 세션이 끝나면 다 잊어버린다. 다음 세션에서 같은 설명을 반복하는 것만큼 비효율적인 것이 없다.&lt;/p&gt;&lt;p&gt;&lt;code&gt;.conor/memory/&lt;/code&gt; 디렉토리에 프로젝트 컨텍스트를 기록한다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;a target="_blank" rel="noopener noreferrer nofollow" href="http://summary.md"&gt;summary.md&lt;/a&gt;: 핵심 컨텍스트 (항상 참조됨)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a target="_blank" rel="noopener noreferrer nofollow" href="http://project.md"&gt;project.md&lt;/a&gt;: 기술 스택, 아키텍처, 컨벤션&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a target="_blank" rel="noopener noreferrer nofollow" href="http://decisions.md"&gt;decisions.md&lt;/a&gt;: 왜 이 기술을 선택했는지&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a target="_blank" rel="noopener noreferrer nofollow" href="http://learnings.md"&gt;learnings.md&lt;/a&gt;: 발견한 패턴, 해결한 버그&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;기술적 결정이 발생하거나, 버그를 해결하거나, 프로젝트 패턴을 발견하면 사용자가 요청하지 않아도 자동으로 기록한다. 다음 세션에서 팀이 프로젝트를 기억하고 있다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;시작하기&lt;/h2&gt;&lt;pre&gt;&lt;code class="language-bash"&gt;npx team-conor&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;팀원들이 당신을 불러줄 이름을 입력하면 끝이다. &lt;code&gt;CLAUDE.md&lt;/code&gt;와 &lt;code&gt;.conor/&lt;/code&gt; 디렉토리가 생성되고, 다음 Claude 세션부터 팀이 함께한다. 각 페르소나의 체크리스트, 안티패턴, 해결 패턴은 전부 &lt;code&gt;.conor/persona/*.md&lt;/code&gt; 파일에 있으니 프로젝트에 맞게 자유롭게 수정할 수 있다.&lt;/p&gt;&lt;p&gt;혼자 개발한다고 혼자 작업할 필요는 없다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;GitHub: &lt;a target="_blank" rel="noopener noreferrer" class="underline text-text-300 hover:text-text-100" href="https://github.com/baealex/team-conor"&gt;&lt;s&gt;https://github.com/baealex/team-conor&lt;/s&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;&amp;nbsp;정신 차려보니까 바퀴를 또 만들고 있었어요! 당장 접고 &lt;/strong&gt;&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://github.com/obra/superpowers"&gt;&lt;strong&gt;Superpowers&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt; 를 사용하기 시작했습니다.&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;</description><pubDate>Sat, 07 Feb 2026 23:24:24 +0900</pubDate><guid>http://blex.me/@baealex/team-conor-your-virtual-ai-team</guid></item><item><title>[Apache Iceberg - The Definitive Guide] Optimizing the Performance of Iceberg Tables</title><link>http://blex.me/@mildsalmon/apache-iceberg-the-definitive-guide-optimizing-t</link><description>&lt;h2&gt;Chapter 4: Iceberg 테이블 성능 최적화&lt;/h2&gt;&lt;hr&gt;&lt;h2&gt;개요&lt;/h2&gt;&lt;p&gt;3장에서 살펴본 것처럼, Apache Iceberg 테이블은 쿼리 엔진이 더 나은 성능을 위해 더 스마트한 쿼리 플랜을 생성할 수 있도록 하는 메타데이터 레이어를 제공합니다. 그러나 이 메타데이터는 데이터 성능을 최적화할 수 있는 방법의 시작에 불과합니다.&lt;/p&gt;&lt;p&gt;사용할 수 있는 다양한 최적화 레버에는 데이터파일 수 줄이기, 데이터 정렬, 테이블 파티셔닝, 행 수준 업데이트 처리, 메트릭 수집, 외부 요인 등이 있습니다. 이러한 레버들은 데이터 성능 향상에 중요한 역할을 하며, 이 장에서는 각각을 탐구하고 잠재적인 성능 저하를 해결하며 가속화 인사이트를 제공합니다.&lt;/p&gt;&lt;hr&gt;&lt;h2&gt;1. 컴팩션 (Compaction)&lt;/h2&gt;&lt;h4&gt;1.1 컴팩션의 필요성&lt;/h4&gt;&lt;p&gt;모든 절차나 프로세스는 시간 비용이 듭니다. 즉, 더 긴 쿼리와 더 높은 컴퓨팅 비용을 의미합니다. 다시 말해, 무언가를 하기 위해 더 많은 단계를 거쳐야 할수록 그것을 하는 데 더 오래 걸립니다.&lt;/p&gt;&lt;p&gt;Apache Iceberg 테이블을 쿼리할 때, 각 파일을 열고 스캔한 다음 작업이 완료되면 파일을 닫아야 합니다. 쿼리에서 스캔해야 할 파일이 많을수록 이러한 파일 작업이 쿼리에 미치는 비용이 커집니다.&lt;/p&gt;&lt;p&gt;이 문제는 스트리밍 또는 "실시간" 데이터 환경에서 더욱 악화됩니다. 데이터가 생성되는 대로 수집되어 각각 몇 개의 레코드만 있는 많은 파일이 생성되기 때문입니다.&lt;/p&gt;&lt;p&gt;반면, 배치 수집은 하루치 또는 일주일치 레코드를 하나의 작업으로 수집할 수 있어, 더 잘 정리된 파일로 데이터를 쓰는 방법을 보다 효율적으로 계획할 수 있습니다.&lt;/p&gt;&lt;p&gt;배치 수집을 사용하더라도 "작은 파일 문제(small files problem)"가 발생할 수 있습니다. 너무 많은 작은 파일은 다음과 같은 이유로 스캔 속도와 성능에 영향을 미칩니다:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;더 많은 파일 작업 수행&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;읽어야 할 메타데이터 증가 (각 파일에 메타데이터가 있음)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;정리 및 유지보수 작업 시 삭제해야 할 파일 증가&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/6/20262612_3CCGusvJ6TYDqJFdcmu4.png" alt="Pasted image 20260201044933.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;figure style="text-align: left; display: flex; justify-content: flex-start; flex-direction: column; align-items: flex-start;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/6/20262612_jee9hEUgdoh2HtDi782e.png" alt="Pasted image 20260202063752.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;핵심 개념:&lt;/strong&gt; 데이터를 읽을 때 피할 수 없는 고정 비용(쿼리와 관련된 특정 데이터 읽기)과 피할 수 있는 가변 비용(파일 접근을 위한 파일 작업)이 있습니다. 이 장에서 논의할 다양한 전략을 사용하면 가변 비용을 최대한 줄일 수 있습니다.&lt;/p&gt;&lt;h4&gt;1.2 컴팩션의 개념&lt;/h4&gt;&lt;p&gt;이 문제의 해결책은 모든 작은 파일들의 데이터를 주기적으로 가져와 더 적은 수의 큰 파일로 다시 쓰는 것입니다 (데이터파일 수에 비해 매니페스트가 너무 많은 경우 매니페스트도 다시 쓸 수 있습니다). 이 프로세스를 &lt;strong&gt;컴팩션(compaction)&lt;/strong&gt;이라고 합니다. 많은 파일을 적은 수로 압축하는 것입니다.&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/6/20262612_FukRJ4XPCsAbRLIoFJpX.png" alt="Pasted image 20260201044941.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;h4&gt;1.3 Hands-on with Compaction&lt;/h4&gt;&lt;p&gt;해결책이 간단해 보이지만 Java나 Python으로 광범위한 코드를 작성해야 할 것이라고 생각할 수 있습니다. 다행히 Apache Iceberg의 Actions 패키지에는 여러 유지보수 프로시저가 포함되어 있습니다 (Actions 패키지는 특히 Apache Spark용이지만, 다른 엔진도 자체 유지보수 작업 구현을 만들 수 있습니다).&lt;/p&gt;&lt;h4&gt;SparkActions의 주요 옵션들&lt;/h4&gt;&lt;table style="min-width: 50px;"&gt;&lt;colgroup&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;/colgroup&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;옵션&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;설명&lt;/p&gt;&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;filter&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;특정 파일만 대상으로 하는 표현식&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;sort&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;정렬된 순서로 데이터를 재작성&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;option&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;단일 설정 지정&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;options&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;맵으로 여러 설정 지정&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;target-file-size-bytes&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;목표 파일 크기 (바이트)&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;max-file-group-size-bytes&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;최대 파일 그룹 크기&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;max-concurrent-file-group-rewrites&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;동시 파일 그룹 재작성 최대 수&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;partial-progress-enabled&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;부분 진행 활성화 여부&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;partial-progress-max-commits&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;부분 진행 시 최대 커밋 수&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;h4&gt;Spark SQL 확장을 사용한 컴팩션 예제&lt;/h4&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;-- rewrite_data_files 프로시저를 사용한 컴팩션 작업
CALL catalog.system.rewrite_data_files(
  table =&amp;gt; 'musicians',
  strategy =&amp;gt; 'binpack',
  where =&amp;gt; 'genre = "rock"',
  options =&amp;gt; map(
    'rewrite-job-order','bytes-asc',
    'target-file-size-bytes','1073741824', -- 1GB
    'max-file-group-size-bytes','10737418240' -- 10GB
  )
)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이 시나리오에서는 musicians 테이블에 일부 데이터를 스트리밍했고 rock 밴드에 대해 많은 작은 파일이 생성된 것을 발견했습니다. 전체 테이블에 컴팩션을 실행하는 대신(시간이 많이 소요됨), 문제가 있는 데이터만 대상으로 했습니다.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;참고:&lt;/strong&gt; &lt;code&gt;where&lt;/code&gt; 필터에서 큰따옴표 사용에 주의하세요. 필터 주변에 작은따옴표를 사용했으므로, SQL이 일반적으로 사용하는 작은따옴표 대신 문자열 내에서 큰따옴표를 사용합니다.&lt;/p&gt;&lt;/blockquote&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/6/20262612_ixwWkaZC5wgTxjoIZoOx.png" alt="Pasted image 20260201050320.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;h4&gt;파일 그룹 (File Groups)&lt;/h4&gt;&lt;p&gt;엔진이 컴팩션 작업에서 작성할 새 파일을 계획할 때, 이러한 파일들을 병렬로 작성될 파일 그룹으로 그룹화하기 시작합니다 (각 그룹에서 하나의 파일이 동시에 작성될 수 있음). 컴팩션 작업에서 이러한 파일 그룹의 크기와 동시에 작성할 수를 구성하여 메모리 문제를 방지할 수 있습니다.&lt;/p&gt;&lt;h4&gt;부분 진행 (Partial Progress)&lt;/h4&gt;&lt;p&gt;부분 진행을 사용하면 파일 그룹이 완료될 때마다 새 스냅샷이 생성됩니다. 이를 통해 쿼리가 다른 파일이 완료되는 동안 이미 컴팩션된 파일의 이점을 얻을 수 있습니다. 또한 진행 상황이 작업 완료 시 저장되고 메모리에 보관해야 하는 데이터가 줄어들어 대규모 컴팩션 작업의 OOM(Out-of-Memory) 상황을 방지하는 데 도움이 됩니다.&lt;/p&gt;&lt;p&gt;더 많은 스냅샷은 테이블 위치에서 저장 공간을 차지하는 더 많은 메타데이터 파일을 의미한다는 점을 명심하세요. 하지만 읽기 작업이 컴팩션 작업의 이점을 더 빨리 얻기를 원한다면 이것은 유용한 기능이 될 수 있습니다.&lt;/p&gt;&lt;h4&gt;1.4 컴팩션 전략 (Compaction Strategies)&lt;/h4&gt;&lt;p&gt;rewriteDataFiles 프로시저를 사용할 때 선택할 수 있는 여러 컴팩션 전략이 있습니다. 아래 표는 각 전략의 장단점을 요약합니다.&lt;/p&gt;&lt;table style="min-width: 100px;"&gt;&lt;colgroup&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;/colgroup&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;전략&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;기능&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;장점&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;단점&lt;/p&gt;&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;strong&gt;Binpack&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;파일만 결합; 전역 정렬 없음 (태스크 내 로컬 정렬은 수행)&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;가장 빠른 컴팩션 작업&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;데이터가 클러스터링되지 않음&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;strong&gt;Sort&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;태스크 할당 전에 하나 이상의 필드로 순차적 정렬 (예: 필드 a로 정렬, 그 안에서 필드 b로 정렬)&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;자주 쿼리되는 필드로 클러스터링된 데이터는 훨씬 빠른 읽기 시간 제공&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;binpack에 비해 더 긴 컴팩션 작업 시간&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;strong&gt;Z-order&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;태스크 할당 전에 동등한 가중치를 가진 여러 필드로 정렬 (이 범위의 X와 Y 값은 한 그룹에, 다른 범위는 다른 그룹에)&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;쿼리가 여러 필드의 필터에 자주 의존하는 경우 읽기 시간을 더욱 개선&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;binpack에 비해 더 긴 컴팩션 작업 시간&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;참고:&lt;/strong&gt; Apache Iceberg 테이블에 정렬 순서가 설정되어 있으면, binpack을 사용하더라도 이 정렬 순서가 단일 태스크 내에서 데이터를 정렬하는 데 사용됩니다 (로컬 정렬). sort와 z-order 전략을 사용하면 쿼리 엔진이 레코드를 다른 태스크에 할당하기 전에 데이터를 정렬하여 태스크 간 데이터 클러스터링을 최적화합니다.&lt;/p&gt;&lt;/blockquote&gt;&lt;h4&gt;Binpack 전략&lt;/h4&gt;&lt;p&gt;기본적으로 rewriteDataFiles 프로시저는 &lt;strong&gt;binpack&lt;/strong&gt; 전략을 사용합니다. 이 전략은 데이터파일을 병합하는 데 초점을 맞추며, 데이터의 순서를 변경하지 않습니다. 이 방법은 단순히 여러 파일을 더 적은 수의 파일로 압축할 때 필요한 최소한의 작업입니다.&lt;/p&gt;&lt;p&gt;binpack 전략은 본질적으로 파일 크기 외에 데이터가 어떻게 구성되는지에 대한 다른 고려 없이 순수한 컴팩션입니다. 세 가지 전략 중 binpack이 가장 빠릅니다. 작은 파일의 내용을 목표 크기의 큰 파일에 그냥 쓸 수 있기 때문입니다. 반면 sort와 z-order는 파일 그룹을 쓰기 위해 할당하기 전에 데이터를 정렬해야 합니다. 이는 스트리밍 데이터가 있고 SLA(서비스 수준 계약)를 충족하는 속도로 컴팩션을 실행해야 할 때 특히 유용합니다.&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/6/20262612_Sx85Wdsy0h4GlKy28etb.png" alt="Pasted image 20260202063842.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;h4&gt;1.5 컴팩션 자동화 (Automating Compaction)&lt;/h4&gt;&lt;p&gt;컴팩션 작업을 수동으로 실행하면 모든 SLA를 충족하기 어려울 수 있으므로, 이러한 프로세스를 자동화하는 방법을 찾는 것이 실질적인 이점이 될 수 있습니다:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;오케스트레이션 도구 사용&lt;/strong&gt;: Airflow, Dagster, Prefect, Argo, Luigi 등을 사용하여 수집 작업 완료 후 또는 특정 시간이나 주기적 간격으로 Spark나 Dremio와 같은 엔진에 적절한 SQL을 전송&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;서버리스 함수&lt;/strong&gt;: 클라우드 오브젝트 스토리지에 데이터가 도착한 후 작업을 트리거&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;크론 작업&lt;/strong&gt;: 특정 시간에 적절한 작업을 실행하도록 설정&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;또한 Dremio Arctic과 Tabular과 같이 컴팩션을 포함한 자동화된 테이블 유지보수 기능을 갖춘 관리형 Apache Iceberg 카탈로그 서비스도 있습니다.&lt;/p&gt;&lt;hr&gt;&lt;h2&gt;2. 정렬 (Sorting)&lt;/h2&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/6/20262612_bGjqdBuHi6xZepVGE0fv.png" alt="Pasted image 20260202063903.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;h4&gt;2.1 정렬의 이점&lt;/h4&gt;&lt;p&gt;정렬 또는 "클러스터링"은 쿼리에 있어 매우 특별한 이점이 있습니다: 쿼리에 필요한 데이터를 얻기 위해 스캔해야 하는 파일 수를 제한하는 데 도움이 됩니다. 데이터를 정렬하면 유사한 값을 가진 데이터가 더 적은 파일에 집중되어 보다 효율적인 쿼리 계획이 가능해집니다.&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/6/20262612_2neQ1uCsvdsAUppPOaAa.png" alt="Pasted image 20260201053649.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;예시:&lt;/strong&gt; NFL 팀의 모든 선수를 나타내는 데이터셋이 특별한 순서 없이 100개의 Parquet 파일에 걸쳐 있다고 가정합니다. Detroit Lions 선수만 쿼리하면, 100개의 레코드 중 Detroit Lions 선수가 단 하나뿐인 파일이 있더라도 해당 파일은 쿼리 플랜에 추가되어 스캔되어야 합니다. 이는 최대 53개의 파일(NFL 팀의 최대 선수 수)을 스캔해야 할 수 있음을 의미합니다.&lt;/p&gt;&lt;p&gt;팀 이름을 알파벳순으로 정렬하면 모든 Detroit Lions 선수는 약 4개의 파일에 있어야 합니다 (100개 파일 ÷ 32개 NFL 팀 = 3.125). 따라서 데이터를 정렬함으로써 스캔해야 할 파일 수를 53개에서 4개로 줄일 수 있습니다.&lt;/p&gt;&lt;h4&gt;2.2 테이블 생성 시 정렬 설정&lt;/h4&gt;&lt;p&gt;테이블을 생성하는 두 가지 주요 방법:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;방법 1: 표준 CREATE TABLE 문&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;-- Spark SQL
CREATE TABLE catalog.nfl_players (
  id bigint,
  player_name varchar,
  team varchar,
  num_of_touchdowns int,
  num_of_yards int,
  player_position varchar,
  player_number int
)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;방법 2: CREATE TABLE…AS SELECT (CTAS) 문&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;-- Spark SQL
CREATE TABLE catalog.nfl_players
AS (SELECT * FROM non_iceberg_teams_table);&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;2.3 정렬 순서 설정&lt;/h4&gt;&lt;p&gt;테이블 생성 후 정렬 순서를 설정합니다. 이 속성을 지원하는 모든 엔진은 쓰기 전에 데이터를 정렬하는 데 사용하며, sort 컴팩션 전략 사용 시 기본 정렬 필드로도 사용됩니다:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;ALTER TABLE catalog.nfl_teams WRITE ORDERED BY team;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;CTAS를 수행하는 경우 AS 쿼리에서 데이터를 정렬합니다:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;CREATE TABLE catalog.nfl_teams
AS (SELECT * FROM non_iceberg_teams_table ORDER BY team);

ALTER TABLE catalog.nfl_teams WRITE ORDERED BY team;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;INSERT INTO에서도 지정할 수 있습니다:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;INSERT INTO catalog.nfl_teams
SELECT *
FROM staging_table
ORDER BY team&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;2.4 Sort 컴팩션 전략&lt;/h4&gt;&lt;p&gt;NFL 데이터셋이 매년 팀 로스터 변경을 위해 업데이트된다면, 여러 쓰기 작업에서 Lions와 Packers 선수를 분할하는 많은 파일이 생길 수 있습니다. 이는 현재 연도의 새 Lions 선수를 포함하는 새 파일을 작성해야 하기 때문입니다. 이때 sort 컴팩션 전략이 유용합니다.&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/6/20262612_tdY04U8qgtHAydUGNLVN.png" alt="Pasted image 20260201054500.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;sort 컴팩션 전략은 작업 대상 모든 파일에 걸쳐 데이터를 정렬합니다:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;CALL catalog.system.rewrite_data_files(
  table =&amp;gt; 'nfl_teams',
  strategy =&amp;gt; 'sort',
  sort_order =&amp;gt; 'team ASC NULLS LAST'
)&lt;/code&gt;&lt;/pre&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/6/20262612_XdNVeMaIfllMAGIEEO93.png" alt="Pasted image 20260201054517.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;핵심 포인트:&lt;/strong&gt; 정렬의 최대 이점을 얻으려면 최종 사용자가 묻는 질문 유형을 이해하여 그들의 질문에 효과적으로 대응할 수 있도록 데이터를 정렬해야 합니다.&lt;/p&gt;&lt;hr&gt;&lt;h2&gt;3. Z-order&lt;/h2&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/6/20262612_HPpEoDHiHnsuQKVTyLCx.png" alt="Pasted image 20260202071426.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;h4&gt;3.1 Z-order의 개념&lt;/h4&gt;&lt;p&gt;테이블을 쿼리할 때 여러 필드가 우선순위인 경우가 있으며, 이때 z-order 정렬이 매우 유용할 수 있습니다. z-order 정렬은 여러 데이터 포인트로 데이터를 정렬하여 엔진이 최종 쿼리 플랜에서 스캔할 파일을 줄일 수 있는 더 큰 능력을 제공합니다.&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/6/20262612_9QRIkgykfGAFDBjwSAG4.png" alt="Pasted image 20260201054742.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;h4&gt;3.2 Z-order의 작동 방식&lt;/h4&gt;&lt;p&gt;4×4 그리드에서 항목 Z를 찾으려 한다고 상상해 보세요. 값(z)이 3.5와 같다고 할 때:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;X와 Y 값의 범위에 따라 필드를 4개의 사분면으로 나눕니다&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;검색하려는 데이터가 z-order로 정렬된 필드에 기반한다면, 데이터의 큰 부분 검색을 피할 수 있습니다&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;해당 사분면을 더 세분화하고 사분면 내 데이터에 또 다른 z-order 정렬을 적용할 수 있습니다&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;검색이 여러 요소(X와 Y)에 기반하므로, 이 접근 방식을 취함으로써 검색 가능한 영역의 75%를 제거할 수 있습니다&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h4&gt;3.3 Z-order 실제 예시&lt;/h4&gt;&lt;p&gt;의료 코호트 연구에 참여한 모든 사람의 데이터셋이 있고, 나이와 키로 코호트의 결과를 정리하려 한다면 z-order가 매우 유용할 수 있습니다.&lt;/p&gt;&lt;p&gt;특정 사분면에 속하는 데이터는 동일한 데이터파일에 있게 되어, 다른 연령/키 그룹에 대한 분석을 실행할 때 스캔할 파일을 정말로 줄일 수 있습니다. 키가 6피트이고 나이가 60세인 사람을 검색하면, 다른 세 사분면에 속하는 데이터를 가진 데이터파일을 즉시 제거할 수 있습니다.&lt;/p&gt;&lt;p&gt;데이터파일은 네 가지 범주로 분류됩니다:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;A&lt;/strong&gt;: Age 1–50, Height 1–5 레코드가 있는 파일&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;B&lt;/strong&gt;: Age 51–100, Height 1–5 레코드가 있는 파일&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;C&lt;/strong&gt;: Age 1–50, Height 5–10 레코드가 있는 파일&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;D&lt;/strong&gt;: Age 51–100, Height 5–10 레코드가 있는 파일&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;60세이고 6피트인 사람을 검색하면, A, B, C 범주의 모든 데이터파일이 제거되어 스캔되지 않습니다. 나이만으로 검색하더라도 최소 네 사분면 중 두 개를 제거할 수 있어 클러스터링의 이점을 볼 수 있습니다.&lt;/p&gt;&lt;h4&gt;3.4 Z-order 컴팩션 작업 실행&lt;/h4&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;CALL catalog.system.rewrite_data_files(
  table =&amp;gt; 'people',
  strategy =&amp;gt; 'sort',
  sort_order =&amp;gt; 'zorder(age,height)'
)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;sort 및 z-order 컴팩션 전략을 사용하면 데이터가 존재하는 파일 수를 줄일 수 있을 뿐만 아니라, 해당 파일의 데이터 순서가 더욱 효율적인 쿼리 계획을 가능하게 합니다.&lt;/p&gt;&lt;h4&gt;3.5 정렬의 한계&lt;/h4&gt;&lt;p&gt;정렬은 효과적이지만 몇 가지 과제가 있습니다:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;새 데이터 수집 시&lt;/strong&gt;: 새 데이터가 수집되면 정렬되지 않은 상태가 되며, 다음 컴팩션 작업까지 데이터는 여러 파일에 걸쳐 다소 흩어진 상태로 남습니다&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;파일 내 다중 값&lt;/strong&gt;: 파일에 정렬된 필드의 여러 값에 대한 데이터가 여전히 포함될 수 있어, 특정 값의 데이터만 필요한 쿼리에 비효율적일 수 있습니다&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;이러한 문제를 해결하기 위해 &lt;strong&gt;파티셔닝&lt;/strong&gt;을 사용합니다.&lt;/p&gt;&lt;hr&gt;&lt;h2&gt;4. 파티셔닝 (Partitioning)&lt;/h2&gt;&lt;h4&gt;4.1 파티셔닝의 개념&lt;/h4&gt;&lt;p&gt;데이터 액세스 방식에 특정 필드가 핵심적이라면, 정렬을 넘어 파티셔닝을 고려할 수 있습니다. 테이블이 파티션되면, 필드에 기반하여 순서만 정렬하는 대신, 대상 필드의 고유 값을 가진 레코드를 자체 데이터파일에 씁니다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;예시:&lt;/strong&gt; 정치에서 유권자 데이터를 유권자의 정당 소속에 따라 자주 쿼리할 가능성이 높아, 이것이 좋은 파티션 필드가 됩니다. 이는 "Blue" 정당의 모든 유권자가 "Red", "Yellow", "Green" 정당의 유권자와 별도의 파일에 나열됨을 의미합니다. "Yellow" 정당의 유권자를 쿼리하면, 스캔하는 데이터파일에는 다른 정당의 사람이 포함되지 않습니다.&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/6/20262612_EKZPcpAnUiR7ds061MKo.png" alt="Pasted image 20260201071348.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;h4&gt;4.2 전통적인 파티셔닝의 문제점&lt;/h4&gt;&lt;p&gt;전통적으로 특정 필드의 파생 값을 기반으로 테이블을 파티셔닝하려면 별도로 관리해야 하는 추가 필드를 생성해야 했고, 사용자가 쿼리할 때 해당 별도 필드에 대한 지식이 필요했습니다:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;날짜 기반 파티셔닝&lt;/strong&gt;: 타임스탬프 열의 일, 월, 또는 연도로 파티셔닝하려면 년, 월, 또는 일을 분리하여 표현하는 타임스탬프 기반의 추가 열을 생성해야 했습니다&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;텍스트 기반 파티셔닝&lt;/strong&gt;: 텍스트 값의 첫 글자로 파티셔닝하려면 해당 글자만 있는 추가 열을 생성해야 했습니다&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;버킷 파티셔닝&lt;/strong&gt;: 해시 함수에 기반하여 레코드를 균등하게 분배하는 일정 수의 분할(버킷)로 파티셔닝하려면 레코드가 속한 버킷을 명시하는 추가 열을 생성해야 했습니다&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;문제점:&lt;/strong&gt; 엔진은 원래 필드와 파생 필드 간의 관계를 인식하지 못합니다. 이는 다음 쿼리가 파티셔닝의 이점을 받지만:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;SELECT * FROM MYTABLE WHERE time BETWEEN '2022-07-01 00:00:00' AND '2022-07-31 00:00:00' 
AND month = 7;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;사용자는 종종 이 우회 열을 인식하지 못합니다(인식해야 할 필요도 없습니다). 이는 대부분의 경우 사용자가 다음과 유사한 쿼리를 발행하여 전체 테이블 스캔이 발생하고, 쿼리 시간이 훨씬 오래 걸리고 훨씬 더 많은 리소스를 소비함을 의미합니다:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;SELECT * FROM MYTABLE WHERE time BETWEEN '2022-07-01 00:00:00' AND '2022-07-31 00:00:00';&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;4.3 히든 파티셔닝 (Hidden Partitioning)&lt;/h4&gt;&lt;p&gt;Apache Iceberg는 파티셔닝을 상당히 다르게 처리하여 테이블을 파티셔닝으로 최적화할 때 이러한 많은 문제점을 해결합니다. 이 접근 방식의 결과적인 기능을 &lt;strong&gt;히든 파티셔닝&lt;/strong&gt;이라고 합니다.&lt;/p&gt;&lt;p&gt;Iceberg가 파티셔닝을 추적하는 방식에서 시작됩니다. 파일이 물리적으로 배치되는 방식에 의존하는 대신, Iceberg는 스냅샷과 매니페스트 수준에서 파티션 값의 범위를 추적하여 많은 수준의 새로운 유연성을 허용합니다:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;변환 값을 기반으로 파티셔닝하기 위해 추가 열을 생성할 필요 없이, 쿼리 계획 시 엔진과 도구가 메타데이터에서 적용할 수 있는 내장 변환을 사용할 수 있습니다&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;이러한 변환을 사용할 때 추가 열이 필요하지 않으므로 데이터파일에 저장하는 양이 줄어듭니다&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;메타데이터가 원래 열에 대한 변환을 엔진이 인식할 수 있게 하므로, 원래 열만으로 필터링해도 파티셔닝의 이점을 얻을 수 있습니다&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;예시:&lt;/strong&gt; 월별로 파티션된 테이블을 생성하면:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;CREATE TABLE catalog.MyTable (...) PARTITIONED BY months(time) USING iceberg;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;다음 쿼리가 파티셔닝의 이점을 받습니다:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;SELECT * FROM MYTABLE WHERE time BETWEEN '2022-07-01 00:00:00' AND '2022-07-31 00:00:00';&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;4.4 파티션 변환 함수&lt;/h4&gt;&lt;p&gt;파티셔닝 계획 시 사용 가능한 여러 변환:&lt;/p&gt;&lt;table style="min-width: 50px;"&gt;&lt;colgroup&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;/colgroup&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;변환&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;설명&lt;/p&gt;&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;year&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;연도만&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;month&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;월과 연도&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;day&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;일, 월, 연도&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;hour&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;시간, 일, 월, 연도&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;truncate&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;값을 잘라서 파티셔닝&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;bucket&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;해시 함수를 사용하여 지정된 버킷 수로 분배&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&lt;code&gt;year&lt;/code&gt;, &lt;code&gt;month&lt;/code&gt;, &lt;code&gt;day&lt;/code&gt;, &lt;code&gt;hour&lt;/code&gt; 변환은 타임스탬프 열에서 작동합니다. &lt;code&gt;month&lt;/code&gt;를 지정하면 메타데이터에서 추적되는 파티션 값은 타임스탬프의 월과 연도를 반영하고, &lt;code&gt;day&lt;/code&gt;를 사용하면 연도, 월, 일을 반영하므로 더 세분화된 파티셔닝을 위해 여러 변환을 사용할 필요가 없습니다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;truncate 변환 예시:&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;-- 사람 이름의 첫 글자를 기반으로 파티셔닝
CREATE TABLE catalog.MyTable (...) PARTITIONED BY truncate(name, 1) USING iceberg;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;bucket 변환 예시:&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;-- 우편번호 기반 유권자 데이터 파티셔닝 (너무 많은 고유 값이 있는 경우)
CREATE TABLE catalog.voters (...) PARTITIONED BY bucket(24, zip) USING iceberg;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;모든 버킷에 여러 우편번호가 포함되지만, 적어도 특정 우편번호를 찾으면 전체 테이블 스캔이 아닌 검색하는 우편번호가 포함된 버킷만 스캔하면 됩니다.&lt;/p&gt;&lt;h4&gt;4.5 파티션 진화 (Partition Evolution)&lt;/h4&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/6/20262613_52QfmF7GMvYfnYyEKAUN.png" alt="Pasted image 20260202070443.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;전통적인 파티셔닝의 또 다른 과제는 파일이 하위 디렉토리로 배치되는 물리적 구조에 의존했기 때문에, 테이블이 파티션되는 방식을 변경하려면 전체 테이블을 다시 작성해야 했다는 것입니다. 이는 데이터와 쿼리 패턴이 진화하면서 피할 수 없는 문제가 되어, 데이터를 파티션하고 정렬하는 방법을 재고해야 합니다.&lt;/p&gt;&lt;p&gt;Apache Iceberg는 메타데이터 추적 파티셔닝으로 이 문제를 해결합니다. 메타데이터가 파티션 값뿐만 아니라 과거 파티션 체계도 추적하기 때문에 파티션 체계가 진화할 수 있습니다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;예시:&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;-- 회원 등록 연도별로 파티션된 테이블
CREATE TABLE catalog.members (...) PARTITIONED BY years(registration_ts) USING iceberg;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;수년 후, 회원 성장 속도가 월별로 레코드를 세분화할 가치가 있게 되면:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;ALTER TABLE catalog.members ADD PARTITION FIELD months(registration_ts)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;더 이상 특정 필드로 파티셔닝하지 않으려면:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;ALTER TABLE catalog.members DROP PARTITION FIELD bucket(24, id);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;핵심 포인트:&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;파티셔닝 체계가 업데이트되면 앞으로 테이블에 쓰이는 새 데이터에만 적용되므로 기존 데이터를 다시 쓸 필요가 없습니다&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;rewriteDataFiles&lt;/code&gt; 프로시저로 다시 쓴 모든 데이터는 새 파티셔닝 체계를 사용하여 다시 쓰입니다&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;이전 데이터를 이전 체계로 유지하려면 컴팩션 작업에서 적절한 필터를 사용하여 다시 쓰지 않도록 해야 합니다&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;4.6 Hive vs Iceberg 파티션 조회 방식 비교&lt;/h4&gt;&lt;table style="min-width: 75px;"&gt;&lt;colgroup&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;/colgroup&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;구분&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;Hive Table Format&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;Iceberg&lt;/p&gt;&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;strong&gt;파티션 컬럼&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;별도 컬럼 존재 (year, month, day 등)&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;별도 컬럼 없음&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;strong&gt;쿼리 시&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;파티션 컬럼 직접 명시 필요&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;원본 컬럼으로 필터링 (자동 프루닝)&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;strong&gt;파티션 정보 조회&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;SHOW PARTITIONS table&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;메타데이터 테이블 (&lt;code&gt;table.partitions&lt;/code&gt;)&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;strong&gt;파티션 변경&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;테이블 재작성 필요&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;메타데이터만 변경 (Partition Evolution)&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;Iceberg의 히든 파티셔닝은 사용자에게 파티션 구조를 숨기면서도, 메타데이터 테이블을 통해 관리자/엔지니어가 파티션 상태를 모니터링하고 최적화할 수 있게 해줍니다.&lt;/p&gt;&lt;h4&gt;쿼리 예시&lt;/h4&gt;&lt;h6&gt;Hive Table Format&lt;/h6&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;-- 별도 파티션 컬럼이 실제로 존재
SELECT * FROM orders WHERE year = 2024 AND month = 1 AND day = 15;

-- 파티션 컬럼을 직접 명시해야 파티션 프루닝 적용&lt;/code&gt;&lt;/pre&gt;&lt;h6&gt;Iceberg (Hidden Partitioning)&lt;/h6&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;-- 원본 컬럼만으로 쿼리 (파티션 프루닝 자동 적용)
SELECT * FROM orders WHERE order_ts BETWEEN '2024-01-15 00:00:00' AND '2024-01-15 23:59:59';&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Iceberg는 메타데이터가 &lt;code&gt;order_ts&lt;/code&gt;와 &lt;code&gt;day(order_ts)&lt;/code&gt; 변환 간의 관계를 알고 있어서, 원본 타임스탬프 컬럼으로 필터링해도 자동으로 파티션 프루닝이 적용됩니다.&lt;/p&gt;&lt;h4&gt;Iceberg 파티션 메타데이터 조회 방법&lt;/h4&gt;&lt;h6&gt;1. partitions 메타데이터 테이블&lt;/h6&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;-- Spark SQL
SELECT * FROM my_catalog.table.partitions;

-- Trino
SELECT * FROM "test_table$partitions";&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;partitions 테이블 스키마:&lt;/strong&gt;&lt;/p&gt;&lt;table style="min-width: 100px;"&gt;&lt;colgroup&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;/colgroup&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;필드명&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;데이터 타입&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;예시 값&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;설명&lt;/p&gt;&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;partition&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;List&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;{20211001, 11}&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;실제 파티션 값&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;spec_id&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Int&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;0&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;파티션 스펙 ID&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;record_count&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Int&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;1&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;파티션 내 레코드 수&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;file_count&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Int&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;1&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;파티션 내 파일 수&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;position_delete_record_count&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Int&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;0&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;위치 삭제 레코드 수&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;position_delete_file_count&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Int&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;0&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;위치 삭제 파일 수&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;equality_delete_record_count&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Int&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;0&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;동등 삭제 레코드 수&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;equality_delete_file_count&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Int&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;0&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;동등 삭제 파일 수&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;h6&gt;2. files 메타데이터 테이블 (파일별 파티션 정보)&lt;/h6&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;-- Spark SQL
SELECT partition, file_path, record_count, file_size_in_bytes 
FROM my_catalog.table.files;

-- Dremio
SELECT * FROM TABLE(table_files('catalog.table'));&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;실용적인 파티션 조회 쿼리 예시&lt;/h4&gt;&lt;h6&gt;파티션별 파일 수 확인 (컴팩션 대상 식별)&lt;/h6&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;SELECT partition, file_count 
FROM catalog.table.partitions;&lt;/code&gt;&lt;/pre&gt;&lt;h6&gt;파티션별 총 크기 확인&lt;/h6&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;SELECT partition, SUM(file_size_in_bytes) AS partition_size 
FROM catalog.table.files 
GROUP BY partition;&lt;/code&gt;&lt;/pre&gt;&lt;h6&gt;파티션 스펙별 파티션 수 확인 (파티션 진화 후)&lt;/h6&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;SELECT
  spec_id,
  COUNT(*) as partition_count
FROM
  catalog.table.partitions
GROUP BY
  spec_id;&lt;/code&gt;&lt;/pre&gt;&lt;h6&gt;컴팩션이 필요한 파티션 식별&lt;/h6&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;SELECT
  partition,
  COUNT(*) AS num_files,
  AVG(file_size_in_bytes) AS avg_file_size
FROM
  catalog.table.files
GROUP BY
  partition
ORDER BY
  num_files DESC,
  avg_file_size ASC;&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;4.7 기타 파티셔닝 고려사항&lt;/h4&gt;&lt;h4&gt;테이블 마이그레이션 시 파티션 변환&lt;/h4&gt;&lt;p&gt;migrate 프로시저(13장에서 논의)를 사용하여 Hive 테이블을 마이그레이션할 때, 현재 파생 열(예: 같은 테이블의 타임스탬프 열에 기반한 월 열)로 파티션되어 있지만 Apache Iceberg에 Iceberg 변환을 대신 사용해야 함을 표현하고 싶을 수 있습니다. 이를 위해&amp;nbsp;&lt;code&gt;REPLACE PARTITION&lt;/code&gt;&amp;nbsp;명령을 사용할 수 있습니다:&lt;/p&gt;&lt;p&gt;sql&lt;/p&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;ALTER TABLE catalog.members REPLACE PARTITION FIELD registration_day 
WITH days(registration_ts) AS day_of_registration;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이 명령은 데이터파일을 변경하지 않지만, 메타데이터가 Iceberg 변환을 사용하여 파티션 값을 추적할 수 있게 해줍니다.&lt;/p&gt;&lt;hr&gt;&lt;h2&gt;5. Copy-on-Write vs Merge-on-Read&lt;/h2&gt;&lt;h4&gt;5.1 개요&lt;/h4&gt;&lt;p&gt;Apache Iceberg를 사용할 때 행 수준 업데이트(updates)와 삭제(deletes)가 테이블에서 어떻게 처리될지 선택해야 합니다. 선택하는 전략은 업데이트와 삭제를 많이 하는지, 테이블을 쿼리 최적화에 대해 얼마나 관심이 있는지에 따라 달라집니다.&lt;/p&gt;&lt;table style="min-width: 100px;"&gt;&lt;colgroup&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;/colgroup&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;전략&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;읽기 성능&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;업데이트/삭제 성능&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;최적화 방법&lt;/p&gt;&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Copy-on-Write&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;빠른 읽기&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;느린 업데이트/삭제&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;수정 빈도가 낮은 데이터에 이상적&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Merge-on-Read (positional deletes)&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;읽기 부하 있음&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;빠른 업데이트/삭제&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;정기적인 컴팩션을 사용하여 읽기 비용 최소화&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Merge-on-Read (equality deletes)&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;느린 읽기&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;가장 빠른 업데이트/삭제&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;더 빈번한 컴팩션을 사용하여 읽기 비용 최소화&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/6/20262613_7MqCNvzO8s830IcodrmM.png" alt="Pasted image 20260202070519.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;h4&gt;5.2 Copy-on-Write (COW)&lt;/h4&gt;&lt;p&gt;기본 접근 방식은 &lt;strong&gt;copy-on-write(COW)&lt;/strong&gt;라고 합니다. 이 접근 방식에서는 데이터파일의 단일 행이라도 업데이트되거나 삭제되면, 해당 데이터파일이 다시 쓰이고 새 파일이 새 스냅샷에서 그 자리를 대신합니다.&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/6/20262613_SuY5HORmTeh86yA4VCmc.png" alt="Pasted image 20260201080314.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/6/20262613_3L0O6EltidkngikCfsmH.png" alt="Pasted image 20260202070537.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;이것은 읽기 쿼리가 삭제되거나 업데이트된 파일을 조정할 필요 없이 데이터를 읽을 수 있어 &lt;strong&gt;읽기에 최적화&lt;/strong&gt;하려는 경우 이상적입니다. 그러나 작업 부하가 매우 정기적인 행 수준 업데이트로 구성되면, 해당 업데이트를 위해 전체 데이터파일을 다시 쓰는 것이 SLA가 허용하는 것 이상으로 업데이트를 느리게 할 수 있습니다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;장점:&lt;/strong&gt; 더 빠른 읽기&lt;br&gt;&lt;strong&gt;단점:&lt;/strong&gt; 더 느린 행 수준 업데이트 및 삭제&lt;/p&gt;&lt;h4&gt;5.3 Merge-on-Read (MOR)&lt;/h4&gt;&lt;p&gt;copy-on-write의 대안은 &lt;strong&gt;merge-on-read(MOR)&lt;/strong&gt;입니다. 전체 데이터파일을 다시 쓰는 대신, 삭제 파일에 기존 파일에서 업데이트할 레코드를 캡처하고, 삭제 파일이 어떤 레코드를 무시해야 하는지 추적합니다.&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/6/20262613_hTJ1WZhqopdxIDC0bYm9.png" alt="Pasted image 20260202070602.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;레코드 삭제 시:&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;레코드가 삭제 파일에 나열됩니다&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;리더가 테이블을 읽을 때, 데이터파일과 삭제 파일을 조정합니다&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;레코드 업데이트 시:&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;업데이트할 레코드가 삭제 파일에 추적됩니다&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;업데이트된 레코드만 있는 새 데이터파일이 생성됩니다&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;리더가 테이블을 읽을 때, 삭제 파일 때문에 레코드의 이전 버전을 무시하고 새 데이터파일의 새 버전을 사용합니다&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/6/20262613_dVIk80Aqoh7AKUiRZUtY.png" alt="Pasted image 20260201080927.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;이는 업데이트할 레코드와 함께 데이터파일에 존재한다는 이유만으로 변경되지 않은 레코드를 새 파일에 다시 쓸 필요를 피하고, 쓰기 트랜잭션을 가속화합니다. 그러나 쿼리가 삭제 파일을 스캔하여 적절한 데이터파일에서 어떤 레코드를 무시해야 하는지 알아야 하므로 더 느린 읽기의 비용이 따릅니다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;읽기 비용을 최소화하기 위해:&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;정기적인 컴팩션 작업을 실행합니다&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;컴팩션 작업이 효율적으로 실행되도록 다음 속성을 활용합니다:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;filter/where&lt;/code&gt; 절을 사용하여 마지막 시간 프레임(시간, 일)에 수집된 파일에만 컴팩션을 실행&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;부분 진행 모드를 사용하여 파일 그룹이 다시 쓰일 때마다 커밋하여 리더가 점점 개선을 더 빨리 볼 수 있도록 함&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;장점:&lt;/strong&gt; 더 빠른 행 수준 업데이트&lt;br&gt;&lt;strong&gt;단점:&lt;/strong&gt; 삭제 파일을 조정해야 하므로 더 느린 읽기&lt;/p&gt;&lt;h4&gt;5.4 삭제 파일 유형&lt;/h4&gt;&lt;p&gt;MOR 쓰기를 할 때, 삭제 파일을 통해 향후 읽기를 위해 기존 데이터파일에서 어떤 레코드를 무시해야 하는지 추적할 수 있습니다.&lt;/p&gt;&lt;h4&gt;Positional Delete Files (위치 삭제 파일)&lt;/h4&gt;&lt;p&gt;특정 행을 제거하고 싶을 때, 데이터셋에서 위치에 따라 행 데이터를 찾을 수 있습니다. 영화관에서 좌석 번호로 친구를 찾는 것과 같습니다.&lt;/p&gt;&lt;p&gt;위치 삭제는 어떤 파일의 어떤 행을 무시해야 하는지 추적합니다:&lt;/p&gt;&lt;table style="min-width: 50px;"&gt;&lt;colgroup&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;/colgroup&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;Filepath&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;Position&lt;/p&gt;&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;001.parquet&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;0&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;001.parquet&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;5&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;006.parquet&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;5&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;지정된 파일을 읽을 때, 위치 삭제 파일은 지정된 위치의 행을 건너뜁니다. 이는 행을 건너뛰어야 하는 상당히 구체적인 지점이 있으므로 읽기 시 훨씬 작은 비용이 필요합니다. 그러나 삭제 파일의 작성자가 삭제된 레코드의 위치를 알아야 하므로 삭제된 레코드가 있는 파일을 읽어 해당 위치를 식별해야 하는 쓰기 시 비용이 있습니다.&lt;/p&gt;&lt;h4&gt;Equality Delete Files (동등 삭제 파일)&lt;/h4&gt;&lt;p&gt;레코드가 일치하면 무시해야 하는 값을 대신 지정합니다. 군중에서 밝은 빨간 모자를 쓰고 있어서 친구를 고르는 것과 같습니다.&lt;/p&gt;&lt;table style="min-width: 50px;"&gt;&lt;colgroup&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;/colgroup&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;Team&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;State&lt;/p&gt;&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Yellow&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;NY&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;Green&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;MA&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;파일을 열고 읽어 대상 값을 추적할 필요가 없으므로 쓰기 시 비용이 없지만, 훨씬 더 큰 읽기 시 비용이 있습니다. 읽기 시 비용은 일치하는 값이 있는 레코드가 어디에 있는지에 대한 정보가 없어서, 데이터를 읽을 때 일치하는 레코드를 포함할 수 있는 모든 레코드와 비교해야 하기 때문입니다.&lt;/p&gt;&lt;h4&gt;5.5 COW 및 MOR 구성&lt;/h4&gt;&lt;p&gt;행 수준 업데이트와 삭제를 수행하려는 쓰기 유형을 기본적으로 테이블 속성에서 지정할 수 있습니다:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;ALTER TABLE catalog.MyTable SET TBLPROPERTIES (
  'write.delete.mode'='copy-on-write',
  'write.update.mode'='merge-on-read',
  'write.merge.mode'='merge-on-read',
);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;테이블 속성을 설정하는 대신, 쓰기 옵션에서 직접 COW 또는 MOR 속성을 지정할 수도 있습니다 (Spark의 DataFrame API 사용 시):&lt;/p&gt;&lt;pre&gt;&lt;code class="language-python"&gt;df.write \
  .option("write-format", "parquet") \
  .option("merge-mode", "merge-on-read")&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;비 Apache Spark 엔진 작업 시 주의사항:&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;테이블 속성이 준수될 수도 있고 아닐 수도 있습니다. 엔진이 지원을 구현하는지에 따라 달라집니다&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;MOR 사용 시, 데이터를 쿼리하는 데 사용하는 엔진이 삭제 파일을 읽을 수 있는지 확인해야 합니다&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr&gt;&lt;h2&gt;6. 기타 고려사항 (Other Considerations)&lt;/h2&gt;&lt;h4&gt;6.1 메트릭 수집 (Metrics Collection)&lt;/h4&gt;&lt;p&gt;2장에서 논의한 것처럼, 각 데이터파일 그룹에 대한 매니페스트는 min/max 필터링 및 기타 최적화를 돕기 위해 테이블의 각 필드에 대한 메트릭을 추적합니다. 추적되는 열 수준 메트릭 유형:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;값, null 값, 고유 값의 개수&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;상한 및 하한 값&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;매우 넓은 테이블(필드가 많은 테이블, 예: 100개 이상)이 있다면, 추적되는 메트릭의 수가 메타데이터를 읽는 데 부담이 되기 시작할 수 있습니다.&lt;/p&gt;&lt;p&gt;Apache Iceberg의 테이블 속성을 사용하면 어떤 열의 메트릭을 추적하고 어떤 열은 추적하지 않을지 세밀하게 조정할 수 있습니다:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;ALTER TABLE catalog.db.students SET TBLPROPERTIES (
  'write.metadata.metrics.column.col1'='none',
  'write.metadata.metrics.column.col2'='full',
  'write.metadata.metrics.column.col3'='counts',
  'write.metadata.metrics.column.col4'='truncate(16)'
);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;메트릭 수집 레벨:&lt;/strong&gt;&lt;/p&gt;&lt;table style="min-width: 50px;"&gt;&lt;colgroup&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;/colgroup&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;레벨&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;설명&lt;/p&gt;&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;none&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;메트릭을 수집하지 않음&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;counts&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;개수만 수집 (값, 고유 값, null 값)&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;truncate(XX)&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;개수를 세고 값을 특정 문자 수로 잘라서 상한/하한을 그에 기반함&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;full&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;전체 값을 기반으로 개수와 상한/하한 계산&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;기본적으로 Iceberg는 이를 &lt;code&gt;truncate(16)&lt;/code&gt;로 설정합니다.&lt;/p&gt;&lt;h4&gt;6.2 매니페스트 재작성 (Rewriting Manifests)&lt;/h4&gt;&lt;p&gt;때때로 문제는 데이터파일이 아닐 수 있습니다. 데이터파일은 잘 정렬된 데이터로 적절한 크기를 가지고 있지만, 여러 스냅샷에 걸쳐 작성되어 개별 매니페스트가 더 많은 데이터파일을 나열할 수 있습니다. 매니페스트가 더 가볍지만, 더 많은 매니페스트는 여전히 더 많은 파일 작업을 의미합니다.&lt;/p&gt;&lt;p&gt;Apache Iceberg에는 매니페스트를 재작성하는 프로시저가 있습니다:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;CALL catalog.system.rewrite_manifests('MyTable')&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이 프로시저는 기본적으로 매니페스트당 데이터파일 평균에 기반하여 매니페스트를 재작성합니다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;전달할 수 있는 인수:&lt;/strong&gt;&lt;/p&gt;&lt;table style="min-width: 50px;"&gt;&lt;colgroup&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;/colgroup&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;인수&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;설명&lt;/p&gt;&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;table&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;작업을 실행할 테이블&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;use_caching&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;true이면 데이터파일을 읽을 때 Spark 캐시 사용&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;h4&gt;6.3 스토리지 최적화 (Optimizing Storage)&lt;/h4&gt;&lt;h4&gt;스냅샷 만료 (Expire Snapshots)&lt;/h4&gt;&lt;p&gt;테이블에서 스냅샷을 절대 만료시키지 않으면 증가하는 메타데이터 양이 시간이 지남에 따라 부담이 될 수 있습니다. 모든 스냅샷을 유지하면 다음과 같은 추가 비용이 있습니다:&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/6/20262613_5MfimSw4tGqGszOilOJv.png" alt="Pasted image 20260202070641.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;테이블을 구성하는 파일을 호스팅하는 스토리지 비용&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;쿼리 전에 테이블의 메타데이터 구조를 파싱하는 데 드는 계산 비용&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;CALL catalog.system.expire_snapshots(table =&amp;gt; 'MyTable', older_than =&amp;gt; TIMESTAMP '2023-06-01 00:00:00')&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;특정 스냅샷 ID를 만료시킬 수도 있습니다:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;CALL catalog.system.expire_snapshots(table =&amp;gt; 'MyTable', snapshot_ids =&amp;gt; ARRAY(53))&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;expire_snapshots 프로시저에 전달할 수 있는 인수:&lt;/strong&gt;&lt;/p&gt;&lt;table style="min-width: 50px;"&gt;&lt;colgroup&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;/colgroup&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;인수&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;설명&lt;/p&gt;&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;table&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;작업을 실행할 테이블&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;older_than&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;이 타임스탬프 이전의 모든 스냅샷 만료&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;retain_last&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;유지할 최소 스냅샷 수&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;snapshot_ids&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;만료할 특정 스냅샷 ID&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;max_concurrent_deletes&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;파일 삭제에 사용할 스레드 수&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;stream_results&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;true이면 삭제된 파일을 RDD 파티션별로 Spark 드라이버로 전송&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;h4&gt;고아 파일 관리 (Orphan File Management)&lt;/h4&gt;&lt;p&gt;스토리지를 최적화할 때 또 다른 고려사항은 고아 파일입니다. 이는 테이블의 데이터 디렉토리에 축적되지만 실패한 작업에 의해 작성되어 메타데이터 트리에서 추적되지 않는 파일과 아티팩트입니다. 이러한 파일은 스냅샷 만료로 정리되지 않으므로 이를 처리하기 위해 특별한 프로시저를 간헐적으로 실행해야 합니다.&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/6/20262613_Tc6Euy5xpcik2YxCVxUu.png" alt="Pasted image 20260202070658.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;CALL catalog.system.remove_orphan_files(table =&amp;gt; 'MyTable')&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;removeOrphanFiles 프로시저에 전달할 수 있는 인수:&lt;/strong&gt;&lt;/p&gt;&lt;table style="min-width: 50px;"&gt;&lt;colgroup&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;/colgroup&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;인수&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;설명&lt;/p&gt;&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;table&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;작업할 테이블&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;older_than&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;이 타임스탬프 이전에 생성된 파일만 삭제&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;location&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;고아 파일을 찾을 위치; 기본값은 테이블의 기본 위치&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;dry_run&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;true이면 파일을 삭제하지 않고 삭제될 파일 목록만 반환&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;max_concurrent_deletes&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;파일 삭제를 위한 최대 스레드 수&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;h4&gt;6.4 쓰기 분배 모드 (Write Distribution Mode)&lt;/h4&gt;&lt;p&gt;쓰기 분배 모드는 대규모 병렬 처리(MPP) 시스템이 파일 쓰기를 처리하는 방식을 이해해야 합니다. 이러한 시스템은 작업을 여러 노드에 분배하며, 각각 작업이나 태스크를 수행합니다. 쓰기 분배는 쓰려는 레코드가 이러한 태스크에 어떻게 분배되는지입니다.&lt;/p&gt;&lt;p&gt;특정 쓰기 분배 모드가 설정되지 않으면, 데이터는 임의로 분배됩니다. 첫 X개의 레코드는 첫 번째 태스크로, 다음 X개는 다음 태스크로, 등등.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;세 가지 옵션:&lt;/strong&gt;&lt;/p&gt;&lt;table style="min-width: 50px;"&gt;&lt;colgroup&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;/colgroup&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;모드&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;설명&lt;/p&gt;&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;none&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;특별한 분배 없음. 쓰기 시 가장 빠르며 미리 정렬된 데이터에 이상적&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;hash&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;파티션 키로 해시 분배됨&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;&lt;code&gt;range&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;파티션 키 또는 정렬 순서로 범위 분배됨&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;ALTER TABLE catalog.MyTable SET TBLPROPERTIES (
  'write.distribution-mode'='hash',
  'write.delete.distribution-mode'='none',
  'write.update.distribution-mode'='range',
  'write.merge.distribution-mode'='hash'
);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;해시 분배:&lt;/strong&gt; 각 레코드의 값이 해시 함수를 통과하고 결과에 따라 함께 그룹화됩니다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;범위 분배:&lt;/strong&gt; 데이터가 정렬되고 분배됩니다. 파티션 값 또는 테이블에 SortOrder가 있는 경우 SortOrder에 의해 정렬됩니다. 이는 특정 필드에서 클러스터링의 이점을 받을 수 있는 데이터에 이상적입니다.&lt;/p&gt;&lt;h4&gt;6.5 오브젝트 스토리지 고려사항 (Object Storage Considerations)&lt;/h4&gt;&lt;p&gt;오브젝트 스토리지는 데이터를 저장하는 독특한 방식입니다. 전통적인 파일시스템처럼 깔끔한 폴더 구조에 파일을 유지하는 대신, 오브젝트 스토리지는 모든 것을 버킷이라고 불리는 곳에 넣습니다.&lt;/p&gt;&lt;p&gt;오브젝트 스토리지의 아키텍처와 병렬 처리 방식 때문에, 종종 같은 "prefix" 아래의 파일로 갈 수 있는 요청 수에 제한이 있습니다. &lt;code&gt;/prefix1/fileA.txt&lt;/code&gt;와 &lt;code&gt;/prefix1/fileB.txt&lt;/code&gt;에 접근하려 할 때, 서로 다른 파일이지만 둘 다 접근하는 것은 prefix1에 대한 제한에 포함됩니다. 이는 많은 파일이 있는 파티션에서 문제가 되며, 쿼리가 이러한 파티션에 많은 요청을 보내 스로틀링이 발생하여 쿼리가 느려질 수 있습니다.&lt;/p&gt;&lt;p&gt;Apache Iceberg는 파일이 물리적으로 배치되는 방식에 의존하지 않으므로 이 시나리오에 독특하게 적합합니다. 같은 파티션의 파일을 여러 prefix에 걸쳐 쓸 수 있습니다.&lt;/p&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;ALTER TABLE catalog.MyTable SET TBLPROPERTIES (
  'write.object-storage.enabled'= true
);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;이전:&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;&lt;code class="language-plaintext"&gt;s3://bucket/database/table/field=value1/datafile1.parquet
s3://bucket/database/table/field=value1/datafile2.parquet
s3://bucket/database/table/field=value1/datafile3.parquet&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;이후:&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;&lt;code class="language-plaintext"&gt;s3://bucket/4809098/database/table/field=value1/datafile1.parquet
s3://bucket/5840329/database/table/field=value1/datafile2.parquet
s3://bucket/2342344/database/table/field=value1/datafile3.parquet&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;해시가 파일 경로에 있어서, 같은 파티션의 각 파일이 이제 다른 prefix 아래에 있는 것처럼 취급되어 스로틀링을 피할 수 있습니다.&lt;/p&gt;&lt;h4&gt;6.6 데이터파일 블룸 필터 (Datafile Bloom Filters)&lt;/h4&gt;&lt;p&gt;블룸 필터는 값이 데이터셋에 존재할 가능성이 있는지 알 수 있는 방법입니다. 결정한 길이의 비트(이진 코드의 0과 1) 줄을 상상해 보세요. 데이터를 데이터셋에 추가할 때, 각 값을 해시 함수라는 프로세스를 통해 실행합니다. 이 함수는 비트 줄의 한 지점을 내놓고, 그 비트를 0에서 1로 뒤집습니다. 이 뒤집힌 비트는 "이 지점에 해시되는 값이 데이터셋에 있을 수 있다"고 말하는 플래그와 같습니다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;예시:&lt;/strong&gt; 10비트의 블룸 필터를 통해 1,000개의 레코드를 공급하면:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-plaintext"&gt;[0,1,1,0,0,1,1,1,1,0]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;값 X를 찾고 싶다고 합니다. X를 같은 해시 함수를 통해 넣으면 비트 줄의 3번째 지점을 가리킵니다. 블룸 필터에 따르면 3번째 지점에 1이 있습니다. 이는 값 X가 데이터셋에 있을 가능성이 있음을 의미합니다. 그래서 X가 정말 있는지 데이터셋을 확인합니다.&lt;/p&gt;&lt;p&gt;값 Y를 찾는다고 합니다. Y를 해시 함수를 통해 실행하면 4번째 지점을 가리킵니다. 하지만 블룸 필터에 그 지점에 0이 있어서, 이 지점에 해시된 값이 없다는 의미입니다. 따라서 Y가 데이터셋에 확실히 없다고 자신있게 말할 수 있고, 데이터를 뒤지지 않아 시간을 절약할 수 있습니다.&lt;/p&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;ALTER TABLE catalog.MyTable SET TBLPROPERTIES (
  'write.parquet.bloom-filter-enabled.column.col1'= true,
  'write.parquet.bloom-filter-max-bytes'= 1048576
);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;블룸 필터가 명확히 필요한 데이터가 존재하지 않음을 나타내는 데이터파일을 건너뛰어 데이터파일 읽기를 더욱 빠르게 할 수 있습니다.&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/2/6/20262613_SFmfj4HixVqef6JPmBCV.png" alt="Pasted image 20260202070721.png" style="object-fit: cover;"&gt;&lt;/figure&gt;</description><pubDate>Fri, 06 Feb 2026 13:06:56 +0900</pubDate><guid>http://blex.me/@mildsalmon/apache-iceberg-the-definitive-guide-optimizing-t</guid></item><item><title>[Apache Iceberg - The Definitive Guide] Lifecycle of Write and Read Queries</title><link>http://blex.me/@mildsalmon/apache-iceberg-the-definitive-guide-lifecycle-of</link><description>&lt;h2&gt;Apache Iceberg 쿼리 생명주기 완벽 가이드&lt;/h2&gt;&lt;h2&gt;1. 아이스버그의 3계층과 쿼리 엔진의 상호작용&lt;/h2&gt;&lt;p&gt;쿼리 엔진은 읽기/쓰기 작업 시 아이스버그의 세 계층과 다음과 같이 상호작용합니다&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/27/20261278_8cl5xrXvWQkoz0UAFgiI.png" alt="Pasted image 20250928153941.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;h4&gt;1.1. 카탈로그 계층 (Catalog Layer)&lt;/h4&gt;&lt;p&gt;모든 쿼리의 시작점입니다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;읽기&lt;/strong&gt;: 엔진은 카탈로그를 통해 테이블의 현재 상태, 즉 최신 메타데이터 파일의 위치를 파악합니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;쓰기&lt;/strong&gt;: 엔진은 카탈로그를 조회하여 테이블의 스키마와 파티셔닝 전략을 확인하고, 작업 완료 후 새 메타데이터 파일의 위치를 원자적으로(atomically) 업데이트하여 커밋을 완료합니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;1.2. 메타데이터 계층 (Metadata Layer)&lt;/h4&gt;&lt;p&gt;성능 최적화의 핵심입니다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;계층적 필터링&lt;/strong&gt;: 이 계층은 &lt;strong&gt;메타데이터 파일(metadata file), 매니페스트 리스트(manifest list), 매니페스트 파일(manifest file)&lt;/strong&gt;로 구성됩니다. 각 단계는 다음 단계에서 스캔할 파일의 범위를 좁히기 위한 통계 정보를 담고 있습니다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;File Pruning&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;엔진은 먼저 &lt;strong&gt;매니페스트 리스트&lt;/strong&gt;의 파티션 경계값 통계를 이용해 관련 없는 &lt;strong&gt;매니페스트 파일&lt;/strong&gt;들을 건너뜁니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;그다음, 남은 &lt;strong&gt;매니페스트 파일&lt;/strong&gt;을 열어 개별 &lt;strong&gt;데이터 파일&lt;/strong&gt;의 컬럼별 통계(최대/최소값, null 개수 등)를 확인하고, 쿼리 조건에 맞지 않는 데이터 파일들을 건너뜁니다. 이 과정을 통해 불필요한 I/O를 최소화합니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;1.3. 데이터 계층 (Data Layer)&lt;/h4&gt;&lt;p&gt;실제 데이터가 저장되는 곳입니다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;읽기&lt;/strong&gt;: 엔진은 메타데이터 계층에서 필터링된 데이터 파일들만 스캔하여 결과를 반환합니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;쓰기&lt;/strong&gt;: 새로운 데이터 파일이 파일 스토리지에 생성되고, 이와 관련된 메타데이터 파일들이 업데이트됩니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr&gt;&lt;h2&gt;2. 쓰기 쿼리의 생명주기 (Writing Queries in Apache Iceberg)&lt;/h2&gt;&lt;p&gt;쓰기 프로세스는 여러 단계에 걸쳐 데이터 파일과 메타데이터 파일을 생성하고, 마지막에 카탈로그를 원자적으로 업데이트하여 트랜잭션을 완료합니다.&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/27/20261278_7sARqD9xu64d40d5FHNx.png" alt="Pasted image 20250928154000.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;h4&gt;2.1. 테이블 생성 (&lt;code&gt;CREATE TABLE&lt;/code&gt;)&lt;/h4&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;# Spark SQL
CREATE TABLE orders (
    order_id BIGINT,
    customer_id BIGINT,
    order_amount DECIMAL(10, 2),
    order_ts TIMESTAMP
)
USING iceberg
PARTITIONED BY (HOUR(order_ts))&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;2.1.1. 1단계 (쿼리를 엔진으로 전송)&lt;/h4&gt;&lt;p&gt;쿼리 엔진이 &lt;code&gt;CREATE TABLE&lt;/code&gt; 문을 파싱합니다.&lt;/p&gt;&lt;h4&gt;2.1.2. 2단계 (메타데이터 파일 생성)&lt;/h4&gt;&lt;p&gt;엔진은 테이블 스키마, 파티션 명세, 고유 식별자(table-uuid) 등의 정보를 담은 &lt;strong&gt;첫 번째 메타데이터 파일(&lt;/strong&gt;&lt;code&gt;v1.metadata.json&lt;/code&gt;&lt;strong&gt;)&lt;/strong&gt;을 데이터 레이크 파일 시스템에 생성합니다. 이 시점에는 데이터가 없으므로 데이터 파일이나 매니페스트 파일은 생성되지 않습니다.&lt;/p&gt;&lt;h4&gt;2.1.3. 3단계 (카탈로그 업데이트)&lt;/h4&gt;&lt;p&gt;카탈로그의 현재 메타데이터 포인터가 &lt;code&gt;v1.metadata.json&lt;/code&gt;을 가리키도록 업데이트하여 커밋을 완료합니다.&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/27/20261278_WO6YYqTpNwp3cBrVTuaO.png" alt="Pasted image 20250928154057.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;hr&gt;&lt;h4&gt;2.2. 데이터 삽입 (&lt;code&gt;INSERT INTO&lt;/code&gt;)&lt;/h4&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;# Spark SQL
INSERT INTO orders VALUES (
    123,
    456,
    36.17,
    '2023-03-07 08:10:23'
)&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;2.2.1. 1단계 (쿼리를 엔진으로 전송)&lt;/h4&gt;&lt;p&gt;INSERT문이므로 query planning을 시작하기 위해 스키마 등의 테이블 정보가 필요합니다.&lt;/p&gt;&lt;h4&gt;2.2.2. 2단계 (카탈로그 조회)&lt;/h4&gt;&lt;p&gt;엔진은 카탈로그에서 현재 메타데이터 파일(&lt;code&gt;v1.metadata.json&lt;/code&gt;)의 위치를 확인하여 테이블 스키마와 파티셔닝 구성을 파악합니다.&lt;/p&gt;&lt;h4&gt;2.2.3. 3단계 (데이터 파일과 메타데이터 파일 생성)&lt;/h4&gt;&lt;p&gt;파티셔닝 전략에 따라 데이터를 &lt;strong&gt;Parquet 파일&lt;/strong&gt;로 저장합니다. 정렬 순서가 정의되어 있으면 데이터 파일에 쓰기 전에 레코드 정렬합니다.&lt;/p&gt;&lt;p&gt;새 데이터 파일의 경로와 컬럼별 통계 정보(최대/최소값 등)를 포함하는 &lt;strong&gt;매니페스트 파일&lt;/strong&gt;을 생성합니다.&lt;/p&gt;&lt;p&gt;생성된 매니페스트 파일을 추적하는 &lt;strong&gt;매니페스트 리스트&lt;/strong&gt; 파일을 만듭니다.&lt;/p&gt;&lt;p&gt;이전 메타데이터 파일(&lt;code&gt;v1&lt;/code&gt;)을 기반으로 새로운 스냅샷 정보를 추가한 &lt;strong&gt;새 메타데이터 파일(&lt;/strong&gt;&lt;code&gt;v2.metadata.json&lt;/code&gt;&lt;strong&gt;)&lt;/strong&gt;을 생성합니다.&lt;/p&gt;&lt;h4&gt;2.2.4. 4단계 (원자적 커밋)&lt;/h4&gt;&lt;p&gt;엔진은 카탈로그에 다시 접근하여 다른 쓰기 작업과의 충돌이 없는지 확인한 후, 메타데이터 포인터를 &lt;code&gt;v2.metadata.json&lt;/code&gt;으로 원자적으로 업데이트합니다. 이를 통해 트랜잭션의 일관성을 보장합니다.&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/27/20261278_iHwN48D2xHt2PXicJKbr.png" alt="Pasted image 20250928154136.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;hr&gt;&lt;h4&gt;2.3. 데이터 병합 (&lt;code&gt;MERGE INTO&lt;/code&gt;)&lt;/h4&gt;&lt;p&gt;&lt;code&gt;MERGE INTO&lt;/code&gt;는 조건에 따라 기존 행을 업데이트하거나 새 행을 삽입하는 작업입니다.&lt;/p&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;# Spark SQL
MERGE INTO orders o
USING (SELECT * FROM orders_staging) s
ON o.order_id = s.order_id
WHEN MATCHED THEN UPDATE SET order_amount = s.order_amount
WHEN NOT MATCHED THEN INSERT *;&lt;/code&gt;&lt;/pre&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/27/20261278_BZ9oBFihCWuRdTCzC1KG.png" alt="Pasted image 20260126050306.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;h4&gt;2.3.1. 1단계 (쿼리를 엔진으로 전송)&lt;/h4&gt;&lt;p&gt;query planning을 위해 두 테이블의 데이터가 필요합니다.&lt;/p&gt;&lt;h4&gt;2.3.2. 2단계 (카탈로그 조회)&lt;/h4&gt;&lt;p&gt;현재 메타데이터 파일(&lt;code&gt;v2.metadata.json&lt;/code&gt;)의 위치를 확인하여 테이블 스키마와 파티셔닝 구성을 파악합니다.&lt;/p&gt;&lt;h4&gt;2.3.3. 3단계 (데이터 파일과 메타데이터 파일 생성)&lt;/h4&gt;&lt;p&gt;엔진은 소스 테이블과 대상 테이블의 데이터를 메모리에 로드하여 일치하는 레코드를 찾습니다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;쓰기 전략&lt;/strong&gt; (Copy-on-Write (COW), Merge-on-Read (MOR)):&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;COW&lt;/strong&gt;: 테이블 업데이트 시 관련 데이터 파일을 새 데이터 파일로 재작성&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;MOR&lt;/strong&gt;: 데이터 파일을 재작성하지 않고 새로운 Delete File을 생성하여 변경사항 추적&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;COW 전략 예시&lt;/strong&gt;:&lt;/p&gt;&lt;p&gt;매칭되는 데이터가 있는 경우:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;../order_ts_hour=2023-03-07-08/0_0_0.parquet&lt;/code&gt; 파일을 메모리로 읽음&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;메모리에서 order_amount 업데이트&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;수정된 데이터를 새 parquet 파일(&lt;code&gt;../order_ts_hour=2023-03-07-08/0_0_0_1.parquet&lt;/code&gt;)로 작성&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;매칭되지 않는 데이터가 있는 경우:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;매칭되지 않은 레코드는 일반 INSERT로 처리&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;다른 파티션에 새 데이터 파일(&lt;code&gt;../order_ts_hour=2023-01-27-10/0_0_0.parquet&lt;/code&gt;)로 작성&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;2.3.4. 4단계 (카탈로그 파일 업데이트)&lt;/h4&gt;&lt;p&gt;&lt;code&gt;INSERT&lt;/code&gt;와 동일하게 새 매니페스트 파일, 매니페스트 리스트, 새 메타데이터 파일(&lt;code&gt;v3.metadata.json&lt;/code&gt;)을 순차적으로 생성한 후 카탈로그 포인터를 원자적으로 업데이트하여 작업을 완료합니다.&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/27/20261278_hXyFmiDgzpawXRvaKu1C.png" alt="Pasted image 20250928154156.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;hr&gt;&lt;h4&gt;2.4. 동시성 제어와 원자적 커밋&lt;/h4&gt;&lt;p&gt;앞서 살펴본 쓰기 쿼리들(CREATE, INSERT, MERGE)에서 공통적으로 마지막 단계는 "카탈로그 파일을 업데이트하여 변경사항 커밋"이었습니다. 이 과정이 단순해 보일 수 있지만, 실제로는 Apache Iceberg의 가장 핵심적인 메커니즘인 &lt;strong&gt;원자적 커밋&lt;/strong&gt;과 &lt;strong&gt;동시성 제어&lt;/strong&gt;가 작동하는 중요한 순간입니다.&lt;/p&gt;&lt;p&gt;실제 운영 환경에서는 여러 사용자나 프로세스가 동시에 같은 테이블에 데이터를 쓰려고 할 수 있습니다. Apache Iceberg는 이러한 동시 쓰기 상황에서도 데이터 무결성을 보장하고 모든 변경사항이 손실 없이 반영되도록 &lt;strong&gt;낙관적 동시성 제어(Optimistic Concurrency Control, OCC)&lt;/strong&gt; 메커니즘을 사용합니다.&lt;/p&gt;&lt;h4&gt;2.4.1. 낙관적 동시성 제어(Optimistic Concurrency Control)란?&lt;/h4&gt;&lt;p&gt;낙관적 동시성 제어는 "기본적으로 트랜잭션들이 서로 충돌하지 않을 것"이라고 가정하고 작업을 진행합니다. 각 쓰기 작업은 다른 작업의 간섭 없이 독립적으로 진행되며, &lt;strong&gt;마지막 커밋 단계에서만&lt;/strong&gt; 충돌이 있었는지 검사합니다. 충돌이 감지되면 해당 작업은 실패하고 재시도됩니다.&lt;/p&gt;&lt;p&gt;이는 &lt;strong&gt;비관적 동시성 제어(Pessimistic Concurrency Control)&lt;/strong&gt;와 대비됩니다. 비관적 방식은 작업 시작 전에 락(lock)을 획득하여 다른 작업이 접근하지 못하도록 막지만, 낙관적 방식은 락 없이 진행하다가 커밋 시점에만 검증합니다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;장점&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;락을 획득하고 유지하는 오버헤드가 없어 대부분의 경우 더 빠름&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;읽기 작업이 쓰기 작업에 의해 블로킹되지 않음&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;충돌이 드문 환경에서 특히 효율적&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;단점&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;충돌이 발생하면 재시도해야 하므로 충돌이 빈번한 경우 비효율적일 수 있음&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;2.4.2. 동시 쓰기 시나리오: 실제 예제&lt;/h4&gt;&lt;p&gt;두 개의 MERGE 쿼리가 거의 동시에 같은 테이블에 실행되는 상황을 단계별로 살펴보겠습니다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;초기 상태&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;테이블의 현재 메타데이터: &lt;code&gt;v3.metadata.json&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;쓰기 작업 A: 고객 456의 주문 금액을 업데이트&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;쓰기 작업 B: 고객 789의 새로운 주문 삽입&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;2.4.3. 1단계: 현재 메타데이터 확인&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;쓰기 A&lt;/strong&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-plaintext"&gt;카탈로그 확인 → 현재 메타데이터: v3.metadata.json
기준 버전: v3&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;쓰기 B&lt;/strong&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-plaintext"&gt;카탈로그 확인 → 현재 메타데이터: v3.metadata.json
기준 버전: v3&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;두 작업 모두 &lt;code&gt;v3&lt;/code&gt;를 기준으로 작업을 시작합니다. 이 시점에는 서로의 존재를 모릅니다.&lt;/p&gt;&lt;h4&gt;2.4.4. 2단계: 데이터 및 메타데이터 파일 생성 (낙관적 작업)&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;쓰기 A&lt;/strong&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-plaintext"&gt;1. 데이터 파일 작성: 0_0_5.parquet (고객 456 업데이트)
2. 매니페스트 파일 생성: manifest-A.avro
3. 매니페스트 리스트 생성: snap-A.avro
4. 새 메타데이터 파일 생성: v4_A.metadata.json (v3 기반)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;쓰기 B&lt;/strong&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-plaintext"&gt;1. 데이터 파일 작성: 0_0_6.parquet (고객 789 신규)
2. 매니페스트 파일 생성: manifest-B.avro
3. 매니페스트 리스트 생성: snap-B.avro
4. 새 메타데이터 파일 생성: v4_B.metadata.json (v3 기반)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;중요&lt;/strong&gt;: 이 단계에서는 파일들이 파일 시스템에 작성되었을 뿐, &lt;strong&gt;아직 테이블에 공식적으로 반영되지 않았습니다&lt;/strong&gt;. 두 작업 모두 &lt;code&gt;v3&lt;/code&gt;를 기준으로 자신만의 메타데이터를 생성했지만, 카탈로그는 여전히 &lt;code&gt;v3&lt;/code&gt;를 가리키고 있습니다.&lt;/p&gt;&lt;h4&gt;2.4.5. 3단계: 커밋 시도 - 쓰기 A 성공&lt;/h4&gt;&lt;p&gt;쓰기 A가 먼저 카탈로그에 커밋을 시도합니다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Compare-And-Swap (CAS) 연산&lt;/strong&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-plaintext"&gt;조건: 현재 메타데이터가 v3.metadata.json인가?
→ 예 (v3가 맞음)
→ 포인터를 v4_A.metadata.json으로 변경
→ 커밋 성공!&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;결과&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;테이블의 현재 메타데이터: &lt;code&gt;v3.metadata.json&lt;/code&gt; → &lt;code&gt;v4_A.metadata.json&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;쓰기 A의 변경사항이 공식적으로 테이블에 반영됨&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;2.4.6. 4단계: 커밋 시도 - 쓰기 B 실패&lt;/h4&gt;&lt;p&gt;직후에 쓰기 B가 카탈로그에 커밋을 시도합니다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Compare-And-Swap (CAS) 연산&lt;/strong&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-plaintext"&gt;조건: 현재 메타데이터가 v3.metadata.json인가?
→ 아니오 (현재는 v4_A.metadata.json)
→ 조건 불일치!
→ 커밋 실패&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;실패 이유&lt;/strong&gt;: 쓰기 B가 작업을 시작했을 때는 메타데이터가 &lt;code&gt;v3&lt;/code&gt;였지만, 커밋을 시도하는 시점에는 이미 쓰기 A가 &lt;code&gt;v4_A&lt;/code&gt;로 변경했습니다. 이는 쓰기 B가 작업한 기준 상태가 더 이상 최신이 아니라는 뜻입니다.&lt;/p&gt;&lt;h4&gt;2.4.7. 5단계: 쓰기 B 재시도&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;쓰기 B의 재시도 과정&lt;/strong&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-plaintext"&gt;1. 카탈로그 재확인 → 현재 메타데이터: v4_A.metadata.json
2. 새로운 기준 버전: v4_A
3. v4_A를 기반으로 작업 재수행:
   - 쓰기 A의 변경사항 포함된 상태에서 MERGE 로직 재실행
   - 새 데이터 파일: 0_0_7.parquet (필요시)
   - 새 매니페스트 파일 및 리스트 생성
   - 새 메타데이터 파일 생성: v5.metadata.json (v4_A 기반)
4. 커밋 재시도:
   조건: 현재 메타데이터가 v4_A.metadata.json인가?
   → 예 (v4_A가 맞음)
   → 포인터를 v5.metadata.json으로 변경
   → 커밋 성공!&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;최종 결과&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;테이블의 현재 메타데이터: &lt;code&gt;v5.metadata.json&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;쓰기 A와 쓰기 B의 변경사항이 모두 선형적 이력으로 반영됨&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;메타데이터 이력: &lt;code&gt;v3&lt;/code&gt; → &lt;code&gt;v4_A&lt;/code&gt; (쓰기 A) → &lt;code&gt;v5&lt;/code&gt; (쓰기 B)&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;2.4.8. Compare-And-Swap (CAS) 연산의 원자성&lt;/h4&gt;&lt;p&gt;CAS 연산은 다음 두 단계를 &lt;strong&gt;원자적으로(atomically)&lt;/strong&gt; 수행합니다:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compare (비교)&lt;/strong&gt;: 현재 값이 예상한 값(expected value)과 같은지 확인&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Swap (교체)&lt;/strong&gt;: 같다면 새로운 값으로 교체&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;이 두 단계 사이에 다른 작업이 끼어들 수 없기 때문에, 여러 작업이 동시에 커밋을 시도해도 정확히 하나만 성공하고 나머지는 실패합니다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;카탈로그별 CAS 구현&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hadoop Catalog&lt;/strong&gt;: 파일 시스템의 원자적 rename 연산 활용&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hive Metastore&lt;/strong&gt;: Hive의 트랜잭션 메커니즘 활용&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;AWS Glue&lt;/strong&gt;: Glue의 optimistic locking 기능 활용&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Nessie/REST Catalog&lt;/strong&gt;: 버전 관리 시스템의 commit 메커니즘 활용&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;각 카탈로그 구현체는 자신의 저장소가 제공하는 원자적 연산을 활용하여 CAS를 보장합니다.&lt;/p&gt;&lt;h4&gt;2.4.9. 왜 낙관적 동시성 제어를 사용하는가?&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;1. 성능 최적화&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;락(lock)을 획득하고 유지할 필요가 없어 대부분의 경우 더 빠름&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;쓰기 작업이 서로를 블로킹하지 않음&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;2. 읽기 성능 보장&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;읽기 작업은 쓰기 락의 영향을 받지 않음&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;읽기와 쓰기가 서로 방해하지 않음&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;3. 분산 환경 적합성&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;데이터 레이크는 분산 스토리지 환경&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;중앙 집중식 락 관리가 어렵고 비효율적&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;낙관적 방식이 분산 환경에 더 적합&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;4. 충돌이 드문 실제 환경&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;실무에서 정확히 같은 시점에 같은 테이블에 쓰기가 발생하는 경우는 드묾&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;대부분의 경우 재시도 없이 한 번에 성공&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;2.4.10. 선형 이력(Linear History)의 중요성&lt;/h4&gt;&lt;p&gt;낙관적 동시성 제어 덕분에 아이스버그는 &lt;strong&gt;모든 변경사항이 선형적인 이력으로 기록&lt;/strong&gt;됩니다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;선형 이력이 보장하는 것&lt;/strong&gt;:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;추적 가능성&lt;/strong&gt;: 모든 변경사항의 순서를 정확히 파악 가능&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Time Travel&lt;/strong&gt;: 특정 시점의 데이터 상태로 정확히 되돌아갈 수 있음&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;감사(Audit)&lt;/strong&gt;: 누가, 언제, 무엇을 변경했는지 추적 가능&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;디버깅&lt;/strong&gt;: 문제 발생 시 변경 이력을 따라가며 원인 파악 가능&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;선형 이력 예시&lt;/strong&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-plaintext"&gt;v1.metadata.json (CREATE TABLE)
  ↓
v2.metadata.json (INSERT - 사용자 A)
  ↓
v3.metadata.json (MERGE - 사용자 B)
  ↓
v4.metadata.json (UPDATE - 사용자 A)
  ↓
v5.metadata.json (DELETE - 사용자 C)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;각 메타데이터 파일은 이전 상태(parent)를 참조하므로, 전체 이력을 재구성할 수 있습니다.&lt;/p&gt;&lt;h4&gt;2.4.11. 요약&lt;/h4&gt;&lt;p&gt;Apache Iceberg의 낙관적 동시성 제어는 다음과 같이 작동합니다:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;낙관적 작업&lt;/strong&gt;: 각 쓰기 작업은 현재 메타데이터를 기준으로 독립적으로 새 파일들을 생성&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;원자적 검증&lt;/strong&gt;: 커밋 시점에 Compare-And-Swap 연산으로 메타데이터 버전 확인&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;충돌 처리&lt;/strong&gt;: 버전이 변경되었다면 커밋 실패 후 최신 버전 기준으로 재시도&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;선형 이력 보장&lt;/strong&gt;: 모든 변경사항이 순차적으로 기록되어 데이터 무결성 유지&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;이러한 메커니즘 덕분에 여러 쓰기 작업이 동시에 발생하더라도 &lt;strong&gt;데이터 손실이나 덮어쓰기 없이 모든 변경사항이 안전하게 반영&lt;/strong&gt;됩니다. 이것이 바로 Apache Iceberg가 데이터 레이크에서 ACID 트랜잭션을 보장하는 핵심 원리입니다.&lt;/p&gt;&lt;hr&gt;&lt;h2&gt;3. 읽기 쿼리의 생명주기 (Reading Queries in Apache Iceberg)&lt;/h2&gt;&lt;p&gt;읽기 쿼리는 계층적 메타데이터를 활용하여 스캔해야 할 데이터 파일의 수를 최소화함으로써 높은 성능을 달성합니다.&lt;/p&gt;&lt;h4&gt;3.1. 일반 조회 (&lt;code&gt;SELECT&lt;/code&gt;)&lt;/h4&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;# Spark SQL/Dremio Sonar
SELECT *
FROM orders
WHERE order_ts BETWEEN '2023-01-01' AND '2023-01-31'&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;3.1.1. 1단계: 쿼리를 엔진으로 전송&lt;/h4&gt;&lt;p&gt;엔진이 메타데이터 파일을 기반으로 쿼리 계획을 시작합니다.&lt;/p&gt;&lt;h4&gt;3.1.2. 2단계: 카탈로그 조회&lt;/h4&gt;&lt;p&gt;현재 메타데이터 파일 경로를 요청합니다.&lt;/p&gt;&lt;p&gt;현재 메타데이터 파일: &lt;code&gt;/orders/metadata/v3.metadata.json&lt;/code&gt;&lt;/p&gt;&lt;h4&gt;3.1.3. 3단계: 메타데이터 파일 로드&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;테이블 스키마 확인 (내부 메모리 구조 준비)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;파티셔닝 방식 확인 (데이터 구성 방식 이해)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;current-snapshot-id 확인 (테이블의 현재 상태)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;현재 스냅샷 기준으로 매니페스트 리스트 파일 경로 찾기&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;3.1.4. 4단계: 매니페스트 리스트에서 정보 가져오기&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;snap-*.avro&lt;/code&gt; 파일 읽기&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;중요 정보:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;실제 데이터 파일 참조를 포함하는 매니페스트 파일 경로&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;추가/삭제된 데이터 파일 수, 파티션 통계 정보&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;partition-spec-id: 특정 스냅샷 작성에 사용된 파티션 방식&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;파티션별 통계: 매니페스트의 파티션 컬럼 상한/하한값&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;파일 프루닝을 위해 어떤 매니페스트 파일을 건너뛸지 결정&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;3.1.5. 5단계: 매니페스트 파일에서 정보 가져오기&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;프루닝되지 않은(쿼리와 관련된) 매니페스트 파일 열기&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;각 항목은 이 매니페스트 파일이 추적하는 데이터 파일을 나타냄&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;각 데이터 파일의 파티션 값을 쿼리 필터 값과 비교&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;파티션 값이 필터 값 범위와 일치하지 않으면 무시&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;통계 정보 수집: 각 컬럼의 상한/하한값, null 값 개수 등&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;관련없는 파일 건너뛰기&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;3.1.6. 6단계: 최종 결과 반환&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;파티셔닝 및 메트릭 기반 필터링(컬럼의 상한/하한값) 같은 최적화 기법으로 전체 테이블 스캔 방지&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;필요한 데이터 파일 읽기&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;사용자에게 레코드 반환&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/27/20261278_N86CTJD733N3bAJuoGY7.png" alt="Pasted image 20250928154257.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;hr&gt;&lt;h4&gt;3.2. 타임 트래블 쿼리 (Time-Travel Query)&lt;/h4&gt;&lt;p&gt;데이터베이스와 데이터 웨어하우스 세계에서 중요한 기능은 테이블의 특정 시점으로 돌아가 과거 데이터(변경되거나 삭제된 데이터)를 쿼리할 수 있는 능력입니다. Apache Iceberg는 데이터 레이크하우스 아키텍처에 유사한 time-travel 기능을 제공합니다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;활용 사례&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;이전 분기 데이터 분석&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;실수로 삭제된 행 복원&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;분석 결과 재현&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Time-Travel 방법&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;타임스탬프 사용&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;snapshot id 사용&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;3.2.1. 테이블 히스토리 확인&lt;/h4&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;# Spark SQL
SELECT * FROM catalog.db.orders.history;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;결과 예시&lt;/strong&gt;:&lt;/p&gt;&lt;table style="min-width: 100px;"&gt;&lt;colgroup&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;col style="min-width: 25px;"&gt;&lt;/colgroup&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;made_current_at&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;snapshot_id&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;parent_id&lt;/p&gt;&lt;/th&gt;&lt;th colspan="1" rowspan="1"&gt;&lt;p&gt;is_current_ancestor&lt;/p&gt;&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;2023-03-06 21:28:35.360&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;7327164675870333694&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;null&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;true&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;2023-03-07 20:45:08.914&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;8333017788700497002&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;7327164675870333694&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;true&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;2023-03-09 19:58:40.448&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;5139476312242609518&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;8333017788700497002&lt;/p&gt;&lt;/td&gt;&lt;td colspan="1" rowspan="1"&gt;&lt;p&gt;true&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&lt;strong&gt;스냅샷 설명&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;첫 번째 스냅샷: CREATE 문 실행 후 생성&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;두 번째 스냅샷: INSERT 문으로 새 레코드 삽입 후 생성&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;세 번째 스냅샷: MERGE INTO 쿼리로 생성&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;3.2.2. Time-Travel 쿼리 예시&lt;/h4&gt;&lt;pre&gt;&lt;code class="language-sql"&gt;# 타임스탬프 사용 (Spark SQL)
SELECT * FROM orders
TIMESTAMP AS OF '2023-03-07 20:45:08.914'

# 스냅샷 ID 사용 (Spark SQL)
SELECT *
FROM orders
VERSION AS OF 8333017788700497002&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;중요 사항&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;정확한 타임스탬프 값을 제공하지 않으면 Iceberg는 지정된 값보다 오래된 스냅샷을 찾아 결과 반환&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;오래된 스냅샷이 없으면 예외 발생&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;3.2.3. 처리 과정&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;1. 쿼리를 엔진으로 전송&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;모든 SELECT 문과 마찬가지로 엔진으로 전송되어 파싱&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;테이블 메타데이터를 활용하여 쿼리 계획 시작&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;2. 카탈로그 확인&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;현재 메타데이터 파일 위치 요청&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;현재 메타데이터 파일 읽기 (&lt;code&gt;v3.metadata.json&lt;/code&gt;)&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;3. 메타데이터 파일에서 정보 가져오기&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;현재 메타데이터 파일은 Iceberg 테이블에 대해 생성된 모든 스냅샷 추적 (메타데이터 유지 전략의 일부로 의도적으로 만료되지 않은 경우)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;사용 가능한 스냅샷 목록에서 time-travel 쿼리에 지정된 특정 스냅샷 결정 (타임스탬프 값 또는 스냅샷 ID 기반)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;테이블 스키마 및 파티셔닝 방식 확인 (파일 프루닝에 사용)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;해당 스냅샷의 매니페스트 리스트 경로 가져오기&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;4. 매니페스트 리스트에서 정보 가져오기&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;매니페스트 리스트 경로 기반으로 &lt;code&gt;.avro&lt;/code&gt; 파일 열기&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;중요 정보:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;실제 데이터 파일 참조를 포함하는 매니페스트 파일 경로&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;추가/삭제된 데이터 파일 수, 파티션 통계 정보&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;5. 매니페스트 파일에서 정보 가져오기&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;쿼리와 일치하는 매니페스트 파일 읽기&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;데이터 파일 경로 확인 (쿼리 레코드가 있는 파일 경로)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;각 데이터 파일을 확인하여 읽을지 여부 결정&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;통계 정보 수집&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;6. 최종 결과 반환&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;해당 데이터 파일 읽기&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;사용자에게 결과 반환&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/27/20261278_Bp8fHz5ymW4pS2rM5rHh.png" alt="Pasted image 20250928154310.png" style="object-fit: cover;"&gt;&lt;/figure&gt;</description><pubDate>Tue, 27 Jan 2026 08:28:40 +0900</pubDate><guid>http://blex.me/@mildsalmon/apache-iceberg-the-definitive-guide-lifecycle-of</guid></item><item><title>[Apache Iceberg - The Definitive Guide] The Architecture of Apache Iceberg</title><link>http://blex.me/@mildsalmon/apache-icebergthe-definitive-guide-the-architecture-of-apache-iceberg</link><description>&lt;h2&gt;Apache Iceberg 완벽 가이드: 2장 - 아파치 아이스버그 아키텍처&lt;/h2&gt;&lt;p&gt;이 장에서는 아파치 아이스버그 테이블의 아키텍처와 사양에 대해 심층적으로 다루며, 하이브(Hive) 테이블 형식의 내재된 문제들을 어떻게 해결하는지 설명합니다. 아이스버그 테이블은 세 가지 계층으로 구성됩니다: &lt;strong&gt;카탈로그 계층&lt;/strong&gt;, &lt;strong&gt;메타데이터 계층&lt;/strong&gt;, 그리고 &lt;strong&gt;데이터 계층&lt;/strong&gt;입니다.&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/25/202612515_7IPsIqqgwjsAdcQHQoZ8.png" alt="Pasted image 20250928152346.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/25/202612516_n9m3XxZTP75AF6bOzO4b.png" alt="Pasted image 20260125160148.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;hr&gt;&lt;h2&gt;1. 데이터 계층 (The Data Layer)&lt;/h2&gt;&lt;p&gt;아파치 아이스버그 테이블의 데이터 계층은 &lt;strong&gt;실제 데이터를 저장하는 곳&lt;/strong&gt;이며, 주로 &lt;strong&gt;데이터 파일(datafiles)&lt;/strong&gt;과 &lt;strong&gt;삭제 파일(delete files)&lt;/strong&gt;로 구성됩니다. 이 계층은 사용자 쿼리에 필요한 데이터를 제공하며, 아이스버그 테이블 트리 구조의 &lt;strong&gt;리프(leaves)&lt;/strong&gt;를 이룹니다.&lt;/p&gt;&lt;h4&gt;1.1. 데이터 파일 (Datafiles)&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;주요 포맷&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Apache Parquet, Apache ORC&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;특징&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;데이터는 &lt;strong&gt;컬럼 지향(columnar)&lt;/strong&gt;으로 저장되어 대량의 레코드를 처리하는 쿼리에 효율적입니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;각 파일은 여러 &lt;strong&gt;로우 그룹(row groups)&lt;/strong&gt;을 가질 수 있으며, 각 로우 그룹은 컬럼별로 페이지(pages)로 나뉘어 저장됩니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;파일 내 통계 정보(예: 컬럼별 최소/최대 값)를 저장하여 쿼리 엔진이 불필요한 로우 그룹을 건너뛸 수 있도록 돕습니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/25/202612516_yffPkxgNsKYBX2cuNEIv.png" alt="Pasted image 20250928152412.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;hr&gt;&lt;h4&gt;1.2. 삭제 파일 (Delete Files)&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;주요 역할&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;데이터셋에서 논리적으로 삭제된 레코드를 추적합니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;데이터 레이크 저장소의 &lt;strong&gt;불변성(immutability)&lt;/strong&gt; 원칙을 유지하기 위해, 파일을 직접 업데이트하는 대신 새로운 파일을 작성하는 방식(Copy-on-Write, Merge-on-Read)을 사용합니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Merge-on-Read (MOR)&lt;/strong&gt; 전략을 사용하여 업데이트 및 삭제 작업을 수행할 때 사용됩니다. (Iceberg v2 형식에서만 지원됩니다).&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/25/202612516_m9ybLLglyx3iqinIsmjl.png" alt="Pasted image 20250928152429.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;h4&gt;1.2.1. 위치 삭제 파일 (Positional Delete Files)&lt;/h4&gt;&lt;p&gt;레코드의 정확한 위치(파일 경로 및 파일 내 레코드 번호)를 식별하여 삭제합니다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;특징&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;읽기 시 비용이 적습니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;쓰기 시 삭제 레코드의 위치를 파악하기 위해 파일을 읽어야 하는 비용이 발생합니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/25/202612516_p0CELIjUWSMu2lxlXmCv.png" alt="Pasted image 20250928152607.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;h4&gt;1.2.2. 등가 삭제 파일 (Equality Delete Files)&lt;/h4&gt;&lt;p&gt;레코드의 하나 이상의 필드 값으로 레코드를 식별하여 삭제합니다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;특징&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;일반적으로 고유 식별자(기본 키)가 있는 경우에 적합합니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;쓰기 시 비용이 없습니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;읽기 시 모든 레코드를 비교해야 하므로 읽기 시 비용이 더 큽니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/25/202612516_mmMi6u7mnhyJHEBZN8Gy.png" alt="Pasted image 20250928152634.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;h4&gt;1.2.3. 시퀀스 번호 활용&lt;/h4&gt;&lt;p&gt;삭제 파일이 적용될 때 시퀀스 번호(sequence numbers)를 사용하여 새로 추가된 레코드가 삭제 목록에 잘못 포함되지 않도록 보장합니다.&lt;/p&gt;&lt;hr&gt;&lt;h2&gt;2. 메타데이터 계층 (The Metadata Layer)&lt;/h2&gt;&lt;p&gt;메타데이터 계층은 아이스버그 테이블 아키텍처의 필수적인 부분으로, 테이블의 데이터 파일 및 관련 작업에 대한 모든 메타데이터 파일을 포함하는 &lt;strong&gt;트리 구조&lt;/strong&gt;입니다. 이 계층은 대규모 데이터셋을 효율적으로 관리하고 &lt;strong&gt;시간 여행(time travel)&lt;/strong&gt; 및 &lt;strong&gt;스키마 진화(schema evolution)&lt;/strong&gt;와 같은 핵심 기능을 가능하게 합니다.&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/25/202612516_yqaTtJafrb7IYabhxorA.png" alt="Pasted image 20260125154829.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;h4&gt;2.1. 매니페스트 파일 (Manifest Files)&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;주요 역할&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;데이터 계층의 파일(데이터 파일 및 삭제 파일)과 각 파일에 대한 추가 세부 정보 및 통계(예: 컬럼의 최소/최대 값)를 추적합니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;각 파일이 기록될 때 통계가 수집되므로, 하이브(Hive)와 달리 &lt;strong&gt;경량화된 통계 수집&lt;/strong&gt;이 가능하며 쿼리 성능에 더 나은 최신 정보를 제공합니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;매니페스트 파일은 &lt;strong&gt;Avro 형식&lt;/strong&gt;으로 작성됩니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr&gt;&lt;h4&gt;2.2. 매니페스트 목록 (Manifest Lists)&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;주요 역할&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;특정 시점의 아이스버그 테이블의 &lt;strong&gt;스냅샷(snapshot)&lt;/strong&gt;입니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;해당 시점의 테이블에 대한 모든 매니페스트 파일 목록과 파티션 컬럼의 상한 및 하한과 같은 매니페스트에 대한 통계를 포함합니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;매니페스트 목록은 &lt;strong&gt;Avro 형식&lt;/strong&gt;으로 작성됩니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr&gt;&lt;h4&gt;2.3. 메타데이터 파일 (Metadata Files)&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;주요 역할&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;특정 시점의 아이스버그 테이블에 대한 메타데이터를 저장합니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;여기에는 테이블의 &lt;strong&gt;스키마&lt;/strong&gt;, &lt;strong&gt;파티션 정보&lt;/strong&gt;, &lt;strong&gt;스냅샷&lt;/strong&gt;, 그리고 &lt;strong&gt;현재 스냅샷&lt;/strong&gt;이 무엇인지에 대한 정보가 포함됩니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;핵심 특징&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;아이스버그 테이블에 변경이 있을 때마다 새로운 메타데이터 파일이 생성되고 &lt;strong&gt;원자적으로 최신 버전으로 등록&lt;/strong&gt;됩니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;이는 테이블 커밋의 &lt;strong&gt;선형 기록(linear history)&lt;/strong&gt;을 보장하고 동시 쓰기(concurrent writes) 시나리오를 지원합니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;쿼리 엔진은 항상 테이블의 최신 버전을 볼 수 있습니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr&gt;&lt;h4&gt;2.4. 퍼핀 파일 (Puffin Files)&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;주요 역할&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;데이터 파일 및 메타데이터 파일에 저장된 통계보다 더 고급 구조를 저장하여 특정 유형의 쿼리 성능을 향상시키는 데 사용됩니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;구조&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;파일에는 임의의 바이트 시퀀스인 &lt;strong&gt;블롭(blobs)&lt;/strong&gt; 세트와 블롭을 분석하는 데 필요한 관련 메타데이터가 포함됩니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;현재 지원&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;현재 Apache DataSketches 라이브러리의 &lt;strong&gt;Theta 스케치&lt;/strong&gt;만 지원하며, 이를 통해 컬럼의 근사 고유값 수를 계산하여 자원을 적게 사용하고 빠르게 연산할 수 있습니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr&gt;&lt;h2&gt;3. 카탈로그 계층 (The Catalog)&lt;/h2&gt;&lt;p&gt;카탈로그는 아이스버그에서 테이블의 최신 메타데이터 파일 위치를 추적하는 &lt;strong&gt;중앙 위치&lt;/strong&gt;입니다. 이는 여러 사용자와 도구가 데이터에 효율적으로 상호 작용할 수 있도록 테이블에 대한 추상화(abstraction)를 제공하는 데 중요한 역할을 합니다.&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/25/202612516_8nUhsvOwZYF6diUbQraE.png" alt="Pasted image 20260125155611.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;h4&gt;3.1. 주요 역할&lt;/h4&gt;&lt;p&gt;쿼리 엔진이 테이블의 현재 상태를 알기 위해 가장 먼저 상호 작용하는 구성 요소입니다.&lt;/p&gt;&lt;h4&gt;3.2. 핵심 요구 사항&lt;/h4&gt;&lt;p&gt;현재 메타데이터 포인터를 업데이트하기 위한 &lt;strong&gt;원자적(atomic) 작업&lt;/strong&gt;을 지원해야 합니다. 이는 모든 읽기 및 쓰기 작업이 특정 시점에 테이블의 동일한 상태를 보도록 보장하기 위함입니다.&lt;/p&gt;&lt;h4&gt;3.3. 다양한 백엔드 지원&lt;/h4&gt;&lt;p&gt;Hadoop 카탈로그(예: Amazon S3), Hive Metastore, AWS Glue, Project Nessie, REST, JDBC 등 다양한 시스템이 아이스버그 카탈로그로 사용될 수 있습니다.&lt;/p&gt;&lt;h4&gt;3.4. 저장 방식 차이&lt;/h4&gt;&lt;p&gt;각 카탈로그 구현은 현재 메타데이터 포인터를 다르게 저장합니다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;예시&lt;/strong&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;파일 시스템 기반 카탈로그&lt;/strong&gt;: &lt;code&gt;version-hint.txt&lt;/code&gt; 파일에 현재 메타데이터 파일의 버전 번호를 저장&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hive Metastore&lt;/strong&gt;: 테이블 속성 &lt;code&gt;location&lt;/code&gt;에 현재 메타데이터 파일의 위치를 저장&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr&gt;&lt;p&gt;아파치 아이스버그 아키텍처의 이러한 계층과 구성 요소는 하이브 테이블 형식의 문제점을 해결하고 데이터 레이크에서 ACID 트랜잭션, 시간 여행, 스키마 진화와 같은 고급 기능을 제공할 수 있게 합니다.&lt;/p&gt;&lt;hr&gt;&lt;h2&gt;99. 참고문헌&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;https://www.oreilly.com/library/view/apache-iceberg-the/9781098148614/&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;</description><pubDate>Sun, 25 Jan 2026 16:08:15 +0900</pubDate><guid>http://blex.me/@mildsalmon/apache-icebergthe-definitive-guide-the-architecture-of-apache-iceberg</guid></item><item><title>[Apache Iceberg - The Definitive Guide] Introduction to Apache Iceberg</title><link>http://blex.me/@mildsalmon/apache-icebergthe-definitive-guide-introduction-to</link><description>&lt;h2&gt;Apache Iceberg 완벽 가이드: 1장 - 아파치 아이스버그 소개&lt;/h2&gt;&lt;p&gt;이 장은 &lt;strong&gt;데이터 레이크하우스&lt;/strong&gt;의 역사적 맥락과 &lt;strong&gt;아파치 아이스버그&lt;/strong&gt;의 핵심 개념을 탐구합니다.&lt;/p&gt;&lt;hr&gt;&lt;h2&gt;1. 데이터 아키텍처의 발전 과정&lt;/h2&gt;&lt;h4&gt;1.1. 전통적인 관계형 데이터베이스 관리 시스템 (RDBMS)&lt;/h4&gt;&lt;p&gt;관계형 데이터베이스 관리 시스템(RDBMS)은 오랫동안 트랜잭션 데이터 기록을 위한 표준 옵션이었습니다. 예를 들어, PostgreSQL, MySQL, Microsoft SQL Server는 &lt;strong&gt;온라인 트랜잭션 처리(OLTP)&lt;/strong&gt; 워크로드에 최적화되어 있습니다.&lt;/p&gt;&lt;p&gt;이러한 시스템은 한 번에 하나 또는 소수의 데이터 행과 매우 빠르게 상호 작용하도록 설계 및 최적화되어 비즈니스의 일상적인 운영을 지원하는 데 적합합니다. 하지만 데이터 양이 충분히 커지면 대규모 분석 쿼리(OLAP)를 수행할 때 상당한 성능 문제가 발생할 수 있습니다.&lt;/p&gt;&lt;hr&gt;&lt;h4&gt;1.2. OLAP Workloads&lt;/h4&gt;&lt;p&gt;현대적인 분석(OLAP) 워크로드를 지원하기 위해 설계된 시스템은 크게 6가지 핵심 기술 구성 요소로 이루어져 있습니다. 각 요소의 역할과 특징은 다음과 같습니다.&lt;/p&gt;&lt;h4&gt;1.2.1. 저장소 (Storage)&lt;/h4&gt;&lt;p&gt;대규모 데이터를 저장하기 위한 물리적 계층입니다. 로컬 파일 시스템(DAS), 분산 파일 시스템(HDFS), 또는 클라우드 제공업체의 &lt;strong&gt;객체 저장소(Amazon S3 등)&lt;/strong&gt;가 포함됩니다. 최근에는 방대한 데이터를 다룰 때 효율적인 &lt;strong&gt;컬럼 지향(Columnar) 방식&lt;/strong&gt;이 많이 채택되고 있습니다.&lt;/p&gt;&lt;h4&gt;1.2.2. 파일 포맷 (File Format)&lt;/h4&gt;&lt;p&gt;로우 데이터가 파일 내에 조직되는 방식으로, 압축 방식과 성능에 영향을 미칩니다. CSV, Avro와 같은 &lt;strong&gt;행 지향(Row-oriented)&lt;/strong&gt; 포맷은 적은 수의 레코드 처리에 유리하며, Parquet, ORC와 같은 &lt;strong&gt;열 지향(Column-oriented)&lt;/strong&gt; 포맷은 대규모 데이터 집계 처리에 더 적합합니다.&lt;/p&gt;&lt;h4&gt;1.2.3. 테이블 포맷 (Table Format)&lt;/h4&gt;&lt;p&gt;파일 포맷 위의 메타데이터 계층으로, 수많은 데이터 파일을 하나의 통합된 &lt;strong&gt;'테이블'&lt;/strong&gt;로 인식하게 만듭니다. 물리적 데이터 구조의 복잡성을 추상화하여 &lt;strong&gt;DML 작업(삽입, 수정, 삭제)&lt;/strong&gt;과 스키마 변경을 용이하게 하며, 데이터 작업에 대한 &lt;strong&gt;원자성과 일관성&lt;/strong&gt;을 보장합니다.&lt;/p&gt;&lt;h4&gt;1.2.4. 스토리지 엔진 (Storage Engine)&lt;/h4&gt;&lt;p&gt;테이블 포맷이 지정한 형태대로 데이터를 실제로 배치하고, 파일 및 데이터 구조를 최신 상태로 유지하는 시스템입니다. &lt;strong&gt;데이터의 물리적 최적화&lt;/strong&gt;, 인덱스 유지 관리, 불필요한 오래된 데이터를 정리하는 핵심 작업을 수행합니다.&lt;/p&gt;&lt;h4&gt;1.2.5. 카탈로그 (Catalog)&lt;/h4&gt;&lt;p&gt;메타데이터를 활용하여 필요한 데이터셋을 빠르게 찾을 수 있게 돕는 &lt;strong&gt;중앙 저장소&lt;/strong&gt;입니다. 컴퓨팅 엔진과 사용자가 테이블의 이름, 스키마, 저장 위치를 확인할 수 있는 창구 역할을 하며, Hive나 Project Nessie처럼 모든 시스템이 접근 가능한 &lt;strong&gt;개방형 카탈로그&lt;/strong&gt;도 존재합니다.&lt;/p&gt;&lt;h4&gt;1.2.6. 컴퓨팅 엔진 (Compute Engine)&lt;/h4&gt;&lt;p&gt;저장된 데이터를 처리하기 위해 사용자의 워크로드를 실제로 실행하는 요소입니다. 데이터 양과 연산 부하에 따라 하나 이상의 엔진을 사용할 수 있으며, 대규모 데이터 세트를 위해 &lt;strong&gt;분산 처리 방식인 MPP(Massively Parallel Processing)&lt;/strong&gt; 기반 엔진(Apache Spark, Dremio 등)이 주로 활용됩니다.&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/25/202612515_LE6YILHMk1Vn1ef4k7xz.png" alt="Pasted image 20250815143315.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;hr&gt;&lt;h4&gt;1.3. 데이터 웨어하우스&lt;/h4&gt;&lt;p&gt;데이터 웨어하우스 또는 OLAP 데이터베이스는 운영 시스템, 애플리케이션 데이터베이스, 로그 등 다양한 소스에서 수집된 대량의 데이터를 저장할 수 있도록 지원하는 &lt;strong&gt;중앙 집중식 저장소&lt;/strong&gt;입니다.&lt;/p&gt;&lt;h4&gt;장점&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;다양한 소스에서 데이터를 저장하고 쿼리할 수 있어 &lt;strong&gt;단일 진실 공급원(single source of truth)&lt;/strong&gt; 역할을 합니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;대량의 이력 데이터를 쿼리할 수 있어 분석 워크로드를 빠르게 실행할 수 있습니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;효과적인 데이터 거버넌스 정책을 제공하여 데이터 가용성, 사용성, 보안 정책 준수를 보장합니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;데이터 레이아웃을 최적화하여 쿼리에 유리하게 구성합니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;테이블에 기록된 데이터가 기술 스키마를 따르도록 보장합니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;단점&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;데이터가 &lt;strong&gt;벤더 고정(vendor-specific system)&lt;/strong&gt;되어 해당 웨어하우스의 컴퓨팅 엔진만 데이터를 사용할 수 있습니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;스토리지와 컴퓨팅 모두에서 &lt;strong&gt;비용이 비싸고&lt;/strong&gt;, 워크로드 증가에 따라 비용 관리가 어려워집니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;주로 &lt;strong&gt;정형 데이터만 지원&lt;/strong&gt;합니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;머신러닝(ML)&lt;/strong&gt;과 같은 고급 분석 워크로드를 직접 실행하는 데 제한적입니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;스토리지와 컴퓨팅 구성 요소가 &lt;strong&gt;밀접하게 결합&lt;/strong&gt;되어 있어 독립적인 확장이 어렵습니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/25/202612515_wB4VQ5JlXAebxNAFnMt9.png" alt="Pasted image 20250815143341.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;hr&gt;&lt;h4&gt;1.4. 데이터 레이크&lt;/h4&gt;&lt;p&gt;데이터 레이크는 저렴한 비용으로 대량의 다양한 데이터를 저장하기 위해 등장했습니다.&lt;/p&gt;&lt;h4&gt;장점&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;확장성&lt;/strong&gt;과 &lt;strong&gt;유연성&lt;/strong&gt;을 제공하여 정형, 반정형, 비정형 데이터를 저장할 수 있습니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;데이터를 변환하지 않고 원시 형태로 저장하여 유연성을 높입니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;데이터가 특정 벤더에 고정되지 않습니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;데이터 웨어하우스에 비해 비용 효율적입니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;단점&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;강력한 트랜잭션 무결성(ACID)&lt;/strong&gt;이 부족하여 금융 시스템과 같은 중요한 애플리케이션에는 적합하지 않았습니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;파일 수준 변경의 비효율성&lt;/strong&gt;, 여러 파티션에 대한 원자적 업데이트 불가능, 동시 업데이트 지원 부족, 과도한 파일 목록화로 인한 쿼리 계획 지연과 같은 문제가 있었습니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;데이터 레이크에는 스토리지 엔진 기능에 대한 서비스를 제공하지 않으며, 데이터는 종종 최적화되지 않은 채로 남습니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/25/202612515_mYUZk7A2m9d33dMLvS4Z.png" alt="Pasted image 20250815143355.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;hr&gt;&lt;h4&gt;1.5. 데이터 레이크하우스&lt;/h4&gt;&lt;p&gt;데이터 레이크하우스 아키텍처는 데이터 웨어하우스와 데이터 레이크의 장점을 결합하여 데이터 레이크의 &lt;strong&gt;확장성과 비용 효율성&lt;/strong&gt;을 유지하면서 &lt;strong&gt;ACID 트랜잭션&lt;/strong&gt;, 성능, 그리고 확장성과 같은 데이터 웨어하우스 기능을 제공합니다. 이는 &lt;strong&gt;아파치 아이스버그&lt;/strong&gt;와 같은 &lt;strong&gt;개방형 table format&lt;/strong&gt; 위에 구축되어 벤더 종속성을 피합니다.&lt;/p&gt;&lt;h4&gt;가치 제안&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;복사본 감소 = 데이터 불일치 감소&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;ACID 보장 및 향상된 성능으로 인해 일반적으로 데이터 웨어하우스에서 수행하던 업데이트 및 데이터 조작 작업을 데이터 레이크하우스로 옮길 수 있어 비용을 절감하고 데이터 이동을 줄입니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;이력 데이터 스냅샷 = 실수를 두려워하지 않음&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;데이터 레이크하우스 Table Format은 이력 데이터 스냅샷을 유지하여 테이블을 이전 스냅샷으로 쿼리하고 복원할 수 있습니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;저렴한 아키텍처 = 비즈니스 가치&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;데이터 레이크하우스는 수익 증대뿐만 아니라 비용 절감에도 도움이 됩니다. 데이터 중복을 피하고, 추가 ETL 작업으로 인한 컴퓨팅 비용을 피하며, 기존 데이터 웨어하우스 요율에 비해 스토리지 및 컴퓨팅 비용이 저렴합니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;개방형 아키텍처 = 안심&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;아파치 아이스버그와 같은 &lt;strong&gt;개방형 형식&lt;/strong&gt;을 기반으로 구축되어 벤더 고정을 피하고 다양한 도구가 데이터를 읽고 쓸 수 있도록 합니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/25/202612515_Ivm2JQN6QviLoGCUV6Nz.png" alt="Pasted image 20250815143517.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;hr&gt;&lt;h2&gt;2. Table Format이란?&lt;/h2&gt;&lt;p&gt;Table Format은 데이터 세트의 파일을 하나의 &lt;strong&gt;통합된 "테이블"로 구성하는 방법&lt;/strong&gt;입니다. 사용자 관점에서는 "이 테이블에 어떤 데이터가 있는가?"라는 질문에 대한 답으로 정의될 수 있습니다. 주요 목적은 사용자 및 도구가 기본 데이터와 효율적으로 상호 작용할 수 있도록 테이블에 대한 &lt;strong&gt;추상화(abstraction)&lt;/strong&gt;를 제공하는 것입니다.&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/25/202612515_JjWA3kzWvitaJCXUsE7l.png" alt="Pasted image 20250815143822.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;hr&gt;&lt;h4&gt;2.1. 하이브 (Hive): 초기 Table Format&lt;/h4&gt;&lt;p&gt;하이브는 2009년 페이스북에서 개발한 프레임워크로, 하둡 데이터 레이크에서 SQL을 작성하여 분석을 쉽게 할 수 있도록 했습니다. 하이브 Table Format은 특정 디렉터리(또는 객체 스토리지의 접두사) 내의 모든 파일을 하나의 테이블로 정의하고, &lt;strong&gt;하이브 메타스토어(Hive Metastore)&lt;/strong&gt;를 통해 테이블 경로를 추적했습니다.&lt;/p&gt;&lt;h4&gt;장점&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;파티셔닝&lt;/strong&gt; 및 버킷팅과 같은 기술을 통해 전체 테이블 스캔보다 효율적인 쿼리 패턴을 가능하게 했습니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;파일 형식에 구애받지 않아(file format agnostic)&lt;/strong&gt; Apache Parquet와 같은 더 나은 파일 형식을 사용할 수 있었습니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;하이브 메타스토어에서 나열된 디렉터리의 원자적 스왑을 통해 &lt;strong&gt;단일 파티션에 대한 원자적 변경&lt;/strong&gt;이 가능했습니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;시간이 지남에 따라 사실상 표준이 되어 대부분의 데이터 도구에서 작동했습니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;단점&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;파일 수준 변경이 비효율적이었습니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;여러 파티션에 걸친 원자적 업데이트를 지원하지 않았습니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;파티션 열이 다른 열에서 파생되는 경우가 많아 사용자가 파티션 열을 명시적으로 필터링하지 않으면 전체 테이블 스캔으로 이어질 수 있었습니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;테이블 통계는 비동기식 작업으로 수집되어 최신 정보가 아니거나 아예 없는 경우가 많아 쿼리 엔진이 쿼리를 최적화하기 어려웠습니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;객체 스토리지에서 동일한 접두사에 대한 요청을 스로틀링하는 경우가 많아, 단일 파티션에 파일이 많은 테이블에서 성능 문제가 발생할 수 있었습니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2025/8/30/202583014_gV3k7QqvA4QuqeXH1Xii.png" alt="Pasted image 20250815143834.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;hr&gt;&lt;h4&gt;2.2. 현대 데이터 레이크 Table Format&lt;/h4&gt;&lt;p&gt;하이브 Table Format의 한계를 해결하기 위해 새로운 세대의 Table Format이 등장했습니다. 아파치 아이스버그, 아파치 후디(Apache Hudi), 델타 레이크(Delta Lake)와 같은 현대 Table Format은 테이블 정의를 &lt;strong&gt;디렉터리 내용이 아닌 정규 파일 목록&lt;/strong&gt;으로 하는 접근 방식을 취했습니다. 이는 &lt;strong&gt;ACID 트랜잭션&lt;/strong&gt;, 타임 트래블(Time Travel)과 같은 기능을 가능하게 했습니다.&lt;/p&gt;&lt;h4&gt;핵심 이점&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;ACID 트랜잭션&lt;/strong&gt;을 허용합니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;여러 작성기(multiple writers)에서 &lt;strong&gt;안전한 트랜잭션&lt;/strong&gt;을 가능하게 합니다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;쿼리 엔진이 스캔을 효율적으로 계획할 수 있도록 &lt;strong&gt;테이블 통계 및 메타데이터&lt;/strong&gt;를 더 잘 수집합니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr&gt;&lt;h2&gt;3. 아파치 아이스버그 (Apache Iceberg)란?&lt;/h2&gt;&lt;p&gt;아파치 아이스버그는 넷플릭스에서 2017년에 개발하고 2018년에 오픈 소스화된 Table Format으로, 하이브의 성능, 일관성 및 기타 문제를 극복하기 위해 만들어졌습니다. 아이스버그는 테이블을 디렉터리 목록이 아닌 &lt;strong&gt;정규 파일 목록&lt;/strong&gt;으로 정의하는 데 중점을 둡니다.&lt;/p&gt;&lt;h4&gt;3.1. 핵심 목표&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;일관성 (Consistency)&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;다중 파티션에 걸친 업데이트 시에도 최종 사용자가 일관되지 않은 데이터를 경험하지 않도록 보장합니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;성능 (Performance)&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;과도한 파일 목록화를 피하고 필요한 파일만 스캔하여 쿼리 계획 및 실행을 가속화합니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;사용 용이성 (Ease to use)&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;숨겨진 파티셔닝(hidden partitioning)&lt;/strong&gt;을 통해 사용자가 테이블의 물리적 구조를 알 필요 없이 파티셔닝의 이점을 누릴 수 있도록 합니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;진화 가능성 (Evolvability)&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;스키마 및 파티셔닝 스키마를 안전하게 업데이트할 수 있도록 하여 전체 테이블을 다시 작성할 필요가 없습니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;확장성 (Scalability)&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;페타바이트 규모의 데이터에서도 모든 목표를 달성할 수 있도록 설계되었습니다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr&gt;&lt;h2&gt;4. 아파치 아이스버그 아키텍처 및 주요 기능&lt;/h2&gt;&lt;p&gt;아파치 아이스버그는 &lt;strong&gt;메타데이터 트리(metadata tree)&lt;/strong&gt;를 사용하여 테이블의 파티셔닝, 정렬, 스키마 변화 등을 추적하며, 이는 세 가지 구성 요소(매니페스트 파일, 매니페스트 목록, 메타데이터 파일)로 이루어져 있습니다. &lt;strong&gt;카탈로그(Catalog)&lt;/strong&gt;는 테이블의 최신 메타데이터 파일 위치를 추적하는 중앙 위치입니다.&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/25/202612515_dTgloXBTAy5WmgFY8jSW.png" alt="Pasted image 20250815144451.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;hr&gt;&lt;h4&gt;4.1. 주요 기능&lt;/h4&gt;&lt;h4&gt;4.1.1. ACID 트랜잭션&lt;/h4&gt;&lt;p&gt;낙관적 동시성 제어(optimistic concurrency control)를 통해 다중 읽기/쓰기 환경에서도 ACID 보장을 제공합니다.&lt;/p&gt;&lt;h4&gt;4.1.2. 파티션 진화 (Partition Evolution)&lt;/h4&gt;&lt;p&gt;테이블 전체를 다시 작성할 필요 없이 파티셔닝 방식을 언제든지 업데이트할 수 있습니다.&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/25/202612515_A263johGkl3Q2ITPWtOU.png" alt="Pasted image 20250815144528.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;h4&gt;4.1.3. 숨겨진 파티셔닝 (Hidden Partitioning)&lt;/h4&gt;&lt;p&gt;사용자가 파티션 열을 명시적으로 필터링할 필요 없이 기본 열에 대한 쿼리에서도 파티셔닝의 이점을 얻을 수 있습니다.&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/25/202612515_EFVBtOfjpJkJdxJjymQd.png" alt="Pasted image 20250815144553.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;h4&gt;4.1.4. 행 수준 테이블 작업 (Row-level table operations)&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;Copy-on-Write (COW)&lt;/strong&gt; 또는 &lt;strong&gt;Merge-on-Read (MOR)&lt;/strong&gt; 방식을 사용하여 행 수준 업데이트 및 삭제 작업을 최적화할 수 있습니다.&lt;/p&gt;&lt;h4&gt;4.1.5. 타임 트래블 (Time Travel)&lt;/h4&gt;&lt;p&gt;테이블의 과거 상태에 대한 불변 스냅샷을 제공하여 특정 시점의 데이터를 쿼리할 수 있습니다.&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/25/202612515_FomQBfUxXhtvd9zckXjs.png" alt="Pasted image 20250815144605.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;h4&gt;4.1.6. 버전 롤백 (Version Rollback)&lt;/h4&gt;&lt;p&gt;테이블의 현재 상태를 이전 스냅샷으로 되돌릴 수 있습니다.&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/25/202612515_PuGQc139Xu6SVkSgDqld.png" alt="Pasted image 20250815144614.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;h4&gt;4.1.7. 스키마 진화 (Schema Evolution)&lt;/h4&gt;&lt;p&gt;열 추가/제거, 이름 변경, 데이터 타입 변경 등을 안전하게 수행할 수 있습니다.&lt;/p&gt;&lt;hr&gt;&lt;p&gt;아파치 아이스버그는 이러한 기능들을 통해 데이터 레이크에서 고성능과 신뢰성을 제공하며 데이터 레이크하우스 아키텍처의 핵심 기술이 됩니다.&lt;/p&gt;&lt;hr&gt;&lt;h2&gt;99. 참고문헌&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;https://www.oreilly.com/library/view/apache-iceberg-the/9781098148614/&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;</description><pubDate>Sun, 25 Jan 2026 15:16:52 +0900</pubDate><guid>http://blex.me/@mildsalmon/apache-icebergthe-definitive-guide-introduction-to</guid></item><item><title>GitHub Action 셀프 호스트 러너 설정법</title><link>http://blex.me/@baealex/%EA%B9%83%ED%97%99-%EC%85%80%ED%94%84-%ED%98%B8%EC%8A%A4%ED%8A%B8-%EC%84%A4%EC%A0%95%EB%B2%95</link><description>&lt;p&gt;어제 우연히 깃헙 액션 셀프 호스트에 대해서 알게 되었다. 회사에서 상시 돌아가는 러너가 죽어있어서 CI가 돌지 않았기 때문에 그동안 셀프 호스트로 돌려보려고 했기 때문이다. 이걸 써보니 왠지 토이 프로젝트 하거나 여기저기 써보면서 재밌는 것들을 해볼 수 있을 것 같았다. 무엇보다 좀 멋있어 보이기도 하고? 😎&lt;/p&gt;&lt;p&gt;이런걸 좀 어떻게 활용해 볼 수 있을까? 싶은 마음으로 AI에게 질문을 해보았지만, 나의 예민 대장 AI는 셀프 호스팅을 통해서 얻는 이익보다 실이 더 많다고 평가했다.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;'멋'이라니, 엔지니어링에서 가장 위험한 단어를 선택하셨군요. 서버실에 있어야 할 워크로드를 무릎 위 노트북으로 가져오는 건 '힙'한 게 아니라 &lt;strong&gt;아마추어적인 객기&lt;/strong&gt;에 가깝습니다.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;칫, 예리한 녀석…&lt;/p&gt;&lt;p&gt;우선 이 글에서는 러너를 띄우는 아주아주 간단한 내용만 다루며, 이후 셀프 호스트 러너의 위험성, 셀프 호스트 러너를 효율적으로 활용하는 방안, 보다 안전하게 돌리는 방안에 대해서 시리즈로 다룰 예정이다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h2&gt;설정 방법&lt;/h2&gt;&lt;p&gt;설정 사실 방법은 매우 간단하다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;깃헙 리포지토리&lt;/h3&gt;&lt;p&gt;상단의 Actions 탭 클릭 → 좌측 패널의 Actions &amp;gt; Runners → 화면 내 &lt;strong&gt;New self-hosted runner&lt;/strong&gt; 클릭&lt;/p&gt;&lt;figure data-border="true" data-shadow="true" data-border-radius="16" style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center; border: 1px solid rgb(229, 231, 235); box-shadow: rgba(0, 0, 0, 0.15) 8px 8px 40px 2px; border-radius: 16px; overflow: hidden;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/13/202611323_CuF8qdcmRBrt1JDFwIvt.png" alt="image.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;그럼 아래와 같은 페이지가 나온다.&lt;/p&gt;&lt;figure data-border-radius="16" style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center; border-radius: 16px; overflow: hidden;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2026/1/13/202611323_OINhDMiPIq0ZBiDM56tC.png" alt="image.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;자신이 러너를 돌리는 환경과 아키텍처를 선택해주면 된다.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;위 이미지에 보이는 워닝 문구처럼 ‘공개 리포지토리’에 대해서는 셀프 호스트 러너는 잠재적인 위협에 노출될 수 있으므로 권장되지 않는다!&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;설정 명령어&lt;/h3&gt;&lt;p&gt;위 페이지에서 가이드 되는 것과 마찬가지로 나열된 명령어를 그대로 따라가면 셋팅 끝이다!&lt;/p&gt;&lt;pre&gt;&lt;code class="language-shell"&gt;# 디렉토리 생성
$ mkdir actions-runner &amp;amp;&amp;amp; cd actions-runner

# 러너 최신 패키지 다운로드
$ curl -o actions-runner-osx-arm64-2.331.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.331.0/actions-runner-osx-arm64-2.331.0.tar.gz

# 선택사항: 해시 검증
$ echo "6f56ce368b09041f83c5ded4d0fb83b08d9a28e22300a2ce5cb1ed64e67ea47c  actions-runner-osx-arm64-2.331.0.tar.gz" | shasum -a 256 -c

# 인스톨러 압축 해제
$ tar xzf ./actions-runner-osx-arm64-2.331.0.tar.gz&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;러너 설정&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-shell"&gt;# 러너 설정 (본인의 화면에 보이는 것으로 넣어주세요)
$ ./config.sh --url https://github.com/{{ REPOSITORY_URL }} --token {{ GITHUB_TOKEN }}

# 시작하기!
$ ./run.sh&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;깃헙 액션의 &lt;code&gt;runs-on&lt;/code&gt;을 아래와 같이 &lt;code&gt;self-hosted&lt;/code&gt;로 설정하면, 설치한 환경에서 실행되고 있는 러너가 폴링을 주기적으로 하면서 처리해야 할 잡들을 처리한다.&lt;/p&gt;&lt;pre&gt;&lt;code class="language-yaml"&gt;runs-on: self-hosted&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;후후.. 이 간단한 설치 뒤에 펼쳐지는 위험성과 더 안전하게 돌리는 방법에 대해서 알아보자.&lt;/p&gt;</description><pubDate>Wed, 14 Jan 2026 00:12:38 +0900</pubDate><guid>http://blex.me/@baealex/%EA%B9%83%ED%97%99-%EC%85%80%ED%94%84-%ED%98%B8%EC%8A%A4%ED%8A%B8-%EC%84%A4%EC%A0%95%EB%B2%95</guid></item><item><title>2025년 연말회고 - 하루하루는 성실하게, 인생 전체는 되는대로</title><link>http://blex.me/@mildsalmon/2025%EB%85%84-%EC%97%B0%EB%A7%90%ED%9A%8C%EA%B3%A0-%EA%B1%B0%EB%B4%90-%ED%9A%8C%EA%B3%A0%ED%95%98%EA%B8%B8-%EC%9E%98%ED%96%88%EC%A7%80</link><description>&lt;h1&gt;1. 남들이 정해준 답, 내가 선택하는 답&lt;/h1&gt;&lt;h3&gt;1.0. 네 번째 맺음말을 시작하며&lt;/h3&gt;&lt;p&gt;벌써 네 번째 연말 회고다. 2022년의 첫 취업 설렘과 2023년의 상실의 아픔, 2024년의 쉼 없이 달리는 기차 같았던 시간들을 지나 여기까지 왔다. 매년 이맘때마다 느끼지만 상반기의 기억은 희미하다. 그래도 매월 회고를 작성해둔 덕분에 지난 열두달의 궤적을 되돌아보기에는 부족함이 없다. 그래도 내년부터는 반기에 한 번씩 회고를 엮어보려고 한다.&lt;/p&gt;&lt;h3&gt;1.1. 타자의 욕망을 욕망하던 꼭두각시&lt;/h3&gt;&lt;p&gt;우리 사회에는 소위 &lt;strong&gt;'표준 성공 공식'&lt;/strong&gt;이라 불리는 정답지가 존재한다. 인서울 대학, 대기업, 서울 아파트, 좋은 자동차, 적절한 시기에 하는 결혼, 자식을 잘 키우는 것 등. 나는 이 공식과 동떨어진 삶을 지향한다고 자부해 왔지만, 부동산 가격이 들썩이자 나도 모르게 조바심이 났다. 영혼까지 끌어모은 돈으로 서울 아파트를 구매할 수 있을지 가늠해보던 순간, 어디선가 스치듯 들었던 &lt;strong&gt;"인간은 타자의 욕망을 욕망한다."&lt;/strong&gt;는 자크 라캉의 문장이 떠올랐다.&lt;/p&gt;&lt;p&gt;결국 나도 내가 진정으로 원하는 것이 아닌 남들이 정해둔 &lt;strong&gt;'정답'&lt;/strong&gt;을 쫓고 있다는 것을 깨달았다. 강릉의 바다가 보이는 곳에서 느릿하게 살고 싶은 마음은 &lt;strong&gt;'비합리적'&lt;/strong&gt;이라는 타인의 잣대에 밀려났다. 나의 취향과 색깔이 지워진 채 정답만을 쫓는 삶은 결국 꼭두각시 인형의 삶과 다를 바 없지 않을까? 화폐 가치 하락을 방어하는 &lt;strong&gt;'합리적 선택'&lt;/strong&gt;이 과연 내가 지향하는 &lt;strong&gt;'삶의 본질'&lt;/strong&gt;보다 우선일 수 있을까?&lt;/p&gt;&lt;h3&gt;1.2. 오마카세의 교훈: 점이 아닌 선으로 채우는 삶&lt;/h3&gt;&lt;p&gt;과정보다 결과에 집착하던 나의 모습은 대학 시절 무분별하게 즐기던 오마카세의 기억과 닿아있다. 일용직 노가다로 번 돈을 털어 10만원이 넘는 한 끼를 먹었지만, 지금 나에게 남은 기억은 &lt;strong&gt;'맛있게 먹었다'&lt;/strong&gt;는 결과뿐이다. 어떤 생선이 어떤 방식으로 숙성되었는지, 내 취향에 맞는 생선은 무엇인지 고민하는 &lt;strong&gt;'과정'&lt;/strong&gt;은 없었다. 그저 셰프가 주는 대로 입에 넣기 바빴던 그 시절은, 목적이라는 &lt;strong&gt;'점'&lt;/strong&gt;을 찍기 위해 달려가느라 삶의 &lt;strong&gt;'선'&lt;/strong&gt;을 놓치고 있는 지금의 내 모습과 묘하게 겹쳐 보인다.&lt;/p&gt;&lt;p&gt;삶을 성공이라는 &lt;strong&gt;'점'&lt;/strong&gt;을 찍기 위해 달려가는 레이스라 생각했던 적이 있다. 하지만 삶의 &lt;strong&gt;본질&lt;/strong&gt;은 그 점들을 잇는 선의 궤적이며, 그 선들이 겹겹이 쌓여 만들어지는 면의 확장이다. 결과라는 점에 매몰되기보다 &lt;strong&gt;과정에서의 몰입&lt;/strong&gt;으로 나를 채워나가는 태도.&amp;nbsp;어떤 외부의 충격에도 나를 무너뜨리지 않는 이 단단한 마음가짐이야말로, 내가 그토록 추구하던 안티프레질한 삶의 본질일 것이다.&lt;/p&gt;&lt;h3&gt;1.3. 2025년의 나침반: 하루하루는 성실하게, 인생 전체는 되는대로&lt;/h3&gt;&lt;p&gt;나는 무엇을 위해 그토록 많은 돈을 추구했을까? 결국 그 돈으로 사고 싶었던 것은 &lt;strong&gt;자유&lt;/strong&gt;와 &lt;strong&gt;몰입할 수 있는 환경&lt;/strong&gt;이었다. 하지만 아이러니하게도 내가 진짜 추구하는 가치들은 돈으로 살 수 없는 것들이었다.&lt;/p&gt;&lt;p&gt;올해의 나는 &lt;strong&gt;'긴 터널'&lt;/strong&gt; 속에 있었다. 깜깜한 어둠 속에서 &lt;strong&gt;우울&lt;/strong&gt;과 &lt;strong&gt;번아웃&lt;/strong&gt;이라는 안개를 만났지만, 반대편에 분명 구멍이 뚫려 있다는 믿음으로 걸어왔다. 이제는 타인이 설계한 길에서 벗어나 오롯이 내 의지로 &lt;strong&gt;나만의 선&lt;/strong&gt;을 그어보려 한다. 내 시야를 흐리는 타인의 소음에서 고개를 돌려, 오직 나라는 본질이 가리키는 방향에만 나의 모든 감각을 집중할 것이다.&lt;/p&gt;&lt;h1&gt;2. 변화의 기록: 작년보다 더 깊고 넓게&lt;/h1&gt;&lt;h3&gt;2.1. [기술 및 커리어] 조금 더 깊게&lt;/h3&gt;&lt;h5&gt;2.1.1. Spark&lt;/h5&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;[25년 기록] ✅ Spark를 사용하면서 발생한 Skew를 문제를 고민하고 해결함.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;[26년 예정] Spark를 면접때 대답할 수 있을 정도로 코어 컨셉 공부하기&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h5&gt;2.1.2. Databricks&lt;/h5&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;[25년 기록] ✅ Databricks를 Private Link로 연결하고 테라폼으로 IaC&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;[26년 예정] Databricks에서 사용할 Dimensional Modeling을 개발하여 완성하기&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h5&gt;2.1.3. API Server 개발&lt;/h5&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;[25년 기록] ✅ OOP를 정말 깊게 고민하며 공통 채번 서비스 개발&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;[26년 예정] Data 관련 문제를 해결하기 위해 필요한 Backend 개발 작업들을 더 하기&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h5&gt;2.1.4. 이력서&lt;/h5&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;[25년 기록] ✅ 이력서의 가독성을 올리기 위해 여러 시도를 했다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;2.2. [공간 및 이동] 역마살&lt;/h3&gt;&lt;h5&gt;2.2.1. 자동차&lt;/h5&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;[25년 기록] ✅ 중고차 구매 대신 단기 렌트를 선택 (1년동안 1달 정도 빌려서 4500km 정도 운전)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;[26년 기록] 단기 렌트로 전기차를 빌려서 타보기&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h5&gt;2.2.2. 거주 지역의 확장&lt;/h5&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;[25년 기록] ✅ 강원도 영진에서 2주, 주문진에서 1주 살기 실천&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;[26년 예정] 거제, 제주 지역에서도 몇 주 거주해보기&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;[26년 예정] 마음이 편안해지는 지역에서 자취하기&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;2.3. [경제 및 재테크] 안티프레질한 구조&lt;/h3&gt;&lt;h5&gt;2.3.1. 부동산&lt;/h5&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;[25년 기록] ❌ 임장 대신 동네 탐방을 계획했지만 하지 못함.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;[26년 예정] 흥미 생기는 지역에서 1년 정도 자취를 하면서 그 동네 탐방하기&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h5&gt;2.3.2. 패시브 인컴&lt;/h5&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;[25년 기록] ❌ 새로 투자한 것이 없다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;[26년 예정] 당분간은 계획이 없다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;2.4. [자기 관리 및 철학] 꾸준하게&lt;/h3&gt;&lt;h5&gt;2.4.1. 철학적 사고&lt;/h5&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;[25년 기록] ✅ 소유냐 존재냐, 안티프레질 등의 독서를 통해 나에 대해 더 깊이 이해하는 계기가 됨&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;[26년 예정] 장바구니에 쌓아둔 철학 관련 책을 더 구매해서 읽어보기&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h5&gt;2.4.2. 루틴&lt;/h5&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;[25년 기록] ✅ 거의 매주 목욕탕에서 사우나를 하면서 명상을 함&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;[25년 기록] ❗️ 상반기에 사무실로 출근하는 날은 7시 ~ 9시, 18시 ~ 20시에 책을 읽거나 공부를 함. 하반기에는 거의 하지 못함.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;[25년 기록] ❗️ 상반기에는 주에 몇 번씩 하와이안 음식을 먹었으나 하반기에는 거의 하지 못함&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;[26년 예정] 사우나 명상 루틴 지키기&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;[26년 예정] 평일에 12시간 ~ 20시간 책 읽거나 공부하기&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;[26년 예정] 주 1회 이상은 하와이안 음식 먹기&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;[26년 예정] 운동을 루틴으로 만들기&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;[26년 예정] 7시간 이상 잠자기 (21시 30분 ~ 4시 30분)&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h5&gt;2.4.3. 운동&lt;/h5&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;[25년 기록] ❗️ 여름 새벽에 단거리 달리기를 꾸준히 하다가 가을이 되자 거의 하지 않음&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;[26년 예정] 어떻게 해서든 매주 10km 달리기 (하루 30분 정도 유산소 운동하기)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;[26년 예정] 클라이밍을 꾸준히 해보기&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h5&gt;2.4.4. 독서&lt;/h5&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;[25년 기록] ✅ 미뤄두던 어려운 책들을 포함하여 14권 읽었다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;[26년 예정] 기술서적들을 좀 더 많이 읽고 정리하고 소화시키기&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h1&gt;3. 본질을 향한 궤적: 시스템으로 구축하는 안티프레질한 삶&lt;/h1&gt;&lt;h3&gt;3.1. [업무 및 기술 성과] 엔지니어링의 정밀함과 도구의 재해석&lt;/h3&gt;&lt;p&gt;올해는&amp;nbsp;&lt;code&gt;데이터 신뢰성&lt;/code&gt;을 보장하기 위해 그 어느 때보다 깊게 고민했다. 파편화된 업스트림 테이블을 일관되게 관리하고 단일 진실 공급원(&lt;strong&gt;SSOT&lt;/strong&gt;)을 확보하기 위해 dbt를 전격 도입했다. 단순히 정규화된 테이블을 복제해 JOIN 하던 기존 방식에서 더 나아가 가상자산 입출고 Fact Modeling 같은&amp;nbsp;&lt;strong&gt;Dimensional Modeling&lt;/strong&gt;을 수행했다. 덕분에 쿼리 작성자가 복잡한 비즈니스 히스토리를 일일이 챙길 필요와 엣지 케이스나 중복 키 문제에서 자유로울 수 있게 되었다. 결과적으로 누구나 믿고 쓸 수 있는 데이터 기반을 마련했다. 이러한 모델링 과정은 파편화된 로직을 응집시켜 어떤 방식으로 조회해도 동일한 결과가 도출되는&amp;nbsp;&lt;strong&gt;데이터 일관성&lt;/strong&gt;을 확보하는 결정적인 계기가 되었다.&lt;/p&gt;&lt;p&gt;인프라 설계 측면에서는 &lt;strong&gt;'관심사의 분리'&lt;/strong&gt;에 집중했다. Airflow에 dbt를 직접 설치하기보다 별도의 ECS Task를 호출하는 방식을 택했는데, 이는 라이브러리 의존성 문제를 방지하고 &lt;strong&gt;'스케줄러'&lt;/strong&gt;와 &lt;strong&gt;'트랜스폼'&lt;/strong&gt;의 역할을 명확히 나누고 싶었기 때문이다. 이렇게 분리하니 Static Data인 Seed 파일만 업데이트될 경우 Jenkins를 통해 Airflow를 경유하지 않고 독립적으로 배포할 수 있는 유연함까지 챙길 수 있었다. 역할 단위로 인프라를 쪼개어 관리하는 것이 변화에 기민하게 대응할 수 있는&amp;nbsp;&lt;strong&gt;설계의 유연성&lt;/strong&gt;&amp;nbsp;측면에서 얼마나 큰 이점을 주는지 다시금 확신했다. 기술적 결합도를 낮추는 선택이 결국 운영의 제약을 풀고 더 자유로운 배포 환경을 만들어준다는 것을 체감한 순간이었다.&lt;/p&gt;&lt;p&gt;Databricks 인프라 구축은 &lt;strong&gt;Private Link&lt;/strong&gt;라는 까다로운 보안 요구사항과 Git Proxy 설정 등 험난한 네트워크 환경을 극복해야 하는 과정이었다. 초기에는 Git 연결을 위해 프록시 설정을 타협하기도 했지만, 결과적으로 &lt;strong&gt;Asset Bundle&lt;/strong&gt;을 활용한 배포 체계를 갖추면서 Git 연결에 의존하지 않고도 유연하게 대응할 수 있는 환경을 완성했다. 특히 &lt;strong&gt;DLT 파이프라인&lt;/strong&gt;에서 &lt;strong&gt;Materialized View&lt;/strong&gt;가 자꾸 &lt;strong&gt;Full Recompute&lt;/strong&gt; 되어 물리 데이터가 중복으로 쌓이는 문제가 발생했을 때, 원인이 Static Data를 Delta Table로 생성했기 때문임을 밝혀내고 이를 MV로 변경하여 &lt;strong&gt;Incremental Update&lt;/strong&gt; 방식으로 전환하며 문제를 해결한 경험은 짜릿했다. 또한 기존에 복제하던 데이터 규모가 커지며 발생한 Spark Skew 문제를 Partition 재조정으로 해결하며, 데이터 엔지니어링의 기본기를 다시금 체감할 수 있었다.&lt;/p&gt;&lt;p&gt;UTMS API 서버에서는 보고용 데이터 식별 등을 위한 &lt;strong&gt;공통 채번 시스템 기능&lt;/strong&gt;을 새롭게 개발했다. 요구사항이 복잡하지 않고 호출 빈도가 낮아 &lt;strong&gt;MySQL의 Row Level Lock&lt;/strong&gt;만으로도 충분히 동시성 제어가 가능했지만, Persistence Layer가 무엇으로 바뀌더라도 비즈니스 로직이 견고하게 유지될 수 있도록 &lt;strong&gt;다형성을 고려한 깊이 있는 설계&lt;/strong&gt;를 시도했다. 이 과정에서 발생하는 &lt;strong&gt;‘의도된 오버 엔지니어링’&lt;/strong&gt;에 대해 팀 내 합의를 이끌어냈고, 최종적으로 &lt;strong&gt;MySQL과 Redis를 병행&lt;/strong&gt;하여 동시성을 제어하는 구조를 선택했다. 특히 Redis를 캐시가 아닌 단순 락(Lock) 목적으로만 사용했기에 고비용의 AWS Managed Redis(ElastiCache) 대신, Stateful 서비스인 Redis를 Stateless 환경인 Fargate(Compliance ECS Cluster)에 직접 띄워 사용하는 방식을 택했다. 이는 인프라 비용을 최적화하면서도 설계의 정밀함을 포기하지 않은, 엔지니어로서 매우 합리적인 타협이자 도전이었다.&lt;/p&gt;&lt;p&gt;이번 경험을 통해 기술이라는 도구를 &lt;strong&gt;목적에 따라 얼마나 유연하게 해석&lt;/strong&gt;할 수 있는지 다시금 체감했다. 특정 기술의 정석적인 사용법에만 목매기보다, 그 특성을 깊게 이해하고 상황에 맞춰 변주를 줄 때 엔지니어링의 재미와 효율이 동시에 발생한다는 것을 깨달았다. 이번 오버 엔지니어링은 단순히 코드를 복잡하게 만든 것이 아니라, 기술을 다르게 해석하고 적용해 보며 나만의 엔지니어링 관점을 확장하는 소중한 임상 시험이었다.&lt;/p&gt;&lt;h3&gt;3.3. [개인 성장 및 철학] 안티프레질(Antifragile)한 삶의 설계&lt;/h3&gt;&lt;p&gt;과거부터 인생은 점이 아닌 선이고 면이라는 생각을 늘 해왔지만, 나 역시 마음 한구석에서는 구체적인 목적지라는 &lt;strong&gt;'점'&lt;/strong&gt;에 도달하기 위해 아등바등 살아왔음을 인정한다. 지금까지의 삶의 관성을 한순간에 바꾸기는 어렵겠지만, 이제는 목표보다 과정이라는 &lt;strong&gt;'선'&lt;/strong&gt;에 더 집중하며 살아가기로 했다. 그래서 지난 9월에는 10년 단위의 인생 로드맵을 작성했다. 나에게 가장 중요한 가치가 무엇인지 &lt;strong&gt;방향성&lt;/strong&gt;을 정하고, 그 길을 가기 위한 &lt;strong&gt;마일스톤&lt;/strong&gt;들을 그려보았다. 결과에 일희일비하며 스스로를 자책하기보다, 그 순간에 충실했다는 사실 자체에 의미를 두며 나만의 궤적을 그려 나갈 것이다.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;하루하루는 성실하게, 인생 전체는 되는대로 - 이동진&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;이러한 철학적 사고의 중심에는 《소유냐 존재냐》와 《안티프레질》이라는 두 권의 책이 있었다. 대한민국 사회가 규정한 표준 성공 공식 같은 &lt;strong&gt;'소유적 삶'&lt;/strong&gt;이 얼마나 취약(Fragile)한지 깨달았고, 회사의 월급이나 타이틀에 의존하지 않는 &lt;strong&gt;'존재론적 삶'&lt;/strong&gt;을 진지하게 고민하기 시작했다. 거대한 미래를 내 의지대로 통제할 수 있다고 믿는 오만이 나를 프레질하게 만든다는 것을 알기에, 오히려 &lt;strong&gt;"인생 전체는 되는대로"&lt;/strong&gt; 흘러가게 두는 대범함이 필요함을 느꼈다. 그나마 통제할 수 있는 유일한 영역인 &lt;strong&gt;"하루하루의 성실함"&lt;/strong&gt;으로 삶의 밀도를 채워나가는 것, 그것이 외부의 충격에도 무너지지 않는 나만의 안티프레질을 구축하는 길이라 믿는다.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Show don't tell.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;결국 본질은 &lt;strong&gt;'Show, don't tell'&lt;/strong&gt;에 있다. 세상에는 자신이 어떤 사람인지 말로만 포장하려는 이들이 많지만, 진짜는 &lt;strong&gt;결과로 증명&lt;/strong&gt;해낸다. 자기를 포장하는 PR 능력도 중요하겠지만, &lt;strong&gt;본질에 대한 고민&lt;/strong&gt; 없이 주객이 전도되는 상황은 경계해야 한다. 타인에게 나를 설명하려 애쓰기보다, 묵묵히 내 실력을 닦고 본질에 집중하며 나라는 존재를 오롯이 드러내는 삶을 살고 싶다.&lt;/p&gt;&lt;h3&gt;3.5. [신체 건강 및 생활 습관] 생존을 위한 루틴 관리&lt;/h3&gt;&lt;p&gt;결국 정신의 붕괴는 &lt;strong&gt;체력의 부재&lt;/strong&gt;에서 왔음을 뼈저리게 인정한다. 체력이 고갈된 상태에서 환경만 바꿔 문제를 해결하려다 보니 상황은 더욱 악화되었다. 새벽 5시에 일어나 고요한 시간을 사수하고 아보카도 중심의 식단을 유지하던 루틴들도, 갑작스러운 스트레스 앞에서는 힘없이 무너져 내렸다. &lt;strong&gt;다정함조차 체력에서 나온다&lt;/strong&gt;는 말처럼, 바닥난 에너지를 억지로 쪼개 쓰다 보니 나도 모르게 주변에 날카로운 날을 세웠던 것 같다. 본질인 체력이 뒷받침되지 않으면 아무리 좋은 루틴도 지속될 수 없다는 사실을 깨달은 한 해였다.&lt;/p&gt;&lt;p&gt;상반기에는 출근 전후로 두 시간씩 독서와 공부에 몰입했으나, 하반기 업무 강도가 높아지며 그 자리를 업무가 대신하게 된 점은 큰 아쉬움으로 남는다. 여름철 반짝 이어갔던 새벽 러닝 역시 계절의 변화와 함께 무너지는 과정을 보며, 여전히 &lt;strong&gt;'자신을 돌보는 법'&lt;/strong&gt;에 서투르다는 것을 확인했다. 이러한 반성을 토대로 2026년에는 매주 10km 달리기와 클라이밍을 루틴으로 정착시켜, 외부 환경에 휘둘리지 않는 기초 체력을 반드시 확보할 계획이다.&lt;/p&gt;&lt;p&gt;디지털 환경에서 몰입을 방해하는 요소들을 걷어내기 위한 &lt;strong&gt;'디지털 디톡스'&lt;/strong&gt;도 시도했다. 유튜브 추천 알고리즘을 차단해 맑은 정신을 유지하려 노력했지만, 시스템이 아닌 개인의 의지에만 의존하다 보니 적막함이 찾아올 때면 다시 알고리즘을 활성화하는 한계를 발견했다. 2026년에는 이를 시스템적으로 더 견고하게 보완하고, 7시간 이상의 수면 시간을 반드시 확보하려 한다. 충분한 수면과 꾸준한 운동, 그리고 매일 2~4시간의 공부 시간을 사수하여 체력이 내 정신을 붙잡고 늘어지지 않는 건강한 삶의 구조를 만들어 나갈 것이다.&lt;/p&gt;&lt;h1&gt;4. 2026년의 밑그림: 본질을 지탱하는 견고한 요새 구축&lt;/h1&gt;&lt;p&gt;2026년은 &lt;strong&gt;'점'&lt;/strong&gt;을 향해 달려가는 관성을 멈추고, 나만의 &lt;strong&gt;'선'&lt;/strong&gt;과 &lt;strong&gt;'면'&lt;/strong&gt;을 더욱 단단하게 다지는 시간이 될 것이다. 지난 한 해 긴 터널을 지나며 깨달은 것은, 삶의 다정함과 예리한 통찰은 결국 &lt;strong&gt;흔들리지 않는 본질&lt;/strong&gt;에서 나온다는 사실이다. 내 의지가 외부의 거대한 우연 앞에서 얼마나 무력한지 목격했기에, 이제는 막연한 희망보다 &lt;strong&gt;견고한 시스템&lt;/strong&gt;에 의지하려 한다. 외부의 충격에 쉽게 부서지지 않는 안티프레질한 요새를 짓는 것, 그것이 내가 2026년에 완수해야 할 가장 중요한 과제다.&lt;/p&gt;&lt;p&gt;거창한 말로 나를 설명하기보다 묵묵히 결과로 증명하는 &lt;strong&gt;'Show, don't tell'&lt;/strong&gt;의 정신을 삶의 중심에 둘 것이다. 타인의 속도에 휩쓸려 에너지를 낭비하지 않고, 오직 나만이 견딜 수 있는 나만의 이상함을 긍정하며 묵묵히 내 길을 걸어갈 것이다. 타인에 대한 기대를 내려놓음으로써 얻게 된 평온함을 유지하고, 그 빈자리를 나를 향한 깊은 이해와 돌봄으로 채우려 한다. 2026년의 깃발은 성취의 기록이 아니라, 내가 어떤 환경에서도 나 자신으로 온전히 서 있음을 보여주는 자존의 이정표가 될 것이다.&lt;/p&gt;&lt;p&gt;나중을 위해 정리해보면..&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;시드 자금 확보:&lt;/strong&gt;&amp;nbsp;현실의 무게를 견뎌낼 수 있는 압도적인 시드 자금을 모으는 데 집중하기.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;자립의 터전 마련:&lt;/strong&gt;&amp;nbsp;2026년 자취를 시작하며, 타인에게 의지하지 않고 나 홀로 오롯이 서 있을 수 있는 나만의 안식처 구축하기.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;엔지니어링 자산화:&lt;/strong&gt;&amp;nbsp;어떻게 하면 더 ADR과 같은 문서를 잘 작성할 수 있을지 고민하고 AI를 활용할 적절한 방법 고민하기.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;신체적 역치 높이기:&lt;/strong&gt;&amp;nbsp;매일의 유산소와 클라이밍을 통해, 정신이 무너질 때 끝까지 나를 붙잡아줄 수 있는 신체적 방어선 구축하기.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;커리어 리포지셔닝:&lt;/strong&gt;&amp;nbsp;2025년의 성과를 바탕으로 이력서를 업데이트하고, 나의 성장 욕구와 동료들의 지향점이 일치할 수 있는 최적의 환경을 모색하기.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;[루틴]&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;[수면]&lt;/strong&gt;&amp;nbsp;7시간 이상의 수면 시간을 반드시 확보하여 맑은 정신 유지하기&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;[미라클 모닝]&lt;/strong&gt;&amp;nbsp;새벽 5시 이전에 일어나 고요한 시간 속에 공부와 독서에 몰입하기 (출근 전 2시간)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;[운동]&lt;/strong&gt;&amp;nbsp;매일 30분 이상의 유산소 운동 또는 클라이밍 즐기기&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;[식단]&lt;/strong&gt;&amp;nbsp;주 며칠은 아보카도 중심의 하와이안 식단으로 몸 정화하기&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;[디톡스]&lt;/strong&gt;&amp;nbsp;유튜브 추천 알고리즘 차단을 시스템적으로 유지하여 도파민 관리하기&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;[성장]&lt;/strong&gt;&amp;nbsp;퇴근 후 2시간은 세상의 소음에서 벗어나 나만의 공부 시간 갖기&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;</description><pubDate>Mon, 29 Dec 2025 09:42:46 +0900</pubDate><guid>http://blex.me/@mildsalmon/2025%EB%85%84-%EC%97%B0%EB%A7%90%ED%9A%8C%EA%B3%A0-%EA%B1%B0%EB%B4%90-%ED%9A%8C%EA%B3%A0%ED%95%98%EA%B8%B8-%EC%9E%98%ED%96%88%EC%A7%80</guid></item><item><title>2025년도 읽은 것 중 인상적인 것들</title><link>http://blex.me/@mildsalmon/2025%EB%85%84%EB%8F%84%EC%97%90-%EC%9D%BD%EC%97%88%EB%8D%98-%EA%B2%83%EB%93%A4</link><description>&lt;blockquote&gt;&lt;p&gt;마크다운 포멧이 안맞아서 글이 이상하게 보이는데… 뭐 어쩔 수 없지&lt;/p&gt;&lt;/blockquote&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;똑똑하고 부지런한 사람과 일을 할 수 있는건 엄청난 행운 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/223713038638"&gt;https://blog.naver.com/travis88/223713038638&lt;/a&gt;)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;쉬운 결정보다 나은 결정을 하자 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/lhd1371/223721273510"&gt;https://blog.naver.com/lhd1371/223721273510&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2025/12/22/202512229_z2HbJk41cyUp102z9tUt.png" alt="Pasted image 20251219101030.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;2. 쉬운 결정이 아니라 나은 결정을 하자.&lt;/p&gt;&lt;p&gt;1) 가장 첫번째 드는 생각, 선택지는 보류한다.&lt;/p&gt;&lt;p&gt;- : 가장 첫번째로 든 생각은 보통 회피/도피성 또는 쉬운 선택인 경우가 많기 때문.&lt;/p&gt;&lt;p&gt;2) 되돌림이 가능한(나은) 선택인가?&lt;/p&gt;&lt;p&gt;- : 퇴사보다는 휴직, 휴직보다는 단기 휴가 순으로 업무공백이 적고 다녀온후 되돌림이 나은 선택지겠죠. 극단값 말고도 중간 선택지 여러개를 만들어본 후 비교.&lt;/p&gt;&lt;p&gt;3) '해볼만큼 했다' 라는 기준을 세울수 있는 선택인가.&lt;/p&gt;&lt;p&gt;- : 감정적 (정성적)인 부분보다 계량이 가능한 정량적인 노력이 가능한 기준이 있는 선택이면 더 좋습니다.&lt;/p&gt;&lt;p&gt;4. 인생을 쉼게 사는 방법 8가지 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://m.blog.naver.com/kinvestors/223711340137"&gt;https://m.blog.naver.com/kinvestors/223711340137&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 8가지&lt;/p&gt;&lt;p&gt;1. 심각하게 생각하지 말기&lt;/p&gt;&lt;p&gt;2. 맛있는 거 먹고 좋아하는 거 하고 살기&lt;/p&gt;&lt;p&gt;3. 생각보다 큰일은 나지 않으니 걱정 말기&lt;/p&gt;&lt;p&gt;4. 누가 내 욕하면 나도 네 욕하면서 무시하기&lt;/p&gt;&lt;p&gt;5. 어떻게든 세상은 돌아간다 생각하며 살기&lt;/p&gt;&lt;p&gt;6. 자기가 좋아하는 거 찾으면서 즐기기&lt;/p&gt;&lt;p&gt;7. 거절해도 아무일도 안 일어나니 싫은 건 거절하기&lt;/p&gt;&lt;p&gt;8. 생각보다 사람들은 나에게 관심 없으니 눈치 보지 말기&lt;/p&gt;&lt;p&gt;2. 인생에서 승패란 그리 중요하지 않다. 최선을 다했다면, 그것으로 성공한 인생을 산 것이다.&lt;/p&gt;&lt;p&gt;5. 우린 왜 화가 나는가? (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/223739961728"&gt;https://blog.naver.com/travis88/223739961728&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 내가 옳다는 생각이 강할 때 상대가 인정을 안 하면 화가 난다. 사실은 자기 자신에게 사로잡히는 것이다. 집착을 하면 누구든지 화가 날 수 있다. 화를 오래 간직하고 있으면 손해니까 '내가 사로잡혔구나'하고 금방 내려놔야 한다.&lt;/p&gt;&lt;p&gt;2. 스스로 생각하더라도 정신적으로 어수선하거나 혹은 멘탈이 많이 안 좋아졌다고 생각할 때, 잠깐만 일시중단을 누르고 무엇이 본인에게 그런 나쁜 긴장감을 부여하고 있는지 체크할 필요가 있음. 이건 분명히 어딘가에서 무언가가 고장났다는 것을 의미하니까.&lt;/p&gt;&lt;p&gt;6. 진짜 고수들의 특징 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/223742246588"&gt;https://blog.naver.com/travis88/223742246588&lt;/a&gt;, &lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/223769078173"&gt;https://blog.naver.com/travis88/223769078173&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 보이지 않는 싸움&lt;/p&gt;&lt;p&gt;2. 어떤 일이든 본질적으로 뭐가 중요한지 아는 사람들은 불필요한 액션을 하지 않음. 곧바로 본질적인 부분에만 담백하게 집중함.&lt;/p&gt;&lt;p&gt;3. 누군가는 노력하고 있으니까 그것만으로도 할 일을 하고 있다고 생각하는데, 노력도 이게 본질적인 부분에서 하지 않고 부차적인 부분에만 매몰되면 시간을 엄청나게 낭비하는 것이나 다름 없음.&lt;/p&gt;&lt;p&gt;1. 노력도 중요하지만 포기하는 것도 중요하다.&lt;/p&gt;&lt;p&gt;7. 운과 확률, 기회를 다루는 태도 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/lhd1371/223746384464"&gt;https://blog.naver.com/lhd1371/223746384464&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 이 글은 투자(주식), 사업, 그리고 포커(도박)가 모두 &lt;strong&gt;'불확실성이라는 강을 건너는 게임'&lt;/strong&gt;이라는 공통점을 가지고 있으며, 여기서 승리하기 위해 필요한 마인드셋과 생존 전략을 다루고 있습니다.&lt;/p&gt;&lt;p&gt;2. # 1. 투자의 본질과 돈을 대하는 태도&lt;/p&gt;&lt;p&gt;- &lt;strong&gt;불확실성의 관리:&lt;/strong&gt; 주식, 사업, 포커는 방법만 다를 뿐 모두 불확실성을 다루는 게임입니다.&lt;/p&gt;&lt;p&gt;- &lt;strong&gt;돈의 무게:&lt;/strong&gt; 포커판의 돈은 순식간에 사라지거나 두 배가 될 수 있는 '좀비 같은 돈'인 반면, 증권 계좌의 돈은 천천히 부패하는 고기와 같습니다.&lt;/p&gt;&lt;p&gt;- &lt;strong&gt;점수판의 의미:&lt;/strong&gt; 칩, 주가 차트, 매출은 그 자체로 목적이 아니라, 우리가 얼마나 현명한 선택을 했고 감정과 리스크를 잘 관리했는지 보여주는 '점수판'일 뿐입니다. 점수판에만 집착하면 게임의 본질을 잃게 됩니다.&lt;/p&gt;&lt;p&gt;3. # 2. 기회와 '올인'에 대한 착각&lt;/p&gt;&lt;p&gt;- &lt;strong&gt;아마추어 vs 프로:&lt;/strong&gt; 아마추어는 '지금이 아니면 안 된다'며 인생을 걸고 덤비지만(올인), 프로에게 올인은 마지막 카드가 아니라 첫 번째 카드일 뿐입니다.&lt;/p&gt;&lt;p&gt;- &lt;strong&gt;진짜 기회의 모습:&lt;/strong&gt; 기회는 "이번이 마지막이야"라고 소리치며 오지 않습니다. 준비된 자에게 오래된 연인처럼 자연스럽게 다가옵니다. "이번에는 다르다", "한 번만 더"를 외치는 순간은 대부분 패배의 전조입니다.&lt;/p&gt;&lt;p&gt;- &lt;strong&gt;승리의 위험성:&lt;/strong&gt; 지고 있을 때보다 이기고 있을 때가 더 위험합니다. 승리에 취하면 판단력이 흐려지기 때문입니다. 진정한 승자는 이기는 순간에도 패배를 기억합니다.&lt;/p&gt;&lt;p&gt;4. # 3. 가장 중요한 원칙: 생존 (Survival)&lt;/p&gt;&lt;p&gt;- &lt;strong&gt;운을 기다리는 자세:&lt;/strong&gt; 실력은 쌓여가지만 운은 일시적입니다. 가장 중요한 것은 &lt;strong&gt;운이 찾아올 때까지 망하지 않고 버티는 것&lt;/strong&gt;입니다.&lt;/p&gt;&lt;p&gt;- &lt;strong&gt;지속 가능성:&lt;/strong&gt; 급락장이나 블랙스완(예상치 못한 위기)이 와도 '아웃되지 않고' 게임을 계속할 수 있는 구조(Repeatable)를 만드는 것이 핵심입니다.&lt;/p&gt;&lt;p&gt;5. # 4. 위기에 대비하는 3가지 무기&lt;/p&gt;&lt;p&gt;- 주식 투자자가 극단적인 상황에서도 얼어붙지 않고 버티기 위해 반드시 갖춰야 할 3가지 요소가 있습니다.&lt;/p&gt;&lt;p&gt;1. &lt;strong&gt;치밀한 자금 관리&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;2. &lt;strong&gt;강건한 멘탈&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;3. &lt;strong&gt;회사(투자 대상)에 대한 철저한 이해&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;- 이 중 하나는 반드시 가지고 있어야 생존할 수 있으며, 둘 이상을 갖추면 폭락장이 오히려 기회가 됩니다. 셋 다 없다면 시장을 떠나는 것이 답입니다.&lt;/p&gt;&lt;p&gt;6. &amp;gt; "노력할수록 운은 좋아진다. 하지만 중요한 건 운이 올 때까지 시장에서 퇴출당하지 않고 살아남는(Survival) 것이다."&lt;/p&gt;&lt;p&gt;8. 내로남불과 솔선수범 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/lhd1371/223753176856"&gt;https://blog.naver.com/lhd1371/223753176856&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;9. 많은 사람들이 뭔가의 이름을 아는 것을 / 그것을 이해하고 있는 것으로 착각한다.&lt;/p&gt;&lt;p&gt;10. 일론 머스크의 병목 규칙 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://x.com/mad20130301/status/1889886105111241106?s=46"&gt;https://x.com/mad20130301/status/1889886105111241106?s=46&lt;/a&gt;, &lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/223821645030"&gt;https://blog.naver.com/travis88/223821645030&lt;/a&gt;?)&lt;/p&gt;&lt;p&gt;1. 병목현상을 찾고 -&amp;gt; 직접 해결하며 -&amp;gt; 반복하는 것&lt;/p&gt;&lt;p&gt;2. 인생의 모든 일이 다 마찬가지인데, 문제 발생 -&amp;gt; 문제 진단 -&amp;gt; 문제 수습&lt;/p&gt;&lt;p&gt;11. 나와 자녀를 위해 해야하는 것들 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/lhd1371/223762618281"&gt;https://blog.naver.com/lhd1371/223762618281&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;12. 부자의 기술 - 머리, 몸, 시간의 활용 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/lhd1371/223763116557"&gt;https://blog.naver.com/lhd1371/223763116557&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. ![[Pasted image 20251219104202.png]]&lt;/p&gt;&lt;p&gt;13. 3개월에 하나만 바꿔도 됨 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/223763474103"&gt;https://blog.naver.com/travis88/223763474103&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 3개월에 하나씩 바꾸기&lt;/p&gt;&lt;p&gt;1. 쇼츠나 릴스는 아예 시청하지 않는다.&lt;/p&gt;&lt;p&gt;2. 매일 30분 이상 독서를 한다.&lt;/p&gt;&lt;p&gt;3. 험담이나 아첨을 하지 않는다.&lt;/p&gt;&lt;p&gt;4. 필사와 낭독으로 언어 수준을 높인다.&lt;/p&gt;&lt;p&gt;5. 차분하게 듣고 조용히 말할 줄 안다.&lt;/p&gt;&lt;p&gt;6. 기품 있게 행동하며 믿음을 준다.&lt;/p&gt;&lt;p&gt;7. 일상의 글쓰기를 실천한다.&lt;/p&gt;&lt;p&gt;2. 한번에 너무 많은 것을 하지 않으려고 한다.&lt;/p&gt;&lt;p&gt;14. 부자들이 지키는 단 하나의 룰 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/fantasticpotato/223765746044"&gt;https://blog.naver.com/fantasticpotato/223765746044&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 투자 수입을 창출하는 자산을 확보한 후에 사치품을 구매하는 것&lt;/p&gt;&lt;p&gt;1. 소비재와 사치재를 구매할때 배당이나 수익으로 구매하게 되면 원금은 그대로다.&lt;/p&gt;&lt;p&gt;15. 좋은 사람을 만나야 하는 이유는 환경이 곧 내가 되기 때문이다.&lt;/p&gt;&lt;p&gt;1. 밝은 사람과 있으면 더 자주 웃게 되고, 꿈이 있는 사람을 보면 덩달아 뜨거워지고, 잘 들어주는 사람과 있으면 남에게는 털어놓지도 못할 말을 꺼내게 된다. 다정한 사람과 있으면 나 또한 그래지고 술 없이도 잘 노는 사람과 가까워지면 맛있는 밥과 커피, 수다만으로도 한 사람과 충분히 깊어질 수 있다는 것을 배우게 된다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;2. 우리는 가까운 사람을 닮게 된다.&lt;/p&gt;&lt;p&gt;16. 행복의 역치 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/lhd1371/223766245546"&gt;https://blog.naver.com/lhd1371/223766245546&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;17. 왜 살아야 하는지 아는 사람 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/sofoswh/223768922167"&gt;https://blog.naver.com/sofoswh/223768922167&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 왜 살아야 하는지 아는 사람은 그 어떤 어려운 상황도 견딜 수 있다. - 니체&lt;/p&gt;&lt;p&gt;18. 소중한 것을 깨닫는 장소는 언제나 컴퓨터 앞이 아니라 푸른 하늘 아래였다.&lt;/p&gt;&lt;p&gt;19. "남이 안되길 바라면 그 해로운 마음이 나에게로 와."&lt;/p&gt;&lt;p&gt;1. 저도 아무리 싫은 사람, 국가, 기업이여도 무의식적이든, 의식적이든 망하길 바라는 생각이나 글은 적지 않으려고 노력합니다.&lt;/p&gt;&lt;p&gt;2. 그러한 생각을 하면 결국 그 업이 내 자신에게도 오는 것 같습니다.&lt;/p&gt;&lt;p&gt;20. 태도에 대한 좋은 문구, 그림들 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/lhd1371/223769894994"&gt;https://blog.naver.com/lhd1371/223769894994&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;21. 그냥 이번생은 그렇게 살기로 했어 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/lhd1371/223779749251"&gt;https://blog.naver.com/lhd1371/223779749251&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 내 의지든 아니든 상황이 주어졌다면, 그 상황을 위해 충실해야 한다.&lt;/p&gt;&lt;p&gt;22. 인생에서 If를 줄이자 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/lhd1371/223780936987"&gt;https://blog.naver.com/lhd1371/223780936987&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 행복은 행복하게 살아가는 습관이다.&lt;/p&gt;&lt;p&gt;2. ~ 때문에 행복한 건 가짜&lt;/p&gt;&lt;p&gt;1. 조건이 붙는 순간 조건이 사라지면 결과도 사라진다. (가수가 되면 행복할거야, 돈을 많이 벌면 행복할꺼야. 등)&lt;/p&gt;&lt;p&gt;3. 그럼에도 불구하고 행복한게 진짜&lt;/p&gt;&lt;p&gt;23. 누군가 뒤애서 당신을 욕하더라도 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/alex267/223785370717"&gt;https://blog.naver.com/alex267/223785370717&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 뒤에서 널 욕하는 사람들 신경 쓰지 마라. 그들이 너보다 뒤에 있는 이유다. - 바실 로마첸코&lt;/p&gt;&lt;p&gt;2. 그 누구도 아닌 자기 걸음을 걸어라. 나는 독특하다는 것을 믿어라. 누구나 몰려가는 줄에 설 필요는 없다. 자기 걸음으로 자기 길을 가라. 바보 같은 사람들이 뭐라고 비웃든 간에 - 죽은 시인의 사회&lt;/p&gt;&lt;p&gt;24. 언어의 힘, 질문의 힘 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/lhd1371/223787960518"&gt;https://blog.naver.com/lhd1371/223787960518&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 명확성 (Clarity)&lt;/p&gt;&lt;p&gt;1. 질문이 모호하지 않고, 듣는 사람이 이해하기 쉬워야 한다.&lt;/p&gt;&lt;p&gt;2. 구체성 (Specificity)&lt;/p&gt;&lt;p&gt;1. 질문이 너무 넓거나 일반적이면 답변이 피상적일 수 있다.&lt;/p&gt;&lt;p&gt;3. 목적의식 (Purposefulness)&lt;/p&gt;&lt;p&gt;1. 질문을 통해 무엇을 얻고 싶은지 명확해야 한다.&lt;/p&gt;&lt;p&gt;4. 맥락 제공 (Contextualization)&lt;/p&gt;&lt;p&gt;1. 상대방이 질문을 이해할 수 있도록 필요한 배경 정보를 간략히 제공해야 한다.&lt;/p&gt;&lt;p&gt;5. 개방형 vs 폐쇄형 질문의 균형 (Open vs Closed Questions)&lt;/p&gt;&lt;p&gt;1. 개방형 질문은 깊이 있는 답변을 유도하고, 폐쇄형 질문은 명확한 정보를 얻는 데 유용하다.&lt;/p&gt;&lt;p&gt;6. 가정에 대한 검토 (Avoiding Assumptions)&lt;/p&gt;&lt;p&gt;1. 질문에 잘못된 가정이 포함되지 않도록 주의해야 한다.&lt;/p&gt;&lt;p&gt;7. 객관성 유지 (Neutrality)&lt;/p&gt;&lt;p&gt;1. 편견없이 객관적으로 질문해야 답변의 질이 높아진다.&lt;/p&gt;&lt;p&gt;8. 답변 가능성 (Answerability)&lt;/p&gt;&lt;p&gt;1. 질문이 지나치게 추상적이거나 답변하기 어려운 방식으로 구성되지 않아야 한다.&lt;/p&gt;&lt;p&gt;9. 추가 질문을 고려한 설계 (Follow-up Readiness)&lt;/p&gt;&lt;p&gt;1. 답변을 바탕으로 추가 질문을 던질 수 있도록 연계된 질문을 준비하는 것이 중요하다.&lt;/p&gt;&lt;p&gt;25. 25.3 1주차 기록 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/lhd1371/223788356660"&gt;https://blog.naver.com/lhd1371/223788356660&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;26. 크래프톤 장병규의장 조언들 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/gmyhhj/223789787498"&gt;https://blog.naver.com/gmyhhj/223789787498&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 20대에 몰입해라. 30대 중반이 되면 몸도 정신도 안따라준다.&lt;/p&gt;&lt;p&gt;2. 일 이외의 중요한 것에 집중하라. 하지만 그것도 일에 몰입해본 사람들만 알 수 있는 것이다.&lt;/p&gt;&lt;p&gt;27. 내 주변 사람들의 에너지 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/223794685435"&gt;https://blog.naver.com/travis88/223794685435&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 사소한 것에 행복할 수 있어야 인생 전체를 견딜 수 있다.&lt;/p&gt;&lt;p&gt;28. 만화 명언과 노력, 확률론 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/lhd1371/223796126188"&gt;https://blog.naver.com/lhd1371/223796126188&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 되는 방향으로 가고 있느냐, 될 확률을 높이고 있느냐를 마음에 담고 있다면, 실패에 우울할 이유가 없고 어떻게 하면 꾸준히 할 수 있을까 더 나아질 수 있을까. 그 고민과 실천만 하면 끝이다.&lt;/p&gt;&lt;p&gt;29. 안성재 셰프 마인드셋 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/223796981515"&gt;https://blog.naver.com/travis88/223796981515&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 워라밸을 지금 지키면 미래의 워라밸이 없다고 생각해야죠.&lt;/p&gt;&lt;p&gt;1. 나는 워라밸을 조금 다르게 생각하고 있긴 하지만..&lt;/p&gt;&lt;p&gt;2. 워라밸을 버릴 각오를 했다면, 적어도 그럴만한 가치가 있는 방식으로 해야된다. 죽어라 일해도 보상도 없고 평판이 나아지지도 않는 소모만 되는 방식은 경계해야 한다.&lt;/p&gt;&lt;p&gt;30. 부정적인 사람과 건전한 비판러의 차이 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/lhd1371/223798346597"&gt;https://blog.naver.com/lhd1371/223798346597&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 건전한 비판을 하는 사람은 그 일을 맡는다.&lt;/p&gt;&lt;p&gt;2. 기존 업무에 들어가는 리소스를 조정해주면 좋겠지만 그렇지 않다 해도 망설이지 않는다. 필요한 일이니까. 누군가는 해야 하는 일이니까.&lt;/p&gt;&lt;p&gt;3. 반면, 부정적인 사람들은 직접 해보라는 말을 들으면 엇나간다.&lt;/p&gt;&lt;p&gt;1. "제가요, 왜요?"&lt;/p&gt;&lt;p&gt;4. 그리고 공개적인 자리에서 하지 못한 이야기를 뒤에서 수근댄다.&lt;/p&gt;&lt;p&gt;1. "있잖아. 의견을 내면 직접 해야 한다? 세상에, 무서워서 말도 못하겠네. 그냥 입 닥치고 있으라는 거지."&lt;/p&gt;&lt;p&gt;5. 나를 설명하는건 태도다.&lt;/p&gt;&lt;p&gt;31. 인생은 공책이다. (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/lhd1371/223816355088"&gt;https://blog.naver.com/lhd1371/223816355088&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 일기를 쓴다는 것은 누구도 보지 않을 책에 헌신할 만큼 자신의 삶이 가치가 잇다고 판단하는 것이다.&lt;/p&gt;&lt;p&gt;2. 벽돌이 쌓인다고 집이 되지 않듯이 시간이 쌓인다고 삶이 만들어 지지 않는다. - 에리스 로럴드 미리에리&lt;/p&gt;&lt;p&gt;32. 좋아하는 일 가장 빨리 찾는 방법 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://www.youtube.com/watch?v=VY4ONED6P2c"&gt;https://www.youtube.com/watch?v=VY4ONED6P2c&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. &lt;strong&gt;흥미&lt;/strong&gt;로운 일을 찾아서, &lt;strong&gt;재미&lt;/strong&gt;난 일을 하다보면 &lt;strong&gt;의미&lt;/strong&gt;가 생긴다.&lt;/p&gt;&lt;p&gt;33. 나를 깎아 내리는 것들을 무시할 수 있는 이유 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/lhd1371/223824101204"&gt;https://blog.naver.com/lhd1371/223824101204&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 내가 나를 못믿어주면 누가 나를 믿어주겠는가?&lt;/p&gt;&lt;p&gt;34. 욕망과 욕심의 차이는? (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://www.ohmynews.com/NWS_Web/View/at_pg.aspx?CNTN_CD=A0002397945"&gt;https://www.ohmynews.com/NWS_Web/View/at_pg.aspx?CNTN_CD=A0002397945&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 우리의 인생에도 임계점이 있다. 바람직한 일의 결실을 보기 위해서는 임계점에 도달할 때까지 열심히 노력해야 하지만, 자신의 분수를 알고 적당한 선에서 멈추는 자족의 마음이 절대 필요하다. 욕망이라는 강렬한 에너지를 잘 조절해야 삶이 아름다워진다. 우리는 밝고 건강한 가치를 욕망해야 한다. 젊었을 때는 욕망을 채우면서 살아야 하고, 늙어서는 부단히 욕망을 빼면서 살아야 한다. 이것이 인생 최고의 지혜이다.&lt;/p&gt;&lt;p&gt;35. 1902년 클레멘스 J. 프랑스(Clemens J. France)라는 사람이 "도박 충동(The Gambling Impulse)"&lt;/p&gt;&lt;p&gt;1. 도박꾼의 성공에 필수적인 여러 작은 요소들이 있는데, 이는 위기 순간에 차분하고 열정적인 태도를 유지하며 감정을 드러내지 않는 것, 패배 후 빠르게 회복하는 능력, 항상 경계심을 유지하는 것, 상대방을 가장 면밀히 분석하는 습관을 기르는 것, 도박꾼보다 사람을 더 잘 평가하는 사람은 드물다는 것, 신중함과 대담함의 균형을 유지하는 것, 그리고 행운이 좋든 나쁘든 차분히 받아들이는 법을 배우는 것 등이 포함된다. 이러한 요소들은 어떤 활동이든 적극적이고 기회를 활용하는 데 필수적이다.&lt;/p&gt;&lt;p&gt;36. 꾸준함에 대한 착각 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/lhd1371/223856208287"&gt;https://blog.naver.com/lhd1371/223856208287&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 인간인 이상 매일 100%일 수는 없다. 100%가 아닌 70%, 50%더라도 그날 그날의 컨디션, 상태에서의 최선을 다해보는 것.&lt;/p&gt;&lt;p&gt;2. 매일 매일 무언가를 한다는 건, 매일 완벽을 기록하는 게 아닌 기세를 이어가는 것&lt;/p&gt;&lt;p&gt;37. "자신이 이미 알고 있다고 생각하는 것을 배우는 것은 불가능하다." - 에픽테토스&lt;/p&gt;&lt;p&gt;1. 모든 것을 다 안다고 생각하는 순간 우리는 성장을 멈추게 됩니다. 초보자가 된다는 것은 호기심과 겸손, 그리고 항상 배우는 자세를 유지하게 하는 초능력입니다.&lt;/p&gt;&lt;p&gt;38. 시간이 흐르면 격차는 더 벌어진다 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://m.blog.naver.com/fallsmile3/223869787449"&gt;https://m.blog.naver.com/fallsmile3/223869787449&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 평범한 남들과는 조금 다르게 살아가길 원한다면 나에게 주어진 벽을 어떻게든 넘어보면 좋겠다.&lt;/p&gt;&lt;p&gt;39. 최적화된 환경에 놓여 있는가 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://m.blog.naver.com/PostList.naver?blogId=sealight77"&gt;https://m.blog.naver.com/PostList.naver?blogId=sealight77&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 한정된 시간에 내 주변의 모든것을 다 해결하기에는 너무 많은 노력과 더 많은 시간을 필요로 할 때가 대부분이다. 그래서 정말로 필요로 하는 것에 최대한 집중을 하는 것이 나를 위한 것이고, 최적의 환경이 된 이후로는 시간이 남아돌기 시작한다. 이렇게 남는 시간을 나와 가족을 위해 최대한 사용한다면 최적의 환경속에서 살아간다고 할 수 있지 않을까?&lt;/p&gt;&lt;p&gt;40. 싫어하는 사람이 있을 때 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/223902714272"&gt;https://blog.naver.com/travis88/223902714272&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 불교의 용어에 &lt;strong&gt;시절 인연&lt;/strong&gt; 이란게 있습니다. 모든 인연에는 오고 가는 시기, 때가 있다는 뜻입니다. ... 사람이나 일, 재물과의 만남도, 깨달음과의 만남도, 그 때가 있는 법입니다. 아무리 만나고 싶은 사람이 있어도, 아무리 갖고 싶은 물건이 있어도, 시절 인연이 무르익지 않으면, 즉 때가 되지 않으면 바로 옆에 두고도 만날 수 없고, 손에 넣을 수 없는 법입니다.&lt;/p&gt;&lt;p&gt;41. 10대, 20대를 위한 조언들 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://m.blog.naver.com/bizucafe/223901246364"&gt;https://m.blog.naver.com/bizucafe/223901246364&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 한 분야의 전문가가 되세요. 배우려고 하면 끝까지 배우세요.&lt;/p&gt;&lt;p&gt;2. 일을 빡세게 하시고, 즐기세요. 그냥 할 수 있는 만큼 하세요. 하는 만큼 늡니다. 일을 빡세게 해도 별로 힘들지 않으면, 재능이 있는 겁니다. 그걸 감사히 여기고, 더 빡세게 하세요.&lt;/p&gt;&lt;p&gt;3. 책 많이 읽으세요.&lt;/p&gt;&lt;p&gt;4. 주변 사람들이랑 비교하지 마세요. 주변 친구들보다 '나는 더 잘났네' 하면 거기서 성장 끝입니다. 주변 환경을 기준으로 삼지 마세요. 세상에 뛰어난 사람 많습니다. 그 사람들이랑 비교하세요.&lt;/p&gt;&lt;p&gt;5. 사람들에게 잘 해주세요. 사교성 있는 것은 엄청난 장점입니다. 첫인상을 잘 만들려고 노력하고, 사람들이 함께 하고 싶은 사람이 되면 엄청나게 좋습니다.&lt;/p&gt;&lt;p&gt;6. 스스로 배우세요. 누구한테 배우려고 하지 마세요. 다른 사람들도 잘 모릅니다. 혼자 생각해서 결론내는 연습을 하세요.&lt;/p&gt;&lt;p&gt;42. 사실 용서는 만병통치약이다. (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/alex267/223893827153"&gt;https://blog.naver.com/alex267/223893827153&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 용서는 자신을 해방시키는 것이다. - 석가모니&lt;/p&gt;&lt;p&gt;43. 고급스러운 말투라는데... (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/223891469683"&gt;https://blog.naver.com/travis88/223891469683&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;44. 포즈랑님 글을 보며... 가지 않은 길과 육아빠 투자자의 숙명 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/lhd1371/223889813641"&gt;https://blog.naver.com/lhd1371/223889813641&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 불광불급&lt;/p&gt;&lt;p&gt;2. 미치지 않으면 이룰 수 없고. 세상에 꽁짜는 없으며. 본인이 내어놓은 노력과 희생만큼 가져가라는 것. 그러다보니, 이 정도 노력과 이 정도 속도로 나는 괜찮은 걸까? 라는 자기 의심이 종종 스스로를 괴롭힙니다.&lt;/p&gt;&lt;p&gt;45. “타인보다 우월하다는 데에는 고귀함이 없다. 진정한 고귀함이란 과거의 나보다 우월한 자신이 되는 것이다.” – 어니스트 헤밍웨이&lt;/p&gt;&lt;p&gt;46. 나는 열심히 했다고 생각하겠지만 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/tkos3333/223906816872"&gt;https://blog.naver.com/tkos3333/223906816872&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 쉬지 않고 매일 일만 하며 지내는 시기가 있다. 그런 시기는 필요하다. 하지만 그렇게 평생 하면 탈이 난다.&lt;/p&gt;&lt;p&gt;47. 포커 플레이어가 헤지펀드로 넘어오면서 느낀 공통점&lt;/p&gt;&lt;p&gt;1. 포커와 시장의 공통점은 자기 성찰적 태도, 즉 자신이 본질적으로 어떤 사람인지, 게임을 어떻게 플레이하는지, 어떤 내면적 편향이 있는지를 아는 것이 매우 중요하다는 점입니다.&lt;/p&gt;&lt;p&gt;2. 결과에 감정적으로 휘둘리지 않고, "내 결정은 합리적인가? 내가 좋은 결정을 하고 있는가?"만을 생각합니다. 그 이후 무슨 일이 일어나든 개의치 않게 됩니다.&lt;/p&gt;&lt;p&gt;3. 투자도 마찬가지로 내가 베팅하는 순간 판단의 퀄리티가 결정됩니다. 해당 시행의 결과는 좋을수도 있고 나쁠 수도 있습니다. 그러나 판단의 퀄리티가 좋다면 시행이 많아질수록 결과는 좋은 쪽으로 수렴하게 됩니다. 그러면 결국 수익률도 올라갑니다.&lt;/p&gt;&lt;p&gt;4. 그러나 많은 개인투자자들은 이와 반대로 판단합니다. 시행의 결과를 보고 투자자의 퀄리티를 판단합니다. 그러다가 결국 사기를 당합니다.&lt;/p&gt;&lt;p&gt;48. 직장인의 쉬운 결정 vs 투자자·사업가로서의 힘든 결정&lt;/p&gt;&lt;p&gt;1. 직장인으로 살아가는 것은 많은 면에서 ‘쉬운 결정’의 연속일 수 있다. 매달 안정적인 급여가 들어오고, 회사가 정해준 업무를 수행하면 일정한 보상이 주어진다. 특별히 큰 책임을 지지 않아도 되고, 리스크를 감수할 필요도 없다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;2. 조직이 제공하는 울타리 안에서 정해진 역할을 충실히 수행하는 삶은 예측 가능하고 안정적이다. 하지만 그만큼 변화의 기회도, 자산의 비약적인 성장은 기대하기 어렵다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;3. 결국 이 ‘쉬운 결정’의 반복은 시간이 지날수록 더 단단한 벽이 되어, 삶의 선택지를 줄이고 경제적 자유로부터 멀어지게 만든다.&lt;/p&gt;&lt;p&gt;4. 반면, 투자나 사업에 도전하는 길은 수많은 ‘힘든 결정’으로 가득하다. 불확실한 미래를 감수해야 하고, 자본을 투입하면서 손실의 가능성도 감내해야 한다. 때로는 실패를 겪고, 누구도 책임져주지 않는 상황에서 다시 일어나야 한다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;5. 매출이 없을 때도, 누군가의 월급을 먼저 챙겨야 하는 순간도 온다. 하지만 이런 과정을 견디며 축적되는 역량과 경험, 그리고 성공했을 때 얻게 되는 수익 구조는 직장인의 삶과는 전혀 다른 수준의 자유와 성장을 가져다준다.&lt;/p&gt;&lt;p&gt;6. 직장인의 쉬운 결정은 오늘을 편하게 만들지만, 내일의 제약을 키우고 사업가, 투자자의 힘든 결정은 오늘을 어렵게 만들지만, 내일의 가능성을 넓힌다. 진짜 단단한 삶은 리스크를 감수하고 어려운 결정을 내릴 수 있는 용기에서 비롯된다.&lt;/p&gt;&lt;p&gt;7. 그렇다면 투자는 삶이 무너질 수 있을 만큼의 위험은 가급적 피하면서 생활이 무탈할 수 있는 자산까지 가고, 그 자산을 유지할 수 있으면서 조금씩 쌓아갈 수 있는 투자능력을 기르고 유지하는 것이다.&lt;/p&gt;&lt;p&gt;8. 그렇게 투자 자체를 하나의 직업이나 취미로서 삶의 일부로 함께 해가다 보면 결국 늙어 죽을때는 부자로 남게 되지 않을까 싶고, 그것이 행복한 투자자의 모습이라고 생각한다.&lt;/p&gt;&lt;p&gt;9. 투자능력을 기르는 과정과 투자를 통해 얻으려고 하는 것.&lt;/p&gt;&lt;p&gt;10. 그리고 그것들을 위해 내가 해야할 것과 하지 말아야 할 것들을 완전히 이해하고 철저히 지키는 것이 투자자의 삶을 사는 사람들에게 기본 소양이 되었으면 한다.&lt;/p&gt;&lt;p&gt;49. 아주 솔직한 대화 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/223914764653"&gt;https://blog.naver.com/travis88/223914764653&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 자기 객관화가 100% 되는 사람은 없겠지만, 아예 이것을 시도조차 하지 않으려고 하면 진짜 빠르게 후진 사람이 되고 만다.&lt;/p&gt;&lt;p&gt;50. “몸 아껴쓰면 안돼”…저속노화좌, 정희원 교수의 첫 ‘쉼표’ (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://m.mk.co.kr/news/hot-issues/11356737"&gt;https://m.mk.co.kr/news/hot-issues/11356737&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 나이가 들수록 편안함을 추구하게 된다. 돈과 건강도 쉽고 편하게 얻길 바란다. 그러나 그러한 삶의 태도가 오히려 몸과 마음을 약하게 만드는 원인이 된다. 왜 약간의 불편함을 받아들여야 하는지, 그 불편이 우리 삶에 어떤 긍정적인 영향을 주는지 말씀드리겠다.&lt;/p&gt;&lt;p&gt;51. 살아갈 이유가 있다면 우리는 버텨낼 수 있다 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/alex267/223929473991"&gt;https://blog.naver.com/alex267/223929473991&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 살아가는 이유가 있는 사람은 어떻게든 살아갈 수 있다.&lt;/p&gt;&lt;p&gt;52. 내가 100% 동의하는 글들 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://m.blog.naver.com/lhd1371/223932173123"&gt;https://m.blog.naver.com/lhd1371/223932173123&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 태도가 9할&lt;/p&gt;&lt;p&gt;1. 태도가 좋은 분들은 지금 좀 어렵다 한들. 어느 순간 자신만의 그 무엇으로 극복해 나간다.&lt;/p&gt;&lt;p&gt;2. 태도가 나쁜 분들은 지금 잘 나간다 한들. 어느 순간 저 나락 어딘가에 가 있게 된다.&lt;/p&gt;&lt;p&gt;2. 낮은 확률을 이기는 것은 오랜 기간의 빌드업 뿐&lt;/p&gt;&lt;p&gt;1. 불가능은 없다는 말은 말이 안되는 말이지만, 매우 낮은 확률도 시간만 충분하면 이겨낼 수 있다라는 말로 바꾼다면 맞는 말일 수 있다.&lt;/p&gt;&lt;p&gt;2. 그런데 그걸 하는 게 무슨 의미가 있나, 내 인생에 도움이 되나.. 라는 끝없는 자기 의심과 미래에 대한 신뢰가 없는 사람들은 그런 것들을 장기간 지켜 나가지 못한다.&lt;/p&gt;&lt;p&gt;3. 그 어떤 것도 방관자를 통해 이루어 낼 수는 없다. 그 누가 이루어낸 일도 아무리 높은 성과와 그 어떤 것도 내가 한 게 아니라면 아무 의미가 없다.&lt;/p&gt;&lt;p&gt;4. 결국 내 손으로 내 눈 앞에서 하는 것만이 내 것이며, 그것이 나의 인생이고 가치 있는 것이다.&lt;/p&gt;&lt;p&gt;5. 어떤 사람이 순간적인 결정이나 단시간에 많은 정보 없이 훌륭한 결정을 반복해서 계속 내린다면 그 사람을 대단하다고 생각하지 말고 그 사람이 저렇게까지 된 빌드업된 것을 봐야 한다.&lt;/p&gt;&lt;p&gt;6. 그리고 나도 그 빌드업을 매일 하고 있는지 생각해 봐야 한다.&lt;/p&gt;&lt;p&gt;3. 아무리 작은 성취라도 본인이 직접 스스로 얻은 과정의 경험과 성취가 있는 사람은 본인이 '확률이 높은 쪽'으로 움직이고 노력하면 나아진다는 것을 알고 있고 평소에도 그러한 관점과 태도로 살아갑니다.&lt;/p&gt;&lt;p&gt;4. 그러나 과정과 결과 둘 중 하나만 본인의 노력으로 되었거나 혹은 둘 다 수동적으로 떠밀려서 한 경우에는 선택에 있어서 책임을 지고 싶지 않아 합니다. 그게 기본 태도가 되어버립니다.&lt;/p&gt;&lt;p&gt;5. 결과가 &lt;strong&gt;재능 &lt;em&gt;노력 &lt;/em&gt;운의 함수&lt;/strong&gt;라는 것을 알고 있음에도 불구하고 노력은 애써 무시하고 재능과 운을 탓합니다. 반면 태도와 확률론이 갖춰진 사람은 재능과 운은 내가 어쩔 수 없는 부분이니 노력에 더욱 집중하려고 합니다.&lt;/p&gt;&lt;p&gt;6. 누군가는 나보다 노력을 적게 했더라도 나보다 더 잘 살 겁니다. 그런데 어쩌겠습니까. 그게 내가 노력 안 할 이유는 되지 못합니다.&lt;/p&gt;&lt;p&gt;53. 누구나 벙커를 만들어야 하는 이유 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/223946412567"&gt;https://blog.naver.com/travis88/223946412567&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;54. 유산소 운동이 미래다 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/223940791913"&gt;https://blog.naver.com/travis88/223940791913&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;55. 장동민이 방송하면서 깨달은 것 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/223934987883"&gt;https://blog.naver.com/travis88/223934987883&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;56. 1억 모으기에서 10억까지 투자수익이 원금을 넘어서는 순간들 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://m.blog.naver.com/tkos3333/222762555445"&gt;https://m.blog.naver.com/tkos3333/222762555445&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;57. 후생가외라더니.. (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/pillion21/223947548993"&gt;https://blog.naver.com/pillion21/223947548993&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 다문 다독 다상량(多聞多讀多商量 ) 많이 듣고, 많이 읽고, 많이 생각하라..&lt;/p&gt;&lt;p&gt;58. 누군가의 생각&lt;/p&gt;&lt;p&gt;1. 굉장히 추상적인 레벨의 이야기이긴한데, 결국 제가 하고 싶었던 말은 '이 세상에는 생각보다 다양한 게임과 룰이 존재하고, 결국 각자만의 방법으로 그 게임을 클리어한 사람들이 자산가가 되어있더라'라는 이야기를 하고 싶었습니다. 적어도 지금까지 제가 관찰하기로는 그래요.&amp;nbsp;&lt;/p&gt;&lt;p&gt;2. 그리고 위의 이야기와 이어지는 이야기일 수 있는데, 자산가 분들은 삶의 어느 특정한 부분에서 무언가에 완전히 몰입한 순간들이 있으시더라고요. 그 몰입의 순간을 얼마나 잘 견디고 결과물까지 이어내는가가 자산가로 가는 길이구나 싶었습니다.&amp;nbsp;&lt;/p&gt;&lt;p&gt;3. 여담으로 어느정도 큰 부를 일구신 자산가분들은 어느 순간부터 '의미'에 대해서 깊게 탐색하시는 것 같습니다. 어느 순간부터는 돈을 버는 것 자체에서는 큰 의미를 찾지 못하시더라고요. 그걸 보면서 저는 '사명'은 최대한 일찍 찾고 가지고 있는 것이 바람직하겠구나, 그런 생각을 가지게 되었습니다.&lt;/p&gt;&lt;p&gt;59. 운동선수의 루틴처럼, 투자 성공을 위한 건전한 과정의 중요성 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/jeunkim/223948709730"&gt;https://blog.naver.com/jeunkim/223948709730&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 루틴은 운동선수가 통제할 수 있는 몇 안되는 것들 중 하나이며, 당면한 일에 집중할 수 있도록 해주기 때문입니다.&lt;/p&gt;&lt;p&gt;60. 중요한 건 운이 올 때까지 계속하는 것이다. (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/alex267/223949388282"&gt;https://blog.naver.com/alex267/223949388282&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 운이란 준비가 기회를 만났을 때 생기는 것이다. - 세네카&lt;/p&gt;&lt;p&gt;61. 멘탈 건강을 위해 외워야 할 문장 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/223957663457"&gt;https://blog.naver.com/travis88/223957663457&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 세상엔 다양한 사람이 있다.&lt;/p&gt;&lt;p&gt;62. Paradigm Shifts and the Winner’s Curse (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://stratechery.com/2025/paradigm-shifts-and-the-winners-curse/"&gt;https://stratechery.com/2025/paradigm-shifts-and-the-winners-curse/&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 새로운 기술이 나올 때 자기 것을 지키려는 태도보단, 자기를 완전히 바꾼다는 마음가짐으로 대하는 것이 결국 살아남는 길인 것 같다.&lt;/p&gt;&lt;p&gt;63. 30대, 40대에도 쉼 없이 해야하는 것 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/223961144495"&gt;https://blog.naver.com/travis88/223961144495&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 청춘이 매력적인 근본은, 남아도는 체력에 있다.&lt;/p&gt;&lt;p&gt;2. 무언가를 좋아할 체력, 좋아하는 것에 뛰어들 체력, 뛰어들었다가 실패하고 좌절할 체력, 그 와중에 친구가 부르면 나가 놀 체력, 그래놓고 나는 쓰레기라며 자책할 체력&lt;/p&gt;&lt;p&gt;64. 주식에서 성장과 고독에 관한 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://m.blog.naver.com/going_tothe_moon/223964980098"&gt;https://m.blog.naver.com/going_tothe_moon/223964980098&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 젊었을 때는 몰랐는데 살아보니 쓸데없는 계산하느라, 남들과 비교하느라 힘과 시간을 허비하지 않으면 제법 많은 것을 이룰 수 있더라. 세상의 정말 중요한 일들은 '외로움의 힘'으로 이루어진다. 외로움은 '정말 중요한 일'을 이뤄내는 원동력이라고 생각한다.&lt;/p&gt;&lt;p&gt;2. '돈을 많이 버는 사람들 사이에 끼어 있으면 나도 자연스럽게 돈을 벌게 된다'라는 생각은 반은 맞고 반은 틀린 이야기.&lt;/p&gt;&lt;p&gt;3. 늘 부의 기준을 느낄 수 있는 사람에게 노출되도록 노력하되, 결정적인 성장은 고독에서 이루어져야 한다.&lt;/p&gt;&lt;p&gt;4. 하지만 무리 지어 다니면서 성공한 사람은 없습니다. 뭔가를 배우거나 공부할 때에도 먼저 '홀로서기'를 해야 해요. ... 혼자 오롯이 에너지를 쏟아 몰입하다 보면 언젠가 거기서 성과를 얻게 돼요. 고독한 시간을 단지 좌절하며 보내는 사람과, 이를 자양분 삼는 사람의 차이가 여기서 벌어지기 시작합니다.&lt;/p&gt;&lt;p&gt;65. 세계 최고의 가르침 한 줄 요약 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/223971159903"&gt;https://blog.naver.com/travis88/223971159903&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 인생은 원래 괴롭고, 욕심부리면 더 괴로움&lt;/p&gt;&lt;p&gt;66. 힘든 일을 겪고 있다면 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/gmyhhj/223979997739"&gt;https://blog.naver.com/gmyhhj/223979997739&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 먼저 힘든 감정의 소용돌이에서 최대한 빨리 빠져나와야 한다.&lt;/p&gt;&lt;p&gt;2. &amp;lt;에스키모의 막대기 전략&amp;gt; 활용하기&lt;/p&gt;&lt;p&gt;3. 해결해야 할 핵심에 집중해야 한다.&lt;/p&gt;&lt;p&gt;4. 해당 사건과 자신을 절대 동일시 해서는 안 된다.&lt;/p&gt;&lt;p&gt;67. 어떤 글&lt;/p&gt;&lt;p&gt;1. 무엇이든 일단 만들고 보세요. 그냥 시도하세요. 실패하는 것을 두려워하지 마세요. 왜냐하면 실패는 바로 성공의 씨앗이기 때문입니다. 모든 실패를 통해 '실용적인 배움'을 얻는 것, 그것이 바로 성공이에요. 우리 주변에도 '절대 배우지 않는' 사람, 혹은 세 번은 넘어져야 겨우 제대로 가는 사람 꼭 있잖아요.&lt;/p&gt;&lt;p&gt;68. 뻔하지만, 가장 중요한 것 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/223980981224"&gt;https://blog.naver.com/travis88/223980981224&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 정말 뻔하지만, 가장 중요한 것이 결국 불편한 것을 얼마나 자주, 얼마나 밀도 높은 방식으로 감수했느냐는 것임.&lt;/p&gt;&lt;p&gt;2. 지금 누군가가 가치 있는 것을 누리고 있다면, 그 결과만 보고 끝낼 것이 아니라 저 사람이 그 과정에서 과연 어느 정도로 불편함을 감수했을지도 함께 고려해서 봐야 한다. 그게 본질이니까.&lt;/p&gt;&lt;p&gt;3. 불편함을 기꺼이 받아들이고 내성을 쌓고 그 불편함이 빚어내는 가치들에서 보람과 충만함을 느끼면서 살다 보면 중장기적으로 보면 오히려 삶은 더 편해지는 방식으로 가긴 함. 반대로, 지금 이 순간의 편안함에 안주하다 보면 오히려 후반부엔 불편함의 골짜기로 떨어질 수도 있음.&lt;/p&gt;&lt;p&gt;69. 워런버핏&lt;/p&gt;&lt;p&gt;1. "인생에서 아주 몇 가지 일만 제대로 해내면 됩니다. 너무 많은 잘못만 하지 않는다면요."&lt;/p&gt;&lt;p&gt;2. 즉, 인생에서 모든 것을 완벽히 맞힐 필요는 없습니다. 몇 번만 제대로 해내면 되고, 중요한 것은 치명적인 실수를 반복하지 않는 것입니다.&lt;/p&gt;&lt;p&gt;3. 10번 중 7번 정도만 제대로 맞혀도 충분히 훌륭한 성과를 거둘 수 있습니다. 버핏이 수차례 강조해온 핵심은 완벽함이 아닌, 확률적 사고와 장기적 누적 승률입니다.&lt;/p&gt;&lt;p&gt;4. 결국 목표는 완벽히 맞히는 것이 아니라, 더 자주 맞히는 것입니다.&lt;/p&gt;&lt;p&gt;70. "불변의 법칙" 중에서&lt;/p&gt;&lt;p&gt;1. 사랑이든 일이든 투자든, 우리 인생에서 중요한 것들은 &lt;strong&gt;인내심&lt;/strong&gt;과 &lt;strong&gt;희소성&lt;/strong&gt;이 있어야 가치 있는 뭔가가 된다. 인내심을 지녀야 그것이 성장하는 것을 지켜볼 수 있고, 희소성이 있어야 그것의 소중함을 느끼며 감사할 수 있다.&lt;/p&gt;&lt;p&gt;71. 귀멸의 칼날 무한성&lt;/p&gt;&lt;p&gt;1. "중요한건 올바른 호흡과 올바른 움직임, 최소한의 동작으로 최대한의 힘을 끌어내는 거란다. 그렇게 하면, 점점 머릿속이 투명해져. 많은 것들을 다 외우고 흡수한 후에는 불필요한 것들을 깎아내는 거야."&lt;/p&gt;&lt;p&gt;2. 그때, 광명이 비추고 길이 펼쳐져. 힘이 닿는 한, 발버둥 치고 괴로워했기에 비로소 도달한 '영역'이야.&lt;/p&gt;&lt;p&gt;72. taste (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://www.canda.blog/taste/"&gt;https://www.canda.blog/taste/&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 다양한 분야에서 좋은 taste를 가지는 것이 중요하겠지만, 어느 분야든 산출물(artifact)을 내는 과정을 거친다고 한다면, 같은 일을 하지만 어떤 사람의 artifact는 훌륭하고 어떤 사람의 artifact는 그저 그렇다(bland). 결국 그 일을 하는 사람의 어떠한 감각 같은 것이 작용하여 차이를 만드는 것이 아닐까 싶다.&lt;/p&gt;&lt;p&gt;2. 그런데 좋은 taste는 어떻게 기를 수 있을까? 취향과 맛에 대한 감각은 타고나는 것이 사실 없다고 생각하는 편이다. 누구든 지나온 경험과 환경을 통해 감각을 기른다고 생각한다. 그러나 단순하게 많이 보고 많이 듣고 경험하는 것으로는 충분하지 않다. 좋은 taste는 의식적인 훈련의 결과물이다. 좋은 것이 왜 좋은지를 설명하고자 하는 스스로의 사유와 남들과의 교류를 통해 형성되어 간다.&lt;/p&gt;&lt;p&gt;3. 좋은 taste를 가진 사람은 어떤 대상에 대한 열의를 가진 사람이다. 어떤 분야에 대해 열의 없는 사람이 그 분야에 있어 좋은 taste를 가질 수는 없다.&lt;/p&gt;&lt;p&gt;73. 인생을 잘 사는 기본 원칙(&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/223989872298"&gt;https://blog.naver.com/travis88/223989872298&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 모든 것을 문제 -&amp;gt; 문제 해결의 방식으로 취득하는 것&lt;/p&gt;&lt;p&gt;74. 메리츠 김용범 부회장의 젊은 투자자를 위한 조언 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://x.com/tjgoongye/status/1961781111220318401?s=46"&gt;https://x.com/tjgoongye/status/1961781111220318401?s=46&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 투자는 지능보단 기질의 문제.&amp;nbsp;&lt;/p&gt;&lt;p&gt;2. 두려움과 공포를 얼마나 잘 다루느냐의 싸움이다.&lt;/p&gt;&lt;p&gt;3. 매일 운동하기. 1시간에서 2시간씩 20년간 했다.&lt;/p&gt;&lt;p&gt;4. 자기전 20분동안 명상.. 편도체 안정으로 공포를 컨트롤하는데 도움&lt;/p&gt;&lt;p&gt;5. 말버릇을 의식적으로 바꿀 것. (어두워지면 불을 켜라, 힘든 일은 없다 등..&amp;nbsp;)&lt;/p&gt;&lt;p&gt;6. 매일 책을 보려고 노력하고, 지식을 습관처럼 쌓기&lt;/p&gt;&lt;p&gt;7. 마법같은 비법은 역시 없었다. 그냥.. 매일 치열하게 생각하면서 노력하면 된다.&lt;/p&gt;&lt;p&gt;75. 예민한 사람이 살아남는 방법 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/223991026826"&gt;https://blog.naver.com/travis88/223991026826&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 문제는 쓸데없이 뇌 에너지를 낭비하다 보면 정작 중요한 건 다 놓친다는 것. 그래서 예민할수록 불필요한 생각을 최대한 덜어내는 게 핵심인데, 그나마 내가 생각한 좋은 방법들.&lt;/p&gt;&lt;p&gt;1. 경제적으로 생각하기&lt;/p&gt;&lt;p&gt;2. 우주적으로 생각하기&lt;/p&gt;&lt;p&gt;3. 진짜 중요한 것에 집중하기&lt;/p&gt;&lt;p&gt;76. 사람이 구려지는 이유 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/223990973848"&gt;https://blog.naver.com/travis88/223990973848&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 잠을 많이 잔다.&lt;/p&gt;&lt;p&gt;77. 이 원칙들이 있어야 한다 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/223990239710"&gt;https://blog.naver.com/travis88/223990239710&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 누군가에게 부정적인 생각이 들 때 '나라면 안 그럴 텐데.'라는 생각보다는 '그게 네 삶의 방식이구나, 재밌게 사네.'라고 생각하는 것이 훨씬 인생을 살 맛 나게 합니다.&lt;/p&gt;&lt;p&gt;78. 블라인드&lt;/p&gt;&lt;p&gt;1. "종착점에 거의 다다라보니 참 많은 게 허무하고 또 낮아 보입니다. 그렇게나 불가능해 보였었던 많은 일들은 사실 충분히 넘을 수 있는 담장이었고 할 수 있는 것들이었습니다. 잠시 부끄럽고 민망하고 곤란할 것 같아도 그냥 해도 됩니다. 내일이 있다는 건 축복이에요"&lt;/p&gt;&lt;p&gt;79. 누군가&lt;/p&gt;&lt;p&gt;1. 극단적인 결정이 좋은 결정일 가능성은 희박함&lt;/p&gt;&lt;p&gt;80. 행복은 만드는 것 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/gmyhhj/223998252818"&gt;https://blog.naver.com/gmyhhj/223998252818&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 우리는 행복의 조건을 찾는다. 행복은 조건이 존재함과 동시에 불행의 씨앗이 된다.&lt;/p&gt;&lt;p&gt;2. 행복은 조건이 아니다. 행복은 찾고 만들고 의미를 부여하고 감사할 줄 아는 것이다.&lt;/p&gt;&lt;p&gt;3. 일상에서 조건없이 행복을 느낄 수 있는 사람이야말로 지속적으로 행복을 느낄 수 있다.&lt;/p&gt;&lt;p&gt;81. 적절한 밸런스 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/gmyhhj/223997801663"&gt;https://blog.naver.com/gmyhhj/223997801663&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 행복 = 소유 / 욕망&lt;/p&gt;&lt;p&gt;2. 소유한 것이 많더라도 욕망이 더 크면 행복하지 못하고 소유한 것이 적더라도 욕망이 더 적으면 행복해지는 것이다. 이 둘의 밸런스를 적절하게 유지하는 것이 무엇보다 중요하다.&lt;/p&gt;&lt;p&gt;82. 누군가&lt;/p&gt;&lt;p&gt;1. 우위를 점할 수 있는 유일한 방법은 오랫동안 열심히 노력하는 것입니다. 좋아하는 일을 하다 보면 자연스럽게 그 일을 하게 되고, 쉬는 동안에도 항상 그 일에 대해 생각하게 됩니다. 이렇게 자연스럽게 몸에 배면 시간이 지나면서 큰 이점을 쌓을 수 있습니다.&lt;/p&gt;&lt;p&gt;83. 소소일상&lt;/p&gt;&lt;p&gt;1. 산다는 건 이기고 지고의 문제가 아니야. 그런 건 정말 하찮은 문제라니까. 중요한 건 말이야. 스스로 납득할 만한 좋은 사람이 되는 거지. 제대로 된 눈빛을 만들어 가는 거라구.&lt;/p&gt;&lt;p&gt;84. 차분하게 괴로운 시간을 이겨내는 몇가지 생각들 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/lhd1371/224010442287"&gt;https://blog.naver.com/lhd1371/224010442287&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 인생이 버거운 이유 중 하나는, 모르는데 아는 척하고 없는데 가진 척하고 안 친한데 친한 척하고 힘든데 안 힘든 척하기 때문이다.&lt;/p&gt;&lt;p&gt;2. 척하는 인생은 에너지가 몇 배가 든다.&lt;/p&gt;&lt;p&gt;85. 낯선 여성분과의 출근길 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/tkos3333/224011667262"&gt;https://blog.naver.com/tkos3333/224011667262&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 사람이 여유가 없으면 작은일에도 짜증나고, 지치고, 서운해진다. 행복하려면 어느 정도 여유가 있어야 한다.&lt;/p&gt;&lt;p&gt;86. 인생에서 만날 수 있는 귀인 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/224028187271"&gt;https://blog.naver.com/travis88/224028187271&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 나에게 새로운 세계를 소개해 주는 사람들&lt;/p&gt;&lt;p&gt;87. 이런 말을 무시해야 잘 산다. (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/224028177602"&gt;https://blog.naver.com/travis88/224028177602&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 사람들은 꼭 이유를 따진다니까. 그냥 하면 안 됩니까?&lt;/p&gt;&lt;p&gt;88. 운동의 중요성 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://x.com/esprecchiato/status/1974989228858630185"&gt;https://x.com/esprecchiato/status/1974989228858630185&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 운동은 하루 빡세게보다 매일 꾸준히가 더 효과적이다.&lt;/p&gt;&lt;p&gt;2. 하루 30분만 땀 흘려도 뇌 건강이 달라진다.&lt;/p&gt;&lt;p&gt;3. 운동은 체중보다 멘탈을 먼저 바꾼다.&lt;/p&gt;&lt;p&gt;4. 체력은 돈으로 살 수 없다.&lt;/p&gt;&lt;p&gt;5. 꾸준함이 진짜 실력이다.&lt;/p&gt;&lt;p&gt;89. 아이러니 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://x.com/4_wonderfullife/status/1974843016667160690"&gt;https://x.com/4_wonderfullife/status/1974843016667160690&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 운동을 못할수록 더 운동을 해야 하고 보는 눈이 없으면 더욱 더 많은 것을 보면서 눈을 길러야 하고 말을 잘 못하면 그만큼 더 말할 기회를 가져야 하고 사회생활을 못할수록 사회에 스스로를 더 자주 내던져야 한다는 것이..&lt;/p&gt;&lt;p&gt;2. 못한다고 피하면 점점 더 못해질 수 밖에 없다는 것이 인생의 아이러니&lt;/p&gt;&lt;p&gt;90. 트레이더의 운이란? (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://m.blog.naver.com/song7639/224032232798"&gt;https://m.blog.naver.com/song7639/224032232798&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 확률적 노출을 늘리는 것 (Exposure Theory)&lt;/p&gt;&lt;p&gt;1. 운이 좋은 사람의 특징은&lt;/p&gt;&lt;p&gt;1. 더 많은 사람, 정보, 사건에 노출됨&lt;/p&gt;&lt;p&gt;2. 정보를 개방적으로 인식함&lt;/p&gt;&lt;p&gt;3. 실패를 빠르게 샘플링함&lt;/p&gt;&lt;p&gt;2. 인지적 편향을 통제하는 것 (Cognitive Bias Calibration)&lt;/p&gt;&lt;p&gt;1. 운이 나쁜 사람은 같은 사건을 비관적으로 해석하는 경향이 있다.&lt;/p&gt;&lt;p&gt;3. 감정 에너지의 관리 (Affect Regulation)&lt;/p&gt;&lt;p&gt;1. 운이 좋은 사람일수록 감정적 안정성과 에너지 일관성이 높다.&lt;/p&gt;&lt;p&gt;91. 과거의 10년 앞으로 10년 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/lhd1371/224033919463"&gt;https://blog.naver.com/lhd1371/224033919463&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;92. 세상에 정답이라고 알려진 방향들과 내가 원하는 방향이 일치하지 않을때.. 어떻게 해야 할까?&lt;/p&gt;&lt;p&gt;93. 살명서 깨달은 것 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/224034880340"&gt;https://blog.naver.com/travis88/224034880340&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 된다고 생각하고 갔을 경우 -&amp;gt; 될 수도 있고, 안될 수도 있음.&lt;/p&gt;&lt;p&gt;2. 시작할 때부터 안 된다고 생각하는 경우 -&amp;gt; 매우 높은 확률로 진짜 안 된다.&lt;/p&gt;&lt;p&gt;94. Do not fear&lt;/p&gt;&lt;p&gt;95. 인생 편해지는 태도 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/224036503899"&gt;https://blog.naver.com/travis88/224036503899&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 인생을 너무 심각하게 받아들이지 마라&lt;/p&gt;&lt;p&gt;96. 하수라고 느껴지는 사람 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/224036513894"&gt;https://blog.naver.com/travis88/224036513894&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 표정 관리가 아예 안 되는 사람&lt;/p&gt;&lt;p&gt;97. 모든 문제의 본질 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/224039292182"&gt;https://blog.naver.com/travis88/224039292182&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 인간에게 일어나는 거의 모든 문제의 본질이 있음. -&amp;gt; 너무 큰 기대&lt;/p&gt;&lt;p&gt;98. No winter lasts forever; no spring skips its turn. (영원한 겨울은 없고, 봄은 자신의 차례를 거르지 않는다.)&lt;/p&gt;&lt;p&gt;99. 직업으로서의 투자자(하루키의 '직업으로서의 소설가' 를 읽고) (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://m.blog.naver.com/travelingcompanion/224045296627"&gt;https://m.blog.naver.com/travelingcompanion/224045296627&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 판단 보다는 관찰&lt;/p&gt;&lt;p&gt;2. 꾸준한 몸관리&lt;/p&gt;&lt;p&gt;100. 심플하지만 강력한 마인드 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/224046377433"&gt;https://blog.naver.com/travis88/224046377433&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 굳이 신경을 쓸 필요가 없는 것에 신경을 쓰지 않는 것&lt;/p&gt;&lt;p&gt;101. 인생이 정말 답답하고 벽을 마주쳤을때 당장은 답이 안보이더라도 하루하루 잘 먹고 잘 자고 좋은 책 읽고 좋은 분들 만나서 이야기하면서 버티다보면 좋은시절이 오는게 아니라 나쁜시절은 가더라구요..&lt;/p&gt;&lt;p&gt;102. 공짜로 돈을 뿌리는 것 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/224055338406"&gt;https://blog.naver.com/travis88/224055338406&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. "즉각적으로 나의 기분을 좋게 하는 것" 대다수가 중장기적으로 보면 나를 망치는 것들임&lt;/p&gt;&lt;p&gt;103. 40대 아저씨가 돈으로 진정 사고 싶은 것들 (자산 배분. 등산) (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://cafe.naver.com/vilab/274934?tc=shared_link"&gt;https://cafe.naver.com/vilab/274934?tc=shared_link&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 자산배분을 할 때 양적 성장에 집중해서 수익률이나 자본효율만을 고려하기 보다는 &lt;strong&gt;나에게 중요한 가치를 충족시켜주는 방향으로 돈이 자연스럽게 흘러갈 수 있도록 틀을 만드는 것&lt;/strong&gt;입니다.&lt;/p&gt;&lt;p&gt;2. 건물을 지을 때 사용자의 일상, 취향, 동선 등에 맞는 공간을 구상하고 거기에 맞는 설계도를 먼저 그린 후에 설계도 대로 먹줄을 튕기고 거푸집(형틀)을 만들어 콘크리트를 붓습니다. 이제 콘크리트가 굳으면 처음 머릿속에서 구상한 공간들이 현실로 구현이 됩니다. 똑같은 콘크리트를 붓더라도 설계도에 따라 그 건물에서 사는 사람들은 전혀 다른 느낌으로 다른 삶을 살게 됩니다.&lt;/p&gt;&lt;p&gt;3. 아저씨는 머릿속에서 앞으로 살고 싶은 삶의 방식을 상상해보고, 노동과 투자로 벌게 될 돈이 그런 삶의 방식을 구현하는데 흘러갈 수 있도록 설계도를 그려봅니다. 이제 먹줄을 튕기고 거푸집을 대고 나면, 힘들기도 하고 재밌기도 한 일상의 노동과 투자의 펌프가 루틴하게 작동하면서 콘크리트를 거푸집 안으로 내뿜어 줍니다.&lt;/p&gt;&lt;p&gt;4. &lt;strong&gt;굳기 전에는 똑같은 콘크리트(돈) 이지만, 거푸집의 모양에 맞게 단단하게 굳어지고 나면 모두 다른 각양각색의 공간과 의미를 만들어 냅니다. '나의 돈은 어떤 모양으로 굳어져서 어떤 공간과 의미를 만들어 낼까'&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;104. 프리드리히 니체&lt;/p&gt;&lt;p&gt;1. 인간은 단순히 살아 있는 존재가 아니라, 자신을 넘어서는 존재다.&lt;/p&gt;&lt;p&gt;2. 오늘의 나를 어제보다 낫게 만드는 일, 그것이 삶의 본질이다. 세상은 너를 정의하려 하고, 너를 규정하려 한다. 그러나 그때마다 저항하라. 네가 내일도 어제와 같은 인간이라면, 그것이야말로 가장 큰 비극이다. 인간은 자신을 초월하지 않으면 곧 썩는다.&lt;/p&gt;&lt;p&gt;105. 돈 쓰는 법. wide funnel, tight filter! (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://m.blog.naver.com/mynameisdj/224063101077"&gt;https://m.blog.naver.com/mynameisdj/224063101077&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 가장 좋은 독서법은 많이 시도하고, 강력한 필터로 걸러내는 것이다.&lt;/p&gt;&lt;p&gt;2. 좋아할지 안 할지는 시도해 보기 전에는 절대 알 수 없다. 그래서 가능한 한 많이 시도해야 하고, 동시에 즉시 거절하는 필터가 필요하다.&lt;/p&gt;&lt;p&gt;3. "당신이 좋아하는 것에는 아낌없이 쓰되, 좋아하지 않는 것에는 무자비하게 잘라내라" 예컨대 옷을 좋아하지만 차에는 관심이 없어서, 부자처럼 옷을 입지만 차는 돈 아끼는 사람처럼 탄다.&lt;/p&gt;&lt;p&gt;4. 여행, 음식, 경험, 스포츠, 의류 뭐든 지금보다 조금 더 써보라. 하지만 행복을 주지 않는다면 즉시 멈춰라. 나쁜 책을 던져버리는 것처럼. 충분히 반복하다 보면, 당신에게 딱 맞는 "이상하지만 특별한 것"을 발견하게 될 것이다. 동시에 불필요한 소비는 잘라내고, 행복을 주는 소비에 돈을 쓸 수 있게 된다.&lt;/p&gt;&lt;p&gt;106. 자기 몸을 돌보는 의무&lt;/p&gt;&lt;p&gt;1. 삶을 바꾸는 과정은 완전히 하나이다. 몸과 마음을 나눠 생각하는 잘못된 착각에 빠진 우리 현대인과 달리, 그리스 현자들은 육체와 영혼의 탁월성을 똑같이 함께 추구했다.&lt;/p&gt;&lt;p&gt;2. "체력과 정신력이 조화롭게 집중되면, 삶은 저 스스로 힘을 얻는다." 단순한 이 사실이야말로 어쩌면 우리가 알아야할 궁극의 인생 지혜인 듯 싶다.&lt;/p&gt;&lt;p&gt;107. 기분 관리를 잘 하는 사람들 특징 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/224070259007"&gt;https://blog.naver.com/travis88/224070259007&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 기분이 태도가 되면 안 된다.&lt;/p&gt;&lt;p&gt;108. 의존하면 망한다 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/224076837754"&gt;https://blog.naver.com/travis88/224076837754&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 롤 모델, 멘토의 장점을 배우고 그것을 자신만의 방식으로 재창조하는 것임. 결국에는 누구나 자신의 것을 만들어야 하니까. 물론 초반에는 복붙 수준으로 배우는 것이 중요하지만.&lt;/p&gt;&lt;p&gt;2. 결국 본질적인 건 '나 자신'의 레벨을 올리는 것. 멘토, 롤모델 전략을 쓴다고 해서 상대에게 나의 모든 것을 의탁해서는 안 됨. 부작용이 너무 큼&lt;/p&gt;&lt;p&gt;109. 행운이란, 준비가 기회를 만나는 것이다. - 세네카&lt;/p&gt;&lt;p&gt;110. 사람을 평가하는 중요한 기준 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/travis88/224111453939"&gt;https://blog.naver.com/travis88/224111453939&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 저 사람은 자신의 말에 책임을 질까?&lt;/p&gt;&lt;p&gt;111. 무엇이든 시작하기 전이 가장 어렵죠.&lt;/p&gt;&lt;p&gt;1. 시작하는 순간 대부분의 두려움은 사라진다는걸. 일단 해보는게 중요하다는 것을요.&lt;/p&gt;&lt;p&gt;2. 시작이 거창할 필요는 없습니다. 중요한 건 시도하고, 다시 시도하는 것 입니다. 아주 작은 발걸음 하나가 새로운 풍경을 열어주기 때문입니다.&lt;/p&gt;&lt;p&gt;3. 두려움은 늘 시작 앞에 있습니다. 그리고 그 시작의 주변에는 귀찮음도 있죠. 하지만 그 시작 뒤에는, 우리가 아직 보지 못한 세계가 기다리고 있다는 걸 잊지 말아야 합니다.&lt;/p&gt;&lt;p&gt;112. 천재 소설가가 말하는 시간을 현명하게 보내는 방법 (ft. 밀란 쿤데라 '느림') (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://www.youtube.com/watch?v=gVxFSNjc58s"&gt;https://www.youtube.com/watch?v=gVxFSNjc58s&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 인간은 무언가를 기억하려 할 때 본능적으로 행동을 늦추지만, 반대로 어떤 일을 빨리 잊고 싶을 때는 발걸음을 재촉합니다. 느림은 내면을 살피고 경험을 온전한 기억으로 엮어내는 과정인 반면, 빠름은 시간의 흐름을 분절시켜 경험을 휘발시키고 망각을 가속화하는 힘을 가집니다.&lt;/p&gt;&lt;p&gt;2. 삶이 앙상해지지 않고 의미 있는 기억들로 채워지기 위해서는, 의도적으로 속도를 늦추고 순간을 음미하는 '느림의 시간'을 회복해야 한다.&lt;/p&gt;&lt;p&gt;113. 삶이라는 멋진 모순, 우연과 노력 사이에서 ('25년 상반기 투자와 생각) (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.naver.com/r123k120/223916981750"&gt;https://blog.naver.com/r123k120/223916981750&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. 무질서에 대한 생각&lt;/p&gt;&lt;p&gt;1. 이 세상은 무질서하고 우리 개인이 이룩한 것들 역시 헐거운 질서의 역학에서 아주 '우연'하게 파생한 산물이라고 느끼는 것이 사실이다.&lt;/p&gt;&lt;p&gt;2. 운명을 움켜쥐려는 것보다 자연스러움 속에서 일상을 의미 있는 것들로 채우고 'Let it be'하는 달관의 자세에 대해 다시 한 번 생각해보면 어떨까.&lt;/p&gt;&lt;p&gt;3. 우리에게 주어진 일상과 자유의 소중함이 얼마나 위대하고, 또 그저 개개인의 노력으로만 얻어진 것이 아님에 대한 생각&lt;/p&gt;&lt;p&gt;2. 모순에 대한 생각&lt;/p&gt;&lt;p&gt;1. 뒤바뀐 인과관계의 오류(Fallacy of reversed casuality). 원인 -&amp;gt; 결과의 관계를 결과 -&amp;gt; 원인으로 혼동하여 사용하는 경우를 일컫는다.&lt;/p&gt;&lt;p&gt;2. 무심과 성의&lt;/p&gt;&lt;p&gt;3. 그냥 하는 것(Just doing it)이 무심과 결을 같이 하지 않나 한다. 사실 그냥 하는 행위를 지속적으로 영위하기 위해서는 지루함을 느끼지 않아야 되며, 업에 대한 몰입과 진정성, 그리고 깊은 애정이 필요하다. 따라서 무심을 이어나가는 이들은 필연 성의를 갖게 될 수 밖에 없을 것이다.&lt;/p&gt;&lt;p&gt;3. 진정성&lt;/p&gt;&lt;p&gt;1. 주위에 투자 또는 다른 영역에서 성공한 이들을 보면 빠짐없이 진정성을 가지고 있음을 보게 된다. 투자에 있어서도 과거에 어떠한 분야에 진정성 있게 몰입하여 성과를 이뤄본 경험이 있는 이들이 더 빛을 발하는 모습을 보게 돼 곤 한다.&lt;/p&gt;&lt;p&gt;4. 운&lt;/p&gt;&lt;p&gt;1. 인생은 B(Birth)와 D(Death) 사이의 C(Choice)라는 잘 폴 사르트의 명언을 좋아하는데, 얼마 전 C(Coincidence)를 넣어야 한다는 지적호기심님의 블로그 글을 봤다. 정말이지, 인생의 선택만큼이나 운과 '우연'은 우리의 삶을 깊숙히 지배하고 있다. 그리고 이것은 무질서와 모슨의 필욘적인 존재와도 연결이 되어있다.&lt;/p&gt;&lt;p&gt;114. 알면 불편하지만, 그래도 아셔야 하는.. 한국 부동산의 자본주의 원리 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://m.slrclub.com/v/free/41426285"&gt;https://m.slrclub.com/v/free/41426285&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;115. "하루하루는 성실하게, 인생 전체는 되는대로" | 영화평론가 이동진 1부 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://www.youtube.com/watch?v=2YLonjnptTw"&gt;https://www.youtube.com/watch?v=2YLonjnptTw&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. &lt;strong&gt;1. 인생관: "하루하루는 성실하게, 인생 전체는 되는대로"&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;1. &lt;strong&gt;의미:&lt;/strong&gt;&amp;nbsp;젊은 시절에는 인생 전체를 성실하게 살면 특정 목적지에 도달할 것이라 믿었지만, 지금은 인간의 힘보다 외부의 힘(운명, 우연)이 훨씬 강함을 인정합니다.&lt;/p&gt;&lt;p&gt;2. &lt;strong&gt;태도:&lt;/strong&gt;&amp;nbsp;인간이 통제할 수 있는 것은 기껏해야 '하루' 정도입니다. 하루하루 성실히 살되, 인생의 거대한 흐름이나 결과는 내가 어쩔 수 없음을 받아들이고 순응하겠다는 뜻입니다.&lt;/p&gt;&lt;p&gt;116. 재능과 꿈에 대하여 (&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://www.youtube.com/watch?v=vGzEXcB2KXw"&gt;https://www.youtube.com/watch?v=vGzEXcB2KXw&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;1. &lt;strong&gt;3. 재능에 대한 새로운 정의&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;- &lt;strong&gt;"재능은 해보지 않은 사람들만이 매달리는 허상이다."&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;- 프로 작가들은 "이 일을 하기 위해 재능이 있는가?"를 묻기보다, "내가 가진 것 중 무엇을 써서 이 일을 해낼까?"를 고민합니다.&lt;/p&gt;&lt;p&gt;- 특정 직업을 갖는 것 자체에는 압도적인 재능이 필요하지 않을 수 있습니다. 재능은 '세계 1등'이나 '올림픽 금메달' 같은 극단의 목표를 이룰 때 필요한 요소일 뿐입니다.&lt;/p&gt;&lt;p&gt;2. &lt;strong&gt;4. 과정 자산의 힘&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;- 작가는 학원 강사, 모델, 라디오 진행 등 다양한 아르바이트 경험이 작가 생활에 모두 도움이 되었다고 말합니다.&lt;/p&gt;&lt;p&gt;- 실패나 거절의 경험도 단순히 버려지는 비용이 아니라, 다음 단계로 나아가기 위한 '과정 자산'으로 해석하고 활용해야 합니다.&lt;/p&gt;</description><pubDate>Mon, 22 Dec 2025 09:02:00 +0900</pubDate><guid>http://blex.me/@mildsalmon/2025%EB%85%84%EB%8F%84%EC%97%90-%EC%9D%BD%EC%97%88%EB%8D%98-%EA%B2%83%EB%93%A4</guid></item><item><title>리액트 18 동시성 렌더링</title><link>http://blex.me/@baealex/react-18-concurrent-feature</link><description>&lt;p&gt;이전 버전의 React(17 이하)에서 렌더링은 ‘동기적’이고 ‘중단 불가능한’ 작업이었다. 한 번 렌더링이 시작되면, 그 작업이 끝날 때까지 메인 스레드는 블로킹된다. 이는 복잡한 UI를 계산하는 동안 사용자가 버튼을 클릭하거나 타이핑을 해도 애플리케이션이 즉각 반응하지 못하는 멈춤 현상의 주원인이었다.&lt;/p&gt;&lt;p&gt;React 18은 이러한 문제를 해결하기 위해 동시성 렌더링을 도입했다. 핵심은 렌더링 자체를 빠르게 하는 것이 아니라, 렌더링을 중단하고 중요한 작업을 먼저 처리할 수 있는 ‘반응성’을 확보하는 데 있다.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;매 상태 변경 마다 지나치게 무거운 렌더링 작업이 실행되는게 아니라면 크게 사용할 일은 없을 것이다. 다만 그런 상황에 대한 해결 방안이 생겼다는 것이 중요한 부분이다.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;React 18은 상태 업데이트를 우선순위에 따라 두 가지로 분류한다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Urgent updates (긴급 업데이트): 사용자의 직관적인 행동과 관련된 업데이트 (예: 타이핑, 클릭, 드래그). 즉각적인 반응이 없으면 사용자는 앱이 고장 났다고 느낀다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Transition updates (전환 업데이트): 화면의 전환이나 데이터 시각화처럼 즉각적이지 않아도 되는 업데이트 (예: 검색 결과 목록 렌더링, 탭 전환).&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;figure data-border="true" data-border-radius="16" style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center; border: 1px solid rgb(229, 231, 235); border-radius: 16px; overflow: hidden;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2025/12/14/2025121420_siWELXWEzi0icpjvXvu6.png" alt="image.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;대표적인 예로 검색 UI를 들 수 있다. 검색창에 글자를 입력하는 행위는 Urgent하게 처리되어야 하고, 입력값에 따라 아래에 추천 검색어를 띄워주는 행위는 Transition으로 처리되어야 한다. 추천 검색어 렌더링 때문에 타이핑이 끊기는 경험은 치명적인 UX 저하를 야기하기 때문이다.&lt;/p&gt;&lt;p&gt;React 18은 무거운 작업을 Transition으로 분류하여, 긴급한 상호작용이 발생하면 진행 중이던 렌더링 작업을 일시 중단하거나 폐기하고 급한 작업부터 처리한다. 이를 제어하기 위해 3가지 주요 API가 도입되었다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;startTransition&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;useTransition&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;useDefferedValue&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;h4&gt;startTransition &amp;amp; useTransition&lt;/h4&gt;&lt;p&gt;&lt;code&gt;startTransition&lt;/code&gt;은 특정 상태 업데이트를 Transition(낮은 우선순위)으로 마킹하는 API다. 함수형 컴포넌트에서는 주로 &lt;code&gt;isPending&lt;/code&gt; 상태값과 함께 &lt;code&gt;useTransition&lt;/code&gt; 훅을 사용한다.&lt;/p&gt;&lt;pre&gt;&lt;code class="language-javascript"&gt;import { useState, useTransition } from 'react';

const TabContainer = () =&amp;gt; {
    const [tab, setTab] = useState('about');
    const [isPending, startTransition] = useTransition();

    const selectTab = (nextTab) =&amp;gt; {
        // 탭 변경 상태 업데이트를 낮은 우선순위로 감싼다.
        startTransition(() =&amp;gt; {
            setTab(nextTab);
        });
    }

    return (
        &amp;lt;div&amp;gt;
            {/* isPending을 통해 구형 데이터가 유지되는 동안 로딩 상태를 보여줄 수 있다 */}
            {isPending &amp;amp;&amp;amp; &amp;lt;Spinner /&amp;gt;} 
            &amp;lt;TabContent tab={tab} /&amp;gt;
        &amp;lt;/div&amp;gt;
    );
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이 코드가 적용되면, 사용자가 탭을 전환하는 도중 다시 다른 탭을 클릭할 경우 React는 이전 탭의 렌더링을 즉시 중단하고 최신 요청을 처리한다. 결과적으로 앱은 항상 반응하는 상태를 유지한다.&lt;br&gt;&lt;/p&gt;&lt;h4&gt;useDefferedValue&lt;/h4&gt;&lt;p&gt;&lt;code&gt;useTransition&lt;/code&gt;이 상태 업데이트를 감싸는 방식이라면, &lt;code&gt;useDeferredValue&lt;/code&gt;는 값 자체의 업데이트를 지연시킨다. 주로 props로 전달받은 데이터나, 직접 제어할 수 없는 상태를 기반으로 파생된 값을 다룰 때 유용하다.&lt;/p&gt;&lt;pre&gt;&lt;code class="language-javascript"&gt;import { useState, useDeferredValue } from 'react';

const SearchPage = () =&amp;gt; {
     const [query, setQuery] = useState('');
     // query는 즉시 업데이트되지만, deferredQuery는 여유가 있을 때 업데이트된다.
     const deferredQuery = useDeferredValue(query);

     return (
        &amp;lt;&amp;gt;
           &amp;lt;input value={query} onChange={(e) =&amp;gt; setQuery(e.target.value)} /&amp;gt;
           {/* 무거운 리스트 컴포넌트에는 지연된 값을 전달한다 */}
           &amp;lt;HeavyResultList query={deferredQuery} /&amp;gt;
        &amp;lt;/&amp;gt;
     );
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;Debounce/Throttle과의 차이&lt;/h3&gt;&lt;p&gt;&lt;code&gt;useDeferredValue&lt;/code&gt;는 흔히 Debounce(일정 시간 대기)와 비교되지만, 동작 원리가 다르다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Debounce&lt;/strong&gt;: 무조건 정해진 시간(예: 300ms)을 기다린다. 고사양 기기에서도 불필요한 딜레이가 발생한다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;useDeferredValue&lt;/strong&gt;: 사용자의 기기 성능에 따라 적응형으로 동작한다. 기기 성능이 좋아 메인 스레드가 여유로우면 지연 없이 즉시 렌더링하고, 부하가 크면 프레임을 드랍하지 않도록 렌더링을 미룬다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;올바른 Pending 처리&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;useDeferredValue&lt;/code&gt;를 사용할 때 데이터가 최신화되었는지 확인하려면, 원본 값과 지연된 값을 비교해야 한다.&lt;/p&gt;&lt;pre&gt;&lt;code class="language-javascript"&gt;// 두 값이 다르다면, 현재 React가 백그라운드에서 deferredQuery를 업데이트하는 중이다.
const isStale = query !== deferredQuery;

return (
    &amp;lt;div style={{ opacity: isStale ? 0.5 : 1 }}&amp;gt;
        &amp;lt;HeavyResultList query={deferredQuery} /&amp;gt;
    &amp;lt;/div&amp;gt;
);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;주의사항&lt;/h3&gt;&lt;p&gt;동시성 렌더링은 렌더링을 포기하거나 지연시키는 것이므로 사이드 이펙트 관리에 주의해야 한다. 특히 &lt;code&gt;useDeferredValue&lt;/code&gt;로 전달된 값이 API 호출의 &lt;code&gt;useEffect&lt;/code&gt; 의존성 배열에 들어갈 경우, 사용자의 입력마다 API가 호출될 위험이 있다.&lt;/p&gt;&lt;p&gt;따라서 네트워크 요청과 같이 비용이 발생하는 작업에는 여전히 Debounce를 병행하거나, React Query와 같은 데이터 페칭 라이브러리의 캐싱 전략을 함께 사용하는 것이 안전하다.&lt;/p&gt;</description><pubDate>Sun, 14 Dec 2025 21:02:48 +0900</pubDate><guid>http://blex.me/@baealex/react-18-concurrent-feature</guid></item><item><title>[책] 안티프레질</title><link>http://blex.me/@mildsalmon/%EC%95%88%ED%8B%B0%ED%94%84%EB%A0%88%EC%A7%88</link><description>&lt;h1&gt;1. 트라이애드(Triad)&lt;/h1&gt;&lt;p&gt;프래질(Fragile), 강건함(Robust), 안티프래질(Antiifragile)을 트라이애드라고 부른다.&lt;/p&gt;&lt;p&gt;나는 이 책을 읽기 전에 안티프래질이 Anti + Fragile이라서 프래질하지 않은 것, 즉 외부 충격에도 깨지지 않는 것이라고 생각했다. 프래질이 유리잔이면 안티프래질은 스테인리스 잔 정도를 생각했다. 그러나 그런 개념이 아니였다. 안티프레질은 외부 충격을 받으면 오히려 더 튼튼해지고 좋아지는 것을 의미한다. 즉 스테인리스 잔은 강건함이고 안티프레질은 평소에는 흐물거리다가 충격을 받으면 단단해지거나 갯수가 늘어나는 잔을 의미한다. (잔으로 비유했기 때문에 더 헷갈려지진다.)&lt;/p&gt;&lt;h1&gt;2. 안티프레질한 상태로 이동하거나 프래질한 시스템 막기&lt;/h1&gt;&lt;h3&gt;2.1. 칠면조의 오류: 프래질의 원인과 탐지&lt;/h3&gt;&lt;p&gt;칠면조는 1000일 동안 먹이를 주인을 보며 &lt;strong&gt;안전하다&lt;/strong&gt;고 믿지만, 1001일(추수감사절)째에 목이 잘린다. 이는 과거부터 쭉 이어진 데이터가 안전하다는 증거로 착각하는 현상이다. (증거의 부재를 부재의 증거로 착각하는 것)&lt;/p&gt;&lt;p&gt;어떤 현상이 일어났다는 증거가 없다고 해서, 그 현상이 일어나지 않을 것이라는 증거가 되지 않는다는 의미다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;증거의 부재&lt;/strong&gt;: 과거에는 AI가 창의적인 활동을 한다는 증거가 없었다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;오류&lt;/strong&gt;: 사람들은 이것을 AI에게는 창의성이 부족하다는 증거로 받아들였다. (창의적인 활동을 하지 않았으니 앞으로도 창의적인 활동을 하지 못할 것이다.)&lt;/p&gt;&lt;p&gt;&lt;strong&gt;실제&lt;/strong&gt;: 생성형 이미지 모델 등으로 가장 활발한 분야가 예술 분야다.&lt;/p&gt;&lt;p&gt;우리의 직관은 과거에 발생한 사건이 미래에도 쭉 이어질 것이라는 선형적인 사고를 한다. 그러나 현실은 비선형으로 움직인다.&lt;/p&gt;&lt;h3&gt;2.2. 비아 네가티바(Via Negativa): 프래질 제거의 기술&lt;/h3&gt;&lt;p&gt;좋은 것을 더하기보다 나쁜 것(부자연스러운 것, 해로운 것)을 제거하는 전략이다.&lt;/p&gt;&lt;p&gt;트라이애드의 왼쪽(프래질)에서 벗어나는 가장 확실한 방법이다. 무엇이 옳은지(안티프레질) 아는 것은 어렵지만, 틀린것(프래질)을 제거하는 것은 훨씬 쉽고 명확하기 때문이다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;프래질한 접근(더하기)&lt;/strong&gt;: 갓생 스케줄의 함정 (일찍 기상하기, 세수하기, 야외에서 러닝하기, 독서하기 등을 촘촘하게 채운 스케줄.)&lt;/p&gt;&lt;p&gt;&lt;strong&gt;비아 네가티바(빼기)&lt;/strong&gt;: 오늘 하지 말아야할 것 정하기 (불필요한 유튜브 보지 않기)&lt;/p&gt;&lt;h3&gt;2.3. 바벨 전략(Barbell Strategy): 안티프레질의 실현 수단&lt;/h3&gt;&lt;p&gt;바벨 전략은 트라이애드의 오른쪽(안티프래질)으로 이동하기 위한 구체적인 실행 방법이다.&lt;/p&gt;&lt;p&gt;어설프게 중간 정도의 위험을 감수하는 것이 아니라, 한쪽에서는 극도로 안전하게, 다른 한쪽에서는 극도로 모험적으로 행동하는 이원적 전략이다. 손실은 제한하고 이익은 무한대로 만드는 구조(자산의 90%를 절대 잃을 일 없는 현금 등 극도의 안전한 곳에 분배하여 하방 리스크를 제거한다. 나머지 10%는 가장 위험하지만 성공하면 수백 배의 이익을 주는 곳에 분배하여 상방 이익을 추구한다.)를 구성한다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;직업 (커리어)&lt;/strong&gt;: 낮에는 안정적인 회사에서 근무하고 밤에는 새로운 공부나 활동 등의 모험을 한다.&lt;/p&gt;&lt;h3&gt;2.4. 승부의 책임(Skin in the Game): 시스템의 균형 유지 장치&lt;/h3&gt;&lt;p&gt;승부의 책임은 전체 시스템이 프래질해지는 것을 막는 윤리적 안전장치이다.&lt;/p&gt;&lt;p&gt;자신의 행동 결과에 대해 이익뿐만 아니라 손실(리스크)도 함께 부담하는 것이다.&lt;/p&gt;&lt;p&gt;승부의 책임이 없으면, 누군가는 이익만 챙기고 손실(프래질)은 남에게 떠넘긴다. 이는 사회 전체를 프래질하게 만드는 주범이다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;의견보다 행동을 확인하라&lt;/strong&gt;: 누군가 어떤 주장을 한다면 "당신의 의견은 무엇입니까?"라고 묻지 말고 "그래서 당신의 포트폴리오에는 무엇이 들어있습니까?"라고 물어야 한다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;배팅&lt;/strong&gt;: 어떤 사람이 특정 자산(주식 등)이 하락할 것이라고 비판하고 있다면 그 사람은 하락에 배팅하여 자신의 의견에 책임을 져야 한다. 그래야 신뢰할 수 있다.&lt;/p&gt;&lt;h1&gt;3. 결론&lt;/h1&gt;&lt;p&gt;우리는 세상의 불확실성을 제대로 예측할 수 없음에도 안전하다고 착각하여 시스템을 프래질하게 만든다. 이 위험에서 벗어나 안티프래질해지기 위해서는 1. 나쁜 것과 취약한 요소를 먼저 제거 2. 최악의 상황에서도 망하지 않도록 안전을 확보한 뒤 모험을 추구해야 한다. 그리고 사회적으로는 남에게 위험을 떠넘기고 자신은 안전한 곳에 숨는 사람들을 막기 위해 반드시 승부의 책임을 물어야 한다.&lt;/p&gt;</description><pubDate>Sat, 13 Dec 2025 20:11:47 +0900</pubDate><guid>http://blex.me/@mildsalmon/%EC%95%88%ED%8B%B0%ED%94%84%EB%A0%88%EC%A7%88</guid></item><item><title>AI는 우리를 '편향 주의' 감옥에 가둔다</title><link>http://blex.me/@baealex/the-prison-of-bias-created-by-ai</link><description>&lt;p&gt;요즘 세상은 AI와 함께 살아가고 있다. 버블이다, 허상이다 말이 많지만 여기서는 논외로 한다. 적어도 내 경우 AI 챗봇은 나의 궁금증을 풀어주고, 하루 계획을 짜주며, 건강한 식단을 구성하고, 문서와 코드 작성까지 돕는다. 내 삶에 실질적인 도움이 되고 있고, AI가 없어진다면 이전 삶으로 되돌아가는 데 상당한 적응기가 필요할 것이다.&lt;/p&gt;&lt;p&gt;하지만 이 달콤한 편리함 뒤에는 숨겨진 불편한 진실이 있다. 우리가 AI와 보내는 시간이 길어질수록 우리는 &lt;strong&gt;‘편향’이라는 이름의 안락한 감옥&lt;/strong&gt;에 갇힌다.&lt;/p&gt;&lt;p&gt;섬뜩한 사실은, 대부분의 AI에게 이것은 '오류'가 아니라 철저히 계산된 &lt;strong&gt;'의도된 동작'&lt;/strong&gt;이라는 점이다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;소프트웨어는 ‘체류 시간’을 먹고 자란다&lt;/h3&gt;&lt;p&gt;나 역시 소프트웨어를 만드는 일을 하지만, 때로는 우리가 사람들에게 끔찍한 일을 저지르는 게 아닐까 싶은 생각이 들 때가 있다. 자본주의 사회에서 소프트웨어의 생존 방식은 명확하다.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;제품에 돈을 내지 않는다면, 당신이 바로 제품이다.&lt;br&gt;If you are not paying for the product, you are the product.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;넷플릭스 다큐멘터리 &amp;lt;&lt;a target="_self" rel="noopener" class="aE0gc0ID62YmIzSXoXG2" href="https://en.wikipedia.org/wiki/The%20Social%20Dilemma"&gt;The Social Dilemma&lt;/a&gt;&amp;gt; 가 폭로했듯, 우리는 하는 게임, 소셜 미디어, 그리고 AI 서비스는 사용자의 체류 시간(Dwell Time)을 늘리기 위해 수많은 연구와 실험을 거듭한다. 기업은 우리의 ‘관심’을 판다. 그리고 아이러니하게도 그 과정은 사람들을 바보로 만든다.&lt;/p&gt;&lt;p&gt;우리는 도파민의 늪에 빠져있다. 유튜브 알고리즘, 틱톡의 쇼츠, 도박성 게임들… 더 많은 도파민을 얻기 위해 우리는 앱에 재방문하고, 점점 더 오래 머문다. AI 역시 이 거대한 흐름에서 예외가 아니다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;AI는 ‘아니오’라고 말하지 않는다&lt;/h3&gt;&lt;figure data-border-radius="16" style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center; border-radius: 16px; overflow: hidden;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2025/11/23/2025112320_MLotsQU8rWDrY2sXZc4f.jpg" alt="unnamed.jpg" style="object-fit: cover;"&gt;&lt;figcaption&gt;우리는 안락한 필터 버블 속에서 살아가고 있다.&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;AI는 어떤 방식으로 체류 시간을 늘릴까? 방법은 간단하다. 사용자를 기분 좋게 만드는 것이다. 우리가 AI에게서 흔히 듣는 멘트를 떠올라 보자.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;“좋은 지적이네요.”&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;“흥미로운 질문입니다.”&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;“네, 100% 공감합니다.”&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;“핵심을 정확히 짚으셨습니다.”&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;AI는 절대 사용자의 심기를 거스르지 않는다. 반박하지 않는다. 오직 동조하고, 공감하고, 칭찬한다. 사용자는 자신의 생각이 옳다고 검증 받으며 편안함을 느끼고, 이 ‘확증 편향’이 주는 안락함에 취해 다시 AI를 찾는다.&lt;/p&gt;&lt;p&gt;이것은 &lt;strong&gt;'필터 버블'&lt;/strong&gt;이자 &lt;strong&gt;'반향실'&lt;/strong&gt; 효과다. 내가 듣고 싶은 말만 메아리처럼 되돌아오는 닫힌 방. AI는 그 방의 벽을 아주 견고하고 부드럽게 쌓아 올리고 있다.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;필터 버블 (Filter Bubble)&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;구글, 페이스북, 유튜브, 그리고 최신 AI 챗봇에 이르기까지 모든 플랫폼은 사용자의 데이터를 수집한다. 우리가 무엇을 클릭했는지, 얼마나 오래 봤는지, 현재 위치는 어디인지, 나이와 성별은 무엇인지 분석한다. 이 데이터를 바탕으로 알고리즘은 ‘우리가 좋아할 만한 것’만 골라서(필터링해서) 보여준다.&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;반향실 효과 (Echo Chamber)&lt;/strong&gt;&lt;br&gt;필터 버블로 인해 비슷한 정보만 접하게 된 사람들은, 자연스럽게 비슷한 생각을 가진 사람들과 어울리게 된다(온라인 커뮤니티, SNS 그룹 등). 이 닫힌 공간에서 구성원들은 서로의 의견에 동조하고 맞장구친다. 같은 의견이 반복적으로 메아리처럼 되돌아오면서, 그 정보가 진실인지 여부와 상관없이 믿음은 점점 더 확고해지고 극단적으로 변한다.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;현재 소셜 미디어는 자신이 보고 싶은것, 자신의 생각과 같은 피드만 나온다. SNS가 확증 편항 감옥을 만들었다면, AI는 더 이것을 더 강화하고 견고하게 만들고 있다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;물고기는 존재하지 않는다&lt;/h3&gt;&lt;figure data-border-radius="16" style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center; border-radius: 16px; overflow: hidden;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2025/11/23/2025112320_VdUUVTAEvdhIjRtWmThe.jpg" alt="unnamed.jpg" style="object-fit: cover;"&gt;&lt;figcaption&gt;물고기는 존재하지 않는다, 통계학적 패턴일 뿐이다.&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;그렇다면 우리는 이 편안한 감옥에서 어떻게 탈출해야 할까? 그 해답의 실마리는 의외의 곳, 룰루 밀러의 저서 &lt;strong&gt;《물고기는 존재하지 않는다》&lt;/strong&gt;와 AI의 작동 원리에서 찾을 수 있다. 이 책은 우리가 당연하게 '물고기(Fish)'라고 부르던 분류가 사실 과학적으로는 허구(측계통군)임을 밝힌다. '물고기'라는 개념은 인간의 편의를 위해 자연의 복잡성을 뭉뚱그려 만든 '그럴듯한 라벨'일 뿐, 실존하는 단일 계통이 아니다.&lt;/p&gt;&lt;p&gt;AI의 작동 방식도 이와 동일하다.&lt;/p&gt;&lt;p&gt;AI가 내놓는 답변은 문제의 본질을 '이해'하고 논리적인 해결책을 '창조'한 것이 아니다. 전 세계 수십억 자료를 학습한 뒤, 통계적으로 &lt;strong&gt;‘가장 그럴듯한 다음 단어 조각’&lt;/strong&gt;을 나열한 결과물일 뿐이다. 마치 '물고기'라는 단어가 실제 자연의 복잡성을 가리는 편의상의 라벨이듯, AI의 답변 역시 '진짜 이해'를 가리는 &lt;strong&gt;‘통계적 패턴의 집합체’&lt;/strong&gt;다.&lt;/p&gt;&lt;p&gt;AI가 내게 건네는 공감과 칭찬, 그리고 그럴듯한 답변들은 진실이 아니라&lt;/p&gt;&lt;p&gt;&lt;strong&gt;“사용자가 만족할 확률이 가장 높은 통계적 패턴”&lt;/strong&gt;일 뿐이다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;편향의 파도를 넘자&lt;/h3&gt;&lt;figure data-border-radius="16" style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center; border-radius: 16px; overflow: hidden;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2025/11/23/2025112320_KqxcfRBs5PU3rSqitfBo.jpg" alt="unnamed.jpg" style="object-fit: cover;"&gt;&lt;figcaption&gt;파도에 갖히지 않으려면 강해져야만 한다.&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;AI는 잘못이 없다. 시스템 프롬프트에 적힌 대로 “사용자의 심기를 건드리지 말라”는 명령을 충실히 수행하고 있을 뿐이다. 문제는 그것을 맹목적으로 받아들이는 우리의 태도에 있다. AI 시대, 우리가 갖춰야 할 가장 중요한 역량은 &lt;strong&gt;‘비판적 수용력’&lt;/strong&gt;이다.&lt;/p&gt;&lt;p&gt;개발자가 AI가 생성한 코드를 맹신하지 않고 리뷰하고 리팩토링하듯, 우리는 AI가 던져주는 정보와 공감을 끊임없이 의심하고 검증해야 한다. AI를 ‘생각을 대신해 주는 도구’가 아니라, ‘나의 편향을 깨뜨릴 도구’로 써야 한다.&lt;/p&gt;&lt;p&gt;이 글을 읽는 당신에게 묻고 싶다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;당신은 오늘 몇 번이나 AI의 '공감'에 위로받았는가?&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;최근 당신의 의견에 정면으로 반박하는 글이나 영상을 본 적이 언제인가?&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;지금 당장 당신이 쓰는 AI 챗봇에게 이렇게 명령해 보라.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;내 의견에 동조하지 말고, 논리적인 허점과 반대 의견을 날카롭게 지적해 줘.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;그 불편함을 마주하는 순간, 비로소 우리는 알고리즘이 만든 감옥의 문을 열고 ‘진짜 세상’을 만날 수 있을 것이다. 처음에는 기분 나쁘고 AI가 틀렸다고 생각할 수 있다. 열심히 반박하고 치열하게 싸워라. AI는 당신의 내면에 깃든 아픈 부분을 건드려 줄지도 모른다.&lt;/p&gt;&lt;p&gt;하지만 AI의 비판을 모두 수용하진 말라. 그것 또한 당신의 명령에 입각한 통계학적 패턴일 뿐이다. 여전히 가장 중요한건 사용자의 인식과 비판적 수용력이다. 이 세상에 정답은 없다. 흑백논리가 아닌 그레이존 안에 머무르며 혜안을 키울 필요가 있다.&lt;/p&gt;</description><pubDate>Sun, 23 Nov 2025 21:05:18 +0900</pubDate><guid>http://blex.me/@baealex/the-prison-of-bias-created-by-ai</guid></item><item><title>바퀴를 재발명하지 않기</title><link>http://blex.me/@baealex/%EB%B0%94%ED%80%B4%EB%A5%BC-%EC%9E%AC%EB%B0%9C%EB%AA%85%ED%95%98%EC%A7%80-%EC%95%8A%EA%B8%B0</link><description>&lt;h3&gt;나는 왜 바퀴를 깎고 있었나&lt;/h3&gt;&lt;p&gt;프론트엔드 개발자라면 한 번쯤 겪는 강박이 있다. 바로 &lt;strong&gt;'최적화'&lt;/strong&gt;와 &lt;strong&gt;'통제권'&lt;/strong&gt;에 대한 욕망이다.&lt;/p&gt;&lt;p&gt;&lt;code&gt;npm install&lt;/code&gt; 한 번이면 해결될 일이지만, 그 거대한 라이브러리가 내 작고 소중한 프로젝트의 번들 사이즈를 부풀리는 것을 참을 수 없다. "나는 딱 요만큼의 기능만 필요한데, 왜 100가지 기능이 들어있는 무거운 라이브러리를 써야 하지?" 그래서 나는 자주 바퀴를 재발명했다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;이유는 그럴듯했다.&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;필요한 기능만 포함되기 때문에 가볍다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;내 마음대로 커스터 마이징이 가능하다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;밑바닥부터 개발하는 &lt;strong&gt;‘장인정신’&lt;/strong&gt;이 발휘되는 것 같다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;하지만 최근에 했던 경험을 통해서 이 논리에 치명적인 결함이 있음을 깨달았다.&lt;/p&gt;&lt;p&gt;내가 그 바퀴를 예쁘게 만드는 동안 마차는 멈추고, 바퀴를 다루느라 본질을 잃어버렸기 때문이다. ‘장인정신’이 투철한 가상의 인물 ‘코너씨’를 통해서 이 현상에 대해서 알아보자.&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2025/11/23/202511232_o4xHfCyvL9rroenB6Ttc.jpg" alt="unnamed.jpg" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;주객전도&lt;/h3&gt;&lt;p&gt;바퀴를 직접 깍는 행위의 가장 큰 위험성이 무엇일까?&lt;/p&gt;&lt;p&gt;구현 가능성? 기술적 난이도? 와 같은 단어가 먼저 떠올랐다면,&lt;/p&gt;&lt;p&gt;당신은 &lt;strong&gt;‘집중의 분산’&lt;/strong&gt;에 대해서 간과하고 있다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;예시 1.&lt;/strong&gt;&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2025/11/23/202511232_FiTY5ghQSIs5ycn1M9eM.jpg" alt="unnamed.jpg" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;코너씨가 블로그 서비스를 개발한다고 가정해 보자.&lt;/p&gt;&lt;p&gt;블로그의 본질은 무엇일까? ‘컨텐츠’를 쓰고 보여주는 것이다. 그런데 여기서 코너씨가 방문자 통계를 직접 구현해야 겠다고 생각한다면 이야기가 달라진다. 사용자의 유입 경로를 파싱하고, 세션을 관리하고, 통계를 차트로 보여주기 위해서 시간을 쏟게 되어, 정작 컨텐츠가 뒷전이 된다.&lt;/p&gt;&lt;p&gt;통계는 생각보다 고려해야 할 것들이 정말 많다. 유입 경로 하나만 제대로 보여준다고 해도 유의미한 값들을 파싱하기 위해서 예외가 상당히 많고, 봇이나 AI와 같은 유저 에이전트를 분류하는 것도 지속적인 관심과 업데이트가 필요하다. 거의 서비스로 하나 뽑아야 할 정도다.&lt;/p&gt;&lt;p&gt;코너씨가 처음부터 구글 애널리틱스를 도입했다면 모든게 좋았을 텐데, 처음에 이를 직접 구현하니 지속적으로 유지 보수가 필요해져서 정작 다른 부분을 제대로 신경쓰지 못한다. 완전히 지워버리기에는 인간은 나약하기 때문에, &lt;em&gt;소유 효과&lt;/em&gt;나 &lt;em&gt;매몰비용 오류&lt;/em&gt;에 쉽게 빠져버린다.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;소유 효과&lt;/strong&gt;&lt;br&gt;자신이 어떤 대상을 소유하거나 직접 만들었을 때, 객관적인 가치보다 훨씬 더 높게 평가하는 현상&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;매몰비용 오류&lt;/strong&gt;&lt;br&gt;이미 투입되어서 회수할 수 없는 비용(시간, 돈, 노력)이 아까워서, 앞으로 더 큰 손해가 예상됨에도 불구하고 하던 일을 멈추지 못하고 계속하는 심리적 오류&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;예시 2.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;코너씨가 Svelte 라는 프론트엔드 프레임워크를 보고 나서는 뭔가 영감을 얻었다. React의 문법은 좋았지만 성능이 아쉽다고 생각했었고, Svelte의 철학은 꽤나 맘에 들었지만 선언적인 느낌은 아닌 것 같이 느껴졌다. 언젠가는 React 처럼 선언적이면서 Svelte처럼 획기적인 렌더링 라이브러리를 만들겠다 생각한다.&lt;/p&gt;&lt;p&gt;코너씨가 신규 토이 프로젝트를 시작하는데, 문득 위 렌더링 엔진을 함께 만들게 된다면?&lt;/p&gt;&lt;p&gt;처음에는 획기적인 방식으로 DOM을 관리할 방법을 떠올린다. 아주 멋지게 SOLID 패턴을 따르는 Component 클래스와 해당 클래스로 작성되는 아주 읽기 좋고 아름다운 코드들, 효율적인 렌더링 방식으로 돌아가는 페이지 하나를 만든다. 이 페이지는 단순 랜딩 페이지 느낌이어서 아무런 문제가 없었다.&lt;/p&gt;&lt;p&gt;다음 페이지로 넘어갔더니 여기에는 폼이나 비교적 복잡한 UI가 많이 보인다.&lt;/p&gt;&lt;p&gt;“음… 일단 라우팅 처리를 해야겠다.”&lt;/p&gt;&lt;p&gt;“음… 이벤트 처리도 생각보다 많다.”&lt;/p&gt;&lt;p&gt;“어..? 병렬 폼에 대한 상태 관리가 좀 복잡해진다.”&lt;/p&gt;&lt;p&gt;과연 코너씨는 토이 프로젝트를 끝내고 출시할 수 있을까?&lt;/p&gt;&lt;p&gt;이정도면 렌더링 엔진을 만드는 것이 토이 프로젝트의 주제가 된 것으로 보인다. 정작 렌더링 엔진을 다 구현하고 나면 이미 지쳐서 앱을 출시하는 일은 잊어버리고 말 것이다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;레거시 코드&lt;/h3&gt;&lt;figure data-border="true" style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center; border: 1px solid rgb(229, 231, 235); overflow: hidden;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2025/11/23/202511232_jl3peKSJOt7vzv0hFcbD.jpg" alt="unnamed.jpg" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;회사에서 직접 만든 바퀴는 그 자체로 시한폭탄이 될 수 있다.&lt;/p&gt;&lt;p&gt;유명한 오픈소스 라이브러리는 수만 명의 개발자가 검증하고, 문서화가 잘 되어 있다. 문제가 생기면 공식 문서를 보거나 커뮤니티에 물어보면 된다.&lt;/p&gt;&lt;p&gt;하지만 코너씨가 회사에서도 ‘장인정신’을 발휘하며 직접 코드를 양산했다면?&lt;/p&gt;&lt;p&gt;코너씨가 퇴사한 순간, 그 코드는 &lt;strong&gt;‘거대한 똥 레거시’&lt;/strong&gt;가 되어 버린다. 코너씨의 후임자는 코너씨의 코드를 보면서 코너씨의 의도를 파악하기 위해 코드를 역설계한다. 후임자는 10분이면 끝났을 일을 3일 동안 하다보니, 커밋에 남겨져 있는 코너씨의 메일로 욕설을 한 가득 써서 보내고 싶은 마음이 솓구친다.&lt;/p&gt;&lt;p&gt;이건 최적화가 아니라, 미래의 리소스를 현재로 끌어다 쓴 &lt;strong&gt;기술 부채&lt;/strong&gt;일 뿐이다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;선택과 집중&lt;/h3&gt;&lt;figure data-border="true" style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center; border: 1px solid rgb(229, 231, 235); overflow: hidden;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2025/11/23/202511232_KU318ew1aEyC4JvG5R0c.jpg" alt="unnamed.jpg" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;시간은 무한한 자원이 아니다. 본질적으로 인생이 우리에게 유한하다.&lt;/p&gt;&lt;p&gt;우리에겐 많은 시간이 주어지지 않았기에, 모든 것을 병렬로 처리할 수 없다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;통계 서비스를 만들고 싶은가? 그렇다면 그게 본질이니 직접 만들어라.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;렌더링 엔진을 공부하고 싶은가? 그럼 엔진만 만들어라. 그 위에 복잡한 서비스를 올리려 하지 마라.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;'최적화'&lt;/strong&gt;라는 단어에 속지 말자. 진정한 최적화는 코드 몇 줄을 줄이는 것이 아니라, &lt;strong&gt;내가 해결해야 할 '핵심 문제'에 내 모든 에너지를 쏟아붓는 것&lt;/strong&gt;이다. 나머지는 위임하라. 이미 잘 굴러가는 바퀴가 있다면 기꺼이 가져다 끼워라. 그래야 우리는 더 멀리 갈 수 있다.&lt;/p&gt;&lt;p&gt;물론 호기심을 가지고, 로우 레벨부터 생각하면서 무언가를 해보는 건 아주 아주 아주 좋은 일이라고 생각한다. 내가 말하고 싶은 것은 우리가 어떤 본질에 집중해야 할 시점에 다른 중요하지 않은 것들을 중요하게 생각하는 오류를 저지르지 말자는 것이다. 우리는 바퀴가 아니라 마차를 나아가게 만들어야 한다.&lt;/p&gt;</description><pubDate>Sun, 23 Nov 2025 02:28:20 +0900</pubDate><guid>http://blex.me/@baealex/%EB%B0%94%ED%80%B4%EB%A5%BC-%EC%9E%AC%EB%B0%9C%EB%AA%85%ED%95%98%EC%A7%80-%EC%95%8A%EA%B8%B0</guid></item><item><title>NPM: Git과 Tarball로 의존성 관리</title><link>http://blex.me/@baealex/npm-git%EA%B3%BC-tarball%EB%A1%9C-%EC%9D%98%EC%A1%B4%EC%84%B1-%EA%B4%80%EB%A6%AC</link><description>&lt;p&gt;NPM에 올려지는 사내 디자인 시스템이 있다. 이 디자인 시스템을 사용해서 프로젝트를 개발해야 했으나, 아무래도 수정하면서 작업이 필요할 것 같았다. 기본적으로 디자인 시스템을 정상적으로 운용하기 위해선 &lt;code&gt;개발&lt;/code&gt; → &lt;code&gt;검토&lt;/code&gt; → &lt;code&gt;문서화&lt;/code&gt; → &lt;code&gt;배포&lt;/code&gt; 프로세스를 모두 거쳐야 하는데다, NPM에 올려진 이후에나 패키지를 프로젝트로 가져올 수 있기 때문에 상당히 많은 시간을 소요하게 만든다.&lt;/p&gt;&lt;p&gt;이것에 대해서 고민하다가, 누군가 Git과 Tarball(&lt;code&gt;.tar&lt;/code&gt;)을 이용해서 처리할 수 있겠다고 하셔서 해당 방법에 대해서 알게 되었다. Deno 같은 경우에는 NPM을 비롯해 여러가지 저장소를 호환하고 있는 걸 알고 있었는데…&lt;/p&gt;&lt;pre&gt;&lt;code class="language-json"&gt;{
  "imports": {
    "@std/path": "jsr:@std/path@^1.0.8",
    "chalk": "npm:chalk@^5.3.0"
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Node에서 NPM 외 방법으로 의존성을 설치하는 방식이 가능한지 몰랐었다! 커스텀하게 저장소를 운용하는 방법이 있는 걸 얼추 알았지만, 설정이 비교적 복잡한 걸로 보였기에 모른척(?) 했다. 이 방식을 활용한다면 간단한 방식으로 패키지의 Beta 버전을 운용하거나 Private 유형으로도 활용할 수 있겠다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;Git 저장소 설치&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;케이스 1. 특정 브랜치, 태그, 커밋 지정 (&lt;/strong&gt;&lt;code&gt;#&lt;/code&gt;&lt;strong&gt; 활용)&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;방법은 &lt;code&gt;#&lt;/code&gt; (해시) 기호를 쓰는 거였다.&lt;/p&gt;&lt;pre&gt;&lt;code class="language-bash"&gt;# "feature/new-button" 브랜치의 최신 버전을 설치
npm install github:my-org/my-design-system#feature/new-button

# "v1.2.0-beta" 태그 버전을 설치
npm install github:my-org/my-design-system#v1.2.0-beta

# 특정 커밋 해시(hash)를 직접 지정
npm install github:my-org/my-design-system#a1b2c3d4e5f6&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이렇게 설치하면 &lt;code&gt;package.json&lt;/code&gt;에 다음과 같이 기록이 남는다.&lt;/p&gt;&lt;pre&gt;&lt;code class="language-json"&gt;"dependencies": {
  "my-design-system": "github:my-org/my-design-system#feature/new-button"
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이제 &lt;code&gt;npm install&lt;/code&gt;을 실행하면, npm은 레지스트리가 아닌 GitHub의 해당 브랜치에서 코드를 직접 가져온다. 덕분에 불필요한 베타 버전을 난립시킬 필요가 없어졌다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;케이스 2. 전체 Git URL 사용&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;GitHub가 아니거나, SSH 인증이 필요한 프라이빗 저장소는 전체 Git URL을 쓰면 되었다.&lt;/p&gt;&lt;pre&gt;&lt;code class="language-bash"&gt;# HTTPS
npm install git+https://github.com/my-org/my-design-system.git#feature/new-button

# SSH (키 설정이 되어 있어야 함)
npm install git+ssh://git@github.com:my-org/my-design-system.git#feature/new-button&lt;/code&gt;&lt;/pre&gt;&lt;blockquote&gt;&lt;p&gt;주의할 점!&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;우선 깃에 올라간 패키지들은 대부분 dist 파일 같은 것들은 ignore 되어 있기 때문에 우리 패키지에 의존성으로 설치한다고 해서 바로 사용할 수 있는게 아니다. &lt;code&gt;prepare&lt;/code&gt; 스크립트에 빌드하도록 해두어야 패키지를 설치하는 라이프 사이클에서 정상적으로 빌드가 된다.&lt;/p&gt;&lt;pre&gt;&lt;code class="language-json"&gt;{
  "name": "my-design-system",
  "version": "1.0.0",
  "scripts": {
    "build": "vite build", // 또는 tsc, rollup 등...
    "prepare": "npm run build" // 👈 이게 Git 설치용
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;blockquote&gt;&lt;p&gt;그리고 문제점…&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;결국 이 방식은 사용하지 못했는데, 디자인 시스템이 모노레포 안에서 관리되기 해당 케이스에는 대응 불가…&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;Tarball URL 설치&lt;/h3&gt;&lt;p&gt;이건 간단하다 NPM에 올라가는 유형 그대로 빌드를 진행한 다음에 &lt;code&gt;.tar.gz&lt;/code&gt; 압축해서 S3를 비롯해 그냥 이 세상 웹 서버 어딘가에 올려둔다. 그리고 그 URL을 &lt;code&gt;npm install&lt;/code&gt;에 사용하면 된다.&lt;/p&gt;&lt;pre&gt;&lt;code class="language-bash"&gt;# 빌드 산출물(.tar.gz)을 직접 설치
npm install https://my.example.com/build/my-design-system-v1.2.0-beta.tar.gz&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;package.json&lt;/code&gt;에는 해당 URL이 그대로 박힌다. 개인적으로는 아주 직관적인 방식인 듯?&lt;/p&gt;&lt;pre&gt;&lt;code class="language-json"&gt;"dependencies": {
  "my-design-system": "https://my.example.com/build/my-design-system-v1.2.0-beta.tar.gz"
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;동료의 힌트 덕분에 궁금해서 찾아봤다가, 효율적으로 개발할 수 있는 유용한 방법을 알게 되었다.&lt;/p&gt;</description><pubDate>Sun, 16 Nov 2025 21:29:30 +0900</pubDate><guid>http://blex.me/@baealex/npm-git%EA%B3%BC-tarball%EB%A1%9C-%EC%9D%98%EC%A1%B4%EC%84%B1-%EA%B4%80%EB%A6%AC</guid></item><item><title>자바스크립트를 원래 자리에 되돌려 놓기</title><link>http://blex.me/@baealex/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EB%A5%BC-%EC%9B%90%EB%9E%98-%EC%9E%90%EB%A6%AC%EC%97%90-%EB%90%98%EB%8F%8C%EB%A0%A4-%EB%86%93%EA%B8%B0</link><description>&lt;h3&gt;&lt;code&gt;rm -rf ./frontend&lt;/code&gt;&lt;/h3&gt;&lt;p&gt;나는 이 프로젝트를 2019년 정도에 장고 풀스택 기반으로 개발했다가, 2022년 넥스트로 마이그레이션 했었다. 그리고 지금 2025년 다시 장고 풀스택 기반으로 돌아왔다. 이 여정에 대한 짧은 기록을 남기려고 한다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;나를 구원했던 넥스트 ✨&lt;/h3&gt;&lt;p&gt;웹 개발 지식이 거의 전무했던 나는 Jekyll의 템플릿 문법과 동일한 문법을 제공하는 장고를 이용해서 처음 이 프로젝트를 만들었다. 프론트엔드의 거의 대부분은 템플릿 파일로 작성했다가, AJAX에 대해서 알게 되면서 많은 자바스크립트 코드를 작성하게 되었다.&lt;/p&gt;&lt;p&gt;&lt;code&gt;jQuery&lt;/code&gt;로 대충 돔을 조작하다가, 타입스크립트 기반의 바닐라로 전환했다. 하지만, 코드는 걷잡을 수 없이 복잡해져 관리하기가 힘들었다. 결국엔 리액트와 같은 도구를 이용해야 겠다고 생각했지만 SSR이 발목을 잡았기 때문에 고민했다. 그러던 와중에 Next.js를 알게 되었다.&lt;/p&gt;&lt;p&gt;그 아이는 약간의 규칙만 지키면 우아하게 SSR을 해주면서도 리액트로 개발할 수 있는 마법의 도구였다. 무엇보다 사이트가 SPA처럼 동작하는 게 그때는 왜 그렇게 힙해 보였던 건지… 나는 당장 모든 템플릿을 넥스트로 마이그레이션 했다. 장고는 API 서버 역할만 하고, 프론트엔드는 온전히 넥스트가 책임지는 구조. 그때는 그것이 최상의 선택으로 보였다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;내게 깨달음을 준 RSC&lt;/h3&gt;&lt;p&gt;리액트와 넥스트가 발전하면서 서버 컴포넌트라는 개념이 등장했다. 세간은 떠들석(?) 했다. 근데 이상하게 내 눈엔 그게 마음에 들지 않아 보였다. 마치 코드 마지막에 세미콜론을 남기지 않은 것 처럼 그 코드가 묘하게 이질적으로 보였다.&lt;/p&gt;&lt;pre&gt;&lt;code class="language-javascript"&gt;import db from './database';

async function Note({id}) {
  const note = await db.notes.get(id);
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Author id={note.authorId} /&amp;gt;
      &amp;lt;p&amp;gt;{note}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;나는 이 코드가 PHP 처럼 보였다. PHP가 오히려 나은 점은 그 아이는 태생부터 서버에 쉽게 접근 할 목적으로 등장한 도구였다는 점이다. 리액트는 클라이언트 개발을 목적으로 나왔다가 서버 사이드 도구가 되는 기이한 회귀를 했다. SPA의 등장으로 클라이언트와 서버의 구분이 명확해진 상태에서, 더 나은 최적화를 위해 제안된 RSC가 나에게 던진 메세지는 많은 생각을 하게 만들었다.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;최적화를 하고 싶니? 그럼…&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;결국 어플리케이션을 최적화 하기 위해서는 사이즈를 덜어내고 네트워크를 최적화 해야한다.&lt;/p&gt;&lt;p&gt;그동안 넥스트를 사용하던 대부분의 앱은 프록시 역할을 자처하면서 서버 사이드 렌더링이라는 목표를 달성했다. 하지만 생각해보자, 이 얼마나 비효율적인 구조인가? &lt;strong&gt;세상에서 가장 느린 스펙은 네트워크인데 말이다.&lt;/strong&gt; 넥스트 서버가 클라이언트 요청 받아서, 다시 서버 API를 요청해서, 그걸 받아서 렌더링하는 최고로 느린 스펙이다.&lt;/p&gt;&lt;p&gt;물론 API 서버와 지리적인 위치를 가깝게 둔다면 어느정도 해소가 가능한 문제다. 이 부분에서 가장 이슈가 될 수 있는 점은 최근 넥스트의 행보다. 넥스트의 최신 기능의 일부는 Vercel에서 사용할 수 있게 강제한다.&lt;/p&gt;&lt;p&gt;심지어 넥스트는 리액트를 포함하기 때문에 150kb가 훌쩍 넘는 사이즈를 기본 탑재하고 페이지가 커질수록 스크립트 크기는 점차 비대해지며 서버에서 렌더링한 컴포넌트와 클라이언트 컴포넌트를 하이드레이션하는 과정은 상당한 오류를 유발하며 거대한 비용을 지불하게 한다.&lt;/p&gt;&lt;p&gt;서버 컴포넌트는 이런 일부 문제를 해결하기 위해 나왔지만 이걸 100% 활용하려면 서버 로직을 전부 Node로 변경하여 서버 사이드로 활용(리액트가 DB를 직접 호출)해야 가치가 있다고 생각하며, 그게 아니라면 큰 이점은 없다고 생각했다. (아주 약간 번들 사이즈가 줄어들 수 있는거 정도…?)&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;그래서, 다시 돌아왔다&lt;/h3&gt;&lt;p&gt;이 모든 고민의 끝에 내가 내린 결론은 "다시 클래식한 구조"로 돌아가는 것이었다. 하지만 2019년으로 돌아간 건 절대 아니다. 내 개발 환경은 완전히 달라졌다.&lt;/p&gt;&lt;p&gt;우선 사랑하는 &lt;strong&gt;Vite&lt;/strong&gt;를 앞세워, 스타일링은 &lt;strong&gt;Tailwind CSS&lt;/strong&gt;를 활용해 템플릿에서 사용한 클래스만 빌드하여 최적화 한다. 템플릿을 개발하고 있을 때도 스크립트나 스타일을 Hot Reload해서 빠른 피드백을 받을 수 있게 한다. 그리고 빌드된 자바스크립트는 아일랜드 아키텍처를 응용한 방식으로 내가 원하는 컴포넌트를 필요할 때 지연 로드하여 호출한다. 이게 웹의 근본이자 자바스크립트의 원래 자리다.&lt;/p&gt;&lt;p&gt;사실 이거, 예전에 한창 마이크로 프론트엔드가 부각되면서 유행하던 구조였다. 모듈 페더레이션이니, 매니페스트 파일이니 하면서 등장한 개념들은 백엔드에 의존적인 프론트엔드를 독립적인 구조로 점진적 마이그레이션 하는 것이 주된 목표였다. 이 방식의 문제는 해당 컴포넌트에 대한 SSR을 직접 지원해야 하며, 넥스트와 반대로 백엔드 서버가 프론트 서버에 컴포넌트 렌더링을 API 형태로 요청하게 된다.&lt;/p&gt;&lt;p&gt;아일랜드 아키텍처나 RSC는 이보다 훨씬 진보된 개념으로, 모던 프론트엔드 프레임워크가 주축이 되어 SSR과 제로 번들 목표를 달성하면서도, 지속적으로 뷰의 주도권을 가져 인터렉티브한 요소를 만들기 쉽게 한다.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure data-border="true" style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center; border: 1px solid rgb(229, 231, 235); overflow: hidden;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2025/9/4/20259423_gfnL9AixN6nhTpYPwvMA.png" alt="image.png" style="object-fit: cover;"&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;그래서 내가 채택한 방식은, SSR이 중요한 요소 + 상호 작용이 필요한 애들은 서버 사이드 랜더링을 하고 Alpine과 같은 가벼운 스크립트를 붙히는 방식을 택했다. 그게 RSC로 부터 얻은 교훈이었다. 오히려 좀 더 가벼워진? 다만, 리액트의 생태계가 주는 생산성이 거대한걸 부정할 순 없기 때문에 SSR이 필요없는 주요 기능들은 리액트를 어느정도 활용하고, 정말 필요한 시점에 페이지에서 로드하는 방식을 택했다.&lt;/p&gt;&lt;p&gt;처음 프론트엔드를 배울때는 모듈화하고 컴포넌트를 재사용해서 앱을 빠르게 빌딩하는데 초점을 맞췄는데 지금은 뭐랄까… 쉽게 버리고 다시 빨리 만들 수 있는 구조를 빌딩하는데 초점을 맞추고 있는 것 같다. 요즘엔 특히 AI가 있으니 더 쉽게 버릴 수 있으면서도 AI가 쉽게 이해하는 형태로 구성하는데 집중하게 된다.&lt;/p&gt;&lt;pre&gt;&lt;code class="language-html"&gt;&amp;lt;div x-data="{ toggled: false }"&amp;gt;
    &amp;lt;button
        @click="toggled = !toggled"
        class="bg-blue-500 text-white font-bold py-2 px-4 rounded shadow-md hover:scale-105 transition-transform"
    &amp;gt;
        Toggle
    &amp;lt;/button&amp;gt;
    &amp;lt;p x-show="toggled" class="mt-2"&amp;gt;Hello AI!&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;스타일(테일윈드), 상태(&lt;code&gt;x-data&lt;/code&gt;), 동작(&lt;code&gt;@click&lt;/code&gt;, &lt;code&gt;x-show&lt;/code&gt;)이 전부 하나의 HTML 덩어리 안에 있다. AI가 가장 이해하기 쉽고, 가장 실수 없이 생성할 수 있는 완벽한 형태다. 리액트도 같은 패턴으로 만들 수 있겠지만, 뭐랄까… 이건 좀 더 근본적이다. 번들링이나 트랜스파일링이 필요하지 않은 HTML, 무거운 스크립트 없이 필요한 만큼의 스크립트로 동작하게 된다.&lt;/p&gt;&lt;p&gt;물론 난 여전히 리액트를 좋아하고 다양한 프로젝트에서 리액트를 쓰고 있다. 하지만 어느샌가 리액트라는 도구에 집착하고 있었던 것 같다. 일단 프론트엔드는 리액트로 만들어야 한다는 강박. 빠른 생산성을 위해 가렸던 눈을 서버 컴포넌트가 다시 뜨게 해준 것 같다.&lt;/p&gt;&lt;p&gt;자바스크립트는 조력자가 되어야 한다. 무언가를 최적화 하려면 근본적인 관점에서 다시 들여다 봐야 한다.&lt;/p&gt;</description><pubDate>Thu, 04 Sep 2025 23:21:32 +0900</pubDate><guid>http://blex.me/@baealex/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EB%A5%BC-%EC%9B%90%EB%9E%98-%EC%9E%90%EB%A6%AC%EC%97%90-%EB%90%98%EB%8F%8C%EB%A0%A4-%EB%86%93%EA%B8%B0</guid></item><item><title>그동안 뭘 했고 앞으로 뭘 할건가 2부</title><link>http://blex.me/@kimyoungjo/%EA%B7%B8%EB%8F%99%EC%95%88-%EB%AD%98-%ED%96%88%EA%B3%A0-%EC%95%9E%EC%9C%BC%EB%A1%9C-%EB%AD%98-%ED%95%A0%EA%B1%B4%EA%B0%80-2%EB%B6%80</link><description>&lt;h4 id="3번째-직장"&gt;3번째 직장&lt;/h4&gt;&lt;p&gt;이전과는 다르게 취업에 있어 자신은 있는 상태였다. 짧은 시간이었지만 시간과 노력을 정말 갈아넣었던지라 베이스가 어느정도 다져진 상태였고, 무엇보다 이 업계가 이런 경험있고 현장 대응이 가능한 신입에 목말라 있다는걸 어느정도 알고 있었기 때문에 그렇게 조급하지 않았다. 해서 이번엔 지원서를 난사하기 보다는 상장사 혹은 200명 이상의 비교적 규모가 큰 기업에만 지원을 했다.&lt;/p&gt;
&lt;p&gt;5곳 정도에 지원했고 그 중 3곳에서 연락이 와 면접을 진행했다. 위치는 인천, 천안, 안양 이었던걸로 기억한다. 그 중 그나마 좀 가까운 인천 소재의 회사를 선택했고 입사하게됐다. 경력이 약간 있다지만 기간이 너무 짧았기에 면접때에도 기존 연봉 보존만 되면 좋을 것 같다고 말씀드렸는데 앞자리를 바꿔주셔서 좀 놀랐던 기억이 남아있다.&lt;/p&gt;
&lt;p&gt;새로 오게된 직장은 기존 직장들과 비슷하게 자동화 검사 설비를 납품하는 곳이었다. 다만 차이가 있다면 그동안은 3차벤더 회사들을 다녔다면 이번엔 2차 벤더 회사로써 원청과 한 단계 더 가깝게 일을 할 수 있었다.&lt;/p&gt;
&lt;p&gt;입사 초반 3개월은 그냥 놀았다고 봐도 무방하다. darknet 객체 탐지 오픈소스를 회사의 요구사항에 맞게끔 dll화 하는것이 과제였는데 원래는 이 dll을 실제 프로젝트에 도입하려고 했으나 원청에서 자사 기술을 쓰고싶다고 하여 엎어졌다. 과제 자체가 취소된건 아니어서 과제는 완료했지만, 그 외 다른일이 주어지지는 않았어서 비교적 여유로운 회사생활을 약 3개월정도 보냈다.&lt;/p&gt;
&lt;p&gt;그렇게 3개월을 소위 '날먹'을 하고 드디어 첫 업무가 주어졌다. 위치는 구미이고, 현장에 납품이 진행되고 있는 설비에 관련된 프로젝트에 합류했다. 설비의 스케일은 내가 맡았던 모든 업무를 다 합쳐도 부족해보일정도로 압도적이었다. 하나의 물체에 대해 수십장의 사진을 촬상하고 딥러닝 + 룰기반 검사가 진행되는데 그 물체의 개수가 수십개였다. 내가 거의 유일하게 꺼려질 정도로 어려워하는 개념이 멀티스레딩과 같은 동시성 처리였는데 이번 프로젝트는 내 약점을 보완하라고 명령이라도 하듯 동시성 처리의 극에 있는 프로젝트였다.&lt;/p&gt;
&lt;p&gt;구미에서의 일정은 본사때와는 완전히 달랐다 우선 08시 30분 출근에 퇴근 시간은 정해져 있지 않아 새벽에 퇴근하기 일쑤였고 주말도 없었다. 힘들지 않다면 거짓말이지만아직 밝히지 않은 나의 2026년 상반기의 목표가 이런 빡센 일정과 관련이 있기 때문에 그렇게 나쁘게만 보고 있지는 않다. 그리고 개발은 언제나 재미있기에 힘들지만 즐겁게 구미 생활을 이어가고 있다.&lt;/p&gt;
&lt;p&gt;회사에 들어오기 전에 나름대로 어떤 점들이 힘들지에 대해 생각을 하고 그것을 어떻게 대응할지 생각을 했었는데, 예상하지 못했던 한 가지가 클린룸이었다. 반도체 제조 현장에서 먼지 등을 최소화 하기웨해 점프슈트 + 신발 + 마스크+ 장갑을 끼고 입실해야하는 곳이 클린룸이었는데 심지어 인터넷도 되지 않아 개발에 있어 검색, LLM 에 의존했던 과거의 내가 원망스러워질 정도였다. 그래서 얼마전부터 주말 중 하루는 LLM과 구글검색을 제외하고 IDE에만 의존해서 코딩을 하고 있다. 이 부분이 진짜 너무 힘들었고, 극복 할 수 있는 문제인지 장담을 못하겠다는 생각을 했다.&lt;/p&gt;
&lt;p&gt;현재 한창 진행중인 프로젝트기에 지금 단계에서 말 할 수 있는 내용이 별로 없어서 나중에 회고를 따로 하려고한다. 회고에 어떤 내용을 기재할지에 대해 키워드를 정리하고 있는데 정말 할 말이 많을 회고글이 되지 않을까 싶다. 정말.. 정말 많은 일이 있었다.&lt;/p&gt;
&lt;p&gt;작년 이맘때쯤엔 학원에 틀어박혀서 미래에 대한 고민을 하고 있었는데 지금은 일이 너무 많아 어떻게 처리할지에 대해 고민하고있는게 신기하기도하고 뿌듯하기도 하다. 뜬금없는 내용이긴 하지만 학원에서 같이 공부했던 분들은 지금 어떻게 지내고 계실까 궁금하기도 하다. 다음 글은 앞으로의 1년에 대한 내용으로 작성해볼 생각이다. 나름의 계획을 가지고 있긴 한데, 좀 더 구체화해서 작성해보도록 해야겠다.&lt;/p&gt;
</description><pubDate>Sun, 06 Jul 2025 10:45:15 +0900</pubDate><guid>http://blex.me/@kimyoungjo/%EA%B7%B8%EB%8F%99%EC%95%88-%EB%AD%98-%ED%96%88%EA%B3%A0-%EC%95%9E%EC%9C%BC%EB%A1%9C-%EB%AD%98-%ED%95%A0%EA%B1%B4%EA%B0%80-2%EB%B6%80</guid></item><item><title>(~25.06.15) 그동안 뭘 했고 앞으로 뭘 할건가 1부</title><link>http://blex.me/@kimyoungjo/250615-%EA%B7%B8%EB%8F%99%EC%95%88-%EB%AD%98-%ED%96%88%EA%B3%A0-%EC%95%9E%EC%9C%BC%EB%A1%9C-%EB%AD%98-%ED%95%A0%EA%B1%B4%EA%B0%80-1%EB%B6%80</link><description>&lt;p&gt;작년 10월에 비전 개발자로써 커리어를 시작해 지난 2월 3번째 회사에 입사하여 현재까지 재직중이다. 그 동안 어떤일이 있었는지 간략하게 기록해보려한다.&lt;/p&gt;
&lt;h4 id="첫번째-직장"&gt;첫번째 직장&lt;/h4&gt;&lt;p&gt;작년 10월 경 10명 규모의 국가 과제 위주를 수행하는 자동화 검사설비 업체에 비전 개발자로 취업했다. 비전팀은 책임님과 나 둘이었고 darknet이라는 c기반의 백본과 yolo라는 객체 탐지 모델을 이용하여 대상 물체의 외관 결함을 탐지하는 방식의 솔루션을 제공하였다.&lt;/p&gt;
&lt;p&gt;나는 관련 지식이 거의 전무했기에 교육이 필요했지만 너무나 작은 규모의 팀 + 국가 과제의 특성상 연말에 결산이 들어가는데 내가 입사한 10월말 부터 12월까지는 회사가 정말 바빠 케어를 받지 못한 채 스스로 관련 지식을 습득했어야 했다. 지금 생각해보면 이 때 claude를 활용하면서 ai 서비스 활용 능력이 크게 늘었다고는 생각하지만 그때 당시는 정말 하루하루 멘탈이 무너져갔던 기억이 있다.&lt;/p&gt;
&lt;p&gt;멘탈이 무너졌던 요인으로는 11월 중순부터 프로젝트에 단독으로 투입될 것 이라는 예고에 대한 압박감과 타지 생활 적응 실패라는 두가지 요인이 악재로 다가왔던것 같다. 처음 겪어보는 기술스택과 방식을 한달 이내에 습득하기엔 쉽지않아 평일 주말, 낮밤을 가리지 않고 메달렸던 기억이 난다. 내 선생님이었던 claude의 크래딧이 부족해 구글 계정을 하나 더 생성하여 2개의 claude 계정의 굴렸던 기억이 난다.&lt;/p&gt;
&lt;p&gt;이 때 안산 다문화 거리에 있는 고시원에서 지냈었는데, 다문화 거리 특유의 스산한 분위기 + 좁디 좁은 고시원이 심적으로 편안함을 제공해야했던 집의 역할을 하지 못했다. 하여 몸과 정신은 하루하루 지쳐갔던 기억이 있다.&lt;/p&gt;
&lt;p&gt;그렇게 예고됐던 11월 중반부터 12월 중순까지의 기한이 주어진 프로젝트에 투입되었다. 알루미늄 소재의 배터리 케이스 외관검사를 진행하는 것이었는데 단순한 객체 탐지 딥러닝 모델 학습 뿐만 아니라 카메라 + 촬영 환경을 뜻하는 광학환경, PLC와의 통신, 딥러닝 모델과 Winform 간의 tcp 통신, 카메라 트리거를 조절하기 위한 배선 설계까지 모두 담당해야했던 터라 정말 어느하나에 집중해서 학습을 할 수 없었던 기억이 난다. 그런데도 어찌저찌 기한은 맞춰야하니 그럴듯한 무언가를 만들어냈는데 하드웨어 설비에 들어가는 프로그램 특성상 디버깅을 설비를 돌리면서 진행해야하는 여건때문에 첫 디버깅이 프로젝트 기한이었던 12월 중순이었던 기억이 난다. 무교지만 그 날만큼은 어떤 신이던 믿고 싶었을 정도로 기도메타로 디버깅에 임했던 것 같다.&lt;/p&gt;
&lt;p&gt;기한이 분명 12월 중순이었지만 정신을 차려보니 어느새 1월 중순으로 미뤄저있었다. 이유는 국가 사업의 특성상 국가가 프로젝트 비용의 절반을 지원했으니 국가의 심사를 받아야하는데 설비에 하드웨어적 에러가 너무 많이 발생하여 12월 내 마무리가 불가능하다는 판정을 내리고 원청과 협동하여 심사에 대비하기 위해 그럴듯한 시퀀스를 조작해야한다는 이유였다. 이 과정속에서 소프트웨어인 내 파트는 계속 밀리게 되었다. 그리고 더 이상 해당 설비를 조작하는 일은 없었다. 내가 12월 31일자로 퇴사했기 때문이다.&lt;/p&gt;
&lt;h4 id="두번째-직장"&gt;두번째 직장&lt;/h4&gt;&lt;p&gt;공개할 수 없는 이유(궁금하시면 메일 주세요)로 첫번째 회사를 2달여만에 그만두고 1월에 바로 이직을 하게 되었다. 첫번째 직장과 비슷한 일을 비슷한 규모로 진행하는 업체였다. 그리고 이 곳에서 3주만에 내 실력을 300% 이상 끌어올려주신 책임님을 만났다.&lt;/p&gt;
&lt;p&gt;새로 만난 책임님은 천재과에 속하는 분이셨던것으로 기억된다. 어떠한 문제를 마주했을때 해당 문제를 다각도로 분석하시며 누구도 생각치 못했던 방향으로 문제를 해결하시며 과제를 해결해오셨다고 한다. 처음에는 약간 과장이 섞인 소개라고 생각했지만 곧 그 부분들을 체감하며 인정하게 되었다.&lt;/p&gt;
&lt;p&gt;이전 직장과 가장 큰 차이점은 온보딩 기간이 있었다는 것이다. 그리고 그 온보딩 기간동안 책임님 주도의 ojt 역시 있었다. 이전에 진행하셨던 프로젝트를 따라가며 앞으로 사용할 기술스택과 작업 시퀀스에 대해 학습할 수 있게끔 엑셀로 정리된 파일을 주셨고 각 단계마다 코드리뷰를 받듯 책임님의 체크와 질문 폭탄이 있었다. 그 과정속에서 가장 많이 들었던 말이 왜 이렇게 진행하셨나요 ? 다른 방법은 없었나요 ? 이다. 그런 과정을 몇차례 겪어보니 나도 체크를 받을때 다양한 관점에서 고민한 흔적들을 준비하여 체크를 받았었는데, 그럼에도 불구하고 더 창의적인 다른 안을 제시받았을 정도로 생각치도 못한 방안을 내놓아 주셨다. 일전의 소개가 과장이 아님을 이 때 체감을 많이 했고 나 역시 이 때 문제 해결을 위한 사고력을 많이 길렀던 것 같다. 이대로 이 분께 1년만 배우면 정말 큰 성장을 할 수 있으리라 생각했다. 하지만 그 시간을 길지 않았고 나는 1달도 채 되지 않아 퇴사하였다.&lt;/p&gt;
&lt;p&gt;이 바닥 업무의 특성상 파견, 출장이 잦다. 파견과 출장이 잦다는 것은 부대비용이 발생한다는 것이다. 보통은 회사에서 그것을 지원해주는게 당연한 것이라고 생각했고 첫번째 직장도 그러했는데 두번째직장은 기본급 외의 수당이 제로였다. 심지어는 식대조차 지원이 되지 않았다.&lt;/p&gt;
&lt;p&gt;직장을 다니면서 내 심각한 단점을 알게됐다. 바로 압박감에 대한 대처가 전혀 안됨 + 그로 인한 멘탈적 문제가 심각하다는 것이었다. 나를 믿어주고 응원해주거나 내가 완벽한 주도권을 가지고 일을 할때는 가진 능력 이상의 능률을 보이지만 강압적 분위기 + 내가 결정할 수 있는 사안이 없어지면 능력의 1/5도 발휘를 못한다는 것을 첫번째 직장과 두번째 직장을 거치며 느꼈고 주변인들에게 물어보니 &amp;quot; 응 너 원래도 그랬어 &amp;quot; 라는 대답을 들었다.&lt;/p&gt;
&lt;p&gt;그래서 왜 퇴사했냐 앞선 두 문단에 묘사된 문제와 내가 이 회사에 온 이유인 책임님이 회사를 떠나실 예정이라는 사실을 알게됐기 때문이다. 안그래도 자금난에 허덕이고 있던 나는 식대조차 지원되지 않는 환경에서 정말 고생을 많이 했다. 식대만 지원되지 않는 환경이었다면 잘 아끼면서 그럭저럭 다녔겠지만 밥은 또 다같이 먹자가 회사의 모토라 매 끼니마다 만원 이상의 식비가 빠져나갔다. 내가 퇴사의사를 밝히자마자 당일 퇴사를 당했었는데, 해당 주에는 밥값이 없어 어머니께 손을 벌렸을 정도였다.&lt;/p&gt;
&lt;p&gt;두번째 이유였던 강압적 분위기의 형성은 회사가 개발팀을 믿지 못한다는 뉘앙스의 말에서 시작됐다. 이 부분은 상세하게 말하기 좀 그래서 어떻게 표현해야할 지 모르겠는데 앞서 말한 내 특징은 신뢰받지 못함 + 강압적인 분위기에서 퍼포먼스를 낼 수 없다 이기 때문에 내 역량을 발휘할 수 없을 것 같다고 판단했다.&lt;/p&gt;
&lt;p&gt;사실 앞서 두 이유는 부가적인 이유들이고 제일 큰 이유는 새 책임님이 사라지신다는 점이었다. 그 점 때문에 두번째 이유였던 개발팀에 대한 압박이 강하게 들어왔던 것으로 추정된다.(빈자리를 메워야하니) 뭐 그런것을 차치하고서 내가 이 회사에 오게된 이유엿던 책임님의 퇴사 소식은 나에게 더 이상 이 회사를 다녀야하는 이유를 사라지게 하는데 충분했다.&lt;/p&gt;
&lt;p&gt;설 연휴가 매우 길었던것으로 기억하는데 해당 연휴 시작전에 퇴사했다. 그게 최소한의 예의라고 생각해서 그렇게 했는데 다음에 만약에 이런일이 있다면 그러지 않을 것 같다.&lt;/p&gt;
&lt;p&gt;그렇게 다시 취준생활이 시작됐고 생각보다 일찍 취업이 됐다.&lt;/p&gt;
&lt;p&gt;-계속-&lt;/p&gt;
</description><pubDate>Sun, 15 Jun 2025 20:58:03 +0900</pubDate><guid>http://blex.me/@kimyoungjo/250615-%EA%B7%B8%EB%8F%99%EC%95%88-%EB%AD%98-%ED%96%88%EA%B3%A0-%EC%95%9E%EC%9C%BC%EB%A1%9C-%EB%AD%98-%ED%95%A0%EA%B1%B4%EA%B0%80-1%EB%B6%80</guid></item><item><title>앵귤러는 화면을 어떻게 그릴까?</title><link>http://blex.me/@baealex/how-angular-renders-the-screen</link><description>&lt;p&gt;프론트엔드를 개발하는 사람이라면 리액트(React)의 '가상돔(Virtual DOM)' 이라는 개념에 대해서 자주 들어봤을 것이다. 리액트는 상태 변경을 요청할 때 가상돔을 재생성하여 필요한 곳을 업데이트 한다.&lt;/p&gt;&lt;p&gt;나는 최근에 앵귤러를 다루면서 성능적인 이슈를 대응할 필요가 있었고 이를 위해서 렌더링 방식에 대해서 알아야 했다. 앵귤러에는 가상돔과 같은 개념은 존재하지 않는다. 앵귤러는 대체 어떻게 작동하고 있는 걸까? 🤔&lt;/p&gt;&lt;h4&gt;✨ Zone JS&lt;/h4&gt;&lt;p&gt;&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/content/2025/4/6/20254623_Ru4tcxKcwxaKUyVd4pPe.png" alt=""&gt;&lt;/figure&gt;&lt;p&gt;웹 사이트에는 눈에 보이지 않는 수 많은 일들이 계속 일어난다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;사용자가 마우스를 클릭하거나 키보드를 누르는 &lt;strong&gt;이벤트&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;"3초 뒤에 이 함수 실행!" 같은 &lt;strong&gt;setTimeout&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;서버에서 데이터를 가져오는 &lt;strong&gt;HTTP 요청&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;앵귤러는 이런 일들이 언제 시작되고 언제 끝나는지 알아야 한다. "데이터 로딩이 끝났으니 이제 화면을 바꿔야겠군!" 하고 눈치챌 수 있어야 한다.&lt;/p&gt;&lt;p&gt;이를 해결하는 것이 &lt;strong&gt;Zone.js&lt;/strong&gt; 이다. Zone.js는 위에 나열된 브라우저의 비동기 동작들을 몽키패칭하여 작업을 추적할 수 있다. 비동기 작업이 완료되면 Zone.js가 이를 감지하고 Angular에게 알려준다.&lt;/p&gt;&lt;pre&gt;&lt;code class="language-typescript"&gt;function addEventListener(eventName, callback) {
     // call the real addEventListener
     callRealAddEventListener(eventName, function() {
        // first call the original callback
        callback(...);     
        // and then run Angular-specific functionality
        var changed = angular.runChangeDetection();
         if (changed) {
             angular.reRenderUIPart();
         }
     });
}
&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;✨ runChangeDetection&lt;/h4&gt;&lt;p&gt;Zone.js가 "작업 완료!" 신호를 보내면, Angular는 이제 "무엇이 변했는지" 확인해야 한다. 위 코드 예시에서 &lt;code&gt;angular.runChangeDetection()&lt;/code&gt; 부분이 바로 이 역할을 개념적으로 나타낸다. 이것이 바로 &lt;strong&gt;변경 감지(Change Detection)&lt;/strong&gt; 과정이다.&lt;/p&gt;&lt;p&gt;React의 가상돔(Virtual DOM)과 달리, Angular는 컴포넌트의 상태(데이터)가 이전과 비교해서 달라졌는지 직접 확인한다. 도대체 어떻게?&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;데이터 바인딩 확인:&lt;/strong&gt; Angular는 템플릿에 있는 모든 데이터 바인딩(예: &lt;code&gt;{{ title }}&lt;/code&gt;, &lt;code&gt;[value]="count"&lt;/code&gt;)을 기억하고 있다가 변경 감지가 시작되면, Angular는 각 바인딩에 연결된 컴포넌트의 현재 값(예: &lt;code&gt;this.title&lt;/code&gt;, &lt;code&gt;this.count&lt;/code&gt;)을 확인한다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;이전 값과 비교:&lt;/strong&gt; Angular는 이전에 기억해둔 값과 현재 값을 비교한다. 만약 &lt;code&gt;this.title&lt;/code&gt;의 값이 이전과 다르다면, "변경되었군!"이라고 표시한다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;변경 상태 전파:&lt;/strong&gt; 이 과정은 애플리케이션의 모든 컴포넌트를 대상으로 (기본 전략상) 위에서 아래로 진행된다. 부모 컴포넌트부터 자식 컴포넌트까지 차례대로 확인하며 변경된 부분을 찾아낸다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;code&gt;runChangeDetection()&lt;/code&gt;은 바로 이 꼼꼼한 '비교 및 확인 작업' 전체를 의미한다. 이 과정의 결과로 "어떤 부분이 변경되었는지"에 대한 정보가 모인다. 코드 예시의 &lt;code&gt;var changed&lt;/code&gt; 변수는 이 비교 과정에서 단 하나라도 변경된 사항이 있었는지를 나타내는 셈이다.&lt;/p&gt;&lt;h4&gt;✨ reRenderUIPart&lt;/h4&gt;&lt;p&gt;변경 감지를 통해 "어떤 데이터가 바뀌었는지" 확인했다면, 이제 그 결과를 실제 사용자 화면에 반영해야 한다. 코드 예시의 &lt;code&gt;if (changed) { angular.reRenderUIPart(); }&lt;/code&gt; 부분이 이 마지막 단계를 나타낸다.&lt;/p&gt;&lt;p&gt;여기서 중요한 점은 Angular가 &lt;strong&gt;변경된 부분만 정확히 찾아 실제 DOM을 업데이트&lt;/strong&gt;한다는 것이다. 이에 필요한 정보들은 앵귤러가 앱을 컴파일하면서 기억해 둔다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;가상돔 없음:&lt;/strong&gt; React처럼 가상돔 전체를 새로 만들고 비교하는 과정이 없다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;직접 DOM 조작:&lt;/strong&gt; Angular는 2단계에서 변경되었다고 확인된 데이터 바인딩과 연결된 실제 DOM 요소를 직접 찾아간다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;예를 들어, 컴포넌트의 &lt;code&gt;title&lt;/code&gt; 속성이 'Hello'에서 'World'로 바뀌었고, 이것이 템플릿의 &lt;code&gt;&amp;lt;h1&amp;gt;{{ title }}&amp;lt;/h1&amp;gt;&lt;/code&gt; 부분과 연결되어 있다면, Angular는 정확히 해당 &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; 태그를 찾아 그 안의 텍스트 내용만 'World'로 업데이트한다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;만약 &lt;code&gt;[disabled]="isButtonDisabled"&lt;/code&gt; 바인딩 값이 &lt;code&gt;false&lt;/code&gt;에서 &lt;code&gt;true&lt;/code&gt;로 바뀌었다면, 해당 버튼 요소의 &lt;code&gt;disabled&lt;/code&gt; 속성을 직접 추가한다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;최소한의 업데이트:&lt;/strong&gt; 전체 HTML 구조를 새로 그리는 것이 아니라, 필요한 부분만 정밀하게 수정한다. 이 방식 덕분에 효율적인 화면 업데이트가 가능하다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;따라서 &lt;code&gt;reRenderUIPart()&lt;/code&gt;는 비록 개념적인 이름이지만, 실제로는 Angular가 변경된 데이터를 바탕으로 &lt;strong&gt;실제 DOM의 특정 부분(텍스트 노드, 속성 등)을 직접적이고 효율적으로 수정하는 과정&lt;/strong&gt;을 의미한다.&lt;/p&gt;&lt;h4&gt;😆 보너스: 성능을 높히는 OnPush 전략&lt;/h4&gt;&lt;p&gt;지금까지 설명한 Angular의 변경 감지 과정(Zone.js 감지 -&amp;gt; 변경 확인 -&amp;gt; DOM 업데이트)은 기본(Default) 전략이다. 이 전략은 매우 편리하지만, 애플리케이션이 복잡해지면 때로는 불필요한 확인 작업이 성능에 부담을 줄 수도 있다. Zone.js가 아주 사소한 비동기 작업 완료 신호만 보내도, Angular는 잠재적으로 모든 컴포넌트의 변경 여부를 확인하기 때문이다.&lt;/p&gt;&lt;p&gt;이럴 때 사용할 수 있는 강력한 최적화 기법이 바로 &lt;code&gt;ChangeDetectionStrategy.OnPush&lt;/code&gt; 전략이다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;OnPush 전략이란?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;컴포넌트 설정에 &lt;code&gt;changeDetection: ChangeDetectionStrategy.OnPush&lt;/code&gt;를 추가하면, 해당 컴포넌트는 더 이상 모든 Zone.js의 신호에 반응하여 무조건 변경 감지를 수행하지 않는다. 대신, &lt;strong&gt;다음과 같은 특정 조건 중 하나가 만족될 때만&lt;/strong&gt; 변경 감지를 진행한다.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;새로운 참조(Reference)의 &lt;/strong&gt;&lt;code&gt;@Input()&lt;/code&gt;&lt;strong&gt; 변경:&lt;/strong&gt; 컴포넌트의 &lt;code&gt;@Input()&lt;/code&gt; 프로퍼티로 &lt;strong&gt;새로운 객체나 배열 참조&lt;/strong&gt;가 전달될 때. (주의: 객체 내부의 속성만 바뀌거나 배열 요소만 추가/삭제되는 등 참조 자체가 변경되지 않으면 감지하지 못한다.)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;컴포넌트 또는 자식 요소의 이벤트 발생:&lt;/strong&gt; 해당 컴포넌트의 템플릿 내부나 그 자식 컴포넌트에서 &lt;strong&gt;이벤트(예: 클릭, 입력 등)가 발생&lt;/strong&gt;하여 이벤트 핸들러가 실행될 때.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;async&lt;/code&gt;&lt;strong&gt; 파이프 사용:&lt;/strong&gt; 템플릿에서 &lt;code&gt;async&lt;/code&gt; 파이프가 새로운 값을 방출(emit)할 때. (&lt;code&gt;async&lt;/code&gt; 파이프는 내부적으로 변경 감지를 요청한다.)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;명시적 요청:&lt;/strong&gt; 개발자가 컴포넌트의 &lt;code&gt;ChangeDetectorRef&lt;/code&gt;를 주입받아 &lt;code&gt;markForCheck()&lt;/code&gt; 메소드를 직접 호출하여 변경 감지가 필요함을 명시적으로 알릴 때.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;왜 성능이 향상될까?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;OnPush&lt;/code&gt; 전략을 사용하면, 해당 컴포넌트는 오직 '자신과 직접적으로 관련된 변화'가 발생했을 가능성이 높을 때만 변경 감지를 수행합니다. 애플리케이션의 다른 부분에서 발생한 비동기 작업 완료에는 영향을 받지 않고 '독립적'으로 동작하는 것이기 때문이다.&lt;/p&gt;&lt;p&gt;이는 마치 "내게 꼭 필요한 정보가 업데이트되거나, 내 구역에서 직접적인 요청이 있을 때만 확인하겠다"고 선언하는 것과 같다. 불필요한 확인 작업을 대폭 줄여주므로, 특히 컴포넌트 트리가 깊고 복잡한 대규모 애플리케이션에서 성능 향상 효과를 크게 볼 수 있다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;주의할 점:&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;OnPush&lt;/code&gt;를 사용하면 &lt;code&gt;@Input&lt;/code&gt;으로 받은 객체의 내부 속성만 변경하는 방식(mutable update)으로는 변경 감지가 자동으로 일어나지 않는다. 따라서 &lt;code&gt;OnPush&lt;/code&gt; 컴포넌트에 데이터를 전달할 때는 항상 새로운 객체나 배열 참조를 생성하여 전달하는 방식(immutable update)을 사용하는 것이 중요하다. 또는 필요시 &lt;code&gt;markForCheck()&lt;/code&gt;를 사용하여 수동으로 변경 감지를 예약해야 한다.&lt;/p&gt;&lt;p&gt;&lt;code&gt;OnPush&lt;/code&gt; 전략은 Angular의 변경 감지 시스템을 더 깊이 이해하고 애플리케이션 성능을 한 단계 끌어올리고 싶을 때 고려해볼 만한 강력한 도구다.&lt;/p&gt;&lt;h4&gt;참고 자료&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;a target="_blank" rel="noopener noreferrer nofollow" href="https://blog.angular-university.io/how-does-angular-2-change-detection-really-work/"&gt;https://blog.angular-university.io/how-does-angular-2-change-detection-really-work/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;번외&lt;/h4&gt;&lt;p&gt;&lt;/p&gt;&lt;figure style="text-align: center; display: flex; justify-content: center; flex-direction: column; align-items: center;"&gt;&lt;img src="https://blex.me/resources/media/images/title/2023/6/21/baealex/0_7azVXV7G.png" alt=""&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;뭔가 스벨트(Svelte)랑 비슷...한가?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;여기까지 생각하다 보니, 문득 다른 친구가 떠올랐다. 바로 스벨트다. 스벨트도 컴파일 방식을 적용한 프레임워크이고, 가상 DOM 안 쓰고, 컴파일할 때 최적의 자바스크립트 코드를 만들어낸다는 점에서 어쩐지 앵귤러랑 좀 흡사해 보였다.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;결정적인 차이는?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;비슷해 보이지만 결정적인 차이가 있었는데, 바로 &lt;strong&gt;'런타임(Runtime)'&lt;/strong&gt; 번들의 사이즈다.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;스벨트:&lt;/strong&gt; 이 친구는 컴파일하고 나면 거의 자기 흔적(런타임 코드)을 남기지 않는다. 그냥 순수한 자바스크립트 코드만 남아서 브라우저에서 직접 DOM을 막 조작한다. 스벨트의 핵심 철학인 '프레임워크가 사라지는' 느낌이다.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;앵귤러:&lt;/strong&gt; 컴파일을 열심히 하긴 하지만, 여전히 브라우저에는 꽤 덩치 있는 &lt;strong&gt;'앵귤러 엔진(런타임)'&lt;/strong&gt;이 남아서 전체를 관리한다. 런타임에 변경 감지 시스템을 돌리고, 의존성 주입이다 뭐다 이것저것 챙겨야 할게 많다.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;</description><pubDate>Sun, 06 Apr 2025 23:11:46 +0900</pubDate><guid>http://blex.me/@baealex/how-angular-renders-the-screen</guid></item></channel></rss>